4 "http://example.com/browser/dom/tests/browser/beforeunload_test_page.html";
7 * Adds 1 or more inert beforeunload event listeners in this browser.
8 * By default, will target the top-level content window, but callers
9 * can specify the index of a subframe to target. See prepareSubframes
10 * for an idea of how the subframes are structured.
12 * @param {<xul:browser>} browser
13 * The browser to add the beforeunload event listener in.
14 * @param {int} howMany
15 * How many beforeunload event listeners to add. Note that these
16 * beforeunload event listeners are inert and will not actually
17 * prevent the host window from navigating.
18 * @param {optional int} frameDepth
19 * The depth of the frame to add the event listener to. Defaults
20 * to 0, which is the top-level content window.
23 function addBeforeUnloadListeners(browser
, howMany
= 1, frameDepth
= 0) {
24 return controlFrameAt(browser
, frameDepth
, {
25 name
: "AddBeforeUnload",
31 * Adds 1 or more inert beforeunload event listeners in this browser on
32 * a particular subframe. By default, this will target the first subframe
33 * under the top-level content window, but callers can specify the index
34 * of a subframe to target. See prepareSubframes for an idea of how the
35 * subframes are structured.
37 * Note that this adds the beforeunload event listener on the "outer" window,
40 * iframe.addEventListener("beforeunload", ...);
42 * @param {<xul:browser>} browser
43 * The browser to add the beforeunload event listener in.
44 * @param {int} howMany
45 * How many beforeunload event listeners to add. Note that these
46 * beforeunload event listeners are inert and will not actually
47 * prevent the host window from navigating.
48 * @param {optional int} frameDepth
49 * The depth of the frame to add the event listener to. Defaults
50 * to 1, which is the first subframe inside the top-level content
51 * window. Setting this to 0 will throw.
54 function addOuterBeforeUnloadListeners(browser
, howMany
= 1, frameDepth
= 1) {
55 if (frameDepth
== 0) {
57 "When adding a beforeunload listener on an outer " +
58 "window, the frame you're targeting needs to be at " +
63 return controlFrameAt(browser
, frameDepth
, {
64 name
: "AddOuterBeforeUnload",
70 * Removes 1 or more inert beforeunload event listeners in this browser.
71 * This assumes that addBeforeUnloadListeners has been called previously
72 * for the target frame.
74 * By default, will target the top-level content window, but callers
75 * can specify the index of a subframe to target. See prepareSubframes
76 * for an idea of how the subframes are structured.
78 * @param {<xul:browser>} browser
79 * The browser to remove the beforeunload event listener from.
80 * @param {int} howMany
81 * How many beforeunload event listeners to remove.
82 * @param {optional int} frameDepth
83 * The depth of the frame to remove the event listener from. Defaults
84 * to 0, which is the top-level content window.
87 function removeBeforeUnloadListeners(browser
, howMany
= 1, frameDepth
= 0) {
88 return controlFrameAt(browser
, frameDepth
, {
89 name
: "RemoveBeforeUnload",
95 * Removes 1 or more inert beforeunload event listeners in this browser on
96 * a particular subframe. By default, this will target the first subframe
97 * under the top-level content window, but callers can specify the index
98 * of a subframe to target. See prepareSubframes for an idea of how the
99 * subframes are structured.
101 * Note that this removes the beforeunload event listener on the "outer" window,
104 * iframe.removeEventListener("beforeunload", ...);
106 * @param {<xul:browser>} browser
107 * The browser to remove the beforeunload event listener from.
108 * @param {int} howMany
109 * How many beforeunload event listeners to remove.
110 * @param {optional int} frameDepth
111 * The depth of the frame to remove the event listener from. Defaults
112 * to 1, which is the first subframe inside the top-level content
113 * window. Setting this to 0 will throw.
116 function removeOuterBeforeUnloadListeners(
121 if (frameDepth
== 0) {
123 "When removing a beforeunload listener from an outer " +
124 "window, the frame you're targeting needs to be at " +
129 return controlFrameAt(browser
, frameDepth
, {
130 name
: "RemoveOuterBeforeUnload",
136 * Navigates a content window to a particular URL and waits for it to
137 * finish loading that URL.
139 * By default, will target the top-level content window, but callers
140 * can specify the index of a subframe to target. See prepareSubframes
141 * for an idea of how the subframes are structured.
143 * @param {<xul:browser>} browser
144 * The browser that will have the navigation occur within it.
145 * @param {string} url
146 * The URL to send the content window to.
147 * @param {optional int} frameDepth
148 * The depth of the frame to navigate. Defaults to 0, which is
149 * the top-level content window.
152 function navigateSubframe(browser
, url
, frameDepth
= 0) {
153 let navigatePromise
= controlFrameAt(browser
, frameDepth
, {
157 let subframeLoad
= BrowserTestUtils
.browserLoaded(
162 return Promise
.all([navigatePromise
, subframeLoad
]);
166 * Removes the <iframe> from a content window pointed at PAGE_URL.
168 * By default, will target the top-level content window, but callers
169 * can specify the index of a subframe to target. See prepareSubframes
170 * for an idea of how the subframes are structured.
172 * @param {<xul:browser>} browser
173 * The browser that will have removal occur within it.
174 * @param {optional int} frameDepth
175 * The depth of the frame that will have the removal occur within
176 * it. Defaults to 0, which is the top-level content window, meaning
177 * that the first subframe will be removed.
180 function removeSubframeFrom(browser
, frameDepth
= 0) {
181 return controlFrameAt(browser
, frameDepth
, {
182 name
: "RemoveSubframe",
187 * Sends a command to a frame pointed at PAGE_URL. There are utility
188 * functions defined in this file that call this function. You should
191 * @param {<xul:browser>} browser
192 * The browser to send the command to.
193 * @param {int} frameDepth
194 * The depth of the frame that we'll send the command to. 0 means
195 * sending it to the top-level content window.
196 * @param {object} command
197 * An object with the following structure:
201 * <arbitrary arguments to send with the command>
204 * Here are the commands that can be sent:
208 * How many beforeunload event listeners to add.
210 * AddOuterBeforeUnload
212 * How many beforeunload event listeners to add to
213 * the iframe in the document at this depth.
217 * How many beforeunload event listeners to remove.
219 * RemoveOuterBeforeUnload
221 * How many beforeunload event listeners to remove from
222 * the iframe in the document at this depth.
226 * The URL to send the frame to.
232 function controlFrameAt(browser
, frameDepth
, command
) {
233 return SpecialPowers
.spawn(
235 [{ frameDepth
, command
}],
236 async
function (args
) {
237 const { TestUtils
} = ChromeUtils
.importESModule(
238 "resource://testing-common/TestUtils.sys.mjs"
241 let { command
: contentCommand
, frameDepth
: contentFrameDepth
} = args
;
243 let targetContent
= content
;
244 let targetSubframe
= content
.document
.getElementById("subframe");
246 // We want to not only find the frame that maps to the
247 // target frame depth that we've been given, but we also want
248 // to count the total depth so that if a middle frame is removed
249 // or navigated, then we know how many outer-window-destroyed
250 // observer notifications to expect.
251 let currentContent
= targetContent
;
252 let currentSubframe
= targetSubframe
;
257 currentContent
= currentSubframe
.contentWindow
;
258 currentSubframe
= currentContent
.document
.getElementById("subframe");
260 if (depth
== contentFrameDepth
) {
261 targetContent
= currentContent
;
262 targetSubframe
= currentSubframe
;
264 } while (currentSubframe
);
266 switch (contentCommand
.name
) {
267 case "AddBeforeUnload": {
268 let BeforeUnloader
= targetContent
.wrappedJSObject
.BeforeUnloader
;
269 Assert
.ok(BeforeUnloader
, "Found BeforeUnloader in the test page.");
270 BeforeUnloader
.pushInner(contentCommand
.howMany
);
273 case "AddOuterBeforeUnload": {
274 let BeforeUnloader
= targetContent
.wrappedJSObject
.BeforeUnloader
;
275 Assert
.ok(BeforeUnloader
, "Found BeforeUnloader in the test page.");
276 BeforeUnloader
.pushOuter(contentCommand
.howMany
);
279 case "RemoveBeforeUnload": {
280 let BeforeUnloader
= targetContent
.wrappedJSObject
.BeforeUnloader
;
281 Assert
.ok(BeforeUnloader
, "Found BeforeUnloader in the test page.");
282 BeforeUnloader
.popInner(contentCommand
.howMany
);
285 case "RemoveOuterBeforeUnload": {
286 let BeforeUnloader
= targetContent
.wrappedJSObject
.BeforeUnloader
;
287 Assert
.ok(BeforeUnloader
, "Found BeforeUnloader in the test page.");
288 BeforeUnloader
.popOuter(contentCommand
.howMany
);
292 // How many frames are going to be destroyed when we do this? We
293 // need to wait for that many window destroyed notifications.
294 targetContent
.location
= contentCommand
.url
;
296 let destroyedOuterWindows
= depth
- contentFrameDepth
;
297 if (destroyedOuterWindows
) {
298 await TestUtils
.topicObserved("outer-window-destroyed", () => {
299 destroyedOuterWindows
--;
300 return !destroyedOuterWindows
;
305 case "RemoveSubframe": {
306 let subframe
= targetContent
.document
.getElementById("subframe");
309 "Found subframe at frame depth of " + contentFrameDepth
313 let destroyedOuterWindows
= depth
- contentFrameDepth
;
314 if (destroyedOuterWindows
) {
315 await TestUtils
.topicObserved("outer-window-destroyed", () => {
316 destroyedOuterWindows
--;
317 return !destroyedOuterWindows
;
324 ).catch(console
.error
);
328 * Sets up a structure where a page at PAGE_URL will host an
329 * <iframe> also pointed at PAGE_URL, and does this repeatedly
330 * until we've achieved the desired frame depth. Note that this
331 * will cause the top-level browser to reload, and wipe out any
332 * previous changes to the DOM under it.
334 * @param {<xul:browser>} browser
335 * The browser in which we'll load our structure at the
337 * @param {Array<object>} options
338 * Set-up options for each subframe. The following properties
341 * {string} sandboxAttributes
342 * The value to set the sandbox attribute to. If null, no sandbox
343 * attribute will be set (and any pre-existing sandbox attributes)
344 * on the <iframe> will be removed.
346 * The number of entries on the options Array corresponds to how many
347 * subframes are under the top-level content window.
351 * yield prepareSubframes(browser, [
352 * { sandboxAttributes: null },
353 * { sandboxAttributes: "allow-modals" },
356 * This would create the following structure:
358 * <top-level content window at PAGE_URL>
360 * |--> <iframe at PAGE_URL, no sandbox attributes>
362 * |--> <iframe at PAGE_URL, sandbox="allow-modals">
366 async
function prepareSubframes(browser
, options
) {
368 await BrowserTestUtils
.browserLoaded(browser
);
370 await SpecialPowers
.spawn(
372 [{ options
, PAGE_URL
}],
373 async
function (args
) {
374 let { options
: allSubframeOptions
, PAGE_URL
: contentPageURL
} = args
;
375 function loadBeforeUnloadHelper(doc
, url
, subframeOptions
) {
376 let subframe
= doc
.getElementById("subframe");
378 if (subframeOptions
.sandboxAttributes
=== null) {
379 subframe
.removeAttribute("sandbox");
381 subframe
.setAttribute("sandbox", subframeOptions
.sandboxAttributes
);
383 doc
.body
.appendChild(subframe
);
384 subframe
.contentWindow
.location
= url
;
385 return ContentTaskUtils
.waitForEvent(subframe
, "load").then(() => {
386 return subframe
.contentDocument
;
390 let currentDoc
= content
.document
;
392 for (let subframeOptions
of allSubframeOptions
) {
393 // Circumvent recursive load checks.
394 let url
= new URL(contentPageURL
);
395 url
.search
= `depth=${depth++}`;
396 currentDoc
= await
loadBeforeUnloadHelper(
407 * Ensures that a browser's nsIRemoteTab hasBeforeUnload attribute
408 * is set to the expected value.
410 * @param {<xul:browser>} browser
411 * The browser whose nsIRemoteTab we will check.
412 * @param {bool} expected
413 * True if hasBeforeUnload is expected to be true.
415 function assertHasBeforeUnload(browser
, expected
) {
416 Assert
.equal(browser
.hasBeforeUnload
, expected
);
420 * Tests that the MozBrowser hasBeforeUnload property works under
421 * a number of different scenarios on inner windows. At a high-level,
422 * we test that hasBeforeUnload works properly during page / iframe
423 * navigation, or when an <iframe> with a beforeunload listener on its
424 * inner window is removed from the DOM.
426 add_task(async
function test_inner_window_scenarios() {
427 // Turn this off because the test expects the page to be not bfcached.
428 await SpecialPowers
.pushPrefEnv({
430 ["docshell.shistory.bfcache.ship_allow_beforeunload_listeners", false],
433 await BrowserTestUtils
.withNewTab(
438 async
function (browser
) {
440 browser
.isRemoteBrowser
,
441 "This test only makes sense with out of process browsers."
443 assertHasBeforeUnload(browser
, false);
445 // Test the simple case on the top-level window by adding a single
446 // beforeunload event listener on the inner window and then removing
448 await
addBeforeUnloadListeners(browser
);
449 assertHasBeforeUnload(browser
, true);
450 await
removeBeforeUnloadListeners(browser
);
451 assertHasBeforeUnload(browser
, false);
453 // Now let's add several beforeunload listeners, and
454 // ensure that we only set hasBeforeUnload to false once
455 // the last listener is removed.
456 await
addBeforeUnloadListeners(browser
, 3);
457 assertHasBeforeUnload(browser
, true);
458 await
removeBeforeUnloadListeners(browser
); // 2 left...
459 assertHasBeforeUnload(browser
, true);
460 await
removeBeforeUnloadListeners(browser
); // 1 left...
461 assertHasBeforeUnload(browser
, true);
462 await
removeBeforeUnloadListeners(browser
); // None left!
464 assertHasBeforeUnload(browser
, false);
466 // Now let's have the top-level content window navigate
467 // away with a beforeunload listener set, and ensure
468 // that we clear the hasBeforeUnload value.
469 await
addBeforeUnloadListeners(browser
, 5);
470 await
navigateSubframe(browser
, "http://example.com");
471 assertHasBeforeUnload(browser
, false);
473 // Now send the page back to the test page for
474 // the next few tests.
475 BrowserTestUtils
.startLoadingURIString(browser
, PAGE_URL
);
476 await BrowserTestUtils
.browserLoaded(browser
);
478 // We want to test hasBeforeUnload works properly with
479 // beforeunload event listeners in <iframe> elements too.
480 // We prepare a structure like this with 3 content windows
483 // <top-level content window at PAGE_URL> (TOP)
485 // |--> <iframe at PAGE_URL> (MIDDLE)
487 // |--> <iframe at PAGE_URL> (BOTTOM)
489 await
prepareSubframes(browser
, [
490 { sandboxAttributes
: null },
491 { sandboxAttributes
: null },
493 // These constants are just to make it easier to know which
494 // frame we're referring to without having to remember the
500 // We should initially start with hasBeforeUnload set to false.
501 assertHasBeforeUnload(browser
, false);
503 // Tests that if there are beforeunload event listeners on
504 // all levels of our window structure, that we only set
505 // hasBeforeUnload to false once the last beforeunload
506 // listener has been unset.
507 await
addBeforeUnloadListeners(browser
, 2, MIDDLE
);
508 assertHasBeforeUnload(browser
, true);
509 await
addBeforeUnloadListeners(browser
, 1, TOP
);
510 assertHasBeforeUnload(browser
, true);
511 await
addBeforeUnloadListeners(browser
, 5, BOTTOM
);
512 assertHasBeforeUnload(browser
, true);
514 await
removeBeforeUnloadListeners(browser
, 1, TOP
);
515 assertHasBeforeUnload(browser
, true);
516 await
removeBeforeUnloadListeners(browser
, 5, BOTTOM
);
517 assertHasBeforeUnload(browser
, true);
518 await
removeBeforeUnloadListeners(browser
, 2, MIDDLE
);
519 assertHasBeforeUnload(browser
, false);
521 // Tests that if a beforeunload event listener is set on
522 // an iframe that navigates away to a page without a
523 // beforeunload listener, that hasBeforeUnload is set
525 await
addBeforeUnloadListeners(browser
, 5, BOTTOM
);
526 assertHasBeforeUnload(browser
, true);
528 await
navigateSubframe(browser
, "http://example.com", BOTTOM
);
529 assertHasBeforeUnload(browser
, false);
531 // Reset our window structure now.
532 await
prepareSubframes(browser
, [
533 { sandboxAttributes
: null },
534 { sandboxAttributes
: null },
537 // This time, add beforeunload event listeners to both the
538 // MIDDLE and BOTTOM frame, and then navigate the MIDDLE
539 // away. This should set hasBeforeUnload to false.
540 await
addBeforeUnloadListeners(browser
, 3, MIDDLE
);
541 await
addBeforeUnloadListeners(browser
, 1, BOTTOM
);
542 assertHasBeforeUnload(browser
, true);
543 await
navigateSubframe(browser
, "http://example.com", MIDDLE
);
544 assertHasBeforeUnload(browser
, false);
546 // Tests that if the MIDDLE and BOTTOM frames have beforeunload
547 // event listeners, and if we remove the BOTTOM <iframe> and the
548 // MIDDLE <iframe>, that hasBeforeUnload is set to false.
549 await
prepareSubframes(browser
, [
550 { sandboxAttributes
: null },
551 { sandboxAttributes
: null },
553 await
addBeforeUnloadListeners(browser
, 3, MIDDLE
);
554 await
addBeforeUnloadListeners(browser
, 1, BOTTOM
);
555 assertHasBeforeUnload(browser
, true);
556 await
removeSubframeFrom(browser
, MIDDLE
);
557 assertHasBeforeUnload(browser
, true);
558 await
removeSubframeFrom(browser
, TOP
);
559 assertHasBeforeUnload(browser
, false);
561 // Tests that if the MIDDLE and BOTTOM frames have beforeunload
562 // event listeners, and if we remove just the MIDDLE <iframe>, that
563 // hasBeforeUnload is set to false.
564 await
prepareSubframes(browser
, [
565 { sandboxAttributes
: null },
566 { sandboxAttributes
: null },
568 await
addBeforeUnloadListeners(browser
, 3, MIDDLE
);
569 await
addBeforeUnloadListeners(browser
, 1, BOTTOM
);
570 assertHasBeforeUnload(browser
, true);
571 await
removeSubframeFrom(browser
, TOP
);
572 assertHasBeforeUnload(browser
, false);
574 // Test that two sandboxed iframes, _without_ the allow-modals
575 // permission, do not result in the hasBeforeUnload attribute
576 // being set to true when beforeunload event listeners are added.
577 await
prepareSubframes(browser
, [
578 { sandboxAttributes
: "allow-scripts" },
579 { sandboxAttributes
: "allow-scripts" },
582 await
addBeforeUnloadListeners(browser
, 3, MIDDLE
);
583 await
addBeforeUnloadListeners(browser
, 1, BOTTOM
);
584 assertHasBeforeUnload(browser
, false);
586 await
removeBeforeUnloadListeners(browser
, 3, MIDDLE
);
587 await
removeBeforeUnloadListeners(browser
, 1, BOTTOM
);
588 assertHasBeforeUnload(browser
, false);
590 // Test that two sandboxed iframes, both with the allow-modals
591 // permission, cause the hasBeforeUnload attribute to be set
592 // to true when beforeunload event listeners are added.
593 await
prepareSubframes(browser
, [
594 { sandboxAttributes
: "allow-scripts allow-modals" },
595 { sandboxAttributes
: "allow-scripts allow-modals" },
598 await
addBeforeUnloadListeners(browser
, 3, MIDDLE
);
599 await
addBeforeUnloadListeners(browser
, 1, BOTTOM
);
600 assertHasBeforeUnload(browser
, true);
602 await
removeBeforeUnloadListeners(browser
, 1, BOTTOM
);
603 assertHasBeforeUnload(browser
, true);
604 await
removeBeforeUnloadListeners(browser
, 3, MIDDLE
);
605 assertHasBeforeUnload(browser
, false);
611 * Tests that the nsIRemoteTab hasBeforeUnload attribute works under
612 * a number of different scenarios on outer windows. Very similar to
613 * the above set of tests, except that we add the beforeunload listeners
614 * to the iframe DOM nodes instead of the inner windows.
616 add_task(async
function test_outer_window_scenarios() {
617 // Turn this off because the test expects the page to be not bfcached.
618 await SpecialPowers
.pushPrefEnv({
620 ["docshell.shistory.bfcache.ship_allow_beforeunload_listeners", false],
623 await BrowserTestUtils
.withNewTab(
628 async
function (browser
) {
630 browser
.isRemoteBrowser
,
631 "This test only makes sense with out of process browsers."
633 assertHasBeforeUnload(browser
, false);
635 // We want to test hasBeforeUnload works properly with
636 // beforeunload event listeners in <iframe> elements.
637 // We prepare a structure like this with 3 content windows
640 // <top-level content window at PAGE_URL> (TOP)
642 // |--> <iframe at PAGE_URL> (MIDDLE)
644 // |--> <iframe at PAGE_URL> (BOTTOM)
646 await
prepareSubframes(browser
, [
647 { sandboxAttributes
: null },
648 { sandboxAttributes
: null },
651 // These constants are just to make it easier to know which
652 // frame we're referring to without having to remember the
658 // Test the simple case on the top-level window by adding a single
659 // beforeunload event listener on the outer window of the iframe
660 // in the TOP document.
661 await
addOuterBeforeUnloadListeners(browser
);
662 assertHasBeforeUnload(browser
, true);
664 await
removeOuterBeforeUnloadListeners(browser
);
665 assertHasBeforeUnload(browser
, false);
667 // Now let's add several beforeunload listeners, and
668 // ensure that we only set hasBeforeUnload to false once
669 // the last listener is removed.
670 await
addOuterBeforeUnloadListeners(browser
, 3);
671 assertHasBeforeUnload(browser
, true);
672 await
removeOuterBeforeUnloadListeners(browser
); // 2 left...
673 assertHasBeforeUnload(browser
, true);
674 await
removeOuterBeforeUnloadListeners(browser
); // 1 left...
675 assertHasBeforeUnload(browser
, true);
676 await
removeOuterBeforeUnloadListeners(browser
); // None left!
678 assertHasBeforeUnload(browser
, false);
680 // Now let's have the top-level content window navigate away
681 // with a beforeunload listener set on the outer window of the
682 // iframe inside it, and ensure that we clear the hasBeforeUnload
684 await
addOuterBeforeUnloadListeners(browser
, 5);
685 await
navigateSubframe(browser
, "http://example.com", TOP
);
686 assertHasBeforeUnload(browser
, false);
688 // Now send the page back to the test page for
689 // the next few tests.
690 BrowserTestUtils
.startLoadingURIString(browser
, PAGE_URL
);
691 await BrowserTestUtils
.browserLoaded(browser
);
693 // We should initially start with hasBeforeUnload set to false.
694 assertHasBeforeUnload(browser
, false);
696 await
prepareSubframes(browser
, [
697 { sandboxAttributes
: null },
698 { sandboxAttributes
: null },
701 // Tests that if there are beforeunload event listeners on
702 // all levels of our window structure, that we only set
703 // hasBeforeUnload to false once the last beforeunload
704 // listener has been unset.
705 await
addOuterBeforeUnloadListeners(browser
, 3, MIDDLE
);
706 assertHasBeforeUnload(browser
, true);
707 await
addOuterBeforeUnloadListeners(browser
, 7, BOTTOM
);
708 assertHasBeforeUnload(browser
, true);
710 await
removeOuterBeforeUnloadListeners(browser
, 7, BOTTOM
);
711 assertHasBeforeUnload(browser
, true);
712 await
removeOuterBeforeUnloadListeners(browser
, 3, MIDDLE
);
713 assertHasBeforeUnload(browser
, false);
715 // Tests that if a beforeunload event listener is set on
716 // an iframe that navigates away to a page without a
717 // beforeunload listener, that hasBeforeUnload is set
718 // to false. We're setting the event listener on the
719 // outer window on the <iframe> in the MIDDLE, which
720 // itself contains the BOTTOM frame it our structure.
721 await
addOuterBeforeUnloadListeners(browser
, 5, BOTTOM
);
722 assertHasBeforeUnload(browser
, true);
724 // Now navigate that BOTTOM frame.
725 await
navigateSubframe(browser
, "http://example.com", BOTTOM
);
726 assertHasBeforeUnload(browser
, false);
728 // Reset our window structure now.
729 await
prepareSubframes(browser
, [
730 { sandboxAttributes
: null },
731 { sandboxAttributes
: null },
734 // This time, add beforeunload event listeners to the outer
735 // windows for MIDDLE and BOTTOM. Then navigate the MIDDLE
736 // frame. This should set hasBeforeUnload to false.
737 await
addOuterBeforeUnloadListeners(browser
, 3, MIDDLE
);
738 await
addOuterBeforeUnloadListeners(browser
, 1, BOTTOM
);
739 assertHasBeforeUnload(browser
, true);
740 await
navigateSubframe(browser
, "http://example.com", MIDDLE
);
741 assertHasBeforeUnload(browser
, false);
743 // Adds beforeunload event listeners to the outer windows of
744 // MIDDLE and BOTOTM, and then removes those iframes. Removing
745 // both iframes should set hasBeforeUnload to false.
746 await
prepareSubframes(browser
, [
747 { sandboxAttributes
: null },
748 { sandboxAttributes
: null },
750 await
addOuterBeforeUnloadListeners(browser
, 3, MIDDLE
);
751 await
addOuterBeforeUnloadListeners(browser
, 1, BOTTOM
);
752 assertHasBeforeUnload(browser
, true);
753 await
removeSubframeFrom(browser
, BOTTOM
);
754 assertHasBeforeUnload(browser
, true);
755 await
removeSubframeFrom(browser
, MIDDLE
);
756 assertHasBeforeUnload(browser
, false);
758 // Adds beforeunload event listeners to the outer windows of MIDDLE
759 // and BOTTOM, and then removes just the MIDDLE iframe (which will
760 // take the bottom one with it). This should set hasBeforeUnload to
762 await
prepareSubframes(browser
, [
763 { sandboxAttributes
: null },
764 { sandboxAttributes
: null },
766 await
addOuterBeforeUnloadListeners(browser
, 3, MIDDLE
);
767 await
addOuterBeforeUnloadListeners(browser
, 1, BOTTOM
);
768 assertHasBeforeUnload(browser
, true);
769 await
removeSubframeFrom(browser
, TOP
);
770 assertHasBeforeUnload(browser
, false);
772 // Test that two sandboxed iframes, _without_ the allow-modals
773 // permission, do not result in the hasBeforeUnload attribute
774 // being set to true when beforeunload event listeners are added
775 // to the outer windows. Note that this requires the
776 // allow-same-origin permission, otherwise a cross-origin
777 // security exception is thrown.
778 await
prepareSubframes(browser
, [
779 { sandboxAttributes
: "allow-same-origin allow-scripts" },
780 { sandboxAttributes
: "allow-same-origin allow-scripts" },
783 await
addOuterBeforeUnloadListeners(browser
, 3, MIDDLE
);
784 await
addOuterBeforeUnloadListeners(browser
, 1, BOTTOM
);
785 assertHasBeforeUnload(browser
, false);
787 await
removeOuterBeforeUnloadListeners(browser
, 3, MIDDLE
);
788 await
removeOuterBeforeUnloadListeners(browser
, 1, BOTTOM
);
789 assertHasBeforeUnload(browser
, false);
791 // Test that two sandboxed iframes, both with the allow-modals
792 // permission, cause the hasBeforeUnload attribute to be set
793 // to true when beforeunload event listeners are added. Note
794 // that this requires the allow-same-origin permission,
795 // otherwise a cross-origin security exception is thrown.
796 await
prepareSubframes(browser
, [
797 { sandboxAttributes
: "allow-same-origin allow-scripts allow-modals" },
798 { sandboxAttributes
: "allow-same-origin allow-scripts allow-modals" },
801 await
addOuterBeforeUnloadListeners(browser
, 3, MIDDLE
);
802 await
addOuterBeforeUnloadListeners(browser
, 1, BOTTOM
);
803 assertHasBeforeUnload(browser
, true);
805 await
removeOuterBeforeUnloadListeners(browser
, 1, BOTTOM
);
806 assertHasBeforeUnload(browser
, true);
807 await
removeOuterBeforeUnloadListeners(browser
, 3, MIDDLE
);
808 assertHasBeforeUnload(browser
, false);
814 * Tests hasBeforeUnload behaviour when beforeunload event listeners
815 * are added on both inner and outer windows.
817 add_task(async
function test_mixed_inner_and_outer_window_scenarios() {
818 // Turn this off because the test expects the page to be not bfcached.
819 await SpecialPowers
.pushPrefEnv({
821 ["docshell.shistory.bfcache.ship_allow_beforeunload_listeners", false],
824 await BrowserTestUtils
.withNewTab(
829 async
function (browser
) {
831 browser
.isRemoteBrowser
,
832 "This test only makes sense with out of process browsers."
834 assertHasBeforeUnload(browser
, false);
836 // We want to test hasBeforeUnload works properly with
837 // beforeunload event listeners in <iframe> elements.
838 // We prepare a structure like this with 3 content windows
841 // <top-level content window at PAGE_URL> (TOP)
843 // |--> <iframe at PAGE_URL> (MIDDLE)
845 // |--> <iframe at PAGE_URL> (BOTTOM)
847 await
prepareSubframes(browser
, [
848 { sandboxAttributes
: null },
849 { sandboxAttributes
: null },
852 // These constants are just to make it easier to know which
853 // frame we're referring to without having to remember the
859 await
addBeforeUnloadListeners(browser
, 1, TOP
);
860 assertHasBeforeUnload(browser
, true);
861 await
addBeforeUnloadListeners(browser
, 2, MIDDLE
);
862 assertHasBeforeUnload(browser
, true);
863 await
addBeforeUnloadListeners(browser
, 5, BOTTOM
);
864 assertHasBeforeUnload(browser
, true);
866 await
addOuterBeforeUnloadListeners(browser
, 3, MIDDLE
);
867 assertHasBeforeUnload(browser
, true);
868 await
addOuterBeforeUnloadListeners(browser
, 7, BOTTOM
);
869 assertHasBeforeUnload(browser
, true);
871 await
removeBeforeUnloadListeners(browser
, 5, BOTTOM
);
872 assertHasBeforeUnload(browser
, true);
874 await
removeBeforeUnloadListeners(browser
, 2, MIDDLE
);
875 assertHasBeforeUnload(browser
, true);
877 await
removeOuterBeforeUnloadListeners(browser
, 3, MIDDLE
);
878 assertHasBeforeUnload(browser
, true);
880 await
removeBeforeUnloadListeners(browser
, 1, TOP
);
881 assertHasBeforeUnload(browser
, true);
883 await
removeOuterBeforeUnloadListeners(browser
, 7, BOTTOM
);
884 assertHasBeforeUnload(browser
, false);