Bug 1416284: Dismiss alerts when we hit an UnexpectedAlertOpen error. r?ato draft
authorDavid Burns <dburns@mozilla.com>
Fri, 10 Nov 2017 17:25:50 +0000
changeset 701784 2569648127c7ef49e892e652182ada360de66adb
parent 701610 b96f009478987d44a68a8d7cad40c6a3d6626235
child 701785 f1fdd6daf93d62dbc90facb81021d6980690f580
child 701895 99f3dcc155383f652ca932bb8e6020e1d81a5d34
push id90281
push userbmo:dburns@mozilla.com
push dateWed, 22 Nov 2017 08:55:00 +0000
reviewersato
bugs1416284
milestone59.0a1
Bug 1416284: Dismiss alerts when we hit an UnexpectedAlertOpen error. r?ato The Browser Testing and Tools group agreed that the webdriver endpoint, when asserting for a modal, that it clears the modal on the screen and raises an an Unexpected Alert Open error. See https://github.com/w3c/webdriver/pull/1145 MozReview-Commit-ID: 1OnT1AMM0tY
testing/marionette/driver.js
testing/marionette/harness/marionette_harness/tests/unit/test_modal_dialogs.py
testing/web-platform/tests/webdriver/tests/support/fixtures.py
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -1092,17 +1092,17 @@ GeckoDriver.prototype.execute_ = async f
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.get = async function(cmd) {
   assert.content(this.context);
   assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   let url = cmd.parameters.url;
 
   let get = this.listener.get({url, pageTimeout: this.timeouts.pageLoad});
 
   // If a reload of the frame script interrupts our page load, this will
   // never return. We need to re-issue this request to correctly poll for
   // readyState and send errors.
@@ -1135,17 +1135,17 @@ GeckoDriver.prototype.get = async functi
  *
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.getCurrentUrl = function() {
   assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   return this.currentURL.toString();
 };
 
 /**
  * Gets the current title of the window.
  *
  * @return {string}
@@ -1153,17 +1153,17 @@ GeckoDriver.prototype.getCurrentUrl = fu
  *
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.getTitle = function() {
   assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   return this.title;
 };
 
 /** Gets the current type of the window. */
 GeckoDriver.prototype.getWindowType = function(cmd, resp) {
   let win = assert.window(this.getCurrentWindow());
 
@@ -1179,17 +1179,17 @@ GeckoDriver.prototype.getWindowType = fu
  *
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.getPageSource = async function(cmd, resp) {
   const win = assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   switch (this.context) {
     case Context.Chrome:
       let s = new win.XMLSerializer();
       resp.body.value = s.serializeToString(win.document);
       break;
 
     case Context.Content:
@@ -1207,17 +1207,17 @@ GeckoDriver.prototype.getPageSource = as
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.goBack = async function() {
   assert.content(this.context);
   assert.contentBrowser(this.curBrowser);
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   // If there is no history, just return
   if (!this.curBrowser.contentBrowser.webNavigation.canGoBack) {
     return;
   }
 
   let lastURL = this.currentURL;
   let goBack = this.listener.goBack({pageTimeout: this.timeouts.pageLoad});
@@ -1250,17 +1250,17 @@ GeckoDriver.prototype.goBack = async fun
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.goForward = async function() {
   assert.content(this.context);
   assert.contentBrowser(this.curBrowser);
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   // If there is no history, just return
   if (!this.curBrowser.contentBrowser.webNavigation.canGoForward) {
     return;
   }
 
   let lastURL = this.currentURL;
   let goForward = this.listener.goForward(
@@ -1294,17 +1294,17 @@ GeckoDriver.prototype.goForward = async 
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.refresh = async function() {
   assert.content(this.context);
   assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   let refresh = this.listener.refresh(
       {pageTimeout: this.timeouts.pageLoad});
 
   // If a reload of the frame script interrupts our page load, this will
   // never return. We need to re-issue this request to correctly poll for
   // readyState and send errors.
   this.curBrowser.pendingCommands.push(() => {
@@ -1435,17 +1435,17 @@ GeckoDriver.prototype.getChromeWindowHan
  *
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.getWindowRect = function() {
   assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
   return this.curBrowser.rect;
 };
 
 /**
  * Set the window position and size of the browser on the operating
  * system window manager.
  *
  * The supplied |width| and |height| values refer to the window outerWidth
@@ -1472,17 +1472,17 @@ GeckoDriver.prototype.getWindowRect = fu
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.setWindowRect = async function(cmd) {
   assert.firefox();
   const win = assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   let {x, y, width, height} = cmd.parameters;
   let origRect = this.curBrowser.rect;
 
   // Synchronous resize to |width| and |height| dimensions.
   async function resizeWindow(width, height) {
     return new Promise(resolve => {
       win.addEventListener("resize", whenIdle(win, resolve), {once: true});
@@ -1702,17 +1702,17 @@ GeckoDriver.prototype.getActiveFrame = f
  *
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.switchToParentFrame = async function() {
   assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   await this.listener.switchToParentFrame();
 };
 
 /**
  * Switch to a given frame within the current window.
  *
  * @param {Object} element
@@ -1723,17 +1723,17 @@ GeckoDriver.prototype.switchToParentFram
  *
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.switchToFrame = async function(cmd) {
   assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   let {id, focus} = cmd.parameters;
 
   // TODO(ato): element can be either string (deprecated) or a web
   // element JSON Object.  Can be removed with Firefox 60.
   let byFrame;
   if (typeof cmd.parameters.element == "string") {
     byFrame = WebElement.fromUUID(cmd.parameters.element, Context.Chrome);
@@ -1962,17 +1962,17 @@ GeckoDriver.prototype.singleTap = async 
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.performActions = async function(cmd) {
   assert.content(this.context,
       "Command 'performActions' is not yet available in chrome context");
   assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   let actions = cmd.parameters.actions;
   await this.listener.performActions({"actions": actions});
 };
 
 /**
  * Release all the keys and pointer buttons that are currently depressed.
  *
@@ -1981,17 +1981,17 @@ GeckoDriver.prototype.performActions = a
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.releaseActions = async function() {
   assert.content(this.context);
   assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   await this.listener.releaseActions();
 };
 
 /**
  * An action chain.
  *
  * @param {Object} value
@@ -2005,17 +2005,17 @@ GeckoDriver.prototype.releaseActions = a
  *     Not applicable to application.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.actionChain = async function(cmd, resp) {
   const win = assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   let {chain, nextId} = cmd.parameters;
 
   switch (this.context) {
     case Context.Chrome:
       // be conservative until this has a use case and is established
       // to work as expected in Fennec
       assert.firefox();
@@ -2044,17 +2044,17 @@ GeckoDriver.prototype.actionChain = asyn
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.multiAction = async function(cmd) {
   assert.content(this.context);
   assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   let {value, max_length} = cmd.parameters; // eslint-disable-line camelcase
 
   this.addFrameCloseListener("multi action chain");
   await this.listener.multiAction(value, max_length);
 };
 
 /**
@@ -2067,17 +2067,17 @@ GeckoDriver.prototype.multiAction = asyn
  *
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.findElement = async function(cmd, resp) {
   const win = assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   let {using, value} = cmd.parameters;
   let startNode;
   if (typeof cmd.parameters.element != "undefined") {
     startNode = WebElement.fromUUID(cmd.parameters.element, this.context);
   }
 
   let opts = {
@@ -2163,17 +2163,17 @@ GeckoDriver.prototype.findElements = asy
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.getActiveElement = async function(cmd, resp) {
   assert.content(this.context);
   assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   resp.body.value = await this.listener.getActiveElement();
 };
 
 /**
  * Send click event to element.
  *
  * @param {string} id
@@ -2185,17 +2185,17 @@ GeckoDriver.prototype.getActiveElement =
  *     If element represented by reference <var>id</var> is unknown.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.clickElement = async function(cmd) {
   assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   let id = assert.string(cmd.parameters.id);
   let webEl = WebElement.fromUUID(id, this.context);
 
   switch (this.context) {
     case Context.Chrome:
       let el = this.curBrowser.seenEls.get(webEl);
       await interaction.clickElement(el, this.a11yChecks);
@@ -2249,17 +2249,17 @@ GeckoDriver.prototype.clickElement = asy
  *     If element represented by reference <var>id</var> is unknown.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.getElementAttribute = async function(cmd, resp) {
   assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   let id = assert.string(cmd.parameters.id);
   let name = assert.string(cmd.parameters.name);
   let webEl = WebElement.fromUUID(id, this.context);
 
   switch (this.context) {
     case Context.Chrome:
       let el = this.curBrowser.seenEls.get(webEl);
@@ -2289,17 +2289,17 @@ GeckoDriver.prototype.getElementAttribut
  *     If element represented by reference <var>id</var> is unknown.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.getElementProperty = async function(cmd, resp) {
   assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   let id = assert.string(cmd.parameters.id);
   let name = assert.string(cmd.parameters.name);
   let webEl = WebElement.fromUUID(id, this.context);
 
   switch (this.context) {
     case Context.Chrome:
       let el = this.curBrowser.seenEls.get(webEl);
@@ -2328,17 +2328,17 @@ GeckoDriver.prototype.getElementProperty
  *     If element represented by reference <var>id</var> is unknown.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.getElementText = async function(cmd, resp) {
   assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   let id = assert.string(cmd.parameters.id);
   let webEl = WebElement.fromUUID(id, this.context);
 
   switch (this.context) {
     case Context.Chrome:
       // for chrome, we look at text nodes, and any node with a "label" field
       let el = this.curBrowser.seenEls.get(webEl);
@@ -2368,17 +2368,17 @@ GeckoDriver.prototype.getElementText = a
  *     If element represented by reference <var>id</var> is unknown.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.getElementTagName = async function(cmd, resp) {
   assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   let id = assert.string(cmd.parameters.id);
   let webEl = WebElement.fromUUID(id, this.context);
 
   switch (this.context) {
     case Context.Chrome:
       let el = this.curBrowser.seenEls.get(webEl);
       resp.body.value = el.tagName.toLowerCase();
@@ -2405,17 +2405,17 @@ GeckoDriver.prototype.getElementTagName 
  *     If element represented by reference <var>id</var> is unknown.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.isElementDisplayed = async function(cmd, resp) {
   assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   let id = assert.string(cmd.parameters.id);
   let webEl = WebElement.fromUUID(id, this.context);
 
   switch (this.context) {
     case Context.Chrome:
       let el = this.curBrowser.seenEls.get(webEl);
       resp.body.value = await interaction.isElementDisplayed(
@@ -2446,17 +2446,17 @@ GeckoDriver.prototype.isElementDisplayed
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.getElementValueOfCssProperty = async function(
     cmd, resp) {
   const win = assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   let id = assert.string(cmd.parameters.id);
   let prop = assert.string(cmd.parameters.propertyName);
   let webEl = WebElement.fromUUID(id, this.context);
 
   switch (this.context) {
     case Context.Chrome:
       let el = this.curBrowser.seenEls.get(webEl);
@@ -2486,17 +2486,17 @@ GeckoDriver.prototype.getElementValueOfC
  *     If element represented by reference <var>id</var> is unknown.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.isElementEnabled = async function(cmd, resp) {
   assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   let id = assert.string(cmd.parameters.id);
   let webEl = WebElement.fromUUID(id, this.context);
 
   switch (this.context) {
     case Context.Chrome:
       // Selenium atom doesn't quite work here
       let el = this.curBrowser.seenEls.get(webEl);
@@ -2525,17 +2525,17 @@ GeckoDriver.prototype.isElementEnabled =
  *     If element represented by reference <var>id</var> is unknown.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.isElementSelected = async function(cmd, resp) {
   assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   let id = assert.string(cmd.parameters.id);
   let webEl = WebElement.fromUUID(id, this.context);
 
   switch (this.context) {
     case Context.Chrome:
       // Selenium atom doesn't quite work here
       let el = this.curBrowser.seenEls.get(webEl);
@@ -2556,17 +2556,17 @@ GeckoDriver.prototype.isElementSelected 
  *     If element represented by reference <var>id</var> is unknown.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.getElementRect = async function(cmd, resp) {
   const win = assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   let id = assert.string(cmd.parameters.id);
   let webEl = WebElement.fromUUID(id, this.context);
 
   switch (this.context) {
     case Context.Chrome:
       let el = this.curBrowser.seenEls.get(webEl);
       let rect = el.getBoundingClientRect();
@@ -2598,17 +2598,17 @@ GeckoDriver.prototype.getElementRect = a
  *     If element represented by reference <var>id</var> is unknown.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.sendKeysToElement = async function(cmd) {
   assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   let id = assert.string(cmd.parameters.id);
   let text = assert.string(cmd.parameters.text);
   let webEl = WebElement.fromUUID(id, this.context);
 
   switch (this.context) {
     case Context.Chrome:
       let el = this.curBrowser.seenEls.get(webEl);
@@ -2634,17 +2634,17 @@ GeckoDriver.prototype.sendKeysToElement 
  *     If element represented by reference <var>id</var> is unknown.
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.clearElement = async function(cmd) {
   assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   let id = assert.string(cmd.parameters.id);
   let webEl = WebElement.fromUUID(id, this.context);
 
   switch (this.context) {
     case Context.Chrome:
       // the selenium atom doesn't work here
       let el = this.curBrowser.seenEls.get(webEl);
@@ -2696,17 +2696,17 @@ GeckoDriver.prototype.switchToShadowRoot
  *     A modal dialog is open, blocking this operation.
  * @throws {InvalidCookieDomainError}
  *     If <var>cookie</var> is for a different domain than the active
  *     document's host.
  */
 GeckoDriver.prototype.addCookie = function(cmd) {
   assert.content(this.context);
   assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   let {protocol, hostname} = this.currentURL;
 
   const networkSchemes = ["ftp:", "http:", "https:"];
   if (!networkSchemes.includes(protocol)) {
     throw new InvalidCookieDomainError("Document is cookie-averse");
   }
 
@@ -2726,17 +2726,17 @@ GeckoDriver.prototype.addCookie = functi
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.getCookies = function(cmd, resp) {
   assert.content(this.context);
   assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   let {hostname, pathname} = this.currentURL;
   resp.body = [...cookie.iter(hostname, pathname)];
 };
 
 /**
  * Delete all cookies that are visible to a document.
  *
@@ -2745,17 +2745,17 @@ GeckoDriver.prototype.getCookies = funct
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.deleteAllCookies = function() {
   assert.content(this.context);
   assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   let {hostname, pathname} = this.currentURL;
   for (let toDelete of cookie.iter(hostname, pathname)) {
     cookie.remove(toDelete);
   }
 };
 
 /**
@@ -2766,17 +2766,17 @@ GeckoDriver.prototype.deleteAllCookies =
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.deleteCookie = function(cmd) {
   assert.content(this.context);
   assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   let {hostname, pathname} = this.currentURL;
   let name = assert.string(cmd.parameters.name);
   for (let c of cookie.iter(hostname, pathname)) {
     if (c.name === name) {
       cookie.remove(c);
     }
   }
@@ -2796,17 +2796,17 @@ GeckoDriver.prototype.deleteCookie = fun
  *
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.close = async function() {
   assert.contentBrowser(this.curBrowser);
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   let nwins = 0;
 
   for (let win of this.windows) {
     // For browser windows count the tabs. Otherwise take the window itself.
     let tabbrowser = browser.getTabBrowser(win);
     if (tabbrowser && tabbrowser.tabs) {
       nwins += tabbrowser.tabs.length;
@@ -3068,17 +3068,17 @@ GeckoDriver.prototype.setScreenOrientati
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.minimizeWindow = async function() {
   assert.firefox();
   const win = assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   if (WindowState.from(win.windowState) == WindowState.Fullscreen) {
     await exitFullscreen(win);
   }
 
   if (WindowState.from(win.windowState) != WindowState.Minimized) {
     await new Promise(resolve => {
       this.curBrowser.eventObserver.addEventListener("visibilitychange", resolve, {once: true});
@@ -3105,17 +3105,17 @@ GeckoDriver.prototype.minimizeWindow = a
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.maximizeWindow = async function() {
   assert.firefox();
   const win = assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   switch (WindowState.from(win.windowState)) {
     case WindowState.Fullscreen:
       await exitFullscreen(win);
       break;
 
     case WindowState.Minimized:
       await restoreWindow(win, this.curBrowser.eventObserver);
@@ -3191,17 +3191,17 @@ GeckoDriver.prototype.maximizeWindow = a
  * @throws {NoSuchWindowError}
  *     Top-level browsing context has been discarded.
  * @throws {UnexpectedAlertOpenError}
  *     A modal dialog is open, blocking this operation.
  */
 GeckoDriver.prototype.fullscreenWindow = async function() {
   assert.firefox();
   const win = assert.window(this.getCurrentWindow());
-  assert.noUserPrompt(this.dialog);
+  this._assertAndDismissModal();
 
   if (WindowState.from(win.windowState) == WindowState.Minimized) {
     await restoreWindow(win, this.curBrowser.eventObserver);
   }
 
   if (WindowState.from(win.windowState) != WindowState.Fullscreen) {
     await new Promise(resolve => {
       win.addEventListener("sizemodechange", resolve, {once: true});
@@ -3288,16 +3288,25 @@ GeckoDriver.prototype.sendKeysToDialog =
 };
 
 GeckoDriver.prototype._checkIfAlertIsPresent = function() {
   if (!this.dialog || !this.dialog.ui) {
     throw new NoAlertOpenError("No modal dialog is currently open");
   }
 };
 
+GeckoDriver.prototype._assertAndDismissModal = function() {
+  try {
+    assert.noUserPrompt(this.dialog);
+  } catch (e) {
+    this.dismissDialog();
+    throw e;
+  }
+};
+
 /**
  * Enables or disables accepting new socket connections.
  *
  * By calling this method with `false` the server will not accept any
  * further connections, but existing connections will not be forcible
  * closed. Use `true` to re-enable accepting connections.
  *
  * Please note that when closing the connection via the client you can
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_modal_dialogs.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_modal_dialogs.py
@@ -199,16 +199,24 @@ class TestTabModalAlerts(BaseAlertTestCa
         self.wait_for_condition(lambda mn: mn.get_url() == "about:blank")
 
     def test_unrelated_command_when_alert_present(self):
         self.marionette.find_element(By.ID, "tab-modal-alert").click()
         self.wait_for_alert()
         with self.assertRaises(errors.UnexpectedAlertOpen):
             self.marionette.find_element(By.ID, "click-result")
 
+    def test_modal_is_dismissed_after_unexpected_alert(self):
+        self.marionette.find_element(By.ID, "tab-modal-alert").click()
+        self.wait_for_alert()
+        with self.assertRaises(errors.UnexpectedAlertOpen):
+            self.marionette.find_element(By.ID, "click-result")
+
+        assert not self.alert_present()
+
 
 class TestModalAlerts(BaseAlertTestCase):
 
     def setUp(self):
         super(TestModalAlerts, self).setUp()
 
     def tearDown(self):
         # Ensure to close a possible remaining modal dialog
--- a/testing/web-platform/tests/webdriver/tests/support/fixtures.py
+++ b/testing/web-platform/tests/webdriver/tests/support/fixtures.py
@@ -3,16 +3,17 @@ import os
 import urlparse
 import re
 
 import webdriver
 import mozlog
 
 from tests.support.asserts import assert_error
 from tests.support.http_request import HTTPRequest
+from tests.support.wait import wait
 from tests.support import merge_dictionaries
 
 default_host = "http://127.0.0.1"
 default_port = "4444"
 
 logger = mozlog.get_default_logger()
 
 
@@ -249,15 +250,20 @@ def create_dialog(session):
             setTimeout(function() {{
                 window.{0} = window.{1}("{2}");
             }}, 0);
         """.format(result_var, dialog_type, text)
 
         session.send_session_command("POST",
                                      "execute/async",
                                      {"script": spawn, "args": []})
+        wait(session,
+             lambda s: s.send_session_command("GET", "alert/text") == text,
+             "modal has not appeared",
+             timeout=15,
+             ignored_exceptions=webdriver.NoSuchAlertException)
 
     return create_dialog
 
+
 def clear_all_cookies(session):
     """Removes all cookies associated with the current active document"""
     session.transport.send("DELETE", "session/%s/cookie" % session.session_id)
-