Skip to content

Commit 19cc83e

Browse files
committed
Bug 1693857 - [marionette] Implicitly accept "beforeunload" prompts in Marionette. r=webdriver-reviewers,jdescottes
Differential Revision: https://phabricator.services.mozilla.com/D198681
1 parent b14035e commit 19cc83e

File tree

9 files changed

+44
-115
lines changed

9 files changed

+44
-115
lines changed

remote/components/Marionette.sys.mjs

+1-8
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,6 @@ const ENV_ENABLED = "MOZ_MARIONETTE";
4040
// pref being set to 4444.
4141
const ENV_PRESERVE_PREFS = "MOZ_MARIONETTE_PREF_STATE_ACROSS_RESTARTS";
4242

43-
// Map of Marionette-specific preferences that should be set via
44-
// RecommendedPreferences.
45-
const RECOMMENDED_PREFS = new Map([
46-
// Automatically unload beforeunload alerts
47-
["dom.disable_beforeunload", true],
48-
]);
49-
5043
const isRemote =
5144
Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT;
5245

@@ -147,7 +140,7 @@ class MarionetteParentProcess {
147140
Services.obs.addObserver(this, "domwindowopened");
148141
}
149142

150-
lazy.RecommendedPreferences.applyPreferences(RECOMMENDED_PREFS);
143+
lazy.RecommendedPreferences.applyPreferences();
151144

152145
// Only set preferences to preserve in a new profile
153146
// when Marionette is enabled.

remote/marionette/driver.sys.mjs

+24-3
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ export function GeckoDriver(server) {
114114
// WebDriver Session
115115
this._currentSession = null;
116116

117+
// Flag to indicate that the application is shutting down
118+
this._isShuttingDown = false;
119+
117120
this.browsers = {};
118121

119122
// points to current browser
@@ -211,7 +214,16 @@ GeckoDriver.prototype.handleClosedModalDialog = function () {
211214
*/
212215
GeckoDriver.prototype.handleOpenModalDialog = function (eventName, data) {
213216
this.dialog = data.prompt;
214-
this.getActor().notifyDialogOpened();
217+
218+
if (this.dialog.promptType === "beforeunload") {
219+
lazy.logger.trace(`Implicitly accepted "beforeunload" prompt`);
220+
this.dialog.accept();
221+
return;
222+
}
223+
224+
if (!this._isShuttingDown) {
225+
this.getActor().notifyDialogOpened(this.dialog);
226+
}
215227
};
216228

217229
/**
@@ -2375,7 +2387,9 @@ GeckoDriver.prototype.deleteSession = function () {
23752387
// reset to the top-most frame
23762388
this.mainFrame = null;
23772389

2378-
if (this.promptListener) {
2390+
if (!this._isShuttingDown && this.promptListener) {
2391+
// Do not stop the prompt listener when quitting the browser to
2392+
// allow us to also accept beforeunload prompts during shutdown.
23792393
this.promptListener.stopListening();
23802394
this.promptListener = null;
23812395
}
@@ -2792,6 +2806,12 @@ GeckoDriver.prototype._handleUserPrompts = async function () {
27922806
return;
27932807
}
27942808

2809+
if (this.dialog.promptType == "beforeunload") {
2810+
// Wait until the "beforeunload" prompt has been accepted.
2811+
await this.promptListener.dialogClosed();
2812+
return;
2813+
}
2814+
27952815
const textContent = await this.dialog.getText();
27962816

27972817
const behavior = this.currentSession.unhandledPromptBehavior;
@@ -2899,16 +2919,17 @@ GeckoDriver.prototype.quit = async function (cmd) {
28992919

29002920
let quitApplicationResponse;
29012921
try {
2922+
this._isShuttingDown = true;
29022923
quitApplicationResponse = await lazy.quit(
29032924
flags,
29042925
safeMode,
29052926
this.currentSession.capabilities.get("moz:windowless")
29062927
);
29072928
} catch (e) {
2929+
this._isShuttingDown = false;
29082930
if (e instanceof TypeError) {
29092931
throw new lazy.error.InvalidArgumentError(e.message);
29102932
}
2911-
29122933
throw new lazy.error.UnsupportedOperationError(e.message);
29132934
} finally {
29142935
Services.obs.removeObserver(this, TOPIC_QUIT_APPLICATION_REQUESTED);

remote/marionette/navigate.sys.mjs

+14-8
Original file line numberDiff line numberDiff line change
@@ -255,20 +255,26 @@ navigate.waitForNavigationCompleted = async function waitForNavigationCompleted(
255255
}
256256
};
257257

258-
const onPromptOpened = action => {
258+
const onPromptOpened = (_, data) => {
259+
if (data.prompt.promptType === "beforeunload") {
260+
// Ignore beforeunload prompts which are handled by the driver class.
261+
return;
262+
}
263+
259264
lazy.logger.trace("Canceled page load listener because a dialog opened");
260265
checkDone({ finished: true });
261266
};
262267

263268
const onTimer = timer => {
264-
// In the case when a document has a beforeunload handler
265-
// registered, the currently active command will return immediately
266-
// due to the modal dialog observer.
269+
// For the command "Element Click" we want to detect a potential navigation
270+
// as early as possible. The `beforeunload` event is an indication for that
271+
// but could still cause the navigation to get aborted by the user. As such
272+
// wait a bit longer for the `unload` event to happen, which usually will
273+
// occur pretty soon after `beforeunload`.
267274
//
268-
// Otherwise the timeout waiting for the document to start
269-
// navigating is increased by 5000 ms to ensure a possible load
270-
// event is not missed. In the common case such an event should
271-
// occur pretty soon after beforeunload, and we optimise for this.
275+
// Note that with WebDriver BiDi enabled the `beforeunload` prompts might
276+
// not get implicitly accepted, so lets keep the timer around until we know
277+
// that it is really not required.
272278
if (seenBeforeUnload) {
273279
seenBeforeUnload = false;
274280
unloadTimer.initWithCallback(

testing/marionette/client/marionette_driver/geckoinstance.py

-2
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,6 @@ class GeckoInstance(object):
6060
# Do not show datareporting policy notifications which can interfere with tests
6161
"datareporting.policy.dataSubmissionEnabled": False,
6262
"datareporting.policy.dataSubmissionPolicyBypassNotification": True,
63-
# Automatically unload beforeunload alerts
64-
"dom.disable_beforeunload": True,
6563
# Enabling the support for File object creation in the content process.
6664
"dom.file.createInChild": True,
6765
# Disable delayed user input event handling

testing/marionette/harness/marionette_harness/tests/unit/test_click.py

-23
Original file line numberDiff line numberDiff line change
@@ -505,29 +505,6 @@ def test_click_link_page_load(self):
505505
self.assertNotEqual(self.marionette.get_url(), self.test_page)
506506
self.assertEqual(self.marionette.title, "Marionette Test")
507507

508-
def test_click_link_page_load_dismissed_beforeunload_prompt(self):
509-
self.marionette.navigate(
510-
inline(
511-
"""
512-
<input type="text"></input>
513-
<a href="{}">Click</a>
514-
<script>
515-
window.addEventListener("beforeunload", function (event) {{
516-
event.preventDefault();
517-
}});
518-
</script>
519-
""".format(
520-
self.marionette.absolute_url("clicks.html")
521-
)
522-
)
523-
)
524-
self.marionette.find_element(By.TAG_NAME, "input").send_keys("foo")
525-
self.marionette.find_element(By.TAG_NAME, "a").click()
526-
527-
# navigation auto-dismisses beforeunload prompt
528-
with self.assertRaises(errors.NoAlertPresentException):
529-
Alert(self.marionette).text
530-
531508
def test_click_link_anchor(self):
532509
self.marionette.find_element(By.ID, "anchor").click()
533510
self.assertEqual(self.marionette.get_url(), "{}#".format(self.test_page))

testing/marionette/harness/marionette_harness/tests/unit/test_navigation.py

+1-49
Original file line numberDiff line numberDiff line change
@@ -385,12 +385,7 @@ def run_bfcache_test(self, test_pages):
385385

386386
def check_page_status(page, expected_history_length):
387387
if "alert_text" in page:
388-
if page["alert_text"] is None:
389-
# navigation auto-dismisses beforeunload prompt
390-
with self.assertRaises(errors.NoAlertPresentException):
391-
Alert(self.marionette).text
392-
else:
393-
self.assertEqual(Alert(self.marionette).text, page["alert_text"])
388+
self.assertEqual(Alert(self.marionette).text, page["alert_text"])
394389

395390
self.assertEqual(self.marionette.get_url(), page["url"])
396391
self.assertEqual(self.history_length, expected_history_length)
@@ -442,29 +437,6 @@ def test_no_history_items(self):
442437
self.marionette.go_back()
443438
self.marionette.go_forward()
444439

445-
def test_dismissed_beforeunload_prompt(self):
446-
url_beforeunload = inline(
447-
"""
448-
<input type="text">
449-
<script>
450-
window.addEventListener("beforeunload", function (event) {
451-
event.preventDefault();
452-
});
453-
</script>
454-
"""
455-
)
456-
457-
def modify_page():
458-
self.marionette.find_element(By.TAG_NAME, "input").send_keys("foo")
459-
460-
test_pages = [
461-
{"url": inline("<p>foobar</p>"), "alert_text": None},
462-
{"url": url_beforeunload, "callback": modify_page},
463-
{"url": inline("<p>foobar</p>"), "alert_text": None},
464-
]
465-
466-
self.run_bfcache_test(test_pages)
467-
468440
def test_data_urls(self):
469441
test_pages = [
470442
{"url": inline("<p>foobar</p>")},
@@ -686,26 +658,6 @@ def test_file_url(self):
686658
self.marionette.refresh()
687659
self.assertEqual(self.test_page_file_url, self.marionette.get_url())
688660

689-
def test_dismissed_beforeunload_prompt(self):
690-
self.marionette.navigate(
691-
inline(
692-
"""
693-
<input type="text">
694-
<script>
695-
window.addEventListener("beforeunload", function (event) {
696-
event.preventDefault();
697-
});
698-
</script>
699-
"""
700-
)
701-
)
702-
self.marionette.find_element(By.TAG_NAME, "input").send_keys("foo")
703-
self.marionette.refresh()
704-
705-
# navigation auto-dismisses beforeunload prompt
706-
with self.assertRaises(errors.NoAlertPresentException):
707-
Alert(self.marionette).text
708-
709661
def test_image(self):
710662
image = self.marionette.absolute_url("black.png")
711663

testing/marionette/harness/marionette_harness/tests/unit/test_window_close_content.py

-20
Original file line numberDiff line numberDiff line change
@@ -59,26 +59,6 @@ def test_close_window_for_browser_tab(self):
5959
self.assertNotIn(new_tab, window_handles)
6060
self.assertListEqual(self.start_tabs, window_handles)
6161

62-
def test_close_window_with_dismissed_beforeunload_prompt(self):
63-
new_tab = self.open_tab()
64-
self.marionette.switch_to_window(new_tab)
65-
66-
self.marionette.navigate(
67-
inline(
68-
"""
69-
<input type="text">
70-
<script>
71-
window.addEventListener("beforeunload", function (event) {
72-
event.preventDefault();
73-
});
74-
</script>
75-
"""
76-
)
77-
)
78-
79-
self.marionette.find_element(By.TAG_NAME, "input").send_keys("foo")
80-
self.marionette.close()
81-
8262
def test_close_window_for_browser_window_with_single_tab(self):
8363
new_tab = self.open_window()
8464
self.marionette.switch_to_window(new_tab)

testing/profiles/web-platform/user.js

-2
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@ user_pref("browser.safebrowsing.downloads.enabled", false);
4747
user_pref("browser.safebrowsing.malware.enabled", false);
4848
user_pref("browser.safebrowsing.phishing.enabled", false);
4949
user_pref("browser.safebrowsing.update.enabled", false);
50-
// Automatically unload beforeunload alerts
51-
user_pref("dom.disable_beforeunload", true);
5250
// Disable high DPI
5351
user_pref("layout.css.devPixelsPerPx", "1.0");
5452
// Enable the parallel styling code.

testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/firefox_android.py

+4
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,10 @@ def _set_required_prefs(self, profile):
161161
"dom.send_after_paint_to_content": True,
162162
})
163163

164+
if self.package_name == "org.mozilla.geckoview.test_runner":
165+
# Bug 1879324: The TestRunner doesn't support "beforeunload" prompts yet
166+
profile.set_preferences({"dom.disable_beforeunload": True})
167+
164168
if self.test_type == "reftest":
165169
self.logger.info("Setting android reftest preferences")
166170
profile.set_preferences({

0 commit comments

Comments
 (0)