Bug 1434872 - [marionette] Firefox has to automatically dismiss "beforeunload" prompts. draft
authorHenrik Skupin <mail@hskupin.info>
Thu, 19 Apr 2018 14:47:32 +0200
changeset 793242 ff8e513a0a7f93631bfa477fcc8318c8419e2254
parent 793241 c46ca2dce2da51c6a5d218d4564bbe5ca04c2301
push id109323
push userbmo:hskupin@gmail.com
push dateWed, 09 May 2018 19:25:09 +0000
bugs1434872
milestone62.0a1
Bug 1434872 - [marionette] Firefox has to automatically dismiss "beforeunload" prompts. As requested by the WebDriver specification any beforeunload prompt has to be automatically dismissed for the close and navigate commands. MozReview-Commit-ID: 1qp0nzIYTaM
testing/marionette/client/marionette_driver/geckoinstance.py
testing/marionette/components/marionette.js
testing/marionette/harness/marionette_harness/tests/unit/test_click.py
testing/marionette/harness/marionette_harness/tests/unit/test_modal_dialogs.py
testing/marionette/harness/marionette_harness/tests/unit/test_navigation.py
testing/marionette/harness/marionette_harness/tests/unit/test_quit_restart.py
testing/marionette/harness/marionette_harness/tests/unit/test_window_close_content.py
testing/marionette/harness/marionette_harness/www/beforeunload.html
testing/marionette/harness/marionette_harness/www/test_tab_modal_dialogs.html
testing/web-platform/meta/MANIFEST.json
testing/web-platform/tests/webdriver/tests/close_window/close.py
testing/web-platform/tests/webdriver/tests/delete_session/__init__.py
testing/web-platform/tests/webdriver/tests/delete_session/delete.py
--- a/testing/marionette/client/marionette_driver/geckoinstance.py
+++ b/testing/marionette/client/marionette_driver/geckoinstance.py
@@ -45,16 +45,20 @@ class GeckoInstance(object):
         # Do not send Firefox health reports to the production server
         # removed in Firefox 59
         "datareporting.healthreport.about.reportUrl": "http://%(server)s/dummy/abouthealthreport/",
         "datareporting.healthreport.documentServerURI": "http://%(server)s/dummy/healthreport/",
 
         # Do not show datareporting policy notifications which can interfer with tests
         "datareporting.policy.dataSubmissionPolicyBypassNotification": True,
 
+        # Automatically unload beforeunload alerts
+        "dom.disable_beforeunload": True,
+
+        # Disable the ProcessHangMonitor
         "dom.ipc.reportProcessHangs": False,
 
         # No slow script dialogs
         "dom.max_chrome_script_run_time": 0,
         "dom.max_script_run_time": 0,
 
         # Only load extensions from the application and user profile
         # AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_APPLICATION
--- a/testing/marionette/components/marionette.js
+++ b/testing/marionette/components/marionette.js
@@ -165,16 +165,19 @@ const RECOMMENDED_PREFS = new Map([
   ["datareporting.healthreport.logging.consoleEnabled", false],
   ["datareporting.healthreport.service.enabled", false],
   ["datareporting.healthreport.service.firstRun", false],
   ["datareporting.healthreport.uploadEnabled", false],
   ["datareporting.policy.dataSubmissionEnabled", false],
   ["datareporting.policy.dataSubmissionPolicyAccepted", false],
   ["datareporting.policy.dataSubmissionPolicyBypassNotification", true],
 
+  // Automatically unload beforeunload alerts
+  ["dom.disable_beforeunload", true],
+
   // Disable popup-blocker
   ["dom.disable_open_during_load", false],
 
   // Enabling the support for File object creation in the content process
   ["dom.file.createInChild", true],
 
   // Disable the ProcessHangMonitor
   ["dom.ipc.reportProcessHangs", false],
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_click.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_click.py
@@ -2,16 +2,17 @@
 # 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
 
 import urllib
 
 from marionette_driver import By, errors
+from marionette_driver.marionette import Alert
 
 from marionette_harness import (
     MarionetteTestCase,
     run_if_e10s,
     skip_if_mobile,
     WindowManagerMixin,
 )
 
@@ -375,27 +376,32 @@ class TestClickNavigation(MarionetteTest
         except errors.NoSuchElementException:
             pass
 
     def test_click_link_page_load(self):
         self.marionette.find_element(By.LINK_TEXT, "333333").click()
         self.assertNotEqual(self.marionette.get_url(), self.test_page)
         self.assertEqual(self.marionette.title, "Marionette Test")
 
-    @skip_if_mobile("Bug 1325738 - Modal dialogs block execution of code for Fennec")
-    def test_click_link_page_load_aborted_by_beforeunload(self):
-        page = self.marionette.absolute_url("beforeunload.html")
-        self.marionette.navigate(page)
+    def test_click_link_page_load_dismissed_beforeunload_prompt(self):
+        self.marionette.navigate(inline("""
+          <input type="text"></input>
+          <a href="{}">Click</a>
+          <script>
+            window.addEventListener("beforeunload", function (event) {{
+              event.preventDefault();
+            }});
+          </script>
+        """.format(self.marionette.absolute_url("clicks.html"))))
+        self.marionette.find_element(By.TAG_NAME, "input").send_keys("foo")
         self.marionette.find_element(By.TAG_NAME, "a").click()
 
-        # click returns immediately when a beforeunload handler is invoked
-        alert = self.marionette.switch_to_alert()
-        alert.dismiss()
-
-        self.assertEqual(self.marionette.get_url(), page)
+        # navigation auto-dismisses beforeunload prompt
+        with self.assertRaises(errors.NoAlertPresentException):
+            Alert(self.marionette).text
 
     def test_click_link_anchor(self):
         self.marionette.find_element(By.ID, "anchor").click()
         self.assertEqual(self.marionette.get_url(), "{}#".format(self.test_page))
 
     def test_click_link_install_addon(self):
         try:
             self.marionette.find_element(By.ID, "install-addon").click()
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_modal_dialogs.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_modal_dialogs.py
@@ -36,18 +36,16 @@ class TestTabModalAlerts(BaseAlertTestCa
     def setUp(self):
         super(TestTabModalAlerts, self).setUp()
         self.assertTrue(self.marionette.get_pref("prompts.tab_modal.enabled",
                         "Tab modal alerts should be enabled by default."))
 
         self.marionette.navigate(self.marionette.absolute_url("test_tab_modal_dialogs.html"))
 
     def tearDown(self):
-        self.marionette.execute_script("window.onbeforeunload = null;")
-
         # Ensure to close a possible remaining tab modal dialog
         try:
             alert = self.marionette.switch_to_alert()
             alert.dismiss()
 
             self.wait_for_alert_closed()
         except:
             pass
@@ -168,43 +166,16 @@ class TestTabModalAlerts(BaseAlertTestCa
         self.marionette.find_element(By.ID, "tab-modal-prompt").click()
         self.wait_for_alert()
         alert = self.marionette.switch_to_alert()
         alert.send_keys("Some text!")
         alert.dismiss()
         self.wait_for_condition(
             lambda mn: mn.find_element(By.ID, "prompt-result").text == "null")
 
-    def test_onbeforeunload_dismiss(self):
-        start_url = self.marionette.get_url()
-        self.marionette.find_element(By.ID, "onbeforeunload-handler").click()
-        self.wait_for_condition(
-            lambda mn: mn.execute_script("""
-              return window.onbeforeunload !== null;
-            """))
-        self.marionette.navigate("about:blank")
-        self.wait_for_alert()
-        alert = self.marionette.switch_to_alert()
-        self.assertTrue(alert.text.startswith("This page is asking you to confirm"))
-        alert.dismiss()
-        self.assertTrue(self.marionette.get_url().startswith(start_url))
-
-    def test_onbeforeunload_accept(self):
-        self.marionette.find_element(By.ID, "onbeforeunload-handler").click()
-        self.wait_for_condition(
-            lambda mn: mn.execute_script("""
-              return window.onbeforeunload !== null;
-            """))
-        self.marionette.navigate("about:blank")
-        self.wait_for_alert()
-        alert = self.marionette.switch_to_alert()
-        self.assertTrue(alert.text.startswith("This page is asking you to confirm"))
-        alert.accept()
-        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()
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_navigation.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_navigation.py
@@ -5,34 +5,37 @@
 from __future__ import absolute_import, print_function
 
 import contextlib
 import os
 import urllib
 
 from marionette_driver import By, errors, expected, Wait
 from marionette_driver.keys import Keys
+from marionette_driver.marionette import Alert
 from marionette_harness import (
     MarionetteTestCase,
     run_if_e10s,
     run_if_manage_instance,
     skip,
     skip_if_mobile,
     WindowManagerMixin,
 )
 
 here = os.path.abspath(os.path.dirname(__file__))
 
 
 BLACK_PIXEL = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==' # noqa
 RED_PIXEL = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEX/TQBcNTh/AAAAAXRSTlPM0jRW/QAAAApJREFUeJxjYgAAAAYAAzY3fKgAAAAASUVORK5CYII=' # noqa
 
+
 def inline(doc):
     return "data:text/html;charset=utf-8,%s" % urllib.quote(doc)
 
+
 def inline_image(data):
     return 'data:image/png;base64,%s' % data
 
 
 class BaseNavigationTestCase(WindowManagerMixin, MarionetteTestCase):
 
     def setUp(self):
         super(BaseNavigationTestCase, self).setUp()
@@ -355,64 +358,95 @@ class TestNavigate(BaseNavigationTestCas
             lambda mn: mn.get_url() == "about:addons",
             message="'about:addons' hasn't been loaded")
 
 
 class TestBackForwardNavigation(BaseNavigationTestCase):
 
     def run_bfcache_test(self, test_pages):
         # Helper method to run simple back and forward testcases.
+
+        def check_page_status(page, expected_history_length):
+            if "alert_text" in page:
+                if page["alert_text"] is None:
+                    # navigation auto-dismisses beforeunload prompt
+                    with self.assertRaises(errors.NoAlertPresentException):
+                        Alert(self.marionette).text
+                else:
+                    self.assertEqual(Alert(self.marionette).text, page["alert_text"])
+
+            self.assertEqual(page["url"], self.marionette.get_url())
+            self.assertEqual(self.history_length, expected_history_length)
+
+            if "is_remote" in page:
+                self.assertEqual(page["is_remote"], self.is_remote_tab,
+                                 "'{}' doesn't match expected remoteness state: {}".format(
+                                     page["url"], page["is_remote"]))
+
+            if "callback" in page and callable(page["callback"]):
+                page["callback"]()
+
         for index, page in enumerate(test_pages):
             if "error" in page:
                 with self.assertRaises(page["error"]):
                     self.marionette.navigate(page["url"])
             else:
                 self.marionette.navigate(page["url"])
-            self.assertEqual(page["url"], self.marionette.get_url())
-            self.assertEqual(self.history_length, index + 1)
 
-            if "is_remote" in page:
-                self.assertEqual(page["is_remote"], self.is_remote_tab,
-                                 "'{}' doesn't match expected remoteness state: {}".format(
-                                     page["url"], page["is_remote"]))
+            check_page_status(page, index + 1)
 
         # Now going back in history for all test pages by backward iterating
         # through the list (-1) and skipping the first entry at the end (-2).
         for page in test_pages[-2::-1]:
             if "error" in page:
                 with self.assertRaises(page["error"]):
                     self.marionette.go_back()
             else:
                 self.marionette.go_back()
             self.assertEqual(page["url"], self.marionette.get_url())
 
-            if "is_remote" in page:
-                self.assertEqual(page["is_remote"], self.is_remote_tab,
-                                 "'{}' doesn't match expected remoteness state: {}".format(
-                                     page["url"], page["is_remote"]))
+            check_page_status(page, len(test_pages))
 
         # Now going forward in history by skipping the first entry.
         for page in test_pages[1::]:
             if "error" in page:
                 with self.assertRaises(page["error"]):
                     self.marionette.go_forward()
             else:
                 self.marionette.go_forward()
             self.assertEqual(page["url"], self.marionette.get_url())
 
-            if "is_remote" in page:
-                self.assertEqual(page["is_remote"], self.is_remote_tab,
-                                 "'{}' doesn't match expected remoteness state: {}".format(
-                                     page["url"], page["is_remote"]))
+            check_page_status(page, len(test_pages))
 
     def test_no_history_items(self):
         # Both methods should not raise a failure if no navigation is possible
         self.marionette.go_back()
         self.marionette.go_forward()
 
+    def test_dismissed_beforeunload_prompt(self):
+        url_beforeunload = inline("""
+          <input type="text">
+          <script>
+            window.addEventListener("beforeunload", function (event) {
+              event.preventDefault();
+            });
+          </script>
+        """)
+
+        def modify_page():
+            self.marionette.find_element(By.TAG_NAME, "input").send_keys("foo")
+
+        test_pages = [
+            {"url": inline("<p>foobar</p>"), "alert_text": None},
+            {"url": url_beforeunload, "callback": modify_page},
+            {"url": inline("<p>foobar</p>"), "alert_text": None},
+        ]
+
+        self.run_bfcache_test(test_pages)
+
     def test_data_urls(self):
         test_pages = [
             {"url": inline("<p>foobar</p>")},
             {"url": self.test_page_remote},
             {"url": inline("<p>foobar</p>")},
         ]
         self.run_bfcache_test(test_pages)
 
@@ -616,16 +650,32 @@ class TestRefresh(BaseNavigationTestCase
     @skip_if_mobile("Test file is only located on host machine")
     def test_file_url(self):
         self.marionette.navigate(self.test_page_file_url)
         self.assertEqual(self.test_page_file_url, self.marionette.get_url())
 
         self.marionette.refresh()
         self.assertEqual(self.test_page_file_url, self.marionette.get_url())
 
+    def test_dismissed_beforeunload_prompt(self):
+        self.marionette.navigate(inline("""
+          <input type="text">
+          <script>
+            window.addEventListener("beforeunload", function (event) {
+              event.preventDefault();
+            });
+          </script>
+        """))
+        self.marionette.find_element(By.TAG_NAME, "input").send_keys("foo")
+        self.marionette.refresh()
+
+        # navigation auto-dismisses beforeunload prompt
+        with self.assertRaises(errors.NoAlertPresentException):
+            Alert(self.marionette).text
+
     def test_image(self):
         image = self.marionette.absolute_url('black.png')
 
         self.marionette.navigate(image)
         self.assertEqual(image, self.marionette.get_url())
 
         self.marionette.refresh()
         self.assertEqual(image, self.marionette.get_url())
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_quit_restart.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_quit_restart.py
@@ -1,18 +1,25 @@
 # 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, print_function
 
+import urllib
+
 from marionette_driver import errors
+from marionette_driver.by import By
 from marionette_harness import MarionetteTestCase, skip
 
 
+def inline(doc):
+    return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
+
+
 class TestServerQuitApplication(MarionetteTestCase):
 
     def tearDown(self):
         if self.marionette.session is None:
             self.marionette.start_session()
 
     def quit(self, flags=None):
         body = None
@@ -280,16 +287,30 @@ class TestQuitRestart(MarionetteTestCase
                 self.marionette.quit(in_app=True, callback=lambda: False)
         finally:
             self.marionette.DEFAULT_SHUTDOWN_TIMEOUT = timeout
 
     def test_in_app_quit_with_callback_not_callable(self):
         with self.assertRaisesRegexp(ValueError, "is not callable"):
             self.marionette.restart(in_app=True, callback=4)
 
+    def test_in_app_quit_with_dismissed_beforeunload_prompt(self):
+        self.marionette.navigate(inline("""
+          <input type="text">
+          <script>
+            window.addEventListener("beforeunload", function (event) {
+              event.preventDefault();
+            });
+          </script>
+        """))
+
+        self.marionette.find_element(By.TAG_NAME, "input").send_keys("foo")
+        self.marionette.quit(in_app=True)
+        self.marionette.start_session()
+
     @skip("Bug 1363368 - Wrong window handles after in_app restarts")
     def test_reset_context_after_quit_by_set_context(self):
         if self.marionette.session_capabilities["platformName"] != "windows_nt":
             skip("Bug 1363368 - Wrong window handles after in_app restarts")
 
         # Check that we are in content context which is used by default in
         # Marionette
         self.assertNotIn("chrome://", self.marionette.get_url(),
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_window_close_content.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_window_close_content.py
@@ -1,17 +1,24 @@
 # 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
 
+import urllib
+
+from marionette_driver.by import By
 from marionette_harness import MarionetteTestCase, skip_if_mobile, WindowManagerMixin
 
 
+def inline(doc):
+    return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
+
+
 class TestCloseWindow(WindowManagerMixin, MarionetteTestCase):
 
     def tearDown(self):
         self.close_all_windows()
         self.close_all_tabs()
 
         super(TestCloseWindow, self).tearDown()
 
@@ -59,16 +66,33 @@ class TestCloseWindow(WindowManagerMixin
     def test_close_window_for_browser_tab(self):
         tab = self.open_tab()
         self.marionette.switch_to_window(tab)
 
         window_handles = self.marionette.close()
         self.assertNotIn(tab, window_handles)
         self.assertListEqual(self.start_tabs, window_handles)
 
+    @skip_if_mobile("Needs application independent method to open a new tab")
+    def test_close_window_with_dismissed_beforeunload_prompt(self):
+        tab = self.open_tab()
+        self.marionette.switch_to_window(tab)
+
+        self.marionette.navigate(inline("""
+          <input type="text">
+          <script>
+            window.addEventListener("beforeunload", function (event) {
+              event.preventDefault();
+            });
+          </script>
+        """))
+
+        self.marionette.find_element(By.TAG_NAME, "input").send_keys("foo")
+        self.marionette.close()
+
     @skip_if_mobile("Interacting with chrome windows not available for Fennec")
     def test_close_window_for_browser_window_with_single_tab(self):
         win = self.open_window()
         self.marionette.switch_to_window(win)
 
         self.assertEqual(len(self.start_tabs) + 1, len(self.marionette.window_handles))
         window_handles = self.marionette.close()
         self.assertNotIn(win, window_handles)
deleted file mode 100644
--- a/testing/marionette/harness/marionette_harness/www/beforeunload.html
+++ /dev/null
@@ -1,21 +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/. -->
-
-<!DOCTYPE html>
-<html>
-<head>
-  <title>Marionette Test</title>
-  <script type="text/javascript">
-    function unload() {
-      window.onbeforeunload = null;
-      return "asdf";
-    }
-
-    window.onbeforeunload = unload;
-  </script>
-</head>
-<body>
-   <a href="empty.html">Click</a> to leave the page.<br/>
-</body>
-</html>
--- a/testing/marionette/harness/marionette_harness/www/test_tab_modal_dialogs.html
+++ b/testing/marionette/harness/marionette_harness/www/test_tab_modal_dialogs.html
@@ -15,25 +15,20 @@
       var alertAccepted = window.confirm('Marionette confirm');
       document.getElementById('confirm-result').innerHTML = alertAccepted;
     }
 
     function handlePrompt () {
       var promptText = window.prompt('Marionette prompt');
       document.getElementById('prompt-result').innerHTML = promptText === null ? 'null' : promptText;
     }
-
-    function onBeforeUnload () {
-      window.onbeforeunload = function () { return "Are you sure?"; }
-    }
   </script>
 </head>
 <body>
    <a href="#" id="tab-modal-alert" onclick="handleAlert()">Open an alert dialog.</a>
    <a href="#" id="tab-modal-confirm" onclick="handleConfirm()">Open a confirm dialog.</a>
    <a href="#" id="tab-modal-prompt" onclick="handlePrompt()">Open a prompt dialog.</a>
-   <a href="#" id="onbeforeunload-handler" onclick="onBeforeUnload()">Add an onbeforeunload handler.</a>
    <a href="#" id="click-handler" onclick="document.getElementById('click-result').innerHTML='result';">Make text appear.</a>
    <div id="confirm-result"></div>
    <div id="prompt-result"></div>
    <div id="click-result"></div>
 </body>
 </html>
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -298147,16 +298147,21 @@
      {}
     ]
    ],
    "webdriver/tests/delete_cookie/__init__.py": [
     [
      {}
     ]
    ],
+   "webdriver/tests/delete_session/__init__.py": [
+    [
+     {}
+    ]
+   ],
    "webdriver/tests/dismiss_alert/__init__.py": [
     [
      {}
     ]
    ],
    "webdriver/tests/element_clear/__init__.py": [
     [
      {}
@@ -397170,16 +397175,22 @@
     ]
    ],
    "webdriver/tests/delete_cookie/user_prompts.py": [
     [
      "/webdriver/tests/delete_cookie/user_prompts.py",
      {}
     ]
    ],
+   "webdriver/tests/delete_session/delete.py": [
+    [
+     "/webdriver/tests/delete_session/delete.py",
+     {}
+    ]
+   ],
    "webdriver/tests/dismiss_alert/dismiss.py": [
     [
      "/webdriver/tests/dismiss_alert/dismiss.py",
      {}
     ]
    ],
    "webdriver/tests/element_clear/clear.py": [
     [
@@ -611498,17 +611509,17 @@
    "29891e121def1917c47c70efd19b40ed5f2ea61d",
    "wdspec"
   ],
   "webdriver/tests/close_window/__init__.py": [
    "da39a3ee5e6b4b0d3255bfef95601890afd80709",
    "support"
   ],
   "webdriver/tests/close_window/close.py": [
-   "a98fdaa5d8afe6ddca892e8857c134ba24b0e43a",
+   "8c22860607cb0f3d610888c9816bf2384e2c5445",
    "wdspec"
   ],
   "webdriver/tests/close_window/user_prompts.py": [
    "7b1255736f8772f5790b9bf1e46cbf1c5b1c2dee",
    "wdspec"
   ],
   "webdriver/tests/conftest.py": [
    "c812269d034c9ca1b8c4f136dd5d0cea52f4d0f0",
@@ -611521,16 +611532,24 @@
   "webdriver/tests/delete_cookie/delete.py": [
    "1f0d6b861be1ed682fd87a402908cee186a3987c",
    "wdspec"
   ],
   "webdriver/tests/delete_cookie/user_prompts.py": [
    "65b753bd80a06c3c20b0330f624a4d395fdb7ab2",
    "wdspec"
   ],
+  "webdriver/tests/delete_session/__init__.py": [
+   "da39a3ee5e6b4b0d3255bfef95601890afd80709",
+   "support"
+  ],
+  "webdriver/tests/delete_session/delete.py": [
+   "6cbfd4c20b548e668d6251df9d0a26b0824e6ae1",
+   "wdspec"
+  ],
   "webdriver/tests/dismiss_alert/__init__.py": [
    "da39a3ee5e6b4b0d3255bfef95601890afd80709",
    "support"
   ],
   "webdriver/tests/dismiss_alert/dismiss.py": [
    "e213f1939ff5cc2fbdebb2bd8e9445e284803a60",
    "wdspec"
   ],
--- a/testing/web-platform/tests/webdriver/tests/close_window/close.py
+++ b/testing/web-platform/tests/webdriver/tests/close_window/close.py
@@ -1,9 +1,13 @@
+import pytest
+from webdriver import error
+
 from tests.support.asserts import assert_error, assert_success
+from tests.support.inline import inline
 
 
 def close(session):
     return session.transport.send(
         "DELETE", "session/{session_id}/window".format(**vars(session)))
 
 
 def test_no_browsing_context(session, create_window):
@@ -13,25 +17,52 @@ def test_no_browsing_context(session, cr
     session.close()
     assert new_handle not in session.handles
 
     response = close(session)
     assert_error(response, "no such window")
 
 
 def test_close_browsing_context(session, create_window):
-    handles = session.handles
+    original_handles = session.handles
 
     new_handle = create_window()
     session.window_handle = new_handle
 
     response = close(session)
-    value = assert_success(response, handles)
-    assert session.handles == handles
-    assert new_handle not in value
+    handles = assert_success(response, original_handles)
+    assert session.handles == original_handles
+    assert new_handle not in handles
+
+
+def test_close_browsing_context_with_dismissed_beforeunload_prompt(session, create_window):
+    original_handles = session.handles
+
+    new_handle = create_window()
+    session.window_handle = new_handle
+
+    session.url = inline("""
+      <input type="text">
+      <script>
+        window.addEventListener("beforeunload", function (event) {
+          event.preventDefault();
+        });
+      </script>
+    """)
+
+    session.find.css("input", all=False).send_keys("foo")
+
+    response = close(session)
+    handles = assert_success(response, original_handles)
+    assert session.handles == original_handles
+    assert new_handle not in handles
+
+    # A beforeunload prompt has to be automatically dismissed
+    with pytest.raises(error.NoSuchWindowException):
+        session.alert.text
 
 
 def test_close_last_browsing_context(session):
     assert len(session.handles) == 1
     response = close(session)
 
     assert_success(response, [])
 
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/webdriver/tests/delete_session/delete.py
@@ -0,0 +1,29 @@
+import pytest
+from webdriver import error
+
+from tests.support.asserts import assert_success
+from tests.support.inline import inline
+
+
+def delete_session(session):
+    return session.transport.send("DELETE", "session/{session_id}".format(**vars(session)))
+
+
+def test_delete_session_with_dismissed_beforeunload_prompt(session):
+    session.url = inline("""
+      <input type="text">
+      <script>
+        window.addEventListener("beforeunload", function (event) {
+          event.preventDefault();
+        });
+      </script>
+    """)
+
+    session.find.css("input", all=False).send_keys("foo")
+
+    response = delete_session(session)
+    assert_success(response)
+
+    # A beforeunload prompt has to be automatically dismissed, and the session deleted
+    with pytest.raises(error.SessionNotCreatedException):
+        session.alert.text