Bug 1850631 - Call nsIDefaultAgent directly to set default. r=nrishel,nalexander

Differential Revision: https://phabricator.services.mozilla.com/D187853
This commit is contained in:
Masatoshi Kimura
2023-09-13 03:18:46 +00:00
parent a5326397df
commit d8eea9e97a
4 changed files with 83 additions and 156 deletions

View File

@@ -9,9 +9,7 @@ const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
Subprocess: "resource://gre/modules/Subprocess.sys.mjs",
WindowsRegistry: "resource://gre/modules/WindowsRegistry.sys.mjs",
setTimeout: "resource://gre/modules/Timer.sys.mjs",
});
XPCOMUtils.defineLazyServiceGetter(
@@ -128,20 +126,6 @@ let ShellServiceInternal = {
return false;
},
/*
* Invoke the Windows Default Browser agent with the given options.
*
* Separated for easy stubbing in tests.
*/
_callExternalDefaultBrowserAgent(options = {}) {
const wdba = Services.dirsvc.get("XREExeF", Ci.nsIFile);
wdba.leafName = "default-browser-agent.exe";
return lazy.Subprocess.call({
...options,
command: options.command || wdba.path,
});
},
/*
* Check if UserChoice is impossible.
*
@@ -267,13 +251,6 @@ let ShellServiceInternal = {
lazy.log.info("Setting Firefox as default using UserChoice");
// 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.
let telemetryResult = "ErrOther";
try {
@@ -289,22 +266,26 @@ let ShellServiceInternal = {
const aumi = lazy.XreDirProvider.getInstallHash();
telemetryResult = "ErrLaunchExe";
const exeArgs = ["set-default-browser-user-choice", aumi];
const extraFileExtensions = [];
if (
lazy.NimbusFeatures.shellService.getVariable("setDefaultPDFHandler")
) {
if (this._shouldSetDefaultPDFHandler()) {
lazy.log.info("Setting Firefox as default PDF handler");
exeArgs.push(".pdf", "FirefoxPDF");
extraFileExtensions.push(".pdf", "FirefoxPDF");
} else {
lazy.log.info("Not setting Firefox as default PDF handler");
}
}
const exeProcess = await this._callExternalDefaultBrowserAgent({
arguments: exeArgs,
});
telemetryResult = "ErrOther";
await this._handleWDBAResult(exeProcess);
try {
this.defaultAgent.setDefaultBrowserUserChoice(
aumi,
extraFileExtensions
);
} catch (err) {
telemetryResult = "ErrOther";
this._handleWDBAResult(err.result || Cr.NS_ERROR_FAILURE);
}
telemetryResult = "Success";
} catch (ex) {
if (ex instanceof WDBAError) {
@@ -327,22 +308,19 @@ let ShellServiceInternal = {
throw new Error("Windows-only");
}
// See comment in setAsDefaultUserChoice for an explanation of why we shell
// out to WDBA.
let telemetryResult = "ErrOther";
try {
const aumi = lazy.XreDirProvider.getInstallHash();
const exeProcess = await this._callExternalDefaultBrowserAgent({
arguments: [
"set-default-extension-handlers-user-choice",
aumi,
try {
this.defaultAgent.setDefaultExtensionHandlersUserChoice(aumi, [
".pdf",
"FirefoxPDF",
],
});
telemetryResult = "ErrOther";
await this._handleWDBAResult(exeProcess);
]);
} catch (err) {
telemetryResult = "ErrOther";
this._handleWDBAResult(err.result || Cr.NS_ERROR_FAILURE);
}
telemetryResult = "Success";
} catch (ex) {
if (ex instanceof WDBAError) {
@@ -505,23 +483,10 @@ let ShellServiceInternal = {
}
},
async _handleWDBAResult(exeProcess, exeWaitTimeoutMs = 2000) {
const STILL_ACTIVE = 0x103;
const exeWaitPromise = exeProcess.wait();
const timeoutPromise = new Promise(function (resolve) {
lazy.setTimeout(
() => resolve({ exitCode: STILL_ACTIVE }),
exeWaitTimeoutMs
);
});
const { exitCode } = await Promise.race([exeWaitPromise, timeoutPromise]);
_handleWDBAResult(exitCode) {
if (exitCode != Cr.NS_OK) {
const telemetryResult =
new Map([
[STILL_ACTIVE, "ErrExeTimeout"],
[Cr.NS_ERROR_WDBA_NO_PROGID, "ErrExeProgID"],
[Cr.NS_ERROR_WDBA_HASH_CHECK, "ErrExeHash"],
[Cr.NS_ERROR_WDBA_REJECTED, "ErrExeRejected"],
@@ -534,6 +499,7 @@ let ShellServiceInternal = {
};
XPCOMUtils.defineLazyServiceGetters(ShellServiceInternal, {
defaultAgent: ["@mozilla.org/default-agent;1", "nsIDefaultAgent"],
shellService: ["@mozilla.org/browser/shell-service;1", "nsIShellService"],
macDockSupport: ["@mozilla.org/widget/macdocksupport;1", "nsIMacDockSupport"],
});

View File

@@ -8,19 +8,13 @@ ChromeUtils.defineESModuleGetters(this, {
sinon: "resource://testing-common/Sinon.sys.mjs",
});
const _handleWDBAResultStub = sinon
.stub(ShellService, "_handleWDBAResult")
.callsFake(async () => {
throw new Error("from _handleWDBAResultStub");
});
const setDefaultBrowserUserChoiceStub = () => {
throw Components.Exception("", Cr.NS_ERROR_WDBA_NO_PROGID);
};
const _callExternalDefaultBrowserAgentStub = sinon
.stub(ShellService, "_callExternalDefaultBrowserAgent")
.callsFake(async () => ({
async wait() {
return { exitCode: 0 };
},
}));
const defaultAgentStub = sinon
.stub(ShellService, "defaultAgent")
.value({ setDefaultBrowserUserChoice: setDefaultBrowserUserChoiceStub });
const _userChoiceImpossibleTelemetryResultStub = sinon
.stub(ShellService, "_userChoiceImpossibleTelemetryResult")
@@ -35,8 +29,7 @@ const shellStub = sinon
.value({ setDefaultBrowser: setDefaultStub });
registerCleanupFunction(() => {
_handleWDBAResultStub.restore();
_callExternalDefaultBrowserAgentStub.restore();
defaultAgentStub.restore();
_userChoiceImpossibleTelemetryResultStub.restore();
userChoiceStub.restore();
shellStub.restore();
@@ -136,10 +129,13 @@ add_task(async function ensure_fallback() {
Assert.ok(userChoiceStub.called, "Set default with user choice called");
let thrown = false;
await userChoicePromise.catch(() => (thrown = true));
let message = "";
await userChoicePromise.catch(err => (message = err.message || ""));
Assert.ok(thrown, "Set default with user choice threw an error");
Assert.ok(
message.includes("ErrExeProgID"),
"Set default with user choice threw an expected error"
);
Assert.ok(setDefaultStub.called, "Fallbacked to plain set default");
await doCleanup();

View File

@@ -4,10 +4,22 @@
ChromeUtils.defineESModuleGetters(this, {
ExperimentAPI: "resource://nimbus/ExperimentAPI.sys.mjs",
ExperimentFakes: "resource://testing-common/NimbusTestUtils.sys.mjs",
MockRegistrar: "resource://testing-common/MockRegistrar.sys.mjs",
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
sinon: "resource://testing-common/Sinon.sys.mjs",
});
const mockedDefaultAgent = {
setDefaultBrowserUserChoice: sinon.stub(),
setDefaultExtensionHandlersUserChoice: sinon.stub(),
QueryInterface: ChromeUtils.generateQI(["nsIDefaultAgent"]),
};
const defaultAgentCID = MockRegistrar.register(
"@mozilla.org/default-agent;1",
mockedDefaultAgent
);
XPCOMUtils.defineLazyServiceGetter(
this,
"XreDirProvider",
@@ -15,14 +27,6 @@ XPCOMUtils.defineLazyServiceGetter(
"nsIXREDirProvider"
);
const _callExternalDefaultBrowserAgentStub = sinon
.stub(ShellService, "_callExternalDefaultBrowserAgent")
.callsFake(async () => ({
async wait() {
return { exitCode: 0 };
},
}));
const _userChoiceImpossibleTelemetryResultStub = sinon
.stub(ShellService, "_userChoiceImpossibleTelemetryResult")
.callsFake(() => null);
@@ -37,7 +41,7 @@ const shellStub = sinon.stub(ShellService, "shellService").value({
});
registerCleanupFunction(() => {
_callExternalDefaultBrowserAgentStub.restore();
MockRegistrar.unregister(defaultAgentCID);
_userChoiceImpossibleTelemetryResultStub.restore();
shellStub.restore();
@@ -74,21 +78,15 @@ add_task(async function remoteEnableWithPDF() {
true
);
_callExternalDefaultBrowserAgentStub.resetHistory();
mockedDefaultAgent.setDefaultBrowserUserChoice.resetHistory();
ShellService.setDefaultBrowser();
const aumi = XreDirProvider.getInstallHash();
Assert.ok(_callExternalDefaultBrowserAgentStub.called);
Assert.deepEqual(_callExternalDefaultBrowserAgentStub.firstCall.args, [
{
arguments: [
"set-default-browser-user-choice",
aumi,
".pdf",
"FirefoxPDF",
],
},
]);
Assert.ok(mockedDefaultAgent.setDefaultBrowserUserChoice.called);
Assert.deepEqual(
mockedDefaultAgent.setDefaultBrowserUserChoice.firstCall.args,
[aumi, [".pdf", "FirefoxPDF"]]
);
await doCleanup();
});
@@ -125,22 +123,13 @@ add_task(async function remoteEnableWithPDF_testOnlyReplaceBrowsers() {
for (let progId of ["", "MSEdgePDF"]) {
queryCurrentDefaultHandlerForStub.callsFake(() => progId);
_callExternalDefaultBrowserAgentStub.resetHistory();
mockedDefaultAgent.setDefaultBrowserUserChoice.resetHistory();
ShellService.setDefaultBrowser();
Assert.ok(_callExternalDefaultBrowserAgentStub.called);
Assert.ok(mockedDefaultAgent.setDefaultBrowserUserChoice.called);
Assert.deepEqual(
_callExternalDefaultBrowserAgentStub.firstCall.args,
[
{
arguments: [
"set-default-browser-user-choice",
aumi,
".pdf",
"FirefoxPDF",
],
},
],
mockedDefaultAgent.setDefaultBrowserUserChoice.firstCall.args,
[aumi, [".pdf", "FirefoxPDF"]],
`Will take default from missing association or known browser with ProgID '${progId}'`
);
}
@@ -148,13 +137,13 @@ add_task(async function remoteEnableWithPDF_testOnlyReplaceBrowsers() {
// But not from a non-browser.
queryCurrentDefaultHandlerForStub.callsFake(() => "Acrobat.Document.DC");
_callExternalDefaultBrowserAgentStub.resetHistory();
mockedDefaultAgent.setDefaultBrowserUserChoice.resetHistory();
ShellService.setDefaultBrowser();
Assert.ok(_callExternalDefaultBrowserAgentStub.called);
Assert.ok(mockedDefaultAgent.setDefaultBrowserUserChoice.called);
Assert.deepEqual(
_callExternalDefaultBrowserAgentStub.firstCall.args,
[{ arguments: ["set-default-browser-user-choice", aumi] }],
mockedDefaultAgent.setDefaultBrowserUserChoice.firstCall.args,
[aumi, []],
`Will not take default from non-browser`
);
@@ -180,14 +169,15 @@ add_task(async function remoteEnableWithoutPDF() {
false
);
_callExternalDefaultBrowserAgentStub.resetHistory();
mockedDefaultAgent.setDefaultBrowserUserChoice.resetHistory();
ShellService.setDefaultBrowser();
const aumi = XreDirProvider.getInstallHash();
Assert.ok(_callExternalDefaultBrowserAgentStub.called);
Assert.deepEqual(_callExternalDefaultBrowserAgentStub.firstCall.args, [
{ arguments: ["set-default-browser-user-choice", aumi] },
]);
Assert.ok(mockedDefaultAgent.setDefaultBrowserUserChoice.called);
Assert.deepEqual(
mockedDefaultAgent.setDefaultBrowserUserChoice.firstCall.args,
[aumi, []]
);
await doCleanup();
});
@@ -211,10 +201,10 @@ add_task(async function remoteDisable() {
true
);
_callExternalDefaultBrowserAgentStub.resetHistory();
mockedDefaultAgent.setDefaultBrowserUserChoice.resetHistory();
ShellService.setDefaultBrowser();
Assert.ok(_callExternalDefaultBrowserAgentStub.notCalled);
Assert.ok(mockedDefaultAgent.setDefaultBrowserUserChoice.notCalled);
Assert.ok(setDefaultStub.called);
await doCleanup();
@@ -224,12 +214,7 @@ add_task(async function test_setAsDefaultPDFHandler_knownBrowser() {
const sandbox = sinon.createSandbox();
const aumi = XreDirProvider.getInstallHash();
const expectedArguments = [
"set-default-extension-handlers-user-choice",
aumi,
".pdf",
"FirefoxPDF",
];
const expectedArguments = [aumi, [".pdf", "FirefoxPDF"]];
try {
const pdfHandlerResult = { registered: true, knownBrowser: true };
@@ -240,51 +225,51 @@ add_task(async function test_setAsDefaultPDFHandler_knownBrowser() {
info("Testing setAsDefaultPDFHandler(true) when knownBrowser = true");
ShellService.setAsDefaultPDFHandler(true);
Assert.ok(
_callExternalDefaultBrowserAgentStub.called,
mockedDefaultAgent.setDefaultExtensionHandlersUserChoice.called,
"Called default browser agent"
);
Assert.deepEqual(
_callExternalDefaultBrowserAgentStub.firstCall.args,
[{ arguments: expectedArguments }],
mockedDefaultAgent.setDefaultExtensionHandlersUserChoice.firstCall.args,
expectedArguments,
"Called default browser agent with expected arguments"
);
_callExternalDefaultBrowserAgentStub.resetHistory();
mockedDefaultAgent.setDefaultExtensionHandlersUserChoice.resetHistory();
info("Testing setAsDefaultPDFHandler(false) when knownBrowser = true");
ShellService.setAsDefaultPDFHandler(false);
Assert.ok(
_callExternalDefaultBrowserAgentStub.called,
mockedDefaultAgent.setDefaultExtensionHandlersUserChoice.called,
"Called default browser agent"
);
Assert.deepEqual(
_callExternalDefaultBrowserAgentStub.firstCall.args,
[{ arguments: expectedArguments }],
mockedDefaultAgent.setDefaultExtensionHandlersUserChoice.firstCall.args,
expectedArguments,
"Called default browser agent with expected arguments"
);
_callExternalDefaultBrowserAgentStub.resetHistory();
mockedDefaultAgent.setDefaultExtensionHandlersUserChoice.resetHistory();
pdfHandlerResult.knownBrowser = false;
info("Testing setAsDefaultPDFHandler(true) when knownBrowser = false");
ShellService.setAsDefaultPDFHandler(true);
Assert.ok(
_callExternalDefaultBrowserAgentStub.notCalled,
mockedDefaultAgent.setDefaultExtensionHandlersUserChoice.notCalled,
"Did not call default browser agent"
);
_callExternalDefaultBrowserAgentStub.resetHistory();
mockedDefaultAgent.setDefaultExtensionHandlersUserChoice.resetHistory();
info("Testing setAsDefaultPDFHandler(false) when knownBrowser = false");
ShellService.setAsDefaultPDFHandler(false);
Assert.ok(
_callExternalDefaultBrowserAgentStub.called,
mockedDefaultAgent.setDefaultExtensionHandlersUserChoice.called,
"Called default browser agent"
);
Assert.deepEqual(
_callExternalDefaultBrowserAgentStub.firstCall.args,
[{ arguments: expectedArguments }],
mockedDefaultAgent.setDefaultExtensionHandlersUserChoice.firstCall.args,
expectedArguments,
"Called default browser agent with expected arguments"
);
_callExternalDefaultBrowserAgentStub.resetHistory();
mockedDefaultAgent.setDefaultExtensionHandlersUserChoice.resetHistory();
} finally {
sandbox.restore();
}