Bug 1703578 - Part 3: Invoke WDBA to set UserChoice. r=bytesized

Depends on D113427

Differential Revision: https://phabricator.services.mozilla.com/D113428
This commit is contained in:
Adam Gashlin 2021-06-17 18:06:35 +00:00
Родитель f22ce8ffdd
Коммит 00133ca183
5 изменённых файлов: 158 добавлений и 1 удалений

Просмотреть файл

@ -263,6 +263,12 @@ pref("browser.shell.mostRecentDateSetAsDefault", "");
pref("browser.shell.skipDefaultBrowserCheckOnFirstRun", true);
pref("browser.shell.didSkipDefaultBrowserCheckOnFirstRun", false);
pref("browser.shell.defaultBrowserCheckCount", 0);
#if defined(XP_WIN)
// Attempt to set the default browser on Windows 10 using the UserChoice registry keys,
// before falling back to launching the modern Settings dialog.
pref("browser.shell.setDefaultBrowserUserChoice", true);
#endif
// 0 = blank, 1 = home (browser.startup.homepage), 2 = last visited page, 3 = resume previous browser session
// The behavior of option 3 is detailed at: http://wiki.mozilla.org/Session_Restore

Просмотреть файл

@ -19,6 +19,18 @@ ChromeUtils.defineModuleGetter(
"resource://gre/modules/WindowsRegistry.jsm"
);
XPCOMUtils.defineLazyModuleGetters(this, {
Subprocess: "resource://gre/modules/Subprocess.jsm",
setTimeout: "resource://gre/modules/Timer.jsm",
});
XPCOMUtils.defineLazyServiceGetter(
this,
"XreDirProvider",
"@mozilla.org/xre/directory-provider;1",
"nsIXREDirProvider"
);
/**
* Internal functionality to save and restore the docShell.allow* properties.
*/
@ -112,6 +124,84 @@ let ShellServiceInternal = {
return false;
},
/*
* Set the default browser through the UserChoice registry keys on Windows.
*
* NOTE: This does NOT open the System Settings app for manual selection
* in case of failure. If that is desired, catch the exception and call
* setDefaultBrowser().
*
* @return Promise, resolves when successful, rejects with Error on failure.
*/
async setAsDefaultUserChoice() {
if (AppConstants.platform != "win") {
throw new Error("Windows-only");
}
// We launch the WDBA to handle the registry writes, see
// SetDefaultBrowserUserChoice() in
// toolkit/mozapps/defaultagent/SetDefaultBrowser.cpp.
// This is external in case an overzealous antimalware product decides to
// quarrantine any program that writes UserChoice, though this has not
// occurred during extensive testing.
if (!ShellService.checkAllProgIDsExist()) {
throw new Error("checkAllProgIDsExist() failed");
}
if (!ShellService.checkBrowserUserChoiceHashes()) {
throw new Error("checkBrowserUserChoiceHashes() failed");
}
const wdba = Services.dirsvc.get("XREExeF", Ci.nsIFile);
wdba.leafName = "default-browser-agent.exe";
const aumi = XreDirProvider.getInstallHash();
const exeProcess = await Subprocess.call({
command: wdba.path,
arguments: ["set-default-browser-user-choice", aumi],
});
// exit codes
const S_OK = 0;
const STILL_ACTIVE = 0x103;
const exeWaitTimeoutMs = 2000; // 2 seconds
const exeWaitPromise = exeProcess.wait();
const timeoutPromise = new Promise(function(resolve, reject) {
setTimeout(() => resolve({ exitCode: STILL_ACTIVE }), exeWaitTimeoutMs);
});
const { exitCode } = await Promise.race([exeWaitPromise, timeoutPromise]);
if (exitCode != S_OK) {
throw new Error(`WDBA nonzero exit code ${exitCode}`);
}
},
// override nsIShellService.setDefaultBrowser() on the ShellService proxy.
setDefaultBrowser(claimAllTypes, forAllUsers) {
// On Windows 10, our best chance is to set UserChoice, so try that first.
if (
AppConstants.isPlatformAndVersionAtLeast("win", "10") &&
Services.prefs.getBoolPref(
"browser.shell.setDefaultBrowserUserChoice",
true
)
) {
// nsWindowsShellService::SetDefaultBrowser() kicks off several
// operations, but doesn't wait for their result. So we don't need to
// await the result of setAsDefaultUserChoice() here, either, we just need
// to fall back in case it fails.
this.setAsDefaultUserChoice().catch(err => {
Cu.reportError(err);
this.shellService.setDefaultBrowser(claimAllTypes, forAllUsers);
});
return;
}
this.shellService.setDefaultBrowser(claimAllTypes, forAllUsers);
},
setAsDefault() {
let claimAllTypes = true;
let setAsDefaultError = false;

Просмотреть файл

@ -51,11 +51,14 @@ elif CONFIG["OS_ARCH"] == "WINNT":
SOURCES += [
"nsWindowsShellService.cpp",
"WindowsDefaultBrowser.cpp",
"WindowsUserChoice.cpp",
]
LOCAL_INCLUDES += [
"../../../other-licenses/nsis/Contrib/CityHash/cityhash",
]
OS_LIBS += [
"bcrypt",
"crypt32",
"propsys",
]
@ -70,7 +73,12 @@ EXTRA_JS_MODULES += [
"ShellService.jsm",
]
for var in ("MOZ_APP_DISPLAYNAME", "MOZ_APP_NAME", "MOZ_APP_VERSION"):
for var in (
"MOZ_APP_DISPLAYNAME",
"MOZ_APP_NAME",
"MOZ_APP_VERSION",
"MOZ_DEFAULT_BROWSER_AGENT",
):
DEFINES[var] = '"%s"' % CONFIG[var]
CXXFLAGS += CONFIG["TK_CFLAGS"]

Просмотреть файл

@ -89,4 +89,23 @@ interface nsIWindowsShellService : nsISupports
* for noncritical telemetry use.
*/
AString classifyShortcut(in AString aPath);
/*
* Check if setDefaultBrowserUserChoice() is expected to succeed.
*
* This checks the ProgIDs for this installation, and the hash of the existing
* UserChoice association.
*
* @return true if the check succeeds, false otherwise.
*/
bool canSetDefaultBrowserUserChoice();
/*
* checkAllProgIDsExist() and checkBrowserUserChoiceHashes() are components
* of canSetDefaultBrowserUserChoice(), broken out for telemetry purposes.
*
* @return true if the check succeeds, false otherwise.
*/
bool checkAllProgIDsExist();
bool checkBrowserUserChoiceHashes();
};

Просмотреть файл

@ -31,6 +31,7 @@
#include "mozilla/ErrorResult.h"
#include "mozilla/gfx/2D.h"
#include "WindowsDefaultBrowser.h"
#include "WindowsUserChoice.h"
#include <windows.h>
#include <shellapi.h>
@ -231,6 +232,39 @@ nsresult nsWindowsShellService::LaunchControlPanelDefaultPrograms() {
return ::LaunchControlPanelDefaultPrograms() ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsWindowsShellService::CheckAllProgIDsExist(bool* aResult) {
*aResult = false;
nsAutoString aumid;
if (!mozilla::widget::WinTaskbar::GetAppUserModelID(aumid)) {
return NS_OK;
}
*aResult =
CheckProgIDExists(FormatProgID(L"FirefoxURL", aumid.get()).get()) &&
CheckProgIDExists(FormatProgID(L"FirefoxHTML", aumid.get()).get());
return NS_OK;
}
NS_IMETHODIMP
nsWindowsShellService::CheckBrowserUserChoiceHashes(bool* aResult) {
*aResult = ::CheckBrowserUserChoiceHashes();
return NS_OK;
}
NS_IMETHODIMP
nsWindowsShellService::CanSetDefaultBrowserUserChoice(bool* aResult) {
*aResult = false;
// If the WDBA is not available, this could never succeed.
#ifdef MOZ_DEFAULT_BROWSER_AGENT
bool progIDsExist = false;
bool hashOk = false;
*aResult = NS_SUCCEEDED(CheckAllProgIDsExist(&progIDsExist)) &&
progIDsExist &&
NS_SUCCEEDED(CheckBrowserUserChoiceHashes(&hashOk)) && hashOk;
#endif
return NS_OK;
}
nsresult nsWindowsShellService::LaunchModernSettingsDialogDefaultApps() {
return ::LaunchModernSettingsDialogDefaultApps() ? NS_OK : NS_ERROR_FAILURE;
}