зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 8 changesets (bug 1726465) for causing failures on browser_preferences_usage.js. CLOSED TREE
Backed out changeset 4b57310622a0 (bug 1726465) Backed out changeset 0c6c4a15621a (bug 1726465) Backed out changeset 081a0f91e0a7 (bug 1726465) Backed out changeset 60bceb18ff57 (bug 1726465) Backed out changeset f241655ea876 (bug 1726465) Backed out changeset 7bc0a3aeb254 (bug 1726465) Backed out changeset b2f7c710daef (bug 1726465) Backed out changeset e9b90fd1b4ec (bug 1726465)
This commit is contained in:
Родитель
5e5b3f791c
Коммит
70783e1c31
|
@ -8403,16 +8403,6 @@ const gRemoteControl = {
|
|||
},
|
||||
|
||||
updateVisualCue() {
|
||||
// Disable updating the remote control cue for performance tests,
|
||||
// because these could fail due to an early initialization of Marionette.
|
||||
const disableRemoteControlCue = Services.prefs.getBoolPref(
|
||||
"browser.chrome.disableRemoteControlCueForTests",
|
||||
false
|
||||
);
|
||||
if (disableRemoteControlCue && Cu.isInAutomation) {
|
||||
return;
|
||||
}
|
||||
|
||||
const mainWindow = document.documentElement;
|
||||
const remoteControlComponent = this.getRemoteControlComponent();
|
||||
if (remoteControlComponent) {
|
||||
|
|
|
@ -15,9 +15,6 @@ prefs =
|
|||
extensions.formautofill.addresses.available='on'
|
||||
extensions.formautofill.creditCards.available='on'
|
||||
browser.urlbar.disableExtendForTests=true
|
||||
# For perfomance tests do not enable the remote control cue, which gets set
|
||||
# when Marionette is enabled, but users normally don't see.
|
||||
browser.chrome.disableRemoteControlCueForTests=true
|
||||
# The Screenshots extension is disabled by default in Mochitests. We re-enable
|
||||
# it here, since it's a more realistic configuration.
|
||||
extensions.screenshots.disabled=false
|
||||
|
|
|
@ -112,25 +112,6 @@ XPCOMUtils.defineLazyServiceGetters(this, {
|
|||
PushService: ["@mozilla.org/push/Service;1", "nsIPushService"],
|
||||
});
|
||||
|
||||
if (AppConstants.ENABLE_WEBDRIVER) {
|
||||
XPCOMUtils.defineLazyServiceGetter(
|
||||
this,
|
||||
"Marionette",
|
||||
"@mozilla.org/remote/marionette;1",
|
||||
"nsIMarionette"
|
||||
);
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(
|
||||
this,
|
||||
"RemoteAgent",
|
||||
"@mozilla.org/remote/agent;1",
|
||||
"nsIRemoteAgent"
|
||||
);
|
||||
} else {
|
||||
this.Marionette = { running: false };
|
||||
this.RemoteAgent = { running: false };
|
||||
}
|
||||
|
||||
const PREF_PDFJS_ISDEFAULT_CACHE_STATE = "pdfjs.enabledCache.state";
|
||||
const PREF_DFPI_ENABLED_BY_DEFAULT =
|
||||
"privacy.restrict3rdpartystorage.rollout.enabledByDefault";
|
||||
|
@ -2699,8 +2680,10 @@ BrowserGlue.prototype = {
|
|||
// `UpdateService.disabledForTesting`, but without creating the
|
||||
// service, which can perform a good deal of I/O in order to log its
|
||||
// state. Since this is in the startup path, we avoid all of that.
|
||||
// We also don't test for Marionette and Remote Agent since they are
|
||||
// not yet initialized.
|
||||
let disabledForTesting =
|
||||
(Cu.isInAutomation || Marionette.running || RemoteAgent.running) &&
|
||||
Cu.isInAutomation &&
|
||||
Services.prefs.getBoolPref("app.update.disabledForTesting", false);
|
||||
if (!disabledForTesting) {
|
||||
BackgroundUpdate.maybeScheduleBackgroundUpdateTask();
|
||||
|
@ -2749,7 +2732,10 @@ BrowserGlue.prototype = {
|
|||
},
|
||||
},
|
||||
|
||||
// WebDriver components (Marionette) need to be
|
||||
// initialized as very last step.
|
||||
{
|
||||
condition: AppConstants.ENABLE_WEBDRIVER,
|
||||
task: () => {
|
||||
// Use idleDispatch a second time to run this after the per-window
|
||||
// idle tasks.
|
||||
|
@ -2758,10 +2744,12 @@ BrowserGlue.prototype = {
|
|||
null,
|
||||
"browser-startup-idle-tasks-finished"
|
||||
);
|
||||
|
||||
Services.obs.notifyObservers(null, "marionette-startup-requested");
|
||||
});
|
||||
},
|
||||
},
|
||||
// Do NOT add anything after idle tasks finished.
|
||||
// Do NOT add anything after WebDriver initialization.
|
||||
];
|
||||
|
||||
for (let task of idleTasks) {
|
||||
|
|
|
@ -32,8 +32,6 @@ async function runFirefox(args) {
|
|||
environment: {
|
||||
ASAN_OPTIONS:
|
||||
"detect_leaks=0:quarantine_size=50331648:malloc_context_size=5",
|
||||
// Don't enable Marionette.
|
||||
MOZ_MARIONETTE: null,
|
||||
},
|
||||
});
|
||||
let stdout;
|
||||
|
|
|
@ -851,6 +851,16 @@ function startup() {
|
|||
"browser-idle-startup-tasks-finished"
|
||||
)
|
||||
);
|
||||
|
||||
InitLater(() => {
|
||||
// This lets Marionette start listening (when enabled).
|
||||
// Both GeckoView and this remote protocol do most of their
|
||||
// initialization in "profile-after-change", and there is no order enforced
|
||||
// between them. Therefore we defer asking the Marionette component to
|
||||
// startup until after all "profile-after-change" handlers (including this
|
||||
// one) have completed.
|
||||
Services.obs.notifyObservers(null, "marionette-startup-requested");
|
||||
});
|
||||
});
|
||||
|
||||
// Move focus to the content window at the end of startup,
|
||||
|
|
|
@ -98,7 +98,7 @@ class CDP {
|
|||
// to find any available target. Also when closing the application while
|
||||
// it's still starting up can cause shutdown hangs. As such CDP will be
|
||||
// started when the initial application window has finished initializing.
|
||||
logger.debug(`Waiting for initial application window`);
|
||||
logger.debug(`Waiting for initial application window to be loaded`);
|
||||
await this.agent.browserStartupFinished;
|
||||
|
||||
Cu.printStderr(`DevTools listening on ${this.address}\n`);
|
||||
|
|
|
@ -12,7 +12,6 @@ const { XPCOMUtils } = ChromeUtils.import(
|
|||
);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
Deferred: "chrome://remote/content/shared/Sync.jsm",
|
||||
EnvironmentPrefs: "chrome://remote/content/marionette/prefs.js",
|
||||
Log: "chrome://remote/content/shared/Log.jsm",
|
||||
MarionettePrefs: "chrome://remote/content/marionette/prefs.js",
|
||||
|
@ -35,6 +34,9 @@ XPCOMUtils.defineLazyServiceGetter(
|
|||
"nsIEnvironment"
|
||||
);
|
||||
|
||||
const XMLURI_PARSE_ERROR =
|
||||
"http://www.mozilla.org/newlayout/xml/parsererror.xml";
|
||||
|
||||
const NOTIFY_LISTENING = "marionette-listening";
|
||||
|
||||
// Complements -marionette flag for starting the Marionette server.
|
||||
|
@ -66,8 +68,6 @@ const isRemote =
|
|||
Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT;
|
||||
|
||||
class MarionetteParentProcess {
|
||||
#browserStartupFinished;
|
||||
|
||||
constructor() {
|
||||
this.server = null;
|
||||
this._activePortPath;
|
||||
|
@ -75,22 +75,18 @@ class MarionetteParentProcess {
|
|||
this.classID = Components.ID("{786a1369-dca5-4adc-8486-33d23c88010a}");
|
||||
this.helpInfo = " --marionette Enable remote control server.\n";
|
||||
|
||||
// holds reference to ChromeWindow
|
||||
// used to run GFX sanity tests on Windows
|
||||
this.gfxWindow = null;
|
||||
|
||||
// indicates that all pending window checks have been completed
|
||||
// and that we are ready to start the Marionette server
|
||||
this.finalUIStartup = false;
|
||||
|
||||
// Initially set the enabled state based on the environment variable.
|
||||
this.enabled = env.exists(ENV_ENABLED);
|
||||
|
||||
Services.ppmm.addMessageListener("Marionette:IsRunning", this);
|
||||
|
||||
this.#browserStartupFinished = Deferred();
|
||||
}
|
||||
|
||||
/**
|
||||
* A promise that resolves when the initial application window has been opened.
|
||||
*
|
||||
* @returns {Promise}
|
||||
* Promise that resolves when the initial application window is open.
|
||||
*/
|
||||
get browserStartupFinished() {
|
||||
return this.#browserStartupFinished.promise;
|
||||
}
|
||||
|
||||
get enabled() {
|
||||
|
@ -153,14 +149,8 @@ class MarionetteParentProcess {
|
|||
this.enabled = subject.handleFlag("marionette", false);
|
||||
|
||||
if (this.enabled) {
|
||||
// Marionette needs to be initialized before any window is shown.
|
||||
Services.obs.addObserver(this, "final-ui-startup");
|
||||
|
||||
// We want to suppress the modal dialog that's shown
|
||||
// when starting up in safe-mode to enable testing.
|
||||
if (Services.appinfo.inSafeMode) {
|
||||
Services.obs.addObserver(this, "domwindowopened");
|
||||
}
|
||||
Services.obs.addObserver(this, "toplevel-window-ready");
|
||||
Services.obs.addObserver(this, "marionette-startup-requested");
|
||||
|
||||
RecommendedPreferences.applyPreferences(RECOMMENDED_PREFS);
|
||||
|
||||
|
@ -169,6 +159,25 @@ class MarionetteParentProcess {
|
|||
for (let [pref, value] of EnvironmentPrefs.from(ENV_PRESERVE_PREFS)) {
|
||||
Preferences.set(pref, value);
|
||||
}
|
||||
|
||||
// We want to suppress the modal dialog that's shown
|
||||
// when starting up in safe-mode to enable testing.
|
||||
if (Services.appinfo.inSafeMode) {
|
||||
Services.obs.addObserver(this, "domwindowopened");
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "domwindowclosed":
|
||||
if (this.gfxWindow === null || subject === this.gfxWindow) {
|
||||
Services.obs.removeObserver(this, topic);
|
||||
Services.obs.removeObserver(this, "toplevel-window-ready");
|
||||
|
||||
Services.obs.addObserver(this, "quit-application");
|
||||
|
||||
this.finalUIStartup = true;
|
||||
await this.init();
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -177,29 +186,57 @@ class MarionetteParentProcess {
|
|||
this.suppressSafeModeDialog(subject);
|
||||
break;
|
||||
|
||||
case "final-ui-startup":
|
||||
Services.obs.removeObserver(this, topic);
|
||||
case "toplevel-window-ready":
|
||||
subject.addEventListener(
|
||||
"load",
|
||||
async ev => {
|
||||
if (ev.target.documentElement.namespaceURI == XMLURI_PARSE_ERROR) {
|
||||
Services.obs.removeObserver(this, topic);
|
||||
|
||||
Services.obs.addObserver(this, "browser-idle-startup-tasks-finished");
|
||||
Services.obs.addObserver(this, "mail-idle-startup-tasks-finished");
|
||||
Services.obs.addObserver(this, "quit-application");
|
||||
|
||||
await this.init();
|
||||
let parserError = ev.target.querySelector("parsererror");
|
||||
logger.fatal(parserError.textContent);
|
||||
await this.uninit();
|
||||
Services.startup.quit(Ci.nsIAppStartup.eForceQuit);
|
||||
}
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
break;
|
||||
|
||||
// Used to wait until the initial application window has been opened.
|
||||
case "browser-idle-startup-tasks-finished":
|
||||
case "mail-idle-startup-tasks-finished":
|
||||
Services.obs.removeObserver(
|
||||
this,
|
||||
"browser-idle-startup-tasks-finished"
|
||||
);
|
||||
Services.obs.removeObserver(this, "mail-idle-startup-tasks-finished");
|
||||
this.#browserStartupFinished.resolve();
|
||||
case "marionette-startup-requested":
|
||||
Services.obs.removeObserver(this, topic);
|
||||
|
||||
// When Firefox starts on Windows, an additional GFX sanity test
|
||||
// window may appear off-screen. Marionette should wait for it
|
||||
// to close.
|
||||
for (let win of Services.wm.getEnumerator(null)) {
|
||||
if (
|
||||
win.document.documentURI ==
|
||||
"chrome://gfxsanity/content/sanityparent.html"
|
||||
) {
|
||||
this.gfxWindow = win;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.gfxWindow) {
|
||||
logger.trace(
|
||||
"GFX sanity window detected, waiting until it has been closed..."
|
||||
);
|
||||
Services.obs.addObserver(this, "domwindowclosed");
|
||||
} else {
|
||||
Services.obs.removeObserver(this, "toplevel-window-ready");
|
||||
|
||||
Services.obs.addObserver(this, "quit-application");
|
||||
|
||||
this.finalUIStartup = true;
|
||||
await this.init();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "quit-application":
|
||||
Services.obs.removeObserver(this, topic);
|
||||
Services.obs.removeObserver(this, "quit-application");
|
||||
await this.uninit();
|
||||
break;
|
||||
}
|
||||
|
@ -222,40 +259,56 @@ class MarionetteParentProcess {
|
|||
);
|
||||
}
|
||||
|
||||
async init() {
|
||||
if (!this.enabled || this.running) {
|
||||
async init(quit = true) {
|
||||
if (this.running || !this.enabled || !this.finalUIStartup) {
|
||||
logger.debug(
|
||||
`Init aborted (enabled=${this.enabled}, running=${this.running})`
|
||||
`Init aborted (running=${this.running}, ` +
|
||||
`enabled=${this.enabled}, finalUIStartup=${this.finalUIStartup})`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.server = new TCPListener(MarionettePrefs.port);
|
||||
this.server.start();
|
||||
} catch (e) {
|
||||
logger.fatal("Marionette server failed to start", e);
|
||||
await this.uninit();
|
||||
Services.startup.quit(Ci.nsIAppStartup.eForceQuit);
|
||||
return;
|
||||
}
|
||||
|
||||
env.set(ENV_ENABLED, "1");
|
||||
Services.obs.notifyObservers(this, NOTIFY_LISTENING, true);
|
||||
logger.debug("Marionette is listening");
|
||||
|
||||
// Write Marionette port to MarionetteActivePort file within the profile.
|
||||
this._activePortPath = PathUtils.join(
|
||||
PathUtils.profileDir,
|
||||
"MarionetteActivePort"
|
||||
logger.trace(
|
||||
`Waiting until startup recorder finished recording startup scripts...`
|
||||
);
|
||||
Services.tm.idleDispatchToMainThread(async () => {
|
||||
let startupRecorder = Promise.resolve();
|
||||
if ("@mozilla.org/test/startuprecorder;1" in Cc) {
|
||||
startupRecorder = Cc["@mozilla.org/test/startuprecorder;1"].getService()
|
||||
.wrappedJSObject.done;
|
||||
}
|
||||
await startupRecorder;
|
||||
logger.trace(`All scripts recorded.`);
|
||||
|
||||
const data = `${this.server.port}`;
|
||||
try {
|
||||
await IOUtils.write(this._activePortPath, textEncoder.encode(data));
|
||||
} catch (e) {
|
||||
logger.warn(`Failed to create ${this._activePortPath} (${e.message})`);
|
||||
}
|
||||
try {
|
||||
this.server = new TCPListener(MarionettePrefs.port);
|
||||
this.server.start();
|
||||
} catch (e) {
|
||||
logger.fatal("Remote protocol server failed to start", e);
|
||||
await this.uninit();
|
||||
if (quit) {
|
||||
Services.startup.quit(Ci.nsIAppStartup.eForceQuit);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
env.set(ENV_ENABLED, "1");
|
||||
Services.obs.notifyObservers(this, NOTIFY_LISTENING, true);
|
||||
logger.debug("Marionette is listening");
|
||||
|
||||
// Write Marionette port to MarionetteActivePort file within the profile.
|
||||
this._activePortPath = PathUtils.join(
|
||||
PathUtils.profileDir,
|
||||
"MarionetteActivePort"
|
||||
);
|
||||
|
||||
const data = `${this.server.port}`;
|
||||
try {
|
||||
await IOUtils.write(this._activePortPath, textEncoder.encode(data));
|
||||
} catch (e) {
|
||||
logger.warn(`Failed to create ${this._activePortPath} (${e.message})`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async uninit() {
|
||||
|
|
|
@ -44,11 +44,11 @@ const isRemote =
|
|||
class RemoteAgentParentProcess {
|
||||
#allowHosts;
|
||||
#allowOrigins;
|
||||
#browserStartupFinished;
|
||||
#classID;
|
||||
#enabled;
|
||||
#port;
|
||||
#server;
|
||||
#browserStartupFinished;
|
||||
|
||||
#cdp;
|
||||
#webDriverBiDi;
|
||||
|
@ -56,11 +56,11 @@ class RemoteAgentParentProcess {
|
|||
constructor() {
|
||||
this.#allowHosts = null;
|
||||
this.#allowOrigins = null;
|
||||
this.#browserStartupFinished = Deferred();
|
||||
this.#classID = Components.ID("{8f685a9d-8181-46d6-a71d-869289099c6d}");
|
||||
this.#enabled = false;
|
||||
this.#port = DEFAULT_PORT;
|
||||
this.#server = null;
|
||||
this.#browserStartupFinished = Deferred();
|
||||
|
||||
// Supported protocols
|
||||
this.#cdp = null;
|
||||
|
@ -103,10 +103,11 @@ class RemoteAgentParentProcess {
|
|||
}
|
||||
|
||||
/**
|
||||
* A promise that resolves when the initial application window has been opened.
|
||||
* A promise resolved once initialization of the browser has completed and
|
||||
* all the windows restored.
|
||||
*
|
||||
* @returns {Promise}
|
||||
* Promise that resolves when the initial application window is open.
|
||||
* Promise that resolves when the application startup has finished.
|
||||
*/
|
||||
get browserStartupFinished() {
|
||||
return this.#browserStartupFinished.promise;
|
||||
|
@ -372,7 +373,8 @@ class RemoteAgentParentProcess {
|
|||
// Listen for application shutdown to also shutdown the Remote Agent
|
||||
// and a possible running instance of httpd.js.
|
||||
case "quit-application":
|
||||
Services.obs.removeObserver(this, topic);
|
||||
Services.obs.removeObserver(this, "quit-application");
|
||||
|
||||
this.#stop();
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -37,7 +37,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||
IdlePromise: "chrome://remote/content/marionette/sync.js",
|
||||
l10n: "chrome://remote/content/marionette/l10n.js",
|
||||
Log: "chrome://remote/content/shared/Log.jsm",
|
||||
Marionette: "chrome://remote/content/components/Marionette.jsm",
|
||||
MarionettePrefs: "chrome://remote/content/marionette/prefs.js",
|
||||
modal: "chrome://remote/content/marionette/modal.js",
|
||||
navigate: "chrome://remote/content/marionette/navigate.js",
|
||||
|
@ -423,67 +422,56 @@ GeckoDriver.prototype.newSession = async function(cmd) {
|
|||
this._currentSession.capabilities.delete("webSocketUrl");
|
||||
}
|
||||
|
||||
const win = await windowManager.waitForInitialApplicationWindow();
|
||||
|
||||
if (MarionettePrefs.clickToStart) {
|
||||
Services.prompt.alert(
|
||||
win,
|
||||
"",
|
||||
"Click to start execution of marionette tests"
|
||||
);
|
||||
}
|
||||
|
||||
this.addBrowser(win);
|
||||
this.mainFrame = win;
|
||||
|
||||
registerCommandsActor();
|
||||
enableEventsActor();
|
||||
|
||||
// Don't wait for the initial window when Marionette is in windowless mode
|
||||
if (!this.currentSession.capabilities.get("moz:windowless")) {
|
||||
// Creating a WebDriver session too early can cause issues with
|
||||
// clients in not being able to find any available window handle.
|
||||
// Also when closing the application while it's still starting up can
|
||||
// cause shutdown hangs. As such Marionette will return a new session
|
||||
// once the initial application window has finished initializing.
|
||||
logger.debug(`Waiting for initial application window`);
|
||||
await Marionette.browserStartupFinished;
|
||||
// Setup observer for modal dialogs
|
||||
this.dialogObserver = new modal.DialogObserver(() => this.curBrowser);
|
||||
this.dialogObserver.add(this.handleModalDialog.bind(this));
|
||||
|
||||
const appWin = await windowManager.waitForInitialApplicationWindowLoaded();
|
||||
for (let win of windowManager.windows) {
|
||||
const tabBrowser = TabManager.getTabBrowser(win);
|
||||
|
||||
if (MarionettePrefs.clickToStart) {
|
||||
Services.prompt.alert(
|
||||
appWin,
|
||||
"",
|
||||
"Click to start execution of marionette tests"
|
||||
);
|
||||
}
|
||||
|
||||
this.addBrowser(appWin);
|
||||
this.mainFrame = appWin;
|
||||
|
||||
// Setup observer for modal dialogs
|
||||
this.dialogObserver = new modal.DialogObserver(() => this.curBrowser);
|
||||
this.dialogObserver.add(this.handleModalDialog.bind(this));
|
||||
|
||||
for (let win of windowManager.windows) {
|
||||
const tabBrowser = TabManager.getTabBrowser(win);
|
||||
|
||||
if (tabBrowser) {
|
||||
for (const tab of tabBrowser.tabs) {
|
||||
const contentBrowser = TabManager.getBrowserForTab(tab);
|
||||
this.registerBrowser(contentBrowser);
|
||||
}
|
||||
if (tabBrowser) {
|
||||
for (const tab of tabBrowser.tabs) {
|
||||
const contentBrowser = TabManager.getBrowserForTab(tab);
|
||||
this.registerBrowser(contentBrowser);
|
||||
}
|
||||
|
||||
this.registerListenersForWindow(win);
|
||||
}
|
||||
|
||||
if (this.mainFrame) {
|
||||
this.currentSession.chromeBrowsingContext = this.mainFrame.browsingContext;
|
||||
this.mainFrame.focus();
|
||||
}
|
||||
|
||||
if (this.curBrowser.tab) {
|
||||
const browsingContext = this.curBrowser.contentBrowser.browsingContext;
|
||||
this.currentSession.contentBrowsingContext = browsingContext;
|
||||
|
||||
await waitForInitialNavigationCompleted(browsingContext.webProgress);
|
||||
|
||||
this.curBrowser.contentBrowser.focus();
|
||||
}
|
||||
|
||||
// Check if there is already an open dialog for the selected browser window.
|
||||
this.dialog = modal.findModalDialogs(this.curBrowser);
|
||||
this.registerListenersForWindow(win);
|
||||
}
|
||||
|
||||
if (this.mainFrame) {
|
||||
this.currentSession.chromeBrowsingContext = this.mainFrame.browsingContext;
|
||||
this.mainFrame.focus();
|
||||
}
|
||||
|
||||
if (this.curBrowser.tab) {
|
||||
const browsingContext = this.curBrowser.contentBrowser.browsingContext;
|
||||
this.currentSession.contentBrowsingContext = browsingContext;
|
||||
|
||||
await waitForInitialNavigationCompleted(browsingContext.webProgress);
|
||||
|
||||
this.curBrowser.contentBrowser.focus();
|
||||
}
|
||||
|
||||
// Check if there is already an open dialog for the selected browser window.
|
||||
this.dialog = modal.findModalDialogs(this.curBrowser);
|
||||
|
||||
Services.obs.addObserver(this, "browser-delayed-startup-finished");
|
||||
} catch (e) {
|
||||
throw new error.SessionNotCreatedError(e);
|
||||
|
@ -2065,13 +2053,10 @@ GeckoDriver.prototype.close = async function() {
|
|||
assert.open(this.getBrowsingContext({ context: Context.Content, top: true }));
|
||||
await this._handleUserPrompts();
|
||||
|
||||
// If there is only one window left, do not close unless windowless mode is
|
||||
// enabled. Instead return a faked empty array of window handles.
|
||||
// This will instruct geckodriver to terminate the application.
|
||||
if (
|
||||
TabManager.getTabCount() === 1 &&
|
||||
!this.currentSession.capabilities.get("moz:windowless")
|
||||
) {
|
||||
// If there is only one window left, do not close it. Instead return
|
||||
// a faked empty array of window handles. This will instruct geckodriver
|
||||
// to terminate the application.
|
||||
if (TabManager.getTabCount() === 1) {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
@ -2105,10 +2090,10 @@ GeckoDriver.prototype.closeChromeWindow = async function() {
|
|||
nwins++;
|
||||
}
|
||||
|
||||
// If there is only one window left, do not close unless windowless mode is
|
||||
// enabled. Instead return a faked empty array of window handles.
|
||||
// This will instruct geckodriver to terminate the application.
|
||||
if (nwins == 1 && !this.currentSession.capabilities.get("moz:windowless")) {
|
||||
// If there is only one window left, do not close it. Instead return
|
||||
// a faked empty array of window handles. This will instruct geckodriver
|
||||
// to terminate the application.
|
||||
if (nwins == 1) {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
@ -2630,19 +2615,6 @@ GeckoDriver.prototype.quit = async function(cmd) {
|
|||
);
|
||||
}
|
||||
|
||||
if (flags.includes("eSilently")) {
|
||||
if (!this.currentSession.capabilities.get("moz:windowless")) {
|
||||
throw new error.UnsupportedOperationError(
|
||||
`Silent restarts only allowed with "moz:windowless" capability set`
|
||||
);
|
||||
}
|
||||
if (!flags.includes("eRestart")) {
|
||||
throw new error.InvalidArgumentError(
|
||||
`"silently" only works with restart flag`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let quitSeen;
|
||||
let mode = 0;
|
||||
if (flags.length > 0) {
|
||||
|
|
|
@ -245,32 +245,58 @@ class WindowManager {
|
|||
* @return {Promise<WindowProxy>}
|
||||
* A promise that resolved to the application window.
|
||||
*/
|
||||
waitForInitialApplicationWindowLoaded() {
|
||||
waitForInitialApplicationWindow() {
|
||||
return new TimedPromise(
|
||||
async resolve => {
|
||||
const windowReadyTopic = AppInfo.isThunderbird
|
||||
? "mail-delayed-startup-finished"
|
||||
: "browser-delayed-startup-finished";
|
||||
resolve => {
|
||||
const waitForWindow = () => {
|
||||
let windowTypes;
|
||||
if (AppInfo.isThunderbird) {
|
||||
windowTypes = ["mail:3pane"];
|
||||
} else {
|
||||
// We assume that an app either has GeckoView windows, or
|
||||
// Firefox/Fennec windows, but not both.
|
||||
windowTypes = ["navigator:browser", "navigator:geckoview"];
|
||||
}
|
||||
|
||||
// This call includes a fallback to "mail3:pane" as well.
|
||||
const win = Services.wm.getMostRecentBrowserWindow();
|
||||
let win;
|
||||
for (const windowType of windowTypes) {
|
||||
win = Services.wm.getMostRecentWindow(windowType);
|
||||
if (win) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const windowLoaded = waitForObserverTopic(windowReadyTopic, {
|
||||
checkFn: subject => (win !== null ? subject == win : true),
|
||||
});
|
||||
if (!win) {
|
||||
// if the window isn't even created, just poll wait for it
|
||||
let checkTimer = Cc["@mozilla.org/timer;1"].createInstance(
|
||||
Ci.nsITimer
|
||||
);
|
||||
checkTimer.initWithCallback(
|
||||
waitForWindow,
|
||||
100,
|
||||
Ci.nsITimer.TYPE_ONE_SHOT
|
||||
);
|
||||
} else if (win.document.readyState != "complete") {
|
||||
// otherwise, wait for it to be fully loaded before proceeding
|
||||
let listener = ev => {
|
||||
// ensure that we proceed, on the top level document load event
|
||||
// (not an iframe one...)
|
||||
if (ev.target != win.document) {
|
||||
return;
|
||||
}
|
||||
win.removeEventListener("load", listener);
|
||||
waitForWindow();
|
||||
};
|
||||
win.addEventListener("load", listener, true);
|
||||
} else {
|
||||
resolve(win);
|
||||
}
|
||||
};
|
||||
|
||||
// The current window has already been finished loading.
|
||||
if (win && win.document.readyState == "complete") {
|
||||
resolve(win);
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait for the next browser/mail window to open and finished loading.
|
||||
const { subject } = await windowLoaded;
|
||||
resolve(subject);
|
||||
waitForWindow();
|
||||
},
|
||||
{
|
||||
errorMessage: "No applicable application window found",
|
||||
errorMessage: "No applicable application windows found",
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -457,7 +457,6 @@ class Capabilities extends Map {
|
|||
],
|
||||
["moz:useNonSpecCompliantPointerOrigin", false],
|
||||
["moz:webdriverClick", true],
|
||||
["moz:windowless", false],
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -587,11 +586,6 @@ class Capabilities extends Map {
|
|||
assert.boolean(v, pprint`Expected ${k} to be boolean, got ${v}`);
|
||||
break;
|
||||
|
||||
// Don't set the value because it's only used to return the address
|
||||
// of the Remote Agent's debugger (HTTP server).
|
||||
case "moz:debuggerAddress":
|
||||
continue;
|
||||
|
||||
case "moz:useNonSpecCompliantPointerOrigin":
|
||||
assert.boolean(v, pprint`Expected ${k} to be boolean, got ${v}`);
|
||||
break;
|
||||
|
@ -600,16 +594,10 @@ class Capabilities extends Map {
|
|||
assert.boolean(v, pprint`Expected ${k} to be boolean, got ${v}`);
|
||||
break;
|
||||
|
||||
case "moz:windowless":
|
||||
assert.boolean(v, pprint`Expected ${k} to be boolean, got ${v}`);
|
||||
|
||||
// Only supported on MacOS
|
||||
if (v && !AppInfo.isMac) {
|
||||
throw new error.InvalidArgumentError(
|
||||
"moz:windowless only supported on MacOS"
|
||||
);
|
||||
}
|
||||
break;
|
||||
// Don't set the value because it's only used to return the address
|
||||
// of the Remote Agent's debugger (HTTP server).
|
||||
case "moz:debuggerAddress":
|
||||
continue;
|
||||
}
|
||||
|
||||
matched.set(k, v);
|
||||
|
|
|
@ -99,7 +99,7 @@ class WebDriverBiDi {
|
|||
// Also when closing the application while it's still starting up can
|
||||
// cause shutdown hangs. As such WebDriver BiDi will return a new session
|
||||
// once the initial application window has finished initializing.
|
||||
logger.debug(`Waiting for initial application window`);
|
||||
logger.debug(`Waiting for initial application window to be loaded`);
|
||||
await this.agent.browserStartupFinished;
|
||||
|
||||
this.agent.server.registerPathHandler(session.path, session);
|
||||
|
|
|
@ -1050,9 +1050,7 @@ class Marionette(object):
|
|||
return quit_details
|
||||
|
||||
@do_process_check
|
||||
def restart(
|
||||
self, callback=None, clean=False, in_app=False, safe_mode=False, silent=False
|
||||
):
|
||||
def restart(self, callback=None, clean=False, in_app=False, safe_mode=False):
|
||||
"""
|
||||
This will terminate the currently running instance, and spawn a new instance
|
||||
with the same profile and then reuse the session id when creating a session again.
|
||||
|
@ -1071,10 +1069,6 @@ class Marionette(object):
|
|||
:param safe_mode: Optional flag to indicate that the application has to
|
||||
be restarted in safe mode.
|
||||
|
||||
:param silent: Optional flag to indicate that the application should
|
||||
not open any window after a restart. Note that this flag is only
|
||||
supported on MacOS.
|
||||
|
||||
:returns: A dictionary containing details of the application restart.
|
||||
The `cause` property reflects the reason, and `forced` indicates
|
||||
that something prevented the shutdown and the application had
|
||||
|
@ -1089,8 +1083,8 @@ class Marionette(object):
|
|||
context = self._send_message("Marionette:GetContext", key="value")
|
||||
restart_details = {"cause": "restart", "forced": False}
|
||||
|
||||
# Safe mode and the silent flag require in_app restarts.
|
||||
if safe_mode or silent:
|
||||
# Safe mode is only available with in_app restarts.
|
||||
if safe_mode:
|
||||
in_app = True
|
||||
|
||||
if in_app:
|
||||
|
@ -1112,19 +1106,9 @@ class Marionette(object):
|
|||
if callback is not None:
|
||||
callback()
|
||||
else:
|
||||
flags = ["eRestart"]
|
||||
if silent:
|
||||
flags.append("eSilently")
|
||||
|
||||
try:
|
||||
restart_details = self._request_in_app_shutdown(
|
||||
flags=flags, safe_mode=safe_mode
|
||||
)
|
||||
except Exception as e:
|
||||
self._send_message(
|
||||
"Marionette:AcceptConnections", {"value": True}
|
||||
)
|
||||
raise e
|
||||
restart_details = self._request_in_app_shutdown(
|
||||
flags=["eRestart"], safe_mode=safe_mode
|
||||
)
|
||||
|
||||
except IOError:
|
||||
# A possible IOError should be ignored at this point, given that
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
from __future__ import absolute_import, print_function
|
||||
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
from marionette_driver.errors import SessionNotCreatedException
|
||||
from marionette_harness import MarionetteTestCase
|
||||
|
@ -105,9 +103,6 @@ class TestCapabilities(MarionetteTestCase):
|
|||
self.assertIn("moz:webdriverClick", self.caps)
|
||||
self.assertTrue(self.caps["moz:webdriverClick"])
|
||||
|
||||
self.assertIn("moz:windowless", self.caps)
|
||||
self.assertFalse(self.caps["moz:windowless"])
|
||||
|
||||
def test_disable_webdriver_click(self):
|
||||
self.marionette.delete_session()
|
||||
self.marionette.start_session({"moz:webdriverClick": False})
|
||||
|
@ -120,26 +115,13 @@ class TestCapabilities(MarionetteTestCase):
|
|||
caps = self.marionette.session_capabilities
|
||||
self.assertTrue(caps["moz:useNonSpecCompliantPointerOrigin"])
|
||||
|
||||
def test_valid_uuid4_when_creating_a_session(self):
|
||||
def test_we_get_valid_uuid4_when_creating_a_session(self):
|
||||
self.assertNotIn(
|
||||
"{",
|
||||
self.marionette.session_id,
|
||||
"Session ID has {{}} in it: {}".format(self.marionette.session_id),
|
||||
)
|
||||
|
||||
def test_windowless_false(self):
|
||||
self.marionette.delete_session()
|
||||
self.marionette.start_session({"moz:windowless": False})
|
||||
caps = self.marionette.session_capabilities
|
||||
self.assertFalse(caps["moz:windowless"])
|
||||
|
||||
@unittest.skipUnless(sys.platform.startswith("darwin"), "Only supported on MacOS")
|
||||
def test_windowless_true(self):
|
||||
self.marionette.delete_session()
|
||||
self.marionette.start_session({"moz:windowless": True})
|
||||
caps = self.marionette.session_capabilities
|
||||
self.assertTrue(caps["moz:windowless"])
|
||||
|
||||
|
||||
class TestCapabilityMatching(MarionetteTestCase):
|
||||
def setUp(self):
|
||||
|
|
|
@ -67,6 +67,10 @@ class TestServerQuitApplication(MarionetteTestCase):
|
|||
with self.assertRaises(errors.InvalidArgumentException):
|
||||
self.quit(("eAttemptQuit", "eForceQuit"))
|
||||
|
||||
def test_safe_mode_requires_restart(self):
|
||||
with self.assertRaises(errors.InvalidArgumentException):
|
||||
self.quit(("eAttemptQuit",), True)
|
||||
|
||||
def test_attempt_quit(self):
|
||||
cause = self.quit(("eAttemptQuit",))
|
||||
self.assertEqual("shutdown", cause)
|
||||
|
@ -75,15 +79,6 @@ class TestServerQuitApplication(MarionetteTestCase):
|
|||
cause = self.quit(("eForceQuit",))
|
||||
self.assertEqual("shutdown", cause)
|
||||
|
||||
def test_safe_mode_requires_restart(self):
|
||||
with self.assertRaises(errors.InvalidArgumentException):
|
||||
self.quit(("eAttemptQuit",), True)
|
||||
|
||||
@unittest.skipUnless(sys.platform.startswith("darwin"), "Only supported on MacOS")
|
||||
def test_silent_quit_missing_windowless_capability(self):
|
||||
with self.assertRaises(errors.UnsupportedOperationException):
|
||||
self.quit(("eSilently",))
|
||||
|
||||
|
||||
class TestQuitRestart(MarionetteTestCase):
|
||||
def setUp(self):
|
||||
|
@ -302,36 +297,6 @@ class TestQuitRestart(MarionetteTestCase):
|
|||
self.marionette.restart(in_app=True)
|
||||
self.assertEqual(self.marionette.session.get("moz:fooBar"), True)
|
||||
|
||||
@unittest.skipUnless(sys.platform.startswith("darwin"), "Only supported on MacOS")
|
||||
def test_in_app_silent_restart_fails_without_windowless_flag_on_mac_os(self):
|
||||
self.marionette.delete_session()
|
||||
self.marionette.start_session()
|
||||
|
||||
with self.assertRaises(errors.UnsupportedOperationException):
|
||||
self.marionette.restart(silent=True)
|
||||
|
||||
@unittest.skipUnless(sys.platform.startswith("darwin"), "Only supported on MacOS")
|
||||
def test_in_app_silent_restart_windowless_flag_on_mac_os(self):
|
||||
self.marionette.delete_session()
|
||||
self.marionette.start_session(capabilities={"moz:windowless": True})
|
||||
|
||||
self.marionette.restart(silent=True)
|
||||
self.assertTrue(self.marionette.session_capabilities["moz:windowless"])
|
||||
|
||||
self.marionette.restart(in_app=True)
|
||||
self.assertTrue(self.marionette.session_capabilities["moz:windowless"])
|
||||
|
||||
self.marionette.delete_session()
|
||||
|
||||
@unittest.skipIf(
|
||||
sys.platform.startswith("darwin"), "Not supported on other platforms than MacOS"
|
||||
)
|
||||
def test_in_app_silent_restart_windowless_flag_unsupported_platforms(self):
|
||||
self.marionette.delete_session()
|
||||
|
||||
with self.assertRaises(errors.SessionNotCreatedException):
|
||||
self.marionette.start_session(capabilities={"moz:windowless": True})
|
||||
|
||||
def test_in_app_quit(self):
|
||||
details = self.marionette.quit(in_app=True)
|
||||
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
# 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/.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from marionette_driver import errors, Wait
|
||||
from marionette_harness import MarionetteTestCase
|
||||
|
||||
|
||||
class TestWindowless(MarionetteTestCase):
|
||||
def setUp(self):
|
||||
super(TestWindowless, self).setUp()
|
||||
|
||||
self.marionette.delete_session()
|
||||
self.marionette.start_session({"moz:windowless": True})
|
||||
|
||||
def tearDown(self):
|
||||
# Reset the browser and active WebDriver session
|
||||
self.marionette.restart(in_app=True)
|
||||
self.marionette.delete_session()
|
||||
|
||||
super(TestWindowless, self).tearDown()
|
||||
|
||||
def test_last_chrome_window_can_be_closed(self):
|
||||
with self.marionette.using_context("chrome"):
|
||||
handles = self.marionette.chrome_window_handles
|
||||
self.assertGreater(len(handles), 0)
|
||||
self.marionette.switch_to_window(handles[0])
|
||||
self.marionette.close_chrome_window()
|
||||
self.assertEqual(len(self.marionette.chrome_window_handles), 0)
|
||||
|
||||
def test_last_content_window_can_be_closed(self):
|
||||
handles = self.marionette.window_handles
|
||||
self.assertGreater(len(handles), 0)
|
||||
self.marionette.switch_to_window(handles[0])
|
||||
self.marionette.close()
|
||||
self.assertEqual(len(self.marionette.window_handles), 0)
|
||||
|
||||
def test_no_window_handles_after_silent_restart(self):
|
||||
# Check that windows are present, but not after a silent restart
|
||||
handles = self.marionette.window_handles
|
||||
self.assertGreater(len(handles), 0)
|
||||
|
||||
self.marionette.restart(silent=True)
|
||||
with self.assertRaises(errors.TimeoutException):
|
||||
wait = Wait(
|
||||
self.marionette,
|
||||
ignored_exceptions=errors.NoSuchWindowException,
|
||||
timeout=5,
|
||||
)
|
||||
wait.until(lambda _: self.marionette.window_handles)
|
||||
|
||||
# After a normal restart a browser window will be opened again
|
||||
self.marionette.restart(in_app=True)
|
||||
handles = self.marionette.window_handles
|
||||
self.assertGreater(len(handles), 0)
|
||||
self.marionette.switch_to_window(handles[0])
|
|
@ -71,8 +71,7 @@ expected = fail
|
|||
[test_execute_isolate.py]
|
||||
[test_click_scrolling.py]
|
||||
[test_profile_management.py]
|
||||
skip-if =
|
||||
manage_instance == false || (debug && ((os == 'mac') || (os == 'linux'))) # Bug 1450355
|
||||
skip-if = manage_instance == false || (debug && ((os == 'mac') || (os == 'linux'))) # Bug 1450355
|
||||
[test_quit_restart.py]
|
||||
skip-if = manage_instance == false
|
||||
[test_context.py]
|
||||
|
@ -99,8 +98,7 @@ skip-if = manage_instance == false
|
|||
|
||||
[test_select.py]
|
||||
[test_crash.py]
|
||||
skip-if =
|
||||
asan || manage_instance == false
|
||||
skip-if = asan || manage_instance == false
|
||||
[test_localization.py]
|
||||
|
||||
[test_reftest.py]
|
||||
|
@ -111,6 +109,3 @@ skip-if =
|
|||
[test_sendkeys_menupopup_chrome.py]
|
||||
|
||||
[test_get_shadow_root.py]
|
||||
|
||||
[test_windowless.py]
|
||||
skip-if = os != 'mac' # only supported on MacOS
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
from copy import deepcopy
|
||||
|
||||
|
||||
def test_marionette_fallback_webdriver_session(configuration, geckodriver):
|
||||
config = deepcopy(configuration)
|
||||
config["capabilities"]["webSocketUrl"] = True
|
||||
|
||||
prefs = config["capabilities"]["moz:firefoxOptions"].get("prefs", {})
|
||||
prefs.update({"remote.active-protocols": 2})
|
||||
config["capabilities"]["moz:firefoxOptions"]["prefs"] = prefs
|
||||
|
||||
driver = geckodriver(config=config)
|
||||
driver.new_session()
|
||||
|
||||
assert driver.session.capabilities.get("webSocketUrl") is None
|
||||
|
||||
# Sanity check that Marionette works as expected and by default returns
|
||||
# at least one window handle
|
||||
assert len(driver.session.handles) >= 1
|
Загрузка…
Ссылка в новой задаче