Backed out 3 changesets (bug 1883476, bug 1826375) for causing windows build bustages...
[gecko.git] / toolkit / mozapps / defaultagent / DefaultBrowser.cpp
blob87d3f62632e59310f18000331166005f65c2ccdc
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"
9 #include <string>
11 #include <shlobj.h>
13 #include "EventLog.h"
14 #include "Registry.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) {
60 return mapBrowser;
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));
72 if (FAILED(hr)) {
73 LOG_ERROR(hr);
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,
82 &rawRegisteredApp);
83 if (FAILED(hr)) {
84 LOG_ERROR(hr);
85 return BrowserResult(mozilla::WindowsError::FromHResult(hr));
87 registeredApp = mozilla::UniquePtr<wchar_t, mozilla::CoTaskMemFreeDeleter>(
88 rawRegisteredApp);
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(),
98 &friendlyNameLen);
99 if (FAILED(hr)) {
100 LOG_ERROR(hr);
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
125 // listed below.
126 // Error - not a real browser.
127 // Unknown - not a real browser.
128 // EdgeWithEdgeHTML - duplicate friendly name with EdgeWithBlink with special
129 // handling below.
130 static_assert(mozilla::ArrayLength(kFriendlyNamePrefixes) ==
131 kBrowserCount - 3);
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;
162 return browser;
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());
191 MOZ_TRY_VAR(
192 browserInfo.previousDefaultBrowser,
193 TryGetReplacePreviousDefaultBrowser(browserInfo.currentDefaultBrowser));
195 return browserInfo;
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()) {
210 return;
212 mozilla::Maybe<std::string> maybeValue = valueResult.unwrap();
213 if (maybeValue.isNothing()) {
214 // No value to migrate
215 return;
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()) {
224 return;
226 if (valueResult.unwrap().isNothing()) {
227 mozilla::Unused << RegistrySetValueString(IsPrefixed::Unprefixed, valueName,
228 value.c_str());
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