summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents')
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-1.html7
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-2.html9
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-3.html4
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003.html22
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-1.html7
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-2.html9
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-3.html4
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004.html22
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/005.html16
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/006.html17
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/007.html16
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/008.html17
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/009.html22
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/010.html17
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/011.html21
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/012.html20
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/013.html20
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/014.html21
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/015.html20
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load-1.html32
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load-2.html32
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load.html30
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/about-srcdoc-navigation-blocked.window.js52
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit-longfragment.html29
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit-withpath.html35
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit.html29
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-jsurl-form-submit.html29
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/child-navigates-parent-cross-origin.window.js90
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/child-navigates-parent-same-origin.window.js24
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/cross-origin-top-navigation-with-user-activation-in-parent.window.js8
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/cross-origin-top-navigation-without-user-activation.window.js8
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty-iframe-load-event.html39
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty_fragment.html20
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty_fragment_iframe.html11
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/failure-check-sequence.https.html76
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-nosrc.html178
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204-fragment.html144
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204-pushState-replaceState.html66
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204.html178
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-navigate-immediately.html124
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-wait-for-load.html134
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/initial-content-replacement.html86
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-event-iframe-element.html64
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-iframe-contentWindow.html44
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-window-open.html35
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/resources/code-injector.html8
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/resources/helpers.js121
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204-fragment.html63
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204-pushState-replaceState.html61
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204.html81
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-aboutblank.html81
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-history-length.html31
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-nourl.html78
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/javascript-url-abort-return-value-string.tentative.html28
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/javascript-url-abort-return-value-undefined.tentative.html25
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/support/iframe-and-links.html18
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/support/set-child-loaded.html5
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-global-scope.html16
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-load-as-html.xhtml37
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-no-beforeunload.window.js80
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-query-fragment-components.html28
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-referrer.window.js38
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-return-value-handling-dynamic.html58
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-return-value-handling.html36
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-failure.sub.html56
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-multi-globals.sub.html66
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-same-origin-domain.sub.html26
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-task-queuing.html58
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location-assign.html27
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location-href.html27
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location.html27
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-window-open.html28
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/entry/entry.html4
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/entry/target.html2
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/incumbent/empty.html0
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/incumbent/target.html2
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/relevant/empty.html0
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/relevant/target.html2
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/resources/context-helper.js34
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/resources/target.js11
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url-with-fragment-fire-load-event.html31
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url-with-fragment.html27
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url.html22
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-to-unparseable-url.html58
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-cross-origin.sub.window.js15
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-data-url.window.js15
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit-1.html23
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit-2.html7
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit.html24
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-javascript-url.window.js15
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment-1.html9
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment-2.html9
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment.html29
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin.window.js15
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/plugin-document.historical.html22
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-about.window.js34
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-data.html75
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-unparseable-url.html71
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/README.md1
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/navigate.window.js23
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/multiple.asis6
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refresh.py4
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refreshed.txt1
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/subresource.any.js6
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click-during-load.html32
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click-during-pageshow.html32
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click.html30
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click-during-load.html36
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click-during-pageshow.html36
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click.html34
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit-during-load.html41
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit-during-pageshow.html41
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit.html39
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click-during-load.html45
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click-during-pageshow.html45
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click.html43
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-cross-frame-crossorigin.sub.html42
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-cross-frame.html40
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-during-load.html41
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-during-pageshow.html41
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-popup-crossorigin.sub.html39
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-popup.html37
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit.html39
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate-during-load.html28
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate-during-pageshow.html29
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate.html22
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src-during-load.html31
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src-during-pageshow.html31
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src.html39
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-during-load.html31
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-during-pageshow.html31
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-user-click.html34
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign.html25
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-during-load.html76
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-during-pageshow.html77
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-user-click.html82
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-user-mouseup.html89
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter.html58
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/code-injector.html9
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/helpers.js89
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/message-opener.html8
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/slow-code-injector.html16
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/slow-message-source-with-history-and-location.html16
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup-during-load.html67
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup-during-pageshow.html67
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup.html49
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self-during-load.html29
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self-during-pageshow.html29
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self.html23
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/blank.html1
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-cross-origin-inner.html20
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-destination.html4
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-location-initial.html3
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-location-inner.html5
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-submit-initial.html3
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-submit-inner.html6
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/click.html4
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/document-domain-set-to-site.sub.html7
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/form.html5
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/href.html5
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/message-opener.html8
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/message-parent.html3
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/multi-globals-subframe-1.sub.html10
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/multi-globals-subframe-2.sub.html8
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/no-cache-single-redirect.py21
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/page-that-post-message-to-opener.html6
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/page-with-top-navigating-iframe.html24
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/portable-document-format-sample-valid.pdfbin0 -> 58927 bytes
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/portable-document-format-sample-valid.pdf.headers1
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/redirect.py4
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/top-navigating-page.html15
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/wait-for-messages.js15
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/xhtml-and-non-utf-8.xhtml9
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-parent-then-fragment.html36
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-parent.html18
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-src.html18
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function.html19
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-src-about-blank.html20
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/dummy.html3
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/location-set.html8
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/set-parent-src.html8
-rw-r--r--testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/top-level-data-url.window.js20
182 files changed, 5848 insertions, 0 deletions
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-1.html
new file mode 100644
index 0000000000..4d2229eb51
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-1.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<script>
+onload = function() {
+ parent.postMessage("003-1", "*");
+ setTimeout(function() {location = "003-2.html";}, 100);
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-2.html
new file mode 100644
index 0000000000..827a069479
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-2.html
@@ -0,0 +1,9 @@
+<!doctype html>
+003-2
+<script>
+onload = function() {
+ parent.postMessage("003-2", "*")
+ setTimeout(function() {history.go(-1)})
+}
+onunload = function() {location = "003-3.html"}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-3.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-3.html
new file mode 100644
index 0000000000..8b26c896fd
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003-3.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<script>
+parent.postMessage("003-3", "*");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003.html
new file mode 100644
index 0000000000..f437150960
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/003.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>Navigation from unload whilst traversing history</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe src="003-1.html"></iframe>
+<script>
+var t = async_test();
+
+var pages = [];
+var iframe = document.getElementsByTagName("iframe")[0];
+
+
+onmessage = t.step_func(function(e) {
+ pages.push(e.data);
+ if(pages.length == 3) {
+ assert_array_equals(pages, ["003-1", "003-2", "003-1"]);
+ t.done();
+ iframe.parentNode.removeChild(iframe);
+ }
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-1.html
new file mode 100644
index 0000000000..02f916fd9c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-1.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<script>
+onload = function() {
+ parent.postMessage("004-1", "*");
+ setTimeout(function() {location = location.href.replace("http://", "http://www.").replace("004-1.html", "004-2.html");}, 100);
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-2.html
new file mode 100644
index 0000000000..f2ef83ee1a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-2.html
@@ -0,0 +1,9 @@
+<!doctype html>
+003-2
+<script>
+onload = function() {
+ parent.postMessage("004-2", "*")
+ setTimeout(function() {history.go(-1)})
+}
+onunload = function() {location = "004-3.html"}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-3.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-3.html
new file mode 100644
index 0000000000..c98711ae98
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004-3.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<script>
+parent.postMessage("004-3", "*");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004.html
new file mode 100644
index 0000000000..dddde4918b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/004.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>Navigation from unload whilst traversing cross-origin history</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe src="004-1.html"></iframe>
+<script>
+var t = async_test();
+
+var pages = [];
+var iframe = document.getElementsByTagName("iframe")[0];
+
+
+onmessage = t.step_func(function(e) {
+ pages.push(e.data);
+ if(pages.length == 3) {
+ assert_array_equals(pages, ["004-1", "004-2", "004-1"]);
+ t.done();
+ iframe.parentNode.removeChild(iframe);
+ }
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/005.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/005.html
new file mode 100644
index 0000000000..a87e6e9b3d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/005.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>Link with onclick navigation and href navigation </title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe id="test" name="test"></iframe>
+<a target="test" onclick="document.getElementById('test').contentWindow.location='resources/click.html'" href="resources/href.html">Test</a>
+<script>
+var t = async_test();
+t.step(function() {document.links[0].click()});
+onmessage = t.step_func(
+ function(e) {
+ assert_equals(e.data, "href");
+ t.done();
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/006.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/006.html
new file mode 100644
index 0000000000..17b3123faa
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/006.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>Link with onclick form submit and href navigation </title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe name="test"></iframe>
+<form target="test" action="resources/click.html"></form>
+<a target="test" onclick="document.forms[0].submit()" href="resources/href.html">Test</a>
+<script>
+var t = async_test();
+t.step(function() {document.links[0].click()});
+onmessage = t.step_func(
+ function(e) {
+ assert_equals(e.data, "href");
+ t.done();
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/007.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/007.html
new file mode 100644
index 0000000000..dbb23ee644
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/007.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>Link with onclick javascript url and href navigation </title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe id="test" name="test"></iframe>
+<a target="test" onclick="document.getElementById('test').contentWindow.location = 'javascript:\'abc<script>parent.postMessage(&quot;click&quot;, &quot;*&quot;)</script>\'';" href="resources/href.html">Test</a>
+<script>
+var t = async_test();
+t.step(function() {document.getElementsByTagName("a")[0].click()});
+onmessage = t.step_func(
+ function(e) {
+ assert_equals(e.data, "href");
+ t.done();
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/008.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/008.html
new file mode 100644
index 0000000000..3201d0679d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/008.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>Link with onclick form submit to javascript url and href navigation </title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe id="test" name="test"></iframe>
+<form target="test" action="javascript:'<script>parent.postMessage(&quot;click&quot;, &quot;*&quot;)</script>'"></form>
+<a target="test" onclick="document.forms[0].submit()" href="resources/href.html">Test</a>
+<script>
+var t = async_test();
+t.step(function() {document.getElementsByTagName("a")[0].click()});
+onmessage = t.step_func(
+ function(e) {
+ assert_equals(e.data, "href");
+ t.done();
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/009.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/009.html
new file mode 100644
index 0000000000..d963bed2bf
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/009.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>Link with onclick form submit to javascript url with document.write and href navigation </title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe id="test" name="test"></iframe>
+<form target="test" action="javascript:(function() {document.write('<script>parent.postMessage(&quot;write&quot;, &quot;*&quot;)</script>'); return '<script>parent.postMessage(&quot;click&quot;, &quot;*&quot;)</script>'})()"></form>
+<a target="test" onclick="document.forms[0].submit()" href="resources/href.html">Test</a>
+<script>
+var t = async_test();
+var events = [];
+t.step(function() {
+ document.getElementsByTagName("a")[0].click()});
+onmessage = t.step_func(
+ function(e) {
+ events.push(e.data);
+ if (events.length === 2) {
+ assert_array_equals(events, ["write", "href"]);
+ t.done();
+ }
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/010.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/010.html
new file mode 100644
index 0000000000..606ad82f48
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/010.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>Link with onclick form submit to javascript url with delayed document.write and href navigation </title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe id="test" name="test"></iframe>
+<form target="test" action="javascript:(function() {var x = new XMLHttpRequest(); x.open('GET', 'resources/blank.html?pipe=trickle(d2)', false); x.send(); document.write('<script>parent.postMessage(&quot;write&quot;, &quot;*&quot;)</script>'); return '<script>parent.postMessage(&quot;click&quot;, &quot;*&quot;)</script>'})()"></form>
+<a target="test" onclick="document.forms[0].submit()" href="resources/href.html">Test</a>
+<script>
+var t = async_test();
+onload = t.step_func(function() {document.getElementsByTagName("a")[0].click()});
+onmessage = t.step_func(
+ function(e) {
+ assert_equals(e.data, "href");
+ t.done();
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/011.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/011.html
new file mode 100644
index 0000000000..4d54267968
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/011.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<title>Link with onclick navigation to javascript url with document.write and href navigation </title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe id="test" name="test"></iframe>
+<a target="test" onclick="javascript:(function() {document.write('<script>parent.postMessage(&quot;write&quot;, &quot;*&quot;)</script>'); return '<script>parent.postMessage(&quot;click&quot;, &quot;*&quot;)</script>'})()" href="resources/href.html">Test</a>
+<script>
+var t = async_test();
+var events = [];
+t.step(function() {
+ document.getElementsByTagName("a")[0].click()});
+onmessage = t.step_func(
+ function(e) {
+ events.push(e.data);
+ if (events.length === 2) {
+ assert_array_equals(events, ["write", "href"]);
+ t.done();
+ }
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/012.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/012.html
new file mode 100644
index 0000000000..3795975d70
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/012.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>Link with onclick navigation to javascript url with delayed document.write and href navigation </title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe id="test" name="test"></iframe>
+<!-- XXX: What is this test trying to do? It's navigating the subframe, but
+ doing a write() to _this_ document, and the "javascript:" in there is
+ completely a red herring: it's a label, not a protocol. There is no
+ javascript url involved here, unlike what the title claims! -->
+<a target="test" onclick="javascript:(function() {var x = new XMLHttpRequest(); x.open('GET', 'blank.html?pipe=trickle(d2)', false); x.send(); document.write('write<script>parent.postMessage(&quot;write&quot;, &quot;*&quot;)</script>'); return '<script>parent.postMessage(&quot;click&quot;, &quot;*&quot;)</script>'})()" href="href.html">Test</a>
+<script>
+var t = async_test();
+t.step(function() {document.getElementsByTagName("a")[0].click()});
+onmessage = t.step_func(
+ function(e) {
+ assert_equals(e.data, "href");
+ t.done();
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/013.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/013.html
new file mode 100644
index 0000000000..36a4521843
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/013.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>Link with onclick navigation to javascript url with delayed document.write and href navigation </title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe id="test" name="test"></iframe>
+<a target="test" href="javascript:parent.events.push('javascript');">Test</a>
+<script>
+var t = async_test();
+var events = [];
+t.step(function() {
+ document.getElementsByTagName("a")[0].click();
+ events.push('after script');
+});
+onload = t.step_func(function() {
+ // javascript: executions are async.
+ assert_array_equals(events, ['after script', 'javascript']);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/014.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/014.html
new file mode 100644
index 0000000000..b27ca992bd
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/014.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<title> Link with javascript onclick form submission script order </title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe id="test" name="test"></iframe>
+<form target="test" action="javascript:parent.events.push('submit');"></form>
+<a target="test" onclick="document.forms[0].submit()">Test</a>
+<script>
+var t = async_test();
+var events = [];
+t.step(function() {
+ document.getElementsByTagName("a")[0].click();
+ events.push('after script');
+});
+onload = t.step_func(function() {
+ // javascript: executions are async.
+ assert_array_equals(events, ['after script', 'submit']);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/015.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/015.html
new file mode 100644
index 0000000000..696aaec2c8
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/015.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title> Link with javascript onclick and href script order </title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<iframe id="test" name="test"></iframe>
+<a target="test" onclick="parent.events.push('click');" href="javascript:parent.events.push('href')">Test</a>
+<script>
+var t = async_test();
+var events = [];
+t.step(function() {
+ document.getElementsByTagName("a")[0].click();
+ events.push('after script');
+});
+onload = t.step_func(function() {
+ // javascript: executions are async.
+ assert_array_equals(events, ['click', 'after script', 'href']);
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load-1.html
new file mode 100644
index 0000000000..50a9a50fa0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load-1.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<script>
+parent.postMessage(document.readyState, "*");
+let f = document.createElement("iframe");
+f.onload = function() {
+ parent.postMessage("stop", "*");
+ window.stop();
+};
+document.documentElement.appendChild(f);
+
+window.addEventListener("load", (event) => {
+ parent.postMessage("load", "*");
+});
+window.addEventListener("error", (event) => {
+ parent.postMessage("error", "*");
+});
+window.addEventListener("abort", (event) => {
+ parent.postMessage("abort", "*");
+});
+window.addEventListener("pageshow", (event) => {
+ parent.postMessage("pageshow", "*");
+});
+window.addEventListener("DOMContentLoaded", (event) => {
+ parent.postMessage("DOMContentLoaded", "*");
+});
+document.addEventListener("readystatechange", (event) => {
+ if (document.readyState === "complete") {
+ parent.postMessage("complete", "*");
+ }
+});
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load-2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load-2.html
new file mode 100644
index 0000000000..966b93c51a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load-2.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<script>
+parent.postMessage(document.readyState, "*");
+
+window.addEventListener("load", (event) => {
+ parent.postMessage("load", "*");
+});
+window.addEventListener("error", (event) => {
+ parent.postMessage("error", "*");
+});
+window.addEventListener("abort", (event) => {
+ parent.postMessage("abort", "*");
+});
+window.addEventListener("pageshow", (event) => {
+ parent.postMessage("pageshow", "*");
+});
+window.addEventListener("DOMContentLoaded", (event) => {
+ parent.postMessage("DOMContentLoaded", "*");
+});
+document.addEventListener("readystatechange", (event) => {
+ if (document.readyState === "complete") {
+ parent.postMessage("complete", "*");
+ }
+});
+
+window.setTimeout(function() {
+ parent.postMessage("stop", "*");
+ window.stop();
+}, 100);
+
+</script>
+<link rel="stylesheet" href="/common/slow.py"></link>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load.html
new file mode 100644
index 0000000000..4a4c3df4e7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/abort-document-load.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<title>Aborting a Document load</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/browsing-the-web.html#aborting-a-document-load">
+<div id="log"></div>
+<script>
+var events = [];
+onmessage = function(e) {
+ events.push(e.data);
+};
+async_test(test => {
+ test.step_timeout(() => {
+ const frame = document.querySelector('iframe');
+ const child = frame.contentWindow;
+ assert_equals(child.document.readyState, 'complete', 'readyState is complete');
+ assert_array_equals(events, ["loading", "DOMContentLoaded", "stop", "complete"], 'no load event was fired');
+ events = [];
+ frame.src = "abort-document-load-2.html";
+
+ test.step_timeout(() => {
+ const child = frame.contentWindow;
+ assert_equals(child.document.readyState, 'complete', 'readyState is complete');
+ assert_array_equals(events, ["loading", "DOMContentLoaded", "stop", "complete"], 'no load event was fired');
+ test.done();
+ }, 1000);
+ }, 1000);
+});
+</script>
+<iframe src="abort-document-load-1.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/about-srcdoc-navigation-blocked.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/about-srcdoc-navigation-blocked.window.js
new file mode 100644
index 0000000000..58b73494e1
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/about-srcdoc-navigation-blocked.window.js
@@ -0,0 +1,52 @@
+// META: title=Navigation to about:srcdoc, not via srcdoc="", must be blocked
+// META: script=../resources/helpers.js
+
+promise_test(async t => {
+ const iframe = await addSrcdocIframe();
+
+ iframe.contentWindow.location = "/common/blank.html";
+ await waitForIframeLoad(iframe);
+
+ iframe.contentWindow.location = "about:srcdoc";
+
+ // Fetching "about:srcdoc" should result in a network error, and navigating
+ // to a network error should produce an opaque-origin page. In particular,
+ // since the error page should end up being cross-origin to the parent
+ // frame, `contentDocument` should return `null`.
+ //
+ // If instead this results in a message because we re-loaded a srcdoc document
+ // from the contents of the srcdoc="" attribute, immediately fail.
+ await Promise.race([
+ t.step_wait(() => iframe.contentDocument === null),
+ failOnMessage(iframe.contentWindow)
+ ]);
+}, "Navigations to about:srcdoc via window.location must be blocked");
+
+promise_test(async t => {
+ const iframe = await addSrcdocIframe();
+
+ iframe.contentWindow.location = "about:srcdoc?query";
+
+ // See the documentation in the above test.
+ await Promise.race([
+ t.step_wait(() => iframe.contentDocument === null),
+ failOnMessage(iframe.contentWindow)
+ ]);
+}, "Navigations to about:srcdoc?query via window.location within an " +
+ "about:srcdoc document must be blocked");
+
+promise_test(async t => {
+ const iframe = await addSrcdocIframe();
+ iframe.contentWindow.name = "test_frame";
+
+ iframe.contentWindow.location = "/common/blank.html";
+ await waitForIframeLoad(iframe);
+
+ window.open("about:srcdoc", "test_frame");
+
+ // See the documentation in the above test.
+ await Promise.race([
+ t.step_wait(() => iframe.contentDocument === null),
+ failOnMessage(iframe.contentWindow)
+ ]);
+}, "Navigations to about:srcdoc via window.open() must be blocked");
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit-longfragment.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit-longfragment.html
new file mode 100644
index 0000000000..8b9802f589
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit-longfragment.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/C/#following-hyperlinks">
+<title>Anchor element with onclick form submission and href to fragment</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- When an anchor element has an onclick handler which submits a form,
+ the anchor's navigation should occur instead of the form's navigation.
+ However, if the anchor has an href which is just a fragment like "#",
+ then the form should be submitted. Many sites rely on this behavior. -->
+
+<iframe name="test"></iframe>
+<form target="test" action="resources/form.html"></form>
+<a id="anchor" target="test" onclick="document.forms[0].submit()" href="#fragment">Test</a>
+
+<script>
+async_test(t => {
+ const anchor = document.getElementById('anchor');
+ t.step(() => anchor.click());
+ window.onmessage = t.step_func(event => {
+ if (typeof event.data === 'string' && event.data.includes('navigation')) {
+ assert_equals(event.data, 'form navigation');
+ t.done();
+ }
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit-withpath.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit-withpath.html
new file mode 100644
index 0000000000..823174181e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit-withpath.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/C/#following-hyperlinks">
+<title>Anchor element with onclick form submission and href to fragment</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- When an anchor element has an onclick handler which submits a form,
+ the anchor's navigation should occur instead of the form's navigation.
+ However, if the anchor has an href which is just a fragment like "#",
+ then the form should be submitted. Many sites rely on this behavior. -->
+
+<iframe name="test"></iframe>
+<form target="test" action="resources/form.html"></form>
+<a id="anchor" target="test" onclick="document.forms[0].submit()">Test</a>
+
+<script>
+async_test(t => {
+ const iframe = document.querySelector('iframe');
+ iframe.onload = t.step_func(() => {
+ const anchor = document.getElementById('anchor');
+ anchor.href = '/#';
+ anchor.click();
+ window.onmessage = t.step_func(event => {
+ if (typeof event.data === 'string' && event.data.includes('navigation')) {
+ assert_equals(event.data, 'form navigation');
+ t.done();
+ }
+ });
+ });
+
+ iframe.src = '/';
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit.html
new file mode 100644
index 0000000000..e0c0e6f82d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-fragment-form-submit.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/C/#following-hyperlinks">
+<title>Anchor element with onclick form submission and href to fragment</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- When an anchor element has an onclick handler which submits a form,
+ the anchor's navigation should occur instead of the form's navigation.
+ However, if the anchor has an href which is just a fragment like "#",
+ then the form should be submitted. Many sites rely on this behavior. -->
+
+<iframe name="test"></iframe>
+<form target="test" action="resources/form.html"></form>
+<a id="anchor" target="test" onclick="document.forms[0].submit()" href="#">Test</a>
+
+<script>
+async_test(t => {
+ const anchor = document.getElementById('anchor');
+ t.step(() => anchor.click());
+ window.onmessage = t.step_func(event => {
+ if (typeof event.data === 'string' && event.data.includes('navigation')) {
+ assert_equals(event.data, 'form navigation');
+ t.done();
+ }
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-jsurl-form-submit.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-jsurl-form-submit.html
new file mode 100644
index 0000000000..1bf5d3595a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/anchor-jsurl-form-submit.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/C/#following-hyperlinks">
+<title>Anchor element with onclick form submission and href to javascript: url</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- When an anchor element has an onclick handler which submits a form,
+ the anchor's navigation should occur instead of the form's navigation.
+ However, if the anchor has an href which returns undefined, then the form
+ should be submitted. -->
+
+<iframe name="test"></iframe>
+<form target="test" action="resources/form.html"></form>
+<a id="anchor" target="test" onclick="document.forms[0].submit()" href="javascript:void(0)">Test</a>
+
+<script>
+async_test(t => {
+ const anchor = document.getElementById('anchor');
+ t.step(() => anchor.click());
+ window.onmessage = t.step_func(event => {
+ if (typeof event.data === 'string' && event.data.includes('navigation')) {
+ assert_equals(event.data, 'form navigation');
+ t.done();
+ }
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/child-navigates-parent-cross-origin.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/child-navigates-parent-cross-origin.window.js
new file mode 100644
index 0000000000..c5bed0fd4c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/child-navigates-parent-cross-origin.window.js
@@ -0,0 +1,90 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=resources/wait-for-messages.js
+
+function testNavigationFails(params) {
+ return async (t) => {
+ // Start waiting for messages before inserting the child frame, to avoid any
+ // race conditions. Note that this would be racy if we executed tests
+ // concurrently, thankfully `promise_test` executes sequentially. See also:
+ // https://github.com/web-platform-tests/rfcs/pull/75
+ const messagesPromise = waitForMessages(1);
+
+ // Execute the test in an iframe, so that the document executing the test
+ // is not navigated away mid-test in case of failure.
+ const child = document.createElement("iframe");
+ document.body.appendChild(child);
+ t.add_cleanup(() => { document.body.removeChild(child); });
+
+ const url = new URL(
+ "resources/child-navigates-parent-cross-origin-inner.html",
+ window.location);
+
+ // Load the grandchild iframe from a different origin.
+ url.host = get_host_info().REMOTE_HOST;
+
+ for (const key in params || {}) {
+ url.searchParams.set(key, params[key]);
+ }
+
+ const grandchild = child.contentDocument.createElement("iframe");
+ grandchild.src = url;
+ child.contentDocument.body.appendChild(grandchild);
+
+ const messages = await messagesPromise;
+ assert_array_equals(messages, ["error: SecurityError"]);
+ }
+}
+
+promise_test(
+ testNavigationFails(),
+ "Child document attempts to navigate cross-origin parent via location");
+
+promise_test(
+ testNavigationFails({ "property": "hash" }),
+ "Child document attempts to navigate cross-origin parent via "+
+ "location.hash");
+
+promise_test(
+ testNavigationFails({ "property": "host" }),
+ "Child document attempts to navigate cross-origin parent via "+
+ "location.host");
+
+promise_test(
+ testNavigationFails({ "property": "hostname" }),
+ "Child document attempts to navigate cross-origin parent via "+
+ "location.hostname");
+
+promise_test(
+ testNavigationFails({ "property": "href" }),
+ "Child document attempts to navigate cross-origin parent via "+
+ "location.href");
+
+promise_test(
+ testNavigationFails({ "property": "pathname" }),
+ "Child document attempts to navigate cross-origin parent via "+
+ "location.pathname");
+
+promise_test(
+ testNavigationFails({ "property": "protocol" }),
+ "Child document attempts to navigate cross-origin parent via "+
+ "location.protocol");
+
+promise_test(
+ testNavigationFails({ "property": "reload" }),
+ "Child document attempts to navigate cross-origin parent via "+
+ "location.reload()");
+
+promise_test(
+ testNavigationFails({ "property": "replace" }),
+ "Child document attempts to navigate cross-origin parent via "+
+ "location.replace()");
+
+promise_test(
+ testNavigationFails({ "property": "search" }),
+ "Child document attempts to navigate cross-origin parent via "+
+ "location.search");
+
+promise_test(
+ testNavigationFails({ "property": "xxxNonExistent" }),
+ "Child document attempts to navigate cross-origin parent via non-standard "+
+ "location property");
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/child-navigates-parent-same-origin.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/child-navigates-parent-same-origin.window.js
new file mode 100644
index 0000000000..a40c412029
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/child-navigates-parent-same-origin.window.js
@@ -0,0 +1,24 @@
+// META: script=resources/wait-for-messages.js
+
+function testNavigation(url) {
+ return async (t) => {
+ // Start waiting for messages before inserting the child frame, to avoid any
+ // race conditions.
+ const messagesPromise = waitForMessages(3);
+
+ const iframe = document.createElement("iframe");
+ iframe.src = url;
+ document.body.appendChild(iframe);
+
+ const messages = await messagesPromise;
+ assert_array_equals(messages, ["initial", "inner", "destination"]);
+ }
+}
+
+promise_test(
+ testNavigation("resources/child-navigates-parent-location-initial.html"),
+ "Child document navigates same-origin parent via document.location");
+
+promise_test(
+ testNavigation("resources/child-navigates-parent-submit-initial.html"),
+ "Child document navigates same-origin parent via form submission");
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/cross-origin-top-navigation-with-user-activation-in-parent.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/cross-origin-top-navigation-with-user-activation-in-parent.window.js
new file mode 100644
index 0000000000..42b4b208ee
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/cross-origin-top-navigation-with-user-activation-in-parent.window.js
@@ -0,0 +1,8 @@
+async_test(t => {
+ addEventListener('message', t.step_func_done(e => {
+ assert_equals(e.data, 'Denied');
+ }));
+ const w = open("resources/page-with-top-navigating-iframe.html?parent_user_gesture=true");
+ t.add_cleanup(() => {w.close()});
+
+}, "Cross-origin top navigation is blocked without user activation, even if the parent has user activation");
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/cross-origin-top-navigation-without-user-activation.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/cross-origin-top-navigation-without-user-activation.window.js
new file mode 100644
index 0000000000..57f0bce247
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/cross-origin-top-navigation-without-user-activation.window.js
@@ -0,0 +1,8 @@
+async_test(t => {
+ addEventListener('message', t.step_func_done(e => {
+ assert_equals(e.data, 'Denied');
+ }));
+ const w = open("resources/page-with-top-navigating-iframe.html");
+ t.add_cleanup(() => {w.close()});
+
+}, "Cross-origin top navigation is blocked without user activation");
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty-iframe-load-event.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty-iframe-load-event.html
new file mode 100644
index 0000000000..b9108f9937
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty-iframe-load-event.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<title>load event for empty iframe in relation to the event loop</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+setup({explicit_done:true});
+let ran = false;
+
+onload = function() {
+ let iframe = document.createElement("iframe");
+ iframe.onload = function() {
+ test(function() {
+ assert_equals(ran, false, 'Expected onload to run first');
+ }, "Check execution order on load handler");
+ if (ran) {
+ done();
+ } else {
+ ran = true;
+ }
+ };
+ document.body.appendChild(iframe);
+
+ // Nested timeout to accommodate Gecko, because the it seems
+ // the outer setTimeout takes its slot in the event queue right away
+ // but the load event task takes its slot only at the end of this script.
+ setTimeout(function() {
+ setTimeout(function() {
+ test(function() {
+ assert_equals(ran, true, 'Expected nested setTimeout to run second');
+ }, "Check execution order from nested timeout");
+ if (ran) {
+ done();
+ } else {
+ ran = true;
+ }
+ });
+ });
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty_fragment.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty_fragment.html
new file mode 100644
index 0000000000..18a6f84c9f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty_fragment.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Navigating to the same URL with an empty fragment aborts the navigation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe src="empty_fragment_iframe.html"></iframe>
+<script>
+// If the navigation were not aborted, we would expect multiple load events
+// as the page continually reloads itself.
+async_test(function(t) {
+ var count = 0;
+ var iframe = document.querySelector('iframe');
+ iframe.onload = t.step_func(function() {
+ count++;
+ });
+ window.child_succeeded = t.step_func_done(function() {
+ assert_equals(count, 1);
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty_fragment_iframe.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty_fragment_iframe.html
new file mode 100644
index 0000000000..26b28a0d7d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/empty_fragment_iframe.html
@@ -0,0 +1,11 @@
+<script>
+var timeout;
+onload = function() {
+ location.hash = "";
+ timeout = setTimeout(function() { parent.child_succeeded() }, 2000);
+};
+
+onbeforeunload = function() {
+ clearTimeout(timeout);
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/failure-check-sequence.https.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/failure-check-sequence.https.html
new file mode 100644
index 0000000000..5d7aa2b9cb
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/failure-check-sequence.https.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Sequence of the checks performed against a navigation response</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+'use strict';
+const collect = (win) => {
+ const report = new Promise((resolve) => {
+ if (!win.ReportingObserver) {
+ return;
+ }
+
+ const observer = new win.ReportingObserver(resolve);
+ observer.observe();
+ }).then((reports) => reports[0].type);
+ // Although CSP also makes use of ReportingObserver, monitoring this event
+ // allows the test to provide value to implementations that have not yet
+ // integrated CSP and Reporting (as of the time of this writing, Firefox and
+ // Safari).
+ const cspViolation = new Promise((resolve) => {
+ win.document.addEventListener('securitypolicyviolation', () => resolve('csp-violation'));
+ });
+ const halfASecond = new Promise((resolve) => setTimeout(() => resolve(null), 500));
+
+ return Promise.race([report, cspViolation, halfASecond]);
+};
+
+const createWindow = (t, url) => {
+ const win = open(url);
+ t.add_cleanup(() => win.close());
+ return new Promise((resolve) => win.onload = () => resolve(win));
+};
+
+promise_test(async (t) => {
+ const win = await createWindow(t, '/common/blank.html?pipe=header(content-security-policy, frame-src none)');
+ const iframe = win.document.createElement('iframe');
+ iframe.src = '/common/blank.html?pipe=header(x-frame-options, deny)';
+ win.document.body.appendChild(iframe);
+
+ assert_equals(await collect(win), 'csp-violation');
+}, 'CSP check precedes X-Frame-Options check');
+
+promise_test(async (t) => {
+ const win = await createWindow(t, '/common/blank.html?pipe=header(content-security-policy, frame-src none)|header(cross-origin-embedder-policy, require-corp)');
+ const iframe = win.document.createElement('iframe');
+ iframe.src = '/common/blank.html';
+ win.document.body.appendChild(iframe);
+
+ assert_equals(await collect(win), 'csp-violation');
+}, 'CSP check precedes COEP check - CSP header first');
+
+promise_test(async (t) => {
+ const win = await createWindow(t, '/common/blank.html?pipe=header(cross-origin-embedder-policy, require-corp)|header(content-security-policy, frame-src none)');
+ const iframe = win.document.createElement('iframe');
+ iframe.src = '/common/blank.html';
+ win.document.body.appendChild(iframe);
+
+ assert_equals(await collect(win), 'csp-violation');
+}, 'CSP check precedes COEP check - COEP header first');
+
+promise_test(async (t) => {
+ const win = await createWindow(t, '/common/blank.html?pipe=header(cross-origin-embedder-policy, require-corp)');
+ const iframe = win.document.createElement('iframe');
+ iframe.src = '/common/blank.html?pipe=header(x-frame-options, deny)';
+ win.document.body.appendChild(iframe);
+
+ assert_equals(await collect(win), 'coep');
+}, 'COEP check precedes X-Frame-Options check');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-nosrc.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-nosrc.html
new file mode 100644
index 0000000000..b89d3bb54b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-nosrc.html
@@ -0,0 +1,178 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>Navigations on iframe without src attribute</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+/*
+ When an iframe is created it will contain the initial empty document. If the
+ src and srcdoc attribute is not set, it will stay on the initial empty
+ document.
+ These tests verifies the behavior of navigations that happen on the initial
+ empty document in that situation. They should all be converted to do a
+ replacement.
+*/
+"use strict";
+const url1 = "/common/blank.html?1";
+const url2 = "/common/blank.html?2";
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src not set, which will stay on the initial empty
+ // document.
+ const iframe = insertIframe(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with no src must not change history.length");
+
+ // Navigate away from the initial empty document through iframe.src. This
+ // should do a replacement.
+ iframe.src = url1;
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on document loaded by iframe with no src");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ iframe.src = url2;
+ await waitForLoad(t, iframe, url2);
+ assert_equals(history.length, startingHistoryLength + 1,
+ "history.length increases after normal navigation from non-initial empty document");
+}, "src");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src not set, which will stay on the initial empty
+ // document.
+ const iframe = insertIframe(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with no src must not change history.length");
+
+ // Navigate away from the initial empty document through setting
+ // location.href. This should do a replacement.
+ iframe.contentWindow.location.href = url1;
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on document loaded by iframe with no src");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ iframe.contentWindow.location.href = url2;
+ await waitForLoad(t, iframe, url2);
+ assert_equals(history.length, startingHistoryLength + 1,
+ "history.length increases after normal navigation from non-initial empty document");
+}, "location.href");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src not set, which will stay on the initial empty
+ // document.
+ const iframe = insertIframe(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with no src must not change history.length");
+
+ // Navigate away from the initial empty document through location.assign().
+ // This should do a replacement.
+ iframe.contentWindow.location.assign(url1);
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on document loaded by iframe with no src");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ iframe.contentWindow.location.assign(url2);
+ await waitForLoad(t, iframe, url2);
+ assert_equals(history.length, startingHistoryLength + 1,
+ "history.length increases after normal navigation from non-initial empty document");
+}, "location.assign");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src not set, which will stay on the initial empty
+ // document.
+ const iframe = insertIframe(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with no src must not change history.length");
+
+ // Navigate away from the initial empty document through window.open().
+ // This should do a replacement.
+ iframe.contentWindow.open(url1, "_self");
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on document loaded by iframe with no src");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ iframe.contentWindow.open(url2, "_self");
+ await waitForLoad(t, iframe, url2);
+ assert_equals(history.length, startingHistoryLength + 1,
+ "history.length increases after normal navigation from non-initial empty document");
+}, "window.open");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src not set, which will stay on the initial empty
+ // document.
+ const iframe = insertIframe(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with no src must not change history.length");
+
+ // Navigate away from the initial empty document through clicking an <a>
+ // element. This should do a replacement.
+ const a1 = iframe.contentDocument.createElement("a");
+ a1.href = url1;
+ iframe.contentDocument.body.appendChild(a1);
+ a1.click();
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on document loaded by iframe with no src");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ const a2 = iframe.contentDocument.createElement("a");
+ a2.href = url2;
+ iframe.contentDocument.body.appendChild(a2);
+ a2.click();
+ await waitForLoad(t, iframe, url2);
+ assert_equals(history.length, startingHistoryLength + 1,
+ "history.length increases after normal navigation from non-initial empty document");
+}, "link click");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src not set, which will stay on the initial empty
+ // document.
+ const iframe = insertIframe(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with no src must not change history.length");
+
+ // Navigate away from the initial empty document through form submission.
+ // This should do a replacement.
+ const form1 = iframe.contentDocument.createElement("form");
+ form1.action = "/common/blank.html";
+ iframe.contentDocument.body.appendChild(form1);
+ const input1 = iframe.contentDocument.createElement("input");
+ input1.type = "hidden";
+ input1.name = "1";
+ form1.append(input1);
+ form1.submit();
+ await waitForLoad(t, iframe, url1 + "=");
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on document loaded by iframe with no src");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ const form2 = iframe.contentDocument.createElement("form");
+ form2.action = "/common/blank.html";
+ iframe.contentDocument.body.appendChild(form2);
+ const input2 = iframe.contentDocument.createElement("input");
+ input2.type = "hidden";
+ input2.name = "2";
+ form2.append(input2);
+ form2.submit();
+ await waitForLoad(t, iframe, url2 + "=");
+ assert_equals(history.length, startingHistoryLength + 1,
+ "history.length increases after normal navigation from non-initial empty document");
+}, "form submission");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204-fragment.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204-fragment.html
new file mode 100644
index 0000000000..2a472f6a6d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204-fragment.html
@@ -0,0 +1,144 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>Fragment navigations on iframe with src set to URL that doesn't load a document (HTTP 204)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+/*
+ When an iframe is created it will contain the initial empty document. If the
+ src is set to a URL that doesn't load a new document (e.g. it results in a
+ HTTP 204 response), it will stay on the initial empty document. After fragment
+ navigations happen on it, it should still stay on the initial empty document.
+ These tests verifies the behavior of navigations that happen on the initial
+ empty document in that situation. They should all be converted to do a
+ replacement.
+*/
+"use strict";
+const url1 = "about:blank#1";
+const url2 = "/common/blank.html?2";
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to URL that doesn't load a new document, so
+ // it will stay in the initial empty document.
+ const iframe = insertIframeWith204Src(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src set to URL that doesn't load a new document must not change history.length");
+
+ // Do a fragment navigation within the initial empty document through iframe.src.
+ iframe.src = url1;
+ await new Promise(resolve => t.step_timeout(resolve, 100));
+ assert_equals(iframe.contentWindow.location.href, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after fragment navigation on initial empty document");
+
+ // Navigate away from the initial empty document through iframe.src.
+ iframe.src = url2;
+ await waitForLoad(t, iframe, url2);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation from initial empty document");
+}, "src");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to URL that doesn't load a new document, so
+ // it will stay in the initial empty document.
+ const iframe = insertIframeWith204Src(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src set to URL that doesn't load a new document must not change history.length");
+
+ // Do a fragment navigation within the initial empty document through setting location.href.
+ // This should do a replacement.
+ iframe.contentWindow.location.href = url1;
+ await new Promise(resolve => t.step_timeout(resolve, 100));
+ assert_equals(iframe.contentWindow.location.href, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after fragment navigation on initial empty document");
+
+ // Navigate away from the initial empty document through setting location.href.
+ // This should do a replacement.
+ iframe.contentWindow.location.href = url2;
+ await waitForLoad(t, iframe, url2);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation from initial empty document");
+}, "location.href");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to URL that doesn't load a new document, so
+ // it will stay in the initial empty document.
+ const iframe = insertIframeWith204Src(t);
+ await new Promise(resolve => t.step_timeout(resolve, 100));
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src set to URL that doesn't load a new document must not change history.length");
+
+ // Do a fragment navigation within the initial empty document through location.assign().
+ // This should do a replacement.
+ iframe.contentWindow.location.assign(url1);
+ assert_equals(iframe.contentWindow.location.href, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after fragment navigation on initial empty document");
+
+ // Navigate away from the initial empty document through location.assign().
+ // This should do a replacement.
+ iframe.contentWindow.location.assign(url2);
+ await waitForLoad(t, iframe, url2);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation from initial empty document");
+}, "location.assign");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to URL that doesn't load a new document, so
+ // it will stay in the initial empty document.
+ const iframe = insertIframeWith204Src(t);
+ await new Promise(resolve => t.step_timeout(resolve, 100));
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src set to URL that doesn't load a new document must not change history.length");
+
+ // Do a fragment navigation within the initial empty document through window.open().
+ // This should do a replacement.
+ iframe.contentWindow.open(url1, "_self");
+ assert_equals(iframe.contentWindow.location.href, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after fragment navigation on initial empty document");
+
+ // Navigate away from the initial empty document through window.open().
+ // This should do a replacement.
+ iframe.contentWindow.open(url2, "_self");
+ await waitForLoad(t, iframe, url2);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation from initial empty document");
+}, "window.open");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to URL that doesn't load a new document, so it will stay in the initial empty document.
+ const iframe = insertIframeWith204Src(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src set to URL that doesn't load a new document must not change history.length");
+
+ // Do a fragment navigation within the initial empty document through clicking an <a> element.
+ // This should do a replacement.
+ const a1 = iframe.contentDocument.createElement("a");
+ a1.href = url1;
+ iframe.contentDocument.body.appendChild(a1);
+ a1.click();
+ await new Promise(resolve => t.step_timeout(resolve, 100));
+ assert_equals(iframe.contentWindow.location.href, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after fragment navigation on initial empty document");
+
+ // Navigate away from the initial empty document through clicking an <a> element.
+ // This should do a replacement.
+ const a2 = iframe.contentDocument.createElement("a");
+ a2.href = url2;
+ iframe.contentDocument.body.appendChild(a2);
+ a2.click();
+ await waitForLoad(t, iframe, url2);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation from initial empty document");
+}, "link click");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204-pushState-replaceState.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204-pushState-replaceState.html
new file mode 100644
index 0000000000..8a97fd36a4
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204-pushState-replaceState.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>pushState/replaceState on iframe with src set to URL that doesn't load a document (HTTP 204)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+/*
+ When an iframe is created it will contain the initial empty document. If the
+ src is set to a URL that doesn't load a new document (e.g. it results in a
+ HTTP 204 response), it will stay on the initial empty document. If
+ history.pushState() or history.replaceState() is called on it, it should
+ still stay on the initial empty document.
+ These tests verifies the behavior of navigations that happen on the initial
+ empty document in that situation. They should all be converted to do a
+ replacement.
+*/
+"use strict";
+const crossDocumentURL = "/common/blank.html?2";
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to URL that doesn't load a new document, so
+ // it will stay on the initial empty document.
+ const iframe = insertIframeWith204Src(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src set to URL that doesn't load a new document must not change history.length");
+
+ // Do a history.pushState() to about:blank#foo.
+ let pushURL = "about:blank#foo";
+ iframe.contentWindow.history.pushState({}, "title", pushURL);
+ assert_equals(iframe.contentWindow.location.href, pushURL);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after history.pushState() on the initial empty document");
+
+ // Navigate away from the initial empty document. This should do replacement.
+ iframe.src = crossDocumentURL;
+ await waitForLoad(t, iframe, crossDocumentURL);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation from initial empty document");
+}, "history.pushState");
+
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to URL that doesn't load a new document, so
+ // it will stay on the initial empty document.
+ const iframe = insertIframeWith204Src(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src set to URL that doesn't load a new document must not change history.length");
+
+ // Do a history.replaceState() to about:blank#foo.
+ let replaceURL = "about:blank#foo";
+ iframe.contentWindow.history.replaceState({}, "title", replaceURL);
+ assert_equals(iframe.contentWindow.location.href, replaceURL);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after history.replaceState() on the initial empty document");
+
+ // Navigate away from the initial empty document. This should do replacement.
+ iframe.src = crossDocumentURL;
+ await waitForLoad(t, iframe, crossDocumentURL);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation from initial empty document");
+}, "history.replaceState");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204.html
new file mode 100644
index 0000000000..f7bade68fb
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-204.html
@@ -0,0 +1,178 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>Navigations on iframe with src set to URL that doesn't load a new document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+/*
+ When an iframe is created it will contain the initial empty document. If the
+ src is set to a URL that doesn't load a new document (e.g. it results in a
+ HTTP 204 response), it will stay on the initial empty document.
+ These tests verify the behavior of navigations that happen on the initial
+ empty document in that situation. They should all be converted to do a
+ replacement.
+*/
+"use strict";
+const url1 = "/common/blank.html?1";
+const url2 = "/common/blank.html?2";
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to URL that doesn't load a new document, so
+ // it will stay on the initial empty document.
+ const iframe = insertIframeWith204Src(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src set to URL that doesn't load a new document must not change history.length");
+
+ // Navigate away from the initial empty document through iframe.src. This
+ // should do a replacement.
+ iframe.src = url1;
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on document loaded by iframe with no src");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ iframe.src = url2;
+ await waitForLoad(t, iframe, url2);
+ assert_equals(history.length, startingHistoryLength + 1,
+ "history.length increases after normal navigation from non-initial empty document");
+}, "Navigating to a different document with src");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to URL that doesn't load a new document, so
+ // it will stay on the initial empty document.
+ const iframe = insertIframeWith204Src(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src set to URL that doesn't load a new document must not change history.length");
+
+ // Navigate away from the initial empty document through setting
+ // location.href. This should do a replacement.
+ iframe.contentWindow.location.href = url1;
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on document loaded by iframe with no src");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ iframe.contentWindow.location.href = url2;
+ await waitForLoad(t, iframe, url2);
+ assert_equals(history.length, startingHistoryLength + 1,
+ "history.length increases after normal navigation from non-initial empty document");
+}, "Navigating to a different document with location.href");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to URL that doesn't load a new document, so
+ // it will stay on the initial empty document.
+ const iframe = insertIframeWith204Src(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src set to URL that doesn't load a new document must not change history.length");
+
+ // Navigate away from the initial empty document through location.assign().
+ // This should do a replacement.
+ iframe.contentWindow.location.assign(url1);
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on document loaded by iframe with no src");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ iframe.contentWindow.location.assign(url2);
+ await waitForLoad(t, iframe, url2);
+ assert_equals(history.length, startingHistoryLength + 1,
+ "history.length increases after normal navigation from non-initial empty document");
+}, "Navigating to a different document with location.assign");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to URL that doesn't load a new document, so
+ // it will stay on the initial empty document.
+ const iframe = insertIframeWith204Src(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src set to URL that doesn't load a new document must not change history.length");
+
+ // Navigate away from the initial empty document through window.open().
+ // This should do a replacement.
+ iframe.contentWindow.open(url1, "_self");
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on document loaded by iframe with no src");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ iframe.contentWindow.open(url2, "_self");
+ await waitForLoad(t, iframe, url2);
+ assert_equals(history.length, startingHistoryLength + 1,
+ "history.length increases after normal navigation from non-initial empty document");
+}, "Navigating to a different document with window.open");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to URL that doesn't load a new document, so
+ // it will stay on the initial empty document.
+ const iframe = insertIframeWith204Src(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src set to URL that doesn't load a new document must not change history.length");
+
+ // Navigate away from the initial empty document through clicking an <a>
+ // element. This should do a replacement.
+ const a1 = iframe.contentDocument.createElement("a");
+ a1.href = url1;
+ iframe.contentDocument.body.appendChild(a1);
+ a1.click();
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on document loaded by iframe with no src");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ const a2 = iframe.contentDocument.createElement("a");
+ a2.href = url2;
+ iframe.contentDocument.body.appendChild(a2);
+ a2.click();
+ await waitForLoad(t, iframe, url2);
+ assert_equals(history.length, startingHistoryLength + 1,
+ "history.length increases after normal navigation from non-initial empty document");
+}, "Navigating to a different document with link click");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to URL that doesn't load a new document, so
+ // it will stay on the initial empty document.
+ const iframe = insertIframeWith204Src(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src set to URL that doesn't load a new document must not change history.length");
+
+ // Navigate away from the initial empty document through form submission.
+ // This should do a replacement.
+ const form1 = iframe.contentDocument.createElement("form");
+ form1.action = "/common/blank.html";
+ iframe.contentDocument.body.appendChild(form1);
+ const input1 = iframe.contentDocument.createElement("input");
+ input1.type = "hidden";
+ input1.name = "1";
+ form1.append(input1);
+ form1.submit();
+ await waitForLoad(t, iframe, url1 + "=");
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on document loaded by iframe with no src");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ const form2 = iframe.contentDocument.createElement("form");
+ form2.action = "/common/blank.html";
+ iframe.contentDocument.body.appendChild(form2);
+ const input2 = iframe.contentDocument.createElement("input");
+ input2.type = "hidden";
+ input2.name = "2";
+ form2.append(input2);
+ form2.submit();
+ await waitForLoad(t, iframe, url2 + "=");
+ assert_equals(history.length, startingHistoryLength + 1,
+ "history.length increases after normal navigation from non-initial empty document");
+}, "Navigating to a different document with form submission");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-navigate-immediately.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-navigate-immediately.html
new file mode 100644
index 0000000000..a75257d91c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-navigate-immediately.html
@@ -0,0 +1,124 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>Navigations immediately after appending iframe with src='about:blank'</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+/*
+ When an iframe is created with src="about:blank", it will stay on the initial
+ empty document. These tests verify the behavior of navigations that happen
+ immediately after the iframe is created, which should result in replacement.
+*/
+"use strict";
+const url1 = "/common/blank.html?1";
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to about:blank. This would trigger a
+ // navigation to a non-initial about:blank document.
+ const iframe = insertIframeWithAboutBlankSrc(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src='about:blank' must not change history.length");
+
+ // Trigger a navigation to url1 through the iframe's src attribute.
+ // The iframe should still be on the initial empty document, and the
+ // navigation should do replacement.
+ iframe.src = url1;
+ // Wait for the latest navigation to finish.
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on initial empty document");
+}, "Navigating to a different document with src");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to about:blank but navigate away from it immediately below.
+ const iframe = insertIframeWithAboutBlankSrc(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src='about:blank' must not change history.length");
+
+ // Navigate away from the initial empty document through setting
+ // location.href. The iframe should still be on the initial empty document,
+ // and the navigation should do replacement.
+ iframe.contentWindow.location.href = url1;
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on initial empty document");
+}, "Navigating to a different document with location.href");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to about:blank but navigate away from it immediately below.
+ const iframe = insertIframeWithAboutBlankSrc(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src='about:blank' must not change history.length");
+
+ // Navigate away from the initial empty document through location.assign().
+ // The iframe should still be on the initial empty document, and the
+ // navigation should do replacement.
+ iframe.contentWindow.location.assign(url1);
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on initial empty document");
+}, "Navigating to a different document with location.assign");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to about:blank but navigate away from it immediately below.
+ const iframe = insertIframeWithAboutBlankSrc(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src='about:blank' must not change history.length");
+
+ // Navigate away from the initial empty document through window.open().
+ // The iframe should still be on the initial empty document, and the
+ // navigation should do replacement.
+ iframe.contentWindow.open(url1, "_self");
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on initial empty document");
+}, "Navigating to a different document with window.open");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to about:blank but navigate away from it immediately below.
+ const iframe = insertIframeWithAboutBlankSrc(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src='about:blank' must not change history.length");
+
+ // Navigate away from the initial empty document through clicking an <a>
+ // element. The iframe should still be on the initial empty document, and the
+ // navigation should do replacement.
+ const a = iframe.contentDocument.createElement("a");
+ a.href = url1;
+ iframe.contentDocument.body.appendChild(a);
+ a.click();
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on initial empty document");
+}, "Navigating to a different document with link click");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to about:blank but navigate away from it immediately below.
+ const iframe = insertIframeWithAboutBlankSrc(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src='about:blank' must not change history.length");
+
+ // Navigate away from the initial empty document through form submission.
+ // The iframe should still be on the initial empty document, and the
+ // navigation should do replacement.
+ const form = iframe.contentDocument.createElement("form");
+ form.action = "/common/blank.html";
+ iframe.contentDocument.body.appendChild(form);
+ const input = iframe.contentDocument.createElement("input");
+ input.type = "hidden";
+ input.name = "1";
+ form.append(input);
+ form.submit();
+ await waitForLoad(t, iframe, url1 + "=");
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on initial empty document");
+}, "Navigating to a different document with form submission");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-wait-for-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-wait-for-load.html
new file mode 100644
index 0000000000..b7066ce521
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-wait-for-load.html
@@ -0,0 +1,134 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>Navigations after iframe with src='about:blank' finished loading</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+ /*
+ When an iframe is created with src="about:blank", it will stay on the initial
+ empty document. These tests verify the behavior of navigations that happen
+ immediately after the load event is fired on the iframe element, which
+ should result in replacement.
+ */
+"use strict";
+const url1 = "/common/blank.html?1";
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to about:blank, and wait for it to finish
+ // loading. This would trigger and commit a navigation to a non-initial
+ // about:blank document.
+ const iframe = await insertIframeWithAboutBlankSrcWaitForLoad(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src='about:blank' must not change history.length");
+
+ // Trigger a navigation to url1 through the iframe's src attribute.
+ // The iframe should still be on the initial empty document, and the
+ // navigation should do replacement.
+ iframe.src = url1;
+ // Wait for the latest navigation to finish.
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on initial empty document");
+}, "Navigating to a different document with src");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to about:blank, and wait for it to finish
+ // loading. This would trigger and commit a navigation to a non-initial
+ // about:blank document.
+ const iframe = await insertIframeWithAboutBlankSrcWaitForLoad(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src='about:blank' must not change history.length");
+
+ // Navigate away from the initial empty document through setting
+ // location.href. The iframe should still be on the initial empty document,
+ // and the navigation should do replacement.
+ iframe.contentWindow.location.href = url1;
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on initial empty document");
+ }, "Navigating to a different document with location.href");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to about:blank, and wait for it to finish
+ // loading. This would trigger and commit a navigation to a non-initial
+ // about:blank document.
+ const iframe = await insertIframeWithAboutBlankSrcWaitForLoad(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src='about:blank' must not change history.length");
+
+ // Navigate away from the initial empty document through setting
+ // location.href. The iframe should still be on the initial empty document,
+ // and the navigation should do replacement.
+ iframe.contentWindow.location.href = url1;
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on initial empty document");
+}, "Navigating to a different document with location.assign");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to about:blank, and wait for it to finish
+ // loading. This would trigger and commit a navigation to a non-initial
+ // about:blank document.
+ const iframe = await insertIframeWithAboutBlankSrcWaitForLoad(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src='about:blank' must not change history.length");
+
+ // Navigate away from the initial empty document through window.open().
+ // The iframe should still be on the initial empty document, and the
+ // navigation should do replacement.
+ iframe.contentWindow.open(url1, "_self");
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on initial empty document");
+}, "Navigating to a different document with window.open");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to about:blank, and wait for it to finish
+ // loading. This would trigger and commit a navigation to a non-initial
+ // about:blank document.
+ const iframe = await insertIframeWithAboutBlankSrcWaitForLoad(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src='about:blank' must not change history.length");
+
+ // Navigate away from the initial empty document through clicking an <a>
+ // element. The iframe should still be on the initial empty document, and the
+ // navigation should do replacement.
+ const a = iframe.contentDocument.createElement("a");
+ a.href = url1;
+ iframe.contentDocument.body.appendChild(a);
+ a.click();
+ await waitForLoad(t, iframe, url1);
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on initial empty document");
+}, "Navigating to a different document with link click");
+
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+ // Create an iframe with src set to about:blank which will commit an about:blank document that is not the initial empty document, and wait for it to load.
+ const iframe = await insertIframeWithAboutBlankSrcWaitForLoad(t);
+ assert_equals(history.length, startingHistoryLength,
+ "Inserting iframe with src='about:blank' must not change history.length");
+
+ // Navigate away from the initial empty document through form submission.
+ // The iframe should still be on the initial empty document, and the
+ // navigation should do replacement.
+ const form = iframe.contentDocument.createElement("form");
+ form.action = "/common/blank.html";
+ iframe.contentDocument.body.appendChild(form);
+ const input = iframe.contentDocument.createElement("input");
+ input.type = "hidden";
+ input.name = "1";
+ form.append(input);
+ form.submit();
+ await waitForLoad(t, iframe, url1 + "=");
+ assert_equals(history.length, startingHistoryLength,
+ "history.length must not change after normal navigation on initial empty document");
+}, "Navigating to a different document with form submission");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/initial-content-replacement.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/initial-content-replacement.html
new file mode 100644
index 0000000000..44f890df6c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/initial-content-replacement.html
@@ -0,0 +1,86 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>
+ Content synchronously added to iframe/opened window's document after creation
+ won't get replaced asynchronously when staying on the initial empty document
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+"use strict";
+
+// Asserts the document on |w| stays the same after waiting 100ms.
+function assertDocumentStaysTheSame(t, w) {
+ const initialDocument = w.document;
+ initialDocument.body.innerHTML = "foo";
+ return new Promise((resolve) => {
+ t.step_timeout(() => {
+ assert_equals(w.document.body.innerHTML, "foo");
+ assert_equals(w.document, initialDocument);
+ resolve();
+ }, 100);
+ });
+}
+
+promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ document.body.appendChild(iframe);
+ await assertDocumentStaysTheSame(t, iframe.contentWindow);
+}, "Content synchronously added to <iframe> with no src won't get replaced");
+
+promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "";
+ document.body.appendChild(iframe);
+ await assertDocumentStaysTheSame(t, iframe.contentWindow);
+}, "Content synchronously added to <iframe> with src='' won't get replaced");
+
+promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "about:blank";
+ document.body.appendChild(iframe);
+ await assertDocumentStaysTheSame(t, iframe.contentWindow);
+}, "Content synchronously added to <iframe> with src='about:blank' won't get replaced");
+
+promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "about:blank#foo";
+ document.body.appendChild(iframe);
+ await assertDocumentStaysTheSame(t, iframe.contentWindow);
+}, "Content synchronously added to <iframe> with src='about:blank#foo' won't get replaced");
+
+promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "about:blank?foo";
+ document.body.appendChild(iframe);
+ await assertDocumentStaysTheSame(t, iframe.contentWindow);
+}, "Content synchronously added to <iframe> with src='about:blank?foo' won't get replaced");
+
+promise_test(async t => {
+ const w = window.open();
+ await assertDocumentStaysTheSame(t, w);
+}, "Content synchronously added to window.open()-ed document won't get replaced");
+
+promise_test(async t => {
+ const w = window.open("");
+ await assertDocumentStaysTheSame(t, w);
+}, "Content synchronously added to window.open('')-ed document won't get replaced");
+
+promise_test(async t => {
+ const w = window.open("about:blank");
+ await assertDocumentStaysTheSame(t, w);
+}, "Content synchronously added to window.open('about:blank')-ed document won't get replaced");
+
+promise_test(async t => {
+ const w = window.open("about:blank#foo");
+ await assertDocumentStaysTheSame(t, w);
+}, "Content synchronously added to window.open('about:blank#foo')-ed document won't get replaced");
+
+promise_test(async t => {
+ const w = window.open("about:blank?foo");
+ await assertDocumentStaysTheSame(t, w);
+}, "Content synchronously added to window.open('about:blank?foo')-ed document won't get replaced");
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-event-iframe-element.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-event-iframe-element.html
new file mode 100644
index 0000000000..0d19770cc1
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-event-iframe-element.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>"load" event fires on the iframe element when loading the initial empty document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+"use strict";
+
+promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ let loadCount = 0;
+ iframe.addEventListener("load", () => {
+ loadCount++;
+ });
+ document.body.appendChild(iframe);
+ assert_equals(loadCount, 1);
+}, "load event fires synchronously on <iframe> element created with no src");
+
+promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "";
+ let loadCount = 0;
+ iframe.addEventListener("load", () => {
+ loadCount++;
+ });
+ document.body.appendChild(iframe);
+ assert_equals(loadCount, 1);
+}, "load event fires synchronously on <iframe> element created with src=''");
+
+promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "about:blank";
+ let loadCount = 0;
+ iframe.addEventListener("load", () => {
+ loadCount++;
+ });
+ document.body.appendChild(iframe);
+ assert_equals(loadCount, 1);
+}, "load event fires synchronously on <iframe> element created with src='about:blank'");
+
+promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "about:blank#foo";
+ let loadCount = 0;
+ iframe.addEventListener("load", () => {
+ loadCount++;
+ });
+ document.body.appendChild(iframe);
+ assert_equals(loadCount, 1);
+}, "load event fires synchronously on <iframe> element created with src='about:blank#foo'");
+
+promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "about:blank?foo";
+ let loadCount = 0;
+ iframe.addEventListener("load", () => {
+ loadCount++;
+ });
+ document.body.appendChild(iframe);
+ assert_equals(loadCount, 1);
+}, "load event fires synchronously on <iframe> element created with src='about:blank?foo'");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-iframe-contentWindow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-iframe-contentWindow.html
new file mode 100644
index 0000000000..4aea4aac81
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-iframe-contentWindow.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>"load" & "pageshow" events do not fire on contentWindow of iframe that stays on the initial empty document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+"use strict";
+
+promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ document.body.appendChild(iframe);
+ return assertNoLoadAndPageshowEvent(t, iframe.contentWindow);
+}, "load & pageshow event do not fire on contentWindow of <iframe> element created with no src");
+
+promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "";
+ document.body.appendChild(iframe);
+ return assertNoLoadAndPageshowEvent(t, iframe.contentWindow);
+}, "load & pageshow events do not fire on contentWindow of <iframe> element created with src=''");
+
+promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "about:blank";
+ document.body.appendChild(iframe);
+ return assertNoLoadAndPageshowEvent(t, iframe.contentWindow);
+}, "load & pageshow events do not fire on contentWindow of <iframe> element created with src='about:blank'");
+
+promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "about:blank#foo";
+ document.body.appendChild(iframe);
+ return assertNoLoadAndPageshowEvent(t, iframe.contentWindow);
+}, "load & pageshow events do not fire on contentWindow of <iframe> element created with src='about:blank#foo'");
+
+promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "about:blank?foo";
+ document.body.appendChild(iframe);
+ return assertNoLoadAndPageshowEvent(t, iframe.contentWindow);
+}, "load & pageshow events do not fire on contentWindow of <iframe> element created with src='about:blank?foo'");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-window-open.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-window-open.html
new file mode 100644
index 0000000000..9703502f7f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-window-open.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>"load" and "pageshow" events don't fire on window.open() that stays on the initial empty document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+"use strict";
+
+promise_test(async t => {
+ const w = window.open();
+ return assertNoLoadAndPageshowEvent(t, w)
+}, "load event does not fire on window.open()");
+
+promise_test(async t => {
+ const w = window.open("");
+ return assertNoLoadAndPageshowEvent(t, w)
+}, "load event does not fire on window.open('')");
+
+promise_test(async t => {
+ const w = window.open("about:blank");
+ return assertNoLoadAndPageshowEvent(t, w)
+}, "load event does not fire on window.open('about:blank')");
+
+promise_test(async t => {
+ const w = window.open("about:blank#foo");
+ return assertNoLoadAndPageshowEvent(t, w)
+}, "load event does not fire on window.open('about:blank#foo')");
+
+promise_test(async t => {
+ const w = window.open("about:blank?foo");
+ return assertNoLoadAndPageshowEvent(t, w)
+}, "load event does not fire on window.open('about:blank?foo')");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/resources/code-injector.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/resources/code-injector.html
new file mode 100644
index 0000000000..631b95f9ed
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/resources/code-injector.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Subframe</title>
+
+<script>
+"use strict";
+{{GET[code]}}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/resources/helpers.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/resources/helpers.js
new file mode 100644
index 0000000000..8d9473a949
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/resources/helpers.js
@@ -0,0 +1,121 @@
+// Returns a promise that asserts the "load" and "pageshow" events are not
+// fired on |target|.
+function assertNoLoadAndPageshowEvent(t, target) {
+ target.addEventListener("load", t.unreached_func("load should not be fired"));
+ target.addEventListener("pageshow", t.unreached_func("pageshow should not be fired"));
+ return new Promise(resolve => {
+ // Wait 50ms to ensure events fired after asynchronous navigations are
+ // also captured.
+ setTimeout(resolve, 50);
+ });
+}
+
+const url204 = "/common/blank.html?pipe=status(204)";
+const postMessageToOpenerOnLoad = `
+ window.onload = () => {
+ window.opener.postMessage("loaded", "*")
+ }
+ `;
+
+// -- Start of helpers for iframe initial empty document tests.
+
+// Creates an iframe with an unset src and appends it to the document.
+window.insertIframe = (t) => {
+ const iframe = document.createElement("iframe");
+ t.add_cleanup(() => iframe.remove());
+ document.body.append(iframe);
+ return iframe;
+};
+
+// Creates an iframe with src set to a URL that doesn't commit a new document
+// (results in a HTTP 204 response) and appends it to the document.
+window.insertIframeWith204Src = (t) => {
+ const iframe = document.createElement("iframe");
+ iframe.src = url204;
+ t.add_cleanup(() => iframe.remove());
+ document.body.append(iframe);
+ return iframe;
+};
+
+// Creates an iframe with src="about:blank" and appends it to the document.
+window.insertIframeWithAboutBlankSrc = (t) => {
+ const iframe = document.createElement("iframe");
+ t.add_cleanup(() => iframe.remove());
+ iframe.src = "about:blank";
+ document.body.append(iframe);
+ return iframe;
+};
+
+// Creates an iframe with src="about:blank", appends it to the document, and
+// waits for the non-initial about:blank document finished loading.
+window.insertIframeWithAboutBlankSrcWaitForLoad = async (t) => {
+ const iframe = insertIframeWithAboutBlankSrc(t);
+ const aboutBlankLoad = new Promise(resolve => {
+ // In some browsers, the non-initial about:blank navigation commits
+ // asynchronously, while in other browsers, it would commit synchronously.
+ // This means we can't wait for the "load" event as it might have already
+ // ran. Instead, just wait for 100ms before resolving, as the non-initial
+ // about:blank navigation will most likely take less than 100 ms to commit.
+ t.step_timeout(resolve, 100);
+ });
+ await aboutBlankLoad;
+ return iframe;
+};
+
+// Waits for the "load" event for |urlRelativeToThisDocument| to run on
+// |iframe|.
+window.waitForLoad = (t, iframe, urlRelativeToThisDocument) => {
+ return new Promise(resolve => {
+ iframe.addEventListener("load", t.step_func(() => {
+ assert_equals(iframe.contentWindow.location.href, (new URL(urlRelativeToThisDocument, location.href)).href);
+
+ // Wait a bit longer to ensure all history stuff has settled, e.g. the document is "completely loaded"
+ // (which happens from a queued task).
+ setTimeout(resolve, 0);
+ }), { once: true });
+ });
+};
+
+// -- End of helpers for iframe initial empty document tests.
+
+// -- Start of helpers for opened windows' initial empty document tests.
+
+// window.open() to a URL that doesn't load a new document (results in a HTTP
+// 204 response). This should create a new window that stays on the initial
+// empty document.
+window.windowOpen204 = (t) => {
+ const openedWindow = window.open(url204);
+ t.add_cleanup(() => openedWindow.close());
+ return openedWindow;
+};
+
+// window.open() (no URL set). This should create a new window that stays on
+// the initial empty document as it won't trigger a non-initial about:blank
+// navigation.
+window.windowOpenNoURL = (t) => {
+ const openedWindow = window.open();
+ t.add_cleanup(() => openedWindow.close());
+ return openedWindow;
+};
+
+// window.open("about:blank"). This should create a new window that stays on
+// the initial empty document as it won't trigger a non-initial about:blank
+// navigation.
+window.windowOpenAboutBlank = (t) => {
+ const openedWindow = window.open("about:blank");
+ t.add_cleanup(() => openedWindow.close());
+ return openedWindow;
+};
+
+// Waits for a postMessage with data set to |message| is received on the current
+// window.
+window.waitForMessage = (t, message) => {
+ return new Promise(resolve => {
+ window.addEventListener("message", t.step_func((event) => {
+ if (event.data == message)
+ resolve();
+ }));
+ });
+};
+
+// -- End of helpers for opened windows' initial empty document tests.
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204-fragment.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204-fragment.html
new file mode 100644
index 0000000000..bb47cd3820
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204-fragment.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>Fragment navigation on initial empty document created through window.open(url-with-204-response)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+/*
+ When a new window is opened through window.open() it will contain the initial
+ empty document. If the URL parameter is set to the URL that doesn't load a
+ new document (e.g. it results in a HTTP 204 response), it will stay on the
+ initial empty document. If fragment navigations happen, it will still stay on
+ the initial empty document.
+ These tests verify the behavior of navigations that happen on the initial
+ empty document in that situation. They should all be converted to do a
+ replacement.
+*/
+"use strict";
+const url1 = "about:blank#foo";
+const url2 = "resources/code-injector.html?2&pipe=sub(none)&code=" +
+ encodeURIComponent(postMessageToOpenerOnLoad);
+
+promise_test(async t => {
+ // Open a new window with a URL that doesn't load a new document, so it will stay in the initial empty document.
+ const openedWindow = windowOpen204(t);
+
+ // Do a fragment navigation within the initial empty document through setting location.href.
+ // This should do a replacement.
+ openedWindow.location.href = url1;
+ await new Promise(resolve => t.step_timeout(resolve, 100));
+ assert_equals(openedWindow.location.hash, "#foo");
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should not increase after fragment navigation on initial empty document");
+
+ // Navigate away from the initial empty document through setting location.href.
+ // This should do a replacement.
+ openedWindow.location.href = url2;
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should not increase after normal navigation away from initial empty document");
+}, "location.href");
+
+promise_test(async t => {
+ // Open a new window with a URL that doesn't load a new document, so it will stay in the initial empty document.
+ const openedWindow = windowOpen204(t);
+
+ // Do a fragment navigation within the initial empty document through location.assign().
+ // This should do a replacement.
+ openedWindow.location.assign(url1);
+ await new Promise(resolve => t.step_timeout(resolve, 100));
+ assert_equals(openedWindow.location.hash, "#foo");
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should not increase after fragment navigation on initial empty document");
+
+ // Navigate away from the initial empty document through location.assign().
+ // This should do a replacement.
+ openedWindow.location.assign(url2);
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should not increase after normal navigation away from initial empty document");
+}, "location.assign");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204-pushState-replaceState.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204-pushState-replaceState.html
new file mode 100644
index 0000000000..b5382b189f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204-pushState-replaceState.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>Fragment navigation on initial empty document created through window.open(url-with-204-response)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+/*
+ When a new window is opened through window.open() it will contain the initial
+ empty document. If the URL parameter is set to the URL that doesn't load a
+ new document (e.g. it results in a HTTP 204 response), it will stay on the
+ initial empty document. If history.pushState() or history.replaceState() is
+ called on it, it should still stay on the initial empty document.
+ These tests verify the behavior of navigations that happen on the initial
+ empty document in that situation. They should all be converted to do a
+ replacement.
+*/
+"use strict";
+const url1 = "about:blank#foo";
+const url2 = "resources/code-injector.html?2&pipe=sub(none)&code=" +
+ encodeURIComponent(postMessageToOpenerOnLoad);
+
+promise_test(async t => {
+ // Open a new window with a URL that doesn't load a new document, so it will stay in the initial empty document.
+ const openedWindow = windowOpen204(t);
+
+ // Do a history.pushState() to about:blank#foo.
+ let pushURL = "about:blank#foo";
+ openedWindow.history.pushState({}, "title", pushURL);
+ assert_equals(openedWindow.location.href, pushURL);
+ assert_equals(history.length, 1,
+ "history.length must not change after history.pushState() on the initial empty document");
+
+ // Navigate away from the initial empty document through setting location.href.
+ // This should do a replacement.
+ openedWindow.location.href = url2;
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should not increase after normal navigation away from initial empty document");
+}, "history.pushState");
+
+promise_test(async t => {
+ // Open a new window with a URL that doesn't load a new document, so it will stay in the initial empty document.
+ const openedWindow = windowOpen204(t);
+
+ // Do a history.pushState() to about:blank#foo.
+ let replaceURL = "about:blank#foo";
+ openedWindow.history.replaceState({}, "title", replaceURL);
+ assert_equals(openedWindow.location.href, replaceURL);
+ assert_equals(history.length, 1,
+ "history.length must not change after history.replaceState() on the initial empty document");
+
+ // Navigate away from the initial empty document through location.assign().
+ // This should do a replacement.
+ openedWindow.location.assign(url2);
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should not increase after normal navigation away from initial empty document");
+}, "history.replaceState");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204.html
new file mode 100644
index 0000000000..6461b3c8fc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-204.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>Navigations on window.open() with URL that doesn't load a new document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+/*
+ When a new window is opened through window.open() it will contain the initial
+ empty document. If the URL parameter is set to the URL that doesn't load a
+ new document (e.g. it results in a HTTP 204 response), it will stay on the
+ initial empty document.
+ These tests verify the behavior of navigations that happen on the initial
+ empty document in that situation. They should all be converted to do a
+ replacement.
+*/
+"use strict";
+const url1 = "resources/code-injector.html?1&pipe=sub(none)&code=" +
+ encodeURIComponent(postMessageToOpenerOnLoad);
+const url2 = "resources/code-injector.html?2&pipe=sub(none)&code=" +
+ encodeURIComponent(postMessageToOpenerOnLoad);
+
+promise_test(async t => {
+ // Open a new window with a URL that doesn't load a new document, so it will stay in the initial empty document.
+ const openedWindow = windowOpen204(t);
+
+ // Navigate away from the initial empty document through setting
+ // location.href. This should do a replacement.
+ openedWindow.location.href = url1;
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should not increase after normal navigation away from initial empty document");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ openedWindow.location.href = url2;
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 2,
+ "history.length should increase after normal navigation away from non-initial empty document");
+}, "location.href");
+
+promise_test(async t => {
+ // Open a new window with a URL that doesn't load a new document, so it will stay in the initial empty document.
+ const openedWindow = windowOpen204(t);
+
+ // Navigate away from the initial empty document through location.assign().
+ // This should do a replacement.
+ openedWindow.location.assign(url1);
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should not increase after normal navigation away from initial empty document");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ openedWindow.location.assign(url2);
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 2,
+ "history.length should increase after normal navigation away from non-initial empty document");
+}, "location.assign");
+/*
+promise_test(async t => {
+ // Open a new window with a URL that doesn't load a new document, so it will stay in the initial empty document.
+ const openedWindow = windowOpen204(t);
+
+ // Navigate away from the initial empty document through setting
+ // window.open(). This should do a replacement.
+ openedWindow.open(url1, "_self");
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should increase after normal navigation away from non-initial empty document");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ openedWindow.open(url2, "_self");
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 2,
+ "history.length should increase after normal navigation away from non-initial empty document");
+}, "window.open");
+*/
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-aboutblank.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-aboutblank.html
new file mode 100644
index 0000000000..f3033d6a21
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-aboutblank.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>Navigations on window.open(about:blank) after waiting for it to load</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+/*
+ When a new window is opened through window.open() it will contain the initial
+ empty document. If the URL parameter is set to about:blank, it will stay on
+ the initial empty document (unlike iframes with src="about:blank", which will
+ start a navigation to a non-initial about:blank document).
+ These tests verify the behavior of navigations that happen on the initial
+ empty document in that situation. They should all be converted to do a
+ replacement.
+*/
+"use strict";
+const url1 = "resources/code-injector.html?1&pipe=sub(none)&code=" +
+ encodeURIComponent(postMessageToOpenerOnLoad);
+const url2 = "resources/code-injector.html?2&pipe=sub(none)&code=" +
+ encodeURIComponent(postMessageToOpenerOnLoad);
+
+promise_test(async t => {
+ // Open a window with URL about:blank, which will commit the
+ // initial empty document and stay on it.
+ const openedWindow = windowOpenAboutBlank(t);
+
+ // Unlike iframe with src="about:blank", window.open("about:blank") won't
+ // trigger a navigation to a non-initial about:blank document, so it should
+ // stay on the initial empty document. To verify, wait for 100ms before
+ // continuing.
+ await new Promise((resolve) => t.step_timeout(resolve, 100));
+
+ // Navigate away from the initial empty document through location.href.
+ // This should do a replacement.
+ openedWindow.location.href = url1;
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should not increase after normal navigation away from initial empty document");
+}, "location.href");
+
+promise_test(async t => {
+ // Open a window with URL about:blank, which will commit the
+ // initial empty document and stay on it.
+ const openedWindow = windowOpenAboutBlank(t);
+
+ // Unlike iframe with src="about:blank", window.open("about:blank") won't
+ // trigger a navigation to a non-initial about:blank document, so it should
+ // stay on the initial empty document. To verify, wait for 100ms before
+ // continuing.
+ await new Promise((resolve) => t.step_timeout(resolve, 100));
+
+ // Navigate away from the initial empty document through location.assign().
+ // This should do a replacement.
+ openedWindow.location.assign(url1);
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should not increase after normal navigation away from initial empty document");
+}, "location.assign");
+/*
+promise_test(async t => {
+ // Open a window with URL about:blank, which will commit the
+ // initial empty document and stay on it.
+ const openedWindow = windowOpenAboutBlank(t);
+
+ // Unlike iframe with src="about:blank", window.open("about:blank") won't
+ // trigger a navigation to a non-initial about:blank document, so it should
+ // stay on the initial empty document. To verify, wait for 100ms before
+ // continuing.
+ await new Promise((resolve) => t.step_timeout(resolve, 100));
+
+ // Navigate away from the initial empty document through window.open().
+ // This should do a replacement.
+ openedWindow.open(url1, "_self");
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should not increase after normal navigation away from initial empty document");
+}, "window.open");
+*/
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-history-length.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-history-length.html
new file mode 100644
index 0000000000..ab89bc4098
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-history-length.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>history.length value on window.open()-ed window</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+/*
+ When a new window is opened through window.open() it will contain the initial
+ empty document, and the history.length value should be 1.
+*/
+
+promise_test(async t => {
+ const openedWindow = windowOpenNoURL(t);
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should start at 1 for newly opened window");
+}, "Starting history.length for window.open()");
+
+promise_test(async t => {
+ const openedWindow = windowOpenAboutBlank(t);
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should start at 1 for newly opened window");
+}, "Starting history.length for window.open(about:blank)");
+
+promise_test(async t => {
+ const openedWindow = windowOpen204(t);
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should start at 1 for newly opened window");
+}, "Starting history.length for window.open(url-with-204-response)");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-nourl.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-nourl.html
new file mode 100644
index 0000000000..801f77ad48
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/window-open-nourl.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>Navigations on window.open() to URL that doesn't load a new document</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<body></body>
+<script>
+/*
+ When a new window is opened through window.open() it will contain the initial
+ empty document. If the URL parameter is not set, it will stay on the initial
+ empty document.
+ These tests verify the behavior of navigations that happen on the initial
+ empty document in that situation. They should all be converted to do a
+ replacement.
+*/
+"use strict";
+const url1 = "resources/code-injector.html?1&pipe=sub(none)&code=" + encodeURIComponent(postMessageToOpenerOnLoad);
+const url2 = "resources/code-injector.html?2&pipe=sub(none)&code=" + encodeURIComponent(postMessageToOpenerOnLoad);
+
+promise_test(async t => {
+ // Open a new window with no URL, which will stay in the initial empty document until the navigation below.
+ const openedWindow = windowOpenNoURL(t);
+
+ // Navigate away from the initial empty document through setting
+ // location.href. This should do a replacement.
+ openedWindow.location.href = url1;
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should not increase after normal navigation away from initial empty document");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ openedWindow.location.href = url2;
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 2,
+ "history.length should increase after normal navigation away from non-initial empty document");
+}, "location.href");
+
+promise_test(async t => {
+ // Open a new window with no URL, which will stay in the initial empty document until the navigation below.
+ const openedWindow = windowOpenNoURL(t);
+
+ // Navigate away from the initial empty document through location.assign().
+ // This should do a replacement.
+ openedWindow.location.assign(url1);
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should not increase after normal navigation away from initial empty document");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ openedWindow.location.assign(url2);
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 2,
+ "history.length should increase after normal navigation away from non-initial empty document");
+}, "location.assign");
+/*
+promise_test(async t => {
+ // Open a new window with no URL, which will stay in the initial empty document until the navigation below.
+ const openedWindow = windowOpenNoURL(t);
+
+ // Navigate away from the initial empty document through setting
+ // window.open(). This should do a replacement.
+ openedWindow.open(url1, "_self");
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 1,
+ "history.length should not increase after normal navigation away from initial empty document");
+
+ // Navigate again using the same method, but this time it shouldn't do a
+ // replacement since it's no longer on the initial empty document.
+ openedWindow.open(url2, "_self");
+ await waitForMessage(t, "loaded");
+ assert_equals(openedWindow.history.length, 2,
+ "history.length should increase after normal navigation away from non-initial empty document");
+}, "window.open");
+*/
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/javascript-url-abort-return-value-string.tentative.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/javascript-url-abort-return-value-string.tentative.html
new file mode 100644
index 0000000000..f626a79ae6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/javascript-url-abort-return-value-string.tentative.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<title>Aborting fetch for javascript:string navigation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#navigate">
+<link rel="help" href="https://github.com/whatwg/html/issues/2590">
+<div id="log"></div>
+<iframe src="support/iframe-and-links.html"></iframe>
+<script>
+async_test(test => {
+ onload = () => {
+ const child = document.querySelector('iframe').contentWindow;
+ child.document.querySelector("#slowLink").click();
+ // The step below is in a timeout. The framed page can't communicate back mid-parse because that
+ // would involve running script, which makes that navigation "mature", and we need to do this
+ // before it matures.
+ test.step_timeout(() => {
+ child.document.querySelector("#javascriptStringLink").click();
+ child.document.querySelector("iframe").onload = test.step_func_done(() => {
+ assert_false(child.childLoaded, 'child.childLoaded');
+ });
+ }, 100);
+ };
+ window.javascriptStringDocLoaded = test.step_func(() => {
+ assert_unreached("javascript: URL doc replaced the document, should be targeted to child iframe.");
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/javascript-url-abort-return-value-undefined.tentative.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/javascript-url-abort-return-value-undefined.tentative.html
new file mode 100644
index 0000000000..80a0d27a7d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/javascript-url-abort-return-value-undefined.tentative.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>Not aborting fetch for javascript:undefined navigation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#navigate">
+<link rel="help" href="https://github.com/whatwg/html/issues/2590">
+<div id="log"></div>
+<iframe src="support/iframe-and-links.html"></iframe>
+<script>
+async_test(test => {
+ onload = () => {
+ const child = document.querySelector('iframe').contentWindow;
+ child.document.querySelector("#slowLink").click();
+ // The step below is in a timeout. The framed page can't communicate back mid-parse because that
+ // would involve running script, which makes that navigation "mature", and we need to do this
+ // before it matures.
+ test.step_timeout(() => {
+ child.document.querySelector("#javascriptUndefinedLink").click();
+ child.document.querySelector("iframe").onload = test.step_func_done(() => {
+ assert_true(child.childLoaded, 'child.childLoaded');
+ });
+ }, 100);
+ };
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/support/iframe-and-links.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/support/iframe-and-links.html
new file mode 100644
index 0000000000..545b0988ba
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/support/iframe-and-links.html
@@ -0,0 +1,18 @@
+<!doctype html>
+
+<iframe name="iframe"></iframe>
+
+<!-- slow link's response is delayed by 1 second -->
+<!-- https://wptserve.readthedocs.io/en/latest/pipes.html#trickle -->
+<a target="iframe" href="set-child-loaded.html?pipe=trickle(d1)" id="slowLink">slow link</a>
+<a target="iframe" href="javascript:'javascript:string <script> parent.javascriptStringDocLoaded(); </script>'" id="javascriptStringLink">javascript:string link</a>
+<a target="iframe" href="javascript:undefined" id="javascriptUndefinedLink">javascript:undefined link</a>
+
+<script>
+// set-child-loaded.html (the slow link) sets this to true.
+window.childLoaded = false;
+
+// Do nothing when the javascript:string doc has loaded, if it's correctly targeted to the above iframe.
+// However, if it replaces this document, it needs to fail the test (handled in the parent).
+function javascriptStringDocLoaded() {}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/support/set-child-loaded.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/support/set-child-loaded.html
new file mode 100644
index 0000000000..a4b34ad6e9
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-abort/support/set-child-loaded.html
@@ -0,0 +1,5 @@
+<!doctype html>
+set-child-loaded.html
+<script>
+parent.childLoaded = true;
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-global-scope.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-global-scope.html
new file mode 100644
index 0000000000..d3dd38ebed
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-global-scope.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+
+<a id="javascript-link" href="javascript:changeStatus()">link</a>
+
+<script>
+function changeStatus() {
+ t.done();
+}
+
+var t = async_test(function(t) {
+ document.querySelector("#javascript-link").click();
+}, "javascript: scheme urls should be executed in current global scope");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-load-as-html.xhtml b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-load-as-html.xhtml
new file mode 100644
index 0000000000..b23bef5917
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-load-as-html.xhtml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="windows-1250"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="../resources/helpers.js"></script>
+ <meta charset="windows-1250"/>
+ <title>javascript: URL navigation to a string must create a HTML document using the correct properties</title>
+</head>
+<body>
+ <!--
+ This document is XHTML and windows-1250 so that we can test the resulting javascript: URL document is not.
+ The same for the window we open.
+ -->
+ <script><![CDATA[
+ promise_test(async (t) => {
+ const w = await openWindow("resources/xhtml-and-non-utf-8.xhtml", t);
+
+ w.location.href = `javascript:'a string<script>
+ opener.postMessage({
+ compatMode: document.compatMode,
+ contentType: document.contentType,
+ characterSet: document.characterSet,
+ doctypeIsNull: document.doctype === null
+ }, "*");
+ <` + `/script>'`;
+
+ const results = await waitForMessage(w);
+
+ assert_equals(results.compatMode, "BackCompat");
+ assert_equals(results.contentType, "text/html");
+ assert_equals(results.characterSet, "UTF-8");
+ assert_equals(results.doctypeIsNull, true);
+ });
+ ]]></script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-no-beforeunload.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-no-beforeunload.window.js
new file mode 100644
index 0000000000..47e8f11797
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-no-beforeunload.window.js
@@ -0,0 +1,80 @@
+// META: script=../resources/helpers.js
+
+for (const stringCompletion of [false, true]) {
+ const testNameSuffix = stringCompletion ? ": string completion" : ": undefined completion";
+
+ testNoBeforeunload(
+ { testRunnerWindow: "top", stringCompletion },
+ async (t, urlToSet) => {
+ const iframe = await addIframe();
+ iframe.contentWindow.location.href = urlToSet;
+
+ return iframe.contentWindow;
+ },
+ `Navigating an iframe via location.href to a javascript: URL must not fire beforeunload${testNameSuffix}`
+ );
+
+ testNoBeforeunload(
+ { testRunnerWindow: "top", stringCompletion },
+ async (t, urlToSet) => {
+ const iframe = await addIframe();
+ iframe.src = urlToSet;
+
+ return iframe.contentWindow;
+ },
+ `Navigating an iframe via src="" to a javascript: URL after insertion must not fire beforeunload${testNameSuffix}`
+ );
+
+ testNoBeforeunload(
+ { testRunnerWindow: "opener", stringCompletion },
+ async (t, urlToSet) => {
+ const w = await openWindow("/common/blank.html", t);
+ w.location.href = urlToSet;
+
+ return w;
+ },
+ `Navigating an opened window via location.href to a javascript: URL must not fire beforeunload${testNameSuffix}`
+ );
+
+
+ testNoBeforeunload(
+ { testRunnerWindow: "opener", stringCompletion },
+ async (t, urlToSet) => {
+ const w = await openWindow("../resources/has-iframe.html", t);
+ w.frames[0].onbeforeunload = t.unreached_func("beforeunload must not fire on the iframe");
+ w.location.href = urlToSet;
+
+ return w;
+ },
+ `Navigating an opened window with an iframe via location.href to a javascript: URL must not fire beforeunload on the iframe${testNameSuffix}`
+ );
+}
+
+function testNoBeforeunload({ testRunnerWindow, stringCompletion }, setupAndNavigateFunc, description) {
+ promise_test(async t => {
+ t.add_cleanup(() => {
+ delete window.resolveTestPromise;
+ });
+
+ const ranPromise = new Promise(resolve => {
+ window.resolveTestPromise = resolve;
+ });
+
+ const urlToSet = makeURL({ testRunnerWindow, stringCompletion });
+ const w = await setupAndNavigateFunc(t, urlToSet);
+ w.onbeforeunload = t.unreached_func("beforeunload must not fire");
+
+ await ranPromise;
+ if (stringCompletion) {
+ await waitForMessage(w);
+ }
+ }, description);
+}
+
+function makeURL({ testRunnerWindow, stringCompletion }) {
+ const completion = stringCompletion ?
+ `"a string<script>window.${testRunnerWindow}.postMessage('ready', '*');</script>";` :
+ `undefined;`;
+
+ return `javascript:window.${testRunnerWindow}.resolveTestPromise();${completion};`;
+}
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-query-fragment-components.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-query-fragment-components.html
new file mode 100644
index 0000000000..eced9646e5
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-query-fragment-components.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<title> javascript url with query and fragment components </title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+var a = null;
+var b = null;
+var c = null;
+</script>
+
+<iframe id="a" src='javascript:"nope" ? "yep" : "what";'></iframe>
+<iframe id="b" src='javascript:"wrong"; // # %0a "ok";'></iframe>
+<iframe id="c" src='javascript:"%252525 ? %252525 # %252525"'></iframe>
+
+<script>
+var t = async_test("iframes with javascript src");
+function check(id, expected) {
+ assert_equals(
+ document.getElementById(id).contentDocument.body.textContent,
+ expected);
+}
+onload = t.step_func(function() {
+ check("a", "yep");
+ check("b", "ok");
+ check("c", "%2525 ? %2525 # %2525");
+ t.done();
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-referrer.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-referrer.window.js
new file mode 100644
index 0000000000..1f11429c9e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-referrer.window.js
@@ -0,0 +1,38 @@
+// META: script=../resources/helpers.js
+// META: title=javascript: URL navigation to a string must create a document whose referrer is the navigation initiator
+
+const originalURL = location.href;
+
+const testCases = [
+ ["unsafe-url", location.href],
+ ["origin", self.origin + "/"],
+ ["no-referrer", ""]
+];
+
+for (const [referrerPolicyForStartingWindowCreation, expectedReferrer] of testCases) {
+ promise_test(async (t) => {
+ const meta = document.createElement("meta");
+ meta.name = "referrer";
+ meta.content = referrerPolicyForStartingWindowCreation;
+ t.add_cleanup(() => meta.remove());
+ document.head.append(meta);
+
+ const w = await openWindow("/common/blank.html", t);
+ const originalReferrer = w.document.referrer;
+ assert_equals(originalReferrer, expectedReferrer,
+ "Sanity check: opened window's referrer is set correctly");
+
+ // Mess with the current document's URL so that the initiator URL is different. Then, if that
+ // shows up as the javascript: URL document's referrer, we know the navigation initiator's URL is
+ // being used as the referrer, which is incorrect.
+ history.replaceState(undefined, "", "/incorrect-referrer.html");
+ t.add_cleanup(() => history.replaceState(undefined, "", originalURL));
+
+ w.location.href = `javascript:'a string<script>opener.postMessage(document.referrer, "*");</script>'`;
+
+ const referrer = await waitForMessage(w);
+
+ assert_equals(referrer, originalReferrer,
+ "javascript: URL-created document's referrer equals the previous document's referrer");
+ }, `${referrerPolicyForStartingWindowCreation} referrer policy used to create the starting page`);
+}
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-return-value-handling-dynamic.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-return-value-handling-dynamic.html
new file mode 100644
index 0000000000..3c08d29674
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-return-value-handling-dynamic.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<meta charset=windows-1252> <!-- intentionally not UTF-8 to test that the javascript: frames are forced to UTF-8 -->
+<title>Test javascript URL string return values in direct and indirect (target) frame contexts.</title>
+<!-- Waiting on https://github.com/whatwg/html/pull/6781 to be non-tentative. -->
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+const testCases = [
+ [[0x41]],
+ [[0x80,0xFF]],
+ [[0x80,0xFF,0x100]],
+ [[0xD83D,0xDE0D]],
+ [[0xDE0D,0x41], [0xFFFD,0x41]]
+];
+
+function formatCharCodes(charCodes) {
+ return charCodes.map(code => code.toString(16).toUpperCase().padStart(4, '0')).join(" ");
+}
+
+for (const [input, expected = input] of testCases) {
+ const javascriptURL = `javascript:String.fromCharCode(${input})`;
+ const output = String.fromCharCode(...expected);
+
+ async_test(t => {
+ const frame = document.createElement("iframe");
+ t.add_cleanup(() => frame.remove());
+ frame.src = javascriptURL;
+
+ t.step_timeout(() => {
+ assert_equals(frame.contentDocument.body.textContent, output);
+ assert_equals(frame.contentDocument.charset, "UTF-8");
+ t.done();
+ }, 200);
+
+ document.body.appendChild(frame);
+ }, `${formatCharCodes(input)} set in src=""`);
+
+ async_test(t => {
+ const frame = document.createElement("iframe");
+ const href = document.createElement("a");
+ t.add_cleanup(() => { frame.remove(); href.remove(); });
+ frame.name = "hi" + input;
+ href.target = "hi" + input;
+ href.href = javascriptURL;
+
+ t.step_timeout(() => {
+ assert_equals(frame.contentDocument.body.textContent, output);
+ assert_equals(frame.contentDocument.charset, "UTF-8");
+ t.done();
+ }, 200)
+
+ document.body.appendChild(frame);
+ document.body.appendChild(href);
+ href.click();
+ }, `${formatCharCodes(input)} set in href="" targeting a frame and clicked`);
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-return-value-handling.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-return-value-handling.html
new file mode 100644
index 0000000000..621a8cbaec
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-return-value-handling.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test that javascript: evaluation only performs a navigation to the
+ result when the result is a string value.</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<iframe src="javascript:'1'"></iframe>
+<iframe src="javascript:1"></iframe>
+<iframe src="javascript:({ toString: function() { return '1'; } })"></iframe>
+<iframe src="javascript:undefined"></iframe>
+<iframe src="javascript:null"></iframe>
+<iframe src="javascript:true"></iframe>
+<iframe src="javascript:new String('1')"></iframe>
+<script>
+ var t = async_test();
+ onload = t.step_func_done(function() {
+ assert_equals(frames[0].document.documentElement.textContent,
+ "1", "string return should cause navigation");
+ // The rest of the test is disabled for now, until
+ // https://github.com/whatwg/html/issues/1895 gets sorted out
+/*
+ assert_equals(frames[1].document.documentElement.textContent,
+ "", "number return should not cause navigation");
+ assert_equals(frames[2].document.documentElement.textContent,
+ "", "object return should not cause navigation");
+ assert_equals(frames[3].document.documentElement.textContent,
+ "", "undefined return should not cause navigation");
+ assert_equals(frames[4].document.documentElement.textContent,
+ "", "null return should not cause navigation");
+ assert_equals(frames[5].document.documentElement.textContent,
+ "", "null return should not cause navigation");
+ assert_equals(frames[6].document.documentElement.textContent,
+ "", "String object return should not cause navigation");
+*/
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-failure.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-failure.sub.html
new file mode 100644
index 0000000000..a153ad3e48
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-failure.sub.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>javascript: URL security check</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+"use strict";
+
+const cases = [
+ ["cross-origin", "http://{{hosts[][www]}}:{{ports[http][0]}}/common/blank.html"],
+ ["cross-origin-domain but same-origin", "/html/browsers/windows/resources/document-domain-setter.html"]
+];
+
+for (const [description, url] of cases) {
+ promise_test(async t => {
+ const iframe = await insertIframe(t, url);
+
+ const unreached = t.unreached_func("message event fired");
+ t.add_cleanup(() => window.removeEventListener("message", unreached));
+ window.addEventListener("message", unreached);
+
+ iframe.src = `javascript:parent.postMessage("boo", "*")`;
+
+ // If no message was received after this time, the test passes.
+ await new Promise(r => t.step_timeout(r, 50));
+ }, `${description}, setting src`);
+
+ promise_test(async t => {
+ const iframe = await insertIframe(t, url);
+
+ const unreached = t.unreached_func("message event fired");
+ t.add_cleanup(() => window.removeEventListener("message", unreached));
+ window.addEventListener("message", unreached);
+
+ iframe.contentWindow.location.href = `javascript:parent.postMessage("boo", "*")`;
+
+ // If no message was received after this time, the test passes.
+ await new Promise(r => t.step_timeout(r, 50));
+ }, `${description}, setting location.href`);
+}
+
+function insertIframe(t, url) {
+ return new Promise((resolve, reject) => {
+ const iframe = document.createElement("iframe");
+ iframe.src = url;
+ iframe.onload = () => resolve(iframe);
+ iframe.onerror = () => reject(new Error("Failed to load the outer iframe"));
+
+ t.add_cleanup(() => iframe.remove());
+
+ document.body.append(iframe);
+ });
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-multi-globals.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-multi-globals.sub.html
new file mode 100644
index 0000000000..4b9d3b7afa
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-multi-globals.sub.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Multi-globals: which one is the initiator for the javascript: URL security check?</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+"use strict";
+document.domain = "{{hosts[][]}}";
+
+// These tests would fail if a different pair of origins were compared (see, e.g., the discussion in
+// https://github.com/whatwg/html/issues/6514).
+
+promise_test(async t => {
+ const iframe = await insertIframe(t);
+ const innerIframe = iframe.contentDocument.querySelector("iframe");
+
+ // - incumbentNavigationOrigin = this page's origin, http://{{hosts[][]}}:{{ports[http][0]}}
+ // - iframe's current origin is this origin, http://{{hosts[][]}}:{{ports[http][0]}}.
+ // javascript:'s security check uses incumbentNavigationOrigin vs. the iframe's current origin
+ // so the check will pass and the result will get written.
+ innerIframe.src = "javascript:'test'";
+
+ await waitForLoad(innerIframe, "Failed to load the javascript: URL");
+
+ assert_equals(innerIframe.contentDocument.body.textContent, "test");
+}, "Using iframeEl.src");
+
+promise_test(async t => {
+ const iframe = await insertIframe(t);
+ const innerIframe = iframe.contentDocument.querySelector("iframe");
+
+ // Here, https://html.spec.whatwg.org/#location-object-navigate sets the source browsing context to the
+ // incumbent settings object's browsing context. So incumbentNavigationOrigin = this page's origin,
+ // http://{{hosts[][]}}:{{ports[http][0]}}.
+ //
+ // So again, the check will pass.
+
+ iframe.contentWindow.frames[0].location.href = "javascript:'test'";
+
+ await waitForLoad(innerIframe, "Failed to load the javascript: URL");
+
+ assert_equals(innerIframe.contentDocument.body.textContent, "test");
+}, "Using location.href");
+
+function insertIframe(t) {
+ return new Promise((resolve, reject) => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "http://{{hosts[][www]}}:{{ports[http][0]}}/html/browsers/browsing-the-web/navigating-across-documents/resources/multi-globals-subframe-1.sub.html";
+ iframe.onload = () => resolve(iframe);
+ iframe.onerror = () => reject(new Error("Failed to load the outer iframe"));
+
+ t.add_cleanup(() => iframe.remove());
+
+ document.body.append(iframe);
+ });
+}
+
+function waitForLoad(iframe, errorMessage = "Failed to load iframe") {
+ return new Promise((resolve, reject) => {
+ iframe.onload = () => resolve(iframe);
+ iframe.onerror = () => reject(new Error(errorMessage));
+ });
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-same-origin-domain.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-same-origin-domain.sub.html
new file mode 100644
index 0000000000..a14a13cfd6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-security-check-same-origin-domain.sub.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>javascript: URL security check for same-origin-domain but not same-origin</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe src="http://{{hosts[][www]}}:{{ports[http][0]}}/html/browsers/browsing-the-web/navigating-across-documents/resources/document-domain-set-to-site.sub.html"></iframe>
+<script>
+"use strict";
+document.domain = "{{host}}";
+
+setup({ explicit_done: true });
+
+window.onload = () => {
+ async_test(t => {
+ assert_equals(frames[0].document.body.textContent, "", "before");
+
+ window.onmessage = t.step_func_done(() => {
+ assert_equals(frames[0].document.body.textContent, "new", "after");
+ });
+
+ frames[0].location.href = "javascript:parent.postMessage('done', '*'); 'new';";
+ });
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-task-queuing.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-task-queuing.html
new file mode 100644
index 0000000000..1bb05bfb19
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-task-queuing.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>javascript: URL task queuing</title>
+<link rel="help" href="https://github.com/whatwg/html/issues/3730">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+"use strict";
+
+testIsAsync(() => {
+ const iframe = document.createElement("iframe");
+ document.body.append(iframe);
+ iframe.contentWindow.location.href = "javascript:window.top.javascriptURLRan = true; window.top.resolveTestPromise();";
+}, `Navigating an iframe via location.href to a javascript: URL must queue a task`);
+
+testIsAsync(() => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "javascript:window.top.javascriptURLRan = true; window.top.resolveTestPromise();";
+ document.body.append(iframe);
+}, `Navigating an iframe via src="" to a javascript: URL before insertion must queue a task`);
+
+testIsAsync(() => {
+ const iframe = document.createElement("iframe");
+ document.body.append(iframe);
+ iframe.src = "javascript:window.top.javascriptURLRan = true; window.top.resolveTestPromise();";
+}, `Navigating an iframe via src="" to a javascript: URL after insertion must queue a task`);
+
+testIsAsync(() => {
+ const w = window.open();
+ w.location.href = "javascript:window.opener.javascriptURLRan = true; window.opener.resolveTestPromise();";
+}, `Navigating an opened window via location.href to a javascript: URL must queue a task`);
+
+testIsAsync(() => {
+ window.open("javascript:window.opener.javascriptURLRan = true; window.opener.resolveTestPromise();");
+}, `Navigating an opened window as part of creation to a javascript: URL must queue a task`);
+
+function testIsAsync(setupFunc, description) {
+ promise_test(async t => {
+ t.add_cleanup(() => {
+ delete window.resolveTestPromise;
+ delete window.javascriptURLRan;
+ });
+
+ const ranPromise = new Promise(resolve => {
+ window.resolveTestPromise = resolve;
+ });
+
+ setupFunc();
+
+ assert_equals(window.javascriptURLRan, undefined, "Must not run sync");
+
+ // Ensure that we do actually run the code, though.
+ await ranPromise;
+ }, description);
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location-assign.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location-assign.html
new file mode 100644
index 0000000000..cb2984d409
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location-assign.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/context-helper.js"></script>
+<script>
+window.scriptToRun = 'relevantWindow.location.assign("target.html");';
+
+async_test(t => {
+ window.addEventListener("message", t.step_func_done(function(e) {
+ // Base URL used for parsing a relative URL to `target.html`
+ // should be the base URL of the entry settings object in
+ // https://html.spec.whatwg.org/C/#dom-location-assign
+ assert_equals(
+ e.data.location,
+ new URL('target.html', entryUrl).href,
+ 'Base URL should use the entry settings object');
+
+ // `document.referrer` should reflect the source browsing context,
+ // which is the incumbent in
+ // https://html.spec.whatwg.org/C/#location-object-navigate
+ assert_equals(
+ e.data.referrer, incumbentUrl,
+ 'Referrer should use the incumbent settings object');
+ }));
+}, 'Fetch client and URL resolution for location.assign()');
+</script>
+<iframe id="entry" src="entry/entry.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location-href.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location-href.html
new file mode 100644
index 0000000000..02ff214ed5
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location-href.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/context-helper.js"></script>
+<script>
+window.scriptToRun = 'relevantWindow.location.href = "target.html";';
+
+async_test(t => {
+ window.addEventListener("message", t.step_func_done(function(e) {
+ // Base URL used for parsing a relative URL to `target.html`
+ // should be the base URL of the entry settings object in
+ // https://html.spec.whatwg.org/C/#dom-location-href
+ assert_equals(
+ e.data.location,
+ new URL('target.html', entryUrl).href,
+ 'Base URL should use the entry settings object');
+
+ // `document.referrer` should reflect the source browsing context,
+ // which is the incumbent in
+ // https://html.spec.whatwg.org/C/#location-object-navigate
+ assert_equals(
+ e.data.referrer, incumbentUrl,
+ 'Referrer should use the incumbent settings object');
+ }));
+}, 'Fetch client and URL resolution for location.href setter');
+</script>
+<iframe id="entry" src="entry/entry.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location.html
new file mode 100644
index 0000000000..fae17dd2ac
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-location.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/context-helper.js"></script>
+<script>
+window.scriptToRun = 'relevantWindow.location = "target.html";';
+
+async_test(t => {
+ window.addEventListener("message", t.step_func_done(function(e) {
+ // Base URL used for parsing a relative URL to `target.html`
+ // should be the base URL of the entry settings object in
+ // https://html.spec.whatwg.org/C/#dom-location-assign
+ assert_equals(
+ e.data.location,
+ new URL('target.html', entryUrl).href,
+ 'Base URL should use the entry settings object');
+
+ // `document.referrer` should reflect the source browsing context,
+ // which is the incumbent in
+ // https://html.spec.whatwg.org/C/#location-object-navigate
+ assert_equals(
+ e.data.referrer, incumbentUrl,
+ 'Referrer should use the incumbent settings object');
+ }));
+}, 'Fetch client and URL resolution for location setter');
+</script>
+<iframe id="entry" src="entry/entry.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-window-open.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-window-open.html
new file mode 100644
index 0000000000..0a391ef28e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/context-for-window-open.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/context-helper.js"></script>
+<script>
+window.scriptToRun =
+ 'relevantWindow.open("target.html", "target");';
+
+async_test(t => {
+ window.addEventListener("message", t.step_func_done(function(e) {
+ // Base URL used for parsing a relative URL to `target.html`
+ // should be the base URL of the entry settings object in
+ // https://html.spec.whatwg.org/C/#window-open-steps
+ assert_equals(
+ e.data.location,
+ new URL('target.html', entryUrl).href,
+ 'Base URL should use the entry settings object');
+
+ // `document.referrer` should reflect the source browsing context,
+ // which is the entry in
+ // https://html.spec.whatwg.org/C/#window-open-steps
+ assert_equals(
+ e.data.referrer, entryUrl,
+ 'Referrer should use the entry settings object');
+ }));
+}, 'Fetch client and URL resolution for window.open()');
+</script>
+<iframe id="entry" src="entry/entry.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/entry/entry.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/entry/entry.html
new file mode 100644
index 0000000000..82fecfdd38
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/entry/entry.html
@@ -0,0 +1,4 @@
+<body onload="top.go()">
+<iframe id="incumbent" src="../incumbent/empty.html"></iframe>
+<iframe id="relevant" src="../relevant/empty.html"></iframe>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/entry/target.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/entry/target.html
new file mode 100644
index 0000000000..5ceaeaf07e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/entry/target.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<script src="../resources/target.js"></script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/incumbent/empty.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/incumbent/empty.html
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/incumbent/empty.html
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/incumbent/target.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/incumbent/target.html
new file mode 100644
index 0000000000..5ceaeaf07e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/incumbent/target.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<script src="../resources/target.js"></script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/relevant/empty.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/relevant/empty.html
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/relevant/empty.html
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/relevant/target.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/relevant/target.html
new file mode 100644
index 0000000000..5ceaeaf07e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/relevant/target.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<script src="../resources/target.js"></script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/resources/context-helper.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/resources/context-helper.js
new file mode 100644
index 0000000000..dda338b4cc
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/resources/context-helper.js
@@ -0,0 +1,34 @@
+// Usage: in the top-level Window, include:
+//
+// <script src="resources/context-helper.js"></script>
+// <script>
+// window.scriptToRun = '...';
+// </script>
+// <iframe id="entry" src="entry/entry.html"></iframe>
+//
+// Then `scriptToRun` is evaluated, with:
+// - The entry Realm is that of entry/entry.html
+// - The incumbent Realm is that of incumbent/empty.html
+// - The relevant Realm of `relevantWindow`, `relevantWindow.location` etc. is
+// that of relevant/empty.html
+
+window.scriptToRun = '';
+
+const entryUrl = new URL('entry/entry.html', location).href;
+const incumbentUrl = new URL('incumbent/empty.html', location).href;
+const relevantUrl = new URL('relevant/empty.html', location).href;
+
+function go() {
+ const entry = document.querySelector('#entry');
+ const incumbent = entry.contentDocument.querySelector('#incumbent');
+ const incumbentScript = incumbent.contentDocument.createElement('script');
+ incumbentScript.textContent = `
+ function go() {
+ const relevantWindow =
+ parent.document.querySelector('#relevant').contentWindow;
+ ${window.scriptToRun}
+ }
+ `;
+ incumbent.contentDocument.head.appendChild(incumbentScript);
+ incumbent.contentWindow.go();
+}
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/resources/target.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/resources/target.js
new file mode 100644
index 0000000000..e3a507b0d8
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/multiple-globals/resources/target.js
@@ -0,0 +1,11 @@
+window.onload = function() {
+ let testWindow;
+ if (opener) {
+ testWindow = opener.top;
+ } else {
+ testWindow = top;
+ }
+ testWindow.postMessage(
+ {location: location.href, referrer: document.referrer},
+ "*");
+}
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url-with-fragment-fire-load-event.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url-with-fragment-fire-load-event.html
new file mode 100644
index 0000000000..f74bbfd7d3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url-with-fragment-fire-load-event.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<body>
+<script>
+async_test(t => {
+ const crossOriginUrl = new URL(get_host_info().HTTPS_REMOTE_ORIGIN);
+ crossOriginUrl.pathname = "/common/blank.html";
+ const i = document.createElement("iframe");
+ i.src = crossOriginUrl;
+ document.body.appendChild(i);
+
+ let wasLoadEventFired = false;
+ i.onload = t.step_func(() => {
+ // Though iframe is cross-origin and changing hash leads soft reload, the
+ // load event should be fired to protect sensitive information.
+ // See: https://crbug.com/1248444
+ crossOriginUrl.hash = "#foo";
+ i.onload = () => {
+ assert_false(wasLoadEventFired)
+ wasLoadEventFired = true;
+ // Wait for a while to ensure other onload events are never fired.
+ t.step_timeout(() => t.done(), 100);
+ };
+ i.src = crossOriginUrl;
+ });
+
+}, "Changing the URL hash of a cross-origin iframe should fire a load event");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url-with-fragment.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url-with-fragment.html
new file mode 100644
index 0000000000..ed228ad59b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url-with-fragment.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<body>
+<script>
+async_test(t => {
+ let starting_history_length = history.length;
+ let cross_origin_url = new URL(get_host_info().HTTPS_REMOTE_ORIGIN);
+ cross_origin_url.pathname = "/common/blank.html";
+ cross_origin_url.hash = "#foo";
+ let i = document.createElement("iframe");
+ i.src = cross_origin_url;
+ document.body.appendChild(i);
+
+ window.onload = () => t.step_timeout(() => {
+ assert_equals(starting_history_length, history.length);
+ i.src = cross_origin_url;
+ // Give the navigation time to happen - no events will fire.
+ t.step_timeout(() => {
+ assert_equals(starting_history_length + 1, history.length);
+ t.done();
+ }, 100);
+ }, 0);
+}, "Navigating a cross-origin iframe to its current url should not replace");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url.html
new file mode 100644
index 0000000000..9996d58914
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-cross-origin-iframe-to-same-url.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<body>
+<script>
+async_test(t => {
+ let starting_history_length = history.length;
+ let cross_origin_url = new URL(get_host_info().HTTPS_REMOTE_ORIGIN);
+ cross_origin_url.pathname = "/common/blank.html";
+ let i = document.createElement("iframe");
+ i.src = cross_origin_url;
+ document.body.appendChild(i);
+
+ window.onload = () => t.step_timeout(() => {
+ assert_equals(starting_history_length, history.length);
+ i.onload = t.step_func_done(() => assert_equals(starting_history_length + 1, history.length));
+ i.src = cross_origin_url;
+ }, 0);
+}, "Navigating a cross-origin iframe to its current url should not replace");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-to-unparseable-url.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-to-unparseable-url.html
new file mode 100644
index 0000000000..d20a440d03
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigate-to-unparseable-url.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>location.href unparseable URL throws a SyntaxError DOMException</title>
+<link rel="help" href="https://html.spec.whatwg.org/#the-location-interface:dom-location-href-2">
+<link rel="help" href="https://html.spec.whatwg.org/#following-hyperlinks-2">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+const kUnparseableURL = self.origin + ":notaport/common/blank.html";
+
+promise_test(async t => {
+ const win = window.open("/common/blank.html");
+ t.add_cleanup(() => {
+ win.close();
+ });
+
+ await new Promise(resolve => {
+ win.onload = resolve;
+ });
+
+ assert_throws_dom("SyntaxError", win.DOMException, () => {
+ win.location.href = kUnparseableURL;
+ }, "location.href setter throws a SyntaxError DOMException");
+}, "location.href setter throws a SyntaxError DOMException for unparseable " +
+ "URLs");
+
+promise_test(async t => {
+ const win = window.open("/common/blank.html");
+ t.add_cleanup(() => {
+ win.close();
+ });
+
+ await new Promise(resolve => {
+ win.onload = resolve;
+ });
+
+ // If the newly-opened window tries to navigate, fail the test.
+ const failPromise = new Promise((resolve, reject) => {
+ win.onpagehide = () =>
+ reject(new Error("Navigation was attempted to unparseable URL"));
+ });
+
+ // A promise to wait on to confirm the newly-opened window did not navigate.
+ const successPromise = new Promise(resolve => {
+ t.step_timeout(resolve, 2000);
+ });
+
+ const a = win.document.createElement('a');
+ a.href = kUnparseableURL;
+ win.document.body.append(a);
+ a.click();
+
+ return Promise.race([successPromise, failPromise]);
+}, "<a> tag navigate fails for unparseable URLs");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-cross-origin.sub.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-cross-origin.sub.window.js
new file mode 100644
index 0000000000..f23e2c440b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-cross-origin.sub.window.js
@@ -0,0 +1,15 @@
+// META: title=Cross-origin navigation started from unload handler must be ignored
+// META: script=../resources/helpers.js
+
+promise_test(async () => {
+ const iframe = await addIframe();
+
+ iframe.contentWindow.addEventListener("unload", () => {
+ iframe.contentWindow.location.href = "//{{hosts[][www]}}/common/blank.html?fail";
+ });
+
+ iframe.src = "/common/blank.html?pass";
+
+ await waitForIframeLoad(iframe);
+ assert_equals(iframe.contentWindow.location.search, "?pass");
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-data-url.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-data-url.window.js
new file mode 100644
index 0000000000..cf39b01107
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-data-url.window.js
@@ -0,0 +1,15 @@
+// META: title=data: URL navigation started from unload handler must be ignored
+// META: script=../resources/helpers.js
+
+promise_test(async () => {
+ const iframe = await addIframe();
+
+ iframe.contentWindow.addEventListener("unload", () => {
+ iframe.contentWindow.location.href =
+ `data:text/html,unload<script>parent.postMessage('fail', '*');</script>`;
+ });
+
+ iframe.src =
+ `data:text/html,load<script>parent.postMessage('pass', '*')</script>`;
+ assert_equals(await waitForMessage(iframe.contentWindow), "pass");
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit-1.html
new file mode 100644
index 0000000000..e06def9b20
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit-1.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+
+<h1>navigation-unload-form-submit-1.html</h1>
+
+<script>
+let isSubmit = false;
+
+window.unload = function () {
+ window.location = 'is-Submit' + isSubmit;
+}
+
+function setIsSubmit() {
+ isSubmit = true;
+}
+</script>
+
+<form onsubmit="setIsSubmit" action="navigation-unload-form-submit-2.html">
+ <input type="submit">
+</form>
+
+<script>
+parent.finishedLoading();
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit-2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit-2.html
new file mode 100644
index 0000000000..43cd3c1b33
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit-2.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+
+<h1>navigation-unload-form-submit-2.html</h1>
+
+<script>
+parent.finishedLoading();
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit.html
new file mode 100644
index 0000000000..029170d46a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-form-submit.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/browsers.html#navigating-across-documents">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<iframe id="i" src="navigation-unload-form-submit-1.html"></iframe>
+
+<!-- derived from https://bugzilla.mozilla.org/show_bug.cgi?id=247660#c0 -->
+
+<script>
+var test = async_test('Tests that navigation during an unload caused by a form submit does nothing');
+window.onload = test.step_func(function() {
+ var i = document.querySelector('#i');
+
+ window.finishedLoading = test.step_func_done(function () {
+ assert_equals(i.contentWindow.location.pathname.split('/').pop(), 'navigation-unload-form-submit-2.html');
+ assert_equals(i.contentWindow.location.hash, '');
+ });
+
+ i.contentWindow.document.querySelector('input[type="submit"]').click();
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-javascript-url.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-javascript-url.window.js
new file mode 100644
index 0000000000..abbcb888a0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-javascript-url.window.js
@@ -0,0 +1,15 @@
+// META: title=javascript: URL navigation started from unload handler must be ignored
+// META: script=../resources/helpers.js
+
+promise_test(async () => {
+ const iframe = await addIframe();
+
+ iframe.contentWindow.addEventListener("unload", () => {
+ iframe.contentWindow.location.href =
+ `javascript:"unload<script>parent.postMessage('fail', '*');</script>"`;
+ });
+
+ iframe.src =
+ `javascript:"load<script>parent.postMessage('pass', '*')</script>"`;
+ assert_equals(await waitForMessage(iframe.contentWindow), "pass");
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment-1.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment-1.html
new file mode 100644
index 0000000000..3b5d725fee
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment-1.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+
+<h1>navigation-unload-same-origin-fragment-1.html</h1>
+
+<script>
+if (parent.finishedLoading) {
+ parent.finishedLoading();
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment-2.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment-2.html
new file mode 100644
index 0000000000..2fb7f50b43
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment-2.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+
+<h1>navigation-unload-same-origin-fragment-2.html</h1>
+
+<script>
+if (parent.finishedLoading) {
+ parent.finishedLoading();
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment.html
new file mode 100644
index 0000000000..c4dceb9e04
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/browsers.html#navigating-across-documents">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<iframe id="i" src="navigation-unload-same-origin-fragment-1.html"></iframe>
+
+<!-- a timeout indicates that setting i.contentWindow.location.hash (a second navigation) aborted the first navigation,
+ and so it stayed on a.html and finishedLoading was never called -->
+
+<script>
+var test = async_test('Tests that a fragment navigation in the unload handler will not block the initial navigation');
+window.onload = test.step_func(function() {
+ var i = document.querySelector('#i');
+
+ i.contentWindow.onunload = test.step_func(function() {
+ i.contentWindow.location.hash = '#fragment';
+ });
+
+ window.finishedLoading = test.step_func_done(function () {
+ assert_equals(i.contentWindow.location.pathname.split('/').pop(), 'navigation-unload-same-origin-fragment-2.html');
+ assert_equals(i.contentWindow.location.hash, '');
+ });
+
+ i.contentWindow.location.href = 'navigation-unload-same-origin-fragment-2.html';
+});
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin.window.js
new file mode 100644
index 0000000000..826af4b75b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin.window.js
@@ -0,0 +1,15 @@
+// META: title=Same-origin navigation started from unload handler must be ignored
+// META: script=../resources/helpers.js
+
+promise_test(async () => {
+ const iframe = await addIframe();
+
+ iframe.contentWindow.addEventListener("unload", () => {
+ iframe.contentWindow.location.href = "/common/blank.html?fail";
+ });
+
+ iframe.src = "/common/blank.html?pass";
+
+ await waitForIframeLoad(iframe);
+ assert_equals(iframe.contentWindow.location.search, "?pass");
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/plugin-document.historical.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/plugin-document.historical.html
new file mode 100644
index 0000000000..547917b795
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/plugin-document.historical.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Same-origin PDFs must not create accessible Document objects</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- https://github.com/whatwg/html/pull/6947 -->
+
+<iframe src="resources/portable-document-format-sample-valid.pdf"></iframe>
+
+<script>
+setup({ explicit_done: true });
+
+window.onload = () => {
+ test(() => {
+ assert_throws_dom("SecurityError", () => {
+ document.querySelector("iframe").contentWindow.document;
+ });
+ });
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-about.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-about.window.js
new file mode 100644
index 0000000000..5480911895
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-about.window.js
@@ -0,0 +1,34 @@
+"use strict";
+
+["about:blank", "about:srcdoc", "about:nonstandard"].forEach(aboutURL => {
+ promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = `resources/redirect.py?location=${aboutURL}`;
+ document.body.append(iframe);
+
+ // Unfortunately Firefox does not fire a load event for network errors yet, but there is no
+ // other way I can see to test this. (Also applicable below.)
+ await new Promise(r => iframe.onload = r);
+
+ // Must throw since error pages are opaque origin.
+ assert_throws_dom("SecurityError", () => {
+ iframe.contentWindow.document;
+ });
+ }, `An iframe with src set to a redirect to ${aboutURL}`);
+
+ promise_test(async t => {
+ const iframe = document.createElement("iframe");
+ iframe.src = "/common/blank.html";
+ document.body.append(iframe);
+
+ await new Promise(r => iframe.onload = r);
+
+ iframe.contentWindow.location.href = `resources/redirect.py?location=${aboutURL}`;
+ await new Promise(r => iframe.onload = r);
+
+ // Must throw since error pages are opaque origin.
+ assert_throws_dom("SecurityError", () => {
+ iframe.contentWindow.document;
+ });
+ }, `An iframe that is navigated to a redirect to ${aboutURL}`);
+});
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-data.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-data.html
new file mode 100644
index 0000000000..f9e8021ddf
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-data.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Redirecting to data: URLs is disallowed</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+"use strict";
+
+promise_test(async (t) => {
+ window.onmessage = t.unreached_func("must not be messaged");
+ t.add_cleanup(() => { window.onmessage = null; });
+
+ const iframe = document.createElement("iframe");
+ iframe.src = `resources/redirect.py?location=data:text/html,FAIL<script>parent.postMessage('FAIL', '*')</${'script'}>`;
+ document.body.append(iframe);
+
+ await new Promise(r => iframe.onload = r);
+
+ // Must throw since error pages are opaque origin.
+ assert_throws_dom("SecurityError", () => {
+ iframe.contentWindow.document;
+ });
+
+ // Test passes if after 100 ms we haven't gotten the message.
+ await new Promise(r => t.step_timeout(r, 100));
+}, "Loading an iframe with src=redirecting URL");
+
+promise_test(async (t) => {
+ window.onmessage = t.unreached_func("must not be messaged");
+ t.add_cleanup(() => { window.onmessage = null; });
+
+ const iframe = document.createElement("iframe");
+ iframe.src = "/common/blank.html";
+ document.body.append(iframe);
+
+ await new Promise(r => iframe.onload = r);
+
+ iframe.contentWindow.location.href = `resources/redirect.py?location=data:text/html,FAIL<script>parent.postMessage('FAIL', '*')</${'script'}>`;
+ await new Promise(r => iframe.onload = r);
+
+ // Must throw since error pages are opaque origin.
+ assert_throws_dom("SecurityError", () => {
+ iframe.contentWindow.document;
+ });
+
+ // Test passes if after 100 ms we haven't gotten the message.
+ await new Promise(r => t.step_timeout(r, 100));
+}, "Navigating an iframe to a redirecting URL");
+
+promise_test(async (t) => {
+ window.onmessage = t.unreached_func("must not be messaged");
+ t.add_cleanup(() => { window.onmessage = null; });
+
+ const w = window.open(`resources/redirect.py?location=data:text/html,FAIL<script>parent.postMessage('FAIL', '*')</${'script'}>`);
+
+ // Test passes if after 100 ms we haven't gotten the message.
+ await new Promise(r => t.step_timeout(r, 100));
+}, "Loading a popup directly to the redirecting URL");
+
+promise_test(async (t) => {
+ const w = window.open(`resources/message-opener.html`);
+ await new Promise(r => window.onmessage = r);
+
+ window.onmessage = t.unreached_func("must not be messaged");
+ t.add_cleanup(() => { window.onmessage = null; });
+
+ w.location.href = `resources/redirect.py?location=data:text/html,FAIL<script>parent.postMessage('FAIL', '*')</${'script'}>`;
+
+ // Test passes if after 100 ms we haven't gotten the message.
+ await new Promise(r => t.step_timeout(r, 100));
+}, "Loading a popup that eventually goes to the redirecting URL");
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-unparseable-url.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-unparseable-url.html
new file mode 100644
index 0000000000..b025f34478
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/redirect-to-unparseable-url.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Session history interaction with redirects to unparseable URLs</title>
+<link rel="help" href="https://html.spec.whatwg.org/#create-navigation-params-by-fetching">
+<link rel="help" href="https://html.spec.whatwg.org/#read-ua-inline">
+<script src="/common/utils.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+const kUnparseableURL = self.origin + ":notaport/common/blank.html";
+
+promise_test(async t => {
+ const iframe = document.createElement('iframe');
+ t.add_cleanup(() => {
+ iframe.remove();
+ });
+
+ function getIframeLoadPromise() {
+ return new Promise(resolve => {
+ iframe.addEventListener('load', () => {
+ // Wait for the iframe to load + one task so that its navigations are
+ // not done in "replace" mode.
+ t.step_timeout(resolve, 0);
+ }, {once: true});
+ });
+ }
+
+ document.body.append(iframe);
+
+ assert_equals(history.length, 1, "Precondition: history.length is 1");
+
+ const first_load_promise = getIframeLoadPromise();
+ iframe.src = '/common/blank.html';
+ await first_load_promise;
+
+ // This navigation will fail, because it redirects to an unparseable URL.
+ const error_load_promise = getIframeLoadPromise();
+ const error_url = new URL('resources/no-cache-single-redirect.py', location.href);
+ error_url.searchParams.append('uuid', token());
+ error_url.searchParams.append('location', kUnparseableURL);
+ iframe.src = error_url;
+ await error_load_promise;
+
+ assert_equals(history.length, 2,
+ "history.length is 2 after two iframe navigations beyond the initial " +
+ "about:blank Document, the first of which 'replaced' the initial " +
+ "about:blank Document");
+
+ // Per https://html.spec.whatwg.org/#read-ua-inline, error Documents have
+ // opaque origins, so the `contentDocument` shouldn't be accessible.
+ assert_equals(iframe.contentDocument, null,
+ "Cannot reach iframe.contentDocument for error Documents");
+
+ const back_load_promise = getIframeLoadPromise();
+ history.back();
+ await back_load_promise;
+
+ const forward_load_promise = getIframeLoadPromise();
+ history.forward();
+ await forward_load_promise;
+
+ assert_not_equals(iframe.contentDocument, null, "iframe.contentDocument is accessible");
+ assert_equals(iframe.contentDocument.body.innerText, "No redirect",
+ "Traversal to history entry whose URL was once associated with an " +
+ "error Document correctly requests the same URL again");
+}, "Navigating to a url (A) that redirects to an unparseable URL (B), saves " +
+ "the URL (A) in the history entry, for later traversal");
+</script>
+</body>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/README.md b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/README.md
new file mode 100644
index 0000000000..52548db656
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/README.md
@@ -0,0 +1 @@
+See `/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/parsing.html` for more detailed parsing tests (shared with `<meta http-equiv=refresh>`).
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/navigate.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/navigate.window.js
new file mode 100644
index 0000000000..7d5a0fe21d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/navigate.window.js
@@ -0,0 +1,23 @@
+async_test(t => {
+ const frame = document.createElement("iframe");
+ frame.src = "resources/refresh.py"
+ frame.onload = t.step_func(() => {
+ // Could be better by verifying that resources/refresh.py loads too
+ if(frame.contentWindow.location.href === (new URL("resources/refreshed.txt?\u0080\u00FF", self.location)).href) { // Make sure bytes got mapped to code points of the same value
+ t.done();
+ }
+ });
+ document.body.appendChild(frame)
+}, "When navigating the Refresh header needs to be followed");
+
+async_test(t => {
+ const frame = document.createElement("iframe");
+ frame.src = "resources/multiple.asis"
+ frame.onload = t.step_func(() => {
+ // Could be better by verifying that resources/refresh.py loads too
+ if(frame.contentWindow.location.href === (new URL("resources/refreshed.txt", self.location)).href) {
+ t.done();
+ }
+ });
+ document.body.appendChild(frame)
+}, "When there's both a Refresh header and <meta> the Refresh header wins")
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/multiple.asis b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/multiple.asis
new file mode 100644
index 0000000000..3026d8297b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/multiple.asis
@@ -0,0 +1,6 @@
+HTTP/1.1 200 OK
+Refresh: 0,./refreshed.txt
+Content-Type:text/html
+
+I don't understand.
+<meta http-equiv=refresh content=1;./>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refresh.py b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refresh.py
new file mode 100644
index 0000000000..ecdd24f268
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refresh.py
@@ -0,0 +1,4 @@
+def main(request, response):
+ response.headers.set(b"Content-Type", b"text/plain")
+ response.headers.set(b"Refresh", b"0;./refreshed.txt?\x80\xFF") # Test byte to Unicode conversion
+ response.content = u"Not refreshed.\n"
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refreshed.txt b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refreshed.txt
new file mode 100644
index 0000000000..5df065b456
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/resources/refreshed.txt
@@ -0,0 +1 @@
+Have another.
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/subresource.any.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/subresource.any.js
new file mode 100644
index 0000000000..930dd34ad5
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/refresh/subresource.any.js
@@ -0,0 +1,6 @@
+promise_test(() => {
+ return fetch("resources/refresh.py").then(response => {
+ assert_equals(response.headers.get("refresh"), "0;./refreshed.txt?\u0080\u00FF"); // Make sure bytes got mapped to code points of the same value
+ assert_equals(response.url, (new URL("resources/refresh.py", self.location)).href);
+ });
+}, "Refresh does not affect subresources.");
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click-during-load.html
new file mode 100644
index 0000000000..e035b1f517
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click-during-load.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onload = () => {
+ const a = document.createElement("a");
+ a.href = "/common/blank.html?thereplacement";
+ document.body.append(a);
+ a.click();
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load");
+}, "aElement.click() during the load event must NOT replace");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click-during-pageshow.html
new file mode 100644
index 0000000000..006ce531e0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click-during-pageshow.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onpageshow = () => {
+ const a = document.createElement("a");
+ a.href = "/common/blank.html?thereplacement";
+ document.body.append(a);
+ a.click();
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load");
+}, "aElement.click() during the pageshow event must NOT replace");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click.html
new file mode 100644
index 0000000000..be7d1a9849
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ const a = document.createElement("a");
+ a.href = "/common/blank.html?thereplacement";
+ document.currentScript.before(a);
+ a.click();
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load");
+}, "aElement.click() before the load event must NOT replace");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click-during-load.html
new file mode 100644
index 0000000000..811c828331
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click-during-load.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onload = () => {
+ const a = document.createElement("a");
+ a.href = "/common/blank.html?thereplacement";
+ a.id = "the-anchor";
+ a.textContent = "needs to have content to be clickable";
+ document.body.append(a);
+ parent.test_driver.click(a);
+ };
+ `;
+
+ const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load");
+}, "User click on <a> during the load event must NOT replace");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click-during-pageshow.html
new file mode 100644
index 0000000000..6621b081e1
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click-during-pageshow.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onpageshow = () => {
+ const a = document.createElement("a");
+ a.href = "/common/blank.html?thereplacement";
+ a.id = "the-anchor";
+ a.textContent = "needs to have content to be clickable";
+ document.body.append(a);
+ parent.test_driver.click(a);
+ };
+ `;
+
+ const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load");
+}, "User click on <a> during the pageshow event must NOT replace");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click.html
new file mode 100644
index 0000000000..c9034f3573
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-user-click.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ const a = document.createElement("a");
+ a.href = "/common/blank.html?thereplacement";
+ a.id = "the-anchor";
+ a.textContent = "needs to have content to be clickable";
+ document.currentScript.before(a);
+ parent.test_driver.click(a);
+ `;
+
+ const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load");
+}, "User click on <a> before the load event must NOT replace");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit-during-load.html
new file mode 100644
index 0000000000..0dee49edb3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit-during-load.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onload = () => {
+ const form = document.createElement("form");
+ form.action = "/common/blank.html";
+
+ const input = document.createElement("input");
+ input.type = "hidden";
+ input.name = "thereplacement";
+ form.append(input);
+
+ document.body.append(form);
+ form.requestSubmit();
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement=";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "Replace during the load event, triggered by formElement.submit()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit-during-pageshow.html
new file mode 100644
index 0000000000..0cf0838496
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit-during-pageshow.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onpageshow = () => {
+ const form = document.createElement("form");
+ form.action = "/common/blank.html";
+
+ const input = document.createElement("input");
+ input.type = "hidden";
+ input.name = "thereplacement";
+ form.append(input);
+
+ document.body.append(form);
+ form.requestSubmit();
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement=";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "Replace during the pageshow event, triggered by formElement.submit()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit.html
new file mode 100644
index 0000000000..80beb3718b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ const form = document.createElement("form");
+ form.action = "/common/blank.html";
+
+ const input = document.createElement("input");
+ input.type = "hidden";
+ input.name = "thereplacement";
+ form.append(input);
+
+ document.currentScript.before(form);
+ form.requestSubmit();
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement=";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "Replace before load, triggered by formElement.requestSubmit()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click-during-load.html
new file mode 100644
index 0000000000..1b3a163176
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click-during-load.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onload = () => {
+ const form = document.createElement("form");
+ form.action = "/common/blank.html";
+
+ const input = document.createElement("input");
+ input.type = "hidden";
+ input.name = "thereplacement";
+ form.append(input);
+
+ const button = document.createElement("button");
+ button.type = "submit";
+ form.append(button);
+
+ document.body.append(form);
+ button.click();
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement=";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "Replace during load, triggered by submitButton.click()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click-during-pageshow.html
new file mode 100644
index 0000000000..c0022fd0f2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click-during-pageshow.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onpageshow = () => {
+ const form = document.createElement("form");
+ form.action = "/common/blank.html";
+
+ const input = document.createElement("input");
+ input.type = "hidden";
+ input.name = "thereplacement";
+ form.append(input);
+
+ const button = document.createElement("button");
+ button.type = "submit";
+ form.append(button);
+
+ document.body.append(form);
+ button.click();
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement=";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "Replace during pageshow, triggered by submitButton.click()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click.html
new file mode 100644
index 0000000000..873c49b5be
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-button-click.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ const form = document.createElement("form");
+ form.action = "/common/blank.html";
+
+ const input = document.createElement("input");
+ input.type = "hidden";
+ input.name = "thereplacement";
+ form.append(input);
+
+ const button = document.createElement("button");
+ button.type = "submit";
+ form.append(button);
+
+ document.currentScript.before(form);
+ button.click();
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement=";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "Replace before load, triggered by submitButton.click()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-cross-frame-crossorigin.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-cross-frame-crossorigin.sub.html
new file mode 100644
index 0000000000..a2ea20bf7f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-cross-frame-crossorigin.sub.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<form target="the-frame">
+ <input type="hidden" name="pushed">
+</form>
+
+<script>
+"use strict";
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+
+ const form = document.querySelector("form");
+ const frameEndingURL = changeURLHost(
+ absoluteURL("resources/slow-message-source-with-history-and-location.html?pushed="),
+ "{{hosts[][www]}}"
+ );
+ form.action = frameEndingURL;
+
+ const frameStartingCode = `
+ parent.postMessage({ historyLength: history.length, locationHref: location.href }, "*");
+ `;
+
+ const frameStartingURL = codeInjectorURL(frameStartingCode);
+ const frame = insertIframe(t, frameStartingURL, "the-frame");
+ t.add_cleanup(() => frame.remove()); // helps avoid waiting for the slow load to finish the tests
+ assert_equals(history.length, startingHistoryLength, "Inserting frame must not change history.length");
+
+ const frameBeforeLoadedMessage = await waitForMessage();
+ assert_equals(frameBeforeLoadedMessage.historyLength, startingHistoryLength, "frame's starting history.length");
+ assert_equals(frameBeforeLoadedMessage.locationHref, frame.src, "frame's starting location.href");
+
+ form.submit();
+
+ const frameAfterFormSubmitMessage = await waitForMessage();
+ assert_equals(frameAfterFormSubmitMessage.historyLength, startingHistoryLength + 1, "frame's after-submit history.length");
+ assert_equals(frameAfterFormSubmitMessage.locationHref, frameEndingURL, "frame's after-submit location.href");
+}, "No replace before load, triggered by cross-iframe formElement.submit() [iframe is cross-origin]");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-cross-frame.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-cross-frame.html
new file mode 100644
index 0000000000..d647e6ad06
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-cross-frame.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<form target="the-frame">
+ <input type="hidden" name="pushed">
+</form>
+
+<script>
+"use strict";
+promise_test(async t => {
+ const startingHistoryLength = history.length;
+
+ const form = document.querySelector("form");
+ const frameEndingURL = absoluteURL("resources/slow-message-source-with-history-and-location.html?pushed=");
+ form.action = frameEndingURL;
+
+ const frameStartingCode = `
+ window.onload = () => { window.onloadFired = true; };
+ parent.postMessage({ historyLength: history.length, locationHref: location.href }, "*");
+ parent.document.querySelector("form").submit();
+ `;
+
+ const frameStartingURL = codeInjectorURL(frameStartingCode);
+ const frame = insertIframe(t, frameStartingURL, "the-frame");
+ t.add_cleanup(() => frame.remove()); // helps avoid waiting for the slow load to finish the tests
+ assert_equals(history.length, startingHistoryLength, "Inserting frame must not change history.length");
+
+ const frameBeforeLoadedMessage = await waitForMessage();
+ assert_equals(frameBeforeLoadedMessage.historyLength, startingHistoryLength, "frame's starting history.length");
+ assert_equals(frameBeforeLoadedMessage.locationHref, frame.src, "frame's starting location.href");
+ assert_equals(frame.contentWindow.onloadFired, undefined, "frame's onload not fired yet");
+
+ const frameAfterFormSubmitMessage = await waitForMessage();
+ assert_equals(frameAfterFormSubmitMessage.historyLength, startingHistoryLength + 1, "frame's after-submit history.length");
+ assert_equals(frameAfterFormSubmitMessage.locationHref, frameEndingURL, "frame's after-submit location.href");
+}, "No replace before load, triggered by cross-iframe formElement.submit()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-during-load.html
new file mode 100644
index 0000000000..2a60f44fc6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-during-load.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onload = () => {
+ const form = document.createElement("form");
+ form.action = "/common/blank.html";
+
+ const input = document.createElement("input");
+ input.type = "hidden";
+ input.name = "thereplacement";
+ form.append(input);
+
+ document.body.append(form);
+ form.submit();
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement=";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "Replace during the load event, triggered by formElement.submit()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-during-pageshow.html
new file mode 100644
index 0000000000..886bf469a2
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-during-pageshow.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onpageshow = () => {
+ const form = document.createElement("form");
+ form.action = "/common/blank.html";
+
+ const input = document.createElement("input");
+ input.type = "hidden";
+ input.name = "thereplacement";
+ form.append(input);
+
+ document.body.append(form);
+ form.submit();
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement=";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "Replace during the pageshow event, triggered by formElement.submit()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-popup-crossorigin.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-popup-crossorigin.sub.html
new file mode 100644
index 0000000000..d54cc81477
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-popup-crossorigin.sub.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<form target="the-window">
+ <input type="hidden" name="pushed">
+</form>
+
+<script>
+"use strict";
+promise_test(async t => {
+ const form = document.querySelector("form");
+ const wEndingURL = changeURLHost(
+ absoluteURL("resources/slow-message-source-with-history-and-location.html?pushed="),
+ "{{hosts[][www]}}"
+ );
+ form.action = wEndingURL;
+
+ const wStartingCode = `
+ opener.postMessage({ historyLength: history.length, locationHref: location.href }, "*");
+ `;
+
+ const wStartingURL = codeInjectorURL(wStartingCode);
+ const w = window.open(wStartingURL, "the-window");
+ t.add_cleanup(() => w.close());
+
+ const wBeforeLoadedMessage = await waitForMessage();
+ assert_equals(wBeforeLoadedMessage.historyLength, 1, "window's starting history.length");
+ assert_equals(wBeforeLoadedMessage.locationHref, wStartingURL, "window's starting location.href");
+
+ form.submit();
+
+ const wAfterFormSubmitMessage = await waitForMessage();
+ assert_equals(wAfterFormSubmitMessage.historyLength, 2, "window's after-submit history.length");
+ assert_equals(wAfterFormSubmitMessage.locationHref, wEndingURL, "window's after-submit location.href");
+}, "No replace before load, triggered by formElement.submit() in the opener window, after the opener has loaded [window is cross-origin]");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-popup.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-popup.html
new file mode 100644
index 0000000000..b27d54675d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit-popup.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<form target="the-window">
+ <input type="hidden" name="pushed">
+</form>
+
+<script>
+"use strict";
+promise_test(async t => {
+ const form = document.querySelector("form");
+ const wEndingURL = absoluteURL("resources/slow-message-source-with-history-and-location.html?pushed=");
+ form.action = wEndingURL;
+
+ const wStartingCode = `
+ window.onload = () => { window.onloadFired = true; };
+ opener.postMessage({ historyLength: history.length, locationHref: location.href }, "*");
+ opener.document.querySelector("form").submit();
+ `;
+
+ const wStartingURL = codeInjectorURL(wStartingCode);
+ const w = window.open(wStartingURL, "the-window");
+ t.add_cleanup(() => w.close());
+
+ const wBeforeLoadedMessage = await waitForMessage();
+ assert_equals(wBeforeLoadedMessage.historyLength, 1, "window's starting history.length");
+ assert_equals(wBeforeLoadedMessage.locationHref, wStartingURL, "window's starting location.href");
+ assert_equals(w.onloadFired, undefined, "window's onload not fired yet");
+
+ const wAfterFormSubmitMessage = await waitForMessage();
+ assert_equals(wAfterFormSubmitMessage.historyLength, 2, "window's after-submit history.length");
+ assert_equals(wAfterFormSubmitMessage.locationHref, wEndingURL, "window's after-submit location.href");
+}, "No replace before load, triggered by formElement.submit() in the opener window, after the opener has loaded");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit.html
new file mode 100644
index 0000000000..eace0b0a69
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-submit.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ const form = document.createElement("form");
+ form.action = "/common/blank.html";
+
+ const input = document.createElement("input");
+ input.type = "hidden";
+ input.name = "thereplacement";
+ form.append(input);
+
+ document.currentScript.before(form);
+ form.submit();
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement=";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "Replace before load, triggered by same-document formElement.submit()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate-during-load.html
new file mode 100644
index 0000000000..233d80d8d7
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate-during-load.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onload = () => {
+ history.pushState(null, null, "/common/blank.html?thereplacement");
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the replacement");
+}, "history.pushState() during the load event must NOT replace");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate-during-pageshow.html
new file mode 100644
index 0000000000..a4e83baf1d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate-during-pageshow.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onpageshow = () => {
+ history.pushState(null, null, "/common/blank.html?thereplacement");
+ parent.postMessage("done", "*");
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ assert_equals(await waitForMessage(), "done");
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the replacement");
+}, "history.pushState() during the pageshow event must NOT replace");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate.html
new file mode 100644
index 0000000000..2faef7c319
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/history-pushstate.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent("history.pushState(null, null, `/common/blank.html?thereplacement`);");
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the replacement");
+}, "history.pushState() before the load event must NOT replace");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src-during-load.html
new file mode 100644
index 0000000000..83f26b2aa9
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src-during-load.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onload = () => {
+ parent.document.querySelectorAll("iframe")[1].src = "/common/blank.html?thereplacement";
+ };
+ `;
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "Replace during the load event, triggered by setting iframeElement.src");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src-during-pageshow.html
new file mode 100644
index 0000000000..61345cf7f9
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src-during-pageshow.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onpageshow = () => {
+ parent.document.querySelectorAll("iframe")[1].src = "/common/blank.html?thereplacement";
+ };
+ `;
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "Replace during the pageshow event, triggered by setting iframeElement.src");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src.html
new file mode 100644
index 0000000000..47407c106e
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/iframe-src.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onload = () => { window.onloadFired = true; };
+ `;
+ const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ const absoluteStartURL = (new URL(startURL, location.href)).href;
+ while (true) {
+ if (iframe.contentWindow.location.href === absoluteStartURL) {
+ break;
+ }
+ await new Promise(r => setTimeout(r, 0));
+ }
+
+ assert_equals(iframe.contentWindow.location.href, (new URL(startURL, location.href)).href, "Iframe must be navigated away from the initial about:blank document");
+ assert_equals(iframe.contentWindow.onloadFired, undefined, "onload must not yet have fired");
+
+ iframe.src = afterReplacementURL;
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "Replace before load, triggered by setting iframeElement.src");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-during-load.html
new file mode 100644
index 0000000000..2b4ac49533
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-during-load.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onload = () => {
+ location.assign("/common/blank.html?thereplacement");
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "Replace during the load event, triggered by location.assign()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-during-pageshow.html
new file mode 100644
index 0000000000..0f84cbe246
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-during-pageshow.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onpageshow = () => {
+ location.assign("/common/blank.html?thereplacement");
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "Replace during the pageshow event, triggered by location.assign()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-user-click.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-user-click.html
new file mode 100644
index 0000000000..785ec024e0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign-user-click.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ const button = document.createElement("button");
+ button.id = "the-button";
+ button.textContent = "needs to have content to be clickable";
+ button.onclick = () => { location.assign("/common/blank.html?thereplacement"); };
+ document.currentScript.before(button);
+ parent.test_driver.click(button);
+ `;
+
+ const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load");
+}, "NO replace before load, triggered by location.assign()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign.html
new file mode 100644
index 0000000000..53ea96ffea
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-assign.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent("location.assign(`/common/blank.html?thereplacement`);");
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "Replace before load, triggered by location.assign()");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-during-load.html
new file mode 100644
index 0000000000..e4c8ca80a1
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-during-load.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Replace during the load event, triggered by location setters</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onload = () => {
+ location.href = "/common/blank.html?thereplacement";
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "href");
+
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onload = () => {
+ location.search = "thereplacement";
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "resources/code-injector.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+}, "search");
+
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onload = () => {
+ location.hash = "thereplacement";
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = startURL + "#thereplacement";
+ const iframe = insertIframe(t, startURL);
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "hash");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-during-pageshow.html
new file mode 100644
index 0000000000..9997783ff5
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-during-pageshow.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Replace during the pageshow event, triggered by location setters</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onpageshow = () => {
+ location.href = "/common/blank.html?thereplacement";
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "href");
+
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onpageshow = () => {
+ location.search = "thereplacement";
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "resources/code-injector.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+}, "search");
+
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onpageshow = () => {
+ location.hash = "thereplacement";
+ parent.postMessage("done", "*");
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = startURL + "#thereplacement";
+ const iframe = insertIframe(t, startURL);
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ assert_equals(await waitForMessage(), "done");
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "hash");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-user-click.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-user-click.html
new file mode 100644
index 0000000000..9276327f1a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-user-click.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>No replace before load, triggered by location setters called as part of user-initiated clicks</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<body>
+<script>
+"use strict";
+
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ const button = document.createElement("button");
+ button.id = "the-button";
+ button.textContent = "needs to have content to be clickable";
+ button.onclick = () => { location.href = "/common/blank.html?thereplacement"; };
+ document.currentScript.before(button);
+ parent.test_driver.click(button);
+ `;
+
+ const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load");
+}, "href");
+
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ const button = document.createElement("button");
+ button.id = "the-button";
+ button.textContent = "needs to have content to be clickable";
+ button.onclick = () => { location.search = "thereplacement"; };
+ document.currentScript.before(button);
+ parent.test_driver.click(button);
+ `;
+
+ const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "resources/slow-code-injector.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load");
+}, "search");
+
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ const button = document.createElement("button");
+ button.id = "the-button";
+ button.textContent = "needs to have content to be clickable";
+ button.onclick = () => { location.hash = "thereplacement"; };
+ document.currentScript.before(button);
+ parent.test_driver.click(button);
+ `;
+
+ const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = startURL + "#thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load");
+}, "hash");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-user-mouseup.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-user-mouseup.html
new file mode 100644
index 0000000000..9068e5a116
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter-user-mouseup.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>No replace before load, triggered by location setters called as part of user-initiated mouseups</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<!--
+ We test this separate from click because the spec as of
+ https://html.spec.whatwg.org/commit-snapshots/4ba46b025ec806ded7b4911bf8f9dd7bf9ff365e/#location-object-setter-navigate
+ referenced click handlers specifically, instead of using the general user activation concept which
+ includes other events like mouseup.
+-->
+
+<body>
+<script>
+"use strict";
+
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ const button = document.createElement("button");
+ button.id = "the-button";
+ button.textContent = "needs to have content to be clickable";
+ button.onmouseup = () => { location.href = "/common/blank.html?thereplacement"; };
+ document.currentScript.before(button);
+ parent.test_driver.click(button);
+ `;
+
+ const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load");
+}, "href");
+
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ const button = document.createElement("button");
+ button.id = "the-button";
+ button.textContent = "needs to have content to be clickable";
+ button.onmouseup = () => { location.search = "thereplacement"; };
+ document.currentScript.before(button);
+ parent.test_driver.click(button);
+ `;
+
+ const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "resources/slow-code-injector.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load");
+}, "search");
+
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ const button = document.createElement("button");
+ button.id = "the-button";
+ button.textContent = "needs to have content to be clickable";
+ button.onmouseup = () => { location.hash = "thereplacement"; };
+ document.currentScript.before(button);
+ parent.test_driver.click(button);
+ `;
+
+ const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = startURL + "#thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must change after waiting for the load");
+}, "hash");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter.html
new file mode 100644
index 0000000000..b8049f084b
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/location-setter.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Replace before load, triggered by location setters</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent("location.href = `/common/blank.html?thereplacement`;");
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "href");
+
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent("location.search = `thereplacement`;");
+ const afterReplacementURL = "resources/code-injector.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+}, "search");
+
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent("location.hash = `thereplacement`;");
+ const afterReplacementURL = startURL + "#thereplacement";
+ const iframe = insertIframe(t, startURL);
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after waiting for the replacement");
+
+ await checkSentinelIframe(t, sentinelIframe);
+ assert_equals(history.length, startingHistoryLength, "history.length must not change after checking the sentinel iframe");
+}, "hash");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/code-injector.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/code-injector.html
new file mode 100644
index 0000000000..73dacf0d76
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/code-injector.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Subframe</title>
+
+<body>
+<script>
+"use strict";
+{{GET[code]}}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/helpers.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/helpers.js
new file mode 100644
index 0000000000..58102aa925
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/helpers.js
@@ -0,0 +1,89 @@
+window.waitForLoad = (t, iframe, urlRelativeToThisDocument) => {
+ return new Promise(resolve => {
+ iframe.addEventListener("load", t.step_func(() => {
+ assert_equals(iframe.contentWindow.location.href, (new URL(urlRelativeToThisDocument, location.href)).href);
+
+ // Wait a bit longer to ensure all history stuff has settled, e.g. the document is "completely loaded"
+ // (which happens from a queued task).
+ setTimeout(resolve, 0);
+ }), { once: true });
+ });
+};
+
+window.waitForLoadAllowingIntermediateLoads = (t, iframe, urlRelativeToThisDocument) => {
+ return new Promise(resolve => {
+ const handler = t.step_func(() => {
+ if (iframe.contentWindow.location.href === (new URL(urlRelativeToThisDocument, location.href)).href) {
+ // Wait a bit longer to ensure all history stuff has settled, e.g. the document is "completely loaded"
+ // (which happens from a queued task).
+ setTimeout(resolve, 0);
+ iframe.removeEventListener("load", handler);
+ }
+ });
+
+ iframe.addEventListener("load", handler);
+ });
+};
+
+window.waitForMessage = () => {
+ return new Promise(resolve => {
+ window.addEventListener("message", e => {
+ resolve(e.data);
+ }, { once: true });
+ });
+};
+
+window.setupSentinelIframe = async (t) => {
+ // If this iframe gets navigated by history.back(), then the iframe under test did not, so we did a replace.
+ const sentinelIframe = document.createElement("iframe");
+ sentinelIframe.src = "/common/blank.html?sentinelstart";
+ document.body.append(sentinelIframe);
+ t.add_cleanup(() => sentinelIframe.remove());
+
+ await waitForLoad(t, sentinelIframe, "/common/blank.html?sentinelstart");
+
+ sentinelIframe.src = "/common/blank.html?sentinelend";
+ await waitForLoad(t, sentinelIframe, "/common/blank.html?sentinelend");
+
+ return sentinelIframe;
+};
+
+window.checkSentinelIframe = async (t, sentinelIframe) => {
+ // Go back. Since iframe should have done a replace, this should move sentinelIframe back, not iframe.
+ history.back();
+ await waitForLoad(t, sentinelIframe, "/common/blank.html?sentinelstart");
+};
+
+window.insertIframe = (t, url, name) => {
+ const iframe = document.createElement("iframe");
+ iframe.src = url;
+
+ // In at least Chromium, window name targeting for form submission doesn't work if the name is set
+ // after the iframe is inserted into the DOM. So we can't just have callers do this themselves.
+ if (name) {
+ iframe.name = name;
+ }
+
+ document.body.append(iframe);
+
+ // Intentionally not including the following:
+ // t.add_cleanup(() => iframe.remove());
+ // Doing so breaks some of the testdriver.js tests with "cannot find window" errors.
+ return iframe;
+};
+
+// TODO(domenic): clean up other tests in the parent directory to use this.
+window.absoluteURL = relativeURL => {
+ return (new URL(relativeURL, location.href)).href;
+};
+
+// TODO(domenic): clean up other tests in the parent directory to use this.
+window.codeInjectorURL = code => {
+ return absoluteURL("resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code));
+};
+
+window.changeURLHost = (url, newHost) => {
+ const urlObj = new URL(url);
+ urlObj.host = newHost;
+ return urlObj.href;
+};
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/message-opener.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/message-opener.html
new file mode 100644
index 0000000000..b6f9d31358
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/message-opener.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Popup</title>
+
+<script>
+"use strict";
+opener.postMessage("ready", "*");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/slow-code-injector.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/slow-code-injector.html
new file mode 100644
index 0000000000..b7e99dcbfa
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/slow-code-injector.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Subframe</title>
+
+<body>
+<script>
+"use strict";
+{{GET[code]}}
+</script>
+
+<!--
+ This is necessary in cases involving user interaction because those happen async through the
+ webdriver infrastructure. Without this, the load event might happen before the click ever goes
+ through.
+-->
+<img src="/common/slow.py">
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/slow-message-source-with-history-and-location.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/slow-message-source-with-history-and-location.html
new file mode 100644
index 0000000000..da279ebf82
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/resources/slow-message-source-with-history-and-location.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Subframe or popup</title>
+
+<script>
+"use strict";
+(window.opener || window.parent).postMessage(
+ { historyLength: history.length, locationHref: location.href },
+ "*"
+);
+</script>
+
+<!--
+ This delays the load event, hopefully long enough that we can do whatever before-load action we're aiming for.
+-->
+<img src="/common/slow.py">
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup-during-load.html
new file mode 100644
index 0000000000..5308c01c39
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup-during-load.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ // Generating a new one instead of hard-coding makes running tests manually a bit easier.
+ const windowName = token();
+
+ const code = `
+ window.onload = () => opener.navigateMe();
+ opener.postMessage("arrived at start URL", "*");
+ `;
+
+ const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const absoluteStartURL = (new URL(startURL, location.href)).href;
+
+ const afterReplacementURL = "resources/message-opener.html";
+ const absoluteAfterReplacementURL = (new URL(afterReplacementURL, location.href)).href;
+
+ window.navigateMe = () => {
+ window.open(absoluteAfterReplacementURL, windowName);
+ };
+
+ // First message sent is ignored; we only check it after navigating back.
+ const w = window.open(startURL, windowName);
+ t.add_cleanup(() => w.close());
+
+ // Wait to get past any initial about:blank
+ while (true) {
+ if (w.location.href === absoluteStartURL) {
+ break;
+ }
+ await new Promise(r => t.step_timeout(r, 0));
+ }
+
+ assert_equals(w.onloadFired, undefined, "onload must not yet have fired");
+ assert_equals(w.history.length, 1, "history.length for the opened window must start at 1");
+
+ await new Promise(r => {
+ window.addEventListener("message", t.step_func(e => {
+ if (e.data === "ready") {
+ resolve();
+ }
+ }));
+ });
+
+ assert_equals(w.history.length, 2, "history.length must increase");
+ assert_equals(w.location.href, absoluteAfterReplacementURL);
+
+ const promise = new Promise(resolve => {
+ window.addEventListener("message", t.step_func(e => {
+ assert_equals(e.data, "arrived at start URL");
+ resolve();
+ }));
+ });
+
+ w.history.back();
+
+ await promise;
+ assert_equals(w.location.href, absoluteStartURL, "1 second after attempting to go back, it indeed went back");
+}, "No replace before load, triggered by window.open() on a non-_self window");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup-during-pageshow.html
new file mode 100644
index 0000000000..065d933225
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup-during-pageshow.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ // Generating a new one instead of hard-coding makes running tests manually a bit easier.
+ const windowName = token();
+
+ const code = `
+ window.onpageshow = () => opener.navigateMe();
+ opener.postMessage("arrived at start URL", "*");
+ `;
+
+ const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const absoluteStartURL = (new URL(startURL, location.href)).href;
+
+ const afterReplacementURL = "resources/message-opener.html";
+ const absoluteAfterReplacementURL = (new URL(afterReplacementURL, location.href)).href;
+
+ window.navigateMe = () => {
+ window.open(absoluteAfterReplacementURL, windowName);
+ };
+
+ // First message sent is ignored; we only check it after navigating back.
+ const w = window.open(startURL, windowName);
+ t.add_cleanup(() => w.close());
+
+ // Wait to get past any initial about:blank
+ while (true) {
+ if (w.location.href === absoluteStartURL) {
+ break;
+ }
+ await new Promise(r => t.step_timeout(r, 0));
+ }
+
+ assert_equals(w.onloadFired, undefined, "onload must not yet have fired");
+ assert_equals(w.history.length, 1, "history.length for the opened window must start at 1");
+
+ await new Promise(r => {
+ window.addEventListener("message", t.step_func(e => {
+ if (e.data === "ready") {
+ resolve();
+ }
+ }));
+ });
+
+ assert_equals(w.history.length, 2, "history.length must increase");
+ assert_equals(w.location.href, absoluteAfterReplacementURL);
+
+ const promise = new Promise(resolve => {
+ window.addEventListener("message", t.step_func(e => {
+ assert_equals(e.data, "arrived at start URL");
+ resolve();
+ }));
+ });
+
+ w.history.back();
+
+ await promise;
+ assert_equals(w.location.href, absoluteStartURL, "1 second after attempting to go back, it indeed went back");
+}, "No replace before load, triggered by window.open() on a non-_self window");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup.html
new file mode 100644
index 0000000000..7b3e05f8f6
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-popup.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ // Generating a new one instead of hard-coding makes running manual tests a bit easier.
+ const windowName = token();
+
+ const code = `
+ window.onload = () => { window.onloadFired = true; };
+ `;
+
+ const startURL = "resources/slow-code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const absoluteStartURL = (new URL(startURL, location.href)).href;
+
+ const afterReplacementURL = "resources/message-opener.html";
+ const absoluteAfterReplacementURL = (new URL(afterReplacementURL, location.href)).href;
+
+ const w = window.open(startURL, windowName);
+ t.add_cleanup(() => w.close());
+
+ // Wait to get past any initial about:blank
+ while (true) {
+ if (w.location.href === absoluteStartURL) {
+ break;
+ }
+ await new Promise(r => setTimeout(r, 0));
+ }
+
+ assert_equals(w.onloadFired, undefined, "onload must not yet have fired");
+ assert_equals(w.history.length, 1, "history.length for the opened window must start at 1");
+
+ window.open(afterReplacementURL, windowName);
+ await new Promise(r => { window.onmessage = r; });
+
+ assert_equals(w.history.length, 2, "history.length must increase");
+ assert_equals(w.location.href, absoluteAfterReplacementURL);
+
+ w.history.back();
+
+ await new Promise(r => t.step_timeout(r, 1000));
+ assert_equals(w.location.href, absoluteStartURL, "1 second after attempting to go back, it indeed went back");
+}, "No replace before load, triggered by window.open() on a non-_self window");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self-during-load.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self-during-load.html
new file mode 100644
index 0000000000..255601ba8d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self-during-load.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onload = () => {
+ window.open("/common/blank.html?thereplacement", "_self");
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must increase");
+}, "No replace during load, triggered by window.open(_self) on an iframe");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self-during-pageshow.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self-during-pageshow.html
new file mode 100644
index 0000000000..a8327cfac3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self-during-pageshow.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const code = `
+ window.onpageshow = () => {
+ window.open("/common/blank.html?thereplacement", "_self");
+ };
+ `;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent(code);
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoadAllowingIntermediateLoads(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must increase");
+}, "No replace during pageshow, triggered by window.open(_self) on an iframe");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self.html
new file mode 100644
index 0000000000..10e8f38001
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/window-open-self.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+
+<body>
+<script>
+"use strict";
+promise_test(async t => {
+ const sentinelIframe = await setupSentinelIframe(t);
+ const startingHistoryLength = history.length;
+
+ const startURL = "resources/code-injector.html?pipe=sub(none)&code=" + encodeURIComponent("window.open(`/common/blank.html?thereplacement`, `_self`);");
+ const afterReplacementURL = "/common/blank.html?thereplacement";
+ const iframe = insertIframe(t, startURL);
+
+ assert_equals(history.length, startingHistoryLength, "Inserting the under-test iframe must not change history.length");
+
+ await waitForLoad(t, iframe, afterReplacementURL);
+ assert_equals(history.length, startingHistoryLength + 1, "history.length must increase");
+}, "No replace before load, triggered by window.open(_self) on an iframe");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/blank.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/blank.html
new file mode 100644
index 0000000000..c50eddd41f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/blank.html
@@ -0,0 +1 @@
+<!doctype html>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-cross-origin-inner.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-cross-origin-inner.html
new file mode 100644
index 0000000000..72b92c8061
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-cross-origin-inner.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<script>
+const params = new URL(window.location).searchParams;
+const property = params.get("property");
+
+try {
+ if (property === null) {
+ parent.location = "foo";
+ } else if (property === "reload") {
+ parent.location.reload();
+ } else if (property === "replace") {
+ parent.location.replace("foo");
+ } else {
+ parent.location[property] = "foo";
+ }
+ parent.parent.postMessage("success", "*");
+} catch (e) {
+ parent.parent.postMessage(`error: ${e.name}`, "*");
+}
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-destination.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-destination.html
new file mode 100644
index 0000000000..bb8ba4e698
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-destination.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<script>
+parent.postMessage("destination", "*");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-location-initial.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-location-initial.html
new file mode 100644
index 0000000000..a4a1713a27
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-location-initial.html
@@ -0,0 +1,3 @@
+<!doctype html>
+<script>parent.postMessage("initial", "*")</script>
+<iframe src="child-navigates-parent-location-inner.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-location-inner.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-location-inner.html
new file mode 100644
index 0000000000..4d7d24730f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-location-inner.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<script>
+parent.parent.postMessage("inner", "*");
+parent.location = "child-navigates-parent-destination.html"
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-submit-initial.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-submit-initial.html
new file mode 100644
index 0000000000..943708e0cd
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-submit-initial.html
@@ -0,0 +1,3 @@
+<!doctype html>
+<script>parent.postMessage("initial", "*")</script>
+<iframe src="child-navigates-parent-submit-inner.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-submit-inner.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-submit-inner.html
new file mode 100644
index 0000000000..0b49e13d65
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-submit-inner.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<form action="child-navigates-parent-destination.html" target="_parent"></form>
+<script>
+parent.parent.postMessage("inner", "*");
+document.forms[0].submit();
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/click.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/click.html
new file mode 100644
index 0000000000..8cb03b74d5
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/click.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<script>
+parent.postMessage("click", "*");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/document-domain-set-to-site.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/document-domain-set-to-site.sub.html
new file mode 100644
index 0000000000..3c4355c452
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/document-domain-set-to-site.sub.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+
+<script>
+"use strict";
+document.domain = "{{host}}";
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/form.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/form.html
new file mode 100644
index 0000000000..6523a82b39
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/form.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<script>
+parent.postMessage("form navigation", "*");
+</script>
+form navigation
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/href.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/href.html
new file mode 100644
index 0000000000..eccadadf41
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/href.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<script>
+parent.postMessage("href", "*");
+</script>
+href
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/message-opener.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/message-opener.html
new file mode 100644
index 0000000000..b6f9d31358
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/message-opener.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Popup</title>
+
+<script>
+"use strict";
+opener.postMessage("ready", "*");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/message-parent.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/message-parent.html
new file mode 100644
index 0000000000..3656358f2d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/message-parent.html
@@ -0,0 +1,3 @@
+<script>
+ window.parent.postMessage("ready", "*");
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/multi-globals-subframe-1.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/multi-globals-subframe-1.sub.html
new file mode 100644
index 0000000000..a87b2fd2be
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/multi-globals-subframe-1.sub.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Multi-globals test outer subframe</title>
+
+<script>
+"use strict";
+document.domain = "{{hosts[][]}}";
+</script>
+
+<iframe src="http://{{hosts[][]}}:{{ports[http][0]}}/html/browsers/browsing-the-web/navigating-across-documents/resources/multi-globals-subframe-2.sub.html"></iframe>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/multi-globals-subframe-2.sub.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/multi-globals-subframe-2.sub.html
new file mode 100644
index 0000000000..593c428a67
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/multi-globals-subframe-2.sub.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Multi-globals test inner subframe</title>
+
+<script>
+"use strict";
+document.domain = "{{hosts[][]}}";
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/no-cache-single-redirect.py b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/no-cache-single-redirect.py
new file mode 100644
index 0000000000..9d3aff817d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/no-cache-single-redirect.py
@@ -0,0 +1,21 @@
+# This handler receives requests identified by UUIDs that have mandatory
+# `location` query parameters. Every other request for the same URL will result
+# in a redirect to the URL described by `location`. When we don't redirect, we
+# simply return the HTML document "No redirect".
+def main(request, response):
+ response.headers.set(b"Cache-Control", b"no-store")
+
+ uuid = request.GET.first(b"uuid")
+ value = request.server.stash.take(uuid)
+
+ if value is None:
+ response.status = 302
+ location = request.GET.first(b"location")
+ response.headers.set(b"Location", location)
+ # Ensure that the next time this uuid is request, we don't redirect.
+ request.server.stash.put(uuid, "sentinel value")
+ else:
+ # If we're in this branch, then `value` is not none, but the stash now
+ # has `None` associated with `uuid`, which means on the next request for
+ # this `uuid` we'll end up in the above branch instead.
+ return ([(b"Content-Type", b"text/html")], b"No redirect")
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/page-that-post-message-to-opener.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/page-that-post-message-to-opener.html
new file mode 100644
index 0000000000..65a0c82149
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/page-that-post-message-to-opener.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Page that postMessage to its opener</title>
+<script>
+ opener.postMessage('Allowed', '*');
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/page-with-top-navigating-iframe.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/page-with-top-navigating-iframe.html
new file mode 100644
index 0000000000..568e44296f
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/page-with-top-navigating-iframe.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src=/common/get-host-info.sub.js></script>
+<script src=/resources/testdriver.js></script>
+<script src=/resources/testdriver-vendor.js></script>
+<title>Page that embeds an iframe that navigates its top</title>
+<script>
+function addIframe() {
+ const iframe = document.createElement('iframe');
+ const path = new URL("top-navigating-page.html", window.location).pathname;
+ iframe.src = get_host_info().HTTP_NOTSAMESITE_ORIGIN + path;
+ document.body.appendChild(iframe);
+}
+
+addEventListener('load', () => {
+ const urlParams = new URLSearchParams(location.search);
+ const parentUserGesture = urlParams.get('parent_user_gesture') === 'true';
+ if (parentUserGesture)
+ test_driver.bless("Giving parent frame user activation").then(addIframe);
+ else
+ addIframe();
+});
+</script>
+
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/portable-document-format-sample-valid.pdf b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/portable-document-format-sample-valid.pdf
new file mode 100644
index 0000000000..d008b1f23a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/portable-document-format-sample-valid.pdf
Binary files differ
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/portable-document-format-sample-valid.pdf.headers b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/portable-document-format-sample-valid.pdf.headers
new file mode 100644
index 0000000000..5a8e57e482
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/portable-document-format-sample-valid.pdf.headers
@@ -0,0 +1 @@
+Content-Type: application/pdf
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/redirect.py b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/redirect.py
new file mode 100644
index 0000000000..3c78c256b0
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/redirect.py
@@ -0,0 +1,4 @@
+def main(request, response):
+ location = request.GET.first(b"location")
+ response.status = 302
+ response.headers.set(b"Location", location)
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/top-navigating-page.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/top-navigating-page.html
new file mode 100644
index 0000000000..557f408fba
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/top-navigating-page.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Page that navigates its top</title>
+<script src=/common/get-host-info.sub.js></script>
+<script>
+
+let path = new URL("page-that-post-message-to-opener.html", window.location).pathname;
+let fullUrl = get_host_info().HTTP_NOTSAMESITE_ORIGIN + path;
+try {
+ top.location = fullUrl;
+} catch {
+ top.opener.postMessage('Denied', '*');
+}
+
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/wait-for-messages.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/wait-for-messages.js
new file mode 100644
index 0000000000..62ddec49f1
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/wait-for-messages.js
@@ -0,0 +1,15 @@
+// Asynchronous function that waits for the given number of messages to be
+// received by `window`, then returns those messages.
+function waitForMessages(numMessages) {
+ return new Promise((resolve) => {
+ const messages = [];
+
+ window.addEventListener("message", function handler(evt) {
+ messages.push(evt.data);
+ if (messages.length == numMessages) {
+ window.removeEventListener("message", handler);
+ resolve(messages);
+ }
+ });
+ });
+}
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/xhtml-and-non-utf-8.xhtml b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/xhtml-and-non-utf-8.xhtml
new file mode 100644
index 0000000000..3aacf33f1c
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/resources/xhtml-and-non-utf-8.xhtml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="windows-1250"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <meta charset="windows-1250"/>
+ <title>A test document used when you need something very non-default</title>
+</head>
+<body>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-parent-then-fragment.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-parent-then-fragment.html
new file mode 100644
index 0000000000..d01ef4c77a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-parent-then-fragment.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>
+ Set location from a parent, then do a fragment navigation from within the
+ frame.
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe></iframe>
+<script>
+ promise_test(async test => {
+ // Wait for the DOM to be ready before inserting an <iframe> into it.
+ await new Promise(resolve => { onload = resolve });
+ // Insert an <iframe> and wait for a dummy document to be loaded into it.
+ let iframe = document.createElement("iframe");
+ iframe.src = "support/dummy.html";
+ let iframe_loaded = new Promise(resolve => { iframe.onload = resolve });
+ document.body.appendChild(iframe);
+ await iframe_loaded;
+ // The referrer is the main frame's URL since it initiated the iframe
+ // creation.
+ assert_equals(iframe.contentDocument.referrer, document.URL);
+ // Do a fragment navigation from the frame, which will fire the
+ // 'hashchange' function.
+ let hash_changed = new Promise(resolve => {
+ iframe.contentWindow.onhashchange = resolve
+ });
+ let navigateScript = iframe.contentDocument.createElement("script");
+ navigateScript.innerHTML = "location.href = '#foo'";
+ iframe.contentDocument.body.appendChild(navigateScript);
+ await hash_changed;
+ // The referrer stays the same, even when the last navigation was
+ // initiated by the iframe (instead of the main frame document).
+ assert_equals(iframe.contentDocument.referrer, document.URL);
+ });
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-parent.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-parent.html
new file mode 100644
index 0000000000..2efc3a6b4a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-parent.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Set location from a parent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<iframe></iframe>
+<script>
+ setup({ single_test: true });
+ onload = function() {
+ var fr = document.querySelector("iframe")
+ fr.contentWindow.location = "support/dummy.html"
+ fr.onload = function() {
+ assert_equals(fr.contentDocument.referrer, document.URL)
+ done()
+ }
+ }
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-src.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-src.html
new file mode 100644
index 0000000000..e21260cd35
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function-src.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Set src from a function called from a parent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<iframe src="support/set-parent-src.html"></iframe>
+<script>
+async_test(function() {
+ onload = this.step_func(function() {
+ var fr = document.querySelector("iframe")
+ fr.contentWindow.go()
+ fr.onload = this.step_func_done(function() {
+ assert_equals(fr.contentDocument.referrer, document.URL)
+ })
+ })
+})
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function.html
new file mode 100644
index 0000000000..c6fa765b89
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-function.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Set location from a function called from a parent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<iframe src="support/location-set.html"></iframe>
+<script>
+async_test(function() {
+ onload = this.step_func(function() {
+ var fr = document.querySelector("iframe")
+ var url = fr.contentDocument.URL
+ fr.contentWindow.go()
+ fr.onload = this.step_func_done(function() {
+ assert_equals(fr.contentDocument.referrer, url)
+ })
+ })
+})
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-src-about-blank.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-src-about-blank.html
new file mode 100644
index 0000000000..479019d847
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/navigate-child-src-about-blank.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Set the src attribute to about:blank and check referrer</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<iframe></iframe>
+<script>
+ setup({
+ single_test: true
+ });
+ onload = function() {
+ var fr = document.querySelector("iframe")
+ fr.src = "about:blank"
+ fr.onload = function() {
+ assert_equals(fr.contentDocument.referrer, document.location.origin + '/')
+ done()
+ }
+ }
+</script>
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/dummy.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/dummy.html
new file mode 100644
index 0000000000..0638657093
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/dummy.html
@@ -0,0 +1,3 @@
+<!doctype html>
+<meta charset=utf-8>
+<p>Hello. \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/location-set.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/location-set.html
new file mode 100644
index 0000000000..ad733afac3
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/location-set.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<meta charset=utf-8>
+<script>
+ function go() {
+ location.href = "support/dummy.html"
+ }
+</script>
+<p>Hello. Go. \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/set-parent-src.html b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/set-parent-src.html
new file mode 100644
index 0000000000..9d45be8c8d
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/source/support/set-parent-src.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<meta charset=utf-8>
+<script>
+ function go() {
+ frameElement.src = "support/dummy.html"
+ }
+</script>
+<p>Hello. Go. \ No newline at end of file
diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/top-level-data-url.window.js b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/top-level-data-url.window.js
new file mode 100644
index 0000000000..ca321f106a
--- /dev/null
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/navigating-across-documents/top-level-data-url.window.js
@@ -0,0 +1,20 @@
+// META: timeout=long
+
+const dataURL = `data:text/html,...`;
+const encodedDataURL = encodeURIComponent(dataURL);
+
+[dataURL, `resources/redirect.py?location=${encodedDataURL}`].forEach(url => {
+ [undefined, "opener", "noopener", "noreferrer"].forEach(opener => {
+ async_test(t => {
+ const popup = window.open(url, "", opener);
+ t.step_timeout(() => {
+ if (opener === "noopener" || opener == "noreferrer") {
+ assert_equals(popup, null);
+ } else {
+ assert_true(popup.closed);
+ }
+ t.done();
+ }, 1500);
+ }, `Navigating a popup using window.open("${url}", "", "${opener}")`);
+ });
+});