1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "DefaultBrowser.h"
16 #include "mozilla/ArrayUtils.h"
17 #include "mozilla/RefPtr.h"
18 #include "mozilla/Unused.h"
19 #include "mozilla/Try.h"
20 #include "mozilla/WinHeaderOnlyUtils.h"
22 namespace mozilla::default_agent
{
24 using BrowserResult
= mozilla::WindowsErrorResult
<Browser
>;
26 constexpr std::string_view kUnknownBrowserString
= "";
28 constexpr std::pair
<std::string_view
, Browser
> kStringBrowserMap
[]{
29 {"error", Browser::Error
},
30 {kUnknownBrowserString
, Browser::Unknown
},
31 {"firefox", Browser::Firefox
},
32 {"chrome", Browser::Chrome
},
33 {"edge", Browser::EdgeWithEdgeHTML
},
34 {"edge-chrome", Browser::EdgeWithBlink
},
35 {"ie", Browser::InternetExplorer
},
36 {"opera", Browser::Opera
},
37 {"brave", Browser::Brave
},
38 {"yandex", Browser::Yandex
},
39 {"qq-browser", Browser::QQBrowser
},
40 {"360-browser", Browser::_360Browser
},
41 {"sogou", Browser::Sogou
},
42 {"duckduckgo", Browser::DuckDuckGo
},
45 static_assert(mozilla::ArrayLength(kStringBrowserMap
) == kBrowserCount
);
47 std::string
GetStringForBrowser(Browser browser
) {
48 for (const auto& [mapString
, mapBrowser
] : kStringBrowserMap
) {
49 if (browser
== mapBrowser
) {
50 return std::string
{mapString
};
54 return std::string(kUnknownBrowserString
);
57 Browser
GetBrowserFromString(const std::string
& browserString
) {
58 for (const auto& [mapString
, mapBrowser
] : kStringBrowserMap
) {
59 if (browserString
== mapString
) {
64 return Browser::Unknown
;
67 BrowserResult
TryGetDefaultBrowser() {
68 RefPtr
<IApplicationAssociationRegistration
> pAAR
;
69 HRESULT hr
= CoCreateInstance(
70 CLSID_ApplicationAssociationRegistration
, nullptr, CLSCTX_INPROC
,
71 IID_IApplicationAssociationRegistration
, getter_AddRefs(pAAR
));
74 return BrowserResult(mozilla::WindowsError::FromHResult(hr
));
77 // Whatever is handling the HTTP protocol is effectively the default browser.
78 mozilla::UniquePtr
<wchar_t, mozilla::CoTaskMemFreeDeleter
> registeredApp
;
80 wchar_t* rawRegisteredApp
;
81 hr
= pAAR
->QueryCurrentDefault(L
"http", AT_URLPROTOCOL
, AL_EFFECTIVE
,
85 return BrowserResult(mozilla::WindowsError::FromHResult(hr
));
87 registeredApp
= mozilla::UniquePtr
<wchar_t, mozilla::CoTaskMemFreeDeleter
>(
91 // Get the application Friendly Name associated to the found ProgID. This is
92 // sized to be larger than any observed or expected friendly names. Long
93 // friendly names tend to be in the form `[Company] [Browser] [Variant]`
94 std::array
<wchar_t, 256> friendlyName
{};
95 DWORD friendlyNameLen
= friendlyName
.size();
96 hr
= AssocQueryStringW(ASSOCF_NONE
, ASSOCSTR_FRIENDLYAPPNAME
,
97 registeredApp
.get(), nullptr, friendlyName
.data(),
101 return BrowserResult(mozilla::WindowsError::FromHResult(hr
));
104 // This maps a browser's Friendly Name prefix to an enum variant that we'll
105 // use to identify that browser in our telemetry ping (which is this
106 // function's return value).
107 constexpr std::pair
<std::wstring_view
, Browser
> kFriendlyNamePrefixes
[] = {
108 {L
"Firefox", Browser::Firefox
},
109 {L
"Google Chrome", Browser::Chrome
},
110 {L
"Microsoft Edge", Browser::EdgeWithBlink
},
111 {L
"Internet Explorer", Browser::InternetExplorer
},
112 {L
"Opera", Browser::Opera
},
113 {L
"Brave", Browser::Brave
},
114 {L
"Yandex", Browser::Yandex
},
115 {L
"QQBrowser", Browser::QQBrowser
},
116 // 360安全浏览器 UTF-16 encoding
117 {L
"\u0033\u0036\u0030\u5b89\u5168\u6d4f\u89c8\u5668",
118 Browser::_360Browser
},
119 // 搜狗高速浏览器 UTF-16 encoding
120 {L
"\u641c\u72d7\u9ad8\u901f\u6d4f\u89c8\u5668", Browser::Sogou
},
121 {L
"DuckDuckGo", Browser::DuckDuckGo
},
124 // We should have one prefix for every browser we track, minus exceptions
126 // Error - not a real browser.
127 // Unknown - not a real browser.
128 // EdgeWithEdgeHTML - duplicate friendly name with EdgeWithBlink with special
130 static_assert(mozilla::ArrayLength(kFriendlyNamePrefixes
) ==
133 for (const auto& [prefix
, browser
] : kFriendlyNamePrefixes
) {
134 // Find matching Friendly Name prefix.
135 if (!wcsnicmp(friendlyName
.data(), prefix
.data(), prefix
.length())) {
136 if (browser
== Browser::EdgeWithBlink
) {
137 // Disambiguate EdgeWithEdgeHTML and EdgeWithBlink.
138 // The ProgID below is documented as having not changed while Edge was
139 // actively developed. It's assumed but unverified this is true in all
140 // cases (e.g. across locales).
142 // Note: at time of commit EdgeWithBlink from the Windows Store was a
143 // wrapper for Edge Installer instead of a package containing Edge,
144 // therefore the Default Browser associating ProgID was not in the form
145 // "AppX[hash]" as expected. It is unclear if the EdgeWithEdgeHTML and
146 // EdgeWithBlink ProgIDs would differ if the latter is changed into a
147 // package containing Edge.
148 constexpr std::wstring_view progIdEdgeHtml1
{
149 L
"AppXq0fevzme2pys62n3e0fbqa7peapykr8v"};
150 // Apparently there is at least one other ProgID used by EdgeHTML Edge.
151 constexpr std::wstring_view progIdEdgeHtml2
{
152 L
"AppXd4nrz8ff68srnhf9t5a8sbjyar1cr723"};
154 if (!wcsnicmp(registeredApp
.get(), progIdEdgeHtml1
.data(),
155 progIdEdgeHtml1
.length()) ||
156 !wcsnicmp(registeredApp
.get(), progIdEdgeHtml2
.data(),
157 progIdEdgeHtml2
.length())) {
158 return Browser::EdgeWithEdgeHTML
;
166 // The default browser is one that we don't know about.
167 return Browser::Unknown
;
170 BrowserResult
TryGetReplacePreviousDefaultBrowser(Browser currentDefault
) {
171 // This function uses a registry value which stores the current default
172 // browser. It returns the data stored in that registry value and replaces the
173 // stored string with the current default browser string that was passed in.
175 std::string currentDefaultStr
= GetStringForBrowser(currentDefault
);
176 std::string previousDefault
=
177 RegistryGetValueString(IsPrefixed::Unprefixed
, L
"CurrentDefault")
178 .unwrapOr(mozilla::Some(currentDefaultStr
))
179 .valueOr(currentDefaultStr
);
181 mozilla::Unused
<< RegistrySetValueString(
182 IsPrefixed::Unprefixed
, L
"CurrentDefault", currentDefaultStr
.c_str());
184 return GetBrowserFromString(previousDefault
);
187 DefaultBrowserResult
GetDefaultBrowserInfo() {
188 DefaultBrowserInfo browserInfo
;
190 MOZ_TRY_VAR(browserInfo
.currentDefaultBrowser
, TryGetDefaultBrowser());
192 browserInfo
.previousDefaultBrowser
,
193 TryGetReplacePreviousDefaultBrowser(browserInfo
.currentDefaultBrowser
));
198 // We used to prefix this key with the installation directory, but that causes
199 // problems with our new "only one ping per day across installs" restriction.
200 // To make sure all installations use consistent data, the value's name is
201 // being migrated to a shared, non-prefixed name.
202 // This function doesn't really do any error handling, because there isn't
203 // really anything to be done if it fails.
204 void MaybeMigrateCurrentDefault() {
205 const wchar_t* valueName
= L
"CurrentDefault";
207 MaybeStringResult valueResult
=
208 RegistryGetValueString(IsPrefixed::Prefixed
, valueName
);
209 if (valueResult
.isErr()) {
212 mozilla::Maybe
<std::string
> maybeValue
= valueResult
.unwrap();
213 if (maybeValue
.isNothing()) {
214 // No value to migrate
217 std::string value
= maybeValue
.value();
219 mozilla::Unused
<< RegistryDeleteValue(IsPrefixed::Prefixed
, valueName
);
221 // Only migrate the value if no value is in the new location yet.
222 valueResult
= RegistryGetValueString(IsPrefixed::Unprefixed
, valueName
);
223 if (valueResult
.isErr()) {
226 if (valueResult
.unwrap().isNothing()) {
227 mozilla::Unused
<< RegistrySetValueString(IsPrefixed::Unprefixed
, valueName
,
232 Browser
GetDefaultBrowser() {
233 return TryGetDefaultBrowser().unwrapOr(Browser::Error
);
235 Browser
GetReplacePreviousDefaultBrowser(Browser currentBrowser
) {
236 return TryGetReplacePreviousDefaultBrowser(currentBrowser
)
237 .unwrapOr(Browser::Error
);
240 } // namespace mozilla::default_agent