Bug 1403428 - switch to window and close it atomically. r=maja_zf
This seems to do the trick, but I'm pretty lost in this code so there could be
other things I'm not seeing.
MozReview-Commit-ID: CDtAB119FQn
--- a/testing/marionette/client/marionette_driver/marionette.py
+++ b/testing/marionette/client/marionette_driver/marionette.py
@@ -1479,16 +1479,20 @@ class Marionette(object):
def close_chrome_window(self):
"""Close the currently selected chrome window, ending the session
if it's the last window open.
:returns: Unordered list of remaining unique chrome window handles as strings
"""
return self._send_message("closeChromeWindow")
+ def close_named_window(self, name):
+ """An atomic version of switch_to_window followed by close"""
+ return self._send_message("closeNamedWindow", {"name": name})
+
def set_context(self, context):
"""Sets the context that Marionette commands are running in.
:param context: Context, may be one of the class properties
`CONTEXT_CHROME` or `CONTEXT_CONTENT`.
Usage example::
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -2752,16 +2752,40 @@ GeckoDriver.prototype.close = async func
this.mm.removeDelayedFrameScript(FRAME_SCRIPT);
}
await this.curBrowser.closeTab();
return this.windowHandles.map(String);
};
/**
+ * Close the tab/window with name or id name.
+ *
+ * This is essentially an atomic way for Python to ask the browser to close a
+ * window with a given name (if the window could potentially be closed between
+ * Python calling switchToWindow and then close itself).
+ *
+ * @return {Array.<string>}
+ * Unique window handles of remaining windows.
+ *
+ * @throws {NoSuchWindowError}
+ * Top-level browsing context has been discarded.
+ * @throws {UnexpectedAlertOpenError}
+ * A modal dialog is open, blocking this operation.
+ */
+GeckoDriver.prototype.closeNamedWindow = async function(cmd, resp) {
+ assert.contentBrowser(this.curBrowser);
+ assert.noUserPrompt(this.dialog);
+
+ let fakeCmd = {parameters: {name: cmd.parameters.name}};
+ await this.switchToWindow(fakeCmd, resp);
+ return await this.close(fakeCmd, resp);
+};
+
+/**
* Close the currently selected chrome window.
*
* If it is the last window currently open, the chrome window will not be
* closed to prevent a shutdown of the application. Instead the returned
* list of chrome window handles is empty.
*
* @return {Array.<string>}
* Unique chrome window handles of remaining chrome windows.
@@ -3552,16 +3576,17 @@ GeckoDriver.prototype.commands = {
"reftest:teardown": GeckoDriver.prototype.teardownReftest,
// WebDriver service
"WebDriver:AcceptDialog": GeckoDriver.prototype.acceptDialog,
"WebDriver:AddCookie": GeckoDriver.prototype.addCookie,
"WebDriver:Back": GeckoDriver.prototype.goBack,
"WebDriver:CloseChromeWindow": GeckoDriver.prototype.closeChromeWindow,
"WebDriver:CloseWindow": GeckoDriver.prototype.close,
+ "WebDriver:CloseNamedWindow": GeckoDriver.prototype.closeNamedWindow,
"WebDriver:DeleteAllCookies": GeckoDriver.prototype.deleteAllCookies,
"WebDriver:DeleteCookie": GeckoDriver.prototype.deleteCookie,
"WebDriver:DeleteSession": GeckoDriver.prototype.deleteSession,
"WebDriver:DismissAlert": GeckoDriver.prototype.dismissDialog,
"WebDriver:ElementClear": GeckoDriver.prototype.clearElement,
"WebDriver:ElementClick": GeckoDriver.prototype.clickElement,
"WebDriver:ElementSendKeys": GeckoDriver.prototype.sendKeysToElement,
"WebDriver:ExecuteAsyncScript": GeckoDriver.prototype.executeAsyncScript,
@@ -3616,16 +3641,17 @@ GeckoDriver.prototype.commands = {
// deprecated WebDriver commands, remove in Firefox 60
"acceptDialog": GeckoDriver.prototype.acceptDialog,
"actionChain": GeckoDriver.prototype.actionChain,
"addCookie": GeckoDriver.prototype.addCookie,
"clearElement": GeckoDriver.prototype.clearElement,
"clickElement": GeckoDriver.prototype.clickElement,
"closeChromeWindow": GeckoDriver.prototype.closeChromeWindow,
"close": GeckoDriver.prototype.close,
+ "closeNamedWindow": GeckoDriver.prototype.closeNamedWindow,
"deleteAllCookies": GeckoDriver.prototype.deleteAllCookies,
"deleteCookie": GeckoDriver.prototype.deleteCookie,
"deleteSession": GeckoDriver.prototype.deleteSession,
"dismissDialog": GeckoDriver.prototype.dismissDialog,
"executeAsyncScript": GeckoDriver.prototype.executeAsyncScript,
"executeScript": GeckoDriver.prototype.executeScript,
"findElement": GeckoDriver.prototype.findElement,
"findElements": GeckoDriver.prototype.findElements,
--- a/testing/web-platform/tests/tools/wptrunner/wptrunner/executors/executormarionette.py
+++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/executors/executormarionette.py
@@ -155,22 +155,21 @@ class MarionetteProtocol(Protocol):
# This isn't supposed to happen, but marionette ids are not yet stable
# We assume that the first handle returned corresponds to the runner,
# but it hopefully doesn't matter too much if that assumption is
# wrong since we reload the runner in that tab anyway.
runner_handle = handles.pop(0)
for handle in handles:
try:
- self.marionette.switch_to_window(handle)
+ self.marionette.close_named_window(handle)
except errors.NoSuchWindowException:
# We might have raced with the previous test to close this
# window, skip it.
pass
- self.marionette.close()
self.marionette.switch_to_window(runner_handle)
if runner_handle != self.runner_handle:
self.load_runner(protocol)
def wait(self):
socket_timeout = self.marionette.client.sock.gettimeout()
if socket_timeout: