Bug 1434872 - [marionette] Firefox has to automatically dismiss "beforeunload" prompts. r=ato

As requested by the WebDriver specification any beforeunload prompt
has to be automatically dismissed for the close and navigate commands.

MozReview-Commit-ID: 1qp0nzIYTaM

--HG--
extra : rebase_source : 418dca8e9fa61df6d5423c8c5cce6a97a0001821
This commit is contained in:
Henrik Skupin 2018-04-19 14:47:32 +02:00
parent 35e12cd652
commit 9fde0f005d
13 changed files with 215 additions and 83 deletions

View File

@ -50,6 +50,10 @@ class GeckoInstance(object):
# 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

View File

@ -170,6 +170,9 @@ const RECOMMENDED_PREFS = new Map([
["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],

View File

@ -7,6 +7,7 @@ from __future__ import absolute_import
import urllib
from marionette_driver import By, errors
from marionette_driver.marionette import Alert
from marionette_harness import (
MarionetteTestCase,
@ -380,17 +381,22 @@ class TestClickNavigation(MarionetteTestCase):
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()

View File

@ -41,8 +41,6 @@ class TestTabModalAlerts(BaseAlertTestCase):
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()
@ -173,33 +171,6 @@ class TestTabModalAlerts(BaseAlertTestCase):
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()

View File

@ -10,6 +10,7 @@ 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,
@ -25,9 +26,11 @@ 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
@ -360,19 +363,35 @@ 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).
@ -384,10 +403,7 @@ class TestBackForwardNavigation(BaseNavigationTestCase):
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::]:
@ -398,16 +414,34 @@ class TestBackForwardNavigation(BaseNavigationTestCase):
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>")},
@ -621,6 +655,22 @@ class TestRefresh(BaseNavigationTestCase):
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')

View File

@ -4,10 +4,17 @@
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):
@ -285,6 +292,20 @@ class TestQuitRestart(MarionetteTestCase):
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":

View File

@ -4,9 +4,16 @@
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):
@ -64,6 +71,23 @@ class TestCloseWindow(WindowManagerMixin, MarionetteTestCase):
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()

View File

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

View File

@ -20,17 +20,12 @@
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>

View File

@ -298152,6 +298152,11 @@
{}
]
],
"webdriver/tests/delete_session/__init__.py": [
[
{}
]
],
"webdriver/tests/dismiss_alert/__init__.py": [
[
{}
@ -397175,6 +397180,12 @@
{}
]
],
"webdriver/tests/delete_session/delete.py": [
[
"/webdriver/tests/delete_session/delete.py",
{}
]
],
"webdriver/tests/dismiss_alert/dismiss.py": [
[
"/webdriver/tests/dismiss_alert/dismiss.py",
@ -611503,7 +611514,7 @@
"support"
],
"webdriver/tests/close_window/close.py": [
"a98fdaa5d8afe6ddca892e8857c134ba24b0e43a",
"8c22860607cb0f3d610888c9816bf2384e2c5445",
"wdspec"
],
"webdriver/tests/close_window/user_prompts.py": [
@ -611526,6 +611537,14 @@
"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"

View File

@ -1,4 +1,8 @@
import pytest
from webdriver import error
from tests.support.asserts import assert_error, assert_success
from tests.support.inline import inline
def close(session):
@ -18,15 +22,42 @@ def test_no_browsing_context(session, create_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):

View File

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