Bug 1829334 - [marionette] Move browser quit logic into shared module. r=webdriver-reviewers,whimboo

Differential Revision: https://phabricator.services.mozilla.com/D181774
This commit is contained in:
Alexandra Borovova 2023-07-07 10:50:06 +00:00
Родитель b7f4737fff
Коммит 86a079c36f
3 изменённых файлов: 132 добавлений и 64 удалений

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

@ -14,6 +14,7 @@ remote.jar:
# shared modules (all protocols)
content/shared/AppInfo.sys.mjs (shared/AppInfo.sys.mjs)
content/shared/Browser.sys.mjs (shared/Browser.sys.mjs)
content/shared/Capture.sys.mjs (shared/Capture.sys.mjs)
content/shared/DOM.sys.mjs (shared/DOM.sys.mjs)
content/shared/Format.sys.mjs (shared/Format.sys.mjs)

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

@ -35,6 +35,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
permissions: "chrome://remote/content/marionette/permissions.sys.mjs",
pprint: "chrome://remote/content/shared/Format.sys.mjs",
print: "chrome://remote/content/shared/PDF.sys.mjs",
quit: "chrome://remote/content/shared/Browser.sys.mjs",
reftest: "chrome://remote/content/marionette/reftest.sys.mjs",
registerCommandsActor:
"chrome://remote/content/marionette/actors/MarionetteCommandsParent.sys.mjs",
@ -49,7 +50,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
"chrome://remote/content/marionette/actors/MarionetteCommandsParent.sys.mjs",
waitForInitialNavigationCompleted:
"chrome://remote/content/shared/Navigate.sys.mjs",
waitForObserverTopic: "chrome://remote/content/marionette/sync.sys.mjs",
WebDriverSession: "chrome://remote/content/shared/webdriver/Session.sys.mjs",
WebElement: "chrome://remote/content/marionette/web-reference.sys.mjs",
windowManager: "chrome://remote/content/shared/WindowManager.sys.mjs",
@ -84,6 +84,8 @@ const TIMEOUT_NO_WINDOW_MANAGER = 5000;
// Observer topic to wait for until the browser window is ready.
const TOPIC_BROWSER_READY = "browser-delayed-startup-finished";
// Observer topic to perform clean up when application quit is requested.
const TOPIC_QUIT_APPLICATION_REQUESTED = "quit-application-requested";
/**
* The Marionette WebDriver services provides a standard conforming
@ -551,11 +553,18 @@ GeckoDriver.prototype.handleEvent = function ({ target, type }) {
}
};
GeckoDriver.prototype.observe = function (subject, topic, data) {
GeckoDriver.prototype.observe = async function (subject, topic, data) {
switch (topic) {
case TOPIC_BROWSER_READY:
this.registerWindow(subject);
break;
case TOPIC_QUIT_APPLICATION_REQUESTED:
// Run Marionette specific cleanup steps before allowing
// the application to shutdown
await this._server.setAcceptConnections(false);
this.deleteSession();
break;
}
};
@ -2893,7 +2902,6 @@ GeckoDriver.prototype.acceptConnections = async function (cmd) {
*/
GeckoDriver.prototype.quit = async function (cmd) {
const { flags = [], safeMode = false } = cmd.parameters;
const quits = ["eConsiderQuit", "eAttemptQuit", "eForceQuit"];
lazy.assert.array(flags, `Expected "flags" to be an array`);
lazy.assert.boolean(safeMode, `Expected "safeMode" to be a boolean`);
@ -2904,70 +2912,27 @@ GeckoDriver.prototype.quit = async function (cmd) {
);
}
if (flags.includes("eSilently")) {
if (!this.currentSession.capabilities.get("moz:windowless")) {
throw new lazy.error.UnsupportedOperationError(
`Silent restarts only allowed with "moz:windowless" capability set`
);
}
if (!flags.includes("eRestart")) {
throw new lazy.error.InvalidArgumentError(
`"silently" only works with restart flag`
);
// Register handler to run Marionette specific shutdown code.
Services.obs.addObserver(this, TOPIC_QUIT_APPLICATION_REQUESTED);
let quitApplicationResponse;
try {
quitApplicationResponse = await lazy.quit(
flags,
safeMode,
this.currentSession.capabilities.get("moz:windowless")
);
} catch (e) {
if (e instanceof TypeError) {
throw new lazy.error.InvalidArgumentError(e.message);
}
throw new lazy.error.UnsupportedOperationError(e.message);
} finally {
Services.obs.removeObserver(this, TOPIC_QUIT_APPLICATION_REQUESTED);
}
let quitSeen;
let mode = 0;
if (flags.length) {
for (let k of flags) {
lazy.assert.in(k, Ci.nsIAppStartup);
if (quits.includes(k)) {
if (quitSeen) {
throw new lazy.error.InvalidArgumentError(
`${k} cannot be combined with ${quitSeen}`
);
}
quitSeen = k;
}
mode |= Ci.nsIAppStartup[k];
}
}
if (!quitSeen) {
mode |= Ci.nsIAppStartup.eAttemptQuit;
}
await this._server.setAcceptConnections(false);
this.deleteSession();
// Notify all windows that an application quit has been requested.
const cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(
Ci.nsISupportsPRBool
);
Services.obs.notifyObservers(cancelQuit, "quit-application-requested");
// If the shutdown of the application is prevented force quit it instead.
if (cancelQuit.data) {
mode |= Ci.nsIAppStartup.eForceQuit;
}
// delay response until the application is about to quit
let quitApplication = lazy.waitForObserverTopic("quit-application");
if (safeMode) {
Services.startup.restartInSafeMode(mode);
} else {
Services.startup.quit(mode);
}
return {
cause: (await quitApplication).data,
forced: cancelQuit.data,
in_app: true,
};
return quitApplicationResponse;
};
GeckoDriver.prototype.installAddon = function (cmd) {

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

@ -0,0 +1,102 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
pprint: "chrome://remote/content/shared/Format.sys.mjs",
waitForObserverTopic: "chrome://remote/content/marionette/sync.sys.mjs",
});
/**
* Quits the application with the provided flags.
*
* Optional {@link nsIAppStartup} flags may be provided as
* an array of masks, and these will be combined by ORing
* them with a bitmask. The available masks are defined in
* https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIAppStartup.
*
* Crucially, only one of the *Quit flags can be specified. The |eRestart|
* flag may be bit-wise combined with one of the *Quit flags to cause
* the application to restart after it quits.
*
* @param {Array.<string>=} flags
* Constant name of masks to pass to |Services.startup.quit|.
* If empty or undefined, |nsIAppStartup.eAttemptQuit| is used.
* @param {boolean=} safeMode
* Optional flag to indicate that the application has to
* be restarted in safe mode.
* @param {boolean=} isWindowless
* Optional flag to indicate that the browser was started in windowless mode.
*
* @returns {Object<string,boolean>}
* Dictionary containing information that explains the shutdown reason.
* The value for `cause` contains the shutdown kind like "shutdown" or
* "restart", while `forced` will indicate if it was a normal or forced
* shutdown of the application. "in_app" is always set to indicate that
* it is a shutdown triggered from within the application.
*/
export async function quit(flags = [], safeMode = false, isWindowless = false) {
if (flags.includes("eSilently")) {
if (!isWindowless) {
throw new Error(
`Silent restarts only allowed with "moz:windowless" capability set`
);
}
if (!flags.includes("eRestart")) {
throw new TypeError(`"silently" only works with restart flag`);
}
}
const quits = ["eConsiderQuit", "eAttemptQuit", "eForceQuit"];
let quitSeen;
let mode = 0;
if (flags.length) {
for (let k of flags) {
if (!Ci.nsIAppStartup.hasOwnProperty(k)) {
throw new TypeError(lazy.pprint`Expected ${k} in ${Ci.nsIAppStartup}`);
}
if (quits.includes(k)) {
if (quitSeen) {
throw new TypeError(`${k} cannot be combined with ${quitSeen}`);
}
quitSeen = k;
}
mode |= Ci.nsIAppStartup[k];
}
}
if (!quitSeen) {
mode |= Ci.nsIAppStartup.eAttemptQuit;
}
// Notify all windows that an application quit has been requested.
const cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(
Ci.nsISupportsPRBool
);
Services.obs.notifyObservers(cancelQuit, "quit-application-requested");
// If the shutdown of the application is prevented force quit it instead.
if (cancelQuit.data) {
mode |= Ci.nsIAppStartup.eForceQuit;
}
// Delay response until the application is about to quit.
const quitApplication = lazy.waitForObserverTopic("quit-application");
if (safeMode) {
Services.startup.restartInSafeMode(mode);
} else {
Services.startup.quit(mode);
}
return {
cause: (await quitApplication).data,
forced: cancelQuit.data,
in_app: true,
};
}