Backed out 6 changesets (bug 1726465) for causing several browser-chrome failures. CLOSED TREE

Backed out changeset 74016350f302 (bug 1726465)
Backed out changeset e7dd44d08121 (bug 1726465)
Backed out changeset 25865657dd3c (bug 1726465)
Backed out changeset f874f1a56157 (bug 1726465)
Backed out changeset 2fea5a6446f3 (bug 1726465)
Backed out changeset 0f96ac926aac (bug 1726465)
This commit is contained in:
Cosmin Sabou 2022-05-09 17:01:17 +03:00
parent 55a0b4f06d
commit 56702f5f93
15 changed files with 259 additions and 371 deletions

View File

@ -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) {

View File

@ -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,

View File

@ -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`);

View File

@ -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() {

View File

@ -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;
}

View File

@ -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) {

View File

@ -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",
}
);
}

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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):

View File

@ -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)

View File

@ -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])

View File

@ -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

View File

@ -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