summaryrefslogtreecommitdiffstats
path: root/docshell/test
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--docshell/test/browser/Bug1622420Child.sys.mjs9
-rw-r--r--docshell/test/browser/Bug422543Child.sys.mjs98
-rw-r--r--docshell/test/browser/browser.ini244
-rw-r--r--docshell/test/browser/browser_alternate_fixup_middle_click_link.js59
-rw-r--r--docshell/test/browser/browser_backforward_restore_scroll.js54
-rw-r--r--docshell/test/browser/browser_backforward_userinteraction.js374
-rw-r--r--docshell/test/browser/browser_backforward_userinteraction_about.js67
-rw-r--r--docshell/test/browser/browser_backforward_userinteraction_systemprincipal.js112
-rw-r--r--docshell/test/browser/browser_badCertDomainFixup.js92
-rw-r--r--docshell/test/browser/browser_bfcache_copycommand.js98
-rw-r--r--docshell/test/browser/browser_browsingContext-01.js178
-rw-r--r--docshell/test/browser/browser_browsingContext-02.js233
-rw-r--r--docshell/test/browser/browser_browsingContext-embedder.js156
-rw-r--r--docshell/test/browser/browser_browsingContext-getAllBrowsingContextsInSubtree.js53
-rw-r--r--docshell/test/browser/browser_browsingContext-getWindowByName.js34
-rw-r--r--docshell/test/browser/browser_browsingContext-webProgress.js238
-rw-r--r--docshell/test/browser/browser_browsing_context_attached.js179
-rw-r--r--docshell/test/browser/browser_browsing_context_discarded.js65
-rw-r--r--docshell/test/browser/browser_bug1206879.js50
-rw-r--r--docshell/test/browser/browser_bug1309900_crossProcessHistoryNavigation.js54
-rw-r--r--docshell/test/browser/browser_bug1328501.js64
-rw-r--r--docshell/test/browser/browser_bug1347823.js85
-rw-r--r--docshell/test/browser/browser_bug134911.js57
-rw-r--r--docshell/test/browser/browser_bug1415918_beforeunload_options.js162
-rw-r--r--docshell/test/browser/browser_bug1543077-3.js46
-rw-r--r--docshell/test/browser/browser_bug1594938.js99
-rw-r--r--docshell/test/browser/browser_bug1622420.js31
-rw-r--r--docshell/test/browser/browser_bug1648464-1.js46
-rw-r--r--docshell/test/browser/browser_bug1673702.js26
-rw-r--r--docshell/test/browser/browser_bug1674464.js37
-rw-r--r--docshell/test/browser/browser_bug1688368-1.js24
-rw-r--r--docshell/test/browser/browser_bug1691153.js73
-rw-r--r--docshell/test/browser/browser_bug1705872.js74
-rw-r--r--docshell/test/browser/browser_bug1716290-1.js24
-rw-r--r--docshell/test/browser/browser_bug1716290-2.js24
-rw-r--r--docshell/test/browser/browser_bug1716290-3.js24
-rw-r--r--docshell/test/browser/browser_bug1716290-4.js24
-rw-r--r--docshell/test/browser/browser_bug1719178.js34
-rw-r--r--docshell/test/browser/browser_bug1736248-1.js34
-rw-r--r--docshell/test/browser/browser_bug1757005.js73
-rw-r--r--docshell/test/browser/browser_bug1769189.js33
-rw-r--r--docshell/test/browser/browser_bug1798780.js51
-rw-r--r--docshell/test/browser/browser_bug234628-1.js47
-rw-r--r--docshell/test/browser/browser_bug234628-10.js46
-rw-r--r--docshell/test/browser/browser_bug234628-11.js46
-rw-r--r--docshell/test/browser/browser_bug234628-2.js49
-rw-r--r--docshell/test/browser/browser_bug234628-3.js47
-rw-r--r--docshell/test/browser/browser_bug234628-4.js46
-rw-r--r--docshell/test/browser/browser_bug234628-5.js46
-rw-r--r--docshell/test/browser/browser_bug234628-6.js47
-rw-r--r--docshell/test/browser/browser_bug234628-8.js18
-rw-r--r--docshell/test/browser/browser_bug234628-9.js18
-rw-r--r--docshell/test/browser/browser_bug349769.js72
-rw-r--r--docshell/test/browser/browser_bug388121-1.js22
-rw-r--r--docshell/test/browser/browser_bug388121-2.js74
-rw-r--r--docshell/test/browser/browser_bug420605.js133
-rw-r--r--docshell/test/browser/browser_bug422543.js253
-rw-r--r--docshell/test/browser/browser_bug441169.js44
-rw-r--r--docshell/test/browser/browser_bug503832.js76
-rw-r--r--docshell/test/browser/browser_bug554155.js33
-rw-r--r--docshell/test/browser/browser_bug655270.js64
-rw-r--r--docshell/test/browser/browser_bug655273.js55
-rw-r--r--docshell/test/browser/browser_bug670318.js144
-rw-r--r--docshell/test/browser/browser_bug673087-1.js46
-rw-r--r--docshell/test/browser/browser_bug673087-2.js36
-rw-r--r--docshell/test/browser/browser_bug673467.js62
-rw-r--r--docshell/test/browser/browser_bug852909.js35
-rw-r--r--docshell/test/browser/browser_bug92473.js70
-rw-r--r--docshell/test/browser/browser_click_link_within_view_source.js78
-rw-r--r--docshell/test/browser/browser_cross_process_csp_inheritance.js125
-rw-r--r--docshell/test/browser/browser_csp_sandbox_no_script_js_uri.js55
-rw-r--r--docshell/test/browser/browser_csp_uir.js89
-rw-r--r--docshell/test/browser/browser_dataURI_unique_opaque_origin.js30
-rw-r--r--docshell/test/browser/browser_data_load_inherit_csp.js110
-rw-r--r--docshell/test/browser/browser_fall_back_to_https.js73
-rw-r--r--docshell/test/browser/browser_fission_maxOrigins.js222
-rw-r--r--docshell/test/browser/browser_frameloader_swap_with_bfcache.js37
-rw-r--r--docshell/test/browser/browser_history_triggeringprincipal_viewsource.js88
-rw-r--r--docshell/test/browser/browser_isInitialDocument.js323
-rw-r--r--docshell/test/browser/browser_loadURI_postdata.js42
-rw-r--r--docshell/test/browser/browser_multiple_pushState.js25
-rw-r--r--docshell/test/browser/browser_onbeforeunload_frame.js45
-rw-r--r--docshell/test/browser/browser_onbeforeunload_navigation.js165
-rw-r--r--docshell/test/browser/browser_onbeforeunload_parent.js48
-rw-r--r--docshell/test/browser/browser_onunload_stop.js23
-rw-r--r--docshell/test/browser/browser_overlink.js27
-rw-r--r--docshell/test/browser/browser_platform_emulation.js69
-rw-r--r--docshell/test/browser/browser_search_notification.js49
-rw-r--r--docshell/test/browser/browser_tab_replace_while_loading.js83
-rw-r--r--docshell/test/browser/browser_tab_touch_events.js74
-rw-r--r--docshell/test/browser/browser_targetTopLevelLinkClicksToBlank.js285
-rw-r--r--docshell/test/browser/browser_timelineMarkers-01.js45
-rw-r--r--docshell/test/browser/browser_timelineMarkers-02.js16
-rw-r--r--docshell/test/browser/browser_timelineMarkers-03.js8
-rw-r--r--docshell/test/browser/browser_timelineMarkers-04.js9
-rw-r--r--docshell/test/browser/browser_timelineMarkers-05.js16
-rw-r--r--docshell/test/browser/browser_timelineMarkers-frame-02.js185
-rw-r--r--docshell/test/browser/browser_timelineMarkers-frame-03.js108
-rw-r--r--docshell/test/browser/browser_timelineMarkers-frame-04.js125
-rw-r--r--docshell/test/browser/browser_timelineMarkers-frame-05.js152
-rw-r--r--docshell/test/browser/browser_title_in_session_history.js63
-rw-r--r--docshell/test/browser/browser_ua_emulation.js70
-rw-r--r--docshell/test/browser/browser_uriFixupAlternateRedirects.js66
-rw-r--r--docshell/test/browser/browser_uriFixupIntegration.js104
-rw-r--r--docshell/test/browser/browser_viewsource_chrome_to_content.js20
-rw-r--r--docshell/test/browser/browser_viewsource_multipart.js44
-rw-r--r--docshell/test/browser/dummy_iframe_page.html8
-rw-r--r--docshell/test/browser/dummy_page.html6
-rw-r--r--docshell/test/browser/favicon_bug655270.icobin0 -> 1406 bytes
-rw-r--r--docshell/test/browser/file_backforward_restore_scroll.html10
-rw-r--r--docshell/test/browser/file_backforward_restore_scroll.html^headers^1
-rw-r--r--docshell/test/browser/file_basic_multipart.sjs24
-rw-r--r--docshell/test/browser/file_bug1046022.html54
-rw-r--r--docshell/test/browser/file_bug1206879.html9
-rw-r--r--docshell/test/browser/file_bug1328501.html27
-rw-r--r--docshell/test/browser/file_bug1328501_frame.html4
-rw-r--r--docshell/test/browser/file_bug1328501_framescript.js38
-rw-r--r--docshell/test/browser/file_bug1543077-3-child.html11
-rw-r--r--docshell/test/browser/file_bug1543077-3.html16
-rw-r--r--docshell/test/browser/file_bug1622420.html1
-rw-r--r--docshell/test/browser/file_bug1648464-1-child.html13
-rw-r--r--docshell/test/browser/file_bug1648464-1.html18
-rw-r--r--docshell/test/browser/file_bug1673702.json1
-rw-r--r--docshell/test/browser/file_bug1673702.json^headers^1
-rw-r--r--docshell/test/browser/file_bug1688368-1.sjs44
-rw-r--r--docshell/test/browser/file_bug1691153.html27
-rw-r--r--docshell/test/browser/file_bug1716290-1.sjs21
-rw-r--r--docshell/test/browser/file_bug1716290-2.sjs18
-rw-r--r--docshell/test/browser/file_bug1716290-3.sjs17
-rw-r--r--docshell/test/browser/file_bug1716290-4.sjs17
-rw-r--r--docshell/test/browser/file_bug1736248-1.html4
-rw-r--r--docshell/test/browser/file_bug234628-1-child.html12
-rw-r--r--docshell/test/browser/file_bug234628-1.html17
-rw-r--r--docshell/test/browser/file_bug234628-10-child.xhtml4
-rw-r--r--docshell/test/browser/file_bug234628-10.html17
-rw-r--r--docshell/test/browser/file_bug234628-11-child.xhtml4
-rw-r--r--docshell/test/browser/file_bug234628-11-child.xhtml^headers^1
-rw-r--r--docshell/test/browser/file_bug234628-11.html17
-rw-r--r--docshell/test/browser/file_bug234628-2-child.html12
-rw-r--r--docshell/test/browser/file_bug234628-2.html17
-rw-r--r--docshell/test/browser/file_bug234628-3-child.html13
-rw-r--r--docshell/test/browser/file_bug234628-3.html18
-rw-r--r--docshell/test/browser/file_bug234628-4-child.html12
-rw-r--r--docshell/test/browser/file_bug234628-4.html18
-rw-r--r--docshell/test/browser/file_bug234628-5-child.htmlbin0 -> 498 bytes
-rw-r--r--docshell/test/browser/file_bug234628-5.html18
-rw-r--r--docshell/test/browser/file_bug234628-6-child.htmlbin0 -> 540 bytes
-rw-r--r--docshell/test/browser/file_bug234628-6-child.html^headers^1
-rw-r--r--docshell/test/browser/file_bug234628-6.html18
-rw-r--r--docshell/test/browser/file_bug234628-8-child.html12
-rw-r--r--docshell/test/browser/file_bug234628-8.html17
-rw-r--r--docshell/test/browser/file_bug234628-9-child.html12
-rw-r--r--docshell/test/browser/file_bug234628-9.htmlbin0 -> 740 bytes
-rw-r--r--docshell/test/browser/file_bug420605.html31
-rw-r--r--docshell/test/browser/file_bug503832.html35
-rw-r--r--docshell/test/browser/file_bug655270.html11
-rw-r--r--docshell/test/browser/file_bug670318.html23
-rw-r--r--docshell/test/browser/file_bug673087-1-child.html13
-rw-r--r--docshell/test/browser/file_bug673087-1.htmlbin0 -> 432 bytes
-rw-r--r--docshell/test/browser/file_bug673087-1.html^headers^1
-rw-r--r--docshell/test/browser/file_bug673087-2.html2
-rw-r--r--docshell/test/browser/file_bug852909.pdfbin0 -> 1568 bytes
-rw-r--r--docshell/test/browser/file_bug852909.pngbin0 -> 94 bytes
-rw-r--r--docshell/test/browser/file_click_link_within_view_source.html6
-rw-r--r--docshell/test/browser/file_cross_process_csp_inheritance.html11
-rw-r--r--docshell/test/browser/file_csp_sandbox_no_script_js_uri.html11
-rw-r--r--docshell/test/browser/file_csp_sandbox_no_script_js_uri.html^headers^1
-rw-r--r--docshell/test/browser/file_csp_uir.html11
-rw-r--r--docshell/test/browser/file_csp_uir_dummy.html1
-rw-r--r--docshell/test/browser/file_data_load_inherit_csp.html11
-rw-r--r--docshell/test/browser/file_multiple_pushState.html20
-rw-r--r--docshell/test/browser/file_onbeforeunload_0.html9
-rw-r--r--docshell/test/browser/file_onbeforeunload_1.html9
-rw-r--r--docshell/test/browser/file_onbeforeunload_2.html10
-rw-r--r--docshell/test/browser/file_onbeforeunload_3.html9
-rw-r--r--docshell/test/browser/file_open_about_blank.html2
-rw-r--r--docshell/test/browser/file_slow_load.sjs8
-rw-r--r--docshell/test/browser/frame-head.js111
-rw-r--r--docshell/test/browser/head.js258
-rw-r--r--docshell/test/browser/head_browser_onbeforeunload.js271
-rw-r--r--docshell/test/browser/onload_message.html25
-rw-r--r--docshell/test/browser/onpageshow_message.html41
-rw-r--r--docshell/test/browser/overlink_test.html7
-rw-r--r--docshell/test/browser/print_postdata.sjs25
-rw-r--r--docshell/test/browser/redirect_to_example.sjs5
-rw-r--r--docshell/test/browser/test-form_sjis.html24
-rw-r--r--docshell/test/browser/timelineMarkers-04.html56
-rw-r--r--docshell/test/chrome/112564_nocache.html10
-rw-r--r--docshell/test/chrome/112564_nocache.html^headers^1
-rw-r--r--docshell/test/chrome/215405_nocache.html14
-rw-r--r--docshell/test/chrome/215405_nocache.html^headers^1
-rw-r--r--docshell/test/chrome/215405_nostore.html14
-rw-r--r--docshell/test/chrome/215405_nostore.html^headers^1
-rw-r--r--docshell/test/chrome/582176_dummy.html1
-rw-r--r--docshell/test/chrome/582176_xml.xml2
-rw-r--r--docshell/test/chrome/582176_xslt.xsl8
-rw-r--r--docshell/test/chrome/662200a.html8
-rw-r--r--docshell/test/chrome/662200b.html8
-rw-r--r--docshell/test/chrome/662200c.html7
-rw-r--r--docshell/test/chrome/89419.html7
-rw-r--r--docshell/test/chrome/92598_nostore.html10
-rw-r--r--docshell/test/chrome/92598_nostore.html^headers^1
-rw-r--r--docshell/test/chrome/DocShellHelpers.sys.mjs74
-rw-r--r--docshell/test/chrome/allowContentRetargeting.sjs7
-rw-r--r--docshell/test/chrome/blue.pngbin0 -> 2745 bytes
-rw-r--r--docshell/test/chrome/bug112564_window.xhtml86
-rw-r--r--docshell/test/chrome/bug113934_window.xhtml165
-rw-r--r--docshell/test/chrome/bug215405_window.xhtml177
-rw-r--r--docshell/test/chrome/bug293235.html13
-rw-r--r--docshell/test/chrome/bug293235_p2.html8
-rw-r--r--docshell/test/chrome/bug293235_window.xhtml118
-rw-r--r--docshell/test/chrome/bug294258_testcase.html43
-rw-r--r--docshell/test/chrome/bug294258_window.xhtml72
-rw-r--r--docshell/test/chrome/bug298622_window.xhtml135
-rw-r--r--docshell/test/chrome/bug301397_1.html9
-rw-r--r--docshell/test/chrome/bug301397_2.html10
-rw-r--r--docshell/test/chrome/bug301397_3.html10
-rw-r--r--docshell/test/chrome/bug301397_4.html9
-rw-r--r--docshell/test/chrome/bug301397_window.xhtml218
-rw-r--r--docshell/test/chrome/bug303267.html23
-rw-r--r--docshell/test/chrome/bug303267_window.xhtml83
-rw-r--r--docshell/test/chrome/bug311007_window.xhtml204
-rw-r--r--docshell/test/chrome/bug321671_window.xhtml128
-rw-r--r--docshell/test/chrome/bug360511_case1.html15
-rw-r--r--docshell/test/chrome/bug360511_case2.html15
-rw-r--r--docshell/test/chrome/bug360511_window.xhtml127
-rw-r--r--docshell/test/chrome/bug364461_window.xhtml253
-rw-r--r--docshell/test/chrome/bug396519_window.xhtml132
-rw-r--r--docshell/test/chrome/bug396649_window.xhtml119
-rw-r--r--docshell/test/chrome/bug449778_window.xhtml107
-rw-r--r--docshell/test/chrome/bug449780_window.xhtml80
-rw-r--r--docshell/test/chrome/bug454235-subframe.xhtml7
-rw-r--r--docshell/test/chrome/bug582176_window.xhtml74
-rw-r--r--docshell/test/chrome/bug608669.xhtml14
-rw-r--r--docshell/test/chrome/bug662200_window.xhtml119
-rw-r--r--docshell/test/chrome/bug690056_window.xhtml171
-rw-r--r--docshell/test/chrome/bug846906.html10
-rw-r--r--docshell/test/chrome/bug89419.sjs12
-rw-r--r--docshell/test/chrome/bug89419_window.xhtml69
-rw-r--r--docshell/test/chrome/bug909218.html11
-rw-r--r--docshell/test/chrome/bug909218.js2
-rw-r--r--docshell/test/chrome/bug92598_window.xhtml89
-rw-r--r--docshell/test/chrome/chrome.ini105
-rw-r--r--docshell/test/chrome/docshell_helpers.js758
-rw-r--r--docshell/test/chrome/file_viewsource_forbidden_in_iframe.html11
-rw-r--r--docshell/test/chrome/gen_template.pl39
-rw-r--r--docshell/test/chrome/generic.html12
-rw-r--r--docshell/test/chrome/mozFrameType_window.xhtml49
-rw-r--r--docshell/test/chrome/red.pngbin0 -> 82 bytes
-rw-r--r--docshell/test/chrome/test.template.txt41
-rw-r--r--docshell/test/chrome/test_allowContentRetargeting.html76
-rw-r--r--docshell/test/chrome/test_bug112564.xhtml37
-rw-r--r--docshell/test/chrome/test_bug113934.xhtml29
-rw-r--r--docshell/test/chrome/test_bug215405.xhtml37
-rw-r--r--docshell/test/chrome/test_bug293235.xhtml38
-rw-r--r--docshell/test/chrome/test_bug294258.xhtml38
-rw-r--r--docshell/test/chrome/test_bug298622.xhtml38
-rw-r--r--docshell/test/chrome/test_bug301397.xhtml38
-rw-r--r--docshell/test/chrome/test_bug303267.xhtml39
-rw-r--r--docshell/test/chrome/test_bug311007.xhtml42
-rw-r--r--docshell/test/chrome/test_bug321671.xhtml38
-rw-r--r--docshell/test/chrome/test_bug360511.xhtml39
-rw-r--r--docshell/test/chrome/test_bug364461.xhtml43
-rw-r--r--docshell/test/chrome/test_bug396519.xhtml28
-rw-r--r--docshell/test/chrome/test_bug396649.xhtml41
-rw-r--r--docshell/test/chrome/test_bug428288.html37
-rw-r--r--docshell/test/chrome/test_bug449778.xhtml29
-rw-r--r--docshell/test/chrome/test_bug449780.xhtml29
-rw-r--r--docshell/test/chrome/test_bug453650.xhtml120
-rw-r--r--docshell/test/chrome/test_bug454235.xhtml40
-rw-r--r--docshell/test/chrome/test_bug456980.xhtml29
-rw-r--r--docshell/test/chrome/test_bug565388.xhtml79
-rw-r--r--docshell/test/chrome/test_bug582176.xhtml38
-rw-r--r--docshell/test/chrome/test_bug608669.xhtml80
-rw-r--r--docshell/test/chrome/test_bug662200.xhtml38
-rw-r--r--docshell/test/chrome/test_bug690056.xhtml26
-rw-r--r--docshell/test/chrome/test_bug789773.xhtml67
-rw-r--r--docshell/test/chrome/test_bug846906.xhtml94
-rw-r--r--docshell/test/chrome/test_bug89419.xhtml38
-rw-r--r--docshell/test/chrome/test_bug909218.html117
-rw-r--r--docshell/test/chrome/test_bug92598.xhtml37
-rw-r--r--docshell/test/chrome/test_docRedirect.sjs6
-rw-r--r--docshell/test/chrome/test_docRedirect.xhtml91
-rw-r--r--docshell/test/chrome/test_mozFrameType.xhtml42
-rw-r--r--docshell/test/chrome/test_open_and_immediately_close_opener.html54
-rw-r--r--docshell/test/chrome/test_viewsource_forbidden_in_iframe.xhtml159
-rw-r--r--docshell/test/chrome/window.template.txt44
-rw-r--r--docshell/test/iframesandbox/file_child_navigation_by_location.html1
-rw-r--r--docshell/test/iframesandbox/file_marquee_event_handlers.html17
-rw-r--r--docshell/test/iframesandbox/file_other_auxiliary_navigation_by_location.html15
-rw-r--r--docshell/test/iframesandbox/file_our_auxiliary_navigation_by_location.html15
-rw-r--r--docshell/test/iframesandbox/file_parent_navigation_by_location.html18
-rw-r--r--docshell/test/iframesandbox/file_sibling_navigation_by_location.html15
-rw-r--r--docshell/test/iframesandbox/file_top_navigation_by_location.html20
-rw-r--r--docshell/test/iframesandbox/file_top_navigation_by_location_exotic.html27
-rw-r--r--docshell/test/iframesandbox/file_top_navigation_by_user_activation.html27
-rw-r--r--docshell/test/iframesandbox/file_top_navigation_by_user_activation_iframe.html32
-rw-r--r--docshell/test/iframesandbox/mochitest.ini28
-rw-r--r--docshell/test/iframesandbox/test_child_navigation_by_location.html91
-rw-r--r--docshell/test/iframesandbox/test_marquee_event_handlers.html95
-rw-r--r--docshell/test/iframesandbox/test_other_auxiliary_navigation_by_location.html80
-rw-r--r--docshell/test/iframesandbox/test_our_auxiliary_navigation_by_location.html84
-rw-r--r--docshell/test/iframesandbox/test_parent_navigation_by_location.html75
-rw-r--r--docshell/test/iframesandbox/test_sibling_navigation_by_location.html78
-rw-r--r--docshell/test/iframesandbox/test_top_navigation_by_location.html167
-rw-r--r--docshell/test/iframesandbox/test_top_navigation_by_location_exotic.html204
-rw-r--r--docshell/test/iframesandbox/test_top_navigation_by_user_activation.html74
-rw-r--r--docshell/test/mochitest/bug1422334_redirect.html3
-rw-r--r--docshell/test/mochitest/bug1422334_redirect.html^headers^2
-rw-r--r--docshell/test/mochitest/bug404548-subframe.html7
-rw-r--r--docshell/test/mochitest/bug404548-subframe_window.html1
-rw-r--r--docshell/test/mochitest/bug413310-post.sjs10
-rw-r--r--docshell/test/mochitest/bug413310-subframe.html7
-rw-r--r--docshell/test/mochitest/bug529119-window.html7
-rw-r--r--docshell/test/mochitest/bug530396-noref.sjs22
-rw-r--r--docshell/test/mochitest/bug530396-subframe.html7
-rw-r--r--docshell/test/mochitest/bug570341_recordevents.html21
-rw-r--r--docshell/test/mochitest/bug668513_redirect.html1
-rw-r--r--docshell/test/mochitest/bug668513_redirect.html^headers^2
-rw-r--r--docshell/test/mochitest/bug691547_frame.html12
-rw-r--r--docshell/test/mochitest/clicker.html7
-rw-r--r--docshell/test/mochitest/double_submit.sjs79
-rw-r--r--docshell/test/mochitest/dummy_page.html6
-rw-r--r--docshell/test/mochitest/file_anchor_scroll_after_document_open.html15
-rw-r--r--docshell/test/mochitest/file_bfcache_plus_hash_1.html24
-rw-r--r--docshell/test/mochitest/file_bfcache_plus_hash_2.html17
-rw-r--r--docshell/test/mochitest/file_bug1121701_1.html29
-rw-r--r--docshell/test/mochitest/file_bug1121701_2.html23
-rw-r--r--docshell/test/mochitest/file_bug1151421.html19
-rw-r--r--docshell/test/mochitest/file_bug1186774.html1
-rw-r--r--docshell/test/mochitest/file_bug1450164.html16
-rw-r--r--docshell/test/mochitest/file_bug1729662.html8
-rw-r--r--docshell/test/mochitest/file_bug1740516_1.html29
-rw-r--r--docshell/test/mochitest/file_bug1740516_1_inner.html15
-rw-r--r--docshell/test/mochitest/file_bug1740516_2.html11
-rw-r--r--docshell/test/mochitest/file_bug1741132.html29
-rw-r--r--docshell/test/mochitest/file_bug1742865.sjs77
-rw-r--r--docshell/test/mochitest/file_bug1742865_outer.sjs25
-rw-r--r--docshell/test/mochitest/file_bug1743353.html37
-rw-r--r--docshell/test/mochitest/file_bug1747033.sjs110
-rw-r--r--docshell/test/mochitest/file_bug1773192_1.html13
-rw-r--r--docshell/test/mochitest/file_bug1773192_2.html13
-rw-r--r--docshell/test/mochitest/file_bug1773192_3.sjs3
-rw-r--r--docshell/test/mochitest/file_bug385434_1.html29
-rw-r--r--docshell/test/mochitest/file_bug385434_2.html26
-rw-r--r--docshell/test/mochitest/file_bug385434_3.html22
-rw-r--r--docshell/test/mochitest/file_bug475636.sjs98
-rw-r--r--docshell/test/mochitest/file_bug509055.html9
-rw-r--r--docshell/test/mochitest/file_bug511449.html6
-rw-r--r--docshell/test/mochitest/file_bug540462.html25
-rw-r--r--docshell/test/mochitest/file_bug580069_1.html8
-rw-r--r--docshell/test/mochitest/file_bug580069_2.sjs8
-rw-r--r--docshell/test/mochitest/file_bug590573_1.html7
-rw-r--r--docshell/test/mochitest/file_bug590573_2.html8
-rw-r--r--docshell/test/mochitest/file_bug598895_1.html1
-rw-r--r--docshell/test/mochitest/file_bug598895_2.html1
-rw-r--r--docshell/test/mochitest/file_bug634834.html5
-rw-r--r--docshell/test/mochitest/file_bug637644_1.html1
-rw-r--r--docshell/test/mochitest/file_bug637644_2.html1
-rw-r--r--docshell/test/mochitest/file_bug640387.html26
-rw-r--r--docshell/test/mochitest/file_bug653741.html13
-rw-r--r--docshell/test/mochitest/file_bug66040413
-rw-r--r--docshell/test/mochitest/file_bug660404-1.html12
-rw-r--r--docshell/test/mochitest/file_bug660404^headers^1
-rw-r--r--docshell/test/mochitest/file_bug662170.html13
-rw-r--r--docshell/test/mochitest/file_bug668513.html101
-rw-r--r--docshell/test/mochitest/file_bug669671.sjs17
-rw-r--r--docshell/test/mochitest/file_bug675587.html1
-rw-r--r--docshell/test/mochitest/file_bug680257.html16
-rw-r--r--docshell/test/mochitest/file_bug703855.html2
-rw-r--r--docshell/test/mochitest/file_bug728939.html3
-rw-r--r--docshell/test/mochitest/file_close_onpagehide1.html5
-rw-r--r--docshell/test/mochitest/file_close_onpagehide2.html5
-rw-r--r--docshell/test/mochitest/file_compressed_multipartbin0 -> 111 bytes
-rw-r--r--docshell/test/mochitest/file_compressed_multipart^headers^2
-rw-r--r--docshell/test/mochitest/file_content_javascript_loads_frame.html17
-rw-r--r--docshell/test/mochitest/file_content_javascript_loads_root.html42
-rw-r--r--docshell/test/mochitest/file_form_restoration_no_store.html38
-rw-r--r--docshell/test/mochitest/file_form_restoration_no_store.html^headers^1
-rw-r--r--docshell/test/mochitest/file_framedhistoryframes.html16
-rw-r--r--docshell/test/mochitest/file_load_during_reload.html12
-rw-r--r--docshell/test/mochitest/file_pushState_after_document_open.html11
-rw-r--r--docshell/test/mochitest/file_redirect_history.html18
-rw-r--r--docshell/test/mochitest/form_submit.sjs40
-rw-r--r--docshell/test/mochitest/form_submit_redirect.sjs15
-rw-r--r--docshell/test/mochitest/historyframes.html176
-rw-r--r--docshell/test/mochitest/mochitest.ini174
-rw-r--r--docshell/test/mochitest/ping.html6
-rw-r--r--docshell/test/mochitest/start_historyframe.html1
-rw-r--r--docshell/test/mochitest/test_anchor_scroll_after_document_open.html55
-rw-r--r--docshell/test/mochitest/test_bfcache_plus_hash.html153
-rw-r--r--docshell/test/mochitest/test_bug1045096.html29
-rw-r--r--docshell/test/mochitest/test_bug1121701.html108
-rw-r--r--docshell/test/mochitest/test_bug1151421.html61
-rw-r--r--docshell/test/mochitest/test_bug1186774.html51
-rw-r--r--docshell/test/mochitest/test_bug1422334.html40
-rw-r--r--docshell/test/mochitest/test_bug1450164.html31
-rw-r--r--docshell/test/mochitest/test_bug1507702.html57
-rw-r--r--docshell/test/mochitest/test_bug1645781.html90
-rw-r--r--docshell/test/mochitest/test_bug1729662.html76
-rw-r--r--docshell/test/mochitest/test_bug1740516.html79
-rw-r--r--docshell/test/mochitest/test_bug1741132.html79
-rw-r--r--docshell/test/mochitest/test_bug1742865.html137
-rw-r--r--docshell/test/mochitest/test_bug1743353.html57
-rw-r--r--docshell/test/mochitest/test_bug1747033.html97
-rw-r--r--docshell/test/mochitest/test_bug1773192.html61
-rw-r--r--docshell/test/mochitest/test_bug385434.html211
-rw-r--r--docshell/test/mochitest/test_bug387979.html52
-rw-r--r--docshell/test/mochitest/test_bug402210.html50
-rw-r--r--docshell/test/mochitest/test_bug404548.html39
-rw-r--r--docshell/test/mochitest/test_bug413310.html106
-rw-r--r--docshell/test/mochitest/test_bug475636.html52
-rw-r--r--docshell/test/mochitest/test_bug509055.html115
-rw-r--r--docshell/test/mochitest/test_bug511449.html56
-rw-r--r--docshell/test/mochitest/test_bug529119-1.html110
-rw-r--r--docshell/test/mochitest/test_bug529119-2.html116
-rw-r--r--docshell/test/mochitest/test_bug530396.html56
-rw-r--r--docshell/test/mochitest/test_bug540462.html44
-rw-r--r--docshell/test/mochitest/test_bug551225.html32
-rw-r--r--docshell/test/mochitest/test_bug570341.html142
-rw-r--r--docshell/test/mochitest/test_bug580069.html58
-rw-r--r--docshell/test/mochitest/test_bug590573.html198
-rw-r--r--docshell/test/mochitest/test_bug598895.html52
-rw-r--r--docshell/test/mochitest/test_bug634834.html52
-rw-r--r--docshell/test/mochitest/test_bug637644.html52
-rw-r--r--docshell/test/mochitest/test_bug640387_1.html107
-rw-r--r--docshell/test/mochitest/test_bug640387_2.html89
-rw-r--r--docshell/test/mochitest/test_bug653741.html49
-rw-r--r--docshell/test/mochitest/test_bug660404.html76
-rw-r--r--docshell/test/mochitest/test_bug662170.html51
-rw-r--r--docshell/test/mochitest/test_bug668513.html28
-rw-r--r--docshell/test/mochitest/test_bug669671.html145
-rw-r--r--docshell/test/mochitest/test_bug675587.html33
-rw-r--r--docshell/test/mochitest/test_bug680257.html76
-rw-r--r--docshell/test/mochitest/test_bug691547.html59
-rw-r--r--docshell/test/mochitest/test_bug694612.html34
-rw-r--r--docshell/test/mochitest/test_bug703855.html79
-rw-r--r--docshell/test/mochitest/test_bug728939.html37
-rw-r--r--docshell/test/mochitest/test_bug797909.html66
-rw-r--r--docshell/test/mochitest/test_close_onpagehide_by_history_back.html24
-rw-r--r--docshell/test/mochitest/test_close_onpagehide_by_window_close.html20
-rw-r--r--docshell/test/mochitest/test_compressed_multipart.html41
-rw-r--r--docshell/test/mochitest/test_content_javascript_loads.html163
-rw-r--r--docshell/test/mochitest/test_double_submit.html98
-rw-r--r--docshell/test/mochitest/test_forceinheritprincipal_overrule_owner.html57
-rw-r--r--docshell/test/mochitest/test_form_restoration.html77
-rw-r--r--docshell/test/mochitest/test_framedhistoryframes.html32
-rw-r--r--docshell/test/mochitest/test_iframe_srcdoc_to_remote.html44
-rw-r--r--docshell/test/mochitest/test_javascript_sandboxed_popup.html27
-rw-r--r--docshell/test/mochitest/test_load_during_reload.html35
-rw-r--r--docshell/test/mochitest/test_navigate_after_pagehide.html34
-rw-r--r--docshell/test/mochitest/test_pushState_after_document_open.html39
-rw-r--r--docshell/test/mochitest/test_redirect_history.html58
-rw-r--r--docshell/test/mochitest/test_triggeringprincipal_location_seturi.html105
-rw-r--r--docshell/test/mochitest/test_windowedhistoryframes.html32
-rw-r--r--docshell/test/mochitest/url1_historyframe.html1
-rw-r--r--docshell/test/mochitest/url2_historyframe.html1
-rw-r--r--docshell/test/moz.build137
-rw-r--r--docshell/test/navigation/NavigationUtils.js203
-rw-r--r--docshell/test/navigation/blank.html1
-rw-r--r--docshell/test/navigation/bluebox_bug430723.html6
-rw-r--r--docshell/test/navigation/browser.ini23
-rw-r--r--docshell/test/navigation/browser_bug1757458.js45
-rw-r--r--docshell/test/navigation/browser_bug343515.js267
-rw-r--r--docshell/test/navigation/browser_ghistorymaxsize_is_0.js81
-rw-r--r--docshell/test/navigation/browser_test-content-chromeflags.js57
-rw-r--r--docshell/test/navigation/browser_test_bfcache_eviction.js98
-rw-r--r--docshell/test/navigation/browser_test_shentry_wireframe.js128
-rw-r--r--docshell/test/navigation/browser_test_simultaneous_normal_and_history_loads.js53
-rw-r--r--docshell/test/navigation/bug343515_pg1.html5
-rw-r--r--docshell/test/navigation/bug343515_pg2.html7
-rw-r--r--docshell/test/navigation/bug343515_pg3.html7
-rw-r--r--docshell/test/navigation/bug343515_pg3_1.html6
-rw-r--r--docshell/test/navigation/bug343515_pg3_1_1.html1
-rw-r--r--docshell/test/navigation/bug343515_pg3_2.html1
-rw-r--r--docshell/test/navigation/cache_control_max_age_3600.sjs20
-rw-r--r--docshell/test/navigation/file_beforeunload_and_bfcache.html31
-rw-r--r--docshell/test/navigation/file_blockBFCache.html33
-rw-r--r--docshell/test/navigation/file_bug1300461.html61
-rw-r--r--docshell/test/navigation/file_bug1300461_back.html37
-rw-r--r--docshell/test/navigation/file_bug1300461_redirect.html10
-rw-r--r--docshell/test/navigation/file_bug1300461_redirect.html^headers^2
-rw-r--r--docshell/test/navigation/file_bug1326251.html212
-rw-r--r--docshell/test/navigation/file_bug1326251_evict_cache.html17
-rw-r--r--docshell/test/navigation/file_bug1364364-1.html33
-rw-r--r--docshell/test/navigation/file_bug1364364-2.html14
-rw-r--r--docshell/test/navigation/file_bug1375833-frame1.html8
-rw-r--r--docshell/test/navigation/file_bug1375833-frame2.html8
-rw-r--r--docshell/test/navigation/file_bug1375833.html22
-rw-r--r--docshell/test/navigation/file_bug1379762-1.html35
-rw-r--r--docshell/test/navigation/file_bug1536471.html8
-rw-r--r--docshell/test/navigation/file_bug1583110.html26
-rw-r--r--docshell/test/navigation/file_bug1609475.html51
-rw-r--r--docshell/test/navigation/file_bug1706090.html40
-rw-r--r--docshell/test/navigation/file_bug1745638.html15
-rw-r--r--docshell/test/navigation/file_bug1750973.html45
-rw-r--r--docshell/test/navigation/file_bug1758664.html32
-rw-r--r--docshell/test/navigation/file_bug386782_contenteditable.html1
-rw-r--r--docshell/test/navigation/file_bug386782_designmode.html1
-rw-r--r--docshell/test/navigation/file_bug462076_1.html55
-rw-r--r--docshell/test/navigation/file_bug462076_2.html52
-rw-r--r--docshell/test/navigation/file_bug462076_3.html52
-rw-r--r--docshell/test/navigation/file_bug508537_1.html33
-rw-r--r--docshell/test/navigation/file_bug534178.html30
-rw-r--r--docshell/test/navigation/file_contentpolicy_block_window.html5
-rw-r--r--docshell/test/navigation/file_docshell_gotoindex.html42
-rw-r--r--docshell/test/navigation/file_document_write_1.html18
-rw-r--r--docshell/test/navigation/file_evict_from_bfcache.html29
-rw-r--r--docshell/test/navigation/file_fragment_handling_during_load.html27
-rw-r--r--docshell/test/navigation/file_fragment_handling_during_load_frame1.html6
-rw-r--r--docshell/test/navigation/file_fragment_handling_during_load_frame2.sjs20
-rw-r--r--docshell/test/navigation/file_load_history_entry_page_with_one_link.html7
-rw-r--r--docshell/test/navigation/file_load_history_entry_page_with_two_links.html9
-rw-r--r--docshell/test/navigation/file_meta_refresh.html40
-rw-r--r--docshell/test/navigation/file_navigation_type.html25
-rw-r--r--docshell/test/navigation/file_nested_frames.html27
-rw-r--r--docshell/test/navigation/file_nested_frames_innerframe.html1
-rw-r--r--docshell/test/navigation/file_nested_srcdoc.html3
-rw-r--r--docshell/test/navigation/file_new_shentry_during_history_navigation_1.html5
-rw-r--r--docshell/test/navigation/file_new_shentry_during_history_navigation_1.html^headers^1
-rw-r--r--docshell/test/navigation/file_new_shentry_during_history_navigation_2.html10
-rw-r--r--docshell/test/navigation/file_new_shentry_during_history_navigation_2.html^headers^1
-rw-r--r--docshell/test/navigation/file_new_shentry_during_history_navigation_3.html22
-rw-r--r--docshell/test/navigation/file_new_shentry_during_history_navigation_3.html^headers^1
-rw-r--r--docshell/test/navigation/file_new_shentry_during_history_navigation_4.html16
-rw-r--r--docshell/test/navigation/file_online_offline_bfcache.html41
-rw-r--r--docshell/test/navigation/file_reload.html23
-rw-r--r--docshell/test/navigation/file_reload_large_postdata.sjs46
-rw-r--r--docshell/test/navigation/file_reload_nonbfcached_srcdoc.sjs27
-rw-r--r--docshell/test/navigation/file_same_url.html24
-rw-r--r--docshell/test/navigation/file_scrollRestoration_bfcache_and_nobfcache.html30
-rw-r--r--docshell/test/navigation/file_scrollRestoration_bfcache_and_nobfcache_part2.html35
-rw-r--r--docshell/test/navigation/file_scrollRestoration_bfcache_and_nobfcache_part2.html^headers^1
-rw-r--r--docshell/test/navigation/file_scrollRestoration_navigate.html17
-rw-r--r--docshell/test/navigation/file_scrollRestoration_part1_nobfcache.html63
-rw-r--r--docshell/test/navigation/file_scrollRestoration_part1_nobfcache.html^headers^1
-rw-r--r--docshell/test/navigation/file_scrollRestoration_part2_bfcache.html57
-rw-r--r--docshell/test/navigation/file_scrollRestoration_part3_nobfcache.html157
-rw-r--r--docshell/test/navigation/file_scrollRestoration_part3_nobfcache.html^headers^1
-rw-r--r--docshell/test/navigation/file_session_history_on_redirect.html16
-rw-r--r--docshell/test/navigation/file_session_history_on_redirect.html^headers^1
-rw-r--r--docshell/test/navigation/file_session_history_on_redirect_2.html16
-rw-r--r--docshell/test/navigation/file_session_history_on_redirect_2.html^headers^1
-rw-r--r--docshell/test/navigation/file_sessionhistory_iframe_removal.html37
-rw-r--r--docshell/test/navigation/file_shiftReload_and_pushState.html28
-rw-r--r--docshell/test/navigation/file_ship_beforeunload_fired.html37
-rw-r--r--docshell/test/navigation/file_static_and_dynamic_1.html31
-rw-r--r--docshell/test/navigation/file_tell_opener.html8
-rw-r--r--docshell/test/navigation/file_triggeringprincipal_frame_1.html27
-rw-r--r--docshell/test/navigation/file_triggeringprincipal_frame_2.html8
-rw-r--r--docshell/test/navigation/file_triggeringprincipal_iframe_iframe_window_open_frame_a.html6
-rw-r--r--docshell/test/navigation/file_triggeringprincipal_iframe_iframe_window_open_frame_a_nav.html6
-rw-r--r--docshell/test/navigation/file_triggeringprincipal_iframe_iframe_window_open_frame_b.html15
-rw-r--r--docshell/test/navigation/file_triggeringprincipal_parent_iframe_window_open_base.html6
-rw-r--r--docshell/test/navigation/file_triggeringprincipal_parent_iframe_window_open_nav.html6
-rw-r--r--docshell/test/navigation/file_triggeringprincipal_subframe.html15
-rw-r--r--docshell/test/navigation/file_triggeringprincipal_subframe_nav.html21
-rw-r--r--docshell/test/navigation/file_triggeringprincipal_subframe_same_origin_nav.html20
-rw-r--r--docshell/test/navigation/file_triggeringprincipal_window_open.html6
-rw-r--r--docshell/test/navigation/frame0.html3
-rw-r--r--docshell/test/navigation/frame1.html3
-rw-r--r--docshell/test/navigation/frame2.html3
-rw-r--r--docshell/test/navigation/frame3.html3
-rw-r--r--docshell/test/navigation/frame_1_out_of_6.html6
-rw-r--r--docshell/test/navigation/frame_2_out_of_6.html6
-rw-r--r--docshell/test/navigation/frame_3_out_of_6.html6
-rw-r--r--docshell/test/navigation/frame_4_out_of_6.html6
-rw-r--r--docshell/test/navigation/frame_5_out_of_6.html6
-rw-r--r--docshell/test/navigation/frame_6_out_of_6.html6
-rw-r--r--docshell/test/navigation/frame_load_as_example_com.html6
-rw-r--r--docshell/test/navigation/frame_load_as_example_org.html6
-rw-r--r--docshell/test/navigation/frame_load_as_host1.html6
-rw-r--r--docshell/test/navigation/frame_load_as_host2.html6
-rw-r--r--docshell/test/navigation/frame_load_as_host3.html6
-rw-r--r--docshell/test/navigation/frame_recursive.html6
-rw-r--r--docshell/test/navigation/goback.html5
-rw-r--r--docshell/test/navigation/iframe.html9
-rw-r--r--docshell/test/navigation/iframe_slow_onload.html5
-rw-r--r--docshell/test/navigation/iframe_slow_onload_inner.html19
-rw-r--r--docshell/test/navigation/iframe_static.html8
-rw-r--r--docshell/test/navigation/mochitest.ini219
-rw-r--r--docshell/test/navigation/navigate.html38
-rw-r--r--docshell/test/navigation/navigation_target_popup_url.html1
-rw-r--r--docshell/test/navigation/navigation_target_url.html1
-rw-r--r--docshell/test/navigation/object_recursive_load.html6
-rw-r--r--docshell/test/navigation/open.html10
-rw-r--r--docshell/test/navigation/parent.html14
-rw-r--r--docshell/test/navigation/redbox_bug430723.html6
-rw-r--r--docshell/test/navigation/redirect_handlers.sjs29
-rw-r--r--docshell/test/navigation/redirect_to_blank.sjs6
-rw-r--r--docshell/test/navigation/slow.sjs16
-rw-r--r--docshell/test/navigation/test_aboutblank_change_process.html46
-rw-r--r--docshell/test/navigation/test_beforeunload_and_bfcache.html97
-rw-r--r--docshell/test/navigation/test_blockBFCache.html294
-rw-r--r--docshell/test/navigation/test_bug1300461.html70
-rw-r--r--docshell/test/navigation/test_bug1326251.html47
-rw-r--r--docshell/test/navigation/test_bug1364364.html65
-rw-r--r--docshell/test/navigation/test_bug1375833.html131
-rw-r--r--docshell/test/navigation/test_bug1379762.html67
-rw-r--r--docshell/test/navigation/test_bug13871.html85
-rw-r--r--docshell/test/navigation/test_bug145971.html29
-rw-r--r--docshell/test/navigation/test_bug1536471.html75
-rw-r--r--docshell/test/navigation/test_bug1583110.html36
-rw-r--r--docshell/test/navigation/test_bug1609475.html35
-rw-r--r--docshell/test/navigation/test_bug1699721.html110
-rw-r--r--docshell/test/navigation/test_bug1706090.html49
-rw-r--r--docshell/test/navigation/test_bug1745638.html40
-rw-r--r--docshell/test/navigation/test_bug1747019.html48
-rw-r--r--docshell/test/navigation/test_bug1750973.html20
-rw-r--r--docshell/test/navigation/test_bug1758664.html21
-rw-r--r--docshell/test/navigation/test_bug270414.html103
-rw-r--r--docshell/test/navigation/test_bug278916.html37
-rw-r--r--docshell/test/navigation/test_bug279495.html44
-rw-r--r--docshell/test/navigation/test_bug344861.html35
-rw-r--r--docshell/test/navigation/test_bug386782.html122
-rw-r--r--docshell/test/navigation/test_bug430624.html57
-rw-r--r--docshell/test/navigation/test_bug430723.html124
-rw-r--r--docshell/test/navigation/test_child.html47
-rw-r--r--docshell/test/navigation/test_contentpolicy_block_window.html98
-rw-r--r--docshell/test/navigation/test_docshell_gotoindex.html29
-rw-r--r--docshell/test/navigation/test_dynamic_frame_forward_back.html35
-rw-r--r--docshell/test/navigation/test_evict_from_bfcache.html63
-rw-r--r--docshell/test/navigation/test_fragment_handling_during_load.html35
-rw-r--r--docshell/test/navigation/test_grandchild.html47
-rw-r--r--docshell/test/navigation/test_load_history_entry.html196
-rw-r--r--docshell/test/navigation/test_meta_refresh.html42
-rw-r--r--docshell/test/navigation/test_navigation_type.html47
-rw-r--r--docshell/test/navigation/test_nested_frames.html35
-rw-r--r--docshell/test/navigation/test_new_shentry_during_history_navigation.html90
-rw-r--r--docshell/test/navigation/test_not-opener.html56
-rw-r--r--docshell/test/navigation/test_online_offline_bfcache.html101
-rw-r--r--docshell/test/navigation/test_open_javascript_noopener.html44
-rw-r--r--docshell/test/navigation/test_opener.html56
-rw-r--r--docshell/test/navigation/test_performance_navigation.html41
-rw-r--r--docshell/test/navigation/test_popup-navigates-children.html69
-rw-r--r--docshell/test/navigation/test_rate_limit_location_change.html100
-rw-r--r--docshell/test/navigation/test_recursive_frames.html167
-rw-r--r--docshell/test/navigation/test_reload.html42
-rw-r--r--docshell/test/navigation/test_reload_large_postdata.html61
-rw-r--r--docshell/test/navigation/test_reload_nonbfcached_srcdoc.html40
-rw-r--r--docshell/test/navigation/test_reserved.html92
-rw-r--r--docshell/test/navigation/test_same_url.html56
-rw-r--r--docshell/test/navigation/test_scrollRestoration.html214
-rw-r--r--docshell/test/navigation/test_session_history_entry_cleanup.html35
-rw-r--r--docshell/test/navigation/test_session_history_on_redirect.html92
-rw-r--r--docshell/test/navigation/test_sessionhistory.html48
-rw-r--r--docshell/test/navigation/test_sessionhistory_document_write.html34
-rw-r--r--docshell/test/navigation/test_sessionhistory_iframe_removal.html33
-rw-r--r--docshell/test/navigation/test_shiftReload_and_pushState.html35
-rw-r--r--docshell/test/navigation/test_ship_beforeunload_fired.html63
-rw-r--r--docshell/test/navigation/test_ship_beforeunload_fired_2.html65
-rw-r--r--docshell/test/navigation/test_ship_beforeunload_fired_3.html65
-rw-r--r--docshell/test/navigation/test_sibling-matching-parent.html46
-rw-r--r--docshell/test/navigation/test_sibling-off-domain.html46
-rw-r--r--docshell/test/navigation/test_state_size.html32
-rw-r--r--docshell/test/navigation/test_static_and_dynamic.html36
-rw-r--r--docshell/test/navigation/test_triggeringprincipal_frame_nav.html74
-rw-r--r--docshell/test/navigation/test_triggeringprincipal_frame_same_origin_nav.html63
-rw-r--r--docshell/test/navigation/test_triggeringprincipal_iframe_iframe_window_open.html87
-rw-r--r--docshell/test/navigation/test_triggeringprincipal_parent_iframe_window_open.html70
-rw-r--r--docshell/test/navigation/test_triggeringprincipal_window_open.html79
-rw-r--r--docshell/test/unit/AllowJavascriptChild.sys.mjs41
-rw-r--r--docshell/test/unit/AllowJavascriptParent.sys.mjs28
-rw-r--r--docshell/test/unit/data/engine.xml10
-rw-r--r--docshell/test/unit/data/enginePost.xml10
-rw-r--r--docshell/test/unit/data/enginePrivate.xml10
-rw-r--r--docshell/test/unit/head_docshell.js106
-rw-r--r--docshell/test/unit/test_URIFixup.js123
-rw-r--r--docshell/test/unit/test_URIFixup_check_host.js183
-rw-r--r--docshell/test/unit/test_URIFixup_external_protocol_fallback.js107
-rw-r--r--docshell/test/unit/test_URIFixup_forced.js162
-rw-r--r--docshell/test/unit/test_URIFixup_info.js1075
-rw-r--r--docshell/test/unit/test_URIFixup_search.js144
-rw-r--r--docshell/test/unit/test_allowJavascript.js269
-rw-r--r--docshell/test/unit/test_browsing_context_structured_clone.js68
-rw-r--r--docshell/test/unit/test_bug442584.js35
-rw-r--r--docshell/test/unit/test_pb_notification.js18
-rw-r--r--docshell/test/unit/test_privacy_transition.js21
-rw-r--r--docshell/test/unit/test_subframe_stop_after_parent_error.js141
-rw-r--r--docshell/test/unit/xpcshell.ini35
-rw-r--r--docshell/test/unit_ipc/test_pb_notification_ipc.js15
-rw-r--r--docshell/test/unit_ipc/xpcshell.ini7
682 files changed, 34856 insertions, 0 deletions
diff --git a/docshell/test/browser/Bug1622420Child.sys.mjs b/docshell/test/browser/Bug1622420Child.sys.mjs
new file mode 100644
index 0000000000..c5520d5943
--- /dev/null
+++ b/docshell/test/browser/Bug1622420Child.sys.mjs
@@ -0,0 +1,9 @@
+export class Bug1622420Child extends JSWindowActorChild {
+ receiveMessage(msg) {
+ switch (msg.name) {
+ case "hasWindowContextForTopBC":
+ return !!this.browsingContext.top.currentWindowContext;
+ }
+ return null;
+ }
+}
diff --git a/docshell/test/browser/Bug422543Child.sys.mjs b/docshell/test/browser/Bug422543Child.sys.mjs
new file mode 100644
index 0000000000..524ac33ffd
--- /dev/null
+++ b/docshell/test/browser/Bug422543Child.sys.mjs
@@ -0,0 +1,98 @@
+class SHistoryListener {
+ constructor() {
+ this.retval = true;
+ this.last = "initial";
+ }
+
+ OnHistoryNewEntry(aNewURI) {
+ this.last = "newentry";
+ }
+
+ OnHistoryGotoIndex() {
+ this.last = "gotoindex";
+ }
+
+ OnHistoryPurge() {
+ this.last = "purge";
+ }
+
+ OnHistoryReload() {
+ this.last = "reload";
+ return this.retval;
+ }
+
+ OnHistoryReplaceEntry() {}
+}
+SHistoryListener.prototype.QueryInterface = ChromeUtils.generateQI([
+ "nsISHistoryListener",
+ "nsISupportsWeakReference",
+]);
+
+let listeners;
+
+export class Bug422543Child extends JSWindowActorChild {
+ constructor() {
+ super();
+ }
+
+ actorCreated() {
+ if (listeners) {
+ return;
+ }
+
+ this.shistory = this.docShell.nsIWebNavigation.sessionHistory;
+ listeners = [new SHistoryListener(), new SHistoryListener()];
+
+ for (let listener of listeners) {
+ this.shistory.legacySHistory.addSHistoryListener(listener);
+ }
+ }
+
+ cleanup() {
+ for (let listener of listeners) {
+ this.shistory.legacySHistory.removeSHistoryListener(listener);
+ }
+ this.shistory = null;
+ listeners = null;
+ return {};
+ }
+
+ getListenerStatus() {
+ return listeners.map(l => l.last);
+ }
+
+ resetListeners() {
+ for (let listener of listeners) {
+ listener.last = "initial";
+ }
+
+ return {};
+ }
+
+ notifyReload() {
+ let history = this.shistory.legacySHistory;
+ let rval = history.notifyOnHistoryReload();
+ return { rval };
+ }
+
+ setRetval({ num, val }) {
+ listeners[num].retval = val;
+ return {};
+ }
+
+ receiveMessage(msg) {
+ switch (msg.name) {
+ case "cleanup":
+ return this.cleanup();
+ case "getListenerStatus":
+ return this.getListenerStatus();
+ case "notifyReload":
+ return this.notifyReload();
+ case "resetListeners":
+ return this.resetListeners();
+ case "setRetval":
+ return this.setRetval(msg.data);
+ }
+ return null;
+ }
+}
diff --git a/docshell/test/browser/browser.ini b/docshell/test/browser/browser.ini
new file mode 100644
index 0000000000..c8ed2e345b
--- /dev/null
+++ b/docshell/test/browser/browser.ini
@@ -0,0 +1,244 @@
+[DEFAULT]
+support-files =
+ Bug422543Child.sys.mjs
+ dummy_page.html
+ favicon_bug655270.ico
+ file_bug234628-1-child.html
+ file_bug234628-1.html
+ file_bug234628-10-child.xhtml
+ file_bug234628-10.html
+ file_bug234628-11-child.xhtml
+ file_bug234628-11-child.xhtml^headers^
+ file_bug234628-11.html
+ file_bug234628-2-child.html
+ file_bug234628-2.html
+ file_bug234628-3-child.html
+ file_bug234628-3.html
+ file_bug234628-4-child.html
+ file_bug234628-4.html
+ file_bug234628-5-child.html
+ file_bug234628-5.html
+ file_bug234628-6-child.html
+ file_bug234628-6-child.html^headers^
+ file_bug234628-6.html
+ file_bug234628-8-child.html
+ file_bug234628-8.html
+ file_bug234628-9-child.html
+ file_bug234628-9.html
+ file_bug420605.html
+ file_bug503832.html
+ file_bug655270.html
+ file_bug670318.html
+ file_bug673087-1.html
+ file_bug673087-1.html^headers^
+ file_bug673087-1-child.html
+ file_bug673087-2.html
+ file_bug852909.pdf
+ file_bug852909.png
+ file_bug1046022.html
+ file_bug1206879.html
+ file_bug1328501.html
+ file_bug1328501_frame.html
+ file_bug1328501_framescript.js
+ file_bug1543077-3-child.html
+ file_bug1543077-3.html
+ file_multiple_pushState.html
+ file_onbeforeunload_0.html
+ file_onbeforeunload_1.html
+ file_onbeforeunload_2.html
+ file_onbeforeunload_3.html
+ print_postdata.sjs
+ test-form_sjis.html
+ timelineMarkers-04.html
+ browser_timelineMarkers-frame-02.js
+ browser_timelineMarkers-frame-03.js
+ browser_timelineMarkers-frame-04.js
+ browser_timelineMarkers-frame-05.js
+ head.js
+ frame-head.js
+ file_data_load_inherit_csp.html
+ file_click_link_within_view_source.html
+ onload_message.html
+ onpageshow_message.html
+ file_cross_process_csp_inheritance.html
+ file_open_about_blank.html
+ file_slow_load.sjs
+ file_bug1648464-1.html
+ file_bug1648464-1-child.html
+ file_bug1688368-1.sjs
+ file_bug1691153.html
+ file_bug1716290-1.sjs
+ file_bug1716290-2.sjs
+ file_bug1716290-3.sjs
+ file_bug1716290-4.sjs
+ file_bug1736248-1.html
+
+[browser_alternate_fixup_middle_click_link.js]
+https_first_disabled = true
+[browser_backforward_userinteraction.js]
+support-files =
+ dummy_iframe_page.html
+skip-if =
+ os == "linux" && bits == 64 && !debug # Bug 1607713
+[browser_backforward_userinteraction_about.js]
+[browser_backforward_userinteraction_systemprincipal.js]
+skip-if =
+ os == "win" && os_version == "6.1" # Skip on Azure - frequent failure
+[browser_bug1543077-3.js]
+[browser_bug1594938.js]
+[browser_bug1206879.js]
+https_first_disabled = true
+[browser_bug1309900_crossProcessHistoryNavigation.js]
+https_first_disabled = true
+[browser_bug1328501.js]
+https_first_disabled = true
+[browser_bug1347823.js]
+[browser_bug134911.js]
+[browser_bug1415918_beforeunload_options.js]
+https_first_disabled = true
+[browser_bug1622420.js]
+support-files =
+ file_bug1622420.html
+ Bug1622420Child.sys.mjs
+[browser_bug1673702.js]
+https_first_disabled = true
+skip-if =
+ os == "linux" && bits == 64 && os_version == "18.04" && debug # Bug 1674513
+ os == "win" # Bug 1674513
+support-files =
+ file_bug1673702.json
+ file_bug1673702.json^headers^
+[browser_bug1674464.js]
+https_first_disabled = true
+skip-if = !fission || !crashreporter # On a crash we only keep history when fission is enabled.
+[browser_bug1719178.js]
+[browser_bug1757005.js]
+[browser_bug1769189.js]
+[browser_bug1798780.js]
+[browser_bug234628-1.js]
+[browser_bug234628-10.js]
+[browser_bug234628-11.js]
+[browser_bug234628-2.js]
+[browser_bug234628-3.js]
+[browser_bug234628-4.js]
+[browser_bug234628-5.js]
+[browser_bug234628-6.js]
+[browser_bug234628-8.js]
+[browser_bug234628-9.js]
+[browser_bug349769.js]
+[browser_bug388121-1.js]
+[browser_bug388121-2.js]
+[browser_bug420605.js]
+skip-if = verify
+[browser_bug422543.js]
+https_first_disabled = true
+[browser_bug441169.js]
+[browser_bug503832.js]
+skip-if = verify
+[browser_bug554155.js]
+[browser_bug655270.js]
+[browser_bug655273.js]
+[browser_bug670318.js]
+[browser_bug673087-1.js]
+[browser_bug673087-2.js]
+[browser_bug673467.js]
+[browser_bug852909.js]
+skip-if = (verify && debug && (os == 'win'))
+[browser_bug92473.js]
+[browser_csp_sandbox_no_script_js_uri.js]
+support-files =
+ file_csp_sandbox_no_script_js_uri.html
+ file_csp_sandbox_no_script_js_uri.html^headers^
+[browser_data_load_inherit_csp.js]
+[browser_dataURI_unique_opaque_origin.js]
+[browser_fission_maxOrigins.js]
+https_first_disabled = true
+[browser_frameloader_swap_with_bfcache.js]
+[browser_backforward_restore_scroll.js]
+https_first_disabled = true
+support-files =
+ file_backforward_restore_scroll.html
+ file_backforward_restore_scroll.html^headers^
+[browser_targetTopLevelLinkClicksToBlank.js]
+[browser_title_in_session_history.js]
+skip-if = !sessionHistoryInParent
+[browser_uriFixupIntegration.js]
+[browser_uriFixupAlternateRedirects.js]
+https_first_disabled = true
+support-files =
+ redirect_to_example.sjs
+[browser_loadURI_postdata.js]
+[browser_multiple_pushState.js]
+https_first_disabled = true
+[browser_onbeforeunload_frame.js]
+support-files = head_browser_onbeforeunload.js
+[browser_onbeforeunload_parent.js]
+support-files = head_browser_onbeforeunload.js
+[browser_onbeforeunload_navigation.js]
+skip-if = (os == 'win' && !debug) # bug 1300351
+[browser_onunload_stop.js]
+https_first_disabled = true
+[browser_overlink.js]
+support-files =
+ overlink_test.html
+[browser_platform_emulation.js]
+[browser_search_notification.js]
+[browser_tab_touch_events.js]
+[browser_timelineMarkers-01.js]
+[browser_timelineMarkers-02.js]
+skip-if = true # Bug 1220415
+[browser_timelineMarkers-03.js]
+[browser_timelineMarkers-04.js]
+[browser_timelineMarkers-05.js]
+[browser_ua_emulation.js]
+[browser_history_triggeringprincipal_viewsource.js]
+https_first_disabled = true
+[browser_click_link_within_view_source.js]
+[browser_browsingContext-01.js]
+https_first_disabled = true
+[browser_browsingContext-02.js]
+https_first_disabled = true
+[browser_browsingContext-getAllBrowsingContextsInSubtree.js]
+[browser_browsingContext-getWindowByName.js]
+[browser_browsingContext-embedder.js]
+[browser_browsingContext-webProgress.js]
+skip-if =
+ os == "linux" && bits == 64 && !debug # Bug 1721261
+ os == "win" && os_version == "6.1" # Skip on Azure - frequent failure
+https_first_disabled = true
+[browser_csp_uir.js]
+support-files =
+ file_csp_uir.html
+ file_csp_uir_dummy.html
+[browser_cross_process_csp_inheritance.js]
+https_first_disabled = true
+[browser_tab_replace_while_loading.js]
+skip-if = (os == 'linux' && bits == 64 && os_version == '18.04') || (os == "win") # Bug 1604237, Bug 1671794
+[browser_browsing_context_attached.js]
+https_first_disabled = true
+[browser_browsing_context_discarded.js]
+https_first_disabled = true
+[browser_fall_back_to_https.js]
+https_first_disabled = true
+skip-if = (os == 'mac')
+[browser_badCertDomainFixup.js]
+[browser_viewsource_chrome_to_content.js]
+[browser_viewsource_multipart.js]
+support-files =
+ file_basic_multipart.sjs
+[browser_bug1648464-1.js]
+[browser_bug1688368-1.js]
+[browser_bug1691153.js]
+https_first_disabled = true
+[browser_bug1705872.js]
+[browser_isInitialDocument.js]
+https_first_disabled = true
+[browser_bug1716290-1.js]
+[browser_bug1716290-2.js]
+[browser_bug1716290-3.js]
+[browser_bug1716290-4.js]
+[browser_bfcache_copycommand.js]
+skip-if =
+ os == "linux" && bits == 64 # Bug 1730593
+ os == "win" && os_version == "6.1" # Skip on Azure - frequent failure
+[browser_bug1736248-1.js]
diff --git a/docshell/test/browser/browser_alternate_fixup_middle_click_link.js b/docshell/test/browser/browser_alternate_fixup_middle_click_link.js
new file mode 100644
index 0000000000..8a55e12e52
--- /dev/null
+++ b/docshell/test/browser/browser_alternate_fixup_middle_click_link.js
@@ -0,0 +1,59 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Check that we don't do alternate fixup when users middle-click
+ * broken links in the content document.
+ */
+add_task(async function test_alt_fixup_middle_click() {
+ await BrowserTestUtils.withNewTab("about:blank", async browser => {
+ await SpecialPowers.spawn(browser, [], () => {
+ let link = content.document.createElement("a");
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ link.href = "http://example/foo";
+ link.textContent = "Me, me, click me!";
+ content.document.body.append(link);
+ });
+ let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser);
+ await BrowserTestUtils.synthesizeMouseAtCenter(
+ "a[href]",
+ { button: 1 },
+ browser
+ );
+ let tab = await newTabPromise;
+ let { browsingContext } = tab.linkedBrowser;
+ // Account for the possibility of a race, where the error page has already loaded:
+ if (
+ !browsingContext.currentWindowGlobal?.documentURI.spec.startsWith(
+ "about:neterror"
+ )
+ ) {
+ await BrowserTestUtils.browserLoaded(
+ tab.linkedBrowser,
+ false,
+ null,
+ true
+ );
+ }
+ // TBH, if the test fails, we probably force-crash because we try to reach
+ // *www.* example.com, which isn't proxied by the test infrastructure so
+ // will forcibly abort the test. But we need some asserts so they might as
+ // well be meaningful:
+ is(
+ tab.linkedBrowser.currentURI.spec,
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example/foo",
+ "URL for tab should be correct."
+ );
+
+ ok(
+ browsingContext.currentWindowGlobal.documentURI.spec.startsWith(
+ "about:neterror"
+ ),
+ "Should be showing error page."
+ );
+ BrowserTestUtils.removeTab(tab);
+ });
+});
diff --git a/docshell/test/browser/browser_backforward_restore_scroll.js b/docshell/test/browser/browser_backforward_restore_scroll.js
new file mode 100644
index 0000000000..d8c570b1a9
--- /dev/null
+++ b/docshell/test/browser/browser_backforward_restore_scroll.js
@@ -0,0 +1,54 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const ROOT = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "http://mochi.test:8888"
+);
+const URL1 = ROOT + "file_backforward_restore_scroll.html";
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+const URL2 = "http://example.net/";
+
+const SCROLL0 = 500;
+const SCROLL1 = 1000;
+
+function promiseBrowserLoaded(url) {
+ return BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false, url);
+}
+
+add_task(async function test() {
+ await BrowserTestUtils.openNewForegroundTab(gBrowser, URL1);
+
+ // Scroll the 2 frames.
+ let children = gBrowser.selectedBrowser.browsingContext.children;
+ await SpecialPowers.spawn(children[0], [SCROLL0], scrollY =>
+ content.scrollTo(0, scrollY)
+ );
+ await SpecialPowers.spawn(children[1], [SCROLL1], scrollY =>
+ content.scrollTo(0, scrollY)
+ );
+
+ // Navigate forwards then backwards.
+ let loaded = promiseBrowserLoaded(URL2);
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, URL2);
+ await loaded;
+
+ loaded = promiseBrowserLoaded(URL1);
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
+ content.history.back();
+ });
+ await loaded;
+
+ // And check the results.
+ children = gBrowser.selectedBrowser.browsingContext.children;
+ await SpecialPowers.spawn(children[0], [SCROLL0], scrollY => {
+ Assert.equal(content.scrollY, scrollY, "frame 0 has correct scroll");
+ });
+ await SpecialPowers.spawn(children[1], [SCROLL1], scrollY => {
+ Assert.equal(content.scrollY, scrollY, "frame 1 has correct scroll");
+ });
+
+ gBrowser.removeTab(gBrowser.selectedTab);
+});
diff --git a/docshell/test/browser/browser_backforward_userinteraction.js b/docshell/test/browser/browser_backforward_userinteraction.js
new file mode 100644
index 0000000000..77e0df402d
--- /dev/null
+++ b/docshell/test/browser/browser_backforward_userinteraction.js
@@ -0,0 +1,374 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_PAGE =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "dummy_page.html";
+const IFRAME_PAGE =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "dummy_iframe_page.html";
+
+async function assertMenulist(entries, baseURL = TEST_PAGE) {
+ // Wait for the session data to be flushed before continuing the test
+ await new Promise(resolve =>
+ SessionStore.getSessionHistory(gBrowser.selectedTab, resolve)
+ );
+
+ let backButton = document.getElementById("back-button");
+ let contextMenu = document.getElementById("backForwardMenu");
+
+ info("waiting for the history menu to open");
+
+ let popupShownPromise = BrowserTestUtils.waitForEvent(
+ contextMenu,
+ "popupshown"
+ );
+ EventUtils.synthesizeMouseAtCenter(backButton, {
+ type: "contextmenu",
+ button: 2,
+ });
+ await popupShownPromise;
+
+ ok(true, "history menu opened");
+
+ let nodes = contextMenu.childNodes;
+
+ is(
+ nodes.length,
+ entries.length,
+ "Has the expected number of contextMenu entries"
+ );
+
+ for (let i = 0; i < entries.length; i++) {
+ let node = nodes[i];
+ is(
+ node.getAttribute("uri").replace(/[?|#]/, "!"),
+ baseURL + "!entry=" + entries[i],
+ "contextMenu node has the correct uri"
+ );
+ }
+
+ let popupHiddenPromise = BrowserTestUtils.waitForEvent(
+ contextMenu,
+ "popuphidden"
+ );
+ contextMenu.hidePopup();
+ await popupHiddenPromise;
+}
+
+// There are different ways of loading a page, but they should exhibit roughly the same
+// back-forward behavior for the purpose of requiring user interaction. Thus, we
+// have a utility function that runs the same test with a parameterized method of loading
+// new URLs.
+async function runTopLevelTest(loadMethod, useHashes = false) {
+ let p = useHashes ? "#" : "?";
+
+ // Test with both pref on and off
+ for (let requireUserInteraction of [true, false]) {
+ Services.prefs.setBoolPref(
+ "browser.navigation.requireUserInteraction",
+ requireUserInteraction
+ );
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ TEST_PAGE + p + "entry=0"
+ );
+ let browser = tab.linkedBrowser;
+
+ assertBackForwardState(false, false);
+
+ await loadMethod(TEST_PAGE + p + "entry=1");
+
+ assertBackForwardState(true, false);
+ await assertMenulist([1, 0]);
+
+ await loadMethod(TEST_PAGE + p + "entry=2");
+
+ assertBackForwardState(true, false);
+ await assertMenulist(requireUserInteraction ? [2, 0] : [2, 1, 0]);
+
+ await loadMethod(TEST_PAGE + p + "entry=3");
+
+ info("Adding user interaction for entry=3");
+ // Add some user interaction to entry 3
+ await BrowserTestUtils.synthesizeMouse(
+ "body",
+ 0,
+ 0,
+ {},
+ browser.browsingContext,
+ true
+ );
+
+ assertBackForwardState(true, false);
+ await assertMenulist(requireUserInteraction ? [3, 0] : [3, 2, 1, 0]);
+
+ await loadMethod(TEST_PAGE + p + "entry=4");
+
+ assertBackForwardState(true, false);
+ await assertMenulist(requireUserInteraction ? [4, 3, 0] : [4, 3, 2, 1, 0]);
+
+ info("Adding user interaction for entry=4");
+ // Add some user interaction to entry 4
+ await BrowserTestUtils.synthesizeMouse(
+ "body",
+ 0,
+ 0,
+ {},
+ browser.browsingContext,
+ true
+ );
+
+ await loadMethod(TEST_PAGE + p + "entry=5");
+
+ assertBackForwardState(true, false);
+ await assertMenulist(
+ requireUserInteraction ? [5, 4, 3, 0] : [5, 4, 3, 2, 1, 0]
+ );
+
+ await goBack(TEST_PAGE + p + "entry=4");
+ await goBack(TEST_PAGE + p + "entry=3");
+
+ if (!requireUserInteraction) {
+ await goBack(TEST_PAGE + p + "entry=2");
+ await goBack(TEST_PAGE + p + "entry=1");
+ }
+
+ assertBackForwardState(true, true);
+ await assertMenulist(
+ requireUserInteraction ? [5, 4, 3, 0] : [5, 4, 3, 2, 1, 0]
+ );
+
+ await goBack(TEST_PAGE + p + "entry=0");
+
+ assertBackForwardState(false, true);
+
+ if (!requireUserInteraction) {
+ await goForward(TEST_PAGE + p + "entry=1");
+ await goForward(TEST_PAGE + p + "entry=2");
+ }
+
+ await goForward(TEST_PAGE + p + "entry=3");
+
+ assertBackForwardState(true, true);
+ await assertMenulist(
+ requireUserInteraction ? [5, 4, 3, 0] : [5, 4, 3, 2, 1, 0]
+ );
+
+ await goForward(TEST_PAGE + p + "entry=4");
+
+ assertBackForwardState(true, true);
+ await assertMenulist(
+ requireUserInteraction ? [5, 4, 3, 0] : [5, 4, 3, 2, 1, 0]
+ );
+
+ await goForward(TEST_PAGE + p + "entry=5");
+
+ assertBackForwardState(true, false);
+ await assertMenulist(
+ requireUserInteraction ? [5, 4, 3, 0] : [5, 4, 3, 2, 1, 0]
+ );
+
+ BrowserTestUtils.removeTab(tab);
+ }
+
+ Services.prefs.clearUserPref("browser.navigation.requireUserInteraction");
+}
+
+async function runIframeTest(loadMethod) {
+ // Test with both pref on and off
+ for (let requireUserInteraction of [true, false]) {
+ Services.prefs.setBoolPref(
+ "browser.navigation.requireUserInteraction",
+ requireUserInteraction
+ );
+
+ // First test the boring case where we only have one iframe.
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ IFRAME_PAGE + "?entry=0"
+ );
+ let browser = tab.linkedBrowser;
+
+ assertBackForwardState(false, false);
+
+ await loadMethod(TEST_PAGE + "?sub_entry=1", "frame1");
+
+ assertBackForwardState(true, false);
+ await assertMenulist([0, 0], IFRAME_PAGE);
+
+ await loadMethod(TEST_PAGE + "?sub_entry=2", "frame1");
+
+ assertBackForwardState(true, false);
+ await assertMenulist(
+ requireUserInteraction ? [0, 0] : [0, 0, 0],
+ IFRAME_PAGE
+ );
+
+ let bc = await SpecialPowers.spawn(browser, [], function() {
+ return content.document.getElementById("frame1").browsingContext;
+ });
+
+ // Add some user interaction to sub entry 2
+ await BrowserTestUtils.synthesizeMouse("body", 0, 0, {}, bc, true);
+
+ await loadMethod(TEST_PAGE + "?sub_entry=3", "frame1");
+
+ assertBackForwardState(true, false);
+ await assertMenulist(
+ requireUserInteraction ? [0, 0, 0] : [0, 0, 0, 0],
+ IFRAME_PAGE
+ );
+
+ await loadMethod(TEST_PAGE + "?sub_entry=4", "frame1");
+
+ assertBackForwardState(true, false);
+ await assertMenulist(
+ requireUserInteraction ? [0, 0, 0] : [0, 0, 0, 0, 0],
+ IFRAME_PAGE
+ );
+
+ if (!requireUserInteraction) {
+ await goBack(TEST_PAGE + "?sub_entry=3", true);
+ }
+
+ await goBack(TEST_PAGE + "?sub_entry=2", true);
+
+ assertBackForwardState(true, true);
+ await assertMenulist(
+ requireUserInteraction ? [0, 0, 0] : [0, 0, 0, 0, 0],
+ IFRAME_PAGE
+ );
+
+ await loadMethod(IFRAME_PAGE + "?entry=1");
+
+ assertBackForwardState(true, false);
+ await assertMenulist(
+ requireUserInteraction ? [1, 0, 0] : [1, 0, 0, 0],
+ IFRAME_PAGE
+ );
+
+ BrowserTestUtils.removeTab(tab);
+
+ // Two iframes, now we're talking.
+ tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ IFRAME_PAGE + "?entry=0"
+ );
+ browser = tab.linkedBrowser;
+
+ await loadMethod(IFRAME_PAGE + "?entry=1");
+
+ assertBackForwardState(true, false);
+ await assertMenulist(requireUserInteraction ? [1, 0] : [1, 0], IFRAME_PAGE);
+
+ // Add some user interaction to frame 1.
+ bc = await SpecialPowers.spawn(browser, [], function() {
+ return content.document.getElementById("frame1").browsingContext;
+ });
+ await BrowserTestUtils.synthesizeMouse("body", 0, 0, {}, bc, true);
+
+ // Add some user interaction to frame 2.
+ bc = await SpecialPowers.spawn(browser, [], function() {
+ return content.document.getElementById("frame2").browsingContext;
+ });
+ await BrowserTestUtils.synthesizeMouse("body", 0, 0, {}, bc, true);
+
+ // Navigate frame 2.
+ await loadMethod(TEST_PAGE + "?sub_entry=1", "frame2");
+
+ assertBackForwardState(true, false);
+ await assertMenulist(
+ requireUserInteraction ? [1, 1, 0] : [1, 1, 0],
+ IFRAME_PAGE
+ );
+
+ // Add some user interaction to frame 1, again.
+ bc = await SpecialPowers.spawn(browser, [], function() {
+ return content.document.getElementById("frame1").browsingContext;
+ });
+ await BrowserTestUtils.synthesizeMouse("body", 0, 0, {}, bc, true);
+
+ // Navigate frame 2, again.
+ await loadMethod(TEST_PAGE + "?sub_entry=2", "frame2");
+
+ assertBackForwardState(true, false);
+ await assertMenulist(
+ requireUserInteraction ? [1, 1, 1, 0] : [1, 1, 1, 0],
+ IFRAME_PAGE
+ );
+
+ await goBack(TEST_PAGE + "?sub_entry=1", true);
+
+ assertBackForwardState(true, true);
+ await assertMenulist(
+ requireUserInteraction ? [1, 1, 1, 0] : [1, 1, 1, 0],
+ IFRAME_PAGE
+ );
+
+ await goBack(TEST_PAGE + "?sub_entry=0", true);
+
+ assertBackForwardState(true, true);
+ await assertMenulist(
+ requireUserInteraction ? [1, 1, 1, 0] : [1, 1, 1, 0],
+ IFRAME_PAGE
+ );
+
+ BrowserTestUtils.removeTab(tab);
+ }
+
+ Services.prefs.clearUserPref("browser.navigation.requireUserInteraction");
+}
+
+// Test that when the pref is flipped, we are skipping history
+// entries without user interaction when following links with hash URIs.
+add_task(async function test_hashURI() {
+ async function followLinkHash(url) {
+ info(`Creating and following a link to ${url}`);
+ let browser = gBrowser.selectedBrowser;
+ let loaded = BrowserTestUtils.waitForLocationChange(gBrowser, url);
+ await SpecialPowers.spawn(browser, [url], function(url) {
+ let a = content.document.createElement("a");
+ a.href = url;
+ content.document.body.appendChild(a);
+ a.click();
+ });
+ await loaded;
+ info(`Loaded ${url}`);
+ }
+
+ await runTopLevelTest(followLinkHash, true);
+});
+
+// Test that when the pref is flipped, we are skipping history
+// entries without user interaction when using history.pushState.
+add_task(async function test_pushState() {
+ await runTopLevelTest(pushState);
+});
+
+// Test that when the pref is flipped, we are skipping history
+// entries without user interaction when following a link.
+add_task(async function test_followLink() {
+ await runTopLevelTest(followLink);
+});
+
+// Test that when the pref is flipped, we are skipping history
+// entries without user interaction when navigating inside an iframe
+// using history.pushState.
+add_task(async function test_iframe_pushState() {
+ await runIframeTest(pushState);
+});
+
+// Test that when the pref is flipped, we are skipping history
+// entries without user interaction when navigating inside an iframe
+// by following links.
+add_task(async function test_iframe_followLink() {
+ await runIframeTest(followLink);
+});
diff --git a/docshell/test/browser/browser_backforward_userinteraction_about.js b/docshell/test/browser/browser_backforward_userinteraction_about.js
new file mode 100644
index 0000000000..606fcc45c5
--- /dev/null
+++ b/docshell/test/browser/browser_backforward_userinteraction_about.js
@@ -0,0 +1,67 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_PAGE =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "dummy_page.html";
+
+// Regression test for navigating back after visiting an about: page
+// loaded in the parent process.
+add_task(async function test_about_back() {
+ // Test with both pref on and off
+ for (let requireUserInteraction of [true, false]) {
+ Services.prefs.setBoolPref(
+ "browser.navigation.requireUserInteraction",
+ requireUserInteraction
+ );
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ TEST_PAGE + "?entry=0"
+ );
+ let browser = tab.linkedBrowser;
+ assertBackForwardState(false, false);
+
+ await followLink(TEST_PAGE + "?entry=1");
+ assertBackForwardState(true, false);
+
+ await followLink(TEST_PAGE + "?entry=2");
+ assertBackForwardState(true, false);
+
+ // Add some user interaction to entry 2
+ await BrowserTestUtils.synthesizeMouse("body", 0, 0, {}, browser, true);
+
+ await loadURI("about:config");
+ assertBackForwardState(true, false);
+
+ await goBack(TEST_PAGE + "?entry=2");
+ assertBackForwardState(true, true);
+
+ if (!requireUserInteraction) {
+ await goBack(TEST_PAGE + "?entry=1");
+ assertBackForwardState(true, true);
+ }
+
+ await goBack(TEST_PAGE + "?entry=0");
+ assertBackForwardState(false, true);
+
+ if (!requireUserInteraction) {
+ await goForward(TEST_PAGE + "?entry=1");
+ assertBackForwardState(true, true);
+ }
+
+ await goForward(TEST_PAGE + "?entry=2");
+ assertBackForwardState(true, true);
+
+ await goForward("about:config");
+ assertBackForwardState(true, false);
+
+ BrowserTestUtils.removeTab(tab);
+ }
+
+ Services.prefs.clearUserPref("browser.navigation.requireUserInteraction");
+});
diff --git a/docshell/test/browser/browser_backforward_userinteraction_systemprincipal.js b/docshell/test/browser/browser_backforward_userinteraction_systemprincipal.js
new file mode 100644
index 0000000000..f46048632e
--- /dev/null
+++ b/docshell/test/browser/browser_backforward_userinteraction_systemprincipal.js
@@ -0,0 +1,112 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_PAGE =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "dummy_page.html";
+
+async function runTest(privilegedLoad) {
+ let prefVals;
+ // Test with both pref on and off, unless parent-controlled pref is enabled.
+ // This distinction can be removed once SHIP is enabled by default.
+ if (
+ Services.prefs.getBoolPref("browser.tabs.documentchannel.parent-controlled")
+ ) {
+ prefVals = [false];
+ } else {
+ prefVals = [true, false];
+ }
+
+ for (let requireUserInteraction of prefVals) {
+ Services.prefs.setBoolPref(
+ "browser.navigation.requireUserInteraction",
+ requireUserInteraction
+ );
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ TEST_PAGE + "?entry=0"
+ );
+
+ assertBackForwardState(false, false);
+
+ await followLink(TEST_PAGE + "?entry=1");
+
+ assertBackForwardState(true, false);
+
+ await followLink(TEST_PAGE + "?entry=2");
+
+ assertBackForwardState(true, false);
+
+ await followLink(TEST_PAGE + "?entry=3");
+
+ assertBackForwardState(true, false);
+
+ // Entry 4 will be added through a user action in browser chrome,
+ // giving user interaction to entry 3. Entry 4 should not gain automatic
+ // user interaction.
+ await privilegedLoad(TEST_PAGE + "?entry=4");
+
+ assertBackForwardState(true, false);
+
+ await followLink(TEST_PAGE + "?entry=5");
+
+ assertBackForwardState(true, false);
+
+ if (!requireUserInteraction) {
+ await goBack(TEST_PAGE + "?entry=4");
+ }
+ await goBack(TEST_PAGE + "?entry=3");
+
+ if (!requireUserInteraction) {
+ await goBack(TEST_PAGE + "?entry=2");
+ await goBack(TEST_PAGE + "?entry=1");
+ }
+
+ assertBackForwardState(true, true);
+
+ await goBack(TEST_PAGE + "?entry=0");
+
+ assertBackForwardState(false, true);
+
+ if (!requireUserInteraction) {
+ await goForward(TEST_PAGE + "?entry=1");
+ await goForward(TEST_PAGE + "?entry=2");
+ }
+
+ await goForward(TEST_PAGE + "?entry=3");
+
+ assertBackForwardState(true, true);
+
+ if (!requireUserInteraction) {
+ await goForward(TEST_PAGE + "?entry=4");
+ }
+
+ await goForward(TEST_PAGE + "?entry=5");
+
+ assertBackForwardState(true, false);
+
+ BrowserTestUtils.removeTab(tab);
+ }
+
+ Services.prefs.clearUserPref("browser.navigation.requireUserInteraction");
+}
+
+// Test that we add a user interaction flag to the previous site when loading
+// a new site from user interaction with privileged UI, e.g. through the
+// URL bar.
+add_task(async function test_urlBar() {
+ await runTest(async function(url) {
+ info(`Loading ${url} via the URL bar.`);
+ let browser = gBrowser.selectedBrowser;
+ let loaded = BrowserTestUtils.browserLoaded(browser, false, url);
+ gURLBar.focus();
+ gURLBar.value = url;
+ gURLBar.goButton.click();
+ await loaded;
+ });
+});
diff --git a/docshell/test/browser/browser_badCertDomainFixup.js b/docshell/test/browser/browser_badCertDomainFixup.js
new file mode 100644
index 0000000000..783360d7b7
--- /dev/null
+++ b/docshell/test/browser/browser_badCertDomainFixup.js
@@ -0,0 +1,92 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// This test checks if we are correctly fixing https URLs by prefixing
+// with www. when we encounter a SSL_ERROR_BAD_CERT_DOMAIN error.
+// For example, https://example.com -> https://www.example.com.
+
+const PREF_BAD_CERT_DOMAIN_FIX_ENABLED =
+ "security.bad_cert_domain_error.url_fix_enabled";
+const PREF_ALLOW_HIJACKING_LOCALHOST =
+ "network.proxy.allow_hijacking_localhost";
+
+const BAD_CERT_DOMAIN_ERROR_URL = "https://badcertdomain.example.com:443";
+const FIXED_URL = "https://www.badcertdomain.example.com/";
+
+const BAD_CERT_DOMAIN_ERROR_URL2 =
+ "https://mismatch.badcertdomain.example.com:443";
+const IPV4_ADDRESS = "https://127.0.0.3:433";
+const BAD_CERT_DOMAIN_ERROR_PORT = "https://badcertdomain.example.com:82";
+
+async function verifyErrorPage(errorPageURL) {
+ let certErrorLoaded = BrowserTestUtils.waitForErrorPage(
+ gBrowser.selectedBrowser
+ );
+ BrowserTestUtils.loadURI(gBrowser, errorPageURL);
+ await certErrorLoaded;
+
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function() {
+ let ec;
+ await ContentTaskUtils.waitForCondition(() => {
+ ec = content.document.getElementById("errorCode");
+ return ec.textContent;
+ }, "Error code has been set inside the advanced button panel");
+ is(
+ ec.textContent,
+ "SSL_ERROR_BAD_CERT_DOMAIN",
+ "Correct error code is shown"
+ );
+ });
+}
+
+// Test that "www." is prefixed to a https url when we encounter a bad cert domain
+// error if the "www." form is included in the certificate's subjectAltNames.
+add_task(async function prefixBadCertDomain() {
+ // Turn off the pref and ensure that we show the error page as expected.
+ Services.prefs.setBoolPref(PREF_BAD_CERT_DOMAIN_FIX_ENABLED, false);
+
+ gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
+ await verifyErrorPage(BAD_CERT_DOMAIN_ERROR_URL);
+ info("Cert error is shown as expected when the fixup pref is disabled");
+
+ // Turn on the pref and test that we fix the HTTPS URL.
+ Services.prefs.setBoolPref(PREF_BAD_CERT_DOMAIN_FIX_ENABLED, true);
+ gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
+ let loadSuccessful = BrowserTestUtils.browserLoaded(
+ gBrowser.selectedBrowser,
+ false,
+ FIXED_URL
+ );
+ BrowserTestUtils.loadURI(gBrowser, BAD_CERT_DOMAIN_ERROR_URL);
+ await loadSuccessful;
+
+ info("The URL was fixed as expected");
+
+ BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
+
+// Test that we don't prefix "www." to a https url when we encounter a bad cert domain
+// error under certain conditions.
+add_task(async function ignoreBadCertDomain() {
+ Services.prefs.setBoolPref(PREF_BAD_CERT_DOMAIN_FIX_ENABLED, true);
+ gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
+
+ // Test for when "www." form is not present in the certificate.
+ await verifyErrorPage(BAD_CERT_DOMAIN_ERROR_URL2);
+ info("Certificate error was shown as expected");
+
+ // Test that urls with IP addresses are not fixed.
+ Services.prefs.setBoolPref(PREF_ALLOW_HIJACKING_LOCALHOST, true);
+ await verifyErrorPage(IPV4_ADDRESS);
+ Services.prefs.clearUserPref(PREF_ALLOW_HIJACKING_LOCALHOST);
+ info("Certificate error was shown as expected for an IP address");
+
+ // Test that urls with ports are not fixed.
+ await verifyErrorPage(BAD_CERT_DOMAIN_ERROR_PORT);
+ info("Certificate error was shown as expected for a host with port");
+
+ BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
diff --git a/docshell/test/browser/browser_bfcache_copycommand.js b/docshell/test/browser/browser_bfcache_copycommand.js
new file mode 100644
index 0000000000..6b9a5870aa
--- /dev/null
+++ b/docshell/test/browser/browser_bfcache_copycommand.js
@@ -0,0 +1,98 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function dummyPageURL(domain, query = "") {
+ return (
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ `https://${domain}`
+ ) +
+ "dummy_page.html" +
+ query
+ );
+}
+
+async function openContextMenu(browser) {
+ let contextMenu = document.getElementById("contentAreaContextMenu");
+ let awaitPopupShown = BrowserTestUtils.waitForEvent(
+ contextMenu,
+ "popupshown"
+ );
+ await BrowserTestUtils.synthesizeMouse(
+ "body",
+ 1,
+ 1,
+ {
+ type: "contextmenu",
+ button: 2,
+ },
+ browser
+ );
+ await awaitPopupShown;
+}
+
+async function closeContextMenu() {
+ let contextMenu = document.getElementById("contentAreaContextMenu");
+ let awaitPopupHidden = BrowserTestUtils.waitForEvent(
+ contextMenu,
+ "popuphidden"
+ );
+ contextMenu.hidePopup();
+ await awaitPopupHidden;
+}
+
+async function testWithDomain(domain) {
+ // Passing a query to make sure the next load is never a same-document
+ // navigation.
+ let dummy = dummyPageURL("example.org", "?start");
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, dummy);
+ let browser = tab.linkedBrowser;
+
+ let sel = await SpecialPowers.spawn(browser, [], function() {
+ let sel = content.getSelection();
+ sel.removeAllRanges();
+ sel.selectAllChildren(content.document.body);
+ return sel.toString();
+ });
+
+ await openContextMenu(browser);
+
+ let copyItem = document.getElementById("context-copy");
+ ok(!copyItem.disabled, "Copy item should be enabled if text is selected.");
+
+ await closeContextMenu();
+
+ let loaded = BrowserTestUtils.browserLoaded(browser, false, null, true);
+ BrowserTestUtils.loadURI(browser, dummyPageURL(domain));
+ await loaded;
+ loaded = BrowserTestUtils.waitForLocationChange(gBrowser, dummy);
+ browser.goBack();
+ await loaded;
+
+ let sel2 = await SpecialPowers.spawn(browser, [], function() {
+ return content.getSelection().toString();
+ });
+ is(sel, sel2, "Selection should remain when coming out of BFCache.");
+
+ await openContextMenu(browser);
+
+ ok(!copyItem.disabled, "Copy item should be enabled if text is selected.");
+
+ await closeContextMenu();
+
+ await BrowserTestUtils.removeTab(tab);
+}
+
+add_task(async function testValidSameOrigin() {
+ await testWithDomain("example.org");
+});
+
+add_task(async function testValidCrossOrigin() {
+ await testWithDomain("example.com");
+});
+
+add_task(async function testInvalid() {
+ await testWithDomain("this.is.invalid");
+});
diff --git a/docshell/test/browser/browser_browsingContext-01.js b/docshell/test/browser/browser_browsingContext-01.js
new file mode 100644
index 0000000000..81a32985a0
--- /dev/null
+++ b/docshell/test/browser/browser_browsingContext-01.js
@@ -0,0 +1,178 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const URL = "about:blank";
+
+async function getBrowsingContextId(browser, id) {
+ return SpecialPowers.spawn(browser, [id], async function(id) {
+ let contextId = content.window.docShell.browsingContext.id;
+
+ let frames = [content.window];
+ while (frames.length) {
+ let frame = frames.pop();
+ let target = frame.document.getElementById(id);
+ if (target) {
+ contextId = target.docShell.browsingContext.id;
+ break;
+ }
+
+ frames = frames.concat(Array.from(frame.frames));
+ }
+
+ return contextId;
+ });
+}
+
+async function addFrame(browser, id, parentId) {
+ return SpecialPowers.spawn(browser, [{ parentId, id }], async function({
+ parentId,
+ id,
+ }) {
+ let parent = null;
+ if (parentId) {
+ let frames = [content.window];
+ while (frames.length) {
+ let frame = frames.pop();
+ let target = frame.document.getElementById(parentId);
+ if (target) {
+ parent = target.contentWindow.document.body;
+ break;
+ }
+ frames = frames.concat(Array.from(frame.frames));
+ }
+ } else {
+ parent = content.document.body;
+ }
+
+ let frame = await new Promise(resolve => {
+ let frame = content.document.createElement("iframe");
+ frame.id = id || "";
+ frame.url = "about:blank";
+ frame.onload = () => resolve(frame);
+ parent.appendChild(frame);
+ });
+
+ return frame.contentWindow.docShell.browsingContext.id;
+ });
+}
+
+async function removeFrame(browser, id) {
+ return SpecialPowers.spawn(browser, [id], async function(id) {
+ let frames = [content.window];
+ while (frames.length) {
+ let frame = frames.pop();
+ let target = frame.document.getElementById(id);
+ if (target) {
+ target.remove();
+ break;
+ }
+
+ frames = frames.concat(Array.from(frame.frames));
+ }
+ });
+}
+
+function getBrowsingContextById(id) {
+ return BrowsingContext.get(id);
+}
+
+add_task(async function() {
+ await BrowserTestUtils.withNewTab({ gBrowser, url: URL }, async function(
+ browser
+ ) {
+ let topId = await getBrowsingContextId(browser, "");
+ let topContext = getBrowsingContextById(topId);
+ isnot(topContext, null);
+ is(topContext.parent, null);
+ is(
+ topId,
+ browser.browsingContext.id,
+ "<browser> has the correct browsingContext"
+ );
+ is(
+ browser.browserId,
+ topContext.browserId,
+ "browsing context should have a correct <browser> id"
+ );
+
+ let id0 = await addFrame(browser, "frame0");
+ let browsingContext0 = getBrowsingContextById(id0);
+ isnot(browsingContext0, null);
+ is(browsingContext0.parent, topContext);
+
+ await removeFrame(browser, "frame0");
+
+ is(topContext.children.indexOf(browsingContext0), -1);
+
+ // TODO(farre): Handle browsingContext removal [see Bug 1486719].
+ todo_isnot(browsingContext0.parent, topContext);
+ });
+});
+
+add_task(async function() {
+ // If Fission is disabled, the pref is no-op.
+ await SpecialPowers.pushPrefEnv({ set: [["fission.bfcacheInParent", true]] });
+
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url:
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com"
+ ) + "dummy_page.html",
+ },
+ async function(browser) {
+ let path = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com"
+ );
+ await SpecialPowers.spawn(browser, [path], async function(path) {
+ var bc = new content.BroadcastChannel("browser_browsingContext");
+ function waitForMessage(command) {
+ let p = new Promise(resolve => {
+ bc.addEventListener("message", e => resolve(e), { once: true });
+ });
+ command();
+ return p;
+ }
+
+ // Open a new window and wait for the message.
+ let e1 = await waitForMessage(_ =>
+ content.window.open(path + "onpageshow_message.html", "", "noopener")
+ );
+
+ is(e1.data, "pageshow", "Got page show");
+
+ let e2 = await waitForMessage(_ => bc.postMessage("createiframe"));
+ is(e2.data.framesLength, 1, "Here we should have an iframe");
+
+ let e3 = await waitForMessage(_ => bc.postMessage("nextpage"));
+
+ is(e3.data.event, "load");
+ is(e3.data.framesLength, 0, "Here there shouldn't be an iframe");
+
+ // Return to the previous document. N.B. we expect to trigger
+ // BFCache here, hence we wait for pageshow.
+ let e4 = await waitForMessage(_ => bc.postMessage("back"));
+
+ is(e4.data, "pageshow");
+
+ let e5 = await waitForMessage(_ => bc.postMessage("queryframes"));
+ is(e5.data.framesLength, 1, "And again there should be an iframe");
+
+ is(e5.outerWindowId, e2.outerWindowId, "BF cache cached outer window");
+ is(e5.browsingContextId, e2.browsingContextId, "BF cache cached BC");
+
+ let e6 = await waitForMessage(_ => bc.postMessage("close"));
+ is(e6.data, "closed");
+
+ bc.close();
+ });
+ }
+ );
+});
diff --git a/docshell/test/browser/browser_browsingContext-02.js b/docshell/test/browser/browser_browsingContext-02.js
new file mode 100644
index 0000000000..5d09802412
--- /dev/null
+++ b/docshell/test/browser/browser_browsingContext-02.js
@@ -0,0 +1,233 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function() {
+ await BrowserTestUtils.withNewTab(
+ { gBrowser, url: "about:blank" },
+ async function(browser) {
+ const BASE1 = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com"
+ );
+ const BASE2 = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://test1.example.com"
+ );
+ const URL = BASE1 + "onload_message.html";
+ let sixth = BrowserTestUtils.waitForNewTab(
+ gBrowser,
+ URL + "#sixth",
+ true,
+ true
+ );
+ let seventh = BrowserTestUtils.waitForNewTab(
+ gBrowser,
+ URL + "#seventh",
+ true,
+ true
+ );
+ let browserIds = await SpecialPowers.spawn(
+ browser,
+ [{ base1: BASE1, base2: BASE2 }],
+ async function({ base1, base2 }) {
+ let top = content;
+ top.name = "top";
+ top.location.href += "#top";
+
+ let contexts = {
+ top: top.location.href,
+ first: base1 + "dummy_page.html#first",
+ third: base2 + "dummy_page.html#third",
+ second: base1 + "dummy_page.html#second",
+ fourth: base2 + "dummy_page.html#fourth",
+ fifth: base1 + "dummy_page.html#fifth",
+ sixth: base1 + "onload_message.html#sixth",
+ seventh: base1 + "onload_message.html#seventh",
+ };
+
+ function addFrame(target, name) {
+ return content.SpecialPowers.spawn(
+ target,
+ [name, contexts[name]],
+ async (name, context) => {
+ let doc = this.content.document;
+
+ let frame = doc.createElement("iframe");
+ doc.body.appendChild(frame);
+ frame.name = name;
+ frame.src = context;
+ await new Promise(resolve => {
+ frame.addEventListener("load", resolve, { once: true });
+ });
+ return frame.browsingContext;
+ }
+ );
+ }
+
+ function addWindow(target, name, { options, resolve }) {
+ return content.SpecialPowers.spawn(
+ target,
+ [name, contexts[name], options, resolve],
+ (name, context, options, resolve) => {
+ let win = this.content.open(context, name, options);
+ let bc = win && win.docShell.browsingContext;
+
+ if (resolve) {
+ return new Promise(resolve =>
+ this.content.addEventListener("message", () => resolve(bc))
+ );
+ }
+ return Promise.resolve({ name });
+ }
+ );
+ }
+
+ // We're going to create a tree that looks like the
+ // following.
+ //
+ // top sixth seventh
+ // / \
+ // / \ /
+ // first second
+ // / \ /
+ // / \
+ // third fourth - - -
+ // /
+ // /
+ // fifth
+ //
+ // The idea is to have one top level non-auxiliary browsing
+ // context, five nested, one top level auxiliary with an
+ // opener, and one top level without an opener. Given that
+ // set of related and one unrelated browsing contexts we
+ // wish to confirm that targeting is able to find
+ // appropriate browsing contexts.
+
+ // BrowsingContext.findWithName requires access checks, which
+ // can only be performed in the process of the accessor BC's
+ // docShell.
+ function findWithName(bc, name) {
+ return content.SpecialPowers.spawn(bc, [bc, name], (bc, name) => {
+ return bc.findWithName(name);
+ });
+ }
+
+ async function reachable(start, target) {
+ info(start.name, target.name);
+ is(
+ await findWithName(start, target.name),
+ target,
+ [start.name, "can reach", target.name].join(" ")
+ );
+ }
+
+ async function unreachable(start, target) {
+ is(
+ await findWithName(start, target.name),
+ null,
+ [start.name, "can't reach", target.name].join(" ")
+ );
+ }
+
+ let first = await addFrame(top, "first");
+ info("first");
+ let second = await addFrame(top, "second");
+ info("second");
+ let third = await addFrame(first, "third");
+ info("third");
+ let fourth = await addFrame(first, "fourth");
+ info("fourth");
+ let fifth = await addFrame(fourth, "fifth");
+ info("fifth");
+ let sixth = await addWindow(fourth, "sixth", { resolve: true });
+ info("sixth");
+ let seventh = await addWindow(fourth, "seventh", {
+ options: ["noopener"],
+ });
+ info("seventh");
+
+ let origin1 = [first, second, fifth, sixth];
+ let origin2 = [third, fourth];
+
+ let topBC = BrowsingContext.getFromWindow(top);
+ let frames = new Map([
+ [topBC, [topBC, first, second, third, fourth, fifth, sixth]],
+ [first, [topBC, ...origin1, third, fourth]],
+ [second, [topBC, ...origin1, third, fourth]],
+ [third, [topBC, ...origin2, fifth, sixth]],
+ [fourth, [topBC, ...origin2, fifth, sixth]],
+ [fifth, [topBC, ...origin1, third, fourth]],
+ [sixth, [...origin1, third, fourth]],
+ ]);
+
+ for (let [start, accessible] of frames) {
+ for (let frame of frames.keys()) {
+ if (accessible.includes(frame)) {
+ await reachable(start, frame);
+ } else {
+ await unreachable(start, frame);
+ }
+ }
+ await unreachable(start, seventh);
+ }
+
+ let topBrowserId = topBC.browserId;
+ ok(topBrowserId > 0, "Should have a browser ID.");
+ for (let [name, bc] of Object.entries({
+ first,
+ second,
+ third,
+ fourth,
+ fifth,
+ })) {
+ is(
+ bc.browserId,
+ topBrowserId,
+ `${name} frame should have the same browserId as top.`
+ );
+ }
+
+ ok(sixth.browserId > 0, "sixth should have a browserId.");
+ isnot(
+ sixth.browserId,
+ topBrowserId,
+ "sixth frame should have a different browserId to top."
+ );
+
+ return [topBrowserId, sixth.browserId];
+ }
+ );
+
+ [sixth, seventh] = await Promise.all([sixth, seventh]);
+
+ is(
+ browser.browserId,
+ browserIds[0],
+ "browser should have the right browserId."
+ );
+ is(
+ browser.browsingContext.browserId,
+ browserIds[0],
+ "browser's BrowsingContext should have the right browserId."
+ );
+ is(
+ sixth.linkedBrowser.browserId,
+ browserIds[1],
+ "sixth should have the right browserId."
+ );
+ is(
+ sixth.linkedBrowser.browsingContext.browserId,
+ browserIds[1],
+ "sixth's BrowsingContext should have the right browserId."
+ );
+
+ for (let tab of [sixth, seventh]) {
+ BrowserTestUtils.removeTab(tab);
+ }
+ }
+ );
+});
diff --git a/docshell/test/browser/browser_browsingContext-embedder.js b/docshell/test/browser/browser_browsingContext-embedder.js
new file mode 100644
index 0000000000..9473a46eb4
--- /dev/null
+++ b/docshell/test/browser/browser_browsingContext-embedder.js
@@ -0,0 +1,156 @@
+"use strict";
+
+function observeOnce(topic) {
+ return new Promise(resolve => {
+ Services.obs.addObserver(function observer(aSubject, aTopic) {
+ if (topic == aTopic) {
+ Services.obs.removeObserver(observer, topic);
+ setTimeout(() => resolve(aSubject), 0);
+ }
+ }, topic);
+ });
+}
+
+add_task(async function runTest() {
+ let fissionWindow = await BrowserTestUtils.openNewBrowserWindow({
+ fission: true,
+ remote: true,
+ });
+
+ info(`chrome, parent`);
+ let chromeBC = fissionWindow.docShell.browsingContext;
+ ok(chromeBC.currentWindowGlobal, "Should have a current WindowGlobal");
+ is(chromeBC.embedderWindowGlobal, null, "chrome has no embedder global");
+ is(chromeBC.embedderElement, null, "chrome has no embedder element");
+ is(chromeBC.parent, null, "chrome has no parent");
+
+ // Open a new tab, and check that basic frames work out.
+ let tab = await BrowserTestUtils.openNewForegroundTab({
+ gBrowser: fissionWindow.gBrowser,
+ });
+
+ info(`root, parent`);
+ let rootBC = tab.linkedBrowser.browsingContext;
+ ok(rootBC.currentWindowGlobal, "[parent] root has a window global");
+ is(
+ rootBC.embedderWindowGlobal,
+ chromeBC.currentWindowGlobal,
+ "[parent] root has chrome as embedder global"
+ );
+ is(
+ rootBC.embedderElement,
+ tab.linkedBrowser,
+ "[parent] root has browser as embedder element"
+ );
+ is(rootBC.parent, null, "[parent] root has no parent");
+
+ // Test with an in-process frame
+ let frameId = await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
+ info(`root, child`);
+ let rootBC = content.docShell.browsingContext;
+ is(rootBC.embedderElement, null, "[child] root has no embedder");
+ is(rootBC.parent, null, "[child] root has no parent");
+
+ info(`frame, child`);
+ let iframe = content.document.createElement("iframe");
+ content.document.body.appendChild(iframe);
+
+ let frameBC = iframe.contentWindow.docShell.browsingContext;
+ is(frameBC.embedderElement, iframe, "[child] frame embedded within iframe");
+ is(frameBC.parent, rootBC, "[child] frame has root as parent");
+
+ return frameBC.id;
+ });
+
+ info(`frame, parent`);
+ let frameBC = BrowsingContext.get(frameId);
+ ok(frameBC.currentWindowGlobal, "[parent] frame has a window global");
+ is(
+ frameBC.embedderWindowGlobal,
+ rootBC.currentWindowGlobal,
+ "[parent] frame has root as embedder global"
+ );
+ is(frameBC.embedderElement, null, "[parent] frame has no embedder element");
+ is(frameBC.parent, rootBC, "[parent] frame has root as parent");
+
+ // Test with an out-of-process iframe.
+
+ let oopID = await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
+ info(`creating oop iframe`);
+ let oop = content.document.createElement("iframe");
+ oop.setAttribute("src", "https://example.com");
+ content.document.body.appendChild(oop);
+
+ await new Promise(resolve => {
+ oop.addEventListener("load", resolve, { once: true });
+ });
+
+ info(`oop frame, child`);
+ let oopBC = oop.frameLoader.browsingContext;
+ is(oopBC.embedderElement, oop, "[child] oop frame embedded within iframe");
+ is(
+ oopBC.parent,
+ content.docShell.browsingContext,
+ "[child] frame has root as parent"
+ );
+
+ return oopBC.id;
+ });
+
+ info(`oop frame, parent`);
+ let oopBC = BrowsingContext.get(oopID);
+ is(
+ oopBC.embedderWindowGlobal,
+ rootBC.currentWindowGlobal,
+ "[parent] oop frame has root as embedder global"
+ );
+ is(oopBC.embedderElement, null, "[parent] oop frame has no embedder element");
+ is(oopBC.parent, rootBC, "[parent] oop frame has root as parent");
+
+ info(`waiting for oop window global`);
+ ok(oopBC.currentWindowGlobal, "[parent] oop frame has a window global");
+
+ // Open a new window, and adopt |tab| into it.
+
+ let newWindow = await BrowserTestUtils.openNewBrowserWindow({
+ fission: true,
+ remote: true,
+ });
+
+ info(`new chrome, parent`);
+ let newChromeBC = newWindow.docShell.browsingContext;
+ ok(newChromeBC.currentWindowGlobal, "Should have a current WindowGlobal");
+ is(
+ newChromeBC.embedderWindowGlobal,
+ null,
+ "new chrome has no embedder global"
+ );
+ is(newChromeBC.embedderElement, null, "new chrome has no embedder element");
+ is(newChromeBC.parent, null, "new chrome has no parent");
+
+ isnot(newChromeBC, chromeBC, "different chrome browsing context");
+
+ info(`adopting tab`);
+ let newTab = newWindow.gBrowser.adoptTab(tab);
+
+ is(
+ newTab.linkedBrowser.browsingContext,
+ rootBC,
+ "[parent] root browsing context survived"
+ );
+ is(
+ rootBC.embedderWindowGlobal,
+ newChromeBC.currentWindowGlobal,
+ "[parent] embedder window global updated"
+ );
+ is(
+ rootBC.embedderElement,
+ newTab.linkedBrowser,
+ "[parent] embedder element updated"
+ );
+ is(rootBC.parent, null, "[parent] root has no parent");
+
+ info(`closing window`);
+ await BrowserTestUtils.closeWindow(newWindow);
+ await BrowserTestUtils.closeWindow(fissionWindow);
+});
diff --git a/docshell/test/browser/browser_browsingContext-getAllBrowsingContextsInSubtree.js b/docshell/test/browser/browser_browsingContext-getAllBrowsingContextsInSubtree.js
new file mode 100644
index 0000000000..1aba3c00aa
--- /dev/null
+++ b/docshell/test/browser/browser_browsingContext-getAllBrowsingContextsInSubtree.js
@@ -0,0 +1,53 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+async function addFrame(url) {
+ let iframe = content.document.createElement("iframe");
+ await new Promise(resolve => {
+ iframe.addEventListener("load", resolve, { once: true });
+ iframe.src = url;
+ content.document.body.appendChild(iframe);
+ });
+ return iframe.browsingContext;
+}
+
+add_task(async function() {
+ await BrowserTestUtils.withNewTab(
+ { gBrowser, url: "about:blank" },
+ async browser => {
+ // Add 15 example.com frames to the toplevel document.
+ let frames = await Promise.all(
+ Array.from({ length: 15 }).map(_ =>
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ SpecialPowers.spawn(browser, ["http://example.com/"], addFrame)
+ )
+ );
+
+ // Add an example.org subframe to each example.com frame.
+ let subframes = await Promise.all(
+ Array.from({ length: 15 }).map((_, i) =>
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ SpecialPowers.spawn(frames[i], ["http://example.org/"], addFrame)
+ )
+ );
+
+ Assert.deepEqual(
+ subframes[0].getAllBrowsingContextsInSubtree(),
+ [subframes[0]],
+ "Childless context only has self in subtree"
+ );
+ Assert.deepEqual(
+ frames[0].getAllBrowsingContextsInSubtree(),
+ [frames[0], subframes[0]],
+ "Single-child context has 2 contexts in subtree"
+ );
+ Assert.deepEqual(
+ browser.browsingContext.getAllBrowsingContextsInSubtree(),
+ [browser.browsingContext, ...frames, ...subframes],
+ "Toplevel context has all subtree contexts"
+ );
+ }
+ );
+});
diff --git a/docshell/test/browser/browser_browsingContext-getWindowByName.js b/docshell/test/browser/browser_browsingContext-getWindowByName.js
new file mode 100644
index 0000000000..a51951dd2e
--- /dev/null
+++ b/docshell/test/browser/browser_browsingContext-getWindowByName.js
@@ -0,0 +1,34 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function addWindow(name) {
+ var blank = Cc["@mozilla.org/supports-string;1"].createInstance(
+ Ci.nsISupportsString
+ );
+ blank.data = "about:blank";
+ let promise = BrowserTestUtils.waitForNewWindow({
+ anyWindow: true,
+ url: "about:blank",
+ });
+ Services.ww.openWindow(
+ null,
+ AppConstants.BROWSER_CHROME_URL,
+ name,
+ "chrome,dialog=no",
+ blank
+ );
+ return promise;
+}
+
+add_task(async function() {
+ let windows = [await addWindow("first"), await addWindow("second")];
+
+ for (let w of windows) {
+ isnot(w, null);
+ is(Services.ww.getWindowByName(w.name, null), w, `Found ${w.name}`);
+ }
+
+ await Promise.all(windows.map(BrowserTestUtils.closeWindow));
+});
diff --git a/docshell/test/browser/browser_browsingContext-webProgress.js b/docshell/test/browser/browser_browsingContext-webProgress.js
new file mode 100644
index 0000000000..53d3e3a085
--- /dev/null
+++ b/docshell/test/browser/browser_browsingContext-webProgress.js
@@ -0,0 +1,238 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function() {
+ const tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "about:blank"
+ );
+ const browser = tab.linkedBrowser;
+ const aboutBlankBrowsingContext = browser.browsingContext;
+ const { webProgress } = aboutBlankBrowsingContext;
+ ok(
+ webProgress,
+ "Got a WebProgress interface on BrowsingContext in the parent process"
+ );
+ is(
+ webProgress.browsingContext,
+ browser.browsingContext,
+ "WebProgress.browsingContext refers to the right BrowsingContext"
+ );
+
+ const onLocationChanged = waitForNextLocationChange(webProgress);
+ const newLocation = "data:text/html;charset=utf-8,first-page";
+ let loaded = BrowserTestUtils.browserLoaded(browser);
+ BrowserTestUtils.loadURI(browser, newLocation);
+ await loaded;
+
+ const firstPageBrowsingContext = browser.browsingContext;
+ const isFissionAndBfcacheInParentEnabled =
+ SpecialPowers.useRemoteSubframes &&
+ SpecialPowers.Services.prefs.getBoolPref("fission.bfcacheInParent");
+ if (isFissionAndBfcacheInParentEnabled) {
+ isnot(
+ aboutBlankBrowsingContext,
+ firstPageBrowsingContext,
+ "With fission and bfcache in parent, navigations spawn a new BrowsingContext"
+ );
+ } else {
+ is(
+ aboutBlankBrowsingContext,
+ firstPageBrowsingContext,
+ "Without fission or bfcache in parent, navigations reuse the same BrowsingContext"
+ );
+ }
+
+ info("Wait for onLocationChange to be fired");
+ {
+ const {
+ browsingContext,
+ location,
+ request,
+ flags,
+ } = await onLocationChanged;
+ is(
+ browsingContext,
+ firstPageBrowsingContext,
+ "Location change fires on the new BrowsingContext"
+ );
+ ok(location instanceof Ci.nsIURI);
+ is(location.spec, newLocation);
+ ok(request instanceof Ci.nsIChannel);
+ is(request.URI.spec, newLocation);
+ is(flags, 0);
+ }
+
+ const onIframeLocationChanged = waitForNextLocationChange(webProgress);
+ const iframeLocation = "data:text/html;charset=utf-8,iframe";
+ const iframeBC = await SpecialPowers.spawn(
+ browser,
+ [iframeLocation],
+ async url => {
+ const iframe = content.document.createElement("iframe");
+ await new Promise(resolve => {
+ iframe.addEventListener("load", resolve, { once: true });
+ iframe.src = url;
+ content.document.body.appendChild(iframe);
+ });
+
+ return iframe.browsingContext;
+ }
+ );
+ ok(
+ iframeBC.webProgress,
+ "The iframe BrowsingContext also exposes a WebProgress"
+ );
+ {
+ const {
+ browsingContext,
+ location,
+ request,
+ flags,
+ } = await onIframeLocationChanged;
+ is(
+ browsingContext,
+ iframeBC,
+ "Iframe location change fires on the iframe BrowsingContext"
+ );
+ ok(location instanceof Ci.nsIURI);
+ is(location.spec, iframeLocation);
+ ok(request instanceof Ci.nsIChannel);
+ is(request.URI.spec, iframeLocation);
+ is(flags, 0);
+ }
+
+ const onSecondLocationChanged = waitForNextLocationChange(webProgress);
+ const onSecondPageDocumentStart = waitForNextDocumentStart(webProgress);
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ const secondLocation = "http://example.com/document-builder.sjs?html=com";
+ loaded = BrowserTestUtils.browserLoaded(browser);
+ BrowserTestUtils.loadURI(browser, secondLocation);
+ await loaded;
+
+ const secondPageBrowsingContext = browser.browsingContext;
+ if (isFissionAndBfcacheInParentEnabled) {
+ isnot(
+ firstPageBrowsingContext,
+ secondPageBrowsingContext,
+ "With fission and bfcache in parent, navigations spawn a new BrowsingContext"
+ );
+ } else {
+ is(
+ firstPageBrowsingContext,
+ secondPageBrowsingContext,
+ "Without fission or bfcache in parent, navigations reuse the same BrowsingContext"
+ );
+ }
+ {
+ const {
+ browsingContext,
+ location,
+ request,
+ flags,
+ } = await onSecondLocationChanged;
+ is(
+ browsingContext,
+ secondPageBrowsingContext,
+ "Second location change fires on the new BrowsingContext"
+ );
+ ok(location instanceof Ci.nsIURI);
+ is(location.spec, secondLocation);
+ ok(request instanceof Ci.nsIChannel);
+ is(request.URI.spec, secondLocation);
+ is(flags, 0);
+ }
+ {
+ const { browsingContext, request } = await onSecondPageDocumentStart;
+ is(
+ browsingContext,
+ firstPageBrowsingContext,
+ "STATE_START, when navigating to another process, fires on the BrowsingContext we navigate *from*"
+ );
+ ok(request instanceof Ci.nsIChannel);
+ is(request.URI.spec, secondLocation);
+ }
+
+ const onBackLocationChanged = waitForNextLocationChange(webProgress, true);
+ const onBackDocumentStart = waitForNextDocumentStart(webProgress);
+ browser.goBack();
+
+ {
+ const {
+ browsingContext,
+ location,
+ request,
+ flags,
+ } = await onBackLocationChanged;
+ is(
+ browsingContext,
+ firstPageBrowsingContext,
+ "location change, when navigating back, fires on the BrowsingContext we navigate *to*"
+ );
+ ok(location instanceof Ci.nsIURI);
+ is(location.spec, newLocation);
+ ok(request instanceof Ci.nsIChannel);
+ is(request.URI.spec, newLocation);
+ is(flags, 0);
+ }
+ {
+ const { browsingContext, request } = await onBackDocumentStart;
+ is(
+ browsingContext,
+ secondPageBrowsingContext,
+ "STATE_START, when navigating back, fires on the BrowsingContext we navigate *from*"
+ );
+ ok(request instanceof Ci.nsIChannel);
+ is(request.URI.spec, newLocation);
+ }
+
+ BrowserTestUtils.removeTab(tab);
+});
+
+function waitForNextLocationChange(webProgress, onlyTopLevel = false) {
+ return new Promise(resolve => {
+ const wpl = {
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIWebProgressListener",
+ "nsISupportsWeakReference",
+ ]),
+ onLocationChange(progress, request, location, flags) {
+ if (onlyTopLevel && progress.browsingContext.parent) {
+ // Ignore non-toplevel.
+ return;
+ }
+ webProgress.removeProgressListener(wpl, Ci.nsIWebProgress.NOTIFY_ALL);
+ resolve({
+ browsingContext: progress.browsingContext,
+ location,
+ request,
+ flags,
+ });
+ },
+ };
+ webProgress.addProgressListener(wpl, Ci.nsIWebProgress.NOTIFY_ALL);
+ });
+}
+
+function waitForNextDocumentStart(webProgress) {
+ return new Promise(resolve => {
+ const wpl = {
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIWebProgressListener",
+ "nsISupportsWeakReference",
+ ]),
+ onStateChange(progress, request, flags, status) {
+ if (
+ flags & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT &&
+ flags & Ci.nsIWebProgressListener.STATE_START
+ ) {
+ webProgress.removeProgressListener(wpl, Ci.nsIWebProgress.NOTIFY_ALL);
+ resolve({ browsingContext: progress.browsingContext, request });
+ }
+ },
+ };
+ webProgress.addProgressListener(wpl, Ci.nsIWebProgress.NOTIFY_ALL);
+ });
+}
diff --git a/docshell/test/browser/browser_browsing_context_attached.js b/docshell/test/browser/browser_browsing_context_attached.js
new file mode 100644
index 0000000000..60ef5e4aa6
--- /dev/null
+++ b/docshell/test/browser/browser_browsing_context_attached.js
@@ -0,0 +1,179 @@
+"use strict";
+
+const TEST_PATH =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com"
+ ) + "dummy_page.html";
+
+const TOPIC = "browsing-context-attached";
+
+async function observeAttached(callback) {
+ let attached = new Map();
+
+ function observer(subject, topic, data) {
+ is(topic, TOPIC, "observing correct topic");
+ ok(BrowsingContext.isInstance(subject), "subject to be a BrowsingContext");
+ is(typeof data, "string", "data to be a String");
+ info(`*** bc id=${subject.id}, why=${data}`);
+ attached.set(subject.id, { browsingContext: subject, why: data });
+ }
+
+ Services.obs.addObserver(observer, TOPIC);
+ try {
+ await callback();
+ return attached;
+ } finally {
+ Services.obs.removeObserver(observer, TOPIC);
+ }
+}
+
+add_task(async function toplevelForNewWindow() {
+ let win;
+
+ let attached = await observeAttached(async () => {
+ win = await BrowserTestUtils.openNewBrowserWindow();
+ });
+
+ ok(
+ attached.has(win.browsingContext.id),
+ "got notification for window's chrome browsing context"
+ );
+ is(
+ attached.get(win.browsingContext.id).why,
+ "attach",
+ "expected reason for chrome browsing context"
+ );
+
+ ok(
+ attached.has(win.gBrowser.selectedBrowser.browsingContext.id),
+ "got notification for toplevel browsing context"
+ );
+ is(
+ attached.get(win.gBrowser.selectedBrowser.browsingContext.id).why,
+ "attach",
+ "expected reason for toplevel browsing context"
+ );
+
+ await BrowserTestUtils.closeWindow(win);
+});
+
+add_task(async function toplevelForNewTab() {
+ let tab;
+
+ let attached = await observeAttached(async () => {
+ tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+ });
+
+ ok(
+ !attached.has(window.browsingContext.id),
+ "no notification for the current window's chrome browsing context"
+ );
+ ok(
+ attached.has(tab.linkedBrowser.browsingContext.id),
+ "got notification for toplevel browsing context"
+ );
+ is(
+ attached.get(tab.linkedBrowser.browsingContext.id).why,
+ "attach",
+ "expected reason for toplevel browsing context"
+ );
+
+ BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function subframe() {
+ let browsingContext;
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+
+ let attached = await observeAttached(async () => {
+ browsingContext = await SpecialPowers.spawn(tab.linkedBrowser, [], () => {
+ let iframe = content.document.createElement("iframe");
+ content.document.body.appendChild(iframe);
+ iframe.contentWindow.location = "https://example.com/";
+ return iframe.browsingContext;
+ });
+ });
+
+ ok(
+ !attached.has(window.browsingContext.id),
+ "no notification for the current window's chrome browsing context"
+ );
+ ok(
+ !attached.has(tab.linkedBrowser.browsingContext.id),
+ "no notification for toplevel browsing context"
+ );
+ ok(
+ attached.has(browsingContext.id),
+ "got notification for frame's browsing context"
+ );
+ is(
+ attached.get(browsingContext.id).why,
+ "attach",
+ "expected reason for frame's browsing context"
+ );
+
+ BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function toplevelReplacedBy() {
+ let tab;
+
+ let attached = await observeAttached(async () => {
+ tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:robots");
+ });
+
+ const firstContext = tab.linkedBrowser.browsingContext;
+ ok(
+ attached.has(firstContext.id),
+ "got notification for initial toplevel browsing context"
+ );
+ is(
+ attached.get(firstContext.id).why,
+ "attach",
+ "expected reason for initial toplevel browsing context"
+ );
+
+ attached = await observeAttached(async () => {
+ await loadURI(TEST_PATH);
+ });
+ const secondContext = tab.linkedBrowser.browsingContext;
+ ok(
+ attached.has(secondContext.id),
+ "got notification for replaced toplevel browsing context"
+ );
+ isnot(secondContext, firstContext, "browsing context to be replaced");
+ is(
+ attached.get(secondContext.id).why,
+ "replace",
+ "expected reason for replaced toplevel browsing context"
+ );
+ is(
+ secondContext.browserId,
+ firstContext.browserId,
+ "browserId has been kept"
+ );
+
+ attached = await observeAttached(async () => {
+ await loadURI("about:robots");
+ });
+ const thirdContext = tab.linkedBrowser.browsingContext;
+ ok(
+ attached.has(thirdContext.id),
+ "got notification for replaced toplevel browsing context"
+ );
+ isnot(thirdContext, secondContext, "browsing context to be replaced");
+ is(
+ attached.get(thirdContext.id).why,
+ "replace",
+ "expected reason for replaced toplevel browsing context"
+ );
+ is(
+ thirdContext.browserId,
+ secondContext.browserId,
+ "browserId has been kept"
+ );
+
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/docshell/test/browser/browser_browsing_context_discarded.js b/docshell/test/browser/browser_browsing_context_discarded.js
new file mode 100644
index 0000000000..a300737d4f
--- /dev/null
+++ b/docshell/test/browser/browser_browsing_context_discarded.js
@@ -0,0 +1,65 @@
+"use strict";
+
+const TOPIC = "browsing-context-discarded";
+
+async function observeDiscarded(browsingContexts, callback) {
+ let discarded = [];
+
+ let promise = BrowserUtils.promiseObserved(TOPIC, subject => {
+ ok(BrowsingContext.isInstance(subject), "subject to be a BrowsingContext");
+ discarded.push(subject);
+
+ return browsingContexts.every(item => discarded.includes(item));
+ });
+ await callback();
+ await promise;
+
+ return discarded;
+}
+
+add_task(async function toplevelForNewWindow() {
+ let win = await BrowserTestUtils.openNewBrowserWindow();
+ let browsingContext = win.gBrowser.selectedBrowser.browsingContext;
+
+ await observeDiscarded([win.browsingContext, browsingContext], async () => {
+ await BrowserTestUtils.closeWindow(win);
+ });
+});
+
+add_task(async function toplevelForNewTab() {
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+ let browsingContext = tab.linkedBrowser.browsingContext;
+
+ let discarded = await observeDiscarded([browsingContext], () => {
+ BrowserTestUtils.removeTab(tab);
+ });
+
+ ok(
+ !discarded.includes(window.browsingContext),
+ "no notification for the current window's chrome browsing context"
+ );
+});
+
+add_task(async function subframe() {
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+ let browsingContext = await SpecialPowers.spawn(tab.linkedBrowser, [], () => {
+ let iframe = content.document.createElement("iframe");
+ content.document.body.appendChild(iframe);
+ iframe.contentWindow.location = "https://example.com/";
+ return iframe.browsingContext;
+ });
+
+ let discarded = await observeDiscarded([browsingContext], async () => {
+ await SpecialPowers.spawn(tab.linkedBrowser, [], () => {
+ let iframe = content.document.querySelector("iframe");
+ iframe.remove();
+ });
+ });
+
+ ok(
+ !discarded.includes(tab.browsingContext),
+ "no notification for toplevel browsing context"
+ );
+
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/docshell/test/browser/browser_bug1206879.js b/docshell/test/browser/browser_bug1206879.js
new file mode 100644
index 0000000000..135e6c99ff
--- /dev/null
+++ b/docshell/test/browser/browser_bug1206879.js
@@ -0,0 +1,50 @@
+add_task(async function() {
+ let url =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content/",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/"
+ ) + "file_bug1206879.html";
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url, true);
+
+ let numLocationChanges = await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [],
+ async function() {
+ let webprogress = content.docShell.QueryInterface(Ci.nsIWebProgress);
+ let locationChangeCount = 0;
+ let listener = {
+ onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
+ info("onLocationChange: " + aLocation.spec);
+ locationChangeCount++;
+ this.resolve();
+ },
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIWebProgressListener",
+ "nsISupportsWeakReference",
+ ]),
+ };
+ let locationPromise = new Promise((resolve, reject) => {
+ listener.resolve = resolve;
+ });
+ webprogress.addProgressListener(
+ listener,
+ Ci.nsIWebProgress.NOTIFY_LOCATION
+ );
+
+ content.frames[0].history.pushState(null, null, "foo");
+
+ await locationPromise;
+ webprogress.removeProgressListener(listener);
+
+ return locationChangeCount;
+ }
+ );
+
+ gBrowser.removeTab(tab);
+ is(
+ numLocationChanges,
+ 1,
+ "pushState with a different URI should cause a LocationChange event."
+ );
+});
diff --git a/docshell/test/browser/browser_bug1309900_crossProcessHistoryNavigation.js b/docshell/test/browser/browser_bug1309900_crossProcessHistoryNavigation.js
new file mode 100644
index 0000000000..011811c74d
--- /dev/null
+++ b/docshell/test/browser/browser_bug1309900_crossProcessHistoryNavigation.js
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+add_task(async function runTests() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.navigation.requireUserInteraction", false]],
+ });
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "about:about"
+ );
+
+ registerCleanupFunction(function() {
+ gBrowser.removeTab(tab);
+ });
+
+ let browser = tab.linkedBrowser;
+
+ let loaded = BrowserTestUtils.browserLoaded(browser);
+ BrowserTestUtils.loadURI(browser, "about:config");
+ let href = await loaded;
+ is(href, "about:config", "Check about:config loaded");
+
+ // Using a dummy onunload listener to disable the bfcache as that can prevent
+ // the test browser load detection mechanism from working.
+ loaded = BrowserTestUtils.browserLoaded(browser);
+ BrowserTestUtils.loadURI(
+ browser,
+ "data:text/html,<body%20onunload=''><iframe></iframe></body>"
+ );
+ href = await loaded;
+ is(
+ href,
+ "data:text/html,<body%20onunload=''><iframe></iframe></body>",
+ "Check data URL loaded"
+ );
+
+ loaded = BrowserTestUtils.browserLoaded(browser);
+ browser.goBack();
+ href = await loaded;
+ is(href, "about:config", "Check we've gone back to about:config");
+
+ loaded = BrowserTestUtils.browserLoaded(browser);
+ browser.goForward();
+ href = await loaded;
+ is(
+ href,
+ "data:text/html,<body%20onunload=''><iframe></iframe></body>",
+ "Check we've gone forward to data URL"
+ );
+});
diff --git a/docshell/test/browser/browser_bug1328501.js b/docshell/test/browser/browser_bug1328501.js
new file mode 100644
index 0000000000..32cc281f53
--- /dev/null
+++ b/docshell/test/browser/browser_bug1328501.js
@@ -0,0 +1,64 @@
+const HTML_URL =
+ "http://mochi.test:8888/browser/docshell/test/browser/file_bug1328501.html";
+const FRAME_URL =
+ "http://mochi.test:8888/browser/docshell/test/browser/file_bug1328501_frame.html";
+const FRAME_SCRIPT_URL =
+ "chrome://mochitests/content/browser/docshell/test/browser/file_bug1328501_framescript.js";
+add_task(async function testMultiFrameRestore() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.navigation.requireUserInteraction", false],
+ // Disable bfcache so that dummy_page.html doesn't enter there.
+ // The actual test page does already prevent bfcache and the test
+ // is just for http-on-opening-request handling in the child process.
+ ["browser.sessionhistory.max_total_viewers", 0],
+ ],
+ });
+ await BrowserTestUtils.withNewTab({ gBrowser, url: HTML_URL }, async function(
+ browser
+ ) {
+ // Navigate 2 subframes and load about:blank.
+ let browserLoaded = BrowserTestUtils.browserLoaded(browser);
+ await SpecialPowers.spawn(browser, [FRAME_URL], async function(FRAME_URL) {
+ function frameLoaded(frame) {
+ frame.contentWindow.location = FRAME_URL;
+ return new Promise(r => (frame.onload = r));
+ }
+ let frame1 = content.document.querySelector("#testFrame1");
+ let frame2 = content.document.querySelector("#testFrame2");
+ ok(frame1, "check found testFrame1");
+ ok(frame2, "check found testFrame2");
+ await frameLoaded(frame1);
+ await frameLoaded(frame2);
+ content.location = "dummy_page.html";
+ });
+ await browserLoaded;
+
+ // Load a frame script to query nsIDOMWindow on "http-on-opening-request",
+ // which will force about:blank content viewers being created.
+ browser.messageManager.loadFrameScript(FRAME_SCRIPT_URL, false);
+
+ // The frame script also forwards frames-loaded.
+ let framesLoaded = BrowserTestUtils.waitForMessage(
+ browser.messageManager,
+ "test:frames-loaded"
+ );
+
+ browser.goBack();
+ await framesLoaded;
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ await new Promise(r => setTimeout(r, 1000));
+ await SpecialPowers.spawn(browser, [FRAME_URL], FRAME_URL => {
+ is(
+ content.document.querySelector("#testFrame1").contentWindow.location
+ .href,
+ FRAME_URL
+ );
+ is(
+ content.document.querySelector("#testFrame2").contentWindow.location
+ .href,
+ FRAME_URL
+ );
+ });
+ });
+});
diff --git a/docshell/test/browser/browser_bug1347823.js b/docshell/test/browser/browser_bug1347823.js
new file mode 100644
index 0000000000..9a6e14a76f
--- /dev/null
+++ b/docshell/test/browser/browser_bug1347823.js
@@ -0,0 +1,85 @@
+/**
+ * Test that session history's expiration tracker would remove bfcache on
+ * expiration.
+ */
+
+// With bfcache not expired.
+add_task(async function testValidCache() {
+ // Make an unrealistic large timeout.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.sessionhistory.contentViewerTimeout", 86400],
+ // If Fission is disabled, the pref is no-op.
+ ["fission.bfcacheInParent", true],
+ ],
+ });
+
+ await BrowserTestUtils.withNewTab(
+ { gBrowser, url: "data:text/html;charset=utf-8,pageA1" },
+ async function(browser) {
+ // Make a simple modification for bfcache testing.
+ await SpecialPowers.spawn(browser, [], () => {
+ content.document.body.textContent = "modified";
+ });
+
+ // Load a random page.
+ BrowserTestUtils.loadURI(browser, "data:text/html;charset=utf-8,pageA2");
+ await BrowserTestUtils.browserLoaded(browser);
+
+ // Go back and verify text content.
+ let awaitPageShow = BrowserTestUtils.waitForContentEvent(
+ browser,
+ "pageshow"
+ );
+ browser.goBack();
+ await awaitPageShow;
+ await SpecialPowers.spawn(browser, [], () => {
+ is(content.document.body.textContent, "modified");
+ });
+ }
+ );
+});
+
+// With bfcache expired.
+add_task(async function testExpiredCache() {
+ // Make bfcache timeout in 1 sec.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.sessionhistory.contentViewerTimeout", 1],
+ // If Fission is disabled, the pref is no-op.
+ ["fission.bfcacheInParent", true],
+ ],
+ });
+
+ await BrowserTestUtils.withNewTab(
+ { gBrowser, url: "data:text/html;charset=utf-8,pageB1" },
+ async function(browser) {
+ // Make a simple modification for bfcache testing.
+ await SpecialPowers.spawn(browser, [], () => {
+ content.document.body.textContent = "modified";
+ });
+
+ // Load a random page.
+ BrowserTestUtils.loadURI(browser, "data:text/html;charset=utf-8,pageB2");
+ await BrowserTestUtils.browserLoaded(browser);
+
+ // Wait for 3 times of expiration timeout, hopefully it's evicted...
+ await SpecialPowers.spawn(browser, [], () => {
+ return new Promise(resolve => {
+ content.setTimeout(resolve, 5000);
+ });
+ });
+
+ // Go back and verify text content.
+ let awaitPageShow = BrowserTestUtils.waitForContentEvent(
+ browser,
+ "pageshow"
+ );
+ browser.goBack();
+ await awaitPageShow;
+ await SpecialPowers.spawn(browser, [], () => {
+ is(content.document.body.textContent, "pageB1");
+ });
+ }
+ );
+});
diff --git a/docshell/test/browser/browser_bug134911.js b/docshell/test/browser/browser_bug134911.js
new file mode 100644
index 0000000000..2fd3e82d4a
--- /dev/null
+++ b/docshell/test/browser/browser_bug134911.js
@@ -0,0 +1,57 @@
+const TEXT = {
+ /* The test text decoded correctly as Shift_JIS */
+ rightText:
+ "\u30E6\u30CB\u30B3\u30FC\u30C9\u306F\u3001\u3059\u3079\u3066\u306E\u6587\u5B57\u306B\u56FA\u6709\u306E\u756A\u53F7\u3092\u4ED8\u4E0E\u3057\u307E\u3059",
+
+ enteredText1: "The quick brown fox jumps over the lazy dog",
+ enteredText2:
+ "\u03BE\u03B5\u03C3\u03BA\u03B5\u03C0\u03AC\u03B6\u03C9\u0020\u03C4\u1F74\u03BD\u0020\u03C8\u03C5\u03C7\u03BF\u03C6\u03B8\u03CC\u03C1\u03B1\u0020\u03B2\u03B4\u03B5\u03BB\u03C5\u03B3\u03BC\u03AF\u03B1",
+};
+
+function test() {
+ waitForExplicitFinish();
+
+ var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+ gBrowser.selectedTab = BrowserTestUtils.addTab(
+ gBrowser,
+ rootDir + "test-form_sjis.html"
+ );
+ BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(afterOpen);
+}
+
+function afterOpen() {
+ BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(
+ afterChangeCharset
+ );
+
+ SpecialPowers.spawn(gBrowser.selectedBrowser, [TEXT], function(TEXT) {
+ content.document.getElementById("testtextarea").value = TEXT.enteredText1;
+ content.document.getElementById("testinput").value = TEXT.enteredText2;
+ }).then(() => {
+ /* Force the page encoding to Shift_JIS */
+ BrowserForceEncodingDetection();
+ });
+}
+
+function afterChangeCharset() {
+ SpecialPowers.spawn(gBrowser.selectedBrowser, [TEXT], function(TEXT) {
+ is(
+ content.document.getElementById("testpar").innerHTML,
+ TEXT.rightText,
+ "encoding successfully changed"
+ );
+ is(
+ content.document.getElementById("testtextarea").value,
+ TEXT.enteredText1,
+ "text preserved in <textarea>"
+ );
+ is(
+ content.document.getElementById("testinput").value,
+ TEXT.enteredText2,
+ "text preserved in <input>"
+ );
+ }).then(() => {
+ gBrowser.removeCurrentTab();
+ finish();
+ });
+}
diff --git a/docshell/test/browser/browser_bug1415918_beforeunload_options.js b/docshell/test/browser/browser_bug1415918_beforeunload_options.js
new file mode 100644
index 0000000000..8ca5daf6c8
--- /dev/null
+++ b/docshell/test/browser/browser_bug1415918_beforeunload_options.js
@@ -0,0 +1,162 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const TEST_PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com"
+);
+
+const { PromptTestUtils } = ChromeUtils.import(
+ "resource://testing-common/PromptTestUtils.jsm"
+);
+
+SimpleTest.requestFlakyTimeout("Needs to test a timeout");
+
+function delay(msec) {
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ return new Promise(resolve => setTimeout(resolve, msec));
+}
+
+function allowNextNavigation(browser) {
+ return PromptTestUtils.handleNextPrompt(
+ browser,
+ { modalType: Services.prompt.MODAL_TYPE_CONTENT, promptType: "confirmEx" },
+ { buttonNumClick: 0 }
+ );
+}
+
+function cancelNextNavigation(browser) {
+ return PromptTestUtils.handleNextPrompt(
+ browser,
+ { modalType: Services.prompt.MODAL_TYPE_CONTENT, promptType: "confirmEx" },
+ { buttonNumClick: 1 }
+ );
+}
+
+add_task(async function test() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.require_user_interaction_for_beforeunload", false]],
+ });
+
+ const permitUnloadTimeout = Services.prefs.getIntPref(
+ "dom.beforeunload_timeout_ms"
+ );
+
+ let url = TEST_PATH + "dummy_page.html";
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
+ let browser = tab.linkedBrowser;
+
+ await SpecialPowers.spawn(browser.browsingContext, [], () => {
+ content.addEventListener("beforeunload", event => {
+ event.preventDefault();
+ });
+ });
+
+ /*
+ * Check condition where beforeunload handlers request a prompt.
+ */
+
+ // Prompt is shown, user clicks OK.
+
+ let promptShownPromise = allowNextNavigation(browser);
+ ok(browser.permitUnload().permitUnload, "permit unload should be true");
+ await promptShownPromise;
+
+ // Prompt is shown, user clicks CANCEL.
+ promptShownPromise = cancelNextNavigation(browser);
+ ok(!browser.permitUnload().permitUnload, "permit unload should be false");
+ await promptShownPromise;
+
+ // Prompt is not shown, don't permit unload.
+ let promptShown = false;
+ let shownCallback = () => {
+ promptShown = true;
+ };
+
+ browser.addEventListener("DOMWillOpenModalDialog", shownCallback);
+ ok(
+ !browser.permitUnload("dontUnload").permitUnload,
+ "permit unload should be false"
+ );
+ ok(!promptShown, "prompt should not have been displayed");
+
+ // Prompt is not shown, permit unload.
+ promptShown = false;
+ ok(
+ browser.permitUnload("unload").permitUnload,
+ "permit unload should be true"
+ );
+ ok(!promptShown, "prompt should not have been displayed");
+ browser.removeEventListener("DOMWillOpenModalDialog", shownCallback);
+
+ promptShownPromise = PromptTestUtils.waitForPrompt(browser, {
+ modalType: Services.prompt.MODAL_TYPE_CONTENT,
+ promptType: "confirmEx",
+ });
+
+ let promptDismissed = false;
+ let closedCallback = () => {
+ promptDismissed = true;
+ };
+
+ browser.addEventListener("DOMModalDialogClosed", closedCallback);
+
+ let promise = browser.asyncPermitUnload();
+
+ let promiseResolved = false;
+ promise.then(() => {
+ promiseResolved = true;
+ });
+
+ let dialog = await promptShownPromise;
+ ok(!promiseResolved, "Should not have resolved promise yet");
+
+ await delay(permitUnloadTimeout * 1.5);
+
+ ok(!promptDismissed, "Should not have dismissed prompt yet");
+ ok(!promiseResolved, "Should not have resolved promise yet");
+
+ await PromptTestUtils.handlePrompt(dialog, { buttonNumClick: 1 });
+
+ let { permitUnload } = await promise;
+ ok(promptDismissed, "Should have dismissed prompt");
+ ok(!permitUnload, "Should not have permitted unload");
+
+ browser.removeEventListener("DOMModalDialogClosed", closedCallback);
+
+ promptShownPromise = allowNextNavigation(browser);
+
+ /*
+ * Check condition where no one requests a prompt. In all cases,
+ * permitUnload should be true, and all handlers fired.
+ */
+ url += "?1";
+ BrowserTestUtils.loadURI(browser, url);
+ await BrowserTestUtils.browserLoaded(browser, false, url);
+ await promptShownPromise;
+
+ promptShown = false;
+ browser.addEventListener("DOMWillOpenModalDialog", shownCallback);
+
+ ok(browser.permitUnload().permitUnload, "permit unload should be true");
+ ok(!promptShown, "prompt should not have been displayed");
+
+ promptShown = false;
+ ok(
+ browser.permitUnload("dontUnload").permitUnload,
+ "permit unload should be true"
+ );
+ ok(!promptShown, "prompt should not have been displayed");
+
+ promptShown = false;
+ ok(
+ browser.permitUnload("unload").permitUnload,
+ "permit unload should be true"
+ );
+ ok(!promptShown, "prompt should not have been displayed");
+
+ browser.removeEventListener("DOMWillOpenModalDialog", shownCallback);
+
+ await BrowserTestUtils.removeTab(tab, { skipPermitUnload: true });
+});
diff --git a/docshell/test/browser/browser_bug1543077-3.js b/docshell/test/browser/browser_bug1543077-3.js
new file mode 100644
index 0000000000..7cef4aef10
--- /dev/null
+++ b/docshell/test/browser/browser_bug1543077-3.js
@@ -0,0 +1,46 @@
+function test() {
+ var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+ runCharsetTest(
+ rootDir + "file_bug1543077-3.html",
+ afterOpen,
+ afterChangeCharset
+ );
+}
+
+function afterOpen() {
+ is(
+ content.document.documentElement.textContent.indexOf("\u3042"),
+ 136,
+ "Parent doc should be ISO-2022-JP initially"
+ );
+
+ is(
+ content.frames[0].document.documentElement.textContent.indexOf("\u3042"),
+ 92,
+ "Child doc should be ISO-2022-JP initially"
+ );
+}
+
+function afterChangeCharset() {
+ is(
+ content.document.documentElement.textContent.indexOf("\u3042"),
+ 136,
+ "Parent doc should decode as ISO-2022-JP subsequently"
+ );
+ is(
+ content.frames[0].document.documentElement.textContent.indexOf("\u3042"),
+ 92,
+ "Child doc should decode as ISO-2022-JP subsequently"
+ );
+
+ is(
+ content.document.characterSet,
+ "ISO-2022-JP",
+ "Parent doc should report ISO-2022-JP subsequently"
+ );
+ is(
+ content.frames[0].document.characterSet,
+ "ISO-2022-JP",
+ "Child doc should report ISO-2022-JP subsequently"
+ );
+}
diff --git a/docshell/test/browser/browser_bug1594938.js b/docshell/test/browser/browser_bug1594938.js
new file mode 100644
index 0000000000..5a9d4814ba
--- /dev/null
+++ b/docshell/test/browser/browser_bug1594938.js
@@ -0,0 +1,99 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Test for Bug 1594938
+ *
+ * If a session history listener blocks reloads we shouldn't crash.
+ */
+
+add_task(async function test() {
+ await BrowserTestUtils.withNewTab(
+ { gBrowser, url: "https://example.com/" },
+ async function(browser) {
+ if (!SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ await SpecialPowers.spawn(browser, [], async function() {
+ let history = this.content.docShell.QueryInterface(
+ Ci.nsIWebNavigation
+ ).sessionHistory;
+
+ let testDone = {};
+ testDone.promise = new Promise(resolve => {
+ testDone.resolve = resolve;
+ });
+
+ let listenerCalled = false;
+ let listener = {
+ OnHistoryNewEntry: aNewURI => {},
+ OnHistoryReload: () => {
+ listenerCalled = true;
+ this.content.setTimeout(() => {
+ testDone.resolve();
+ });
+ return false;
+ },
+ OnHistoryGotoIndex: () => {},
+ OnHistoryPurge: () => {},
+ OnHistoryReplaceEntry: () => {},
+
+ QueryInterface: ChromeUtils.generateQI([
+ "nsISHistoryListener",
+ "nsISupportsWeakReference",
+ ]),
+ };
+
+ history.legacySHistory.addSHistoryListener(listener);
+
+ history.reload(Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE);
+ await testDone.promise;
+
+ Assert.ok(listenerCalled, "reloads were blocked");
+
+ history.legacySHistory.removeSHistoryListener(listener);
+ });
+
+ return;
+ }
+
+ let history = browser.browsingContext.sessionHistory;
+
+ let testDone = {};
+ testDone.promise = new Promise(resolve => {
+ testDone.resolve = resolve;
+ });
+
+ let listenerCalled = false;
+ let listener = {
+ OnHistoryNewEntry: aNewURI => {},
+ OnHistoryReload: () => {
+ listenerCalled = true;
+ setTimeout(() => {
+ testDone.resolve();
+ });
+ return false;
+ },
+ OnHistoryGotoIndex: () => {},
+ OnHistoryPurge: () => {},
+ OnHistoryReplaceEntry: () => {},
+
+ QueryInterface: ChromeUtils.generateQI([
+ "nsISHistoryListener",
+ "nsISupportsWeakReference",
+ ]),
+ };
+
+ history.addSHistoryListener(listener);
+
+ await SpecialPowers.spawn(browser, [], () => {
+ let history = this.content.docShell.QueryInterface(Ci.nsIWebNavigation)
+ .sessionHistory;
+ history.reload(Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE);
+ });
+ await testDone.promise;
+
+ Assert.ok(listenerCalled, "reloads were blocked");
+
+ history.removeSHistoryListener(listener);
+ }
+ );
+});
diff --git a/docshell/test/browser/browser_bug1622420.js b/docshell/test/browser/browser_bug1622420.js
new file mode 100644
index 0000000000..81026611e4
--- /dev/null
+++ b/docshell/test/browser/browser_bug1622420.js
@@ -0,0 +1,31 @@
+const ACTOR = "Bug1622420";
+
+add_task(async function test() {
+ let base = getRootDirectory(gTestPath).slice(0, -1);
+ ChromeUtils.registerWindowActor(ACTOR, {
+ allFrames: true,
+ child: {
+ esModuleURI: `${base}/Bug1622420Child.sys.mjs`,
+ },
+ });
+
+ registerCleanupFunction(async () => {
+ gBrowser.removeTab(tab);
+
+ ChromeUtils.unregisterWindowActor(ACTOR);
+ });
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.org/browser/docshell/test/browser/file_bug1622420.html"
+ );
+ let childBC = tab.linkedBrowser.browsingContext.children[0];
+ let success = await childBC.currentWindowGlobal
+ .getActor(ACTOR)
+ .sendQuery("hasWindowContextForTopBC");
+ ok(
+ success,
+ "Should have a WindowContext for the top BrowsingContext in the process of a child BrowsingContext"
+ );
+});
diff --git a/docshell/test/browser/browser_bug1648464-1.js b/docshell/test/browser/browser_bug1648464-1.js
new file mode 100644
index 0000000000..c2a8093a3d
--- /dev/null
+++ b/docshell/test/browser/browser_bug1648464-1.js
@@ -0,0 +1,46 @@
+function test() {
+ var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+ runCharsetTest(
+ rootDir + "file_bug1648464-1.html",
+ afterOpen,
+ afterChangeCharset
+ );
+}
+
+function afterOpen() {
+ is(
+ content.document.documentElement.textContent.indexOf("\u00A4"),
+ 146,
+ "Parent doc should be windows-1252 initially"
+ );
+
+ is(
+ content.frames[0].document.documentElement.textContent.indexOf("\u00A4"),
+ 95,
+ "Child doc should be windows-1252 initially"
+ );
+}
+
+function afterChangeCharset() {
+ is(
+ content.document.documentElement.textContent.indexOf("\u3042"),
+ 146,
+ "Parent doc should decode as EUC-JP subsequently"
+ );
+ is(
+ content.frames[0].document.documentElement.textContent.indexOf("\u3042"),
+ 95,
+ "Child doc should decode as EUC-JP subsequently"
+ );
+
+ is(
+ content.document.characterSet,
+ "EUC-JP",
+ "Parent doc should report EUC-JP subsequently"
+ );
+ is(
+ content.frames[0].document.characterSet,
+ "EUC-JP",
+ "Child doc should report EUC-JP subsequently"
+ );
+}
diff --git a/docshell/test/browser/browser_bug1673702.js b/docshell/test/browser/browser_bug1673702.js
new file mode 100644
index 0000000000..3be350b0d7
--- /dev/null
+++ b/docshell/test/browser/browser_bug1673702.js
@@ -0,0 +1,26 @@
+const DUMMY =
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.org/browser/docshell/test/browser/dummy_page.html";
+const JSON =
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/browser/docshell/test/browser/file_bug1673702.json";
+
+add_task(async function test_backAndReload() {
+ await BrowserTestUtils.withNewTab({ gBrowser, url: DUMMY }, async function(
+ browser
+ ) {
+ info("Start JSON load.");
+ BrowserTestUtils.loadURI(browser, JSON);
+ await BrowserTestUtils.waitForLocationChange(gBrowser, JSON);
+
+ info("JSON load has started, go back.");
+ browser.goBack();
+ await BrowserTestUtils.browserStopped(browser);
+
+ info("Reload.");
+ BrowserReload();
+ await BrowserTestUtils.waitForLocationChange(gBrowser);
+
+ is(browser.documentURI.spec, DUMMY);
+ });
+});
diff --git a/docshell/test/browser/browser_bug1674464.js b/docshell/test/browser/browser_bug1674464.js
new file mode 100644
index 0000000000..1b78802cc0
--- /dev/null
+++ b/docshell/test/browser/browser_bug1674464.js
@@ -0,0 +1,37 @@
+const DUMMY_1 =
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.org/browser/docshell/test/browser/dummy_page.html";
+const DUMMY_2 =
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/browser/docshell/test/browser/dummy_page.html";
+
+add_task(async function test_backAndReload() {
+ await BrowserTestUtils.withNewTab({ gBrowser, url: DUMMY_1 }, async function(
+ browser
+ ) {
+ await BrowserTestUtils.crashFrame(browser);
+
+ info("Start second load.");
+ BrowserTestUtils.loadURI(browser, DUMMY_2);
+ await BrowserTestUtils.waitForLocationChange(gBrowser, DUMMY_2);
+
+ browser.goBack();
+ await BrowserTestUtils.waitForLocationChange(gBrowser);
+
+ is(
+ browser.browsingContext.childSessionHistory.index,
+ 0,
+ "We should have gone back to the first page"
+ );
+ is(
+ browser.browsingContext.childSessionHistory.count,
+ 2,
+ "If a tab crashes after a load has finished we shouldn't have an entry for about:tabcrashed"
+ );
+ is(
+ browser.documentURI.spec,
+ DUMMY_1,
+ "If a tab crashes after a load has finished we shouldn't have an entry for about:tabcrashed"
+ );
+ });
+});
diff --git a/docshell/test/browser/browser_bug1688368-1.js b/docshell/test/browser/browser_bug1688368-1.js
new file mode 100644
index 0000000000..04fc3dd9a8
--- /dev/null
+++ b/docshell/test/browser/browser_bug1688368-1.js
@@ -0,0 +1,24 @@
+function test() {
+ var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+ runCharsetTest(
+ rootDir + "file_bug1688368-1.sjs",
+ afterOpen,
+ afterChangeCharset
+ );
+}
+
+function afterOpen() {
+ is(
+ content.document.body.textContent.indexOf("â"),
+ 0,
+ "Doc should be windows-1252 initially"
+ );
+}
+
+function afterChangeCharset() {
+ is(
+ content.document.body.textContent.indexOf("☃"),
+ 0,
+ "Doc should be UTF-8 subsequently"
+ );
+}
diff --git a/docshell/test/browser/browser_bug1691153.js b/docshell/test/browser/browser_bug1691153.js
new file mode 100644
index 0000000000..b6d465393f
--- /dev/null
+++ b/docshell/test/browser/browser_bug1691153.js
@@ -0,0 +1,73 @@
+"use strict";
+
+add_task(async () => {
+ const TEST_PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com"
+ );
+
+ const HTML_URI = TEST_PATH + "file_bug1691153.html";
+
+ // Opening the page that contains the iframe
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+ let browser = tab.linkedBrowser;
+ let browserLoaded = BrowserTestUtils.browserLoaded(
+ browser,
+ true,
+ HTML_URI,
+ true
+ );
+ info("new tab loaded");
+
+ BrowserTestUtils.loadURI(browser, HTML_URI);
+ await browserLoaded;
+ info("The test page has loaded!");
+
+ let first_message_promise = SpecialPowers.spawn(
+ browser,
+ [],
+ async function() {
+ let blobPromise = new Promise((resolve, reject) => {
+ content.addEventListener("message", event => {
+ if (event.data.bloburl) {
+ info("Sanity check: recvd blob URL as " + event.data.bloburl);
+ resolve(event.data.bloburl);
+ }
+ });
+ });
+ content.postMessage("getblob", "*");
+ return blobPromise;
+ }
+ );
+ info("The test page has loaded!");
+ let blob_url = await first_message_promise;
+
+ Assert.ok(blob_url.startsWith("blob:"), "Sanity check: recvd blob");
+ info(`Received blob URL message from content: ${blob_url}`);
+ // try to open the blob in a new tab, manually created by the user
+ let tab2 = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ blob_url,
+ true,
+ false,
+ true
+ );
+
+ let principal = gBrowser.selectedTab.linkedBrowser._contentPrincipal;
+ Assert.ok(
+ !principal.isSystemPrincipal,
+ "Newly opened blob shouldn't be Systemprincipal"
+ );
+ Assert.ok(
+ !principal.isExpandedPrincipal,
+ "Newly opened blob shouldn't be ExpandedPrincipal"
+ );
+ Assert.ok(
+ principal.isContentPrincipal,
+ "Newly opened blob tab should be ContentPrincipal"
+ );
+
+ BrowserTestUtils.removeTab(tab);
+ BrowserTestUtils.removeTab(tab2);
+});
diff --git a/docshell/test/browser/browser_bug1705872.js b/docshell/test/browser/browser_bug1705872.js
new file mode 100644
index 0000000000..35a3f66d13
--- /dev/null
+++ b/docshell/test/browser/browser_bug1705872.js
@@ -0,0 +1,74 @@
+"use strict";
+
+async function doLoadAndGoBack(browser, ext) {
+ let loaded = BrowserTestUtils.browserLoaded(browser);
+ BrowserTestUtils.loadURI(browser, "https://example.com/");
+ await ext.awaitMessage("redir-handled");
+ await loaded;
+
+ let pageShownPromise = BrowserTestUtils.waitForContentEvent(
+ browser,
+ "pageshow",
+ true
+ );
+ await SpecialPowers.spawn(browser, [], () => {
+ content.history.back();
+ });
+ return pageShownPromise;
+}
+
+add_task(async function test_back() {
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ permissions: ["webRequest", "webRequestBlocking", "https://example.com/"],
+ web_accessible_resources: ["test.html"],
+ },
+ files: {
+ "test.html":
+ "<!DOCTYPE html><html><head><title>Test add-on</title></head><body></body></html>",
+ },
+ background: () => {
+ let { browser } = this;
+ browser.webRequest.onHeadersReceived.addListener(
+ details => {
+ if (details.statusCode != 200) {
+ return undefined;
+ }
+ browser.test.sendMessage("redir-handled");
+ return { redirectUrl: browser.runtime.getURL("test.html") };
+ },
+ {
+ urls: ["https://example.com/"],
+ types: ["main_frame"],
+ },
+ ["blocking"]
+ );
+ },
+ });
+
+ await extension.startup();
+
+ await BrowserTestUtils.withNewTab("about:home", async function(browser) {
+ await doLoadAndGoBack(browser, extension);
+
+ await SpecialPowers.spawn(browser, [], () => {
+ is(
+ content.document.documentURI,
+ "about:home",
+ "Gone back to the right page"
+ );
+ });
+
+ await doLoadAndGoBack(browser, extension);
+
+ await SpecialPowers.spawn(browser, [], () => {
+ is(
+ content.document.documentURI,
+ "about:home",
+ "Gone back to the right page"
+ );
+ });
+ });
+
+ await extension.unload();
+});
diff --git a/docshell/test/browser/browser_bug1716290-1.js b/docshell/test/browser/browser_bug1716290-1.js
new file mode 100644
index 0000000000..48add6f0eb
--- /dev/null
+++ b/docshell/test/browser/browser_bug1716290-1.js
@@ -0,0 +1,24 @@
+function test() {
+ var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+ runCharsetTest(
+ rootDir + "file_bug1716290-1.sjs",
+ afterOpen,
+ afterChangeCharset
+ );
+}
+
+function afterOpen() {
+ is(
+ content.document.characterSet,
+ "Shift_JIS",
+ "Doc should report Shift_JIS initially"
+ );
+}
+
+function afterChangeCharset() {
+ is(
+ content.document.characterSet,
+ "windows-1252",
+ "Doc should report windows-1252 subsequently (detector should override header)"
+ );
+}
diff --git a/docshell/test/browser/browser_bug1716290-2.js b/docshell/test/browser/browser_bug1716290-2.js
new file mode 100644
index 0000000000..a33c61f076
--- /dev/null
+++ b/docshell/test/browser/browser_bug1716290-2.js
@@ -0,0 +1,24 @@
+function test() {
+ var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+ runCharsetTest(
+ rootDir + "file_bug1716290-2.sjs",
+ afterOpen,
+ afterChangeCharset
+ );
+}
+
+function afterOpen() {
+ is(
+ content.document.characterSet,
+ "Shift_JIS",
+ "Doc should report Shift_JIS initially"
+ );
+}
+
+function afterChangeCharset() {
+ is(
+ content.document.characterSet,
+ "windows-1252",
+ "Doc should report windows-1252 subsequently (detector should override meta resolving to the replacement encoding)"
+ );
+}
diff --git a/docshell/test/browser/browser_bug1716290-3.js b/docshell/test/browser/browser_bug1716290-3.js
new file mode 100644
index 0000000000..f7e6ca5bbd
--- /dev/null
+++ b/docshell/test/browser/browser_bug1716290-3.js
@@ -0,0 +1,24 @@
+function test() {
+ var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+ runCharsetTest(
+ rootDir + "file_bug1716290-3.sjs",
+ afterOpen,
+ afterChangeCharset
+ );
+}
+
+function afterOpen() {
+ is(
+ content.document.characterSet,
+ "Shift_JIS",
+ "Doc should report Shift_JIS initially"
+ );
+}
+
+function afterChangeCharset() {
+ is(
+ content.document.characterSet,
+ "replacement",
+ "Doc should report replacement subsequently (non-ASCII-compatible HTTP header should override detector)"
+ );
+}
diff --git a/docshell/test/browser/browser_bug1716290-4.js b/docshell/test/browser/browser_bug1716290-4.js
new file mode 100644
index 0000000000..ab0d46d7ff
--- /dev/null
+++ b/docshell/test/browser/browser_bug1716290-4.js
@@ -0,0 +1,24 @@
+function test() {
+ var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+ runCharsetTest(
+ rootDir + "file_bug1716290-4.sjs",
+ afterOpen,
+ afterChangeCharset
+ );
+}
+
+function afterOpen() {
+ is(
+ content.document.characterSet,
+ "Shift_JIS",
+ "Doc should report Shift_JIS initially"
+ );
+}
+
+function afterChangeCharset() {
+ is(
+ content.document.characterSet,
+ "UTF-16BE",
+ "Doc should report UTF-16BE subsequently (BOM should override detector)"
+ );
+}
diff --git a/docshell/test/browser/browser_bug1719178.js b/docshell/test/browser/browser_bug1719178.js
new file mode 100644
index 0000000000..ec42ec79d5
--- /dev/null
+++ b/docshell/test/browser/browser_bug1719178.js
@@ -0,0 +1,34 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+SimpleTest.requestFlakyTimeout(
+ "The test needs to let objects die asynchronously."
+);
+
+add_task(async function test_accessing_shistory() {
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "about:preferences"
+ );
+ let sh = tab.linkedBrowser.browsingContext.sessionHistory;
+ ok(sh, "Should have SessionHistory object");
+ gBrowser.removeTab(tab);
+ tab = null;
+ for (let i = 0; i < 5; ++i) {
+ SpecialPowers.Services.obs.notifyObservers(
+ null,
+ "memory-pressure",
+ "heap-minimize"
+ );
+ SpecialPowers.DOMWindowUtils.garbageCollect();
+ await new Promise(function(r) {
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ setTimeout(r, 50);
+ });
+ }
+
+ try {
+ sh.reloadCurrentEntry();
+ } catch (ex) {}
+ ok(true, "This test shouldn't crash.");
+});
diff --git a/docshell/test/browser/browser_bug1736248-1.js b/docshell/test/browser/browser_bug1736248-1.js
new file mode 100644
index 0000000000..d9cbe4fd85
--- /dev/null
+++ b/docshell/test/browser/browser_bug1736248-1.js
@@ -0,0 +1,34 @@
+function test() {
+ var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+ runCharsetTest(
+ rootDir + "file_bug1736248-1.html",
+ afterOpen,
+ afterChangeCharset
+ );
+}
+
+function afterOpen() {
+ is(
+ content.document.documentElement.textContent.indexOf("\u00C3"),
+ 1064,
+ "Doc should be windows-1252 initially"
+ );
+ is(
+ content.document.characterSet,
+ "windows-1252",
+ "Doc should report windows-1252 initially"
+ );
+}
+
+function afterChangeCharset() {
+ is(
+ content.document.documentElement.textContent.indexOf("\u00E4"),
+ 1064,
+ "Doc should be UTF-8 subsequently"
+ );
+ is(
+ content.document.characterSet,
+ "UTF-8",
+ "Doc should report UTF-8 subsequently"
+ );
+}
diff --git a/docshell/test/browser/browser_bug1757005.js b/docshell/test/browser/browser_bug1757005.js
new file mode 100644
index 0000000000..873ae15d21
--- /dev/null
+++ b/docshell/test/browser/browser_bug1757005.js
@@ -0,0 +1,73 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function() {
+ // (1) Load one page with bfcache disabled and another one with bfcache enabled.
+ // (2) Check that BrowsingContext.getCurrentTopByBrowserId(browserId) returns
+ // the expected browsing context both in the parent process and in the child process.
+ // (3) Go back and then forward
+ // (4) Run the same checks as in step 2 again.
+
+ let url1 = "data:text/html,<body onunload='/* disable bfcache */'>";
+ let url2 = "data:text/html,page2";
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: url1,
+ },
+ async function(browser) {
+ info("Initial load");
+
+ let loaded = BrowserTestUtils.browserLoaded(browser);
+ BrowserTestUtils.loadURI(browser, url2);
+ await loaded;
+ info("Second page loaded");
+
+ let browserId = browser.browserId;
+ ok(!!browser.browsingContext, "Should have a BrowsingContext. (1)");
+ is(
+ BrowsingContext.getCurrentTopByBrowserId(browserId),
+ browser.browsingContext,
+ "Should get the correct browsingContext(1)"
+ );
+
+ await ContentTask.spawn(browser, browserId, async function(browserId) {
+ Assert.ok(
+ BrowsingContext.getCurrentTopByBrowserId(browserId) ==
+ docShell.browsingContext
+ );
+ Assert.ok(docShell.browsingContext.browserId == browserId);
+ });
+
+ let awaitPageShow = BrowserTestUtils.waitForContentEvent(
+ browser,
+ "pageshow"
+ );
+ browser.goBack();
+ await awaitPageShow;
+ info("Back");
+
+ awaitPageShow = BrowserTestUtils.waitForContentEvent(browser, "pageshow");
+ browser.goForward();
+ await awaitPageShow;
+ info("Forward");
+
+ ok(!!browser.browsingContext, "Should have a BrowsingContext. (2)");
+ is(
+ BrowsingContext.getCurrentTopByBrowserId(browserId),
+ browser.browsingContext,
+ "Should get the correct BrowsingContext. (2)"
+ );
+
+ await ContentTask.spawn(browser, browserId, async function(browserId) {
+ Assert.ok(
+ BrowsingContext.getCurrentTopByBrowserId(browserId) ==
+ docShell.browsingContext
+ );
+ Assert.ok(docShell.browsingContext.browserId == browserId);
+ });
+ }
+ );
+});
diff --git a/docshell/test/browser/browser_bug1769189.js b/docshell/test/browser/browser_bug1769189.js
new file mode 100644
index 0000000000..5be78c84f3
--- /dev/null
+++ b/docshell/test/browser/browser_bug1769189.js
@@ -0,0 +1,33 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function test_beforeUnload_and_replaceState() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url:
+ "data:text/html,<script>window.addEventListener('beforeunload', () => { window.history.replaceState(true, ''); });</script>",
+ },
+ async function(browser) {
+ let initialState = await SpecialPowers.spawn(browser, [], () => {
+ return content.history.state;
+ });
+
+ is(initialState, null, "history.state should be initially null.");
+
+ let awaitPageShow = BrowserTestUtils.waitForContentEvent(
+ browser,
+ "pageshow"
+ );
+ BrowserReload();
+ await awaitPageShow;
+
+ let updatedState = await SpecialPowers.spawn(browser, [], () => {
+ return content.history.state;
+ });
+ is(updatedState, true, "history.state should have been updated.");
+ }
+ );
+});
diff --git a/docshell/test/browser/browser_bug1798780.js b/docshell/test/browser/browser_bug1798780.js
new file mode 100644
index 0000000000..4da531be00
--- /dev/null
+++ b/docshell/test/browser/browser_bug1798780.js
@@ -0,0 +1,51 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// The test loads an initial page and then another page which does enough
+// fragment navigations so that when going back to the initial page and then
+// forward to the last page, the initial page is evicted from the bfcache.
+add_task(async function testBFCacheEviction() {
+ // Make an unrealistic large timeout.
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.sessionhistory.contentViewerTimeout", 86400]],
+ });
+
+ const uri = "data:text/html,initial page";
+ const uri2 =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "dummy_page.html";
+
+ await BrowserTestUtils.withNewTab({ gBrowser, url: uri }, async function(
+ browser
+ ) {
+ BrowserTestUtils.loadURI(browser, uri2);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ let awaitPageShow = BrowserTestUtils.waitForContentEvent(
+ browser,
+ "pageshow"
+ );
+ await SpecialPowers.spawn(browser, [], async function() {
+ content.location.hash = "1";
+ content.location.hash = "2";
+ content.location.hash = "3";
+ content.history.go(-4);
+ });
+
+ await awaitPageShow;
+
+ let awaitPageShow2 = BrowserTestUtils.waitForContentEvent(
+ browser,
+ "pageshow"
+ );
+ await SpecialPowers.spawn(browser, [], async function() {
+ content.history.go(4);
+ });
+ await awaitPageShow2;
+ ok(true, "Didn't time out.");
+ });
+});
diff --git a/docshell/test/browser/browser_bug234628-1.js b/docshell/test/browser/browser_bug234628-1.js
new file mode 100644
index 0000000000..566da65bca
--- /dev/null
+++ b/docshell/test/browser/browser_bug234628-1.js
@@ -0,0 +1,47 @@
+function test() {
+ var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+ runCharsetTest(
+ rootDir + "file_bug234628-1.html",
+ afterOpen,
+ afterChangeCharset
+ );
+}
+
+function afterOpen() {
+ is(
+ content.document.documentElement.textContent.indexOf("\u20AC"),
+ 129,
+ "Parent doc should be windows-1252 initially"
+ );
+
+ is(
+ content.frames[0].document.documentElement.textContent.indexOf("\u20AC"),
+ 85,
+ "Child doc should be windows-1252 initially"
+ );
+}
+
+function afterChangeCharset() {
+ is(
+ content.document.documentElement.textContent.indexOf("\u20AC"),
+ 129,
+ "Parent doc should be windows-1252 subsequently"
+ );
+
+ is(
+ content.frames[0].document.documentElement.textContent.indexOf("\u20AC"),
+ 85,
+ "Child doc should be windows-1252 subsequently"
+ );
+
+ is(
+ content.document.characterSet,
+ "windows-1252",
+ "Parent doc should report windows-1252 subsequently"
+ );
+ is(
+ content.frames[0].document.characterSet,
+ "windows-1252",
+ "Child doc should report windows-1252 subsequently"
+ );
+}
diff --git a/docshell/test/browser/browser_bug234628-10.js b/docshell/test/browser/browser_bug234628-10.js
new file mode 100644
index 0000000000..8fb51cf27c
--- /dev/null
+++ b/docshell/test/browser/browser_bug234628-10.js
@@ -0,0 +1,46 @@
+function test() {
+ var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+ runCharsetTest(
+ rootDir + "file_bug234628-10.html",
+ afterOpen,
+ afterChangeCharset
+ );
+}
+
+function afterOpen() {
+ is(
+ content.document.documentElement.textContent.indexOf("\u20AC"),
+ 151,
+ "Parent doc should be windows-1252 initially"
+ );
+
+ is(
+ content.frames[0].document.documentElement.textContent.indexOf("\u20AC"),
+ 71,
+ "Child doc should be utf-8 initially"
+ );
+}
+
+function afterChangeCharset() {
+ is(
+ content.document.documentElement.textContent.indexOf("\u20AC"),
+ 151,
+ "Parent doc should be windows-1252 initially"
+ );
+ is(
+ content.frames[0].document.documentElement.textContent.indexOf("\u20AC"),
+ 71,
+ "Child doc should decode as utf-8 subsequently"
+ );
+
+ is(
+ content.document.characterSet,
+ "windows-1252",
+ "Parent doc should report windows-1252 subsequently"
+ );
+ is(
+ content.frames[0].document.characterSet,
+ "UTF-8",
+ "Child doc should report UTF-8 subsequently"
+ );
+}
diff --git a/docshell/test/browser/browser_bug234628-11.js b/docshell/test/browser/browser_bug234628-11.js
new file mode 100644
index 0000000000..d11645ff76
--- /dev/null
+++ b/docshell/test/browser/browser_bug234628-11.js
@@ -0,0 +1,46 @@
+function test() {
+ var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+ runCharsetTest(
+ rootDir + "file_bug234628-11.html",
+ afterOpen,
+ afterChangeCharset
+ );
+}
+
+function afterOpen() {
+ is(
+ content.document.documentElement.textContent.indexOf("\u20AC"),
+ 193,
+ "Parent doc should be windows-1252 initially"
+ );
+
+ is(
+ content.frames[0].document.documentElement.textContent.indexOf("\u20AC"),
+ 107,
+ "Child doc should be utf-8 initially"
+ );
+}
+
+function afterChangeCharset() {
+ is(
+ content.document.documentElement.textContent.indexOf("\u20AC"),
+ 193,
+ "Parent doc should be windows-1252 subsequently"
+ );
+ is(
+ content.frames[0].document.documentElement.textContent.indexOf("\u20AC"),
+ 107,
+ "Child doc should decode as utf-8 subsequently"
+ );
+
+ is(
+ content.document.characterSet,
+ "windows-1252",
+ "Parent doc should report windows-1252 subsequently"
+ );
+ is(
+ content.frames[0].document.characterSet,
+ "UTF-8",
+ "Child doc should report UTF-8 subsequently"
+ );
+}
diff --git a/docshell/test/browser/browser_bug234628-2.js b/docshell/test/browser/browser_bug234628-2.js
new file mode 100644
index 0000000000..da93dc2ac2
--- /dev/null
+++ b/docshell/test/browser/browser_bug234628-2.js
@@ -0,0 +1,49 @@
+function test() {
+ var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+ runCharsetTest(
+ rootDir + "file_bug234628-2.html",
+ afterOpen,
+ afterChangeCharset
+ );
+}
+
+function afterOpen() {
+ is(
+ content.document.documentElement.textContent.indexOf("\u20AC"),
+ 129,
+ "Parent doc should be windows-1252 initially"
+ );
+
+ is(
+ content.frames[0].document.documentElement.textContent.indexOf(
+ "\u00E2\u201A\u00AC"
+ ),
+ 78,
+ "Child doc should be windows-1252 initially"
+ );
+}
+
+function afterChangeCharset() {
+ is(
+ content.document.documentElement.textContent.indexOf("\u20AC"),
+ 129,
+ "Parent doc should be windows-1252 subsequently"
+ );
+
+ is(
+ content.frames[0].document.documentElement.textContent.indexOf("\u20AC"),
+ 78,
+ "Child doc should be UTF-8 subsequently"
+ );
+
+ is(
+ content.document.characterSet,
+ "windows-1252",
+ "Parent doc should report windows-1252 subsequently"
+ );
+ is(
+ content.frames[0].document.characterSet,
+ "UTF-8",
+ "Child doc should report UTF-8 subsequently"
+ );
+}
diff --git a/docshell/test/browser/browser_bug234628-3.js b/docshell/test/browser/browser_bug234628-3.js
new file mode 100644
index 0000000000..8a143b51a6
--- /dev/null
+++ b/docshell/test/browser/browser_bug234628-3.js
@@ -0,0 +1,47 @@
+function test() {
+ var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+ runCharsetTest(
+ rootDir + "file_bug234628-3.html",
+ afterOpen,
+ afterChangeCharset
+ );
+}
+
+function afterOpen() {
+ is(
+ content.document.documentElement.textContent.indexOf("\u20AC"),
+ 118,
+ "Parent doc should be windows-1252 initially"
+ );
+
+ is(
+ content.frames[0].document.documentElement.textContent.indexOf("\u20AC"),
+ 73,
+ "Child doc should be utf-8 initially"
+ );
+}
+
+function afterChangeCharset() {
+ is(
+ content.document.documentElement.textContent.indexOf("\u20AC"),
+ 118,
+ "Parent doc should be windows-1252 subsequently"
+ );
+
+ is(
+ content.frames[0].document.documentElement.textContent.indexOf("\u20AC"),
+ 73,
+ "Child doc should be utf-8 subsequently"
+ );
+
+ is(
+ content.document.characterSet,
+ "windows-1252",
+ "Parent doc should report windows-1252 subsequently"
+ );
+ is(
+ content.frames[0].document.characterSet,
+ "UTF-8",
+ "Child doc should report UTF-8 subsequently"
+ );
+}
diff --git a/docshell/test/browser/browser_bug234628-4.js b/docshell/test/browser/browser_bug234628-4.js
new file mode 100644
index 0000000000..19ec0f8dbf
--- /dev/null
+++ b/docshell/test/browser/browser_bug234628-4.js
@@ -0,0 +1,46 @@
+function test() {
+ var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+ runCharsetTest(
+ rootDir + "file_bug234628-4.html",
+ afterOpen,
+ afterChangeCharset
+ );
+}
+
+function afterOpen() {
+ is(
+ content.document.documentElement.textContent.indexOf("\u20AC"),
+ 132,
+ "Parent doc should be windows-1252 initially"
+ );
+
+ is(
+ content.frames[0].document.documentElement.textContent.indexOf("\u20AC"),
+ 79,
+ "Child doc should be utf-8 initially"
+ );
+}
+
+function afterChangeCharset() {
+ is(
+ content.document.documentElement.textContent.indexOf("\u20AC"),
+ 132,
+ "Parent doc should decode as windows-1252 subsequently"
+ );
+ is(
+ content.frames[0].document.documentElement.textContent.indexOf("\u20AC"),
+ 79,
+ "Child doc should decode as utf-8 subsequently"
+ );
+
+ is(
+ content.document.characterSet,
+ "windows-1252",
+ "Parent doc should report windows-1252 subsequently"
+ );
+ is(
+ content.frames[0].document.characterSet,
+ "UTF-8",
+ "Child doc should report UTF-8 subsequently"
+ );
+}
diff --git a/docshell/test/browser/browser_bug234628-5.js b/docshell/test/browser/browser_bug234628-5.js
new file mode 100644
index 0000000000..77753ed78d
--- /dev/null
+++ b/docshell/test/browser/browser_bug234628-5.js
@@ -0,0 +1,46 @@
+function test() {
+ var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+ runCharsetTest(
+ rootDir + "file_bug234628-5.html",
+ afterOpen,
+ afterChangeCharset
+ );
+}
+
+function afterOpen() {
+ is(
+ content.document.documentElement.textContent.indexOf("\u20AC"),
+ 146,
+ "Parent doc should be windows-1252 initially"
+ );
+
+ is(
+ content.frames[0].document.documentElement.textContent.indexOf("\u20AC"),
+ 87,
+ "Child doc should be utf-16 initially"
+ );
+}
+
+function afterChangeCharset() {
+ is(
+ content.document.documentElement.textContent.indexOf("\u20AC"),
+ 146,
+ "Parent doc should be windows-1252 subsequently"
+ );
+ is(
+ content.frames[0].document.documentElement.textContent.indexOf("\u20AC"),
+ 87,
+ "Child doc should decode as utf-16 subsequently"
+ );
+
+ is(
+ content.document.characterSet,
+ "windows-1252",
+ "Parent doc should report windows-1252 subsequently"
+ );
+ is(
+ content.frames[0].document.characterSet,
+ "UTF-16LE",
+ "Child doc should report UTF-16LE subsequently"
+ );
+}
diff --git a/docshell/test/browser/browser_bug234628-6.js b/docshell/test/browser/browser_bug234628-6.js
new file mode 100644
index 0000000000..88ff6c1a82
--- /dev/null
+++ b/docshell/test/browser/browser_bug234628-6.js
@@ -0,0 +1,47 @@
+function test() {
+ var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+ runCharsetTest(
+ rootDir + "file_bug234628-6.html",
+ afterOpen,
+ afterChangeCharset
+ );
+}
+
+function afterOpen() {
+ is(
+ content.document.documentElement.textContent.indexOf("\u20AC"),
+ 190,
+ "Parent doc should be windows-1252 initially"
+ );
+
+ is(
+ content.frames[0].document.documentElement.textContent.indexOf("\u20AC"),
+ 109,
+ "Child doc should be utf-16 initially"
+ );
+}
+
+function afterChangeCharset() {
+ is(
+ content.document.documentElement.textContent.indexOf("\u20AC"),
+ 190,
+ "Parent doc should be windows-1252 subsequently"
+ );
+
+ is(
+ content.frames[0].document.documentElement.textContent.indexOf("\u20AC"),
+ 109,
+ "Child doc should be utf-16 subsequently"
+ );
+
+ is(
+ content.document.characterSet,
+ "windows-1252",
+ "Parent doc should report windows-1252 subsequently"
+ );
+ is(
+ content.frames[0].document.characterSet,
+ "UTF-16BE",
+ "Child doc should report UTF-16 subsequently"
+ );
+}
diff --git a/docshell/test/browser/browser_bug234628-8.js b/docshell/test/browser/browser_bug234628-8.js
new file mode 100644
index 0000000000..024a3d4d64
--- /dev/null
+++ b/docshell/test/browser/browser_bug234628-8.js
@@ -0,0 +1,18 @@
+function test() {
+ var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+ runCharsetCheck(rootDir + "file_bug234628-8.html", afterOpen);
+}
+
+function afterOpen() {
+ is(
+ content.document.documentElement.textContent.indexOf("\u0402"),
+ 156,
+ "Parent doc should be windows-1251"
+ );
+
+ is(
+ content.frames[0].document.documentElement.textContent.indexOf("\u0402"),
+ 99,
+ "Child doc should be windows-1251"
+ );
+}
diff --git a/docshell/test/browser/browser_bug234628-9.js b/docshell/test/browser/browser_bug234628-9.js
new file mode 100644
index 0000000000..ceb7dc4e63
--- /dev/null
+++ b/docshell/test/browser/browser_bug234628-9.js
@@ -0,0 +1,18 @@
+function test() {
+ var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+ runCharsetCheck(rootDir + "file_bug234628-9.html", afterOpen);
+}
+
+function afterOpen() {
+ is(
+ content.document.documentElement.textContent.indexOf("\u20AC"),
+ 145,
+ "Parent doc should be UTF-16"
+ );
+
+ is(
+ content.frames[0].document.documentElement.textContent.indexOf("\u20AC"),
+ 96,
+ "Child doc should be windows-1252"
+ );
+}
diff --git a/docshell/test/browser/browser_bug349769.js b/docshell/test/browser/browser_bug349769.js
new file mode 100644
index 0000000000..6fde42e876
--- /dev/null
+++ b/docshell/test/browser/browser_bug349769.js
@@ -0,0 +1,72 @@
+add_task(async function test() {
+ const uris = [undefined, "about:blank"];
+
+ function checkContentProcess(newBrowser, uri) {
+ return ContentTask.spawn(newBrowser, [uri], async function(uri) {
+ var prin = content.document.nodePrincipal;
+ Assert.notEqual(
+ prin,
+ null,
+ "Loaded principal must not be null when adding " + uri
+ );
+ Assert.notEqual(
+ prin,
+ undefined,
+ "Loaded principal must not be undefined when loading " + uri
+ );
+
+ Assert.equal(
+ prin.isSystemPrincipal,
+ false,
+ "Loaded principal must not be system when loading " + uri
+ );
+ });
+ }
+
+ for (var uri of uris) {
+ await BrowserTestUtils.withNewTab({ gBrowser }, async function(newBrowser) {
+ let loadedPromise = BrowserTestUtils.browserLoaded(newBrowser);
+ BrowserTestUtils.loadURI(newBrowser, uri);
+
+ var prin = newBrowser.contentPrincipal;
+ isnot(
+ prin,
+ null,
+ "Forced principal must not be null when loading " + uri
+ );
+ isnot(
+ prin,
+ undefined,
+ "Forced principal must not be undefined when loading " + uri
+ );
+ is(
+ prin.isSystemPrincipal,
+ false,
+ "Forced principal must not be system when loading " + uri
+ );
+
+ // Belt-and-suspenders e10s check: make sure that the same checks hold
+ // true in the content process.
+ await checkContentProcess(newBrowser, uri);
+
+ await loadedPromise;
+
+ prin = newBrowser.contentPrincipal;
+ isnot(prin, null, "Loaded principal must not be null when adding " + uri);
+ isnot(
+ prin,
+ undefined,
+ "Loaded principal must not be undefined when loading " + uri
+ );
+ is(
+ prin.isSystemPrincipal,
+ false,
+ "Loaded principal must not be system when loading " + uri
+ );
+
+ // Belt-and-suspenders e10s check: make sure that the same checks hold
+ // true in the content process.
+ await checkContentProcess(newBrowser, uri);
+ });
+ }
+});
diff --git a/docshell/test/browser/browser_bug388121-1.js b/docshell/test/browser/browser_bug388121-1.js
new file mode 100644
index 0000000000..6206e158af
--- /dev/null
+++ b/docshell/test/browser/browser_bug388121-1.js
@@ -0,0 +1,22 @@
+add_task(async function test() {
+ await BrowserTestUtils.withNewTab(
+ { gBrowser, url: "about:blank" },
+ async function(newBrowser) {
+ await SpecialPowers.spawn(newBrowser, [], async function() {
+ var prin = content.document.nodePrincipal;
+ Assert.notEqual(prin, null, "Loaded principal must not be null");
+ Assert.notEqual(
+ prin,
+ undefined,
+ "Loaded principal must not be undefined"
+ );
+
+ Assert.equal(
+ prin.isSystemPrincipal,
+ false,
+ "Loaded principal must not be system"
+ );
+ });
+ }
+ );
+});
diff --git a/docshell/test/browser/browser_bug388121-2.js b/docshell/test/browser/browser_bug388121-2.js
new file mode 100644
index 0000000000..695ff1079c
--- /dev/null
+++ b/docshell/test/browser/browser_bug388121-2.js
@@ -0,0 +1,74 @@
+function test() {
+ waitForExplicitFinish();
+
+ var w;
+ var iteration = 1;
+ const uris = ["", "about:blank"];
+ var uri;
+ var origWgp;
+
+ function testLoad() {
+ let wgp = w.gBrowser.selectedBrowser.browsingContext.currentWindowGlobal;
+ if (wgp == origWgp) {
+ // Go back to polling
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ setTimeout(testLoad, 10);
+ return;
+ }
+ var prin = wgp.documentPrincipal;
+ isnot(prin, null, "Loaded principal must not be null when adding " + uri);
+ isnot(
+ prin,
+ undefined,
+ "Loaded principal must not be undefined when loading " + uri
+ );
+ is(
+ prin.isSystemPrincipal,
+ false,
+ "Loaded principal must not be system when loading " + uri
+ );
+ w.close();
+
+ if (iteration == uris.length) {
+ finish();
+ } else {
+ ++iteration;
+ doTest();
+ }
+ }
+
+ function doTest() {
+ uri = uris[iteration - 1];
+ window.open(uri, "_blank", "width=10,height=10,noopener");
+ w = Services.wm.getMostRecentWindow("navigator:browser");
+ origWgp = w.gBrowser.selectedBrowser.browsingContext.currentWindowGlobal;
+ var prin = origWgp.documentPrincipal;
+ if (!uri) {
+ uri = undefined;
+ }
+ isnot(prin, null, "Forced principal must not be null when loading " + uri);
+ isnot(
+ prin,
+ undefined,
+ "Forced principal must not be undefined when loading " + uri
+ );
+ is(
+ prin.isSystemPrincipal,
+ false,
+ "Forced principal must not be system when loading " + uri
+ );
+ if (uri == undefined) {
+ // No actual load here, so just move along.
+ w.close();
+ ++iteration;
+ doTest();
+ } else {
+ // Need to poll, because load listeners on the content window won't
+ // survive the load.
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ setTimeout(testLoad, 10);
+ }
+ }
+
+ doTest();
+}
diff --git a/docshell/test/browser/browser_bug420605.js b/docshell/test/browser/browser_bug420605.js
new file mode 100644
index 0000000000..13e3695eae
--- /dev/null
+++ b/docshell/test/browser/browser_bug420605.js
@@ -0,0 +1,133 @@
+/* Test for Bug 420605
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=420605
+ */
+
+const { PlacesTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/PlacesTestUtils.sys.mjs"
+);
+
+add_task(async function test() {
+ var pageurl =
+ "http://mochi.test:8888/browser/docshell/test/browser/file_bug420605.html";
+ var fragmenturl =
+ "http://mochi.test:8888/browser/docshell/test/browser/file_bug420605.html#firefox";
+
+ /* Queries nsINavHistoryService and returns a single history entry
+ * for a given URI */
+ function getNavHistoryEntry(aURI) {
+ var options = PlacesUtils.history.getNewQueryOptions();
+ options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY;
+ options.maxResults = 1;
+
+ var query = PlacesUtils.history.getNewQuery();
+ query.uri = aURI;
+ var result = PlacesUtils.history.executeQuery(query, options);
+ result.root.containerOpen = true;
+
+ if (!result.root.childCount) {
+ return null;
+ }
+ return result.root.getChild(0);
+ }
+
+ // We'll save the favicon URL of the orignal page here and check that the
+ // page with a hash has the same favicon.
+ var originalFavicon;
+
+ // Control flow in this test is a bit complicated.
+ //
+ // When the page loads, onPageLoad (the DOMContentLoaded handler) and
+ // favicon-changed are both called, in some order. Once
+ // they've both run, we click a fragment link in the content page
+ // (clickLinkIfReady), which should trigger another favicon-changed event,
+ // this time for the fragment's URL.
+
+ var _clickLinkTimes = 0;
+ function clickLinkIfReady() {
+ _clickLinkTimes++;
+ if (_clickLinkTimes == 2) {
+ BrowserTestUtils.synthesizeMouseAtCenter(
+ "#firefox-link",
+ {},
+ gBrowser.selectedBrowser
+ );
+ }
+ }
+
+ function onPageLoad() {
+ clickLinkIfReady();
+ }
+
+ // Make sure neither of the test pages haven't been loaded before.
+ var info = getNavHistoryEntry(makeURI(pageurl));
+ ok(!info, "The test page must not have been visited already.");
+ info = getNavHistoryEntry(makeURI(fragmenturl));
+ ok(!info, "The fragment test page must not have been visited already.");
+
+ let promiseIcon1 = PlacesTestUtils.waitForNotification(
+ "favicon-changed",
+ events =>
+ events.some(e => {
+ if (e.url == pageurl) {
+ ok(
+ e.faviconUrl,
+ "Favicon value is not null for page without fragment."
+ );
+ originalFavicon = e.faviconUrl;
+
+ // Now that the favicon has loaded, click on fragment link.
+ // This should trigger the |case fragmenturl| below.
+ clickLinkIfReady();
+ return true;
+ }
+ return false;
+ }),
+ "places"
+ );
+ let promiseIcon2 = PlacesTestUtils.waitForNotification(
+ "favicon-changed",
+ events =>
+ events.some(e => {
+ if (e.url == fragmenturl) {
+ // If the fragment URL's favicon isn't set, this branch won't
+ // be called and the test will time out.
+ is(
+ e.faviconUrl,
+ originalFavicon,
+ "New favicon should be same as original favicon."
+ );
+ ok(
+ e.faviconUrl,
+ "Favicon value is not null for page without fragment."
+ );
+ originalFavicon = e.faviconUrl;
+
+ // Now that the favicon has loaded, click on fragment link.
+ // This should trigger the |case fragmenturl| below.
+ clickLinkIfReady();
+ return true;
+ }
+ return false;
+ }),
+ "places"
+ );
+
+ // Now open the test page in a new tab.
+ gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
+ BrowserTestUtils.waitForContentEvent(
+ gBrowser.selectedBrowser,
+ "DOMContentLoaded",
+ true
+ ).then(onPageLoad);
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, pageurl);
+
+ await promiseIcon1;
+ await promiseIcon2;
+
+ // Let's explicitly check that we can get the favicon
+ // from nsINavHistoryService now.
+ info = getNavHistoryEntry(makeURI(fragmenturl));
+ ok(info, "There must be a history entry for the fragment.");
+ ok(info.icon, "The history entry must have an associated favicon.");
+ gBrowser.removeCurrentTab();
+});
diff --git a/docshell/test/browser/browser_bug422543.js b/docshell/test/browser/browser_bug422543.js
new file mode 100644
index 0000000000..886b9c183a
--- /dev/null
+++ b/docshell/test/browser/browser_bug422543.js
@@ -0,0 +1,253 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const ACTOR = "Bug422543";
+
+let getActor = browser => {
+ return browser.browsingContext.currentWindowGlobal.getActor(ACTOR);
+};
+
+add_task(async function runTests() {
+ if (!SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ await setupAsync();
+ let browser = gBrowser.selectedBrowser;
+ // Now that we're set up, initialize our frame script.
+ await checkListenersAsync("initial", "listeners initialized");
+
+ // Check if all history listeners are always notified.
+ info("# part 1");
+ await whenPageShown(browser, () =>
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ BrowserTestUtils.loadURI(browser, "http://www.example.com/")
+ );
+ await checkListenersAsync("newentry", "shistory has a new entry");
+ ok(browser.canGoBack, "we can go back");
+
+ await whenPageShown(browser, () => browser.goBack());
+ await checkListenersAsync("gotoindex", "back to the first shentry");
+ ok(browser.canGoForward, "we can go forward");
+
+ await whenPageShown(browser, () => browser.goForward());
+ await checkListenersAsync("gotoindex", "forward to the second shentry");
+
+ await whenPageShown(browser, () => browser.reload());
+ await checkListenersAsync("reload", "current shentry reloaded");
+
+ await whenPageShown(browser, () => browser.gotoIndex(0));
+ await checkListenersAsync("gotoindex", "back to the first index");
+
+ // Check nsISHistory.notifyOnHistoryReload
+ info("# part 2");
+ ok(await notifyReloadAsync(), "reloading has not been canceled");
+ await checkListenersAsync("reload", "saw the reload notification");
+
+ // Let the first listener cancel the reload action.
+ info("# part 3");
+ await resetListenersAsync();
+ await setListenerRetvalAsync(0, false);
+ ok(!(await notifyReloadAsync()), "reloading has been canceled");
+ await checkListenersAsync("reload", "saw the reload notification");
+
+ // Let both listeners cancel the reload action.
+ info("# part 4");
+ await resetListenersAsync();
+ await setListenerRetvalAsync(1, false);
+ ok(!(await notifyReloadAsync()), "reloading has been canceled");
+ await checkListenersAsync("reload", "saw the reload notification");
+
+ // Let the second listener cancel the reload action.
+ info("# part 5");
+ await resetListenersAsync();
+ await setListenerRetvalAsync(0, true);
+ ok(!(await notifyReloadAsync()), "reloading has been canceled");
+ await checkListenersAsync("reload", "saw the reload notification");
+
+ function sendQuery(message, arg = {}) {
+ return getActor(gBrowser.selectedBrowser).sendQuery(message, arg);
+ }
+
+ function checkListenersAsync(aLast, aMessage) {
+ return sendQuery("getListenerStatus").then(listenerStatuses => {
+ is(listenerStatuses[0], aLast, aMessage);
+ is(listenerStatuses[1], aLast, aMessage);
+ });
+ }
+
+ function resetListenersAsync() {
+ return sendQuery("resetListeners");
+ }
+
+ function notifyReloadAsync() {
+ return sendQuery("notifyReload").then(({ rval }) => {
+ return rval;
+ });
+ }
+
+ function setListenerRetvalAsync(num, val) {
+ return sendQuery("setRetval", { num, val });
+ }
+
+ async function setupAsync() {
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "http://mochi.test:8888"
+ );
+
+ let base = getRootDirectory(gTestPath).slice(0, -1);
+ ChromeUtils.registerWindowActor(ACTOR, {
+ child: {
+ esModuleURI: `${base}/Bug422543Child.sys.mjs`,
+ },
+ });
+
+ registerCleanupFunction(async () => {
+ await sendQuery("cleanup");
+ gBrowser.removeTab(tab);
+
+ ChromeUtils.unregisterWindowActor(ACTOR);
+ });
+
+ await sendQuery("init");
+ }
+ return;
+ }
+
+ await setup();
+ let browser = gBrowser.selectedBrowser;
+ // Now that we're set up, initialize our frame script.
+ checkListeners("initial", "listeners initialized");
+
+ // Check if all history listeners are always notified.
+ info("# part 1");
+ await whenPageShown(browser, () =>
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ BrowserTestUtils.loadURI(browser, "http://www.example.com/")
+ );
+ checkListeners("newentry", "shistory has a new entry");
+ ok(browser.canGoBack, "we can go back");
+
+ await whenPageShown(browser, () => browser.goBack());
+ checkListeners("gotoindex", "back to the first shentry");
+ ok(browser.canGoForward, "we can go forward");
+
+ await whenPageShown(browser, () => browser.goForward());
+ checkListeners("gotoindex", "forward to the second shentry");
+
+ await whenPageShown(browser, () => browser.reload());
+ checkListeners("reload", "current shentry reloaded");
+
+ await whenPageShown(browser, () => browser.gotoIndex(0));
+ checkListeners("gotoindex", "back to the first index");
+
+ // Check nsISHistory.notifyOnHistoryReload
+ info("# part 2");
+ ok(notifyReload(browser), "reloading has not been canceled");
+ checkListeners("reload", "saw the reload notification");
+
+ // Let the first listener cancel the reload action.
+ info("# part 3");
+ resetListeners();
+ setListenerRetval(0, false);
+ ok(!notifyReload(browser), "reloading has been canceled");
+ checkListeners("reload", "saw the reload notification");
+
+ // Let both listeners cancel the reload action.
+ info("# part 4");
+ resetListeners();
+ setListenerRetval(1, false);
+ ok(!notifyReload(browser), "reloading has been canceled");
+ checkListeners("reload", "saw the reload notification");
+
+ // Let the second listener cancel the reload action.
+ info("# part 5");
+ resetListeners();
+ setListenerRetval(0, true);
+ ok(!notifyReload(browser), "reloading has been canceled");
+ checkListeners("reload", "saw the reload notification");
+});
+
+class SHistoryListener {
+ constructor() {
+ this.retval = true;
+ this.last = "initial";
+ }
+
+ OnHistoryNewEntry(aNewURI) {
+ this.last = "newentry";
+ }
+
+ OnHistoryGotoIndex() {
+ this.last = "gotoindex";
+ }
+
+ OnHistoryPurge() {
+ this.last = "purge";
+ }
+
+ OnHistoryReload() {
+ this.last = "reload";
+ return this.retval;
+ }
+
+ OnHistoryReplaceEntry() {}
+}
+SHistoryListener.prototype.QueryInterface = ChromeUtils.generateQI([
+ "nsISHistoryListener",
+ "nsISupportsWeakReference",
+]);
+
+let listeners = [new SHistoryListener(), new SHistoryListener()];
+
+function checkListeners(aLast, aMessage) {
+ is(listeners[0].last, aLast, aMessage);
+ is(listeners[1].last, aLast, aMessage);
+}
+
+function resetListeners() {
+ for (let listener of listeners) {
+ listener.last = "initial";
+ }
+}
+
+function notifyReload(browser) {
+ return browser.browsingContext.sessionHistory.notifyOnHistoryReload();
+}
+
+function setListenerRetval(num, val) {
+ listeners[num].retval = val;
+}
+
+async function setup() {
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "http://mochi.test:8888"
+ );
+
+ let browser = tab.linkedBrowser;
+ registerCleanupFunction(async function() {
+ for (let listener of listeners) {
+ browser.browsingContext.sessionHistory.removeSHistoryListener(listener);
+ }
+ gBrowser.removeTab(tab);
+ });
+ for (let listener of listeners) {
+ browser.browsingContext.sessionHistory.addSHistoryListener(listener);
+ }
+}
+
+function whenPageShown(aBrowser, aNavigation) {
+ let promise = new Promise(resolve => {
+ let unregister = BrowserTestUtils.addContentEventListener(
+ aBrowser,
+ "pageshow",
+ () => {
+ unregister();
+ resolve();
+ },
+ { capture: true }
+ );
+ });
+
+ aNavigation();
+ return promise;
+}
diff --git a/docshell/test/browser/browser_bug441169.js b/docshell/test/browser/browser_bug441169.js
new file mode 100644
index 0000000000..09e83b040c
--- /dev/null
+++ b/docshell/test/browser/browser_bug441169.js
@@ -0,0 +1,44 @@
+/* Make sure that netError won't allow HTML injection through badcert parameters. See bug 441169. */
+var newBrowser;
+
+function task() {
+ let resolve;
+ let promise = new Promise(r => {
+ resolve = r;
+ });
+
+ addEventListener("DOMContentLoaded", checkPage, false);
+
+ function checkPage(event) {
+ if (event.target != content.document) {
+ return;
+ }
+ removeEventListener("DOMContentLoaded", checkPage, false);
+
+ is(
+ content.document.getElementById("test_span"),
+ null,
+ "Error message should not be parsed as HTML, and hence shouldn't include the 'test_span' element."
+ );
+ resolve();
+ }
+
+ var chromeURL =
+ "about:neterror?e=nssBadCert&u=https%3A//test.kuix.de/&c=UTF-8&d=This%20sentence%20should%20not%20be%20parsed%20to%20include%20a%20%3Cspan%20id=%22test_span%22%3Enamed%3C/span%3E%20span%20tag.%0A%0AThe%20certificate%20is%20only%20valid%20for%20%3Ca%20id=%22cert_domain_link%22%20title=%22kuix.de%22%3Ekuix.de%3C/a%3E%0A%0A(Error%20code%3A%20ssl_error_bad_cert_domain)";
+ content.location = chromeURL;
+
+ return promise;
+}
+
+function test() {
+ waitForExplicitFinish();
+
+ var newTab = BrowserTestUtils.addTab(gBrowser);
+ gBrowser.selectedTab = newTab;
+ newBrowser = gBrowser.getBrowserForTab(newTab);
+
+ ContentTask.spawn(newBrowser, null, task).then(() => {
+ gBrowser.removeCurrentTab();
+ finish();
+ });
+}
diff --git a/docshell/test/browser/browser_bug503832.js b/docshell/test/browser/browser_bug503832.js
new file mode 100644
index 0000000000..c912b83a31
--- /dev/null
+++ b/docshell/test/browser/browser_bug503832.js
@@ -0,0 +1,76 @@
+/* Test for Bug 503832
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=503832
+ */
+
+add_task(async function() {
+ var pagetitle = "Page Title for Bug 503832";
+ var pageurl =
+ "http://mochi.test:8888/browser/docshell/test/browser/file_bug503832.html";
+ var fragmenturl =
+ "http://mochi.test:8888/browser/docshell/test/browser/file_bug503832.html#firefox";
+
+ var historyService = Cc[
+ "@mozilla.org/browser/nav-history-service;1"
+ ].getService(Ci.nsINavHistoryService);
+
+ let fragmentPromise = new Promise(resolve => {
+ const listener = events => {
+ const { url, title } = events[0];
+
+ switch (url) {
+ case pageurl:
+ is(title, pagetitle, "Correct page title for " + url);
+ return;
+ case fragmenturl:
+ is(title, pagetitle, "Correct page title for " + url);
+ // If titles for fragment URLs aren't set, this code
+ // branch won't be called and the test will timeout,
+ // resulting in a failure
+ PlacesObservers.removeListener(["page-title-changed"], listener);
+ resolve();
+ }
+ };
+
+ PlacesObservers.addListener(["page-title-changed"], listener);
+ });
+
+ /* Queries nsINavHistoryService and returns a single history entry
+ * for a given URI */
+ function getNavHistoryEntry(aURI) {
+ var options = historyService.getNewQueryOptions();
+ options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY;
+ options.maxResults = 1;
+
+ var query = historyService.getNewQuery();
+ query.uri = aURI;
+
+ var result = historyService.executeQuery(query, options);
+ result.root.containerOpen = true;
+
+ if (!result.root.childCount) {
+ return null;
+ }
+ var node = result.root.getChild(0);
+ result.root.containerOpen = false;
+ return node;
+ }
+
+ // Make sure neither of the test pages haven't been loaded before.
+ var info = getNavHistoryEntry(makeURI(pageurl));
+ ok(!info, "The test page must not have been visited already.");
+ info = getNavHistoryEntry(makeURI(fragmenturl));
+ ok(!info, "The fragment test page must not have been visited already.");
+
+ // Now open the test page in a new tab
+ await BrowserTestUtils.openNewForegroundTab(gBrowser, pageurl);
+
+ // Now that the page is loaded, click on fragment link
+ await BrowserTestUtils.synthesizeMouseAtCenter(
+ "#firefox-link",
+ {},
+ gBrowser.selectedBrowser
+ );
+ await fragmentPromise;
+
+ gBrowser.removeCurrentTab();
+});
diff --git a/docshell/test/browser/browser_bug554155.js b/docshell/test/browser/browser_bug554155.js
new file mode 100644
index 0000000000..ad63706195
--- /dev/null
+++ b/docshell/test/browser/browser_bug554155.js
@@ -0,0 +1,33 @@
+add_task(async function test() {
+ await BrowserTestUtils.withNewTab(
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ { gBrowser, url: "http://example.com" },
+ async function(browser) {
+ let numLocationChanges = 0;
+
+ let listener = {
+ onLocationChange(browser, webProgress, request, uri, flags) {
+ info("location change: " + (uri && uri.spec));
+ numLocationChanges++;
+ },
+ };
+
+ gBrowser.addTabsProgressListener(listener);
+
+ await SpecialPowers.spawn(browser, [], function() {
+ // pushState to a new URL (http://example.com/foo"). This should trigger
+ // exactly one LocationChange event.
+ content.history.pushState(null, null, "foo");
+ });
+
+ await Promise.resolve();
+
+ gBrowser.removeTabsProgressListener(listener);
+ is(
+ numLocationChanges,
+ 1,
+ "pushState should cause exactly one LocationChange event."
+ );
+ }
+ );
+});
diff --git a/docshell/test/browser/browser_bug655270.js b/docshell/test/browser/browser_bug655270.js
new file mode 100644
index 0000000000..231105b33e
--- /dev/null
+++ b/docshell/test/browser/browser_bug655270.js
@@ -0,0 +1,64 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Test for Bug 655273
+ *
+ * Call pushState and then make sure that the favicon service associates our
+ * old favicon with the new URI.
+ */
+
+const { PlacesTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/PlacesTestUtils.sys.mjs"
+);
+
+add_task(async function test() {
+ const testDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+ const origURL = testDir + "file_bug655270.html";
+ const newURL = origURL + "?new_page";
+
+ const faviconURL = testDir + "favicon_bug655270.ico";
+
+ let icon1;
+ let promiseIcon1 = PlacesTestUtils.waitForNotification(
+ "favicon-changed",
+ events =>
+ events.some(e => {
+ if (e.url == origURL) {
+ icon1 = e.faviconUrl;
+ return true;
+ }
+ return false;
+ }),
+ "places"
+ );
+ let icon2;
+ let promiseIcon2 = PlacesTestUtils.waitForNotification(
+ "favicon-changed",
+ events =>
+ events.some(e => {
+ if (e.url == newURL) {
+ icon2 = e.faviconUrl;
+ return true;
+ }
+ return false;
+ }),
+ "places"
+ );
+
+ // The page at origURL has a <link rel='icon'>, so we should get a call into
+ // our observer below when it loads. Once we verify that we have the right
+ // favicon URI, we call pushState, which should trigger another favicon change
+ // event, this time for the URI after pushState.
+ let tab = BrowserTestUtils.addTab(gBrowser, origURL);
+ await promiseIcon1;
+ is(icon1, faviconURL, "FaviconURL for original URI");
+ // Ignore the promise returned here and wait for the next
+ // onPageChanged notification.
+ SpecialPowers.spawn(tab.linkedBrowser, [], function() {
+ content.history.pushState("", "", "?new_page");
+ });
+ await promiseIcon2;
+ is(icon2, faviconURL, "FaviconURL for new URI");
+ gBrowser.removeTab(tab);
+});
diff --git a/docshell/test/browser/browser_bug655273.js b/docshell/test/browser/browser_bug655273.js
new file mode 100644
index 0000000000..3974d2d86a
--- /dev/null
+++ b/docshell/test/browser/browser_bug655273.js
@@ -0,0 +1,55 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Test for Bug 655273. Make sure that after changing the URI via
+ * history.pushState, the resulting SHEntry has the same title as our old
+ * SHEntry.
+ **/
+
+add_task(async function test() {
+ waitForExplicitFinish();
+
+ await BrowserTestUtils.withNewTab(
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ { gBrowser, url: "http://example.com" },
+ async function(browser) {
+ if (!SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ await SpecialPowers.spawn(browser, [], async function() {
+ let cw = content;
+ let oldTitle = cw.document.title;
+ ok(oldTitle, "Content window should initially have a title.");
+ cw.history.pushState("", "", "new_page");
+
+ let shistory = cw.docShell.QueryInterface(Ci.nsIWebNavigation)
+ .sessionHistory;
+
+ is(
+ shistory.legacySHistory.getEntryAtIndex(shistory.index).title,
+ oldTitle,
+ "SHEntry title after pushstate."
+ );
+ });
+
+ return;
+ }
+
+ let bc = browser.browsingContext;
+ let oldTitle = browser.browsingContext.currentWindowGlobal.documentTitle;
+ ok(oldTitle, "Content window should initially have a title.");
+ SpecialPowers.spawn(browser, [], async function() {
+ content.history.pushState("", "", "new_page");
+ });
+
+ let shistory = bc.sessionHistory;
+ await SHListener.waitForHistory(shistory, SHListener.NewEntry);
+
+ is(
+ shistory.getEntryAtIndex(shistory.index).title,
+ oldTitle,
+ "SHEntry title after pushstate."
+ );
+ }
+ );
+});
diff --git a/docshell/test/browser/browser_bug670318.js b/docshell/test/browser/browser_bug670318.js
new file mode 100644
index 0000000000..cb92ee4159
--- /dev/null
+++ b/docshell/test/browser/browser_bug670318.js
@@ -0,0 +1,144 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Test for Bug 670318
+ *
+ * When LoadEntry() is called on a browser that has multiple duplicate history
+ * entries, history.index can end up out of range (>= history.count).
+ */
+
+const URL =
+ "http://mochi.test:8888/browser/docshell/test/browser/file_bug670318.html";
+
+add_task(async function test() {
+ await BrowserTestUtils.withNewTab(
+ { gBrowser, url: "about:blank" },
+ async function(browser) {
+ if (!SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ await ContentTask.spawn(browser, URL, async function(URL) {
+ let history = docShell.QueryInterface(Ci.nsIWebNavigation)
+ .sessionHistory;
+ let count = 0;
+
+ let testDone = {};
+ testDone.promise = new Promise(resolve => {
+ testDone.resolve = resolve;
+ });
+
+ // Since listener implements nsISupportsWeakReference, we are
+ // responsible for keeping it alive so that the GC doesn't clear
+ // it before the test completes. We do this by anchoring the listener
+ // to the message manager, and clearing it just before the test
+ // completes.
+ this._testListener = {
+ owner: this,
+ OnHistoryNewEntry(aNewURI) {
+ info("OnHistoryNewEntry " + aNewURI.spec + ", " + count);
+ if (aNewURI.spec == URL && 5 == ++count) {
+ addEventListener(
+ "load",
+ function onLoad() {
+ Assert.ok(
+ history.index < history.count,
+ "history.index is valid"
+ );
+ testDone.resolve();
+ },
+ { capture: true, once: true }
+ );
+
+ history.legacySHistory.removeSHistoryListener(
+ this.owner._testListener
+ );
+ delete this.owner._testListener;
+ this.owner = null;
+ content.setTimeout(() => {
+ content.location.reload();
+ }, 0);
+ }
+ },
+
+ OnHistoryReload: () => true,
+ OnHistoryGotoIndex: () => {},
+ OnHistoryPurge: () => {},
+ OnHistoryReplaceEntry: () => {
+ // The initial load of about:blank causes a transient entry to be
+ // created, so our first navigation to a real page is a replace
+ // instead of a new entry.
+ ++count;
+ },
+
+ QueryInterface: ChromeUtils.generateQI([
+ "nsISHistoryListener",
+ "nsISupportsWeakReference",
+ ]),
+ };
+
+ history.legacySHistory.addSHistoryListener(this._testListener);
+ content.location = URL;
+
+ await testDone.promise;
+ });
+
+ return;
+ }
+
+ let history = browser.browsingContext.sessionHistory;
+ let count = 0;
+
+ let testDone = {};
+ testDone.promise = new Promise(resolve => {
+ testDone.resolve = resolve;
+ });
+
+ let listener = {
+ async OnHistoryNewEntry(aNewURI) {
+ if (aNewURI.spec == URL && 5 == ++count) {
+ history.removeSHistoryListener(listener);
+ await ContentTask.spawn(browser, null, () => {
+ return new Promise(resolve => {
+ addEventListener(
+ "load",
+ evt => {
+ let history = docShell.QueryInterface(Ci.nsIWebNavigation)
+ .sessionHistory;
+ Assert.ok(
+ history.index < history.count,
+ "history.index is valid"
+ );
+ resolve();
+ },
+ { capture: true, once: true }
+ );
+
+ content.location.reload();
+ });
+ });
+ testDone.resolve();
+ }
+ },
+
+ OnHistoryReload: () => true,
+ OnHistoryGotoIndex: () => {},
+ OnHistoryPurge: () => {},
+ OnHistoryReplaceEntry: () => {
+ // The initial load of about:blank causes a transient entry to be
+ // created, so our first navigation to a real page is a replace
+ // instead of a new entry.
+ ++count;
+ },
+
+ QueryInterface: ChromeUtils.generateQI([
+ "nsISHistoryListener",
+ "nsISupportsWeakReference",
+ ]),
+ };
+
+ history.addSHistoryListener(listener);
+ BrowserTestUtils.loadURI(browser, URL);
+
+ await testDone.promise;
+ }
+ );
+});
diff --git a/docshell/test/browser/browser_bug673087-1.js b/docshell/test/browser/browser_bug673087-1.js
new file mode 100644
index 0000000000..427b246d76
--- /dev/null
+++ b/docshell/test/browser/browser_bug673087-1.js
@@ -0,0 +1,46 @@
+function test() {
+ var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+ runCharsetTest(
+ rootDir + "file_bug673087-1.html",
+ afterOpen,
+ afterChangeCharset
+ );
+}
+
+function afterOpen() {
+ is(
+ content.document.documentElement.textContent.indexOf("\u00A4"),
+ 151,
+ "Parent doc should be windows-1252 initially"
+ );
+
+ is(
+ content.frames[0].document.documentElement.textContent.indexOf("\u00A4"),
+ 95,
+ "Child doc should be windows-1252 initially"
+ );
+}
+
+function afterChangeCharset() {
+ is(
+ content.document.documentElement.textContent.indexOf("\u3042"),
+ 151,
+ "Parent doc should decode as EUC-JP subsequently"
+ );
+ is(
+ content.frames[0].document.documentElement.textContent.indexOf("\u3042"),
+ 95,
+ "Child doc should decode as EUC-JP subsequently"
+ );
+
+ is(
+ content.document.characterSet,
+ "EUC-JP",
+ "Parent doc should report EUC-JP subsequently"
+ );
+ is(
+ content.frames[0].document.characterSet,
+ "EUC-JP",
+ "Child doc should report EUC-JP subsequently"
+ );
+}
diff --git a/docshell/test/browser/browser_bug673087-2.js b/docshell/test/browser/browser_bug673087-2.js
new file mode 100644
index 0000000000..13a7a2a82c
--- /dev/null
+++ b/docshell/test/browser/browser_bug673087-2.js
@@ -0,0 +1,36 @@
+function test() {
+ var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+ runCharsetTest(
+ rootDir + "file_bug673087-2.html",
+ afterOpen,
+ afterChangeCharset
+ );
+}
+
+function afterOpen() {
+ is(
+ content.document.documentElement.textContent.indexOf("\uFFFD"),
+ 0,
+ "Doc should decode as replacement initially"
+ );
+
+ is(
+ content.document.characterSet,
+ "replacement",
+ "Doc should report replacement initially"
+ );
+}
+
+function afterChangeCharset() {
+ is(
+ content.document.documentElement.textContent.indexOf("\uFFFD"),
+ 0,
+ "Doc should decode as replacement subsequently"
+ );
+
+ is(
+ content.document.characterSet,
+ "replacement",
+ "Doc should report replacement subsequently"
+ );
+}
diff --git a/docshell/test/browser/browser_bug673467.js b/docshell/test/browser/browser_bug673467.js
new file mode 100644
index 0000000000..182cc0ee80
--- /dev/null
+++ b/docshell/test/browser/browser_bug673467.js
@@ -0,0 +1,62 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test for bug 673467. In a new tab, load a page which inserts a new iframe
+// before the load and then sets its location during the load. This should
+// create just one SHEntry.
+
+var doc =
+ "data:text/html,<html><body onload='load()'>" +
+ "<script>" +
+ " var iframe = document.createElement('iframe');" +
+ " iframe.id = 'iframe';" +
+ " document.documentElement.appendChild(iframe);" +
+ " function load() {" +
+ " iframe.src = 'data:text/html,Hello!';" +
+ " }" +
+ "</script>" +
+ "</body></html>";
+
+function test() {
+ waitForExplicitFinish();
+
+ let taskFinished;
+
+ let tab = BrowserTestUtils.addTab(gBrowser, doc, {}, tab => {
+ taskFinished = ContentTask.spawn(tab.linkedBrowser, null, () => {
+ return new Promise(resolve => {
+ addEventListener(
+ "load",
+ function() {
+ // The main page has loaded. Now wait for the iframe to load.
+ let iframe = content.document.getElementById("iframe");
+ iframe.addEventListener(
+ "load",
+ function listener(aEvent) {
+ // Wait for the iframe to load the new document, not about:blank.
+ if (!iframe.src) {
+ return;
+ }
+
+ iframe.removeEventListener("load", listener, true);
+ let shistory = content.docShell.QueryInterface(
+ Ci.nsIWebNavigation
+ ).sessionHistory;
+
+ Assert.equal(shistory.count, 1, "shistory count should be 1.");
+ resolve();
+ },
+ true
+ );
+ },
+ true
+ );
+ });
+ });
+ });
+
+ taskFinished.then(() => {
+ gBrowser.removeTab(tab);
+ finish();
+ });
+}
diff --git a/docshell/test/browser/browser_bug852909.js b/docshell/test/browser/browser_bug852909.js
new file mode 100644
index 0000000000..108baa0626
--- /dev/null
+++ b/docshell/test/browser/browser_bug852909.js
@@ -0,0 +1,35 @@
+var rootDir = "http://mochi.test:8888/browser/docshell/test/browser/";
+
+function test() {
+ waitForExplicitFinish();
+
+ gBrowser.selectedTab = BrowserTestUtils.addTab(
+ gBrowser,
+ rootDir + "file_bug852909.png"
+ );
+ BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(image);
+}
+
+function image(event) {
+ ok(
+ !gBrowser.selectedTab.mayEnableCharacterEncodingMenu,
+ "Docshell should say the menu should be disabled for images."
+ );
+
+ gBrowser.removeCurrentTab();
+ gBrowser.selectedTab = BrowserTestUtils.addTab(
+ gBrowser,
+ rootDir + "file_bug852909.pdf"
+ );
+ BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(pdf);
+}
+
+function pdf(event) {
+ ok(
+ !gBrowser.selectedTab.mayEnableCharacterEncodingMenu,
+ "Docshell should say the menu should be disabled for PDF.js."
+ );
+
+ gBrowser.removeCurrentTab();
+ finish();
+}
diff --git a/docshell/test/browser/browser_bug92473.js b/docshell/test/browser/browser_bug92473.js
new file mode 100644
index 0000000000..7e386f5ee9
--- /dev/null
+++ b/docshell/test/browser/browser_bug92473.js
@@ -0,0 +1,70 @@
+/* The test text as octets for reference
+ * %83%86%83%6a%83%52%81%5b%83%68%82%cd%81%41%82%b7%82%d7%82%c4%82%cc%95%b6%8e%9a%82%c9%8c%c5%97%4c%82%cc%94%d4%8d%86%82%f0%95%74%97%5e%82%b5%82%dc%82%b7
+ */
+
+function testContent(text) {
+ return SpecialPowers.spawn(gBrowser.selectedBrowser, [text], text => {
+ Assert.equal(
+ content.document.getElementById("testpar").innerHTML,
+ text,
+ "<p> contains expected text"
+ );
+ Assert.equal(
+ content.document.getElementById("testtextarea").innerHTML,
+ text,
+ "<textarea> contains expected text"
+ );
+ Assert.equal(
+ content.document.getElementById("testinput").value,
+ text,
+ "<input> contains expected text"
+ );
+ });
+}
+
+function afterOpen() {
+ BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(
+ afterChangeCharset
+ );
+
+ /* The test text decoded incorrectly as Windows-1251. This is the "right" wrong
+ text; anything else is unexpected. */
+ const wrongText =
+ "\u0453\u2020\u0453\u006A\u0453\u0052\u0403\u005B\u0453\u0068\u201A\u041D\u0403\u0041\u201A\u00B7\u201A\u0427\u201A\u0414\u201A\u041C\u2022\u00B6\u040B\u0459\u201A\u0419\u040A\u0415\u2014\u004C\u201A\u041C\u201D\u0424\u040C\u2020\u201A\u0440\u2022\u0074\u2014\u005E\u201A\u00B5\u201A\u042C\u201A\u00B7";
+
+ /* Test that the content on load is the expected wrong decoding */
+ testContent(wrongText).then(() => {
+ BrowserForceEncodingDetection();
+ });
+}
+
+function afterChangeCharset() {
+ /* The test text decoded correctly as Shift_JIS */
+ const rightText =
+ "\u30E6\u30CB\u30B3\u30FC\u30C9\u306F\u3001\u3059\u3079\u3066\u306E\u6587\u5B57\u306B\u56FA\u6709\u306E\u756A\u53F7\u3092\u4ED8\u4E0E\u3057\u307E\u3059";
+
+ /* test that the content is decoded correctly */
+ testContent(rightText).then(() => {
+ gBrowser.removeCurrentTab();
+ finish();
+ });
+}
+
+function test() {
+ waitForExplicitFinish();
+
+ // Get the local directory. This needs to be a file: URI because chrome: URIs
+ // are always UTF-8 (bug 617339) and we are testing decoding from other
+ // charsets.
+ var jar = getJar(getRootDirectory(gTestPath));
+ var dir = jar
+ ? extractJarToTmp(jar)
+ : getChromeDir(getResolvedURI(gTestPath));
+ var rootDir = Services.io.newFileURI(dir).spec;
+
+ gBrowser.selectedTab = BrowserTestUtils.addTab(
+ gBrowser,
+ rootDir + "test-form_sjis.html"
+ );
+ BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(afterOpen);
+}
diff --git a/docshell/test/browser/browser_click_link_within_view_source.js b/docshell/test/browser/browser_click_link_within_view_source.js
new file mode 100644
index 0000000000..5c298a56c6
--- /dev/null
+++ b/docshell/test/browser/browser_click_link_within_view_source.js
@@ -0,0 +1,78 @@
+"use strict";
+
+/**
+ * Test for Bug 1359204
+ *
+ * Loading a local file, then view-source on that file. Make sure that
+ * clicking a link within that view-source page is not blocked by security checks.
+ */
+
+add_task(async function test_click_link_within_view_source() {
+ let TEST_FILE = "file_click_link_within_view_source.html";
+ let TEST_FILE_URI = getChromeDir(getResolvedURI(gTestPath));
+ TEST_FILE_URI.append(TEST_FILE);
+ TEST_FILE_URI = Services.io.newFileURI(TEST_FILE_URI).spec;
+
+ let DUMMY_FILE = "dummy_page.html";
+ let DUMMY_FILE_URI = getChromeDir(getResolvedURI(gTestPath));
+ DUMMY_FILE_URI.append(DUMMY_FILE);
+ DUMMY_FILE_URI = Services.io.newFileURI(DUMMY_FILE_URI).spec;
+
+ await BrowserTestUtils.withNewTab(TEST_FILE_URI, async function(aBrowser) {
+ let tabSpec = gBrowser.selectedBrowser.currentURI.spec;
+ info("loading: " + tabSpec);
+ ok(
+ tabSpec.startsWith("file://") && tabSpec.endsWith(TEST_FILE),
+ "sanity check to make sure html loaded"
+ );
+
+ info("click view-source of html");
+ let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser);
+ document.getElementById("View:PageSource").doCommand();
+
+ let tab = await tabPromise;
+ tabSpec = gBrowser.selectedBrowser.currentURI.spec;
+ info("loading: " + tabSpec);
+ ok(
+ tabSpec.startsWith("view-source:file://") && tabSpec.endsWith(TEST_FILE),
+ "loading view-source of html succeeded"
+ );
+
+ info("click testlink within view-source page");
+ let loadPromise = BrowserTestUtils.browserLoaded(
+ tab.linkedBrowser,
+ false,
+ url => url.endsWith("dummy_page.html")
+ );
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function() {
+ if (content.document.readyState != "complete") {
+ await ContentTaskUtils.waitForEvent(
+ content.document,
+ "readystatechange",
+ false,
+ () => content.document.readyState == "complete"
+ );
+ }
+ // document.getElementById() does not work on a view-source page, hence we use document.links
+ let linksOnPage = content.document.links;
+ is(
+ linksOnPage.length,
+ 1,
+ "sanity check: make sure only one link is present on page"
+ );
+ let myLink = content.document.links[0];
+ myLink.click();
+ });
+
+ await loadPromise;
+
+ tabSpec = gBrowser.selectedBrowser.currentURI.spec;
+ info("loading: " + tabSpec);
+ ok(
+ tabSpec.startsWith("view-source:file://") && tabSpec.endsWith(DUMMY_FILE),
+ "loading view-source of html succeeded"
+ );
+
+ BrowserTestUtils.removeTab(tab);
+ });
+});
diff --git a/docshell/test/browser/browser_cross_process_csp_inheritance.js b/docshell/test/browser/browser_cross_process_csp_inheritance.js
new file mode 100644
index 0000000000..504700e6c4
--- /dev/null
+++ b/docshell/test/browser/browser_cross_process_csp_inheritance.js
@@ -0,0 +1,125 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com"
+);
+const TEST_URI = TEST_PATH + "file_cross_process_csp_inheritance.html";
+const DATA_URI =
+ "data:text/html,<html>test-same-diff-process-csp-inhertiance</html>";
+
+const FISSION_ENABLED = SpecialPowers.useRemoteSubframes;
+
+function getCurrentPID(aBrowser) {
+ return SpecialPowers.spawn(aBrowser, [], () => {
+ return Services.appinfo.processID;
+ });
+}
+
+function getCurrentURI(aBrowser) {
+ return SpecialPowers.spawn(aBrowser, [], () => {
+ let channel = content.docShell.currentDocumentChannel;
+ return channel.URI.asciiSpec;
+ });
+}
+
+function verifyResult(
+ aTestName,
+ aBrowser,
+ aDataURI,
+ aPID,
+ aSamePID,
+ aFissionEnabled
+) {
+ return SpecialPowers.spawn(
+ aBrowser,
+ [{ aTestName, aDataURI, aPID, aSamePID, aFissionEnabled }],
+ async function({ aTestName, aDataURI, aPID, aSamePID, aFissionEnabled }) {
+ // sanity, to make sure the correct URI was loaded
+ let channel = content.docShell.currentDocumentChannel;
+ is(
+ channel.URI.asciiSpec,
+ aDataURI,
+ aTestName + ": correct data uri loaded"
+ );
+
+ // check that the process ID is the same/different when opening the new tab
+ let pid = Services.appinfo.processID;
+ if (aSamePID) {
+ is(pid, aPID, aTestName + ": process ID needs to be identical");
+ } else if (aFissionEnabled) {
+ // TODO: Fission discards dom.noopener.newprocess.enabled and puts
+ // data: URIs in the same process. Unfortunately todo_isnot is not
+ // defined in that scope, hence we have to use a workaround.
+ todo(
+ false,
+ pid == aPID,
+ ": process ID needs to be different in fission"
+ );
+ } else {
+ isnot(pid, aPID, aTestName + ": process ID needs to be different");
+ }
+
+ // finally, evaluate that the CSP was set.
+ let cspOBJ = JSON.parse(content.document.cspJSON);
+ let policies = cspOBJ["csp-policies"];
+ is(policies.length, 1, "should be one policy");
+ let policy = policies[0];
+ is(
+ policy["script-src"],
+ "'none'",
+ aTestName + ": script-src directive matches"
+ );
+ }
+ );
+}
+
+async function simulateCspInheritanceForNewTab(aTestName, aSamePID) {
+ await BrowserTestUtils.withNewTab(TEST_URI, async function(browser) {
+ // do some sanity checks
+ let currentURI = await getCurrentURI(gBrowser.selectedBrowser);
+ is(currentURI, TEST_URI, aTestName + ": correct test uri loaded");
+
+ let pid = await getCurrentPID(gBrowser.selectedBrowser);
+ let loadPromise = BrowserTestUtils.waitForNewTab(gBrowser, DATA_URI, true);
+ // simulate click
+ BrowserTestUtils.synthesizeMouseAtCenter(
+ "#testLink",
+ {},
+ gBrowser.selectedBrowser
+ );
+ let tab = await loadPromise;
+ gBrowser.selectTabAtIndex(2);
+ await verifyResult(
+ aTestName,
+ gBrowser.selectedBrowser,
+ DATA_URI,
+ pid,
+ aSamePID,
+ FISSION_ENABLED
+ );
+ await BrowserTestUtils.removeTab(tab);
+ });
+}
+
+add_task(async function test_csp_inheritance_diff_process() {
+ // forcing the new data: URI load to happen in a *new* process by flipping the pref
+ // to force <a rel="noopener" ...> to be loaded in a new process.
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.noopener.newprocess.enabled", true]],
+ });
+ await simulateCspInheritanceForNewTab("diff-process-inheritance", false);
+});
+
+add_task(async function test_csp_inheritance_same_process() {
+ // forcing the new data: URI load to happen in a *same* process by resetting the pref
+ // and loaded <a rel="noopener" ...> in the *same* process.
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.noopener.newprocess.enabled", false]],
+ });
+ await simulateCspInheritanceForNewTab("same-process-inheritance", true);
+});
diff --git a/docshell/test/browser/browser_csp_sandbox_no_script_js_uri.js b/docshell/test/browser/browser_csp_sandbox_no_script_js_uri.js
new file mode 100644
index 0000000000..d0b92084ec
--- /dev/null
+++ b/docshell/test/browser/browser_csp_sandbox_no_script_js_uri.js
@@ -0,0 +1,55 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+
+/**
+ * Test that javascript URIs in CSP-sandboxed contexts can't be used to bypass
+ * script restrictions.
+ */
+add_task(async function test_csp_sandbox_no_script_js_uri() {
+ await BrowserTestUtils.withNewTab(
+ TEST_PATH + "dummy_page.html",
+ async browser => {
+ info("Register observer and wait for javascript-uri-blocked message.");
+ let observerPromise = SpecialPowers.spawn(browser, [], () => {
+ return new Promise(resolve => {
+ SpecialPowers.addObserver(function obs(subject) {
+ ok(
+ subject == content,
+ "Should block script spawned via javascript uri"
+ );
+ SpecialPowers.removeObserver(
+ obs,
+ "javascript-uri-blocked-by-sandbox"
+ );
+ resolve();
+ }, "javascript-uri-blocked-by-sandbox");
+ });
+ });
+
+ info("Spawn csp-sandboxed iframe with javascript URI");
+ let frameBC = await SpecialPowers.spawn(
+ browser,
+ [TEST_PATH + "file_csp_sandbox_no_script_js_uri.html"],
+ async url => {
+ let frame = content.document.createElement("iframe");
+ let loadPromise = ContentTaskUtils.waitForEvent(frame, "load", true);
+ frame.src = url;
+ content.document.body.appendChild(frame);
+ await loadPromise;
+ return frame.browsingContext;
+ }
+ );
+
+ info("Click javascript URI link in iframe");
+ BrowserTestUtils.synthesizeMouseAtCenter("a", {}, frameBC);
+ await observerPromise;
+ }
+ );
+});
diff --git a/docshell/test/browser/browser_csp_uir.js b/docshell/test/browser/browser_csp_uir.js
new file mode 100644
index 0000000000..9eb5ec1a89
--- /dev/null
+++ b/docshell/test/browser/browser_csp_uir.js
@@ -0,0 +1,89 @@
+"use strict";
+
+const TEST_PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com"
+);
+const TEST_URI = TEST_PATH + "file_csp_uir.html"; // important to be http: to test upgrade-insecure-requests
+const RESULT_URI =
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ TEST_PATH.replace("http://", "https://") + "file_csp_uir_dummy.html";
+
+function verifyCSP(aTestName, aBrowser, aResultURI) {
+ return SpecialPowers.spawn(
+ aBrowser,
+ [{ aTestName, aResultURI }],
+ async function({ aTestName, aResultURI }) {
+ let channel = content.docShell.currentDocumentChannel;
+ is(channel.URI.asciiSpec, aResultURI, "testing CSP for " + aTestName);
+ }
+ );
+}
+
+add_task(async function test_csp_inheritance_regular_click() {
+ await BrowserTestUtils.withNewTab(TEST_URI, async function(browser) {
+ let loadPromise = BrowserTestUtils.browserLoaded(
+ browser,
+ false,
+ RESULT_URI
+ );
+ // set the data href + simulate click
+ BrowserTestUtils.synthesizeMouseAtCenter(
+ "#testlink",
+ {},
+ gBrowser.selectedBrowser
+ );
+ await loadPromise;
+ await verifyCSP("click()", gBrowser.selectedBrowser, RESULT_URI);
+ });
+});
+
+add_task(async function test_csp_inheritance_ctrl_click() {
+ await BrowserTestUtils.withNewTab(TEST_URI, async function(browser) {
+ let loadPromise = BrowserTestUtils.waitForNewTab(
+ gBrowser,
+ RESULT_URI,
+ true
+ );
+ // set the data href + simulate ctrl+click
+ BrowserTestUtils.synthesizeMouseAtCenter(
+ "#testlink",
+ { ctrlKey: true, metaKey: true },
+ gBrowser.selectedBrowser
+ );
+ let tab = await loadPromise;
+ gBrowser.selectTabAtIndex(2);
+ await verifyCSP("ctrl-click()", gBrowser.selectedBrowser, RESULT_URI);
+ await BrowserTestUtils.removeTab(tab);
+ });
+});
+
+add_task(
+ async function test_csp_inheritance_right_click_open_link_in_new_tab() {
+ await BrowserTestUtils.withNewTab(TEST_URI, async function(browser) {
+ let loadPromise = BrowserTestUtils.waitForNewTab(gBrowser, RESULT_URI);
+ // set the data href + simulate right-click open link in tab
+ BrowserTestUtils.waitForEvent(document, "popupshown", false, event => {
+ // These are operations that must be executed synchronously with the event.
+ document.getElementById("context-openlinkintab").doCommand();
+ event.target.hidePopup();
+ return true;
+ });
+ BrowserTestUtils.synthesizeMouseAtCenter(
+ "#testlink",
+ { type: "contextmenu", button: 2 },
+ gBrowser.selectedBrowser
+ );
+
+ let tab = await loadPromise;
+ gBrowser.selectTabAtIndex(2);
+ await verifyCSP(
+ "right-click-open-in-new-tab()",
+ gBrowser.selectedBrowser,
+ RESULT_URI
+ );
+ await BrowserTestUtils.removeTab(tab);
+ });
+ }
+);
diff --git a/docshell/test/browser/browser_dataURI_unique_opaque_origin.js b/docshell/test/browser/browser_dataURI_unique_opaque_origin.js
new file mode 100644
index 0000000000..76a8eb4251
--- /dev/null
+++ b/docshell/test/browser/browser_dataURI_unique_opaque_origin.js
@@ -0,0 +1,30 @@
+add_task(async function test_dataURI_unique_opaque_origin() {
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ let tab = BrowserTestUtils.addTab(gBrowser, "http://example.com");
+ let browser = tab.linkedBrowser;
+ await BrowserTestUtils.browserLoaded(browser);
+
+ let pagePrincipal = browser.contentPrincipal;
+ info("pagePrincial " + pagePrincipal.origin);
+
+ BrowserTestUtils.loadURI(browser, "data:text/html,hi");
+ await BrowserTestUtils.browserLoaded(browser);
+
+ await SpecialPowers.spawn(
+ browser,
+ [{ principal: pagePrincipal }],
+ async function(args) {
+ info("data URI principal: " + content.document.nodePrincipal.origin);
+ Assert.ok(
+ content.document.nodePrincipal.isNullPrincipal,
+ "data: URI should have NullPrincipal."
+ );
+ Assert.ok(
+ !content.document.nodePrincipal.equals(args.principal),
+ "data: URI should have unique opaque origin."
+ );
+ }
+ );
+
+ gBrowser.removeTab(tab);
+});
diff --git a/docshell/test/browser/browser_data_load_inherit_csp.js b/docshell/test/browser/browser_data_load_inherit_csp.js
new file mode 100644
index 0000000000..8ad05ef7e3
--- /dev/null
+++ b/docshell/test/browser/browser_data_load_inherit_csp.js
@@ -0,0 +1,110 @@
+"use strict";
+
+const TEST_PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com"
+);
+const HTML_URI = TEST_PATH + "file_data_load_inherit_csp.html";
+const DATA_URI = "data:text/html;html,<html><body>foo</body></html>";
+
+function setDataHrefOnLink(aBrowser, aDataURI) {
+ return SpecialPowers.spawn(aBrowser, [aDataURI], function(uri) {
+ let link = content.document.getElementById("testlink");
+ link.href = uri;
+ });
+}
+
+function verifyCSP(aTestName, aBrowser, aDataURI) {
+ return SpecialPowers.spawn(
+ aBrowser,
+ [{ aTestName, aDataURI }],
+ async function({ aTestName, aDataURI }) {
+ let channel = content.docShell.currentDocumentChannel;
+ is(channel.URI.spec, aDataURI, "testing CSP for " + aTestName);
+ let cspJSON = content.document.cspJSON;
+ let cspOBJ = JSON.parse(cspJSON);
+ let policies = cspOBJ["csp-policies"];
+ is(policies.length, 1, "should be one policy");
+ let policy = policies[0];
+ is(
+ policy["script-src"],
+ "'unsafe-inline'",
+ "script-src directive matches"
+ );
+ }
+ );
+}
+
+add_setup(async function() {
+ // allow top level data: URI navigations, otherwise clicking data: link fails
+ await SpecialPowers.pushPrefEnv({
+ set: [["security.data_uri.block_toplevel_data_uri_navigations", false]],
+ });
+});
+
+add_task(async function test_data_csp_inheritance_regular_click() {
+ await BrowserTestUtils.withNewTab(HTML_URI, async function(browser) {
+ let loadPromise = BrowserTestUtils.browserLoaded(browser, false, DATA_URI);
+ // set the data href + simulate click
+ await setDataHrefOnLink(gBrowser.selectedBrowser, DATA_URI);
+ BrowserTestUtils.synthesizeMouseAtCenter(
+ "#testlink",
+ {},
+ gBrowser.selectedBrowser
+ );
+ await loadPromise;
+ await verifyCSP("click()", gBrowser.selectedBrowser, DATA_URI);
+ });
+});
+
+add_task(async function test_data_csp_inheritance_ctrl_click() {
+ await BrowserTestUtils.withNewTab(HTML_URI, async function(browser) {
+ let loadPromise = BrowserTestUtils.waitForNewTab(gBrowser, DATA_URI, true);
+ // set the data href + simulate ctrl+click
+ await setDataHrefOnLink(gBrowser.selectedBrowser, DATA_URI);
+ BrowserTestUtils.synthesizeMouseAtCenter(
+ "#testlink",
+ { ctrlKey: true, metaKey: true },
+ gBrowser.selectedBrowser
+ );
+ let tab = await loadPromise;
+ gBrowser.selectTabAtIndex(2);
+ await verifyCSP("ctrl-click()", gBrowser.selectedBrowser, DATA_URI);
+ await BrowserTestUtils.removeTab(tab);
+ });
+});
+
+add_task(
+ async function test_data_csp_inheritance_right_click_open_link_in_new_tab() {
+ await BrowserTestUtils.withNewTab(HTML_URI, async function(browser) {
+ let loadPromise = BrowserTestUtils.waitForNewTab(
+ gBrowser,
+ DATA_URI,
+ true
+ );
+ // set the data href + simulate right-click open link in tab
+ await setDataHrefOnLink(gBrowser.selectedBrowser, DATA_URI);
+ BrowserTestUtils.waitForEvent(document, "popupshown", false, event => {
+ // These are operations that must be executed synchronously with the event.
+ document.getElementById("context-openlinkintab").doCommand();
+ event.target.hidePopup();
+ return true;
+ });
+ BrowserTestUtils.synthesizeMouseAtCenter(
+ "#testlink",
+ { type: "contextmenu", button: 2 },
+ gBrowser.selectedBrowser
+ );
+
+ let tab = await loadPromise;
+ gBrowser.selectTabAtIndex(2);
+ await verifyCSP(
+ "right-click-open-in-new-tab()",
+ gBrowser.selectedBrowser,
+ DATA_URI
+ );
+ await BrowserTestUtils.removeTab(tab);
+ });
+ }
+);
diff --git a/docshell/test/browser/browser_fall_back_to_https.js b/docshell/test/browser/browser_fall_back_to_https.js
new file mode 100644
index 0000000000..b84780eec1
--- /dev/null
+++ b/docshell/test/browser/browser_fall_back_to_https.js
@@ -0,0 +1,73 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/*
+ * This test is for bug 1002724.
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=1002724
+ *
+ * When a user enters a host name or IP address in the URL bar, "http" is
+ * assumed. If the host rejects connections on port 80, we try HTTPS as a
+ * fall-back and only fail if HTTPS connection fails.
+ *
+ * This tests that when a user enters "example.com", it attempts to load
+ * http://example.com:80 (not rejected), and when trying secureonly.example.com
+ * (which rejects connections on port 80), it fails then loads
+ * https://secureonly.example.com:443 instead.
+ */
+
+const { UrlbarTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/UrlbarTestUtils.sys.mjs"
+);
+
+const bug1002724_tests = [
+ {
+ original: "example.com",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ expected: "http://example.com",
+ explanation: "Should load HTTP version of example.com",
+ },
+ {
+ original: "secureonly.example.com",
+ expected: "https://secureonly.example.com",
+ explanation:
+ "Should reject secureonly.example.com on HTTP but load the HTTPS version",
+ },
+];
+
+async function test_one(test_obj) {
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "about:blank"
+ );
+ gURLBar.focus();
+ gURLBar.value = test_obj.original;
+
+ let loadPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser, false);
+ EventUtils.synthesizeKey("KEY_Enter");
+ await loadPromise;
+
+ ok(
+ tab.linkedBrowser.currentURI.spec.startsWith(test_obj.expected),
+ test_obj.explanation
+ );
+
+ BrowserTestUtils.removeTab(tab);
+}
+
+add_task(async function test_bug1002724() {
+ await SpecialPowers.pushPrefEnv(
+ // Disable HSTS preload just in case.
+ {
+ set: [
+ ["network.stricttransportsecurity.preloadlist", false],
+ ["network.dns.native-is-localhost", true],
+ ],
+ }
+ );
+
+ for (let test of bug1002724_tests) {
+ await test_one(test);
+ }
+});
diff --git a/docshell/test/browser/browser_fission_maxOrigins.js b/docshell/test/browser/browser_fission_maxOrigins.js
new file mode 100644
index 0000000000..b8efbe0ca1
--- /dev/null
+++ b/docshell/test/browser/browser_fission_maxOrigins.js
@@ -0,0 +1,222 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+SimpleTest.requestFlakyTimeout("Need to test expiration timeout");
+
+function delay(msec) {
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ return new Promise(resolve => setTimeout(resolve, msec));
+}
+
+function promiseIdle() {
+ return new Promise(resolve => {
+ Services.tm.idleDispatchToMainThread(resolve);
+ });
+}
+
+const ORIGIN_CAP = 5;
+const SLIDING_WINDOW_MS = 5000;
+
+const PREF_ORIGIN_CAP = "fission.experiment.max-origins.origin-cap";
+const PREF_SLIDING_WINDOW_MS =
+ "fission.experiment.max-origins.sliding-window-ms";
+const PREF_QUALIFIED = "fission.experiment.max-origins.qualified";
+const PREF_LAST_QUALIFIED = "fission.experiment.max-origins.last-qualified";
+const PREF_LAST_DISQUALIFIED =
+ "fission.experiment.max-origins.last-disqualified";
+
+const SITE_ORIGINS = [
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.org/",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.net/",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.tw/",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.cn/",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.fi/",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.in/",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.lk/",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://w3c-test.org/",
+ "https://www.mozilla.org/",
+];
+
+function openTab(url) {
+ return BrowserTestUtils.openNewForegroundTab({
+ gBrowser,
+ url,
+ waitForStateStop: true,
+ });
+}
+
+async function assertQualified() {
+ // The unique origin calculation runs from an idle task, so make sure
+ // the queued idle task has had a chance to run.
+ await promiseIdle();
+
+ // Make sure the clock has advanced since the qualification timestamp
+ // was recorded.
+ await delay(1);
+
+ let qualified = Services.prefs.getBoolPref(PREF_QUALIFIED);
+ let lastQualified = Services.prefs.getIntPref(PREF_LAST_QUALIFIED);
+ let lastDisqualified = Services.prefs.getIntPref(PREF_LAST_DISQUALIFIED);
+ let currentTime = Date.now() / 1000;
+
+ ok(qualified, "Should be qualified");
+ ok(
+ lastQualified > 0,
+ `Last qualified timestamp (${lastQualified}) should be greater than 0`
+ );
+ ok(
+ lastQualified < currentTime,
+ `Last qualified timestamp (${lastQualified}) should be less than the current time (${currentTime})`
+ );
+ ok(
+ lastQualified > lastDisqualified,
+ `Last qualified timestamp (${lastQualified}) should be after the last disqualified time (${lastDisqualified})`
+ );
+
+ ok(
+ lastDisqualified < currentTime,
+ `Last disqualified timestamp (${lastDisqualified}) should be less than the current time (${currentTime})`
+ );
+}
+
+async function assertDisqualified(opts) {
+ // The unique origin calculation runs from an idle task, so make sure
+ // the queued idle task has had a chance to run.
+ await promiseIdle();
+
+ let qualified = Services.prefs.getBoolPref(PREF_QUALIFIED);
+ let lastQualified = Services.prefs.getIntPref(PREF_LAST_QUALIFIED, 0);
+ let lastDisqualified = Services.prefs.getIntPref(PREF_LAST_DISQUALIFIED);
+ let currentTime = Date.now() / 1000;
+
+ ok(!qualified, "Should not be qualified");
+ if (!opts.initialValues) {
+ ok(
+ lastQualified > 0,
+ `Last qualified timestamp (${lastQualified}) should be greater than 0`
+ );
+ }
+ ok(
+ lastQualified < currentTime,
+ `Last qualified timestamp (${lastQualified}) should be less than the current time (${currentTime})`
+ );
+
+ ok(
+ lastDisqualified < currentTime,
+ `Last disqualified timestamp (${lastDisqualified}) should be less than the current time (${currentTime})`
+ );
+
+ ok(
+ lastDisqualified > 0,
+ `Last disqualified timestamp (${lastDisqualified}) should be greater than 0`
+ );
+
+ if (opts.qualificationPending) {
+ ok(
+ lastQualified > lastDisqualified,
+ `Last qualified timestamp (${lastQualified}) should be after the last disqualified time (${lastDisqualified})`
+ );
+ } else {
+ ok(
+ lastDisqualified > lastQualified,
+ `Last disqualified timestamp (${lastDisqualified}) should be after the last qualified time (${lastQualified})`
+ );
+ }
+}
+
+add_task(async function() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ [PREF_ORIGIN_CAP, ORIGIN_CAP],
+ [PREF_SLIDING_WINDOW_MS, SLIDING_WINDOW_MS],
+ ],
+ });
+
+ const { BrowserTelemetryUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/BrowserTelemetryUtils.sys.mjs"
+ );
+
+ // Make sure we actually record telemetry for our disqualifying origin
+ // count.
+ BrowserTelemetryUtils.min_interval = 1;
+
+ let tabs = [];
+
+ // Open one initial tab to make sure the origin counting code has had
+ // a chance to run before checking the initial state.
+ tabs.push(await openTab("http://mochi.test:8888/"));
+
+ await assertQualified();
+
+ let lastDisqualified = Services.prefs.getIntPref(PREF_LAST_DISQUALIFIED);
+ is(lastDisqualified, 0, "Last disqualification timestamp should be 0");
+
+ info(
+ `Opening ${SITE_ORIGINS.length} tabs with distinct origins to exceed the cap (${ORIGIN_CAP})`
+ );
+ ok(
+ SITE_ORIGINS.length > ORIGIN_CAP,
+ "Should have enough site origins to exceed the origin cap"
+ );
+ tabs.push(...(await Promise.all(SITE_ORIGINS.map(openTab))));
+
+ await assertDisqualified({ qualificationPending: false });
+
+ info("Close unique-origin tabs");
+ await Promise.all(tabs.map(tab => BrowserTestUtils.removeTab(tab)));
+
+ info("Open a new tab to trigger the origin count code once more");
+ tabs = [await openTab(SITE_ORIGINS[0])];
+
+ await assertDisqualified({ qualificationPending: true });
+
+ info(
+ "Wait long enough to clear the sliding window since last disqualified state"
+ );
+ await delay(SLIDING_WINDOW_MS + 1000);
+
+ info("Open a new tab to trigger the origin count code again");
+ tabs.push(await openTab(SITE_ORIGINS[0]));
+
+ await assertQualified();
+
+ info(
+ "Clear preference values and re-populate the initial value from telemetry"
+ );
+ Services.prefs.clearUserPref(PREF_QUALIFIED);
+ Services.prefs.clearUserPref(PREF_LAST_QUALIFIED);
+ Services.prefs.clearUserPref(PREF_LAST_DISQUALIFIED);
+ BrowserTelemetryUtils._checkedInitialExperimentQualification = false;
+
+ info("Open a new tab to trigger the origin count code again");
+ tabs.push(await openTab(SITE_ORIGINS[0]));
+
+ await assertDisqualified({ initialValues: true });
+
+ info(
+ "Wait long enough to clear the sliding window since last disqualified state"
+ );
+ await delay(SLIDING_WINDOW_MS + 1000);
+
+ info("Open a new tab to trigger the origin count code again");
+ tabs.push(await openTab(SITE_ORIGINS[0]));
+
+ await assertQualified();
+
+ await Promise.all(tabs.map(tab => BrowserTestUtils.removeTab(tab)));
+
+ // Clear the cached recording interval so it resets to the default
+ // value on the next call.
+ BrowserTelemetryUtils.min_interval = null;
+});
diff --git a/docshell/test/browser/browser_frameloader_swap_with_bfcache.js b/docshell/test/browser/browser_frameloader_swap_with_bfcache.js
new file mode 100644
index 0000000000..eba49064f1
--- /dev/null
+++ b/docshell/test/browser/browser_frameloader_swap_with_bfcache.js
@@ -0,0 +1,37 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+add_task(async function test() {
+ if (
+ !SpecialPowers.Services.appinfo.sessionHistoryInParent ||
+ !SpecialPowers.Services.prefs.getBoolPref("fission.bfcacheInParent")
+ ) {
+ ok(
+ true,
+ "This test is currently only for the bfcache in the parent process."
+ );
+ return;
+ }
+ const uri =
+ "data:text/html,<script>onpageshow = function(e) { document.documentElement.setAttribute('persisted', e.persisted); }; </script>";
+ let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, uri, true);
+ let browser1 = tab1.linkedBrowser;
+
+ let tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser, uri, true);
+ let browser2 = tab2.linkedBrowser;
+ BrowserTestUtils.loadURI(browser2, uri + "nextpage");
+ await BrowserTestUtils.browserLoaded(browser2, false);
+
+ gBrowser.swapBrowsersAndCloseOther(tab1, tab2);
+ is(tab1.linkedBrowser, browser1, "Tab's browser should stay the same.");
+ browser1.goBack(false);
+ await BrowserTestUtils.browserLoaded(browser1, false);
+ let persisted = await SpecialPowers.spawn(browser1, [], async function() {
+ return content.document.documentElement.getAttribute("persisted");
+ });
+
+ is(persisted, "false", "BFCache should be evicted when swapping browsers.");
+
+ BrowserTestUtils.removeTab(tab1);
+});
diff --git a/docshell/test/browser/browser_history_triggeringprincipal_viewsource.js b/docshell/test/browser/browser_history_triggeringprincipal_viewsource.js
new file mode 100644
index 0000000000..808232460c
--- /dev/null
+++ b/docshell/test/browser/browser_history_triggeringprincipal_viewsource.js
@@ -0,0 +1,88 @@
+"use strict";
+
+const TEST_PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com"
+);
+const HTML_URI = TEST_PATH + "dummy_page.html";
+const VIEW_SRC_URI = "view-source:" + HTML_URI;
+
+add_task(async function() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.navigation.requireUserInteraction", false]],
+ });
+
+ info("load baseline html in new tab");
+ await BrowserTestUtils.withNewTab(HTML_URI, async function(aBrowser) {
+ is(
+ gBrowser.selectedBrowser.currentURI.spec,
+ HTML_URI,
+ "sanity check to make sure html loaded"
+ );
+
+ info("right-click -> view-source of html");
+ let vSrcCtxtMenu = document.getElementById("contentAreaContextMenu");
+ let popupPromise = BrowserTestUtils.waitForEvent(
+ vSrcCtxtMenu,
+ "popupshown"
+ );
+ BrowserTestUtils.synthesizeMouseAtCenter(
+ "body",
+ { type: "contextmenu", button: 2 },
+ aBrowser
+ );
+ await popupPromise;
+ let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, VIEW_SRC_URI);
+ let vSrcItem = vSrcCtxtMenu.querySelector("#context-viewsource");
+ vSrcCtxtMenu.activateItem(vSrcItem);
+ let tab = await tabPromise;
+ is(
+ gBrowser.selectedBrowser.currentURI.spec,
+ VIEW_SRC_URI,
+ "loading view-source of html succeeded"
+ );
+
+ info("load html file again before going .back()");
+ let loadPromise = BrowserTestUtils.browserLoaded(
+ tab.linkedBrowser,
+ false,
+ HTML_URI
+ );
+ await SpecialPowers.spawn(tab.linkedBrowser, [HTML_URI], HTML_URI => {
+ content.document.location = HTML_URI;
+ });
+ await loadPromise;
+ is(
+ gBrowser.selectedBrowser.currentURI.spec,
+ HTML_URI,
+ "loading html another time succeeded"
+ );
+
+ info(
+ "click .back() to view-source of html again and make sure the history entry has a triggeringPrincipal"
+ );
+ let backCtxtMenu = document.getElementById("contentAreaContextMenu");
+ popupPromise = BrowserTestUtils.waitForEvent(backCtxtMenu, "popupshown");
+ BrowserTestUtils.synthesizeMouseAtCenter(
+ "body",
+ { type: "contextmenu", button: 2 },
+ aBrowser
+ );
+ await popupPromise;
+ loadPromise = BrowserTestUtils.waitForContentEvent(
+ tab.linkedBrowser,
+ "pageshow"
+ );
+ let backItem = backCtxtMenu.querySelector("#context-back");
+ backCtxtMenu.activateItem(backItem);
+ await loadPromise;
+ is(
+ gBrowser.selectedBrowser.currentURI.spec,
+ VIEW_SRC_URI,
+ "clicking .back() to view-source of html succeeded"
+ );
+
+ BrowserTestUtils.removeTab(tab);
+ });
+});
diff --git a/docshell/test/browser/browser_isInitialDocument.js b/docshell/test/browser/browser_isInitialDocument.js
new file mode 100644
index 0000000000..7b62be8f6f
--- /dev/null
+++ b/docshell/test/browser/browser_isInitialDocument.js
@@ -0,0 +1,323 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tag every new WindowGlobalParent with an expando indicating whether or not
+// they were an initial document when they were created for the duration of this
+// test.
+function wasInitialDocumentObserver(subject) {
+ subject._test_wasInitialDocument = subject.isInitialDocument;
+}
+Services.obs.addObserver(wasInitialDocumentObserver, "window-global-created");
+SimpleTest.registerCleanupFunction(function() {
+ Services.obs.removeObserver(
+ wasInitialDocumentObserver,
+ "window-global-created"
+ );
+});
+
+add_task(async function new_about_blank_tab() {
+ await BrowserTestUtils.withNewTab("about:blank", async browser => {
+ is(
+ browser.browsingContext.currentWindowGlobal.isInitialDocument,
+ false,
+ "After loading an actual, final about:blank in the tab, the field is false"
+ );
+ });
+});
+
+add_task(async function iframe_initial_about_blank() {
+ await BrowserTestUtils.withNewTab(
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/document-builder.sjs?html=com",
+ async browser => {
+ info("Create an iframe without any explicit location");
+ await SpecialPowers.spawn(browser, [], async () => {
+ const iframe = content.document.createElement("iframe");
+ // Add the iframe to the DOM tree in order to be able to have its browsingContext
+ content.document.body.appendChild(iframe);
+ const { browsingContext } = iframe;
+
+ is(
+ iframe.contentDocument.isInitialDocument,
+ true,
+ "The field is true on just-created iframes"
+ );
+ let beforeLoadPromise = SpecialPowers.spawnChrome(
+ [browsingContext],
+ bc => [
+ bc.currentWindowGlobal.isInitialDocument,
+ bc.currentWindowGlobal._test_wasInitialDocument,
+ ]
+ );
+
+ await new Promise(resolve => {
+ iframe.addEventListener("load", resolve, { once: true });
+ });
+ is(
+ iframe.contentDocument.isInitialDocument,
+ false,
+ "The field is false after having loaded the final about:blank document"
+ );
+ let afterLoadPromise = SpecialPowers.spawnChrome(
+ [browsingContext],
+ bc => [
+ bc.currentWindowGlobal.isInitialDocument,
+ bc.currentWindowGlobal._test_wasInitialDocument,
+ ]
+ );
+
+ // Wait to await the parent process promises, so we can't miss the "load" event.
+ let [beforeIsInitial, beforeWasInitial] = await beforeLoadPromise;
+ is(beforeIsInitial, true, "before load is initial in parent");
+ is(beforeWasInitial, true, "before load was initial in parent");
+ let [afterIsInitial, afterWasInitial] = await afterLoadPromise;
+ is(afterIsInitial, false, "after load is not initial in parent");
+ is(afterWasInitial, true, "after load was initial in parent");
+ iframe.remove();
+ });
+
+ info("Create an iframe with a cross origin location");
+ const iframeBC = await SpecialPowers.spawn(browser, [], async () => {
+ const iframe = content.document.createElement("iframe");
+ await new Promise(resolve => {
+ iframe.addEventListener("load", resolve, { once: true });
+ iframe.src =
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.org/document-builder.sjs?html=org-iframe";
+ content.document.body.appendChild(iframe);
+ });
+
+ return iframe.browsingContext;
+ });
+
+ is(
+ iframeBC.currentWindowGlobal.isInitialDocument,
+ false,
+ "The field is true after having loaded the final document"
+ );
+ }
+ );
+});
+
+add_task(async function window_open() {
+ async function testWindowOpen({ browser, args, isCrossOrigin, willLoad }) {
+ info(`Open popup with ${JSON.stringify(args)}`);
+ const onNewTab = BrowserTestUtils.waitForNewTab(
+ gBrowser,
+ args[0] || "about:blank"
+ );
+ await SpecialPowers.spawn(
+ browser,
+ [args, isCrossOrigin, willLoad],
+ async (args, crossOrigin, willLoad) => {
+ const win = content.window.open(...args);
+ is(
+ win.document.isInitialDocument,
+ true,
+ "The field is true right after calling window.open()"
+ );
+ let beforeLoadPromise = SpecialPowers.spawnChrome(
+ [win.browsingContext],
+ bc => [
+ bc.currentWindowGlobal.isInitialDocument,
+ bc.currentWindowGlobal._test_wasInitialDocument,
+ ]
+ );
+
+ // In cross origin, it is harder to watch for new document load, and if
+ // no argument is passed no load will happen.
+ if (!crossOrigin && willLoad) {
+ await new Promise(r =>
+ win.addEventListener("load", r, { once: true })
+ );
+ is(
+ win.document.isInitialDocument,
+ false,
+ "The field becomes false right after the popup document is loaded"
+ );
+ }
+
+ // Perform the await after the load to avoid missing it.
+ let [beforeIsInitial, beforeWasInitial] = await beforeLoadPromise;
+ is(beforeIsInitial, true, "before load is initial in parent");
+ is(beforeWasInitial, true, "before load was initial in parent");
+ }
+ );
+ const newTab = await onNewTab;
+ const windowGlobal =
+ newTab.linkedBrowser.browsingContext.currentWindowGlobal;
+ if (willLoad) {
+ is(
+ windowGlobal.isInitialDocument,
+ false,
+ "The field is false in the parent process after having loaded the final document"
+ );
+ } else {
+ is(
+ windowGlobal.isInitialDocument,
+ true,
+ "The field remains true in the parent process as nothing will be loaded"
+ );
+ }
+ BrowserTestUtils.removeTab(newTab);
+ }
+
+ await BrowserTestUtils.withNewTab(
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/document-builder.sjs?html=com",
+ async browser => {
+ info("Use window.open() with cross-origin document");
+ await testWindowOpen({
+ browser,
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ args: ["http://example.org/document-builder.sjs?html=org-popup"],
+ isCrossOrigin: true,
+ willLoad: true,
+ });
+
+ info("Use window.open() with same-origin document");
+ await testWindowOpen({
+ browser,
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ args: ["http://example.com/document-builder.sjs?html=com-popup"],
+ isCrossOrigin: false,
+ willLoad: true,
+ });
+
+ info("Use window.open() with final about:blank document");
+ await testWindowOpen({
+ browser,
+ args: ["about:blank"],
+ isCrossOrigin: false,
+ willLoad: true,
+ });
+
+ info("Use window.open() with no argument");
+ await testWindowOpen({
+ browser,
+ args: [],
+ isCrossOrigin: false,
+ willLoad: false,
+ });
+ }
+ );
+});
+
+add_task(async function document_open() {
+ await BrowserTestUtils.withNewTab(
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/document-builder.sjs?html=com",
+ async browser => {
+ is(browser.browsingContext.currentWindowGlobal.isInitialDocument, false);
+ await SpecialPowers.spawn(browser, [], async () => {
+ const iframe = content.document.createElement("iframe");
+ // Add the iframe to the DOM tree in order to be able to have its browsingContext
+ content.document.body.appendChild(iframe);
+ const { browsingContext } = iframe;
+
+ // Check the state before the call in both parent and content.
+ is(
+ iframe.contentDocument.isInitialDocument,
+ true,
+ "Is an initial document before calling document.open"
+ );
+ let beforeOpenParentPromise = SpecialPowers.spawnChrome(
+ [browsingContext],
+ bc => [
+ bc.currentWindowGlobal.isInitialDocument,
+ bc.currentWindowGlobal._test_wasInitialDocument,
+ bc.currentWindowGlobal.innerWindowId,
+ ]
+ );
+
+ // Run the `document.open` call with reduced permissions.
+ iframe.contentWindow.eval(`
+ document.open();
+ document.write("new document");
+ document.close();
+ `);
+
+ is(
+ iframe.contentDocument.isInitialDocument,
+ false,
+ "Is no longer an initial document after calling document.open"
+ );
+ let [
+ afterIsInitial,
+ afterWasInitial,
+ afterID,
+ ] = await SpecialPowers.spawnChrome([browsingContext], bc => [
+ bc.currentWindowGlobal.isInitialDocument,
+ bc.currentWindowGlobal._test_wasInitialDocument,
+ bc.currentWindowGlobal.innerWindowId,
+ ]);
+ let [
+ beforeIsInitial,
+ beforeWasInitial,
+ beforeID,
+ ] = await beforeOpenParentPromise;
+ is(beforeIsInitial, true, "Should be initial before in the parent");
+ is(beforeWasInitial, true, "Was initial before in the parent");
+ is(afterIsInitial, false, "Should not be initial after in the parent");
+ is(afterWasInitial, true, "Was initial after in the parent");
+ is(beforeID, afterID, "Should be the same WindowGlobalParent");
+ });
+ }
+ );
+});
+
+add_task(async function windowless_browser() {
+ info("Create a Windowless browser");
+ const browser = Services.appShell.createWindowlessBrowser(false);
+ const { browsingContext } = browser;
+ is(
+ browsingContext.currentWindowGlobal.isInitialDocument,
+ true,
+ "The field is true for a freshly created WindowlessBrowser"
+ );
+ is(
+ browser.currentURI.spec,
+ "about:blank",
+ "The location is immediately set to about:blank"
+ );
+
+ const principal = Services.scriptSecurityManager.getSystemPrincipal();
+ browser.docShell.createAboutBlankContentViewer(principal, principal);
+ is(
+ browsingContext.currentWindowGlobal.isInitialDocument,
+ false,
+ "The field becomes false when creating an artificial blank document"
+ );
+
+ info("Load a final about:blank document in it");
+ const onLocationChange = new Promise(resolve => {
+ let wpl = {
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIWebProgressListener",
+ "nsISupportsWeakReference",
+ ]),
+ onLocationChange() {
+ browsingContext.webProgress.removeProgressListener(
+ wpl,
+ Ci.nsIWebProgress.NOTIFY_ALL
+ );
+ resolve();
+ },
+ };
+ browsingContext.webProgress.addProgressListener(
+ wpl,
+ Ci.nsIWebProgress.NOTIFY_ALL
+ );
+ });
+ browser.loadURI("about:blank", { triggeringPrincipal: principal });
+ info("Wait for the location change");
+ await onLocationChange;
+ is(
+ browsingContext.currentWindowGlobal.isInitialDocument,
+ false,
+ "The field is false after the location change event"
+ );
+ browser.close();
+});
diff --git a/docshell/test/browser/browser_loadURI_postdata.js b/docshell/test/browser/browser_loadURI_postdata.js
new file mode 100644
index 0000000000..616fbd9d8e
--- /dev/null
+++ b/docshell/test/browser/browser_loadURI_postdata.js
@@ -0,0 +1,42 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const gPostData = "postdata=true";
+const gUrl =
+ "http://mochi.test:8888/browser/docshell/test/browser/print_postdata.sjs";
+
+add_task(async function test_loadURI_persists_postData() {
+ waitForExplicitFinish();
+
+ let tab = (gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser));
+ registerCleanupFunction(function() {
+ gBrowser.removeTab(tab);
+ });
+
+ var dataStream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(
+ Ci.nsIStringInputStream
+ );
+ dataStream.data = gPostData;
+
+ var postStream = Cc[
+ "@mozilla.org/network/mime-input-stream;1"
+ ].createInstance(Ci.nsIMIMEInputStream);
+ postStream.addHeader("Content-Type", "application/x-www-form-urlencoded");
+ postStream.setData(dataStream);
+ var systemPrincipal = Cc["@mozilla.org/systemprincipal;1"].getService(
+ Ci.nsIPrincipal
+ );
+
+ tab.linkedBrowser.loadURI(gUrl, {
+ triggeringPrincipal: systemPrincipal,
+ postData: postStream,
+ });
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, gUrl);
+ let body = await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [],
+ () => content.document.body.textContent
+ );
+ is(body, gPostData, "post data was submitted correctly");
+ finish();
+});
diff --git a/docshell/test/browser/browser_multiple_pushState.js b/docshell/test/browser/browser_multiple_pushState.js
new file mode 100644
index 0000000000..5917841c0f
--- /dev/null
+++ b/docshell/test/browser/browser_multiple_pushState.js
@@ -0,0 +1,25 @@
+add_task(async function test_multiple_pushState() {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url:
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.org/browser/docshell/test/browser/file_multiple_pushState.html",
+ },
+ async function(browser) {
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ const kExpected = "http://example.org/bar/ABC/DEF?key=baz";
+
+ let contentLocation = await SpecialPowers.spawn(
+ browser,
+ [],
+ async function() {
+ return content.document.location.href;
+ }
+ );
+
+ is(contentLocation, kExpected);
+ is(browser.documentURI.spec, kExpected);
+ }
+ );
+});
diff --git a/docshell/test/browser/browser_onbeforeunload_frame.js b/docshell/test/browser/browser_onbeforeunload_frame.js
new file mode 100644
index 0000000000..266407864d
--- /dev/null
+++ b/docshell/test/browser/browser_onbeforeunload_frame.js
@@ -0,0 +1,45 @@
+"use strict";
+
+// We need to test a lot of permutations here, and there isn't any sensible way
+// to split them up or run them faster.
+requestLongerTimeout(12);
+
+Services.scriptloader.loadSubScript(
+ getRootDirectory(gTestPath) + "head_browser_onbeforeunload.js",
+ this
+);
+
+add_task(async function() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.require_user_interaction_for_beforeunload", false]],
+ });
+
+ for (let actions of PERMUTATIONS) {
+ info(
+ `Testing frame actions: [${actions.map(action =>
+ ACTION_NAMES.get(action)
+ )}]`
+ );
+
+ for (let startIdx = 0; startIdx < FRAMES.length; startIdx++) {
+ info(`Testing content reload from frame ${startIdx}`);
+
+ await doTest(actions, startIdx, (tab, frames) => {
+ return SpecialPowers.spawn(frames[startIdx], [], () => {
+ let eventLoopSpun = false;
+ SpecialPowers.Services.tm.dispatchToMainThread(() => {
+ eventLoopSpun = true;
+ });
+
+ content.location.reload();
+
+ return { eventLoopSpun };
+ });
+ });
+ }
+ }
+});
+
+add_task(async function cleanup() {
+ await TabPool.cleanup();
+});
diff --git a/docshell/test/browser/browser_onbeforeunload_navigation.js b/docshell/test/browser/browser_onbeforeunload_navigation.js
new file mode 100644
index 0000000000..df1d8c93d4
--- /dev/null
+++ b/docshell/test/browser/browser_onbeforeunload_navigation.js
@@ -0,0 +1,165 @@
+"use strict";
+
+const TEST_PAGE =
+ "http://mochi.test:8888/browser/docshell/test/browser/file_bug1046022.html";
+const TARGETED_PAGE =
+ "data:text/html," +
+ encodeURIComponent("<body>Shouldn't be seeing this</body>");
+
+const { PromptTestUtils } = ChromeUtils.import(
+ "resource://testing-common/PromptTestUtils.jsm"
+);
+
+var loadStarted = false;
+var tabStateListener = {
+ resolveLoad: null,
+ expectLoad: null,
+
+ onStateChange(webprogress, request, flags, status) {
+ const WPL = Ci.nsIWebProgressListener;
+ if (flags & WPL.STATE_IS_WINDOW) {
+ if (flags & WPL.STATE_START) {
+ loadStarted = true;
+ } else if (flags & WPL.STATE_STOP) {
+ let url = request.QueryInterface(Ci.nsIChannel).URI.spec;
+ is(url, this.expectLoad, "Should only see expected document loads");
+ if (url == this.expectLoad) {
+ this.resolveLoad();
+ }
+ }
+ }
+ },
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIWebProgressListener",
+ "nsISupportsWeakReference",
+ ]),
+};
+
+function promiseLoaded(url, callback) {
+ if (tabStateListener.expectLoad) {
+ throw new Error("Can't wait for multiple loads at once");
+ }
+ tabStateListener.expectLoad = url;
+ return new Promise(resolve => {
+ tabStateListener.resolveLoad = resolve;
+ if (callback) {
+ callback();
+ }
+ }).then(() => {
+ tabStateListener.expectLoad = null;
+ tabStateListener.resolveLoad = null;
+ });
+}
+
+function promiseStayOnPagePrompt(browser, acceptNavigation) {
+ return PromptTestUtils.handleNextPrompt(
+ browser,
+ { modalType: Services.prompt.MODAL_TYPE_CONTENT, promptType: "confirmEx" },
+ { buttonNumClick: acceptNavigation ? 0 : 1 }
+ );
+}
+
+add_task(async function test() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.require_user_interaction_for_beforeunload", false]],
+ });
+
+ let testTab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ TEST_PAGE,
+ false,
+ true
+ );
+ let browser = testTab.linkedBrowser;
+ browser.addProgressListener(
+ tabStateListener,
+ Ci.nsIWebProgress.NOTIFY_STATE_WINDOW
+ );
+
+ const NUM_TESTS = 7;
+ await SpecialPowers.spawn(browser, [NUM_TESTS], testCount => {
+ let { testFns } = this.content.wrappedJSObject;
+ Assert.equal(
+ testFns.length,
+ testCount,
+ "Should have the correct number of test functions"
+ );
+ });
+
+ for (let allowNavigation of [false, true]) {
+ for (let i = 0; i < NUM_TESTS; i++) {
+ info(
+ `Running test ${i} with navigation ${
+ allowNavigation ? "allowed" : "forbidden"
+ }`
+ );
+
+ if (allowNavigation) {
+ // If we're allowing navigations, we need to re-load the test
+ // page after each test, since the tests will each navigate away
+ // from it.
+ await promiseLoaded(TEST_PAGE, () => {
+ browser.loadURI(TEST_PAGE, {
+ triggeringPrincipal: document.nodePrincipal,
+ });
+ });
+ }
+
+ let promptPromise = promiseStayOnPagePrompt(browser, allowNavigation);
+ let loadPromise;
+ if (allowNavigation) {
+ loadPromise = promiseLoaded(TARGETED_PAGE);
+ }
+
+ let winID = await SpecialPowers.spawn(
+ browser,
+ [i, TARGETED_PAGE],
+ (testIdx, url) => {
+ let { testFns } = this.content.wrappedJSObject;
+ this.content.onbeforeunload = testFns[testIdx];
+ this.content.location = url;
+ return this.content.windowGlobalChild.innerWindowId;
+ }
+ );
+
+ await promptPromise;
+ await loadPromise;
+
+ if (allowNavigation) {
+ await SpecialPowers.spawn(
+ browser,
+ [TARGETED_PAGE, winID],
+ (url, winID) => {
+ this.content.onbeforeunload = null;
+ Assert.equal(
+ this.content.location.href,
+ url,
+ "Page should have navigated to the correct URL"
+ );
+ Assert.notEqual(
+ this.content.windowGlobalChild.innerWindowId,
+ winID,
+ "Page should have a new inner window"
+ );
+ }
+ );
+ } else {
+ await SpecialPowers.spawn(browser, [TEST_PAGE, winID], (url, winID) => {
+ this.content.onbeforeunload = null;
+ Assert.equal(
+ this.content.location.href,
+ url,
+ "Page should have the same URL"
+ );
+ Assert.equal(
+ this.content.windowGlobalChild.innerWindowId,
+ winID,
+ "Page should have the same inner window"
+ );
+ });
+ }
+ }
+ }
+
+ gBrowser.removeTab(testTab);
+});
diff --git a/docshell/test/browser/browser_onbeforeunload_parent.js b/docshell/test/browser/browser_onbeforeunload_parent.js
new file mode 100644
index 0000000000..79cf815734
--- /dev/null
+++ b/docshell/test/browser/browser_onbeforeunload_parent.js
@@ -0,0 +1,48 @@
+"use strict";
+
+// We need to test a lot of permutations here, and there isn't any sensible way
+// to split them up or run them faster.
+requestLongerTimeout(6);
+
+Services.scriptloader.loadSubScript(
+ getRootDirectory(gTestPath) + "head_browser_onbeforeunload.js",
+ this
+);
+
+add_task(async function() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.require_user_interaction_for_beforeunload", false]],
+ });
+
+ for (let actions of PERMUTATIONS) {
+ info(
+ `Testing frame actions: [${actions.map(action =>
+ ACTION_NAMES.get(action)
+ )}]`
+ );
+
+ info(`Testing tab close from parent process`);
+ await doTest(actions, -1, (tab, frames) => {
+ let eventLoopSpun = false;
+ Services.tm.dispatchToMainThread(() => {
+ eventLoopSpun = true;
+ });
+
+ BrowserTestUtils.removeTab(tab);
+
+ let result = { eventLoopSpun };
+
+ // Make an extra couple of trips through the event loop to give us time
+ // to process SpecialPowers.spawn responses before resolving.
+ return new Promise(resolve => {
+ executeSoon(() => {
+ executeSoon(() => resolve(result));
+ });
+ });
+ });
+ }
+});
+
+add_task(async function cleanup() {
+ await TabPool.cleanup();
+});
diff --git a/docshell/test/browser/browser_onunload_stop.js b/docshell/test/browser/browser_onunload_stop.js
new file mode 100644
index 0000000000..fe3c0c0834
--- /dev/null
+++ b/docshell/test/browser/browser_onunload_stop.js
@@ -0,0 +1,23 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_PAGE_1 =
+ "http://mochi.test:8888/browser/docshell/test/browser/dummy_page.html";
+
+const TEST_PAGE_2 =
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/browser/docshell/test/browser/dummy_page.html";
+
+add_task(async function test() {
+ await BrowserTestUtils.withNewTab(TEST_PAGE_1, async function(browser) {
+ let loaded = BrowserTestUtils.browserLoaded(browser, false, TEST_PAGE_2);
+ await SpecialPowers.spawn(browser, [], () => {
+ content.addEventListener("unload", e => e.currentTarget.stop(), true);
+ });
+ BrowserTestUtils.loadURI(browser, TEST_PAGE_2);
+ await loaded;
+ ok(true, "Page loaded successfully");
+ });
+});
diff --git a/docshell/test/browser/browser_overlink.js b/docshell/test/browser/browser_overlink.js
new file mode 100644
index 0000000000..64973985ad
--- /dev/null
+++ b/docshell/test/browser/browser_overlink.js
@@ -0,0 +1,27 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+
+add_task(async function test_stripAuthCredentials() {
+ await BrowserTestUtils.withNewTab(
+ TEST_PATH + "overlink_test.html",
+ async function(browser) {
+ await SpecialPowers.spawn(browser, [], function() {
+ content.document.getElementById("link").focus();
+ });
+
+ await TestUtils.waitForCondition(
+ () => XULBrowserWindow.overLink == "https://example.com",
+ "Overlink should be missing auth credentials"
+ );
+
+ ok(true, "Test successful");
+ }
+ );
+});
diff --git a/docshell/test/browser/browser_platform_emulation.js b/docshell/test/browser/browser_platform_emulation.js
new file mode 100644
index 0000000000..3a9d3abe94
--- /dev/null
+++ b/docshell/test/browser/browser_platform_emulation.js
@@ -0,0 +1,69 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const URL = "data:text/html;charset=utf-8,<iframe id='test-iframe'></iframe>";
+
+async function contentTaskNoOverride() {
+ let docshell = docShell;
+ is(
+ docshell.browsingContext.customPlatform,
+ "",
+ "There should initially be no customPlatform"
+ );
+}
+
+async function contentTaskOverride() {
+ let docshell = docShell;
+ is(
+ docshell.browsingContext.customPlatform,
+ "foo",
+ "The platform should be changed to foo"
+ );
+
+ is(
+ content.navigator.platform,
+ "foo",
+ "The platform should be changed to foo"
+ );
+
+ let frameWin = content.document.querySelector("#test-iframe").contentWindow;
+ is(
+ frameWin.navigator.platform,
+ "foo",
+ "The platform should be passed on to frames."
+ );
+
+ let newFrame = content.document.createElement("iframe");
+ content.document.body.appendChild(newFrame);
+
+ let newFrameWin = newFrame.contentWindow;
+ is(
+ newFrameWin.navigator.platform,
+ "foo",
+ "Newly created frames should use the new platform"
+ );
+
+ newFrameWin.location.reload();
+ await ContentTaskUtils.waitForEvent(newFrame, "load");
+
+ is(
+ newFrameWin.navigator.platform,
+ "foo",
+ "New platform should persist across reloads"
+ );
+}
+
+add_task(async function() {
+ await BrowserTestUtils.withNewTab({ gBrowser, url: URL }, async function(
+ browser
+ ) {
+ await SpecialPowers.spawn(browser, [], contentTaskNoOverride);
+
+ let browsingContext = BrowserTestUtils.getBrowsingContextFrom(browser);
+ browsingContext.customPlatform = "foo";
+
+ await SpecialPowers.spawn(browser, [], contentTaskOverride);
+ });
+});
diff --git a/docshell/test/browser/browser_search_notification.js b/docshell/test/browser/browser_search_notification.js
new file mode 100644
index 0000000000..291b670862
--- /dev/null
+++ b/docshell/test/browser/browser_search_notification.js
@@ -0,0 +1,49 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const { SearchTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/SearchTestUtils.sys.mjs"
+);
+
+SearchTestUtils.init(this);
+
+add_task(async function() {
+ // Our search would be handled by the urlbar normally and not by the docshell,
+ // thus we must force going through dns first, so that the urlbar thinks
+ // the value may be a url, and asks the docshell to visit it.
+ // On NS_ERROR_UNKNOWN_HOST the docshell will fix it up.
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.fixup.dns_first_for_single_words", true]],
+ });
+ const kSearchEngineID = "test_urifixup_search_engine";
+ await SearchTestUtils.installSearchExtension(
+ {
+ name: kSearchEngineID,
+ search_url: "http://localhost/",
+ search_url_get_params: "search={searchTerms}",
+ },
+ { setAsDefault: true }
+ );
+
+ let selectedName = (await Services.search.getDefault()).name;
+ Assert.equal(
+ selectedName,
+ kSearchEngineID,
+ "Check fake search engine is selected"
+ );
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+ gBrowser.selectedTab = tab;
+
+ gURLBar.value = "firefox";
+ gURLBar.handleCommand();
+
+ let [subject, data] = await TestUtils.topicObserved("keyword-search");
+
+ let engine = subject.QueryInterface(Ci.nsISupportsString).data;
+
+ Assert.equal(engine, kSearchEngineID, "Should be the search engine id");
+ Assert.equal(data, "firefox", "Notification data is search term.");
+
+ gBrowser.removeTab(tab);
+});
diff --git a/docshell/test/browser/browser_tab_replace_while_loading.js b/docshell/test/browser/browser_tab_replace_while_loading.js
new file mode 100644
index 0000000000..e1b88334ff
--- /dev/null
+++ b/docshell/test/browser/browser_tab_replace_while_loading.js
@@ -0,0 +1,83 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/* Test for bug 1578379. */
+
+add_task(async function test_window_open_about_blank() {
+ const URL =
+ "http://mochi.test:8888/browser/docshell/test/browser/file_open_about_blank.html";
+ let firstTab = await BrowserTestUtils.openNewForegroundTab(gBrowser, URL);
+ let promiseTabOpened = BrowserTestUtils.waitForNewTab(
+ gBrowser,
+ "about:blank"
+ );
+
+ info("Opening about:blank using a click");
+ await SpecialPowers.spawn(firstTab.linkedBrowser, [""], async function() {
+ content.document.querySelector("#open").click();
+ });
+
+ info("Waiting for the second tab to be opened");
+ let secondTab = await promiseTabOpened;
+
+ info("Detaching tab");
+ let windowOpenedPromise = BrowserTestUtils.waitForNewWindow();
+ gBrowser.replaceTabWithWindow(secondTab);
+ let win = await windowOpenedPromise;
+
+ info("Asserting document is visible");
+ let tab = win.gBrowser.selectedTab;
+ await SpecialPowers.spawn(tab.linkedBrowser, [""], async function() {
+ is(
+ content.document.visibilityState,
+ "visible",
+ "Document should be visible"
+ );
+ });
+
+ await BrowserTestUtils.closeWindow(win);
+ await BrowserTestUtils.removeTab(firstTab);
+});
+
+add_task(async function test_detach_loading_page() {
+ const URL =
+ "http://mochi.test:8888/browser/docshell/test/browser/file_slow_load.sjs";
+ // Open a dummy tab so that detaching the second tab works.
+ let dummyTab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "about:blank"
+ );
+ let slowLoadingTab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ URL,
+ /* waitForLoad = */ false
+ );
+
+ info("Wait for content document to be created");
+ await BrowserTestUtils.waitForCondition(async function() {
+ return SpecialPowers.spawn(
+ slowLoadingTab.linkedBrowser,
+ [URL],
+ async function(url) {
+ return content.document.documentURI == url;
+ }
+ );
+ });
+
+ info("Detaching tab");
+ let windowOpenedPromise = BrowserTestUtils.waitForNewWindow();
+ gBrowser.replaceTabWithWindow(slowLoadingTab);
+ let win = await windowOpenedPromise;
+
+ info("Asserting document is visible");
+ let tab = win.gBrowser.selectedTab;
+ await SpecialPowers.spawn(tab.linkedBrowser, [""], async function() {
+ is(content.document.readyState, "loading");
+ is(content.document.visibilityState, "visible");
+ });
+
+ await BrowserTestUtils.closeWindow(win);
+ await BrowserTestUtils.removeTab(dummyTab);
+});
diff --git a/docshell/test/browser/browser_tab_touch_events.js b/docshell/test/browser/browser_tab_touch_events.js
new file mode 100644
index 0000000000..8e66e12253
--- /dev/null
+++ b/docshell/test/browser/browser_tab_touch_events.js
@@ -0,0 +1,74 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function() {
+ const URI = "data:text/html;charset=utf-8,<iframe id='test-iframe'></iframe>";
+
+ await BrowserTestUtils.withNewTab({ gBrowser, url: URI }, async function(
+ browser
+ ) {
+ await SpecialPowers.spawn(browser, [], test_init);
+
+ browser.browsingContext.touchEventsOverride = "disabled";
+
+ await SpecialPowers.spawn(browser, [], test_body);
+ });
+});
+
+async function test_init() {
+ is(
+ content.browsingContext.touchEventsOverride,
+ "none",
+ "touchEventsOverride flag should be initially set to NONE"
+ );
+}
+
+async function test_body() {
+ let bc = content.browsingContext;
+ is(
+ bc.touchEventsOverride,
+ "disabled",
+ "touchEventsOverride flag should be changed to DISABLED"
+ );
+
+ let frameWin = content.document.querySelector("#test-iframe").contentWindow;
+ bc = frameWin.browsingContext;
+ is(
+ bc.touchEventsOverride,
+ "disabled",
+ "touchEventsOverride flag should be passed on to frames."
+ );
+
+ let newFrame = content.document.createElement("iframe");
+ content.document.body.appendChild(newFrame);
+
+ let newFrameWin = newFrame.contentWindow;
+ bc = newFrameWin.browsingContext;
+ is(
+ bc.touchEventsOverride,
+ "disabled",
+ "Newly created frames should use the new touchEventsOverride flag"
+ );
+
+ // Wait for the non-transient about:blank to load.
+ await ContentTaskUtils.waitForEvent(newFrame, "load");
+ newFrameWin = newFrame.contentWindow;
+ bc = newFrameWin.browsingContext;
+ is(
+ bc.touchEventsOverride,
+ "disabled",
+ "Newly created frames should use the new touchEventsOverride flag"
+ );
+
+ newFrameWin.location.reload();
+ await ContentTaskUtils.waitForEvent(newFrame, "load");
+ newFrameWin = newFrame.contentWindow;
+ bc = newFrameWin.browsingContext;
+ is(
+ bc.touchEventsOverride,
+ "disabled",
+ "New touchEventsOverride flag should persist across reloads"
+ );
+}
diff --git a/docshell/test/browser/browser_targetTopLevelLinkClicksToBlank.js b/docshell/test/browser/browser_targetTopLevelLinkClicksToBlank.js
new file mode 100644
index 0000000000..41cbb0f82c
--- /dev/null
+++ b/docshell/test/browser/browser_targetTopLevelLinkClicksToBlank.js
@@ -0,0 +1,285 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * This test exercises the behaviour where user-initiated link clicks on
+ * the top-level document result in pageloads in a _blank target in a new
+ * browser window.
+ */
+
+const TEST_PAGE = "https://example.com/browser/";
+const TEST_PAGE_2 = "https://example.com/browser/components/";
+const TEST_IFRAME_PAGE =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "dummy_iframe_page.html";
+
+// There is an <a> element with this href=".." in the TEST_PAGE
+// that we will click, which should take us up a level.
+const LINK_URL = "https://example.com/";
+
+/**
+ * Test that a user-initiated link click results in targeting to a new
+ * <browser> element, and that this properly sets the referrer on the newly
+ * loaded document.
+ */
+add_task(async function target_to_new_blank_browser() {
+ let win = await BrowserTestUtils.openNewBrowserWindow();
+ let originalTab = win.gBrowser.selectedTab;
+ let originalBrowser = originalTab.linkedBrowser;
+ BrowserTestUtils.loadURI(originalBrowser, TEST_PAGE);
+ await BrowserTestUtils.browserLoaded(originalBrowser, false, TEST_PAGE);
+
+ // Now set the targetTopLevelLinkClicksToBlank property to true, since it
+ // defaults to false.
+ originalBrowser.browsingContext.targetTopLevelLinkClicksToBlank = true;
+
+ let newTabPromise = BrowserTestUtils.waitForNewTab(win.gBrowser, LINK_URL);
+ await SpecialPowers.spawn(originalBrowser, [], async () => {
+ let anchor = content.document.querySelector(`a[href=".."]`);
+ let userInput = content.windowUtils.setHandlingUserInput(true);
+ try {
+ anchor.click();
+ } finally {
+ userInput.destruct();
+ }
+ });
+ let newTab = await newTabPromise;
+ let newBrowser = newTab.linkedBrowser;
+
+ Assert.ok(
+ originalBrowser !== newBrowser,
+ "A new browser should have been created."
+ );
+ await SpecialPowers.spawn(newBrowser, [TEST_PAGE], async referrer => {
+ Assert.equal(
+ content.document.referrer,
+ referrer,
+ "Should have gotten the right referrer set"
+ );
+ });
+ await BrowserTestUtils.switchTab(win.gBrowser, originalTab);
+ BrowserTestUtils.removeTab(newTab);
+
+ // Now do the same thing with a subframe targeting "_top". This should also
+ // get redirected to "_blank".
+ BrowserTestUtils.loadURI(originalBrowser, TEST_IFRAME_PAGE);
+ await BrowserTestUtils.browserLoaded(
+ originalBrowser,
+ false,
+ TEST_IFRAME_PAGE
+ );
+
+ newTabPromise = BrowserTestUtils.waitForNewTab(win.gBrowser, LINK_URL);
+ let frameBC1 = originalBrowser.browsingContext.children[0];
+ Assert.ok(frameBC1, "Should have found a subframe BrowsingContext");
+
+ await SpecialPowers.spawn(frameBC1, [LINK_URL], async linkUrl => {
+ let anchor = content.document.createElement("a");
+ anchor.setAttribute("href", linkUrl);
+ anchor.setAttribute("target", "_top");
+ content.document.body.appendChild(anchor);
+ let userInput = content.windowUtils.setHandlingUserInput(true);
+ try {
+ anchor.click();
+ } finally {
+ userInput.destruct();
+ }
+ });
+ newTab = await newTabPromise;
+ newBrowser = newTab.linkedBrowser;
+
+ Assert.ok(
+ originalBrowser !== newBrowser,
+ "A new browser should have been created."
+ );
+ await SpecialPowers.spawn(
+ newBrowser,
+ [frameBC1.currentURI.spec],
+ async referrer => {
+ Assert.equal(
+ content.document.referrer,
+ referrer,
+ "Should have gotten the right referrer set"
+ );
+ }
+ );
+ await BrowserTestUtils.switchTab(win.gBrowser, originalTab);
+ BrowserTestUtils.removeTab(newTab);
+
+ await BrowserTestUtils.closeWindow(win);
+});
+
+/**
+ * Test that we don't target to _blank loads caused by:
+ * 1. POST requests
+ * 2. Any load that isn't "normal" (in the nsIDocShell.LOAD_CMD_NORMAL sense)
+ * 3. Any loads that are caused by location.replace
+ * 4. Any loads that were caused by setting location.href
+ * 5. Link clicks fired without user interaction.
+ */
+add_task(async function skip_blank_target_for_some_loads() {
+ let win = await BrowserTestUtils.openNewBrowserWindow();
+ let currentBrowser = win.gBrowser.selectedBrowser;
+ BrowserTestUtils.loadURI(currentBrowser, TEST_PAGE);
+ await BrowserTestUtils.browserLoaded(currentBrowser, false, TEST_PAGE);
+
+ // Now set the targetTopLevelLinkClicksToBlank property to true, since it
+ // defaults to false.
+ currentBrowser.browsingContext.targetTopLevelLinkClicksToBlank = true;
+
+ let ensureSingleBrowser = () => {
+ Assert.equal(
+ win.gBrowser.browsers.length,
+ 1,
+ "There should only be 1 browser."
+ );
+
+ Assert.ok(
+ currentBrowser.browsingContext.targetTopLevelLinkClicksToBlank,
+ "Should still be targeting top-level clicks to _blank"
+ );
+ };
+
+ // First we'll test a POST request
+ let sameBrowserLoad = BrowserTestUtils.browserLoaded(
+ currentBrowser,
+ false,
+ TEST_PAGE
+ );
+ await SpecialPowers.spawn(currentBrowser, [], async () => {
+ let doc = content.document;
+ let form = doc.createElement("form");
+ form.setAttribute("method", "post");
+ doc.body.appendChild(form);
+ let userInput = content.windowUtils.setHandlingUserInput(true);
+ try {
+ form.submit();
+ } finally {
+ userInput.destruct();
+ }
+ });
+ await sameBrowserLoad;
+ ensureSingleBrowser();
+
+ // Next, we'll try a non-normal load - specifically, we'll try a reload.
+ // Since we've got a page loaded via a POST request, an attempt to reload
+ // will cause the "repost" dialog to appear, so we temporarily allow the
+ // repost to go through with the always_accept testing pref.
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.confirm_repost.testing.always_accept", true]],
+ });
+ sameBrowserLoad = BrowserTestUtils.browserLoaded(
+ currentBrowser,
+ false,
+ TEST_PAGE
+ );
+ await SpecialPowers.spawn(currentBrowser, [], async () => {
+ let userInput = content.windowUtils.setHandlingUserInput(true);
+ try {
+ content.location.reload();
+ } finally {
+ userInput.destruct();
+ }
+ });
+ await sameBrowserLoad;
+ ensureSingleBrowser();
+ await SpecialPowers.popPrefEnv();
+
+ // Next, we'll try a location.replace
+ sameBrowserLoad = BrowserTestUtils.browserLoaded(
+ currentBrowser,
+ false,
+ TEST_PAGE_2
+ );
+ await SpecialPowers.spawn(currentBrowser, [TEST_PAGE_2], async page2 => {
+ let userInput = content.windowUtils.setHandlingUserInput(true);
+ try {
+ content.location.replace(page2);
+ } finally {
+ userInput.destruct();
+ }
+ });
+ await sameBrowserLoad;
+ ensureSingleBrowser();
+
+ // Finally we'll try setting location.href
+ sameBrowserLoad = BrowserTestUtils.browserLoaded(
+ currentBrowser,
+ false,
+ TEST_PAGE
+ );
+ await SpecialPowers.spawn(currentBrowser, [TEST_PAGE], async page1 => {
+ let userInput = content.windowUtils.setHandlingUserInput(true);
+ try {
+ content.location.href = page1;
+ } finally {
+ userInput.destruct();
+ }
+ });
+ await sameBrowserLoad;
+ ensureSingleBrowser();
+
+ // Now that we're back at TEST_PAGE, let's try a scripted link click. This
+ // shouldn't target to _blank.
+ sameBrowserLoad = BrowserTestUtils.browserLoaded(
+ currentBrowser,
+ false,
+ LINK_URL
+ );
+ await SpecialPowers.spawn(currentBrowser, [], async () => {
+ let anchor = content.document.querySelector(`a[href=".."]`);
+ anchor.click();
+ });
+ await sameBrowserLoad;
+ ensureSingleBrowser();
+
+ // A javascript:void(0); link should also not target to _blank.
+ sameBrowserLoad = BrowserTestUtils.browserLoaded(
+ currentBrowser,
+ false,
+ TEST_PAGE
+ );
+ await SpecialPowers.spawn(currentBrowser, [TEST_PAGE], async newPageURL => {
+ let anchor = content.document.querySelector(`a[href=".."]`);
+ anchor.href = "javascript:void(0);";
+ anchor.addEventListener("click", e => {
+ content.location.href = newPageURL;
+ });
+ let userInput = content.windowUtils.setHandlingUserInput(true);
+ try {
+ anchor.click();
+ } finally {
+ userInput.destruct();
+ }
+ });
+ await sameBrowserLoad;
+ ensureSingleBrowser();
+
+ // Let's also try a non-void javascript: location.
+ sameBrowserLoad = BrowserTestUtils.browserLoaded(
+ currentBrowser,
+ false,
+ TEST_PAGE
+ );
+ await SpecialPowers.spawn(currentBrowser, [TEST_PAGE], async newPageURL => {
+ let anchor = content.document.querySelector(`a[href=".."]`);
+ anchor.href = `javascript:"string-to-navigate-to"`;
+ anchor.addEventListener("click", e => {
+ content.location.href = newPageURL;
+ });
+ let userInput = content.windowUtils.setHandlingUserInput(true);
+ try {
+ anchor.click();
+ } finally {
+ userInput.destruct();
+ }
+ });
+ await sameBrowserLoad;
+ ensureSingleBrowser();
+
+ await BrowserTestUtils.closeWindow(win);
+});
diff --git a/docshell/test/browser/browser_timelineMarkers-01.js b/docshell/test/browser/browser_timelineMarkers-01.js
new file mode 100644
index 0000000000..3109b6d427
--- /dev/null
+++ b/docshell/test/browser/browser_timelineMarkers-01.js
@@ -0,0 +1,45 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that the docShell has the right profile timeline API
+
+const URL = "data:text/html;charset=utf-8,Test page";
+
+add_task(async function() {
+ await BrowserTestUtils.withNewTab({ gBrowser, url: URL }, async function(
+ browser
+ ) {
+ await SpecialPowers.spawn(browser, [], function() {
+ ok(
+ "recordProfileTimelineMarkers" in docShell,
+ "The recordProfileTimelineMarkers attribute exists"
+ );
+ ok(
+ "popProfileTimelineMarkers" in docShell,
+ "The popProfileTimelineMarkers function exists"
+ );
+ ok(
+ docShell.recordProfileTimelineMarkers === false,
+ "recordProfileTimelineMarkers is false by default"
+ );
+ ok(
+ docShell.popProfileTimelineMarkers().length === 0,
+ "There are no markers by default"
+ );
+
+ docShell.recordProfileTimelineMarkers = true;
+ ok(
+ docShell.recordProfileTimelineMarkers === true,
+ "recordProfileTimelineMarkers can be set to true"
+ );
+
+ docShell.recordProfileTimelineMarkers = false;
+ ok(
+ docShell.recordProfileTimelineMarkers === false,
+ "recordProfileTimelineMarkers can be set to false"
+ );
+ });
+ });
+});
diff --git a/docshell/test/browser/browser_timelineMarkers-02.js b/docshell/test/browser/browser_timelineMarkers-02.js
new file mode 100644
index 0000000000..a2b569d9d6
--- /dev/null
+++ b/docshell/test/browser/browser_timelineMarkers-02.js
@@ -0,0 +1,16 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+var TEST_URL =
+ "<!DOCTYPE html><style>" +
+ "body {margin:0; padding: 0;} " +
+ "div {width:100px;height:100px;background:red;} " +
+ ".resize-change-color {width:50px;height:50px;background:blue;} " +
+ ".change-color {width:50px;height:50px;background:yellow;} " +
+ ".add-class {}" +
+ "</style><div></div>";
+TEST_URL = "data:text/html;charset=utf8," + encodeURIComponent(TEST_URL);
+
+var test = makeTimelineTest("browser_timelineMarkers-frame-02.js", TEST_URL);
diff --git a/docshell/test/browser/browser_timelineMarkers-03.js b/docshell/test/browser/browser_timelineMarkers-03.js
new file mode 100644
index 0000000000..b104367c10
--- /dev/null
+++ b/docshell/test/browser/browser_timelineMarkers-03.js
@@ -0,0 +1,8 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+var URL = "data:text/html;charset=utf-8,<p>Test page</p>";
+
+var test = makeTimelineTest("browser_timelineMarkers-frame-03.js", URL);
diff --git a/docshell/test/browser/browser_timelineMarkers-04.js b/docshell/test/browser/browser_timelineMarkers-04.js
new file mode 100644
index 0000000000..3630b0683f
--- /dev/null
+++ b/docshell/test/browser/browser_timelineMarkers-04.js
@@ -0,0 +1,9 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const URL =
+ "http://mochi.test:8888/browser/docshell/test/browser/timelineMarkers-04.html";
+
+var test = makeTimelineTest("browser_timelineMarkers-frame-04.js", URL);
diff --git a/docshell/test/browser/browser_timelineMarkers-05.js b/docshell/test/browser/browser_timelineMarkers-05.js
new file mode 100644
index 0000000000..391ce54a92
--- /dev/null
+++ b/docshell/test/browser/browser_timelineMarkers-05.js
@@ -0,0 +1,16 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+var TEST_URL =
+ "<!DOCTYPE html><style>" +
+ "body {margin:0; padding: 0;} " +
+ "div {width:100px;height:100px;background:red;} " +
+ ".resize-change-color {width:50px;height:50px;background:blue;} " +
+ ".change-color {width:50px;height:50px;background:yellow;} " +
+ ".add-class {}" +
+ "</style><div></div>";
+TEST_URL = "data:text/html;charset=utf8," + encodeURIComponent(TEST_URL);
+
+var test = makeTimelineTest("browser_timelineMarkers-frame-05.js", TEST_URL);
diff --git a/docshell/test/browser/browser_timelineMarkers-frame-02.js b/docshell/test/browser/browser_timelineMarkers-frame-02.js
new file mode 100644
index 0000000000..52d1e43782
--- /dev/null
+++ b/docshell/test/browser/browser_timelineMarkers-frame-02.js
@@ -0,0 +1,185 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env mozilla/frame-script */
+
+// This file expects frame-head.js to be loaded in the environment.
+/* import-globals-from frame-head.js */
+
+"use strict";
+
+// Test that the docShell profile timeline API returns the right markers when
+// restyles, reflows and paints occur
+
+function rectangleContains(rect, x, y, width, height) {
+ return (
+ rect.x <= x && rect.y <= y && rect.width >= width && rect.height >= height
+ );
+}
+
+function sanitizeMarkers(list) {
+ // These markers are currently gathered from all docshells, which may
+ // interfere with this test.
+ return list.filter(e => e.name != "Worker" && e.name != "MinorGC");
+}
+
+var TESTS = [
+ {
+ desc: "Changing the width of the test element",
+ searchFor: "Paint",
+ setup(docShell) {
+ let div = content.document.querySelector("div");
+ div.setAttribute("class", "resize-change-color");
+ },
+ check(markers) {
+ markers = sanitizeMarkers(markers);
+ ok(!!markers.length, "markers were returned");
+ console.log(markers);
+ info(JSON.stringify(markers.filter(m => m.name == "Paint")));
+ ok(
+ markers.some(m => m.name == "Reflow"),
+ "markers includes Reflow"
+ );
+ ok(
+ markers.some(m => m.name == "Paint"),
+ "markers includes Paint"
+ );
+ for (let marker of markers.filter(m => m.name == "Paint")) {
+ // This change should generate at least one rectangle.
+ ok(marker.rectangles.length >= 1, "marker has one rectangle");
+ // One of the rectangles should contain the div.
+ ok(marker.rectangles.some(r => rectangleContains(r, 0, 0, 100, 100)));
+ }
+ ok(
+ markers.some(m => m.name == "Styles"),
+ "markers includes Restyle"
+ );
+ },
+ },
+ {
+ desc: "Changing the test element's background color",
+ searchFor: "Paint",
+ setup(docShell) {
+ let div = content.document.querySelector("div");
+ div.setAttribute("class", "change-color");
+ },
+ check(markers) {
+ markers = sanitizeMarkers(markers);
+ ok(!!markers.length, "markers were returned");
+ ok(
+ !markers.some(m => m.name == "Reflow"),
+ "markers doesn't include Reflow"
+ );
+ ok(
+ markers.some(m => m.name == "Paint"),
+ "markers includes Paint"
+ );
+ for (let marker of markers.filter(m => m.name == "Paint")) {
+ // This change should generate at least one rectangle.
+ ok(marker.rectangles.length >= 1, "marker has one rectangle");
+ // One of the rectangles should contain the div.
+ ok(marker.rectangles.some(r => rectangleContains(r, 0, 0, 50, 50)));
+ }
+ ok(
+ markers.some(m => m.name == "Styles"),
+ "markers includes Restyle"
+ );
+ },
+ },
+ {
+ desc: "Changing the test element's classname",
+ searchFor: "Paint",
+ setup(docShell) {
+ let div = content.document.querySelector("div");
+ div.setAttribute("class", "change-color add-class");
+ },
+ check(markers) {
+ markers = sanitizeMarkers(markers);
+ ok(!!markers.length, "markers were returned");
+ ok(
+ !markers.some(m => m.name == "Reflow"),
+ "markers doesn't include Reflow"
+ );
+ ok(
+ !markers.some(m => m.name == "Paint"),
+ "markers doesn't include Paint"
+ );
+ ok(
+ markers.some(m => m.name == "Styles"),
+ "markers includes Restyle"
+ );
+ },
+ },
+ {
+ desc: "sync console.time/timeEnd",
+ searchFor: "ConsoleTime",
+ setup(docShell) {
+ content.console.time("FOOBAR");
+ content.console.timeEnd("FOOBAR");
+ let markers = docShell.popProfileTimelineMarkers();
+ is(markers.length, 1, "Got one marker");
+ is(markers[0].name, "ConsoleTime", "Got ConsoleTime marker");
+ is(markers[0].causeName, "FOOBAR", "Got ConsoleTime FOOBAR detail");
+ content.console.time("FOO");
+ content.setTimeout(() => {
+ content.console.time("BAR");
+ content.setTimeout(() => {
+ content.console.timeEnd("FOO");
+ content.console.timeEnd("BAR");
+ }, 100);
+ }, 100);
+ },
+ check(markers) {
+ markers = sanitizeMarkers(markers);
+ is(markers.length, 2, "Got 2 markers");
+ is(markers[0].name, "ConsoleTime", "Got first ConsoleTime marker");
+ is(markers[0].causeName, "FOO", "Got ConsoleTime FOO detail");
+ is(markers[1].name, "ConsoleTime", "Got second ConsoleTime marker");
+ is(markers[1].causeName, "BAR", "Got ConsoleTime BAR detail");
+ },
+ },
+ {
+ desc: "Timestamps created by console.timeStamp()",
+ searchFor: "Timestamp",
+ setup(docShell) {
+ content.console.timeStamp("rock");
+ let markers = docShell.popProfileTimelineMarkers();
+ is(markers.length, 1, "Got one marker");
+ is(markers[0].name, "TimeStamp", "Got Timestamp marker");
+ is(markers[0].causeName, "rock", "Got Timestamp label value");
+ content.console.timeStamp("paper");
+ content.console.timeStamp("scissors");
+ content.console.timeStamp();
+ content.console.timeStamp(undefined);
+ },
+ check(markers) {
+ markers = sanitizeMarkers(markers);
+ is(markers.length, 4, "Got 4 markers");
+ is(markers[0].name, "TimeStamp", "Got Timestamp marker");
+ is(markers[0].causeName, "paper", "Got Timestamp label value");
+ is(markers[1].name, "TimeStamp", "Got Timestamp marker");
+ is(markers[1].causeName, "scissors", "Got Timestamp label value");
+ is(
+ markers[2].name,
+ "TimeStamp",
+ "Got empty Timestamp marker when no argument given"
+ );
+ is(markers[2].causeName, void 0, "Got empty Timestamp label value");
+ is(
+ markers[3].name,
+ "TimeStamp",
+ "Got empty Timestamp marker when argument is undefined"
+ );
+ is(markers[3].causeName, void 0, "Got empty Timestamp label value");
+ markers.forEach(m =>
+ is(
+ m.end,
+ m.start,
+ "All Timestamp markers should have identical start/end times"
+ )
+ );
+ },
+ },
+];
+
+timelineContentTest(TESTS);
diff --git a/docshell/test/browser/browser_timelineMarkers-frame-03.js b/docshell/test/browser/browser_timelineMarkers-frame-03.js
new file mode 100644
index 0000000000..4758df7fec
--- /dev/null
+++ b/docshell/test/browser/browser_timelineMarkers-frame-03.js
@@ -0,0 +1,108 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env mozilla/frame-script */
+
+// This file expects frame-head.js to be loaded in the environment.
+/* import-globals-from frame-head.js */
+
+"use strict";
+
+// Test that the docShell profile timeline API returns the right
+// markers for DOM events.
+
+var TESTS = [
+ {
+ desc: "Event dispatch with single handler",
+ searchFor: "DOMEvent",
+ setup(docShell) {
+ content.document.body.addEventListener(
+ "dog",
+ function(e) {
+ console.log("hi");
+ },
+ true
+ );
+ content.document.body.dispatchEvent(new content.Event("dog"));
+ },
+ check(markers) {
+ markers = markers.filter(m => m.name == "DOMEvent");
+ is(markers.length, 1, "Got 1 marker");
+ is(markers[0].type, "dog", "Got dog event name");
+ is(markers[0].eventPhase, 2, "Got phase 2");
+ },
+ },
+ {
+ desc: "Event dispatch with a second handler",
+ searchFor(markers) {
+ return markers.filter(m => m.name == "DOMEvent").length >= 2;
+ },
+ setup(docShell) {
+ content.document.body.addEventListener("dog", function(e) {
+ console.log("hi");
+ });
+ content.document.body.dispatchEvent(new content.Event("dog"));
+ },
+ check(markers) {
+ markers = markers.filter(m => m.name == "DOMEvent");
+ is(markers.length, 2, "Got 2 markers");
+ },
+ },
+ {
+ desc: "Event targeted at child",
+ searchFor(markers) {
+ return markers.filter(m => m.name == "DOMEvent").length >= 2;
+ },
+ setup(docShell) {
+ let child = content.document.body.firstElementChild;
+ child.addEventListener("dog", function(e) {});
+ child.dispatchEvent(new content.Event("dog"));
+ },
+ check(markers) {
+ markers = markers.filter(m => m.name == "DOMEvent");
+ is(markers.length, 2, "Got 2 markers");
+ is(markers[0].eventPhase, 1, "Got phase 1 marker");
+ is(markers[1].eventPhase, 2, "Got phase 2 marker");
+ },
+ },
+ {
+ desc: "Event dispatch on a new document",
+ searchFor(markers) {
+ return markers.filter(m => m.name == "DOMEvent").length >= 2;
+ },
+ setup(docShell) {
+ let doc = content.document.implementation.createHTMLDocument("doc");
+ let p = doc.createElement("p");
+ p.innerHTML = "inside";
+ doc.body.appendChild(p);
+
+ p.addEventListener("zebra", function(e) {
+ console.log("hi");
+ });
+ p.dispatchEvent(new content.Event("zebra"));
+ },
+ check(markers) {
+ markers = markers.filter(m => m.name == "DOMEvent");
+ is(markers.length, 1, "Got 1 marker");
+ },
+ },
+ {
+ desc: "Event dispatch on window",
+ searchFor(markers) {
+ return markers.filter(m => m.name == "DOMEvent").length >= 2;
+ },
+ setup(docShell) {
+ content.window.addEventListener("aardvark", function(e) {
+ console.log("I like ants!");
+ });
+
+ content.window.dispatchEvent(new content.Event("aardvark"));
+ },
+ check(markers) {
+ markers = markers.filter(m => m.name == "DOMEvent");
+ is(markers.length, 1, "Got 1 marker");
+ },
+ },
+];
+
+timelineContentTest(TESTS);
diff --git a/docshell/test/browser/browser_timelineMarkers-frame-04.js b/docshell/test/browser/browser_timelineMarkers-frame-04.js
new file mode 100644
index 0000000000..fb69a22054
--- /dev/null
+++ b/docshell/test/browser/browser_timelineMarkers-frame-04.js
@@ -0,0 +1,125 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env mozilla/frame-script */
+
+// This file expects frame-head.js to be loaded in the environment.
+/* import-globals-from frame-head.js */
+
+"use strict";
+
+// Test that the docShell profile timeline API returns the right
+// markers for XMLHttpRequest events.
+
+var TESTS = [
+ {
+ desc: "Event dispatch from XMLHttpRequest",
+ searchFor(markers) {
+ return markers.filter(m => m.name == "DOMEvent").length >= 5;
+ },
+ setup(docShell) {
+ content.dispatchEvent(new content.Event("dog"));
+ },
+ check(markers) {
+ let domMarkers = markers.filter(m => m.name == "DOMEvent");
+ // One subtlety here is that we have five events: the event we
+ // inject in "setup", plus the four state transition events. The
+ // first state transition is reported synchronously and so should
+ // show up as a nested marker.
+ is(domMarkers.length, 5, "Got 5 markers");
+
+ // We should see some Javascript markers, and they should have a
+ // cause.
+ let jsMarkers = markers.filter(
+ m => m.name == "Javascript" && m.causeName
+ );
+ ok(!!jsMarkers.length, "Got some Javascript markers");
+ is(
+ jsMarkers[0].stack.functionDisplayName,
+ "do_xhr",
+ "Javascript marker has entry point name"
+ );
+ },
+ },
+];
+
+if (
+ !Services.prefs.getBoolPref(
+ "javascript.options.asyncstack_capture_debuggee_only"
+ )
+) {
+ TESTS.push(
+ {
+ desc: "Async stack trace on Javascript marker",
+ searchFor: markers => {
+ return markers.some(
+ m => m.name == "Javascript" && m.causeName == "promise callback"
+ );
+ },
+ setup(docShell) {
+ content.dispatchEvent(new content.Event("promisetest"));
+ },
+ check(markers) {
+ markers = markers.filter(
+ m => m.name == "Javascript" && m.causeName == "promise callback"
+ );
+ ok(!!markers.length, "Found a Javascript marker");
+
+ let frame = markers[0].stack;
+ ok(frame.asyncParent !== null, "Parent frame has async parent");
+ is(
+ frame.asyncParent.asyncCause,
+ "promise callback",
+ "Async parent has correct cause"
+ );
+ let asyncFrame = frame.asyncParent;
+ // Skip over self-hosted parts of our Promise implementation.
+ while (asyncFrame.source === "self-hosted") {
+ asyncFrame = asyncFrame.parent;
+ }
+ is(
+ asyncFrame.functionDisplayName,
+ "do_promise",
+ "Async parent has correct function name"
+ );
+ },
+ },
+ {
+ desc: "Async stack trace on Javascript marker with script",
+ searchFor: markers => {
+ return markers.some(
+ m => m.name == "Javascript" && m.causeName == "promise callback"
+ );
+ },
+ setup(docShell) {
+ content.dispatchEvent(new content.Event("promisescript"));
+ },
+ check(markers) {
+ markers = markers.filter(
+ m => m.name == "Javascript" && m.causeName == "promise callback"
+ );
+ ok(!!markers.length, "Found a Javascript marker");
+
+ let frame = markers[0].stack;
+ ok(frame.asyncParent !== null, "Parent frame has async parent");
+ is(
+ frame.asyncParent.asyncCause,
+ "promise callback",
+ "Async parent has correct cause"
+ );
+ let asyncFrame = frame.asyncParent;
+ // Skip over self-hosted parts of our Promise implementation.
+ while (asyncFrame.source === "self-hosted") {
+ asyncFrame = asyncFrame.parent;
+ }
+ is(
+ asyncFrame.functionDisplayName,
+ "do_promise_script",
+ "Async parent has correct function name"
+ );
+ },
+ }
+ );
+}
+
+timelineContentTest(TESTS);
diff --git a/docshell/test/browser/browser_timelineMarkers-frame-05.js b/docshell/test/browser/browser_timelineMarkers-frame-05.js
new file mode 100644
index 0000000000..e239df6382
--- /dev/null
+++ b/docshell/test/browser/browser_timelineMarkers-frame-05.js
@@ -0,0 +1,152 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env mozilla/frame-script */
+
+// This file expects frame-head.js to be loaded in the environment.
+/* import-globals-from frame-head.js */
+
+"use strict";
+
+function forceSyncReflow(div) {
+ div.setAttribute("class", "resize-change-color");
+ // Force a reflow.
+ return div.offsetWidth;
+}
+
+function testSendingEvent() {
+ content.document.body.dispatchEvent(new content.Event("dog"));
+}
+
+function testConsoleTime() {
+ content.console.time("cats");
+}
+
+function testConsoleTimeEnd() {
+ content.console.timeEnd("cats");
+}
+
+function makePromise() {
+ let resolver;
+ new Promise(function(resolve, reject) {
+ testConsoleTime();
+ resolver = resolve;
+ }).then(function(val) {
+ testConsoleTimeEnd();
+ });
+ return resolver;
+}
+
+function resolvePromise(resolver) {
+ resolver(23);
+}
+
+var TESTS = [
+ {
+ desc: "Stack trace on sync reflow",
+ searchFor: "Reflow",
+ setup(docShell) {
+ let div = content.document.querySelector("div");
+ forceSyncReflow(div);
+ },
+ check(markers) {
+ markers = markers.filter(m => m.name == "Reflow");
+ ok(!!markers.length, "Reflow marker includes stack");
+ ok(markers[0].stack.functionDisplayName == "forceSyncReflow");
+ },
+ },
+ {
+ desc: "Stack trace on DOM event",
+ searchFor: "DOMEvent",
+ setup(docShell) {
+ content.document.body.addEventListener(
+ "dog",
+ function(e) {
+ console.log("hi");
+ },
+ true
+ );
+ testSendingEvent();
+ },
+ check(markers) {
+ markers = markers.filter(m => m.name == "DOMEvent");
+ ok(!!markers.length, "DOMEvent marker includes stack");
+ ok(
+ markers[0].stack.functionDisplayName == "testSendingEvent",
+ "testSendingEvent is on the stack"
+ );
+ },
+ },
+ {
+ desc: "Stack trace on console event",
+ searchFor: "ConsoleTime",
+ setup(docShell) {
+ testConsoleTime();
+ testConsoleTimeEnd();
+ },
+ check(markers) {
+ markers = markers.filter(m => m.name == "ConsoleTime");
+ ok(!!markers.length, "ConsoleTime marker includes stack");
+ ok(
+ markers[0].stack.functionDisplayName == "testConsoleTime",
+ "testConsoleTime is on the stack"
+ );
+ ok(
+ markers[0].endStack.functionDisplayName == "testConsoleTimeEnd",
+ "testConsoleTimeEnd is on the stack"
+ );
+ },
+ },
+];
+
+if (
+ !Services.prefs.getBoolPref(
+ "javascript.options.asyncstack_capture_debuggee_only"
+ )
+) {
+ TESTS.push({
+ desc: "Async stack trace on Promise",
+ searchFor: "ConsoleTime",
+ setup(docShell) {
+ let resolver = makePromise();
+ resolvePromise(resolver);
+ },
+ check(markers) {
+ markers = markers.filter(m => m.name == "ConsoleTime");
+ ok(!!markers.length, "Promise marker includes stack");
+ ok(
+ markers[0].stack.functionDisplayName == "testConsoleTime",
+ "testConsoleTime is on the stack"
+ );
+ let frame = markers[0].endStack;
+ ok(
+ frame.functionDisplayName == "testConsoleTimeEnd",
+ "testConsoleTimeEnd is on the stack"
+ );
+
+ frame = frame.parent;
+ ok(
+ frame.functionDisplayName == "makePromise/<",
+ "makePromise/< is on the stack"
+ );
+ let asyncFrame = frame.asyncParent;
+ ok(asyncFrame !== null, "Frame has async parent");
+ is(
+ asyncFrame.asyncCause,
+ "promise callback",
+ "Async parent has correct cause"
+ );
+ // Skip over self-hosted parts of our Promise implementation.
+ while (asyncFrame.source === "self-hosted") {
+ asyncFrame = asyncFrame.parent;
+ }
+ is(
+ asyncFrame.functionDisplayName,
+ "makePromise",
+ "Async parent has correct function name"
+ );
+ },
+ });
+}
+
+timelineContentTest(TESTS);
diff --git a/docshell/test/browser/browser_title_in_session_history.js b/docshell/test/browser/browser_title_in_session_history.js
new file mode 100644
index 0000000000..bdcbbb7dfe
--- /dev/null
+++ b/docshell/test/browser/browser_title_in_session_history.js
@@ -0,0 +1,63 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function test() {
+ const TEST_PAGE =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "dummy_page.html";
+ await BrowserTestUtils.withNewTab(TEST_PAGE, async browser => {
+ let titles = await ContentTask.spawn(browser, null, () => {
+ return new Promise(resolve => {
+ let titles = [];
+ content.document.body.innerHTML = "<div id='foo'>foo</div>";
+ content.document.title = "Initial";
+ content.history.pushState("1", "1", "1");
+ content.document.title = "1";
+ content.history.pushState("2", "2", "2");
+ content.document.title = "2";
+ content.location.hash = "hash";
+ content.document.title = "3-hash";
+ content.addEventListener(
+ "popstate",
+ () => {
+ content.addEventListener(
+ "popstate",
+ () => {
+ titles.push(content.document.title);
+ resolve(titles);
+ },
+ { once: true }
+ );
+
+ titles.push(content.document.title);
+ // Test going forward a few steps.
+ content.history.go(2);
+ },
+ { once: true }
+ );
+ // Test going back a few steps.
+ content.history.go(-3);
+ });
+ });
+ is(
+ titles[0],
+ "3-hash",
+ "Document.title should have the value to which it was last time set."
+ );
+ is(
+ titles[1],
+ "3-hash",
+ "Document.title should have the value to which it was last time set."
+ );
+ let sh = browser.browsingContext.sessionHistory;
+ let count = sh.count;
+ is(sh.getEntryAtIndex(count - 1).title, "3-hash");
+ is(sh.getEntryAtIndex(count - 2).title, "2");
+ is(sh.getEntryAtIndex(count - 3).title, "1");
+ is(sh.getEntryAtIndex(count - 4).title, "Initial");
+ });
+});
diff --git a/docshell/test/browser/browser_ua_emulation.js b/docshell/test/browser/browser_ua_emulation.js
new file mode 100644
index 0000000000..604f302179
--- /dev/null
+++ b/docshell/test/browser/browser_ua_emulation.js
@@ -0,0 +1,70 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const URL = "data:text/html;charset=utf-8,<iframe id='test-iframe'></iframe>";
+
+// Test that the docShell UA emulation works
+async function contentTaskNoOverride() {
+ let docshell = docShell;
+ is(
+ docshell.browsingContext.customUserAgent,
+ "",
+ "There should initially be no customUserAgent"
+ );
+}
+
+async function contentTaskOverride() {
+ let docshell = docShell;
+ is(
+ docshell.browsingContext.customUserAgent,
+ "foo",
+ "The user agent should be changed to foo"
+ );
+
+ is(
+ content.navigator.userAgent,
+ "foo",
+ "The user agent should be changed to foo"
+ );
+
+ let frameWin = content.document.querySelector("#test-iframe").contentWindow;
+ is(
+ frameWin.navigator.userAgent,
+ "foo",
+ "The UA should be passed on to frames."
+ );
+
+ let newFrame = content.document.createElement("iframe");
+ content.document.body.appendChild(newFrame);
+
+ let newFrameWin = newFrame.contentWindow;
+ is(
+ newFrameWin.navigator.userAgent,
+ "foo",
+ "Newly created frames should use the new UA"
+ );
+
+ newFrameWin.location.reload();
+ await ContentTaskUtils.waitForEvent(newFrame, "load");
+
+ is(
+ newFrameWin.navigator.userAgent,
+ "foo",
+ "New UA should persist across reloads"
+ );
+}
+
+add_task(async function() {
+ await BrowserTestUtils.withNewTab({ gBrowser, url: URL }, async function(
+ browser
+ ) {
+ await SpecialPowers.spawn(browser, [], contentTaskNoOverride);
+
+ let browsingContext = BrowserTestUtils.getBrowsingContextFrom(browser);
+ browsingContext.customUserAgent = "foo";
+
+ await SpecialPowers.spawn(browser, [], contentTaskOverride);
+ });
+});
diff --git a/docshell/test/browser/browser_uriFixupAlternateRedirects.js b/docshell/test/browser/browser_uriFixupAlternateRedirects.js
new file mode 100644
index 0000000000..308d14a296
--- /dev/null
+++ b/docshell/test/browser/browser_uriFixupAlternateRedirects.js
@@ -0,0 +1,66 @@
+"use strict";
+
+const { UrlbarTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/UrlbarTestUtils.sys.mjs"
+);
+
+const REDIRECTURL =
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://www.example.com/browser/docshell/test/browser/redirect_to_example.sjs";
+
+add_task(async function() {
+ // Test both directly setting a value and pressing enter, or setting the
+ // value through input events, like the user would do.
+ const setValueFns = [
+ value => {
+ gURLBar.value = value;
+ },
+ value => {
+ return UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ waitForFocus,
+ value,
+ });
+ },
+ ];
+ for (let setValueFn of setValueFns) {
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "about:blank"
+ );
+ // Enter search terms and start a search.
+ gURLBar.focus();
+ await setValueFn(REDIRECTURL);
+ let errorPageLoaded = BrowserTestUtils.waitForErrorPage(tab.linkedBrowser);
+ EventUtils.synthesizeKey("KEY_Enter");
+ await errorPageLoaded;
+ let [contentURL, originalURL] = await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [],
+ () => {
+ return [
+ content.document.documentURI,
+ content.document.mozDocumentURIIfNotForErrorPages.spec,
+ ];
+ }
+ );
+ info("Page that loaded: " + contentURL);
+ const errorURI = "about:neterror?";
+ ok(contentURL.startsWith(errorURI), "Should be on an error page");
+
+ const contentPrincipal = tab.linkedBrowser.contentPrincipal;
+ ok(
+ contentPrincipal.spec.startsWith(errorURI),
+ "Principal should be for the error page"
+ );
+
+ originalURL = new URL(originalURL);
+ is(
+ originalURL.host,
+ "example",
+ "Should be an error for http://example, not http://www.example.com/"
+ );
+
+ BrowserTestUtils.removeTab(tab);
+ }
+});
diff --git a/docshell/test/browser/browser_uriFixupIntegration.js b/docshell/test/browser/browser_uriFixupIntegration.js
new file mode 100644
index 0000000000..1fce8a97c7
--- /dev/null
+++ b/docshell/test/browser/browser_uriFixupIntegration.js
@@ -0,0 +1,104 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { UrlbarTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/UrlbarTestUtils.sys.mjs"
+);
+const { SearchTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/SearchTestUtils.sys.mjs"
+);
+
+SearchTestUtils.init(this);
+
+const kSearchEngineID = "browser_urifixup_search_engine";
+const kSearchEngineURL = "https://example.com/?search={searchTerms}";
+const kPrivateSearchEngineID = "browser_urifixup_search_engine_private";
+const kPrivateSearchEngineURL = "https://example.com/?private={searchTerms}";
+
+add_setup(async function() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.search.separatePrivateDefault.ui.enabled", true],
+ ["browser.search.separatePrivateDefault", true],
+ ],
+ });
+
+ // Add new fake search engines.
+ await SearchTestUtils.installSearchExtension(
+ {
+ name: kSearchEngineID,
+ search_url: "https://example.com/",
+ search_url_get_params: "search={searchTerms}",
+ },
+ { setAsDefault: true }
+ );
+
+ await SearchTestUtils.installSearchExtension(
+ {
+ name: kPrivateSearchEngineID,
+ search_url: "https://example.com/",
+ search_url_get_params: "private={searchTerms}",
+ },
+ { setAsDefaultPrivate: true }
+ );
+});
+
+add_task(async function test() {
+ // Test both directly setting a value and pressing enter, or setting the
+ // value through input events, like the user would do.
+ const setValueFns = [
+ (value, win) => {
+ win.gURLBar.value = value;
+ },
+ (value, win) => {
+ return UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window: win,
+ waitForFocus: SimpleTest.waitForFocus,
+ value,
+ });
+ },
+ ];
+
+ for (let value of ["foo bar", "brokenprotocol:somethingelse"]) {
+ for (let setValueFn of setValueFns) {
+ for (let inPrivateWindow of [false, true]) {
+ await do_test(value, setValueFn, inPrivateWindow);
+ }
+ }
+ }
+});
+
+async function do_test(value, setValueFn, inPrivateWindow) {
+ info(`Search ${value} in a ${inPrivateWindow ? "private" : "normal"} window`);
+ let win = await BrowserTestUtils.openNewBrowserWindow({
+ private: inPrivateWindow,
+ });
+ // Enter search terms and start a search.
+ win.gURLBar.focus();
+ await setValueFn(value, win);
+
+ EventUtils.synthesizeKey("KEY_Enter", {}, win);
+
+ // Check that we load the correct URL.
+ let escapedValue = encodeURIComponent(value).replace("%20", "+");
+ let searchEngineUrl = inPrivateWindow
+ ? kPrivateSearchEngineURL
+ : kSearchEngineURL;
+ let expectedURL = searchEngineUrl.replace("{searchTerms}", escapedValue);
+ await BrowserTestUtils.browserLoaded(
+ win.gBrowser.selectedBrowser,
+ false,
+ expectedURL
+ );
+ // There should be at least one test.
+ Assert.equal(
+ win.gBrowser.selectedBrowser.currentURI.spec,
+ expectedURL,
+ "New tab should have loaded with expected url."
+ );
+
+ // Cleanup.
+ await BrowserTestUtils.closeWindow(win);
+}
diff --git a/docshell/test/browser/browser_viewsource_chrome_to_content.js b/docshell/test/browser/browser_viewsource_chrome_to_content.js
new file mode 100644
index 0000000000..5c73f75a47
--- /dev/null
+++ b/docshell/test/browser/browser_viewsource_chrome_to_content.js
@@ -0,0 +1,20 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const TEST_PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+const TEST_URI = `view-source:${TEST_PATH}dummy_page.html`;
+
+add_task(async function chrome_to_content_view_source() {
+ await BrowserTestUtils.withNewTab("about:mozilla", async browser => {
+ is(browser.documentURI.spec, "about:mozilla");
+
+ // This process switch would previously crash in debug builds due to assertion failures.
+ BrowserTestUtils.loadURI(browser, TEST_URI);
+ await BrowserTestUtils.browserLoaded(browser);
+ is(browser.documentURI.spec, TEST_URI);
+ });
+});
diff --git a/docshell/test/browser/browser_viewsource_multipart.js b/docshell/test/browser/browser_viewsource_multipart.js
new file mode 100644
index 0000000000..4c1d74f2d5
--- /dev/null
+++ b/docshell/test/browser/browser_viewsource_multipart.js
@@ -0,0 +1,44 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const TEST_PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+const MULTIPART_URI = `${TEST_PATH}file_basic_multipart.sjs`;
+
+add_task(async function viewsource_multipart_uri() {
+ await BrowserTestUtils.withNewTab("about:blank", async browser => {
+ BrowserTestUtils.loadURI(browser, MULTIPART_URI);
+ await BrowserTestUtils.browserLoaded(browser);
+ is(browser.currentURI.spec, MULTIPART_URI);
+
+ // Continue probing the URL until we find the h1 we're expecting. This
+ // should handle cases where we somehow beat the second document having
+ // loaded.
+ await TestUtils.waitForCondition(async () => {
+ let value = await SpecialPowers.spawn(browser, [], async () => {
+ let headers = content.document.querySelectorAll("h1");
+ is(headers.length, 1, "only one h1 should be present");
+ return headers[0].textContent;
+ });
+
+ ok(value == "First" || value == "Second", "some other value was found?");
+ return value == "Second";
+ });
+
+ // Load a view-source version of the page, which should show the full
+ // content, not handling multipart.
+ BrowserTestUtils.loadURI(browser, `view-source:${MULTIPART_URI}`);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ let viewSourceContent = await SpecialPowers.spawn(browser, [], async () => {
+ return content.document.body.textContent;
+ });
+
+ ok(viewSourceContent.includes("<h1>First</h1>"), "first header");
+ ok(viewSourceContent.includes("<h1>Second</h1>"), "second header");
+ ok(viewSourceContent.includes("BOUNDARY"), "boundary");
+ });
+});
diff --git a/docshell/test/browser/dummy_iframe_page.html b/docshell/test/browser/dummy_iframe_page.html
new file mode 100644
index 0000000000..12ce921856
--- /dev/null
+++ b/docshell/test/browser/dummy_iframe_page.html
@@ -0,0 +1,8 @@
+<html>
+<head> <meta charset="utf-8"> </head>
+ <body>
+ just a dummy html file with an iframe
+ <iframe id="frame1" src="dummy_page.html?sub_entry=0"></iframe>
+ <iframe id="frame2" src="dummy_page.html?sub_entry=0"></iframe>
+ </body>
+</html>
diff --git a/docshell/test/browser/dummy_page.html b/docshell/test/browser/dummy_page.html
new file mode 100644
index 0000000000..59bf2a5f8f
--- /dev/null
+++ b/docshell/test/browser/dummy_page.html
@@ -0,0 +1,6 @@
+<html>
+<head> <meta charset="utf-8"> </head>
+ <body>
+ just a dummy html file
+ </body>
+</html>
diff --git a/docshell/test/browser/favicon_bug655270.ico b/docshell/test/browser/favicon_bug655270.ico
new file mode 100644
index 0000000000..d44438903b
--- /dev/null
+++ b/docshell/test/browser/favicon_bug655270.ico
Binary files differ
diff --git a/docshell/test/browser/file_backforward_restore_scroll.html b/docshell/test/browser/file_backforward_restore_scroll.html
new file mode 100644
index 0000000000..5a40b36c10
--- /dev/null
+++ b/docshell/test/browser/file_backforward_restore_scroll.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+</head>
+<body>
+ <iframe src="http://mochi.test:8888/"></iframe>
+ <iframe src="http://example.com/"></iframe>
+</body>
+</html>
diff --git a/docshell/test/browser/file_backforward_restore_scroll.html^headers^ b/docshell/test/browser/file_backforward_restore_scroll.html^headers^
new file mode 100644
index 0000000000..59ba296103
--- /dev/null
+++ b/docshell/test/browser/file_backforward_restore_scroll.html^headers^
@@ -0,0 +1 @@
+Cache-control: no-store
diff --git a/docshell/test/browser/file_basic_multipart.sjs b/docshell/test/browser/file_basic_multipart.sjs
new file mode 100644
index 0000000000..5e89b93948
--- /dev/null
+++ b/docshell/test/browser/file_basic_multipart.sjs
@@ -0,0 +1,24 @@
+"use strict";
+
+function handleRequest(request, response) {
+ response.setHeader(
+ "Content-Type",
+ "multipart/x-mixed-replace;boundary=BOUNDARY",
+ false
+ );
+ response.setStatusLine(request.httpVersion, 200, "OK");
+
+ response.write(`--BOUNDARY
+Content-Type: text/html
+
+<h1>First</h1>
+Will be replaced
+--BOUNDARY
+Content-Type: text/html
+
+<h1>Second</h1>
+This will stick around
+--BOUNDARY
+--BOUNDARY--
+`);
+}
diff --git a/docshell/test/browser/file_bug1046022.html b/docshell/test/browser/file_bug1046022.html
new file mode 100644
index 0000000000..27a1e1f079
--- /dev/null
+++ b/docshell/test/browser/file_bug1046022.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Bug 1046022 - test navigating inside onbeforeunload</title>
+ </head>
+ <body>
+ Waiting for onbeforeunload to hit...
+ </body>
+
+ <script>
+var testFns = [
+ function(e) {
+ e.target.location.href = "otherpage-href-set.html";
+ return "stop";
+ },
+ function(e) {
+ e.target.location.reload();
+ return "stop";
+ },
+ function(e) {
+ e.currentTarget.stop();
+ return "stop";
+ },
+ function(e) {
+ e.target.location.replace("otherpage-location-replaced.html");
+ return "stop";
+ },
+ function(e) {
+ var link = e.target.createElement("a");
+ link.href = "otherpage.html";
+ e.target.body.appendChild(link);
+ link.click();
+ return "stop";
+ },
+ function(e) {
+ var link = e.target.createElement("a");
+ link.href = "otherpage.html";
+ link.setAttribute("target", "_blank");
+ e.target.body.appendChild(link);
+ link.click();
+ return "stop";
+ },
+ function(e) {
+ var link = e.target.createElement("a");
+ link.href = e.target.location.href;
+ e.target.body.appendChild(link);
+ link.setAttribute("target", "somearbitrarywindow");
+ link.click();
+ return "stop";
+ },
+];
+ </script>
+</html>
diff --git a/docshell/test/browser/file_bug1206879.html b/docshell/test/browser/file_bug1206879.html
new file mode 100644
index 0000000000..5313902a9b
--- /dev/null
+++ b/docshell/test/browser/file_bug1206879.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Test page for bug 1206879</title>
+ </head>
+ <body>
+ <iframe src="http://example.com/"></iframe>
+ </body>
+</html>
diff --git a/docshell/test/browser/file_bug1328501.html b/docshell/test/browser/file_bug1328501.html
new file mode 100644
index 0000000000..517ef53e02
--- /dev/null
+++ b/docshell/test/browser/file_bug1328501.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Page with iframes</title>
+ <script type="application/javascript">
+ let promiseResolvers = {
+ "testFrame1": {},
+ "testFrame2": {},
+ };
+ let promises = [
+ new Promise(r => promiseResolvers.testFrame1.resolve = r),
+ new Promise(r => promiseResolvers.testFrame2.resolve = r),
+ ];
+ function frameLoaded(frame) {
+ promiseResolvers[frame].resolve();
+ }
+ Promise.all(promises).then(() => window.dispatchEvent(new Event("frames-loaded")));
+ </script>
+ </head>
+ <body onunload="">
+ <div>
+ <iframe id="testFrame1" src="dummy_page.html" onload="frameLoaded(this.id);" ></iframe>
+ <iframe id="testFrame2" src="dummy_page.html" onload="frameLoaded(this.id);" ></iframe>
+ </div>
+ </body>
+</html>
diff --git a/docshell/test/browser/file_bug1328501_frame.html b/docshell/test/browser/file_bug1328501_frame.html
new file mode 100644
index 0000000000..156dd41eaa
--- /dev/null
+++ b/docshell/test/browser/file_bug1328501_frame.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<html lang="en">
+ <body>Subframe page for testing</body>
+</html>
diff --git a/docshell/test/browser/file_bug1328501_framescript.js b/docshell/test/browser/file_bug1328501_framescript.js
new file mode 100644
index 0000000000..19c86c75e7
--- /dev/null
+++ b/docshell/test/browser/file_bug1328501_framescript.js
@@ -0,0 +1,38 @@
+// Forward iframe loaded event.
+
+/* eslint-env mozilla/frame-script */
+
+addEventListener(
+ "frames-loaded",
+ e => sendAsyncMessage("test:frames-loaded"),
+ true,
+ true
+);
+
+let requestObserver = {
+ observe(subject, topic, data) {
+ if (topic == "http-on-opening-request") {
+ // Get DOMWindow on all child docshells to force about:blank
+ // content viewers being created.
+ getChildDocShells().map(ds => {
+ ds
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsILoadContext).associatedWindow;
+ });
+ }
+ },
+ QueryInterface: ChromeUtils.generateQI(["nsIObserver"]),
+};
+Services.obs.addObserver(requestObserver, "http-on-opening-request");
+addEventListener("unload", e => {
+ if (e.target == this) {
+ Services.obs.removeObserver(requestObserver, "http-on-opening-request");
+ }
+});
+
+function getChildDocShells() {
+ return docShell.getAllDocShellsInSubtree(
+ Ci.nsIDocShellTreeItem.typeAll,
+ Ci.nsIDocShell.ENUMERATE_FORWARDS
+ );
+}
diff --git a/docshell/test/browser/file_bug1543077-3-child.html b/docshell/test/browser/file_bug1543077-3-child.html
new file mode 100644
index 0000000000..858a4623ed
--- /dev/null
+++ b/docshell/test/browser/file_bug1543077-3-child.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>No encoding declaration in parent or child</title>
+</head>
+<body>
+<p>Hiragana letter a if decoded as ISO-2022-JP: $B$"(B</p>
+</body>
+</html>
+
diff --git a/docshell/test/browser/file_bug1543077-3.html b/docshell/test/browser/file_bug1543077-3.html
new file mode 100644
index 0000000000..c4f467dd3f
--- /dev/null
+++ b/docshell/test/browser/file_bug1543077-3.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>No encoding declaration in parent or child</title>
+</head>
+<body>
+<h1>No encoding declaration in parent or child</h1>
+
+<p>Hiragana letter a if decoded as ISO-2022-JP: $B$"(B</p>
+
+<iframe src="file_bug1543077-3-child.html"></iframe>
+
+</body>
+</html>
+
diff --git a/docshell/test/browser/file_bug1622420.html b/docshell/test/browser/file_bug1622420.html
new file mode 100644
index 0000000000..63beb38302
--- /dev/null
+++ b/docshell/test/browser/file_bug1622420.html
@@ -0,0 +1 @@
+<iframe src="http://example.com/"></iframe>
diff --git a/docshell/test/browser/file_bug1648464-1-child.html b/docshell/test/browser/file_bug1648464-1-child.html
new file mode 100644
index 0000000000..7bb1ad965b
--- /dev/null
+++ b/docshell/test/browser/file_bug1648464-1-child.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset=windows-1252>
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>windows-1252 in parent and child, actually EUC-JP</title>
+</head>
+<body>
+<p>Hiragana letter a if decoded as EUC-JP: </p>
+<p>ʸ¸Ǥ</p>
+</body>
+</html>
+
diff --git a/docshell/test/browser/file_bug1648464-1.html b/docshell/test/browser/file_bug1648464-1.html
new file mode 100644
index 0000000000..2051cf61ed
--- /dev/null
+++ b/docshell/test/browser/file_bug1648464-1.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset=windows-1252>
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>windows-1252 in parent and child, actually EUC-JP</title>
+</head>
+<body>
+<h1>windows-1252 in parent and child, actually EUC-JP</h1>
+
+<p>Hiragana letter a if decoded as EUC-JP: </p>
+<p>ʸ¸Ǥ</p>
+
+<iframe src="file_bug1648464-1-child.html"></iframe>
+
+</body>
+</html>
+
diff --git a/docshell/test/browser/file_bug1673702.json b/docshell/test/browser/file_bug1673702.json
new file mode 100644
index 0000000000..6d7227eb1f
--- /dev/null
+++ b/docshell/test/browser/file_bug1673702.json
@@ -0,0 +1 @@
+{ "version": 1, }
diff --git a/docshell/test/browser/file_bug1673702.json^headers^ b/docshell/test/browser/file_bug1673702.json^headers^
new file mode 100644
index 0000000000..6010bfd188
--- /dev/null
+++ b/docshell/test/browser/file_bug1673702.json^headers^
@@ -0,0 +1 @@
+Content-Type: application/json; charset=utf-8
diff --git a/docshell/test/browser/file_bug1688368-1.sjs b/docshell/test/browser/file_bug1688368-1.sjs
new file mode 100644
index 0000000000..0693b7970c
--- /dev/null
+++ b/docshell/test/browser/file_bug1688368-1.sjs
@@ -0,0 +1,44 @@
+"use strict";
+
+const DELAY = 1 * 1000; // Delay one second before completing the request.
+
+let nsTimer = Components.Constructor(
+ "@mozilla.org/timer;1",
+ "nsITimer",
+ "initWithCallback"
+);
+
+let timer;
+
+function handleRequest(request, response) {
+ response.processAsync();
+
+ response.setHeader("Content-Type", "text/html", false);
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.write(`<!DOCTYPE html>
+<html>
+<head>
+ <title>UTF-8 file, 1024 bytes long!</title>
+</head>
+<body>`);
+
+ // Note: We need to store a reference to the timer to prevent it from being
+ // canceled when it's GCed.
+ timer = new nsTimer(
+ () => {
+ var snowmen =
+ "\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083\u00E2\u0098\u0083";
+ response.write(
+ snowmen +
+ `
+</body>
+</html>
+
+`
+ );
+ response.finish();
+ },
+ DELAY,
+ Ci.nsITimer.TYPE_ONE_SHOT
+ );
+}
diff --git a/docshell/test/browser/file_bug1691153.html b/docshell/test/browser/file_bug1691153.html
new file mode 100644
index 0000000000..dea144eb41
--- /dev/null
+++ b/docshell/test/browser/file_bug1691153.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>bug 1691153</title>
+</head>
+<body>
+<h1>bug 1691153</h1>
+<script>
+function toBlobURL(data, mimeType) {
+ return URL.createObjectURL(
+ new Blob([data], {
+ type: mimeType,
+ })
+ );
+}
+// closing script element literal split up to not end the parent script element
+let testurl = toBlobURL("<body></body>", "text/html");
+addEventListener("message", event => {
+ if (event.data == "getblob") {
+ postMessage({ bloburl: testurl }, "*");
+ }
+});
+// the blob URL should have a content principal
+</script>
+</body>
+</html>
diff --git a/docshell/test/browser/file_bug1716290-1.sjs b/docshell/test/browser/file_bug1716290-1.sjs
new file mode 100644
index 0000000000..83e6eede3d
--- /dev/null
+++ b/docshell/test/browser/file_bug1716290-1.sjs
@@ -0,0 +1,21 @@
+function handleRequest(request, response) {
+ if (getState("reloaded") == "reloaded") {
+ response.setHeader(
+ "Content-Type",
+ "text/html; charset=windows-1254",
+ false
+ );
+ response.write("\u00E4");
+ } else {
+ response.setHeader("Content-Type", "text/html; charset=Shift_JIS", false);
+ if (getState("loaded") == "loaded") {
+ setState("reloaded", "reloaded");
+ } else {
+ setState("loaded", "loaded");
+ }
+ // kilobyte to force late-detection reload
+ response.write("a".repeat(1024));
+ response.write("<body>");
+ response.write("\u00E4");
+ }
+}
diff --git a/docshell/test/browser/file_bug1716290-2.sjs b/docshell/test/browser/file_bug1716290-2.sjs
new file mode 100644
index 0000000000..e695259e30
--- /dev/null
+++ b/docshell/test/browser/file_bug1716290-2.sjs
@@ -0,0 +1,18 @@
+function handleRequest(request, response) {
+ if (getState("reloaded") == "reloaded") {
+ response.setHeader("Content-Type", "text/html", false);
+ response.write("<meta charset=iso-2022-kr>\u00E4");
+ } else {
+ response.setHeader("Content-Type", "text/html", false);
+ if (getState("loaded") == "loaded") {
+ setState("reloaded", "reloaded");
+ } else {
+ setState("loaded", "loaded");
+ }
+ response.write("<meta charset=Shift_JIS>");
+ // kilobyte to force late-detection reload
+ response.write("a".repeat(1024));
+ response.write("<body>");
+ response.write("\u00E4");
+ }
+}
diff --git a/docshell/test/browser/file_bug1716290-3.sjs b/docshell/test/browser/file_bug1716290-3.sjs
new file mode 100644
index 0000000000..7a302e05e4
--- /dev/null
+++ b/docshell/test/browser/file_bug1716290-3.sjs
@@ -0,0 +1,17 @@
+function handleRequest(request, response) {
+ if (getState("reloaded") == "reloaded") {
+ response.setHeader("Content-Type", "text/html; charset=iso-2022-kr", false);
+ response.write("\u00E4");
+ } else {
+ response.setHeader("Content-Type", "text/html; charset=Shift_JIS", false);
+ if (getState("loaded") == "loaded") {
+ setState("reloaded", "reloaded");
+ } else {
+ setState("loaded", "loaded");
+ }
+ // kilobyte to force late-detection reload
+ response.write("a".repeat(1024));
+ response.write("<body>");
+ response.write("\u00E4");
+ }
+}
diff --git a/docshell/test/browser/file_bug1716290-4.sjs b/docshell/test/browser/file_bug1716290-4.sjs
new file mode 100644
index 0000000000..36753ef532
--- /dev/null
+++ b/docshell/test/browser/file_bug1716290-4.sjs
@@ -0,0 +1,17 @@
+function handleRequest(request, response) {
+ if (getState("reloaded") == "reloaded") {
+ response.setHeader("Content-Type", "text/html", false);
+ response.write("\u00FE\u00FF\u00E4");
+ } else {
+ response.setHeader("Content-Type", "text/html; charset=Shift_JIS", false);
+ if (getState("loaded") == "loaded") {
+ setState("reloaded", "reloaded");
+ } else {
+ setState("loaded", "loaded");
+ }
+ // kilobyte to force late-detection reload
+ response.write("a".repeat(1024));
+ response.write("<body>");
+ response.write("\u00E4");
+ }
+}
diff --git a/docshell/test/browser/file_bug1736248-1.html b/docshell/test/browser/file_bug1736248-1.html
new file mode 100644
index 0000000000..177acb8f77
--- /dev/null
+++ b/docshell/test/browser/file_bug1736248-1.html
@@ -0,0 +1,4 @@
+Kilobyte of ASCII followed by UTF-8.
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+
+Hej världen!
diff --git a/docshell/test/browser/file_bug234628-1-child.html b/docshell/test/browser/file_bug234628-1-child.html
new file mode 100644
index 0000000000..c36197ac4f
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-1-child.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>No encoding declaration in parent or child</title>
+</head>
+<body>
+<p>Euro sign if decoded as Windows-1252: </p>
+<p>a with diaeresis if decoded as Windows-1252: </p>
+</body>
+</html>
+
diff --git a/docshell/test/browser/file_bug234628-1.html b/docshell/test/browser/file_bug234628-1.html
new file mode 100644
index 0000000000..11c523ccd9
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-1.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>No encoding declaration in parent or child</title>
+</head>
+<body>
+<h1>No encoding declaration in parent or child</h1>
+
+<p>Euro sign if decoded as Windows-1252: </p>
+<p>a with diaeresis if decoded as Windows-1252: </p>
+
+<iframe src="file_bug234628-1-child.html"></iframe>
+
+</body>
+</html>
+
diff --git a/docshell/test/browser/file_bug234628-10-child.xhtml b/docshell/test/browser/file_bug234628-10-child.xhtml
new file mode 100644
index 0000000000..cccf6f2bc0
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-10-child.xhtml
@@ -0,0 +1,4 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head><title>XML child with no encoding declaration</title></head>
+<body><p>Euro sign if decoded as UTF-8: €</p></body>
+</html>
diff --git a/docshell/test/browser/file_bug234628-10.html b/docshell/test/browser/file_bug234628-10.html
new file mode 100644
index 0000000000..78b8f0035d
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-10.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>No encoding declaration in HTML parent or XHTML child</title>
+</head>
+<body>
+<h1>No encoding declaration in HTML parent or XHTML child</h1>
+
+<p>Euro sign if decoded as Windows-1252: </p>
+<p>a with diaeresis if decoded as Windows-1252: </p>
+
+<iframe src="file_bug234628-10-child.xhtml"></iframe>
+
+</body>
+</html>
+
diff --git a/docshell/test/browser/file_bug234628-11-child.xhtml b/docshell/test/browser/file_bug234628-11-child.xhtml
new file mode 100644
index 0000000000..11ef668b0c
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-11-child.xhtml
@@ -0,0 +1,4 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head><title>No encoding declaration in HTML parent and HTTP declaration in XHTML child</title></head>
+<body><p>Euro sign if decoded as UTF-8: €</p></body>
+</html>
diff --git a/docshell/test/browser/file_bug234628-11-child.xhtml^headers^ b/docshell/test/browser/file_bug234628-11-child.xhtml^headers^
new file mode 100644
index 0000000000..30fb304056
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-11-child.xhtml^headers^
@@ -0,0 +1 @@
+Content-Type: application/xhtml+xml; charset=utf-8
diff --git a/docshell/test/browser/file_bug234628-11.html b/docshell/test/browser/file_bug234628-11.html
new file mode 100644
index 0000000000..21c5b733e0
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-11.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>No encoding declaration in HTML parent and HTTP declaration in XHTML child</title>
+</head>
+<body>
+<h1>No encoding declaration in HTML parent and HTTP declaration in XHTML child</h1>
+
+<p>Euro sign if decoded as Windows-1252: </p>
+<p>a with diaeresis if decoded as Windows-1252: </p>
+
+<iframe src="file_bug234628-11-child.xhtml"></iframe>
+
+</body>
+</html>
+
diff --git a/docshell/test/browser/file_bug234628-2-child.html b/docshell/test/browser/file_bug234628-2-child.html
new file mode 100644
index 0000000000..0acd2e0b27
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-2-child.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>No encoding declaration in parent or child</title>
+</head>
+<body>
+<p>Euro sign if decoded as UTF-8: €</p>
+<p>a with diaeresis if decoded as UTF-8: ä</p>
+</body>
+</html>
+
diff --git a/docshell/test/browser/file_bug234628-2.html b/docshell/test/browser/file_bug234628-2.html
new file mode 100644
index 0000000000..a87d29e126
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-2.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>No encoding declaration in parent or child</title>
+</head>
+<body>
+<h1>No encoding declaration in parent or child</h1>
+
+<p>Euro sign if decoded as Windows-1252: </p>
+<p>a with diaeresis if decoded as Windows-1252: </p>
+
+<iframe src="file_bug234628-2-child.html"></iframe>
+
+</body>
+</html>
+
diff --git a/docshell/test/browser/file_bug234628-3-child.html b/docshell/test/browser/file_bug234628-3-child.html
new file mode 100644
index 0000000000..a6ad832310
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-3-child.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>meta declaration in parent and child</title>
+</head>
+<body>
+<p>Euro sign if decoded as UTF-8: €</p>
+<p>a with diaeresis if decoded as UTF-8: ä</p>
+</body>
+</html>
+
diff --git a/docshell/test/browser/file_bug234628-3.html b/docshell/test/browser/file_bug234628-3.html
new file mode 100644
index 0000000000..8caab60402
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-3.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="windows-1252">
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>meta declaration in parent and child</title>
+</head>
+<body>
+<h1>meta declaration in parent and child</h1>
+
+<p>Euro sign if decoded as Windows-1252: </p>
+<p>a with diaeresis if decoded as Windows-1252: </p>
+
+<iframe src="file_bug234628-3-child.html"></iframe>
+
+</body>
+</html>
+
diff --git a/docshell/test/browser/file_bug234628-4-child.html b/docshell/test/browser/file_bug234628-4-child.html
new file mode 100644
index 0000000000..f0e7c2c058
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-4-child.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>meta declaration in parent and BOM in child</title>
+</head>
+<body>
+<p>Euro sign if decoded as UTF-8: €</p>
+<p>a with diaeresis if decoded as UTF-8: ä</p>
+</body>
+</html>
+
diff --git a/docshell/test/browser/file_bug234628-4.html b/docshell/test/browser/file_bug234628-4.html
new file mode 100644
index 0000000000..0137579010
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-4.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="windows-1252">
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>meta declaration in parent and BOM in child</title>
+</head>
+<body>
+<h1>meta declaration in parent and BOM in child</h1>
+
+<p>Euro sign if decoded as Windows-1252: </p>
+<p>a with diaeresis if decoded as Windows-1252: </p>
+
+<iframe src="file_bug234628-4-child.html"></iframe>
+
+</body>
+</html>
+
diff --git a/docshell/test/browser/file_bug234628-5-child.html b/docshell/test/browser/file_bug234628-5-child.html
new file mode 100644
index 0000000000..a650552f63
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-5-child.html
Binary files differ
diff --git a/docshell/test/browser/file_bug234628-5.html b/docshell/test/browser/file_bug234628-5.html
new file mode 100644
index 0000000000..987e6420be
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-5.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="windows-1252">
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>meta declaration in parent and UTF-16 BOM in child</title>
+</head>
+<body>
+<h1>meta declaration in parent and UTF-16 BOM in child</h1>
+
+<p>Euro sign if decoded as Windows-1252: </p>
+<p>a with diaeresis if decoded as Windows-1252: </p>
+
+<iframe src="file_bug234628-5-child.html"></iframe>
+
+</body>
+</html>
+
diff --git a/docshell/test/browser/file_bug234628-6-child.html b/docshell/test/browser/file_bug234628-6-child.html
new file mode 100644
index 0000000000..52c37f2596
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-6-child.html
Binary files differ
diff --git a/docshell/test/browser/file_bug234628-6-child.html^headers^ b/docshell/test/browser/file_bug234628-6-child.html^headers^
new file mode 100644
index 0000000000..bfdcf487fb
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-6-child.html^headers^
@@ -0,0 +1 @@
+Content-Type: text/html; charset=utf-16be
diff --git a/docshell/test/browser/file_bug234628-6.html b/docshell/test/browser/file_bug234628-6.html
new file mode 100644
index 0000000000..9d7fc580c3
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-6.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="windows-1252">
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>meta declaration in parent and BOMless UTF-16 with HTTP charset in child</title>
+</head>
+<body>
+<h1>meta declaration in parent and BOMless UTF-16 with HTTP charset in child</h1>
+
+<p>Euro sign if decoded as Windows-1252: </p>
+<p>a with diaeresis if decoded as Windows-1252: </p>
+
+<iframe src="file_bug234628-6-child.html"></iframe>
+
+</body>
+</html>
+
diff --git a/docshell/test/browser/file_bug234628-8-child.html b/docshell/test/browser/file_bug234628-8-child.html
new file mode 100644
index 0000000000..254e0fb2b3
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-8-child.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>meta declaration in parent and no declaration in child</title>
+</head>
+<body>
+<p>Capital dje if decoded as Windows-1251: </p>
+
+</body>
+</html>
+
diff --git a/docshell/test/browser/file_bug234628-8.html b/docshell/test/browser/file_bug234628-8.html
new file mode 100644
index 0000000000..b44e91801c
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-8.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="windows-1251">
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>meta declaration in parent and no declaration in child</title>
+</head>
+<body>
+<h1>meta declaration in parent and no declaration in child</h1>
+
+<p>Capital dje if decoded as Windows-1251: </p>
+
+<iframe src="file_bug234628-8-child.html"></iframe>
+
+</body>
+</html>
+
diff --git a/docshell/test/browser/file_bug234628-9-child.html b/docshell/test/browser/file_bug234628-9-child.html
new file mode 100644
index 0000000000..a86b14d7ee
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-9-child.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>UTF-16 with BOM in parent and no declaration in child</title>
+</head>
+<body>
+<p>Euro sign if decoded as Windows-1251: </p>
+
+</body>
+</html>
+
diff --git a/docshell/test/browser/file_bug234628-9.html b/docshell/test/browser/file_bug234628-9.html
new file mode 100644
index 0000000000..8a469da3aa
--- /dev/null
+++ b/docshell/test/browser/file_bug234628-9.html
Binary files differ
diff --git a/docshell/test/browser/file_bug420605.html b/docshell/test/browser/file_bug420605.html
new file mode 100644
index 0000000000..8424b92f8f
--- /dev/null
+++ b/docshell/test/browser/file_bug420605.html
@@ -0,0 +1,31 @@
+<head>
+<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg=="/>
+ <title>Page Title for Bug 420605</title>
+</head>
+<body>
+ <h1>Fragment links</h1>
+
+ <p>This page has a bunch of fragment links to sections below:</p>
+
+ <ul>
+ <li><a id="firefox-link" href="#firefox">Firefox</a></li>
+ <li><a id="thunderbird-link" href="#thunderbird">Thunderbird</a></li>
+ <li><a id="seamonkey-link" href="#seamonkey">Seamonkey</a></li>
+ </ul>
+
+ <p>And here are the sections:</p>
+
+ <h2 id="firefox">Firefox</h2>
+
+ <p>Firefox is a browser.</p>
+
+ <h2 id="thunderbird">Thunderbird</h2>
+
+ <p>Thunderbird is an email client</p>
+
+ <h2 id="seamonkey">Seamonkey</h2>
+
+ <p>Seamonkey is the all-in-one application.</p>
+
+</body>
+</html>
diff --git a/docshell/test/browser/file_bug503832.html b/docshell/test/browser/file_bug503832.html
new file mode 100644
index 0000000000..338631c8a0
--- /dev/null
+++ b/docshell/test/browser/file_bug503832.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<!--
+Test page for https://bugzilla.mozilla.org/show_bug.cgi?id=503832
+-->
+<head>
+ <title>Page Title for Bug 503832</title>
+</head>
+<body>
+ <h1>Fragment links</h1>
+
+ <p>This page has a bunch of fragment links to sections below:</p>
+
+ <ul>
+ <li><a id="firefox-link" href="#firefox">Firefox</a></li>
+ <li><a id="thunderbird-link" href="#thunderbird">Thunderbird</a></li>
+ <li><a id="seamonkey-link" href="#seamonkey">Seamonkey</a></li>
+ </ul>
+
+ <p>And here are the sections:</p>
+
+ <h2 id="firefox">Firefox</h2>
+
+ <p>Firefox is a browser.</p>
+
+ <h2 id="thunderbird">Thunderbird</h2>
+
+ <p>Thunderbird is an email client</p>
+
+ <h2 id="seamonkey">Seamonkey</h2>
+
+ <p>Seamonkey is the all-in-one application.</p>
+
+</body>
+</html>
diff --git a/docshell/test/browser/file_bug655270.html b/docshell/test/browser/file_bug655270.html
new file mode 100644
index 0000000000..0c08d982b1
--- /dev/null
+++ b/docshell/test/browser/file_bug655270.html
@@ -0,0 +1,11 @@
+<html>
+
+<head>
+ <link rel='icon' href='favicon_bug655270.ico'>
+</head>
+
+<body>
+Nothing to see here...
+</body>
+
+</html>
diff --git a/docshell/test/browser/file_bug670318.html b/docshell/test/browser/file_bug670318.html
new file mode 100644
index 0000000000..a78e8fcb19
--- /dev/null
+++ b/docshell/test/browser/file_bug670318.html
@@ -0,0 +1,23 @@
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<script>
+function load() {
+ function next() {
+ if (count < 5)
+ iframe.src = "data:text/html;charset=utf-8,iframe " + (++count);
+ }
+
+ var count = 0;
+ var iframe = document.createElement("iframe");
+ iframe.onload = function() { setTimeout(next, 0); };
+ document.body.appendChild(iframe);
+
+ setTimeout(next, 0);
+}
+</script>
+</head>
+
+<body onload="load()">
+Testcase
+</body>
+</html>
diff --git a/docshell/test/browser/file_bug673087-1-child.html b/docshell/test/browser/file_bug673087-1-child.html
new file mode 100644
index 0000000000..7bb1ad965b
--- /dev/null
+++ b/docshell/test/browser/file_bug673087-1-child.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset=windows-1252>
+<meta content="width=device-width, initial-scale=1" name="viewport">
+<title>windows-1252 in parent and child, actually EUC-JP</title>
+</head>
+<body>
+<p>Hiragana letter a if decoded as EUC-JP: </p>
+<p>ʸ¸Ǥ</p>
+</body>
+</html>
+
diff --git a/docshell/test/browser/file_bug673087-1.html b/docshell/test/browser/file_bug673087-1.html
new file mode 100644
index 0000000000..3dbea43d66
--- /dev/null
+++ b/docshell/test/browser/file_bug673087-1.html
Binary files differ
diff --git a/docshell/test/browser/file_bug673087-1.html^headers^ b/docshell/test/browser/file_bug673087-1.html^headers^
new file mode 100644
index 0000000000..2340a89c93
--- /dev/null
+++ b/docshell/test/browser/file_bug673087-1.html^headers^
@@ -0,0 +1 @@
+Content-Type: text/html; charset=windows-1252
diff --git a/docshell/test/browser/file_bug673087-2.html b/docshell/test/browser/file_bug673087-2.html
new file mode 100644
index 0000000000..ccbf896ca6
--- /dev/null
+++ b/docshell/test/browser/file_bug673087-2.html
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="ISO-2022-KR">
+FAIL \ No newline at end of file
diff --git a/docshell/test/browser/file_bug852909.pdf b/docshell/test/browser/file_bug852909.pdf
new file mode 100644
index 0000000000..89066463f1
--- /dev/null
+++ b/docshell/test/browser/file_bug852909.pdf
Binary files differ
diff --git a/docshell/test/browser/file_bug852909.png b/docshell/test/browser/file_bug852909.png
new file mode 100644
index 0000000000..c7510d388f
--- /dev/null
+++ b/docshell/test/browser/file_bug852909.png
Binary files differ
diff --git a/docshell/test/browser/file_click_link_within_view_source.html b/docshell/test/browser/file_click_link_within_view_source.html
new file mode 100644
index 0000000000..d78e4ba0ff
--- /dev/null
+++ b/docshell/test/browser/file_click_link_within_view_source.html
@@ -0,0 +1,6 @@
+<html>
+<head> <meta charset="utf-8"> </head>
+ <body>
+ <a id="testlink" href="dummy_page.html">clickme</a>
+ </body>
+</html>
diff --git a/docshell/test/browser/file_cross_process_csp_inheritance.html b/docshell/test/browser/file_cross_process_csp_inheritance.html
new file mode 100644
index 0000000000..d87761a609
--- /dev/null
+++ b/docshell/test/browser/file_cross_process_csp_inheritance.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>Test CSP inheritance if load happens in same and different process</title>
+ <meta http-equiv="Content-Security-Policy" content="script-src 'none'">
+</head>
+<body>
+ <a href="data:text/html,<html>test-same-diff-process-csp-inhertiance</html>" id="testLink" target="_blank" rel="noopener">click to test same/diff process CSP inheritance</a>
+</body>
+</html>
diff --git a/docshell/test/browser/file_csp_sandbox_no_script_js_uri.html b/docshell/test/browser/file_csp_sandbox_no_script_js_uri.html
new file mode 100644
index 0000000000..49341f7481
--- /dev/null
+++ b/docshell/test/browser/file_csp_sandbox_no_script_js_uri.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Test Javascript URI with no script</title>
+</head>
+<body>
+<noscript>no scripts allowed here</noscript>
+<a href="javascript:alert(`origin=${origin} location=${location}`)" target="_parent">click me</a>
+</body>
+</html>
diff --git a/docshell/test/browser/file_csp_sandbox_no_script_js_uri.html^headers^ b/docshell/test/browser/file_csp_sandbox_no_script_js_uri.html^headers^
new file mode 100644
index 0000000000..461f7f99ce
--- /dev/null
+++ b/docshell/test/browser/file_csp_sandbox_no_script_js_uri.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: sandbox allow-same-origin allow-top-navigation;
diff --git a/docshell/test/browser/file_csp_uir.html b/docshell/test/browser/file_csp_uir.html
new file mode 100644
index 0000000000..be60f41a80
--- /dev/null
+++ b/docshell/test/browser/file_csp_uir.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1542858 - Test CSP upgrade-insecure-requests</title>
+ <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
+</head>
+<body>
+ <a id="testlink" href="file_csp_uir_dummy.html">testlink</a>
+</body>
+</html>
diff --git a/docshell/test/browser/file_csp_uir_dummy.html b/docshell/test/browser/file_csp_uir_dummy.html
new file mode 100644
index 0000000000..f0ab6775c0
--- /dev/null
+++ b/docshell/test/browser/file_csp_uir_dummy.html
@@ -0,0 +1 @@
+<html><body>foo</body></html>
diff --git a/docshell/test/browser/file_data_load_inherit_csp.html b/docshell/test/browser/file_data_load_inherit_csp.html
new file mode 100644
index 0000000000..1efe738e4c
--- /dev/null
+++ b/docshell/test/browser/file_data_load_inherit_csp.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1358009 - Inherit CSP into data URI</title>
+ <meta http-equiv="Content-Security-Policy" content="script-src 'unsafe-inline'">
+</head>
+<body>
+ <a id="testlink">testlink</a>
+</body>
+</html>
diff --git a/docshell/test/browser/file_multiple_pushState.html b/docshell/test/browser/file_multiple_pushState.html
new file mode 100644
index 0000000000..6592f3f53f
--- /dev/null
+++ b/docshell/test/browser/file_multiple_pushState.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Test multiple calls to history.pushState</title>
+ </head>
+ <body>
+ <h1>Ohai</h1>
+ </body>
+ <script type="text/javascript">
+ window.history.pushState({}, "", "/bar/ABC?key=baz");
+ let data = new Array(100000).join("a");
+ window.history.pushState({ data }, "", "/bar/ABC/DEF?key=baz");
+ // Test also Gecko specific state object size limit.
+ try {
+ let largeData = new Array(20000000).join("a");
+ window.history.pushState({ largeData }, "", "/bar/ABC/DEF/GHI?key=baz");
+ } catch (ex) {}
+ </script>
+</html>
diff --git a/docshell/test/browser/file_onbeforeunload_0.html b/docshell/test/browser/file_onbeforeunload_0.html
new file mode 100644
index 0000000000..7d9acf057d
--- /dev/null
+++ b/docshell/test/browser/file_onbeforeunload_0.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+</head>
+<body>
+ <iframe src="http://example.com/browser/docshell/test/browser/file_onbeforeunload_1.html"></iframe>
+</body>
+</html>
diff --git a/docshell/test/browser/file_onbeforeunload_1.html b/docshell/test/browser/file_onbeforeunload_1.html
new file mode 100644
index 0000000000..edd27783e4
--- /dev/null
+++ b/docshell/test/browser/file_onbeforeunload_1.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+</head>
+<body>
+ <iframe src="http://mochi.test:8888/browser/docshell/test/browser/file_onbeforeunload_2.html"></iframe>
+</body>
+</html>
diff --git a/docshell/test/browser/file_onbeforeunload_2.html b/docshell/test/browser/file_onbeforeunload_2.html
new file mode 100644
index 0000000000..a52a4ace5c
--- /dev/null
+++ b/docshell/test/browser/file_onbeforeunload_2.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+</head>
+<body>
+ <iframe src="http://example.com/browser/docshell/test/browser/file_onbeforeunload_3.html"></iframe>
+</body>
+</html>
+
diff --git a/docshell/test/browser/file_onbeforeunload_3.html b/docshell/test/browser/file_onbeforeunload_3.html
new file mode 100644
index 0000000000..9914f0cd85
--- /dev/null
+++ b/docshell/test/browser/file_onbeforeunload_3.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+</head>
+<body>
+</body>
+</html>
+
diff --git a/docshell/test/browser/file_open_about_blank.html b/docshell/test/browser/file_open_about_blank.html
new file mode 100644
index 0000000000..134384e2f7
--- /dev/null
+++ b/docshell/test/browser/file_open_about_blank.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<button id="open" onclick="window.open('')">Open child window</button>
diff --git a/docshell/test/browser/file_slow_load.sjs b/docshell/test/browser/file_slow_load.sjs
new file mode 100644
index 0000000000..4c6dd6d5b9
--- /dev/null
+++ b/docshell/test/browser/file_slow_load.sjs
@@ -0,0 +1,8 @@
+"use strict";
+
+function handleRequest(request, response) {
+ response.processAsync();
+ response.setHeader("Content-Type", "text/html");
+ response.write("<!doctype html>Loading... ");
+ // We don't block on this, so it's fine to never finish the response.
+}
diff --git a/docshell/test/browser/frame-head.js b/docshell/test/browser/frame-head.js
new file mode 100644
index 0000000000..6574386b6a
--- /dev/null
+++ b/docshell/test/browser/frame-head.js
@@ -0,0 +1,111 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env mozilla/frame-script */
+
+// Functions that are automatically loaded as frame scripts for
+// timeline tests.
+
+const { setTimeout } = ChromeUtils.importESModule(
+ "resource://gre/modules/Timer.sys.mjs"
+);
+
+// Functions that look like mochitest functions but forward to the
+// browser process.
+
+this.ok = function(value, message) {
+ sendAsyncMessage("browser:test:ok", {
+ value: !!value,
+ message,
+ });
+};
+
+this.is = function(v1, v2, message) {
+ ok(v1 == v2, message);
+};
+
+this.info = function(message) {
+ sendAsyncMessage("browser:test:info", { message });
+};
+
+this.finish = function() {
+ sendAsyncMessage("browser:test:finish");
+};
+
+/* Start a task that runs some timeline tests in the ordinary way.
+ *
+ * @param array tests
+ * The tests to run. This is an array where each element
+ * is of the form { desc, searchFor, setup, check }.
+ *
+ * desc is the test description, a string.
+ * searchFor is a string or a function
+ * If a string, then when a marker with this name is
+ * found, marker-reading is stopped.
+ * If a function, then the accumulated marker array is
+ * passed to it, and marker reading stops when it returns
+ * true.
+ * setup is a function that takes the docshell as an argument.
+ * It should start the test.
+ * check is a function that takes an array of markers
+ * as an argument and checks the results of the test.
+ */
+this.timelineContentTest = function(tests) {
+ (async function() {
+ let docShell = content.docShell;
+
+ info("Start recording");
+ docShell.recordProfileTimelineMarkers = true;
+
+ for (let { desc, searchFor, setup, check } of tests) {
+ info("Running test: " + desc);
+
+ info("Flushing the previous markers if any");
+ docShell.popProfileTimelineMarkers();
+
+ info("Running the test setup function");
+ let onMarkers = timelineWaitForMarkers(docShell, searchFor);
+ setup(docShell);
+ info("Waiting for new markers on the docShell");
+ let markers = await onMarkers;
+
+ // Cycle collection markers are non-deterministic, and none of these tests
+ // expect them to show up.
+ markers = markers.filter(m => !m.name.includes("nsCycleCollector"));
+
+ info("Running the test check function");
+ check(markers);
+ }
+
+ info("Stop recording");
+ docShell.recordProfileTimelineMarkers = false;
+ finish();
+ })();
+};
+
+function timelineWaitForMarkers(docshell, searchFor) {
+ if (typeof searchFor == "string") {
+ let searchForString = searchFor;
+ let f = function(markers) {
+ return markers.some(m => m.name == searchForString);
+ };
+ searchFor = f;
+ }
+
+ return new Promise(function(resolve, reject) {
+ let waitIterationCount = 0;
+ let maxWaitIterationCount = 10; // Wait for 2sec maximum
+ let markers = [];
+
+ setTimeout(function timeoutHandler() {
+ let newMarkers = docshell.popProfileTimelineMarkers();
+ markers = [...markers, ...newMarkers];
+ if (searchFor(markers) || waitIterationCount > maxWaitIterationCount) {
+ resolve(markers);
+ } else {
+ setTimeout(timeoutHandler, 200);
+ waitIterationCount++;
+ }
+ }, 200);
+ });
+}
diff --git a/docshell/test/browser/head.js b/docshell/test/browser/head.js
new file mode 100644
index 0000000000..dd6d9242b9
--- /dev/null
+++ b/docshell/test/browser/head.js
@@ -0,0 +1,258 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* Helper function for timeline tests. Returns an async task that is
+ * suitable for use as a particular timeline test.
+ * @param string frameScriptName
+ * Base name of the frame script file.
+ * @param string url
+ * URL to load.
+ */
+function makeTimelineTest(frameScriptName, url) {
+ info("in timelineTest");
+ return async function() {
+ info("in in timelineTest");
+ waitForExplicitFinish();
+
+ await timelineTestOpenUrl(url);
+
+ const here = "chrome://mochitests/content/browser/docshell/test/browser/";
+
+ let mm = gBrowser.selectedBrowser.messageManager;
+ mm.loadFrameScript(here + "frame-head.js", false);
+ mm.loadFrameScript(here + frameScriptName, false);
+
+ // Set up some listeners so that timeline tests running in the
+ // content process can forward their results to the main process.
+ mm.addMessageListener("browser:test:ok", function(message) {
+ ok(message.data.value, message.data.message);
+ });
+ mm.addMessageListener("browser:test:info", function(message) {
+ info(message.data.message);
+ });
+ mm.addMessageListener("browser:test:finish", function(ignore) {
+ gBrowser.removeCurrentTab();
+ finish();
+ });
+ };
+}
+
+/* Open a URL for a timeline test. */
+function timelineTestOpenUrl(url) {
+ window.focus();
+
+ let tabSwitchPromise = new Promise((resolve, reject) => {
+ window.gBrowser.addEventListener(
+ "TabSwitchDone",
+ function() {
+ resolve();
+ },
+ { once: true }
+ );
+ });
+
+ let loadPromise = new Promise(function(resolve, reject) {
+ let browser = window.gBrowser;
+ let tab = (browser.selectedTab = BrowserTestUtils.addTab(browser, url));
+ let linkedBrowser = tab.linkedBrowser;
+
+ BrowserTestUtils.browserLoaded(linkedBrowser).then(() => resolve(tab));
+ });
+
+ return Promise.all([tabSwitchPromise, loadPromise]).then(([_, tab]) => tab);
+}
+
+/**
+ * Helper function for encoding override tests, loads URL, runs check1,
+ * forces encoding detection, runs check2.
+ */
+function runCharsetTest(url, check1, check2) {
+ waitForExplicitFinish();
+
+ BrowserTestUtils.openNewForegroundTab(gBrowser, url, true).then(afterOpen);
+
+ function afterOpen() {
+ BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(
+ afterChangeCharset
+ );
+
+ SpecialPowers.spawn(gBrowser.selectedBrowser, [], check1).then(() => {
+ BrowserForceEncodingDetection();
+ });
+ }
+
+ function afterChangeCharset() {
+ SpecialPowers.spawn(gBrowser.selectedBrowser, [], check2).then(() => {
+ gBrowser.removeCurrentTab();
+ finish();
+ });
+ }
+}
+
+/**
+ * Helper function for charset tests. It loads |url| in a new tab,
+ * runs |check|.
+ */
+function runCharsetCheck(url, check) {
+ waitForExplicitFinish();
+
+ BrowserTestUtils.openNewForegroundTab(gBrowser, url, true).then(afterOpen);
+
+ function afterOpen() {
+ SpecialPowers.spawn(gBrowser.selectedBrowser, [], check).then(() => {
+ gBrowser.removeCurrentTab();
+ finish();
+ });
+ }
+}
+
+async function pushState(url, frameId) {
+ info(
+ `Doing a pushState, expecting to load ${url} ${
+ frameId ? "in an iframe" : ""
+ }`
+ );
+ let browser = gBrowser.selectedBrowser;
+ let bc = browser.browsingContext;
+ if (frameId) {
+ bc = await SpecialPowers.spawn(bc, [frameId], function(id) {
+ return content.document.getElementById(id).browsingContext;
+ });
+ }
+ let loaded = BrowserTestUtils.waitForLocationChange(gBrowser, url);
+ await SpecialPowers.spawn(bc, [url], function(url) {
+ content.history.pushState({}, "", url);
+ });
+ await loaded;
+ info(`Loaded ${url} ${frameId ? "in an iframe" : ""}`);
+}
+
+async function loadURI(url) {
+ info(`Doing a top-level loadURI, expecting to load ${url}`);
+ let browser = gBrowser.selectedBrowser;
+ let loaded = BrowserTestUtils.browserLoaded(browser, false, url);
+ BrowserTestUtils.loadURI(browser, url);
+ await loaded;
+ info(`Loaded ${url}`);
+}
+
+async function followLink(url, frameId) {
+ info(
+ `Creating and following a link to ${url} ${frameId ? "in an iframe" : ""}`
+ );
+ let browser = gBrowser.selectedBrowser;
+ let bc = browser.browsingContext;
+ if (frameId) {
+ bc = await SpecialPowers.spawn(bc, [frameId], function(id) {
+ return content.document.getElementById(id).browsingContext;
+ });
+ }
+ let loaded = BrowserTestUtils.browserLoaded(browser, !!frameId, url);
+ await SpecialPowers.spawn(bc, [url], function(url) {
+ let a = content.document.createElement("a");
+ a.href = url;
+ content.document.body.appendChild(a);
+ a.click();
+ });
+ await loaded;
+ info(`Loaded ${url} ${frameId ? "in an iframe" : ""}`);
+}
+
+async function goForward(url, useFrame = false) {
+ info(
+ `Clicking the forward button, expecting to load ${url} ${
+ useFrame ? "in an iframe" : ""
+ }`
+ );
+ let loaded = BrowserTestUtils.waitForLocationChange(gBrowser, url);
+ let forwardButton = document.getElementById("forward-button");
+ forwardButton.click();
+ await loaded;
+ info(`Loaded ${url} ${useFrame ? "in an iframe" : ""}`);
+}
+
+async function goBack(url, useFrame = false) {
+ info(
+ `Clicking the back button, expecting to load ${url} ${
+ useFrame ? "in an iframe" : ""
+ }`
+ );
+ let loaded = BrowserTestUtils.waitForLocationChange(gBrowser, url);
+ let backButton = document.getElementById("back-button");
+ backButton.click();
+ await loaded;
+ info(`Loaded ${url} ${useFrame ? "in an iframe" : ""}`);
+}
+
+function assertBackForwardState(canGoBack, canGoForward) {
+ let backButton = document.getElementById("back-button");
+ let forwardButton = document.getElementById("forward-button");
+
+ is(
+ backButton.disabled,
+ !canGoBack,
+ `${gBrowser.currentURI.spec}: back button is ${
+ canGoBack ? "not" : ""
+ } disabled`
+ );
+ is(
+ forwardButton.disabled,
+ !canGoForward,
+ `${gBrowser.currentURI.spec}: forward button is ${
+ canGoForward ? "not" : ""
+ } disabled`
+ );
+}
+
+class SHListener {
+ static NewEntry = 0;
+ static Reload = 1;
+ static GotoIndex = 2;
+ static Purge = 3;
+ static ReplaceEntry = 4;
+ static async waitForHistory(history, event) {
+ return new Promise(resolve => {
+ let listener = {
+ OnHistoryNewEntry: () => {},
+ OnHistoryReload: () => {
+ return true;
+ },
+ OnHistoryGotoIndex: () => {},
+ OnHistoryPurge: () => {},
+ OnHistoryReplaceEntry: () => {},
+
+ QueryInterface: ChromeUtils.generateQI([
+ "nsISHistoryListener",
+ "nsISupportsWeakReference",
+ ]),
+ };
+
+ function finish() {
+ history.removeSHistoryListener(listener);
+ resolve();
+ }
+ switch (event) {
+ case this.NewEntry:
+ listener.OnHistoryNewEntry = finish;
+ break;
+ case this.Reload:
+ listener.OnHistoryReload = () => {
+ finish();
+ return true;
+ };
+ break;
+ case this.GotoIndex:
+ listener.OnHistoryGotoIndex = finish;
+ break;
+ case this.Purge:
+ listener.OnHistoryPurge = finish;
+ break;
+ case this.ReplaceEntry:
+ listener.OnHistoryReplaceEntry = finish;
+ break;
+ }
+
+ history.addSHistoryListener(listener);
+ });
+ }
+}
diff --git a/docshell/test/browser/head_browser_onbeforeunload.js b/docshell/test/browser/head_browser_onbeforeunload.js
new file mode 100644
index 0000000000..e037822cdb
--- /dev/null
+++ b/docshell/test/browser/head_browser_onbeforeunload.js
@@ -0,0 +1,271 @@
+"use strict";
+
+const BASE_URL = "http://mochi.test:8888/browser/docshell/test/browser/";
+
+const TEST_PAGE = BASE_URL + "file_onbeforeunload_0.html";
+
+const CONTENT_PROMPT_SUBDIALOG = Services.prefs.getBoolPref(
+ "prompts.contentPromptSubDialog",
+ false
+);
+
+const { PromptTestUtils } = ChromeUtils.import(
+ "resource://testing-common/PromptTestUtils.jsm"
+);
+
+async function withTabModalPromptCount(expected, task) {
+ const DIALOG_TOPIC = CONTENT_PROMPT_SUBDIALOG
+ ? "common-dialog-loaded"
+ : "tabmodal-dialog-loaded";
+
+ let count = 0;
+ function observer() {
+ count++;
+ }
+
+ Services.obs.addObserver(observer, DIALOG_TOPIC);
+ try {
+ return await task();
+ } finally {
+ Services.obs.removeObserver(observer, DIALOG_TOPIC);
+ is(count, expected, "Should see expected number of tab modal prompts");
+ }
+}
+
+function promiseAllowUnloadPrompt(browser, allowNavigation) {
+ return PromptTestUtils.handleNextPrompt(
+ browser,
+ { modalType: Services.prompt.MODAL_TYPE_CONTENT, promptType: "confirmEx" },
+ { buttonNumClick: allowNavigation ? 0 : 1 }
+ );
+}
+
+// Maintain a pool of background tabs with our test document loaded so
+// we don't have to wait for a load prior to each test step (potentially
+// tearing down and recreating content processes in the process).
+const TabPool = {
+ poolSize: 5,
+
+ pendingCount: 0,
+
+ readyTabs: [],
+
+ readyPromise: null,
+ resolveReadyPromise: null,
+
+ spawnTabs() {
+ while (this.pendingCount + this.readyTabs.length < this.poolSize) {
+ this.pendingCount++;
+ let tab = BrowserTestUtils.addTab(gBrowser, TEST_PAGE);
+ BrowserTestUtils.browserLoaded(tab.linkedBrowser).then(() => {
+ this.readyTabs.push(tab);
+ this.pendingCount--;
+
+ if (this.resolveReadyPromise) {
+ this.readyPromise = null;
+ this.resolveReadyPromise();
+ this.resolveReadyPromise = null;
+ }
+
+ this.spawnTabs();
+ });
+ }
+ },
+
+ getReadyPromise() {
+ if (!this.readyPromise) {
+ this.readyPromise = new Promise(resolve => {
+ this.resolveReadyPromise = resolve;
+ });
+ }
+ return this.readyPromise;
+ },
+
+ async getTab() {
+ while (!this.readyTabs.length) {
+ this.spawnTabs();
+ await this.getReadyPromise();
+ }
+
+ let tab = this.readyTabs.shift();
+ this.spawnTabs();
+
+ gBrowser.selectedTab = tab;
+ return tab;
+ },
+
+ async cleanup() {
+ this.poolSize = 0;
+
+ while (this.pendingCount) {
+ await this.getReadyPromise();
+ }
+
+ while (this.readyTabs.length) {
+ await BrowserTestUtils.removeTab(this.readyTabs.shift());
+ }
+ },
+};
+
+const ACTIONS = {
+ NONE: 0,
+ LISTEN_AND_ALLOW: 1,
+ LISTEN_AND_BLOCK: 2,
+};
+
+const ACTION_NAMES = new Map(Object.entries(ACTIONS).map(([k, v]) => [v, k]));
+
+function* generatePermutations(depth) {
+ if (depth == 0) {
+ yield [];
+ return;
+ }
+ for (let subActions of generatePermutations(depth - 1)) {
+ for (let action of Object.values(ACTIONS)) {
+ yield [action, ...subActions];
+ }
+ }
+}
+
+const PERMUTATIONS = Array.from(generatePermutations(4));
+
+const FRAMES = [
+ { process: 0 },
+ { process: SpecialPowers.useRemoteSubframes ? 1 : 0 },
+ { process: 0 },
+ { process: SpecialPowers.useRemoteSubframes ? 1 : 0 },
+];
+
+function addListener(bc, block) {
+ return SpecialPowers.spawn(bc, [block], block => {
+ return new Promise(resolve => {
+ function onbeforeunload(event) {
+ if (block) {
+ event.preventDefault();
+ }
+ resolve({ event: "beforeunload" });
+ }
+ content.addEventListener("beforeunload", onbeforeunload, { once: true });
+ content.unlisten = () => {
+ content.removeEventListener("beforeunload", onbeforeunload);
+ };
+
+ content.addEventListener(
+ "unload",
+ () => {
+ resolve({ event: "unload" });
+ },
+ { once: true }
+ );
+ });
+ });
+}
+
+function descendants(bc) {
+ if (bc) {
+ return [bc, ...descendants(bc.children[0])];
+ }
+ return [];
+}
+
+async function addListeners(frames, actions, startIdx) {
+ let process = startIdx >= 0 ? FRAMES[startIdx].process : -1;
+
+ let roundTripPromises = [];
+
+ let expectNestedEventLoop = false;
+ let numBlockers = 0;
+ let unloadPromises = [];
+ let beforeUnloadPromises = [];
+
+ for (let [i, frame] of frames.entries()) {
+ let action = actions[i];
+ if (action === ACTIONS.NONE) {
+ continue;
+ }
+
+ let block = action === ACTIONS.LISTEN_AND_BLOCK;
+ let promise = addListener(frame, block);
+ if (startIdx <= i) {
+ if (block || FRAMES[i].process !== process) {
+ expectNestedEventLoop = true;
+ }
+ beforeUnloadPromises.push(promise);
+ numBlockers += block;
+ } else {
+ unloadPromises.push(promise);
+ }
+
+ roundTripPromises.push(SpecialPowers.spawn(frame, [], () => {}));
+ }
+
+ // Wait for round trip messages to any processes with event listeners to
+ // return so we're sure that all listeners are registered and their state
+ // flags are propagated before we continue.
+ await Promise.all(roundTripPromises);
+
+ return {
+ expectNestedEventLoop,
+ expectPrompt: !!numBlockers,
+ unloadPromises,
+ beforeUnloadPromises,
+ };
+}
+
+async function doTest(actions, startIdx, navigate) {
+ let tab = await TabPool.getTab();
+ let browser = tab.linkedBrowser;
+
+ let frames = descendants(browser.browsingContext);
+ let expected = await addListeners(frames, actions, startIdx);
+
+ let awaitingPrompt = false;
+ let promptPromise;
+ if (expected.expectPrompt) {
+ awaitingPrompt = true;
+ promptPromise = promiseAllowUnloadPrompt(browser, false).then(() => {
+ awaitingPrompt = false;
+ });
+ }
+
+ let promptCount = expected.expectPrompt ? 1 : 0;
+ await withTabModalPromptCount(promptCount, async () => {
+ await navigate(tab, frames).then(result => {
+ ok(
+ !awaitingPrompt,
+ "Navigation should not complete while we're still expecting a prompt"
+ );
+
+ is(
+ result.eventLoopSpun,
+ expected.expectNestedEventLoop,
+ "Should have nested event loop?"
+ );
+ });
+
+ for (let result of await Promise.all(expected.beforeUnloadPromises)) {
+ is(
+ result.event,
+ "beforeunload",
+ "Should have seen beforeunload event before unload"
+ );
+ }
+ await promptPromise;
+
+ await Promise.all(
+ frames.map(frame =>
+ SpecialPowers.spawn(frame, [], () => {
+ if (content.unlisten) {
+ content.unlisten();
+ }
+ }).catch(() => {})
+ )
+ );
+
+ await BrowserTestUtils.removeTab(tab);
+ });
+
+ for (let result of await Promise.all(expected.unloadPromises)) {
+ is(result.event, "unload", "Should have seen unload event");
+ }
+}
diff --git a/docshell/test/browser/onload_message.html b/docshell/test/browser/onload_message.html
new file mode 100644
index 0000000000..23f6e37396
--- /dev/null
+++ b/docshell/test/browser/onload_message.html
@@ -0,0 +1,25 @@
+<html>
+ <head>
+ <meta charset="utf-8">
+ <script>
+ addEventListener("load", function() {
+ // This file is used in couple of different tests.
+ if (opener) {
+ opener.postMessage("load", "*");
+ } else {
+ var bc = new BroadcastChannel("browser_browsingContext");
+ bc.onmessage = function(event) {
+ if (event.data == "back") {
+ bc.close();
+ history.back();
+ }
+ };
+ bc.postMessage({event: "load", framesLength: frames.length });
+ }
+ });
+ </script>
+ </head>
+ <body>
+ This file posts a message containing "load" to opener or BroadcastChannel on load completion.
+ </body>
+</html>
diff --git a/docshell/test/browser/onpageshow_message.html b/docshell/test/browser/onpageshow_message.html
new file mode 100644
index 0000000000..105c06f8db
--- /dev/null
+++ b/docshell/test/browser/onpageshow_message.html
@@ -0,0 +1,41 @@
+<html>
+ <head>
+ <meta charset="utf-8">
+ <script>
+ addEventListener("pageshow", function() {
+ var bc = new BroadcastChannel("browser_browsingContext");
+ function frameData() {
+ var win = SpecialPowers.wrap(frames[0]);
+ bc.postMessage(
+ { framesLength: frames.length,
+ browsingContextId: win.docShell.browsingContext.id,
+ outerWindowId: win.docShell.outerWindowID
+ });
+ }
+
+ bc.onmessage = function(event) {
+ if (event.data == "createiframe") {
+ let frame = document.createElement("iframe");
+ frame.src = "dummy_page.html";
+ document.body.appendChild(frame);
+ frame.onload = frameData;
+ } else if (event.data == "nextpage") {
+ bc.close();
+ location.href = "onload_message.html";
+ } else if (event.data == "queryframes") {
+ frameData();
+ } else if (event.data == "close") {
+ bc.postMessage("closed");
+ bc.close();
+ window.close();
+ }
+ }
+ bc.postMessage("pageshow");
+ });
+ </script>
+ </head>
+ <body>
+ This file posts a message containing "pageshow" to a BroadcastChannel and
+ keep the channel open for commands.
+ </body>
+</html>
diff --git a/docshell/test/browser/overlink_test.html b/docshell/test/browser/overlink_test.html
new file mode 100644
index 0000000000..5efd689311
--- /dev/null
+++ b/docshell/test/browser/overlink_test.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+ <head> <meta charset="utf-8"> </head>
+ <body>
+ <a id="link" href="https://user:password@example.com">Link</a>
+ </body>
+</html>
diff --git a/docshell/test/browser/print_postdata.sjs b/docshell/test/browser/print_postdata.sjs
new file mode 100644
index 0000000000..0e3ef38419
--- /dev/null
+++ b/docshell/test/browser/print_postdata.sjs
@@ -0,0 +1,25 @@
+const CC = Components.Constructor;
+const BinaryInputStream = CC(
+ "@mozilla.org/binaryinputstream;1",
+ "nsIBinaryInputStream",
+ "setInputStream"
+);
+
+function handleRequest(request, response) {
+ response.setHeader("Content-Type", "text/plain", false);
+ if (request.method == "GET") {
+ response.write(request.queryString);
+ } else {
+ var body = new BinaryInputStream(request.bodyInputStream);
+
+ var avail;
+ var bytes = [];
+
+ while ((avail = body.available()) > 0) {
+ Array.prototype.push.apply(bytes, body.readByteArray(avail));
+ }
+
+ var data = String.fromCharCode.apply(null, bytes);
+ response.bodyOutputStream.write(data, data.length);
+ }
+}
diff --git a/docshell/test/browser/redirect_to_example.sjs b/docshell/test/browser/redirect_to_example.sjs
new file mode 100644
index 0000000000..283e4793db
--- /dev/null
+++ b/docshell/test/browser/redirect_to_example.sjs
@@ -0,0 +1,5 @@
+function handleRequest(request, response) {
+ response.setStatusLine(request.httpVersion, 302, "Moved Permanently");
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ response.setHeader("Location", "http://example");
+}
diff --git a/docshell/test/browser/test-form_sjis.html b/docshell/test/browser/test-form_sjis.html
new file mode 100644
index 0000000000..91c375deef
--- /dev/null
+++ b/docshell/test/browser/test-form_sjis.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/REC-html401-19991224/strict.dtd">
+<html>
+ <head>
+ <meta http-equiv="content-type" content="text/html; charset=windows-1251">
+ <title>Shift_JIS in body and text area</title>
+ </head>
+ <body>
+ <h1>Incorrect meta charset</h1>
+ <h2>This page is encoded in Shift_JIS, but has an incorrect meta charset
+ claiming that it is windows-1251</h2>
+ <p id="testpar">jR[h́AׂĂ̕ɌŗL̔ԍt^܂</p>
+ <form>
+ <p>
+ <textarea id="testtextarea" rows=6 cols=60>jR[h́AׂĂ̕ɌŗL̔ԍt^܂</textarea>
+ <input id="testinput" type="text" size=60 value="jR[h́AׂĂ̕ɌŗL̔ԍt^܂">
+ </p>
+ </form>
+ <h2>Expected text on load:</h2>
+ <p>&#x453;&#x2020;&#x453;&#x6A;&#x453;&#x52;&#x403;&#x5B;&#x453;&#x68;&#x201A;&#x41D;&#x403;&#x41;&#x201A;&#xB7;&#x201A;&#x427;&#x201A;&#x414;&#x201A;&#x41C;&#x2022;&#xB6;&#x40B;&#x459;&#x201A;&#x419;&#x40A;&#x415;&#x2014;&#x4C;&#x201A;&#x41C;&#x201D;&#x424;&#x40C;&#x2020;&#x201A;&#x440;&#x2022;&#x74;&#x2014;&#x5E;&#x201A;&#xB5;&#x201A;&#x42C;&#x201A;&#xB7;</p>
+ <h2>Expected text on resetting the encoding to Shift_JIS:</h2>
+ <p>&#x30E6;&#x30CB;&#x30B3;&#x30FC;&#x30C9;&#x306F;&#x3001;&#x3059;&#x3079;&#x3066;&#x306E;&#x6587;&#x5B57;&#x306B;&#x56FA;&#x6709;&#x306E;&#x756A;&#x53F7;&#x3092;&#x4ED8;&#x4E0E;&#x3057;&#x307E;&#x3059;</p>
+ </body>
+</html>
diff --git a/docshell/test/browser/timelineMarkers-04.html b/docshell/test/browser/timelineMarkers-04.html
new file mode 100644
index 0000000000..ff2f429d62
--- /dev/null
+++ b/docshell/test/browser/timelineMarkers-04.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8"></meta>
+ <title>markers test</title>
+</head>
+<body>
+
+ <p>Test page</p>
+
+ <script>
+ function do_xhr() {
+ const theURL = "timelineMarkers-04.html";
+
+ var xhr = new XMLHttpRequest();
+ xhr.onreadystatechange = function() {
+ dump("ReadyState = " + xhr.readyState + "\n");
+ };
+ xhr.open("get", theURL, true);
+ xhr.send();
+ }
+
+ window.addEventListener("dog", do_xhr, true);
+
+ function do_promise() {
+ new Promise(function(resolve, reject) {
+ console.time("Bob");
+ window.setTimeout(function() {
+ resolve(23);
+ }, 10);
+ }).then(function(val) {
+ console.timeEnd("Bob");
+ });
+ }
+
+ window.addEventListener("promisetest", do_promise, true);
+
+ var globalResolver;
+ function do_promise_script() {
+ new Promise(function(resolve, reject) {
+ console.time("Bob");
+ globalResolver = resolve;
+ // eslint-disable-next-line no-implied-eval
+ window.setTimeout("globalResolver(23)", 10);
+ }).then(function(val) {
+ console.timeEnd("Bob");
+ });
+ }
+
+ window.addEventListener("promisescript", do_promise_script, true);
+
+ </script>
+
+</body>
+</html>
+
diff --git a/docshell/test/chrome/112564_nocache.html b/docshell/test/chrome/112564_nocache.html
new file mode 100644
index 0000000000..29fb990b86
--- /dev/null
+++ b/docshell/test/chrome/112564_nocache.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+<title>test1</title>
+</head>
+<body>
+<p>
+This document will be sent with a no-cache cache-control header. When sent over a secure connection, it should not be stored in bfcache.
+</p>
+</body>
+</html>
diff --git a/docshell/test/chrome/112564_nocache.html^headers^ b/docshell/test/chrome/112564_nocache.html^headers^
new file mode 100644
index 0000000000..c829a41ae9
--- /dev/null
+++ b/docshell/test/chrome/112564_nocache.html^headers^
@@ -0,0 +1 @@
+Cache-control: no-cache
diff --git a/docshell/test/chrome/215405_nocache.html b/docshell/test/chrome/215405_nocache.html
new file mode 100644
index 0000000000..c7d48c4eba
--- /dev/null
+++ b/docshell/test/chrome/215405_nocache.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html style="height: 100%">
+<head>
+ <title>test1</title>
+</head>
+<body style="height: 100%">
+ <input type="text" id="inp" value="">
+ </input>
+ <div style="height: 50%">Some text</div>
+ <div style="height: 50%">Some text</div>
+ <div style="height: 50%">Some text</div>
+ <div style="height: 50%; width: 300%">Some more text</div>
+</body>
+</html>
diff --git a/docshell/test/chrome/215405_nocache.html^headers^ b/docshell/test/chrome/215405_nocache.html^headers^
new file mode 100644
index 0000000000..c829a41ae9
--- /dev/null
+++ b/docshell/test/chrome/215405_nocache.html^headers^
@@ -0,0 +1 @@
+Cache-control: no-cache
diff --git a/docshell/test/chrome/215405_nostore.html b/docshell/test/chrome/215405_nostore.html
new file mode 100644
index 0000000000..4f5bd0f4f0
--- /dev/null
+++ b/docshell/test/chrome/215405_nostore.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html style="height: 100%">
+<head>
+ <title>test1</title>
+</head>
+<body style="height: 100%">
+ <input type="text" id="inp" value="">
+ </input>
+ <div style="height: 50%">Some text</div>
+ <div style="height: 50%">Some text</div>
+ <div style="height: 50%">Some text</div>
+ <div style="height: 50%; width: 350%">Some more text</div>
+</body>
+</html>
diff --git a/docshell/test/chrome/215405_nostore.html^headers^ b/docshell/test/chrome/215405_nostore.html^headers^
new file mode 100644
index 0000000000..59ba296103
--- /dev/null
+++ b/docshell/test/chrome/215405_nostore.html^headers^
@@ -0,0 +1 @@
+Cache-control: no-store
diff --git a/docshell/test/chrome/582176_dummy.html b/docshell/test/chrome/582176_dummy.html
new file mode 100644
index 0000000000..3b18e512db
--- /dev/null
+++ b/docshell/test/chrome/582176_dummy.html
@@ -0,0 +1 @@
+hello world
diff --git a/docshell/test/chrome/582176_xml.xml b/docshell/test/chrome/582176_xml.xml
new file mode 100644
index 0000000000..d3dd576dfe
--- /dev/null
+++ b/docshell/test/chrome/582176_xml.xml
@@ -0,0 +1,2 @@
+<?xml-stylesheet type="text/xsl" href="582176_xslt.xsl"?>
+<out/>
diff --git a/docshell/test/chrome/582176_xslt.xsl b/docshell/test/chrome/582176_xslt.xsl
new file mode 100644
index 0000000000..5957416899
--- /dev/null
+++ b/docshell/test/chrome/582176_xslt.xsl
@@ -0,0 +1,8 @@
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+ <xsl:template match="out">
+ <html>
+ <head><title>XSLT result doc</title></head>
+ <body><p>xslt result</p></body>
+ </html>
+ </xsl:template>
+</xsl:stylesheet>
diff --git a/docshell/test/chrome/662200a.html b/docshell/test/chrome/662200a.html
new file mode 100644
index 0000000000..0b9ead6f3e
--- /dev/null
+++ b/docshell/test/chrome/662200a.html
@@ -0,0 +1,8 @@
+<html>
+ <head>
+ <title>A</title>
+ </head>
+ <body>
+ <a id="link" href="662200b.html">Next</a>
+ </body>
+</html>
diff --git a/docshell/test/chrome/662200b.html b/docshell/test/chrome/662200b.html
new file mode 100644
index 0000000000..91e6b971d6
--- /dev/null
+++ b/docshell/test/chrome/662200b.html
@@ -0,0 +1,8 @@
+<html>
+ <head>
+ <title>B</title>
+ </head>
+ <body>
+ <a id="link" href="662200c.html">Next</a>
+ </body>
+</html>
diff --git a/docshell/test/chrome/662200c.html b/docshell/test/chrome/662200c.html
new file mode 100644
index 0000000000..bc00e6b14b
--- /dev/null
+++ b/docshell/test/chrome/662200c.html
@@ -0,0 +1,7 @@
+<html>
+ <head>
+ <title>C</title>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/docshell/test/chrome/89419.html b/docshell/test/chrome/89419.html
new file mode 100644
index 0000000000..b36b8d788c
--- /dev/null
+++ b/docshell/test/chrome/89419.html
@@ -0,0 +1,7 @@
+<html>
+<head>
+<title>Bug 89419</title>
+</head>
+<body>
+<img src="http://mochi.test:8888/tests/docshell/test/chrome/bug89419.sjs">
+</body>
diff --git a/docshell/test/chrome/92598_nostore.html b/docshell/test/chrome/92598_nostore.html
new file mode 100644
index 0000000000..47bb90441e
--- /dev/null
+++ b/docshell/test/chrome/92598_nostore.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+<title>test1</title>
+</head>
+<body>
+<p>
+This document will be sent with a no-store cache-control header. It should not be stored in bfcache.
+</p>
+</body>
+</html>
diff --git a/docshell/test/chrome/92598_nostore.html^headers^ b/docshell/test/chrome/92598_nostore.html^headers^
new file mode 100644
index 0000000000..59ba296103
--- /dev/null
+++ b/docshell/test/chrome/92598_nostore.html^headers^
@@ -0,0 +1 @@
+Cache-control: no-store
diff --git a/docshell/test/chrome/DocShellHelpers.sys.mjs b/docshell/test/chrome/DocShellHelpers.sys.mjs
new file mode 100644
index 0000000000..5f4eee8724
--- /dev/null
+++ b/docshell/test/chrome/DocShellHelpers.sys.mjs
@@ -0,0 +1,74 @@
+/* Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/publicdomain/zero/1.0/ */
+
+export class DocShellHelpersParent extends JSWindowActorParent {
+ static eventListener;
+
+ // These static variables should be set when registering the actor
+ // (currently doPageNavigation in docshell_helpers.js).
+ static eventsToListenFor;
+ static observers;
+
+ constructor() {
+ super();
+ }
+ receiveMessage({ name, data }) {
+ if (name == "docshell_helpers:event") {
+ let { event, originalTargetIsHTMLDocument } = data;
+
+ if (this.constructor.eventsToListenFor.includes(event.type)) {
+ this.constructor.eventListener(event, originalTargetIsHTMLDocument);
+ }
+ } else if (name == "docshell_helpers:observe") {
+ let { topic } = data;
+
+ this.constructor.observers.get(topic).call();
+ }
+ }
+}
+
+export class DocShellHelpersChild extends JSWindowActorChild {
+ constructor() {
+ super();
+ }
+ receiveMessage({ name, data }) {
+ if (name == "docshell_helpers:preventBFCache") {
+ // Add an RTCPeerConnection to prevent the page from being bfcached.
+ let win = this.contentWindow;
+ win.blockBFCache = new win.RTCPeerConnection();
+ }
+ }
+ handleEvent(event) {
+ if (
+ Document.isInstance(event.originalTarget) &&
+ event.originalTarget.isInitialDocument
+ ) {
+ dump(`TEST: ignoring a ${event.type} event for an initial about:blank\n`);
+ return;
+ }
+
+ this.sendAsyncMessage("docshell_helpers:event", {
+ event: {
+ type: event.type,
+ persisted: event.persisted,
+ originalTarget: {
+ title: event.originalTarget.title,
+ location: event.originalTarget.location.href,
+ visibilityState: event.originalTarget.visibilityState,
+ hidden: event.originalTarget.hidden,
+ },
+ },
+ originalTargetIsHTMLDocument: HTMLDocument.isInstance(
+ event.originalTarget
+ ),
+ });
+ }
+ observe(subject, topic) {
+ if (Window.isInstance(subject) && subject.document.isInitialDocument) {
+ dump(`TEST: ignoring a topic notification for an initial about:blank\n`);
+ return;
+ }
+
+ this.sendAsyncMessage("docshell_helpers:observe", { topic });
+ }
+}
diff --git a/docshell/test/chrome/allowContentRetargeting.sjs b/docshell/test/chrome/allowContentRetargeting.sjs
new file mode 100644
index 0000000000..96e467ef68
--- /dev/null
+++ b/docshell/test/chrome/allowContentRetargeting.sjs
@@ -0,0 +1,7 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function handleRequest(req, resp) {
+ resp.setHeader("Content-Type", "application/octet-stream", false);
+ resp.write("hi");
+}
diff --git a/docshell/test/chrome/blue.png b/docshell/test/chrome/blue.png
new file mode 100644
index 0000000000..8df58f3a5f
--- /dev/null
+++ b/docshell/test/chrome/blue.png
Binary files differ
diff --git a/docshell/test/chrome/bug112564_window.xhtml b/docshell/test/chrome/bug112564_window.xhtml
new file mode 100644
index 0000000000..b4c7d05dc2
--- /dev/null
+++ b/docshell/test/chrome/bug112564_window.xhtml
@@ -0,0 +1,86 @@
+<?xml version="1.0"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window id="112564Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="600"
+ height="600"
+ onload="onLoad();"
+ title="112564 test">
+
+ <script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js" />
+ <script type="application/javascript" src="docshell_helpers.js" />
+ <script type="application/javascript"><![CDATA[
+ var gTestsIterator;
+
+ function onLoad() {
+ gTestsIterator = testsIterator();
+ nextTest();
+ }
+
+ function nextTest() {
+ gTestsIterator.next();
+ }
+
+ function* testsIterator() {
+ // Load a secure page with a no-cache header, followed by a simple page.
+ // no-cache should not interfere with the bfcache in the way no-store
+ // does.
+ var test1DocURI = "https://example.com:443/chrome/docshell/test/chrome/112564_nocache.html";
+
+ doPageNavigation({
+ uri: test1DocURI,
+ eventsToListenFor: ["load", "pageshow"],
+ expectedEvents: [ { type: "load",
+ title: "test1" },
+ { type: "pageshow",
+ title: "test1",
+ persisted: false } ],
+ onNavComplete: nextTest
+ });
+ yield undefined;
+
+ var test2DocURI = "data:text/html,<html><head><title>test2</title></head>" +
+ "<body>test2</body></html>";
+
+ doPageNavigation({
+ uri: test2DocURI,
+ eventsToListenFor: ["load", "pageshow", "pagehide"],
+ expectedEvents: [ { type: "pagehide",
+ title: "test1",
+ persisted: true },
+ { type: "load",
+ title: "test2" },
+ { type: "pageshow",
+ title: "test2",
+ persisted: false } ],
+ onNavComplete: nextTest
+ });
+ yield undefined;
+
+ // Now go back in history. First page has been cached.
+ // Check persisted property to confirm
+ doPageNavigation({
+ back: true,
+ eventsToListenFor: ["pageshow", "pagehide"],
+ expectedEvents: [ { type: "pagehide",
+ title: "test2",
+ persisted: true },
+ { type: "pageshow",
+ title: "test1",
+ persisted: true } ],
+ onNavComplete: nextTest
+ });
+ yield undefined;
+
+ finish();
+ }
+ ]]></script>
+
+ <browser type="content" primary="true" flex="1" id="content" remote="true" />
+</window>
diff --git a/docshell/test/chrome/bug113934_window.xhtml b/docshell/test/chrome/bug113934_window.xhtml
new file mode 100644
index 0000000000..cf6343f309
--- /dev/null
+++ b/docshell/test/chrome/bug113934_window.xhtml
@@ -0,0 +1,165 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<window title="Mozilla Bug 113934" onload="doTheTest()"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <hbox>
+ <vbox id="box1">
+ </vbox>
+ <vbox id="box2">
+ </vbox>
+ <spacer flex="1"/>
+ </hbox>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+ /* globals SimpleTest, is, isnot, ok, snapshotWindow, compareSnapshots,
+ onerror */
+ var imports = [ "SimpleTest", "is", "isnot", "ok", "snapshotWindow",
+ "compareSnapshots", "onerror" ];
+ for (var name of imports) {
+ window[name] = window.arguments[0][name];
+ }
+
+ function $(id) {
+ return document.getElementById(id);
+ }
+
+ function addBrowser(parent, id, width, height) {
+ var b =
+ document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "browser");
+ var type = window.location.search.slice(1);
+ is(type == "chrome" || type == "content", true, "Unexpected type");
+ b.setAttribute("type", type);
+ b.setAttribute("id", id);
+ b.setAttribute("width", width);
+ b.setAttribute("height", height);
+ $(parent).appendChild(b);
+ }
+ addBrowser("box1", "f1", 300, 200);
+ addBrowser("box1", "f2", 300, 200);
+ addBrowser("box2", "f3", 30, 200);
+
+ /** Test for Bug 113934 **/
+ var doc1 =
+ "data:text/html,<html><body onbeforeunload='document.documentElement.textContent = \"\"' onunload='document.documentElement.textContent = \"\"' onpagehide='document.documentElement.textContent = \"\"'>This is a test</body></html>";
+ var doc2 = "data:text/html,<html><head></head><body>This is a second test</body></html>";
+
+
+ $("f1").setAttribute("src", doc1);
+ $("f2").setAttribute("src", doc2);
+ $("f3").setAttribute("src", doc2);
+
+ async function doTheTest() {
+ var s2 = await snapshotWindow($("f2").contentWindow);
+ // var s3 = await snapshotWindow($("f3").contentWindow);
+
+ // This test is broken - see bug 1090274
+ //ok(!compareSnapshots(s2, s3, true)[0],
+ // "Should look different due to different sizing");
+
+ function getDOM(id) {
+ return $(id).contentDocument.documentElement.innerHTML;
+ }
+
+ var dom1 = getDOM("f1");
+
+ var dom2 = getDOM("f2");
+ $("f2").contentDocument.body.textContent = "Modified the text";
+ var dom2star = getDOM("f2");
+ isnot(dom2, dom2star, "We changed the DOM!");
+
+ $("f1").swapDocShells($("f2"));
+ // now we have doms 2*, 1, 2 in the frames
+
+ is(getDOM("f1"), dom2star, "Shouldn't have changed the DOM on swap");
+ is(getDOM("f2"), dom1, "Shouldn't have fired event handlers");
+
+ // Test for bug 480149
+ // The DOMLink* events are dispatched asynchronously, thus I cannot
+ // just include the <link> element in the initial DOM and swap the
+ // docshells. Instead, the link element is added now. Then, when the
+ // first DOMLinkAdded event (which is a result of the actual addition)
+ // is dispatched, the docshells are swapped and the pageshow and pagehide
+ // events are tested. Only then, we wait for the DOMLink* events,
+ // which are a result of swapping the docshells.
+ var DOMLinkListener = {
+ _afterFirst: false,
+ _removedDispatched: false,
+ _addedDispatched: false,
+ async handleEvent(aEvent) {
+ if (!this._afterFirst) {
+ is(aEvent.type, "DOMLinkAdded");
+
+ var strs = { "f1": "", "f3" : "" };
+ function attachListener(node, type) {
+ var listener = function(e) {
+ if (strs[node.id]) strs[node.id] += " ";
+ strs[node.id] += node.id + ".page" + type;
+ }
+ node.addEventListener("page" + type, listener);
+
+ listener.detach = function() {
+ node.removeEventListener("page" + type, listener);
+ }
+ return listener;
+ }
+
+ var l1 = attachListener($("f1"), "show");
+ var l2 = attachListener($("f1"), "hide");
+ var l3 = attachListener($("f3"), "show");
+ var l4 = attachListener($("f3"), "hide");
+
+ $("f1").swapDocShells($("f3"));
+ // now we have DOMs 2, 1, 2* in the frames
+
+ l1.detach();
+ l2.detach();
+ l3.detach();
+ l4.detach();
+
+ // swapDocShells reflows asynchronously, ensure layout is
+ // clean so that the viewport of f1 is the right size.
+ $("f1").getBoundingClientRect();
+ var s1_new = await snapshotWindow($("f1").contentWindow);
+ var [same, first, second] = compareSnapshots(s1_new, s2, true);
+ ok(same, "Should reflow on swap. Expected " + second + " but got " + first);
+
+ is(strs.f1, "f1.pagehide f1.pageshow");
+ is(strs.f3, "f3.pagehide f3.pageshow");
+ this._afterFirst = true;
+ return;
+ }
+ if (aEvent.type == "DOMLinkAdded") {
+ is(this._addedDispatched, false);
+ this._addedDispatched = true;
+ }
+ else {
+ is(this._removedDispatched, false);
+ this._removedDispatched = true;
+ }
+
+ if (this._addedDispatched && this._removedDispatched) {
+ $("f1").removeEventListener("DOMLinkAdded", this);
+ $("f1").removeEventListener("DOMLinkRemoved", this);
+ $("f3").removeEventListener("DOMLinkAdded", this);
+ $("f3").removeEventListener("DOMLinkRemoved", this);
+ window.close();
+ SimpleTest.finish();
+ }
+ }
+ };
+
+ $("f1").addEventListener("DOMLinkAdded", DOMLinkListener);
+ $("f1").addEventListener("DOMLinkRemoved", DOMLinkListener);
+ $("f3").addEventListener("DOMLinkAdded", DOMLinkListener);
+ $("f3").addEventListener("DOMLinkRemoved", DOMLinkListener);
+
+ var linkElement = $("f1").contentDocument.createElement("link");
+ linkElement.setAttribute("rel", "alternate");
+ linkElement.setAttribute("href", "about:blank");
+ $("f1").contentDocument.documentElement.firstChild.appendChild(linkElement);
+ }
+
+ ]]></script>
+</window>
diff --git a/docshell/test/chrome/bug215405_window.xhtml b/docshell/test/chrome/bug215405_window.xhtml
new file mode 100644
index 0000000000..73366482b7
--- /dev/null
+++ b/docshell/test/chrome/bug215405_window.xhtml
@@ -0,0 +1,177 @@
+<?xml version="1.0"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window id="215405Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="600"
+ height="600"
+ onload="onLoad();"
+ title="215405 test">
+
+ <script type="application/javascript"><![CDATA[
+ const {BrowserTestUtils} = ChromeUtils.import("resource://testing-common/BrowserTestUtils.jsm");
+ Services.prefs.setBoolPref("browser.navigation.requireUserInteraction", false);
+
+ /* globals SimpleTest, is, isnot, ok */
+ var imports = [ "SimpleTest", "is", "isnot", "ok"];
+ for (var name of imports) {
+ window[name] = window.arguments[0][name];
+ }
+
+ const text="MOZILLA";
+ const nostoreURI = "http://mochi.test:8888/tests/docshell/test/chrome/" +
+ "215405_nostore.html";
+ const nocacheURI = "https://example.com:443/tests/docshell/test/chrome/" +
+ "215405_nocache.html";
+
+ var gBrowser;
+ var gTestsIterator;
+ var currScrollX = 0;
+ var currScrollY = 0;
+
+ function finish() {
+ gBrowser.removeEventListener("pageshow", eventListener, true);
+ // Work around bug 467960
+ let historyPurged;
+ if (SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ let history = gBrowser.browsingContext.sessionHistory;
+ history.purgeHistory(history.count);
+ historyPurged = Promise.resolve();
+ } else {
+ historyPurged = SpecialPowers.spawn(gBrowser, [], () => {
+ let history = docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory
+ .legacySHistory;
+ history.purgeHistory(history.count);
+ });
+ }
+
+ historyPurged.then(_ => {
+ window.close();
+ window.arguments[0].SimpleTest.finish();
+ });
+ }
+
+ function onLoad(e) {
+ gBrowser = document.getElementById("content");
+ gBrowser.addEventListener("pageshow", eventListener, true);
+
+ gTestsIterator = testsIterator();
+ nextTest();
+ }
+
+ function eventListener(event) {
+ setTimeout(nextTest, 0);
+ }
+
+ function nextTest() {
+ gTestsIterator.next();
+ }
+
+ function* testsIterator() {
+ // No-store tests
+ var testName = "[nostore]";
+
+ // Load a page with a no-store header
+ BrowserTestUtils.loadURI(gBrowser, nostoreURI);
+ yield undefined;
+
+
+ // Now that the page has loaded, amend the form contents
+ var form = gBrowser.contentDocument.getElementById("inp");
+ form.value = text;
+
+ // Attempt to scroll the page
+ var originalXPosition = gBrowser.contentWindow.scrollX;
+ var originalYPosition = gBrowser.contentWindow.scrollY;
+ var scrollToX = gBrowser.contentWindow.scrollMaxX;
+ var scrollToY = gBrowser.contentWindow.scrollMaxY;
+ gBrowser.contentWindow.scrollBy(scrollToX, scrollToY);
+
+ // Save the scroll position for future comparison
+ currScrollX = gBrowser.contentWindow.scrollX;
+ currScrollY = gBrowser.contentWindow.scrollY;
+ isnot(currScrollX, originalXPosition,
+ testName + " failed to scroll window horizontally");
+ isnot(currScrollY, originalYPosition,
+ testName + " failed to scroll window vertically");
+
+ // Load a new document into the browser
+ var simple = "data:text/html,<html><head><title>test2</title></head>" +
+ "<body>test2</body></html>";
+ BrowserTestUtils.loadURI(gBrowser, simple);
+ yield undefined;
+
+
+ // Now go back in history. First page should not have been cached.
+ gBrowser.goBack();
+ yield undefined;
+
+
+ // First uncacheable page will now be reloaded. Check scroll position
+ // restored, and form contents not
+ is(gBrowser.contentWindow.scrollX, currScrollX, testName +
+ " horizontal axis scroll position not correctly restored");
+ is(gBrowser.contentWindow.scrollY, currScrollY, testName +
+ " vertical axis scroll position not correctly restored");
+ var formValue = gBrowser.contentDocument.getElementById("inp").value;
+ isnot(formValue, text, testName + " form value incorrectly restored");
+
+
+ // https no-cache
+ testName = "[nocache]";
+
+ // Load a page with a no-cache header. This should not be
+ // restricted like no-store (bug 567365)
+ BrowserTestUtils.loadURI(gBrowser, nocacheURI);
+ yield undefined;
+
+
+ // Now that the page has loaded, amend the form contents
+ form = gBrowser.contentDocument.getElementById("inp");
+ form.value = text;
+
+ // Attempt to scroll the page
+ originalXPosition = gBrowser.contentWindow.scrollX;
+ originalYPosition = gBrowser.contentWindow.scrollY;
+ scrollToX = gBrowser.contentWindow.scrollMaxX;
+ scrollToY = gBrowser.contentWindow.scrollMaxY;
+ gBrowser.contentWindow.scrollBy(scrollToX, scrollToY);
+
+ // Save the scroll position for future comparison
+ currScrollX = gBrowser.contentWindow.scrollX;
+ currScrollY = gBrowser.contentWindow.scrollY;
+ isnot(currScrollX, originalXPosition,
+ testName + " failed to scroll window horizontally");
+ isnot(currScrollY, originalYPosition,
+ testName + " failed to scroll window vertically");
+
+ BrowserTestUtils.loadURI(gBrowser, simple);
+ yield undefined;
+
+
+ // Now go back in history to the cached page.
+ gBrowser.goBack();
+ yield undefined;
+
+
+ // First page will now be reloaded. Check scroll position
+ // and form contents are restored
+ is(gBrowser.contentWindow.scrollX, currScrollX, testName +
+ " horizontal axis scroll position not correctly restored");
+ is(gBrowser.contentWindow.scrollY, currScrollY, testName +
+ " vertical axis scroll position not correctly restored");
+ formValue = gBrowser.contentDocument.getElementById("inp").value;
+ is(formValue, text, testName + " form value not correctly restored");
+
+ Services.prefs.clearUserPref("browser.navigation.requireUserInteraction");
+ finish();
+ }
+ ]]></script>
+
+ <browser type="content" primary="true" flex="1" id="content" src="about:blank"/>
+</window>
diff --git a/docshell/test/chrome/bug293235.html b/docshell/test/chrome/bug293235.html
new file mode 100644
index 0000000000..458f88431c
--- /dev/null
+++ b/docshell/test/chrome/bug293235.html
@@ -0,0 +1,13 @@
+<html>
+ <head>
+ <title>Bug 293235 page1</title>
+ <style type="text/css">
+ a:visited, a.forcevisited.forcevisited { color: rgb(128, 0, 128); }
+ a:link, a.forcelink.forcelink { color: rgb(0, 0, 128); }
+ a:focus { color: rgb(128, 0, 0); }
+ </style>
+ </head>
+ <body>
+ <a id="link1" href="bug293235_p2.html">This is a test link.</a>
+ </body>
+</html>
diff --git a/docshell/test/chrome/bug293235_p2.html b/docshell/test/chrome/bug293235_p2.html
new file mode 100644
index 0000000000..2de067b80e
--- /dev/null
+++ b/docshell/test/chrome/bug293235_p2.html
@@ -0,0 +1,8 @@
+<html>
+ <head>
+ <title>Bug 293235 page2</title>
+ </head>
+ <body>
+ Nothing to see here, move along.
+ </body>
+</html>
diff --git a/docshell/test/chrome/bug293235_window.xhtml b/docshell/test/chrome/bug293235_window.xhtml
new file mode 100644
index 0000000000..7d87517824
--- /dev/null
+++ b/docshell/test/chrome/bug293235_window.xhtml
@@ -0,0 +1,118 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window id="293235Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="600"
+ height="600"
+ onload="setTimeout(runTests, 0);"
+ title="bug 293235 test">
+
+ <script type="application/javascript" src= "chrome://mochikit/content/chrome-harness.js" />
+ <script type="application/javascript" src="docshell_helpers.js" />
+ <script src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
+
+ <script type="application/javascript"><![CDATA[
+ var {NetUtil} = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
+
+ // Return the Element object for the specified element id
+ function $(id) { return TestWindow.getDocument().getElementById(id); }
+
+ ////
+ // Generator function for test steps for bug 293235:
+ // A visited link should have the :visited style applied
+ // to it when displayed on a page which was fetched from
+ // the bfcache.
+ //
+ async function runTests() {
+ // Register our observer to know when the link lookup is complete.
+ let testURI = NetUtil.newURI(getHttpUrl("bug293235_p2.html"));
+ let os = SpecialPowers.Services.obs;
+ // Load a test page containing a link that should be initially
+ // blue, per the :link style.
+ await new Promise(resolve => {
+ doPageNavigation({
+ uri: getHttpUrl("bug293235.html"),
+ onNavComplete: resolve,
+ });
+ });
+
+ // Now that we've been notified, we can check our link color.
+ // Since we can't use getComputedStyle() for this because
+ // getComputedStyle lies about styles that result from :visited,
+ // we have to take snapshots.
+ // First, take two reference snapshots.
+ var link1 = $("link1");
+ link1.className = "forcelink";
+ var refLink = await snapshotWindow(TestWindow.getWindow());
+ link1.className = "forcevisited";
+ var refVisited = await snapshotWindow(TestWindow.getWindow());
+ link1.className = "";
+ function snapshotsEqual(snap1, snap2) {
+ return compareSnapshots(snap1, snap2, true)[0];
+ }
+ ok(!snapshotsEqual(refLink, refVisited), "references should not match");
+ ok(snapshotsEqual(refLink, await snapshotWindow(TestWindow.getWindow())),
+ "link should initially be blue");
+
+ let observedVisit = false, observedPageShow = false;
+ await new Promise(resolve => {
+ function maybeResolve() {
+ ok(true, "maybe run next test? visited: " + observedVisit + " pageShow: " + observedPageShow);
+ if (observedVisit && observedPageShow)
+ resolve();
+ }
+
+ // Because adding visits is async, we will not be notified immediately.
+ let visitObserver = {
+ observe(aSubject, aTopic, aData)
+ {
+ if (!testURI.equals(aSubject.QueryInterface(Ci.nsIURI))) {
+ return;
+ }
+ os.removeObserver(this, aTopic);
+ observedVisit = true;
+ maybeResolve();
+ },
+ };
+ os.addObserver(visitObserver, "uri-visit-saved");
+ // Load the page that the link on the previous page points to.
+ doPageNavigation({
+ uri: getHttpUrl("bug293235_p2.html"),
+ onNavComplete() {
+ observedPageShow = true;
+ maybeResolve();
+ }
+ });
+ })
+
+ // And the nodes get notified after the "uri-visit-saved" topic, so
+ // we need to execute soon...
+ await new Promise(SimpleTest.executeSoon);
+
+ // Go back, verify the original page was loaded from the bfcache,
+ // and verify that the link is now purple, per the
+ // :visited style.
+ await new Promise(resolve => {
+ doPageNavigation({
+ back: true,
+ eventsToListenFor: ["pageshow"],
+ expectedEvents: [ { type: "pageshow",
+ persisted: true,
+ title: "Bug 293235 page1" } ],
+ onNavComplete: resolve,
+ });
+ })
+
+ // Now we can test the link color.
+ ok(snapshotsEqual(refVisited, await snapshotWindow(TestWindow.getWindow())),
+ "visited link should be purple");
+
+ // Tell the framework the test is finished.
+ finish();
+ }
+
+ ]]></script>
+
+ <browser type="content" primary="true" flex="1" id="content" src="about:blank"/>
+</window>
diff --git a/docshell/test/chrome/bug294258_testcase.html b/docshell/test/chrome/bug294258_testcase.html
new file mode 100644
index 0000000000..cd80fefd06
--- /dev/null
+++ b/docshell/test/chrome/bug294258_testcase.html
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>Bug 294258 Testcase</title>
+ <meta http-equiv="Content-Type" content="application/xhtml+xml"/>
+ <style type="text/css">
+ * {
+ font-family: monospace;
+ }
+ </style>
+ </head>
+ <body>
+ <div>
+ <p>
+ input type="text": <input id="text" type="text"/>
+ </p>
+ <p>
+ input type="checkbox": <input id="checkbox" type="checkbox"/>
+ </p>
+ <p>
+ input type="file": <input id="file" type="file"/>
+ </p>
+ <p>
+ input type="radio":
+ <input type="radio" id="radio1" name="radio" value="radio1"/>
+ <input id="radio2" type="radio" name="radio" value="radio2"/>
+ </p>
+ <p>
+ textarea: <textarea id="textarea" rows="4" cols="80"></textarea>
+ </p>
+ <p>
+ select -> option: <select id="select">
+ <option>1</option>
+ <option>2</option>
+ <option>3</option>
+ <option>4</option>
+ </select>
+ </p>
+ </div>
+ </body>
+</html>
diff --git a/docshell/test/chrome/bug294258_window.xhtml b/docshell/test/chrome/bug294258_window.xhtml
new file mode 100644
index 0000000000..fe47f3e70f
--- /dev/null
+++ b/docshell/test/chrome/bug294258_window.xhtml
@@ -0,0 +1,72 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window id="294258Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="600"
+ height="600"
+ onload="setTimeout(nextTest, 0);"
+ title="bug 294258 test">
+
+ <script type="application/javascript" src= "chrome://mochikit/content/chrome-harness.js" />
+ <script type="application/javascript" src="docshell_helpers.js" />
+ <script type="application/javascript"><![CDATA[
+
+ // Define the generator-iterator for the tests.
+ var tests = testIterator();
+
+ ////
+ // Execute the next test in the generator function.
+ //
+ function nextTest() {
+ tests.next();
+ }
+
+ function $(id) { return TestWindow.getDocument().getElementById(id); }
+
+ ////
+ // Generator function for test steps for bug 294258:
+ // Form values should be preserved on reload.
+ //
+ function* testIterator()
+ {
+ // Load a page containing a form.
+ doPageNavigation( {
+ uri: getHttpUrl("bug294258_testcase.html"),
+ onNavComplete: nextTest
+ } );
+ yield undefined;
+
+ // Change the data for each of the form fields, and reload.
+ $("text").value = "text value";
+ $("checkbox").checked = true;
+ var file = SpecialPowers.Services.dirsvc.get("TmpD", Ci.nsIFile);
+ file.append("294258_test.file");
+ file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
+ let filePath = file.path;
+ $("file").value = filePath;
+ $("textarea").value = "textarea value";
+ $("radio1").checked = true;
+ $("select").selectedIndex = 2;
+ doPageNavigation( {
+ reload: true,
+ onNavComplete: nextTest
+ } );
+ yield undefined;
+
+ // Verify that none of the form data has changed.
+ is($("text").value, "text value", "Text value changed");
+ is($("checkbox").checked, true, "Checkbox value changed");
+ is($("file").value, filePath, "File value changed");
+ is($("textarea").value, "textarea value", "Textarea value changed");
+ is($("radio1").checked, true, "Radio value changed");
+ is($("select").selectedIndex, 2, "Select value changed");
+
+ // Tell the framework the test is finished.
+ finish();
+ }
+
+ ]]></script>
+
+ <browser type="content" primary="true" flex="1" id="content" src="about:blank"/>
+</window>
diff --git a/docshell/test/chrome/bug298622_window.xhtml b/docshell/test/chrome/bug298622_window.xhtml
new file mode 100644
index 0000000000..318fb9d1d3
--- /dev/null
+++ b/docshell/test/chrome/bug298622_window.xhtml
@@ -0,0 +1,135 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window id="298622Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="600"
+ height="600"
+ onload="setTimeout(runTest, 0);"
+ title="bug 298622 test">
+
+ <script type="application/javascript" src= "chrome://mochikit/content/chrome-harness.js" />
+ <script type="application/javascript" src= "docshell_helpers.js" />
+ <script type="application/javascript"><![CDATA[
+ // Global variable that holds a reference to the find bar.
+ var gFindBar;
+
+ ////
+ // Test for bug 298622:
+ // Find should work correctly on a page loaded from the
+ // bfcache.
+ //
+ async function runTest()
+ {
+ // Make sure bfcache is on.
+ enableBFCache(true);
+
+ // Load a test page which contains some text to be found.
+ await promisePageNavigation({
+ uri: "data:text/html,<html><head><title>test1</title></head>" +
+ "<body>find this!</body></html>",
+ });
+
+ // Load a second, dummy page, verifying that the original
+ // page gets stored in the bfcache.
+ await promisePageNavigation({
+ uri: getHttpUrl("generic.html"),
+ eventsToListenFor: ["pageshow", "pagehide"],
+ expectedEvents: [ { type: "pagehide",
+ title: "test1",
+ persisted: true },
+ { type: "pageshow",
+ title: "generic page" } ],
+ });
+
+ // Make sure we unsuppress painting before continuing
+ await new Promise(resolve => {
+ SimpleTest.executeSoon(resolve);
+ });
+
+ // Search for some text that's on the second page (but not on
+ // the first page), and verify that it can be found.
+ gFindBar = document.getElementById("FindToolbar");
+ document.getElementById("cmd_find").doCommand();
+ ok(!gFindBar.hidden, "failed to open findbar");
+ gFindBar._findField.value = "A generic page";
+ gFindBar._find();
+ await new Promise(resolve => {
+ SimpleTest.executeSoon(resolve);
+ });
+
+ // Make sure Find bar's internal status is not 'notfound'
+ isnot(gFindBar._findField.getAttribute("status"), "notfound",
+ "Findfield status attribute should not have been 'notfound'" +
+ " after Find");
+
+ // Make sure the key events above have time to be processed
+ // before continuing
+ await promiseTrue(() =>
+ SpecialPowers.spawn(document.getElementById("content"), [], function() {
+ return content.document.getSelection().toString().toLowerCase() == "a generic page";
+ }), 20
+ );
+
+ is(gFindBar._findField.value, "A generic page",
+ "expected text not present in find input field");
+ is(await SpecialPowers.spawn(document.getElementById("content"), [], async function() {
+ return content.document.getSelection().toString().toLowerCase();
+ }),
+ "a generic page",
+ "find failed on second page loaded");
+
+ // Go back to the original page and verify it's loaded from the
+ // bfcache.
+ await promisePageNavigation({
+ back: true,
+ eventsToListenFor: ["pageshow"],
+ expectedEvents: [ { type: "pageshow",
+ title: "test1",
+ persisted: true } ],
+ });
+
+ // Search for some text that's on the original page (but not
+ // the dummy page loaded above), and verify that it can
+ // be found.
+ gFindBar = document.getElementById("FindToolbar");
+ document.getElementById("cmd_find").doCommand();
+ ok(!gFindBar.hidden, "failed to open findbar");
+ gFindBar._findField.value = "find this";
+ gFindBar._find();
+ await new Promise(resolve => {
+ SimpleTest.executeSoon(resolve);
+ });
+
+ // Make sure Find bar's internal status is not 'notfound'
+ isnot(gFindBar._findField.getAttribute("status"), "notfound",
+ "Findfield status attribute should not have been 'notfound'" +
+ " after Find");
+
+ // Make sure the key events above have time to be processed
+ // before continuing
+ await promiseTrue(() =>
+ SpecialPowers.spawn(document.getElementById("content"), [], function() {
+ return content.document.getSelection().toString().toLowerCase() == "find this";
+ }), 20
+ );
+
+ is(await SpecialPowers.spawn(document.getElementById("content"), [], async function() {
+ return content.document.getSelection().toString().toLowerCase();
+ }),
+ "find this",
+ "find failed on page loaded from bfcache");
+
+ // Tell the framework the test is finished.
+ finish();
+ }
+
+ ]]></script>
+
+ <commandset>
+ <command id="cmd_find"
+ oncommand="document.getElementById('FindToolbar').onFindCommand();"/>
+ </commandset>
+ <browser type="content" primary="true" flex="1" id="content" messagemanagergroup="test" remote="true" />
+ <findbar id="FindToolbar" browserid="content"/>
+</window>
diff --git a/docshell/test/chrome/bug301397_1.html b/docshell/test/chrome/bug301397_1.html
new file mode 100644
index 0000000000..9943c2efe6
--- /dev/null
+++ b/docshell/test/chrome/bug301397_1.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>iframe parent</title>
+ </head>
+<body>
+ <iframe id="iframe" src="bug301397_2.html"/>
+ </body>
+</html>
diff --git a/docshell/test/chrome/bug301397_2.html b/docshell/test/chrome/bug301397_2.html
new file mode 100644
index 0000000000..4237107060
--- /dev/null
+++ b/docshell/test/chrome/bug301397_2.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>iframe content #1</title>
+ </head>
+<body>
+ iframe page 1<br/>
+ <a id="link" href="bug301397_3.html">go to next page</a>
+ </body>
+</html>
diff --git a/docshell/test/chrome/bug301397_3.html b/docshell/test/chrome/bug301397_3.html
new file mode 100644
index 0000000000..8d36e92461
--- /dev/null
+++ b/docshell/test/chrome/bug301397_3.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>iframe content #2</title>
+ </head>
+<body>
+ iframe page 2<br/>
+ You made it!
+ </body>
+</html>
diff --git a/docshell/test/chrome/bug301397_4.html b/docshell/test/chrome/bug301397_4.html
new file mode 100644
index 0000000000..5584a4554a
--- /dev/null
+++ b/docshell/test/chrome/bug301397_4.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>dummy page, no iframe</title>
+ </head>
+<body>
+ Just a boring test page, nothing special.
+ </body>
+</html>
diff --git a/docshell/test/chrome/bug301397_window.xhtml b/docshell/test/chrome/bug301397_window.xhtml
new file mode 100644
index 0000000000..bf0f33c794
--- /dev/null
+++ b/docshell/test/chrome/bug301397_window.xhtml
@@ -0,0 +1,218 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window id="301397Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="600"
+ height="600"
+ onload="setTimeout(runTest, 0);"
+ title="bug 301397 test">
+
+ <script type="application/javascript" src= "chrome://mochikit/content/chrome-harness.js" />
+ <script type="application/javascript" src="docshell_helpers.js" />
+ <script type="application/javascript"><![CDATA[
+ Services.prefs.setBoolPref("browser.navigation.requireUserInteraction", false);
+
+ ////
+ // Verifies that the given string exists in the innerHTML of the iframe
+ // content.
+ //
+ async function verifyIframeInnerHtml(string) {
+ var iframeInnerHtml = await SpecialPowers.spawn(document.getElementById("content"), [string], (string) =>
+ content.document.getElementById("iframe").contentDocument.body.innerHTML);
+ ok(iframeInnerHtml.includes(string),
+ "iframe contains wrong document: " + iframeInnerHtml);
+ }
+
+ ////
+ // Generator function for test steps for bug 301397:
+ // The correct page should be displayed in an iframe when
+ // navigating back and forwards, when the parent page
+ // occupies multiple spots in the session history.
+ //
+ async function runTest()
+ {
+ // Make sure the bfcache is enabled.
+ enableBFCache(8);
+
+ // Load a dummy page.
+ await promisePageNavigation({
+ uri: getHttpUrl("generic.html"),
+ });
+
+ // Load a page containing an iframe.
+ await promisePageNavigation({
+ uri: getHttpUrl("bug301397_1.html"),
+ eventsToListenFor: ["pageshow", "pagehide"],
+ expectedEvents: [ { type: "pagehide",
+ title: "generic page",
+ persisted: true },
+ { type: "pageshow",
+ title: "iframe content #1",
+ persisted: false }, // false on initial load
+ { type: "pageshow",
+ title: "iframe parent",
+ persisted: false } ], // false on initial load
+ });
+
+ // Click a link in the iframe to cause the iframe to navigate
+ // to a new page, and wait until the related pagehide/pageshow
+ // events have occurred.
+ let waitForLinkNavigation = promisePageEvents({
+ eventsToListenFor: ["pageshow", "pagehide"],
+ expectedEvents: [ { type: "pagehide",
+ title: "iframe content #1",
+ persisted: false }, // false, subframe nav
+ { type: "pageshow",
+ title: "iframe content #2",
+ persisted: false } ], // false on initial load
+ });
+ SpecialPowers.spawn(document.getElementById("content"), [], function() {
+ let iframe = content.document.getElementById("iframe");
+ let link = iframe.contentDocument.getElementById("link");
+ let event = iframe.contentDocument.createEvent("MouseEvents");
+ event.initMouseEvent("click", true, true, iframe.contentWindow,
+ 0, 0, 0, 0, 0,
+ false, false, false, false,
+ 0, null);
+ link.dispatchEvent(event);
+ });
+ await waitForLinkNavigation;
+
+ // Load another dummy page. Verify that both the outgoing parent and
+ // iframe pages are stored in the bfcache.
+ await promisePageNavigation({
+ uri: getHttpUrl("bug301397_4.html"),
+ eventsToListenFor: ["pageshow", "pagehide"],
+ expectedEvents: [ { type: "pagehide",
+ title: "iframe parent",
+ persisted: true },
+ { type: "pagehide",
+ title: "iframe content #2",
+ persisted: true },
+ { type: "pageshow",
+ title: "dummy page, no iframe",
+ persisted: false } ], // false on initial load
+ });
+
+ // Go back. The iframe should show the second page loaded in it.
+ // Both the parent and the iframe pages should be loaded from
+ // the bfcache.
+ await promisePageNavigation({
+ back: true,
+ eventsToListenFor: ["pageshow", "pagehide"],
+ expectedEvents: [ { type: "pagehide",
+ title: "dummy page, no iframe",
+ persisted: true },
+ { type: "pageshow",
+ persisted: true,
+ title: "iframe content #2" },
+ { type: "pageshow",
+ persisted: true,
+ title: "iframe parent" } ],
+ });
+
+ verifyIframeInnerHtml("You made it");
+
+ // Go gack again. The iframe should show the first page loaded in it.
+ await promisePageNavigation({
+ back: true,
+ eventsToListenFor: ["pageshow", "pagehide"],
+ expectedEvents: [ { type: "pagehide",
+ title: "iframe content #2",
+ persisted: false }, // false, subframe nav
+ { type: "pageshow",
+ title: "iframe content #1",
+ // false since this page was never stored
+ // in the bfcache in the first place
+ persisted: false } ],
+ });
+
+ verifyIframeInnerHtml("go to next page");
+
+ // Go back to the generic page. Now go forward to the last page,
+ // again verifying that the iframe shows the first and second
+ // pages in order.
+ await promisePageNavigation({
+ back: true,
+ eventsToListenFor: ["pageshow", "pagehide"],
+ expectedEvents: [ { type: "pagehide",
+ title: "iframe parent",
+ persisted: true },
+ { type: "pagehide",
+ title: "iframe content #1",
+ persisted: true },
+ { type: "pageshow",
+ title: "generic page",
+ persisted: true } ],
+ });
+
+ await promisePageNavigation({
+ forward: true,
+ eventsToListenFor: ["pageshow"],
+ expectedEvents: [ {type: "pageshow",
+ title: "iframe content #1",
+ persisted: true},
+ {type: "pageshow",
+ title: "iframe parent",
+ persisted: true} ],
+ });
+
+ verifyIframeInnerHtml("go to next page");
+
+ await promisePageNavigation({
+ forward: true,
+ eventsToListenFor: ["pageshow", "pagehide"],
+ expectedEvents: [ { type: "pagehide",
+ title: "iframe content #1",
+ persisted: false }, // false, subframe nav
+ { type: "pageshow",
+ title: "iframe content #2",
+ // false because the page wasn't stored in
+ // bfcache last time it was unloaded
+ persisted: false } ],
+ });
+
+ verifyIframeInnerHtml("You made it");
+
+ await promisePageNavigation({
+ forward: true,
+ eventsToListenFor: ["pageshow", "pagehide"],
+ expectedEvents: [ { type: "pagehide",
+ title: "iframe parent",
+ persisted: true },
+ { type: "pagehide",
+ title: "iframe content #2",
+ persisted: true },
+ { type: "pageshow",
+ title: "dummy page, no iframe",
+ persisted: true } ],
+ });
+
+ // Go back once more, and again verify that the iframe shows the
+ // second page loaded in it.
+ await promisePageNavigation({
+ back: true,
+ eventsToListenFor: ["pageshow", "pagehide"],
+ expectedEvents: [ { type: "pagehide",
+ title: "dummy page, no iframe",
+ persisted: true },
+ { type: "pageshow",
+ persisted: true,
+ title: "iframe content #2" },
+ { type: "pageshow",
+ persisted: true,
+ title: "iframe parent" } ],
+ });
+
+ verifyIframeInnerHtml("You made it");
+
+ Services.prefs.clearUserPref("browser.navigation.requireUserInteraction");
+ // Tell the framework the test is finished.
+ finish();
+ }
+
+ ]]></script>
+
+ <browser type="content" primary="true" flex="1" id="content" messagemanagergroup="test" remote="true" />
+</window>
diff --git a/docshell/test/chrome/bug303267.html b/docshell/test/chrome/bug303267.html
new file mode 100644
index 0000000000..21b9f30311
--- /dev/null
+++ b/docshell/test/chrome/bug303267.html
@@ -0,0 +1,23 @@
+<html>
+<head>
+ <title>
+ bug303267.html
+ </title>
+ </head>
+<body onpageshow="showpageshowcount()">
+<script>
+var pageshowcount = 0;
+function showpageshowcount() {
+ pageshowcount++;
+ var div1 = document.getElementById("div1");
+ while (div1.firstChild) {
+ div1.firstChild.remove();
+ }
+ div1.appendChild(document.createTextNode(
+ "pageshowcount: " + pageshowcount));
+}
+</script>
+<div id="div1">
+ </div>
+</body>
+</html>
diff --git a/docshell/test/chrome/bug303267_window.xhtml b/docshell/test/chrome/bug303267_window.xhtml
new file mode 100644
index 0000000000..f985b4da59
--- /dev/null
+++ b/docshell/test/chrome/bug303267_window.xhtml
@@ -0,0 +1,83 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window id="303267Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="600"
+ height="600"
+ onload="setTimeout(runTests, 0);"
+ title="bug 303267 test">
+
+ <script type="application/javascript" src= "chrome://mochikit/content/chrome-harness.js" />
+ <script type="application/javascript" src="docshell_helpers.js" />
+ <script type="application/javascript"><![CDATA[
+ ////
+ // Bug 303267: When a page is displayed from the bfcache, the script globals should
+ // remain intact from the page's initial load.
+ //
+ async function runTests()
+ {
+ // Load an initial test page which should be saved in the bfcache.
+ var navData = {
+ uri: getHttpUrl("bug303267.html"),
+ eventsToListenFor: ["pageshow"],
+ expectedEvents: [ {type: "pageshow", title: "bug303267.html"} ],
+ };
+ await promisePageNavigation(navData);
+
+ // Save the HTML of the test page for later comparison.
+ var originalHTML = await getInnerHTMLById("div1");
+
+ // Load a second test page. The first test page's pagehide event should
+ // have the .persisted property set to true, indicating that it was
+ // stored in the bfcache.
+ navData = {
+ uri: "data:text/html,<html><head><title>page2</title></head>" +
+ "<body>bug303267, page2</body></html>",
+ eventsToListenFor: ["pageshow", "pagehide"],
+ expectedEvents: [ {type: "pagehide",
+ title: "bug303267.html",
+ persisted: true},
+ {type: "pageshow",
+ title: "page2"} ],
+ };
+ await promisePageNavigation(navData);
+
+ // Go back. Verify that the pageshow event for the original test page
+ // had a .persisted property of true, indicating that it came from the
+ // bfcache.
+ navData = {
+ back: true,
+ eventsToListenFor: ["pageshow", "pagehide"],
+ expectedEvents: [ {type: "pagehide",
+ title: "page2"},
+ {type: "pageshow",
+ title: "bug303267.html",
+ persisted: true} ],
+ };
+ await promisePageNavigation(navData);
+
+ // After going back, if showpagecount() could access a global variable
+ // and change the test div's innerHTML, then we pass. Otherwise, it
+ // threw an exception and the following test will fail.
+ var newHTML = await getInnerHTMLById("div1");
+ isnot(originalHTML,
+ newHTML, "HTML not updated on pageshow; javascript broken?");
+
+ // Tell the framework the test is finished.
+ finish();
+ }
+
+ ////
+ // Return the innerHTML of a particular element in the content document.
+ //
+ function getInnerHTMLById(id) {
+ return SpecialPowers.spawn(TestWindow.getBrowser(), [id], (id) => {
+ return content.document.getElementById(id).innerHTML;
+ });
+ }
+
+ ]]></script>
+
+ <browser type="content" primary="true" flex="1" id="content" remote="true" />
+</window>
diff --git a/docshell/test/chrome/bug311007_window.xhtml b/docshell/test/chrome/bug311007_window.xhtml
new file mode 100644
index 0000000000..13ae5e16e3
--- /dev/null
+++ b/docshell/test/chrome/bug311007_window.xhtml
@@ -0,0 +1,204 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window id="311007Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="600"
+ height="600"
+ onload="startup();"
+ title="bug 311007 test">
+
+ <script src="chrome://mochikit/content/chrome-harness.js" />
+ <script type="application/javascript" src="docshell_helpers.js"></script>
+ <script type="application/javascript"><![CDATA[
+ // `content` is the id of the browser element used for the test.
+ /* global content */
+/*
+ Regression test for bug 283733 and bug 307027.
+
+ Bug 283733
+ "accessing a relative anchor in a secure page removes the
+ locked icon and yellow background UI"
+
+ Bug 307027
+ "Going back from secure page to error page does not clear yellow bar"
+
+ And enhancements:
+
+ Bug 478927
+ onLocationChange should notify whether or not loading an error page.
+
+ */
+
+const kDNSErrorURI = "https://example/err.html";
+const kSecureURI =
+ "https://example.com/tests/docshell/test/navigation/blank.html";
+
+/*
+ Step 1: load a network error page. <err.html> Not Secure
+ Step 2: load a secure page. <blank.html> Secure
+ Step 3: a secure page + hashchange. <blank.html#foo> Secure (bug 283733)
+ Step 4: go back to the error page. <err.html> Not Secure (bug 307027)
+ */
+
+var gListener = null;
+
+function WebProgressListener() {
+ this._callback = null;
+}
+
+WebProgressListener.prototype = {
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIWebProgressListener",
+ "nsISupportsWeakReference",
+ ]),
+
+ onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
+ setTimeout(this._callback, 0, aWebProgress, aRequest, aLocation, aFlags);
+ },
+
+ set callback(aVal) {
+ this._callback = aVal;
+ }
+};
+
+function startup() {
+ gListener = new WebProgressListener();
+
+ document.getElementById("content")
+ .webProgress
+ .addProgressListener(gListener,
+ Ci.nsIWebProgress
+ .NOTIFY_LOCATION);
+
+ setTimeout(step1A, 0);
+}
+
+/******************************************************************************
+ * Step 1: Load an error page, and confirm UA knows it's insecure.
+ ******************************************************************************/
+
+function step1A() {
+ gListener.callback = step1B;
+ content.location = kDNSErrorURI;
+}
+
+function step1B(aWebProgress, aRequest, aLocation, aFlags) {
+ is(aLocation.spec, kDNSErrorURI, "Error page's URI (1)");
+
+ ok(!(aFlags & Ci.nsIWebProgressListener
+ .LOCATION_CHANGE_SAME_DOCUMENT),
+ "DocShell loaded a document (1)");
+
+ ok((aFlags & Ci.nsIWebProgressListener
+ .LOCATION_CHANGE_ERROR_PAGE),
+ "This page is an error page.");
+
+ ok(!(document.getElementById("content")
+ .browsingContext
+ .secureBrowserUI.state &
+ Ci.nsIWebProgressListener.STATE_IS_SECURE),
+ "This is not a secure page (1)");
+
+ /* Go to step 2. */
+ setTimeout(step2A, 0);
+}
+
+/******************************************************************************
+ * Step 2: Load a HTTPS page, and confirm it's secure.
+ ******************************************************************************/
+
+function step2A() {
+ gListener.callback = step2B;
+ content.location = kSecureURI;
+}
+
+function step2B(aWebProgress, aRequest, aLocation, aFlags) {
+ is(aLocation.spec, kSecureURI, "A URI on HTTPS (2)");
+
+ ok(!(aFlags & Ci.nsIWebProgressListener
+ .LOCATION_CHANGE_SAME_DOCUMENT),
+ "DocShell loaded a document (2)");
+
+ ok(!(aFlags & Ci.nsIWebProgressListener
+ .LOCATION_CHANGE_ERROR_PAGE),
+ "This page is not an error page.");
+
+ ok((document.getElementById("content")
+ .browsingContext
+ .secureBrowserUI.state &
+ Ci.nsIWebProgressListener.STATE_IS_SECURE),
+ "This is a secure page (2)");
+
+ /* Go to step 3. */
+ setTimeout(step3A, 0);
+}
+
+/*****************************************************************************
+ * Step 3: Trigger hashchange within a secure page, and confirm UA knows
+ * it's secure. (Bug 283733)
+ *****************************************************************************/
+
+function step3A() {
+ gListener.callback = step3B;
+ content.location += "#foo";
+}
+
+function step3B(aWebProgress, aRequest, aLocation, aFlags) {
+ is(aLocation.spec, kSecureURI + "#foo", "hashchange on HTTPS (3)");
+
+ ok((aFlags & Ci.nsIWebProgressListener
+ .LOCATION_CHANGE_SAME_DOCUMENT),
+ "We are in the same document as before (3)");
+
+ ok(!(aFlags & Ci.nsIWebProgressListener
+ .LOCATION_CHANGE_ERROR_PAGE),
+ "This page is not an error page.");
+
+ ok((document.getElementById("content")
+ .browsingContext
+ .secureBrowserUI.state &
+ Ci.nsIWebProgressListener.STATE_IS_SECURE),
+ "This is a secure page (3)");
+
+ /* Go to step 4. */
+ setTimeout(step4A, 0);
+}
+
+/*****************************************************************************
+ * Step 4: Go back from a secure page to an error page, and confirm UA knows
+ * it's not secure. (Bug 307027)
+ *****************************************************************************/
+
+function step4A() {
+ gListener.callback = step4B;
+ content.history.go(-2);
+}
+
+function step4B(aWebProgress, aRequest, aLocation, aFlags) {
+ is(aLocation.spec, kDNSErrorURI, "Go back to the error URI (4)");
+
+ ok(!(aFlags & Ci.nsIWebProgressListener
+ .LOCATION_CHANGE_SAME_DOCUMENT),
+ "DocShell loaded a document (4)");
+
+ ok((aFlags & Ci.nsIWebProgressListener
+ .LOCATION_CHANGE_ERROR_PAGE),
+ "This page is an error page.");
+
+ ok(!(document.getElementById("content")
+ .browsingContext
+ .secureBrowserUI.state &
+ Ci.nsIWebProgressListener.STATE_IS_SECURE),
+ "This is not a secure page (4)");
+
+ /* End. */
+ document.getElementById("content")
+ .webProgress.removeProgressListener(gListener);
+ gListener = null;
+ finish();
+}
+ ]]></script>
+
+ <browser type="content" primary="true" flex="1" id="content" src="about:blank"/>
+</window>
diff --git a/docshell/test/chrome/bug321671_window.xhtml b/docshell/test/chrome/bug321671_window.xhtml
new file mode 100644
index 0000000000..e8fb9fbc06
--- /dev/null
+++ b/docshell/test/chrome/bug321671_window.xhtml
@@ -0,0 +1,128 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window id="321671Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="600"
+ height="600"
+ onload="setTimeout(runTest, 0);"
+ title="bug 321671 test">
+
+ <script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js" />
+ <script type="application/javascript" src="docshell_helpers.js" />
+ <script type="application/javascript"><![CDATA[
+ Services.prefs.setBoolPref("browser.navigation.requireUserInteraction", false);
+
+ // Maximum number of entries in the bfcache for this session history.
+ // This number is hardcoded in docshell code. In the test, we'll
+ // navigate through enough pages so that we hit one that's been
+ // evicted from the bfcache because it's farther from the current
+ // page than this number.
+ const MAX_BFCACHE_PAGES = 3;
+
+ ////
+ // Bug 321671: Scroll position should be retained when moving backwards and
+ // forwards through pages when bfcache is enabled.
+ //
+ async function runTest()
+ {
+ // Variable to hold the scroll positions of the test pages.
+ var scrollPositions = [];
+
+ // Make sure bfcache is on.
+ enableBFCache(true);
+
+ // Load enough test pages that so the first one is evicted from the
+ // bfcache, scroll down on each page, and save the
+ // current scroll position before continuing. Verify that each
+ // page we're navigating away from is initially put into the bfcache.
+ for (var i = 0; i <= MAX_BFCACHE_PAGES + 1; i++) {
+ let eventsToListenFor = ["pageshow"];
+ let expectedEvents = [ { type: "pageshow",
+ title: "bug321671 page" + (i + 1) } ];
+ if (i > 0) {
+ eventsToListenFor.push("pagehide");
+ expectedEvents.unshift({ type: "pagehide",
+ persisted: true,
+ title: "bug321671 page" + i });
+ }
+ await promisePageNavigation( {
+ uri: "data:text/html,<html><head><title>bug321671 page" + (i + 1) +
+ "</title></head>" +
+ "<body><table border='1' width='300' height='1000'>" +
+ "<tbody><tr><td>" +
+ " page " + (i + 1) + ": foobar foobar foobar foobar " +
+ "</td></tr></tbody></table> " +
+ "</body></html>",
+ eventsToListenFor,
+ expectedEvents,
+ } );
+
+ let { initialScrollY, scrollY } = await SpecialPowers.spawn(TestWindow.getBrowser(), [i], (i) => {
+ let initialScrollY = content.scrollY;
+ content.scrollByLines(10 + (2 * i));
+ return { initialScrollY, scrollY: content.scrollY };
+ });
+ is(initialScrollY, 0,
+ "Page initially has non-zero scrollY position");
+ ok(scrollY > 0,
+ "Page has zero scrollY position after scrolling");
+ scrollPositions[i] = scrollY;
+ }
+
+ // Go back to the first page, one page at a time. For each 'back'
+ // action, verify that its vertical scroll position is restored
+ // correctly. Verify that the last page in the sequence
+ // does not come from the bfcache. Again verify that all pages
+ // that we navigate away from are initially
+ // stored in the bfcache.
+ for (i = MAX_BFCACHE_PAGES + 1; i > 0; i--) {
+ await promisePageNavigation( {
+ back: true,
+ eventsToListenFor: ["pageshow", "pagehide"],
+ expectedEvents: [ { type: "pagehide",
+ title: "bug321671 page" + (i+1),
+ persisted: true },
+ { type: "pageshow",
+ title: "bug321671 page" + i,
+ persisted: i > 1 } ],
+ } );
+
+ is(await SpecialPowers.spawn(TestWindow.getBrowser(), [], () => {
+ return content.scrollY;
+ }), scrollPositions[i-1],
+ "Scroll position not restored while going back!");
+ }
+
+ // Traverse history forward now, and verify scroll position is still
+ // restored. Similar to the backward traversal, verify that all
+ // but the last page in the sequence comes from the bfcache. Also
+ // verify that all of the pages get stored in the bfcache when we
+ // navigate away from them.
+ for (i = 1; i <= MAX_BFCACHE_PAGES + 1; i++) {
+ await promisePageNavigation( {
+ forward: true,
+ eventsToListenFor: ["pageshow", "pagehide"],
+ expectedEvents: [ { type: "pagehide",
+ persisted: true,
+ title: "bug321671 page" + i },
+ { type: "pageshow",
+ persisted: i < MAX_BFCACHE_PAGES + 1,
+ title: "bug321671 page" + (i + 1) } ],
+ } );
+
+ is(await SpecialPowers.spawn(TestWindow.getBrowser(), [], () => {
+ return content.scrollY;
+ }), scrollPositions[i],
+ "Scroll position not restored while going forward!");
+ }
+
+ Services.prefs.clearUserPref("browser.navigation.requireUserInteraction");
+ // Tell the framework the test is finished.
+ finish();
+ }
+
+ ]]></script>
+
+ <browser type="content" primary="true" flex="1" id="content" remote="true" />
+</window>
diff --git a/docshell/test/chrome/bug360511_case1.html b/docshell/test/chrome/bug360511_case1.html
new file mode 100644
index 0000000000..cca043bb66
--- /dev/null
+++ b/docshell/test/chrome/bug360511_case1.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html style="height: 100%">
+<head>
+ <title>
+ bug360511 case 1
+ </title>
+ </head>
+<body style="height: 100%">
+<a id="link1" href="#bottom">jump to bottom</a>
+<div id="div1" style="height: 200%; border: thin solid black;">
+ hello large div
+ </div>
+ <a name="bottom">here's the bottom of the page</a>
+</body>
+</html>
diff --git a/docshell/test/chrome/bug360511_case2.html b/docshell/test/chrome/bug360511_case2.html
new file mode 100644
index 0000000000..217f47724e
--- /dev/null
+++ b/docshell/test/chrome/bug360511_case2.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html style="height: 100%">
+<head>
+ <title>
+ bug360511 case 2
+ </title>
+ </head>
+<body style="height: 100%">
+<a id="link1" href="#bottom">jump to bottom</a>
+<div id="div1" style="height: 200%; border: thin solid black;">
+ hello large div
+ </div>
+ <a name="bottom">here's the bottom of the page</a>
+</body>
+</html>
diff --git a/docshell/test/chrome/bug360511_window.xhtml b/docshell/test/chrome/bug360511_window.xhtml
new file mode 100644
index 0000000000..3a22b081a1
--- /dev/null
+++ b/docshell/test/chrome/bug360511_window.xhtml
@@ -0,0 +1,127 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window id="360511Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="600"
+ height="600"
+ onload="setTimeout(runTest, 0);"
+ title="bug 360511 test">
+
+ <script type="application/javascript" src= "chrome://mochikit/content/chrome-harness.js" />
+ <script type="application/javascript" src="docshell_helpers.js" />
+ <script type="application/javascript"><![CDATA[
+ Services.prefs.setBoolPref("browser.navigation.requireUserInteraction", false);
+
+ function getScrollY()
+ {
+ return SpecialPowers.spawn(TestWindow.getBrowser(), [], () => {
+ return content.scrollY;
+ });
+ }
+ function getLocation()
+ {
+ return SpecialPowers.spawn(TestWindow.getBrowser(), [], () => {
+ return content.location.href;
+ });
+ }
+
+ ////
+ // Bug 360511: Fragment uri's in session history should be restored correctly
+ // upon back navigation.
+ //
+ async function runTest()
+ {
+ // Case 1: load a page containing a fragment link; the page should be
+ // stored in the bfcache.
+ // Case 2: load a page containing a fragment link; the page should NOT
+ // be stored in the bfcache.
+ for (var i = 1; i < 3; i++)
+ {
+ var url = "bug360511_case" + i + ".html";
+ await promisePageNavigation( {
+ uri: getHttpUrl(url),
+ preventBFCache: i != 1
+ } );
+
+ // Store the original url for later comparison.
+ var originalUrl = TestWindow.getBrowser().currentURI.spec;
+ var originalDocLocation = await getLocation();
+
+ // Verify we're at the top of the page.
+ is(await getScrollY(), 0, "Page initially has a non-zero scrollY property");
+
+ // Click the on the fragment link in the browser, and use setTimeout
+ // to give the event a chance to be processed.
+ await SpecialPowers.spawn(TestWindow.getBrowser(), [], () => {
+ var event = content.document.createEvent('MouseEvent');
+ event.initMouseEvent("click", true, true, content, 0,
+ 0, 0, 0, 0,
+ false, false, false, false, 0, null);
+ content.document.getElementById("link1").dispatchEvent(event);
+ });
+ await promiseNextPaint();
+
+ // Verify we're no longer at the top of the page.
+ await promiseTrue(async function() {
+ return await getScrollY() > 0;
+ }, 20);
+
+ // Store the fragment url for later comparison.
+ var fragmentUrl = TestWindow.getBrowser().currentURI.spec;
+ let fragDocLocation = await getLocation();
+
+ // Now navigate to any other page
+ var expectedPageTitle = "bug360511 case " + i;
+ await promisePageNavigation( {
+ uri: getHttpUrl("generic.html"),
+ eventsToListenFor: ["pagehide", "pageshow"],
+ expectedEvents: [ {type: "pagehide", title: expectedPageTitle,
+ persisted: i == 1},
+ {type: "pageshow"} ],
+ } );
+
+ // Go back
+ await promisePageNavigation( {
+ back: true,
+ eventsToListenFor: ["pageshow"],
+ expectedEvents: [ {type: "pageshow", title: expectedPageTitle,
+ persisted: i == 1} ],
+ } );
+
+ // Verify the current url is the fragment url
+ is(TestWindow.getBrowser().currentURI.spec, fragmentUrl,
+ "current url is not the previous fragment url");
+ is(await getLocation(), fragDocLocation,
+ "document.location is not the previous fragment url");
+
+ // Go back again. Since we're just going from a fragment url to
+ // parent url, no pageshow event is fired, so don't wait for any
+ // events. Rather, just wait for the page's scrollY property to
+ // change.
+ var originalScrollY = await getScrollY();
+ doPageNavigation( {
+ back: true,
+ eventsToListenFor: []
+ } );
+ await promiseTrue(
+ async function() {
+ return (await getScrollY() != originalScrollY);
+ }, 20);
+
+ // Verify the current url is the original url without fragment
+ is(TestWindow.getBrowser().currentURI.spec, originalUrl,
+ "current url is not the original url");
+ is(await getLocation(), originalDocLocation,
+ "document.location is not the original url");
+ }
+
+ Services.prefs.clearUserPref("browser.navigation.requireUserInteraction");
+ // Tell the framework the test is finished.
+ finish();
+ }
+
+ ]]></script>
+
+ <browser type="content" primary="true" flex="1" id="content" remote="true" />
+</window>
diff --git a/docshell/test/chrome/bug364461_window.xhtml b/docshell/test/chrome/bug364461_window.xhtml
new file mode 100644
index 0000000000..d64a7b9e23
--- /dev/null
+++ b/docshell/test/chrome/bug364461_window.xhtml
@@ -0,0 +1,253 @@
+<?xml version="1.0"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window id="364461Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="600"
+ height="600"
+ onload="runTest();"
+ title="364461 test">
+
+ <script src="chrome://mochikit/content/chrome-harness.js" />
+ <script type="application/javascript" src="docshell_helpers.js" />
+ <script type="application/javascript"><![CDATA[
+ Services.prefs.setBoolPref("browser.navigation.requireUserInteraction", false);
+
+ var gBrowser;
+
+ async function runTest() {
+ gBrowser = document.getElementById("content");
+
+ // Tests 1 + 2:
+ // Back/forward between two simple documents. Bfcache will be used.
+
+ var test1Doc = "data:text/html,<html><head><title>test1</title></head>" +
+ "<body>test1</body></html>";
+
+ await promisePageNavigation({
+ uri: test1Doc,
+ eventsToListenFor: ["load", "pageshow"],
+ expectedEvents: [{type: "load", title: "test1"},
+ {type: "pageshow", title: "test1", persisted: false}],
+ });
+
+ var test2Doc = "data:text/html,<html><head><title>test2</title></head>" +
+ "<body>test2</body></html>";
+
+ await promisePageNavigation({
+ uri: test2Doc,
+ eventsToListenFor: ["load", "pageshow", "pagehide"],
+ expectedEvents: [{type: "pagehide", title: "test1", persisted: true},
+ {type: "load", title: "test2"},
+ {type: "pageshow", title: "test2", persisted: false}],
+ });
+
+ await promisePageNavigation({
+ back: true,
+ eventsToListenFor: ["pageshow", "pagehide"],
+ expectedEvents: [{type: "pagehide", title: "test2", persisted: true},
+ {type: "pageshow", title: "test1", persisted: true}],
+ });
+
+ await promisePageNavigation({
+ forward: true,
+ eventsToListenFor: ["pageshow", "pagehide"],
+ expectedEvents: [{type: "pagehide", title: "test1", persisted: true},
+ {type: "pageshow", title: "test2", persisted: true}],
+ });
+
+ // Tests 3 + 4:
+ // Back/forward between a two-level deep iframed document and a simple
+ // document. Bfcache will be used and events should be dispatched to
+ // all frames.
+
+ var test3Doc = "data:text/html,<html><head><title>test3</title>" +
+ "</head><body>" +
+ "<iframe src='data:text/html," +
+ "<html><head><title>test3-nested1</title></head>" +
+ "<body>test3-nested1" +
+ "<iframe src=\"data:text/html," +
+ "<html><head><title>test3-nested2</title></head>" +
+ "<body>test3-nested2</body></html>\">" +
+ "</iframe>" +
+ "</body></html>'>" +
+ "</iframe>" +
+ "</body></html>";
+
+ await promisePageNavigation({
+ uri: test3Doc,
+ eventsToListenFor: ["load", "pageshow", "pagehide"],
+ expectedEvents: [{type: "pagehide", title: "test2", persisted: true},
+ {type: "load", title: "test3-nested2"},
+ {type: "pageshow", title: "test3-nested2", persisted: false},
+ {type: "load", title: "test3-nested1"},
+ {type: "pageshow", title: "test3-nested1", persisted: false},
+ {type: "load", title: "test3"},
+ {type: "pageshow", title: "test3", persisted: false}],
+ });
+
+ var test4Doc = "data:text/html,<html><head><title>test4</title></head>" +
+ "<body>test4</body></html>";
+
+ await promisePageNavigation({
+ uri: test4Doc,
+ eventsToListenFor: ["load", "pageshow", "pagehide"],
+ expectedEvents: [{type: "pagehide", title: "test3", persisted: true},
+ {type: "pagehide", title: "test3-nested1", persisted: true},
+ {type: "pagehide", title: "test3-nested2", persisted: true},
+ {type: "load", title: "test4"},
+ {type: "pageshow", title: "test4", persisted: false}],
+ });
+
+ await promisePageNavigation({
+ back: true,
+ eventsToListenFor: ["pageshow", "pagehide"],
+ expectedEvents: [{type: "pagehide", title: "test4", persisted: true},
+ {type: "pageshow", title: "test3-nested2", persisted: true},
+ {type: "pageshow", title: "test3-nested1", persisted: true},
+ {type: "pageshow", title: "test3", persisted: true}],
+ });
+
+ // This is where the two nested pagehide are not dispatched in bug 364461
+ await promisePageNavigation({
+ forward: true,
+ eventsToListenFor: ["pageshow", "pagehide"],
+ expectedEvents: [{type: "pagehide", title: "test3", persisted: true},
+ {type: "pagehide", title: "test3-nested1", persisted: true},
+ {type: "pagehide", title: "test3-nested2", persisted: true},
+ {type: "pageshow", title: "test4", persisted: true}],
+ });
+
+ // Tests 5 + 6:
+ // Back/forward between a document containing an unload handler and a
+ // a simple document. Bfcache won't be used for the first one (see
+ // http://developer.mozilla.org/en/docs/Using_Firefox_1.5_caching).
+
+ var test5Doc = "data:text/html,<html><head><title>test5</title></head>" +
+ "<body onunload='while(false) { /* nop */ }'>" +
+ "test5</body></html>";
+
+ await promisePageNavigation({
+ uri: test5Doc,
+ eventsToListenFor: ["load", "pageshow", "pagehide"],
+ expectedEvents: [{type: "pagehide", title: "test4", persisted: true},
+ {type: "load", title: "test5"},
+ {type: "pageshow", title: "test5", persisted: false}],
+ });
+
+ var test6Doc = "data:text/html,<html><head><title>test6</title></head>" +
+ "<body>test6</body></html>";
+
+ await promisePageNavigation({
+ uri: test6Doc,
+ eventsToListenFor: ["load", "unload", "pageshow", "pagehide"],
+ expectedEvents: [{type: "pagehide", title: "test5", persisted: false},
+ {type: "unload", title: "test5"},
+ {type: "load", title: "test6"},
+ {type: "pageshow", title: "test6", persisted: false}],
+ });
+
+ await promisePageNavigation({
+ back: true,
+ eventsToListenFor: ["load", "pageshow", "pagehide"],
+ expectedEvents: [{type: "pagehide", title: "test6", persisted: true},
+ {type: "load", title: "test5"},
+ {type: "pageshow", title: "test5", persisted: false}],
+ });
+
+ await promisePageNavigation({
+ forward: true,
+ eventsToListenFor: ["unload", "pageshow", "pagehide"],
+ expectedEvents: [{type: "pagehide", title: "test5", persisted: false},
+ {type: "unload", title: "test5"},
+ {type: "pageshow", title: "test6", persisted: true}],
+ });
+
+ // Test 7:
+ // Testcase from https://bugzilla.mozilla.org/show_bug.cgi?id=384977#c10
+ // Check that navigation is not blocked after a document is restored
+ // from bfcache
+
+ var test7Doc = "data:text/html,<html><head><title>test7</title>" +
+ "</head><body>" +
+ "<iframe src='data:text/html," +
+ "<html><head><title>test7-nested1</title></head>" +
+ "<body>test7-nested1<br/>" +
+ "<a href=\"data:text/plain,aaa\" target=\"_top\">" +
+ "Click me, hit back, click me again</a>" +
+ "</body></html>'>" +
+ "</iframe>" +
+ "</body></html>";
+
+ await promisePageNavigation({
+ uri: test7Doc,
+ eventsToListenFor: ["load", "pageshow", "pagehide"],
+ expectedEvents: [{type: "pagehide", title: "test6", persisted: true},
+ {type: "load", title: "test7-nested1"},
+ {type: "pageshow", title: "test7-nested1", persisted: false},
+ {type: "load", title: "test7"},
+ {type: "pageshow", title: "test7", persisted: false}],
+ });
+
+ // Simulates a click on the link inside the iframe
+ function clickIframeLink() {
+ SpecialPowers.spawn(TestWindow.getBrowser(), [], () => {
+ var iframe = content.document.getElementsByTagName("iframe")[0];
+ var w = iframe.contentWindow;
+ var d = iframe.contentDocument;
+
+ var evt = d.createEvent("MouseEvents");
+ evt.initMouseEvent("click", true, true, w,
+ 0, 0, 0, 0, 0, false, false, false, false, 0, null);
+ d.getElementsByTagName("a")[0].dispatchEvent(evt);
+ });
+ }
+
+ let clicked = promisePageNavigation({
+ eventsToListenFor: ["load", "pageshow", "pagehide"],
+ expectedEvents: [{type: "pagehide", title: "test7", persisted: true},
+ {type: "pagehide", title: "test7-nested1", persisted: true},
+ {type: "load"},
+ {type: "pageshow", persisted: false}],
+ waitForEventsOnly: true,
+ });
+ clickIframeLink();
+ await clicked;
+
+ is(gBrowser.currentURI.spec, "data:text/plain,aaa",
+ "Navigation is blocked when clicking link");
+
+ await promisePageNavigation({
+ back: true,
+ eventsToListenFor: ["pageshow", "pagehide"],
+ expectedEvents: [{type: "pagehide", persisted: true},
+ {type: "pageshow", title: "test7-nested1", persisted: true},
+ {type: "pageshow", title: "test7", persisted: true}],
+ });
+
+ clicked = promisePageNavigation({
+ eventsToListenFor: ["load", "pageshow", "pagehide"],
+ expectedEvents: [{type: "pagehide", title: "test7", persisted: true},
+ {type: "pagehide", title: "test7-nested1", persisted: true},
+ {type: "load"},
+ {type: "pageshow", persisted: false}],
+ waitForEventsOnly: true,
+ });
+ clickIframeLink();
+ await clicked;
+
+ is(gBrowser.currentURI.spec, "data:text/plain,aaa",
+ "Navigation is blocked when clicking link");
+
+ Services.prefs.clearUserPref("browser.navigation.requireUserInteraction");
+ finish();
+ }
+ ]]></script>
+
+ <browser type="content" primary="true" flex="1" id="content" remote="true" />
+</window>
diff --git a/docshell/test/chrome/bug396519_window.xhtml b/docshell/test/chrome/bug396519_window.xhtml
new file mode 100644
index 0000000000..5cbc6f5a41
--- /dev/null
+++ b/docshell/test/chrome/bug396519_window.xhtml
@@ -0,0 +1,132 @@
+<?xml version="1.0"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window id="396519Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="600"
+ height="600"
+ onload="runTest();"
+ title="396519 test">
+
+ <script src="chrome://mochikit/content/chrome-harness.js" />
+ <script type="application/javascript" src="docshell_helpers.js" />
+ <script type="application/javascript"><![CDATA[
+ Services.prefs.setBoolPref("browser.navigation.requireUserInteraction", false);
+
+ var gTestCount = 0;
+
+ async function navigateAndTest(params, expected) {
+ await promisePageNavigation(params);
+ ++gTestCount;
+ await doTest(expected);
+ }
+
+ async function doTest(expected) {
+ function check(testCount, expected) {
+ let history;
+ if (SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ history = this.browsingContext.sessionHistory;
+ } else {
+ history = this.content.browsingContext.childSessionHistory.legacySHistory;
+ }
+ if (history.count == expected.length) {
+ for (let i = 0; i < history.count; i++) {
+ var shEntry = history.getEntryAtIndex(i).
+ QueryInterface(Ci.nsISHEntry);
+ is(shEntry.isInBFCache, expected[i], `BFCache for shentry[${i}], test ${testCount}`);
+ }
+
+ // Make sure none of the SHEntries share bfcache entries with one
+ // another.
+ for (let i = 0; i < history.count; i++) {
+ for (let j = 0; j < history.count; j++) {
+ if (j == i)
+ continue;
+
+ let shentry1 = history.getEntryAtIndex(i)
+ .QueryInterface(Ci.nsISHEntry);
+ let shentry2 = history.getEntryAtIndex(j)
+ .QueryInterface(Ci.nsISHEntry);
+ ok(!shentry1.sharesDocumentWith(shentry2),
+ 'Test ' + testCount + ': shentry[' + i + "] shouldn't " +
+ "share document with shentry[" + j + ']');
+ }
+ }
+ }
+ else {
+ is(history.count, expected.length, "Wrong history length in test "+testCount);
+ }
+ }
+
+ if (SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ check.call(TestWindow.getBrowser(), gTestCount, expected);
+ } else {
+ await SpecialPowers.spawn(TestWindow.getBrowser(), [gTestCount, expected], check);
+ }
+ }
+
+ async function runTest() {
+ // Tests 1 + 2:
+ // Back/forward between two simple documents. Bfcache will be used.
+
+ var test1Doc = "data:text/html,<html><head><title>test1</title></head>" +
+ "<body>test1</body></html>";
+
+ await navigateAndTest({
+ uri: test1Doc,
+ }, [false]);
+
+ var test2Doc = test1Doc.replace(/1/,"2");
+
+ await navigateAndTest({
+ uri: test2Doc,
+ }, [true, false]);
+
+ await navigateAndTest({
+ uri: test1Doc,
+ }, [true, true, false]);
+
+ await navigateAndTest({
+ uri: test2Doc,
+ }, [true, true, true, false]);
+
+ await navigateAndTest({
+ uri: test1Doc,
+ }, [false, true, true, true, false]);
+
+ await navigateAndTest({
+ uri: test2Doc,
+ }, [false, false, true, true, true, false]);
+
+ await navigateAndTest({
+ back: true,
+ }, [false, false, true, true, false, true]);
+
+ await navigateAndTest({
+ forward: true,
+ }, [false, false, true, true, true, false]);
+
+ await navigateAndTest({
+ gotoIndex: 1,
+ }, [false, false, true, true, true, false]);
+
+ await navigateAndTest({
+ back: true,
+ }, [false, true, true, true, false, false]);
+
+ await navigateAndTest({
+ gotoIndex: 5,
+ }, [false, false, true, true, false, false]);
+
+ Services.prefs.clearUserPref("browser.navigation.requireUserInteraction");
+ finish();
+ }
+ ]]></script>
+
+ <browser type="content" primary="true" flex="1" id="content" remote="true" />
+</window>
diff --git a/docshell/test/chrome/bug396649_window.xhtml b/docshell/test/chrome/bug396649_window.xhtml
new file mode 100644
index 0000000000..16a243bc74
--- /dev/null
+++ b/docshell/test/chrome/bug396649_window.xhtml
@@ -0,0 +1,119 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window id="396649Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="600"
+ height="600"
+ onload="setTimeout(nextTest, 0);"
+ title="bug 396649 test">
+
+ <script type="application/javascript" src= "chrome://mochikit/content/chrome-harness.js" />
+ <script type="application/javascript" src="docshell_helpers.js" />
+ <script type="application/javascript"><![CDATA[
+ Services.prefs.setBoolPref("browser.navigation.requireUserInteraction", false);
+
+ // Define the generator-iterator for the tests.
+ var tests = testIterator();
+
+ // Maximum number of entries in the bfcache for this session history.
+ // This number is hardcoded in docshell code. In the test, we'll
+ // navigate through enough pages so that we hit one that's been
+ // evicted from the bfcache because it's farther from the current
+ // page than this number.
+ const MAX_BFCACHE_PAGES = 3;
+
+ ////
+ // Execute the next test in the generator function.
+ //
+ function nextTest() {
+ tests.next();
+ }
+
+ ////
+ // Generator function for test steps for bug 396649: Content
+ // viewers should be evicted from bfcache when going back if more
+ // than MAX_BFCACHE_PAGES from the current index.
+ //
+ function* testIterator()
+ {
+ // Make sure bfcache is on.
+ enableBFCache(true);
+
+ // Load enough pages so that the first loaded is eviced from
+ // the bfcache, since it is greater the MAX_BFCACHE_PAGES from
+ // the current position in the session history. Verify all
+ // of the pages are initially stored in the bfcache when
+ // they're unloaded.
+ for (var i = 0; i <= MAX_BFCACHE_PAGES + 1; i++) {
+ let eventsToListenFor = ["pageshow"];
+ let expectedEvents = [ { type: "pageshow",
+ title: "bug396649 page" + i } ];
+ if (i > 0) {
+ eventsToListenFor.push("pagehide");
+ expectedEvents.unshift({ type: "pagehide",
+ title: "bug396649 page" + (i-1) });
+ }
+ doPageNavigation( {
+ uri: "data:text/html,<!DOCTYPE html><html>" +
+ "<head><title>bug396649 page" + i +
+ "</title></head>" +
+ "<body>" +
+ "test page " + i +
+ "</body></html>",
+ eventsToListenFor,
+ expectedEvents,
+ onNavComplete: nextTest
+ } );
+ yield undefined;
+ }
+
+ // Go back to the first page, one page at a time. The first
+ // MAX_BFCACHE_PAGES pages loaded via back should come from the bfcache,
+ // the last should not, since it should have been evicted during the
+ // previous navigation sequence. Verify all pages are initially stored
+ // in the bfcache when they're unloaded.
+ for (i = MAX_BFCACHE_PAGES + 1; i > 0; i--) {
+ doPageNavigation( {
+ back: true,
+ eventsToListenFor: ["pageshow", "pagehide"],
+ expectedEvents: [ { type: "pagehide",
+ title: "bug396649 page" + i,
+ persisted: true },
+ { type: "pageshow",
+ title: "bug396649 page" + (i - 1),
+ persisted: i > 1 } ],
+ onNavComplete: nextTest
+ } );
+ yield undefined;
+ }
+
+ // Traverse history forward now. Again, the first MAX_BFCACHE_PAGES
+ // pages should come from the bfcache, the last should not,
+ // since it should have been evicted during the backwards
+ // traversal above. Verify all pages are initially stored
+ // in the bfcache when they're unloaded.
+ for (i = 1; i <= MAX_BFCACHE_PAGES + 1; i++) {
+ doPageNavigation( {
+ forward: true,
+ eventsToListenFor: ["pageshow", "pagehide"],
+ expectedEvents: [ { type: "pagehide",
+ title: "bug396649 page" + (i-1),
+ persisted: true },
+ { type: "pageshow",
+ title: "bug396649 page" + i,
+ persisted: i < MAX_BFCACHE_PAGES + 1 } ],
+ onNavComplete: nextTest
+ } );
+ yield undefined;
+ }
+
+ Services.prefs.clearUserPref("browser.navigation.requireUserInteraction");
+ // Tell the framework the test is finished.
+ finish();
+ }
+
+ ]]></script>
+
+ <browser type="content" primary="true" flex="1" id="content" remote="true" />
+</window>
diff --git a/docshell/test/chrome/bug449778_window.xhtml b/docshell/test/chrome/bug449778_window.xhtml
new file mode 100644
index 0000000000..1bbb8d4b2b
--- /dev/null
+++ b/docshell/test/chrome/bug449778_window.xhtml
@@ -0,0 +1,107 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<window title="Mozilla Bug 449778" onload="doTheTest()"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <hbox id="parent">
+ </hbox>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+ /* globals SimpleTest, is */
+ var imports = [ "SimpleTest", "is" ];
+ for (var name of imports) {
+ window[name] = window.arguments[0][name];
+ }
+
+ function $(id) {
+ return document.getElementById(id);
+ }
+
+ function addBrowser(parent, id, width, height) {
+ var b =
+ document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "browser");
+ b.setAttribute("type", "content");
+ b.setAttribute("id", id);
+ b.setAttribute("width", width);
+ b.setAttribute("height", height);
+ $(parent).appendChild(b);
+ }
+ addBrowser("parent", "f1", 300, 200);
+ addBrowser("parent", "f2", 300, 200);
+
+ /** Test for Bug 449778 **/
+ var doc1 = "data:text/html,<html><body>This is a test</body></html>";
+ var doc2 = "data:text/html,<html><body>This is a second test</body></html>";
+ var doc3 = "data:text/html,<html><body>This is a <script>var evt = document.createEvent('Events'); evt.initEvent('testEvt', true, true); document.dispatchEvent(evt);</script>third test</body></html>";
+
+
+ $("f1").setAttribute("src", doc1);
+ $("f2").setAttribute("src", doc2);
+
+ function doTheTest() {
+ var strs = { "f1": "", "f2" : "" };
+ function attachListener(node, type) {
+ var listener = function(e) {
+ if (strs[node.id]) strs[node.id] += " ";
+ strs[node.id] += node.id + ".page" + type;
+ }
+ node.addEventListener("page" + type, listener);
+
+ listener.detach = function() {
+ node.removeEventListener("page" + type, listener);
+ }
+ return listener;
+ }
+
+ var l1 = attachListener($("f1"), "show");
+ var l2 = attachListener($("f1"), "hide");
+ var l3 = attachListener($("f2"), "show");
+ var l4 = attachListener($("f2"), "hide");
+
+ $("f1").swapDocShells($("f2"));
+
+ is(strs.f1, "f1.pagehide f1.pageshow",
+ "Expected hide then show on first loaded page");
+ is(strs.f2, "f2.pagehide f2.pageshow",
+ "Expected hide then show on second loaded page");
+
+ function listener2() {
+ $("f2").removeEventListener("testEvt", listener2);
+
+ strs = { "f1": "", "f2" : "" };
+
+ $("f1").swapDocShells($("f2"));
+ is(strs.f1, "f1.pagehide",
+ "Expected hide on already-loaded page, then nothing");
+ is(strs.f2, "f2.pageshow f2.pagehide f2.pageshow",
+ "Expected show on still-loading page, then hide on it, then show " +
+ "on already-loaded page");
+
+ strs = { "f1": "", "f2" : "" };
+
+ $("f1").addEventListener("pageshow", listener3);
+ }
+
+ function listener3() {
+ $("f1").removeEventListener("pageshow", listener3);
+
+ is(strs.f1, "f1.pageshow",
+ "Expected show as our page finishes loading");
+ is(strs.f2, "", "Expected no more events here.");
+
+ l1.detach();
+ l2.detach();
+ l3.detach();
+ l4.detach();
+
+ window.close();
+ SimpleTest.finish();
+ }
+
+ $("f2").addEventListener("testEvt", listener2, false, true);
+ $("f2").setAttribute("src", doc3);
+ }
+
+ ]]></script>
+</window>
diff --git a/docshell/test/chrome/bug449780_window.xhtml b/docshell/test/chrome/bug449780_window.xhtml
new file mode 100644
index 0000000000..3b0096d2e9
--- /dev/null
+++ b/docshell/test/chrome/bug449780_window.xhtml
@@ -0,0 +1,80 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<window title="Mozilla Bug 449780" onload="setTimeout(doTheTest, 0);"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <hbox id="parent">
+ </hbox>
+
+ <!-- test code goes here -->
+ <script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js" />
+ <script type="application/javascript" src="docshell_helpers.js" />
+ <script type="application/javascript"><![CDATA[
+ function addBrowser(parent, width, height) {
+ var b =
+ document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "browser");
+ b.setAttribute("type", "content");
+ b.setAttribute("id", "content");
+ b.setAttribute("width", width);
+ b.setAttribute("height", height);
+ b.setAttribute("remote", SpecialPowers.Services.appinfo.sessionHistoryInParent);
+ document.getElementById("parent").appendChild(b);
+ return b;
+ }
+
+ let f1 = addBrowser("parent", 300, 200);
+
+ /** Test for Bug 449780 **/
+ var doc1 = "data:text/html,<html><body>This is a test</body></html>";
+ var doc2 = "data:text/html,<html><body>This is a second test</body></html>";
+
+ async function doTheTest() {
+ await promisePageNavigation({
+ uri: doc1,
+ });
+ let { origDOM, modifiedDOM } = await SpecialPowers.spawn(f1, [], () => {
+ var origDOM = content.document.documentElement.innerHTML;
+ content.document.body.textContent = "Modified";
+ var modifiedDOM = content.document.documentElement.innerHTML;
+ isnot(origDOM, modifiedDOM, "DOM should be different");
+ return { origDOM, modifiedDOM };
+ });
+
+ await promisePageNavigation({
+ uri: doc2,
+ });
+
+ await promisePageNavigation({
+ back: true,
+ });
+
+ await SpecialPowers.spawn(f1, [modifiedDOM], (modifiedDOM) => {
+ is(content.document.documentElement.innerHTML, modifiedDOM, "Should have been bfcached");
+ });
+
+ await promisePageNavigation({
+ forward: true,
+ });
+
+ f1.removeAttribute("id");
+ let f2 = addBrowser("parent", 300, 200);
+
+ // Make sure there's a document or the swap will fail.
+ await promisePageNavigation({
+ uri: "about:blank",
+ });
+
+ f1.swapDocShells(f2);
+
+ await promisePageNavigation({
+ back: true,
+ });
+
+ await SpecialPowers.spawn(f2, [origDOM], (origDOM) => {
+ is(content.document.documentElement.innerHTML, origDOM, "Should not have been bfcached");
+ });
+
+ finish();
+ }
+ ]]></script>
+</window>
diff --git a/docshell/test/chrome/bug454235-subframe.xhtml b/docshell/test/chrome/bug454235-subframe.xhtml
new file mode 100644
index 0000000000..a8b6178e65
--- /dev/null
+++ b/docshell/test/chrome/bug454235-subframe.xhtml
@@ -0,0 +1,7 @@
+<window title="Mozilla Bug 454235 SubFrame"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <deck flex="1">
+ <browser id="topBrowser" src="about:mozilla"/>
+ <browser id="burriedBrowser" src="about:mozilla"/>
+ </deck>
+</window>
diff --git a/docshell/test/chrome/bug582176_window.xhtml b/docshell/test/chrome/bug582176_window.xhtml
new file mode 100644
index 0000000000..f468d17b8b
--- /dev/null
+++ b/docshell/test/chrome/bug582176_window.xhtml
@@ -0,0 +1,74 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window id="303267Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="600"
+ height="600"
+ onload="runTest();"
+ title="bug 582176 test">
+
+ <script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js" />
+ <script type="application/javascript" src="docshell_helpers.js" />
+ <script type="application/javascript"><![CDATA[
+ ////
+ // Bug 582176.
+ //
+ async function runTest()
+ {
+ enableBFCache(true);
+
+ var notificationCount = 0;
+
+ let onGlobalCreation = () => {
+ ++notificationCount;
+ };
+
+ await promisePageNavigation({
+ uri: "http://mochi.test:8888/chrome/docshell/test/chrome/582176_dummy.html",
+ onGlobalCreation,
+ });
+ is(await SpecialPowers.spawn(TestWindow.getBrowser(), [], () => {
+ let testVar = content.testVar;
+ content.testVar = 1;
+ return testVar;
+ }), undefined,
+ "variable unexpectedly there already");
+ is(notificationCount, 1, "Should notify on first navigation");
+
+ await promisePageNavigation({
+ uri: "http://mochi.test:8888/chrome/docshell/test/chrome/582176_dummy.html?2",
+ onGlobalCreation,
+ });
+ is(await SpecialPowers.spawn(TestWindow.getBrowser(), [], () => {
+ return content.testVar;
+ }), undefined,
+ "variable should no longer be there");
+ is(notificationCount, 2, "Should notify on second navigation");
+
+ await promisePageNavigation({
+ back: true,
+ });
+ is(await SpecialPowers.spawn(TestWindow.getBrowser(), [], () => {
+ return content.testVar;
+ }), 1,
+ "variable should still be there");
+ is(notificationCount, 2, "Should not notify on back navigation");
+
+ await promisePageNavigation({
+ uri: "http://mochi.test:8888/chrome/docshell/test/chrome/582176_xml.xml",
+ onGlobalCreation,
+ });
+ is(await SpecialPowers.spawn(TestWindow.getBrowser(), [], () => {
+ return content.document.body.textContent;
+ }), "xslt result",
+ "Transform performed successfully");
+ is(notificationCount, 3, "Should notify only once on XSLT navigation");
+
+ // Tell the framework the test is finished.
+ finish();
+ }
+
+ ]]></script>
+ <browser type="content" primary="true" flex="1" id="content" remote="true" />
+</window>
diff --git a/docshell/test/chrome/bug608669.xhtml b/docshell/test/chrome/bug608669.xhtml
new file mode 100644
index 0000000000..993f24051c
--- /dev/null
+++ b/docshell/test/chrome/bug608669.xhtml
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<window title="Mozilla Bug 608669 - Blank page"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="notifyOpener();">
+ <description flex="1" value="This window is intentionally left blank"/>
+ <script type="application/javascript">
+ function notifyOpener() {
+ if (opener) {
+ opener.postMessage("load", "*");
+ }
+ }
+ </script>
+</window>
diff --git a/docshell/test/chrome/bug662200_window.xhtml b/docshell/test/chrome/bug662200_window.xhtml
new file mode 100644
index 0000000000..7c6f656f26
--- /dev/null
+++ b/docshell/test/chrome/bug662200_window.xhtml
@@ -0,0 +1,119 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window id="303267Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="600"
+ height="600"
+ onload="setTimeout(runTest, 0);"
+ title="bug 662200 test">
+
+ <script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js" />
+ <script type="application/javascript" src="docshell_helpers.js" />
+ <script type="application/javascript"><![CDATA[
+ Services.prefs.setBoolPref("browser.navigation.requireUserInteraction", false);
+
+ ////
+ // Bug 662200
+ //
+ async function runTest()
+ {
+ // Load the first test page
+ var navData = {
+ uri: getHttpUrl("662200a.html"),
+ eventsToListenFor: ["pageshow"],
+ expectedEvents: [ {type: "pageshow", title: "A"} ],
+ };
+ await promisePageNavigation(navData);
+
+ // Load the second test page.
+ navData = {
+ eventsToListenFor: ["pageshow", "pagehide"],
+ expectedEvents: [ {type: "pagehide",
+ title: "A"},
+ {type: "pageshow",
+ title: "B"} ],
+ }
+ let clicked = promisePageEvents(navData);
+ SpecialPowers.spawn(TestWindow.getBrowser(), [], () => {
+ var link = content.document.getElementById("link");
+ var event = content.document.createEvent("MouseEvents");
+ event.initMouseEvent("click", true, true, content,
+ 0, 0, 0, 0, 0, false, false, false, false, 0, null);
+ link.dispatchEvent(event);
+ });
+ await clicked;
+
+ // Load the third test page.
+ navData = {
+ eventsToListenFor: ["pageshow", "pagehide"],
+ expectedEvents: [ {type: "pagehide",
+ title: "B"},
+ {type: "pageshow",
+ title: "C"} ],
+ };
+ clicked = promisePageEvents(navData);
+ SpecialPowers.spawn(TestWindow.getBrowser(), [], () => {
+ var link = content.document.getElementById("link");
+ var event = content.document.createEvent("MouseEvents");
+ event.initMouseEvent("click", true, true, content,
+ 0, 0, 0, 0, 0, false, false, false, false, 0, null);
+ link.dispatchEvent(event);
+ });
+ await clicked;
+
+ // Go back.
+ navData = {
+ back: true,
+ eventsToListenFor: ["pageshow", "pagehide"],
+ expectedEvents: [ {type: "pagehide",
+ title: "C"},
+ {type: "pageshow",
+ title: "B"} ],
+ };
+ await promisePageNavigation(navData);
+
+ // Reload.
+ navData = {
+ eventsToListenFor: ["pageshow", "pagehide"],
+ expectedEvents: [ {type: "pagehide",
+ title: "B"},
+ {type: "pageshow",
+ title: "B"} ],
+ };
+ // Asking the docshell harness to reload for us will call reload on
+ // nsDocShell which has different behavior than the reload on nsSHistory
+ // so we call reloadCurrentEntry() (which is equivalent to reload(0) and
+ // visible from JS) explicitly here.
+ let reloaded = promisePageEvents(navData);
+ if (SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ TestWindow.getBrowser().browsingContext.sessionHistory.reloadCurrentEntry();
+ } else {
+ SpecialPowers.spawn(TestWindow.getBrowser(), [], () => {
+ docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory.legacySHistory.reloadCurrentEntry();
+ });
+ }
+ await reloaded;
+
+ // After this sequence of events, we should be able to go back and forward
+ is(TestWindow.getBrowser().canGoBack, true, "Should be able to go back!");
+ is(TestWindow.getBrowser().canGoForward, true, "Should be able to go forward!");
+ let requestedIndex;
+ if (SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ requestedIndex = TestWindow.getBrowser().browsingContext.sessionHistory.requestedIndex;
+ } else {
+ requestedIndex = await SpecialPowers.spawn(TestWindow.getBrowser(), [], () => {
+ return docShell.sessionHistory.legacySHistory.requestedIndex;
+ })
+ }
+ is(requestedIndex, -1, "Requested index should be cleared!");
+
+ Services.prefs.clearUserPref("browser.navigation.requireUserInteraction");
+ // Tell the framework the test is finished.
+ finish();
+ }
+
+ ]]></script>
+
+ <browser type="content" primary="true" flex="1" id="content" remote="true" />
+</window>
diff --git a/docshell/test/chrome/bug690056_window.xhtml b/docshell/test/chrome/bug690056_window.xhtml
new file mode 100644
index 0000000000..a48e14ce8f
--- /dev/null
+++ b/docshell/test/chrome/bug690056_window.xhtml
@@ -0,0 +1,171 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window id="690056Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="600"
+ height="600"
+ onload="setTimeout(nextTest, 0);"
+ title="bug 6500056 test">
+
+ <script type="application/javascript" src= "chrome://mochikit/content/chrome-harness.js" />
+ <script type="application/javascript" src="docshell_helpers.js" />
+ <script type="application/javascript"><![CDATA[
+ var tests = testIterator();
+
+ function nextTest() {
+ tests.next();
+ }
+
+ // Makes sure that we fire the visibilitychange events
+ function* testIterator() {
+ // Enable bfcache
+ enableBFCache(8);
+
+ // Load something for a start
+ doPageNavigation({
+ uri: 'data:text/html,<title>initial load</title>',
+ onNavComplete: nextTest
+ });
+ yield undefined;
+
+ // Now load a new page
+ doPageNavigation({
+ uri: 'data:text/html,<title>new load</title>',
+ eventsToListenFor: [ "pageshow", "pagehide", "visibilitychange" ],
+ expectedEvents: [ { type: "pagehide",
+ title: "initial load",
+ persisted: true },
+ { type: "visibilitychange",
+ title: "initial load",
+ visibilityState: "hidden",
+ hidden: true },
+ // No visibilitychange events fired for initial pageload
+ { type: "pageshow",
+ title: "new load",
+ persisted: false }, // false on initial load
+ ],
+ onNavComplete: nextTest
+ });
+ yield undefined;
+
+ // Now go back
+ doPageNavigation({
+ back: true,
+ eventsToListenFor: [ "pageshow", "pagehide", "visibilitychange" ],
+ expectedEvents: [ { type: "pagehide",
+ title: "new load",
+ persisted: true },
+ { type: "visibilitychange",
+ title: "new load",
+ visibilityState: "hidden",
+ hidden: true },
+ { type: "visibilitychange",
+ title: "initial load",
+ visibilityState: "visible",
+ hidden: false },
+ { type: "pageshow",
+ title: "initial load",
+ persisted: true },
+ ],
+ onNavComplete: nextTest
+ });
+ yield undefined;
+
+ // And forward
+ doPageNavigation({
+ forward: true,
+ eventsToListenFor: [ "pageshow", "pagehide", "visibilitychange" ],
+ expectedEvents: [ { type: "pagehide",
+ title: "initial load",
+ persisted: true },
+ { type: "visibilitychange",
+ title: "initial load",
+ visibilityState: "hidden",
+ hidden: true },
+ { type: "visibilitychange",
+ title: "new load",
+ visibilityState: "visible",
+ hidden: false },
+ { type: "pageshow",
+ title: "new load",
+ persisted: true },
+ ],
+ onNavComplete: nextTest
+ });
+ yield undefined;
+
+ waitForPageEvents({
+ eventsToListenFor: [ "visibilitychange" ],
+ expectedEvents: [ { type: "visibilitychange",
+ title: "new load",
+ visibilityState: "hidden",
+ hidden: true },
+ ],
+ onNavComplete: nextTest
+ });
+
+ // Now flip our docshell to not active
+ TestWindow.getBrowser().docShellIsActive = false;
+ yield undefined;
+
+ // And navigate back; there should be no visibility state transitions
+ doPageNavigation({
+ back: true,
+ eventsToListenFor: [ "pageshow", "pagehide", "visibilitychange" ],
+ expectedEvents: [ { type: "pagehide",
+ title: "new load",
+ persisted: true },
+ { type: "pageshow",
+ title: "initial load",
+ persisted: true },
+ ],
+ unexpectedEvents: [ "visibilitychange" ],
+ onNavComplete: nextTest
+ });
+ yield undefined;
+
+ waitForPageEvents({
+ eventsToListenFor: [ "visibilitychange" ],
+ expectedEvents: [ { type: "visibilitychange",
+ title: "initial load",
+ visibilityState: "visible",
+ hidden: false },
+ ],
+ onNavComplete: nextTest
+ });
+
+ // Now set the docshell active again
+ TestWindow.getBrowser().docShellIsActive = true;
+ yield undefined;
+
+ // And forward
+ doPageNavigation({
+ forward: true,
+ eventsToListenFor: [ "pageshow", "pagehide", "visibilitychange" ],
+ expectedEvents: [ { type: "pagehide",
+ title: "initial load",
+ persisted: true },
+ { type: "visibilitychange",
+ title: "initial load",
+ visibilityState: "hidden",
+ hidden: true },
+ { type: "visibilitychange",
+ title: "new load",
+ visibilityState: "visible",
+ hidden: false },
+ { type: "pageshow",
+ title: "new load",
+ persisted: true },
+ ],
+ onNavComplete: nextTest
+ });
+ yield undefined;
+
+ // Tell the framework the test is finished.
+ finish();
+ }
+ ]]></script>
+
+ <browser type="content" primary="true" flex="1" id="content" remote="true" />
+</window>
diff --git a/docshell/test/chrome/bug846906.html b/docshell/test/chrome/bug846906.html
new file mode 100644
index 0000000000..a289417ea8
--- /dev/null
+++ b/docshell/test/chrome/bug846906.html
@@ -0,0 +1,10 @@
+<html>
+ <head>
+ <title>
+ </title>
+ </head>
+ <body>
+ <div id="div1" style="width:1024px; height:768px; border:none;">
+ </div>
+ </body>
+</html>
diff --git a/docshell/test/chrome/bug89419.sjs b/docshell/test/chrome/bug89419.sjs
new file mode 100644
index 0000000000..7172690a9a
--- /dev/null
+++ b/docshell/test/chrome/bug89419.sjs
@@ -0,0 +1,12 @@
+function handleRequest(request, response) {
+ var redirectstate = "/docshell/test/chrome/bug89419.sjs";
+ response.setStatusLine("1.1", 302, "Found");
+ if (getState(redirectstate) == "") {
+ response.setHeader("Location", "red.png", false);
+ setState(redirectstate, "red");
+ } else {
+ response.setHeader("Location", "blue.png", false);
+ setState(redirectstate, "");
+ }
+ response.setHeader("Cache-Control", "no-cache", false);
+}
diff --git a/docshell/test/chrome/bug89419_window.xhtml b/docshell/test/chrome/bug89419_window.xhtml
new file mode 100644
index 0000000000..12b9dec650
--- /dev/null
+++ b/docshell/test/chrome/bug89419_window.xhtml
@@ -0,0 +1,69 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window id="89419Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="600"
+ height="600"
+ onload="setTimeout(runTests, 0);"
+ title="bug 89419 test">
+
+ <script type="application/javascript" src= "chrome://mochikit/content/chrome-harness.js" />
+ <script type="application/javascript" src="docshell_helpers.js" />
+ <script src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
+
+ <script type="application/javascript"><![CDATA[
+ ////
+ // A visited link should have the :visited style applied
+ // to it when displayed on a page which was fetched from
+ // the bfcache.
+ //
+ async function runTests() {
+ // Disable rcwn to make cache behavior deterministic.
+ var {SpecialPowers} = window.arguments[0];
+ await SpecialPowers.pushPrefEnv({"set":[["network.http.rcwn.enabled", false]]});
+
+ // Load a test page containing an image referring to the sjs that returns
+ // a different redirect every time it's loaded.
+ await new Promise(resolve => {
+ doPageNavigation({
+ uri: getHttpUrl("89419.html"),
+ onNavComplete: resolve,
+ preventBFCache: true,
+ });
+ })
+
+ var first = await snapshotWindow(TestWindow.getWindow());
+
+ await new Promise(resolve => {
+ doPageNavigation({
+ uri: "about:blank",
+ onNavComplete: resolve,
+ });
+ });
+
+ var second = await snapshotWindow(TestWindow.getWindow());
+ function snapshotsEqual(snap1, snap2) {
+ return compareSnapshots(snap1, snap2, true)[0];
+ }
+ ok(!snapshotsEqual(first, second), "about:blank should not be the same as the image web page");
+
+ await new Promise(resolve => {
+ doPageNavigation({
+ back: true,
+ onNavComplete: resolve,
+ });
+ });
+
+ var third = await snapshotWindow(TestWindow.getWindow());
+ ok(!snapshotsEqual(third, second), "going back should not be the same as about:blank");
+ ok(snapshotsEqual(first, third), "going back should be the same as the initial load");
+
+ // Tell the framework the test is finished.
+ finish();
+ }
+
+ ]]></script>
+
+ <browser type="content" primary="true" flex="1" id="content" src="about:blank"/>
+</window>
diff --git a/docshell/test/chrome/bug909218.html b/docshell/test/chrome/bug909218.html
new file mode 100644
index 0000000000..a11fa6000d
--- /dev/null
+++ b/docshell/test/chrome/bug909218.html
@@ -0,0 +1,11 @@
+<html>
+<head>
+ <link rel="stylesheet" type="text/css" href="http://mochi.test:8888/tests/SimpleTest/test.css">
+ <script src="bug909218.js"></script>
+</head>
+<body>
+ <img src="http://mochi.test:8888/tests/docshell/test/chrome/red.png">
+ <!-- an iframe so we can check these too get the correct flags -->
+ <iframe src="generic.html"/>
+</body>
+</html>
diff --git a/docshell/test/chrome/bug909218.js b/docshell/test/chrome/bug909218.js
new file mode 100644
index 0000000000..2222480cd3
--- /dev/null
+++ b/docshell/test/chrome/bug909218.js
@@ -0,0 +1,2 @@
+// This file exists just to ensure that we load it with the correct flags.
+dump("bug909218.js loaded\n");
diff --git a/docshell/test/chrome/bug92598_window.xhtml b/docshell/test/chrome/bug92598_window.xhtml
new file mode 100644
index 0000000000..5a10ff250a
--- /dev/null
+++ b/docshell/test/chrome/bug92598_window.xhtml
@@ -0,0 +1,89 @@
+<?xml version="1.0"?>
+
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window id="92598Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="600"
+ height="600"
+ onload="onLoad();"
+ title="92598 test">
+
+ <script src="chrome://mochikit/content/chrome-harness.js" />
+ <script type="application/javascript" src="docshell_helpers.js" />
+ <script type="application/javascript"><![CDATA[
+ var gBrowser;
+ var gTestsIterator;
+
+ function onLoad() {
+ gBrowser = document.getElementById("content");
+ gTestsIterator = testsIterator();
+ nextTest();
+ }
+
+ function nextTest() {
+ gTestsIterator.next();
+ }
+
+ function* testsIterator() {
+ // Load a page with a no-cache header, followed by a simple page
+ // On pagehide, first page should report it is not being persisted
+ var test1DocURI = "http://mochi.test:8888/chrome/docshell/test/chrome/92598_nostore.html";
+
+ doPageNavigation({
+ uri: test1DocURI,
+ eventsToListenFor: ["load", "pageshow"],
+ expectedEvents: [ { type: "load",
+ title: "test1" },
+ { type: "pageshow",
+ title: "test1",
+ persisted: false } ],
+ onNavComplete: nextTest
+ });
+ yield undefined;
+
+ var test2Doc = "data:text/html,<html><head><title>test2</title></head>" +
+ "<body>test2</body></html>";
+
+ doPageNavigation({
+ uri: test2Doc,
+ eventsToListenFor: ["load", "pageshow", "pagehide"],
+ expectedEvents: [ { type: "pagehide",
+ title: "test1",
+ persisted: false },
+ { type: "load",
+ title: "test2" },
+ { type: "pageshow",
+ title: "test2",
+ persisted: false } ],
+ onNavComplete: nextTest
+ });
+ yield undefined;
+
+ // Now go back in history. First page should not have been cached.
+ // Check persisted property to confirm
+ doPageNavigation({
+ back: true,
+ eventsToListenFor: ["load", "pageshow", "pagehide"],
+ expectedEvents: [ { type: "pagehide",
+ title: "test2",
+ persisted: true },
+ { type: "load",
+ title: "test1" },
+ { type: "pageshow",
+ title: "test1",
+ persisted: false } ],
+ onNavComplete: nextTest
+ });
+ yield undefined;
+
+ finish();
+ }
+ ]]></script>
+
+ <browser type="content" primary="true" flex="1" id="content" remote="true" />
+</window>
diff --git a/docshell/test/chrome/chrome.ini b/docshell/test/chrome/chrome.ini
new file mode 100644
index 0000000000..2d126b84b7
--- /dev/null
+++ b/docshell/test/chrome/chrome.ini
@@ -0,0 +1,105 @@
+[DEFAULT]
+skip-if = os == 'android'
+support-files =
+ 662200a.html
+ 662200b.html
+ 662200c.html
+ 89419.html
+ bug113934_window.xhtml
+ bug215405_window.xhtml
+ bug293235.html
+ bug293235_p2.html
+ bug293235_window.xhtml
+ bug294258_testcase.html
+ bug294258_window.xhtml
+ bug298622_window.xhtml
+ bug301397_1.html
+ bug301397_2.html
+ bug301397_3.html
+ bug301397_4.html
+ bug301397_window.xhtml
+ bug303267.html
+ bug303267_window.xhtml
+ bug311007_window.xhtml
+ bug321671_window.xhtml
+ bug360511_case1.html
+ bug360511_case2.html
+ bug360511_window.xhtml
+ bug364461_window.xhtml
+ bug396519_window.xhtml
+ bug396649_window.xhtml
+ bug449778_window.xhtml
+ bug449780_window.xhtml
+ bug454235-subframe.xhtml
+ bug608669.xhtml
+ bug662200_window.xhtml
+ bug690056_window.xhtml
+ bug846906.html
+ bug89419_window.xhtml
+ bug909218.html
+ bug909218.js
+ docshell_helpers.js
+ DocShellHelpers.sys.mjs
+ file_viewsource_forbidden_in_iframe.html
+ generic.html
+ mozFrameType_window.xhtml
+ test_docRedirect.sjs
+
+[test_allowContentRetargeting.html]
+[test_bug112564.xhtml]
+support-files =
+ bug112564_window.xhtml
+ 112564_nocache.html
+ 112564_nocache.html^headers^
+[test_bug113934.xhtml]
+[test_bug215405.xhtml]
+[test_bug293235.xhtml]
+skip-if = true # bug 1393441
+[test_bug294258.xhtml]
+[test_bug298622.xhtml]
+[test_bug301397.xhtml]
+skip-if = (os == 'win' && processor == 'aarch64') # bug 1533819
+[test_bug303267.xhtml]
+[test_bug311007.xhtml]
+[test_bug321671.xhtml]
+[test_bug360511.xhtml]
+skip-if = (os == 'win' && processor == 'x86')
+[test_bug364461.xhtml]
+skip-if = (os == 'win' && processor == 'aarch64') # bug 1533814
+[test_bug396519.xhtml]
+[test_bug396649.xhtml]
+[test_bug428288.html]
+[test_bug449778.xhtml]
+[test_bug449780.xhtml]
+[test_bug453650.xhtml]
+[test_bug454235.xhtml]
+[test_bug456980.xhtml]
+[test_bug565388.xhtml]
+skip-if = true # Bug 1026815,Bug 1546159
+[test_bug582176.xhtml]
+support-files =
+ 582176_dummy.html
+ 582176_xml.xml
+ 582176_xslt.xsl
+ bug582176_window.xhtml
+[test_bug608669.xhtml]
+[test_bug662200.xhtml]
+[test_bug690056.xhtml]
+[test_bug789773.xhtml]
+[test_bug846906.xhtml]
+[test_bug89419.xhtml]
+[test_bug909218.html]
+[test_bug92598.xhtml]
+support-files =
+ 92598_nostore.html
+ 92598_nostore.html^headers^
+ bug92598_window.xhtml
+[test_open_and_immediately_close_opener.html]
+# This bug only manifests in the non-e10s window open codepath. The test
+# should be updated to make sure it still opens a new window in the parent
+# process if and when we start running chrome mochitests with e10s enabled.
+skip-if = e10s
+[test_mozFrameType.xhtml]
+[test_viewsource_forbidden_in_iframe.xhtml]
+skip-if = true # bug 1019315
+[test_docRedirect.xhtml]
diff --git a/docshell/test/chrome/docshell_helpers.js b/docshell/test/chrome/docshell_helpers.js
new file mode 100644
index 0000000000..facd627b45
--- /dev/null
+++ b/docshell/test/chrome/docshell_helpers.js
@@ -0,0 +1,758 @@
+if (!window.opener && window.arguments) {
+ window.opener = window.arguments[0];
+}
+/**
+ * Import common SimpleTest methods so that they're usable in this window.
+ */
+/* globals SimpleTest, is, isnot, ok, onerror, todo, todo_is, todo_isnot */
+var imports = [
+ "SimpleTest",
+ "is",
+ "isnot",
+ "ok",
+ "onerror",
+ "todo",
+ "todo_is",
+ "todo_isnot",
+];
+for (var name of imports) {
+ window[name] = window.opener.wrappedJSObject[name];
+}
+const { BrowserTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/BrowserTestUtils.sys.mjs"
+);
+
+const ACTOR_MODULE_URI =
+ "chrome://mochitests/content/chrome/docshell/test/chrome/DocShellHelpers.sys.mjs";
+const { DocShellHelpersParent } = ChromeUtils.importESModule(ACTOR_MODULE_URI);
+// Some functions assume chrome-harness.js has been loaded.
+/* import-globals-from ../../../testing/mochitest/chrome-harness.js */
+
+/**
+ * Define global constants and variables.
+ */
+const NAV_NONE = 0;
+const NAV_BACK = 1;
+const NAV_FORWARD = 2;
+const NAV_GOTOINDEX = 3;
+const NAV_URI = 4;
+const NAV_RELOAD = 5;
+
+var gExpectedEvents; // an array of events which are expected to
+// be triggered by this navigation
+var gUnexpectedEvents; // an array of event names which are NOT expected
+// to be triggered by this navigation
+var gFinalEvent; // true if the last expected event has fired
+var gUrisNotInBFCache = []; // an array of uri's which shouldn't be stored
+// in the bfcache
+var gNavType = NAV_NONE; // defines the most recent navigation type
+// executed by doPageNavigation
+var gOrigMaxTotalViewers = undefined; // original value of max_total_viewers, // to be restored at end of test
+
+var gExtractedPath = null; // used to cache file path for extracting files from a .jar file
+
+/**
+ * The doPageNavigation() function performs page navigations asynchronously,
+ * listens for specified events, and compares actual events with a list of
+ * expected events. When all expected events have occurred, an optional
+ * callback can be notified. The parameter passed to this function is an
+ * object with the following properties:
+ *
+ * uri: if !undefined, the browser will navigate to this uri
+ *
+ * back: if true, the browser will execute goBack()
+ *
+ * forward: if true, the browser will execute goForward()
+ *
+ * gotoIndex: if a number, the browser will execute gotoIndex() with
+ * the number as index
+ *
+ * reload: if true, the browser will execute reload()
+ *
+ * eventsToListenFor: an array containing one or more of the following event
+ * types to listen for: "pageshow", "pagehide", "onload",
+ * "onunload". If this property is undefined, only a
+ * single "pageshow" events will be listened for. If this
+ * property is explicitly empty, [], then no events will
+ * be listened for.
+ *
+ * expectedEvents: an array of one or more expectedEvent objects,
+ * corresponding to the events which are expected to be
+ * fired for this navigation. Each object has the
+ * following properties:
+ *
+ * type: one of the event type strings
+ * title (optional): the title of the window the
+ * event belongs to
+ * persisted (optional): the event's expected
+ * .persisted attribute
+ *
+ * This function will verify that events with the
+ * specified properties are fired in the same order as
+ * specified in the array. If .title or .persisted
+ * properties for an expectedEvent are undefined, those
+ * properties will not be verified for that particular
+ * event.
+ *
+ * This property is ignored if eventsToListenFor is
+ * undefined or [].
+ *
+ * preventBFCache: if true, an RTCPeerConnection will be added to the loaded
+ * page to prevent it from being bfcached. This property
+ * has no effect when eventsToListenFor is [].
+ *
+ * onNavComplete: a callback which is notified after all expected events
+ * have occurred, or after a timeout has elapsed. This
+ * callback is not notified if eventsToListenFor is [].
+ * onGlobalCreation: a callback which is notified when a DOMWindow is created
+ * (implemented by observing
+ * "content-document-global-created")
+ *
+ * There must be an expectedEvent object for each event of the types in
+ * eventsToListenFor which is triggered by this navigation. For example, if
+ * eventsToListenFor = [ "pagehide", "pageshow" ], then expectedEvents
+ * must contain an object for each pagehide and pageshow event which occurs as
+ * a result of this navigation.
+ */
+// eslint-disable-next-line complexity
+function doPageNavigation(params) {
+ // Parse the parameters.
+ let back = params.back ? params.back : false;
+ let forward = params.forward ? params.forward : false;
+ let gotoIndex = params.gotoIndex ? params.gotoIndex : false;
+ let reload = params.reload ? params.reload : false;
+ let uri = params.uri ? params.uri : false;
+ let eventsToListenFor =
+ typeof params.eventsToListenFor != "undefined"
+ ? params.eventsToListenFor
+ : ["pageshow"];
+ gExpectedEvents =
+ typeof params.eventsToListenFor == "undefined" || !eventsToListenFor.length
+ ? undefined
+ : params.expectedEvents;
+ gUnexpectedEvents =
+ typeof params.eventsToListenFor == "undefined" || !eventsToListenFor.length
+ ? undefined
+ : params.unexpectedEvents;
+ let preventBFCache =
+ typeof [params.preventBFCache] == "undefined"
+ ? false
+ : params.preventBFCache;
+ let waitOnly =
+ typeof params.waitForEventsOnly == "boolean" && params.waitForEventsOnly;
+
+ // Do some sanity checking on arguments.
+ let navigation = ["back", "forward", "gotoIndex", "reload", "uri"].filter(k =>
+ params.hasOwnProperty(k)
+ );
+ if (navigation.length > 1) {
+ throw new Error(`Can't specify both ${navigation[0]} and ${navigation[1]}`);
+ } else if (!navigation.length && !waitOnly) {
+ throw new Error(
+ "Must specify back or forward or gotoIndex or reload or uri"
+ );
+ }
+ if (params.onNavComplete && !eventsToListenFor.length) {
+ throw new Error("Can't use onNavComplete when eventsToListenFor == []");
+ }
+ if (params.preventBFCache && !eventsToListenFor.length) {
+ throw new Error("Can't use preventBFCache when eventsToListenFor == []");
+ }
+ if (params.preventBFCache && waitOnly) {
+ throw new Error("Can't prevent bfcaching when only waiting for events");
+ }
+ if (waitOnly && typeof params.onNavComplete == "undefined") {
+ throw new Error(
+ "Must specify onNavComplete when specifying waitForEventsOnly"
+ );
+ }
+ if (waitOnly && navigation.length) {
+ throw new Error(
+ "Can't specify a navigation type when using waitForEventsOnly"
+ );
+ }
+ for (let anEventType of eventsToListenFor) {
+ let eventFound = false;
+ if (anEventType == "pageshow" && !gExpectedEvents) {
+ eventFound = true;
+ }
+ if (gExpectedEvents) {
+ for (let anExpectedEvent of gExpectedEvents) {
+ if (anExpectedEvent.type == anEventType) {
+ eventFound = true;
+ }
+ }
+ }
+ if (gUnexpectedEvents) {
+ for (let anExpectedEventType of gUnexpectedEvents) {
+ if (anExpectedEventType == anEventType) {
+ eventFound = true;
+ }
+ }
+ }
+ if (!eventFound) {
+ throw new Error(
+ `Event type ${anEventType} is specified in ` +
+ "eventsToListenFor, but not in expectedEvents"
+ );
+ }
+ }
+
+ // If the test explicitly sets .eventsToListenFor to [], don't wait for any
+ // events.
+ gFinalEvent = !eventsToListenFor.length;
+
+ // Add observers as needed.
+ let observers = new Map();
+ if (params.hasOwnProperty("onGlobalCreation")) {
+ observers.set("content-document-global-created", params.onGlobalCreation);
+ }
+
+ // Add an event listener for each type of event in the .eventsToListenFor
+ // property of the input parameters, and add an observer for all the topics
+ // in the observers map.
+ let cleanup;
+ let useActor = TestWindow.getBrowser().isRemoteBrowser;
+ if (useActor) {
+ ChromeUtils.registerWindowActor("DocShellHelpers", {
+ parent: {
+ esModuleURI: ACTOR_MODULE_URI,
+ },
+ child: {
+ esModuleURI: ACTOR_MODULE_URI,
+ events: {
+ pageshow: { createActor: true, capture: true },
+ pagehide: { createActor: true, capture: true },
+ load: { createActor: true, capture: true },
+ unload: { createActor: true, capture: true },
+ visibilitychange: { createActor: true, capture: true },
+ },
+ observers: observers.keys(),
+ },
+ allFrames: true,
+ });
+ DocShellHelpersParent.eventsToListenFor = eventsToListenFor;
+ DocShellHelpersParent.observers = observers;
+
+ cleanup = () => {
+ DocShellHelpersParent.eventsToListenFor = null;
+ DocShellHelpersParent.observers = null;
+ ChromeUtils.unregisterWindowActor("DocShellHelpers");
+ };
+ } else {
+ for (let eventType of eventsToListenFor) {
+ dump("TEST: registering a listener for " + eventType + " events\n");
+ TestWindow.getBrowser().addEventListener(
+ eventType,
+ pageEventListener,
+ true
+ );
+ }
+ if (observers.size > 0) {
+ let observer = (_, topic) => {
+ observers.get(topic).call();
+ };
+ for (let topic of observers.keys()) {
+ Services.obs.addObserver(observer, topic);
+ }
+
+ // We only need to do cleanup for the observer, the event listeners will
+ // go away with the window.
+ cleanup = () => {
+ for (let topic of observers.keys()) {
+ Services.obs.removeObserver(observer, topic);
+ }
+ };
+ }
+ }
+
+ if (cleanup) {
+ // Register a cleanup function on domwindowclosed, to avoid contaminating
+ // other tests if we bail out early because of an error.
+ Services.ww.registerNotification(function windowClosed(
+ subject,
+ topic,
+ data
+ ) {
+ if (topic == "domwindowclosed" && subject == window) {
+ Services.ww.unregisterNotification(windowClosed);
+ cleanup();
+ }
+ });
+ }
+
+ // Perform the specified navigation.
+ if (back) {
+ gNavType = NAV_BACK;
+ TestWindow.getBrowser().goBack();
+ } else if (forward) {
+ gNavType = NAV_FORWARD;
+ TestWindow.getBrowser().goForward();
+ } else if (typeof gotoIndex == "number") {
+ gNavType = NAV_GOTOINDEX;
+ TestWindow.getBrowser().gotoIndex(gotoIndex);
+ } else if (uri) {
+ gNavType = NAV_URI;
+ BrowserTestUtils.loadURI(TestWindow.getBrowser(), uri);
+ } else if (reload) {
+ gNavType = NAV_RELOAD;
+ TestWindow.getBrowser().reload();
+ } else if (waitOnly) {
+ gNavType = NAV_NONE;
+ } else {
+ throw new Error("No valid navigation type passed to doPageNavigation!");
+ }
+
+ // If we're listening for events and there is an .onNavComplete callback,
+ // wait for all events to occur, and then call doPageNavigation_complete().
+ if (eventsToListenFor.length && params.onNavComplete) {
+ waitForTrue(
+ function() {
+ return gFinalEvent;
+ },
+ function() {
+ doPageNavigation_complete(
+ eventsToListenFor,
+ params.onNavComplete,
+ preventBFCache,
+ useActor,
+ cleanup
+ );
+ }
+ );
+ } else if (cleanup) {
+ cleanup();
+ }
+}
+
+/**
+ * Finish doPageNavigation(), by removing event listeners, adding an unload
+ * handler if appropriate, and calling the onNavComplete callback. This
+ * function is called after all the expected events for this navigation have
+ * occurred.
+ */
+function doPageNavigation_complete(
+ eventsToListenFor,
+ onNavComplete,
+ preventBFCache,
+ useActor,
+ cleanup
+) {
+ if (useActor) {
+ if (preventBFCache) {
+ let actor = TestWindow.getBrowser().browsingContext.currentWindowGlobal.getActor(
+ "DocShellHelpers"
+ );
+ actor.sendAsyncMessage("docshell_helpers:preventBFCache");
+ }
+ } else {
+ // Unregister our event listeners.
+ dump("TEST: removing event listeners\n");
+ for (let eventType of eventsToListenFor) {
+ TestWindow.getBrowser().removeEventListener(
+ eventType,
+ pageEventListener,
+ true
+ );
+ }
+
+ // If the .preventBFCache property was set, add an RTCPeerConnection to
+ // prevent the page from being bfcached.
+ if (preventBFCache) {
+ let win = TestWindow.getWindow();
+ win.blockBFCache = new win.RTCPeerConnection();
+ }
+ }
+
+ if (cleanup) {
+ cleanup();
+ }
+
+ let uri = TestWindow.getBrowser().currentURI.spec;
+ if (preventBFCache) {
+ // Save the current uri in an array of uri's which shouldn't be
+ // stored in the bfcache, for later verification.
+ if (!(uri in gUrisNotInBFCache)) {
+ gUrisNotInBFCache.push(uri);
+ }
+ } else if (gNavType == NAV_URI) {
+ // If we're navigating to a uri and .preventBFCache was not
+ // specified, splice it out of gUrisNotInBFCache if it's there.
+ gUrisNotInBFCache.forEach(function(element, index, array) {
+ if (element == uri) {
+ array.splice(index, 1);
+ }
+ }, this);
+ }
+
+ // Notify the callback now that we're done.
+ onNavComplete.call();
+}
+
+function promisePageNavigation(params) {
+ if (params.hasOwnProperty("onNavComplete")) {
+ throw new Error(
+ "Can't use a onNavComplete completion callback with promisePageNavigation."
+ );
+ }
+ return new Promise(resolve => {
+ params.onNavComplete = resolve;
+ doPageNavigation(params);
+ });
+}
+
+/**
+ * Allows a test to wait for page navigation events, and notify a
+ * callback when they've all been received. This works exactly the
+ * same as doPageNavigation(), except that no navigation is initiated.
+ */
+function waitForPageEvents(params) {
+ params.waitForEventsOnly = true;
+ doPageNavigation(params);
+}
+
+function promisePageEvents(params) {
+ if (params.hasOwnProperty("onNavComplete")) {
+ throw new Error(
+ "Can't use a onNavComplete completion callback with promisePageEvents."
+ );
+ }
+ return new Promise(resolve => {
+ params.waitForEventsOnly = true;
+ params.onNavComplete = resolve;
+ doPageNavigation(params);
+ });
+}
+
+/**
+ * The event listener which listens for expectedEvents.
+ */
+function pageEventListener(
+ event,
+ originalTargetIsHTMLDocument = HTMLDocument.isInstance(event.originalTarget)
+) {
+ try {
+ dump(
+ "TEST: eventListener received a " +
+ event.type +
+ " event for page " +
+ event.originalTarget.title +
+ ", persisted=" +
+ event.persisted +
+ "\n"
+ );
+ } catch (e) {
+ // Ignore any exception.
+ }
+
+ // If this page shouldn't be in the bfcache because it was previously
+ // loaded with .preventBFCache, make sure that its pageshow event
+ // has .persisted = false, even if the test doesn't explicitly test
+ // for .persisted.
+ if (
+ event.type == "pageshow" &&
+ (gNavType == NAV_BACK ||
+ gNavType == NAV_FORWARD ||
+ gNavType == NAV_GOTOINDEX)
+ ) {
+ let uri = TestWindow.getBrowser().currentURI.spec;
+ if (uri in gUrisNotInBFCache) {
+ ok(
+ !event.persisted,
+ "pageshow event has .persisted = false, even " +
+ "though it was loaded with .preventBFCache previously\n"
+ );
+ }
+ }
+
+ if (typeof gUnexpectedEvents != "undefined") {
+ is(
+ gUnexpectedEvents.indexOf(event.type),
+ -1,
+ "Should not get unexpected event " + event.type
+ );
+ }
+
+ // If no expected events were specified, mark the final event as having been
+ // triggered when a pageshow event is fired; this will allow
+ // doPageNavigation() to return.
+ if (typeof gExpectedEvents == "undefined" && event.type == "pageshow") {
+ waitForNextPaint(function() {
+ gFinalEvent = true;
+ });
+ return;
+ }
+
+ // If there are explicitly no expected events, but we receive one, it's an
+ // error.
+ if (!gExpectedEvents.length) {
+ ok(false, "Unexpected event (" + event.type + ") occurred");
+ return;
+ }
+
+ // Grab the next expected event, and compare its attributes against the
+ // actual event.
+ let expected = gExpectedEvents.shift();
+
+ is(
+ event.type,
+ expected.type,
+ "A " +
+ expected.type +
+ " event was expected, but a " +
+ event.type +
+ " event occurred"
+ );
+
+ if (typeof expected.title != "undefined") {
+ ok(
+ originalTargetIsHTMLDocument,
+ "originalTarget for last " + event.type + " event not an HTMLDocument"
+ );
+ is(
+ event.originalTarget.title,
+ expected.title,
+ "A " +
+ event.type +
+ " event was expected for page " +
+ expected.title +
+ ", but was fired for page " +
+ event.originalTarget.title
+ );
+ }
+
+ if (typeof expected.persisted != "undefined") {
+ is(
+ event.persisted,
+ expected.persisted,
+ "The persisted property of the " +
+ event.type +
+ " event on page " +
+ event.originalTarget.location +
+ " had an unexpected value"
+ );
+ }
+
+ if ("visibilityState" in expected) {
+ is(
+ event.originalTarget.visibilityState,
+ expected.visibilityState,
+ "The visibilityState property of the document on page " +
+ event.originalTarget.location +
+ " had an unexpected value"
+ );
+ }
+
+ if ("hidden" in expected) {
+ is(
+ event.originalTarget.hidden,
+ expected.hidden,
+ "The hidden property of the document on page " +
+ event.originalTarget.location +
+ " had an unexpected value"
+ );
+ }
+
+ // If we're out of expected events, let doPageNavigation() return.
+ if (!gExpectedEvents.length) {
+ waitForNextPaint(function() {
+ gFinalEvent = true;
+ });
+ }
+}
+
+DocShellHelpersParent.eventListener = pageEventListener;
+
+/**
+ * End a test.
+ */
+function finish() {
+ // Work around bug 467960.
+ let historyPurged;
+ if (SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ let history = TestWindow.getBrowser().browsingContext?.sessionHistory;
+ history.purgeHistory(history.count);
+ historyPurged = Promise.resolve();
+ } else {
+ historyPurged = SpecialPowers.spawn(TestWindow.getBrowser(), [], () => {
+ let history = docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory
+ .legacySHistory;
+ history.purgeHistory(history.count);
+ });
+ }
+
+ // If the test changed the value of max_total_viewers via a call to
+ // enableBFCache(), then restore it now.
+ if (typeof gOrigMaxTotalViewers != "undefined") {
+ Services.prefs.setIntPref(
+ "browser.sessionhistory.max_total_viewers",
+ gOrigMaxTotalViewers
+ );
+ }
+
+ // Close the test window and signal the framework that the test is done.
+ let opener = window.opener;
+ let SimpleTest = opener.wrappedJSObject.SimpleTest;
+
+ // Wait for the window to be closed before finishing the test
+ Services.ww.registerNotification(function observer(subject, topic, data) {
+ if (topic == "domwindowclosed") {
+ Services.ww.unregisterNotification(observer);
+ SimpleTest.waitForFocus(SimpleTest.finish, opener);
+ }
+ });
+
+ historyPurged.then(_ => {
+ window.close();
+ });
+}
+
+/**
+ * Helper function which waits until another function returns true, or until a
+ * timeout occurs, and then notifies a callback.
+ *
+ * Parameters:
+ *
+ * fn: a function which is evaluated repeatedly, and when it turns true,
+ * the onWaitComplete callback is notified.
+ *
+ * onWaitComplete: a callback which will be notified when fn() returns
+ * true, or when a timeout occurs.
+ *
+ * timeout: a timeout, in seconds or ms, after which waitForTrue() will
+ * fail an assertion and then return, even if the fn function never
+ * returns true. If timeout is undefined, waitForTrue() will never
+ * time out.
+ */
+function waitForTrue(fn, onWaitComplete, timeout) {
+ promiseTrue(fn, timeout).then(() => {
+ onWaitComplete.call();
+ });
+}
+
+function promiseTrue(fn, timeout) {
+ if (typeof timeout != "undefined") {
+ // If timeoutWait is less than 500, assume it represents seconds, and
+ // convert to ms.
+ if (timeout < 500) {
+ timeout *= 1000;
+ }
+ }
+
+ // Loop until the test function returns true, or until a timeout occurs,
+ // if a timeout is defined.
+ let intervalid, timeoutid;
+ let condition = new Promise(resolve => {
+ intervalid = setInterval(async () => {
+ if (await fn.call()) {
+ resolve();
+ }
+ }, 20);
+ });
+ if (typeof timeout != "undefined") {
+ condition = Promise.race([
+ condition,
+ new Promise((_, reject) => {
+ timeoutid = setTimeout(() => {
+ reject();
+ }, timeout);
+ }),
+ ]);
+ }
+ return condition
+ .finally(() => {
+ clearInterval(intervalid);
+ })
+ .then(() => {
+ clearTimeout(timeoutid);
+ });
+}
+
+function waitForNextPaint(cb) {
+ requestAnimationFrame(_ => requestAnimationFrame(cb));
+}
+
+function promiseNextPaint() {
+ return new Promise(resolve => {
+ waitForNextPaint(resolve);
+ });
+}
+
+/**
+ * Enable or disable the bfcache.
+ *
+ * Parameters:
+ *
+ * enable: if true, set max_total_viewers to -1 (the default); if false, set
+ * to 0 (disabled), if a number, set it to that specific number
+ */
+function enableBFCache(enable) {
+ // If this is the first time the test called enableBFCache(),
+ // store the original value of max_total_viewers, so it can
+ // be restored at the end of the test.
+ if (typeof gOrigMaxTotalViewers == "undefined") {
+ gOrigMaxTotalViewers = Services.prefs.getIntPref(
+ "browser.sessionhistory.max_total_viewers"
+ );
+ }
+
+ if (typeof enable == "boolean") {
+ if (enable) {
+ Services.prefs.setIntPref("browser.sessionhistory.max_total_viewers", -1);
+ } else {
+ Services.prefs.setIntPref("browser.sessionhistory.max_total_viewers", 0);
+ }
+ } else if (typeof enable == "number") {
+ Services.prefs.setIntPref(
+ "browser.sessionhistory.max_total_viewers",
+ enable
+ );
+ }
+}
+
+/*
+ * get http root for local tests. Use a single extractJarToTmp instead of
+ * extracting for each test.
+ * Returns a file://path if we have a .jar file
+ */
+function getHttpRoot() {
+ var location = window.location.href;
+ location = getRootDirectory(location);
+ var jar = getJar(location);
+ if (jar != null) {
+ if (gExtractedPath == null) {
+ var resolved = extractJarToTmp(jar);
+ gExtractedPath = resolved.path;
+ }
+ } else {
+ return null;
+ }
+ return "file://" + gExtractedPath + "/";
+}
+
+/**
+ * Returns the full HTTP url for a file in the mochitest docshell test
+ * directory.
+ */
+function getHttpUrl(filename) {
+ var root = getHttpRoot();
+ if (root == null) {
+ root = "http://mochi.test:8888/chrome/docshell/test/chrome/";
+ }
+ return root + filename;
+}
+
+/**
+ * A convenience object with methods that return the current test window,
+ * browser, and document.
+ */
+var TestWindow = {};
+TestWindow.getWindow = function() {
+ return document.getElementById("content").contentWindow;
+};
+TestWindow.getBrowser = function() {
+ return document.getElementById("content");
+};
+TestWindow.getDocument = function() {
+ return document.getElementById("content").contentDocument;
+};
diff --git a/docshell/test/chrome/file_viewsource_forbidden_in_iframe.html b/docshell/test/chrome/file_viewsource_forbidden_in_iframe.html
new file mode 100644
index 0000000000..fdecbbdfe1
--- /dev/null
+++ b/docshell/test/chrome/file_viewsource_forbidden_in_iframe.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Test ifranes for view-source forbidden in iframe tests</title>
+</head>
+<body>
+ <iframe id="testIframe"></iframe>
+ <iframe id="refIframe"></iframe>
+</body>
+</html>
diff --git a/docshell/test/chrome/gen_template.pl b/docshell/test/chrome/gen_template.pl
new file mode 100644
index 0000000000..109d6161cd
--- /dev/null
+++ b/docshell/test/chrome/gen_template.pl
@@ -0,0 +1,39 @@
+#!/usr/bin/perl
+
+# This script makes docshell test case templates. It takes one argument:
+#
+# -b: a bugnumber
+#
+# For example, this command:
+#
+# perl gen_template.pl -b 303267
+#
+# Writes test case template files test_bug303267.xhtml and bug303267_window.xhtml
+# to the current directory.
+
+use FindBin;
+use Getopt::Long;
+GetOptions("b=i"=> \$bug_number);
+
+$template = "$FindBin::RealBin/test.template.txt";
+
+open(IN,$template) or die("Failed to open input file for reading.");
+open(OUT, ">>test_bug" . $bug_number . ".xhtml") or die("Failed to open output file for appending.");
+while((defined(IN)) && ($line = <IN>)) {
+ $line =~ s/{BUGNUMBER}/$bug_number/g;
+ print OUT $line;
+}
+close(IN);
+close(OUT);
+
+$template = "$FindBin::RealBin/window.template.txt";
+
+open(IN,$template) or die("Failed to open input file for reading.");
+open(OUT, ">>bug" . $bug_number . "_window.xhtml") or die("Failed to open output file for appending.");
+while((defined(IN)) && ($line = <IN>)) {
+ $line =~ s/{BUGNUMBER}/$bug_number/g;
+ print OUT $line;
+}
+close(IN);
+close(OUT);
+
diff --git a/docshell/test/chrome/generic.html b/docshell/test/chrome/generic.html
new file mode 100644
index 0000000000..569a78c05a
--- /dev/null
+++ b/docshell/test/chrome/generic.html
@@ -0,0 +1,12 @@
+<html>
+<head>
+ <title>
+ generic page
+ </title>
+ </head>
+<body>
+<div id="div1" style="height: 1000px; border: thin solid black;">
+ A generic page which can be used any time a test needs to load an arbitrary page via http.
+ </div>
+</body>
+</html>
diff --git a/docshell/test/chrome/mozFrameType_window.xhtml b/docshell/test/chrome/mozFrameType_window.xhtml
new file mode 100644
index 0000000000..e5d0126d22
--- /dev/null
+++ b/docshell/test/chrome/mozFrameType_window.xhtml
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<window title="Test mozFrameType attribute"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTests();">
+
+ <html:iframe id="normalFrame"/>
+ <html:iframe id="typeContentFrame" mozframetype="content"/>
+
+ <script type="application/javascript"><![CDATA[
+ function runTests() {
+ let opener = window.arguments[0];
+ let SimpleTest = opener.SimpleTest;
+
+ function getDocShellType(frame) {
+ return frame.contentWindow.docShell.itemType;
+ }
+
+ var normalFrame = document.getElementById("normalFrame");
+ var typeContentFrame = document.getElementById("typeContentFrame");
+
+ SimpleTest.is(getDocShellType(normalFrame), Ci.nsIDocShellTreeItem.typeChrome,
+ "normal iframe in chrome document is typeChrome");
+ SimpleTest.is(getDocShellType(typeContentFrame), Ci.nsIDocShellTreeItem.typeContent,
+ "iframe with mozFrameType='content' in chrome document is typeContent");
+
+ SimpleTest.executeSoon(function () {
+ // First focus the parent window and then close this one.
+ SimpleTest.waitForFocus(function() {
+ let ww = SpecialPowers.Services.ww;
+ ww.registerNotification(function windowObs(subject, topic, data) {
+ if (topic == "domwindowclosed") {
+ ww.unregisterNotification(windowObs);
+
+ // Don't start the next test synchronously!
+ SimpleTest.executeSoon(function() {
+ SimpleTest.finish();
+ });
+ }
+ });
+
+ window.close();
+ }, opener);
+ });
+ }
+ ]]></script>
+</window>
diff --git a/docshell/test/chrome/red.png b/docshell/test/chrome/red.png
new file mode 100644
index 0000000000..aa9ce25263
--- /dev/null
+++ b/docshell/test/chrome/red.png
Binary files differ
diff --git a/docshell/test/chrome/test.template.txt b/docshell/test/chrome/test.template.txt
new file mode 100644
index 0000000000..b7dd5e5c23
--- /dev/null
+++ b/docshell/test/chrome/test.template.txt
@@ -0,0 +1,41 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet
+ href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id={BUGNUMBER}.xul
+-->
+<window title="Mozilla Bug {BUGNUMBER}"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <title>Test for Bug {BUGNUMBER}</title>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id={BUGNUMBER}">
+ Mozilla Bug {BUGNUMBER}</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+/** Test for Bug {BUGNUMBER} **/
+
+SimpleTest.waitForExplicitFinish();
+window.open("bug{BUGNUMBER}_window.xul", "bug{BUGNUMBER}",
+ "chrome,width=600,height=600");
+
+]]>
+</script>
+
+</window>
diff --git a/docshell/test/chrome/test_allowContentRetargeting.html b/docshell/test/chrome/test_allowContentRetargeting.html
new file mode 100644
index 0000000000..b6b830138f
--- /dev/null
+++ b/docshell/test/chrome/test_allowContentRetargeting.html
@@ -0,0 +1,76 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(runNextTest);
+
+var TEST_URL = "http://mochi.test:8888/tests/docshell/test/chrome/allowContentRetargeting.sjs";
+
+function runNextTest() {
+ var test = tests.shift();
+ if (!test) {
+ SimpleTest.finish();
+ return;
+ }
+ test();
+}
+
+var tests = [
+
+ // Set allowContentRetargeting = false, load a downloadable URL, verify the
+ // downloadable stops loading.
+ function basic() {
+ var iframe = insertIframe();
+ iframe.contentWindow.docShell.allowContentRetargeting = false;
+ loadIframe(iframe);
+ },
+
+ // Set allowContentRetargeting = false on parent docshell, load a downloadable
+ // URL, verify the downloadable stops loading.
+ function inherit() {
+ var docshell = window.docShell;
+ docshell.allowContentRetargeting = false;
+ loadIframe(insertIframe());
+ },
+];
+
+function insertIframe() {
+ var iframe = document.createElement("iframe");
+ document.body.appendChild(iframe);
+ return iframe;
+}
+
+function loadIframe(iframe) {
+ iframe.setAttribute("src", TEST_URL);
+ iframe.contentWindow.docShell.
+ QueryInterface(Ci.nsIInterfaceRequestor).
+ getInterface(Ci.nsIWebProgress).
+ addProgressListener(progressListener,
+ Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
+}
+
+var progressListener = {
+ onStateChange(webProgress, req, flags, status) {
+ if (!(flags & Ci.nsIWebProgressListener.STATE_STOP))
+ return;
+ is(Components.isSuccessCode(status), false,
+ "Downloadable should have failed to load");
+ document.querySelector("iframe").remove();
+ runNextTest();
+ },
+
+ QueryInterface: ChromeUtils.generateQI(["nsIWebProgressListener", "nsISupportsWeakReference"]),
+};
+
+ </script>
+</head>
+<body>
+<p id="display">
+</p>
+</body>
+</html>
diff --git a/docshell/test/chrome/test_bug112564.xhtml b/docshell/test/chrome/test_bug112564.xhtml
new file mode 100644
index 0000000000..33fd187eb2
--- /dev/null
+++ b/docshell/test/chrome/test_bug112564.xhtml
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet
+ href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=112564
+-->
+<window title="Mozilla Bug 112564"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=112564">Mozilla Bug 112564</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+/** Test for Bug 112564 **/
+
+SimpleTest.waitForExplicitFinish();
+window.openDialog("bug112564_window.xhtml", "bug112564",
+ "chrome,width=600,height=600,noopener", window);
+
+]]>
+</script>
+
+</window>
diff --git a/docshell/test/chrome/test_bug113934.xhtml b/docshell/test/chrome/test_bug113934.xhtml
new file mode 100644
index 0000000000..99b8ae253c
--- /dev/null
+++ b/docshell/test/chrome/test_bug113934.xhtml
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=113934
+-->
+<window title="Mozilla Bug 113934"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=113934"
+ target="_blank">Mozilla Bug 396519</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+ SimpleTest.waitForExplicitFinish();
+
+ addLoadEvent(function() {
+ window.openDialog("bug113934_window.xhtml?content", "bug113934",
+ "chrome,width=800,height=800,noopener", window);
+ });
+
+ ]]></script>
+</window>
diff --git a/docshell/test/chrome/test_bug215405.xhtml b/docshell/test/chrome/test_bug215405.xhtml
new file mode 100644
index 0000000000..e40d1d046a
--- /dev/null
+++ b/docshell/test/chrome/test_bug215405.xhtml
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet
+ href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=215405
+-->
+<window title="Mozilla Bug 215405"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=215405">Mozilla Bug 215405</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+/** Test for Bug 215405 **/
+
+SimpleTest.waitForExplicitFinish();
+window.openDialog("bug215405_window.xhtml", "bug215405",
+ "chrome,width=600,height=600,noopener", window);
+
+]]>
+</script>
+
+</window>
diff --git a/docshell/test/chrome/test_bug293235.xhtml b/docshell/test/chrome/test_bug293235.xhtml
new file mode 100644
index 0000000000..5b0f03f1e9
--- /dev/null
+++ b/docshell/test/chrome/test_bug293235.xhtml
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet
+ href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=293235.xul
+-->
+<window title="Mozilla Bug 293235"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=293235">
+ Mozilla Bug 293235</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+/** Test for Bug 293235 **/
+
+SimpleTest.waitForExplicitFinish();
+window.openDialog("bug293235_window.xhtml", "bug293235",
+ "chrome,width=600,height=600,noopener", window);
+
+]]>
+</script>
+
+</window>
diff --git a/docshell/test/chrome/test_bug294258.xhtml b/docshell/test/chrome/test_bug294258.xhtml
new file mode 100644
index 0000000000..97588b6ab2
--- /dev/null
+++ b/docshell/test/chrome/test_bug294258.xhtml
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet
+ href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=294258.xul
+-->
+<window title="Mozilla Bug 294258"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=294258">
+ Mozilla Bug 294258</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+/** Test for Bug 294258 **/
+
+SimpleTest.waitForExplicitFinish();
+window.openDialog("bug294258_window.xhtml", "bug294258",
+ "chrome,width=600,height=600,noopener", window);
+
+]]>
+</script>
+
+</window>
diff --git a/docshell/test/chrome/test_bug298622.xhtml b/docshell/test/chrome/test_bug298622.xhtml
new file mode 100644
index 0000000000..56eae4df37
--- /dev/null
+++ b/docshell/test/chrome/test_bug298622.xhtml
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet
+ href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=298622.xul
+-->
+<window title="Mozilla Bug 298622"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=298622">
+ Mozilla Bug 298622</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+/** Test for Bug 298622 **/
+
+SimpleTest.waitForExplicitFinish();
+window.openDialog("bug298622_window.xhtml", "bug298622",
+ "chrome,width=600,height=600,noopener", window);
+
+]]>
+</script>
+
+</window>
diff --git a/docshell/test/chrome/test_bug301397.xhtml b/docshell/test/chrome/test_bug301397.xhtml
new file mode 100644
index 0000000000..9bcd2f5212
--- /dev/null
+++ b/docshell/test/chrome/test_bug301397.xhtml
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet
+ href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=301397.xul
+-->
+<window title="Mozilla Bug 301397"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=301397">
+ Mozilla Bug 301397</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+/** Test for Bug 301397 **/
+
+SimpleTest.waitForExplicitFinish();
+window.openDialog("bug301397_window.xhtml", "bug301397",
+ "chrome,width=600,height=600,noopener", window);
+
+]]>
+</script>
+
+</window>
diff --git a/docshell/test/chrome/test_bug303267.xhtml b/docshell/test/chrome/test_bug303267.xhtml
new file mode 100644
index 0000000000..105abff0d9
--- /dev/null
+++ b/docshell/test/chrome/test_bug303267.xhtml
@@ -0,0 +1,39 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet
+ href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=303267.xul
+-->
+<window title="Mozilla Bug 303267"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=303267">Mozilla Bug 303267</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+SimpleTest.expectAssertions(0, 1);
+
+/** Test for Bug 303267 **/
+
+SimpleTest.waitForExplicitFinish();
+window.openDialog("bug303267_window.xhtml", "bug303267",
+ "chrome,width=600,height=600,noopener", window);
+
+]]>
+</script>
+
+</window>
diff --git a/docshell/test/chrome/test_bug311007.xhtml b/docshell/test/chrome/test_bug311007.xhtml
new file mode 100644
index 0000000000..b4f13e6d9d
--- /dev/null
+++ b/docshell/test/chrome/test_bug311007.xhtml
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet
+ href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=311007.xul
+-->
+<window title="Mozilla Bug 311007"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=311007">
+ Mozilla Bug 311007</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+if (navigator.platform.startsWith("Win")) {
+ SimpleTest.expectAssertions(0, 1);
+}
+
+/** Test for Bug 311007 **/
+
+SimpleTest.waitForExplicitFinish();
+window.openDialog("bug311007_window.xhtml", "bug311007",
+ "chrome,width=600,height=600,noopener", window);
+
+]]>
+</script>
+
+</window>
diff --git a/docshell/test/chrome/test_bug321671.xhtml b/docshell/test/chrome/test_bug321671.xhtml
new file mode 100644
index 0000000000..aa1712bf1f
--- /dev/null
+++ b/docshell/test/chrome/test_bug321671.xhtml
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet
+ href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=321671.xul
+-->
+<window title="Mozilla Bug 321671"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=321671">
+ Mozilla Bug 321671</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+/** Test for Bug 321671 **/
+
+SimpleTest.waitForExplicitFinish();
+window.openDialog("bug321671_window.xhtml", "bug321671",
+ "chrome,width=600,height=600,scrollbars,noopener", window);
+
+]]>
+</script>
+
+</window>
diff --git a/docshell/test/chrome/test_bug360511.xhtml b/docshell/test/chrome/test_bug360511.xhtml
new file mode 100644
index 0000000000..cbbadcdd45
--- /dev/null
+++ b/docshell/test/chrome/test_bug360511.xhtml
@@ -0,0 +1,39 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet
+ href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=360511.xul
+-->
+<window title="Mozilla Bug 360511"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js">
+ </script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=360511">
+ Mozilla Bug 360511</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+/** Test for Bug 360511 **/
+
+SimpleTest.waitForExplicitFinish();
+window.openDialog("bug360511_window.xhtml", "bug360511",
+ "chrome,scrollbars,width=600,height=600,noopener", window);
+
+]]>
+</script>
+
+</window>
diff --git a/docshell/test/chrome/test_bug364461.xhtml b/docshell/test/chrome/test_bug364461.xhtml
new file mode 100644
index 0000000000..c753f7825b
--- /dev/null
+++ b/docshell/test/chrome/test_bug364461.xhtml
@@ -0,0 +1,43 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet
+ href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=364461
+-->
+<window title="Mozilla Bug 364461"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=364461">Mozilla Bug 364461</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+/** Test for Bug 364461 **/
+
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.pushPrefEnv({
+ "set":[["security.data_uri.block_toplevel_data_uri_navigations", false]]
+}, runTests);
+
+function runTests() {
+ window.openDialog("bug364461_window.xhtml", "bug364461",
+ "chrome,width=600,height=600,noopener", window);
+}
+]]>
+</script>
+
+</window>
diff --git a/docshell/test/chrome/test_bug396519.xhtml b/docshell/test/chrome/test_bug396519.xhtml
new file mode 100644
index 0000000000..a6faea79ae
--- /dev/null
+++ b/docshell/test/chrome/test_bug396519.xhtml
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=396519
+-->
+<window title="Mozilla Bug 396519"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=396519"
+ target="_blank">Mozilla Bug 396519</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+
+ /** Test for Bug 396519 **/
+
+ SimpleTest.waitForExplicitFinish();
+ window.openDialog("bug396519_window.xhtml", "bug396519",
+ "chrome,width=600,height=600,noopener", window);
+
+ ]]></script>
+</window>
diff --git a/docshell/test/chrome/test_bug396649.xhtml b/docshell/test/chrome/test_bug396649.xhtml
new file mode 100644
index 0000000000..eda8e924da
--- /dev/null
+++ b/docshell/test/chrome/test_bug396649.xhtml
@@ -0,0 +1,41 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet
+ href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=396649.xul
+-->
+<window title="Mozilla Bug 396649"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src=
+ "chrome://mochikit/content/tests/SimpleTest/SimpleTest.js">
+ </script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=396649">
+ Mozilla Bug 396649</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+/** Test for Bug 396649 **/
+
+SimpleTest.waitForExplicitFinish();
+window.openDialog("bug396649_window.xhtml", "bug396649",
+ "chrome,width=600,height=600,scrollbars,noopener", window);
+
+]]>
+</script>
+
+</window>
diff --git a/docshell/test/chrome/test_bug428288.html b/docshell/test/chrome/test_bug428288.html
new file mode 100644
index 0000000000..29b90c677d
--- /dev/null
+++ b/docshell/test/chrome/test_bug428288.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=428288
+-->
+<head>
+ <title>Test for Bug 428288</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=428288">Mozilla Bug 428288</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <iframe name="target"></iframe>
+ <a id="crashy" target="target" href="about:blank">crash me</a>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 428288 **/
+
+function makeClick() {
+ var event = document.createEvent("MouseEvents");
+ event.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0,
+ false, false, false, false, 0, null);
+ document.getElementById("crashy").dispatchEvent(event);
+ return true;
+}
+
+ok(makeClick(), "Crashes if bug 428288 is present");
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/docshell/test/chrome/test_bug449778.xhtml b/docshell/test/chrome/test_bug449778.xhtml
new file mode 100644
index 0000000000..67e17164ea
--- /dev/null
+++ b/docshell/test/chrome/test_bug449778.xhtml
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=449778
+-->
+<window title="Mozilla Bug 449778"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=449778"
+ target="_blank">Mozilla Bug 396519</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+ SimpleTest.waitForExplicitFinish();
+
+ addLoadEvent(function() {
+ window.openDialog("bug449778_window.xhtml", "bug449778",
+ "chrome,width=800,height=800,noopener", window);
+ });
+
+ ]]></script>
+</window>
diff --git a/docshell/test/chrome/test_bug449780.xhtml b/docshell/test/chrome/test_bug449780.xhtml
new file mode 100644
index 0000000000..43ed3ce25d
--- /dev/null
+++ b/docshell/test/chrome/test_bug449780.xhtml
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=449780
+-->
+<window title="Mozilla Bug 449780"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=449780"
+ target="_blank">Mozilla Bug 396519</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+ SimpleTest.waitForExplicitFinish();
+
+ addLoadEvent(function() {
+ window.openDialog("bug449780_window.xhtml", "bug449780",
+ "chrome,width=800,height=800,noopener", window);
+ });
+
+ ]]></script>
+</window>
diff --git a/docshell/test/chrome/test_bug453650.xhtml b/docshell/test/chrome/test_bug453650.xhtml
new file mode 100644
index 0000000000..2865200478
--- /dev/null
+++ b/docshell/test/chrome/test_bug453650.xhtml
@@ -0,0 +1,120 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=453650
+-->
+<window title="Mozilla Bug 453650"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ /** Test for Bug 453650 **/
+ SimpleTest.waitForExplicitFinish();
+
+ var iter = runTests();
+ nextTest();
+
+ function* runTests() {
+ var iframe = document.createXULElement("iframe");
+ iframe.style.width = "300px";
+ iframe.style.height = "300px";
+ iframe.setAttribute("src", "data:text/html,<h1 id='h'>hello</h1>");
+
+ document.documentElement.appendChild(iframe);
+ yield whenLoaded(iframe);
+ info("iframe loaded");
+
+ var h1 = iframe.contentDocument.getElementById("h");
+ let myCallback = function() { h1.style.width = "400px"; };
+ info("Calling waitForInterruptibleReflow");
+ yield waitForInterruptibleReflow(iframe.docShell, myCallback);
+ info("got past top-level waitForInterruptibleReflow");
+
+ myCallback = function() { h1.style.width = "300px"; };
+ info("Calling waitForReflow");
+ waitForReflow(iframe.docShell, myCallback);
+ info("got past top-level waitForReflow");
+ yield is(300, h1.offsetWidth, "h1 has correct width");
+
+ SimpleTest.finish();
+ }
+
+ function waitForInterruptibleReflow(docShell,
+ callbackThatShouldTriggerReflow) {
+ waitForReflow(docShell, callbackThatShouldTriggerReflow, true);
+ }
+
+ function waitForReflow(docShell, callbackThatShouldTriggerReflow,
+ interruptible = false) {
+ info("Entering waitForReflow");
+ function done() {
+ info("Entering done (inside of waitForReflow)");
+
+ docShell.removeWeakReflowObserver(observer);
+ SimpleTest.executeSoon(nextTest);
+ }
+
+ var observer = {
+ reflow (start, end) {
+ info("Entering observer.reflow");
+ if (interruptible) {
+ ok(false, "expected interruptible reflow");
+ } else {
+ ok(true, "observed uninterruptible reflow");
+ }
+
+ info("times: " + start + ", " + end);
+ ok(start <= end, "reflow start time lower than end time");
+ done();
+ },
+
+ reflowInterruptible (start, end) {
+ info("Entering observer.reflowInterruptible");
+ if (!interruptible) {
+ ok(false, "expected uninterruptible reflow");
+ } else {
+ ok(true, "observed interruptible reflow");
+ }
+
+ info("times: " + start + ", " + end);
+ ok(start <= end, "reflow start time lower than end time");
+ done();
+ },
+
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIReflowObserver",
+ "nsISupportsWeakReference",
+ ]),
+ };
+
+ info("waitForReflow is adding a reflow observer");
+ docShell.addWeakReflowObserver(observer);
+ callbackThatShouldTriggerReflow();
+ }
+
+ function whenLoaded(iframe) {
+ info("entering whenLoaded");
+ iframe.addEventListener("load", function() {
+ SimpleTest.executeSoon(nextTest);
+ }, { once: true });
+ }
+
+ function nextTest() {
+ info("entering nextTest");
+ iter.next();
+ }
+
+ ]]>
+ </script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=453650"
+ target="_blank">Mozilla Bug 453650</a>
+ </body>
+</window>
diff --git a/docshell/test/chrome/test_bug454235.xhtml b/docshell/test/chrome/test_bug454235.xhtml
new file mode 100644
index 0000000000..ed6dc57c41
--- /dev/null
+++ b/docshell/test/chrome/test_bug454235.xhtml
@@ -0,0 +1,40 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=454235
+-->
+<window title="Mozilla Bug 454235"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=454235"
+ target="_blank">Mozilla Bug 454235</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+
+ /** Test for Bug 454235 **/
+SimpleTest.waitForExplicitFinish();
+
+SimpleTest.waitForFocus(doTest);
+
+function doTest() {
+ var hiddenBrowser = document.getElementById("hiddenBrowser");
+
+ hiddenBrowser.contentWindow.focus();
+ ok(!hiddenBrowser.contentDocument.hasFocus(), "hidden browser is unfocusable");
+
+ SimpleTest.finish();
+}
+
+
+
+ ]]></script>
+ <box flex="1" style="visibility: hidden; border:5px black solid">
+ <browser style="border:5px blue solid" id="hiddenBrowser" src="bug454235-subframe.xhtml"/>
+ </box>
+</window>
diff --git a/docshell/test/chrome/test_bug456980.xhtml b/docshell/test/chrome/test_bug456980.xhtml
new file mode 100644
index 0000000000..d1741209c6
--- /dev/null
+++ b/docshell/test/chrome/test_bug456980.xhtml
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=456980
+-->
+<window title="Mozilla Bug 456980"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=456980"
+ target="_blank">Mozilla Bug 396519</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+ SimpleTest.waitForExplicitFinish();
+
+ addLoadEvent(function() {
+ window.openDialog("bug113934_window.xhtml?chrome", "bug456980",
+ "chrome,width=800,height=800,noopener", window);
+ });
+
+ ]]></script>
+</window>
diff --git a/docshell/test/chrome/test_bug565388.xhtml b/docshell/test/chrome/test_bug565388.xhtml
new file mode 100644
index 0000000000..7919176cba
--- /dev/null
+++ b/docshell/test/chrome/test_bug565388.xhtml
@@ -0,0 +1,79 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=565388
+-->
+<window title="Mozilla Bug 565388"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ /** Test for Bug 565388 **/
+ SimpleTest.waitForExplicitFinish();
+
+function test() {
+ var progressListener = {
+ add(docShell, callback) {
+ this.callback = callback;
+ this.docShell = docShell;
+ docShell.
+ QueryInterface(Ci.nsIInterfaceRequestor).
+ getInterface(Ci.nsIWebProgress).
+ addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_WINDOW);
+ },
+
+ finish() {
+ this.docShell.
+ QueryInterface(Ci.nsIInterfaceRequestor).
+ getInterface(Ci.nsIWebProgress).
+ removeProgressListener(this);
+ this.callback();
+ },
+
+ onStateChange (webProgress, req, flags, status) {
+ if (req.name.startsWith("data:application/vnd.mozilla.xul")) {
+ if (flags & Ci.nsIWebProgressListener.STATE_STOP)
+ this.finish();
+ }
+ },
+
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIWebProgressListener",
+ "nsISupportsWeakReference",
+ ]),
+ }
+
+ var systemPrincipal = Cc["@mozilla.org/systemprincipal;1"]
+ .getService(Ci.nsIPrincipal);
+ var webNav = SpecialPowers.Services.appShell.createWindowlessBrowser(true);
+ var docShell = webNav.docShell;
+ docShell.createAboutBlankContentViewer(systemPrincipal, systemPrincipal);
+ var win = docShell.contentViewer.DOMDocument.defaultView;
+
+ progressListener.add(docShell, function(){
+ is(win.document.documentURI, "data:application/xhtml+xml;charset=utf-8,<window/>");
+ webNav.close();
+ SimpleTest.finish();
+ });
+
+ win.location = "data:application/xhtml+xml;charset=utf-8,<window/>";
+}
+
+addLoadEvent(function onLoad() {
+ test();
+});
+
+ ]]>
+ </script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=565388"
+ target="_blank">Mozilla Bug 565388</a>
+ </body>
+</window>
diff --git a/docshell/test/chrome/test_bug582176.xhtml b/docshell/test/chrome/test_bug582176.xhtml
new file mode 100644
index 0000000000..718a500cdc
--- /dev/null
+++ b/docshell/test/chrome/test_bug582176.xhtml
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet
+ href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=582176.xul
+-->
+<window title="Mozilla Bug 582176"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=582176">
+ Mozilla Bug 582176</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+/** Test for Bug 582176 **/
+
+SimpleTest.waitForExplicitFinish();
+window.openDialog("bug582176_window.xhtml", "bug582176",
+ "chrome,width=600,height=600,noopener", window);
+
+]]>
+</script>
+
+</window>
diff --git a/docshell/test/chrome/test_bug608669.xhtml b/docshell/test/chrome/test_bug608669.xhtml
new file mode 100644
index 0000000000..182c307766
--- /dev/null
+++ b/docshell/test/chrome/test_bug608669.xhtml
@@ -0,0 +1,80 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=608669
+-->
+<window title="Mozilla Bug 608669"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=608669"
+ target="_blank">Mozilla Bug 608669</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+
+/** Test for Bug 608669 **/
+SimpleTest.waitForExplicitFinish();
+
+addLoadEvent(nextTest);
+
+let gen = doTest();
+
+function nextTest() {
+ gen.next();
+}
+
+let chromeWindow = window.browsingContext.topChromeWindow;
+
+function* doTest() {
+ var notificationCount = 0;
+ var observer = {
+ observe(aSubject, aTopic, aData) {
+ is(aTopic, "chrome-document-global-created",
+ "correct topic");
+ is(aData, "null",
+ "correct data");
+ notificationCount++;
+ }
+ };
+
+ var os = SpecialPowers.Services.obs;
+ os.addObserver(observer, "chrome-document-global-created");
+ os.addObserver(observer, "content-document-global-created");
+
+ is(notificationCount, 0, "initial count");
+
+ // create a new window
+ var testWin = chromeWindow.open("", "bug 608669", "chrome,width=600,height=600");
+ testWin.x = "y";
+ is(notificationCount, 1, "after created window");
+
+ // Try loading in the window
+ testWin.location = "bug608669.xhtml";
+ chromeWindow.onmessage = nextTest;
+ yield undefined;
+ is(notificationCount, 1, "after first load");
+ is(testWin.x, "y", "reused window");
+
+ // Try loading again in the window
+ testWin.location = "bug608669.xhtml?x";
+ chromeWindow.onmessage = nextTest;
+ yield undefined;
+ is(notificationCount, 2, "after second load");
+ is("x" in testWin, false, "didn't reuse window");
+
+ chromeWindow.onmessage = null;
+
+ testWin.close();
+
+ os.removeObserver(observer, "chrome-document-global-created");
+ os.removeObserver(observer, "content-document-global-created");
+ SimpleTest.finish();
+}
+
+ ]]></script>
+</window>
diff --git a/docshell/test/chrome/test_bug662200.xhtml b/docshell/test/chrome/test_bug662200.xhtml
new file mode 100644
index 0000000000..5af4e200e2
--- /dev/null
+++ b/docshell/test/chrome/test_bug662200.xhtml
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet
+ href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=662200.xul
+-->
+<window title="Mozilla Bug 662200"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=662200">
+ Mozilla Bug 662200</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+/** Test for Bug 662200 **/
+
+SimpleTest.waitForExplicitFinish();
+window.openDialog("bug662200_window.xhtml", "bug662200",
+ "chrome,width=600,height=600,noopener", window);
+
+]]>
+</script>
+
+</window>
diff --git a/docshell/test/chrome/test_bug690056.xhtml b/docshell/test/chrome/test_bug690056.xhtml
new file mode 100644
index 0000000000..f1b330a3da
--- /dev/null
+++ b/docshell/test/chrome/test_bug690056.xhtml
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=690056
+-->
+<window title="Mozilla Bug 690056"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=690056"
+ target="_blank">Mozilla Bug 690056</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test for Bug 690056 **/
+SimpleTest.waitForExplicitFinish();
+window.openDialog("bug690056_window.xhtml", "bug690056",
+ "chrome,width=600,height=600,noopener", window);
+ ]]>
+ </script>
+</window>
diff --git a/docshell/test/chrome/test_bug789773.xhtml b/docshell/test/chrome/test_bug789773.xhtml
new file mode 100644
index 0000000000..0f4a67fc10
--- /dev/null
+++ b/docshell/test/chrome/test_bug789773.xhtml
@@ -0,0 +1,67 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=789773
+-->
+<window title="Mozilla Bug 789773"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=789773"
+ target="_blank">Mozilla Bug 789773</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /* Test for Bug 789773.
+ *
+ * See comment 50 for the situation we're testing against here.
+ *
+ * Note that the failure mode of this test is to hang, and hang the browser on quit.
+ * This is an unfortunate occurance, but that's why we're testing it.
+ */
+ SimpleTest.waitForExplicitFinish();
+
+ const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+
+ var calledListenerForBrowserChromeURL = false;
+ var testProgressListener = {
+ START_DOC: Ci.nsIWebProgressListener.STATE_START | Ci.nsIWebProgressListener.STATE_IS_DOCUMENT,
+ onStateChange(wp, req, stateFlags, status) {
+ let browserChromeFileName = AppConstants.BROWSER_CHROME_URL.split("/").reverse()[0];
+ if (req.name.includes(browserChromeFileName)) {
+ wp.DOMWindow; // Force the lazy creation of a DOM window.
+ calledListenerForBrowserChromeURL = true;
+ }
+ if (req.name.includes("mozilla.html") && (stateFlags & Ci.nsIWebProgressListener.STATE_STOP))
+ finishTest();
+ },
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIWebProgressListener",
+ "nsISupportsWeakReference",
+ ]),
+ }
+
+ // Add our progress listener
+ var webProgress = Cc['@mozilla.org/docloaderservice;1'].getService(Ci.nsIWebProgress);
+ webProgress.addProgressListener(testProgressListener, Ci.nsIWebProgress.NOTIFY_STATE_REQUEST);
+
+ // Open the window.
+ var popup = window.open("about:mozilla", "_blank", "width=640,height=400");
+
+ // Wait for the window to load.
+ function finishTest() {
+ webProgress.removeProgressListener(testProgressListener);
+ ok(true, "Loaded the popup window without spinning forever in the event loop!");
+ ok(calledListenerForBrowserChromeURL, "Should have called the progress listener for browser.xhtml");
+ popup.close();
+ SimpleTest.finish();
+ }
+
+ ]]>
+ </script>
+</window>
diff --git a/docshell/test/chrome/test_bug846906.xhtml b/docshell/test/chrome/test_bug846906.xhtml
new file mode 100644
index 0000000000..0fdf0e5079
--- /dev/null
+++ b/docshell/test/chrome/test_bug846906.xhtml
@@ -0,0 +1,94 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=846906
+-->
+<window title="Mozilla Bug 846906"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ /** Test for Bug 846906 **/
+ SimpleTest.waitForExplicitFinish();
+
+ var appShellService = SpecialPowers.Services.appShell;
+ ok(appShellService, "Should be able to get app shell service");
+
+ var windowlessBrowser = appShellService.createWindowlessBrowser();
+ ok(windowlessBrowser, "Should be able to create windowless browser");
+
+ ok(windowlessBrowser instanceof Ci.nsIWindowlessBrowser,
+ "Windowless browser should implement nsIWindowlessBrowser");
+
+ var webNavigation = windowlessBrowser.QueryInterface(Ci.nsIWebNavigation);
+ ok(webNavigation, "Windowless browser should implement nsIWebNavigation");
+
+ var interfaceRequestor = windowlessBrowser.QueryInterface(Ci.nsIInterfaceRequestor);
+ ok(interfaceRequestor, "Should be able to query interface requestor interface");
+
+ var docShell = windowlessBrowser.docShell;
+ ok(docShell, "Should be able to get doc shell interface");
+
+ var document = webNavigation.document;
+ ok(document, "Should be able to get document");
+
+ var iframe = document.createXULElement("iframe");
+ ok(iframe, "Should be able to create iframe");
+
+ iframe.onload = function () {
+ ok(true, "Should receive initial onload event");
+
+ iframe.onload = function () {
+ ok(true, "Should receive onload event");
+
+ var contentDocument = iframe.contentDocument;
+ ok(contentDocument, "Should be able to get content document");
+
+ var div = contentDocument.getElementById("div1");
+ ok(div, "Should be able to get element by id");
+
+ var rect = div.getBoundingClientRect();
+ ok(rect, "Should be able to get bounding client rect");
+
+ // xxx: can we do better than hardcoding these values here?
+ is(rect.width, 1024);
+ is(rect.height, 768);
+
+ windowlessBrowser.close();
+
+ // Once the browser is closed, nsIWebNavigation and
+ // nsIInterfaceRequestor methods should no longer be accessible.
+ try {
+ windowlessBrowser.getInterface(Ci.nsIDocShell);
+ ok(false);
+ } catch (e) {
+ is(e.result, Cr.NS_ERROR_NULL_POINTER);
+ }
+
+ try {
+ windowlessBrowser.document;
+ ok(false);
+ } catch (e) {
+ is(e.result, Cr.NS_ERROR_NULL_POINTER);
+ }
+
+ SimpleTest.finish();
+ };
+ iframe.setAttribute("src", "http://mochi.test:8888/chrome/docshell/test/chrome/bug846906.html");
+ };
+ document.documentElement.appendChild(iframe);
+
+ ]]>
+ </script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=846906"
+ target="_blank">Mozilla Bug 846906</a>
+ </body>
+</window>
diff --git a/docshell/test/chrome/test_bug89419.xhtml b/docshell/test/chrome/test_bug89419.xhtml
new file mode 100644
index 0000000000..6a00080aa6
--- /dev/null
+++ b/docshell/test/chrome/test_bug89419.xhtml
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet
+ href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=89419.xul
+-->
+<window title="Mozilla Bug 89419"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=89419">
+ Mozilla Bug 89419</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+/** Test for Bug 89419 **/
+
+SimpleTest.waitForExplicitFinish();
+window.openDialog("bug89419_window.xhtml", "bug89419",
+ "chrome,width=600,height=600,noopener", window);
+
+]]>
+</script>
+
+</window>
diff --git a/docshell/test/chrome/test_bug909218.html b/docshell/test/chrome/test_bug909218.html
new file mode 100644
index 0000000000..bcbcc176eb
--- /dev/null
+++ b/docshell/test/chrome/test_bug909218.html
@@ -0,0 +1,117 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(test);
+
+// The default flags we will stick on the docShell - every request made by the
+// docShell should include those flags.
+const TEST_FLAGS = Ci.nsIRequest.LOAD_ANONYMOUS |
+ Ci.nsIRequest.LOAD_BYPASS_CACHE |
+ Ci.nsIRequest.INHIBIT_CACHING;
+
+var TEST_URL = "http://mochi.test:8888/chrome/docshell/test/chrome/bug909218.html";
+
+// These are the requests we expect to see loading TEST_URL into our iframe.
+
+// The test entry-point. The basic outline is:
+// * Create an iframe and set defaultLoadFlags on its docShell.
+// * Add a web progress listener to observe each request as the iframe is
+// loaded, and check that each request has the flags we specified.
+// * Load our test URL into the iframe and wait for the load to complete.
+function test() {
+ var iframe = document.createElement("iframe");
+ document.body.appendChild(iframe);
+ var docShell = iframe.contentWindow.docShell;
+ // Add our progress listener - when it notices the top-level document is
+ // complete, the test will end.
+ RequestWatcher.init(docShell, SimpleTest.finish);
+ // Set the flags we care about, then load our test URL.
+ docShell.defaultLoadFlags = TEST_FLAGS;
+ iframe.setAttribute("src", TEST_URL);
+}
+
+// an nsIWebProgressListener that checks all requests made by the docShell
+// have the flags we expect.
+var RequestWatcher = {
+ init(docShell, callback) {
+ this.callback = callback;
+ this.docShell = docShell;
+ docShell.
+ QueryInterface(Ci.nsIInterfaceRequestor).
+ getInterface(Ci.nsIWebProgress).
+ addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_REQUEST |
+ Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
+ // These are the requests we expect to see - initialize each to have a
+ // count of zero.
+ this.requestCounts = {};
+ for (var url of [
+ TEST_URL,
+ // content loaded by the above test html.
+ "http://mochi.test:8888/chrome/docshell/test/chrome/bug909218.js",
+ "http://mochi.test:8888/tests/SimpleTest/test.css",
+ "http://mochi.test:8888/tests/docshell/test/chrome/red.png",
+ // the content of an iframe in the test html.
+ "http://mochi.test:8888/chrome/docshell/test/chrome/generic.html",
+ ]) {
+ this.requestCounts[url] = 0;
+ }
+ },
+
+ // Finalize the test after we detect a completed load. We check we saw the
+ // correct requests and make a callback to exit.
+ finalize() {
+ ok(Object.keys(this.requestCounts).length, "we expected some requests");
+ for (var url in this.requestCounts) {
+ var count = this.requestCounts[url];
+ // As we are looking at all request states, we expect more than 1 for
+ // each URL - 0 or 1 would imply something went wrong - >1 just means
+ // multiple states for each request were recorded, which we don't care
+ // about (we do care they all have the correct flags though - but we
+ // do that in onStateChange)
+ ok(count > 1, url + " saw " + count + " requests");
+ }
+ this.docShell.
+ QueryInterface(Ci.nsIInterfaceRequestor).
+ getInterface(Ci.nsIWebProgress).
+ removeProgressListener(this);
+ this.callback();
+ },
+
+ onStateChange(webProgress, req, flags, status) {
+ // We are checking requests - if there isn't one, ignore it.
+ if (!req) {
+ return;
+ }
+ // We will usually see requests for 'about:document-onload-blocker' not
+ // have the flag, so we just ignore them.
+ // We also see, eg, resource://gre-resources/loading-image.png, so
+ // skip resource:// URLs too.
+ // We may also see, eg, chrome://global/skin/icons/chevron.svg, so
+ // skip chrome:// URLs too.
+ if (req.name.startsWith("about:") || req.name.startsWith("resource:") ||
+ req.name.startsWith("chrome:") || req.name.startsWith("documentchannel:")) {
+ return;
+ }
+ is(req.loadFlags & TEST_FLAGS, TEST_FLAGS, "request " + req.name + " has the expected flags");
+ this.requestCounts[req.name] += 1;
+ var stopFlags = Ci.nsIWebProgressListener.STATE_STOP |
+ Ci.nsIWebProgressListener.STATE_IS_DOCUMENT;
+ if (req.name == TEST_URL && (flags & stopFlags) == stopFlags) {
+ this.finalize();
+ }
+ },
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIWebProgressListener",
+ "nsISupportsWeakReference",
+ ]),
+};
+
+</script>
+</head>
+</html>
diff --git a/docshell/test/chrome/test_bug92598.xhtml b/docshell/test/chrome/test_bug92598.xhtml
new file mode 100644
index 0000000000..9c3a46866a
--- /dev/null
+++ b/docshell/test/chrome/test_bug92598.xhtml
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet
+ href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=92598
+-->
+<window title="Mozilla Bug 92598"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=92598">Mozilla Bug 92598</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+/** Test for Bug 92598 **/
+
+SimpleTest.waitForExplicitFinish();
+window.openDialog("bug92598_window.xhtml", "bug92598",
+ "chrome,width=600,height=600,noopener", window);
+
+]]>
+</script>
+
+</window>
diff --git a/docshell/test/chrome/test_docRedirect.sjs b/docshell/test/chrome/test_docRedirect.sjs
new file mode 100644
index 0000000000..4050eb06d7
--- /dev/null
+++ b/docshell/test/chrome/test_docRedirect.sjs
@@ -0,0 +1,6 @@
+function handleRequest(request, response) {
+ response.setStatusLine(request.httpVersion, 301, "Moved Permanently");
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ response.setHeader("Location", "http://example.org/");
+ response.write("Hello world!");
+}
diff --git a/docshell/test/chrome/test_docRedirect.xhtml b/docshell/test/chrome/test_docRedirect.xhtml
new file mode 100644
index 0000000000..1688d9823e
--- /dev/null
+++ b/docshell/test/chrome/test_docRedirect.xhtml
@@ -0,0 +1,91 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1342989
+-->
+<window title="Mozilla Bug 1342989"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <script type="application/javascript">
+ <![CDATA[
+ SimpleTest.waitForExplicitFinish();
+
+ const WEB_PROGRESS_LISTENER_FLAGS =
+ Object.keys(Ci.nsIWebProgressListener).filter(
+ propName => propName.startsWith("STATE_")
+ );
+
+ function bitFlagsToNames(flags, knownNames, intf) {
+ return knownNames.map( (F) => {
+ return (flags & intf[F]) ? F : undefined;
+ }).filter( (s) => !!s );
+ }
+
+ var progressListener = {
+ add(docShell, callback) {
+ this.callback = callback;
+ this.docShell = docShell;
+ docShell.
+ QueryInterface(Ci.nsIInterfaceRequestor).
+ getInterface(Ci.nsIWebProgress).
+ addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_ALL);
+ },
+
+ finish(success) {
+ this.docShell.
+ QueryInterface(Ci.nsIInterfaceRequestor).
+ getInterface(Ci.nsIWebProgress).
+ removeProgressListener(this);
+ this.callback(success);
+ },
+
+ onStateChange (webProgress, req, flags, status) {
+ if (!(flags & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT) &&
+ !(flags & Ci.nsIWebProgressListener.STATE_IS_REDIRECTED_DOCUMENT))
+ return;
+
+ var channel = req.QueryInterface(Ci.nsIChannel);
+
+ if (flags & Ci.nsIWebProgressListener.STATE_IS_REDIRECTED_DOCUMENT) {
+ SimpleTest.is(channel.URI.host, "example.org",
+ "Should be redirected to example.org (see test_docRedirect.sjs)");
+ this.finish(true);
+ }
+
+ // Fail in case we didn't receive document redirection event.
+ if (flags & Ci.nsIWebProgressListener.STATE_STOP)
+ this.finish(false);
+ },
+
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIWebProgressListener",
+ "nsISupportsWeakReference",
+ ]),
+ }
+
+ var webNav = SpecialPowers.Services.appShell.createWindowlessBrowser(true);
+ let docShell = webNav.docShell;
+ let system = Cc["@mozilla.org/systemprincipal;1"].getService(Ci.nsIPrincipal);
+ docShell.createAboutBlankContentViewer(system, system);
+
+ progressListener.add(docShell, function(success) {
+ webNav.close();
+ SimpleTest.is(success, true, "Received document redirect event");
+ SimpleTest.finish();
+ });
+
+ var win = docShell.contentViewer.DOMDocument.defaultView;
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ win.location = "http://example.com/chrome/docshell/test/chrome/test_docRedirect.sjs"
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1342989"
+ target="_blank">Mozilla Bug 1342989</a>
+ </body>
+</window>
diff --git a/docshell/test/chrome/test_mozFrameType.xhtml b/docshell/test/chrome/test_mozFrameType.xhtml
new file mode 100644
index 0000000000..6e8bfd8f85
--- /dev/null
+++ b/docshell/test/chrome/test_mozFrameType.xhtml
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet
+ href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=769771
+-->
+<window title="Test mozFrameType attribute"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+if (navigator.platform.startsWith("Win")) {
+ SimpleTest.expectAssertions(0, 1);
+}
+
+/** Test for Bug 769771 **/
+SimpleTest.waitForExplicitFinish();
+
+addLoadEvent(function () {
+ window.openDialog("mozFrameType_window.xhtml", "mozFrameType",
+ "chrome,width=600,height=600,noopener", window);
+});
+
+]]>
+</script>
+
+</window>
diff --git a/docshell/test/chrome/test_open_and_immediately_close_opener.html b/docshell/test/chrome/test_open_and_immediately_close_opener.html
new file mode 100644
index 0000000000..bb5f6f054d
--- /dev/null
+++ b/docshell/test/chrome/test_open_and_immediately_close_opener.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test for Bug 1702678</title>
+ <meta charset="utf-8">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1702678">Mozilla Bug 1702678</a>
+
+<script type="application/javascript">
+"use strict";
+
+const HTML = `
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <script>
+ // We need to queue a promise reaction job whilch will close the window
+ // during the nested event loop spun up by the window opening code.
+ Promise.resolve().then(() => {
+ window.close();
+ });
+ window.open("data:text/html,Hello");
+ <\/script>
+</head>
+</html>
+`;
+
+add_task(async function() {
+ // This bug only manifests when opening tabs in new windows.
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.link.open_newwindow", 2]],
+ });
+
+ // Create a window in a new BrowsingContextGroup so that it will be the last
+ // window in the group when it closes, and the group will be destroyed.
+ window.open(`data:text/html,${encodeURIComponent(HTML)}`, "", "noopener");
+
+ // Make a few trips through the event loop to ensure we've had a chance to
+ // open and close the relevant windows.
+ for (let i = 0; i < 10; i++) {
+ await new Promise(resolve => setTimeout(resolve, 0));
+ }
+
+ ok(true, "We didn't crash");
+});
+</script>
+
+</body>
+</html>
+
diff --git a/docshell/test/chrome/test_viewsource_forbidden_in_iframe.xhtml b/docshell/test/chrome/test_viewsource_forbidden_in_iframe.xhtml
new file mode 100644
index 0000000000..0cc45c7821
--- /dev/null
+++ b/docshell/test/chrome/test_viewsource_forbidden_in_iframe.xhtml
@@ -0,0 +1,159 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin/global.css"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=624883
+-->
+<window title="Mozilla Bug 624883"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=624883"
+ target="_blank">Mozilla Bug 624883</a>
+ </body>
+
+ <!-- test code goes here -->
+ <iframe type="content" onload="startTest()" src="file_viewsource_forbidden_in_iframe.html"></iframe>
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ SimpleTest.waitForExplicitFinish();
+
+ // We create a promise that will resolve with the error message
+ // on a network error page load and reject on any other load.
+ function createNetworkErrorMessagePromise(frame) {
+ return new Promise(function(resolve, reject) {
+
+ // Error pages do not fire "load" events, so use a progressListener.
+ var originalDocumentURI = frame.contentDocument.documentURI;
+ var progressListener = {
+ onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
+ // Make sure nothing other than an error page is loaded.
+ if (!(aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE)) {
+ reject("location change was not to an error page");
+ }
+ },
+
+ onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
+ // Wait until the documentURI changes (from about:blank) this should
+ // be the error page URI.
+ var documentURI = frame.contentDocument.documentURI;
+ if (documentURI == originalDocumentURI) {
+ return;
+ }
+
+ aWebProgress.removeProgressListener(progressListener,
+ Ci.nsIWebProgress.NOTIFY_ALL);
+ var matchArray = /about:neterror\?.*&d=([^&]*)/.exec(documentURI);
+ if (!matchArray) {
+ reject("no network error message found in URI")
+ return;
+ }
+
+ var errorMsg = matchArray[1];
+ resolve(decodeURIComponent(errorMsg));
+ },
+
+ QueryInterface: ChromeUtils.generateQI(["nsIWebProgressListener",
+ "nsISupportsWeakReference"])
+ };
+
+ frame.contentWindow.docShell
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebProgress)
+ .addProgressListener(progressListener,
+ Ci.nsIWebProgress.NOTIFY_LOCATION |
+ Ci.nsIWebProgress.NOTIFY_STATE_REQUEST);
+ });
+ }
+
+ function startTest() {
+ // Get a reference message that we know will be an unknown protocol message,
+ // so we can use it for comparisons in the test cases.
+ var refIframe = window[0].document.getElementById("refIframe");
+ var refErrorPromise = createNetworkErrorMessagePromise(refIframe);
+
+ refErrorPromise.then(
+ function(msg) {
+ window.refErrorMsg = msg;
+ var testIframe = window[0].document.getElementById("testIframe");
+
+ // Run test cases on load of "about:blank", so that the URI always changes
+ // and we can detect this in our Promise.
+ testIframe.onload = runNextTestCase;
+ testIframe.src = "about:blank";
+ },
+ function(reason) {
+ ok(false, "Could not get reference error message", reason);
+ SimpleTest.finish();
+ })
+ .catch(function(e) {
+ ok(false, "Unexpected exception thrown getting reference error message", e);
+ });
+
+ refIframe.src = "wibble://example.com";
+ }
+
+ function runTestCase(testCase) {
+ var testIframe = window[0].document.getElementById("testIframe");
+ var expectedErrorMsg = window.refErrorMsg.replace("wibble", testCase.expectedProtocolList);
+
+ var testErrorPromise = createNetworkErrorMessagePromise(testIframe);
+ testErrorPromise.then(
+ function(actualErrorMsg) {
+ is(actualErrorMsg, expectedErrorMsg, testCase.desc);
+ testIframe.src = "about:blank";
+ },
+ function(reason) {
+ ok(false, testCase.desc, reason);
+ testIframe.src = "about:blank";
+ })
+ .catch(function(e) {
+ ok(false, testCase.desc + " - unexpected exception thrown", e);
+ });
+
+ testIframe.src = testCase.protocols + "://example.com/!/";
+ }
+
+ var testCaseIndex = -1;
+ let testCases = [
+ {
+ desc: "Test 1: view-source should not be allowed in an iframe",
+ protocols: "view-source:http",
+ expectedProtocolList: "view-source, http"
+ },
+ {
+ desc: "Test 2: jar:view-source should not be allowed in an iframe",
+ protocols: "jar:view-source:http",
+ expectedProtocolList: "jar, view-source, http"
+ },
+ {
+ desc: "Test 3: if invalid protocol first should report before view-source",
+ protocols: "wibble:view-source:http",
+ // Nothing after the invalid protocol gets set as a proper nested URI,
+ // so the list stops there.
+ expectedProtocolList: "wibble"
+ },
+ {
+ desc: "Test 4: if view-source first should report before invalid protocol",
+ protocols: "view-source:wibble:http",
+ expectedProtocolList: "view-source, wibble"
+ }
+ ];
+
+ function runNextTestCase() {
+ ++testCaseIndex;
+ if (testCaseIndex == testCases.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ runTestCase(testCases[testCaseIndex]);
+ }
+
+ ]]>
+ </script>
+</window>
diff --git a/docshell/test/chrome/window.template.txt b/docshell/test/chrome/window.template.txt
new file mode 100644
index 0000000000..4c520dc075
--- /dev/null
+++ b/docshell/test/chrome/window.template.txt
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window id="{BUGNUMBER}Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="600"
+ height="600"
+ onload="setTimeout(nextTest,0);"
+ title="bug {BUGNUMBER} test">
+
+ <script type="application/javascript"
+ src="docshell_helpers.js">
+ </script>
+
+ <script type="application/javascript"><![CDATA[
+
+ // Define the generator-iterator for the tests.
+ var tests = testIterator();
+
+ ////
+ // Execute the next test in the generator function.
+ //
+ function nextTest() {
+ tests.next();
+ }
+
+ ////
+ // Generator function for test steps for bug {BUGNUMBER}:
+ // Description goes here.
+ //
+ function testIterator()
+ {
+ // Test steps go here. See bug303267_window.xhtml for an example.
+
+ // Tell the framework the test is finished. Include the final 'yield'
+ // statement to prevent a StopIteration exception from being thrown.
+ finish();
+ yield undefined;
+ }
+
+ ]]></script>
+
+ <browser type="content" primary="true" flex="1" id="content" src="about:blank"/>
+</window>
diff --git a/docshell/test/iframesandbox/file_child_navigation_by_location.html b/docshell/test/iframesandbox/file_child_navigation_by_location.html
new file mode 100644
index 0000000000..7365bed81f
--- /dev/null
+++ b/docshell/test/iframesandbox/file_child_navigation_by_location.html
@@ -0,0 +1 @@
+<script>function onNav() { parent.parent.postMessage("childIframe", "*"); } window.onload = onNav; window.onhashchange = onNav;</script>
diff --git a/docshell/test/iframesandbox/file_marquee_event_handlers.html b/docshell/test/iframesandbox/file_marquee_event_handlers.html
new file mode 100644
index 0000000000..13ee31ddb7
--- /dev/null
+++ b/docshell/test/iframesandbox/file_marquee_event_handlers.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Test marquee attribute event handlers in iframe sandbox</title>
+</head>
+<body>
+ <!-- Note that the width here is slightly longer than the contents, to make
+ sure we bounce and finish very quickly. -->
+ <marquee loop="2" width="145" behavior="alternate" truespeed scrolldelay="1"
+ onstart="parent.postMessage(window.name + ' marquee onstart', '*');"
+ onbounce="parent.postMessage(window.name + ' marquee onbounce', '*');"
+ onfinish="parent.postMessage(window.name + ' marquee onfinish', '*');">
+ Will bounce and finish
+ </marquee>
+</body>
+</html>
diff --git a/docshell/test/iframesandbox/file_other_auxiliary_navigation_by_location.html b/docshell/test/iframesandbox/file_other_auxiliary_navigation_by_location.html
new file mode 100644
index 0000000000..ad24c0f242
--- /dev/null
+++ b/docshell/test/iframesandbox/file_other_auxiliary_navigation_by_location.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Test window for other auxiliary navigation by location tests</title>
+<script>
+ function onNav() {
+ opener.postMessage(window.name, "*");
+ }
+
+ window.onload = onNav;
+ window.onhashchange = onNav;
+</script>
+</head>
+</html>
diff --git a/docshell/test/iframesandbox/file_our_auxiliary_navigation_by_location.html b/docshell/test/iframesandbox/file_our_auxiliary_navigation_by_location.html
new file mode 100644
index 0000000000..978980df25
--- /dev/null
+++ b/docshell/test/iframesandbox/file_our_auxiliary_navigation_by_location.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Test window for our auxiliary navigation by location tests</title>
+<script>
+ function onNav() {
+ opener.parent.postMessage(window.name, "*");
+ }
+
+ window.onload = onNav;
+ window.onhashchange = onNav;
+</script>
+</head>
+</html>
diff --git a/docshell/test/iframesandbox/file_parent_navigation_by_location.html b/docshell/test/iframesandbox/file_parent_navigation_by_location.html
new file mode 100644
index 0000000000..9a2e95fad0
--- /dev/null
+++ b/docshell/test/iframesandbox/file_parent_navigation_by_location.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Test window for parent navigation by location tests</title>
+<script>
+ function onNav() {
+ parent.postMessage(window.name, "*");
+ }
+
+ window.onload = onNav;
+ window.onhashchange = onNav;
+</script>
+</head>
+<body>
+ <iframe name="childIframe" sandbox="allow-scripts allow-same-origin allow-top-navigation"></iframe>
+</body>
+</html>
diff --git a/docshell/test/iframesandbox/file_sibling_navigation_by_location.html b/docshell/test/iframesandbox/file_sibling_navigation_by_location.html
new file mode 100644
index 0000000000..51a52bb8eb
--- /dev/null
+++ b/docshell/test/iframesandbox/file_sibling_navigation_by_location.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Test window for sibling navigation by location tests</title>
+<script>
+ function onNav() {
+ parent.postMessage(window.name, "*");
+ }
+
+ window.onload = onNav;
+ window.onhashchange = onNav;
+</script>
+</head>
+</html>
diff --git a/docshell/test/iframesandbox/file_top_navigation_by_location.html b/docshell/test/iframesandbox/file_top_navigation_by_location.html
new file mode 100644
index 0000000000..194430f38c
--- /dev/null
+++ b/docshell/test/iframesandbox/file_top_navigation_by_location.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Test window for top navigation by location tests</title>
+<script>
+ function onNav() {
+ opener.postMessage(window.name, "*");
+ }
+
+ window.onload = onNav;
+ window.onhashchange = onNav;
+</script>
+</head>
+<body>
+ <iframe name="if1" sandbox="allow-scripts allow-same-origin"></iframe>
+ <iframe name="if2" sandbox="allow-scripts allow-same-origin allow-top-navigation"></iframe>
+ <iframe name="if3" sandbox="allow-scripts allow-top-navigation"></iframe>
+</body>
+</html>
diff --git a/docshell/test/iframesandbox/file_top_navigation_by_location_exotic.html b/docshell/test/iframesandbox/file_top_navigation_by_location_exotic.html
new file mode 100644
index 0000000000..9a26bc80db
--- /dev/null
+++ b/docshell/test/iframesandbox/file_top_navigation_by_location_exotic.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Test window for top navigation by location tests</title>
+<script>
+ function onBlock() {
+ opener.postMessage({ name: window.name, blocked: true }, "*");
+ }
+
+ function onNav() {
+ opener.postMessage({ name: window.name, blocked: false }, "*");
+ }
+
+ function setOwnHref() {
+ // eslint-disable-next-line no-self-assign
+ location.href = location.href;
+ }
+
+ window.onload = onNav;
+</script>
+</head>
+<body>
+ <iframe name="if1" sandbox="allow-scripts allow-same-origin"></iframe>
+ <iframe name="if2" sandbox="allow-scripts allow-same-origin allow-top-navigation"></iframe>
+</body>
+</html>
diff --git a/docshell/test/iframesandbox/file_top_navigation_by_user_activation.html b/docshell/test/iframesandbox/file_top_navigation_by_user_activation.html
new file mode 100644
index 0000000000..8454f1fac4
--- /dev/null
+++ b/docshell/test/iframesandbox/file_top_navigation_by_user_activation.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Test window for top navigation with user activation</title>
+<script>
+window.onload = () => {
+ opener.postMessage("READY", "*");
+};
+
+window.onhashchange = () => {
+ opener.postMessage("NAVIGATED", "*");
+};
+
+window.onmessage = (e) => {
+ if (e.data == "CLICK" || e.data == "SCRIPT") {
+ frames[0].postMessage([e.data, location.href + "#hash"], "*");
+ } else {
+ opener.postMessage(e.data, "*");
+ }
+};
+</script>
+</head>
+<body>
+ <iframe sandbox="allow-scripts allow-top-navigation-by-user-activation" src="http://example.org/tests/docshell/test/iframesandbox/file_top_navigation_by_user_activation_iframe.html"></iframe>
+</body>
+</html>
diff --git a/docshell/test/iframesandbox/file_top_navigation_by_user_activation_iframe.html b/docshell/test/iframesandbox/file_top_navigation_by_user_activation_iframe.html
new file mode 100644
index 0000000000..b775579f28
--- /dev/null
+++ b/docshell/test/iframesandbox/file_top_navigation_by_user_activation_iframe.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<title>Test window for top navigation with user activation</title>
+<script>
+function navigate(aURL) {
+ try {
+ top.location.href = aURL;
+ } catch (e) {
+ top.postMessage("BLOCKED", "*");
+ }
+}
+
+window.onmessage = (e) => {
+ SpecialPowers.wrap(document).clearUserGestureActivation();
+ let [command, url] = e.data;
+ if (command == "CLICK") {
+ let button = document.querySelector("button");
+ button.addEventListener("click", () => {
+ navigate(url);
+ }, { once: true });
+ synthesizeMouseAtCenter(button, {});
+ } else if (command == "SCRIPT") {
+ navigate(url);
+ }
+};
+</script>
+</head>
+<body><button>Click</button></body>
+</html>
diff --git a/docshell/test/iframesandbox/mochitest.ini b/docshell/test/iframesandbox/mochitest.ini
new file mode 100644
index 0000000000..4b6b1d8ecc
--- /dev/null
+++ b/docshell/test/iframesandbox/mochitest.ini
@@ -0,0 +1,28 @@
+[DEFAULT]
+support-files =
+ file_child_navigation_by_location.html
+ file_marquee_event_handlers.html
+ file_other_auxiliary_navigation_by_location.html
+ file_our_auxiliary_navigation_by_location.html
+ file_parent_navigation_by_location.html
+ file_sibling_navigation_by_location.html
+ file_top_navigation_by_location.html
+ file_top_navigation_by_location_exotic.html
+
+[test_child_navigation_by_location.html]
+[test_marquee_event_handlers.html]
+skip-if = true # Bug 1455996
+[test_other_auxiliary_navigation_by_location.html]
+tags = openwindow
+[test_our_auxiliary_navigation_by_location.html]
+tags = openwindow
+[test_parent_navigation_by_location.html]
+tags = openwindow
+[test_sibling_navigation_by_location.html]
+tags = openwindow
+[test_top_navigation_by_location_exotic.html]
+[test_top_navigation_by_location.html]
+[test_top_navigation_by_user_activation.html]
+support-files =
+ file_top_navigation_by_user_activation.html
+ file_top_navigation_by_user_activation_iframe.html
diff --git a/docshell/test/iframesandbox/test_child_navigation_by_location.html b/docshell/test/iframesandbox/test_child_navigation_by_location.html
new file mode 100644
index 0000000000..383320a02b
--- /dev/null
+++ b/docshell/test/iframesandbox/test_child_navigation_by_location.html
@@ -0,0 +1,91 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=785310
+html5 sandboxed iframe should not be able to perform top navigation with scripts allowed
+-->
+<head>
+<meta charset="utf-8">
+<title>Test for Bug 785310 - iframe sandbox child navigation by location tests</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+<script>
+ SimpleTest.waitForExplicitFinish();
+
+ var testDataUri = "file_child_navigation_by_location.html";
+
+ function runScriptNavigationTest(testCase) {
+ window.onmessage = function(event) {
+ if (event.data != "childIframe") {
+ ok(false, "event.data: got '" + event.data + "', expected 'childIframe'");
+ }
+ ok(!testCase.shouldBeBlocked, testCase.desc + " - child navigation was NOT blocked");
+ runNextTest();
+ };
+ try {
+ window.parentIframe.eval(testCase.script);
+ } catch (e) {
+ ok(testCase.shouldBeBlocked, testCase.desc + " - " + e.message);
+ runNextTest();
+ }
+ }
+
+ var testCaseIndex = -1;
+ var testCases = [
+ {
+ desc: "Test 1: cross origin child location.replace should NOT be blocked",
+ script: "window['crossOriginChildIframe'].location.replace(\"" + testDataUri + "\")",
+ shouldBeBlocked: false,
+ },
+ {
+ desc: "Test 2: cross origin child location.assign should be blocked",
+ script: "window['crossOriginChildIframe'].location.assign(\"" + testDataUri + "\")",
+ shouldBeBlocked: true,
+ },
+ {
+ desc: "Test 3: same origin child location.assign should NOT be blocked",
+ script: "window['sameOriginChildIframe'].location.assign(\"" + testDataUri + "\")",
+ shouldBeBlocked: false,
+ },
+ {
+ desc: "Test 4: cross origin child location.href should NOT be blocked",
+ script: "window['crossOriginChildIframe'].location.href = \"" + testDataUri + "\"",
+ shouldBeBlocked: false,
+ },
+ {
+ desc: "Test 5: cross origin child location.hash should be blocked",
+ script: "window['crossOriginChildIframe'].location.hash = 'wibble'",
+ shouldBeBlocked: true,
+ },
+ {
+ desc: "Test 6: same origin child location.hash should NOT be blocked",
+ script: "window['sameOriginChildIframe'].location.hash = 'wibble'",
+ shouldBeBlocked: false,
+ },
+ ];
+
+ function runNextTest() {
+ ++testCaseIndex;
+ if (testCaseIndex == testCases.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ runScriptNavigationTest(testCases[testCaseIndex]);
+ }
+
+ addLoadEvent(runNextTest);
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=785310">Mozilla Bug 785310</a>
+<p id="display"></p>
+<div id="content">
+Tests for Bug 785310
+</div>
+
+<iframe name="parentIframe" sandbox="allow-scripts allow-same-origin" srcdoc="<iframe name='sameOriginChildIframe'></iframe><iframe name='crossOriginChildIframe' sandbox='allow-scripts'></iframe>"</iframe>
+
+</body>
+</html>
diff --git a/docshell/test/iframesandbox/test_marquee_event_handlers.html b/docshell/test/iframesandbox/test_marquee_event_handlers.html
new file mode 100644
index 0000000000..80added8ab
--- /dev/null
+++ b/docshell/test/iframesandbox/test_marquee_event_handlers.html
@@ -0,0 +1,95 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1277475
+html5 sandboxed iframe should not run marquee attribute event handlers without allow-scripts
+-->
+<head>
+<meta charset="utf-8">
+<title>Test for Bug 1277475 - html5 sandboxed iframe should not run marquee attribute event handlers without allow-scripts</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1277475">Mozilla Bug 1277475</a>
+<p id="display"></p>
+<div id="content">Tests for Bug 1277475</div>
+
+<iframe id="if1" name="if1" src="file_marquee_event_handlers.html"
+ sandbox="allow-same-origin allow-forms allow-top-navigation allow-pointer-lock allow-orientation-lock allow-popups allow-modals allow-popups-to-escape-sandbox">
+</iframe>
+
+<iframe id="if2" name="if2" src="file_marquee_event_handlers.html"
+ sandbox="allow-scripts"></iframe>
+
+<script>
+ SimpleTest.waitForExplicitFinish();
+
+ var expectedMessages = new Set();
+ var numberOfMessagesExpected = 4;
+ var unexpectedMessages = new Set();
+
+ window.onmessage = function(event) {
+ info(event.data + " message received");
+ if (event.data.startsWith("if2") || event.data == "if1 chaser") {
+ expectedMessages.add(event.data);
+ if (expectedMessages.size == numberOfMessagesExpected) {
+ checkRecievedMessages();
+ }
+ } else {
+ unexpectedMessages.add(event.data);
+ }
+ };
+
+ function checkRecievedMessages() {
+ // Check the expected messages explicitly as a cross-check.
+ ok(expectedMessages.has("if1 chaser"),
+ "if1 chaser message should have been received");
+ ok(expectedMessages.has("if2 marquee onstart"),
+ "if2 marquee onstart should have run in iframe sandbox with allow-scripts");
+ ok(expectedMessages.has("if2 marquee onbounce"),
+ "if2 marquee onbounce should have run in iframe sandbox with allow-scripts");
+ ok(expectedMessages.has("if2 marquee onfinish"),
+ "if2 marquee onfinish should have run in iframe sandbox with allow-scripts");
+
+ unexpectedMessages.forEach(
+ (v) => {
+ ok(false, v + " should NOT have run in iframe sandbox without allow-scripts");
+ }
+ );
+
+ SimpleTest.finish();
+ }
+
+ // If things are working properly the attribute event handlers won't run on
+ // the marquee in if1, so add our own capturing listeners on its window, so we
+ // know when they have fired. (These will run as we are not sandboxed.)
+ var if1FiredEvents = new Set();
+ var if1NumberOfEventsExpected = 3;
+ var if1Win = document.getElementById("if1").contentWindow;
+ if1Win.addEventListener("start", () => { checkMarqueeEvent("start"); }, true);
+ if1Win.addEventListener("bounce", () => { checkMarqueeEvent("bounce"); }, true);
+ if1Win.addEventListener("finish", () => { checkMarqueeEvent("finish"); }, true);
+
+ function checkMarqueeEvent(eventType) {
+ info("if1 event " + eventType + " fired");
+ if1FiredEvents.add(eventType);
+ if (if1FiredEvents.size == if1NumberOfEventsExpected) {
+ // Only send the chasing message after a tick of the event loop to allow
+ // event handlers on the marquee to process.
+ SimpleTest.executeSoon(sendChasingMessage);
+ }
+ }
+
+ function sendChasingMessage() {
+ // Add our own message listener to if1's window and echo back a chasing
+ // message to make sure that any messages from incorrectly run marquee
+ // attribute event handlers should have arrived before it.
+ if1Win.addEventListener("message",
+ (e) => { if1Win.parent.postMessage(e.data, "*"); });
+ if1Win.postMessage("if1 chaser", "*");
+ info("if1 chaser message sent");
+ }
+</script>
+</body>
+</html>
diff --git a/docshell/test/iframesandbox/test_other_auxiliary_navigation_by_location.html b/docshell/test/iframesandbox/test_other_auxiliary_navigation_by_location.html
new file mode 100644
index 0000000000..3440878db7
--- /dev/null
+++ b/docshell/test/iframesandbox/test_other_auxiliary_navigation_by_location.html
@@ -0,0 +1,80 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=785310
+html5 sandboxed iframe should not be able to perform top navigation with scripts allowed
+-->
+<head>
+<meta charset="utf-8">
+<title>Test for Bug 785310 - iframe sandbox other auxiliary navigation by location tests</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+<script>
+ SimpleTest.waitForExplicitFinish();
+
+ function runScriptNavigationTest(testCase) {
+ window.onmessage = function(event) {
+ if (event.data != "otherWindow") {
+ ok(false, "event.data: got '" + event.data + "', expected 'otherWindow'");
+ }
+ ok(false, testCase.desc + " - auxiliary navigation was NOT blocked");
+ runNextTest();
+ };
+ try {
+ window.testIframe.eval(testCase.script);
+ } catch (e) {
+ ok(true, testCase.desc + " - " + e.message);
+ runNextTest();
+ }
+ }
+
+ var testCaseIndex = -1;
+ var testCases = [
+ {
+ desc: "Test 1: location.replace on auxiliary NOT opened by us should be blocked",
+ script: "parent.openedWindow.location.replace('file_other_auxiliary_navigation_by_location.html')",
+ },
+ {
+ desc: "Test 2: location.assign on auxiliary NOT opened by us should be blocked",
+ script: "parent.openedWindow.location.assign('file_other_auxiliary_navigation_by_location.html')",
+ },
+ {
+ desc: "Test 3: location.href on auxiliary NOT opened by us should be blocked",
+ script: "parent.openedWindow.location.href = 'file_other_auxiliary_navigation_by_location.html'",
+ },
+ {
+ desc: "Test 4: location.hash on auxiliary NOT opened by us should be blocked",
+ script: "parent.openedWindow.location.hash = 'wibble'",
+ },
+ ];
+
+ function runNextTest() {
+ ++testCaseIndex;
+ if (testCaseIndex == testCases.length) {
+ window.openedWindow.close();
+ SimpleTest.finish();
+ return;
+ }
+
+ runScriptNavigationTest(testCases[testCaseIndex]);
+ }
+
+ window.onmessage = runNextTest;
+
+ window.onload = function() {
+ window.openedWindow = window.open("file_other_auxiliary_navigation_by_location.html", "otherWindow");
+ };
+</script>
+
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=785310">Mozilla Bug 785310</a>
+<p id="display"></p>
+<div id="content">
+Tests for Bug 785310
+</div>
+
+<iframe name="testIframe" sandbox="allow-scripts allow-same-origin allow-top-navigation allow-popups"></iframe>
+</body>
+</html>
diff --git a/docshell/test/iframesandbox/test_our_auxiliary_navigation_by_location.html b/docshell/test/iframesandbox/test_our_auxiliary_navigation_by_location.html
new file mode 100644
index 0000000000..1719f566a5
--- /dev/null
+++ b/docshell/test/iframesandbox/test_our_auxiliary_navigation_by_location.html
@@ -0,0 +1,84 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=785310
+html5 sandboxed iframe should not be able to perform top navigation with scripts allowed
+-->
+<head>
+<meta charset="utf-8">
+<title>Test for Bug 785310 - iframe sandbox our auxiliary navigation by location tests</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+<script>
+ SimpleTest.waitForExplicitFinish();
+
+ function runScriptNavigationTest(testCase) {
+ window.onmessage = function(event) {
+ if (event.data != "ourWindow") {
+ ok(false, "event.data: got '" + event.data + "', expected 'ourWindow'");
+ }
+ ok(!testCase.shouldBeBlocked, testCase.desc + " - auxiliary navigation was NOT blocked");
+ runNextTest();
+ };
+ try {
+ SpecialPowers.wrap(window.testIframe).eval(testCase.script);
+ } catch (e) {
+ ok(testCase.shouldBeBlocked, testCase.desc + " - " + SpecialPowers.wrap(e).message);
+ runNextTest();
+ }
+ }
+
+ var testCaseIndex = -1;
+ var testCases = [
+ {
+ desc: "Test 1: location.replace on auxiliary opened by us should NOT be blocked",
+ script: "openedWindow.location.replace('file_our_auxiliary_navigation_by_location.html')",
+ shouldBeBlocked: false,
+ },
+ {
+ desc: "Test 2: location.assign on auxiliary opened by us should be blocked without allow-same-origin",
+ script: "openedWindow.location.assign('file_our_auxiliary_navigation_by_location.html')",
+ shouldBeBlocked: true,
+ },
+ {
+ desc: "Test 3: location.href on auxiliary opened by us should NOT be blocked",
+ script: "openedWindow.location.href = 'file_our_auxiliary_navigation_by_location.html'",
+ shouldBeBlocked: false,
+ },
+ {
+ desc: "Test 4: location.hash on auxiliary opened by us should be blocked without allow-same-origin",
+ script: "openedWindow.location.hash = 'wibble'",
+ shouldBeBlocked: true,
+ },
+ ];
+
+ function runNextTest() {
+ ++testCaseIndex;
+ if (testCaseIndex == testCases.length) {
+ SpecialPowers.wrap(window.testIframe).eval("openedWindow.close()");
+ SimpleTest.finish();
+ return;
+ }
+
+ runScriptNavigationTest(testCases[testCaseIndex]);
+ }
+
+ window.onmessage = runNextTest;
+
+ window.onload = function() {
+ SpecialPowers.wrap(window.testIframe).eval("var openedWindow = window.open('file_our_auxiliary_navigation_by_location.html', 'ourWindow')");
+ };
+</script>
+
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=785310">Mozilla Bug 785310</a>
+<p id="display"></p>
+<div id="content">
+Tests for Bug 785310
+</div>
+
+<iframe name="testIframe" sandbox="allow-scripts allow-popups"></iframe>
+</body>
+</html>
diff --git a/docshell/test/iframesandbox/test_parent_navigation_by_location.html b/docshell/test/iframesandbox/test_parent_navigation_by_location.html
new file mode 100644
index 0000000000..ac6977a3f3
--- /dev/null
+++ b/docshell/test/iframesandbox/test_parent_navigation_by_location.html
@@ -0,0 +1,75 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=785310
+html5 sandboxed iframe should not be able to perform top navigation with scripts allowed
+-->
+<head>
+<meta charset="utf-8">
+<title>Test for Bug 785310 - iframe sandbox parent navigation by location tests</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+<script>
+ SimpleTest.waitForExplicitFinish();
+
+ function runScriptNavigationTest(testCase) {
+ window.onmessage = function(event) {
+ if (event.data != "parentIframe") {
+ ok(false, "event.data: got '" + event.data + "', expected 'parentIframe'");
+ }
+ ok(false, testCase.desc + " - parent navigation was NOT blocked");
+ runNextTest();
+ };
+ try {
+ window.parentIframe.childIframe.eval(testCase.script);
+ } catch (e) {
+ ok(true, testCase.desc + " - " + e.message);
+ runNextTest();
+ }
+ }
+
+ var testCaseIndex = -1;
+ var testCases = [
+ {
+ desc: "Test 1: parent.location.replace should be blocked even when sandboxed with allow-same-origin allow-top-navigation",
+ script: "parent.location.replace('file_parent_navigation_by_location.html')",
+ },
+ {
+ desc: "Test 2: parent.location.assign should be blocked even when sandboxed with allow-same-origin allow-top-navigation",
+ script: "parent.location.assign('file_parent_navigation_by_location.html')",
+ },
+ {
+ desc: "Test 3: parent.location.href should be blocked even when sandboxed with allow-same-origin allow-top-navigation",
+ script: "parent.location.href = 'file_parent_navigation_by_location.html'",
+ },
+ {
+ desc: "Test 4: parent.location.hash should be blocked even when sandboxed with allow-same-origin allow-top-navigation",
+ script: "parent.location.hash = 'wibble'",
+ },
+ ];
+
+ function runNextTest() {
+ ++testCaseIndex;
+ if (testCaseIndex == testCases.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ runScriptNavigationTest(testCases[testCaseIndex]);
+ }
+
+ window.onmessage = runNextTest;
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=785310">Mozilla Bug 785310</a>
+<p id="display"></p>
+<div id="content">
+Tests for Bug 785310
+</div>
+
+<iframe name="parentIframe" src="file_parent_navigation_by_location.html"></iframe>
+
+</body>
+</html>
diff --git a/docshell/test/iframesandbox/test_sibling_navigation_by_location.html b/docshell/test/iframesandbox/test_sibling_navigation_by_location.html
new file mode 100644
index 0000000000..d7508d5748
--- /dev/null
+++ b/docshell/test/iframesandbox/test_sibling_navigation_by_location.html
@@ -0,0 +1,78 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=785310
+html5 sandboxed iframe should not be able to perform top navigation with scripts allowed
+-->
+<head>
+<meta charset="utf-8">
+<title>Test for Bug 785310 - iframe sandbox sibling navigation by location tests</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+<script>
+ SimpleTest.waitForExplicitFinish();
+
+ function runScriptNavigationTest(testCase) {
+ window.onmessage = function(event) {
+ if (event.data != "siblingIframe") {
+ ok(false, "event.data: got '" + event.data + "', expected 'siblingIframe'");
+ }
+
+ ok(false, testCase.desc + " - sibling navigation was NOT blocked");
+ runNextTest();
+ };
+
+ try {
+ window.testIframe.eval(testCase.script);
+ } catch (e) {
+ ok(true, testCase.desc + " - " + e.message);
+ runNextTest();
+ }
+ }
+
+ var testCaseIndex = -1;
+ var testCases = [
+ {
+ desc: "Test 1: sibling location.replace should be blocked even when sandboxed with allow-same-origin allow-top-navigation",
+ script: "parent['siblingIframe'].location.replace('file_sibling_navigation_by_location.html')",
+ },
+ {
+ desc: "Test 2: sibling location.assign should be blocked even when sandboxed with allow-same-origin allow-top-navigation",
+ script: "parent['siblingIframe'].location.assign('file_sibling_navigation_by_location.html')",
+ },
+ {
+ desc: "Test 3: sibling location.href should be blocked even when sandboxed with allow-same-origin allow-top-navigation",
+ script: "parent['siblingIframe'].location.href = 'file_sibling_navigation_by_location.html'",
+ },
+ {
+ desc: "Test 4: sibling location.hash should be blocked even when sandboxed with allow-same-origin allow-top-navigation",
+ script: "parent['siblingIframe'].location.hash = 'wibble'",
+ },
+ ];
+
+ function runNextTest() {
+ ++testCaseIndex;
+ if (testCaseIndex == testCases.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ runScriptNavigationTest(testCases[testCaseIndex]);
+ }
+
+ window.onmessage = runNextTest;
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=785310">Mozilla Bug 785310</a>
+<p id="display"></p>
+<div id="content">
+Tests for Bug 785310
+</div>
+
+<iframe name="testIframe" sandbox="allow-scripts allow-same-origin allow-top-navigation"></iframe>
+<iframe name="siblingIframe" src="file_sibling_navigation_by_location.html"></iframe>
+
+</body>
+</html>
diff --git a/docshell/test/iframesandbox/test_top_navigation_by_location.html b/docshell/test/iframesandbox/test_top_navigation_by_location.html
new file mode 100644
index 0000000000..248f854bbf
--- /dev/null
+++ b/docshell/test/iframesandbox/test_top_navigation_by_location.html
@@ -0,0 +1,167 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=785310
+html5 sandboxed iframe should not be able to perform top navigation with scripts allowed
+-->
+<head>
+<meta charset="utf-8">
+<title>Test for Bug 785310 - iframe sandbox top navigation by location tests</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+<script>
+ SimpleTest.waitForExplicitFinish();
+
+ var testWin;
+
+ function runScriptNavigationTest(testCase) {
+ window.onmessage = function(event) {
+ if (event.data != "newTop") {
+ ok(false, "event.data: got '" + event.data + "', expected 'newTop'");
+ }
+ ok(!testCase.shouldBeBlocked, testCase.desc + " - top navigation was NOT blocked");
+ runNextTest();
+ };
+ try {
+ SpecialPowers.wrap(testWin[testCase.iframeName]).eval(testCase.script);
+ } catch (e) {
+ ok(testCase.shouldBeBlocked, testCase.desc + " - " + SpecialPowers.wrap(e).message);
+ runNextTest();
+ }
+ }
+
+ var testCaseIndex = -1;
+ var testCases = [
+ {
+ desc: "Test 1: top.location.replace should be blocked when sandboxed without allow-top-navigation",
+ script: "top.location.replace('file_top_navigation_by_location.html')",
+ iframeName: "if1",
+ shouldBeBlocked: true,
+ },
+ {
+ desc: "Test 2: top.location.assign should be blocked when sandboxed without allow-top-navigation",
+ script: "top.location.assign('file_top_navigation_by_location.html')",
+ iframeName: "if1",
+ shouldBeBlocked: true,
+ },
+ {
+ desc: "Test 3: top.location.href should be blocked when sandboxed without allow-top-navigation",
+ script: "top.location.href = 'file_top_navigation_by_location.html'",
+ iframeName: "if1",
+ shouldBeBlocked: true,
+ },
+ {
+ desc: "Test 4: top.location.pathname should be blocked when sandboxed without allow-top-navigation",
+ script: "top.location.pathname = top.location.pathname",
+ iframeName: "if1",
+ shouldBeBlocked: true,
+ },
+ {
+ desc: "Test 5: top.location should be blocked when sandboxed without allow-top-navigation",
+ script: "top.location = 'file_top_navigation_by_location.html'",
+ iframeName: "if1",
+ shouldBeBlocked: true,
+ },
+ {
+ desc: "Test 6: top.location.hash should be blocked when sandboxed without allow-top-navigation",
+ script: "top.location.hash = 'wibble'",
+ iframeName: "if1",
+ shouldBeBlocked: true,
+ },
+ {
+ desc: "Test 7: top.location.replace should NOT be blocked when sandboxed with allow-same-origin allow-top-navigation",
+ script: "top.location.replace('file_top_navigation_by_location.html')",
+ iframeName: "if2",
+ shouldBeBlocked: false,
+ },
+ {
+ desc: "Test 8: top.location.assign should NOT be blocked when sandboxed with allow-same-origin allow-top-navigation",
+ script: "top.location.assign('file_top_navigation_by_location.html')",
+ iframeName: "if2",
+ shouldBeBlocked: false,
+ },
+ {
+ desc: "Test 9: top.location.href should NOT be blocked when sandboxed with allow-same-origin allow-top-navigation",
+ script: "top.location.href = 'file_top_navigation_by_location.html'",
+ iframeName: "if2",
+ shouldBeBlocked: false,
+ },
+ {
+ desc: "Test 10: top.location.pathname should NOT be blocked when sandboxed with allow-same-origin allow-top-navigation",
+ script: "top.location.pathname = top.location.pathname",
+ iframeName: "if2",
+ shouldBeBlocked: false,
+ },
+ {
+ desc: "Test 11: top.location should NOT be blocked when sandboxed with allow-same-origin allow-top-navigation",
+ script: "top.location = 'file_top_navigation_by_location.html'",
+ iframeName: "if2",
+ shouldBeBlocked: false,
+ },
+ {
+ desc: "Test 12: top.location.hash should NOT be blocked when sandboxed with allow-same-origin allow-top-navigation",
+ script: "top.location.hash = 'wibble'",
+ iframeName: "if2",
+ shouldBeBlocked: false,
+ },
+ {
+ desc: "Test 13: top.location.replace should NOT be blocked when sandboxed with allow-top-navigation, but without allow-same-origin",
+ script: "top.location.replace('file_top_navigation_by_location.html')",
+ iframeName: "if3",
+ shouldBeBlocked: false,
+ },
+ {
+ desc: "Test 14: top.location.assign should be blocked when sandboxed with allow-top-navigation, but without allow-same-origin",
+ script: "top.location.assign('file_top_navigation_by_location.html')",
+ iframeName: "if3",
+ shouldBeBlocked: true,
+ },
+ {
+ desc: "Test 15: top.location.href should NOT be blocked when sandboxed with allow-top-navigation, but without allow-same-origin",
+ script: "top.location.href = 'file_top_navigation_by_location.html'",
+ iframeName: "if3",
+ shouldBeBlocked: false,
+ },
+ {
+ desc: "Test 16: top.location.pathname should be blocked when sandboxed with allow-top-navigation, but without allow-same-origin",
+ script: "top.location.pathname = top.location.pathname",
+ iframeName: "if3",
+ shouldBeBlocked: true,
+ },
+ {
+ desc: "Test 17: top.location should NOT be blocked when sandboxed with allow-top-navigation, but without allow-same-origin",
+ script: "top.location = 'file_top_navigation_by_location.html'",
+ iframeName: "if3",
+ shouldBeBlocked: false,
+ },
+ {
+ desc: "Test 18: top.location.hash should be blocked when sandboxed with allow-top-navigation, but without allow-same-origin",
+ script: "top.location.hash = 'wibble'",
+ iframeName: "if3",
+ shouldBeBlocked: true,
+ },
+ ];
+
+ function runNextTest() {
+ ++testCaseIndex;
+ if (testCaseIndex == testCases.length) {
+ testWin.close();
+ SimpleTest.finish();
+ return;
+ }
+
+ runScriptNavigationTest(testCases[testCaseIndex]);
+ }
+
+ window.onmessage = runNextTest;
+ testWin = window.open("file_top_navigation_by_location.html", "newTop");
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=785310">Mozilla Bug 785310</a>
+<p id="display"></p>
+<div id="content">
+Tests for Bug 785310
+</div>
+</body>
+</html>
diff --git a/docshell/test/iframesandbox/test_top_navigation_by_location_exotic.html b/docshell/test/iframesandbox/test_top_navigation_by_location_exotic.html
new file mode 100644
index 0000000000..11b7c78699
--- /dev/null
+++ b/docshell/test/iframesandbox/test_top_navigation_by_location_exotic.html
@@ -0,0 +1,204 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=785310
+html5 sandboxed iframe should not be able to perform top navigation with scripts allowed
+-->
+<head>
+<meta charset="utf-8">
+<title>Test for Bug 785310 - iframe sandbox top navigation by location via exotic means tests</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+<script>
+ SimpleTest.waitForExplicitFinish();
+
+ var testWin;
+
+ function runScriptNavigationTest(testCase) {
+ window.onmessage = function(event) {
+ if (event.data.name != "newWindow") {
+ ok(false, "event.data.name: got '" + event.data.name + "', expected 'newWindow'");
+ }
+ var diag = "top navigation was " + (event.data.blocked ? "" : "NOT ") + "blocked";
+ ok((testCase.shouldBeBlocked == event.data.blocked), testCase.desc + " - " + diag);
+ runNextTest();
+ };
+ try {
+ testWin[testCase.iframeName].eval(testCase.script);
+ } catch (e) {
+ ok(testCase.shouldBeBlocked, testCase.desc + " - " + e.message);
+ runNextTest();
+ }
+ }
+
+ var testCaseIndex = -1;
+ var testCases = [
+ {
+ desc: "Test 1: location.replace.call(top.location, ...) should be blocked when sandboxed without allow-top-navigation",
+ script: "location.replace.call(top.location, 'file_top_navigation_by_location_exotic.html')",
+ iframeName: "if1",
+ shouldBeBlocked: true,
+ },
+ {
+ desc: "Test 2: location.replace.bind(top.location, ...) should be blocked when sandboxed without allow-top-navigation",
+ script: "location.replace.bind(top.location, 'file_top_navigation_by_location_exotic.html')()",
+ iframeName: "if1",
+ shouldBeBlocked: true,
+ },
+ {
+ desc: "Test 3: Function.bind.call(location.replace, top.location, ...) should be blocked when sandboxed without allow-top-navigation",
+ script: "Function.bind.call(location.replace, top.location, 'file_top_navigation_by_location_exotic.html')()",
+ iframeName: "if1",
+ shouldBeBlocked: true,
+ },
+ {
+ desc: "Test 4: location.replace.call(top.location, ...) should NOT be blocked when sandboxed with allow-top-navigation",
+ script: "location.replace.call(top.location, 'file_top_navigation_by_location_exotic.html')",
+ iframeName: "if2",
+ shouldBeBlocked: false,
+ },
+ {
+ desc: "Test 5: location.replace.bind(top.location, ...) should NOT be blocked when sandboxed with allow-top-navigation",
+ script: "location.replace.bind(top.location, 'file_top_navigation_by_location_exotic.html')()",
+ iframeName: "if2",
+ shouldBeBlocked: false,
+ },
+ {
+ desc: "Test 6: Function.bind.call(location.replace, top.location, ...) should NOT be blocked when sandboxed with allow-top-navigation",
+ script: "Function.bind.call(location.replace, top.location, 'file_top_navigation_by_location_exotic.html')()",
+ iframeName: "if2",
+ shouldBeBlocked: false,
+ },
+ {
+ desc: "Test 7: top.location.href, via setTimeout, should be blocked when sandboxed without allow-top-navigation",
+ script: "setTimeout(function() { try { top.location.href = 'file_top_navigation_by_location_exotic.html' } catch (e) { top.onBlock() } }, 0)",
+ iframeName: "if1",
+ shouldBeBlocked: true,
+ },
+ {
+ desc: "Test 8: top.location.href, via setTimeout, should NOT be blocked when sandboxed with allow-top-navigation",
+ script: "setTimeout(function() { try { top.location.href = 'file_top_navigation_by_location_exotic.html' } catch(e) { top.onBlock() } }, 0)",
+ iframeName: "if2",
+ shouldBeBlocked: false,
+ },
+ {
+ desc: "Test 9: top.location.href, via eval, should be blocked when sandboxed without allow-top-navigation",
+ script: "eval('top.location.href = \"file_top_navigation_by_location_exotic.html\"')",
+ iframeName: "if1",
+ shouldBeBlocked: true,
+ },
+ {
+ desc: "Test 10: top.location.href, via eval, should NOT be blocked when sandboxed with allow-top-navigation",
+ script: "eval('top.location.href = \"file_top_navigation_by_location_exotic.html\"')",
+ iframeName: "if2",
+ shouldBeBlocked: false,
+ },
+ {
+ desc: "Test 11: top.location.href, via anonymous function, should be blocked when sandboxed without allow-top-navigation",
+ script: "(function() { top.location.href = 'file_top_navigation_by_location_exotic.html' })()",
+ iframeName: "if1",
+ shouldBeBlocked: true,
+ },
+ {
+ desc: "Test 12: top.location.href, via anonymous function, should NOT be blocked when sandboxed with allow-top-navigation",
+ script: "(function() { top.location.href = 'file_top_navigation_by_location_exotic.html' })()",
+ iframeName: "if2",
+ shouldBeBlocked: false,
+ },
+ {
+ desc: "Test 13: top.location.href, via function inserted in top, should be blocked when sandboxed without allow-top-navigation",
+ script: "top.doTest = function() { top.location.href = 'file_top_navigation_by_location_exotic.html' }; top.doTest();",
+ iframeName: "if1",
+ shouldBeBlocked: true,
+ },
+ {
+ desc: "Test 14: top.location.href, via function inserted in top, should NOT be blocked when sandboxed with allow-top-navigation",
+ script: "top.doTest = function() { top.location.href = 'file_top_navigation_by_location_exotic.html' }; top.doTest();",
+ iframeName: "if2",
+ shouldBeBlocked: false,
+ },
+ {
+ desc: "Test 15: top.location.href, via function inserted in us by top, should NOT be blocked when sandboxed without allow-top-navigation",
+ script: "top.eval('window[\"if1\"].doTest = function() { top.location.href = \"file_top_navigation_by_location_exotic.html\" };'), doTest();",
+ iframeName: "if1",
+ shouldBeBlocked: false,
+ },
+ {
+ desc: "Test 16: top.location.href, via function inserted in top, should NOT be blocked when sandboxed with allow-top-navigation",
+ script: "top.eval('window[\"if2\"].doTest = function() { top.location.href = \"file_top_navigation_by_location_exotic.html\" };'), doTest();",
+ iframeName: "if2",
+ shouldBeBlocked: false,
+ },
+ {
+ desc: "Test 17: top.location.href, via function in top, should NOT be blocked when sandboxed without allow-top-navigation",
+ script: "top.setOwnHref()",
+ iframeName: "if1",
+ shouldBeBlocked: false,
+ },
+ {
+ desc: "Test 18: top.location.href, via function in top, should NOT be blocked when sandboxed with allow-top-navigation",
+ script: "top.setOwnHref()",
+ iframeName: "if2",
+ shouldBeBlocked: false,
+ },
+ {
+ desc: "Test 19: top.location.href, via eval in top, should NOT be blocked when sandboxed without allow-top-navigation",
+ script: "top.eval('location.href = \"file_top_navigation_by_location_exotic.html\"')",
+ iframeName: "if1",
+ shouldBeBlocked: false,
+ },
+ {
+ desc: "Test 20: top.location.href, via eval in top, should NOT be blocked when sandboxed with allow-top-navigation",
+ script: "top.eval('location.href = \"file_top_navigation_by_location_exotic.html\"')",
+ iframeName: "if2",
+ shouldBeBlocked: false,
+ },
+ {
+ desc: "Test 21: top.location.href, via eval in top calling us, should be blocked when sandboxed without allow-top-navigation",
+ script: "function doTest() { top.location.href = 'file_top_navigation_by_location_exotic.html' } top.eval('window[\"if1\"].doTest()');",
+ iframeName: "if1",
+ shouldBeBlocked: true,
+ },
+ {
+ desc: "Test 22: top.location.href, via eval in top calling us, should NOT be blocked when sandboxed with allow-top-navigation",
+ script: "function doTest() { top.location.href = 'file_top_navigation_by_location_exotic.html' } top.eval('window[\"if2\"].doTest()');",
+ iframeName: "if2",
+ shouldBeBlocked: false,
+ },
+ {
+ desc: "Test 23: top.location.href, via function bound to top, should be blocked when sandboxed without allow-top-navigation",
+ script: "(function() { top.location.href = 'file_top_navigation_by_location_exotic.html' }).bind(top)();",
+ iframeName: "if1",
+ shouldBeBlocked: true,
+ },
+ {
+ desc: "Test 24: top.location.href, via function bound to top, should NOT be blocked when sandboxed with allow-top-navigation",
+ script: "(function() { top.location.href = 'file_top_navigation_by_location_exotic.html' }).bind(top)();",
+ iframeName: "if2",
+ shouldBeBlocked: false,
+ },
+ ];
+
+ function runNextTest() {
+ ++testCaseIndex;
+ if (testCaseIndex == testCases.length) {
+ testWin.close();
+ SimpleTest.finish();
+ return;
+ }
+
+ runScriptNavigationTest(testCases[testCaseIndex]);
+ }
+
+ window.onmessage = runNextTest;
+ testWin = window.open("file_top_navigation_by_location_exotic.html", "newWindow");
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=785310">Mozilla Bug 785310</a>
+<p id="display"></p>
+<div id="content">
+Tests for Bug 785310
+</div>
+</body>
+</html>
diff --git a/docshell/test/iframesandbox/test_top_navigation_by_user_activation.html b/docshell/test/iframesandbox/test_top_navigation_by_user_activation.html
new file mode 100644
index 0000000000..b462c54d7b
--- /dev/null
+++ b/docshell/test/iframesandbox/test_top_navigation_by_user_activation.html
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1744321
+-->
+<head>
+<meta charset="utf-8">
+<title>Iframe sandbox top navigation by user activation</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+<script>
+function waitForMessage(aCallback) {
+ return new Promise((aResolve) => {
+ window.addEventListener("message", function listener(aEvent) {
+ aCallback(aEvent);
+ aResolve();
+ }, { once: true });
+ });
+}
+
+[
+ {
+ desc: "A same-origin iframe in sandbox with 'allow-top-navigation-by-user-activation' cannot navigate its top level page, if the navigation is not triggered by a user gesture",
+ sameOrigin: true,
+ userGesture: false,
+ },
+ {
+ desc: "A same-origin iframe in sandbox with 'allow-top-navigation-by-user-activation' can navigate its top level page, if the navigation is triggered by a user gesture",
+ sameOrigin: true,
+ userGesture: true,
+ },
+ {
+ desc: "A cross-origin iframe in sandbox with 'allow-top-navigation-by-user-activation' cannot navigate its top level page, if the navigation is not triggered by a user gesture",
+ sameOrigin: false,
+ userGesture: false,
+ },
+ {
+ desc: "A cross-origin iframe in sandbox with 'allow-top-navigation-by-user-activation' can navigate its top level page, if the navigation is triggered by a user gesture",
+ sameOrigin: false,
+ userGesture: true,
+ },
+].forEach(({desc, sameOrigin, userGesture}) => {
+ add_task(async function() {
+ info(`Test: ${desc}`);
+
+ let url = "file_top_navigation_by_user_activation.html";
+ if (sameOrigin) {
+ url = `${location.origin}/tests/docshell/test/iframesandbox/${url}`;
+ }
+
+ let promise = waitForMessage((e) => {
+ is(e.data, "READY", "Ready for test");
+ });
+ let testWin = window.open(url);
+ await promise;
+
+ promise = waitForMessage((e) => {
+ is(e.data, userGesture ? "NAVIGATED" : "BLOCKED", "Check the result");
+ });
+ testWin.postMessage(userGesture ? "CLICK" : "SCRIPT", "*");
+ await promise;
+ testWin.close();
+ });
+});
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1744321">Mozilla Bug 1744321</a>
+<p id="display"></p>
+<div id="content">
+Tests for Bug 1744321
+</div>
+</body>
+</html>
diff --git a/docshell/test/mochitest/bug1422334_redirect.html b/docshell/test/mochitest/bug1422334_redirect.html
new file mode 100644
index 0000000000..eec7fda2c7
--- /dev/null
+++ b/docshell/test/mochitest/bug1422334_redirect.html
@@ -0,0 +1,3 @@
+<html>
+ <body>You should never see this</body>
+</html>
diff --git a/docshell/test/mochitest/bug1422334_redirect.html^headers^ b/docshell/test/mochitest/bug1422334_redirect.html^headers^
new file mode 100644
index 0000000000..fbf2d1b745
--- /dev/null
+++ b/docshell/test/mochitest/bug1422334_redirect.html^headers^
@@ -0,0 +1,2 @@
+HTTP 302 Moved Temporarily
+Location: ../navigation/blank.html?x=y
diff --git a/docshell/test/mochitest/bug404548-subframe.html b/docshell/test/mochitest/bug404548-subframe.html
new file mode 100644
index 0000000000..9a248b40b3
--- /dev/null
+++ b/docshell/test/mochitest/bug404548-subframe.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<body onload="setTimeout(function() { window.location = 'bug404548-subframe_window.html'; }, 10)">
+<iframe srcdoc="<body onpagehide='var p = window.parent.opener; var e = window.frameElement; e.parentNode.removeChild(e); if (e.parentNode == null && e.contentWindow == null) { p.firstRemoved = true; }'>">
+</iframe>
+<iframe srcdoc="<body onpagehide='window.parent.opener.secondHidden = true;'>">
+</iframe>
diff --git a/docshell/test/mochitest/bug404548-subframe_window.html b/docshell/test/mochitest/bug404548-subframe_window.html
new file mode 100644
index 0000000000..82ea73ea83
--- /dev/null
+++ b/docshell/test/mochitest/bug404548-subframe_window.html
@@ -0,0 +1 @@
+<body onload='window.opener.finishTest()'>
diff --git a/docshell/test/mochitest/bug413310-post.sjs b/docshell/test/mochitest/bug413310-post.sjs
new file mode 100644
index 0000000000..f87937ab56
--- /dev/null
+++ b/docshell/test/mochitest/bug413310-post.sjs
@@ -0,0 +1,10 @@
+function handleRequest(request, response) {
+ response.setHeader("Content-Type", "text/html");
+ response.write(
+ "<body onload='window.parent.onloadCount++'>" +
+ request.method +
+ " " +
+ Date.now() +
+ "</body>"
+ );
+}
diff --git a/docshell/test/mochitest/bug413310-subframe.html b/docshell/test/mochitest/bug413310-subframe.html
new file mode 100644
index 0000000000..bcff1886fd
--- /dev/null
+++ b/docshell/test/mochitest/bug413310-subframe.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+ <body onload="window.parent.onloadCount++">
+ <form action="bug413310-post.sjs" method="POST">
+ </form>
+ </body>
+</html>
diff --git a/docshell/test/mochitest/bug529119-window.html b/docshell/test/mochitest/bug529119-window.html
new file mode 100644
index 0000000000..f1908835a7
--- /dev/null
+++ b/docshell/test/mochitest/bug529119-window.html
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>Test bug 529119, sub-window</title>
+<body onload="window.opener.windowLoaded();">
+</body>
+</html>
diff --git a/docshell/test/mochitest/bug530396-noref.sjs b/docshell/test/mochitest/bug530396-noref.sjs
new file mode 100644
index 0000000000..6a65882160
--- /dev/null
+++ b/docshell/test/mochitest/bug530396-noref.sjs
@@ -0,0 +1,22 @@
+function handleRequest(request, response) {
+ response.setHeader("Content-Type", "text/html");
+ response.setHeader("Cache-Control", "no-cache");
+ response.write("<body onload='");
+
+ if (!request.hasHeader("Referer")) {
+ response.write("window.parent.onloadCount++;");
+ }
+
+ if (request.queryString == "newwindow") {
+ response.write(
+ "if (window.opener) { window.opener.parent.onloadCount++; window.opener.parent.doNextStep(); }"
+ );
+ response.write("if (!window.opener) window.close();");
+ response.write("'>");
+ } else {
+ response.write("window.parent.doNextStep();'>");
+ }
+
+ response.write(request.method + " " + Date.now());
+ response.write("</body>");
+}
diff --git a/docshell/test/mochitest/bug530396-subframe.html b/docshell/test/mochitest/bug530396-subframe.html
new file mode 100644
index 0000000000..be81b9f144
--- /dev/null
+++ b/docshell/test/mochitest/bug530396-subframe.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+ <body onload="window.parent.onloadCount++">
+ <a href="bug530396-noref.sjs" rel="noreferrer foo" id="target1">bug530396-noref.sjs</a>
+ <a href="bug530396-noref.sjs?newwindow" rel="nofollow noreferrer" id="target2" target="newwindow">bug530396-noref.sjs with new window</a>
+ </body>
+</html>
diff --git a/docshell/test/mochitest/bug570341_recordevents.html b/docshell/test/mochitest/bug570341_recordevents.html
new file mode 100644
index 0000000000..45b04866ec
--- /dev/null
+++ b/docshell/test/mochitest/bug570341_recordevents.html
@@ -0,0 +1,21 @@
+<html>
+<head>
+<script>
+ var start = Date.now();
+ window._testing_js_start = Date.now();
+ window["_testing_js_after_" + document.readyState] = start;
+ document.addEventListener("DOMContentLoaded",
+ function() {
+ window._testing_evt_DOMContentLoaded = Date.now();
+ }, true);
+ document.addEventListener("readystatechange", function() {
+ window["_testing_evt_DOM_" + document.readyState] = Date.now();
+ }, true);
+ function recordLoad() {
+ window._testing_evt_load = Date.now();
+ }
+</script>
+</head>
+<body onload="recordLoad()">This document collects time
+for events related to the page load progress.</body>
+</html>
diff --git a/docshell/test/mochitest/bug668513_redirect.html b/docshell/test/mochitest/bug668513_redirect.html
new file mode 100644
index 0000000000..1b8f66c631
--- /dev/null
+++ b/docshell/test/mochitest/bug668513_redirect.html
@@ -0,0 +1 @@
+<html><body>This document is redirected to a blank document.</body></html>
diff --git a/docshell/test/mochitest/bug668513_redirect.html^headers^ b/docshell/test/mochitest/bug668513_redirect.html^headers^
new file mode 100644
index 0000000000..0e785833c6
--- /dev/null
+++ b/docshell/test/mochitest/bug668513_redirect.html^headers^
@@ -0,0 +1,2 @@
+HTTP 302 Moved Temporarily
+Location: navigation/blank.html
diff --git a/docshell/test/mochitest/bug691547_frame.html b/docshell/test/mochitest/bug691547_frame.html
new file mode 100644
index 0000000000..00172f7119
--- /dev/null
+++ b/docshell/test/mochitest/bug691547_frame.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=691547
+-->
+<head>
+ <title>Test for Bug 691547</title>
+</head>
+<body>
+<iframe style="width:95%"></iframe>
+</body>
+</html>
diff --git a/docshell/test/mochitest/clicker.html b/docshell/test/mochitest/clicker.html
new file mode 100644
index 0000000000..b655e27ea5
--- /dev/null
+++ b/docshell/test/mochitest/clicker.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<script>
+ "use strict";
+ let target = window.opener ? window.opener : window.parent;
+
+ onmessage = ({data}) => target.postMessage({}, "*");
+</script>
diff --git a/docshell/test/mochitest/double_submit.sjs b/docshell/test/mochitest/double_submit.sjs
new file mode 100644
index 0000000000..487150d456
--- /dev/null
+++ b/docshell/test/mochitest/double_submit.sjs
@@ -0,0 +1,79 @@
+"use strict";
+
+let self = this;
+
+let { setTimeout } = ChromeUtils.importESModule(
+ "resource://gre/modules/Timer.sys.mjs"
+);
+
+const CC = Components.Constructor;
+const BinaryInputStream = CC(
+ "@mozilla.org/binaryinputstream;1",
+ "nsIBinaryInputStream",
+ "setInputStream"
+);
+
+function log(str) {
+ // dump(`LOG: ${str}\n`);
+}
+
+function readStream(inputStream) {
+ let available = 0;
+ let result = [];
+ while ((available = inputStream.available()) > 0) {
+ result.push(inputStream.readBytes(available));
+ }
+
+ return result.join("");
+}
+
+function now() {
+ return Date.now();
+}
+
+async function handleRequest(request, response) {
+ log("Get query parameters");
+ Cu.importGlobalProperties(["URLSearchParams"]);
+ let params = new URLSearchParams(request.queryString);
+
+ let start = now();
+ let delay = parseInt(params.get("delay")) || 0;
+ log(`Delay for ${delay}`);
+
+ let message = "good";
+ if (request.method !== "POST") {
+ message = "bad";
+ } else {
+ log("Read POST body");
+ let body = new URLSearchParams(
+ readStream(new BinaryInputStream(request.bodyInputStream))
+ );
+ message = body.get("token") || "bad";
+ log(`The result was ${message}`);
+ }
+
+ let body = `<!doctype html>
+ <script>
+ "use strict";
+ let target = (opener || parent);
+ target.postMessage(${JSON.stringify(message)}, '*');
+ </script>`;
+
+ // Sieze power from the response to allow manually transmitting data at any
+ // rate we want, so we can delay transmitting headers.
+ response.seizePower();
+
+ log(`Writing HTTP status line at ${now() - start}`);
+ response.write("HTTP/1.1 200 OK\r\n");
+
+ await new Promise(resolve => setTimeout(() => resolve(), delay));
+
+ log(`Delay completed at ${now() - start}`);
+ response.write("Content-Type: text/html\r\n");
+ response.write(`Content-Length: ${body.length}\r\n`);
+ response.write("\r\n");
+ response.write(body);
+ response.finish();
+
+ log("Finished");
+}
diff --git a/docshell/test/mochitest/dummy_page.html b/docshell/test/mochitest/dummy_page.html
new file mode 100644
index 0000000000..59bf2a5f8f
--- /dev/null
+++ b/docshell/test/mochitest/dummy_page.html
@@ -0,0 +1,6 @@
+<html>
+<head> <meta charset="utf-8"> </head>
+ <body>
+ just a dummy html file
+ </body>
+</html>
diff --git a/docshell/test/mochitest/file_anchor_scroll_after_document_open.html b/docshell/test/mochitest/file_anchor_scroll_after_document_open.html
new file mode 100644
index 0000000000..7903380eac
--- /dev/null
+++ b/docshell/test/mochitest/file_anchor_scroll_after_document_open.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<script>
+ if (location.hash == "#target") {
+ parent.postMessage("haveHash", "*");
+ } else {
+ document.addEventListener("DOMContentLoaded", function() {
+ document.open();
+ document.write("<!DOCTYPE html><html style='height: 100%'><body style='height: 100%'><div style='height: 200%'></div><div id='target'></div></body></html>");
+ document.close();
+ // Notify parent via postMessage, since otherwise exceptions will not get
+ // caught by its onerror handler.
+ parent.postMessage("doTest", "*");
+ });
+ }
+</script>
diff --git a/docshell/test/mochitest/file_bfcache_plus_hash_1.html b/docshell/test/mochitest/file_bfcache_plus_hash_1.html
new file mode 100644
index 0000000000..199f6003e0
--- /dev/null
+++ b/docshell/test/mochitest/file_bfcache_plus_hash_1.html
@@ -0,0 +1,24 @@
+<html><body>
+ Popup 1
+ <script type="application/javascript">
+ var bc = new BroadcastChannel("bug646641_1");
+ window.onload = () => {
+ bc.postMessage({ message: "childLoad", num: 1 })
+ }
+
+ window.onpageshow = () => {
+ bc.postMessage({ message: "childPageshow", num: 1 })
+ }
+ bc.onmessage = (msgEvent) => {
+ var msg = msgEvent.data;
+ if (msg == "pushState") {
+ history.pushState("", "", "");
+ location = "file_bfcache_plus_hash_2.html";
+ } else if (msg == "close") {
+ bc.postMessage({ message: "closed" });
+ bc.close();
+ window.close();
+ }
+ }
+ </script>
+</body></html>
diff --git a/docshell/test/mochitest/file_bfcache_plus_hash_2.html b/docshell/test/mochitest/file_bfcache_plus_hash_2.html
new file mode 100644
index 0000000000..c27d4eaa3b
--- /dev/null
+++ b/docshell/test/mochitest/file_bfcache_plus_hash_2.html
@@ -0,0 +1,17 @@
+<html><body>
+ Popup 2
+ <script type="application/javascript">
+ var bc = new BroadcastChannel("bug646641_2");
+ window.onload = () => {
+ bc.postMessage({ message: "childLoad", num: 2 })
+ requestAnimationFrame(() => bc.postMessage({message: "childPageshow", num: 2}));
+ }
+ bc.onmessage = (msgEvent) => {
+ var msg = msgEvent.data;
+ if (msg == "go-2") {
+ history.go(-2);
+ bc.close();
+ }
+ }
+ </script>
+</body></html>
diff --git a/docshell/test/mochitest/file_bug1121701_1.html b/docshell/test/mochitest/file_bug1121701_1.html
new file mode 100644
index 0000000000..62c58495f7
--- /dev/null
+++ b/docshell/test/mochitest/file_bug1121701_1.html
@@ -0,0 +1,29 @@
+<script>
+ var bc = new BroadcastChannel("file_bug1121701_1");
+ var pageHideAsserts = undefined;
+ bc.onmessage = (msgEvent) => {
+ var msg = msgEvent.data;
+ var command = msg.command;
+ if (command == "setInnerHTML") {
+ document.body.innerHTML = "modified";
+ window.onpagehide = function(event) {
+ window.onpagehide = null;
+ pageHideAsserts = {};
+ pageHideAsserts.persisted = event.persisted;
+ // eslint-disable-next-line no-unsanitized/property
+ pageHideAsserts.innerHTML = window.document.body.innerHTML;
+ };
+ window.location.href = msg.testUrl2;
+ } else if (command == "close") {
+ bc.postMessage({command: "closed"});
+ bc.close();
+ window.close();
+ }
+ }
+ window.onpageshow = function(e) {
+ var msg = {command: "child1PageShow", persisted: e.persisted, pageHideAsserts};
+ // eslint-disable-next-line no-unsanitized/property
+ msg.innerHTML = window.document.body.innerHTML;
+ bc.postMessage(msg);
+ };
+</script>
diff --git a/docshell/test/mochitest/file_bug1121701_2.html b/docshell/test/mochitest/file_bug1121701_2.html
new file mode 100644
index 0000000000..6cec88cd5d
--- /dev/null
+++ b/docshell/test/mochitest/file_bug1121701_2.html
@@ -0,0 +1,23 @@
+<script>
+ var bc = new BroadcastChannel("file_bug1121701_2");
+ bc.onmessage = (msgEvent) => {
+ var msg = msgEvent.data;
+ var command = msg.command;
+ if (command == "setInnerHTML") {
+ window.document.body.innerHTML = "<img>";
+ window.onmessage = function() {
+ bc.postMessage({command: "onmessage"});
+ window.document.body.firstChild.src = msg.location;
+ bc.close();
+ };
+ window.onbeforeunload = function() {
+ window.postMessage("foo", "*");
+ };
+
+ history.back();
+ }
+ }
+ window.onpageshow = function(e) {
+ bc.postMessage({command: "child2PageShow", persisted: e.persisted});
+ };
+</script>
diff --git a/docshell/test/mochitest/file_bug1151421.html b/docshell/test/mochitest/file_bug1151421.html
new file mode 100644
index 0000000000..7bb8c8f363
--- /dev/null
+++ b/docshell/test/mochitest/file_bug1151421.html
@@ -0,0 +1,19 @@
+<html>
+<head>
+<style>
+body, html {
+ height: 100%;
+}
+.spacer {
+ height: 80%;
+}
+</style>
+</head>
+<body onload='(parent || opener).childLoad()'>
+
+<div class="spacer"></div>
+<div id="content">content</div>
+<div class="spacer"></div>
+
+</body>
+</html>
diff --git a/docshell/test/mochitest/file_bug1186774.html b/docshell/test/mochitest/file_bug1186774.html
new file mode 100644
index 0000000000..9af95b09bd
--- /dev/null
+++ b/docshell/test/mochitest/file_bug1186774.html
@@ -0,0 +1 @@
+<div style='height: 9000px;'></div>
diff --git a/docshell/test/mochitest/file_bug1450164.html b/docshell/test/mochitest/file_bug1450164.html
new file mode 100644
index 0000000000..55e32ce93d
--- /dev/null
+++ b/docshell/test/mochitest/file_bug1450164.html
@@ -0,0 +1,16 @@
+<html>
+ <head>
+ <script>
+ function go() {
+ var a = window.history.state;
+ window.history.replaceState(a, "", "1");
+ var ok = opener.ok;
+ var SimpleTest = opener.SimpleTest;
+ ok("Addition of history in unload did not crash browser");
+ SimpleTest.finish();
+ }
+ </script>
+ </head>
+ <body onunload="go()">
+ </body>
+</html>
diff --git a/docshell/test/mochitest/file_bug1729662.html b/docshell/test/mochitest/file_bug1729662.html
new file mode 100644
index 0000000000..f5710e1c49
--- /dev/null
+++ b/docshell/test/mochitest/file_bug1729662.html
@@ -0,0 +1,8 @@
+<script>
+addEventListener("load", () => {
+ (new BroadcastChannel("bug1729662")).postMessage("load");
+ history.pushState(1, null, location.href);
+ history.back();
+ history.forward();
+});
+</script>
diff --git a/docshell/test/mochitest/file_bug1740516_1.html b/docshell/test/mochitest/file_bug1740516_1.html
new file mode 100644
index 0000000000..ac8ca71d93
--- /dev/null
+++ b/docshell/test/mochitest/file_bug1740516_1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <script>
+ window.addEventListener("pageshow", ({ persisted }) => {
+ let bc = new BroadcastChannel("bug1740516_1");
+ bc.addEventListener("message", ({ data }) => {
+ bc.close();
+ switch (data) {
+ case "block_bfcache_and_navigate":
+ window.blockBFCache = new RTCPeerConnection();
+ // Fall through
+ case "navigate":
+ document.location = "file_bug1740516_2.html";
+ break;
+ case "close":
+ window.close();
+ break;
+ }
+ });
+ bc.postMessage(persisted);
+ });
+ </script>
+</head>
+<body>
+ <iframe src="file_bug1740516_1_inner.html"></iframe>
+</body>
+</html>
diff --git a/docshell/test/mochitest/file_bug1740516_1_inner.html b/docshell/test/mochitest/file_bug1740516_1_inner.html
new file mode 100644
index 0000000000..159c6bde5a
--- /dev/null
+++ b/docshell/test/mochitest/file_bug1740516_1_inner.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <script>
+ window.addEventListener("pageshow", ({ persisted }) => {
+ let bc = new BroadcastChannel("bug1740516_1_inner");
+ bc.postMessage(persisted);
+ bc.close();
+ });
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/docshell/test/mochitest/file_bug1740516_2.html b/docshell/test/mochitest/file_bug1740516_2.html
new file mode 100644
index 0000000000..2dc714feef
--- /dev/null
+++ b/docshell/test/mochitest/file_bug1740516_2.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <script>
+ addEventListener("pageshow", () => { history.back(); });
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/docshell/test/mochitest/file_bug1741132.html b/docshell/test/mochitest/file_bug1741132.html
new file mode 100644
index 0000000000..d863b9f015
--- /dev/null
+++ b/docshell/test/mochitest/file_bug1741132.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <script>
+ window.addEventListener("pageshow", ({ persisted }) => {
+ let bc = new BroadcastChannel("bug1741132");
+ bc.addEventListener("message", ({ data: { cmd, arg } }) => {
+ bc.close();
+ switch (cmd) {
+ case "load":
+ document.location = arg;
+ break;
+ case "go":
+ window.blockBFCache = new RTCPeerConnection();
+ history.go(arg);
+ break;
+ case "close":
+ window.close();
+ break;
+ }
+ });
+ bc.postMessage(persisted);
+ });
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/docshell/test/mochitest/file_bug1742865.sjs b/docshell/test/mochitest/file_bug1742865.sjs
new file mode 100644
index 0000000000..950c30ecd2
--- /dev/null
+++ b/docshell/test/mochitest/file_bug1742865.sjs
@@ -0,0 +1,77 @@
+Cu.importGlobalProperties(["URLSearchParams"]);
+
+function handleRequest(request, response) {
+ if (request.queryString == "reset") {
+ setState("index", "0");
+ response.setStatusLine(request.httpVersion, 200, "Ok");
+ response.write("Reset");
+ return;
+ }
+
+ let refresh = "";
+ let index = Number(getState("index"));
+ // index == 0 First load, returns first meta refresh
+ // index == 1 Second load, caused by first meta refresh, returns second meta refresh
+ // index == 2 Third load, caused by second meta refresh, doesn't return a meta refresh
+ let query = new URLSearchParams(request.queryString);
+ if (index < 2) {
+ refresh = query.get("seconds");
+ if (query.get("crossOrigin") == "true") {
+ const hosts = ["example.org", "example.com"];
+
+ let url = `${request.scheme}://${hosts[index]}${request.path}?${request.queryString}`;
+ refresh += `; url=${url}`;
+ }
+ refresh = `<meta http-equiv="Refresh" content="${refresh}">`;
+ }
+ // We want to scroll for the first load, and check that the meta refreshes keep the same
+ // scroll position.
+ let scroll = index == 0 ? `scrollTo(0, ${query.get("scrollTo")});` : "";
+
+ setState("index", String(index + 1));
+
+ response.write(
+ `<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="Cache-Control" content="no-cache">
+ ${refresh}
+ <script>
+ window.addEventListener("pageshow", () => {
+ ${scroll}
+ window.top.opener.postMessage({
+ commandType: "pageShow",
+ commandData: {
+ inputValue: document.getElementById("input").value,
+ scrollPosition: window.scrollY,
+ },
+ }, "*");
+ });
+ window.addEventListener("message", ({ data }) => {
+ if (data == "changeInputValue") {
+ document.getElementById("input").value = "1234";
+ window.top.opener.postMessage({
+ commandType: "onChangedInputValue",
+ commandData: {
+ historyLength: history.length,
+ inputValue: document.getElementById("input").value,
+ },
+ }, "*");
+ } else if (data == "loadNext") {
+ location.href += "&loadnext=1";
+ } else if (data == "back") {
+ history.back();
+ }
+ });
+ </script>
+</head>
+<body>
+<input type="text" id="input" value="initial"></input>
+<div style='height: 9000px;'></div>
+<p>
+</p>
+</body>
+</html>`
+ );
+}
diff --git a/docshell/test/mochitest/file_bug1742865_outer.sjs b/docshell/test/mochitest/file_bug1742865_outer.sjs
new file mode 100644
index 0000000000..bad8b23f00
--- /dev/null
+++ b/docshell/test/mochitest/file_bug1742865_outer.sjs
@@ -0,0 +1,25 @@
+Cu.importGlobalProperties(["URLSearchParams"]);
+
+function handleRequest(request, response) {
+ response.write(
+ `<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <script>
+ window.addEventListener("message", ({ data }) => {
+ if (data == "loadNext") {
+ location.href += "&loadnext=1";
+ return;
+ }
+ // Forward other messages to the frame.
+ document.getElementById("frame").contentWindow.postMessage(data, "*");
+ });
+ </script>
+</head>
+<body>
+ <iframe src="file_bug1742865.sjs?${request.queryString}" id="frame"></iframe>
+</body>
+</html>`
+ );
+}
diff --git a/docshell/test/mochitest/file_bug1743353.html b/docshell/test/mochitest/file_bug1743353.html
new file mode 100644
index 0000000000..c08f8d143f
--- /dev/null
+++ b/docshell/test/mochitest/file_bug1743353.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <script>
+ window.addEventListener("pageshow", () => {
+ let bc = new BroadcastChannel("bug1743353");
+ bc.addEventListener("message", ({ data: cmd }) => {
+ switch (cmd) {
+ case "load":
+ bc.close();
+ document.location += "?1";
+ break;
+ case "back":
+ window.blockBFCache = new RTCPeerConnection();
+ window.addEventListener("pagehide", () => {
+ bc.postMessage("pagehide");
+ });
+ window.addEventListener("unload", () => {
+ bc.postMessage("unload");
+ bc.close();
+ });
+ history.back();
+ break;
+ case "close":
+ bc.close();
+ window.close();
+ break;
+ }
+ });
+ bc.postMessage("pageshow");
+ });
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/docshell/test/mochitest/file_bug1747033.sjs b/docshell/test/mochitest/file_bug1747033.sjs
new file mode 100644
index 0000000000..14401101b2
--- /dev/null
+++ b/docshell/test/mochitest/file_bug1747033.sjs
@@ -0,0 +1,110 @@
+"use strict";
+
+const BOUNDARY = "BOUNDARY";
+
+// waitForPageShow should be false if this is for multipart/x-mixed-replace
+// and it's not the last part, Gecko doesn't fire pageshow for those parts.
+function documentString(waitForPageShow = true) {
+ return `<html>
+ <head>
+ <script>
+ let bc = new BroadcastChannel("bug1747033");
+ bc.addEventListener("message", ({ data: { cmd, arg = undefined } }) => {
+ switch (cmd) {
+ case "load":
+ location.href = arg;
+ break;
+ case "replaceState":
+ history.replaceState({}, "Replaced state", arg);
+ bc.postMessage({ "historyLength": history.length, "location": location.href });
+ break;
+ case "back":
+ history.back();
+ break;
+ case "close":
+ close();
+ break;
+ }
+ });
+
+ function reply() {
+ bc.postMessage({ "historyLength": history.length, "location": location.href });
+ }
+
+ ${waitForPageShow ? `addEventListener("pageshow", reply);` : "reply();"}
+ </script>
+ </head>
+ <body></body>
+</html>
+`;
+}
+
+function boundary(last = false) {
+ let b = `--${BOUNDARY}`;
+ if (last) {
+ b += "--";
+ }
+ return b + "\n";
+}
+
+function sendMultipart(response, last = false) {
+ setState("sendMore", "");
+
+ response.write(`Content-Type: text/html
+
+${documentString(last)}
+`);
+ response.write(boundary(last));
+}
+
+function shouldSendMore() {
+ return new Promise(resolve => {
+ let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ timer.initWithCallback(
+ () => {
+ let sendMore = getState("sendMore");
+ if (sendMore !== "") {
+ timer.cancel();
+ resolve(sendMore);
+ }
+ },
+ 100,
+ Ci.nsITimer.TYPE_REPEATING_SLACK
+ );
+ });
+}
+
+async function handleRequest(request, response) {
+ if (request.queryString == "") {
+ // This is for non-multipart/x-mixed-replace loads.
+ response.write(documentString());
+ return;
+ }
+
+ if (request.queryString == "sendNextPart") {
+ setState("sendMore", "next");
+ return;
+ }
+
+ if (request.queryString == "sendLastPart") {
+ setState("sendMore", "last");
+ return;
+ }
+
+ response.processAsync();
+
+ response.setHeader(
+ "Content-Type",
+ `multipart/x-mixed-replace; boundary=${BOUNDARY}`,
+ false
+ );
+ response.setStatusLine(request.httpVersion, 200, "OK");
+
+ response.write(boundary());
+ sendMultipart(response);
+ while ((await shouldSendMore("sendMore")) !== "last") {
+ sendMultipart(response);
+ }
+ sendMultipart(response, true);
+ response.finish();
+}
diff --git a/docshell/test/mochitest/file_bug1773192_1.html b/docshell/test/mochitest/file_bug1773192_1.html
new file mode 100644
index 0000000000..42d5b3fced
--- /dev/null
+++ b/docshell/test/mochitest/file_bug1773192_1.html
@@ -0,0 +1,13 @@
+<script>
+ let bc = new BroadcastChannel("bug1743353");
+ bc.addEventListener("message", ({ data }) => {
+ if (data == "next") {
+ location = "file_bug1773192_2.html";
+ } else if (data == "close") {
+ window.close();
+ }
+ });
+ window.addEventListener("pageshow", () => {
+ bc.postMessage({ location: location.href, referrer: document.referrer });
+ });
+</script>
diff --git a/docshell/test/mochitest/file_bug1773192_2.html b/docshell/test/mochitest/file_bug1773192_2.html
new file mode 100644
index 0000000000..3b1e5bcb28
--- /dev/null
+++ b/docshell/test/mochitest/file_bug1773192_2.html
@@ -0,0 +1,13 @@
+<html>
+ <head>
+ <meta http-equiv="Cache-Control" content="no-cache, no-store">
+ <meta name="referrer" content="same-origin">
+ </head>
+ <body>
+ <form method="POST" action="file_bug1773192_3.sjs"></form>
+ <script>
+ history.replaceState({}, "", document.referrer);
+ document.forms[0].submit();
+ </script>
+ </body>
+</html>
diff --git a/docshell/test/mochitest/file_bug1773192_3.sjs b/docshell/test/mochitest/file_bug1773192_3.sjs
new file mode 100644
index 0000000000..ce889c7035
--- /dev/null
+++ b/docshell/test/mochitest/file_bug1773192_3.sjs
@@ -0,0 +1,3 @@
+function handleRequest(request, response) {
+ response.write("<script>history.back();</script>");
+}
diff --git a/docshell/test/mochitest/file_bug385434_1.html b/docshell/test/mochitest/file_bug385434_1.html
new file mode 100644
index 0000000000..5c951f1fa6
--- /dev/null
+++ b/docshell/test/mochitest/file_bug385434_1.html
@@ -0,0 +1,29 @@
+<!--
+Inner frame for test of bug 385434.
+https://bugzilla.mozilla.org/show_bug.cgi?id=385434
+-->
+<html>
+<head>
+ <script type="application/javascript">
+ function hashchange() {
+ parent.onIframeHashchange();
+ }
+
+ function load() {
+ parent.onIframeLoad();
+ }
+
+ function scroll() {
+ parent.onIframeScroll();
+ }
+ </script>
+</head>
+
+<body onscroll="scroll()" onload="load()" onhashchange="hashchange()">
+<a href="#link1" id="link1">link1</a>
+<!-- Our parent loads us in an iframe with height 100px, so this spacer ensures
+ that switching between #link1 and #link2 causes us to scroll -->
+<div style="height:200px;"></div>
+<a href="#link2" id="link2">link2</a>
+</body>
+</html>
diff --git a/docshell/test/mochitest/file_bug385434_2.html b/docshell/test/mochitest/file_bug385434_2.html
new file mode 100644
index 0000000000..4aa5ef82b8
--- /dev/null
+++ b/docshell/test/mochitest/file_bug385434_2.html
@@ -0,0 +1,26 @@
+<!--
+Inner frame for test of bug 385434.
+https://bugzilla.mozilla.org/show_bug.cgi?id=385434
+-->
+<html>
+<head>
+ <script type="application/javascript">
+ function hashchange(e) {
+ // pass the event back to the parent so it can check its properties.
+ parent.gSampleEvent = e;
+
+ parent.statusMsg("Hashchange in 2.");
+ parent.onIframeHashchange();
+ }
+
+ function load() {
+ parent.statusMsg("Loading 2.");
+ parent.onIframeLoad();
+ }
+ </script>
+</head>
+
+<frameset onload="load()" onhashchange="hashchange(event)">
+ <frame src="about:blank" />
+</frameset>
+</html>
diff --git a/docshell/test/mochitest/file_bug385434_3.html b/docshell/test/mochitest/file_bug385434_3.html
new file mode 100644
index 0000000000..34dd68ef45
--- /dev/null
+++ b/docshell/test/mochitest/file_bug385434_3.html
@@ -0,0 +1,22 @@
+<!--
+Inner frame for test of bug 385434.
+https://bugzilla.mozilla.org/show_bug.cgi?id=385434
+-->
+<html>
+<head>
+ <script type="application/javascript">
+ // Notify our parent if we have a hashchange and once we're done loading.
+ window.addEventListener("hashchange", parent.onIframeHashchange);
+
+ window.addEventListener("DOMContentLoaded", function() {
+ // This also should trigger a hashchange, becuase the readystate is
+ // "interactive", not "complete" during DOMContentLoaded.
+ window.location.hash = "2";
+ });
+
+ </script>
+</head>
+
+<body>
+</body>
+</html>
diff --git a/docshell/test/mochitest/file_bug475636.sjs b/docshell/test/mochitest/file_bug475636.sjs
new file mode 100644
index 0000000000..ee2207b0e5
--- /dev/null
+++ b/docshell/test/mochitest/file_bug475636.sjs
@@ -0,0 +1,98 @@
+let jsURL =
+ "javascript:" +
+ escape(
+ 'window.parent.postMessage("JS uri ran", "*");\
+return \'\
+<script>\
+window.parent.postMessage("Able to access private: " +\
+ window.parent.private, "*");\
+</script>\''
+ );
+let dataURL =
+ "data:text/html," +
+ escape(
+ '<!DOCTYPE HTML>\
+<script>\
+try {\
+ window.parent.postMessage("Able to access private: " +\
+ window.parent.private, "*");\
+}\
+catch (e) {\
+ window.parent.postMessage("pass", "*");\
+}\
+</script>'
+ );
+
+let tests = [
+ // Plain document should work as normal
+ '<!DOCTYPE HTML>\
+<script>\
+try {\
+ window.parent.private;\
+ window.parent.postMessage("pass", "*");\
+}\
+catch (e) {\
+ window.parent.postMessage("Unble to access private", "*");\
+}\
+</script>',
+
+ // refresh to plain doc
+ { refresh: "file_bug475636.sjs?1", doc: "<!DOCTYPE HTML>" },
+
+ // meta-refresh to plain doc
+ '<!DOCTYPE HTML>\
+<head>\
+ <meta http-equiv="refresh" content="0; url=file_bug475636.sjs?1">\
+</head>',
+
+ // refresh to data url
+ { refresh: dataURL, doc: "<!DOCTYPE HTML>" },
+
+ // meta-refresh to data url
+ '<!DOCTYPE HTML>\
+<head>\
+ <meta http-equiv="refresh" content="0; url=' +
+ dataURL +
+ '">\
+</head>',
+
+ // refresh to js url should not be followed
+ {
+ refresh: jsURL,
+ doc:
+ '<!DOCTYPE HTML>\
+<script>\
+setTimeout(function() {\
+ window.parent.postMessage("pass", "*");\
+}, 2000);\
+</script>',
+ },
+
+ // meta refresh to js url should not be followed
+ '<!DOCTYPE HTML>\
+<head>\
+ <meta http-equiv="refresh" content="0; url=' +
+ jsURL +
+ '">\
+</head>\
+<script>\
+setTimeout(function() {\
+ window.parent.postMessage("pass", "*");\
+}, 2000);\
+</script>',
+];
+
+function handleRequest(request, response) {
+ dump("@@@@@@@@@hi there: " + request.queryString + "\n");
+ let test = tests[parseInt(request.queryString, 10) - 1];
+ response.setHeader("Content-Type", "text/html");
+
+ if (!test) {
+ response.write('<script>parent.postMessage("done", "*");</script>');
+ } else if (typeof test == "string") {
+ response.write(test);
+ } else if (test.refresh) {
+ response.setHeader("Refresh", "0; url=" + test.refresh);
+ response.write(test.doc);
+ }
+}
diff --git a/docshell/test/mochitest/file_bug509055.html b/docshell/test/mochitest/file_bug509055.html
new file mode 100644
index 0000000000..ac30876bbf
--- /dev/null
+++ b/docshell/test/mochitest/file_bug509055.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test inner frame for bug 509055</title>
+</head>
+<body onhashchange="hashchangeCallback()">
+ file_bug509055.html
+</body>
+</html>
diff --git a/docshell/test/mochitest/file_bug511449.html b/docshell/test/mochitest/file_bug511449.html
new file mode 100644
index 0000000000..637732dbbf
--- /dev/null
+++ b/docshell/test/mochitest/file_bug511449.html
@@ -0,0 +1,6 @@
+<!DOCTYPE HTML>
+<title>Used in test for bug 511449</title>
+<input type="text" id="input">
+<script type="text/javascript">
+ document.getElementById("input").focus();
+</script>
diff --git a/docshell/test/mochitest/file_bug540462.html b/docshell/test/mochitest/file_bug540462.html
new file mode 100644
index 0000000000..ab8c07eba5
--- /dev/null
+++ b/docshell/test/mochitest/file_bug540462.html
@@ -0,0 +1,25 @@
+<html>
+ <head>
+ <script>
+ // <!--
+ function test() {
+ document.open();
+ document.write(
+ `<html>
+ <body onload='opener.documentWriteLoad(); rel();'>
+ <a href='foo.html'>foo</a>
+ <script>
+ function rel() { setTimeout('location.reload()', 0); }
+ <\/script>
+ </body>
+ </html>`
+ );
+ document.close();
+ }
+ // -->
+ </script>
+ </head>
+ <body onload="setTimeout('test()', 0)">
+ Test for bug 540462
+ </body>
+</html>
diff --git a/docshell/test/mochitest/file_bug580069_1.html b/docshell/test/mochitest/file_bug580069_1.html
new file mode 100644
index 0000000000..7ab4610334
--- /dev/null
+++ b/docshell/test/mochitest/file_bug580069_1.html
@@ -0,0 +1,8 @@
+<html>
+<body onload='parent.page1Load();'>
+file_bug580069_1.html
+
+<form id='form' action='file_bug580069_2.sjs' method='POST'></form>
+
+</body>
+</html>
diff --git a/docshell/test/mochitest/file_bug580069_2.sjs b/docshell/test/mochitest/file_bug580069_2.sjs
new file mode 100644
index 0000000000..ff03c74e68
--- /dev/null
+++ b/docshell/test/mochitest/file_bug580069_2.sjs
@@ -0,0 +1,8 @@
+function handleRequest(request, response) {
+ response.setHeader("Content-Type", "text/html", false);
+ response.write(
+ "<html><body onload='parent.page2Load(\"" +
+ request.method +
+ "\")'>file_bug580069_2.sjs</body></html>"
+ );
+}
diff --git a/docshell/test/mochitest/file_bug590573_1.html b/docshell/test/mochitest/file_bug590573_1.html
new file mode 100644
index 0000000000..b859ca7469
--- /dev/null
+++ b/docshell/test/mochitest/file_bug590573_1.html
@@ -0,0 +1,7 @@
+<html>
+<body onload='opener.page1Load();' onpageshow='opener.page1PageShow();'>
+
+<div style='height:10000px' id='div1'>This is a very tall div.</div>
+
+</body>
+</html>
diff --git a/docshell/test/mochitest/file_bug590573_2.html b/docshell/test/mochitest/file_bug590573_2.html
new file mode 100644
index 0000000000..5f9ca22be4
--- /dev/null
+++ b/docshell/test/mochitest/file_bug590573_2.html
@@ -0,0 +1,8 @@
+<html>
+<body onpopstate='opener.page2Popstate();' onload='opener.page2Load();'
+ onpageshow='opener.page2PageShow();'>
+
+<div style='height:300%' id='div2'>The second page also has a big div.</div>
+
+</body>
+</html>
diff --git a/docshell/test/mochitest/file_bug598895_1.html b/docshell/test/mochitest/file_bug598895_1.html
new file mode 100644
index 0000000000..d21f2b4a5d
--- /dev/null
+++ b/docshell/test/mochitest/file_bug598895_1.html
@@ -0,0 +1 @@
+<script>window.onload = function() { opener.postMessage("loaded", "*"); };</script><body>Should show</body>
diff --git a/docshell/test/mochitest/file_bug598895_2.html b/docshell/test/mochitest/file_bug598895_2.html
new file mode 100644
index 0000000000..680c9bf22b
--- /dev/null
+++ b/docshell/test/mochitest/file_bug598895_2.html
@@ -0,0 +1 @@
+<script>window.onload = function() { opener.postMessage("loaded", "*"); };</script><body></body>
diff --git a/docshell/test/mochitest/file_bug634834.html b/docshell/test/mochitest/file_bug634834.html
new file mode 100644
index 0000000000..3ff0897451
--- /dev/null
+++ b/docshell/test/mochitest/file_bug634834.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+Nothing to see here; just an empty page.
+</body>
+</html>
diff --git a/docshell/test/mochitest/file_bug637644_1.html b/docshell/test/mochitest/file_bug637644_1.html
new file mode 100644
index 0000000000..d21f2b4a5d
--- /dev/null
+++ b/docshell/test/mochitest/file_bug637644_1.html
@@ -0,0 +1 @@
+<script>window.onload = function() { opener.postMessage("loaded", "*"); };</script><body>Should show</body>
diff --git a/docshell/test/mochitest/file_bug637644_2.html b/docshell/test/mochitest/file_bug637644_2.html
new file mode 100644
index 0000000000..680c9bf22b
--- /dev/null
+++ b/docshell/test/mochitest/file_bug637644_2.html
@@ -0,0 +1 @@
+<script>window.onload = function() { opener.postMessage("loaded", "*"); };</script><body></body>
diff --git a/docshell/test/mochitest/file_bug640387.html b/docshell/test/mochitest/file_bug640387.html
new file mode 100644
index 0000000000..3a939fb41e
--- /dev/null
+++ b/docshell/test/mochitest/file_bug640387.html
@@ -0,0 +1,26 @@
+<html>
+<body onhashchange='hashchange()' onload='load()' onpopstate='popstate()'>
+
+<script>
+function hashchange() {
+ var f = (opener || parent).childHashchange;
+ if (f)
+ f();
+}
+
+function load() {
+ var f = (opener || parent).childLoad;
+ if (f)
+ f();
+}
+
+function popstate() {
+ var f = (opener || parent).childPopstate;
+ if (f)
+ f();
+}
+</script>
+
+Not much to see here...
+</body>
+</html>
diff --git a/docshell/test/mochitest/file_bug653741.html b/docshell/test/mochitest/file_bug653741.html
new file mode 100644
index 0000000000..3202b52573
--- /dev/null
+++ b/docshell/test/mochitest/file_bug653741.html
@@ -0,0 +1,13 @@
+<html>
+<body onload='(parent || opener).childLoad()'>
+
+<div style='height:500px; background:yellow'>
+<a id='#top'>Top of the page</a>
+</div>
+
+<div id='bottom'>
+<a id='#bottom'>Bottom of the page</a>
+</div>
+
+</body>
+</html>
diff --git a/docshell/test/mochitest/file_bug660404 b/docshell/test/mochitest/file_bug660404
new file mode 100644
index 0000000000..ed773420ef
--- /dev/null
+++ b/docshell/test/mochitest/file_bug660404
@@ -0,0 +1,13 @@
+--testingtesting
+Content-Type: text/html
+
+<script>
+ var bc = new BroadcastChannel("bug660404_multipart");
+ bc.postMessage({command: "finishTest",
+ textContent: window.document.documentElement.textContent,
+ innerHTML: window.document.documentElement.innerHTML
+ });
+ bc.close();
+ window.close();
+</script>
+--testingtesting--
diff --git a/docshell/test/mochitest/file_bug660404-1.html b/docshell/test/mochitest/file_bug660404-1.html
new file mode 100644
index 0000000000..878bd80426
--- /dev/null
+++ b/docshell/test/mochitest/file_bug660404-1.html
@@ -0,0 +1,12 @@
+<script>
+ var bc = new BroadcastChannel("bug660404");
+ window.onload = function() {
+ setTimeout(() => {
+ window.onpagehide = function(ev) {
+ bc.postMessage({command: "pagehide", persisted: ev.persisted});
+ bc.close();
+ };
+ window.location.href = "file_bug660404";
+ }, 0);
+ };
+</script>
diff --git a/docshell/test/mochitest/file_bug660404^headers^ b/docshell/test/mochitest/file_bug660404^headers^
new file mode 100644
index 0000000000..5c821f3f48
--- /dev/null
+++ b/docshell/test/mochitest/file_bug660404^headers^
@@ -0,0 +1 @@
+Content-Type: multipart/x-mixed-replace; boundary="testingtesting"
diff --git a/docshell/test/mochitest/file_bug662170.html b/docshell/test/mochitest/file_bug662170.html
new file mode 100644
index 0000000000..3202b52573
--- /dev/null
+++ b/docshell/test/mochitest/file_bug662170.html
@@ -0,0 +1,13 @@
+<html>
+<body onload='(parent || opener).childLoad()'>
+
+<div style='height:500px; background:yellow'>
+<a id='#top'>Top of the page</a>
+</div>
+
+<div id='bottom'>
+<a id='#bottom'>Bottom of the page</a>
+</div>
+
+</body>
+</html>
diff --git a/docshell/test/mochitest/file_bug668513.html b/docshell/test/mochitest/file_bug668513.html
new file mode 100644
index 0000000000..ae417a35bd
--- /dev/null
+++ b/docshell/test/mochitest/file_bug668513.html
@@ -0,0 +1,101 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test file for Bug 668513</title>
+<script>
+ var SimpleTest = opener.SimpleTest;
+ var ok = opener.ok;
+ var is = opener.is;
+
+ function finish() {
+ SimpleTest.finish();
+ close();
+ }
+
+ function onload_test() {
+ var win = frames[0];
+ ok(win.performance, "Window.performance should be defined");
+ ok(win.performance.navigation, "Window.performance.navigation should be defined");
+ var navigation = win.performance && win.performance.navigation;
+ if (navigation === undefined) {
+ // avoid script errors
+ finish();
+ return;
+ }
+
+ // do this with a timeout to see the visuals of the navigations.
+ setTimeout(nav_frame, 100);
+ }
+
+ var step = 1;
+ function nav_frame() {
+ var navigation_frame = frames[0];
+ var navigation = navigation_frame.performance.navigation;
+ switch (step) {
+ case 1:
+ {
+ navigation_frame.location.href = "bug570341_recordevents.html";
+ step++;
+ break;
+ }
+ case 2:
+ {
+ is(navigation.type, navigation.TYPE_NAVIGATE,
+ "Expected window.performance.navigation.type == TYPE_NAVIGATE");
+ navigation_frame.history.back();
+ step++;
+ break;
+ }
+ case 3:
+ {
+ is(navigation.type, navigation.TYPE_BACK_FORWARD,
+ "Expected window.performance.navigation.type == TYPE_BACK_FORWARD");
+ step++;
+ navigation_frame.history.forward();
+ break;
+ }
+ case 4:
+ {
+ is(navigation.type, navigation.TYPE_BACK_FORWARD,
+ "Expected window.performance.navigation.type == TYPE_BACK_FORWARD");
+ navigation_frame.location.href = "bug668513_redirect.html";
+ step++;
+ break;
+ }
+ case 5:
+ {
+ is(navigation.type, navigation.TYPE_NAVIGATE,
+ "Expected timing.navigation.type as TYPE_NAVIGATE");
+ is(navigation.redirectCount, 1,
+ "Expected navigation.redirectCount == 1 on an server redirected navigation");
+
+ var timing = navigation_frame.performance && navigation_frame.performance.timing;
+ if (timing === undefined) {
+ // avoid script errors
+ finish();
+ break;
+ }
+ ok(timing.navigationStart > 0, "navigationStart should be > 0");
+ var sequence = ["navigationStart", "redirectStart", "redirectEnd", "fetchStart"];
+ for (var j = 1; j < sequence.length; ++j) {
+ var prop = sequence[j];
+ var prevProp = sequence[j - 1];
+ ok(timing[prevProp] <= timing[prop],
+ ["Expected ", prevProp, " to happen before ", prop,
+ ", got ", prevProp, " = ", timing[prevProp],
+ ", ", prop, " = ", timing[prop]].join(""));
+ }
+ step++;
+ finish();
+ break;
+ }
+ }
+ }
+</script>
+</head>
+<body>
+<div id="frames">
+<iframe name="child0" onload="onload_test();" src="navigation/blank.html"></iframe>
+</div>
+</body>
+</html>
diff --git a/docshell/test/mochitest/file_bug669671.sjs b/docshell/test/mochitest/file_bug669671.sjs
new file mode 100644
index 0000000000..5871419de8
--- /dev/null
+++ b/docshell/test/mochitest/file_bug669671.sjs
@@ -0,0 +1,17 @@
+function handleRequest(request, response) {
+ var count = parseInt(getState("count"));
+ if (!count || request.queryString == "countreset") {
+ count = 0;
+ }
+
+ setState("count", count + 1 + "");
+
+ response.setHeader("Content-Type", "text/html", false);
+ response.setHeader("Cache-Control", "max-age=0");
+ response.write(
+ '<html><body onload="opener.onChildLoad()" ' +
+ "onunload=\"parseInt('0')\">" +
+ count +
+ "</body></html>"
+ );
+}
diff --git a/docshell/test/mochitest/file_bug675587.html b/docshell/test/mochitest/file_bug675587.html
new file mode 100644
index 0000000000..842ab9ae79
--- /dev/null
+++ b/docshell/test/mochitest/file_bug675587.html
@@ -0,0 +1 @@
+<script>location.hash = "";</script>
diff --git a/docshell/test/mochitest/file_bug680257.html b/docshell/test/mochitest/file_bug680257.html
new file mode 100644
index 0000000000..ff480e96a5
--- /dev/null
+++ b/docshell/test/mochitest/file_bug680257.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <style type='text/css'>
+ a { color: black; }
+ a:target { color: red; }
+ </style>
+</head>
+
+<body onload='(opener || parent).popupLoaded()'>
+
+<a id='a' href='#a'>link</a>
+<a id='b' href='#b'>link2</a>
+
+</body>
+</html>
diff --git a/docshell/test/mochitest/file_bug703855.html b/docshell/test/mochitest/file_bug703855.html
new file mode 100644
index 0000000000..fe15b6e3df
--- /dev/null
+++ b/docshell/test/mochitest/file_bug703855.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<!-- Just need an empty file here, as long as it's served over HTTP -->
diff --git a/docshell/test/mochitest/file_bug728939.html b/docshell/test/mochitest/file_bug728939.html
new file mode 100644
index 0000000000..1cd52a44e1
--- /dev/null
+++ b/docshell/test/mochitest/file_bug728939.html
@@ -0,0 +1,3 @@
+<html>
+<body onload="opener.popupLoaded()">file_bug728939</body>
+</html>
diff --git a/docshell/test/mochitest/file_close_onpagehide1.html b/docshell/test/mochitest/file_close_onpagehide1.html
new file mode 100644
index 0000000000..ccf3b625a1
--- /dev/null
+++ b/docshell/test/mochitest/file_close_onpagehide1.html
@@ -0,0 +1,5 @@
+<script>
+ window.onload = () => {
+ opener.postMessage("initial", "*");
+ };
+</script>
diff --git a/docshell/test/mochitest/file_close_onpagehide2.html b/docshell/test/mochitest/file_close_onpagehide2.html
new file mode 100644
index 0000000000..a8e9479f47
--- /dev/null
+++ b/docshell/test/mochitest/file_close_onpagehide2.html
@@ -0,0 +1,5 @@
+<script>
+ window.onload = () => {
+ opener.postMessage("second", "*");
+ };
+</script>;
diff --git a/docshell/test/mochitest/file_compressed_multipart b/docshell/test/mochitest/file_compressed_multipart
new file mode 100644
index 0000000000..3c56226951
--- /dev/null
+++ b/docshell/test/mochitest/file_compressed_multipart
Binary files differ
diff --git a/docshell/test/mochitest/file_compressed_multipart^headers^ b/docshell/test/mochitest/file_compressed_multipart^headers^
new file mode 100644
index 0000000000..9376927812
--- /dev/null
+++ b/docshell/test/mochitest/file_compressed_multipart^headers^
@@ -0,0 +1,2 @@
+Content-Type: multipart/x-mixed-replace; boundary="testingtesting"
+Content-Encoding: gzip
diff --git a/docshell/test/mochitest/file_content_javascript_loads_frame.html b/docshell/test/mochitest/file_content_javascript_loads_frame.html
new file mode 100644
index 0000000000..9e2851aa8b
--- /dev/null
+++ b/docshell/test/mochitest/file_content_javascript_loads_frame.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<script type="application/javascript">
+"use strict";
+
+addEventListener("message", event => {
+ if ("ping" in event.data) {
+ event.source.postMessage({ pong: event.data.ping }, event.origin);
+ }
+});
+</script>
+</head>
+<body>
+</body>
+</html>
diff --git a/docshell/test/mochitest/file_content_javascript_loads_root.html b/docshell/test/mochitest/file_content_javascript_loads_root.html
new file mode 100644
index 0000000000..b9f2c1faa7
--- /dev/null
+++ b/docshell/test/mochitest/file_content_javascript_loads_root.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<script type="application/javascript">
+"use strict";
+
+window.onload = () => {
+ opener.postMessage("ready", "*");
+};
+
+// eslint-disable-next-line no-shadow
+function promiseMessage(source, filter = event => true) {
+ return new Promise(resolve => {
+ function listener(event) {
+ if (event.source == source && filter(event)) {
+ removeEventListener("message", listener);
+ resolve(event);
+ }
+ }
+ addEventListener("message", listener);
+ });
+}
+
+// Sends a message to the given target window and waits for the response.
+function ping(target) {
+ let msg = { ping: Math.random() };
+ target.postMessage(msg, "*");
+ return promiseMessage(
+ target,
+ event => event.data && event.data.pong == msg.ping
+ );
+}
+
+function setFrameLocation(name, uri) {
+ window[name].location = uri;
+}
+</script>
+</head>
+<body>
+</body>
+</html>
diff --git a/docshell/test/mochitest/file_form_restoration_no_store.html b/docshell/test/mochitest/file_form_restoration_no_store.html
new file mode 100644
index 0000000000..6e8756a693
--- /dev/null
+++ b/docshell/test/mochitest/file_form_restoration_no_store.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <script>
+ window.addEventListener("pageshow", ({ persisted }) => {
+ let bc = new BroadcastChannel("form_restoration");
+ bc.addEventListener("message", ({ data }) => {
+ switch (data) {
+ case "enter_data":
+ document.getElementById("formElement").value = "test";
+ break;
+ case "reload":
+ bc.close();
+ location.reload();
+ break;
+ case "navigate":
+ bc.close();
+ document.location = "file_form_restoration_no_store.html?1";
+ break;
+ case "back":
+ bc.close();
+ history.back();
+ break;
+ case "close":
+ bc.close();
+ window.close();
+ break;
+ }
+ });
+ bc.postMessage({ persisted, formData: document.getElementById("formElement").value });
+ });
+ </script>
+</head>
+<body>
+ <input id="formElement" type="text" value="initial">
+</body>
+</html>
diff --git a/docshell/test/mochitest/file_form_restoration_no_store.html^headers^ b/docshell/test/mochitest/file_form_restoration_no_store.html^headers^
new file mode 100644
index 0000000000..4030ea1d3d
--- /dev/null
+++ b/docshell/test/mochitest/file_form_restoration_no_store.html^headers^
@@ -0,0 +1 @@
+Cache-Control: no-store
diff --git a/docshell/test/mochitest/file_framedhistoryframes.html b/docshell/test/mochitest/file_framedhistoryframes.html
new file mode 100644
index 0000000000..314f9c72d8
--- /dev/null
+++ b/docshell/test/mochitest/file_framedhistoryframes.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<iframe id="iframe" src="historyframes.html"></iframe>
+<script type="application/javascript">
+
+var SimpleTest = window.opener.SimpleTest;
+var is = window.opener.is;
+
+function done() {
+ window.opener.done();
+}
+
+</script>
+</body>
+</html>
diff --git a/docshell/test/mochitest/file_load_during_reload.html b/docshell/test/mochitest/file_load_during_reload.html
new file mode 100644
index 0000000000..600d5c1728
--- /dev/null
+++ b/docshell/test/mochitest/file_load_during_reload.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <script>
+ function notifyOpener() {
+ opener.postMessage("loaded", "*");
+ }
+ </script>
+ </head>
+ <body onload="notifyOpener()">
+ </body>
+</html>
diff --git a/docshell/test/mochitest/file_pushState_after_document_open.html b/docshell/test/mochitest/file_pushState_after_document_open.html
new file mode 100644
index 0000000000..97a6954f2e
--- /dev/null
+++ b/docshell/test/mochitest/file_pushState_after_document_open.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<script>
+ document.addEventListener("DOMContentLoaded", function() {
+ document.open();
+ document.write("<!DOCTYPE html>New Document here");
+ document.close();
+ // Notify parent via postMessage, since otherwise exceptions will not get
+ // caught by its onerror handler.
+ parent.postMessage("doTest", "*");
+ });
+</script>
diff --git a/docshell/test/mochitest/file_redirect_history.html b/docshell/test/mochitest/file_redirect_history.html
new file mode 100644
index 0000000000..3971faf4fd
--- /dev/null
+++ b/docshell/test/mochitest/file_redirect_history.html
@@ -0,0 +1,18 @@
+<html>
+ <head>
+ <script>
+ function loaded() {
+ addEventListener("message", ({ data }) => {
+ document.getElementById("form").action = data;
+ document.getElementById("button").click();
+ }, { once: true });
+ opener.postMessage("loaded", "*");
+ }
+ </script>
+ </head>
+ <body onload="loaded();">
+ <form id="form" method="POST">
+ <input id="button" type="submit" />
+ </form>
+ </body>
+</html>
diff --git a/docshell/test/mochitest/form_submit.sjs b/docshell/test/mochitest/form_submit.sjs
new file mode 100644
index 0000000000..1a1fa5d89c
--- /dev/null
+++ b/docshell/test/mochitest/form_submit.sjs
@@ -0,0 +1,40 @@
+"use strict";
+
+const CC = Components.Constructor;
+const BinaryInputStream = CC(
+ "@mozilla.org/binaryinputstream;1",
+ "nsIBinaryInputStream",
+ "setInputStream"
+);
+
+const BinaryOutputStream = CC(
+ "@mozilla.org/binaryoutputstream;1",
+ "nsIBinaryOutputStream",
+ "setOutputStream"
+);
+
+function log(str) {
+ // dump(`LOG: ${str}\n`);
+}
+
+async function handleRequest(request, response) {
+ if (request.method !== "POST") {
+ throw new Error("Expected a post request");
+ } else {
+ log("Reading request");
+ let available = 0;
+ let inputStream = new BinaryInputStream(request.bodyInputStream);
+ while ((available = inputStream.available()) > 0) {
+ log(inputStream.readBytes(available));
+ }
+ }
+
+ log("Setting Headers");
+ response.setHeader("Content-Type", "text/html", false);
+ response.setStatusLine(request.httpVersion, "200", "OK");
+ log("Writing body");
+ response.write(
+ '<script>"use strict"; let target = opener ? opener : parent; target.postMessage("done", "*");</script>'
+ );
+ log("Done");
+}
diff --git a/docshell/test/mochitest/form_submit_redirect.sjs b/docshell/test/mochitest/form_submit_redirect.sjs
new file mode 100644
index 0000000000..dbc32b9643
--- /dev/null
+++ b/docshell/test/mochitest/form_submit_redirect.sjs
@@ -0,0 +1,15 @@
+"use strict";
+
+Cu.importGlobalProperties(["URLSearchParams"]);
+
+async function handleRequest(request, response) {
+ if (request.method !== "POST") {
+ throw new Error("Expected a post request");
+ } else {
+ let params = new URLSearchParams(request.queryString);
+ let redirect = params.get("redirectTo");
+
+ response.setStatusLine(request.httpVersion, 302, "Moved Temporarily");
+ response.setHeader("Location", redirect);
+ }
+}
diff --git a/docshell/test/mochitest/historyframes.html b/docshell/test/mochitest/historyframes.html
new file mode 100644
index 0000000000..846cdd9f9d
--- /dev/null
+++ b/docshell/test/mochitest/historyframes.html
@@ -0,0 +1,176 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=602256
+-->
+<head>
+ <title>Test for Bug 602256</title>
+</head>
+<body onload="SimpleTest.executeSoon(run_test)">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=602256">Mozilla Bug 602256</a>
+<div id="content">
+ <iframe id="iframe" src="start_historyframe.html"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 602256 **/
+
+var testWin = window.opener ? window.opener : window.parent;
+
+var SimpleTest = testWin.SimpleTest;
+function is() { testWin.is.apply(testWin, arguments); }
+
+var gFrame = null;
+
+function gState() {
+ return location.hash.replace(/^#/, "");
+}
+
+function waitForLoad(aCallback) {
+ function listener() {
+ gFrame.removeEventListener("load", listener);
+ SimpleTest.executeSoon(aCallback);
+ }
+
+ gFrame.addEventListener("load", listener);
+}
+
+function loadContent(aURL, aCallback) {
+ waitForLoad(aCallback);
+
+ gFrame.src = aURL;
+}
+
+function getURL() {
+ return gFrame.contentDocument.documentURI;
+}
+
+function getContent() {
+ return gFrame.contentDocument.getElementById("text").textContent;
+}
+
+var BASE_URI = "http://mochi.test:8888/tests/docshell/test/mochitest/";
+var START = BASE_URI + "start_historyframe.html";
+var URL1 = BASE_URI + "url1_historyframe.html";
+var URL2 = BASE_URI + "url2_historyframe.html";
+
+function run_test() {
+ window.location.hash = "START";
+
+ gFrame = document.getElementById("iframe");
+
+ test_basic_inner_navigation();
+}
+
+function end_test() {
+ testWin.done();
+}
+
+var gTestContinuation = null;
+function continueAsync() {
+ setTimeout(function() { gTestContinuation.next(); })
+}
+
+function test_basic_inner_navigation() {
+ // Navigate the inner frame a few times
+ loadContent(URL1, function() {
+ is(getURL(), URL1, "URL should be correct");
+ is(getContent(), "Test1", "Page should be correct");
+
+ loadContent(URL2, function() {
+ is(getURL(), URL2, "URL should be correct");
+ is(getContent(), "Test2", "Page should be correct");
+
+ // Test that history is working
+ waitForLoad(function() {
+ is(getURL(), URL1, "URL should be correct");
+ is(getContent(), "Test1", "Page should be correct");
+
+ waitForLoad(function() {
+ is(getURL(), URL2, "URL should be correct");
+ is(getContent(), "Test2", "Page should be correct");
+
+ gTestContinuation = test_state_navigation();
+ gTestContinuation.next();
+ });
+ window.history.forward();
+ });
+ window.history.back();
+ });
+ });
+}
+
+function* test_state_navigation() {
+ window.location.hash = "STATE1";
+
+ is(getURL(), URL2, "URL should be correct");
+ is(getContent(), "Test2", "Page should be correct");
+
+ window.location.hash = "STATE2";
+
+ is(getURL(), URL2, "URL should be correct");
+ is(getContent(), "Test2", "Page should be correct");
+
+ window.addEventListener("popstate", (e) => {
+ continueAsync();
+ }, {once: true});
+ window.history.back();
+ yield;
+
+ is(gState(), "STATE1", "State should be correct after going back");
+ is(getURL(), URL2, "URL should be correct");
+ is(getContent(), "Test2", "Page should be correct");
+
+ window.addEventListener("popstate", (e) => {
+ continueAsync();
+ }, {once: true});
+ window.history.forward();
+ yield;
+
+ is(gState(), "STATE2", "State should be correct after going forward");
+ is(getURL(), URL2, "URL should be correct");
+ is(getContent(), "Test2", "Page should be correct");
+
+ window.addEventListener("popstate", (e) => {
+ continueAsync();
+ }, {once: true});
+ window.history.back();
+ yield;
+
+ window.addEventListener("popstate", (e) => {
+ continueAsync();
+ }, {once: true});
+ window.history.back();
+ yield;
+
+ is(gState(), "START", "State should be correct");
+ is(getURL(), URL2, "URL should be correct");
+ is(getContent(), "Test2", "Page should be correct");
+
+ waitForLoad(function() {
+ is(getURL(), URL1, "URL should be correct");
+ is(getContent(), "Test1", "Page should be correct");
+
+ waitForLoad(function() {
+ is(gState(), "START", "State should be correct");
+ is(getURL(), START, "URL should be correct");
+ is(getContent(), "Start", "Page should be correct");
+
+ end_test();
+ });
+
+ window.history.back();
+
+ is(gState(), "START", "State should be correct after going back twice");
+ });
+
+ window.history.back();
+ continueAsync();
+ yield;
+ is(gState(), "START", "State should be correct");
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/mochitest.ini b/docshell/test/mochitest/mochitest.ini
new file mode 100644
index 0000000000..b18e1afb16
--- /dev/null
+++ b/docshell/test/mochitest/mochitest.ini
@@ -0,0 +1,174 @@
+[DEFAULT]
+support-files =
+ bug404548-subframe.html
+ bug404548-subframe_window.html
+ bug413310-post.sjs
+ bug413310-subframe.html
+ bug529119-window.html
+ bug570341_recordevents.html
+ bug668513_redirect.html
+ bug668513_redirect.html^headers^
+ bug691547_frame.html
+ dummy_page.html
+ file_anchor_scroll_after_document_open.html
+ file_bfcache_plus_hash_1.html
+ file_bfcache_plus_hash_2.html
+ file_bug385434_1.html
+ file_bug385434_2.html
+ file_bug385434_3.html
+ file_bug475636.sjs
+ file_bug509055.html
+ file_bug540462.html
+ file_bug580069_1.html
+ file_bug580069_2.sjs
+ file_bug598895_1.html
+ file_bug598895_2.html
+ file_bug590573_1.html
+ file_bug590573_2.html
+ file_bug634834.html
+ file_bug637644_1.html
+ file_bug637644_2.html
+ file_bug640387.html
+ file_bug653741.html
+ file_bug660404
+ file_bug660404^headers^
+ file_bug660404-1.html
+ file_bug662170.html
+ file_bug669671.sjs
+ file_bug680257.html
+ file_bug703855.html
+ file_bug728939.html
+ file_bug1121701_1.html
+ file_bug1121701_2.html
+ file_bug1186774.html
+ file_bug1151421.html
+ file_bug1450164.html
+ file_close_onpagehide1.html
+ file_close_onpagehide2.html
+ file_compressed_multipart
+ file_compressed_multipart^headers^
+ file_pushState_after_document_open.html
+ historyframes.html
+ ping.html
+ start_historyframe.html
+ url1_historyframe.html
+ url2_historyframe.html
+
+[test_anchor_scroll_after_document_open.html]
+[test_bfcache_plus_hash.html]
+[test_bug1422334.html]
+support-files =
+ bug1422334_redirect.html
+ bug1422334_redirect.html^headers^
+ !/docshell/test/navigation/blank.html
+[test_bug385434.html]
+[test_bug387979.html]
+[test_bug402210.html]
+[test_bug404548.html]
+[test_bug413310.html]
+skip-if = true
+# Disabled for too many intermittent failures (bug 719186)
+[test_bug475636.html]
+[test_bug509055.html]
+[test_bug511449.html]
+skip-if = toolkit != "cocoa" || headless # Headless: bug 1410525
+support-files = file_bug511449.html
+[test_bug529119-1.html]
+[test_bug529119-2.html]
+[test_bug530396.html]
+support-files = bug530396-noref.sjs bug530396-subframe.html
+[test_bug540462.html]
+skip-if = toolkit == 'android' && debug && !is_fennec
+[test_bug551225.html]
+[test_bug570341.html]
+skip-if = (verify && !debug && (os == 'win'))
+[test_bug580069.html]
+skip-if = (verify && !debug && (os == 'win'))
+[test_bug590573.html]
+[test_bug598895.html]
+[test_bug634834.html]
+[test_bug637644.html]
+[test_bug640387_1.html]
+[test_bug640387_2.html]
+[test_bug653741.html]
+[test_bug660404.html]
+[test_bug662170.html]
+[test_bug668513.html]
+support-files = file_bug668513.html
+[test_bug669671.html]
+[test_bug675587.html]
+support-files = file_bug675587.html
+[test_bug680257.html]
+[test_bug691547.html]
+[test_bug694612.html]
+[test_bug703855.html]
+[test_bug728939.html]
+[test_bug797909.html]
+[test_bug1045096.html]
+[test_bug1121701.html]
+[test_bug1151421.html]
+[test_bug1186774.html]
+[test_bug1450164.html]
+[test_bug1507702.html]
+[test_bug1645781.html]
+support-files =
+ form_submit.sjs
+[test_bug1729662.html]
+support-files =
+ file_bug1729662.html
+[test_bug1740516.html]
+support-files =
+ file_bug1740516_1.html
+ file_bug1740516_1_inner.html
+ file_bug1740516_2.html
+[test_form_restoration.html]
+support-files =
+ file_form_restoration_no_store.html
+ file_form_restoration_no_store.html^headers^
+[test_bug1741132.html]
+support-files =
+ file_bug1741132.html
+skip-if = toolkit == "android" && !sessionHistoryInParent
+[test_bug1742865.html]
+support-files =
+ file_bug1742865.sjs
+ file_bug1742865_outer.sjs
+skip-if = toolkit == "android" && debug && fission && verify # Bug 1745937
+[test_bug1743353.html]
+support-files =
+ file_bug1743353.html
+[test_bug1747033.html]
+support-files =
+ file_bug1747033.sjs
+[test_bug1773192.html]
+support-files =
+ file_bug1773192_1.html
+ file_bug1773192_2.html
+ file_bug1773192_3.sjs
+[test_close_onpagehide_by_history_back.html]
+[test_close_onpagehide_by_window_close.html]
+[test_compressed_multipart.html]
+[test_content_javascript_loads.html]
+support-files =
+ file_content_javascript_loads_root.html
+ file_content_javascript_loads_frame.html
+[test_forceinheritprincipal_overrule_owner.html]
+[test_framedhistoryframes.html]
+support-files = file_framedhistoryframes.html
+[test_load_during_reload.html]
+support-files = file_load_during_reload.html
+[test_pushState_after_document_open.html]
+[test_navigate_after_pagehide.html]
+[test_redirect_history.html]
+support-files =
+ file_redirect_history.html
+ form_submit_redirect.sjs
+[test_windowedhistoryframes.html]
+skip-if = (!debug && os == 'android') # Bug 1573892
+[test_triggeringprincipal_location_seturi.html]
+[test_double_submit.html]
+support-files =
+ clicker.html
+ double_submit.sjs
+[test_iframe_srcdoc_to_remote.html]
+[test_javascript_sandboxed_popup.html]
diff --git a/docshell/test/mochitest/ping.html b/docshell/test/mochitest/ping.html
new file mode 100644
index 0000000000..7d84560dd1
--- /dev/null
+++ b/docshell/test/mochitest/ping.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<script>
+ "use strict";
+ let target = (window.opener || window.parent);
+ target.postMessage("ping", "*");
+</script>
diff --git a/docshell/test/mochitest/start_historyframe.html b/docshell/test/mochitest/start_historyframe.html
new file mode 100644
index 0000000000..a791af4e64
--- /dev/null
+++ b/docshell/test/mochitest/start_historyframe.html
@@ -0,0 +1 @@
+<p id='text'>Start</p>
diff --git a/docshell/test/mochitest/test_anchor_scroll_after_document_open.html b/docshell/test/mochitest/test_anchor_scroll_after_document_open.html
new file mode 100644
index 0000000000..6e415e3cbe
--- /dev/null
+++ b/docshell/test/mochitest/test_anchor_scroll_after_document_open.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=881487
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 881487</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 881487 **/
+ SimpleTest.waitForExplicitFinish();
+ // Child needs to invoke us, otherwise our onload will fire before the child
+ // has done the write/close bit.
+ var gotOnload = false;
+ addLoadEvent(function() {
+ gotOnload = true;
+ });
+ onmessage = function handleMessage(msg) {
+ if (msg.data == "doTest") {
+ if (!gotOnload) {
+ addLoadEvent(function() { handleMessage(msg); });
+ return;
+ }
+ frames[0].onscroll = function() {
+ ok(true, "Got a scroll event");
+ SimpleTest.finish();
+ };
+ frames[0].location.hash = "#target";
+ return;
+ }
+ if (msg.data == "haveHash") {
+ ok(false, "Child got reloaded");
+ } else {
+ ok(false, "Unexpected message");
+ }
+ SimpleTest.finish();
+ };
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=881487">Mozilla Bug 881487</a>
+<p id="display">
+ <!-- iframe goes here so it can scroll -->
+<iframe src="file_anchor_scroll_after_document_open.html"></iframe>
+</p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bfcache_plus_hash.html b/docshell/test/mochitest/test_bfcache_plus_hash.html
new file mode 100644
index 0000000000..66871fcbbf
--- /dev/null
+++ b/docshell/test/mochitest/test_bfcache_plus_hash.html
@@ -0,0 +1,153 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=646641
+-->
+<head>
+ <title>Test for Bug 646641</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=646641">Mozilla Bug 646641</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 646641 **/
+
+/**
+ * Steps:
+ * - Main page (this one) opens file_bfcache_plus_hash_1.html (subpage 1)
+ * - subpage 1 sends msg { "childLoad", 1 }
+ * - subpage 1 sends msg { "childPageshow", 1 }
+ * - main page sends message "pushState"
+ * - subpage 1 does pushState()
+ * - subpage 1 navigates to file_bfcache_plus_hash_2.html (subpage 2)
+ * - subpage 2 sends msg { "childLoad", 2 }
+ * - subpage 2 sends msg { "childPageshow", 2 }
+ * - main page sends msg "go-2"
+ * - subpage 2 goes back two history entries
+ * - subpage 1 sends msg { "childPageshow", 1 }
+ * - Receiving only this msg shows we have retrieved the document from bfcache
+ * - main page sends msg "close"
+ * - subpage 1 sends msg "closed"
+ */
+SimpleTest.waitForExplicitFinish();
+
+function debug(msg) {
+ // Wrap dump so we can turn debug messages on and off easily.
+ dump(msg + "\n");
+}
+
+var expectedLoadNum = -1;
+var expectedPageshowNum = -1;
+
+function waitForLoad(n) {
+ debug("Waiting for load " + n);
+ expectedLoadNum = n;
+}
+
+function waitForShow(n) {
+ debug("Waiting for show " + n);
+ expectedPageshowNum = n;
+}
+
+
+
+function executeTest() {
+ function* test() {
+ window.open("file_bfcache_plus_hash_1.html", "", "noopener");
+ waitForLoad(1);
+ waitForShow(1);
+ yield undefined;
+ yield undefined;
+
+ bc1.postMessage("pushState");
+
+ waitForLoad(2);
+ waitForShow(2);
+ yield undefined;
+ yield undefined;
+
+ // Now go back 2. The first page should be retrieved from bfcache.
+ bc2.postMessage("go-2");
+ waitForShow(1);
+ yield undefined;
+
+ bc1.postMessage("close");
+ }
+
+ var bc1 = new BroadcastChannel("bug646641_1");
+ var bc2 = new BroadcastChannel("bug646641_2");
+ bc1.onmessage = (msgEvent) => {
+ var msg = msgEvent.data.message;
+ var n = msgEvent.data.num;
+ if (msg == "childLoad") {
+ if (n == expectedLoadNum) {
+ debug("Got load " + n);
+ expectedLoadNum = -1;
+
+ // Spin the event loop before calling gGen.next() so the generator runs
+ // outside the onload handler. This prevents us from encountering all
+ // sorts of docshell quirks.
+ setTimeout(function() { gGen.next(); }, 0);
+ } else {
+ debug("Got unexpected load " + n);
+ ok(false, "Got unexpected load " + n);
+ }
+ } else if (msg == "childPageshow") {
+ if (n == expectedPageshowNum) {
+ debug("Got expected pageshow " + n);
+ expectedPageshowNum = -1;
+ ok(true, "Got expected pageshow " + n);
+ setTimeout(function() { gGen.next(); }, 0);
+ } else {
+ debug("Got unexpected pageshow " + n);
+ ok(false, "Got unexpected pageshow " + n);
+ }
+ } else if (msg == "closed") {
+ bc1.close();
+ bc2.close();
+ SimpleTest.finish();
+ }
+ }
+
+ bc2.onmessage = bc1.onmessage;
+
+ var gGen = test();
+
+ // If Fission is disabled, the pref is no-op.
+ SpecialPowers.pushPrefEnv({set: [["fission.bfcacheInParent", true]]}, () => {
+ gGen.next();
+ });
+}
+if (isXOrigin) {
+ // Bug 1746646: Make mochitests work with TCP enabled (cookieBehavior = 5)
+ // Acquire storage access permission here so that the BroadcastChannel used to
+ // communicate with the opened windows works in xorigin tests. Otherwise,
+ // the iframe containing this page is isolated from first-party storage access,
+ // which isolates BroadcastChannel communication.
+ SpecialPowers.wrap(document).notifyUserGestureActivation();
+ SpecialPowers.addPermission("storageAccessAPI", true, window.location.href).then(() => {
+ SpecialPowers.wrap(document).requestStorageAccess().then(() => {
+ SpecialPowers.pushPrefEnv({
+ set: [["privacy.partition.always_partition_third_party_non_cookie_storage", false]],
+ }).then(() => {
+ executeTest();
+ });
+ });
+ });
+} else {
+ executeTest();
+}
+
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug1045096.html b/docshell/test/mochitest/test_bug1045096.html
new file mode 100644
index 0000000000..e9b8df7a5f
--- /dev/null
+++ b/docshell/test/mochitest/test_bug1045096.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1045096
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1045096</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1045096">Mozilla Bug 1045096</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+ <script type="application/javascript">
+
+ /** Test for Bug 1045096 **/
+ var i = document.createElement("iframe");
+ i.src = "javascript:false"; // This is required!
+ $("content").appendChild(i);
+ ok(i.contentWindow.performance, "Should have a performance object");
+ </script>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug1121701.html b/docshell/test/mochitest/test_bug1121701.html
new file mode 100644
index 0000000000..66391d1f5b
--- /dev/null
+++ b/docshell/test/mochitest/test_bug1121701.html
@@ -0,0 +1,108 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1121701
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1121701</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1121701 **/
+ SimpleTest.waitForExplicitFinish();
+
+ var testUrl1 = "file_bug1121701_1.html";
+ var testUrl2 = "file_bug1121701_2.html";
+
+ var page1LoadCount = 0;
+ let page1Done = {};
+ page1Done.promise = new Promise(resolve => {
+ page1Done.resolve = resolve;
+ });
+ let page2Done = {};
+ page2Done.promise = new Promise(resolve => {
+ page2Done.resolve = resolve;
+ });
+
+ addLoadEvent(async function() {
+
+ // Bug 1746646: Make mochitests work with TCP enabled (cookieBehavior = 5)
+ // Acquire storage access permission here so that the BroadcastChannel used to
+ // communicate with the opened windows works in xorigin tests. Otherwise,
+ // the iframe containing this page is isolated from first-party storage access,
+ // which isolates BroadcastChannel communication.
+ if (isXOrigin) {
+ await SpecialPowers.pushPrefEnv({
+ set: [["privacy.partition.always_partition_third_party_non_cookie_storage", false]],
+ });
+ SpecialPowers.wrap(document).notifyUserGestureActivation();
+ await SpecialPowers.addPermission("storageAccessAPI", true, window.location.href);
+ await SpecialPowers.wrap(document).requestStorageAccess();
+ }
+
+ var bc = new BroadcastChannel("file_bug1121701_1");
+ var bc2 = new BroadcastChannel("file_bug1121701_2");
+
+ async function scheduleFinish() {
+ await Promise.all([page1Done.promise, page2Done.promise]);
+ bc2.close();
+ bc.close();
+ SimpleTest.finish();
+ }
+
+ bc.onmessage = (msgEvent) => {
+ var msg = msgEvent.data;
+ var command = msg.command;
+ if (command == "child1PageShow") {
+ ++page1LoadCount;
+ var persisted = msg.persisted;
+ var pageHideAsserts = msg.pageHideAsserts;
+ if (pageHideAsserts) {
+ ok(pageHideAsserts.persisted, "onpagehide: test page 1 should get persisted");
+ is(pageHideAsserts.innerHTML, "modified", "onpagehide: innerHTML text is 'modified'");
+ }
+ if (page1LoadCount == 1) {
+ SimpleTest.executeSoon(function() {
+ is(persisted, false, "Initial page load shouldn't be persisted.");
+ bc.postMessage({command: "setInnerHTML", testUrl2});
+ });
+ } else if (page1LoadCount == 2) {
+ is(persisted, true, "Page load from bfcache should be persisted.");
+ is(msg.innerHTML, "modified", "innerHTML text is 'modified'");
+ bc.postMessage({command: "close"});
+ }
+ } else if (command == "closed") {
+ page1Done.resolve();
+ }
+ }
+ bc2.onmessage = (msgEvent) => {
+ var msg = msgEvent.data;
+ var command = msg.command;
+ if (command == "child2PageShow") {
+ bc2.postMessage({command: "setInnerHTML", location: location.href});
+ } else if (command == "onmessage") {
+ page2Done.resolve();
+ }
+ }
+
+ scheduleFinish();
+ // If Fission is disabled, the pref is no-op.
+ SpecialPowers.pushPrefEnv({set: [["fission.bfcacheInParent", true]]}, () => {
+ window.open(testUrl1, "", "noopener");
+ });
+ });
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1121701">Mozilla Bug 1121701</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug1151421.html b/docshell/test/mochitest/test_bug1151421.html
new file mode 100644
index 0000000000..4eb647e121
--- /dev/null
+++ b/docshell/test/mochitest/test_bug1151421.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1151421
+-->
+<head>
+ <title>Test for Bug 1151421</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1151421">Mozilla Bug 1151421</a>
+
+<script type="application/javascript">
+
+/** Test for Bug 1151421 **/
+SimpleTest.waitForExplicitFinish();
+
+function childLoad() {
+ // Spin the event loop so we leave the onload handler.
+ SimpleTest.executeSoon(childLoad2);
+}
+
+function childLoad2() {
+ let iframe = document.getElementById("iframe");
+ let cw = iframe.contentWindow;
+ let content = cw.document.getElementById("content");
+
+ // Create a function to calculate an invariant.
+ let topPlusOffset = function() {
+ return Math.round(content.getBoundingClientRect().top + cw.pageYOffset);
+ };
+
+ let initialTPO = topPlusOffset();
+
+ // Scroll the iframe to various positions, and check the TPO.
+ // Scrolling down to the bottom will adjust the page offset by a fractional amount.
+ let positions = [-100, 0.17, 0, 1.5, 10.41, 1e6, 12.1];
+
+ // Run some tests with scrollTo() and ensure we have the same invariant after scrolling.
+ positions.forEach(function(pos) {
+ cw.scrollTo(0, pos);
+ is(topPlusOffset(), initialTPO, "Top plus offset should remain invariant across scrolling.");
+ });
+
+ positions.reverse().forEach(function(pos) {
+ cw.scrollTo(0, pos);
+ is(topPlusOffset(), initialTPO, "(reverse) Top plus offset should remain invariant across scrolling.");
+ });
+
+ SimpleTest.finish();
+}
+
+</script>
+
+<!-- When the iframe loads, it calls childLoad(). -->
+<br>
+<iframe height='100px' id='iframe' src='file_bug1151421.html'></iframe>
+
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug1186774.html b/docshell/test/mochitest/test_bug1186774.html
new file mode 100644
index 0000000000..afaf3b0f34
--- /dev/null
+++ b/docshell/test/mochitest/test_bug1186774.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1186774
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1186774</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1186774 **/
+
+var child;
+
+function runTest() {
+ child = window.open("file_bug1186774.html", "", "width=100,height=100");
+ child.onload = function() {
+ setTimeout(function() {
+ child.scrollTo(0, 0);
+ child.history.pushState({}, "initial");
+ child.scrollTo(0, 3000);
+ child.history.pushState({}, "scrolled");
+ child.scrollTo(0, 6000);
+ child.history.back();
+ });
+ };
+
+ child.onpopstate = function() {
+ is(Math.round(child.scrollY), 6000, "Shouldn't have scrolled before popstate");
+ child.close();
+ SimpleTest.finish();
+ };
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(runTest);
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1186774">Mozilla Bug 1186774</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug1422334.html b/docshell/test/mochitest/test_bug1422334.html
new file mode 100644
index 0000000000..b525ae1d9c
--- /dev/null
+++ b/docshell/test/mochitest/test_bug1422334.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Ensure that reload after replaceState after 3xx redirect does the right thing.</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(function() {
+ var ifr = document.querySelector("iframe");
+ var win = ifr.contentWindow;
+ is(win.location.href, location.href.replace(location.search, "")
+ .replace("mochitest/test_bug1422334.html",
+ "navigation/blank.html?x=y"),
+ "Should have the right location on initial load");
+
+ win.history.replaceState(null, '', win.location.pathname);
+ is(win.location.href, location.href.replace(location.search, "")
+ .replace("mochitest/test_bug1422334.html",
+ "navigation/blank.html"),
+ "Should have the right location after replaceState call");
+
+ ifr.onload = function() {
+ is(win.location.href, location.href.replace(location.search, "")
+ .replace("mochitest/test_bug1422334.html",
+ "navigation/blank.html"),
+ "Should have the right location after reload");
+ SimpleTest.finish();
+ }
+ win.location.reload();
+ });
+ </script>
+</head>
+<body>
+<p id="display"><iframe src="bug1422334_redirect.html"></iframe></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug1450164.html b/docshell/test/mochitest/test_bug1450164.html
new file mode 100644
index 0000000000..190b2f349b
--- /dev/null
+++ b/docshell/test/mochitest/test_bug1450164.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+ <!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=1450164
+ -->
+ <head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1450164</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1450164 **/
+
+ function runTest() {
+ var child = window.open("file_bug1450164.html", "", "width=100,height=100");
+ child.onload = function() {
+ // After the window loads, close it. If we don't crash in debug, consider that a pass.
+ child.close();
+ };
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(runTest);
+
+ </script>
+ </head>
+ <body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1450164">Mozilla Bug 1450164</a>
+ </body>
+</html>
diff --git a/docshell/test/mochitest/test_bug1507702.html b/docshell/test/mochitest/test_bug1507702.html
new file mode 100644
index 0000000000..fd88ee60a5
--- /dev/null
+++ b/docshell/test/mochitest/test_bug1507702.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1507702
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1507702</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <link rel="icon" href="about:crashparent"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1507702">Mozilla Bug 1507702</a>
+<img src="about:crashparent">
+<img src="about:crashcontent">
+<iframe src="about:crashparent"></iframe>
+<iframe src="about:crashcontent"></iframe>
+<script>
+ let urls = ["about:crashparent", "about:crashcontent"];
+ async function testFetch() {
+ const url = urls.shift();
+ if (!url) {
+ return Promise.resolve();
+ }
+
+ let threw;
+ try {
+ await fetch(url);
+ threw = false;
+ } catch (e) {
+ threw = true;
+ }
+
+ ok(threw === true, "fetch should reject");
+ return testFetch();
+ }
+
+ document.body.onload = async () => {
+ for (const url of ["about:crashparent", "about:crashcontent"]) {
+ SimpleTest.doesThrow(() => {
+ top.location.href = url;
+ }, "navigation should throw");
+
+ SimpleTest.doesThrow(() => {
+ location.href = url;
+ }, "navigation should throw");
+ }
+
+ await testFetch();
+ SimpleTest.finish();
+ };
+
+ SimpleTest.waitForExplicitFinish();
+</script>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug1645781.html b/docshell/test/mochitest/test_bug1645781.html
new file mode 100644
index 0000000000..6cf676b7a9
--- /dev/null
+++ b/docshell/test/mochitest/test_bug1645781.html
@@ -0,0 +1,90 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Test for Bug 1590762</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <body>
+ <form id="form" action="form_submit.sjs" method="POST" target="targetFrame">
+ <input id="input" type="text" name="name" value="">
+ <input id="button" type="submit">
+ </form>
+ <script>
+ "use strict";
+ const PATH = "/tests/docshell/test/mochitest/";
+ const SAME_ORIGIN = new URL(PATH, window.location.origin);;
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ const CROSS_ORIGIN_1 = new URL(PATH, "http://test1.example.com/");
+ const CROSS_ORIGIN_2 = new URL(PATH, "https://example.com/");
+ const TARGET = "ping.html";
+ const ACTION = "form_submit.sjs";
+
+ function generateBody(size) {
+ let data = new Uint8Array(size);
+ for (let i = 0; i < size; ++i) {
+ data[i] = 97 + Math.random() * (123 - 97);
+ }
+
+ return new TextDecoder().decode(data);
+ }
+
+ async function withFrame(url) {
+ info("Creating frame");
+ let frame = document.createElement('iframe');
+ frame.name = "targetFrame";
+
+ return new Promise(resolve => {
+ addEventListener('message', async function({source}) {
+ info("Frame loaded");
+ if (frame.contentWindow == source) {
+ resolve(frame);
+ }
+ }, { once: true });
+ frame.src = url;
+ document.body.appendChild(frame);
+ });
+ }
+
+ function click() {
+ synthesizeMouse(document.getElementById('button'), 5, 5, {});
+ }
+
+ function* spec() {
+ let urls = [SAME_ORIGIN, CROSS_ORIGIN_1, CROSS_ORIGIN_2];
+ for (let action of urls) {
+ for (let target of urls) {
+ yield { action: new URL(ACTION, action),
+ target: new URL(TARGET, target) };
+ }
+ }
+ }
+
+ info("Starting tests");
+ let form = document.getElementById('form');
+
+ // The body of the POST needs to be large to trigger this.
+ // 1024*1024 seems to be enough, but scaling to get a margin.
+ document.getElementById('input').value = generateBody(1024*1024);
+ for (let { target, action } of spec()) {
+ add_task(async function runTest() {
+ info(`Running test ${target} with ${action}`);
+ form.action = action;
+ let frame = await withFrame(target);
+ await new Promise(resolve => {
+ addEventListener('message', async function() {
+ info("Form loaded");
+ frame.remove();
+ resolve();
+ }, { once: true });
+
+ click();
+ });
+
+ ok(true, `Submitted to ${origin} with target ${action}`)
+ });
+ };
+ </script>
+ </body>
+</html>
diff --git a/docshell/test/mochitest/test_bug1729662.html b/docshell/test/mochitest/test_bug1729662.html
new file mode 100644
index 0000000000..ec43508494
--- /dev/null
+++ b/docshell/test/mochitest/test_bug1729662.html
@@ -0,0 +1,76 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test back/forward after pushState</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout("Need to wait to make sure an event does not fire");
+
+ async function runTest() {
+ let win = window.open();
+ let goneBackAndForwardOnce = new Promise((resolve) => {
+ let timeoutID;
+
+ // We should only get one load event in win.
+ let bc = new BroadcastChannel("bug1729662");
+ bc.addEventListener("message", () => {
+ bc.addEventListener("message", () => {
+ clearTimeout(timeoutID);
+ resolve(false);
+ });
+ }, { once: true });
+
+ let goneBack = false, goneForward = false;
+ win.addEventListener("popstate", ({ state }) => {
+ // We should only go back and forward once, if we get another
+ // popstate after that then we should fall through to the
+ // failure case below.
+ if (!(goneBack && goneForward)) {
+ // Check if this is the popstate for the forward (the one for
+ // back will have state == undefined).
+ if (state == 1) {
+ ok(goneBack, "We should have gone back before going forward");
+
+ goneForward = true;
+
+ // Wait a bit to make sure there are no more popstate events.
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ timeoutID = setTimeout(resolve, 1000, true);
+
+ return;
+ }
+
+ // Check if we've gone back once before, if we get another
+ // popstate after that then we should fall through to the
+ // failure case below.
+ if (!goneBack) {
+ goneBack = true;
+
+ return;
+ }
+ }
+
+ clearTimeout(timeoutID);
+ resolve(false);
+ });
+ });
+
+ win.location = "file_bug1729662.html";
+
+ ok(await goneBackAndForwardOnce, "Stopped navigating history");
+
+ win.close();
+
+ SimpleTest.finish();
+ }
+ </script>
+</head>
+<body onload="runTest();">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug1740516.html b/docshell/test/mochitest/test_bug1740516.html
new file mode 100644
index 0000000000..b54932c736
--- /dev/null
+++ b/docshell/test/mochitest/test_bug1740516.html
@@ -0,0 +1,79 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test pageshow event order for iframe</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ function waitForPageShow(outer, inner) {
+ return new Promise((resolve) => {
+ let results = [];
+ outer.addEventListener("message", ({ data: persisted }) => {
+ results.push({ name: outer.name, persisted });
+ if (results.length == 2) {
+ resolve(results);
+ }
+ }, { once: true });
+ inner.addEventListener("message", ({ data: persisted }) => {
+ results.push({ name: inner.name, persisted });
+ if (results.length == 2) {
+ resolve(results);
+ }
+ }, { once: true });
+ });
+ }
+ async function runTest() {
+ let outerBC = new BroadcastChannel("bug1740516_1");
+ let innerBC = new BroadcastChannel("bug1740516_1_inner");
+
+ let check = waitForPageShow(outerBC, innerBC).then(([first, second]) => {
+ is(first.name, "bug1740516_1_inner", "Should get pageShow from inner iframe page first.");
+ ok(!first.persisted, "First navigation shouldn't come from BFCache.");
+ is(second.name, "bug1740516_1", "Should get pageShow from outer page second.");
+ ok(!second.persisted, "First navigation shouldn't come from BFCache.");
+ }, () => {
+ ok(false, "The promises should not be rejected.");
+ });
+ window.open("file_bug1740516_1.html", "", "noopener");
+ await check;
+
+ check = waitForPageShow(outerBC, innerBC).then(([first, second]) => {
+ is(first.name, "bug1740516_1_inner", "Should get pageShow from inner iframe page first.");
+ ok(first.persisted, "Second navigation should come from BFCache");
+ is(second.name, "bug1740516_1", "Should get pageShow from outer page second.");
+ ok(second.persisted, "Second navigation should come from BFCache");
+ }, () => {
+ ok(false, "The promises should not be rejected.");
+ });
+ outerBC.postMessage("navigate");
+ await check;
+
+ check = waitForPageShow(outerBC, innerBC).then(([first, second]) => {
+ is(first.name, "bug1740516_1_inner", "Should get pageShow from inner iframe page first.");
+ ok(!first.persisted, "Third navigation should not come from BFCache");
+ is(second.name, "bug1740516_1", "Should get pageShow from outer page second.");
+ ok(!second.persisted, "Third navigation should not come from BFCache");
+ }, () => {
+ ok(false, "The promises should not be rejected.");
+ });
+ outerBC.postMessage("block_bfcache_and_navigate");
+ await check;
+
+ outerBC.postMessage("close");
+
+ outerBC.close();
+ innerBC.close();
+
+ SimpleTest.finish();
+ }
+ </script>
+</head>
+<body onload="runTest();">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug1741132.html b/docshell/test/mochitest/test_bug1741132.html
new file mode 100644
index 0000000000..1ae9727d9c
--- /dev/null
+++ b/docshell/test/mochitest/test_bug1741132.html
@@ -0,0 +1,79 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test form restoration for no-store pages</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ // The number of entries which we keep in the BFCache (see nsSHistory.h).
+ const VIEWER_WINDOW = 3;
+
+ SimpleTest.waitForExplicitFinish();
+
+ function runTest() {
+ let bc = new BroadcastChannel("bug1741132");
+
+ // Setting the pref to 0 should evict all content viewers.
+ let load = SpecialPowers.pushPrefEnv({
+ set: [["browser.sessionhistory.max_total_viewers", 0]],
+ }).then(() => {
+ // Set the pref to VIEWER_WINDOW + 2 now, to be sure we
+ // could fit all entries.
+ SpecialPowers.pushPrefEnv({
+ set: [["browser.sessionhistory.max_total_viewers", VIEWER_WINDOW + 2]],
+ });
+ }).then(() => {
+ return new Promise(resolve => {
+ bc.addEventListener("message", resolve, { once: true });
+ window.open("file_bug1741132.html", "", "noopener");
+ });
+ });
+ // We want to try to keep one entry too many in the BFCache,
+ // so we ensure that there's at least VIEWER_WINDOW + 2
+ // entries in session history (with one for the displayed
+ // page).
+ for (let i = 0; i < VIEWER_WINDOW + 2; ++i) {
+ load = load.then(() => {
+ return new Promise((resolve) => {
+ bc.addEventListener("message", resolve, { once: true });
+ bc.postMessage({ cmd: "load", arg: `file_bug1741132.html?${i}` });
+ });
+ });
+ }
+ load.then(() => {
+ return new Promise((resolve) => {
+ bc.addEventListener("message", ({ data: persisted }) => {
+ resolve(persisted);
+ }, { once: true });
+ // Go back past the first entry that should be in the BFCache.
+ bc.postMessage({ cmd: "go", arg: -(VIEWER_WINDOW + 1) });
+ });
+ }).then((persisted) => {
+ ok(!persisted, "Only 3 pages should be kept in the BFCache");
+ }).then(() => {
+ return new Promise((resolve) => {
+ bc.addEventListener("message", ({ data: persisted }) => {
+ resolve(persisted);
+ }, { once: true });
+ // Go forward to the first entry that should be in the BFCache.
+ bc.postMessage({ cmd: "go", arg: 1 });
+ });
+ }).then((persisted) => {
+ ok(persisted, "3 pages should be kept in the BFCache");
+
+ bc.postMessage("close");
+
+ bc.close();
+
+ SimpleTest.finish();
+ });
+ }
+ </script>
+</head>
+<body onload="runTest();">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug1742865.html b/docshell/test/mochitest/test_bug1742865.html
new file mode 100644
index 0000000000..c8f9a4eca3
--- /dev/null
+++ b/docshell/test/mochitest/test_bug1742865.html
@@ -0,0 +1,137 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Auto refreshing pages shouldn't add an entry to session history</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ const REFRESH_REDIRECT_TIMER = 15;
+
+ // 2 tests (same and cross origin) consisting of 2 refreshes of maximum 1 seconds
+ // 2 tests (same and cross origin) consisting of 2 refreshes of REFRESH_REDIRECT_TIMER seconds
+ // => We need (2 * 1) + (2 * 15) seconds
+ SimpleTest.requestLongerTimeout(3);
+ SimpleTest.waitForExplicitFinish();
+
+ const SJS = new URL("file_bug1742865.sjs", location.href);
+ const SJS_OUTER = new URL("file_bug1742865_outer.sjs", location.href);
+ const SCROLL = 500;
+
+ let tolerance;
+ function setup() {
+ return SpecialPowers.spawn(window.top, [], () => {
+ return SpecialPowers.getDOMWindowUtils(content.window).getResolution();
+ }).then(resolution => {
+ // Allow a half pixel difference if the top document's resolution is lower
+ // than 1.0 because the scroll position is aligned with screen pixels
+ // instead of CSS pixels.
+ tolerance = resolution < 1.0 ? 0.5 : 0.0;
+ });
+ }
+
+ function checkScrollPosition(scrollPosition, shouldKeepScrollPosition) {
+ isfuzzy(scrollPosition, shouldKeepScrollPosition ? SCROLL : 0, tolerance,
+ `Scroll position ${shouldKeepScrollPosition ? "should" : "shouldn't"} be maintained for meta refresh`);
+ }
+
+ function openWindowAndCheckRefresh(url, params, shouldAddToHistory, shouldKeepScrollPosition) {
+ info(`Running test for ${JSON.stringify(params)}`);
+
+ url = new URL(url);
+ Object.entries(params).forEach(([k, v]) => { url.searchParams.append(k, v) });
+ url.searchParams.append("scrollTo", SCROLL);
+
+ let resetURL = new URL(SJS);
+ resetURL.search = "?reset";
+ return fetch(resetURL).then(() => {
+ return new Promise((resolve) => {
+ let count = 0;
+ window.addEventListener("message", function listener({ data: { commandType, commandData = {} } }) {
+ if (commandType == "onChangedInputValue") {
+ let { historyLength, inputValue } = commandData;
+
+ if (shouldAddToHistory) {
+ is(historyLength, count, "Auto-refresh should add entries to session history");
+ } else {
+ is(historyLength, 1, "Auto-refresh shouldn't add entries to session history");
+ }
+
+ is(inputValue, "1234", "Input's value should have been changed");
+
+ win.postMessage("loadNext", "*");
+ return;
+ }
+
+ is(commandType, "pageShow", "Unknown command type");
+
+ let { inputValue, scrollPosition } = commandData;
+
+ switch (++count) {
+ // file_bug1742865.sjs causes 3 loads:
+ // * first load, returns first meta refresh
+ // * second load, caused by first meta refresh, returns second meta refresh
+ // * third load, caused by second meta refresh, doesn't return a meta refresh
+ case 2:
+ checkScrollPosition(scrollPosition, shouldKeepScrollPosition);
+ break;
+ case 3:
+ checkScrollPosition(scrollPosition, shouldKeepScrollPosition);
+ win.postMessage("changeInputValue", "*");
+ break;
+ case 4:
+ win.postMessage("back", "*");
+ break;
+ case 5:
+ is(inputValue, "1234", "Entries for auto-refresh should be attached to session history");
+ checkScrollPosition(scrollPosition, shouldKeepScrollPosition);
+ removeEventListener("message", listener);
+ win.close();
+ resolve();
+ break;
+ }
+ });
+ let win = window.open(url);
+ });
+ });
+ }
+
+ function doTest(seconds, crossOrigin, shouldAddToHistory, shouldKeepScrollPosition) {
+ let params = {
+ seconds,
+ crossOrigin,
+ };
+
+ return openWindowAndCheckRefresh(SJS, params, shouldAddToHistory, shouldKeepScrollPosition).then(() =>
+ openWindowAndCheckRefresh(SJS_OUTER, params, shouldAddToHistory, shouldKeepScrollPosition)
+ );
+ }
+
+ async function runTest() {
+ const FAST = Math.min(1, REFRESH_REDIRECT_TIMER);
+ const SLOW = REFRESH_REDIRECT_TIMER + 1;
+ let tests = [
+ // [ time, crossOrigin, shouldAddToHistory, shouldKeepScrollPosition ]
+ [ FAST, false, false, true ],
+ [ FAST, true, false, false ],
+ [ SLOW, false, false, true ],
+ [ SLOW, true, true, false ],
+ ];
+
+ await setup();
+
+ for (let [ time, crossOrigin, shouldAddToHistory, shouldKeepScrollPosition ] of tests) {
+ await doTest(time, crossOrigin, shouldAddToHistory, shouldKeepScrollPosition);
+ }
+
+ SimpleTest.finish();
+ }
+ </script>
+</head>
+<body onload="runTest();">
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug1743353.html b/docshell/test/mochitest/test_bug1743353.html
new file mode 100644
index 0000000000..a5d88df3f6
--- /dev/null
+++ b/docshell/test/mochitest/test_bug1743353.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test back/forward after pushState</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ function runTest() {
+ let bc = new BroadcastChannel("bug1743353");
+ new Promise((resolve) => {
+ bc.addEventListener("message", () => {
+ resolve();
+ }, { once: true });
+
+ window.open("file_bug1743353.html", "", "noopener");
+ }).then(() => {
+ return new Promise(resolve => {
+ bc.addEventListener("message", () => {
+ resolve();
+ }, { once: true });
+
+ bc.postMessage("load");
+ })
+ }).then(() => {
+ return new Promise(resolve => {
+ let results = [];
+ bc.addEventListener("message", function listener({ data }) {
+ results.push(data);
+ if (results.length == 3) {
+ bc.removeEventListener("message", listener);
+ resolve(results);
+ }
+ });
+
+ bc.postMessage("back");
+ });
+ }).then((results) => {
+ is(results[0], "pagehide", "First event should be 'pagehide'.");
+ is(results[1], "unload", "Second event should be 'unload'.");
+ is(results[2], "pageshow", "Third event should be 'pageshow'.");
+
+ bc.postMessage("close");
+
+ SimpleTest.finish();
+ });
+ }
+ </script>
+</head>
+<body onload="runTest();">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug1747033.html b/docshell/test/mochitest/test_bug1747033.html
new file mode 100644
index 0000000000..539b78fec0
--- /dev/null
+++ b/docshell/test/mochitest/test_bug1747033.html
@@ -0,0 +1,97 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test history after loading multipart</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ function runTest() {
+ let bc = new BroadcastChannel("bug1747033");
+ new Promise(resolve => {
+ bc.addEventListener("message", ({ data: { historyLength } }) => {
+ is(historyLength, 1, "Correct length for first normal load.");
+
+ resolve();
+ }, { once: true });
+
+ window.open("file_bug1747033.sjs", "", "noopener");
+ }).then(() => {
+ return new Promise(resolve => {
+ let loaded = 0;
+ bc.addEventListener("message", function listener({ data: { historyLength } }) {
+ ++loaded;
+
+ is(historyLength, 2, `Correct length for multipart load ${loaded}.`);
+
+ // We want 3 parts in total.
+ if (loaded < 3) {
+ if (loaded == 2) {
+ // We've had 2 parts, make the server send the last part.
+ fetch("file_bug1747033.sjs?sendLastPart");
+ } else {
+ fetch("file_bug1747033.sjs?sendNextPart");
+ }
+ return;
+ }
+
+ bc.removeEventListener("message", listener);
+ resolve();
+ });
+
+ bc.postMessage({ cmd: "load", arg: "file_bug1747033.sjs?multipart" });
+ });
+ }).then(() => {
+ return new Promise(resolve => {
+ bc.addEventListener("message", ({ data: { historyLength } }) => {
+ is(historyLength, 2, "Correct length after calling replaceState in multipart.");
+
+ resolve();
+ }, { once: true });
+
+ bc.postMessage({ cmd: "replaceState", arg: "file_bug1747033.sjs?replaced" });
+ });
+ }).then(() => {
+ return new Promise(resolve => {
+ bc.addEventListener("message", ({ data: { historyLength } }) => {
+ is(historyLength, 3, "Correct length for first normal load after multipart.");
+
+ resolve();
+ }, { once: true });
+
+ bc.postMessage({ cmd: "load", arg: "file_bug1747033.sjs" });
+ });
+ }).then(() => {
+ return new Promise(resolve => {
+ let goneBack = 0;
+ bc.addEventListener("message", function listener({ data: { historyLength } }) {
+ ++goneBack;
+
+ is(historyLength, 3, "Correct length after going back.");
+
+ if (goneBack == 1) {
+ bc.postMessage({ cmd: "back" });
+ } else if (goneBack == 2) {
+ bc.removeEventListener("message", listener);
+ resolve();
+ }
+ });
+
+ bc.postMessage({ cmd: "back" });
+ });
+ }).then(() => {
+ bc.postMessage({ cmd: "close" });
+
+ SimpleTest.finish();
+ });
+ }
+ </script>
+</head>
+<body onload="runTest();">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug1773192.html b/docshell/test/mochitest/test_bug1773192.html
new file mode 100644
index 0000000000..d4c42dc1a7
--- /dev/null
+++ b/docshell/test/mochitest/test_bug1773192.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test referrer with going back</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ // file_bug1773192_1.html will send a message with some data on pageshow.
+ function waitForData(bc) {
+ return new Promise(resolve => {
+ bc.addEventListener(
+ "message",
+ ({ data }) => {
+ resolve(data);
+ },
+ { once: true }
+ );
+ });
+ }
+ async function runTest() {
+ let bc = new BroadcastChannel("bug1743353");
+
+ let getData = waitForData(bc);
+
+ window.open("file_bug1773192_1.html", "", "noreferrer");
+
+ await getData.then(({ referrer }) => {
+ is(referrer, "", "Referrer should be empty at first.");
+ });
+
+ getData = waitForData(bc);
+
+ // When file_bug1773192_1.html receives this message it will navigate to
+ // file_bug1773192_2.html. file_bug1773192_2.html removes itself from
+ // history with replaceState and submits a form with the POST method to
+ // file_bug1773192_3.sjs. file_bug1773192_3.sjs goes back in history.
+ // We should end up back at file_bug1773192_1.html, which will send a
+ // message with some data on pageshow.
+ bc.postMessage("next");
+
+ await getData.then(({ location, referrer }) => {
+ let firstURL = new URL("file_bug1773192_1.html", location).toString();
+ is(location, firstURL, "Location should be the first page again.");
+ is(referrer, firstURL, "Referrer should also be the first page.");
+ });
+
+ bc.postMessage("close");
+
+ SimpleTest.finish();
+ }
+ </script>
+</head>
+<body onload="runTest();">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug385434.html b/docshell/test/mochitest/test_bug385434.html
new file mode 100644
index 0000000000..8c3ea4a266
--- /dev/null
+++ b/docshell/test/mochitest/test_bug385434.html
@@ -0,0 +1,211 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=385434
+-->
+<head>
+ <title>Test for Bug 385434</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=385434">Mozilla Bug 385434</a>
+<p id="display"></p>
+<div id="content">
+ <iframe id="frame" style="height:100px; width:100px; border:0"></iframe>
+ <div id="status" style="display: none"></div>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 385434 **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+SimpleTest.expectAssertions(0, 1); // bug 1333702
+
+var gNumHashchanges = 0;
+var gCallbackOnIframeLoad = false;
+var gSampleEvent;
+
+function statusMsg(msg) {
+ var msgElem = document.createElement("p");
+ msgElem.appendChild(document.createTextNode(msg));
+
+ document.getElementById("status").appendChild(msgElem);
+}
+
+function longWait() {
+ setTimeout(function() { gGen.next(); }, 1000);
+}
+
+// onIframeHashchange, onIframeLoad, and onIframeScroll are all called by the
+// content we load into our iframe in order to notify the parent frame of an
+// event which was fired.
+function onIframeHashchange() {
+ gNumHashchanges++;
+ gGen.next();
+}
+
+function onIframeLoad() {
+ if (gCallbackOnIframeLoad) {
+ gCallbackOnIframeLoad = false;
+ gGen.next();
+ }
+}
+
+function onIframeScroll() {
+ is(gNumHashchanges, 0, "onscroll should fire before onhashchange.");
+}
+
+function enableIframeLoadCallback() {
+ gCallbackOnIframeLoad = true;
+}
+
+function noEventExpected(msg) {
+ is(gNumHashchanges, 0, msg);
+
+ // Even if there's an error, set gNumHashchanges to 0 so other tests don't
+ // fail.
+ gNumHashchanges = 0;
+}
+
+function eventExpected(msg) {
+ is(gNumHashchanges, 1, msg);
+
+ // Eat up this event, whether the test above was true or not
+ gNumHashchanges = 0;
+}
+
+/*
+ * The hashchange event is dispatched asynchronously, so if we want to observe
+ * it, we have to yield within run_test(), transferring control back to the
+ * event loop.
+ *
+ * When we're expecting our iframe to observe a hashchange event after we poke
+ * it, we just yield and wait for onIframeHashchange() to call gGen.next() and
+ * wake us up.
+ *
+ * When we're testing to ensure that the iframe doesn't dispatch a hashchange
+ * event, we try to hook onto the iframe's load event. We call
+ * enableIframeLoadCallback(), which causes onIframeLoad() to call gGen.next()
+ * upon the next observed load. After we get our callback, we check that a
+ * hashchange didn't occur.
+ *
+ * We can't always just wait for page load in order to observe that a
+ * hashchange didn't happen. In these cases, we call longWait() and yield
+ * until either a hashchange occurs or longWait's callback is scheduled. This
+ * is something of a hack; it's entirely possible that longWait won't wait long
+ * enough, and we won't observe what should have been a failure of the test.
+ * But it shouldn't happen that good code will randomly *fail* this test.
+ */
+function* run_test() {
+ /*
+ * TEST 1 tests that:
+ * <body onhashchange = ... > works,
+ * the event is (not) fired at the correct times
+ */
+ var frame = document.getElementById("frame");
+ var frameCw = frame.contentWindow;
+
+ enableIframeLoadCallback();
+ frameCw.document.location = "file_bug385434_1.html";
+ // Wait for the iframe to load and for our callback to fire
+ yield undefined;
+
+ noEventExpected("No hashchange expected initially.");
+
+ sendMouseEvent({type: "click"}, "link1", frameCw);
+ yield undefined;
+ eventExpected("Clicking link1 should trigger a hashchange.");
+
+ sendMouseEvent({type: "click"}, "link1", frameCw);
+ longWait();
+ yield undefined;
+ // succeed if a hashchange event wasn't triggered while we were waiting
+ noEventExpected("Clicking link1 again should not trigger a hashchange.");
+
+ sendMouseEvent({type: "click"}, "link2", frameCw);
+ yield undefined;
+ eventExpected("Clicking link2 should trigger a hashchange.");
+
+ frameCw.history.go(-1);
+ yield undefined;
+ eventExpected("Going back should trigger a hashchange.");
+
+ frameCw.history.go(1);
+ yield undefined;
+ eventExpected("Going forward should trigger a hashchange.");
+
+ // window.location has a trailing '#' right now, so we append "link1", not
+ // "#link1".
+ frameCw.window.location = frameCw.window.location + "link1";
+ yield undefined;
+ eventExpected("Assigning to window.location should trigger a hashchange.");
+
+ // Set up history in the iframe which looks like:
+ // file_bug385434_1.html#link1
+ // file_bug385434_2.html
+ // file_bug385434_1.html#foo <-- current page
+ enableIframeLoadCallback();
+ frameCw.window.location = "file_bug385434_2.html";
+ yield undefined;
+
+ enableIframeLoadCallback();
+ frameCw.window.location = "file_bug385434_1.html#foo";
+ yield undefined;
+
+ // Now when we do history.go(-2) on the frame, it *shouldn't* fire a
+ // hashchange. Although the URIs differ only by their hashes, they belong to
+ // two different Documents.
+ frameCw.history.go(-2);
+ longWait();
+ yield undefined;
+ noEventExpected("Moving between different Documents shouldn't " +
+ "trigger a hashchange.");
+
+ /*
+ * TEST 2 tests that:
+ * <frameset onhashchange = ... > works,
+ * the event is targeted at the window object
+ * the event's cancelable, bubbles settings are correct
+ */
+
+ enableIframeLoadCallback();
+ frameCw.document.location = "file_bug385434_2.html";
+ yield undefined;
+
+ frameCw.document.location = "file_bug385434_2.html#foo";
+ yield undefined;
+
+ eventExpected("frame onhashchange should fire events.");
+ // iframe should set gSampleEvent
+ is(gSampleEvent.target, frameCw,
+ "The hashchange event should be targeted to the window.");
+ is(gSampleEvent.type, "hashchange",
+ "Event type should be 'hashchange'.");
+ is(gSampleEvent.cancelable, false,
+ "The hashchange event shouldn't be cancelable.");
+ is(gSampleEvent.bubbles, false,
+ "The hashchange event should not bubble.");
+
+ /*
+ * TEST 3 tests that:
+ * hashchange is dispatched if the current document readyState is
+ * not "complete" (bug 504837).
+ */
+ frameCw.document.location = "file_bug385434_3.html";
+ yield undefined;
+ eventExpected("Hashchange should fire even if the document " +
+ "hasn't finished loading.");
+
+ SimpleTest.finish();
+}
+
+var gGen = run_test();
+gGen.next();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug387979.html b/docshell/test/mochitest/test_bug387979.html
new file mode 100644
index 0000000000..bd92b0ad08
--- /dev/null
+++ b/docshell/test/mochitest/test_bug387979.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=387979
+-->
+<head>
+ <title>Test for Bug 387979</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=387979">Mozilla Bug 387979</a>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 387979 **/
+function a(s) {
+ var r;
+ try { r = frames[0].document.body; } catch (e) { r = e; }
+ is(r instanceof frames[0].HTMLBodyElement, true, "Can't get body" + s);
+}
+var p = 0;
+function b() {
+ switch (++p) {
+ case 1:
+ frames[0].location = "about:blank";
+ break;
+ case 2:
+ a("before reload");
+ frames[0].location.reload();
+ break;
+ case 3:
+ a("after reload");
+ SimpleTest.finish();
+ break;
+ }
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+<p id="display">
+ <iframe onload="b()"></iframe>
+ <pre id="p">-</pre>
+</p>
+</body>
+</html>
+
diff --git a/docshell/test/mochitest/test_bug402210.html b/docshell/test/mochitest/test_bug402210.html
new file mode 100644
index 0000000000..326f98cf9f
--- /dev/null
+++ b/docshell/test/mochitest/test_bug402210.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+While working on bug 402210, it came up that the code was doing
+
+a.href = proto + host
+
+which technically produces "https:host" instead of "https://host" and
+that the code was relying on href's setting having fixup behaviour
+for this kind of thing.
+
+If we rely on it, we might as well test for it, even if it isn't the
+problem 402210 was meant to fix.
+
+https://bugzilla.mozilla.org/show_bug.cgi?id=402210
+-->
+<head>
+ <title>Test for Bug 402210</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=402210">Mozilla Bug 402210</a>
+<p id="display">
+ <a id="testlink">Test Link</a>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+function runTest() {
+ $("testlink").href = "https:example.com";
+ is($("testlink").href, "https://example.com/", "Setting href on an anchor tag should fixup missing slashes after https protocol");
+
+ $("testlink").href = "ftp:example.com";
+ is($("testlink").href, "ftp://example.com/", "Setting href on an anchor tag should fixup missing slashes after non-http protocol");
+
+ SimpleTest.finish();
+}
+
+addLoadEvent(runTest);
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/docshell/test/mochitest/test_bug404548.html b/docshell/test/mochitest/test_bug404548.html
new file mode 100644
index 0000000000..495341e7b5
--- /dev/null
+++ b/docshell/test/mochitest/test_bug404548.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=404548
+-->
+<head>
+ <title>Test for Bug 404548</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=404548">Mozilla Bug 404548</a>
+<p id="display">
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 404548 **/
+var firstRemoved = false;
+var secondHidden = false;
+
+SimpleTest.waitForExplicitFinish();
+
+var w = window.open("bug404548-subframe.html", "", "width=10,height=10");
+
+function finishTest() {
+ is(firstRemoved, true, "Should have removed iframe from the DOM");
+ is(secondHidden, true, "Should have fired pagehide on second kid");
+ w.close();
+ SimpleTest.finish();
+}
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/docshell/test/mochitest/test_bug413310.html b/docshell/test/mochitest/test_bug413310.html
new file mode 100644
index 0000000000..3d3ba56252
--- /dev/null
+++ b/docshell/test/mochitest/test_bug413310.html
@@ -0,0 +1,106 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=413310
+-->
+<head>
+ <title>Test for Bug 413310</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=413310">Mozilla Bug 413310</a>
+<p id="display">
+<script class="testbody" type="text/javascript">
+
+if (navigator.platform.startsWith("Mac")) {
+ SimpleTest.expectAssertions(0, 2);
+} else {
+ SimpleTest.expectAssertions(0, 1);
+}
+
+/** Test for Bug 413310 **/
+
+// NOTE: If we ever make subframes do bfcache stuff, this test will need to be
+// modified accordingly! It assumes that subframes do NOT get bfcached.
+var onloadCount = 0;
+
+var step = -1; // One increment will come from the initial subframe onload.
+ // Note that this script should come before the subframe,
+ // so that doNextStep is defined when its onload handler fires.
+
+var textContent;
+
+SimpleTest.waitForExplicitFinish();
+
+addLoadEvent(doNextStep);
+
+function doNextStep() {
+ ++step;
+ switch (step) {
+ case 1:
+ is(onloadCount, 1, "Loaded initial page");
+ is($("i").contentWindow.location.href,
+ location.href.replace(/test_bug413310.html/,
+ "bug413310-subframe.html"),
+ "Unexpected subframe location after initial load");
+ $("i").contentDocument.forms[0].submit();
+ break;
+ case 2:
+ is(onloadCount, 2, "Loaded POST result");
+
+ is($("i").contentWindow.location.href,
+ location.href.replace(/test_bug413310.html/,
+ "bug413310-post.sjs"),
+ "Unexpected subframe location after POST load");
+
+ textContent = $("i").contentDocument.body.textContent;
+ isDeeply(textContent.match(/^POST /), ["POST "], "Not a POST?");
+
+ $("i").contentWindow.location.hash = "foo";
+ setTimeout(doNextStep, 0);
+ break;
+ case 3:
+ is(onloadCount, 2, "Anchor scroll should not fire onload");
+ is($("i").contentWindow.location.href,
+ location.href.replace(/test_bug413310.html/,
+ "bug413310-post.sjs#foo"),
+ "Unexpected subframe location after anchor scroll");
+ is(textContent, $("i").contentDocument.body.textContent,
+ "Did a load when scrolling?");
+ $("i").contentWindow.location.href = "bug413310-subframe.html";
+ break;
+ case 4:
+ is(onloadCount, 3, "Done new load");
+ is($("i").contentWindow.location.href,
+ location.href.replace(/test_bug413310.html/,
+ "bug413310-subframe.html"),
+ "Unexpected subframe location after new load");
+ history.back();
+ break;
+ case 5:
+ is(onloadCount, 4,
+ "History traversal didn't fire onload: bfcache issues!");
+ is($("i").contentWindow.location.href,
+ location.href.replace(/test_bug413310.html/,
+ "bug413310-post.sjs#foo"),
+ "Unexpected subframe location");
+ is(textContent, $("i").contentDocument.body.textContent,
+ "Did a load when going back?");
+ SimpleTest.finish();
+ break;
+ }
+}
+</script>
+<!-- Use a timeout in onload so that we don't do a load immediately inside onload -->
+<iframe id="i" src="bug413310-subframe.html" onload="setTimeout(doNextStep, 20)">
+</iframe>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
+
diff --git a/docshell/test/mochitest/test_bug475636.html b/docshell/test/mochitest/test_bug475636.html
new file mode 100644
index 0000000000..fb1827ad04
--- /dev/null
+++ b/docshell/test/mochitest/test_bug475636.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=475636
+Test that refresh to data: URIs don't inherit the principal
+-->
+<head>
+ <title>Test for Bug 475636</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="gen.next()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=475636">Mozilla Bug 475636</a>
+
+<div id="content" style="display: none">
+
+</div>
+<iframe id=loader></iframe>
+<pre id="test">
+<script class="testbody" type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+var gen = runTests();
+
+window.private = 42;
+
+window.addEventListener("message", function(e) {
+ gen.next(e.data);
+});
+
+var url = "file_bug475636.sjs?";
+
+function* runTests() {
+ var loader = document.getElementById("loader");
+ for (var testNum = 1; ; ++testNum) {
+ loader.src = url + testNum;
+ let res = (yield);
+ if (res == "done") {
+ SimpleTest.finish();
+ return;
+ }
+ is(res, "pass");
+ }
+}
+
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug509055.html b/docshell/test/mochitest/test_bug509055.html
new file mode 100644
index 0000000000..d6d871dc99
--- /dev/null
+++ b/docshell/test/mochitest/test_bug509055.html
@@ -0,0 +1,115 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=509055
+-->
+<head>
+ <title>Test for Bug 509055</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=509055">Mozilla Bug 509055</a>
+<p id="display"></p>
+<div id="status"></div>
+<div id="content">
+</div>
+<pre id="test">
+ <script type="application/javascript">
+
+ /** Test for Bug 509055 **/
+
+ SimpleTest.waitForExplicitFinish();
+
+ var gGen;
+
+ function shortWait() {
+ setTimeout(function() { gGen.next(); }, 0, false);
+ }
+
+ function onChildHashchange(e) {
+ // gGen might be undefined when we refresh the page, so we have to check here
+ dump("onChildHashchange() called.\n");
+ if (gGen)
+ gGen.next();
+ }
+
+ function onChildLoad(e) {
+ if (gGen)
+ gGen.next();
+ }
+
+ async function* runTest() {
+ var popup = window.open("file_bug509055.html", "popup 0",
+ "height=200,width=200,location=yes," +
+ "menubar=yes,status=yes,toolbar=yes,dependent=yes");
+ popup.hashchangeCallback = onChildHashchange;
+ popup.onload = onChildLoad;
+ dump("Waiting for initial load.\n");
+ yield undefined;
+
+ // Without this wait, the change to location.hash below doesn't create a
+ // SHEntry or enable the back button.
+ shortWait();
+ dump("Got initial load. Spinning event loop.\n");
+ yield undefined;
+
+ popup.location.hash = "#1";
+ dump("Waiting for hashchange.\n");
+ yield undefined;
+
+ popup.history.back();
+ dump("Waiting for second hashchange.\n");
+ yield undefined; // wait for hashchange
+
+ popup.document.title = "Changed";
+
+ // Wait for listeners to be notified of the title change.
+ shortWait();
+ dump("Got second hashchange. Spinning event loop.\n");
+ yield undefined;
+
+ let sheTitle = "";
+ if (!SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ var sh = SpecialPowers.wrap(popup)
+ .docShell
+ .QueryInterface(SpecialPowers.Ci.nsIWebNavigation)
+ .sessionHistory;
+
+ // Get the title of the inner popup's current SHEntry
+ sheTitle = sh.legacySHistory.getEntryAtIndex(sh.index).title;
+ } else {
+ let chromeScript = SpecialPowers.loadChromeScript(() => {
+ /* eslint-env mozilla/chrome-script */
+ addMessageListener("getTitle", browsingContext => {
+ // eslint-disable-next-line no-shadow
+ let sh = browsingContext.sessionHistory;
+ let title = sh.getEntryAtIndex(sh.index).title;
+ sendAsyncMessage("title", title);
+ });
+ });
+
+ let p = chromeScript.promiseOneMessage("title");
+ let browsingContext = SpecialPowers.wrap(popup)
+ .docShell.browsingContext;
+ chromeScript.sendAsyncMessage("getTitle", browsingContext);
+ sheTitle = await p;
+ chromeScript.destroy();
+ }
+ is(sheTitle, "Changed", "SHEntry's title should change when we change.");
+
+ popup.close();
+
+ SimpleTest.executeSoon(SimpleTest.finish);
+ }
+
+ window.addEventListener("load", function() {
+ gGen = runTest();
+ gGen.next();
+ });
+
+ </script>
+
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug511449.html b/docshell/test/mochitest/test_bug511449.html
new file mode 100644
index 0000000000..de66eb756d
--- /dev/null
+++ b/docshell/test/mochitest/test_bug511449.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=511449
+-->
+<head>
+ <title>Test for Bug 511449</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/NativeKeyCodes.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=511449">Mozilla Bug 511449</a>
+<p id="display"></p>
+<div id="status"></div>
+<div id="content">
+</div>
+<input type="text" id="input">
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 511449 **/
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+window.addEventListener("load", runTest);
+
+var win = null;
+
+function runTest() {
+ document.getElementById("input").focus();
+ win = window.open("file_bug511449.html", "");
+ SimpleTest.waitForFocus(runNextTest, win);
+}
+
+function runNextTest() {
+ var didClose = false;
+ win.onunload = function() {
+ didClose = true;
+ };
+ synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, MAC_VK_ANSI_W, {metaKey: 1}, "w", "w");
+
+ setTimeout(function() {
+ ok(didClose, "Cmd+W should have closed the tab");
+ if (!didClose) {
+ win.close();
+ }
+ SimpleTest.finish();
+ }, 1000);
+}
+
+</script>
+
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug529119-1.html b/docshell/test/mochitest/test_bug529119-1.html
new file mode 100644
index 0000000000..1c89780fc7
--- /dev/null
+++ b/docshell/test/mochitest/test_bug529119-1.html
@@ -0,0 +1,110 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>Test bug 529119</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+
+var workingURL = "http://mochi.test:8888/tests/docshell/test/mochitest/bug529119-window.html";
+var faultyURL = "https://www.some-nonexistent-domain-27489274c892748217cn2384.test/";
+
+var w = null;
+var phase = 0;
+var gotWrongPageOnTryAgainClick = false;
+// Token that represents which page we currently have loaded.
+var token = 0;
+
+function delay(msec) {
+ return new Promise(resolve => setTimeout(resolve, msec));
+}
+
+async function assignToken(tokenToAssign) {
+ await SpecialPowers.spawn(w, [tokenToAssign],
+ newToken => { this.content.token = newToken });
+}
+
+async function pollForPage(win) {
+ while (true) {
+ try {
+ // When we do our navigation, there may be an interstitial about:blank
+ // page if the navigation involves a process switch. That about:blank
+ // will exist between the new process's docshell being created and the
+ // actual page that's being loaded loading (which can happen async from
+ // the docshell creation). We want to avoid treating the initial
+ // about:blank as a new page.
+ //
+ // We could conceivably expose Document::IsInitialDocument() as a
+ // ChromeOnly thing and use it here, but let's just filter out all
+ // about:blank, since we don't expect any in this test.
+ var haveNewPage = await SpecialPowers.spawn(w, [token],
+ currentToken => this.content.token != currentToken &&
+ this.content.location.href != "about:blank");
+
+ if (haveNewPage) {
+ ++token;
+ assignToken(token);
+ break;
+ }
+ } catch (e) {
+ // Something went wrong; just keep waiting.
+ }
+
+ await delay(100);
+ }
+}
+
+async function windowLoaded() {
+ switch (phase) {
+ case 0:
+ assignToken(token);
+
+ /* 2. We have succeededfully loaded a page, now go to a faulty URL */
+ window.setTimeout(function() {
+ w.location.href = faultyURL;
+ }, 0);
+
+ phase = 1;
+
+ await pollForPage(w);
+ is(await SpecialPowers.spawn(w, [], () => this.content.location.href),
+ faultyURL,
+ "Is on an error page initially");
+
+ /* 3. now, while we are on the error page, try to reload it, actually
+ click the "Try Again" button */
+ SpecialPowers.spawn(w, [], () => this.content.location.reload());
+
+ await pollForPage(w);
+
+ /* 4-finish, check we are still on the error page */
+ is(await SpecialPowers.spawn(w, [], () => this.content.location.href),
+ faultyURL,
+ "Is on an error page");
+ is(gotWrongPageOnTryAgainClick, false,
+ "Must not get www.example.com page on reload of an error page");
+ w.close();
+ SimpleTest.finish();
+ break;
+
+ case 1:
+ /* 4-check, we must not get here! */
+ gotWrongPageOnTryAgainClick = true;
+ break;
+ }
+}
+
+function startTest() {
+ /* 1. load a URL that leads to an error page */
+ w = window.open(workingURL);
+}
+
+</script>
+</head>
+<body onload="startTest();">
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug529119-2.html b/docshell/test/mochitest/test_bug529119-2.html
new file mode 100644
index 0000000000..a8bd57d4f7
--- /dev/null
+++ b/docshell/test/mochitest/test_bug529119-2.html
@@ -0,0 +1,116 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>Test bug 529119</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+
+var workingURL = "http://mochi.test:8888/tests/docshell/test/mochitest/bug529119-window.html";
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+var faultyURL = "http://some-nonexistent-domain-27489274c892748217cn2384.test/";
+
+var w = null;
+var phase = 0;
+var isWindowLoaded = false;
+// Token that represents which page we currently have loaded.
+var token = 0;
+
+function delay(msec) {
+ return new Promise(resolve => setTimeout(resolve, msec));
+}
+
+async function assignToken(tokenToAssign) {
+ await SpecialPowers.spawn(w, [tokenToAssign],
+ newToken => { this.content.token = newToken });
+}
+
+// Returns when a new page is loaded and returns whether that page is an
+// error page.
+async function pollForPage(win) {
+ while (true) {
+ try {
+ // When we do our navigation, there may be an interstitial about:blank
+ // page if the navigation involves a process switch. That about:blank
+ // will exist between the new process's docshell being created and the
+ // actual page that's being loaded loading (which can happen async from
+ // the docshell creation). We want to avoid treating the initial
+ // about:blank as a new page.
+ //
+ // We could conceivably expose Document::IsInitialDocument() as a
+ // ChromeOnly thing and use it here, but let's just filter out all
+ // about:blank, since we don't expect any in this test.
+ var haveNewPage = await SpecialPowers.spawn(w, [token],
+ currentToken => this.content.token != currentToken &&
+ this.content.location.href != "about:blank");
+
+ if (haveNewPage) {
+ ++token;
+ assignToken(token);
+
+ // In this test, error pages are non-same-origin with us, and non-error
+ // pages are same-origin.
+ let haveErrorPage = false;
+ try {
+ win.document.title;
+ } catch (ex) {
+ haveErrorPage = true;
+ }
+ return haveErrorPage;
+ }
+ } catch (e) {
+ // Something went wrong; just keep waiting.
+ }
+
+ await delay(100);
+ }
+}
+
+async function windowLoaded() {
+ // The code under here should only be run once
+ // The test popup window workingURL was already opened
+ if (isWindowLoaded)
+ return;
+ isWindowLoaded = true;
+
+ assignToken(token);
+
+ /* 2. We have successfully loaded a page, now go to a faulty URL */
+ // XXX The test fails when we change the location synchronously
+ window.setTimeout(function() {
+ w.location.href = faultyURL;
+ }, 0);
+
+ ok(await pollForPage(w), "Waiting for error page succeeded");
+ /* 3. now, while we are on the error page, navigate back */
+ try {
+ // We need the SpecialPowers bit, because this is a cross-origin window
+ // and we normally can't touch .history on those.
+ await SpecialPowers.spawn(w, [], () => this.content.history.back());
+ } catch (ex) {
+ ok(false, "w.history.back() threw " + ex);
+ }
+
+ ok(!await pollForPage(w), "Waiting for original page succeeded");
+ /* 4-finish, check we are back at the original page */
+ is(await SpecialPowers.spawn(w, [], () => this.content.location.href),
+ workingURL,
+ "Is on the previous page");
+ w.close();
+ SimpleTest.finish();
+}
+
+function startTest() {
+ /* 1. load a URL that leads to an error page */
+ w = window.open(workingURL);
+}
+
+</script>
+</head>
+<body onload="startTest();">
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug530396.html b/docshell/test/mochitest/test_bug530396.html
new file mode 100644
index 0000000000..fa3ddc6db6
--- /dev/null
+++ b/docshell/test/mochitest/test_bug530396.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=530396
+-->
+<head>
+ <title>Test for Bug 530396</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=530396">Mozilla Bug 530396</a>
+
+<p>
+
+<iframe id="testFrame" src="http://mochi.test:8888/tests/docshell/test/mochitest/bug530396-subframe.html"></iframe>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+// NOTE: If we ever make subframes do bfcache stuff, this test will need to be
+// modified accordingly! It assumes that subframes do NOT get bfcached.
+var onloadCount = 0;
+
+var step = 0;
+
+var gTestFrame = document.getElementById("testFrame");
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+addLoadEvent(doNextStep);
+
+function doNextStep() {
+ ++step;
+ switch (step) {
+ case 1:
+ is(onloadCount, 1, "Loaded initial page");
+ sendMouseEvent({type: "click"}, "target2", gTestFrame.contentWindow);
+ window.setTimeout(doNextStep, 1000);
+ break;
+
+ case 2:
+ is(onloadCount, 1, "opener must be null");
+ sendMouseEvent({type: "click"}, "target1", gTestFrame.contentWindow);
+ break;
+
+ case 3:
+ is(onloadCount, 2, "don't send referrer with rel=referrer");
+ SimpleTest.finish();
+ break;
+ }
+}
+</script>
+</pre>
+</html>
diff --git a/docshell/test/mochitest/test_bug540462.html b/docshell/test/mochitest/test_bug540462.html
new file mode 100644
index 0000000000..6560d604a2
--- /dev/null
+++ b/docshell/test/mochitest/test_bug540462.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=540462
+-->
+<head>
+ <title>Test for Bug 540462</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=540462">Mozilla Bug 540462</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 540462 **/
+
+var win;
+function runTest() {
+ win = window.open("file_bug540462.html", "", "width=100,height=100");
+}
+
+var dwlCount = 0;
+var originalURL;
+function documentWriteLoad() {
+ if (++dwlCount == 1) {
+ originalURL = win.document.body.firstChild.href;
+ } else if (dwlCount == 2) {
+ is(win.document.body.firstChild.href, originalURL, "Wrong href!");
+ win.close();
+ SimpleTest.finish();
+ }
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug551225.html b/docshell/test/mochitest/test_bug551225.html
new file mode 100644
index 0000000000..999cb6b945
--- /dev/null
+++ b/docshell/test/mochitest/test_bug551225.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=551225
+-->
+<head>
+ <title>Test for Bug 551225</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=551225">Mozilla Bug 551225</a>
+
+<script type="application/javascript">
+
+/** Test for Bug 551225 **/
+
+var obj = {
+ a: new Date("1/1/2000"),
+ b: /^foo$/,
+ c: "bar",
+};
+
+history.replaceState(obj, "", "");
+is(history.state.a.toString(), new Date("1/1/2000").toString(), "Date object.");
+is(history.state.b.toString(), "/^foo$/", "Regex");
+is(history.state.c, "bar", "Other state");
+
+</script>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug570341.html b/docshell/test/mochitest/test_bug570341.html
new file mode 100644
index 0000000000..363f985407
--- /dev/null
+++ b/docshell/test/mochitest/test_bug570341.html
@@ -0,0 +1,142 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=570341
+-->
+<head>
+ <title>Test for Bug 570341</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+<script>
+ var start = Date.now();
+ var moments = {};
+
+ var unload = 0;
+ var wasEnabled = true;
+
+ function collectMoments() {
+ var win = frames[0];
+ var timing = (win.performance && win.performance.timing) || {};
+ for (let p in timing) {
+ moments[p] = timing[p];
+ }
+ for (let p in win) {
+ if (p.substring(0, 9) == "_testing_") {
+ moments[p.substring(9)] = win[p];
+ }
+ }
+ moments.evt_unload = unload;
+ return moments;
+ }
+
+ function showSequence(node) {
+ while (node.firstChild) {
+ node.firstChild.remove();
+ }
+ var sequence = [];
+ for (var p in moments) {
+ sequence.push(p);
+ }
+ sequence.sort(function(a, b) {
+ return moments[a] - moments[b];
+ });
+ var table = document.createElement("table");
+ node.appendChild(table);
+ var row = document.createElement("tr");
+ table.appendChild(row);
+ var cell = document.createElement("td");
+ row.appendChild(cell);
+ cell.appendChild(document.createTextNode("start"));
+ cell = document.createElement("td");
+ row.appendChild(cell);
+ cell.appendChild(document.createTextNode(start));
+ for (var i = 0; i < sequence.length; ++i) {
+ var prop = sequence[i];
+ row = document.createElement("tr");
+ table.appendChild(row);
+ cell = document.createElement("td");
+ row.appendChild(cell);
+ cell.appendChild(document.createTextNode(prop));
+ cell = document.createElement("td");
+ row.appendChild(cell);
+ cell.appendChild(document.createTextNode(moments[prop]));
+ }
+ }
+
+ function checkValues() {
+ var win = frames[0];
+ ok(win.performance,
+ "window.performance is missing or not accessible for frame");
+ ok(!win.performance || win.performance.timing,
+ "window.performance.timing is missing or not accessible for frame");
+ collectMoments();
+
+ var sequences = [
+ ["navigationStart", "unloadEventStart", "unloadEventEnd"],
+ ["navigationStart", "fetchStart", "domainLookupStart", "domainLookupEnd",
+ "connectStart", "connectEnd", "requestStart", "responseStart", "responseEnd"],
+ ["responseStart", "domLoading", "domInteractive", "domComplete"],
+ ["domContentLoadedEventStart", "domContentLoadedEventEnd",
+ "loadEventStart", "loadEventEnd"],
+ ];
+
+ for (var i = 0; i < sequences.length; ++i) {
+ var seq = sequences[i];
+ for (var j = 0; j < seq.length; ++j) {
+ var prop = seq[j];
+ if (j > 0) {
+ var prevProp = seq[j - 1];
+ ok(moments[prevProp] <= moments[prop],
+ ["Expected ", prevProp, " to happen before ", prop,
+ ", got ", prevProp, " = ", moments[prevProp],
+ ", ", prop, " = ", moments[prop]].join(""));
+ }
+ }
+ }
+
+ SimpleTest.finish();
+ }
+
+window.onload = function() {
+ var win = frames[0];
+ win.addEventListener("unload", function() {
+ unload = Date.now();
+ }, true);
+ var seenLoad = 0;
+ win.addEventListener("load", function() {
+ seenLoad = Date.now();
+ }, true);
+ frames[0].location = "bug570341_recordevents.html";
+ var interval = setInterval(function() {
+ // time constants here are arbitrary, chosen to allow the test to pass
+ var stopPolling = (win.performance && win.performance.loadEventEnd) ||
+ (seenLoad && Date.now() >= seenLoad + 3000) ||
+ Date.now() >= start + 30000;
+ if (stopPolling) {
+ clearInterval(interval);
+ checkValues();
+ } else if (win._testing_evt_load) {
+ seenLoad = Date.now();
+ }
+ }, 100);
+};
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=570341">Mozilla Bug 570341</a>
+<div id="frames">
+<iframe name="child0" src="navigation/blank.html"></iframe>
+</div>
+<button type="button" onclick="showSequence(document.getElementById('display'))">
+ Show Events</button>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug580069.html b/docshell/test/mochitest/test_bug580069.html
new file mode 100644
index 0000000000..bb0a3bc823
--- /dev/null
+++ b/docshell/test/mochitest/test_bug580069.html
@@ -0,0 +1,58 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=580069
+-->
+<head>
+ <title>Test for Bug 580069</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=580069">Mozilla Bug 580069</a>
+
+<script type="application/javascript">
+
+add_task(async function() {
+ let iframe = document.createElement("iframe");
+ iframe.setAttribute("src", "file_bug580069_1.html");
+
+ // Insert the initial <iframe> document, and wait for page1Load to be called
+ // after it loads.
+ document.body.appendChild(iframe);
+ await new Promise(resolve => {
+ window.page1Load = resolve;
+ });
+ let iframeCw = iframe.contentWindow;
+
+ info("iframe's location is: " + iframeCw.location + "\n");
+
+ // Submit the forum and wait for the initial page load using a POST load.
+ iframeCw.document.getElementById("form").submit();
+ let method1 = await new Promise(resolve => {
+ window.page2Load = resolve;
+ });
+ info("iframe's location is: " + iframeCw.location + ", method is " + method1 + "\n");
+ is(method1, "POST", "Method for first load should be POST.");
+
+ // Push a new state, and refresh the page. This refresh shouldn't pop up the
+ // "are you sure you want to refresh a page with POST data?" dialog. If it
+ // does, this test will hang and fail, and we'll see 'Refreshing iframe...' at
+ // the end of the test log.
+ iframeCw.history.replaceState("", "", "?replaced");
+
+ info("Refreshing iframe...\n");
+ iframeCw.location.reload();
+ let method2 = await new Promise(resolve => {
+ window.page2Load = resolve;
+ });
+
+ info("iframe's location is: " + iframeCw.location + ", method is " + method2 + "\n");
+ is(method2, "GET", "Method for second load should be GET.");
+ is(iframeCw.location.search, "?replaced", "Wrong search on iframe after refresh.");
+});
+</script>
+
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug590573.html b/docshell/test/mochitest/test_bug590573.html
new file mode 100644
index 0000000000..83554a7a66
--- /dev/null
+++ b/docshell/test/mochitest/test_bug590573.html
@@ -0,0 +1,198 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=590573
+-->
+<head>
+ <title>Test for Bug 590573</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=590573">Mozilla Bug 590573</a>
+
+<script type='application/javascript'>
+SimpleTest.waitForExplicitFinish();
+
+// Listen to the first callback, since this indicates that the page loaded.
+var page1LoadCallbackEnabled = true;
+function page1Load() {
+ if (page1LoadCallbackEnabled) {
+ page1LoadCallbackEnabled = false;
+ dump("Got page1 load.\n");
+ pageLoad();
+ } else {
+ dump("Ignoring page1 load.\n");
+ }
+}
+
+var page1PageShowCallbackEnabled = false;
+function page1PageShow() {
+ if (page1PageShowCallbackEnabled) {
+ page1PageShowCallbackEnabled = false;
+ dump("Got page1 pageshow.\n");
+ pageLoad();
+ } else {
+ dump("Ignoring page1 pageshow.\n");
+ }
+}
+
+var page2LoadCallbackEnabled = false;
+function page2Load() {
+ if (page2LoadCallbackEnabled) {
+ page2LoadCallbackEnabled = false;
+ dump("Got page2 popstate.\n");
+ pageLoad();
+ } else {
+ dump("Ignoring page2 popstate.\n");
+ }
+}
+
+var page2PopstateCallbackEnabled = false;
+function page2Popstate() {
+ if (page2PopstateCallbackEnabled) {
+ page2PopstateCallbackEnabled = false;
+ dump("Got page2 popstate.\n");
+ pageLoad();
+ } else {
+ dump("Ignoring page2 popstate.\n");
+ }
+}
+
+var page2PageShowCallbackEnabled = false;
+function page2PageShow() {
+ if (page2PageShowCallbackEnabled) {
+ page2PageShowCallbackEnabled = false;
+ dump("Got page2 pageshow.\n");
+ pageLoad();
+ } else {
+ dump("Ignoring page2 pageshow.\n");
+ }
+}
+
+var popup = window.open("file_bug590573_1.html");
+
+var gTestContinuation = null;
+var loads = 0;
+function pageLoad() {
+ loads++;
+ dump("pageLoad(loads=" + loads + ", page location=" + popup.location + ")\n");
+
+ if (!gTestContinuation) {
+ gTestContinuation = testBody();
+ }
+ var ret = gTestContinuation.next();
+ if (ret.done) {
+ SimpleTest.finish();
+ }
+}
+
+function continueAsync() {
+ popup.addEventListener("popstate", function() {
+ popup.requestAnimationFrame(function() { gTestContinuation.next(); });
+ },
+ {once: true});
+}
+
+function* testBody() {
+ is(popup.scrollY, 0, "test 1");
+ popup.scroll(0, 100);
+
+ popup.history.pushState("", "", "?pushed");
+ is(Math.round(popup.scrollY), 100, "test 2");
+ popup.scroll(0, 200); // set state-2's position to 200
+
+ popup.history.back();
+ continueAsync();
+ yield;
+ is(Math.round(popup.scrollY), 100, "test 3");
+ popup.scroll(0, 150); // set original page's position to 150
+
+ popup.history.forward();
+ continueAsync();
+ yield;
+ is(Math.round(popup.scrollY), 200, "test 4");
+
+ popup.history.back();
+ continueAsync();
+ yield;
+ is(Math.round(popup.scrollY), 150, "test 5");
+
+ popup.history.forward();
+ continueAsync();
+ yield;
+ is(Math.round(popup.scrollY), 200, "test 6");
+
+ // At this point, the history looks like:
+ // PATH POSITION
+ // file_bug590573_1.html 150 <-- oldest
+ // file_bug590573_1.html?pushed 200 <-- newest, current
+
+ // Now test that the scroll position is persisted when we have real
+ // navigations involved. First, we need to spin the event loop so that the
+ // navigation doesn't replace our current history entry.
+
+ setTimeout(pageLoad, 0);
+ yield;
+
+ page2LoadCallbackEnabled = true;
+ popup.location = "file_bug590573_2.html";
+ yield;
+
+ ok(popup.location.href.match("file_bug590573_2.html$"),
+ "Location was " + popup.location +
+ " but should end with file_bug590573_2.html");
+
+ is(popup.scrollY, 0, "test 7");
+ popup.scroll(0, 300);
+
+ // We need to spin the event loop again before we go back, otherwise the
+ // scroll positions don't get updated properly.
+ setTimeout(pageLoad, 0);
+ yield;
+
+ page1PageShowCallbackEnabled = true;
+ popup.history.back();
+ yield;
+
+ // Spin the event loop again so that we get the right scroll positions.
+ setTimeout(pageLoad, 0);
+ yield;
+
+ is(popup.location.search, "?pushed");
+ ok(popup.document.getElementById("div1"), "page should have div1.");
+
+ is(Math.round(popup.scrollY), 200, "test 8");
+
+ popup.history.back();
+ continueAsync();
+ yield;
+ is(Math.round(popup.scrollY), 150, "test 9");
+ popup.history.forward();
+ continueAsync();
+ yield;
+
+ is(Math.round(popup.scrollY), 200, "test 10");
+
+ // Spin one last time...
+ setTimeout(pageLoad, 0);
+ yield;
+
+ page2PageShowCallbackEnabled = true;
+ popup.history.forward();
+ yield;
+
+ // Bug 821821, on Android tegras we get 299 instead of 300 sometimes
+ const scrollY = Math.floor(popup.scrollY);
+ if (scrollY >= 299 && scrollY <= 300) {
+ is(1, 1, "test 11");
+ } else {
+ is(1, 0, "test 11, got " + popup.scrollY + " for popup.scrollY instead of 299|300");
+ }
+ popup.close();
+}
+</script>
+
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug598895.html b/docshell/test/mochitest/test_bug598895.html
new file mode 100644
index 0000000000..e5589a8c0f
--- /dev/null
+++ b/docshell/test/mochitest/test_bug598895.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=598895
+-->
+<head>
+ <title>Test for Bug 598895</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=598895">Mozilla Bug 598895</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 598895 **/
+SimpleTest.waitForExplicitFinish();
+
+addLoadEvent(function() {
+var win1 = window.open();
+win1.document.body.textContent = "Should show";
+
+var windowsLoaded = 0;
+
+window.onmessage = async function(ev) {
+ is(ev.data, "loaded", "Message should be 'loaded'");
+ if (++windowsLoaded == 2) {
+ var one = await snapshotWindow(win1);
+ var two = await snapshotWindow(win2);
+ var three = await snapshotWindow(win3);
+ win1.close();
+ win2.close();
+ win3.close();
+ ok(compareSnapshots(one, two, true)[0], "Popups should look identical");
+ ok(compareSnapshots(one, three, false)[0], "Popups should not look identical");
+
+ SimpleTest.finish();
+ }
+};
+
+var win2 = window.open("file_bug598895_1.html");
+var win3 = window.open("file_bug598895_2.html");
+});
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug634834.html b/docshell/test/mochitest/test_bug634834.html
new file mode 100644
index 0000000000..e1f87de000
--- /dev/null
+++ b/docshell/test/mochitest/test_bug634834.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=634834
+-->
+<head>
+ <title>Test for Bug 634834</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=634834">Mozilla Bug 634834</a>
+
+<script type='application/javascript'>
+SimpleTest.waitForExplicitFinish();
+
+function iframe_loaded() {
+ var loadedAfterPushstate = false;
+ $("iframe").onload = function() {
+ loadedAfterPushstate = true;
+ };
+
+ var obj = { name: "name" };
+ obj.__defineGetter__("a", function() {
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ $("iframe").contentWindow.location = "http://example.com";
+
+ // Wait until we've loaded example.com.
+ do {
+ var r = new XMLHttpRequest();
+ r.open("GET", location.href, false);
+ r.overrideMimeType("text/plain");
+ try { r.send(null); } catch (e) {}
+ } while (!loadedAfterPushstate);
+ });
+
+ try {
+ $("iframe").contentWindow.history.pushState(obj, "");
+ ok(false, "pushState should throw exception.");
+ } catch (e) {
+ ok(true, "pushState threw an exception.");
+ }
+ SimpleTest.finish();
+}
+
+</script>
+
+<iframe id='iframe' src='file_bug634834.html' onload='iframe_loaded()'></iframe>
+
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug637644.html b/docshell/test/mochitest/test_bug637644.html
new file mode 100644
index 0000000000..66ed6cb3c8
--- /dev/null
+++ b/docshell/test/mochitest/test_bug637644.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=637644
+-->
+<head>
+ <title>Test for Bug 637644</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=637644">Mozilla Bug 637644</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 637644 **/
+SimpleTest.waitForExplicitFinish();
+
+addLoadEvent(function() {
+var win1 = window.open("", "", "height=500,width=500");
+win1.document.body.textContent = "Should show";
+
+var windowsLoaded = 0;
+
+window.onmessage = async function(ev) {
+ is(ev.data, "loaded", "Message should be 'loaded'");
+ if (++windowsLoaded == 2) {
+ var one = await snapshotWindow(win1);
+ var two = await snapshotWindow(win2);
+ var three = await snapshotWindow(win3);
+ win1.close();
+ win2.close();
+ win3.close();
+ ok(compareSnapshots(one, two, true)[0], "Popups should look identical");
+ ok(compareSnapshots(one, three, false)[0], "Popups should not look identical");
+
+ SimpleTest.finish();
+ }
+};
+
+var win2 = window.open("file_bug637644_1.html", "", "height=500,width=500");
+var win3 = window.open("file_bug637644_2.html", "", "height=500,width=500");
+});
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug640387_1.html b/docshell/test/mochitest/test_bug640387_1.html
new file mode 100644
index 0000000000..b8aab054a1
--- /dev/null
+++ b/docshell/test/mochitest/test_bug640387_1.html
@@ -0,0 +1,107 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=640387
+-->
+<head>
+ <title>Test for Bug 640387</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=640387">Mozilla Bug 640387</a>
+
+<script type='application/javascript'>
+SimpleTest.waitForExplicitFinish();
+
+function* test() {
+ /* Spin the event loop so we get out of the onload handler. */
+ SimpleTest.executeSoon(function() { gGen.next(); });
+ yield undefined;
+
+ popup.history.pushState("", "", "#hash1");
+ popup.history.pushState("", "", "#hash2");
+
+ // Now the history looks like:
+ // file_bug640387.html
+ // file_bug640387.html#hash1
+ // file_bug640387.html#hash2 <-- current
+
+ // Going back should trigger a hashchange, which will wake us up from the
+ // yield.
+ popup.history.back();
+ yield undefined;
+ ok(true, "Got first hashchange.");
+
+ // Going back should wake us up again.
+ popup.history.back();
+ yield undefined;
+ ok(true, "Got second hashchange.");
+
+ // Now the history looks like:
+ // file_bug640387.html <-- current
+ // file_bug640387.html#hash1
+ // file_bug640387.html#hash2
+
+ // Going forward should trigger a hashchange.
+ popup.history.forward();
+ yield undefined;
+ ok(true, "Got third hashchange.");
+
+ // Now modify the history so it looks like:
+ // file_bug640387.html
+ // file_bug640387.html#hash1
+ // file_bug640387.html#hash1 <-- current
+ popup.history.pushState("", "", "#hash1");
+
+ // Now when we go back, we should not get a hashchange. Instead, wait for a
+ // popstate. We need to asynchronously go back because popstate is fired
+ // sync.
+ gHashchangeExpected = false;
+ gCallbackOnPopstate = true;
+ SimpleTest.executeSoon(function() { popup.history.back(); });
+ yield undefined;
+ ok(true, "Got popstate.");
+ gCallbackOnPopstate = false;
+
+ // Spin the event loop so hashchange has a chance to fire, if it's going to.
+ SimpleTest.executeSoon(function() { gGen.next(); });
+ yield undefined;
+
+ popup.close();
+ SimpleTest.finish();
+}
+
+var gGen = null;
+function childLoad() {
+ gGen = test();
+ gGen.next();
+}
+
+var gHashchangeExpected = true;
+function childHashchange() {
+ if (gHashchangeExpected) {
+ gGen.next();
+ } else {
+ ok(false, "Got hashchange when we weren't expecting one.");
+ }
+}
+
+var gCallbackOnPopstate = false;
+function childPopstate() {
+ if (gCallbackOnPopstate) {
+ gGen.next();
+ }
+}
+
+/* We need to run this test in a popup, because navigating an iframe
+ * back/forwards tends to cause intermittent orange. */
+var popup = window.open("file_bug640387.html");
+
+/* Control now flows up to childLoad(), called once the popup loads. */
+
+</script>
+
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug640387_2.html b/docshell/test/mochitest/test_bug640387_2.html
new file mode 100644
index 0000000000..c248a64836
--- /dev/null
+++ b/docshell/test/mochitest/test_bug640387_2.html
@@ -0,0 +1,89 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=640387
+-->
+<head>
+ <title>Test for Bug 640387</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=640387">Mozilla Bug 640387</a>
+
+<!-- Test that, when going from
+
+ http://example.com/#foo
+
+to
+
+ http://example.com/
+
+via a non-history load, we do a true load, rather than a scroll. -->
+
+<script type='application/javascript'>
+SimpleTest.waitForExplicitFinish();
+
+var callbackOnLoad = false;
+function childLoad() {
+ if (callbackOnLoad) {
+ callbackOnLoad = false;
+ gGen.next();
+ }
+}
+
+var errorOnHashchange = false;
+var callbackOnHashchange = false;
+function childHashchange() {
+ if (errorOnHashchange) {
+ ok(false, "Got unexpected hashchange.");
+ }
+ if (callbackOnHashchange) {
+ callbackOnHashchange = false;
+ gGen.next();
+ }
+}
+
+function* run_test() {
+ var iframe = $("iframe").contentWindow;
+
+ ok(true, "Got first load");
+
+ // Spin the event loop so we exit the onload handler.
+ SimpleTest.executeSoon(function() { gGen.next(); });
+ yield undefined;
+
+ let origLocation = iframe.location + "";
+ callbackOnHashchange = true;
+ iframe.location.hash = "#1";
+ // Wait for a hashchange event.
+ yield undefined;
+
+ ok(true, "Got hashchange.");
+
+ iframe.location = origLocation;
+ // This should produce a load event and *not* a hashchange, because the
+ // result of the load is a different document than we had previously.
+ callbackOnLoad = true;
+ errorOnHashchange = true;
+ yield undefined;
+
+ ok(true, "Got final load.");
+
+ // Spin the event loop to give hashchange a chance to fire, if it's going to.
+ SimpleTest.executeSoon(function() { gGen.next(); });
+ yield undefined;
+
+ SimpleTest.finish();
+}
+
+callbackOnLoad = true;
+var gGen = run_test();
+
+</script>
+
+<iframe id='iframe' src='file_bug640387.html'></iframe>
+
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug653741.html b/docshell/test/mochitest/test_bug653741.html
new file mode 100644
index 0000000000..ca29c5ef08
--- /dev/null
+++ b/docshell/test/mochitest/test_bug653741.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=653741
+-->
+<head>
+ <title>Test for Bug 653741</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=653741">Mozilla Bug 653741</a>
+
+<script type="application/javascript">
+
+/** Test for Bug 653741 **/
+SimpleTest.waitForExplicitFinish();
+
+function childLoad() {
+ // Spin the event loop so we leave the onload handler.
+ SimpleTest.executeSoon(childLoad2);
+}
+
+function childLoad2() {
+ let cw = $("iframe").contentWindow;
+
+ // Save the Y offset. For sanity's sake, make sure it's not 0, because we
+ // should be at the bottom of the page!
+ let origYOffset = Math.round(cw.pageYOffset);
+ ok(origYOffset != 0, "Original Y offset is not 0.");
+
+ // Scroll the iframe to the top, then navigate to #bottom again.
+ cw.scrollTo(0, 0);
+
+ // Our current location is #bottom, so this should scroll us down to the
+ // bottom again.
+ cw.location = cw.location + "";
+
+ is(Math.round(cw.pageYOffset), origYOffset, "Correct offset after reloading page.");
+ SimpleTest.finish();
+}
+
+</script>
+
+<iframe height='100px' id='iframe' src='file_bug653741.html#bottom'></iframe>
+
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug660404.html b/docshell/test/mochitest/test_bug660404.html
new file mode 100644
index 0000000000..459035d4a5
--- /dev/null
+++ b/docshell/test/mochitest/test_bug660404.html
@@ -0,0 +1,76 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=660404
+-->
+<head>
+ <title>Test for Bug 660404</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=660404">Mozilla Bug 660404</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 660404 **/
+SimpleTest.waitForExplicitFinish();
+
+var textContent =
+`
+ var bc = new BroadcastChannel("bug660404_multipart");
+ bc.postMessage({command: "finishTest",
+ textContent: window.document.documentElement.textContent,
+ innerHTML: window.document.documentElement.innerHTML
+ });
+ bc.close();
+ window.close();
+`;
+var innerHTML =
+`<head><script>
+ var bc = new BroadcastChannel("bug660404_multipart");
+ bc.postMessage({command: "finishTest",
+ textContent: window.document.documentElement.textContent,
+ innerHTML: window.document.documentElement.innerHTML
+ });
+ bc.close();
+ window.close();
+</`
+// eslint-disable-next-line no-useless-concat
++ `script></head>`
+;
+var bc_multipart = new BroadcastChannel("bug660404_multipart");
+bc_multipart.onmessage = (msgEvent) => {
+ var msg = msgEvent.data;
+ var command = msg.command;
+ if (command == "finishTest") {
+ is(msg.textContent, textContent);
+ is(msg.innerHTML, innerHTML);
+ bc_multipart.close();
+ SimpleTest.finish();
+ }
+}
+var bc = new BroadcastChannel("bug660404");
+bc.onmessage = (msgEvent) => {
+ var msg = msgEvent.data;
+ var command = msg.command;
+ if (command == "pagehide") {
+ is(msg.persisted, true, "Should be bfcached when navigating to multipart");
+ bc.close();
+ }
+}
+
+// If Fission is disabled, the pref is no-op.
+SpecialPowers.pushPrefEnv({set: [["fission.bfcacheInParent", true]]}, () => {
+ // Have to open a new window, since there's no bfcache in subframes
+ window.open("file_bug660404-1.html", "", "noopener");
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug662170.html b/docshell/test/mochitest/test_bug662170.html
new file mode 100644
index 0000000000..fa0f3b6d4c
--- /dev/null
+++ b/docshell/test/mochitest/test_bug662170.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=662170
+-->
+<head>
+ <title>Test for Bug 662170</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=662170">Mozilla Bug 662170</a>
+
+<script type="application/javascript">
+
+/** Test for Bug 662170 **/
+SimpleTest.waitForExplicitFinish();
+
+function childLoad() {
+ // Spin the event loop so we leave the onload handler.
+ SimpleTest.executeSoon(childLoad2);
+}
+
+function childLoad2() {
+ let cw = $("iframe").contentWindow;
+
+ // When we initially load the page, we should be at the top.
+ is(cw.pageYOffset, 0, "Initial Y offset should be 0.");
+
+ // Scroll the iframe to the bottom.
+ cw.scrollTo(0, 300);
+
+ // Did we actually scroll somewhere?
+ isnot(Math.round(cw.pageYOffset), 0, "Y offset should be non-zero after scrolling.");
+
+ // Now load file_bug662170.html#, which should take us to the top of the
+ // page.
+ cw.location = cw.location + "#";
+
+ is(cw.pageYOffset, 0, "Correct Y offset after loading #.");
+ SimpleTest.finish();
+}
+
+</script>
+
+<!-- When the iframe loads, it calls childLoad(). -->
+<iframe height='100px' id='iframe' src='file_bug662170.html'></iframe>
+
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug668513.html b/docshell/test/mochitest/test_bug668513.html
new file mode 100644
index 0000000000..09c848b6c1
--- /dev/null
+++ b/docshell/test/mochitest/test_bug668513.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=668513
+-->
+<head>
+ <title>Test for Bug 668513</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=668513">Mozilla Bug 668513</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+if (navigator.platform.startsWith("Linux")) {
+ SimpleTest.expectAssertions(0, 1);
+}
+
+SimpleTest.waitForExplicitFinish();
+window.open("file_bug668513.html");
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug669671.html b/docshell/test/mochitest/test_bug669671.html
new file mode 100644
index 0000000000..c45f4ea89b
--- /dev/null
+++ b/docshell/test/mochitest/test_bug669671.html
@@ -0,0 +1,145 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=669671
+-->
+<head>
+ <title>Test for Bug 669671</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=669671">Mozilla Bug 669671</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/**
+ * Test for Bug 669671.
+ *
+ * This is a bit complicated. We have a script, file_bug669671.sjs, which counts
+ * how many times it's loaded and returns that count in the body of an HTML
+ * document. For brevity, call this page X.
+ *
+ * X is sent with Cache-Control: max-age=0 and can't be bfcached (it has an
+ * onunload handler). Our test does the following in a popup:
+ *
+ * 1) Load X?pushed, to prime the cache.
+ * 2) Navigate to X.
+ * 3) Call pushState and navigate from X to X?pushed.
+ * 4) Navigate to X?navigated.
+ * 5) Go back (to X?pushed).
+ *
+ * We do all this work so we can check that in step 5, we fetch X?pushed from
+ * the network -- we shouldn't use our cached copy, because of the
+ * cache-control header X sends.
+ *
+ * Then we go back and repeat the whole process but call history.replaceState
+ * instead of pushState. And for good measure, we test once more, this time
+ * modifying only the hash of the URI using replaceState. In this case, we
+ * *should* load from the cache.
+ *
+ **/
+SimpleTest.requestLongerTimeout(2);
+SimpleTest.waitForExplicitFinish();
+
+function onChildLoad() {
+ SimpleTest.executeSoon(function() { gGen.next(); });
+}
+
+var _loadCount = 0;
+function checkPopupLoadCount() {
+ is(popup.document.body.innerHTML, _loadCount + "", "Load count");
+
+ // We normally want to increment _loadCount here. But if the test fails
+ // because we didn't do a load we should have, let's not cause a cascade of
+ // failures by incrementing _loadCount.
+ var origCount = _loadCount;
+ if (popup.document.body.innerHTML >= _loadCount + "")
+ _loadCount++;
+ return origCount;
+}
+
+function* test() {
+ // Step 0 - Make sure the count is reset to 0 in case of reload
+ popup.location = "file_bug669671.sjs?countreset";
+ yield;
+ is(popup.document.body.innerHTML, "0",
+ "Load count should be reset to 0");
+
+ // Step 1 - The popup's body counts how many times we've requested the
+ // resource. This is the first time we've requested it, so it should be '0'.
+ checkPopupLoadCount();
+
+ // Step 2 - We'll get another onChildLoad when this finishes.
+ popup.location = "file_bug669671.sjs";
+ yield undefined;
+
+ // Step 3 - Call pushState and change the URI back to ?pushed.
+ checkPopupLoadCount();
+ popup.history.pushState("", "", "?pushed");
+
+ // Step 4 - Navigate away. This should trigger another onChildLoad.
+ popup.location = "file_bug669671.sjs?navigated-1";
+ yield undefined;
+
+ // Step 5 - Go back. This should result in another onload (because the file is
+ // not in bfcache) and should be the fourth time we've requested the sjs file.
+ checkPopupLoadCount();
+ popup.history.back();
+ yield undefined;
+
+ // This is the check which was failing before we fixed the bug.
+ checkPopupLoadCount();
+
+ popup.close();
+
+ // Do the whole thing again, but with replaceState.
+ popup = window.open("file_bug669671.sjs?replaced");
+ yield undefined;
+ checkPopupLoadCount();
+ popup.location = "file_bug669671.sjs";
+ yield undefined;
+ checkPopupLoadCount();
+ popup.history.replaceState("", "", "?replaced");
+ popup.location = "file_bug669671.sjs?navigated-2";
+ yield undefined;
+ checkPopupLoadCount();
+ popup.history.back();
+ yield undefined;
+ checkPopupLoadCount();
+ popup.close();
+
+ // Once more, with feeling. Notice that we don't have to prime the cache
+ // with an extra load here, because X and X#hash share the same cache entry.
+ popup = window.open("file_bug669671.sjs?hash-test");
+ yield undefined;
+ var initialCount = checkPopupLoadCount();
+ popup.history.replaceState("", "", "#hash");
+ popup.location = "file_bug669671.sjs?navigated-3";
+ yield undefined;
+ checkPopupLoadCount();
+ popup.history.back();
+ yield undefined;
+ is(popup.document.body.innerHTML, initialCount + "",
+ "Load count (should be cached)");
+ popup.close();
+
+ SimpleTest.finish();
+}
+
+var gGen = test();
+var popup;
+
+// Disable RCWN to make cache behavior deterministic.
+SpecialPowers.pushPrefEnv({set: [["network.http.rcwn.enabled", false]]}, () => {
+ // This will call into onChildLoad once it loads.
+ popup = window.open("file_bug669671.sjs?pushed");
+});
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug675587.html b/docshell/test/mochitest/test_bug675587.html
new file mode 100644
index 0000000000..e6bd71a47f
--- /dev/null
+++ b/docshell/test/mochitest/test_bug675587.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=675587
+-->
+<head>
+ <title>Test for Bug 675587</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=675587">Mozilla Bug 675587</a>
+<p id="display">
+ <iframe src="file_bug675587.html#hash"></iframe>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 675587 **/
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+ ok(window.frames[0].location.href.endsWith("file_bug675587.html#"),
+ "Should have the right href");
+ SimpleTest.finish();
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug680257.html b/docshell/test/mochitest/test_bug680257.html
new file mode 100644
index 0000000000..4d5736ac0a
--- /dev/null
+++ b/docshell/test/mochitest/test_bug680257.html
@@ -0,0 +1,76 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=680257
+-->
+<head>
+ <title>Test for Bug 680257</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=680257">Mozilla Bug 680257</a>
+
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+var popup = window.open("file_bug680257.html");
+
+var gTestContinuation = null;
+function continueAsync() {
+ popup.addEventListener("hashchange",
+ function(e) { gTestContinuation.next(); }, { once: true });
+}
+
+// The popup will call into popupLoaded() once it loads.
+function popupLoaded() {
+ // runTests() needs to be called from outside popupLoaded's onload handler.
+ // Otherwise, the navigations we do in runTests won't create new SHEntries.
+ SimpleTest.executeSoon(function() {
+ if (!gTestContinuation) {
+ gTestContinuation = runTests();
+ }
+ gTestContinuation.next();
+ });
+}
+
+function* runTests() {
+ checkPopupLinkStyle(false, "Initial");
+
+ popup.location.hash = "a";
+ continueAsync();
+ yield;
+ checkPopupLinkStyle(true, "After setting hash");
+
+ popup.history.back();
+ continueAsync();
+ yield;
+
+ checkPopupLinkStyle(false, "After going back");
+
+ popup.history.forward();
+ continueAsync();
+ yield;
+ checkPopupLinkStyle(true, "After going forward");
+
+ popup.close();
+ SimpleTest.finish();
+}
+
+function checkPopupLinkStyle(isTarget, desc) {
+ var link = popup.document.getElementById("a");
+ var style = popup.getComputedStyle(link);
+ var color = style.getPropertyValue("color");
+
+ // Color is red if isTarget, black otherwise.
+ if (isTarget) {
+ is(color, "rgb(255, 0, 0)", desc);
+ } else {
+ is(color, "rgb(0, 0, 0)", desc);
+ }
+}
+
+</script>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug691547.html b/docshell/test/mochitest/test_bug691547.html
new file mode 100644
index 0000000000..706cd5013b
--- /dev/null
+++ b/docshell/test/mochitest/test_bug691547.html
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=691547
+-->
+<head>
+ <title>Test for Bug 691547</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+ var navStart = 0;
+ var beforeReload = 0;
+ function onContentLoad() {
+ var frame = frames[0];
+ if (!navStart) {
+ // First time we perform navigation in subframe. The bug is that
+ // load in subframe causes timing.navigationStart to be recorded
+ // as if it was a start of the next navigation.
+ var innerFrame = frame.frames[0];
+ navStart = frame.performance.timing.navigationStart;
+ innerFrame.location = "bug570341_recordevents.html";
+ // Let's wait a bit so the difference is clear anough.
+ setTimeout(reload, 3000);
+ } else {
+ // Content reloaded, time to check. We are allowing a huge time slack,
+ // in case clock is imprecise. If we have a bug, the difference is
+ // expected to be about the timeout value set above.
+ var diff = frame.performance.timing.navigationStart - beforeReload;
+ ok(diff >= -200,
+ "navigationStart should be set after reload request. " +
+ "Measured difference: " + diff + " (should be positive)");
+ SimpleTest.finish();
+ }
+ }
+ function reload() {
+ var frame = frames[0];
+ ok(navStart == frame.performance.timing.navigationStart,
+ "navigationStart should not change when frame loads.");
+ beforeReload = Date.now();
+ frame.location.reload();
+ }
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=570341">Mozilla Bug 570341</a>
+<div id="frames">
+<iframe name="frame0" id="frame0" src="bug691547_frame.html" onload="onContentLoad()"></iframe>
+</div>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug694612.html b/docshell/test/mochitest/test_bug694612.html
new file mode 100644
index 0000000000..445d0433d8
--- /dev/null
+++ b/docshell/test/mochitest/test_bug694612.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=694612
+-->
+<head>
+ <title>Test for Bug 694612</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=694612">Mozilla Bug 694612</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+/** Test for Bug 694612 **/
+SimpleTest.waitForExplicitFinish();
+
+window.addEventListener("message", receiveMessage);
+function receiveMessage(event) {
+ ok(event.data.result, "should have performance API in an <object>");
+ window.removeEventListener("message", receiveMessage);
+ SimpleTest.finish();
+}
+</script>
+<object type="text/html"
+ data="data:text/html,<script>parent.postMessage({result:performance!=null},'*');</script>">
+</object>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug703855.html b/docshell/test/mochitest/test_bug703855.html
new file mode 100644
index 0000000000..8e3d9b5a04
--- /dev/null
+++ b/docshell/test/mochitest/test_bug703855.html
@@ -0,0 +1,79 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=703855
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 703855</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=703855">Mozilla Bug 703855</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <iframe id="f" src="file_bug703855.html"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 703855 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var timingAttributes = [
+ "connectEnd",
+ "connectStart",
+ "domComplete",
+ "domContentLoadedEventEnd",
+ "domContentLoadedEventStart",
+ "domInteractive",
+ "domLoading",
+ "domainLookupEnd",
+ "domainLookupStart",
+ "fetchStart",
+ "loadEventEnd",
+ "loadEventStart",
+ "navigationStart",
+ "redirectEnd",
+ "redirectStart",
+ "requestStart",
+ "responseEnd",
+ "responseStart",
+ "unloadEventEnd",
+ "unloadEventStart",
+];
+var originalTiming = {};
+
+function runTest() {
+ var timing = $("f").contentWindow.performance.timing;
+ for (let i in timingAttributes) {
+ originalTiming[timingAttributes[i]] = timing[timingAttributes[i]];
+ }
+
+ var doc = $("f").contentDocument;
+ doc.open();
+ doc.write("<!DOCTYPE html>");
+ doc.close();
+
+ SimpleTest.executeSoon(function() {
+ var newTiming = $("f").contentWindow.performance.timing;
+ for (let i in timingAttributes) {
+ is(newTiming[timingAttributes[i]], originalTiming[timingAttributes[i]],
+ "document.open should not affect value of " + timingAttributes[i]);
+ }
+ SimpleTest.finish();
+ });
+}
+
+addLoadEvent(function() {
+ SimpleTest.executeSoon(runTest);
+});
+
+
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug728939.html b/docshell/test/mochitest/test_bug728939.html
new file mode 100644
index 0000000000..168184099a
--- /dev/null
+++ b/docshell/test/mochitest/test_bug728939.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=728939
+-->
+<head>
+ <title>Test for Bug 728939</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=728939">Mozilla Bug 728939</a>
+
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+// Called when the popup finishes loading.
+function popupLoaded() {
+ popup.location.hash = "#foo";
+ is(popup.document.URL, popup.location.href, "After hashchange.");
+
+ popup.history.pushState("", "", "bar");
+ is(popup.document.URL, popup.location.href, "After pushState.");
+
+ popup.history.replaceState("", "", "baz");
+ is(popup.document.URL, popup.location.href, "After replaceState.");
+
+ popup.close();
+ SimpleTest.finish();
+}
+
+var popup = window.open("file_bug728939.html");
+
+</script>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_bug797909.html b/docshell/test/mochitest/test_bug797909.html
new file mode 100644
index 0000000000..75e91d9052
--- /dev/null
+++ b/docshell/test/mochitest/test_bug797909.html
@@ -0,0 +1,66 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=797909
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 797909</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=797909">Mozilla Bug 797909</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+ /** Test for Bug 797909 **/
+
+ SimpleTest.waitForExplicitFinish();
+
+ function runTest() {
+ var iframe = document.getElementById("ifr");
+ try {
+ iframe.contentWindow.document;
+ ok(false, "Should have thrown an exception");
+ } catch (ex) {
+ ok(true, "Got an exception");
+ }
+
+ iframe = document.createElement("iframe");
+ // set sandbox attribute
+ iframe.sandbox = "allow-scripts";
+ // and then insert into the doc
+ document.body.appendChild(iframe);
+
+ try {
+ iframe.contentWindow.document;
+ ok(false, "Should have thrown an exception");
+ } catch (ex) {
+ ok(true, "Got an exception");
+ }
+
+ iframe = document.createElement("iframe");
+ // set sandbox attribute
+ iframe.sandbox = "allow-same-origin";
+ // and then insert into the doc
+ document.body.appendChild(iframe);
+
+ try {
+ iframe.contentWindow.document;
+ ok(true, "Shouldn't have thrown an exception");
+ } catch (ex) {
+ ok(false, "Got an unexpected exception");
+ }
+
+ SimpleTest.finish();
+ }
+
+</script>
+</pre>
+<iframe id="ifr" sandbox = "allow-scripts"></iframe>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_close_onpagehide_by_history_back.html b/docshell/test/mochitest/test_close_onpagehide_by_history_back.html
new file mode 100644
index 0000000000..33140502f7
--- /dev/null
+++ b/docshell/test/mochitest/test_close_onpagehide_by_history_back.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<title>Test for closing window in pagehide event callback caused by history.back()</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1432396">Mozilla Bug 1432396</a>
+<p id="display"></p>
+<script>
+SimpleTest.waitForExplicitFinish();
+
+const w = window.open("file_close_onpagehide1.html");
+window.addEventListener("message", e => {
+ is(e.data, "initial", "The initial page loaded");
+ window.addEventListener("message", evt => {
+ is(evt.data, "second", "The second page loaded");
+ w.onpagehide = () => {
+ w.close();
+ info("try to close the popped up window in onpagehide");
+ SimpleTest.finish();
+ };
+ w.history.back();
+ }, { once: true });
+ w.location = "file_close_onpagehide2.html";
+}, { once: true });
+</script>
diff --git a/docshell/test/mochitest/test_close_onpagehide_by_window_close.html b/docshell/test/mochitest/test_close_onpagehide_by_window_close.html
new file mode 100644
index 0000000000..8b094cdaa4
--- /dev/null
+++ b/docshell/test/mochitest/test_close_onpagehide_by_window_close.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>Test for closing window in pagehide event callback caused by window.close()</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1432396">Mozilla Bug 1432396</a>
+<p id="display"></p>
+<script>
+SimpleTest.waitForExplicitFinish();
+
+const w = window.open("file_close_onpagehide1.html");
+window.addEventListener("message", e => {
+ is(e.data, "initial", "The initial page loaded");
+ w.onpagehide = () => {
+ w.close();
+ info("try to close the popped up window in onpagehide");
+ SimpleTest.finish();
+ };
+ w.close();
+}, { once: true });
+</script>
diff --git a/docshell/test/mochitest/test_compressed_multipart.html b/docshell/test/mochitest/test_compressed_multipart.html
new file mode 100644
index 0000000000..ff182d33fc
--- /dev/null
+++ b/docshell/test/mochitest/test_compressed_multipart.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1600211
+
+Loads a document that is served as multipart/x-mixed-replace as well as gzip compressed.
+Checks that we correctly decompress and display it (via running JS within the document to notify us).
+-->
+<head>
+ <title>Test for Bug 1600211</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1600211">Mozilla Bug 1600211</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 1600211 **/
+SimpleTest.waitForExplicitFinish();
+
+var w;
+
+function finishTest() {
+ is(w.document.documentElement.textContent, "opener.finishTest();");
+ is(w.document.documentElement.innerHTML, "<head><script>opener.finishTest();</" +
+ "script></head>");
+ w.close();
+ SimpleTest.finish();
+}
+
+w = window.open("file_compressed_multipart");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_content_javascript_loads.html b/docshell/test/mochitest/test_content_javascript_loads.html
new file mode 100644
index 0000000000..eabc1d314e
--- /dev/null
+++ b/docshell/test/mochitest/test_content_javascript_loads.html
@@ -0,0 +1,163 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test for Bug 1647519</title>
+ <meta charset="utf-8">
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1647519">Mozilla Bug 1647519</a>
+
+<script type="application/javascript">
+"use strict";
+
+function promiseMessage(source, filter = event => true) {
+ return new Promise(resolve => {
+ function listener(event) {
+ if (event.source == source && filter(event)) {
+ window.removeEventListener("message", listener);
+ resolve(event);
+ }
+ }
+ window.addEventListener("message", listener);
+ });
+}
+
+async function runTests(resourcePath) {
+ /* globals Assert, content */
+ let doc = content.document;
+
+ // Sends a message to the given target window and waits for a response a few
+ // times to (more or less) ensure that a `javascript:` load request has had
+ // time to succeed, if it were going to.
+ async function doSomeRoundTrips(target) {
+ for (let i = 0; i < 3; i++) {
+ // Note: The ping message needs to be sent from a script running in the
+ // content scope or there will be no source window for the reply to be
+ // sent to.
+ await content.wrappedJSObject.ping(target);
+ }
+ }
+
+ function promiseEvent(target, name) {
+ return new Promise(resolve => {
+ target.addEventListener(name, resolve, { once: true });
+ });
+ }
+
+ function createIframe(host, id) {
+ let iframe = doc.createElement("iframe");
+ iframe.id = id;
+ iframe.name = id;
+ iframe.src = `https://${host}${resourcePath}file_content_javascript_loads_frame.html`;
+ doc.body.appendChild(iframe);
+ return promiseEvent(iframe, "load");
+ }
+
+ const ID_SAME_ORIGIN = "frame-same-origin";
+ const ID_SAME_BASE_DOMAIN = "frame-same-base-domain";
+ const ID_CROSS_BASE_DOMAIN = "frame-cross-base-domain";
+
+ await Promise.all([
+ createIframe("example.com", ID_SAME_ORIGIN),
+ createIframe("test1.example.com", ID_SAME_BASE_DOMAIN),
+ createIframe("example.org", ID_CROSS_BASE_DOMAIN),
+ ]);
+
+ let gotJSLoadFrom = null;
+ let pendingJSLoadID = null;
+ content.addEventListener("message", event => {
+ if ("javascriptLoadID" in event.data) {
+ Assert.equal(
+ event.data.javascriptLoadID,
+ pendingJSLoadID,
+ "Message from javascript: load should have the expected ID"
+ );
+ Assert.equal(
+ gotJSLoadFrom,
+ null,
+ "Should not have seen a previous load message this cycle"
+ );
+ gotJSLoadFrom = event.source.name;
+ }
+ });
+
+ async function watchForJSLoads(frameName, expected, task) {
+ let loadId = Math.random();
+
+ let jsURI =
+ "javascript:" +
+ encodeURI(`parent.postMessage({ javascriptLoadID: ${loadId} }, "*")`);
+
+ pendingJSLoadID = loadId;
+ gotJSLoadFrom = null;
+
+ await task(jsURI);
+
+ await doSomeRoundTrips(content.wrappedJSObject[frameName]);
+
+ if (expected) {
+ Assert.equal(
+ gotJSLoadFrom,
+ frameName,
+ `Should have seen javascript: URI loaded into ${frameName}`
+ );
+ } else {
+ Assert.equal(
+ gotJSLoadFrom,
+ null,
+ "Should not have seen javascript: URI loaded"
+ );
+ }
+ }
+
+ let frames = [
+ { name: ID_SAME_ORIGIN, expectLoad: true },
+ { name: ID_SAME_BASE_DOMAIN, expectLoad: false },
+ { name: ID_CROSS_BASE_DOMAIN, expectLoad: false },
+ ];
+ for (let { name, expectLoad } of frames) {
+ info(`Checking loads for frame "${name}". Expecting loads: ${expectLoad}`);
+
+ info("Checking location setter");
+ await watchForJSLoads(name, expectLoad, jsURI => {
+ // Note: We need to do this from the content scope since security checks
+ // depend on the JS caller scope.
+ content.wrappedJSObject.setFrameLocation(name, jsURI);
+ });
+
+ info("Checking targeted <a> load");
+ await watchForJSLoads(name, expectLoad, jsURI => {
+ let a = doc.createElement("a");
+ a.target = name;
+ a.href = jsURI;
+ doc.body.appendChild(a);
+ a.click();
+ a.remove();
+ });
+
+ info("Checking targeted window.open load");
+ await watchForJSLoads(name, expectLoad, jsURI => {
+ content.wrappedJSObject.open(jsURI, name);
+ });
+ }
+}
+
+add_task(async function() {
+ const resourcePath = location.pathname.replace(/[^\/]+$/, "");
+
+ let win = window.open(
+ `https://example.com${resourcePath}file_content_javascript_loads_root.html`
+ );
+ await promiseMessage(win, event => event.data == "ready");
+
+ await SpecialPowers.spawn(win, [resourcePath], runTests);
+
+ win.close();
+});
+</script>
+
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_double_submit.html b/docshell/test/mochitest/test_double_submit.html
new file mode 100644
index 0000000000..640930718d
--- /dev/null
+++ b/docshell/test/mochitest/test_double_submit.html
@@ -0,0 +1,98 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Test for Bug 1590762</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <body>
+ <iframe name="targetFrame" id="targetFrame"></iframe>
+ <form id="form" action="double_submit.sjs?delay=1000" method="POST" target="targetFrame">
+ <input id="token" type="text" name="token" value="">
+ <input id="button" type="submit">
+ </form>
+ <script>
+ "use strict";
+
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ const CROSS_ORIGIN_URI = "http://test1.example.com/tests/docshell/test/mochitest/ping.html";
+
+ function asyncClick(counts) {
+ let frame = document.createElement('iframe');
+ frame.addEventListener(
+ 'load', () => frame.contentWindow.postMessage({command: "start"}, "*"),
+ { once:true });
+ frame.src = "clicker.html";
+
+ addEventListener('message', ({source}) => {
+ if (source === frame.contentWindow) {
+ counts.click++;
+ synthesizeMouse(document.getElementById('button'), 5, 5, {});
+ }
+ }, { once: true });
+
+ document.body.appendChild(frame);
+ return stop;
+ }
+
+ function click(button) {
+ synthesizeMouse(button, 5, 5, {});
+ }
+
+ add_task(async function runTest() {
+ let frame = document.getElementById('targetFrame');
+ await new Promise(resolve => {
+ addEventListener('message', resolve, {once: true});
+ frame.src = CROSS_ORIGIN_URI;
+ });
+
+ let form = document.getElementById('form');
+ let button = document.getElementById('button');
+
+ let token = document.getElementById('token');
+ token.value = "first";
+
+ await new Promise((resolve, reject) => {
+ let counts = { click: 0, submit: 0 };
+ form.addEventListener('submit', () => counts.submit++);
+ asyncClick(counts);
+ form.requestSubmit(button);
+ token.value = "bad";
+ let steps = {
+ good: {
+ entered: false,
+ next: () => { steps.good.entered = true; resolve(); },
+ assertion: () => {
+ ok(steps.first.entered && !steps.bad.entered, "good comes after first, but not bad")
+ }
+ },
+ first: {
+ entered: false,
+ next: () => { steps.first.entered = true; token.value = "good"; click(button); },
+ assertion: () => {
+ ok(!steps.good.entered && !steps.bad.entered, "first message is first")
+ is(counts.click, 1, "clicked");
+ is(counts.submit, 2, "did submit");
+ }
+ },
+ bad: {
+ entered: false,
+ next: () => { reject(); },
+ assertion: () => ok(false, "we got a bad message")
+ }
+ };
+ addEventListener('message', ({source, data}) => {
+ if (source !== frame.contentWindow) {
+ return;
+ }
+
+ let step = steps[data] || reject;
+ step.assertion();
+ step.next();
+ })
+ });
+ });
+ </script>
+ </body>
+</html>
diff --git a/docshell/test/mochitest/test_forceinheritprincipal_overrule_owner.html b/docshell/test/mochitest/test_forceinheritprincipal_overrule_owner.html
new file mode 100644
index 0000000000..70d610a677
--- /dev/null
+++ b/docshell/test/mochitest/test_forceinheritprincipal_overrule_owner.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<script type="text/javascript">
+
+var channel = SpecialPowers.wrap(window).docShell.currentDocumentChannel;
+var loadInfo = channel.loadInfo;
+
+// 1) perform some sanity checks
+var triggeringPrincipal = channel.loadInfo.triggeringPrincipal.asciiSpec;
+var loadingPrincipal = channel.loadInfo.loadingPrincipal.asciiSpec;
+var principalToInherit = channel.loadInfo.principalToInherit.asciiSpec;
+
+ok(triggeringPrincipal.startsWith("http://mochi.test:8888/")
+ || triggeringPrincipal.startsWith("http://mochi.xorigin-test:8888/"),
+ "initial triggeringPrincipal correct");
+ok(loadingPrincipal.startsWith("http://mochi.test:8888/")
+ || loadingPrincipal.startsWith("http://mochi.xorigin-test:8888/"),
+ "initial loadingPrincipal correct");
+ok(principalToInherit.startsWith("http://mochi.test:8888/")
+ || principalToInherit.startsWith("http://mochi.xorigin-test:8888/"),
+ "initial principalToInherit correct");
+
+// reset principals on the loadinfo
+loadInfo.resetPrincipalToInheritToNullPrincipal();
+
+// 2) verify loadInfo contains the correct principals
+triggeringPrincipal = channel.loadInfo.triggeringPrincipal.asciiSpec;
+loadingPrincipal = channel.loadInfo.loadingPrincipal.asciiSpec;
+principalToInherit = channel.loadInfo.principalToInherit;
+
+ok(triggeringPrincipal.startsWith("http://mochi.test:8888/")
+ || triggeringPrincipal.startsWith("http://mochi.xorigin-test:8888/"),
+ "triggeringPrincipal after resetting correct");
+ok(loadingPrincipal.startsWith("http://mochi.test:8888/")
+ || loadingPrincipal.startsWith("http://mochi.xorigin-test:8888/"),
+ "loadingPrincipal after resetting correct");
+ok(principalToInherit.isNullPrincipal
+ || principalToInherit.startsWith("http://mochi.xorigin-test:8888/"),
+ "principalToInherit after resetting correct");
+
+// 3) verify that getChannelResultPrincipal returns right principal
+var resultPrincipal = SpecialPowers.Services.scriptSecurityManager
+ .getChannelResultPrincipal(channel);
+
+ok(resultPrincipal.isNullPrincipal,
+ "resultPrincipal after resetting correct");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_form_restoration.html b/docshell/test/mochitest/test_form_restoration.html
new file mode 100644
index 0000000000..b929236770
--- /dev/null
+++ b/docshell/test/mochitest/test_form_restoration.html
@@ -0,0 +1,77 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test form restoration for no-store pages</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ function waitForMessage(aBroadcastChannel) {
+ return new Promise(resolve => {
+ aBroadcastChannel.addEventListener("message", ({ data }) => {
+ resolve(data);
+ }, { once: true });
+ });
+ }
+
+ function postMessageAndWait(aBroadcastChannel, aMsg) {
+ let promise = waitForMessage(aBroadcastChannel);
+ aBroadcastChannel.postMessage(aMsg);
+ return promise;
+ }
+
+ async function startTest(aTestFun) {
+ let bc = new BroadcastChannel("form_restoration");
+
+ let promise = waitForMessage(bc);
+ window.open("file_form_restoration_no_store.html", "", "noopener");
+ await promise;
+
+ // test steps
+ await aTestFun(bc);
+
+ // close broadcast channel and window
+ bc.postMessage("close");
+ bc.close();
+ }
+
+ /* Test for bug1740517 */
+ add_task(async function history_back() {
+ await startTest(async (aBroadcastChannel) => {
+ // update form data
+ aBroadcastChannel.postMessage("enter_data");
+
+ // navigate
+ await postMessageAndWait(aBroadcastChannel, "navigate");
+
+ // history back
+ let { persisted, formData } = await postMessageAndWait(aBroadcastChannel, "back");
+
+ // check form data
+ ok(!persisted, "Page with a no-store header shouldn't be bfcached.");
+ is(formData, "initial", "We shouldn't restore form data when going back to a page with a no-store header.");
+ });
+ });
+
+ /* Test for bug1752250 */
+ add_task(async function location_reload() {
+ await startTest(async (aBroadcastChannel) => {
+ // update form data
+ aBroadcastChannel.postMessage("enter_data");
+
+ // reload
+ let { persisted, formData } = await postMessageAndWait(aBroadcastChannel, "reload");
+
+ // check form data
+ ok(!persisted, "Page with a no-store header shouldn't be bfcached.");
+ is(formData, "initial", "We shouldn't restore form data when reload a page with a no-store header.");
+ });
+ });
+ </script>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_framedhistoryframes.html b/docshell/test/mochitest/test_framedhistoryframes.html
new file mode 100644
index 0000000000..a1bffeed1a
--- /dev/null
+++ b/docshell/test/mochitest/test_framedhistoryframes.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=602256
+-->
+<head>
+ <title>Test for Bug 602256</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=602256">Mozilla Bug 602256</a>
+<p id="display"></p>
+<div id="content">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 602256 **/
+
+SimpleTest.waitForExplicitFinish();
+var win = window.open("file_framedhistoryframes.html");
+
+function done() {
+ win.close();
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_iframe_srcdoc_to_remote.html b/docshell/test/mochitest/test_iframe_srcdoc_to_remote.html
new file mode 100644
index 0000000000..05e0934d50
--- /dev/null
+++ b/docshell/test/mochitest/test_iframe_srcdoc_to_remote.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<head>
+ <meta charset="utf-8">
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body onload="test()">
+ <script>
+ /*
+ Test to verify that when we change an OOP iframe to one that has a
+ srcdoc it loads in the correct process, which in this case is this
+ test document.
+ */
+ SimpleTest.waitForExplicitFinish();
+ async function test() {
+ // Create an OOP iframe
+ let frame = document.createElement("iframe");
+ await new Promise(r => {
+ frame.onload = r;
+ document.body.appendChild(frame);
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ frame.contentWindow.location = "http://example.net/tests/docshell/test/dummy_page.html";
+ });
+ if (SpecialPowers.useRemoteSubframes) {
+ ok(SpecialPowers.Cu.isRemoteProxy(frame.contentWindow), "should be a remote frame");
+ }
+
+ // Remove the attribute so we can set a srcdoc attribute on it
+ frame.removeAttribute("src");
+
+ // Set a srcdoc attribute on this iframe and wait for the load
+ await new Promise(r => {
+ frame.onload = r;
+ frame.setAttribute("srcdoc", '<html><body>body of the srcdoc frame</body></html>');
+ });
+
+ // We should be in the same process as this test document
+ ok(!SpecialPowers.Cu.isRemoteProxy(frame.contentWindow), "should NOT be a remote frame");
+ SimpleTest.finish();
+ }
+ </script>
+</body>
+
diff --git a/docshell/test/mochitest/test_javascript_sandboxed_popup.html b/docshell/test/mochitest/test_javascript_sandboxed_popup.html
new file mode 100644
index 0000000000..edce93c26f
--- /dev/null
+++ b/docshell/test/mochitest/test_javascript_sandboxed_popup.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<iframe srcdoc="<a href='javascript:opener.parent.ok(false, `The JS ran!`)' target=_blank rel=opener>click</a>"
+ sandbox="allow-popups allow-same-origin"></iframe>
+
+<script>
+add_task(async function() {
+ let promise = new Promise(resolve =>{
+ SpecialPowers.addObserver(function obs(subject) {
+ is(subject.opener, window[0],
+ "blocked javascript URI should have been targeting the pop-up document");
+ subject.close();
+ SpecialPowers.removeObserver(obs, "javascript-uri-blocked-by-sandbox");
+ resolve();
+ }, "javascript-uri-blocked-by-sandbox");
+ });
+ document.querySelector("iframe").contentDocument.querySelector("a").click();
+ await promise;
+});
+</script>
+</body>
diff --git a/docshell/test/mochitest/test_load_during_reload.html b/docshell/test/mochitest/test_load_during_reload.html
new file mode 100644
index 0000000000..24eb409110
--- /dev/null
+++ b/docshell/test/mochitest/test_load_during_reload.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test loading a new page after calling reload()</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+
+ function promiseForLoad() {
+ return new Promise(resolve => {
+ addEventListener("message", resolve, { once: true });
+ });
+ }
+
+ add_task(async function runTest() {
+ let win = window.open("file_load_during_reload.html");
+ await promiseForLoad();
+
+ win.location.reload();
+ win.location.href = "file_load_during_reload.html?nextpage";
+ await promiseForLoad();
+
+ ok(win.location.href.includes("nextpage"), "Should have loaded the next page.");
+ win.close();
+ });
+
+ </script>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_navigate_after_pagehide.html b/docshell/test/mochitest/test_navigate_after_pagehide.html
new file mode 100644
index 0000000000..17d58d6e62
--- /dev/null
+++ b/docshell/test/mochitest/test_navigate_after_pagehide.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Test for navigation attempts by scripts in inactive inner window</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ </head>
+<body>
+<iframe src="dummy_page.html" id="iframe"></iframe>
+
+<script>
+"use strict";
+
+add_task(async function() {
+ let iframe = document.getElementById("iframe");
+
+ let navigate = iframe.contentWindow.eval(`(function() {
+ location.href = "/";
+ })`);
+
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ iframe.src = "http://example.com/";
+ await new Promise(resolve =>
+ iframe.addEventListener("load", resolve, { once: true })
+ );
+
+ // This should do nothing. But, importantly, it should especially not crash.
+ navigate();
+
+ ok(true, "We didn't crash");
+});
+</script>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_pushState_after_document_open.html b/docshell/test/mochitest/test_pushState_after_document_open.html
new file mode 100644
index 0000000000..20444d8e86
--- /dev/null
+++ b/docshell/test/mochitest/test_pushState_after_document_open.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=957479
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 957479</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 957479 **/
+ SimpleTest.waitForExplicitFinish();
+ // Child needs to invoke us, otherwise our onload will fire before the child
+ // has done the write/close bit.
+ onmessage = function doTest() {
+ is(frames[0].location.pathname, "/tests/docshell/test/mochitest/file_pushState_after_document_open.html",
+ "Should have the right path here");
+ is(frames[0].location.hash, "", "Should have the right hash here");
+ frames[0].history.pushState({}, "", frames[0].document.URL + "#foopy");
+ is(frames[0].location.pathname, "/tests/docshell/test/mochitest/file_pushState_after_document_open.html",
+ "Pathname should not have changed");
+ is(frames[0].location.hash, "#foopy", "Hash should have changed");
+ SimpleTest.finish();
+ };
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=957479">Mozilla Bug 957479</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe src="file_pushState_after_document_open.html"></iframe>
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_redirect_history.html b/docshell/test/mochitest/test_redirect_history.html
new file mode 100644
index 0000000000..a67c808405
--- /dev/null
+++ b/docshell/test/mochitest/test_redirect_history.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Test for redirect from POST</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <body>
+ <script>
+ "use strict";
+
+ info("Starting tests");
+
+ let tests = new Map([
+ ["sameorigin", window.location.origin],
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ ["crossorigin", "http://test1.example.com"],
+ ]);
+ for (let [kind, origin] of tests) {
+ add_task(async function runTest() {
+ info(`Submitting to ${origin}`);
+
+ let win;
+ await new Promise(resolve => {
+ addEventListener("message", resolve, { once: true });
+ info("Loading file_redirect_history.html");
+ win = window.open("file_redirect_history.html");
+ });
+ info("Done loading file_redirect_history.html");
+
+ let length = win.history.length;
+ let loc = win.location.toString();
+
+ await new Promise(resolve => {
+ addEventListener("message", resolve, { once: true });
+ info("Posting");
+ win.postMessage(`${origin}/tests/docshell/test/mochitest/form_submit_redirect.sjs?redirectTo=${loc}`, "*")
+ });
+ info("Done posting\n");
+ is(win.history.length, length, `Test ${kind}: history length should not change.`);
+ info(`Length=${win.history.length}`);
+ is(win.location.toString(), loc, `Test ${kind}: location should not change.`);
+
+ await new Promise(resolve => {
+ addEventListener("message", resolve, { once: true });
+ info("Reloading");
+ win.location.reload();
+ });
+ info("Done reloading\n");
+ is(win.location.toString(), loc, `Test ${kind}: location should not change after reload.`);
+
+ win.close();
+ });
+ }
+ </script>
+ </body>
+</html>
diff --git a/docshell/test/mochitest/test_triggeringprincipal_location_seturi.html b/docshell/test/mochitest/test_triggeringprincipal_location_seturi.html
new file mode 100644
index 0000000000..92e20b03ea
--- /dev/null
+++ b/docshell/test/mochitest/test_triggeringprincipal_location_seturi.html
@@ -0,0 +1,105 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<script type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+const SAME_ORIGIN_URI = "http://mochi.test:8888/tests/docshell/test/dummy_page.html";
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+const CROSS_ORIGIN_URI = "http://example.com/tests/docshell/test/dummy_page.html";
+const NUMBER_OF_TESTS = 3;
+let testCounter = 0;
+
+function checkFinish() {
+ testCounter++;
+ if (testCounter < NUMBER_OF_TESTS) {
+ return;
+ }
+ SimpleTest.finish();
+}
+
+// ---- test 1 ----
+
+let myFrame1 = document.createElement("iframe");
+myFrame1.src = SAME_ORIGIN_URI;
+myFrame1.addEventListener("load", checkLoadFrame1);
+document.documentElement.appendChild(myFrame1);
+
+function checkLoadFrame1() {
+ myFrame1.removeEventListener("load", checkLoadFrame1);
+ // window.location.href is no longer cross-origin accessible in gecko.
+ is(SpecialPowers.wrap(myFrame1.contentWindow).location.href, SAME_ORIGIN_URI,
+ "initial same origin dummy loaded into frame1");
+
+ SpecialPowers.wrap(myFrame1.contentWindow).location.hash = "#bar";
+ is(SpecialPowers.wrap(myFrame1.contentWindow).location.href, SAME_ORIGIN_URI + "#bar",
+ "initial same origin dummy#bar loaded into iframe1");
+
+ myFrame1.addEventListener("load", checkNavFrame1);
+ myFrame1.src = CROSS_ORIGIN_URI;
+}
+
+async function checkNavFrame1() {
+ myFrame1.removeEventListener("load", checkNavFrame1);
+ is(await SpecialPowers.spawn(myFrame1, [], () => this.content.location.href),
+ CROSS_ORIGIN_URI,
+ "cross origin dummy loaded into frame1");
+
+ myFrame1.addEventListener("load", checkBackNavFrame1);
+ myFrame1.src = SAME_ORIGIN_URI + "#bar";
+}
+
+async function checkBackNavFrame1() {
+ myFrame1.removeEventListener("load", checkBackNavFrame1);
+ is(await SpecialPowers.spawn(myFrame1, [], () => this.content.location.href),
+ SAME_ORIGIN_URI + "#bar",
+ "navagiating back to same origin dummy for frame1");
+ checkFinish();
+}
+
+// ---- test 2 ----
+
+let myFrame2 = document.createElement("iframe");
+myFrame2.src = "about:blank";
+myFrame2.addEventListener("load", checkLoadFrame2);
+document.documentElement.appendChild(myFrame2);
+
+function checkLoadFrame2() {
+ myFrame2.removeEventListener("load", checkLoadFrame2);
+ is(SpecialPowers.wrap(myFrame2.contentWindow).location.href, "about:blank",
+ "initial about:blank frame loaded");
+
+ myFrame2.contentWindow.location.hash = "#foo";
+ is(SpecialPowers.wrap(myFrame2.contentWindow).location.href, "about:blank#foo",
+ "about:blank#foo frame loaded");
+
+ myFrame2.addEventListener("load", checkHistoryFrame2);
+ myFrame2.src = "about:blank";
+}
+
+function checkHistoryFrame2() {
+ myFrame2.removeEventListener("load", checkHistoryFrame2);
+ is(SpecialPowers.wrap(myFrame2.contentWindow).location.href, "about:blank",
+ "about:blank frame loaded again");
+ checkFinish();
+}
+
+// ---- test 3 ----
+
+let myFrame3 = document.createElement("frame");
+document.documentElement.appendChild(myFrame3);
+myFrame3.contentWindow.location.hash = "#foo";
+
+is(myFrame3.contentWindow.location.href, "about:blank#foo",
+ "created history entry with about:blank#foo");
+checkFinish();
+
+</script>
+</body>
+</html>
diff --git a/docshell/test/mochitest/test_windowedhistoryframes.html b/docshell/test/mochitest/test_windowedhistoryframes.html
new file mode 100644
index 0000000000..c2c148b838
--- /dev/null
+++ b/docshell/test/mochitest/test_windowedhistoryframes.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=602256
+-->
+<head>
+ <title>Test for Bug 602256</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=602256">Mozilla Bug 602256</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 602256 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function done() {
+ subWin.close();
+ SimpleTest.finish();
+}
+
+var subWin = window.open("historyframes.html", "_blank");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/mochitest/url1_historyframe.html b/docshell/test/mochitest/url1_historyframe.html
new file mode 100644
index 0000000000..b86af4b3fa
--- /dev/null
+++ b/docshell/test/mochitest/url1_historyframe.html
@@ -0,0 +1 @@
+<p id='text'>Test1</p>
diff --git a/docshell/test/mochitest/url2_historyframe.html b/docshell/test/mochitest/url2_historyframe.html
new file mode 100644
index 0000000000..24374d1a5b
--- /dev/null
+++ b/docshell/test/mochitest/url2_historyframe.html
@@ -0,0 +1 @@
+<p id='text'>Test2</p>
diff --git a/docshell/test/moz.build b/docshell/test/moz.build
new file mode 100644
index 0000000000..7cebe0339f
--- /dev/null
+++ b/docshell/test/moz.build
@@ -0,0 +1,137 @@
+with Files("**"):
+ BUG_COMPONENT = ("Core", "DOM: Navigation")
+
+with Files("browser/*_bug234628*"):
+ BUG_COMPONENT = ("Core", "Internationalization")
+
+with Files("browser/*_bug349769*"):
+ BUG_COMPONENT = ("Core", "DOM: Core & HTML")
+
+with Files("browser/*_bug388121*"):
+ BUG_COMPONENT = ("Core", "DOM: Core & HTML")
+
+with Files("browser/*_bug655270*"):
+ BUG_COMPONENT = ("Toolkit", "Places")
+
+with Files("browser/*_bug655273*"):
+ BUG_COMPONENT = ("Firefox", "Menus")
+
+with Files("browser/*_bug852909*"):
+ BUG_COMPONENT = ("Firefox", "Menus")
+
+with Files("browser/*bug92473*"):
+ BUG_COMPONENT = ("Core", "Internationalization")
+
+with Files("browser/*loadDisallowInherit*"):
+ BUG_COMPONENT = ("Firefox", "Address Bar")
+
+with Files("browser/*tab_touch_events*"):
+ BUG_COMPONENT = ("Core", "DOM: Events")
+
+with Files("browser/*timelineMarkers*"):
+ BUG_COMPONENT = ("DevTools", "Performance Tools (Profiler/Timeline)")
+
+with Files("browser/*ua_emulation*"):
+ BUG_COMPONENT = ("DevTools", "General")
+
+with Files("chrome/*112564*"):
+ BUG_COMPONENT = ("Core", "Networking: HTTP")
+
+with Files("chrome/*303267*"):
+ BUG_COMPONENT = ("Core", "DOM: Core & HTML")
+
+with Files("chrome/*453650*"):
+ BUG_COMPONENT = ("Core", "Layout")
+
+with Files("chrome/*565388*"):
+ BUG_COMPONENT = ("Core", "Widget")
+
+with Files("chrome/*582176*"):
+ BUG_COMPONENT = ("Core", "DOM: Core & HTML")
+
+with Files("chrome/*608669*"):
+ BUG_COMPONENT = ("Core", "DOM: Core & HTML")
+
+with Files("chrome/*690056*"):
+ BUG_COMPONENT = ("Core", "DOM: Core & HTML")
+
+with Files("chrome/*92598*"):
+ BUG_COMPONENT = ("Core", "Networking: HTTP")
+
+with Files("iframesandbox/**"):
+ BUG_COMPONENT = ("Core", "Security")
+
+with Files("iframesandbox/*marquee_event_handlers*"):
+ BUG_COMPONENT = ("Core", "DOM: Security")
+
+
+with Files("mochitest/*1045096*"):
+ BUG_COMPONENT = ("Core", "DOM: Core & HTML")
+
+with Files("mochitest/*1151421*"):
+ BUG_COMPONENT = ("Core", "DOM: Core & HTML")
+
+with Files("mochitest/*402210*"):
+ BUG_COMPONENT = ("Core", "DOM: Security")
+
+with Files("mochitest/*509055*"):
+ BUG_COMPONENT = ("Firefox", "Bookmarks & History")
+
+with Files("mochitest/*511449*"):
+ BUG_COMPONENT = ("Core", "Widget: Cocoa")
+
+with Files("mochitest/*551225*"):
+ BUG_COMPONENT = ("Core", "DOM: Core & HTML")
+
+with Files("mochitest/*570341*"):
+ BUG_COMPONENT = ("Core", "DOM: Core & HTML")
+
+with Files("mochitest/*580069*"):
+ BUG_COMPONENT = ("Core", "DOM: Core & HTML")
+
+with Files("mochitest/*637644*"):
+ BUG_COMPONENT = ("Core", "DOM: Core & HTML")
+
+with Files("mochitest/*640387*"):
+ BUG_COMPONENT = ("Core", "DOM: Events")
+
+with Files("mochitest/*668513*"):
+ BUG_COMPONENT = ("Core", "DOM: Core & HTML")
+
+with Files("mochitest/*797909*"):
+ BUG_COMPONENT = ("Core", "DOM: Core & HTML")
+
+with Files("mochitest/*forceinheritprincipal*"):
+ BUG_COMPONENT = ("Core", "DOM: Security")
+
+
+with Files("navigation/*13871.html"):
+ BUG_COMPONENT = ("Core", "Security")
+
+with Files("navigation/*386782*"):
+ BUG_COMPONENT = ("Core", "DOM: Editor")
+
+with Files("navigation/*430624*"):
+ BUG_COMPONENT = ("Core", "DOM: Editor")
+
+with Files("navigation/*430723*"):
+ BUG_COMPONENT = ("Core", "DOM: UI Events & Focus Handling")
+
+with Files("navigation/*child*"):
+ BUG_COMPONENT = ("Core", "Security")
+
+with Files("navigation/*opener*"):
+ BUG_COMPONENT = ("Core", "Security")
+
+with Files("navigation/*reserved*"):
+ BUG_COMPONENT = ("Core", "Security")
+
+with Files("navigation/*triggering*"):
+ BUG_COMPONENT = ("Core", "DOM: Security")
+
+
+with Files("unit/*442584*"):
+ BUG_COMPONENT = ("Core", "Networking: Cache")
+
+with Files("unit/*setUsePrivateBrowsing*"):
+ BUG_COMPONENT = ("Firefox", "Extension Compatibility")
diff --git a/docshell/test/navigation/NavigationUtils.js b/docshell/test/navigation/NavigationUtils.js
new file mode 100644
index 0000000000..c4b52dc62f
--- /dev/null
+++ b/docshell/test/navigation/NavigationUtils.js
@@ -0,0 +1,203 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// /////////////////////////////////////////////////////////////////////////
+//
+// Utilities for navigation tests
+//
+// /////////////////////////////////////////////////////////////////////////
+
+var body = "This frame was navigated.";
+var target_url = "navigation_target_url.html";
+
+var popup_body = "This is a popup";
+var target_popup_url = "navigation_target_popup_url.html";
+
+// /////////////////////////////////////////////////////////////////////////
+// Functions that navigate frames
+// /////////////////////////////////////////////////////////////////////////
+
+function navigateByLocation(wnd) {
+ try {
+ wnd.location = target_url;
+ } catch (ex) {
+ // We need to keep our finished frames count consistent.
+ // Oddly, this ends up simulating the behavior of IE7.
+ window.open(target_url, "_blank", "width=10,height=10");
+ }
+}
+
+function navigateByOpen(name) {
+ window.open(target_url, name, "width=10,height=10");
+}
+
+function navigateByForm(name) {
+ var form = document.createElement("form");
+ form.action = target_url;
+ form.method = "POST";
+ form.target = name;
+ document.body.appendChild(form);
+ form.submit();
+}
+
+var hyperlink_count = 0;
+
+function navigateByHyperlink(name) {
+ var link = document.createElement("a");
+ link.href = target_url;
+ link.target = name;
+ link.id = "navigation_hyperlink_" + hyperlink_count++;
+ document.body.appendChild(link);
+ sendMouseEvent({ type: "click" }, link.id);
+}
+
+// /////////////////////////////////////////////////////////////////////////
+// Functions that call into Mochitest framework
+// /////////////////////////////////////////////////////////////////////////
+
+async function isNavigated(wnd, message) {
+ var result = null;
+ try {
+ result = await SpecialPowers.spawn(wnd, [], () =>
+ this.content.document.body.innerHTML.trim()
+ );
+ } catch (ex) {
+ result = ex;
+ }
+ is(result, body, message);
+}
+
+function isBlank(wnd, message) {
+ var result = null;
+ try {
+ result = wnd.document.body.innerHTML.trim();
+ } catch (ex) {
+ result = ex;
+ }
+ is(result, "This is a blank document.", message);
+}
+
+function isAccessible(wnd, message) {
+ try {
+ wnd.document.body.innerHTML;
+ ok(true, message);
+ } catch (ex) {
+ ok(false, message);
+ }
+}
+
+function isInaccessible(wnd, message) {
+ try {
+ wnd.document.body.innerHTML;
+ ok(false, message);
+ } catch (ex) {
+ ok(true, message);
+ }
+}
+
+function delay(msec) {
+ return new Promise(resolve => setTimeout(resolve, msec));
+}
+
+// /////////////////////////////////////////////////////////////////////////
+// Functions that uses SpecialPowers.spawn
+// /////////////////////////////////////////////////////////////////////////
+
+async function waitForFinishedFrames(numFrames) {
+ SimpleTest.requestFlakyTimeout("Polling");
+
+ var finishedWindows = new Set();
+
+ async function searchForFinishedFrames(win) {
+ try {
+ let { href, bodyText, readyState } = await SpecialPowers.spawn(
+ win,
+ [],
+ () => {
+ return {
+ href: this.content.location.href,
+ bodyText:
+ this.content.document.body &&
+ this.content.document.body.textContent.trim(),
+ readyState: this.content.document.readyState,
+ };
+ }
+ );
+
+ if (
+ (href.endsWith(target_url) || href.endsWith(target_popup_url)) &&
+ (bodyText == body || bodyText == popup_body) &&
+ readyState == "complete"
+ ) {
+ finishedWindows.add(SpecialPowers.getBrowsingContextID(win));
+ }
+ } catch (e) {
+ // This may throw if a frame is not fully initialized, in which
+ // case we'll handle it in a later iteration.
+ }
+
+ for (let i = 0; i < win.frames.length; i++) {
+ await searchForFinishedFrames(win.frames[i]);
+ }
+ }
+
+ while (finishedWindows.size < numFrames) {
+ await delay(500);
+
+ for (let win of SpecialPowers.getGroupTopLevelWindows(window)) {
+ win = SpecialPowers.unwrap(win);
+ await searchForFinishedFrames(win);
+ }
+ }
+
+ if (finishedWindows.size > numFrames) {
+ throw new Error("Too many frames loaded.");
+ }
+}
+
+async function getFramesByName(name) {
+ let results = [];
+ for (let win of SpecialPowers.getGroupTopLevelWindows(window)) {
+ win = SpecialPowers.unwrap(win);
+ if (
+ (await SpecialPowers.spawn(win, [], () => this.content.name)) === name
+ ) {
+ results.push(win);
+ }
+ }
+
+ return results;
+}
+
+async function cleanupWindows() {
+ for (let win of SpecialPowers.getGroupTopLevelWindows(window)) {
+ win = SpecialPowers.unwrap(win);
+ if (win.closed) {
+ continue;
+ }
+
+ let href = "";
+ try {
+ href = await SpecialPowers.spawn(
+ win,
+ [],
+ () =>
+ this.content && this.content.location && this.content.location.href
+ );
+ } catch (error) {
+ // SpecialPowers.spawn(win, ...) throws if win is closed. We did
+ // our best to not call it on a closed window, but races happen.
+ if (!win.closed) {
+ throw error;
+ }
+ }
+
+ if (
+ href &&
+ (href.endsWith(target_url) || href.endsWith(target_popup_url))
+ ) {
+ win.close();
+ }
+ }
+}
diff --git a/docshell/test/navigation/blank.html b/docshell/test/navigation/blank.html
new file mode 100644
index 0000000000..5360333f1d
--- /dev/null
+++ b/docshell/test/navigation/blank.html
@@ -0,0 +1 @@
+<html><body>This is a blank document.</body></html> \ No newline at end of file
diff --git a/docshell/test/navigation/bluebox_bug430723.html b/docshell/test/navigation/bluebox_bug430723.html
new file mode 100644
index 0000000000..5dcc533562
--- /dev/null
+++ b/docshell/test/navigation/bluebox_bug430723.html
@@ -0,0 +1,6 @@
+<html><head>
+<script> window.addEventListener("pageshow", function() { opener.nextTest(); }); </script>
+</head><body>
+<div style="position:absolute; left:0px; top:0px; width:50%; height:150%; background-color:blue">
+<p>This is a very tall blue box.</p>
+</div></body></html>
diff --git a/docshell/test/navigation/browser.ini b/docshell/test/navigation/browser.ini
new file mode 100644
index 0000000000..baca6b401c
--- /dev/null
+++ b/docshell/test/navigation/browser.ini
@@ -0,0 +1,23 @@
+[DEFAULT]
+support-files =
+ bug343515_pg1.html
+ bug343515_pg2.html
+ bug343515_pg3.html
+ bug343515_pg3_1.html
+ bug343515_pg3_1_1.html
+ bug343515_pg3_2.html
+ blank.html
+ redirect_to_blank.sjs
+
+[browser_bug1757458.js]
+[browser_test_bfcache_eviction.js]
+[browser_bug343515.js]
+[browser_test-content-chromeflags.js]
+tags = openwindow
+[browser_ghistorymaxsize_is_0.js]
+[browser_test_shentry_wireframe.js]
+skip-if =
+ !sessionHistoryInParent
+ os == "win" && os_version == "6.1" # Skip on Azure - frequent failure
+[browser_test_simultaneous_normal_and_history_loads.js]
+skip-if = !sessionHistoryInParent # The test is for the new session history
diff --git a/docshell/test/navigation/browser_bug1757458.js b/docshell/test/navigation/browser_bug1757458.js
new file mode 100644
index 0000000000..2c960bbf45
--- /dev/null
+++ b/docshell/test/navigation/browser_bug1757458.js
@@ -0,0 +1,45 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function() {
+ let testPage =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "view-source:http://example.com"
+ ) + "redirect_to_blank.sjs";
+
+ let testPage2 = "data:text/html,<div>Second page</div>";
+ await BrowserTestUtils.withNewTab({ gBrowser, url: testPage }, async function(
+ browser
+ ) {
+ await ContentTask.spawn(browser, [], async () => {
+ Assert.ok(
+ content.document.getElementById("viewsource").localName == "body",
+ "view-source document's body should have id='viewsource'."
+ );
+ content.document
+ .getElementById("viewsource")
+ .setAttribute("onunload", "/* disable bfcache*/");
+ });
+
+ BrowserTestUtils.loadURI(browser, testPage2);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ let pageShownPromise = BrowserTestUtils.waitForContentEvent(
+ browser,
+ "pageshow",
+ true
+ );
+ browser.browsingContext.goBack();
+ await pageShownPromise;
+
+ await ContentTask.spawn(browser, [], async () => {
+ Assert.ok(
+ content.document.getElementById("viewsource").localName == "body",
+ "view-source document's body should have id='viewsource'."
+ );
+ });
+ });
+});
diff --git a/docshell/test/navigation/browser_bug343515.js b/docshell/test/navigation/browser_bug343515.js
new file mode 100644
index 0000000000..dd6fbdca41
--- /dev/null
+++ b/docshell/test/navigation/browser_bug343515.js
@@ -0,0 +1,267 @@
+// Test for bug 343515 - Need API for tabbrowsers to tell docshells they're visible/hidden
+
+// Globals
+var testPath = "http://mochi.test:8888/browser/docshell/test/navigation/";
+var ctx = {};
+
+add_task(async function() {
+ // Step 1.
+
+ // Get a handle on the initial tab
+ ctx.tab0 = gBrowser.selectedTab;
+ ctx.tab0Browser = gBrowser.getBrowserForTab(ctx.tab0);
+
+ await BrowserTestUtils.waitForCondition(
+ () => ctx.tab0Browser.docShellIsActive,
+ "Timed out waiting for initial tab to be active."
+ );
+
+ // Open a New Tab
+ ctx.tab1 = BrowserTestUtils.addTab(gBrowser, testPath + "bug343515_pg1.html");
+ ctx.tab1Browser = gBrowser.getBrowserForTab(ctx.tab1);
+ await BrowserTestUtils.browserLoaded(ctx.tab1Browser);
+
+ // Step 2.
+ is(
+ testPath + "bug343515_pg1.html",
+ ctx.tab1Browser.currentURI.spec,
+ "Got expected tab 1 url in step 2"
+ );
+
+ // Our current tab should still be active
+ ok(ctx.tab0Browser.docShellIsActive, "Tab 0 should still be active");
+ ok(!ctx.tab1Browser.docShellIsActive, "Tab 1 should not be active");
+
+ // Switch to tab 1
+ await BrowserTestUtils.switchTab(gBrowser, ctx.tab1);
+
+ // Tab 1 should now be active
+ ok(!ctx.tab0Browser.docShellIsActive, "Tab 0 should be inactive");
+ ok(ctx.tab1Browser.docShellIsActive, "Tab 1 should be active");
+
+ // Open another tab
+ ctx.tab2 = BrowserTestUtils.addTab(gBrowser, testPath + "bug343515_pg2.html");
+ ctx.tab2Browser = gBrowser.getBrowserForTab(ctx.tab2);
+
+ await BrowserTestUtils.browserLoaded(ctx.tab2Browser);
+
+ // Step 3.
+ is(
+ testPath + "bug343515_pg2.html",
+ ctx.tab2Browser.currentURI.spec,
+ "Got expected tab 2 url in step 3"
+ );
+
+ // Tab 0 should be inactive, Tab 1 should be active
+ ok(!ctx.tab0Browser.docShellIsActive, "Tab 0 should be inactive");
+ ok(ctx.tab1Browser.docShellIsActive, "Tab 1 should be active");
+
+ // Tab 2's window _and_ its iframes should be inactive
+ ok(!ctx.tab2Browser.docShellIsActive, "Tab 2 should be inactive");
+
+ await SpecialPowers.spawn(ctx.tab2Browser, [], async function() {
+ Assert.equal(content.frames.length, 2, "Tab 2 should have 2 iframes");
+ for (var i = 0; i < content.frames.length; i++) {
+ info("step 3, frame " + i + " info: " + content.frames[i].location);
+ let bc = content.frames[i].browsingContext;
+ Assert.ok(!bc.isActive, `Tab2 iframe ${i} should be inactive`);
+ }
+ });
+
+ // Navigate tab 2 to a different page
+ BrowserTestUtils.loadURI(ctx.tab2Browser, testPath + "bug343515_pg3.html");
+
+ await BrowserTestUtils.browserLoaded(ctx.tab2Browser);
+
+ // Step 4.
+
+ async function checkTab2Active(outerExpected) {
+ await SpecialPowers.spawn(ctx.tab2Browser, [outerExpected], async function(
+ expected
+ ) {
+ function isActive(aWindow) {
+ var docshell = aWindow.docShell;
+ info(`checking ${docshell.browsingContext.id}`);
+ return docshell.browsingContext.isActive;
+ }
+
+ let active = expected ? "active" : "inactive";
+ Assert.equal(content.frames.length, 2, "Tab 2 should have 2 iframes");
+ for (var i = 0; i < content.frames.length; i++) {
+ info("step 4, frame " + i + " info: " + content.frames[i].location);
+ }
+ Assert.equal(
+ content.frames[0].frames.length,
+ 1,
+ "Tab 2 iframe 0 should have 1 iframes"
+ );
+ Assert.equal(
+ isActive(content.frames[0]),
+ expected,
+ `Tab2 iframe 0 should be ${active}`
+ );
+ Assert.equal(
+ isActive(content.frames[0].frames[0]),
+ expected,
+ `Tab2 iframe 0 subiframe 0 should be ${active}`
+ );
+ Assert.equal(
+ isActive(content.frames[1]),
+ expected,
+ `Tab2 iframe 1 should be ${active}`
+ );
+ });
+ }
+
+ is(
+ testPath + "bug343515_pg3.html",
+ ctx.tab2Browser.currentURI.spec,
+ "Got expected tab 2 url in step 4"
+ );
+
+ // Tab 0 should be inactive, Tab 1 should be active
+ ok(!ctx.tab0Browser.docShellIsActive, "Tab 0 should be inactive");
+ ok(ctx.tab1Browser.docShellIsActive, "Tab 1 should be active");
+
+ // Tab2 and all descendants should be inactive
+ await checkTab2Active(false);
+
+ // Switch to Tab 2
+ await BrowserTestUtils.switchTab(gBrowser, ctx.tab2);
+
+ // Check everything
+ ok(!ctx.tab0Browser.docShellIsActive, "Tab 0 should be inactive");
+ ok(!ctx.tab1Browser.docShellIsActive, "Tab 1 should be inactive");
+ ok(ctx.tab2Browser.docShellIsActive, "Tab 2 should be active");
+
+ await checkTab2Active(true);
+
+ // Go back
+ let backDone = BrowserTestUtils.waitForLocationChange(
+ gBrowser,
+ testPath + "bug343515_pg2.html"
+ );
+ ctx.tab2Browser.goBack();
+ await backDone;
+
+ // Step 5.
+
+ // Check everything
+ ok(!ctx.tab0Browser.docShellIsActive, "Tab 0 should be inactive");
+ ok(!ctx.tab1Browser.docShellIsActive, "Tab 1 should be inactive");
+ ok(ctx.tab2Browser.docShellIsActive, "Tab 2 should be active");
+ is(
+ testPath + "bug343515_pg2.html",
+ ctx.tab2Browser.currentURI.spec,
+ "Got expected tab 2 url in step 5"
+ );
+
+ await SpecialPowers.spawn(ctx.tab2Browser, [], async function() {
+ for (var i = 0; i < content.frames.length; i++) {
+ let bc = content.frames[i].browsingContext;
+ Assert.ok(bc.isActive, `Tab2 iframe ${i} should be active`);
+ }
+ });
+
+ // Switch to tab 1
+ await BrowserTestUtils.switchTab(gBrowser, ctx.tab1);
+
+ // Navigate to page 3
+ BrowserTestUtils.loadURI(ctx.tab1Browser, testPath + "bug343515_pg3.html");
+
+ await BrowserTestUtils.browserLoaded(ctx.tab1Browser);
+
+ // Step 6.
+
+ // Check everything
+ ok(!ctx.tab0Browser.docShellIsActive, "Tab 0 should be inactive");
+ ok(ctx.tab1Browser.docShellIsActive, "Tab 1 should be active");
+ is(
+ testPath + "bug343515_pg3.html",
+ ctx.tab1Browser.currentURI.spec,
+ "Got expected tab 1 url in step 6"
+ );
+
+ await SpecialPowers.spawn(ctx.tab1Browser, [], async function() {
+ function isActive(aWindow) {
+ var docshell = aWindow.docShell;
+ info(`checking ${docshell.browsingContext.id}`);
+ return docshell.browsingContext.isActive;
+ }
+
+ Assert.ok(isActive(content.frames[0]), "Tab1 iframe 0 should be active");
+ Assert.ok(
+ isActive(content.frames[0].frames[0]),
+ "Tab1 iframe 0 subiframe 0 should be active"
+ );
+ Assert.ok(isActive(content.frames[1]), "Tab1 iframe 1 should be active");
+ });
+
+ ok(!ctx.tab2Browser.docShellIsActive, "Tab 2 should be inactive");
+
+ await SpecialPowers.spawn(ctx.tab2Browser, [], async function() {
+ for (var i = 0; i < content.frames.length; i++) {
+ let bc = content.frames[i].browsingContext;
+ Assert.ok(!bc.isActive, `Tab2 iframe ${i} should be inactive`);
+ }
+ });
+
+ // Go forward on tab 2
+ let forwardDone = BrowserTestUtils.waitForLocationChange(
+ gBrowser,
+ testPath + "bug343515_pg3.html"
+ );
+ ctx.tab2Browser.goForward();
+ await forwardDone;
+
+ // Step 7.
+
+ async function checkBrowser(browser, outerTabNum, outerActive) {
+ let data = { tabNum: outerTabNum, active: outerActive };
+ await SpecialPowers.spawn(browser, [data], async function({
+ tabNum,
+ active,
+ }) {
+ function isActive(aWindow) {
+ var docshell = aWindow.docShell;
+ info(`checking ${docshell.browsingContext.id}`);
+ return docshell.browsingContext.isActive;
+ }
+
+ let activestr = active ? "active" : "inactive";
+ Assert.equal(
+ isActive(content.frames[0]),
+ active,
+ `Tab${tabNum} iframe 0 should be ${activestr}`
+ );
+ Assert.equal(
+ isActive(content.frames[0].frames[0]),
+ active,
+ `Tab${tabNum} iframe 0 subiframe 0 should be ${activestr}`
+ );
+ Assert.equal(
+ isActive(content.frames[1]),
+ active,
+ `Tab${tabNum} iframe 1 should be ${activestr}`
+ );
+ });
+ }
+
+ // Check everything
+ ok(!ctx.tab0Browser.docShellIsActive, "Tab 0 should be inactive");
+ ok(ctx.tab1Browser.docShellIsActive, "Tab 1 should be active");
+ is(
+ testPath + "bug343515_pg3.html",
+ ctx.tab2Browser.currentURI.spec,
+ "Got expected tab 2 url in step 7"
+ );
+
+ await checkBrowser(ctx.tab1Browser, 1, true);
+
+ ok(!ctx.tab2Browser.docShellIsActive, "Tab 2 should be inactive");
+ await checkBrowser(ctx.tab2Browser, 2, false);
+
+ // Close the tabs we made
+ BrowserTestUtils.removeTab(ctx.tab1);
+ BrowserTestUtils.removeTab(ctx.tab2);
+});
diff --git a/docshell/test/navigation/browser_ghistorymaxsize_is_0.js b/docshell/test/navigation/browser_ghistorymaxsize_is_0.js
new file mode 100644
index 0000000000..4034f63bd9
--- /dev/null
+++ b/docshell/test/navigation/browser_ghistorymaxsize_is_0.js
@@ -0,0 +1,81 @@
+add_task(async function() {
+ // The urls don't really matter as long as they are of the same origin
+ var URL =
+ "http://mochi.test:8888/browser/docshell/test/navigation/bug343515_pg1.html";
+ var URL2 =
+ "http://mochi.test:8888/browser/docshell/test/navigation/bug343515_pg3_1.html";
+
+ // We want to test a specific code path that leads to this call
+ // https://searchfox.org/mozilla-central/rev/e7c61f4a68b974d5fecd216dc7407b631a24eb8f/docshell/base/nsDocShell.cpp#10795
+ // when gHistoryMaxSize is 0 and mIndex and mRequestedIndex are -1
+
+ // 1. Navigate to URL
+ await BrowserTestUtils.withNewTab({ gBrowser, url: URL }, async function(
+ browser
+ ) {
+ // At this point, we haven't set gHistoryMaxSize to 0, and it is still 50 (default value).
+ if (SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ let sh = browser.browsingContext.sessionHistory;
+ is(
+ sh.count,
+ 1,
+ "We should have entry in session history because we haven't changed gHistoryMaxSize to be 0 yet"
+ );
+ is(
+ sh.index,
+ 0,
+ "Shistory's current index should be 0 because we haven't purged history yet"
+ );
+ } else {
+ await ContentTask.spawn(browser, null, () => {
+ var sh = content.window.docShell.QueryInterface(Ci.nsIWebNavigation)
+ .sessionHistory.legacySHistory;
+ is(
+ sh.count,
+ 1,
+ "We should have entry in session history because we haven't changed gHistoryMaxSize to be 0 yet"
+ );
+ is(
+ sh.index,
+ 0,
+ "Shistory's current index should be 0 because we haven't purged history yet"
+ );
+ });
+ }
+
+ var loadPromise = BrowserTestUtils.browserLoaded(browser, false, URL2);
+ // If we set the pref at the beginning of this page, then when we launch a child process
+ // to navigate to URL in Step 1, because of
+ // https://searchfox.org/mozilla-central/rev/e7c61f4a68b974d5fecd216dc7407b631a24eb8f/docshell/shistory/nsSHistory.cpp#308-312
+ // this pref will be set to the default value (currently 50). Setting this pref after the child process launches
+ // is a robust way to make sure it stays 0
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.sessionhistory.max_entries", 0]],
+ });
+ // 2. Navigate to URL2
+ // We are navigating to a page with the same origin so that we will stay in the same process
+ BrowserTestUtils.loadURI(browser, URL2);
+ await loadPromise;
+
+ // 3. Reload the browser with specific flags so that we end up here
+ // https://searchfox.org/mozilla-central/rev/e7c61f4a68b974d5fecd216dc7407b631a24eb8f/docshell/base/nsDocShell.cpp#10795
+ var promise = BrowserTestUtils.browserLoaded(browser);
+ browser.reloadWithFlags(Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE);
+ await promise;
+
+ if (SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ let sh = browser.browsingContext.sessionHistory;
+ is(sh.count, 0, "We should not save any entries in session history");
+ is(sh.index, -1);
+ is(sh.requestedIndex, -1);
+ } else {
+ await ContentTask.spawn(browser, null, () => {
+ var sh = content.window.docShell.QueryInterface(Ci.nsIWebNavigation)
+ .sessionHistory.legacySHistory;
+ is(sh.count, 0, "We should not save any entries in session history");
+ is(sh.index, -1);
+ is(sh.requestedIndex, -1);
+ });
+ }
+ });
+});
diff --git a/docshell/test/navigation/browser_test-content-chromeflags.js b/docshell/test/navigation/browser_test-content-chromeflags.js
new file mode 100644
index 0000000000..af527b2dbc
--- /dev/null
+++ b/docshell/test/navigation/browser_test-content-chromeflags.js
@@ -0,0 +1,57 @@
+const TEST_PAGE = `data:text/html,<html><body><a href="about:blank" target="_blank">Test</a></body></html>`;
+const {
+ CHROME_ALL,
+ CHROME_REMOTE_WINDOW,
+ CHROME_FISSION_WINDOW,
+} = Ci.nsIWebBrowserChrome;
+
+/**
+ * Tests that when we open new browser windows from content they
+ * get the full browser chrome.
+ */
+add_task(async function() {
+ // Make sure that the window.open call will open a new
+ // window instead of a new tab.
+ await new Promise(resolve => {
+ SpecialPowers.pushPrefEnv(
+ {
+ set: [["browser.link.open_newwindow", 2]],
+ },
+ resolve
+ );
+ });
+
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: TEST_PAGE,
+ },
+ async function(browser) {
+ let openedPromise = BrowserTestUtils.waitForNewWindow();
+ BrowserTestUtils.synthesizeMouse("a", 0, 0, {}, browser);
+ let win = await openedPromise;
+
+ let chromeFlags = win.docShell.treeOwner
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIAppWindow).chromeFlags;
+
+ let expected = CHROME_ALL;
+
+ // In the multi-process tab case, the new window will have the
+ // CHROME_REMOTE_WINDOW flag set.
+ if (gMultiProcessBrowser) {
+ expected |= CHROME_REMOTE_WINDOW;
+ }
+
+ // In the multi-process subframe case, the new window will have the
+ // CHROME_FISSION_WINDOW flag set.
+ if (gFissionBrowser) {
+ expected |= CHROME_FISSION_WINDOW;
+ }
+
+ is(chromeFlags, expected, "Window should have opened with all chrome");
+
+ await BrowserTestUtils.closeWindow(win);
+ }
+ );
+});
diff --git a/docshell/test/navigation/browser_test_bfcache_eviction.js b/docshell/test/navigation/browser_test_bfcache_eviction.js
new file mode 100644
index 0000000000..fe0db2742b
--- /dev/null
+++ b/docshell/test/navigation/browser_test_bfcache_eviction.js
@@ -0,0 +1,98 @@
+add_task(async function() {
+ // We don't want the number of total viewers to be calculated by the available size
+ // for this test case. Instead, fix the number of viewers.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.sessionhistory.max_total_viewers", 3],
+ ["docshell.shistory.testing.bfevict", true],
+ // If Fission is disabled, the pref is no-op.
+ ["fission.bfcacheInParent", true],
+ ],
+ });
+
+ // 1. Open a tab
+ var testPage =
+ "data:text/html,<html id='html1'><body id='body1'>First tab ever opened</body></html>";
+ await BrowserTestUtils.withNewTab({ gBrowser, url: testPage }, async function(
+ browser
+ ) {
+ let testDone = {};
+ if (!SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ // 2. Add a promise that will be resolved when the 'content viewer evicted' event goes off
+ testDone.promise = SpecialPowers.spawn(browser, [], async function() {
+ return new Promise(resolve => {
+ let webNavigation = content.docShell.QueryInterface(
+ Ci.nsIWebNavigation
+ );
+ let { legacySHistory } = webNavigation.sessionHistory;
+ // 3. Register a session history listener to listen for a 'content viewer evicted' event.
+ let historyListener = {
+ OnContentViewerEvicted() {
+ ok(
+ true,
+ "History listener got called after a content viewer was evicted"
+ );
+ legacySHistory.removeSHistoryListener(historyListener);
+ // 6. Resolve the promise when we got our 'content viewer evicted' event
+ resolve();
+ },
+ QueryInterface: ChromeUtils.generateQI([
+ "nsISHistoryListener",
+ "nsISupportsWeakReference",
+ ]),
+ };
+ legacySHistory.addSHistoryListener(historyListener);
+ // Keep the weak shistory listener alive
+ content._testListener = historyListener;
+ });
+ });
+ } else {
+ // 2. Add a promise that will be resolved when the 'content viewer evicted' event goes off
+ testDone.promise = new Promise(resolve => {
+ testDone.resolve = resolve;
+ });
+ let shistory = browser.browsingContext.sessionHistory;
+ // 3. Register a session history listener to listen for a 'content viewer evicted' event.
+ let historyListener = {
+ OnContentViewerEvicted() {
+ ok(
+ true,
+ "History listener got called after a content viewer was evicted"
+ );
+ shistory.removeSHistoryListener(historyListener);
+ delete window._testListener;
+ // 6. Resolve the promise when we got our 'content viewer evicted' event
+ testDone.resolve();
+ },
+ QueryInterface: ChromeUtils.generateQI([
+ "nsISHistoryListener",
+ "nsISupportsWeakReference",
+ ]),
+ };
+ shistory.addSHistoryListener(historyListener);
+ // Keep the weak shistory listener alive
+ window._testListener = historyListener;
+ }
+
+ // 4. Open a second tab
+ testPage = `data:text/html,<html id='html1'><body id='body1'>I am a second tab!</body></html>`;
+ let tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser, testPage);
+
+ // 5. Navigate the first tab to 4 different pages.
+ // We should get 1 content viewer evicted because it will be outside of the range.
+ // If we have the following pages in our session history: P1 P2 P3 P4 P5
+ // and we are currently at P5, then P1 is outside of the range
+ // (it is more than 3 entries away from current entry) and thus will be evicted.
+ for (var i = 0; i < 4; i++) {
+ testPage = `data:text/html,<html id='html1'><body id='body1'>${i}</body></html>`;
+ let pagePromise = BrowserTestUtils.browserLoaded(browser);
+ BrowserTestUtils.loadURI(browser, testPage);
+ await pagePromise;
+ }
+ // 7. Wait for 'content viewer evicted' event to go off
+ await testDone.promise;
+
+ // 8. Close the second tab
+ BrowserTestUtils.removeTab(tab2);
+ });
+});
diff --git a/docshell/test/navigation/browser_test_shentry_wireframe.js b/docshell/test/navigation/browser_test_shentry_wireframe.js
new file mode 100644
index 0000000000..a4a78ac961
--- /dev/null
+++ b/docshell/test/navigation/browser_test_shentry_wireframe.js
@@ -0,0 +1,128 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const BUILDER = "http://mochi.test:8888/document-builder.sjs?html=";
+const PAGE_1 = BUILDER + encodeURIComponent(`<html><body>Page 1</body></html>`);
+const PAGE_2 = BUILDER + encodeURIComponent(`<html><body>Page 2</body></html>`);
+
+add_setup(async function() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.history.collectWireframes", true]],
+ });
+});
+
+/**
+ * Test that capturing wireframes on nsISHEntriy's in the parent process
+ * happens at the right times.
+ */
+add_task(async function() {
+ await BrowserTestUtils.withNewTab(PAGE_1, async browser => {
+ let sh = browser.browsingContext.sessionHistory;
+ Assert.equal(
+ sh.count,
+ 1,
+ "Got the right SessionHistory entry count after initial tab load."
+ );
+ Assert.ok(
+ !sh.getEntryAtIndex(0).wireframe,
+ "No wireframe for the loaded entry after initial tab load."
+ );
+
+ let loaded = BrowserTestUtils.browserLoaded(browser, false, PAGE_2);
+ BrowserTestUtils.loadURI(browser, PAGE_2);
+ await loaded;
+
+ Assert.equal(
+ sh.count,
+ 2,
+ "Got the right SessionHistory entry count after loading page 2."
+ );
+ Assert.ok(
+ sh.getEntryAtIndex(0).wireframe,
+ "A wireframe was captured for the first entry after loading page 2."
+ );
+ Assert.ok(
+ !sh.getEntryAtIndex(1).wireframe,
+ "No wireframe for the loaded entry after loading page 2."
+ );
+
+ // Now go back
+ loaded = BrowserTestUtils.waitForContentEvent(browser, "pageshow");
+ browser.goBack();
+ await loaded;
+ Assert.ok(
+ sh.getEntryAtIndex(1).wireframe,
+ "A wireframe was captured for the second entry after going back."
+ );
+ Assert.ok(
+ !sh.getEntryAtIndex(0).wireframe,
+ "No wireframe for the loaded entry after going back."
+ );
+
+ // Now forward again
+ loaded = BrowserTestUtils.waitForContentEvent(browser, "pageshow");
+ browser.goForward();
+ await loaded;
+
+ Assert.equal(
+ sh.count,
+ 2,
+ "Got the right SessionHistory entry count after going forward again."
+ );
+ Assert.ok(
+ sh.getEntryAtIndex(0).wireframe,
+ "A wireframe was captured for the first entry after going forward again."
+ );
+ Assert.ok(
+ !sh.getEntryAtIndex(1).wireframe,
+ "No wireframe for the loaded entry after going forward again."
+ );
+
+ // And using pushState
+ await SpecialPowers.spawn(browser, [], async () => {
+ content.history.pushState({}, "", "nothing-1.html");
+ content.history.pushState({}, "", "nothing-2.html");
+ });
+
+ Assert.equal(
+ sh.count,
+ 4,
+ "Got the right SessionHistory entry count after using pushState."
+ );
+ Assert.ok(
+ sh.getEntryAtIndex(0).wireframe,
+ "A wireframe was captured for the first entry after using pushState."
+ );
+ Assert.ok(
+ sh.getEntryAtIndex(1).wireframe,
+ "A wireframe was captured for the second entry after using pushState."
+ );
+ Assert.ok(
+ sh.getEntryAtIndex(2).wireframe,
+ "A wireframe was captured for the third entry after using pushState."
+ );
+ Assert.ok(
+ !sh.getEntryAtIndex(3).wireframe,
+ "No wireframe for the loaded entry after using pushState."
+ );
+
+ // Now check that wireframes can be written to in case we're restoring
+ // an nsISHEntry from serialization.
+ let wireframe = sh.getEntryAtIndex(2).wireframe;
+ sh.getEntryAtIndex(2).wireframe = null;
+ Assert.equal(
+ sh.getEntryAtIndex(2).wireframe,
+ null,
+ "Successfully cleared wireframe."
+ );
+
+ sh.getEntryAtIndex(3).wireframe = wireframe;
+ Assert.deepEqual(
+ sh.getEntryAtIndex(3).wireframe,
+ wireframe,
+ "Successfully wrote a wireframe to an nsISHEntry."
+ );
+ });
+});
diff --git a/docshell/test/navigation/browser_test_simultaneous_normal_and_history_loads.js b/docshell/test/navigation/browser_test_simultaneous_normal_and_history_loads.js
new file mode 100644
index 0000000000..559cc6180c
--- /dev/null
+++ b/docshell/test/navigation/browser_test_simultaneous_normal_and_history_loads.js
@@ -0,0 +1,53 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function test_normal_and_history_loads() {
+ // This test is for the case when session history lives in the parent process.
+ // BFCache is disabled to get more asynchronousness.
+ await SpecialPowers.pushPrefEnv({
+ set: [["fission.bfcacheInParent", false]],
+ });
+
+ let testPage =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com"
+ ) + "blank.html";
+ await BrowserTestUtils.withNewTab({ gBrowser, url: testPage }, async function(
+ browser
+ ) {
+ for (let i = 0; i < 2; ++i) {
+ BrowserTestUtils.loadURI(browser, testPage + "?" + i);
+ await BrowserTestUtils.browserLoaded(browser);
+ }
+
+ let sh = browser.browsingContext.sessionHistory;
+ is(sh.count, 3, "Should have 3 entries in the session history.");
+ is(sh.index, 2, "index should be 2");
+ is(sh.requestedIndex, -1, "requestedIndex should be -1");
+
+ // The following part is racy by definition. It is testing that
+ // eventually requestedIndex should become -1 again.
+ browser.browsingContext.goToIndex(1);
+ let historyLoad = BrowserTestUtils.browserLoaded(browser);
+ /* eslint-disable mozilla/no-arbitrary-setTimeout */
+ await new Promise(r => {
+ setTimeout(r, 10);
+ });
+ browser.browsingContext.loadURI(testPage + "?newload", {
+ triggeringPrincipal: browser.nodePrincipal,
+ });
+ let newLoad = BrowserTestUtils.browserLoaded(browser);
+ // Note, the loads are racy.
+ await historyLoad;
+ await newLoad;
+ is(sh.requestedIndex, -1, "requestedIndex should be -1");
+
+ browser.browsingContext.goBack();
+ await BrowserTestUtils.browserLoaded(browser);
+ is(sh.requestedIndex, -1, "requestedIndex should be -1");
+ });
+});
diff --git a/docshell/test/navigation/bug343515_pg1.html b/docshell/test/navigation/bug343515_pg1.html
new file mode 100644
index 0000000000..a8337c7f70
--- /dev/null
+++ b/docshell/test/navigation/bug343515_pg1.html
@@ -0,0 +1,5 @@
+<html>
+ <head><meta charset="UTF-8"/></head>
+ <body>Page 1
+ </body>
+</html>
diff --git a/docshell/test/navigation/bug343515_pg2.html b/docshell/test/navigation/bug343515_pg2.html
new file mode 100644
index 0000000000..c5f5665de5
--- /dev/null
+++ b/docshell/test/navigation/bug343515_pg2.html
@@ -0,0 +1,7 @@
+<html>
+ <head><meta charset="UTF-8"/></head>
+ <body>Page 2
+ <iframe src="data:text/html;charset=UTF8,<html><head></head><body>pg2 iframe 0</body></html>"></iframe>
+ <iframe src="data:text/html;charset=UTF8,<html><head></head><body>pg2 iframe 1</body></html>"></iframe>
+ </body>
+</html>
diff --git a/docshell/test/navigation/bug343515_pg3.html b/docshell/test/navigation/bug343515_pg3.html
new file mode 100644
index 0000000000..fdc79fbf7a
--- /dev/null
+++ b/docshell/test/navigation/bug343515_pg3.html
@@ -0,0 +1,7 @@
+<html>
+ <head><meta charset="UTF-8"/></head>
+ <body>Page 3
+ <iframe src="bug343515_pg3_1.html"></iframe>
+ <iframe src="bug343515_pg3_2.html"></iframe>
+ </body>
+</html>
diff --git a/docshell/test/navigation/bug343515_pg3_1.html b/docshell/test/navigation/bug343515_pg3_1.html
new file mode 100644
index 0000000000..254164c9f0
--- /dev/null
+++ b/docshell/test/navigation/bug343515_pg3_1.html
@@ -0,0 +1,6 @@
+<html>
+ <head><meta charset="UTF-8"/></head>
+ <body>pg3 - iframe 0
+ <iframe src="bug343515_pg3_1_1.html"></iframe>
+ </body>
+</html>
diff --git a/docshell/test/navigation/bug343515_pg3_1_1.html b/docshell/test/navigation/bug343515_pg3_1_1.html
new file mode 100644
index 0000000000..be05b74888
--- /dev/null
+++ b/docshell/test/navigation/bug343515_pg3_1_1.html
@@ -0,0 +1 @@
+<html><head><meta charset="UTF-8"/></head><body>How far does the rabbit hole go?</body></html>
diff --git a/docshell/test/navigation/bug343515_pg3_2.html b/docshell/test/navigation/bug343515_pg3_2.html
new file mode 100644
index 0000000000..7655eb526d
--- /dev/null
+++ b/docshell/test/navigation/bug343515_pg3_2.html
@@ -0,0 +1 @@
+<html><head><meta charset="UTF-8"/></head><body>pg3 iframe 1</body></html>
diff --git a/docshell/test/navigation/cache_control_max_age_3600.sjs b/docshell/test/navigation/cache_control_max_age_3600.sjs
new file mode 100644
index 0000000000..49b6439c9f
--- /dev/null
+++ b/docshell/test/navigation/cache_control_max_age_3600.sjs
@@ -0,0 +1,20 @@
+function handleRequest(request, response) {
+ let query = request.queryString;
+ let action =
+ query == "initial"
+ ? "cache_control_max_age_3600.sjs?second"
+ : "goback.html";
+ response.setHeader("Content-Type", "text/html", false);
+ response.setHeader("Cache-Control", "max-age=3600");
+ response.write(
+ "<html><head><script>window.blockBFCache = new RTCPeerConnection();</script></head>" +
+ '<body onload=\'opener.postMessage("loaded", "*")\'>' +
+ "<div id='content'>" +
+ new Date().getTime() +
+ "</div>" +
+ "<form action='" +
+ action +
+ "' method='POST'></form>" +
+ "</body></html>"
+ );
+}
diff --git a/docshell/test/navigation/file_beforeunload_and_bfcache.html b/docshell/test/navigation/file_beforeunload_and_bfcache.html
new file mode 100644
index 0000000000..5c5aea2918
--- /dev/null
+++ b/docshell/test/navigation/file_beforeunload_and_bfcache.html
@@ -0,0 +1,31 @@
+<html>
+ <head>
+ <script>
+ onpageshow = function(pageShowEvent) {
+ var bc = new BroadcastChannel("beforeunload_and_bfcache");
+ bc.onmessage = function(event) {
+ if (event.data == "nextpage") {
+ bc.close();
+ location.href += "?nextpage";
+ } else if (event.data == "back") {
+ bc.close();
+ history.back();
+ } else if (event.data == "forward") {
+ onbeforeunload = function() {
+ bc.postMessage("beforeunload");
+ bc.close();
+ }
+ history.forward();
+ } else if (event.data == "close") {
+ bc.postMessage("closed");
+ bc.close();
+ window.close();
+ }
+ };
+ bc.postMessage({type: pageShowEvent.type, persisted: pageShowEvent.persisted});
+ };
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_blockBFCache.html b/docshell/test/navigation/file_blockBFCache.html
new file mode 100644
index 0000000000..dc743cdc67
--- /dev/null
+++ b/docshell/test/navigation/file_blockBFCache.html
@@ -0,0 +1,33 @@
+<script>
+let keepAlive;
+window.onpageshow = (pageShow) => {
+ let bc = new BroadcastChannel("bfcache_blocking");
+
+ bc.onmessage = async function(m) {
+ switch(m.data.message) {
+ case "load":
+ bc.close();
+ location.href = m.data.url;
+ break;
+ case "runScript":
+ let test = new Function(`return ${m.data.fun};`)();
+ keepAlive = await test.call(window);
+ bc.postMessage({ type: "runScriptDone" });
+ break;
+ case "back":
+ bc.close();
+ history.back();
+ break;
+ case "forward":
+ bc.close();
+ history.forward();
+ break;
+ case "close":
+ window.close();
+ break;
+ }
+ };
+
+ bc.postMessage({ type: pageShow.type, persisted: pageShow.persisted })
+};
+</script>
diff --git a/docshell/test/navigation/file_bug1300461.html b/docshell/test/navigation/file_bug1300461.html
new file mode 100644
index 0000000000..d7abe8be90
--- /dev/null
+++ b/docshell/test/navigation/file_bug1300461.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
+ <title>Bug 1300461</title>
+ </head>
+ <body onload="test();">
+ <script>
+ /**
+ * Bug 1300461 identifies that if a history entry was not bfcached, and
+ * a http redirection happens when navigating to that entry, the history
+ * index would mess up.
+ *
+ * The test case emulates the circumstance by the following steps
+ * 1) Navigate to file_bug1300461_back.html which is not bf-cachable.
+ * 2) In file_bug1300461_back.html, replace its own history state to
+ * file_bug1300461_redirect.html.
+ * 3) Back, and then forward. Since the document is not in bfcache, it
+ * tries to load file_bug1300461_redirect.html directly.
+ * 4) file_bug1300461_redirect.html redirects UA to
+ * file_bug1300461_back.html through HTTP 301 header.
+ *
+ * We verify the history index, canGoBack, canGoForward, etc. keep correct
+ * in this process.
+ */
+ let Ci = SpecialPowers.Ci;
+ let webNav = SpecialPowers.wrap(window)
+ .docShell
+ .QueryInterface(Ci.nsIWebNavigation);
+ let shistory = webNav.sessionHistory;
+ let testSteps = [
+ function() {
+ opener.is(shistory.count, 1, "check history length");
+ opener.is(shistory.index, 0, "check history index");
+ opener.ok(!webNav.canGoForward, "check canGoForward");
+ setTimeout(() => window.location = "file_bug1300461_back.html", 0);
+ },
+ function() {
+ opener.is(shistory.count, 2, "check history length");
+ opener.is(shistory.index, 0, "check history index");
+ opener.ok(webNav.canGoForward, "check canGoForward");
+ window.history.forward();
+ },
+ function() {
+ opener.is(shistory.count, 2, "check history length");
+ opener.is(shistory.index, 0, "check history index");
+ opener.ok(webNav.canGoForward, "check canGoForward");
+ opener.info("file_bug1300461.html tests finished");
+ opener.finishTest();
+ },
+ ];
+
+ function test() {
+ if (opener) {
+ opener.info("file_bug1300461.html test " + opener.testCount);
+ testSteps[opener.testCount++]();
+ }
+ }
+ </script>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_bug1300461_back.html b/docshell/test/navigation/file_bug1300461_back.html
new file mode 100644
index 0000000000..3ec431c7c1
--- /dev/null
+++ b/docshell/test/navigation/file_bug1300461_back.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
+ <title>Bug 1300461</title>
+ </head>
+ <!-- The empty unload handler is to prevent bfcache. -->
+ <body onload="test();" onunload="">
+ <script>
+ let Ci = SpecialPowers.Ci;
+ let webNav = SpecialPowers.wrap(window)
+ .docShell
+ .QueryInterface(Ci.nsIWebNavigation);
+ let shistory = webNav.sessionHistory;
+ async function test() {
+ if (opener) {
+ opener.info("file_bug1300461_back.html");
+ opener.is(shistory.count, 2, "check history length");
+ opener.is(shistory.index, 1, "check history index");
+ if (!SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ opener.is(shistory.legacySHistory.requestedIndex, -1, "check requestedIndex");
+ } else {
+ let index = await opener.getSHRequestedIndex(SpecialPowers.wrap(window).browsingContext.id);
+ opener.is(index, -1, "check requestedIndex");
+ }
+
+ opener.ok(webNav.canGoBack, "check canGoBack");
+ if (opener.testCount == 1) {
+ opener.info("replaceState to redirect.html");
+ window.history.replaceState({}, "", "file_bug1300461_redirect.html");
+ }
+ window.history.back();
+ }
+ }
+ </script>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_bug1300461_redirect.html b/docshell/test/navigation/file_bug1300461_redirect.html
new file mode 100644
index 0000000000..979530c5cf
--- /dev/null
+++ b/docshell/test/navigation/file_bug1300461_redirect.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
+ <title>Bug 1300461</title>
+ </head>
+ <body>
+ Redirect to file_bug1300461_back.html.
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_bug1300461_redirect.html^headers^ b/docshell/test/navigation/file_bug1300461_redirect.html^headers^
new file mode 100644
index 0000000000..241b891826
--- /dev/null
+++ b/docshell/test/navigation/file_bug1300461_redirect.html^headers^
@@ -0,0 +1,2 @@
+HTTP 301 Moved Permanently
+Location: file_bug1300461_back.html
diff --git a/docshell/test/navigation/file_bug1326251.html b/docshell/test/navigation/file_bug1326251.html
new file mode 100644
index 0000000000..57a81f46f1
--- /dev/null
+++ b/docshell/test/navigation/file_bug1326251.html
@@ -0,0 +1,212 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Bug 1326251</title>
+ <script>
+ var bc = new BroadcastChannel("file_bug1326251");
+ bc.onmessage = function(event) {
+ if ("nextTest" in event.data) {
+ testSteps[event.data.nextTest]();
+ }
+ }
+
+ function is(val1, val2, msg) {
+ bc.postMessage({type: "is", value1: val1, value2: val2, message: msg});
+ }
+
+ function ok(val, msg) {
+ bc.postMessage({type: "ok", value: val, message: msg});
+ }
+
+ const BASE_URL = "http://mochi.test:8888/tests/docshell/test/navigation/";
+ let testSteps = [
+ async function() {
+ // Test 1: Create dynamic iframe with bfcache enabled.
+ // Navigate static / dynamic iframes, then navigate top level window
+ // and navigate back. Both iframes should still exist with history
+ // entries preserved.
+ window.onunload = null; // enable bfcache
+ await createDynamicFrame(document);
+ await loadUriInFrame(document.getElementById("staticFrame"), "frame1.html");
+ await loadUriInFrame(document.getElementById("dynamicFrame"), "frame1.html");
+ await loadUriInFrame(document.getElementById("staticFrame"), "frame2.html");
+ await loadUriInFrame(document.getElementById("dynamicFrame"), "frame2.html");
+ is(history.length, 5, "history.length");
+ window.location = "goback.html";
+ },
+ async function() {
+ let webNav = SpecialPowers.wrap(window)
+ .docShell
+ .QueryInterface(SpecialPowers.Ci.nsIWebNavigation);
+ let shistory = webNav.sessionHistory;
+ is(webNav.canGoForward, true, "canGoForward");
+ is(shistory.index, 4, "shistory.index");
+ is(history.length, 6, "history.length");
+ is(document.getElementById("staticFrame").contentWindow.location.href, BASE_URL + "frame2.html", "staticFrame location");
+ is(document.getElementById("dynamicFrame").contentWindow.location.href, BASE_URL + "frame2.html", "dynamicFrame location");
+
+ // Test 2: Load another page in dynamic iframe, canGoForward should be
+ // false.
+ await loadUriInFrame(document.getElementById("dynamicFrame"), "frame3.html");
+ is(webNav.canGoForward, false, "canGoForward");
+ is(shistory.index, 5, "shistory.index");
+ is(history.length, 6, "history.length");
+
+ // Test 3: Navigate to antoher page with bfcache disabled, all dynamic
+ // iframe entries should be removed.
+ window.onunload = function() {}; // disable bfcache
+ window.location = "goback.html";
+ },
+ async function() {
+ let windowWrap = SpecialPowers.wrap(window);
+ let docShell = windowWrap.docShell;
+ let shistory = docShell.QueryInterface(SpecialPowers.Ci.nsIWebNavigation)
+ .sessionHistory;
+ // Now staticFrame has frame0 -> frame1 -> frame2.
+ if (!SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ // *EntryIndex attributes aren't meaningful when the session history
+ // lives in the parent process.
+ is(docShell.previousEntryIndex, 3, "docShell.previousEntryIndex");
+ is(docShell.loadedEntryIndex, 2, "docShell.loadedEntryIndex");
+ }
+ is(shistory.index, 2, "shistory.index");
+ is(history.length, 4, "history.length");
+ is(document.getElementById("staticFrame").contentWindow.location.href, BASE_URL + "frame2.html", "staticFrame location");
+ ok(!document.getElementById("dynamicFrame"), "dynamicFrame should not exist");
+
+ // Test 4: Load a nested frame in the static frame, navigate the inner
+ // static frame, add a inner dynamic frame and navigate the dynamic
+ // frame. Then navigate the outer static frame and go back. The inner
+ // iframe should show the last entry of inner static frame.
+ let staticFrame = document.getElementById("staticFrame");
+ staticFrame.width = "320px";
+ staticFrame.height = "360px";
+ await loadUriInFrame(staticFrame, "iframe_static.html");
+ let innerStaticFrame = staticFrame.contentDocument.getElementById("staticFrame");
+ await loadUriInFrame(innerStaticFrame, "frame1.html");
+ let innerDynamicFrame = await createDynamicFrame(staticFrame.contentDocument, "frame2.html");
+ await loadUriInFrame(innerDynamicFrame, "frame3.html");
+ // staticFrame: frame0 -> frame1 -> frame2 -> iframe_static
+ // innerStaticFrame: frame0 -> frame1
+ // innerDynamicFrame: frame2 -> frame3
+ is(shistory.index, 5, "shistory.index");
+ is(history.length, 6, "history.length");
+
+ // Wait for 2 load events - navigation and goback.
+ let onloadPromise = awaitOnload(staticFrame, 2);
+ await loadUriInFrame(staticFrame, "goback.html");
+ await onloadPromise;
+ // staticFrame: frame0 -> frame1 -> frame2 -> iframe_static -> goback
+ // innerStaticFrame: frame0 -> frame1
+ is(shistory.index, 4, "shistory.index");
+ is(history.length, 6, "history.length");
+ innerStaticFrame = staticFrame.contentDocument.getElementById("staticFrame");
+ is(innerStaticFrame.contentDocument.location.href, BASE_URL + "frame1.html", "innerStaticFrame location");
+ ok(!staticFrame.contentDocument.getElementById("dynamicFrame"), "innerDynamicFrame should not exist");
+
+ // Test 5: Insert and navigate inner dynamic frame again with bfcache
+ // enabled, and navigate top level window to a special page which will
+ // evict bfcache then goback. Verify that dynamic entries are correctly
+ // removed in this case.
+ window.onunload = null; // enable bfcache
+ staticFrame.width = "320px";
+ staticFrame.height = "360px";
+ innerDynamicFrame = await createDynamicFrame(staticFrame.contentDocument, "frame2.html");
+ await loadUriInFrame(innerDynamicFrame, "frame3.html");
+ // staticFrame: frame0 -> frame1 -> frame2 -> iframe_static
+ // innerStaticFrame: frame0 -> frame1
+ // innerDynamicFrame: frame2 -> frame3
+ is(shistory.index, 5, "shistory.index");
+ is(history.length, 6, "history.length");
+ window.location = "file_bug1326251_evict_cache.html";
+ },
+ async function() {
+ let windowWrap = SpecialPowers.wrap(window);
+ let docShell = windowWrap.docShell;
+ let shistory = docShell.QueryInterface(SpecialPowers.Ci.nsIWebNavigation)
+ .sessionHistory;
+ // staticFrame: frame0 -> frame1 -> frame2 -> iframe_static
+ // innerStaticFrame: frame0 -> frame1
+ if (!SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ // *EntryIndex attributes aren't meaningful when the session history
+ // lives in the parent process.
+ is(docShell.previousEntryIndex, 5, "docShell.previousEntryIndex");
+ is(docShell.loadedEntryIndex, 4, "docShell.loadedEntryIndex");
+ }
+ is(shistory.index, 4, "shistory.index");
+ is(history.length, 6, "history.length");
+ let staticFrame = document.getElementById("staticFrame");
+ let innerStaticFrame = staticFrame.contentDocument.getElementById("staticFrame");
+ is(innerStaticFrame.contentDocument.location.href, BASE_URL + "frame1.html", "innerStaticFrame location");
+ ok(!staticFrame.contentDocument.getElementById("dynamicFrame"), "innerDynamicFrame should not exist");
+
+ // Test 6: Insert and navigate inner dynamic frame and then reload outer
+ // frame. Verify that inner dynamic frame entries are all removed.
+ staticFrame.width = "320px";
+ staticFrame.height = "360px";
+ let innerDynamicFrame = await createDynamicFrame(staticFrame.contentDocument, "frame2.html");
+ await loadUriInFrame(innerDynamicFrame, "frame3.html");
+ // staticFrame: frame0 -> frame1 -> frame2 -> iframe_static
+ // innerStaticFrame: frame0 -> frame1
+ // innerDynamicFrame: frame2 -> frame3
+ is(shistory.index, 5, "shistory.index");
+ is(history.length, 6, "history.length");
+ let staticFrameLoadPromise = new Promise(resolve => {
+ staticFrame.onload = resolve;
+ });
+ staticFrame.contentWindow.location.reload();
+ await staticFrameLoadPromise;
+ // staticFrame: frame0 -> frame1 -> frame2 -> iframe_static
+ // innerStaticFrame: frame0 -> frame1
+ is(shistory.index, 4, "shistory.index");
+ is(history.length, 5, "history.length");
+ innerStaticFrame = staticFrame.contentDocument.getElementById("staticFrame");
+ is(innerStaticFrame.contentDocument.location.href, BASE_URL + "frame1.html", "innerStaticFrame location");
+ ok(!staticFrame.contentDocument.getElementById("dynamicFrame"), "innerDynamicFrame should not exist");
+ bc.postMessage("finishTest");
+ bc.close();
+ window.close();
+ },
+ ];
+
+ function awaitOnload(frame, occurances = 1) {
+ return new Promise(function(resolve, reject) {
+ let count = 0;
+ frame.addEventListener("load", function listener(e) {
+ if (++count == occurances) {
+ frame.removeEventListener("load", listener);
+ setTimeout(resolve, 0);
+ }
+ });
+ });
+ }
+
+ async function createDynamicFrame(targetDocument, frameSrc = "frame0.html") {
+ let dynamicFrame = targetDocument.createElement("iframe");
+ let onloadPromise = awaitOnload(dynamicFrame);
+ dynamicFrame.id = "dynamicFrame";
+ dynamicFrame.src = frameSrc;
+ let container = targetDocument.getElementById("frameContainer");
+ container.appendChild(dynamicFrame);
+ await onloadPromise;
+ return dynamicFrame;
+ }
+
+ async function loadUriInFrame(frame, uri) {
+ let onloadPromise = awaitOnload(frame);
+ frame.src = uri;
+ return onloadPromise;
+ }
+
+ function test() {
+ bc.postMessage("requestNextTest");
+ }
+ </script>
+ </head>
+ <body onpageshow="test();">
+ <div id="frameContainer">
+ <iframe id="staticFrame" src="frame0.html"></iframe>
+ </div>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_bug1326251_evict_cache.html b/docshell/test/navigation/file_bug1326251_evict_cache.html
new file mode 100644
index 0000000000..b9873947f4
--- /dev/null
+++ b/docshell/test/navigation/file_bug1326251_evict_cache.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Bug 1326251</title>
+ <script>
+ // Evict bfcache and then go back.
+ async function evictCache() {
+ await SpecialPowers.evictAllContentViewers();
+
+ history.back();
+ }
+ </script>
+ </head>
+ <body onload="setTimeout(evictCache, 0);">
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_bug1364364-1.html b/docshell/test/navigation/file_bug1364364-1.html
new file mode 100644
index 0000000000..d4ecc42ad4
--- /dev/null
+++ b/docshell/test/navigation/file_bug1364364-1.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>title</title>
+ </head>
+ <body onload="loadFramesAndNavigate();">
+ <p id="content"></p>
+ <div id="frameContainer">
+ </div>
+ <script type="application/javascript">
+ function waitForLoad(frame) {
+ return new Promise(r => frame.onload = () => setTimeout(r, 0));
+ }
+
+ async function loadFramesAndNavigate() {
+ let dynamicFrame = document.createElement("iframe");
+ dynamicFrame.src = "data:text/html,iframe1";
+ document.querySelector("#frameContainer").appendChild(dynamicFrame);
+ await waitForLoad(dynamicFrame);
+ dynamicFrame.src = "data:text/html,iframe2";
+ await waitForLoad(dynamicFrame);
+ dynamicFrame.src = "data:text/html,iframe3";
+ await waitForLoad(dynamicFrame);
+ dynamicFrame.src = "data:text/html,iframe4";
+ await waitForLoad(dynamicFrame);
+ dynamicFrame.src = "data:text/html,iframe5";
+ await waitForLoad(dynamicFrame);
+ location.href = "file_bug1364364-2.html";
+ }
+ </script>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_bug1364364-2.html b/docshell/test/navigation/file_bug1364364-2.html
new file mode 100644
index 0000000000..6e52ecaaa9
--- /dev/null
+++ b/docshell/test/navigation/file_bug1364364-2.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>title</title>
+ </head>
+ <body onload="notifyOpener();">
+ <script type="application/javascript">
+ function notifyOpener() {
+ opener.postMessage("navigation-done", "*");
+ }
+ </script>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_bug1375833-frame1.html b/docshell/test/navigation/file_bug1375833-frame1.html
new file mode 100644
index 0000000000..ea38326479
--- /dev/null
+++ b/docshell/test/navigation/file_bug1375833-frame1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>iframe test page 1</title>
+ </head>
+ <body>iframe test page 1</body>
+</html>
diff --git a/docshell/test/navigation/file_bug1375833-frame2.html b/docshell/test/navigation/file_bug1375833-frame2.html
new file mode 100644
index 0000000000..6e76ab7e47
--- /dev/null
+++ b/docshell/test/navigation/file_bug1375833-frame2.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>iframe test page 2</title>
+ </head>
+ <body>iframe test page 2</body>
+</html>
diff --git a/docshell/test/navigation/file_bug1375833.html b/docshell/test/navigation/file_bug1375833.html
new file mode 100644
index 0000000000..373a7fe08e
--- /dev/null
+++ b/docshell/test/navigation/file_bug1375833.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Test for bug 1375833</title>
+ </head>
+ <body onload="test();">
+ <iframe id="testFrame" src="file_bug1375833-frame1.html"></iframe>
+ <script type="application/javascript">
+ function test() {
+ let iframe = document.querySelector("#testFrame");
+ setTimeout(function() { iframe.src = "file_bug1375833-frame1.html"; }, 0);
+ iframe.onload = function(e) {
+ setTimeout(function() { iframe.src = "file_bug1375833-frame2.html"; }, 0);
+ iframe.onload = function() {
+ opener.postMessage(iframe.contentWindow.location.href, "*");
+ };
+ };
+ }
+ </script>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_bug1379762-1.html b/docshell/test/navigation/file_bug1379762-1.html
new file mode 100644
index 0000000000..c8cd666667
--- /dev/null
+++ b/docshell/test/navigation/file_bug1379762-1.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Bug 1379762</title>
+ </head>
+ <img srcset> <!-- This tries to add load blockers during bfcache restoration -->
+ <script>
+ onunload = null; // enable bfcache
+ var bc = new BroadcastChannel('bug1379762');
+ bc.postMessage("init");
+ onpageshow = function() {
+ bc.onmessage = (messageEvent) => {
+ let message = messageEvent.data;
+ if (message == "forward_back") {
+ // Navigate forward and then back.
+ // eslint-disable-next-line no-global-assign
+ setTimeout(function() { location = "goback.html"; }, 0);
+ } else if (message == "finish_test") {
+ // Do this async so our load event gets a chance to fire if it plans to
+ // do it.
+ setTimeout(function() {
+ bc.postMessage("finished");
+ bc.close();
+ window.close();
+ });
+ }
+ }
+ bc.postMessage("increment_testCount");
+ };
+ onload = function() {
+ bc.postMessage("increment_loadCount");
+ };
+ </script>
+</html>
diff --git a/docshell/test/navigation/file_bug1536471.html b/docshell/test/navigation/file_bug1536471.html
new file mode 100644
index 0000000000..53012257ee
--- /dev/null
+++ b/docshell/test/navigation/file_bug1536471.html
@@ -0,0 +1,8 @@
+<html>
+ <body onload="opener.bodyOnLoad()">
+ Nested Frame
+ <div id="frameContainer">
+ <iframe id="staticFrame" src="frame0.html"></iframe>
+ </div>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_bug1583110.html b/docshell/test/navigation/file_bug1583110.html
new file mode 100644
index 0000000000..5b08f54d21
--- /dev/null
+++ b/docshell/test/navigation/file_bug1583110.html
@@ -0,0 +1,26 @@
+<html>
+ <head>
+ <script>
+ var bc;
+ window.onpageshow = function(pageshow) {
+ bc = new BroadcastChannel("bug1583110");
+ bc.onmessage = function(event) {
+ bc.close();
+ if (event.data == "loadnext") {
+ location.search = "?nextpage";
+ } else if (event.data == "back") {
+ history.back();
+ }
+ }
+ bc.postMessage({type: "pageshow", persisted: pageshow.persisted });
+ if (pageshow.persisted) {
+ document.body.appendChild(document.createElement("iframe"));
+ bc.close();
+ window.close();
+ }
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_bug1609475.html b/docshell/test/navigation/file_bug1609475.html
new file mode 100644
index 0000000000..7699d46b08
--- /dev/null
+++ b/docshell/test/navigation/file_bug1609475.html
@@ -0,0 +1,51 @@
+<html>
+ <head>
+ <script>
+
+ var loadCount = 0;
+ function loadListener(event) {
+ ++loadCount;
+ if (loadCount == 2) {
+ // Use a timer to ensure we don't get extra load events.
+ setTimeout(function() {
+ var doc1URI = document.getElementById("i1").contentDocument.documentURI;
+ opener.ok(doc1URI.includes("frame1.html"),
+ "Should have loaded the initial page to the first iframe. Got " + doc1URI);
+ var doc2URI = document.getElementById("i2").contentDocument.documentURI;
+ opener.ok(doc2URI.includes("frame1.html"),
+ "Should have loaded the initial page to the second iframe. Got " + doc2URI);
+ opener.finishTest();
+ }, 1000);
+ } else if (loadCount > 2) {
+ opener.ok(false, "Too many load events");
+ }
+ // if we don't get enough load events, the test will time out.
+ }
+
+ function setupIframe(id) {
+ var ifr = document.getElementById(id);
+ return new Promise(function(resolve) {
+ ifr.onload = function() {
+ // Replace load listener to catch page loads from the session history.
+ ifr.onload = loadListener;
+ // Need to use setTimeout, because triggering loads inside
+ // load event listener has special behavior since at the moment
+ // the docshell keeps track of whether it is executing a load handler or not.
+ setTimeout(resolve);
+ }
+ ifr.contentWindow.location.href = "frame2.html";
+ });
+ }
+
+ async function test() {
+ await setupIframe("i1");
+ await setupIframe("i2");
+ history.go(-2);
+ }
+ </script>
+ </head>
+ <body onload="setTimeout(test)">
+ <iframe id="i1" src="frame1.html"></iframe>
+ <iframe id="i2" src="frame1.html"></iframe>
+ </body>
+</html> \ No newline at end of file
diff --git a/docshell/test/navigation/file_bug1706090.html b/docshell/test/navigation/file_bug1706090.html
new file mode 100644
index 0000000000..9c5bc025d3
--- /dev/null
+++ b/docshell/test/navigation/file_bug1706090.html
@@ -0,0 +1,40 @@
+<html>
+ <head>
+ <script>
+ onpageshow = function(pageShowEvent) {
+ if (location.hostname == "example.com" ||
+ location.hostname == "test1.mochi.test") {
+ // BroadcastChannel doesn't work across domains, so just go to the
+ // previous page explicitly.
+ history.back();
+ return;
+ }
+
+ var bc = new BroadcastChannel("bug1706090");
+ bc.onmessage = function(event) {
+ if (event.data == "close") {
+ bc.postMessage("closed");
+ bc.close();
+ window.close();
+ } else if (event.data == "sameOrigin") {
+ bc.close();
+ location.href = location.href + "?foo"
+ } else if (event.data == "back") {
+ history.back();
+ } else if (event.data == "crossOrigin") {
+ bc.close();
+ location.href = "https://example.com:443" + location.pathname;
+ } else if (event.data == "sameSite") {
+ bc.close();
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ location.href = "http://test1.mochi.test:8888" + location.pathname;
+ }
+ }
+
+ bc.postMessage({type: pageShowEvent.type, persisted: pageShowEvent.persisted});
+ }
+ </script>
+ </head>
+ <body onunload="/* dummy listener*/">
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_bug1745638.html b/docshell/test/navigation/file_bug1745638.html
new file mode 100644
index 0000000000..b98c8de91f
--- /dev/null
+++ b/docshell/test/navigation/file_bug1745638.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<script>
+ function go() {
+ var doc = document.getElementById("testFrame").contentWindow.document;
+ doc.open();
+ doc.close();
+ doc.body.innerText = "passed";
+ }
+</script>
+<body onload="setTimeout(opener.pageLoaded);">
+The iframe below should not be blank after a reload.<br>
+<iframe src="about:blank" id="testFrame"></iframe>
+<script>
+ go();
+</script>
diff --git a/docshell/test/navigation/file_bug1750973.html b/docshell/test/navigation/file_bug1750973.html
new file mode 100644
index 0000000000..28b2f995ae
--- /dev/null
+++ b/docshell/test/navigation/file_bug1750973.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <script>
+ function test() {
+ history.scrollRestoration = "manual";
+ document.getElementById("initialfocus").focus();
+ history.pushState('data', '', '');
+ history.back();
+ }
+
+ window.onpopstate = function() {
+ window.onscroll = function() {
+ window.onscroll = null;
+ document.documentElement.style.display = "none";
+ // focus() triggers recreation of the nsIFrames without a reflow.
+ document.getElementById("focustarget").focus();
+ document.documentElement.style.display = "";
+ // Flush the layout.
+ document.documentElement.getBoundingClientRect();
+ opener.isnot(window.scrollY, 0, "The page should have been scrolled down(1)");
+ requestAnimationFrame(
+ function() {
+ // Extra timeout to ensure we're called after rAF has triggered a
+ // reflow.
+ setTimeout(function() {
+ opener.isnot(window.scrollY, 0, "The page should have been scrolled down(2)");
+ window.close();
+ opener.SimpleTest.finish();
+ });
+ });
+ }
+ window.scrollTo(0, 1000);
+ }
+ </script>
+</head>
+<body onload="setTimeout(test)">
+<div style="position: fixed;">
+ <input type="button" value="" id="initialfocus">
+ <input type="button" value="" id="focustarget">
+</div>
+<div style="border: 1px solid black; width: 100px; height: 9000px;"></div>
+</body>
+</html>
diff --git a/docshell/test/navigation/file_bug1758664.html b/docshell/test/navigation/file_bug1758664.html
new file mode 100644
index 0000000000..07798dfddd
--- /dev/null
+++ b/docshell/test/navigation/file_bug1758664.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+var onIframeOnload = function() {
+ var iframe = window.document.getElementById('applicationIframe');
+ opener.is(iframe.contentWindow.location.search, "?iframe", "Should have loaded the iframe");
+ window.close();
+ opener.SimpleTest.finish();
+}
+
+var onPageOnload = function() {
+ if (location.search == "?iframe") {
+ return;
+ }
+ if(!window.name) {
+ window.name = 'file_bug1758664.html';
+ window.location.reload();
+ return;
+ }
+ var iframe = window.document.getElementById('applicationIframe');
+ iframe.addEventListener('load', onIframeOnload);
+ iframe.src = "file_bug1758664.html?iframe";
+}
+window.document.addEventListener("DOMContentLoaded", onPageOnload);
+
+</script>
+</head>
+<body>
+ <iframe id="applicationIframe"></iframe>
+</body>
+</html>
diff --git a/docshell/test/navigation/file_bug386782_contenteditable.html b/docshell/test/navigation/file_bug386782_contenteditable.html
new file mode 100644
index 0000000000..4515d015d9
--- /dev/null
+++ b/docshell/test/navigation/file_bug386782_contenteditable.html
@@ -0,0 +1 @@
+<html><head><meta charset="utf-8"><script>window.addEventListener("pageshow", function(event) { window.opener.postMessage({persisted: event.persisted}, "*"); });</script></head><body contentEditable="true"><p>contentEditable</p></body></html> \ No newline at end of file
diff --git a/docshell/test/navigation/file_bug386782_designmode.html b/docshell/test/navigation/file_bug386782_designmode.html
new file mode 100644
index 0000000000..faa063cbae
--- /dev/null
+++ b/docshell/test/navigation/file_bug386782_designmode.html
@@ -0,0 +1 @@
+<html><head><meta charset="utf-8"><script>window.addEventListener("pageshow", function(event) { window.opener.postMessage({persisted: event.persisted}, "*"); });</script></head><body><p>designModeDocument</p></body></html> \ No newline at end of file
diff --git a/docshell/test/navigation/file_bug462076_1.html b/docshell/test/navigation/file_bug462076_1.html
new file mode 100644
index 0000000000..5050e79fdc
--- /dev/null
+++ b/docshell/test/navigation/file_bug462076_1.html
@@ -0,0 +1,55 @@
+<html>
+ <head>
+ <title>Bug 462076</title>
+ <script>
+ var srcs = [ "frame0.html",
+ "frame1.html",
+ "frame2.html",
+ "frame3.html" ];
+
+ var checkCount = 0;
+
+ function makeFrame(index) {
+ var ifr = document.createElement("iframe");
+ ifr.src = srcs[index];
+ ifr.onload = checkFrame;
+ document.getElementById("container" + index).appendChild(ifr);
+ }
+
+ function runTest() {
+ var randomNumber = Math.floor(Math.random() * 4);
+ for (var i = randomNumber; i < 4; ++i) {
+ makeFrame(i);
+ }
+ for (var k = 0; k < randomNumber; ++k) {
+ makeFrame(k);
+ }
+ }
+
+ function checkFrame(evt) {
+ var ifr = evt.target;
+ opener.ok(String(ifr.contentWindow.location).includes(ifr.src),
+ "Wrong document loaded (" + ifr.src + ", " +
+ ifr.contentWindow.location + ")!");
+
+ if (++checkCount == 4) {
+ if (++opener.testCount == 10) {
+ opener.nextTest();
+ window.close();
+ } else {
+ window.location.reload();
+ }
+ }
+ }
+ </script>
+ </head>
+ <body>
+ <div id="container0"></div>
+ <div id="container1"></div>
+ <div id="container2"></div>
+ <div id="container3"></div>
+ <script>
+ runTest();
+ </script>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_bug462076_2.html b/docshell/test/navigation/file_bug462076_2.html
new file mode 100644
index 0000000000..63cf3de3f9
--- /dev/null
+++ b/docshell/test/navigation/file_bug462076_2.html
@@ -0,0 +1,52 @@
+<html>
+ <head>
+ <title>Bug 462076</title>
+ <script>
+ var srcs = [ "frame0.html",
+ "frame1.html",
+ "frame2.html",
+ "frame3.html" ];
+
+ var checkCount = 0;
+
+ function makeFrame(index) {
+ var ifr = document.createElement("iframe");
+ ifr.src = srcs[index];
+ ifr.onload = checkFrame;
+ document.getElementById("container" + index).appendChild(ifr);
+ }
+
+ function runTest() {
+ var randomNumber = Math.floor(Math.random() * 4);
+ for (var i = randomNumber; i < 4; ++i) {
+ makeFrame(i);
+ }
+ for (var k = 0; k < randomNumber; ++k) {
+ makeFrame(k);
+ }
+ }
+
+ function checkFrame(evt) {
+ var ifr = evt.target;
+ opener.ok(String(ifr.contentWindow.location).includes(ifr.src),
+ "Wrong document loaded (" + ifr.src + ", " +
+ ifr.contentWindow.location + ")!");
+
+ if (++checkCount == 4) {
+ if (++opener.testCount == 10) {
+ opener.nextTest();
+ window.close();
+ } else {
+ window.location.reload();
+ }
+ }
+ }
+ </script>
+ </head>
+ <body onload="runTest();">
+ <div id="container0"></div>
+ <div id="container1"></div>
+ <div id="container2"></div>
+ <div id="container3"></div>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_bug462076_3.html b/docshell/test/navigation/file_bug462076_3.html
new file mode 100644
index 0000000000..5c779d2f49
--- /dev/null
+++ b/docshell/test/navigation/file_bug462076_3.html
@@ -0,0 +1,52 @@
+<html>
+ <head>
+ <title>Bug 462076</title>
+ <script>
+ var srcs = [ "frame0.html",
+ "frame1.html",
+ "frame2.html",
+ "frame3.html" ];
+
+ var checkCount = 0;
+
+ function makeFrame(index) {
+ var ifr = document.createElement("iframe");
+ ifr.src = srcs[index];
+ ifr.onload = checkFrame;
+ document.getElementById("container" + index).appendChild(ifr);
+ }
+
+ function runTest() {
+ var randomNumber = Math.floor(Math.random() * 4);
+ for (var i = randomNumber; i < 4; ++i) {
+ makeFrame(i);
+ }
+ for (var k = 0; k < randomNumber; ++k) {
+ makeFrame(k);
+ }
+ }
+
+ function checkFrame(evt) {
+ var ifr = evt.target;
+ opener.ok(String(ifr.contentWindow.location).includes(ifr.src),
+ "Wrong document loaded (" + ifr.src + ", " +
+ ifr.contentWindow.location + ")!");
+
+ if (++checkCount == 4) {
+ if (++opener.testCount == 10) {
+ opener.nextTest();
+ window.close();
+ } else {
+ window.location.reload();
+ }
+ }
+ }
+ </script>
+ </head>
+ <body onload="setTimeout(runTest, 0);">
+ <div id="container0"></div>
+ <div id="container1"></div>
+ <div id="container2"></div>
+ <div id="container3"></div>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_bug508537_1.html b/docshell/test/navigation/file_bug508537_1.html
new file mode 100644
index 0000000000..182c085670
--- /dev/null
+++ b/docshell/test/navigation/file_bug508537_1.html
@@ -0,0 +1,33 @@
+<html>
+ <head>
+ <script>
+ function dynFrameLoad() {
+ var ifrs = document.getElementsByTagName("iframe");
+ opener.ok(String(ifrs[0].contentWindow.location).includes(ifrs[0].src),
+ "Wrong document loaded (1)\n");
+ opener.ok(String(ifrs[1].contentWindow.location).includes(ifrs[1].src),
+ "Wrong document loaded (2)\n");
+ if (opener && ++opener.testCount == 1) {
+ window.location = "goback.html";
+ } else {
+ opener.finishTest();
+ }
+ }
+
+ window.addEventListener("load",
+ function() {
+ var container = document.getElementById("t1");
+ container.addEventListener("load", dynFrameLoad, true);
+ container.appendChild(container.appendChild(document.getElementById("i1")));
+ });
+ </script>
+ </head>
+ <body>
+ <h5>Container:</h5>
+ <div id="t1"></div>
+ <h5>Original frames:</h5>
+ <iframe id="i1" src="frame0.html"></iframe>
+ <iframe src="frame1.html"></iframe>
+ </body>
+</html>
+
diff --git a/docshell/test/navigation/file_bug534178.html b/docshell/test/navigation/file_bug534178.html
new file mode 100644
index 0000000000..4d77dd824b
--- /dev/null
+++ b/docshell/test/navigation/file_bug534178.html
@@ -0,0 +1,30 @@
+<html>
+ <head>
+ <script>
+
+ function testDone() {
+ document.body.firstChild.remove();
+ var isOK = false;
+ try {
+ isOK = history.previous != location;
+ } catch (ex) {
+ // history.previous should throw if this is the first page in shistory.
+ isOK = true;
+ }
+ document.body.textContent = isOK ? "PASSED" : "FAILED";
+ opener.ok(isOK, "Duplicate session history entries should have been removed!");
+ opener.finishTest();
+ }
+ function ifrload() {
+ setTimeout(testDone, 0);
+ }
+ function test() {
+ var ifr = document.getElementsByTagName("iframe")[0];
+ ifr.onload = ifrload;
+ ifr.src = "data:text/html,doc2";
+ }
+ </script>
+ </head>
+ <body onload="setTimeout(test, 0)"><iframe src="data:text/html,doc1"></iframe>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_contentpolicy_block_window.html b/docshell/test/navigation/file_contentpolicy_block_window.html
new file mode 100644
index 0000000000..c51e574e5e
--- /dev/null
+++ b/docshell/test/navigation/file_contentpolicy_block_window.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+This window should never be openend!
+</body>
+</html>
diff --git a/docshell/test/navigation/file_docshell_gotoindex.html b/docshell/test/navigation/file_docshell_gotoindex.html
new file mode 100644
index 0000000000..f3e8919822
--- /dev/null
+++ b/docshell/test/navigation/file_docshell_gotoindex.html
@@ -0,0 +1,42 @@
+<html>
+ <head>
+ <script>
+ function loaded() {
+ if (location.search == "") {
+ if (opener.loadedInitialPage) {
+ opener.ok(true, "got back to the initial page.");
+ opener.setTimeout("SimpleTest.finish();");
+ window.close();
+ return;
+ }
+ opener.loadedInitialPage = true;
+ opener.info("Loaded initial page.");
+ // Load another page (which is this same document, but different URL.)
+ location.href = location.href + "?anotherPage";
+ } else {
+ opener.info("Loaded the second page.");
+ location.hash = "1";
+ window.onhashchange = function() {
+ opener.info("hash: " + location.hash);
+ location.hash = "2";
+ window.onhashchange = function() {
+ opener.info("hash: " + location.hash);
+ var docShell = SpecialPowers.wrap(window).docShell;
+ var webNavigation =
+ SpecialPowers.do_QueryInterface(docShell, "nsIWebNavigation");
+ webNavigation.gotoIndex(history.length - 2);
+ window.onhashchange = function() {
+ opener.info("hash: " + location.hash);
+ webNavigation.gotoIndex(history.length - 4);
+ }
+ }
+ }
+ }
+ }
+ </script>
+ </head>
+ <body onpageshow="setTimeout(loaded)">
+ <a href="#1" name="1">1</a>
+ <a href="#2" name="2">2</a>
+ </body>
+</html> \ No newline at end of file
diff --git a/docshell/test/navigation/file_document_write_1.html b/docshell/test/navigation/file_document_write_1.html
new file mode 100644
index 0000000000..be52b60231
--- /dev/null
+++ b/docshell/test/navigation/file_document_write_1.html
@@ -0,0 +1,18 @@
+<html>
+ <head>
+ <script>
+ function start() {
+ var length = history.length;
+ document.open();
+ document.write("<h5 id='dynamic'>document.written content</h5>");
+ document.close();
+ opener.is(history.length, length,
+ "document.open/close should not change history");
+ opener.finishTest();
+ }
+ </script>
+ </head>
+ <body onload="start();">
+ <h5>static content</h5>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_evict_from_bfcache.html b/docshell/test/navigation/file_evict_from_bfcache.html
new file mode 100644
index 0000000000..9f50533543
--- /dev/null
+++ b/docshell/test/navigation/file_evict_from_bfcache.html
@@ -0,0 +1,29 @@
+<html>
+ <head>
+ <script>
+ onpageshow = function(pageShowEvent) {
+ var bc = new BroadcastChannel("evict_from_bfcache");
+ bc.onmessage = function(event) {
+ if (event.data == "nextpage") {
+ bc.close();
+ location.href += "?nextpage";
+ } else if (event.data == "back") {
+ bc.close();
+ history.back();
+ } else if (event.data == "forward") {
+ // Note, we don't close BroadcastChannel
+ history.forward();
+ } else if (event.data == "close") {
+ bc.postMessage("closed");
+ bc.close();
+ window.close();
+ }
+ }
+
+ bc.postMessage({ type: "pageshow", persisted: pageShowEvent.persisted});
+ };
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_fragment_handling_during_load.html b/docshell/test/navigation/file_fragment_handling_during_load.html
new file mode 100644
index 0000000000..a7f468c32d
--- /dev/null
+++ b/docshell/test/navigation/file_fragment_handling_during_load.html
@@ -0,0 +1,27 @@
+<html>
+ <head>
+ <script>
+ function checkHaveLoadedNewDoc() {
+ let l = document.body.firstChild.contentWindow.location.href;
+ if (!l.endsWith("file_fragment_handling_during_load_frame2.sjs")) {
+ opener.ok(true, "Fine. We will check later.");
+ setTimeout(checkHaveLoadedNewDoc, 500);
+ return;
+ }
+ opener.ok(true, "Have loaded a new document.");
+ opener.finishTest();
+ }
+ function test() {
+ // Test that executing back() before load has started doesn't interrupt
+ // the load.
+ var ifr = document.getElementsByTagName("iframe")[0];
+ ifr.onload = checkHaveLoadedNewDoc;
+ ifr.contentWindow.location.hash = "b";
+ ifr.contentWindow.location.href = "file_fragment_handling_during_load_frame2.sjs";
+ history.back();
+ }
+ </script>
+ </head>
+ <body onload="setTimeout(test, 0)"><iframe src="file_fragment_handling_during_load_frame1.html#a"></iframe>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_fragment_handling_during_load_frame1.html b/docshell/test/navigation/file_fragment_handling_during_load_frame1.html
new file mode 100644
index 0000000000..c03ba2bda6
--- /dev/null
+++ b/docshell/test/navigation/file_fragment_handling_during_load_frame1.html
@@ -0,0 +1,6 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+foo
+</body>
+</html>
diff --git a/docshell/test/navigation/file_fragment_handling_during_load_frame2.sjs b/docshell/test/navigation/file_fragment_handling_during_load_frame2.sjs
new file mode 100644
index 0000000000..77abe5949e
--- /dev/null
+++ b/docshell/test/navigation/file_fragment_handling_during_load_frame2.sjs
@@ -0,0 +1,20 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80 ft=javascript: */
+"use strict";
+
+function handleRequest(request, response) {
+ response.setHeader("Content-Type", "text/html", false);
+ response.setHeader("Cache-Control", "no-cache", false);
+ // Wait a bit.
+ var s = Date.now();
+ // eslint-disable-next-line no-empty
+ while (Date.now() - s < 1000) {}
+
+ response.write(`<!DOCTYPE HTML>
+ <html>
+ <body>
+ bar
+ </body>
+ </html>
+ `);
+}
diff --git a/docshell/test/navigation/file_load_history_entry_page_with_one_link.html b/docshell/test/navigation/file_load_history_entry_page_with_one_link.html
new file mode 100644
index 0000000000..a4d1b62176
--- /dev/null
+++ b/docshell/test/navigation/file_load_history_entry_page_with_one_link.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<body onpageshow="opener.bodyOnLoad()">
+<a id="link1" href="#1">Link 1</a>
+<a name="1">link 1</a>
+</body>
+</html>
diff --git a/docshell/test/navigation/file_load_history_entry_page_with_two_links.html b/docshell/test/navigation/file_load_history_entry_page_with_two_links.html
new file mode 100644
index 0000000000..4be2ea6f4e
--- /dev/null
+++ b/docshell/test/navigation/file_load_history_entry_page_with_two_links.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<body onpageshow="opener.bodyOnLoad()">
+<a id="link1" href="#1">Link 1</a>
+<a id="link2" href="#2">Link 2</a>
+<a name="1">link 1</a>
+<a name="2">link 2</a>
+</body>
+</html>
diff --git a/docshell/test/navigation/file_meta_refresh.html b/docshell/test/navigation/file_meta_refresh.html
new file mode 100644
index 0000000000..2a06cc5acf
--- /dev/null
+++ b/docshell/test/navigation/file_meta_refresh.html
@@ -0,0 +1,40 @@
+<html>
+ <head>
+ </head>
+ <body>
+ <script>
+ function addMetaRefresh() {
+ // eslint-disable-next-line no-unsanitized/property
+ document.head.innerHTML = "<meta http-equiv='refresh' content='5; url=" +
+ location.href.replace("?initial", "?refresh") + "'>";
+ }
+
+ onpageshow = function() {
+ let bc = new BroadcastChannel("test_meta_refresh");
+ bc.onmessage = function(event) {
+ if (event.data == "loadnext") {
+ bc.close();
+ addMetaRefresh();
+ location.href = location.href.replace("?initial", "?nextpage");
+ } else if (event.data == "back") {
+ bc.close();
+ history.back();
+ } else if (event.data == "ensuremetarefresh") {
+ bc.close();
+ // This test is designed to work with and without bfcache, but
+ // if bfcache is enabled, meta refresh should be suspended/resumed.
+ if (document.head.firstChild.localName != "meta") {
+ addMetaRefresh();
+ }
+ } else if (event.data == "close") {
+ bc.postMessage("closed");
+ bc.close();
+ window.close();
+ }
+ };
+
+ bc.postMessage({ load: location.search.substr(1) });
+ }
+ </script>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_navigation_type.html b/docshell/test/navigation/file_navigation_type.html
new file mode 100644
index 0000000000..bb538eefec
--- /dev/null
+++ b/docshell/test/navigation/file_navigation_type.html
@@ -0,0 +1,25 @@
+<html>
+ <head>
+ <script>
+ onpageshow = function() {
+ let bc = new BroadcastChannel("navigation_type");
+ bc.onmessage = function(event) {
+ if (event.data == "loadNewPage") {
+ bc.close();
+ location.href = location.href + "?next";
+ } else if (event.data == "back") {
+ bc.close();
+ history.back();
+ } else if (event.data == "close") {
+ window.close();
+ bc.postMessage("closed");
+ bc.close();
+ }
+ }
+ bc.postMessage({ navigationType: performance.navigation.type });
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html> \ No newline at end of file
diff --git a/docshell/test/navigation/file_nested_frames.html b/docshell/test/navigation/file_nested_frames.html
new file mode 100644
index 0000000000..6ec286aa3e
--- /dev/null
+++ b/docshell/test/navigation/file_nested_frames.html
@@ -0,0 +1,27 @@
+<html>
+ <head>
+ <script>
+ function nestedIframeLoaded() {
+ var tf = document.getElementById("testframe");
+ var innerf = tf.contentDocument.getElementsByTagName("iframe")[0];
+ if (!innerf.contentDocument.documentURI.includes("frame0")) {
+ innerf.contentWindow.location.href = "http://mochi.test:8888/tests/docshell/test/navigation/frame0.html";
+ return;
+ }
+ innerf.onload = null;
+ innerf.src = "about:blank";
+ var d = innerf.contentDocument;
+ d.open();
+ d.write("test");
+ d.close();
+ opener.is(window.history.length, 1, "Unexpected history length");
+ opener.finishTest();
+ }
+ </script>
+ </head>
+ <body>
+ <iframe id="testframe" src="file_nested_frames_innerframe.html" onload="frameLoaded()"></iframe>
+ <script>
+ </script>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_nested_frames_innerframe.html b/docshell/test/navigation/file_nested_frames_innerframe.html
new file mode 100644
index 0000000000..e25b6a4f6a
--- /dev/null
+++ b/docshell/test/navigation/file_nested_frames_innerframe.html
@@ -0,0 +1 @@
+<iframe onload='parent.nestedIframeLoaded();'></iframe>
diff --git a/docshell/test/navigation/file_nested_srcdoc.html b/docshell/test/navigation/file_nested_srcdoc.html
new file mode 100644
index 0000000000..ae6d656f27
--- /dev/null
+++ b/docshell/test/navigation/file_nested_srcdoc.html
@@ -0,0 +1,3 @@
+
+iframe loaded inside of a srcdoc
+<iframe id="static" srcdoc="Second nested srcdoc<iframe id='static' srcdoc='Third nested srcdoc'&gt;</iframe&gt;"></iframe>
diff --git a/docshell/test/navigation/file_new_shentry_during_history_navigation_1.html b/docshell/test/navigation/file_new_shentry_during_history_navigation_1.html
new file mode 100644
index 0000000000..2f9a41e1d1
--- /dev/null
+++ b/docshell/test/navigation/file_new_shentry_during_history_navigation_1.html
@@ -0,0 +1,5 @@
+<script>
+ onload = function() {
+ opener.postMessage("load");
+ }
+</script>
diff --git a/docshell/test/navigation/file_new_shentry_during_history_navigation_1.html^headers^ b/docshell/test/navigation/file_new_shentry_during_history_navigation_1.html^headers^
new file mode 100644
index 0000000000..59ba296103
--- /dev/null
+++ b/docshell/test/navigation/file_new_shentry_during_history_navigation_1.html^headers^
@@ -0,0 +1 @@
+Cache-control: no-store
diff --git a/docshell/test/navigation/file_new_shentry_during_history_navigation_2.html b/docshell/test/navigation/file_new_shentry_during_history_navigation_2.html
new file mode 100644
index 0000000000..de456f8f1c
--- /dev/null
+++ b/docshell/test/navigation/file_new_shentry_during_history_navigation_2.html
@@ -0,0 +1,10 @@
+<script>
+ onload = function() {
+ opener.postMessage("load");
+ }
+
+ onbeforeunload = function() {
+ opener.postMessage("beforeunload");
+ history.pushState("data1", "", "?push1");
+ }
+</script>
diff --git a/docshell/test/navigation/file_new_shentry_during_history_navigation_2.html^headers^ b/docshell/test/navigation/file_new_shentry_during_history_navigation_2.html^headers^
new file mode 100644
index 0000000000..59ba296103
--- /dev/null
+++ b/docshell/test/navigation/file_new_shentry_during_history_navigation_2.html^headers^
@@ -0,0 +1 @@
+Cache-control: no-store
diff --git a/docshell/test/navigation/file_new_shentry_during_history_navigation_3.html b/docshell/test/navigation/file_new_shentry_during_history_navigation_3.html
new file mode 100644
index 0000000000..2237e3e367
--- /dev/null
+++ b/docshell/test/navigation/file_new_shentry_during_history_navigation_3.html
@@ -0,0 +1,22 @@
+<script>
+ window.onpageshow = function(e) {
+ if (location.search == "?v2") {
+ onbeforeunload = function() {
+ history.pushState("data1", "", "?push1");
+ }
+ }
+
+ let bc = new BroadcastChannel("new_shentry_during_history_navigation");
+ bc.onmessage = function(event) {
+ bc.close();
+ if (event.data == "loadnext") {
+ location.href = "file_new_shentry_during_history_navigation_4.html";
+ } else if (event.data == "back") {
+ history.back();
+ } else if (event.data == "close") {
+ window.close();
+ }
+ }
+ bc.postMessage({page: location.href, type: e.type, persisted: e.persisted});
+ }
+</script>
diff --git a/docshell/test/navigation/file_new_shentry_during_history_navigation_3.html^headers^ b/docshell/test/navigation/file_new_shentry_during_history_navigation_3.html^headers^
new file mode 100644
index 0000000000..59ba296103
--- /dev/null
+++ b/docshell/test/navigation/file_new_shentry_during_history_navigation_3.html^headers^
@@ -0,0 +1 @@
+Cache-control: no-store
diff --git a/docshell/test/navigation/file_new_shentry_during_history_navigation_4.html b/docshell/test/navigation/file_new_shentry_during_history_navigation_4.html
new file mode 100644
index 0000000000..d5c3f61794
--- /dev/null
+++ b/docshell/test/navigation/file_new_shentry_during_history_navigation_4.html
@@ -0,0 +1,16 @@
+<script>
+ window.onpageshow = function(e) {
+ let bc = new BroadcastChannel("new_shentry_during_history_navigation");
+ bc.onmessage = function(event) {
+ bc.close();
+ if (event.data == "loadnext") {
+ location.href = "file_new_shentry_during_history_navigation_3.html?v2";
+ } else if (event.data == "forward") {
+ history.forward();
+ } else if (event.data == "close") {
+ window.close();
+ }
+ }
+ bc.postMessage({page: location.href, type: e.type, persisted: e.persisted});
+ }
+</script>
diff --git a/docshell/test/navigation/file_online_offline_bfcache.html b/docshell/test/navigation/file_online_offline_bfcache.html
new file mode 100644
index 0000000000..7f8138e758
--- /dev/null
+++ b/docshell/test/navigation/file_online_offline_bfcache.html
@@ -0,0 +1,41 @@
+<html>
+ <head>
+ <script>
+ onpageshow = function(pageshowEvent) {
+ let bc = new BroadcastChannel("online_offline_bfcache");
+ bc.onmessage = function(event) {
+ if (event.data == "nextpage") {
+ bc.close();
+ location.href = location.href + "?nextpage";
+ } else if (event.data == "back") {
+ bc.close();
+ history.back();
+ } else if (event.data == "forward") {
+ bc.close();
+ history.forward();
+ } else if (event.data == "close") {
+ bc.postMessage("closed");
+ bc.close();
+ window.close();
+ }
+ };
+ bc.postMessage({ event: pageshowEvent.type, persisted: pageshowEvent.persisted });
+ }
+
+ onoffline = function(event) {
+ let bc = new BroadcastChannel("online_offline_bfcache");
+ bc.postMessage(event.type);
+ bc.close();
+ }
+
+ ononline = function(event) {
+ let bc = new BroadcastChannel("online_offline_bfcache");
+ bc.postMessage(event.type);
+ bc.close();
+ }
+
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_reload.html b/docshell/test/navigation/file_reload.html
new file mode 100644
index 0000000000..f0cb1c2d52
--- /dev/null
+++ b/docshell/test/navigation/file_reload.html
@@ -0,0 +1,23 @@
+<html>
+ <head>
+ <script>
+ window.onpageshow = function() {
+ let bc = new BroadcastChannel("test_reload");
+ bc.onmessage = function(event) {
+ if (event.data == "reload") {
+ bc.close();
+ location.reload(true);
+ } else if (event.data == "close") {
+ bc.postMessage("closed");
+ bc.close();
+ window.close();
+ }
+ }
+
+ bc.postMessage("pageshow");
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_reload_large_postdata.sjs b/docshell/test/navigation/file_reload_large_postdata.sjs
new file mode 100644
index 0000000000..385d43d99f
--- /dev/null
+++ b/docshell/test/navigation/file_reload_large_postdata.sjs
@@ -0,0 +1,46 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80 ft=javascript: */
+"use strict";
+
+const BinaryInputStream = Components.Constructor(
+ "@mozilla.org/binaryinputstream;1",
+ "nsIBinaryInputStream",
+ "setInputStream"
+);
+
+Cu.importGlobalProperties(["URLSearchParams"]);
+
+function readStream(inputStream) {
+ let available = 0;
+ let result = [];
+ while ((available = inputStream.available()) > 0) {
+ result.push(inputStream.readBytes(available));
+ }
+ return result.join("");
+}
+
+function handleRequest(request, response) {
+ let rv = (() => {
+ try {
+ if (request.method != "POST") {
+ return "ERROR: not a POST request";
+ }
+
+ let body = new URLSearchParams(
+ readStream(new BinaryInputStream(request.bodyInputStream))
+ );
+ return body.get("payload").length;
+ } catch (e) {
+ return "ERROR: Exception: " + e;
+ }
+ })();
+
+ response.setHeader("Content-Type", "text/html", false);
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.write(`<!DOCTYPE HTML>
+<script>
+let rv = (${JSON.stringify(rv)});
+opener.postMessage(rv, "*");
+</script>
+ `);
+}
diff --git a/docshell/test/navigation/file_reload_nonbfcached_srcdoc.sjs b/docshell/test/navigation/file_reload_nonbfcached_srcdoc.sjs
new file mode 100644
index 0000000000..7070cdbefc
--- /dev/null
+++ b/docshell/test/navigation/file_reload_nonbfcached_srcdoc.sjs
@@ -0,0 +1,27 @@
+const createPage = function(msg) {
+ return `
+<html>
+<script>
+ onpageshow = function() {
+ opener.postMessage(document.body.firstChild.contentDocument.body.textContent);
+ }
+</script>
+<body><iframe srcdoc="${msg}"></iframe><body>
+</html>
+`;
+};
+
+function handleRequest(request, response) {
+ response.setHeader("Cache-Control", "no-store");
+ response.setHeader("Content-Type", "text/html");
+
+ let currentState = getState("reload_nonbfcached_srcdoc");
+ let srcdoc = "pageload:" + currentState;
+ if (currentState != "second") {
+ setState("reload_nonbfcached_srcdoc", "second");
+ } else {
+ setState("reload_nonbfcached_srcdoc", "");
+ }
+
+ response.write(createPage(srcdoc));
+}
diff --git a/docshell/test/navigation/file_same_url.html b/docshell/test/navigation/file_same_url.html
new file mode 100644
index 0000000000..72a1dd2564
--- /dev/null
+++ b/docshell/test/navigation/file_same_url.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+let bc = new BroadcastChannel("test_same_url");
+let listener = e => {
+ switch (e.data) {
+ case "linkClick":
+ var link = document.getElementById("link1");
+ link.click();
+ break;
+ case "closeWin":
+ self.close();
+ break;
+ }
+};
+bc.addEventListener("message", listener);
+</script>
+</head>
+<body onpageshow="bc.postMessage({bodyOnLoad: history.length})">
+<a id="link1" href="file_same_url.html">Link 1</a>
+<a name="1">link 1</a>
+</body>
+</html>
diff --git a/docshell/test/navigation/file_scrollRestoration_bfcache_and_nobfcache.html b/docshell/test/navigation/file_scrollRestoration_bfcache_and_nobfcache.html
new file mode 100644
index 0000000000..fec51f821d
--- /dev/null
+++ b/docshell/test/navigation/file_scrollRestoration_bfcache_and_nobfcache.html
@@ -0,0 +1,30 @@
+<html>
+ <head>
+ <script>
+ // Ensure layout is flushed before doing anything with scrolling.
+ function flushAndExecute(callback) {
+ window.requestAnimationFrame(function() {
+ setTimeout(callback);
+ });
+ }
+
+ var bc = new BroadcastChannel("bfcached");
+ bc.onmessage = (msgEvent) => {
+ if (msgEvent.data == "loadNext") {
+ flushAndExecute(() => {
+ location.href = "file_scrollRestoration_bfcache_and_nobfcache_part2.html";
+ });
+ } else if (msgEvent.data == "forward") {
+ flushAndExecute(() => {
+ history.forward();
+ });
+ }
+ };
+ window.onpageshow = (event) => {
+ bc.postMessage({command: "pageshow", persisted: event.persisted});
+ };
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_scrollRestoration_bfcache_and_nobfcache_part2.html b/docshell/test/navigation/file_scrollRestoration_bfcache_and_nobfcache_part2.html
new file mode 100644
index 0000000000..40e0578515
--- /dev/null
+++ b/docshell/test/navigation/file_scrollRestoration_bfcache_and_nobfcache_part2.html
@@ -0,0 +1,35 @@
+<html>
+ <head>
+ <script>
+ // Note, this page does not enter bfcache because of an HTTP header.
+
+ // Ensure layout is flushed before doing anything with scrolling.
+ function flushAndExecute(callback) {
+ window.requestAnimationFrame(function() {
+ setTimeout(callback);
+ });
+ }
+
+ var bc = new BroadcastChannel("notbfcached");
+ bc.onmessage = (msgEvent) => {
+ if (msgEvent.data == "scroll") {
+ flushAndExecute(() => { window.scrollTo(0, 4000); });
+ } else if (msgEvent.data == "getScrollY") {
+ flushAndExecute(() => { bc.postMessage({ scrollY: window.scrollY}); });
+ } else if (msgEvent.data == "back") {
+ flushAndExecute(() => { bc.close(); history.back(); });
+ } else if (msgEvent.data == "close") {
+ bc.postMessage("closed");
+ bc.close();
+ window.close();
+ }
+ };
+ window.onpageshow = (event) => {
+ bc.postMessage({command: "pageshow", persisted: event.persisted});
+ };
+ </script>
+ </head>
+ <body>
+ <div style="height: 5000px; border: 1px solid black;">content</div>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_scrollRestoration_bfcache_and_nobfcache_part2.html^headers^ b/docshell/test/navigation/file_scrollRestoration_bfcache_and_nobfcache_part2.html^headers^
new file mode 100644
index 0000000000..59ba296103
--- /dev/null
+++ b/docshell/test/navigation/file_scrollRestoration_bfcache_and_nobfcache_part2.html^headers^
@@ -0,0 +1 @@
+Cache-control: no-store
diff --git a/docshell/test/navigation/file_scrollRestoration_navigate.html b/docshell/test/navigation/file_scrollRestoration_navigate.html
new file mode 100644
index 0000000000..ac78f0abbe
--- /dev/null
+++ b/docshell/test/navigation/file_scrollRestoration_navigate.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<script>
+ var bc = new BroadcastChannel("navigate");
+ window.onload = () => {
+ bc.onmessage = (event) => {
+ if (event.data.command == "navigate") {
+ window.location = event.data.location;
+ bc.close();
+ }
+ if (event.data.command == "back") {
+ history.back();
+ bc.close();
+ }
+ }
+ bc.postMessage({command: "loaded", scrollRestoration: history.scrollRestoration});
+ }
+</script> \ No newline at end of file
diff --git a/docshell/test/navigation/file_scrollRestoration_part1_nobfcache.html b/docshell/test/navigation/file_scrollRestoration_part1_nobfcache.html
new file mode 100644
index 0000000000..1c94899ac2
--- /dev/null
+++ b/docshell/test/navigation/file_scrollRestoration_part1_nobfcache.html
@@ -0,0 +1,63 @@
+<html>
+ <head>
+ <script>
+ var oldHistoryObject = null;
+ var bc = new BroadcastChannel("bug1155730_part1");
+ bc.onmessage = (msgEvent) => {
+ var msg = msgEvent.data;
+ var command = msg.command;
+ if (command == "test") {
+ var currentCase = msg.currentCase;
+ test(currentCase);
+ }
+ }
+
+ function test(currentCase) {
+ var assertIs = [];
+ var assertOk = [];
+ var assertIsNot = [];
+ switch (currentCase) {
+ case 1: {
+ assertOk.push([history.scrollRestoration, "History object has scrollRestoration property."]);
+ assertIs.push([history.scrollRestoration, "auto", "history.scrollRestoration's default value should be 'auto'."]);
+ history.scrollRestoration = "foobar";
+ assertIs.push([history.scrollRestoration, "auto", "Invalid enum value should not change the value of an attribute."]);
+ history.scrollRestoration = "manual";
+ assertIs.push([history.scrollRestoration, "manual", "Valid enum value should change the value of an attribute."]);
+ history.scrollRestoration = "auto";
+ assertIs.push([history.scrollRestoration, "auto", "Valid enum value should change the value of an attribute."]);
+ bc.postMessage({command: "asserts", currentCase, assertIs, assertOk});
+ document.getElementById("bottom").scrollIntoView();
+ window.location.reload(false);
+ break;
+ }
+ case 2: {
+ assertIsNot.push([Math.round(window.scrollY), 0, "Should have restored scrolling."]);
+ assertIs.push([history.scrollRestoration, "auto", "Should have the same scrollRestoration as before reload."]);
+ history.scrollRestoration = "manual";
+ bc.postMessage({command: "asserts", currentCase, assertIs, assertIsNot});
+ window.location.reload(false);
+ break;
+ }
+ case 3: {
+ assertIs.push([window.scrollY, 0, "Should not have restored scrolling."]);
+ assertIs.push([history.scrollRestoration, "manual", "Should have the same scrollRestoration as before reload."]);
+ bc.postMessage({command: "asserts", currentCase, assertIs});
+ bc.close();
+ window.close();
+ break;
+ }
+ }
+ }
+ window.onpageshow = (event) => {
+ bc.postMessage({command: "pageshow", persisted: event.persisted});
+ }
+ </script>
+ </head>
+ <body>
+ <div style="border: 1px solid black; height: 5000px;">
+ &nbsp;</div>
+ <div id="bottom">Hello world</div>
+ <a href="#hash" name="hash">hash</a>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_scrollRestoration_part1_nobfcache.html^headers^ b/docshell/test/navigation/file_scrollRestoration_part1_nobfcache.html^headers^
new file mode 100644
index 0000000000..f944e3806d
--- /dev/null
+++ b/docshell/test/navigation/file_scrollRestoration_part1_nobfcache.html^headers^
@@ -0,0 +1 @@
+Cache-control: no-store \ No newline at end of file
diff --git a/docshell/test/navigation/file_scrollRestoration_part2_bfcache.html b/docshell/test/navigation/file_scrollRestoration_part2_bfcache.html
new file mode 100644
index 0000000000..2776e42a6e
--- /dev/null
+++ b/docshell/test/navigation/file_scrollRestoration_part2_bfcache.html
@@ -0,0 +1,57 @@
+<html>
+ <head>
+ <script>
+ var bc = new BroadcastChannel("bug1155730_part2");
+ bc.onmessage = (msgEvent) => {
+ var msg = msgEvent.data;
+ var command = msg.command;
+ if (command == "test") {
+ var currentCase = msg.currentCase;
+ test(currentCase);
+ }
+ }
+
+ function test(currentCase) {
+ var assertIs = [];
+ var assertIsNot = [];
+ switch (currentCase) {
+ case 1: {
+ history.scrollRestoration = "manual";
+ document.getElementById("bottom").scrollIntoView();
+ window.location.href = "file_scrollRestoration_navigate.html";
+ break;
+ }
+ case 2: {
+ assertIsNot.push([Math.round(window.scrollY), 0, "Should have kept the old scroll position."]);
+ assertIs.push([history.scrollRestoration, "manual", "Should have the same scrollRestoration as before reload."]);
+ bc.postMessage({command: "asserts", currentCase, assertIs, assertIsNot, assert2: "assert2"});
+ window.scrollTo(0, 0);
+ window.location.hash = "hash";
+ bc.postMessage({command: "nextCase"});
+ requestAnimationFrame(() => {
+ test(currentCase + 1);
+ });
+ break;
+ }
+ case 3: {
+ assertIsNot.push([Math.round(window.scrollY), 0, "Should have scrolled to #hash."]);
+ assertIs.push([history.scrollRestoration, "manual", "Should have the same scrollRestoration mode as before fragment navigation."]);
+ bc.postMessage({command: "asserts", currentCase, assertIs, assertIsNot});
+ bc.close();
+ window.close();
+ break;
+ }
+ }
+ }
+ window.onpageshow = (event) => {
+ bc.postMessage({command: "pageshow", persisted: event.persisted});
+ }
+ </script>
+ </head>
+ <body>
+ <div style="border: 1px solid black; height: 5000px;">
+ &nbsp;</div>
+ <div id="bottom">Hello world</div>
+ <a href="#hash" name="hash">hash</a>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_scrollRestoration_part3_nobfcache.html b/docshell/test/navigation/file_scrollRestoration_part3_nobfcache.html
new file mode 100644
index 0000000000..ffc68d6ccc
--- /dev/null
+++ b/docshell/test/navigation/file_scrollRestoration_part3_nobfcache.html
@@ -0,0 +1,157 @@
+<html>
+ <head>
+ <script>
+ var oldHistoryObject = null;
+ var currCaseForIframe = 0;
+ var bc = new BroadcastChannel("bug1155730_part3");
+ bc.onmessage = (msgEvent) => {
+ var msg = msgEvent.data;
+ var command = msg.command;
+ if (command == "test") {
+ var currentCase = msg.currentCase;
+ test(currentCase);
+ }
+ }
+
+ // If onpopstate event takes place, check if we need to call 'test()'
+ var callTest = false;
+ var nextCase = 0;
+ window.onpopstate = (e) => {
+ if (callTest) {
+ callTest = false;
+ setTimeout(() => {
+ test(nextCase);
+ });
+ }
+ }
+
+ function test(currentCase) {
+ var assertIs = [];
+ var assertOk = [];
+ var assertIsNot = [];
+ switch (currentCase) {
+ case 1: {
+ history.scrollRestoration = "manual";
+ window.location.hash = "hash";
+ bc.postMessage({command: "nextCase"});
+ requestAnimationFrame(() => {
+ test(currentCase + 1);
+ });
+ break;
+ }
+ case 2: {
+ assertIsNot.push([Math.round(window.scrollY), 0, "Should have scrolled to #hash."]);
+ assertIs.push([history.scrollRestoration, "manual", "Should have the same scrollRestoration mode as before fragment navigation."]);
+ bc.postMessage({command: "asserts", currentCase, assertIs, assertIsNot});
+ window.location.href = "file_scrollRestoration_navigate.html";
+ break;
+ }
+ case 3: {
+ assertIs.push([window.scrollY, 0, "Shouldn't have kept the old scroll position."]);
+ assertIs.push([history.scrollRestoration, "manual", "Should have the same scrollRestoration mode as before fragment navigation."]);
+ bc.postMessage({command: "asserts", currentCase, assertIs});
+ history.scrollRestoration = "auto";
+ document.getElementById("bottom").scrollIntoView();
+ history.pushState({ state: "state1" }, "state1");
+ history.pushState({ state: "state2" }, "state2");
+ window.scrollTo(0, 0);
+ bc.postMessage({command: "nextCase"});
+ callTest = true;
+ nextCase = currentCase + 1;
+ history.back(); // go back to state 1
+ break;
+ }
+ case 4: {
+ assertIsNot.push([Math.round(window.scrollY), 0, "Should have scrolled back to the state1's position"]);
+ assertIs.push([history.state.state, "state1", "Unexpected state."]);
+ bc.postMessage({command: "asserts", currentCase, assertIs, assertIsNot});
+
+ history.scrollRestoration = "manual";
+ document.getElementById("bottom").scrollIntoView();
+ history.pushState({ state: "state3" }, "state3");
+ history.pushState({ state: "state4" }, "state4");
+ window.scrollTo(0, 0);
+ bc.postMessage({command: "nextCase"});
+ callTest = true;
+ nextCase = currentCase + 1;
+ history.back(); // go back to state 3
+ break;
+ }
+ case 5: {
+ assertIs.push([Math.round(window.scrollY), 0, "Shouldn't have scrolled back to the state3's position"]);
+ assertIs.push([history.state.state, "state3", "Unexpected state."]);
+
+ history.pushState({ state: "state5" }, "state5");
+ history.scrollRestoration = "auto";
+ document.getElementById("bottom").scrollIntoView();
+ assertIsNot.push([Math.round(window.scrollY), 0, "Should have scrolled to 'bottom'."]);
+ bc.postMessage({command: "asserts", currentCase, assertIs, assertIsNot});
+ bc.postMessage({command: "nextCase"});
+ callTest = true;
+ nextCase = currentCase + 1;
+ // go back to state 3 (state 4 was removed when state 5 was pushed)
+ history.back();
+ break;
+ }
+ case 6: {
+ window.scrollTo(0, 0);
+ bc.postMessage({command: "nextCase"});
+ callTest = true;
+ nextCase = currentCase + 1;
+ history.forward();
+ break;
+ }
+ case 7: {
+ assertIsNot.push([Math.round(window.scrollY), 0, "Should have scrolled back to the state5's position"]);
+ bc.postMessage({command: "asserts", currentCase, assertIsNot});
+
+ var ifr = document.createElement("iframe");
+ ifr.src = "data:text/html,";
+ document.body.appendChild(ifr);
+ bc.postMessage({command: "nextCase"});
+ currCaseForIframe = currentCase + 1;
+ ifr.onload = () => {
+ test(currCaseForIframe);
+ };
+ break;
+ }
+ case 8: {
+ oldHistoryObject = SpecialPowers.wrap(document.getElementsByTagName("iframe")[0]).contentWindow.history;
+ bc.postMessage({command: "nextCase"});
+ currCaseForIframe++;
+ document.getElementsByTagName("iframe")[0].src = "about:blank";
+ break;
+ }
+ case 9: {
+ try {
+ oldHistoryObject.scrollRestoration;
+ assertOk.push([false, "Should have thrown an exception."]);
+ } catch (ex) {
+ assertOk.push([ex != null, "Did get an exception"]);
+ }
+ try {
+ oldHistoryObject.scrollRestoration = "auto";
+ assertOk.push([false, "Should have thrown an exception."]);
+ } catch (ex) {
+ assertOk.push([ex != null, "Did get an exception"]);
+ }
+ bc.postMessage({command: "asserts", currentCase, assertOk});
+ bc.postMessage({command: "finishing"});
+ bc.close();
+ window.close();
+ break;
+ }
+ }
+ }
+ window.onpageshow = (event) => {
+ bc.postMessage({command: "pageshow", persisted: event.persisted});
+ }
+ </script>
+ </head>
+ <body>
+ <div style="border: 1px solid black; height: 5000px;">
+ &nbsp;</div>
+ <div id="bottom">Hello world</div>
+ <a href="#hash" name="hash">hash</a>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_scrollRestoration_part3_nobfcache.html^headers^ b/docshell/test/navigation/file_scrollRestoration_part3_nobfcache.html^headers^
new file mode 100644
index 0000000000..f944e3806d
--- /dev/null
+++ b/docshell/test/navigation/file_scrollRestoration_part3_nobfcache.html^headers^
@@ -0,0 +1 @@
+Cache-control: no-store \ No newline at end of file
diff --git a/docshell/test/navigation/file_session_history_on_redirect.html b/docshell/test/navigation/file_session_history_on_redirect.html
new file mode 100644
index 0000000000..df7e99a1dd
--- /dev/null
+++ b/docshell/test/navigation/file_session_history_on_redirect.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script>
+ window.onpagehide = function(event) {
+ opener.is(event.persisted, false, "Should have disabled bfcache for this test.");
+ }
+
+ window.onpageshow = function(event) {
+ opener.pageshow();
+ }
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/docshell/test/navigation/file_session_history_on_redirect.html^headers^ b/docshell/test/navigation/file_session_history_on_redirect.html^headers^
new file mode 100644
index 0000000000..59ba296103
--- /dev/null
+++ b/docshell/test/navigation/file_session_history_on_redirect.html^headers^
@@ -0,0 +1 @@
+Cache-control: no-store
diff --git a/docshell/test/navigation/file_session_history_on_redirect_2.html b/docshell/test/navigation/file_session_history_on_redirect_2.html
new file mode 100644
index 0000000000..df7e99a1dd
--- /dev/null
+++ b/docshell/test/navigation/file_session_history_on_redirect_2.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script>
+ window.onpagehide = function(event) {
+ opener.is(event.persisted, false, "Should have disabled bfcache for this test.");
+ }
+
+ window.onpageshow = function(event) {
+ opener.pageshow();
+ }
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/docshell/test/navigation/file_session_history_on_redirect_2.html^headers^ b/docshell/test/navigation/file_session_history_on_redirect_2.html^headers^
new file mode 100644
index 0000000000..59ba296103
--- /dev/null
+++ b/docshell/test/navigation/file_session_history_on_redirect_2.html^headers^
@@ -0,0 +1 @@
+Cache-control: no-store
diff --git a/docshell/test/navigation/file_sessionhistory_iframe_removal.html b/docshell/test/navigation/file_sessionhistory_iframe_removal.html
new file mode 100644
index 0000000000..f18e849942
--- /dev/null
+++ b/docshell/test/navigation/file_sessionhistory_iframe_removal.html
@@ -0,0 +1,37 @@
+<html>
+ <head>
+ <script>
+ async function wait() {
+ return opener.SpecialPowers.spawnChrome([], function() { /*dummy */ });
+ }
+ async function run() {
+ var counter = 0;
+ while(true) {
+ // Push new history entries until we hit the limit and start removing
+ // entries from the front.
+ var len = history.length;
+ document.getElementById("ifr1").contentWindow.history.pushState(++counter, null, null);
+ await wait();
+ if (len == history.length) {
+ opener.ok(true, "Found max length " + len);
+ document.getElementById("ifr2").contentWindow.history.pushState(++counter, null, null);
+ await wait();
+ document.getElementById("ifr1").contentWindow.history.pushState(++counter, null, null);
+ await wait();
+ opener.is(len, history.length, "Adding session history entries in different iframe should behave the same way");
+ // This should remove all the history entries for ifr1, leaving just
+ // the ones for ifr2.
+ document.getElementById("ifr1").remove();
+ opener.is(history.length, 2, "Should have entries for the initial load and the pushState for ifr2");
+ opener.finishTest();
+ break;
+ }
+ }
+ }
+ </script>
+ </head>
+ <body onload="setTimeout(run)">
+ <iframe src="blank.html" id="ifr1"></iframe>
+ <iframe src="blank.html" id="ifr2"></iframe>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_shiftReload_and_pushState.html b/docshell/test/navigation/file_shiftReload_and_pushState.html
new file mode 100644
index 0000000000..7882143c83
--- /dev/null
+++ b/docshell/test/navigation/file_shiftReload_and_pushState.html
@@ -0,0 +1,28 @@
+<html>
+ <head>
+ <script>
+ function test() {
+ try {
+ frames[0].history.pushState({}, "state", "?pushed");
+ } catch (ex) {
+ opener.ok(false, "history.pushState shouldn't throw");
+ }
+
+ if (!opener.shiftReloadPushStateFirstRound) {
+ opener.shiftReloadPushStateFirstRound = true;
+ window.location.reload(true);
+ } else {
+ opener.ok(true, "Did run history.push");
+ opener.finishTest();
+ }
+ }
+
+ window.addEventListener("load", function() { setTimeout(test, 0); });
+ </script>
+ </head>
+ <body>
+ <iframe src="frame0.html"></iframe>
+ <script>
+ </script>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_ship_beforeunload_fired.html b/docshell/test/navigation/file_ship_beforeunload_fired.html
new file mode 100644
index 0000000000..a1f416f959
--- /dev/null
+++ b/docshell/test/navigation/file_ship_beforeunload_fired.html
@@ -0,0 +1,37 @@
+<html>
+ <script>
+ onpageshow = function(pageShowEvent) {
+ var bc = new BroadcastChannel("ship_beforeunload");
+ bc.onmessage = function(event) {
+ if (event.data.action == "register_beforeunload") {
+ onbeforeunload = function() {
+ bc.postMessage("beforeunload_fired");
+ bc.close();
+ }
+ if (event.data.loadNextPageFromSessionHistory) {
+ history.back();
+ } else {
+ location.href += "?differentpage";
+ }
+ } else if (event.data.action == "navigate_to_page_b") {
+ bc.close();
+ if (event.data.blockBFCache) {
+ window.blockBFCache = new RTCPeerConnection();
+ }
+ location.href += "?pageb";
+ } else if (event.data.action == "back_to_page_b") {
+ if (event.data.forwardNavigateToPageB) {
+ history.forward();
+ } else {
+ history.back();
+ }
+ bc.close();
+ } else if (event.data.action == "close") {
+ bc.close();
+ window.close();
+ }
+ }
+ bc.postMessage({type: pageShowEvent.type, persisted: pageShowEvent.persisted});
+ }
+ </script>
+</html>
diff --git a/docshell/test/navigation/file_static_and_dynamic_1.html b/docshell/test/navigation/file_static_and_dynamic_1.html
new file mode 100644
index 0000000000..e66216c41e
--- /dev/null
+++ b/docshell/test/navigation/file_static_and_dynamic_1.html
@@ -0,0 +1,31 @@
+<html>
+ <head>
+ <script>
+ function test() {
+ var ifr = document.createElement("iframe");
+ ifr.src = "frame0.html";
+ document.getElementById("dynamic").appendChild(ifr);
+ var staticFrame = document.getElementById("staticframe");
+ staticFrame.onload = window.location = "goback.html";
+ staticFrame.contentWindow.location = "frame1.html";
+ }
+
+ function start() {
+ if (++opener.testCount == 1) {
+ test();
+ } else {
+ var staticFrame = document.getElementById("staticframe");
+ opener.ok(String(staticFrame.contentWindow.location).includes(staticFrame.src),
+ "Wrong document loaded!");
+ opener.finishTest();
+ }
+ }
+ </script>
+ </head>
+ <body onload="setTimeout('start()', 0)">
+ <h5>Dynamic</h5>
+ <div id="dynamic"></div>
+ <h5>Static</h5>
+ <div id="static"><iframe id="staticframe" src="frame0.html"></iframe></div>
+ </body>
+</html>
diff --git a/docshell/test/navigation/file_tell_opener.html b/docshell/test/navigation/file_tell_opener.html
new file mode 100644
index 0000000000..bd45c275e6
--- /dev/null
+++ b/docshell/test/navigation/file_tell_opener.html
@@ -0,0 +1,8 @@
+<html>
+ <body onload="bodyLoaded()">Frame 1</body>
+ <script>
+ function bodyLoaded() {
+ opener.postMessage("body-loaded", "*");
+ }
+ </script>
+</html>
diff --git a/docshell/test/navigation/file_triggeringprincipal_frame_1.html b/docshell/test/navigation/file_triggeringprincipal_frame_1.html
new file mode 100644
index 0000000000..528437f892
--- /dev/null
+++ b/docshell/test/navigation/file_triggeringprincipal_frame_1.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<head><meta charset="utf-8"></head>
+<body>
+<b>Frame 1</b><br/>
+<a href="#"" id="testlink" onclick="parent.frames[1].frames[0].location='http://test2.mochi.test:8888/tests/docshell/test/navigation/file_triggeringprincipal_subframe_nav.html'">click me</a>
+
+<script type="application/javascript">
+ // make sure to set document.domain to the same domain as the subframe
+ window.onload = function() {
+ document.domain = "mochi.test";
+ };
+ window.addEventListener("message", receiveMessage);
+ function receiveMessage(event) {
+ // make sure to get the right start command, otherwise
+ // let the parent know and fail the test
+ if (event.data.start !== "startTest") {
+ window.removeEventListener("message", receiveMessage);
+ window.parent.postMessage({triggeringPrincipalURI: "false"}, "*");
+ }
+ // click the link to navigate the subframe
+ document.getElementById("testlink").click();
+ }
+</script>
+
+</body>
+</html>
diff --git a/docshell/test/navigation/file_triggeringprincipal_frame_2.html b/docshell/test/navigation/file_triggeringprincipal_frame_2.html
new file mode 100644
index 0000000000..ef7cdfc178
--- /dev/null
+++ b/docshell/test/navigation/file_triggeringprincipal_frame_2.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<html>
+<head><meta charset="utf-8"></head>
+<body>
+<b>Frame 2</b><br/>
+<iframe src="http://test2.mochi.test:8888/tests/docshell/test/navigation/file_triggeringprincipal_subframe.html"></iframe>
+</body>
+</html>
diff --git a/docshell/test/navigation/file_triggeringprincipal_iframe_iframe_window_open_frame_a.html b/docshell/test/navigation/file_triggeringprincipal_iframe_iframe_window_open_frame_a.html
new file mode 100644
index 0000000000..75b2933c1b
--- /dev/null
+++ b/docshell/test/navigation/file_triggeringprincipal_iframe_iframe_window_open_frame_a.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+Frame A
+</body>
+</html>
diff --git a/docshell/test/navigation/file_triggeringprincipal_iframe_iframe_window_open_frame_a_nav.html b/docshell/test/navigation/file_triggeringprincipal_iframe_iframe_window_open_frame_a_nav.html
new file mode 100644
index 0000000000..0479f5e1e5
--- /dev/null
+++ b/docshell/test/navigation/file_triggeringprincipal_iframe_iframe_window_open_frame_a_nav.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+Frame A navigated by Frame B
+</body>
+</html>
diff --git a/docshell/test/navigation/file_triggeringprincipal_iframe_iframe_window_open_frame_b.html b/docshell/test/navigation/file_triggeringprincipal_iframe_iframe_window_open_frame_b.html
new file mode 100644
index 0000000000..e5d40b267a
--- /dev/null
+++ b/docshell/test/navigation/file_triggeringprincipal_iframe_iframe_window_open_frame_b.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<body>
+Frame B navigating Frame A
+
+<script type="text/javascript">
+
+window.open("file_triggeringprincipal_iframe_iframe_window_open_frame_a_nav.html", "framea");
+
+</script>
+
+</body>
+</html>
+
+
diff --git a/docshell/test/navigation/file_triggeringprincipal_parent_iframe_window_open_base.html b/docshell/test/navigation/file_triggeringprincipal_parent_iframe_window_open_base.html
new file mode 100644
index 0000000000..caa6b275b9
--- /dev/null
+++ b/docshell/test/navigation/file_triggeringprincipal_parent_iframe_window_open_base.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+base test frame
+</body>
+</html>
diff --git a/docshell/test/navigation/file_triggeringprincipal_parent_iframe_window_open_nav.html b/docshell/test/navigation/file_triggeringprincipal_parent_iframe_window_open_nav.html
new file mode 100644
index 0000000000..f4a4d0e631
--- /dev/null
+++ b/docshell/test/navigation/file_triggeringprincipal_parent_iframe_window_open_nav.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+navigated by window.open()
+</body>
+</html>
diff --git a/docshell/test/navigation/file_triggeringprincipal_subframe.html b/docshell/test/navigation/file_triggeringprincipal_subframe.html
new file mode 100644
index 0000000000..ba6b6dc09a
--- /dev/null
+++ b/docshell/test/navigation/file_triggeringprincipal_subframe.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head><meta charset='utf-8'></head>
+<body>
+<b>Sub Frame 2</b><br/>
+<script type='application/javascript'>
+ // make sure to set document.domain to same domain as frame 1
+ window.onload = function() {
+ document.domain = "mochi.test";
+ // let Frame 1 know that we are ready to run the test
+ window.parent.parent.frames[0].postMessage({start: "startTest"}, "*");
+ };
+</script>
+</body>
+</html>
diff --git a/docshell/test/navigation/file_triggeringprincipal_subframe_nav.html b/docshell/test/navigation/file_triggeringprincipal_subframe_nav.html
new file mode 100644
index 0000000000..582181c00d
--- /dev/null
+++ b/docshell/test/navigation/file_triggeringprincipal_subframe_nav.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<head><meta charset="utf-8"></head>
+<body onload="checkResults()">
+<b>Sub Frame 2 Navigated</b><br/>
+
+<script type='application/javascript'>
+ function checkResults() {
+ // query the uri of the loadingPrincipal and the TriggeringPrincipal and pass
+ // that information on to the parent for verification.
+ var channel = SpecialPowers.wrap(window).docShell.currentDocumentChannel;
+ var triggeringPrincipalURI = channel.loadInfo.triggeringPrincipal.asciiSpec;
+ var loadingPrincipalURI = channel.loadInfo.loadingPrincipal.asciiSpec;
+ var referrerURI = document.referrer;
+ window.parent.parent.postMessage({triggeringPrincipalURI,
+ loadingPrincipalURI,
+ referrerURI}, "*");
+ }
+</script>
+</body>
+</html>
diff --git a/docshell/test/navigation/file_triggeringprincipal_subframe_same_origin_nav.html b/docshell/test/navigation/file_triggeringprincipal_subframe_same_origin_nav.html
new file mode 100644
index 0000000000..c84e216ae8
--- /dev/null
+++ b/docshell/test/navigation/file_triggeringprincipal_subframe_same_origin_nav.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head><meta charset="utf-8"></head>
+<body onload="checkResults()">
+<b>SubFrame Same-Origin Navigated</b><br/>
+
+<script type='application/javascript'>
+ function checkResults() {
+ // query the uri of the loadingPrincipal and the TriggeringPrincipal and pass
+ // that information on to the parent for verification.
+ var channel = SpecialPowers.wrap(window).docShell.currentDocumentChannel;
+ var triggeringPrincipalURI = channel.loadInfo.triggeringPrincipal.asciiSpec;
+ var loadingPrincipalURI = channel.loadInfo.loadingPrincipal.asciiSpec;
+
+ window.parent.postMessage({triggeringPrincipalURI,
+ loadingPrincipalURI}, "*");
+ }
+</script>
+</body>
+</html>
diff --git a/docshell/test/navigation/file_triggeringprincipal_window_open.html b/docshell/test/navigation/file_triggeringprincipal_window_open.html
new file mode 100644
index 0000000000..d0644a4d5c
--- /dev/null
+++ b/docshell/test/navigation/file_triggeringprincipal_window_open.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+http
+</body>
+</html>
diff --git a/docshell/test/navigation/frame0.html b/docshell/test/navigation/frame0.html
new file mode 100644
index 0000000000..93d1c9c822
--- /dev/null
+++ b/docshell/test/navigation/frame0.html
@@ -0,0 +1,3 @@
+<html>
+ <body>Frame 0</body>
+</html>
diff --git a/docshell/test/navigation/frame1.html b/docshell/test/navigation/frame1.html
new file mode 100644
index 0000000000..4d06c09d1c
--- /dev/null
+++ b/docshell/test/navigation/frame1.html
@@ -0,0 +1,3 @@
+<html>
+ <body>Frame 1</body>
+</html>
diff --git a/docshell/test/navigation/frame2.html b/docshell/test/navigation/frame2.html
new file mode 100644
index 0000000000..7a3b5e0b9b
--- /dev/null
+++ b/docshell/test/navigation/frame2.html
@@ -0,0 +1,3 @@
+<html>
+ <body>Frame 2</body>
+</html>
diff --git a/docshell/test/navigation/frame3.html b/docshell/test/navigation/frame3.html
new file mode 100644
index 0000000000..fd24293873
--- /dev/null
+++ b/docshell/test/navigation/frame3.html
@@ -0,0 +1,3 @@
+<html>
+ <body>Frame 3</body>
+</html>
diff --git a/docshell/test/navigation/frame_1_out_of_6.html b/docshell/test/navigation/frame_1_out_of_6.html
new file mode 100644
index 0000000000..93547cd1c4
--- /dev/null
+++ b/docshell/test/navigation/frame_1_out_of_6.html
@@ -0,0 +1,6 @@
+<html>
+ <body>
+ Page 1
+ <iframe style="height: 100vh; width: 100%;" id="static" src="http://test1.mochi.test:8888/tests/docshell/test/navigation/frame_2_out_of_6.html"></iframe>
+ </body>
+</html> \ No newline at end of file
diff --git a/docshell/test/navigation/frame_2_out_of_6.html b/docshell/test/navigation/frame_2_out_of_6.html
new file mode 100644
index 0000000000..02056acce8
--- /dev/null
+++ b/docshell/test/navigation/frame_2_out_of_6.html
@@ -0,0 +1,6 @@
+<html>
+ <body>
+ Page 2
+ <iframe style="height: 100vh; width: 100%;" id="static" src="http://sub1.test1.mochi.test:8888/tests/docshell/test/navigation/frame_3_out_of_6.html"></iframe>
+ </body>
+</html> \ No newline at end of file
diff --git a/docshell/test/navigation/frame_3_out_of_6.html b/docshell/test/navigation/frame_3_out_of_6.html
new file mode 100644
index 0000000000..e9dc308f6e
--- /dev/null
+++ b/docshell/test/navigation/frame_3_out_of_6.html
@@ -0,0 +1,6 @@
+<html>
+ <body>
+ Page 3
+ <iframe style="height: 100vh; width: 100%;" id="static" src="http://sub2.xn--lt-uia.mochi.test:8888/tests/docshell/test/navigation/frame_4_out_of_6.html"></iframe>
+ </body>
+</html> \ No newline at end of file
diff --git a/docshell/test/navigation/frame_4_out_of_6.html b/docshell/test/navigation/frame_4_out_of_6.html
new file mode 100644
index 0000000000..66a5083e4f
--- /dev/null
+++ b/docshell/test/navigation/frame_4_out_of_6.html
@@ -0,0 +1,6 @@
+<html>
+ <body>
+ Page 4
+ <iframe style="height: 100vh; width: 100%;" id="static" src="http://test2.mochi.test:8888/tests/docshell/test/navigation/frame_5_out_of_6.html"></iframe>
+ </body>
+</html> \ No newline at end of file
diff --git a/docshell/test/navigation/frame_5_out_of_6.html b/docshell/test/navigation/frame_5_out_of_6.html
new file mode 100644
index 0000000000..0121f0f749
--- /dev/null
+++ b/docshell/test/navigation/frame_5_out_of_6.html
@@ -0,0 +1,6 @@
+<html>
+ <body>
+ Page 5
+ <iframe style="height: 100vh; width: 100%;" id="static" src="http://example.org:80/tests/docshell/test/navigation/frame_6_out_of_6.html"></iframe>
+ </body>
+</html> \ No newline at end of file
diff --git a/docshell/test/navigation/frame_6_out_of_6.html b/docshell/test/navigation/frame_6_out_of_6.html
new file mode 100644
index 0000000000..c9827ccaae
--- /dev/null
+++ b/docshell/test/navigation/frame_6_out_of_6.html
@@ -0,0 +1,6 @@
+<html>
+ <body>
+ Page 6
+ <iframe style="height: 100vh; width: 100%;" id="static" src="http://example.com/tests/docshell/test/navigation/frame_1_out_of_6.html"></iframe>
+ </body>
+</html> \ No newline at end of file
diff --git a/docshell/test/navigation/frame_load_as_example_com.html b/docshell/test/navigation/frame_load_as_example_com.html
new file mode 100644
index 0000000000..a1a4e7110a
--- /dev/null
+++ b/docshell/test/navigation/frame_load_as_example_com.html
@@ -0,0 +1,6 @@
+<html>
+ <body>
+ example.com
+ <iframe style="height: 100vh; width:100%;" id="static" src="http://example.org/tests/docshell/test/navigation/frame_load_as_example_org.html"></iframe>
+ </body>
+</html>
diff --git a/docshell/test/navigation/frame_load_as_example_org.html b/docshell/test/navigation/frame_load_as_example_org.html
new file mode 100644
index 0000000000..2fbb8038c9
--- /dev/null
+++ b/docshell/test/navigation/frame_load_as_example_org.html
@@ -0,0 +1,6 @@
+<html>
+ <body>
+ example.org
+ <iframe style="height: 100vh; width:100%;" id="static" src="http://example.com/tests/docshell/test/navigation/frame_load_as_example_com.html"></iframe>
+ </body>
+</html>
diff --git a/docshell/test/navigation/frame_load_as_host1.html b/docshell/test/navigation/frame_load_as_host1.html
new file mode 100644
index 0000000000..eb006b21a1
--- /dev/null
+++ b/docshell/test/navigation/frame_load_as_host1.html
@@ -0,0 +1,6 @@
+<html>
+ <body>
+ Page 1
+ <iframe style="height: 100vh; width: 100%;" id="static" src="http://example.com/tests/docshell/test/navigation/frame_load_as_host2.html"></iframe>
+ </body>
+</html>
diff --git a/docshell/test/navigation/frame_load_as_host2.html b/docshell/test/navigation/frame_load_as_host2.html
new file mode 100644
index 0000000000..5457c17e9b
--- /dev/null
+++ b/docshell/test/navigation/frame_load_as_host2.html
@@ -0,0 +1,6 @@
+<html>
+ <body>
+ Page 2
+ <iframe style="height: 100vh; width: 100%;" id="static" src="http://test1.mochi.test:8888/tests/docshell/test/navigation/frame_load_as_host3.html"></iframe>
+ </body>
+</html>
diff --git a/docshell/test/navigation/frame_load_as_host3.html b/docshell/test/navigation/frame_load_as_host3.html
new file mode 100644
index 0000000000..a9064ec867
--- /dev/null
+++ b/docshell/test/navigation/frame_load_as_host3.html
@@ -0,0 +1,6 @@
+<html>
+ <body>
+ Page 3
+ <iframe style="height: 100vh; width: 100%;" id="static" src="http://sub1.test1.mochi.test:8888/tests/docshell/test/navigation/frame_load_as_host1.html"></iframe>
+ </body>
+</html>
diff --git a/docshell/test/navigation/frame_recursive.html b/docshell/test/navigation/frame_recursive.html
new file mode 100644
index 0000000000..835d9d63a2
--- /dev/null
+++ b/docshell/test/navigation/frame_recursive.html
@@ -0,0 +1,6 @@
+<html>
+ <body>
+ example.com
+ <iframe style="height: 100vh; width:100%;" id="static" src="http://example.com/tests/docshell/test/navigation/frame_recursive.html"></iframe>
+ </body>
+</html>
diff --git a/docshell/test/navigation/goback.html b/docshell/test/navigation/goback.html
new file mode 100644
index 0000000000..ce2968374e
--- /dev/null
+++ b/docshell/test/navigation/goback.html
@@ -0,0 +1,5 @@
+<html>
+ <body onload="setTimeout('window.history.go(-1)', 1000);">
+ window.history.go(-1);
+ </body>
+</html>
diff --git a/docshell/test/navigation/iframe.html b/docshell/test/navigation/iframe.html
new file mode 100644
index 0000000000..f8fce53c55
--- /dev/null
+++ b/docshell/test/navigation/iframe.html
@@ -0,0 +1,9 @@
+<html>
+<body>
+<script>
+var src = window.location.hash.substring(1);
+// eslint-disable-next-line no-unsanitized/method
+document.write('<iframe src="' + src + '"></iframe>');
+</script>
+</body>
+</html>
diff --git a/docshell/test/navigation/iframe_slow_onload.html b/docshell/test/navigation/iframe_slow_onload.html
new file mode 100644
index 0000000000..e8555699bb
--- /dev/null
+++ b/docshell/test/navigation/iframe_slow_onload.html
@@ -0,0 +1,5 @@
+<html>
+ <body>
+ <iframe id="inner" src="iframe_slow_onload_inner.html">
+ </body>
+</html>
diff --git a/docshell/test/navigation/iframe_slow_onload_inner.html b/docshell/test/navigation/iframe_slow_onload_inner.html
new file mode 100644
index 0000000000..ad39eba795
--- /dev/null
+++ b/docshell/test/navigation/iframe_slow_onload_inner.html
@@ -0,0 +1,19 @@
+<html>
+ <head>
+ <script>
+ function load() {
+ // Let the test page know that it can try to navigate.
+ top.postMessage("onload", "*");
+ // We're starting an infinite loop, but we need to time out after a
+ // while, or the loop will keep running until shutdown.
+ let t0 = performance.now();
+ while (performance.now() - t0 < 5000) {
+ document.getElementById("output").innerText = Math.random();
+ }
+ }
+ </script>
+ </head>
+ <body onload="load()">
+ <p id="output"></p>
+ </body>
+</html>
diff --git a/docshell/test/navigation/iframe_static.html b/docshell/test/navigation/iframe_static.html
new file mode 100644
index 0000000000..1bdd1437c1
--- /dev/null
+++ b/docshell/test/navigation/iframe_static.html
@@ -0,0 +1,8 @@
+<html>
+ <body>
+ Nested Frame
+ <div id="frameContainer">
+ <iframe id="staticFrame" src="frame0.html"></iframe>
+ </div>
+ </body>
+</html>
diff --git a/docshell/test/navigation/mochitest.ini b/docshell/test/navigation/mochitest.ini
new file mode 100644
index 0000000000..032d5a8471
--- /dev/null
+++ b/docshell/test/navigation/mochitest.ini
@@ -0,0 +1,219 @@
+[DEFAULT]
+support-files =
+ NavigationUtils.js
+ navigation_target_url.html
+ navigation_target_popup_url.html
+ blank.html
+ file_bug386782_contenteditable.html
+ file_bug386782_designmode.html
+ redbox_bug430723.html
+ bluebox_bug430723.html
+ file_bug462076_1.html
+ file_bug462076_2.html
+ file_bug462076_3.html
+ file_bug508537_1.html
+ file_bug534178.html
+ file_document_write_1.html
+ file_fragment_handling_during_load.html
+ file_fragment_handling_during_load_frame1.html
+ file_fragment_handling_during_load_frame2.sjs
+ file_nested_frames.html
+ file_nested_frames_innerframe.html
+ file_shiftReload_and_pushState.html
+ file_static_and_dynamic_1.html
+ frame0.html
+ frame1.html
+ frame2.html
+ frame3.html
+ goback.html
+ iframe.html
+ iframe_static.html
+ navigate.html
+ open.html
+ parent.html
+ file_tell_opener.html
+ file_triggeringprincipal_frame_1.html
+ file_triggeringprincipal_frame_2.html
+ file_triggeringprincipal_subframe.html
+ file_triggeringprincipal_subframe_nav.html
+ file_triggeringprincipal_subframe_same_origin_nav.html
+ file_triggeringprincipal_window_open.html
+ file_triggeringprincipal_parent_iframe_window_open_base.html
+ file_triggeringprincipal_parent_iframe_window_open_nav.html
+ file_triggeringprincipal_iframe_iframe_window_open_frame_a.html
+ file_triggeringprincipal_iframe_iframe_window_open_frame_b.html
+ file_triggeringprincipal_iframe_iframe_window_open_frame_a_nav.html
+ file_load_history_entry_page_with_one_link.html
+ file_load_history_entry_page_with_two_links.html
+ file_bug1300461.html
+ file_bug1300461_redirect.html
+ file_bug1300461_redirect.html^headers^
+ file_bug1300461_back.html
+ file_contentpolicy_block_window.html
+ file_bug1326251.html
+ file_bug1326251_evict_cache.html
+ file_bug1364364-1.html
+ file_bug1364364-2.html
+ file_bug1375833.html
+ file_bug1375833-frame1.html
+ file_bug1375833-frame2.html
+ test_bug145971.html
+ file_bug1609475.html
+ file_bug1379762-1.html
+ file_scrollRestoration_bfcache_and_nobfcache.html
+ file_scrollRestoration_bfcache_and_nobfcache_part2.html
+ file_scrollRestoration_bfcache_and_nobfcache_part2.html^headers^
+ file_scrollRestoration_navigate.html
+ file_scrollRestoration_part1_nobfcache.html
+ file_scrollRestoration_part1_nobfcache.html^headers^
+ file_scrollRestoration_part2_bfcache.html
+ file_scrollRestoration_part3_nobfcache.html
+ file_scrollRestoration_part3_nobfcache.html^headers^
+ file_session_history_on_redirect.html
+ file_session_history_on_redirect.html^headers^
+ file_session_history_on_redirect_2.html
+ file_session_history_on_redirect_2.html^headers^
+ redirect_handlers.sjs
+ frame_load_as_example_com.html
+ frame_load_as_example_org.html
+ frame_load_as_host1.html
+ frame_load_as_host2.html
+ frame_load_as_host3.html
+ frame_1_out_of_6.html
+ frame_2_out_of_6.html
+ frame_3_out_of_6.html
+ frame_4_out_of_6.html
+ frame_5_out_of_6.html
+ frame_6_out_of_6.html
+ frame_recursive.html
+ object_recursive_load.html
+ file_nested_srcdoc.html
+
+[test_aboutblank_change_process.html]
+[test_beforeunload_and_bfcache.html]
+support-files = file_beforeunload_and_bfcache.html
+[test_bug13871.html]
+[test_bug1583110.html]
+support-files = file_bug1583110.html
+[test_bug1706090.html]
+support-files = file_bug1706090.html
+skip-if = fission # The test is currently for the old bfcache implementation
+[test_bug1745638.html]
+support-files = file_bug1745638.html
+[test_bug1747019.html]
+support-files =
+ goback.html
+ cache_control_max_age_3600.sjs
+[test_bug1750973.html]
+support-files = file_bug1750973.html
+[test_bug1758664.html]
+support-files = file_bug1758664.html
+skip-if = !sessionHistoryInParent # the old implementation behaves inconsistently
+[test_bug270414.html]
+[test_bug278916.html]
+[test_bug279495.html]
+[test_bug344861.html]
+skip-if = toolkit == "android"
+[test_bug386782.html]
+[test_bug430624.html]
+[test_bug430723.html]
+skip-if = (!debug && (os == 'mac' || os == 'win')) # Bug 874423
+[test_bug1364364.html]
+skip-if = (os == "android") # Bug 1560378
+[test_bug1375833.html]
+[test_bug1536471.html]
+support-files = file_bug1536471.html
+[test_blockBFCache.html]
+support-files =
+ file_blockBFCache.html
+ slow.sjs
+ iframe_slow_onload.html
+ iframe_slow_onload_inner.html
+[test_child.html]
+[test_docshell_gotoindex.html]
+support-files = file_docshell_gotoindex.html
+[test_evict_from_bfcache.html]
+support-files = file_evict_from_bfcache.html
+[test_grandchild.html]
+[test_load_history_entry.html]
+[test_meta_refresh.html]
+support-files = file_meta_refresh.html
+[test_navigation_type.html]
+support-files = file_navigation_type.html
+[test_new_shentry_during_history_navigation.html]
+support-files =
+ file_new_shentry_during_history_navigation_1.html
+ file_new_shentry_during_history_navigation_1.html^headers^
+ file_new_shentry_during_history_navigation_2.html
+ file_new_shentry_during_history_navigation_2.html^headers^
+ file_new_shentry_during_history_navigation_3.html
+ file_new_shentry_during_history_navigation_3.html^headers^
+ file_new_shentry_during_history_navigation_4.html
+[test_not-opener.html]
+[test_online_offline_bfcache.html]
+support-files = file_online_offline_bfcache.html
+[test_opener.html]
+skip-if =
+ fission && xorigin # Bug 1716402 - New fission platform triage
+ os == "linux" && bits == 64 # Bug 1572299
+ win10_2004 # Bug 1572299
+ win11_2009 # Bug 1797751
+[test_popup-navigates-children.html]
+[test_reload.html]
+support-files = file_reload.html
+[test_reload_nonbfcached_srcdoc.html]
+support-files = file_reload_nonbfcached_srcdoc.sjs
+[test_reserved.html]
+skip-if =
+ debug # bug 1263213
+[test_performance_navigation.html]
+[test_same_url.html]
+support-files = file_same_url.html
+[test_session_history_on_redirect.html]
+[test_sessionhistory.html]
+skip-if = verify && (os == 'mac') && debug # Hit MOZ_CRASH(Shutdown too long, probably frozen, causing a crash.) bug 1677545
+[test_dynamic_frame_forward_back.html]
+[test_sessionhistory_document_write.html]
+[test_sessionhistory_iframe_removal.html]
+support-files = file_sessionhistory_iframe_removal.html
+[test_session_history_entry_cleanup.html]
+[test_fragment_handling_during_load.html]
+[test_nested_frames.html]
+[test_shiftReload_and_pushState.html]
+[test_scrollRestoration.html]
+[test_bug1609475.html]
+[test_bug1300461.html]
+[test_bug1326251.html]
+skip-if = toolkit == 'android' || sessionHistoryInParent # It relies on the bfcache
+[test_bug1379762.html]
+[test_state_size.html]
+[test_static_and_dynamic.html]
+skip-if = true # This was disabled for a few years now anyway, bug 1677544
+[test_sibling-matching-parent.html]
+[test_sibling-off-domain.html]
+[test_triggeringprincipal_frame_nav.html]
+[test_triggeringprincipal_frame_same_origin_nav.html]
+[test_triggeringprincipal_window_open.html]
+[test_triggeringprincipal_parent_iframe_window_open.html]
+[test_triggeringprincipal_iframe_iframe_window_open.html]
+[test_contentpolicy_block_window.html]
+[test_rate_limit_location_change.html]
+[test_reload_large_postdata.html]
+support-files =
+ file_reload_large_postdata.sjs
+[test_recursive_frames.html]
+[test_bug1699721.html]
+skip-if = !fission # tests fission-only process switching behaviour
+[test_ship_beforeunload_fired.html]
+support-files =
+ file_ship_beforeunload_fired.html
+skip-if = !sessionHistoryInParent
+[test_ship_beforeunload_fired_2.html]
+support-files =
+ file_ship_beforeunload_fired.html
+skip-if = !sessionHistoryInParent
+[test_ship_beforeunload_fired_3.html]
+support-files =
+ file_ship_beforeunload_fired.html
+skip-if = !sessionHistoryInParent
+[test_open_javascript_noopener.html]
diff --git a/docshell/test/navigation/navigate.html b/docshell/test/navigation/navigate.html
new file mode 100644
index 0000000000..f68123188e
--- /dev/null
+++ b/docshell/test/navigation/navigate.html
@@ -0,0 +1,38 @@
+<html>
+<head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="NavigationUtils.js"></script>
+ <script>
+ function navigate() {
+ var args = window.location.hash.substring(1).split(",");
+ var target = args[0];
+ var mechanism = args[1];
+
+ switch (mechanism) {
+ case "location":
+ // eslint-disable-next-line no-eval
+ navigateByLocation(eval(target));
+ break;
+ case "open":
+ navigateByOpen(target);
+ break;
+ case "form":
+ navigateByForm(target);
+ break;
+ case "hyperlink":
+ navigateByHyperlink(target);
+ break;
+ }
+ }
+ </script>
+</head>
+<body onload="navigate();">
+<script>
+var args = window.location.hash.substring(1).split(",");
+var target = args[0];
+var mechanism = args[1];
+// eslint-disable-next-line no-unsanitized/method
+document.write("target=" + target + " mechanism=" + mechanism);
+</script>
+</body>
+</html>
diff --git a/docshell/test/navigation/navigation_target_popup_url.html b/docshell/test/navigation/navigation_target_popup_url.html
new file mode 100644
index 0000000000..cfe6de009d
--- /dev/null
+++ b/docshell/test/navigation/navigation_target_popup_url.html
@@ -0,0 +1 @@
+<html><body>This is a popup</body></html>
diff --git a/docshell/test/navigation/navigation_target_url.html b/docshell/test/navigation/navigation_target_url.html
new file mode 100644
index 0000000000..a485e8133f
--- /dev/null
+++ b/docshell/test/navigation/navigation_target_url.html
@@ -0,0 +1 @@
+<html><body>This frame was navigated.</body></html>
diff --git a/docshell/test/navigation/object_recursive_load.html b/docshell/test/navigation/object_recursive_load.html
new file mode 100644
index 0000000000..3ae9521e63
--- /dev/null
+++ b/docshell/test/navigation/object_recursive_load.html
@@ -0,0 +1,6 @@
+<html>
+ <body width="400" height="300">
+ Frame 0
+ <object id="static" width="400" height="300" data="http://sub2.xn--lt-uia.mochi.test:8888/tests/docshell/test/navigation/object_recursive_load.html"></object>
+ </body>
+</html>
diff --git a/docshell/test/navigation/open.html b/docshell/test/navigation/open.html
new file mode 100644
index 0000000000..9a96a8dda7
--- /dev/null
+++ b/docshell/test/navigation/open.html
@@ -0,0 +1,10 @@
+<html>
+<body>
+<script>
+var target = window.location.hash.substring(1);
+// eslint-disable-next-line no-unsanitized/method
+document.write("target=" + target);
+window.open("navigation_target_popup_url.html", target, "width=10,height=10");
+</script>
+</body>
+</html>
diff --git a/docshell/test/navigation/parent.html b/docshell/test/navigation/parent.html
new file mode 100644
index 0000000000..74722b8bdf
--- /dev/null
+++ b/docshell/test/navigation/parent.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<body>
+This document contains a frame.
+<div><iframe src="blank.html"></iframe></div>
+<script>
+frames[0].name = window.name + "_child0";
+window.onload = function() {
+ opener.postMessage("ready", "*");
+};
+</script>
+</body>
+</html>
+
diff --git a/docshell/test/navigation/redbox_bug430723.html b/docshell/test/navigation/redbox_bug430723.html
new file mode 100644
index 0000000000..c2d1f98092
--- /dev/null
+++ b/docshell/test/navigation/redbox_bug430723.html
@@ -0,0 +1,6 @@
+<html><head>
+<script> window.addEventListener("pageshow", function() { opener.nextTest(); }); </script>
+</head><body>
+<div style="position:absolute; left:0px; top:0px; width:50%; height:150%; background-color:red">
+<p>This is a very tall red box.</p>
+</div></body></html>
diff --git a/docshell/test/navigation/redirect_handlers.sjs b/docshell/test/navigation/redirect_handlers.sjs
new file mode 100644
index 0000000000..c2b39e61c9
--- /dev/null
+++ b/docshell/test/navigation/redirect_handlers.sjs
@@ -0,0 +1,29 @@
+function handleRequest(request, response) {
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Cache-Control", "no-store", false);
+
+ let state = getState("sessionhistory_do_redirect");
+ if (state != "doredirect") {
+ response.setHeader("Content-Type", "text/html");
+ const contents = `
+ <script>
+ window.onpageshow = function(event) {
+ opener.pageshow();
+ }
+ </script>
+ `;
+ response.write(contents);
+
+ // The next load should do a redirect.
+ setState("sessionhistory_do_redirect", "doredirect");
+ } else {
+ setState("sessionhistory_do_redirect", "");
+
+ response.setStatusLine("1.1", 302, "Found");
+ response.setHeader(
+ "Location",
+ "file_session_history_on_redirect_2.html",
+ false
+ );
+ }
+}
diff --git a/docshell/test/navigation/redirect_to_blank.sjs b/docshell/test/navigation/redirect_to_blank.sjs
new file mode 100644
index 0000000000..b1668401ea
--- /dev/null
+++ b/docshell/test/navigation/redirect_to_blank.sjs
@@ -0,0 +1,6 @@
+function handleRequest(request, response) {
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Cache-Control", "no-store", false);
+ response.setStatusLine("1.1", 302, "Found");
+ response.setHeader("Location", "blank.html", false);
+}
diff --git a/docshell/test/navigation/slow.sjs b/docshell/test/navigation/slow.sjs
new file mode 100644
index 0000000000..6e9d8b570e
--- /dev/null
+++ b/docshell/test/navigation/slow.sjs
@@ -0,0 +1,16 @@
+function handleRequest(request, response) {
+ response.processAsync();
+
+ let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ timer.init(
+ function() {
+ response.finish();
+ },
+ 5000,
+ Ci.nsITimer.TYPE_ONE_SHOT
+ );
+
+ response.setStatusLine(null, 200, "OK");
+ response.setHeader("Content-Type", "text/plain", false);
+ response.write("Start of the content.");
+}
diff --git a/docshell/test/navigation/test_aboutblank_change_process.html b/docshell/test/navigation/test_aboutblank_change_process.html
new file mode 100644
index 0000000000..61325570f3
--- /dev/null
+++ b/docshell/test/navigation/test_aboutblank_change_process.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<head>
+ <meta charset="utf-8">
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css" />
+</head>
+<script>
+// Open a window and navigate it from https://example.com to about:blank
+// With fission, we should switch processes and about:blank should load in
+// the same process as this test page.
+// This is a crash test.
+add_task(async function test_aboutblank_change_process() {
+ let exampleLoaded = new Promise(resolve => {
+ function onMessage(event) {
+ if (event.data == "body-loaded") {
+ window.removeEventListener("message", onMessage);
+ resolve();
+ }
+ }
+ window.addEventListener("message", onMessage);
+ });
+ let win = window.open();
+ win.location = "https://example.com/tests/docshell/test/navigation/file_tell_opener.html";
+ await exampleLoaded;
+
+ win.location = "about:blank";
+
+ // A crash happens somewhere here when about:blank does not go via
+ // DocumentChannel with fission enabled
+
+ // Wait for about:blank to load in this process
+ await SimpleTest.promiseWaitForCondition(() => {
+ try {
+ return win.location.href == "about:blank";
+ } catch (e) {
+ // While the `win` still has example.com page loaded, `win.location` will
+ // be a cross origin object and querying win.location.href will throw a
+ // SecurityError. Return false as long as this is the case.
+ return false;
+ }
+ })
+
+ ok(true, "We did not crash");
+ win.close();
+});
+</script>
diff --git a/docshell/test/navigation/test_beforeunload_and_bfcache.html b/docshell/test/navigation/test_beforeunload_and_bfcache.html
new file mode 100644
index 0000000000..6bb958c6c6
--- /dev/null
+++ b/docshell/test/navigation/test_beforeunload_and_bfcache.html
@@ -0,0 +1,97 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Loading a page from BFCache and firing beforeunload on the current page</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ /*
+ * This is a simple test to ensure beforeunload is fired on the current page
+ * when restoring a page from bfcache.
+ * (1) The controller page opens a new window. Another page is loaded there
+ * and session history is navigated back to check whether bfcache is
+ * enabled. If not, close message is sent and the opened window closes
+ * and the test ends.
+ * (2) beforeunload event listener is added to the page and history.forward()
+ * is called. The event listener should send a message to the controller
+ * page.
+ * (3) Close message is sent to close the opened window and the test finishes.
+ */
+
+ var pageshowCount = 0;
+ var gotBeforeUnload = false;
+ var bfcacheDisabled = false;
+
+
+ function executeTest() {
+ var bc = new BroadcastChannel("beforeunload_and_bfcache");
+ bc.onmessage = function(event) {
+ if (event.data.type == "pageshow") {
+ ++pageshowCount;
+ if (pageshowCount == 1) {
+ bc.postMessage("nextpage");
+ } else if (pageshowCount == 2) {
+ bc.postMessage("back");
+ } else if (pageshowCount == 3) {
+ if (!event.data.persisted) {
+ ok(true, "BFCache not enabled");
+ bfcacheDisabled = true;
+ bc.postMessage("close");
+ return;
+ }
+ bc.postMessage("forward");
+ } else if (pageshowCount == 4) {
+ ok(event.data.persisted, "Should have loaded a page from bfcache.");
+ bc.postMessage("close");
+ }
+ } else if (event.data == "beforeunload") {
+ gotBeforeUnload = true;
+ } else if (event.data == "closed") {
+ isnot(bfcacheDisabled, gotBeforeUnload,
+ "Either BFCache shouldn't be enabled or a beforeunload event should have been fired.");
+ bc.close();
+ SimpleTest.finish();
+ }
+ };
+
+ function runTest() {
+ SpecialPowers.pushPrefEnv({"set": [["docshell.shistory.bfcache.allow_unload_listeners", false]]},
+ function() {
+ window.open("file_beforeunload_and_bfcache.html", "", "noopener");
+ }
+ );
+ }
+ runTest();
+ }
+
+ if (isXOrigin) {
+ // Bug 1746646: Make mochitests work with TCP enabled (cookieBehavior = 5)
+ // Acquire storage access permission here so that the BroadcastChannel used to
+ // communicate with the opened windows works in xorigin tests. Otherwise,
+ // the iframe containing this page is isolated from first-party storage access,
+ // which isolates BroadcastChannel communication.
+ SpecialPowers.wrap(document).notifyUserGestureActivation();
+ SpecialPowers.addPermission("storageAccessAPI", true, window.location.href).then(() => {
+ SpecialPowers.wrap(document).requestStorageAccess().then(() => {
+ SpecialPowers.pushPrefEnv({
+ set: [["privacy.partition.always_partition_third_party_non_cookie_storage", false]]
+ }).then(() => {
+ executeTest();
+ });
+ });
+ });
+ } else {
+ executeTest();
+ }
+
+ </script>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_blockBFCache.html b/docshell/test/navigation/test_blockBFCache.html
new file mode 100644
index 0000000000..5989f633e9
--- /dev/null
+++ b/docshell/test/navigation/test_blockBFCache.html
@@ -0,0 +1,294 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Blocking pages from entering BFCache</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="">
+<script>
+
+const getUserMediaPrefs = {
+ set: [
+ ["media.devices.insecure.enabled", true],
+ ["media.getusermedia.insecure.enabled", true],
+ ["media.navigator.permission.disabled", true],
+ ],
+};
+const msePrefs = {
+ set: [
+ ["media.mediasource.enabled", true],
+ ["media.audio-max-decode-error", 0],
+ ["media.video-max-decode-error", 0],
+ ]
+};
+
+const blockBFCacheTests = [
+ {
+ name: "Request",
+ test: () => {
+ return new Promise((resolve) => {
+ const xhr = new XMLHttpRequest();
+ xhr.open("GET", "slow.sjs");
+ xhr.addEventListener("progress", () => { resolve(xhr); }, { once: true });
+ xhr.send();
+ });
+ },
+ },
+ {
+ name: "Background request",
+ test: () => {
+ return new Promise((resolve) => {
+ const xhr = new XMLHttpRequest();
+ xhr.open("GET", "slow.sjs");
+ xhr.addEventListener("readystatechange", () => { if (xhr.readyState == xhr.HEADERS_RECEIVED) resolve(xhr); });
+ xhr.send();
+ });
+ },
+ },
+ {
+ name: "getUserMedia",
+ prefs: getUserMediaPrefs,
+ test: () => {
+ return navigator.mediaDevices.getUserMedia({ audio: true, fake: true });
+ },
+ },
+ {
+ name: "RTCPeerConnection",
+ test: () => {
+ let pc = new RTCPeerConnection();
+ return pc.createOffer();
+ },
+ },
+ {
+ name: "MSE",
+ prefs: msePrefs,
+ test: () => {
+ const ms = new MediaSource();
+ const el = document.createElement("video");
+ el.src = URL.createObjectURL(ms);
+ el.preload = "auto";
+ return el;
+ },
+ },
+ {
+ name: "WebSpeech",
+ test: () => {
+ return new Promise((resolve) => {
+ const utterance = new SpeechSynthesisUtterance('bfcache');
+ utterance.lang = 'it-IT-noend';
+ utterance.addEventListener('start', () => { resolve(utterance); })
+ speechSynthesis.speak(utterance);
+ });
+ },
+ },
+ {
+ name: "WebVR",
+ prefs: {
+ set: [
+ ["dom.vr.test.enabled", true],
+ ["dom.vr.puppet.enabled", true],
+ ["dom.vr.require-gesture", false],
+ ],
+ },
+ test: () => {
+ return navigator.requestVRServiceTest();
+ }
+ },
+];
+
+if (SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ blockBFCacheTests.push({
+ name: "Loading OOP iframe",
+ test: () => {
+ return new Promise((resolve) => {
+ const el = document.body.appendChild(document.createElement("iframe"));
+ el.id = "frame";
+ addEventListener("message", ({ data }) => {
+ if (data == "onload") {
+ resolve();
+ }
+ });
+ el.src = "https://example.com/tests/docshell/test/navigation/iframe_slow_onload.html";
+ });
+ },
+ waitForDone: () => {
+ SimpleTest.requestFlakyTimeout("Test has a loop in an onload handler that runs for 5000ms, we need to make sure the loop is done before moving to the next test.");
+ return new Promise(resolve => {
+ setTimeout(resolve, 5000);
+ });
+ },
+ });
+}
+
+const dontBlockBFCacheTests = [
+ {
+ name: "getUserMedia",
+ prefs: getUserMediaPrefs,
+ test: () => {
+ return navigator.mediaDevices.getUserMedia({ video: true, fake: true }).then(stream => {
+ stream.getTracks().forEach(track => track.stop());
+ return stream;
+ });
+ },
+ },
+/*
+ Disabled because MediaKeys rely on being destroyed by the CC before they
+ notify their window, so the test would intermittently fail depending on
+ when the CC runs.
+
+ {
+ name: "MSE",
+ prefs: msePrefs,
+ test: () => {
+ return new Promise((resolve) => {
+ const ms = new MediaSource();
+ const el = document.createElement("video");
+ ms.addEventListener("sourceopen", () => { resolve(el) }, { once: true });
+ el.src = URL.createObjectURL(ms);
+ el.preload = "auto";
+ }).then(el => {
+ el.src = "";
+ return el;
+ });
+ },
+ },
+*/
+];
+
+
+
+function executeTest() {
+
+ let bc = new BroadcastChannel("bfcache_blocking");
+
+ function promiseMessage(type) {
+ return new Promise((resolve, reject) => {
+ bc.addEventListener("message", (e) => {
+ if (e.data.type == type) {
+ resolve(e.data);
+ }
+ }, { once: true });
+ });
+ }
+
+ function promisePageShow(shouldBePersisted) {
+ return promiseMessage("pageshow").then(data => data.persisted == shouldBePersisted);
+ }
+
+ function promisePageShowFromBFCache(e) {
+ return promisePageShow(true);
+ }
+
+ function promisePageShowNotFromBFCache(e) {
+ return promisePageShow(false);
+ }
+
+ function runTests(testArray, shouldBlockBFCache) {
+ for (const { name, prefs = {}, test, waitForDone } of testArray) {
+ add_task(async function() {
+ await SpecialPowers.pushPrefEnv(prefs, async function() {
+ // Load a mostly blank page that we can communicate with over
+ // BroadcastChannel (though it will close the BroadcastChannel after
+ // receiving the next "load" message, to avoid blocking BFCache).
+ let loaded = promisePageShowNotFromBFCache();
+ window.open("file_blockBFCache.html", "", "noopener");
+ await loaded;
+
+ // Load the same page with a different URL.
+ loaded = promisePageShowNotFromBFCache();
+ bc.postMessage({ message: "load", url: `file_blockBFCache.html?${name}_${shouldBlockBFCache}` });
+ await loaded;
+
+ // Run test script in the second page.
+ bc.postMessage({ message: "runScript", fun: test.toString() });
+ await promiseMessage("runScriptDone");
+
+ // Go back to the first page (this should just come from the BFCache).
+ let goneBack = promisePageShowFromBFCache();
+ bc.postMessage({ message: "back" });
+ await goneBack;
+
+ // Go forward again to the second page and check that it does/doesn't come
+ // from the BFCache.
+ let goneForward = promisePageShow(!shouldBlockBFCache);
+ bc.postMessage({ message: "forward" });
+ let result = await goneForward;
+ ok(result, `Page ${shouldBlockBFCache ? "should" : "should not"} have been blocked from going into the BFCache (${name})`);
+
+ // If the test will keep running after navigation, then we need to make
+ // sure it's completely done before moving to the next test, to avoid
+ // interfering with any following tests. If waitForDone is defined then
+ // it'll return a Promise that we can use to wait for the end of the
+ // test.
+ if (waitForDone) {
+ await waitForDone();
+ }
+
+ // Do a similar test, but replace the bfcache test page with a new page,
+ // not a page coming from the session history.
+
+ // Load the same page with a different URL.
+ loaded = promisePageShowNotFromBFCache();
+ bc.postMessage({ message: "load", url: `file_blockBFCache.html?p2_${name}_${shouldBlockBFCache}` });
+ await loaded;
+
+ // Run the test script.
+ bc.postMessage({ message: "runScript", fun: test.toString() });
+ await promiseMessage("runScriptDone");
+
+ // Load a new page.
+ loaded = promisePageShowNotFromBFCache();
+ bc.postMessage({ message: "load", url: "file_blockBFCache.html" });
+ await loaded;
+
+ // Go back to the previous page and check that it does/doesn't come
+ // from the BFCache.
+ goneBack = promisePageShow(!shouldBlockBFCache);
+ bc.postMessage({ message: "back" });
+ result = await goneBack;
+ ok(result, `Page ${shouldBlockBFCache ? "should" : "should not"} have been blocked from going into the BFCache (${name})`);
+
+ if (waitForDone) {
+ await waitForDone();
+ }
+
+ bc.postMessage({ message: "close" });
+
+ SpecialPowers.popPrefEnv();
+ });
+ });
+ }
+ }
+
+ // If Fission is disabled, the pref is no-op.
+ SpecialPowers.pushPrefEnv({set: [["fission.bfcacheInParent", true]]}, () => {
+ runTests(blockBFCacheTests, true);
+ runTests(dontBlockBFCacheTests, false);
+ });
+}
+
+if (isXOrigin) {
+ // Bug 1746646: Make mochitests work with TCP enabled (cookieBehavior = 5)
+ // Acquire storage access permission here so that the BroadcastChannel used to
+ // communicate with the opened windows works in xorigin tests. Otherwise,
+ // the iframe containing this page is isolated from first-party storage access,
+ // which isolates BroadcastChannel communication.
+ SpecialPowers.wrap(document).notifyUserGestureActivation();
+ SpecialPowers.addPermission("storageAccessAPI", true, window.location.href).then(() => {
+ SpecialPowers.wrap(document).requestStorageAccess().then(() => {
+ SpecialPowers.pushPrefEnv({
+ set: [["privacy.partition.always_partition_third_party_non_cookie_storage", false]]
+ }).then(() => {
+ executeTest();
+ });
+ });
+ });
+} else {
+ executeTest();
+}
+
+</script>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug1300461.html b/docshell/test/navigation/test_bug1300461.html
new file mode 100644
index 0000000000..22783c07c2
--- /dev/null
+++ b/docshell/test/navigation/test_bug1300461.html
@@ -0,0 +1,70 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+ <title>Test for Bug 1300461</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1300461">Mozilla Bug 1300461</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+
+ let chromeScript = null;
+ if (SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ chromeScript = SpecialPowers.loadChromeScript(() => {
+ /* eslint-env mozilla/chrome-script */
+ function doSend(message, fn) {
+ try {
+ sendAsyncMessage(message, {success: true, value: fn()});
+ } catch(_) {
+ sendAsyncMessage(message, {success: false});
+ }
+ }
+
+ addMessageListener("requestedIndex", (id) => {
+ doSend("requestedIndex", () => {
+ let shistory = BrowsingContext.get(id).top.sessionHistory;
+ return shistory.requestedIndex;
+ })
+ });
+ });
+ }
+
+ async function getSHRequestedIndex(browsingContextId) {
+ let p = chromeScript.promiseOneMessage("requestedIndex");
+ chromeScript.sendAsyncMessage("requestedIndex", browsingContextId);
+ let result = await p;
+ ok(result.success, "Got requested index from parent");
+ return result.value;
+ }
+
+ var testCount = 0;
+
+ SimpleTest.waitForExplicitFinish();
+
+ var testWindow;
+ function runTest() {
+ testWindow = window.open("file_bug1300461.html", "", "width=360,height=480");
+ testWindow.onunload = function() { }; // to prevent bfcache
+ }
+
+ function finishTest() {
+ if (chromeScript) {
+ chromeScript.destroy();
+ }
+ testWindow.close();
+ SimpleTest.finish();
+ }
+
+ </script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug1326251.html b/docshell/test/navigation/test_bug1326251.html
new file mode 100644
index 0000000000..3c951729e6
--- /dev/null
+++ b/docshell/test/navigation/test_bug1326251.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+ <title>Test for Bug 1326251</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1326251">Mozilla Bug 1326251</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+
+ var testCount = 0;
+
+ var bc = new BroadcastChannel("file_bug1326251");
+ bc.onmessage = function(event) {
+ if (event.data == "requestNextTest") {
+ bc.postMessage({ nextTest: testCount++ });
+ } else if (event.data.type == "is") {
+ is(event.data.value1, event.data.value2, event.data.message);
+ } else if (event.data.type == "ok") {
+ ok(event.data.value, event.data.message);
+ } else if (event.data == "finishTest") {
+ SimpleTest.finish();
+ }
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+ function runTest() {
+ // If Fission is disabled, the pref is no-op.
+ SpecialPowers.pushPrefEnv({set: [["fission.bfcacheInParent", true]]}, () => {
+ window.open("file_bug1326251.html", "", "width=360,height=480,noopener");
+ });
+ }
+
+ </script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug1364364.html b/docshell/test/navigation/test_bug1364364.html
new file mode 100644
index 0000000000..04decf6815
--- /dev/null
+++ b/docshell/test/navigation/test_bug1364364.html
@@ -0,0 +1,65 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1364364
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1364364</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1364364 **/
+ let testWin, testDoc;
+ async function test() {
+ SimpleTest.waitForExplicitFinish();
+ if (SpecialPowers.Services.appinfo.fissionAutostart) {
+ // This test relies on the possibility to modify the bfcached document.
+ // The new implementation is more restricted and thus works only
+ // when the bfcached browsing context is the only top level one
+ // in the browsing context group.
+ ok(true, "This test is for the old bfcache implementation only.");
+ SimpleTest.finish();
+ return;
+ }
+ testWin = window.open("file_bug1364364-1.html");
+ await waitForLoad(testWin);
+ testDoc = testWin.document;
+
+ // file_bug1364364-1.html will load a few dynamic iframes and then navigate
+ // top browsing context to file_bug1364364-2.html, which will postMessage
+ // back.
+ }
+
+ function waitForLoad(win) {
+ return new Promise(r => win.addEventListener("load", r, { once: true}));
+ }
+
+ window.addEventListener("message", async function(msg) {
+ if (msg.data == "navigation-done") {
+ is(testWin.history.length, 6, "check history.length");
+
+ // Modify a document in bfcache should cause the cache being dropped tho
+ // RemoveFromBFCacheAsync.
+ testDoc.querySelector("#content").textContent = "modified";
+ await new Promise(r => setTimeout(r, 0));
+
+ is(testWin.history.length, 2, "check history.length after bfcache dropped");
+ testWin.close();
+ SimpleTest.finish();
+ }
+ });
+
+ </script>
+</head>
+<body onload="test();">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1364364">Mozilla Bug 1364364</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug1375833.html b/docshell/test/navigation/test_bug1375833.html
new file mode 100644
index 0000000000..c2a7750a4e
--- /dev/null
+++ b/docshell/test/navigation/test_bug1375833.html
@@ -0,0 +1,131 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1375833
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1375833</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ SimpleTest.waitForExplicitFinish();
+
+ /**
+ * Test for Bug 1375833. It tests for 2 things in a normal reload -
+ * 1. Static frame history should not be dropped.
+ * 2. In a reload, docshell would parse the reloaded root document and
+ * genearate new child docshells, and then use the child offset
+ */
+
+ let testWin = window.open("file_bug1375833.html");
+ let count = 0;
+ let webNav, shistory;
+ let frameDocShellId;
+ let chromeScript = null;
+ if (SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ chromeScript = SpecialPowers.loadChromeScript(() => {
+ /* eslint-env mozilla/chrome-script */
+ function doSend(message, fn) {
+ try {
+ sendAsyncMessage(message, {success: true, value: fn()});
+ } catch(_) {
+ sendAsyncMessage(message, {success: false});
+ }
+ }
+
+ addMessageListener("test1", id => {
+ doSend("test1", () => {
+ let sessionHistory = BrowsingContext.get(id).top.sessionHistory;
+ let entry = sessionHistory.getEntryAtIndex(sessionHistory.index);
+ let frameEntry = entry.GetChildAt(0);
+ return String(frameEntry.docshellID);
+ })
+ });
+ });
+ }
+
+ window.addEventListener("message", async e => {
+ switch (count++) {
+ case 0:
+ ok(e.data.endsWith("file_bug1375833-frame2.html"), "check location");
+
+ webNav = SpecialPowers.wrap(testWin)
+ .docShell
+ .QueryInterface(SpecialPowers.Ci.nsIWebNavigation);
+ shistory = webNav.sessionHistory;
+ is(shistory.count, 2, "check history length");
+ is(shistory.index, 1, "check history index");
+
+ frameDocShellId = String(getFrameDocShell().historyID);
+ ok(frameDocShellId, "sanity check for docshell ID");
+
+ testWin.location.reload();
+ break;
+ case 1:
+ ok(e.data.endsWith("file_bug1375833-frame2.html"), "check location");
+ is(shistory.count, 4, "check history length");
+ is(shistory.index, 3, "check history index");
+
+ let newFrameDocShellId = String(getFrameDocShell().historyID);
+ ok(newFrameDocShellId, "sanity check for docshell ID");
+ is(newFrameDocShellId, frameDocShellId, "check docshell ID remains after reload");
+
+ if (!SpecialPowers.Services.appinfo.sessionHistoryInParent) {
+ let entry = shistory.legacySHistory.getEntryAtIndex(shistory.index);
+ let frameEntry = entry.GetChildAt(0);
+ is(String(frameEntry.docshellID), frameDocShellId, "check newly added shentry uses the same docshell ID");
+ } else {
+ let p = chromeScript.promiseOneMessage("test1");
+ chromeScript.sendAsyncMessage("test1", SpecialPowers.wrap(testWin).browsingContext.id);
+ let result = await p;
+ ok(result.success, "legacySHistory worked around ok");
+ is(result.value, frameDocShellId, "check newly added shentry uses the same docshell ID");
+ }
+
+ webNav.goBack();
+ break;
+ case 2:
+ ok(e.data.endsWith("file_bug1375833-frame1.html"), "check location");
+ is(shistory.count, 4, "check history length");
+ is(shistory.index, 2, "check history index");
+
+ webNav.goBack();
+ break;
+ case 3:
+ ok(e.data.endsWith("file_bug1375833-frame2.html"), "check location");
+ is(shistory.count, 4, "check history length");
+ is(shistory.index, 1, "check history index");
+
+ webNav.goBack();
+ break;
+ case 4:
+ ok(e.data.endsWith("file_bug1375833-frame1.html"), "check location");
+ is(shistory.count, 4, "check history length");
+ is(shistory.index, 0, "check history index");
+
+ if (chromeScript) {
+ chromeScript.destroy();
+ }
+ testWin.close();
+ SimpleTest.finish();
+ }
+ });
+
+ function getFrameDocShell() {
+ return SpecialPowers.wrap(testWin.window[0]).docShell;
+ }
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1375833">Mozilla Bug 1375833</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug1379762.html b/docshell/test/navigation/test_bug1379762.html
new file mode 100644
index 0000000000..eda3b539a5
--- /dev/null
+++ b/docshell/test/navigation/test_bug1379762.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+ <title>Test for Bug 1379762</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1379762">Mozilla Bug 1379762</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ /**
+ * - This page opens new window
+ * - new window sends 'init' msg
+ * - onload() in new window sends 'increment_loadCount' msg
+ * - onpageshow() in new window sends 'increment_testCount' msg
+ * - This page sends 'forward_back' msg
+ * - onpageshow() in new window 'increment_testCount'
+ * - This page sends 'finish_test' msg
+ * - onpageshow() in new window sends 'finished' msg
+ */
+ var testCount = 0; // Used by the test files.
+ var loadCount = 0;
+ var goneBack = false;
+ var bc = new BroadcastChannel("bug1379762");
+ bc.onmessage = (messageEvent) => {
+ let message = messageEvent.data;
+ if (message == "init") {
+ is(testCount, 0, "new window should only be loaded once; otherwise the loadCount variable makes no sense");
+ } else if (message == "increment_loadCount") {
+ loadCount++;
+ is(loadCount, 1, "Should only get one load")
+ } else if (message == 'increment_testCount') {
+ testCount++;
+ if (testCount == 1) {
+ bc.postMessage("forward_back");
+ goneBack = true;
+ } else if (testCount == 2) {
+ ok(goneBack, "We had a chance to navigate backwards and forwards in the new window to test BFCache");
+ bc.postMessage("finish_test");
+ }
+ } else if (message == "finished") {
+ bc.close();
+ SimpleTest.finish();
+ }
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+ function runTest() {
+ // If Fission is disabled, the pref is no-op.
+ SpecialPowers.pushPrefEnv({set: [["fission.bfcacheInParent", true]]}, () => {
+ window.open("file_bug1379762-1.html", "", "width=360,height=480,noopener");
+ });
+ }
+
+ </script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug13871.html b/docshell/test/navigation/test_bug13871.html
new file mode 100644
index 0000000000..0532bc7b56
--- /dev/null
+++ b/docshell/test/navigation/test_bug13871.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="NavigationUtils.js"></script>
+ <style type="text/css">
+ iframe { width: 90%; height: 50px; }
+ </style>
+<script>
+async function runTest() {
+ navigateByLocation(window0.frames[0]);
+ navigateByOpen("window1_child0");
+ navigateByForm("window2_child0");
+ navigateByHyperlink("window3_child0");
+
+ await waitForFinishedFrames(4);
+
+ isInaccessible(window0.frames[0], "Should not be able to navigate off-domain frame by setting location.");
+ isInaccessible(window1.frames[0], "Should not be able to navigate off-domain frame by calling window.open.");
+ isInaccessible(window2.frames[0], "Should not be able to navigate off-domain frame by submitting form.");
+ isInaccessible(window3.frames[0], "Should not be able to navigate off-domain frame by targeted hyperlink.");
+
+ window0.close();
+ window1.close();
+ window2.close();
+ window3.close();
+
+ await cleanupWindows();
+ SimpleTest.finish();
+}
+
+// Because our open()'d windows are cross-origin, we can't wait for onload.
+// We instead wait for a postMessage from parent.html.
+var windows = new Map();
+addEventListener("message", function windowLoaded(evt) {
+ // Because window.open spins the event loop in order to open new windows,
+ // we might receive the "ready" message before we call waitForLoad.
+ // In that case, windows won't contain evt.source and we just note that the
+ // window is ready. Otherwise, windows contains the "resolve" function for
+ // that window's promise and we just have to call it.
+ if (windows.has(evt.source)) {
+ windows.get(evt.source)();
+ } else {
+ windows.set(evt.source, true);
+ }
+});
+
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+var window0 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/parent.html", "window0", "width=10,height=10");
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+var window1 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/parent.html", "window1", "width=10,height=10");
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+var window2 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/parent.html", "window2", "width=10,height=10");
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+var window3 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/parent.html", "window3", "width=10,height=10");
+
+function waitForLoad(w) {
+ return new Promise(function(resolve, reject) {
+ // If we already got the "ready" message, resolve immediately.
+ if (windows.has(w)) {
+ resolve();
+ } else {
+ windows.set(w, resolve);
+ }
+ });
+}
+
+Promise.all([ waitForLoad(window0),
+ waitForLoad(window1),
+ waitForLoad(window2),
+ waitForLoad(window3) ])
+ .then(runTest);
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=13871">Mozilla Bug 13871</a>
+<pre id="test">
+<script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug145971.html b/docshell/test/navigation/test_bug145971.html
new file mode 100644
index 0000000000..ffad27a9c3
--- /dev/null
+++ b/docshell/test/navigation/test_bug145971.html
@@ -0,0 +1,29 @@
+<html>
+ <head>
+ <script>
+ let pass = false;
+ let initialLoad = false;
+ var bc = new BroadcastChannel("bug145971");
+ function checkNavigationTypeEquals2() {
+ if (performance.navigation.type == 2) {
+ pass = true;
+ }
+ testDone();
+ }
+
+ function testDone() {
+ bc.postMessage({result: pass});
+ bc.close();
+ window.close();
+ }
+
+ function test() {
+ window.onpageshow = checkNavigationTypeEquals2;
+ window.location.href = 'goback.html';
+ }
+ </script>
+ </head>
+ <body onload="setTimeout(test, 0);">
+ Testing bug 145971.
+ </body>
+</html>
diff --git a/docshell/test/navigation/test_bug1536471.html b/docshell/test/navigation/test_bug1536471.html
new file mode 100644
index 0000000000..f37aedba21
--- /dev/null
+++ b/docshell/test/navigation/test_bug1536471.html
@@ -0,0 +1,75 @@
+
+<!DOCTYPE HTML>
+<html>
+ <!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=1536471
+ -->
+<head>
+ <title>Test for Bug 1536471</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="application/javascript">
+
+ let testWin;
+ async function test() {
+ // Open a new tab and load a document with an iframe inside
+ testWin = window.open("file_bug1536471.html");
+ await waitForLoad();
+ var iframe = testWin.document.getElementById("staticFrame");
+ is(testWin.history.length, 1, "Checking the number of session history entries when there is only one iframe");
+
+ // Navigate the iframe to different pages
+ await loadUriInFrame(iframe, "frame1.html");
+ is(testWin.history.length, 2, "Checking the number of session history entries after having navigated a single iframe 1 time");
+ await loadUriInFrame(iframe, "frame2.html");
+ is(testWin.history.length, 3, "Checking the number of session history entries after having navigated a single iframe 2 times");
+ await loadUriInFrame(iframe, "frame3.html");
+ is(testWin.history.length, 4, "Checking the number of session history entries after having navigated a single iframe 3 times");
+
+ // Reload the top document
+ testWin.location.reload(true);
+ await waitForLoad();
+ is(testWin.history.length, 1, "Checking the number of session history entries after reloading the top document");
+
+ testWin.close();
+ SimpleTest.finish();
+ }
+
+ async function waitForLoad() {
+ await new Promise(resolve => {
+ window.bodyOnLoad = function() {
+ setTimeout(resolve, 0);
+ window.bodyOnLoad = undefined;
+ };
+ });
+ }
+
+ async function iframeOnload(frame) {
+ return new Promise(resolve => {
+ frame.addEventListener("load", () => {
+ setTimeout(resolve, 0);
+ }, {once: true});
+ });
+ }
+
+ async function loadUriInFrame(frame, uri) {
+ let onloadPromise = iframeOnload(frame);
+ frame.src = uri;
+ await onloadPromise;
+ }
+ </script>
+</head>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1536471">Mozilla Bug </a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+<body onload="test()">
+</body>
+</html>
+
diff --git a/docshell/test/navigation/test_bug1583110.html b/docshell/test/navigation/test_bug1583110.html
new file mode 100644
index 0000000000..f1c1b65e4d
--- /dev/null
+++ b/docshell/test/navigation/test_bug1583110.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>test bug 1583110</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+ var bc = new BroadcastChannel("bug1583110");
+ var pageshowCount = 0;
+ bc.onmessage = function(event) {
+ ok(event.data.type == "pageshow");
+ ++pageshowCount;
+ if (pageshowCount == 1) {
+ bc.postMessage("loadnext");
+ } else if (pageshowCount == 2) {
+ bc.postMessage("back");
+ } else {
+ ok(event.data.persisted, "Should have persisted the first page");
+ bc.close();
+ SimpleTest.finish();
+ }
+ }
+
+ function test() {
+ window.open("file_bug1583110.html", "", "noopener");
+ }
+ </script>
+</head>
+<body onload="test()">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug1609475.html b/docshell/test/navigation/test_bug1609475.html
new file mode 100644
index 0000000000..4dbe7d17d6
--- /dev/null
+++ b/docshell/test/navigation/test_bug1609475.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+ <title>Test for Bug 1609475</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1609475">Mozilla Bug 1609475</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ var testWindow;
+ function runTest() {
+ testWindow = window.open("file_bug1609475.html", "", "width=360,height=480");
+ testWindow.onunload = function() { }; // to prevent bfcache
+ }
+
+ function finishTest() {
+ testWindow.close();
+ SimpleTest.finish();
+ }
+
+ </script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug1699721.html b/docshell/test/navigation/test_bug1699721.html
new file mode 100644
index 0000000000..687c5306cf
--- /dev/null
+++ b/docshell/test/navigation/test_bug1699721.html
@@ -0,0 +1,110 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script type="text/javascript">
+ add_task(async function() {
+ let popup = window.open("blank.html");
+
+ info("opened popup");
+ await new Promise(resolve => {
+ popup.addEventListener("load", resolve, { once: true });
+ });
+
+ info("popup blank.html loaded");
+ let tell_opener = new URL("file_tell_opener.html", location.href);
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ let xorigin_url = new URL(tell_opener.pathname, "http://example.com");
+
+ let resolveStartedUnload;
+ let startedUnload = new Promise(resolve => {
+ resolveStartedUnload = resolve;
+ });
+ let didFinishUnload = false;
+
+ let finishUnload = false;
+ popup.addEventListener("unload", function() {
+ resolveStartedUnload();
+ try {
+ // Spin a nested event loop in unload until we set `finishUnload`.
+ SpecialPowers.Services.tm.spinEventLoopUntil(
+ "Test(test_switch_back_nested.html)", () => finishUnload);
+ } finally {
+ info("exiting from unload nested event loop...");
+ didFinishUnload = true;
+ }
+ });
+
+ info("wait for message from popup");
+ let messagePromise = new Promise(resolve => {
+ addEventListener("message", evt => {
+ resolve();
+ }, { once: true });
+ });
+ popup.location = xorigin_url.href;
+ await messagePromise;
+
+ info("popup loaded, ensuring we're in unload");
+ await startedUnload;
+ is(didFinishUnload, false, "unload shouldn't have finished");
+
+ let switchStarted = SpecialPowers.spawnChrome([], async () => {
+ await new Promise(resolve => {
+ async function observer(subject, topic) {
+ is(topic, "http-on-examine-response");
+
+ let uri = subject.QueryInterface(Ci.nsIChannel).URI;
+ if (!uri.filePath.endsWith("file_tell_opener.html")) {
+ return;
+ }
+
+ Services.obs.removeObserver(observer, "http-on-examine-response");
+
+ // spin the event loop a few times to ensure we resolve after the process switch
+ for (let i = 0; i < 10; ++i) {
+ await new Promise(res => Services.tm.dispatchToMainThread(res));
+ }
+
+ info("resolving!");
+ resolve();
+ }
+ Services.obs.addObserver(observer, "http-on-examine-response");
+ });
+ });
+
+ info("Navigating back to the current process");
+ await SpecialPowers.spawn(popup, [tell_opener.href], (href) => {
+ content.location.href = href;
+ });
+
+ let messagePromise2 = new Promise(resolve => {
+ addEventListener("message", evt => {
+ resolve();
+ }, { once: true });
+ });
+
+ info("Waiting for the process switch to start");
+ await switchStarted;
+
+ // Finish unloading, and wait for the unload to complete
+ is(didFinishUnload, false, "unload shouldn't be finished");
+ finishUnload = true;
+ await new Promise(resolve => setTimeout(resolve, 0));
+ is(didFinishUnload, true, "unload should be finished");
+
+ info("waiting for navigation to complete");
+ await messagePromise2;
+
+ info("closing popup");
+ popup.close();
+
+ ok(true, "Didn't crash");
+ });
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug1706090.html b/docshell/test/navigation/test_bug1706090.html
new file mode 100644
index 0000000000..293148b9c6
--- /dev/null
+++ b/docshell/test/navigation/test_bug1706090.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1706090</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ var bc = new BroadcastChannel("bug1706090");
+ var pageshowCount = 0;
+ bc.onmessage = function(event) {
+ if (event.data.type == "pageshow") {
+ ++pageshowCount;
+ if (pageshowCount == 1) {
+ is(event.data.persisted, false, "Shouldn't have persisted the initial load.");
+ bc.postMessage("sameOrigin");
+ } else if (pageshowCount == 2) {
+ bc.postMessage("back");
+ } else if (pageshowCount == 3) {
+ is(event.data.persisted, false, "Shouldn't have persisted same origin load.");
+ bc.postMessage("crossOrigin");
+ } else if (pageshowCount == 4) {
+ is(event.data.persisted, true, "Should have persisted cross origin load.");
+ bc.postMessage("sameSite");
+ } else if (pageshowCount == 5) {
+ is(event.data.persisted, false, "Shouldn't have persisted same site load.");
+ bc.postMessage("close");
+ }
+ } else if (event.data == "closed") {
+ bc.close();
+ SimpleTest.finish();
+ }
+ };
+
+ function runTest() {
+ SpecialPowers.pushPrefEnv({set: [["docshell.shistory.bfcache.allow_unload_listeners", true]]}, () => {
+ window.open("file_bug1706090.html", "", "noopener");
+ });
+ }
+ </script>
+</head>
+<body onload="runTest()">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug1745638.html b/docshell/test/navigation/test_bug1745638.html
new file mode 100644
index 0000000000..594c464da3
--- /dev/null
+++ b/docshell/test/navigation/test_bug1745638.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>bug 1745638</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ // This test triggers an assertion in the old session history
+ // implementation.
+ SimpleTest.expectAssertions(0, 1);
+
+ SimpleTest.waitForExplicitFinish();
+ var testWindow;
+ var loadCount = 0;
+ function test() {
+ testWindow = window.open("file_bug1745638.html");
+ }
+
+ function pageLoaded() {
+ ++loadCount;
+ is(testWindow.document.getElementById('testFrame').contentDocument.body.innerHTML,
+ "passed",
+ "Iframe's textual content should be 'passed'.");
+ if (loadCount == 1) {
+ testWindow.history.go(0);
+ } else {
+ testWindow.close();
+ SimpleTest.finish();
+ }
+ }
+
+ </script>
+</head>
+<body onload="test()">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug1747019.html b/docshell/test/navigation/test_bug1747019.html
new file mode 100644
index 0000000000..c7995737df
--- /dev/null
+++ b/docshell/test/navigation/test_bug1747019.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test session history and caching</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ var win;
+ var loadCount = 0;
+ var initialContent;
+ // The test loads first a page in a new window, then using
+ // form submission loads another page and then using form submission
+ // again loads third page. That page triggers history.go(-1).
+ // The second page is loaded now again and should have the same content
+ // as it had before.
+ function test() {
+ win = window.open("cache_control_max_age_3600.sjs?initial");
+ window.onmessage = (e) => {
+ is(e.data, "loaded", "Should get load message 'loaded'");
+ ++loadCount;
+ if (loadCount == 1) {
+ win.document.forms[0].submit();
+ } else if (loadCount == 2) {
+ initialContent = win.document.body.textContent;
+ info("The initial content is [" + initialContent + "].");
+ win.document.forms[0].submit();
+ } else if (loadCount == 3) {
+ let newContent = win.document.body.textContent;
+ info("The new content is [" + newContent + "].");
+ win.close();
+ is(initialContent, newContent, "Should have loaded the page from cache.");
+ SimpleTest.finish();
+ } else {
+ ok(false, "Unexpected load count.");
+ }
+ }
+ }
+ </script>
+</head>
+<body onload="test()">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug1750973.html b/docshell/test/navigation/test_bug1750973.html
new file mode 100644
index 0000000000..9f87075b90
--- /dev/null
+++ b/docshell/test/navigation/test_bug1750973.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>The layout state restoration when reframing the root element</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+ function test() {
+ window.open("file_bug1750973.html");
+ }
+ </script>
+</head>
+<body onload="setTimeout(test)">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug1758664.html b/docshell/test/navigation/test_bug1758664.html
new file mode 100644
index 0000000000..662242e44a
--- /dev/null
+++ b/docshell/test/navigation/test_bug1758664.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1758664</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ function test() {
+ window.open("file_bug1758664.html");
+ }
+ </script>
+</head>
+<body onload="test()">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug270414.html b/docshell/test/navigation/test_bug270414.html
new file mode 100644
index 0000000000..0635e32888
--- /dev/null
+++ b/docshell/test/navigation/test_bug270414.html
@@ -0,0 +1,103 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="NavigationUtils.js"></script>
+ <style type="text/css">
+ iframe { width: 90%; height: 50px; }
+ </style>
+<script>
+/* eslint-disable no-useless-concat */
+/* global window0:true, window1:true, window2:true, window3:true */
+var headerHTML = "<html><head>" +
+ "<script src='/tests/SimpleTest/EventUtils.js'><\/script>" +
+ "<script src='NavigationUtils.js'><\/script>" +
+ "</head><body>";
+var footerHTML = "</body></html>";
+
+function testChild0() {
+ if (!window.window0) {
+ window0 = window.open("", "window0", "width=10,height=10");
+ window0.document.open();
+ // eslint-disable-next-line no-unsanitized/method
+ window0.document.write(headerHTML);
+ window0.document.write("<script>navigateByLocation(opener.frames[0])<\/script>");
+ // eslint-disable-next-line no-unsanitized/method
+ window0.document.write(footerHTML);
+ window0.document.close();
+ }
+}
+
+function testChild1() {
+ if (!window.window1) {
+ window1 = window.open("", "window1", "width=10,height=10");
+ window1.document.open();
+ // eslint-disable-next-line no-unsanitized/method
+ window1.document.write(headerHTML);
+ window1.document.write("<script>navigateByOpen('child1');<\/script>");
+ // eslint-disable-next-line no-unsanitized/method
+ window1.document.write(footerHTML);
+ window1.document.close();
+ }
+}
+
+function testChild2() {
+ if (!window.window2) {
+ window2 = window.open("", "window2", "width=10,height=10");
+ window2.document.open();
+ // eslint-disable-next-line no-unsanitized/method
+ window2.document.write(headerHTML);
+ window2.document.write("<script>navigateByForm('child2');<\/script>");
+ // eslint-disable-next-line no-unsanitized/method
+ window2.document.write(footerHTML);
+ window2.document.close();
+ }
+}
+
+function testChild3() {
+ if (!window.window3) {
+ window3 = window.open("", "window3", "width=10,height=10");
+ window3.document.open();
+ // eslint-disable-next-line no-unsanitized/method
+ window3.document.write(headerHTML);
+ window3.document.write("<script>navigateByHyperlink('child3');<\/script>");
+ // eslint-disable-next-line no-unsanitized/method
+ window3.document.write(footerHTML);
+ window3.document.close();
+ }
+}
+
+add_task(async function() {
+ await waitForFinishedFrames(4);
+
+ await isNavigated(frames[0], "Should be able to navigate on-domain opener's children by setting location.");
+ await isNavigated(frames[1], "Should be able to navigate on-domain opener's children by calling window.open.");
+ await isNavigated(frames[2], "Should be able to navigate on-domain opener's children by submitting form.");
+ await isNavigated(frames[3], "Should be able to navigate on-domain opener's children by targeted hyperlink.");
+
+ window0.close();
+ window1.close();
+ window2.close();
+ window3.close();
+
+ await cleanupWindows();
+});
+
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=270414">Mozilla Bug 270414</a>
+<div id="frames">
+<iframe onload="testChild0();" name="child0" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+<iframe onload="testChild1();" name="child1" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+<iframe onload="testChild2();" name="child2" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+<iframe onload="testChild3();" name="child3" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+</div>
+<pre id="test">
+<script type="text/javascript">
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug278916.html b/docshell/test/navigation/test_bug278916.html
new file mode 100644
index 0000000000..9e2335721e
--- /dev/null
+++ b/docshell/test/navigation/test_bug278916.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="NavigationUtils.js"></script>
+<script>
+window.onload = async function() {
+ document.getElementById("link0").href = target_url;
+ sendMouseEvent({type: "click"}, "link0");
+
+ await waitForFinishedFrames(1);
+
+ var array_of_frames = await getFramesByName("window0");
+ is(array_of_frames.length, 1, "Should only open one window using a fancy hyperlink.");
+
+ for (var i = 0; i < array_of_frames.length; ++i)
+ array_of_frames[i].close();
+
+ await cleanupWindows();
+ SimpleTest.finish();
+};
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=278916">Mozilla Bug 278916</a>
+<div id="links">
+<a id="link0" target="window0" onclick="window.open('', 'window0', 'width=10,height=10');">This is a fancy hyperlink</a>
+</div>
+<pre id="test">
+<script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug279495.html b/docshell/test/navigation/test_bug279495.html
new file mode 100644
index 0000000000..245ed14ed4
--- /dev/null
+++ b/docshell/test/navigation/test_bug279495.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="NavigationUtils.js"></script>
+<script>
+window.onload = async function() {
+ document.getElementById("link0").href = target_url;
+ document.getElementById("link1").href = target_url;
+
+ sendMouseEvent({type: "click"}, "link0");
+ sendMouseEvent({type: "click"}, "link1");
+
+ await waitForFinishedFrames(2);
+ await countAndClose("window0", 1);
+ await countAndClose("window1", 1);
+
+ await cleanupWindows();
+ SimpleTest.finish();
+};
+
+async function countAndClose(name, expected_count) {
+ var array_of_frames = await getFramesByName(name);
+ is(array_of_frames.length, expected_count,
+ "Should only open " + expected_count +
+ " window(s) with name " + name + " using a fancy hyperlink.");
+}
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=279495">Mozilla Bug 279495</a>
+<div id="links">
+<a id="link0" target="window0" onclick="window.open('blank.html', 'window0', 'width=10,height=10');">This is a fancy hyperlink</a>
+<a id="link1" target="window1" onclick="window.open('https://test1.example.org/tests/docshell/test/navigation/blank.html', 'window1', 'width=10,height=10');">This is a fancy hyperlink</a>
+</div>
+<pre id="test">
+<script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug344861.html b/docshell/test/navigation/test_bug344861.html
new file mode 100644
index 0000000000..76967b7b17
--- /dev/null
+++ b/docshell/test/navigation/test_bug344861.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=344861
+-->
+<head>
+ <title>Test for Bug 344861</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=344861">Mozilla Bug 344861</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 344861 **/
+SimpleTest.waitForExplicitFinish();
+
+var newwindow = window.open("/", "testwindow", "width=200,height=200");
+newwindow.onload = function() {
+ is(newwindow.innerHeight, 200, "window.open has correct height dimensions");
+ is(newwindow.innerWidth, 200, "window.open has correct width dimensions");
+ SimpleTest.finish();
+ newwindow.close();
+};
+</script>
+</pre>
+</body>
+</html>
+
+
diff --git a/docshell/test/navigation/test_bug386782.html b/docshell/test/navigation/test_bug386782.html
new file mode 100644
index 0000000000..895b1e49eb
--- /dev/null
+++ b/docshell/test/navigation/test_bug386782.html
@@ -0,0 +1,122 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=386782
+-->
+<head>
+ <title>Test for Bug 386782</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <script>
+
+ // This tests if we can load a document whose root is in designMode,
+ // edit it, navigate to a new page, navigate back, still edit, and still
+ // undo/redo. Note that this is different from the case where the
+ // designMode document is in a frame inside the window, as this means
+ // the editable region is not in the root docshell (a less complicated case).
+
+ var gTests = [
+ {
+ // <html><body><p>designModeDocument</p></body></html>
+ url: "file_bug386782_designmode.html",
+ name: "designModeNavigate",
+ onload(doc) { doc.designMode = "on"; },
+ expectedBodyBeforeEdit: "<p>designModeDocument</p>",
+ expectedBodyAfterEdit: "<p>EDITED designModeDocument</p>",
+ expectedBodyAfterSecondEdit: "<p>EDITED TWICE designModeDocument</p>",
+ },
+ {
+ // <html><body contentEditable="true"><p>contentEditable</p></body></html>
+ url: "file_bug386782_contenteditable.html",
+ name: "contentEditableNavigate",
+ expectedBodyBeforeEdit: "<p>contentEditable</p>",
+ expectedBodyAfterEdit: "EDITED <br><p>contentEditable</p>",
+ expectedBodyAfterSecondEdit: "EDITED TWICE <br><p>contentEditable</p>",
+ },
+ ];
+
+ var gTest = null;
+
+ add_task(async () => {
+ while (gTests.length) {
+ gTest = gTests.shift();
+ await runTest();
+ }
+ });
+
+ async function runTest() {
+ gTest.window = window.open(gTest.url, gTest.name, "width=500,height=500");
+ let e = await new Promise(r => window.onmessage = r);
+ is(e.data.persisted, false, "Initial load cannot be persisted");
+ if ("onload" in gTest) {
+ gTest.onload(gTest.window.document);
+ }
+ await SimpleTest.promiseFocus(gTest.window);
+
+ gTest.window.document.body.focus();
+
+ // WARNING: If the following test fails, give the setTimeout() in the onload()
+ // a bit longer; the doc hasn't had enough time to setup its editor.
+ is(gTest.window.document.body.innerHTML, gTest.expectedBodyBeforeEdit, "Is doc setup yet");
+ sendString("EDITED ", gTest.window);
+ is(gTest.window.document.body.innerHTML, gTest.expectedBodyAfterEdit, "Editing failed.");
+
+ gTest.window.location = "about:blank";
+ await new Promise(r => gTest.window.onpagehide = r);
+ // The active document is updated synchronously after "pagehide" (and
+ // its associated microtasks), so, after waiting for the next global
+ // task, gTest.window will be proxying the realm associated with the
+ // "about:blank" document.
+ // https://html.spec.whatwg.org/multipage/browsing-the-web.html#update-the-session-history-with-the-new-page
+ await new Promise(r => setTimeout(r));
+ is(gTest.window.location.href, "about:blank", "location.href");
+ await SimpleTest.promiseFocus(gTest.window, true);
+
+ gTest.window.history.back();
+ e = await new Promise(r => window.onmessage = r);
+ // Skip the test if the page is not loaded from the bf-cache when going back.
+ if (e.data.persisted) {
+ checkStillEditable();
+ }
+ gTest.window.close();
+ }
+
+ function checkStillEditable() {
+ // Check that the contents are correct.
+ is(gTest.window.document.body.innerHTML, gTest.expectedBodyAfterEdit, "Edited contents still correct?");
+
+ // Check that we can undo/redo and the contents are correct.
+ gTest.window.document.execCommand("undo", false, null);
+ is(gTest.window.document.body.innerHTML, gTest.expectedBodyBeforeEdit, "Can we undo?");
+
+ gTest.window.document.execCommand("redo", false, null);
+ is(gTest.window.document.body.innerHTML, gTest.expectedBodyAfterEdit, "Can we redo?");
+
+ // Check that we can still edit the page.
+ gTest.window.document.body.focus();
+ sendString("TWICE ", gTest.window);
+ is(gTest.window.document.body.innerHTML, gTest.expectedBodyAfterSecondEdit, "Can we still edit?");
+ }
+
+ </script>
+
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=386782">Mozilla Bug 386782</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 386782 **/
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_bug430624.html b/docshell/test/navigation/test_bug430624.html
new file mode 100644
index 0000000000..fbdc5d2677
--- /dev/null
+++ b/docshell/test/navigation/test_bug430624.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=430624
+-->
+<head>
+ <title>Test for Bug 430624</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=430624">Mozilla Bug 430624</a>
+<p id="display"></p>
+
+
+
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 430624 **/
+
+function onLoad() {
+ window.frames[0].frameElement.onload = onReload;
+ // eslint-disable-next-line no-self-assign
+ window.frames[0].frameElement.srcdoc = window.frames[0].frameElement.srcdoc;
+}
+
+function onReload() {
+ var iframe = window.frames[0].frameElement;
+ SimpleTest.waitForFocus(doTest, iframe.contentWindow);
+ iframe.contentDocument.body.focus();
+}
+
+function doTest() {
+ var bodyElement = window.frames[0].frameElement.contentDocument.body;
+ bodyElement.focus();
+ sendString("Still ", window.frames[0].frameElement.contentWindow);
+
+ is(bodyElement.innerHTML, "Still contentEditable", "Check we're contentEditable after reload");
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+
+<iframe onload="onLoad()" srcdoc="<body contenteditable>contentEditable</body>"></iframe>
+
+</body>
+</html>
+
diff --git a/docshell/test/navigation/test_bug430723.html b/docshell/test/navigation/test_bug430723.html
new file mode 100644
index 0000000000..c2ba4b41c3
--- /dev/null
+++ b/docshell/test/navigation/test_bug430723.html
@@ -0,0 +1,124 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=430723
+-->
+<head>
+ <title>Test for Bug 430723</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=430723">Mozilla Bug 430723</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+// <![CDATA[
+
+/** Test for Bug 430723 **/
+
+var BASE_URI = "http://mochi.test:8888/tests/docshell/test/navigation/";
+var gTallRedBoxURI = BASE_URI + "redbox_bug430723.html";
+var gTallBlueBoxURI = BASE_URI + "bluebox_bug430723.html";
+
+window.onload = runTest;
+
+var testWindow;
+var testNum = 0;
+
+var smoothScrollPref = "general.smoothScroll";
+function runTest() {
+ SpecialPowers.pushPrefEnv({"set": [[smoothScrollPref, false]]}, function() {
+ testWindow = window.open(gTallRedBoxURI, "testWindow", "width=300,height=300,location=yes,scrollbars=yes");
+ });
+}
+
+var nextTest = function() {
+ testNum++;
+ switch (testNum) {
+ case 1: setTimeout(step1, 0); break;
+ case 2: setTimeout(step2, 0); break;
+ case 3: setTimeout(step3, 0); break;
+ }
+};
+
+var step1 = function() {
+ window.is(String(testWindow.location), gTallRedBoxURI, "Ensure red page loaded.");
+
+ // Navigate down and up.
+ is(testWindow.document.body.scrollTop, 0,
+ "Page1: Ensure the scrollpane is at the top before we start scrolling.");
+ testWindow.addEventListener("scroll", function() {
+ isnot(testWindow.document.body.scrollTop, 0,
+ "Page1: Ensure we can scroll down.");
+ SimpleTest.executeSoon(step1_2);
+ }, {capture: true, once: true});
+ sendKey("DOWN", testWindow);
+
+ function step1_2() {
+ testWindow.addEventListener("scroll", function() {
+ is(testWindow.document.body.scrollTop, 0,
+ "Page1: Ensure we can scroll up, back to the top.");
+
+ // Nav to blue box page. This should fire step2.
+ testWindow.location = gTallBlueBoxURI;
+ }, {capture: true, once: true});
+ sendKey("UP", testWindow);
+ }
+};
+
+
+var step2 = function() {
+ window.is(String(testWindow.location), gTallBlueBoxURI, "Ensure blue page loaded.");
+
+ // Scroll around a bit.
+ is(testWindow.document.body.scrollTop, 0,
+ "Page2: Ensure the scrollpane is at the top before we start scrolling.");
+
+ var scrollTest = function() {
+ if (++count < 2) {
+ SimpleTest.executeSoon(function() { sendKey("DOWN", testWindow); });
+ } else {
+ testWindow.removeEventListener("scroll", scrollTest, true);
+
+ isnot(testWindow.document.body.scrollTop, 0,
+ "Page2: Ensure we could scroll.");
+
+ // Navigate backwards. This should fire step3.
+ testWindow.history.back();
+ }
+ };
+
+ var count = 0;
+ testWindow.addEventListener("scroll", scrollTest, true);
+ sendKey("DOWN", testWindow);
+};
+
+var step3 = function() {
+ window.is(String(testWindow.location), gTallRedBoxURI,
+ "Ensure red page restored from history.");
+
+ // Check we can still scroll with the keys.
+ is(testWindow.document.body.scrollTop, 0,
+ "Page1Again: Ensure scroll pane at top before we scroll.");
+ testWindow.addEventListener("scroll", function() {
+ isnot(testWindow.document.body.scrollTop, 0,
+ "Page2Again: Ensure we can still scroll.");
+
+ testWindow.close();
+ window.SimpleTest.finish();
+ }, {capture: true, once: true});
+ sendKey("DOWN", testWindow);
+};
+
+SimpleTest.waitForExplicitFinish();
+
+// ]]>
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_child.html b/docshell/test/navigation/test_child.html
new file mode 100644
index 0000000000..87237471cd
--- /dev/null
+++ b/docshell/test/navigation/test_child.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="NavigationUtils.js"></script>
+ <style type="text/css">
+ iframe { width: 90%; height: 50px; }
+ </style>
+<script>
+if (!navigator.platform.startsWith("Win")) {
+ SimpleTest.expectAssertions(0, 1);
+}
+
+window.onload = async function() {
+ navigateByLocation(frames[0]);
+ navigateByOpen("child1");
+ navigateByForm("child2");
+ navigateByHyperlink("child3");
+
+ await waitForFinishedFrames(4);
+ await isNavigated(frames[0], "Should be able to navigate off-domain child by setting location.");
+ await isNavigated(frames[1], "Should be able to navigate off-domain child by calling window.open.");
+ await isNavigated(frames[2], "Should be able to navigate off-domain child by submitting form.");
+ await isNavigated(frames[3], "Should be able to navigate off-domain child by targeted hyperlink.");
+
+ await cleanupWindows();
+ SimpleTest.finish();
+};
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=408052">Mozilla Bug 408052</a>
+<div id="frames">
+<iframe name="child0" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+<iframe name="child1" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+<iframe name="child2" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+<iframe name="child3" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+</div>
+<pre id="test">
+<script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_contentpolicy_block_window.html b/docshell/test/navigation/test_contentpolicy_block_window.html
new file mode 100644
index 0000000000..7ce337c131
--- /dev/null
+++ b/docshell/test/navigation/test_contentpolicy_block_window.html
@@ -0,0 +1,98 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1329288
+-->
+<head>
+ <title>Test for Bug 1329288</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1329288">Mozilla Bug 1329288</a>
+
+
+<!-- have a testlink which we can use for the test to open a new window -->
+<a href="http://test1.example.org/tests/docshell/test/navigation/file_contentpolicy_block_window.html"
+ target="_blank"
+ id="testlink">This is a link</a>
+
+<script class="testbody" type="text/javascript">
+/*
+ * Description of the test:
+ * The test tries to open a new window and makes sure that a registered contentPolicy
+ * gets called with the right (a non null) 'context' for the TYPE_DOCUMENT load.
+ */
+
+const Ci = SpecialPowers.Ci;
+
+var categoryManager = SpecialPowers.Services.catMan;
+var componentManager = SpecialPowers.Components.manager
+ .QueryInterface(Ci.nsIComponentRegistrar);
+
+// Content policy / factory implementation for the test
+var policyID = SpecialPowers.wrap(SpecialPowers.Components).ID("{b80e19d0-878f-d41b-2654-194714a4115c}");
+var policyName = "@mozilla.org/testpolicy;1";
+var policy = {
+ // nsISupports implementation
+ // eslint-disable-next-line mozilla/use-chromeutils-generateqi
+ QueryInterface(iid) {
+ iid = SpecialPowers.wrap(iid);
+ if (iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIFactory) ||
+ iid.equals(Ci.nsIContentPolicy))
+ return this;
+ throw SpecialPowers.Cr.NS_ERROR_NO_INTERFACE;
+ },
+
+ // nsIFactory implementation
+ createInstance(iid) {
+ return this.QueryInterface(iid);
+ },
+
+ // nsIContentPolicy implementation
+ shouldLoad(contentLocation, loadInfo, mimeTypeGuess) {
+ let contentType = loadInfo.externalContentPolicyType;
+ let context = loadInfo.loadingContext;
+
+ if (SpecialPowers.wrap(contentLocation).spec !== document.getElementById("testlink").href) {
+ // not the URI we are looking for, allow the load
+ return Ci.nsIContentPolicy.ACCEPT;
+ }
+
+ is(contentType, Ci.nsIContentPolicy.TYPE_DOCUMENT,
+ "needs to be type document load");
+ ok(context, "context is not allowed to be null");
+ ok(context.name.endsWith("test_contentpolicy_block_window.html"),
+ "context should be the current window");
+
+ // remove the policy and finish test.
+ categoryManager.deleteCategoryEntry("content-policy", policyName, false);
+
+ setTimeout(function() {
+ // Component must be unregistered delayed, otherwise other content
+ // policy will not be removed from the category correctly
+ componentManager.unregisterFactory(policyID, policy);
+ }, 0);
+
+ SimpleTest.finish();
+ return Ci.nsIContentPolicy.REJECT_REQUEST;
+ },
+
+ shouldProcess(contentLocation, loadInfo, mimeTypeGuess) {
+ return Ci.nsIContentPolicy.ACCEPT;
+ },
+};
+
+policy = SpecialPowers.wrapCallbackObject(policy);
+componentManager.registerFactory(policyID, "Test content policy", policyName, policy);
+categoryManager.addCategoryEntry("content-policy", policyName, policyName, false, true);
+
+SimpleTest.waitForExplicitFinish();
+
+// now everything is set up, let's start the test
+document.getElementById("testlink").click();
+
+</script>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_docshell_gotoindex.html b/docshell/test/navigation/test_docshell_gotoindex.html
new file mode 100644
index 0000000000..992c9c9dbe
--- /dev/null
+++ b/docshell/test/navigation/test_docshell_gotoindex.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1684310</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+ function test() {
+ /*
+ * This test is for nsIWebNavigation.gotoIndex.
+ *
+ * The test
+ * - opens a new window
+ * - loads a page there
+ * - loads another page
+ * - navigates to some fragments in the page
+ * - goes back to one of the fragments
+ * - tries to go back to the initial page.
+ */
+ window.open("file_docshell_gotoindex.html");
+ }
+ </script>
+</head>
+<body onload="test()">
+<p id="display"></p>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_dynamic_frame_forward_back.html b/docshell/test/navigation/test_dynamic_frame_forward_back.html
new file mode 100644
index 0000000000..f3a349e09a
--- /dev/null
+++ b/docshell/test/navigation/test_dynamic_frame_forward_back.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+ <title>Test for Bug 508537</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=508537">Mozilla Bug 508537</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ var testWindow;
+ function runTest() {
+ testWindow = window.open("file_bug508537_1.html", "", "width=360,height=480");
+ testWindow.onunload = function() { }; // to prevent bfcache
+ }
+
+ function finishTest() {
+ testWindow.close();
+ SimpleTest.finish();
+ }
+
+ </script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_evict_from_bfcache.html b/docshell/test/navigation/test_evict_from_bfcache.html
new file mode 100644
index 0000000000..0b1eb2fca4
--- /dev/null
+++ b/docshell/test/navigation/test_evict_from_bfcache.html
@@ -0,0 +1,63 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Evict a page from bfcache</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ /*
+ * This test checks that a page can be evicted from bfcache. Sending a
+ * message to an open BroadcastChannel is used for this.
+ *
+ * First the test opens a window and loads a page there. Another page is then
+ * loaded and then session history is navigated back to check if bfcache is
+ * enabled. If not, close message is sent to close the opened window and this
+ * controller page will finish the test.
+ * If bfcache is enabled, session history goes forward, but the
+ * BroadcastChannel in the page isn't closed. Then sending the message to go
+ * back again should evict the bfcached page.
+ * Close message is sent and window closed and test finishes.
+ */
+
+ SimpleTest.waitForExplicitFinish();
+ var bc = new BroadcastChannel("evict_from_bfcache");
+ var pageshowCount = 0;
+ bc.onmessage = function(event) {
+ if (event.data.type == "pageshow") {
+ ++pageshowCount;
+ info("pageshow " + pageshowCount);
+ if (pageshowCount == 1) {
+ bc.postMessage("nextpage");
+ } else if (pageshowCount == 2) {
+ bc.postMessage("back");
+ } else if (pageshowCount == 3) {
+ if (!event.data.persisted) {
+ ok(true, "BFCache isn't enabled.");
+ bc.postMessage("close");
+ } else {
+ bc.postMessage("forward");
+ }
+ } else if (pageshowCount == 4) {
+ bc.postMessage("back");
+ } else if (pageshowCount == 5) {
+ ok(!event.data.persisted,
+ "The page should have been evicted from BFCache");
+ bc.postMessage("close");
+ }
+ } else if (event.data == "closed") {
+ SimpleTest.finish();
+ }
+ }
+
+ function runTest() {
+ window.open("file_evict_from_bfcache.html", "", "noopener");
+ }
+ </script>
+</head>
+<body onload="runTest()">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_fragment_handling_during_load.html b/docshell/test/navigation/test_fragment_handling_during_load.html
new file mode 100644
index 0000000000..9c082c2ecf
--- /dev/null
+++ b/docshell/test/navigation/test_fragment_handling_during_load.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+ <title>Test for fragment navigation during load</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=978408">Mozilla Bug 978408</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ var testWindow;
+ function runTest() {
+ testWindow = window.open("file_fragment_handling_during_load.html", "", "width=360,height=480");
+ testWindow.onunload = function() { }; // to prevent bfcache
+ }
+
+ function finishTest() {
+ testWindow.close();
+ SimpleTest.finish();
+ }
+
+ </script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_grandchild.html b/docshell/test/navigation/test_grandchild.html
new file mode 100644
index 0000000000..10cf610664
--- /dev/null
+++ b/docshell/test/navigation/test_grandchild.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="NavigationUtils.js"></script>
+ <style type="text/css">
+ iframe { width: 90%; height: 200px; }
+ </style>
+<script>
+if (!navigator.platform.startsWith("Win")) {
+ SimpleTest.expectAssertions(0, 1);
+}
+
+window.onload = async function() {
+ navigateByLocation(frames[0].frames[0]);
+ navigateByOpen("child1_child0");
+ navigateByForm("child2_child0");
+ navigateByHyperlink("child3_child0");
+
+ await waitForFinishedFrames(4);
+ await isNavigated(frames[0].frames[0], "Should be able to navigate off-domain grandchild by setting location.");
+ await isNavigated(frames[1].frames[0], "Should be able to navigate off-domain grandchild by calling window.open.");
+ await isNavigated(frames[2].frames[0], "Should be able to navigate off-domain grandchild by submitting form.");
+ await isNavigated(frames[3].frames[0], "Should be able to navigate off-domain grandchild by targeted hyperlink.");
+
+ await cleanupWindows();
+ SimpleTest.finish();
+};
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=408052">Mozilla Bug 408052</a>
+<div id="frames">
+<iframe name="child0" src="http://test1.example.org:80/tests/docshell/test/navigation/parent.html"></iframe>
+<iframe name="child1" src="http://test1.example.org:80/tests/docshell/test/navigation/parent.html"></iframe>
+<iframe name="child2" src="http://test1.example.org:80/tests/docshell/test/navigation/parent.html"></iframe>
+<iframe name="child3" src="http://test1.example.org:80/tests/docshell/test/navigation/parent.html"></iframe>
+</div>
+<pre id="test">
+<script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_load_history_entry.html b/docshell/test/navigation/test_load_history_entry.html
new file mode 100644
index 0000000000..8ca3fcb913
--- /dev/null
+++ b/docshell/test/navigation/test_load_history_entry.html
@@ -0,0 +1,196 @@
+
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="application/javascript" src="/tests/SimpleTest/SpecialPowers.js"></script>
+ <script type="application/javascript">
+ /*
+ * Perform the following steps.
+ * 1) Go to file_load_history_entry_page_with_two_links.html, which contains two links, 'link1' and 'link2'
+ * 2) Click on 'link1' to be taken to file_load_history_entry_page_with_two_links.html#1
+ * 3) Click on 'link2' to be taken to file_load_history_entry_page_with_two_links.html#2
+ * 4) Go to file_load_history_entry_page_with_one_link.html
+ * 5) Push state to go to file_load_history_entry_page_with_one_link.html#1
+ *
+ * After each step
+ * - Check the number of session history entries
+ * - Reload the document and do the above again
+ * - Navigate back and check the correct history index
+ * - Navigate forward and check the correct history index and location
+ */
+ async function test() {
+ let testWin;
+ var promise;
+ var previousLocation;
+ var numSHEntries = 0;
+
+ // Step 1. Open a new tab and load a document with two links inside
+ // Now we are at file_load_history_entry_page_with_two_links.html
+ numSHEntries++;
+ promise = waitForLoad();
+ testWin = window.open("file_load_history_entry_page_with_two_links.html");
+ await promise;
+
+ let shistory = SpecialPowers.wrap(testWin)
+ .docShell
+ .QueryInterface(SpecialPowers.Ci.nsIWebNavigation)
+ .sessionHistory;
+
+ // Step 2. Navigate the document by clicking on the 1st link
+ // Now we are at file_load_history_entry_page_with_two_links.html#1
+ numSHEntries++;
+ previousLocation = testWin.location.href;
+ await clickLink(testWin, "link1");
+ await doAfterEachTest(testWin, shistory, numSHEntries, previousLocation);
+
+ // Step 3. Navigate the document by clicking the 2nd link
+ // Now we are file_load_history_entry_page_with_two_links.html#2
+ numSHEntries++;
+ previousLocation = testWin.location.href;
+ await clickLink(testWin, "link2");
+ await doAfterEachTest(testWin, shistory, numSHEntries, previousLocation);
+
+ // Step 4. Navigate the document to a different page
+ // Now we are at file_load_history_entry_page_with_one_link.html
+ numSHEntries++;
+ previousLocation = testWin.location.href;
+ promise = waitForLoad();
+ testWin.location = "file_load_history_entry_page_with_one_link.html";
+ await promise;
+ await doAfterEachTest(testWin, shistory, numSHEntries, previousLocation,
+ true /* isCrossDocumentLoad */, false /* hashChangeExpected */);
+
+ // Step 5. Push some state
+ // Now we are at file_load_history_entry_page_with_one_link.html#1
+ numSHEntries++;
+ previousLocation = testWin.location.href;
+ testWin.history.pushState({foo: "bar"}, "", "#1");
+ is(testWin.history.length, numSHEntries, "Session history's length is correct after pushing state");
+ is(shistory.index, numSHEntries - 1 /* we haven't switched to new history entry yet*/,
+ "Session history's index is correct after pushing state");
+ await doAfterEachTest(testWin, shistory, numSHEntries, previousLocation);
+
+ // We are done with the test
+ testWin.close();
+ SimpleTest.finish();
+ }
+
+ /*
+ * @prevLocation
+ * if undefined, it is because there is no page to go back to
+ *
+ * @isCrossDocumentLoad
+ * did we just open a different document
+ * @hashChangeExpected
+ * Would we get a hash change event if we navigated backwards and forwards in history?
+ * This is framed with respect to the previous step, e.g. in the previous step was the
+ * hash different from the location we have navigated to just before calling this function?
+ * When we navigate forwards or backwards, we need to wait for this event
+ * because clickLink() also waits for hashchange event and
+ * if this function gets called before clickLink(), sometimes hashchange
+ * events from this function will leak to clickLink.
+ */
+ async function doAfterEachTest(testWin, shistory, expectedNumSHEntries, prevLocation,
+ isCrossDocumentLoad = false, hashChangeExpected = true) {
+ var initialLocation = testWin.location.href;
+ var initialSHIndex = shistory.index;
+ var promise;
+ is(testWin.history.length, expectedNumSHEntries, "Session history's length is correct");
+
+ // Reload the document
+ promise = waitForLoad();
+ testWin.location.reload(true);
+ await promise;
+ is(testWin.history.length, expectedNumSHEntries, "Session history's length is correct after reloading");
+
+ if (prevLocation == undefined) {
+ return;
+ }
+
+ var hashChangePromise;
+ if (hashChangeExpected) {
+ hashChangePromise = new Promise(resolve => {
+ testWin.addEventListener("hashchange", resolve, {once: true});
+ });
+ }
+ // Navigate backwards
+ if (isCrossDocumentLoad) {
+ // Current page must have been a cross document load, so we just need to wait for
+ // document load to complete after we navigate the history back
+ // because popstate event will not be fired in this case
+ promise = waitForLoad();
+ } else {
+ promise = waitForPopstate(testWin);
+ }
+ testWin.history.back();
+ await promise;
+ if (hashChangeExpected) {
+ await hashChangePromise;
+ }
+ is(testWin.location.href, prevLocation, "Window location is correct after navigating back in history");
+ is(shistory.index, initialSHIndex - 1, "Session history's index is correct after navigating back in history");
+
+ // Navigate forwards
+ if (isCrossDocumentLoad) {
+ promise = waitForLoad();
+ } else {
+ promise = waitForPopstate(testWin);
+ }
+ if (hashChangeExpected) {
+ hashChangePromise = new Promise(resolve => {
+ testWin.addEventListener("hashchange", resolve, {once: true});
+ });
+ }
+ testWin.history.forward();
+ await promise;
+ if (hashChangeExpected) {
+ await hashChangePromise;
+ }
+ is(testWin.location.href, initialLocation, "Window location is correct after navigating forward in history");
+ is(shistory.index, initialSHIndex, "Session history's index is correct after navigating forward in history");
+ }
+
+ async function waitForLoad() {
+ return new Promise(resolve => {
+ window.bodyOnLoad = function() {
+ setTimeout(resolve, 0);
+ window.bodyOnLoad = undefined;
+ };
+ });
+ }
+
+ async function waitForPopstate(win) {
+ return new Promise(resolve => {
+ win.addEventListener("popstate", (e) => {
+ setTimeout(resolve, 0);
+ }, {once: true});
+ });
+ }
+
+ async function clickLink(win, id) {
+ var link = win.document.getElementById(id);
+ let clickPromise = new Promise(resolve => {
+ win.addEventListener("hashchange", resolve, {once: true});
+ });
+ link.click();
+ await clickPromise;
+ }
+
+ </script>
+</head>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1539482">Bug 1539482</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+<body onload="test()">
+</body>
+</html>
+
diff --git a/docshell/test/navigation/test_meta_refresh.html b/docshell/test/navigation/test_meta_refresh.html
new file mode 100644
index 0000000000..bda9a9fe73
--- /dev/null
+++ b/docshell/test/navigation/test_meta_refresh.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test meta refresh</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ let hasLoadedInitialOnce = false;
+ let bc = new BroadcastChannel("test_meta_refresh");
+ bc.onmessage = function(event) {
+ info(event.data.load || event.data);
+ if (event.data.load == "initial") {
+ if (!hasLoadedInitialOnce) {
+ hasLoadedInitialOnce = true;
+ bc.postMessage("loadnext");
+ } else {
+ bc.postMessage("ensuremetarefresh");
+ }
+ } else if (event.data.load == "nextpage") {
+ bc.postMessage("back");
+ } else if (event.data.load == "refresh") {
+ bc.postMessage("close");
+ } else if (event.data == "closed") {
+ ok(true, "Meta refresh page was loaded.");
+ SimpleTest.finish();
+ }
+ }
+
+ function test() {
+ window.open("file_meta_refresh.html?initial", "", "noopener");
+ }
+ </script>
+</head>
+<body onload="test()">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_navigation_type.html b/docshell/test/navigation/test_navigation_type.html
new file mode 100644
index 0000000000..75ea88bcbd
--- /dev/null
+++ b/docshell/test/navigation/test_navigation_type.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>performance.navigation.type</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+ let bc = new BroadcastChannel("navigation_type");
+ let pageshowCount = 0;
+ bc.onmessage = function(event) {
+ if (event.data == "closed") {
+ bc.close();
+ SimpleTest.finish();
+ return;
+ }
+
+ ++pageshowCount;
+ if (pageshowCount == 1) {
+ is(event.data.navigationType, PerformanceNavigation.TYPE_NAVIGATE,
+ "Should have navigation type TYPE_NAVIGATE.");
+ bc.postMessage("loadNewPage");
+ } else if (pageshowCount == 2) {
+ is(event.data.navigationType, PerformanceNavigation.TYPE_NAVIGATE,
+ "Should have navigation type TYPE_NAVIGATE.");
+ bc.postMessage("back");
+ } else if (pageshowCount == 3) {
+ is(event.data.navigationType, PerformanceNavigation.TYPE_BACK_FORWARD ,
+ "Should have navigation type TYPE_BACK_FORWARD .");
+ bc.postMessage("close");
+ } else {
+ ok(false, "Unexpected load");
+ }
+ }
+
+ function test() {
+ window.open("file_navigation_type.html", "", "noopener");
+ }
+ </script>
+</head>
+<body onload="test()">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_nested_frames.html b/docshell/test/navigation/test_nested_frames.html
new file mode 100644
index 0000000000..c3b49e0e23
--- /dev/null
+++ b/docshell/test/navigation/test_nested_frames.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+ <title>Test for Bug 1090918</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1090918">Mozilla Bug 1090918</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ var testWindow;
+ function runTest() {
+ testWindow = window.open("file_nested_frames.html", "", "width=360,height=480");
+ testWindow.onunload = function() { }; // to prevent bfcache
+ }
+
+ function finishTest() {
+ testWindow.close()
+ SimpleTest.finish();
+ }
+
+ </script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_new_shentry_during_history_navigation.html b/docshell/test/navigation/test_new_shentry_during_history_navigation.html
new file mode 100644
index 0000000000..0c9adc5280
--- /dev/null
+++ b/docshell/test/navigation/test_new_shentry_during_history_navigation.html
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test adding new session history entries while navigating to another one</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ var win;
+
+ function waitForMessage(msg) {
+ return new Promise(
+ function(resolve) {
+ window.addEventListener("message",
+ function(event) {
+ is(event.data, msg, "Got the expected message " + msg);
+ resolve();
+ }, { once: true }
+ );
+ }
+ );
+ }
+
+ async function test() {
+
+ let loadPromise = waitForMessage("load");
+ win = window.open("file_new_shentry_during_history_navigation_1.html");
+ await loadPromise;
+
+ loadPromise = waitForMessage("load");
+ win.location.href = "file_new_shentry_during_history_navigation_2.html";
+ await loadPromise;
+
+ let beforeunloadPromise = waitForMessage("beforeunload");
+ win.history.back();
+ await beforeunloadPromise;
+ await waitForMessage("load");
+
+ win.history.back();
+ SimpleTest.requestFlakyTimeout("Test that history.back() does not work on the initial entry.");
+ setTimeout(function() {
+ win.onmessage = null;
+ win.close();
+ testBfcache();
+ }, 500);
+ window.onmessage = function(event) {
+ ok(false, "Should not get a message " + event.data);
+ }
+ }
+
+ async function testBfcache() {
+ let bc = new BroadcastChannel("new_shentry_during_history_navigation");
+ let pageshowCount = 0;
+ bc.onmessage = function(event) {
+ if (event.data.type == "pageshow") {
+ ++pageshowCount;
+ info("pageshow: " + pageshowCount + ", page: " + event.data.page);
+ if (pageshowCount == 1) {
+ ok(!event.data.persisted, "The page should not be bfcached.");
+ bc.postMessage("loadnext");
+ } else if (pageshowCount == 2) {
+ ok(!event.data.persisted, "The page should not be bfcached.");
+ bc.postMessage("loadnext");
+ } else if (pageshowCount == 3) {
+ ok(!event.data.persisted, "The page should not be bfcached.");
+ bc.postMessage("back");
+ } else if (pageshowCount == 4) {
+ ok(event.data.persisted, "The page should be bfcached.");
+ bc.postMessage("forward");
+ } else if (pageshowCount == 5) {
+ ok(event.data.page.includes("v2"), "Should have gone forward.");
+ bc.postMessage("close");
+ SimpleTest.finish();
+ }
+ }
+ };
+ win = window.open("file_new_shentry_during_history_navigation_3.html", "", "noopener");
+
+ }
+
+ </script>
+</head>
+<body onload="test()">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_not-opener.html b/docshell/test/navigation/test_not-opener.html
new file mode 100644
index 0000000000..acdb9473e6
--- /dev/null
+++ b/docshell/test/navigation/test_not-opener.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="NavigationUtils.js"></script>
+ <style type="text/css">
+ iframe { width: 90%; height: 50px; }
+ </style>
+<script>
+if (!navigator.platform.startsWith("Win")) {
+ SimpleTest.expectAssertions(0, 1);
+}
+
+window.onload = async function() {
+ // navigateByLocation(window0); // Don't have a handle to the window.
+ navigateByOpen("window1");
+ navigateByForm("window2");
+ navigateByHyperlink("window3");
+
+ await waitForFinishedFrames(6);
+
+ is((await getFramesByName("window1")).length, 2, "Should not be able to navigate popup's popup by calling window.open.");
+ is((await getFramesByName("window2")).length, 2, "Should not be able to navigate popup's popup by submitting form.");
+ is((await getFramesByName("window3")).length, 2, "Should not be able to navigate popup's popup by targeted hyperlink.");
+
+ // opener0.close();
+ opener1.close();
+ opener2.close();
+ opener3.close();
+
+ info("here")
+ await cleanupWindows();
+ info("there")
+ SimpleTest.finish();
+};
+
+// opener0 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/open.html#window0", "_blank", "width=10,height=10");
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+let opener1 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/open.html#window1", "_blank", "width=10,height=10");
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+let opener2 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/open.html#window2", "_blank", "width=10,height=10");
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+let opener3 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/open.html#window3", "_blank", "width=10,height=10");
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=408052">Mozilla Bug 408052</a>
+<pre id="test">
+<script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_online_offline_bfcache.html b/docshell/test/navigation/test_online_offline_bfcache.html
new file mode 100644
index 0000000000..4ad90fd52e
--- /dev/null
+++ b/docshell/test/navigation/test_online_offline_bfcache.html
@@ -0,0 +1,101 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Online/Offline with BFCache</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+
+ /*
+ * The test is designed to work with and without bfcache.
+ * (1) First the test opens a window which then loads another page which
+ * goes back to the original page to detect if bfcache is enabled. If
+ * bfcache isn't enabled, close message is sent to the opened window and it
+ * closes itself and sends a message back and the test finishes.
+ * (2) The browser is set to offline mode. The opened page sends message
+ * that it has received offline event. This controller page then asks the
+ * page to go forward. The page which comes out from the bfcache gets
+ * offline event and sends message about that to this controller.
+ * (3) Browser is set to online mode. Similar cycle as with offline happens.
+ * (4) Controller page sends close message to the opened window and it
+ * closes itself and sends a message back and the test finishes.
+ */
+
+ function offlineOnline(online) {
+ function offlineFn() {
+ /* eslint-env mozilla/chrome-script */
+ Services.io.offline = true;
+ }
+ function onlineFn() {
+ /* eslint-env mozilla/chrome-script */
+ Services.io.offline = false;
+ }
+ SpecialPowers.loadChromeScript(online ? onlineFn : offlineFn);
+ }
+
+ var bc = new BroadcastChannel("online_offline_bfcache");
+ var pageshowCount = 0;
+ var offlineCount = 0;
+ var onlineCount = 0;
+
+ bc.onmessage = function(event) {
+ if (event.data.event == "pageshow") {
+ ++pageshowCount;
+ info("pageshow " + pageshowCount);
+ if (pageshowCount == 1) {
+ ok(!event.data.persisted);
+ bc.postMessage("nextpage");
+ } else if (pageshowCount == 2) {
+ ok(!event.data.persisted);
+ bc.postMessage("back");
+ } else if (pageshowCount == 3) {
+ if (!event.data.persisted) {
+ info("BFCache is not enabled, return early");
+ bc.postMessage("close");
+ } else {
+ offlineOnline(false);
+ }
+ }
+ } else if (event.data == "offline") {
+ ++offlineCount;
+ info("offline " + offlineCount);
+ if (offlineCount == 1) {
+ bc.postMessage("forward");
+ } else if (offlineCount == 2) {
+ offlineOnline(true);
+ } else {
+ ok(false, "unexpected offline event");
+ }
+ } else if (event.data == "online") {
+ ++onlineCount;
+ info("online " + onlineCount);
+ if (onlineCount == 1) {
+ bc.postMessage("back");
+ } else if (onlineCount == 2) {
+ bc.postMessage("close");
+ } else {
+ ok(false, "unexpected online event");
+ }
+ } else if ("closed") {
+ ok(true, "Did pass the test");
+ bc.close();
+ SimpleTest.finish();
+ }
+ };
+
+ function runTest() {
+ SpecialPowers.pushPrefEnv({"set": [["network.manage-offline-status", false]]}, function() {
+ window.open("file_online_offline_bfcache.html", "", "noopener");
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ </script>
+</head>
+<body onload="runTest()">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_open_javascript_noopener.html b/docshell/test/navigation/test_open_javascript_noopener.html
new file mode 100644
index 0000000000..81a6b70d61
--- /dev/null
+++ b/docshell/test/navigation/test_open_javascript_noopener.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <body>
+ <script>
+
+add_task(async function test_open_javascript_noopener() {
+ const topic = "test-javascript-was-run";
+ function jsuri(version) {
+ return `javascript:SpecialPowers.notifyObservers(null, "${topic}", "${version}");window.close()`;
+ }
+
+ let seen = [];
+ function observer(_subject, _topic, data) {
+ info(`got notification ${data}`);
+ seen.push(data);
+ }
+ SpecialPowers.addObserver(observer, topic);
+
+ isDeeply(seen, [], "seen no test notifications");
+ window.open(jsuri("1"));
+
+ // Bounce off the parent process to make sure the JS will have run.
+ await SpecialPowers.spawnChrome([], () => {});
+
+ isDeeply(seen, ["1"], "seen the opener notification");
+
+ window.open(jsuri("2"), "", "noopener");
+
+ // Bounce off the parent process to make sure the JS will have run.
+ await SpecialPowers.spawnChrome([], () => {});
+
+ isDeeply(seen, ["1"], "didn't get a notification from the noopener popup");
+
+ SpecialPowers.removeObserver(observer, topic);
+});
+
+ </script>
+ </body>
+</html>
diff --git a/docshell/test/navigation/test_opener.html b/docshell/test/navigation/test_opener.html
new file mode 100644
index 0000000000..ce966b897d
--- /dev/null
+++ b/docshell/test/navigation/test_opener.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="NavigationUtils.js"></script>
+ <style type="text/css">
+ iframe { width: 90%; height: 50px; }
+ </style>
+<script>
+if (navigator.platform.startsWith("Linux")) {
+ SimpleTest.expectAssertions(0, 1);
+}
+
+window.onload = async function() {
+ navigateByLocation(window0);
+ navigateByOpen("window1");
+ navigateByForm("window2");
+ navigateByHyperlink("window3");
+
+ await waitForFinishedFrames(4);
+ await isNavigated(window0, "Should be able to navigate popup by setting location.");
+ await isNavigated(window1, "Should be able to navigate popup by calling window.open.");
+ await isNavigated(window2, "Should be able to navigate popup by submitting form.");
+ await isNavigated(window3, "Should be able to navigate popup by targeted hyperlink.");
+
+ window0.close();
+ window1.close();
+ window2.close();
+ window3.close();
+
+ await cleanupWindows();
+
+ SimpleTest.finish();
+};
+
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+var window0 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/blank.html", "window0", "width=10,height=10");
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+var window1 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/blank.html", "window1", "width=10,height=10");
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+var window2 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/blank.html", "window2", "width=10,height=10");
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+var window3 = window.open("http://test1.example.org:80/tests/docshell/test/navigation/blank.html", "window3", "width=10,height=10");
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=408052">Mozilla Bug 408052</a>
+<pre id="test">
+<script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_performance_navigation.html b/docshell/test/navigation/test_performance_navigation.html
new file mode 100644
index 0000000000..75abbdd767
--- /dev/null
+++ b/docshell/test/navigation/test_performance_navigation.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=145971
+-->
+<head>
+ <title>Test for Bug 145971</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=145971">Mozilla Bug 145971</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+var testWindow;
+var bc = new BroadcastChannel("bug145971");
+bc.onmessage = function(msgEvent) {
+ var result = msgEvent.data.result;
+ if (result == undefined) {
+ info("Got unexpected message from BroadcastChannel");
+ return;
+ }
+ ok(result, "Bug 145971: Navigation type does not equal 2 when restoring document from session history.");
+ SimpleTest.finish();
+};
+
+function runTest() {
+ // If Fission is disabled, the pref is no-op.
+ SpecialPowers.pushPrefEnv({set: [["fission.bfcacheInParent", true]]}, () => {
+ window.open("test_bug145971.html", "", "width=360,height=480,noopener");
+ });
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_popup-navigates-children.html b/docshell/test/navigation/test_popup-navigates-children.html
new file mode 100644
index 0000000000..82d69e7982
--- /dev/null
+++ b/docshell/test/navigation/test_popup-navigates-children.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="NavigationUtils.js"></script>
+ <style type="text/css">
+ iframe { width: 90%; height: 50px; }
+ </style>
+<script>
+
+let window0 = null;
+let window1 = null;
+let window2 = null;
+let window3 = null;
+
+function testChild0() {
+ if (!window.window0)
+ window0 = window.open("navigate.html#opener.frames[0],location", "window0", "width=10,height=10");
+}
+
+function testChild1() {
+ if (!window.window1)
+ window1 = window.open("navigate.html#child1,open", "window1", "width=10,height=10");
+}
+
+function testChild2() {
+ if (!window.window2)
+ window2 = window.open("navigate.html#child2,form", "window2", "width=10,height=10");
+}
+
+function testChild3() {
+ if (!window.window3)
+ window3 = window.open("navigate.html#child3,hyperlink", "window3", "width=10,height=10");
+}
+
+window.onload = async function() {
+ await waitForFinishedFrames(4);
+ await isNavigated(frames[0], "Should be able to navigate on-domain opener's children by setting location.");
+ await isNavigated(frames[1], "Should be able to navigate on-domain opener's children by calling window.open.");
+ await isNavigated(frames[2], "Should be able to navigate on-domain opener's children by submitting form.");
+ await isNavigated(frames[3], "Should be able to navigate on-domain opener's children by targeted hyperlink.");
+
+ window0.close();
+ window1.close();
+ window2.close();
+ window3.close();
+
+ await cleanupWindows();
+ SimpleTest.finish();
+};
+
+</script>
+</head>
+<body>
+<div id="frames">
+<iframe onload="testChild0()" name="child0" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+<iframe onload="testChild1()" name="child1" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+<iframe onload="testChild2()" name="child2" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+<iframe onload="testChild3()" name="child3" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+</div>
+<pre id="test">
+<script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_rate_limit_location_change.html b/docshell/test/navigation/test_rate_limit_location_change.html
new file mode 100644
index 0000000000..c129824537
--- /dev/null
+++ b/docshell/test/navigation/test_rate_limit_location_change.html
@@ -0,0 +1,100 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1314912
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1314912</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1314912 **/
+
+ const RATE_LIMIT_COUNT = 90;
+ const RATE_LIMIT_TIME_SPAN = 3;
+
+ async function setup() {
+ await SpecialPowers.pushPrefEnv({set: [
+ ["dom.navigation.locationChangeRateLimit.count", RATE_LIMIT_COUNT],
+ ["dom.navigation.locationChangeRateLimit.timespan", RATE_LIMIT_TIME_SPAN]]});
+ }
+
+ let inc = 0;
+
+ const rateLimitedFunctions = (win) => ({
+ "history.replaceState": () => win.history.replaceState(null, "test", `${win.location.href}#${inc++}`),
+ "history.pushState": () => win.history.pushState(null, "test", `${win.location.href}#${inc++}`),
+ "history.back": () => win.history.back(),
+ "history.forward": () => win.history.forward(),
+ "history.go": () => win.history.go(-1),
+ "location.href": () => win.location.href = win.location.href + "",
+ "location.hash": () => win.location.hash = inc++,
+ "location.host": () => win.location.host = win.location.host + "",
+ "location.hostname": () => win.location.hostname = win.location.hostname + "",
+ "location.pathname": () => win.location.pathname = win.location.pathname + "",
+ "location.port": () => win.location.port = win.location.port + "",
+ "location.protocol": () => win.location.protocol = win.location.protocol + "",
+ "location.search": () => win.location.search = win.location.search + "",
+ "location.assign": () => win.location.assign(`${win.location.href}#${inc++}`),
+ "location.replace": () => win.location.replace(`${win.location.href}#${inc++}`),
+ "location.reload": () => win.location.reload(),
+ });
+
+ async function test() {
+ await setup();
+
+ // Open new window and wait for it to load
+ let win = window.open("blank.html");
+ await new Promise((resolve) => SimpleTest.waitForFocus(resolve, win))
+
+ // Execute the history and location functions
+ Object.entries(rateLimitedFunctions(win)).forEach(([name, fn]) => {
+ // Reset the rate limit for the next run.
+ info("Reset rate limit.");
+ SpecialPowers.wrap(win).browsingContext.resetLocationChangeRateLimit();
+
+ info(`Calling ${name} ${RATE_LIMIT_COUNT} times to reach the rate limit.`);
+ for(let i = 0; i< RATE_LIMIT_COUNT; i++) {
+ fn.call(this);
+ }
+ // Next calls should throw because we're above the rate limit
+ for(let i = 0; i < 5; i++) {
+ SimpleTest.doesThrow(() => fn.call(this), `Call #${RATE_LIMIT_COUNT + i + 1} to ${name} should throw.`);
+ }
+ })
+
+ // We didn't reset the rate limit after the last loop iteration above.
+ // Wait for the rate limit timer to expire.
+ SimpleTest.requestFlakyTimeout("Waiting to trigger rate limit reset.");
+ await new Promise((resolve) => setTimeout(resolve, 5000));
+
+ // Calls should be allowed again.
+ Object.entries(rateLimitedFunctions(win)).forEach(([name, fn]) => {
+ let didThrow = false;
+ try {
+ fn.call(this);
+ } catch(error) {
+ didThrow = true;
+ }
+ is(didThrow, false, `Call to ${name} must not throw.`)
+ });
+
+ // Cleanup
+ win.close();
+ SpecialPowers.wrap(win).browsingContext.resetLocationChangeRateLimit();
+ SimpleTest.finish();
+ }
+
+ </script>
+</head>
+<body onload="setTimeout(test, 0);">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1314912">Mozilla Bug 1314912</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_recursive_frames.html b/docshell/test/navigation/test_recursive_frames.html
new file mode 100644
index 0000000000..3ccc09dd14
--- /dev/null
+++ b/docshell/test/navigation/test_recursive_frames.html
@@ -0,0 +1,167 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Recursive Loads</title>
+ <meta charset="utf-8">
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1597427">Mozilla Bug 1597427</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ const TEST_CASES = [
+ { // too many recursive iframes
+ frameId: "recursiveFrame",
+ expectedLocations: [
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/tests/docshell/test/navigation/frame_recursive.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/tests/docshell/test/navigation/frame_recursive.html",
+ "about:blank",
+ ],
+ },
+ { // too many recursive iframes
+ frameId: "twoRecursiveIframes",
+ expectedLocations: [
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/tests/docshell/test/navigation/frame_load_as_example_com.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.org/tests/docshell/test/navigation/frame_load_as_example_org.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/tests/docshell/test/navigation/frame_load_as_example_com.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.org/tests/docshell/test/navigation/frame_load_as_example_org.html",
+ "about:blank",
+ ],
+ },
+ { // too many recursive iframes
+ frameId: "threeRecursiveIframes",
+ expectedLocations: [
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://sub1.test1.mochi.test:8888/tests/docshell/test/navigation/frame_load_as_host1.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/tests/docshell/test/navigation/frame_load_as_host2.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://test1.mochi.test:8888/tests/docshell/test/navigation/frame_load_as_host3.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://sub1.test1.mochi.test:8888/tests/docshell/test/navigation/frame_load_as_host1.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/tests/docshell/test/navigation/frame_load_as_host2.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://test1.mochi.test:8888/tests/docshell/test/navigation/frame_load_as_host3.html",
+ "about:blank",
+ ],
+ },
+ { // too many nested iframes
+ frameId: "sixRecursiveIframes",
+ expectedLocations: [
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/tests/docshell/test/navigation/frame_1_out_of_6.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://test1.mochi.test:8888/tests/docshell/test/navigation/frame_2_out_of_6.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://sub1.test1.mochi.test:8888/tests/docshell/test/navigation/frame_3_out_of_6.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://sub2.xn--lt-uia.mochi.test:8888/tests/docshell/test/navigation/frame_4_out_of_6.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://test2.mochi.test:8888/tests/docshell/test/navigation/frame_5_out_of_6.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.org/tests/docshell/test/navigation/frame_6_out_of_6.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/tests/docshell/test/navigation/frame_1_out_of_6.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://test1.mochi.test:8888/tests/docshell/test/navigation/frame_2_out_of_6.html",
+ ],
+ },
+ { // too many recursive objects
+ frameId: "recursiveObject",
+ expectedLocations: [
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://sub2.xn--lt-uia.mochi.test:8888/tests/docshell/test/navigation/object_recursive_load.html",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://sub2.xn--lt-uia.mochi.test:8888/tests/docshell/test/navigation/object_recursive_load.html",
+ ],
+ },
+ { // 3 nested srcdocs, should show all of them
+ frameId: "nestedSrcdoc",
+ expectedLocations: [
+ "about:srcdoc",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/tests/docshell/test/navigation/file_nested_srcdoc.html",
+ "about:srcdoc",
+ "about:srcdoc",
+ ],
+ },
+ ];
+
+ async function checkRecursiveLoad(level) {
+ let el = content.document.getElementById("static");
+ let documentURI = await SpecialPowers.spawn(
+ el,
+ [],
+ () => this.content.document.documentURI
+ );
+ if (documentURI == "about:blank") {
+ // If we had too many recursive frames, the most inner iframe's uri will be about:blank
+ return [documentURI];
+ }
+ if (documentURI == "about:srcdoc" && level == 3) {
+ // Check that we have the correct most inner srcdoc iframe
+ let innerText = await SpecialPowers.spawn(
+ el,
+ [],
+ () => this.content.document.body.innerText
+ );
+ is(innerText, "Third nested srcdoc", "correct most inner srcdoc iframe");
+ }
+ let nestedIfrOrObjectURI = [];
+ try {
+ // Throws an error when we have too many nested frames/objects, because we
+ // claim to have no content window for the inner most frame/object.
+ nestedIfrOrObjectURI = await SpecialPowers.spawn(
+ el,
+ [level + 1],
+ checkRecursiveLoad
+ );
+ } catch (err) {
+ info(
+ `Tried to spawn another task in the iframe/object, but got err: ${err}, must have had too many nested iframes/objects\n`
+ );
+ }
+ return [documentURI, ...nestedIfrOrObjectURI];
+ }
+
+ add_task(async () => {
+ for (const testCase of TEST_CASES) {
+ let el = document.getElementById(testCase.frameId);
+ let loc = await SpecialPowers.spawn(
+ el,
+ [],
+ () => this.content.location.href
+ );
+ let locations = await SpecialPowers.spawn(el, [1], checkRecursiveLoad);
+ isDeeply(
+ [loc, ...locations],
+ testCase.expectedLocations,
+ "iframes/object loaded in correct order"
+ );
+ }
+ });
+
+ </script>
+</pre>
+<div>
+ <iframe style="height: 100vh; width:25%;" id="recursiveFrame" src="http://example.com/tests/docshell/test/navigation/frame_recursive.html"></iframe>
+ <iframe style="height: 100vh; width:25%;" id="twoRecursiveIframes" src="http://example.com/tests/docshell/test/navigation/frame_load_as_example_com.html"></iframe>
+ <iframe style="height: 100vh; width:25%;" id="threeRecursiveIframes" src="http://sub1.test1.mochi.test:8888/tests/docshell/test/navigation/frame_load_as_host1.html"></iframe>
+ <iframe style="height: 100vh; width:25%;" id="sixRecursiveIframes" src="http://example.com/tests/docshell/test/navigation/frame_1_out_of_6.html"></iframe>
+ <object width="400" height="300" id="recursiveObject" data="http://sub2.xn--lt-uia.mochi.test:8888/tests/docshell/test/navigation/object_recursive_load.html"></object>
+ <iframe id="nestedSrcdoc" srcdoc="Srcdoc that will embed an iframe &lt;iframe id=&quot;static&quot; src=&quot;http://example.com/tests/docshell/test/navigation/file_nested_srcdoc.html&quot;&gt;&lt;/iframe&gt;"></iframe>
+</div>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_reload.html b/docshell/test/navigation/test_reload.html
new file mode 100644
index 0000000000..7e75c7c035
--- /dev/null
+++ b/docshell/test/navigation/test_reload.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Ensure a page which is otherwise bfcacheable doesn't crash on reload</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+ let pageshowCount = 0;
+ let bc = new BroadcastChannel("test_reload");
+ bc.onmessage = function(event) {
+ info("MessageEvent: " + event.data);
+ if (event.data == "pageshow") {
+ ++pageshowCount;
+ info("pageshow: " + pageshowCount);
+ if (pageshowCount < 3) {
+ info("Sending reload");
+ bc.postMessage("reload");
+ } else {
+ info("Sending close");
+ bc.postMessage("close");
+ }
+ } else if (event.data == "closed") {
+ info("closed");
+ bc.close();
+ ok(true, "Passed");
+ SimpleTest.finish();
+ }
+ }
+
+ function test() {
+ window.open("file_reload.html", "", "noopener");
+ }
+ </script>
+</head>
+<body onload="test()">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_reload_large_postdata.html b/docshell/test/navigation/test_reload_large_postdata.html
new file mode 100644
index 0000000000..15fae33ac3
--- /dev/null
+++ b/docshell/test/navigation/test_reload_large_postdata.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+
+<form id="form" action="file_reload_large_postdata.sjs" target="_blank" rel="opener" method="POST">
+ <input id="input" name="payload" type="hidden" value=""/>
+</form>
+
+<pre id="test">
+<script>
+// This is derived from `kTooLargeStream` in `IPCStreamUtils.cpp`.
+const kTooLargeStream = 1024 * 1024;
+
+function waitForPopup(expected) {
+ return new Promise(resolve => {
+ addEventListener("message", evt => {
+ info("got message!");
+ is(evt.source.opener, window, "the event source's opener should be this window");
+ is(evt.data, expected, "got the expected data from the popup");
+ resolve(evt.source);
+ }, { once: true });
+ });
+}
+
+add_task(async function() {
+ await SpecialPowers.pushPrefEnv({"set": [["dom.confirm_repost.testing.always_accept", true]]});
+ let form = document.getElementById("form");
+ let input = document.getElementById("input");
+
+ // Create a very large value to include in the post payload. This should
+ // ensure that the value isn't sent directly over IPC, and is instead sent as
+ // an async inputstream.
+ let payloadSize = kTooLargeStream;
+
+ let popupReady = waitForPopup(payloadSize);
+ input.value = "A".repeat(payloadSize);
+ form.submit();
+
+ let popup = await popupReady;
+ try {
+ let popupReady2 = waitForPopup(payloadSize);
+ info("reloading popup");
+ popup.location.reload();
+ let popup2 = await popupReady2;
+ is(popup, popup2);
+ } finally {
+ popup.close();
+ }
+});
+
+// The .sjs server can time out processing the 1mb payload in debug builds.
+SimpleTest.requestLongerTimeout(2);
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_reload_nonbfcached_srcdoc.html b/docshell/test/navigation/test_reload_nonbfcached_srcdoc.html
new file mode 100644
index 0000000000..2399a0ad7d
--- /dev/null
+++ b/docshell/test/navigation/test_reload_nonbfcached_srcdoc.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test srcdoc handling when reloading a page.</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ // The old session history implementation asserts in
+ // https://searchfox.org/mozilla-central/rev/b822a27de3947d3f4898defac6164e52caf1451b/docshell/shistory/nsSHEntry.cpp#670-672
+ SimpleTest.expectAssertions(0, 1);
+ SimpleTest.waitForExplicitFinish();
+
+ var win;
+ function test() {
+ window.onmessage = function(event) {
+ if (event.data == "pageload:") {
+ // Trigger a similar reload as what the reload button does.
+ SpecialPowers.wrap(win)
+ .docShell
+ .QueryInterface(SpecialPowers.Ci.nsIWebNavigation)
+ .sessionHistory
+ .reload(0);
+ } else if (event.data == "pageload:second") {
+ ok(true, "srcdoc iframe was updated.");
+ win.close();
+ SimpleTest.finish();
+ }
+ }
+ win = window.open("file_reload_nonbfcached_srcdoc.sjs");
+ }
+
+ </script>
+</head>
+<body onload="test()">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_reserved.html b/docshell/test/navigation/test_reserved.html
new file mode 100644
index 0000000000..0242f3941b
--- /dev/null
+++ b/docshell/test/navigation/test_reserved.html
@@ -0,0 +1,92 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="NavigationUtils.js"></script>
+ <style type="text/css">
+ iframe { width: 90%; height: 200px; }
+ </style>
+<script>
+if (navigator.platform.startsWith("Mac")) {
+ SimpleTest.expectAssertions(0, 2);
+}
+
+async function testTop() {
+ let window0 = window.open("iframe.html#http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#top,location", "_blank", "width=10,height=10");
+
+ await waitForFinishedFrames(1);
+ isInaccessible(window0, "Should be able to navigate off-domain top by setting location.");
+ window0.close();
+ await cleanupWindows();
+
+ let window1 = window.open("iframe.html#http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#_top,open", "_blank", "width=10,height=10");
+
+ await waitForFinishedFrames(1);
+ isInaccessible(window1, "Should be able to navigate off-domain top by calling window.open.");
+ window1.close();
+ await cleanupWindows();
+
+ let window2 = window.open("iframe.html#http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#_top,form", "_blank", "width=10,height=10");
+
+ await waitForFinishedFrames(1);
+ isInaccessible(window2, "Should be able to navigate off-domain top by submitting form.");
+ window2.close();
+ await cleanupWindows();
+
+ let window3 = window.open("iframe.html#http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#_top,hyperlink", "_blank", "width=10,height=10");
+
+ await waitForFinishedFrames(1);
+ isInaccessible(window3, "Should be able to navigate off-domain top by targeted hyperlink.");
+ window3.close();
+ await cleanupWindows();
+
+ await testParent();
+}
+
+async function testParent() {
+ document.getElementById("frames").innerHTML = '<iframe src="iframe.html#http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#parent,location"></iframe>';
+
+ await waitForFinishedFrames(1);
+ isAccessible(frames[0], "Should not be able to navigate off-domain parent by setting location.");
+ await cleanupWindows();
+
+ document.getElementById("frames").innerHTML = '<iframe src="iframe.html#http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#_parent,open"></iframe>';
+
+ await waitForFinishedFrames(1);
+ isAccessible(frames[0], "Should not be able to navigate off-domain parent by calling window.open.");
+ await cleanupWindows();
+
+ document.getElementById("frames").innerHTML = '<iframe src="iframe.html#http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#_parent,form"></iframe>';
+
+ await waitForFinishedFrames(1);
+ isAccessible(frames[0], "Should not be able to navigate off-domain parent by submitting form.");
+ await cleanupWindows();
+
+ document.getElementById("frames").innerHTML = '<iframe src="iframe.html#http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#_parent,hyperlink"></iframe>';
+
+ await waitForFinishedFrames(1);
+ isAccessible(frames[0], "Should not be able to navigate off-domain parent by targeted hyperlink.");
+ await cleanupWindows();
+
+ document.getElementById("frames").innerHTML = "";
+ SimpleTest.finish();
+}
+
+window.onload = async function() {
+ await testTop();
+};
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=408052">Mozilla Bug 408052</a>
+<div id="frames">
+</div>
+<pre id="test">
+<script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_same_url.html b/docshell/test/navigation/test_same_url.html
new file mode 100644
index 0000000000..820caa7005
--- /dev/null
+++ b/docshell/test/navigation/test_same_url.html
@@ -0,0 +1,56 @@
+
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="application/javascript" src="/tests/SimpleTest/SpecialPowers.js"></script>
+ <script type="application/javascript">
+ // Since BFCache in parent requires no opener, use BroadcastChannel
+ // to communicate with file_same_url.html.
+ let bc = new BroadcastChannel("test_same_url");
+ async function test() {
+ var promise;
+ let historyLength;
+
+ promise = waitForLoad();
+ window.open("file_same_url.html", "_blank", "noopener=yes");
+ historyLength = await promise;
+ is(historyLength, 1, "before same page navigation");
+
+ promise = waitForLoad();
+ bc.postMessage("linkClick");
+ historyLength = await promise;
+ is(historyLength, 1, "after same page navigation");
+ bc.postMessage("closeWin");
+
+ SimpleTest.finish();
+ }
+
+ async function waitForLoad() {
+ return new Promise(resolve => {
+ let listener = e => {
+ if (e.data.bodyOnLoad) {
+ bc.removeEventListener("message", listener);
+ setTimeout(() => resolve(e.data.bodyOnLoad), 0);
+ }
+ };
+ bc.addEventListener("message", listener);
+ });
+ }
+ </script>
+</head>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1745730">Bug 1745730</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+<body onload="test()">
+</body>
+</html>
+
diff --git a/docshell/test/navigation/test_scrollRestoration.html b/docshell/test/navigation/test_scrollRestoration.html
new file mode 100644
index 0000000000..d31598f391
--- /dev/null
+++ b/docshell/test/navigation/test_scrollRestoration.html
@@ -0,0 +1,214 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+ <title>Test for Bug 1155730</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1155730">Mozilla Bug 1155730</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout("untriaged");
+
+ function assertCheck(data) {
+ if (data.assertIs) {
+ for (const args of data.assertIs) {
+ is(args[0], args[1], args[2]);
+ }
+ }
+ if (data.assertOk) {
+ for (const args of data.assertOk) {
+ ok(args[0], args[1]);
+ }
+ }
+ if (data.assertIsNot) {
+ for (const args of data.assertIsNot) {
+ isnot(args[0], args[1], args[2]);
+ }
+ }
+ }
+
+ var bc1, currentCase = 0;
+ function test1() {
+ bc1 = new BroadcastChannel("bug1155730_part1");
+ bc1.onmessage = (msgEvent) => {
+ var msg = msgEvent.data;
+ var command = msg.command;
+ if (command == "pageshow") {
+ currentCase++;
+ var persisted = msg.persisted;
+ is(persisted, false, "Shouldn't have persisted session history entry.");
+ bc1.postMessage({command: "test", currentCase});
+ } else if (command == "asserts") {
+ is(msg.currentCase, currentCase, "correct case");
+ info(`Checking asserts for case ${msg.currentCase}`);
+ assertCheck(msg);
+ if (currentCase == 3) {
+ // move on to the next test
+ bc1.close();
+ test2();
+ }
+ }
+ }
+ window.open("file_scrollRestoration_part1_nobfcache.html", "", "width=360,height=480,noopener");
+ }
+
+ var bc2, bc2navigate;
+ function test2() {
+ currentCase = 0;
+ bc2 = new BroadcastChannel("bug1155730_part2");
+ bc2.onmessage = (msgEvent) => {
+ var msg = msgEvent.data;
+ var command = msg.command;
+ if (command == "pageshow") {
+ currentCase++;
+ var persisted = msg.persisted;
+ switch (currentCase) {
+ case 1:
+ is(persisted, false, "Shouldn't have persisted session history entry.");
+ break;
+ case 2:
+ is(persisted, true, "Should have persisted session history entry.");
+ }
+ bc2.postMessage({command: "test", currentCase});
+ } else if (command == "asserts") {
+ is(msg.currentCase, currentCase, "correct case");
+ info(`Checking asserts for case ${msg.currentCase}`);
+ assertCheck(msg);
+ if (currentCase == 3) {
+ // move on to the next test
+ bc2.close();
+ test3();
+ }
+ } else if (command == "nextCase") {
+ currentCase++;
+ }
+ }
+
+ bc2navigate = new BroadcastChannel("navigate");
+ bc2navigate.onmessage = (event) => {
+ if (event.data.command == "loaded") {
+ bc2navigate.postMessage({command: "back"})
+ bc2navigate.close();
+ }
+ }
+ window.open("file_scrollRestoration_part2_bfcache.html", "", "width=360,height=480,noopener");
+ }
+
+ var bc3, bc3navigate;
+ function test3() {
+ currentCase = 0;
+ bc3 = new BroadcastChannel("bug1155730_part3");
+ bc3.onmessage = (msgEvent) => {
+ var msg = msgEvent.data;
+ var command = msg.command;
+ if (command == "pageshow") {
+ currentCase++;
+ if (currentCase == 3) {
+ var persisted = msg.persisted;
+ is(persisted, false, "Shouldn't have persisted session history entry.");
+ }
+
+ bc3.postMessage({command: "test", currentCase});
+ } else if (command == "asserts") {
+ is(msg.currentCase, currentCase, "correct case");
+ info(`Checking asserts for case ${msg.currentCase}`);
+ assertCheck(msg);
+ } else if (command == "nextCase") {
+ currentCase++;
+ } else if (command == "finishing") {
+ bc3.close();
+ test4();
+ }
+ }
+
+ bc3navigate = new BroadcastChannel("navigate");
+ bc3navigate.onmessage = (event) => {
+ if (event.data.command == "loaded") {
+ is(event.data.scrollRestoration, 'auto', "correct scroll restoration");
+ bc3navigate.postMessage({command: "back"})
+ bc3navigate.close();
+ }
+ }
+ window.open("file_scrollRestoration_part3_nobfcache.html", "", "width=360,height=480,noopener");
+ }
+
+ // test4 opens a new page which can enter bfcache. That page then loads
+ // another page which can't enter bfcache. That second page then scrolls
+ // down. History API is then used to navigate back and forward. When the
+ // second page loads again, it should scroll down automatically.
+ var bc4a, bc4b;
+ var scrollYCounter = 0;
+ function test4() {
+ currentCase = 0;
+ bc4a = new BroadcastChannel("bfcached");
+ bc4a.onmessage = (msgEvent) => {
+ var msg = msgEvent.data;
+ var command = msg.command;
+ if (command == "pageshow") {
+ ++currentCase;
+ if (currentCase == 1) {
+ ok(!msg.persisted, "The first page should not be persisted initially.");
+ bc4a.postMessage("loadNext");
+ } else if (currentCase == 3) {
+ ok(msg.persisted, "The first page should be persisted.");
+ bc4a.postMessage("forward");
+ bc4a.close();
+ }
+ }
+ }
+
+ bc4b = new BroadcastChannel("notbfcached");
+ bc4b.onmessage = (event) => {
+ var msg = event.data;
+ var command = msg.command;
+ if (command == "pageshow") {
+ ++currentCase;
+ if (currentCase == 2) {
+ ok(!msg.persisted, "The second page should not be persisted.");
+ bc4b.postMessage("getScrollY");
+ bc4b.postMessage("scroll");
+ bc4b.postMessage("getScrollY");
+ bc4b.postMessage("back");
+ } else if (currentCase == 4) {
+ ok(!msg.persisted, "The second page should not be persisted.");
+ bc4b.postMessage("getScrollY");
+ }
+ } else if (msg == "closed") {
+ bc4b.close();
+ SimpleTest.finish();
+ } else if ("scrollY" in msg) {
+ ++scrollYCounter;
+ if (scrollYCounter == 1) {
+ is(msg.scrollY, 0, "The page should be initially scrolled to top.");
+ } else if (scrollYCounter == 2) {
+ isnot(msg.scrollY, 0, "The page should be then scrolled down.");
+ } else if (scrollYCounter == 3) {
+ isnot(msg.scrollY, 0, "The page should be scrolled down after being restored from the session history.");
+ bc4b.postMessage("close");
+ }
+ }
+ }
+ window.open("file_scrollRestoration_bfcache_and_nobfcache.html", "", "width=360,height=480,noopener");
+ }
+
+ function runTest() {
+ // If Fission is disabled, the pref is no-op.
+ SpecialPowers.pushPrefEnv({set: [["fission.bfcacheInParent", true]]}, () => {
+ test1();
+ });
+ }
+
+ </script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_session_history_entry_cleanup.html b/docshell/test/navigation/test_session_history_entry_cleanup.html
new file mode 100644
index 0000000000..a55de0d6c3
--- /dev/null
+++ b/docshell/test/navigation/test_session_history_entry_cleanup.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+ <title>Test for Bug 534178</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=534178">Mozilla Bug 534178</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ var testWindow;
+ function runTest() {
+ testWindow = window.open("file_bug534178.html", "", "width=360,height=480");
+ testWindow.onunload = function() { }; // to prevent bfcache
+ }
+
+ function finishTest() {
+ testWindow.close();
+ SimpleTest.finish();
+ }
+
+ </script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_session_history_on_redirect.html b/docshell/test/navigation/test_session_history_on_redirect.html
new file mode 100644
index 0000000000..a303f81536
--- /dev/null
+++ b/docshell/test/navigation/test_session_history_on_redirect.html
@@ -0,0 +1,92 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Session history on redirect</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ /*
+ * The test opens a new window and loads a page there. Then another document
+ * is loaded to the window. The initial load of that second page doesn't do
+ * a redirect. Now another non-redirecting page is loaded. Then
+ * history.go(-2) and history.forward() are called. The second time
+ * the second page is loaded, it does a redirect. history.back() and
+ * history.forward() are called again. The page which did the redirect
+ * shouldn't be accessed, but the page which it redirected to.
+ * Finally history.forward() is called again and the third page should be
+ * loaded and history.length should have the same value as it had when the
+ * third page was loaded the first time.
+ */
+
+ SimpleTest.waitForExplicitFinish();
+ var win;
+ var finalHistoryLength = 0;
+
+ function run() {
+ win = window.open("file_session_history_on_redirect.html");
+ }
+
+ var pageshowCounter = 0;
+ async function pageshow() {
+ // Need to trigger new loads asynchronously after page load, otherwise
+ // new loads are treated as replace loads.
+ await new Promise((r) => setTimeout(r));
+ ++pageshowCounter;
+ info("Page load: " + win.location.href);
+ switch (pageshowCounter) {
+ case 1:
+ ok(win.location.href.includes("file_session_history_on_redirect.html"));
+ win.location.href = "redirect_handlers.sjs";
+ break;
+ case 2:
+ ok(win.location.href.includes("redirect_handlers.sjs"));
+ // Put the initial page also as the last entry in the session history.
+ win.location.href = "file_session_history_on_redirect.html";
+ break;
+ case 3:
+ ok(win.location.href.includes("file_session_history_on_redirect.html"));
+ finalHistoryLength = win.history.length;
+ win.history.go(-2);
+ break;
+ case 4:
+ ok(win.location.href.includes("file_session_history_on_redirect.html"));
+ win.history.forward();
+ break;
+ case 5:
+ ok(win.location.href.includes("file_session_history_on_redirect_2.html"));
+ win.history.back();
+ break;
+ case 6:
+ ok(win.location.href.includes("file_session_history_on_redirect.html"));
+ win.history.forward();
+ break;
+ case 7:
+ ok(win.location.href.includes("file_session_history_on_redirect_2.html"));
+ is(win.history.length, finalHistoryLength, "Shouldn't have changed the history length.");
+ win.history.forward();
+ break;
+ case 8:
+ ok(win.location.href.includes("file_session_history_on_redirect.html"));
+ is(win.history.length, finalHistoryLength, "Shouldn't have changed the history length.");
+ win.onpagehide = null;
+ finishTest();
+ break;
+ default:
+ ok(false, "unexpected pageshow");
+ }
+ }
+
+ function finishTest() {
+ win.close()
+ SimpleTest.finish();
+ }
+
+ </script>
+</head>
+<body onload="run()">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_sessionhistory.html b/docshell/test/navigation/test_sessionhistory.html
new file mode 100644
index 0000000000..2254ec876b
--- /dev/null
+++ b/docshell/test/navigation/test_sessionhistory.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+ <title>Test for Bug 462076</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="nextTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=462076">Mozilla Bug 462076</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ var testFiles =
+ [ "file_bug462076_1.html", // Dynamic frames before onload
+ "file_bug462076_2.html", // Dynamic frames when handling onload
+ "file_bug462076_3.html", // Dynamic frames after onload
+ ];
+ var testCount = 0; // Used by the test files.
+
+ SimpleTest.waitForExplicitFinish();
+
+ var testWindow;
+ function nextTest_() {
+ if (testFiles.length) {
+ testCount = 0;
+ let nextFile = testFiles.shift();
+ info("Running " + nextFile);
+ testWindow = window.open(nextFile, "", "width=360,height=480");
+ testWindow.onunload = function() { }; // to prevent bfcache
+ } else {
+ SimpleTest.finish();
+ }
+ }
+
+ function nextTest() {
+ setTimeout(nextTest_, 0);
+ }
+
+ </script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_sessionhistory_document_write.html b/docshell/test/navigation/test_sessionhistory_document_write.html
new file mode 100644
index 0000000000..2a48a8154e
--- /dev/null
+++ b/docshell/test/navigation/test_sessionhistory_document_write.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+ <title>Test for Session history + document.write</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest()">
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ var testWindow;
+ function runTest() {
+ testWindow = window.open("file_document_write_1.html", "", "width=360,height=480");
+ testWindow.onunload = function() { }; // to prevent bfcache
+ }
+
+ function finishTest() {
+ testWindow.close();
+ SimpleTest.finish();
+ }
+
+ </script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_sessionhistory_iframe_removal.html b/docshell/test/navigation/test_sessionhistory_iframe_removal.html
new file mode 100644
index 0000000000..242e3baade
--- /dev/null
+++ b/docshell/test/navigation/test_sessionhistory_iframe_removal.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+ <title>Test for Session history + document.write</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest()">
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ var testWindow;
+ function runTest() {
+ testWindow = window.open("file_sessionhistory_iframe_removal.html", "", "width=360,height=480");
+ }
+
+ function finishTest() {
+ testWindow.close();
+ SimpleTest.finish();
+ }
+
+ </script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_shiftReload_and_pushState.html b/docshell/test/navigation/test_shiftReload_and_pushState.html
new file mode 100644
index 0000000000..7525e2e21f
--- /dev/null
+++ b/docshell/test/navigation/test_shiftReload_and_pushState.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+ <title>Test for Bug 1003100</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1003100">Mozilla Bug 1003100</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ var testWindow;
+ function runTest() {
+ testWindow = window.open("file_shiftReload_and_pushState.html", "", "width=360,height=480");
+ testWindow.onunload = function() { }; // to prevent bfcache
+ }
+
+ function finishTest() {
+ testWindow.close();
+ SimpleTest.finish();
+ }
+
+ </script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_ship_beforeunload_fired.html b/docshell/test/navigation/test_ship_beforeunload_fired.html
new file mode 100644
index 0000000000..e43711676b
--- /dev/null
+++ b/docshell/test/navigation/test_ship_beforeunload_fired.html
@@ -0,0 +1,63 @@
+<html>
+ <head>
+ <title>
+ Test that ensures beforeunload is fired when session-history-in-parent is enabled
+ </title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ /*
+ * This test ensures beforeunload is fired on the current page
+ * when it is entering BFCache and the next page is coming out
+ * from BFCache
+ *
+ * (1) The controller page opens a new window, and page A is loaded there.
+ * (2) Page A then navigates to page B, and a beforeunload event
+ * listener is registered on page B.
+ * (3) Page B then navigates back to page A, and the beforeunload handler
+ * should send a message to the controller page.
+ * (4) Page A then navigates back to page B to check if page B has
+ * been successfully added to BFCache.
+ */
+
+ var bc = new BroadcastChannel("ship_beforeunload");
+ var pageshowCount = 0;
+ var beforeUnloadFired = false;
+ bc.onmessage = function(event) {
+ if (event.data.type == "pageshow") {
+ ++pageshowCount;
+ if (pageshowCount == 1) {
+ bc.postMessage({action: "navigate_to_page_b"});
+ } else if (pageshowCount == 2) {
+ ok(!event.data.persisted, "?pageb shouldn't in BFCache because it's the first navigation");
+ bc.postMessage({action: "register_beforeunload", loadNextPageFromSessionHistory: true});
+ } else if (pageshowCount == 3) {
+ ok(event.data.persisted, "navigated back to page A that was in BFCacache from page B");
+ ok(beforeUnloadFired, "beforeunload has fired on page B");
+ bc.postMessage({action: "back_to_page_b", forwardNavigateToPageB: true});
+ } else if (pageshowCount == 4) {
+ ok(event.data.persisted, "page B has beforeunload fired and also entered BFCache");
+ bc.postMessage({action: "close"});
+ SimpleTest.finish();
+ }
+ } else if (event.data == "beforeunload_fired") {
+ beforeUnloadFired = true;
+ }
+ }
+
+ function runTest() {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["fission.bfcacheInParent", true],
+ ["docshell.shistory.bfcache.ship_allow_beforeunload_listeners", true]
+ ]},
+ function() {
+ window.open("file_ship_beforeunload_fired.html", "", "noopener");
+ }
+ );
+ }
+ </script>
+ <body onload="runTest()"></body>
+</html>
diff --git a/docshell/test/navigation/test_ship_beforeunload_fired_2.html b/docshell/test/navigation/test_ship_beforeunload_fired_2.html
new file mode 100644
index 0000000000..93669502a5
--- /dev/null
+++ b/docshell/test/navigation/test_ship_beforeunload_fired_2.html
@@ -0,0 +1,65 @@
+<html>
+ <head>
+ <title>
+ Test that ensures beforeunload is fired when session-history-in-parent is enabled
+ </title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ /*
+ * This test ensures beforeunload is fired on the current page
+ * when it is entering BFCache, and the next page is not coming from
+ * session history and also not coming out from BFCache.
+ *
+ * (1) The controller page opens a new window, and page A is loaded there.
+ * (2) Page A then navigates to page B, and a beforeunload event
+ * listener is registered on page B.
+ * (3) Page B then navigates to page C, and the beforeunload handler
+ * should send a message to the controller page.
+ * (4) Page C then navigates back to page B to check if page B has
+ * been successfully added to BFCache.
+ */
+
+ var bc = new BroadcastChannel("ship_beforeunload");
+ var pageshowCount = 0;
+
+ var beforeUnloadFired = false;
+ bc.onmessage = function(event) {
+ if (event.data.type == "pageshow") {
+ ++pageshowCount;
+ if (pageshowCount == 1) {
+ bc.postMessage({action: "navigate_to_page_b"});
+ } else if (pageshowCount == 2) {
+ ok(!event.data.persisted, "?page B shouldn't in BFCache because it's the first navigation");
+ bc.postMessage({action: "register_beforeunload",
+ loadNextPageFromSessionHistory: false});
+ } else if (pageshowCount == 3) {
+ ok(!event.data.persisted, "navigated to page C that was a new page");
+ ok(beforeUnloadFired, "beforeUnload should be fired on page B");
+ bc.postMessage({action: "back_to_page_b", forwardNavigateToPageB: false});
+ } else if (pageshowCount == 4) {
+ ok(event.data.persisted, "page B has been successfully added to BFCache");
+ bc.postMessage({action: "close"});
+ SimpleTest.finish();
+ }
+ } else if (event.data == "beforeunload_fired") {
+ beforeUnloadFired = true;
+ }
+ }
+
+ function runTest() {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["fission.bfcacheInParent", true],
+ ["docshell.shistory.bfcache.ship_allow_beforeunload_listeners", true]
+ ]},
+ function() {
+ window.open("file_ship_beforeunload_fired.html", "", "noopener");
+ }
+ );
+ }
+ </script>
+ <body onload="runTest()"></body>
+</html>
diff --git a/docshell/test/navigation/test_ship_beforeunload_fired_3.html b/docshell/test/navigation/test_ship_beforeunload_fired_3.html
new file mode 100644
index 0000000000..8951f269c5
--- /dev/null
+++ b/docshell/test/navigation/test_ship_beforeunload_fired_3.html
@@ -0,0 +1,65 @@
+<html>
+ <head>
+ <title>
+ Test that ensures beforeunload is fired when session-history-in-parent is enabled
+ </title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ /*
+ * This test ensures beforeunload is fired on the current page
+ * when it is entering BFCache, and the next page is not coming out
+ * from BFCache, but coming from session history.
+ *
+ * (1) The controller page opens a new window, and page A is loaded there.
+ * (2) Page A then navigates to page B, and a beforeunload event
+ * listener is registered on page B.
+ * (3) Page B then navigates back to page A, and the beforeunload handler
+ * should send a message to the controller page.
+ * (4) Page A then navigates back to page B to check if page B has
+ * been successfully added to BFCache.
+ */
+
+ var bc = new BroadcastChannel("ship_beforeunload");
+ var pageshowCount = 0;
+
+ var beforeUnloadFired = false;
+ bc.onmessage = function(event) {
+ if (event.data.type == "pageshow") {
+ ++pageshowCount;
+ if (pageshowCount == 1) {
+ bc.postMessage({action: "navigate_to_page_b", blockBFCache: true});
+ } else if (pageshowCount == 2) {
+ ok(!event.data.persisted, "?page B shouldn't in BFCache because it's the first navigation");
+ bc.postMessage({action: "register_beforeunload",
+ loadNextPageFromSessionHistory: true});
+ } else if (pageshowCount == 3) {
+ ok(!event.data.persisted, "navigated back to page A that was session history but not in BFCache");
+ ok(beforeUnloadFired, "beforeUnload should be fired on page B");
+ bc.postMessage({action: "back_to_page_b", forwardNavigateToPageB: true});
+ } else if (pageshowCount == 4) {
+ ok(event.data.persisted, "page B has been successfully added to BFCache");
+ bc.postMessage({action: "close"});
+ SimpleTest.finish();
+ }
+ } else if (event.data == "beforeunload_fired") {
+ beforeUnloadFired = true;
+ }
+ }
+
+ function runTest() {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["fission.bfcacheInParent", true],
+ ["docshell.shistory.bfcache.ship_allow_beforeunload_listeners", true]
+ ]},
+ function() {
+ window.open("file_ship_beforeunload_fired.html", "", "noopener");
+ }
+ );
+ }
+ </script>
+ <body onload="runTest()"></body>
+</html>
diff --git a/docshell/test/navigation/test_sibling-matching-parent.html b/docshell/test/navigation/test_sibling-matching-parent.html
new file mode 100644
index 0000000000..3c1bc768db
--- /dev/null
+++ b/docshell/test/navigation/test_sibling-matching-parent.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="NavigationUtils.js"></script>
+ <style type="text/css">
+ iframe { width: 90%; height: 50px; }
+ </style>
+<script>
+window.onload = async function() {
+ document.getElementById("active").innerHTML =
+ '<iframe src="navigate.html#parent.frames[0],location"></iframe>' +
+ '<iframe src="navigate.html#child1,open"></iframe>' +
+ '<iframe src="navigate.html#child2,form"></iframe>' +
+ '<iframe src="navigate.html#child3,hyperlink"></iframe>';
+
+ await waitForFinishedFrames(4);
+
+ await isNavigated(frames[0], "Should be able to navigate sibling with on-domain parent by setting location.");
+ await isNavigated(frames[1], "Should be able to navigate sibling with on-domain parent by calling window.open.");
+ await isNavigated(frames[2], "Should be able to navigate sibling with on-domain parent by submitting form.");
+ await isNavigated(frames[3], "Should be able to navigate sibling with on-domain parent by targeted hyperlink.");
+
+ await cleanupWindows();
+ SimpleTest.finish();
+};
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=408052">Mozilla Bug 408052</a>
+<div id="frames">
+<iframe name="child0" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+<iframe name="child1" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+<iframe name="child2" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+<iframe name="child3" src="http://test1.example.org:80/tests/docshell/test/navigation/blank.html"></iframe>
+</div>
+<div id="active"></div>
+<pre id="test">
+<script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_sibling-off-domain.html b/docshell/test/navigation/test_sibling-off-domain.html
new file mode 100644
index 0000000000..cd70d1ae91
--- /dev/null
+++ b/docshell/test/navigation/test_sibling-off-domain.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="NavigationUtils.js"></script>
+ <style type="text/css">
+ iframe { width: 90%; height: 50px; }
+ </style>
+<script>
+window.onload = async function() {
+ document.getElementById("active").innerHTML =
+ '<iframe src="http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#parent.frames[0],location"></iframe>' +
+ '<iframe src="http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#child1,open"></iframe>' +
+ '<iframe src="http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#child2,form"></iframe>' +
+ '<iframe src="http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#child3,hyperlink"></iframe>';
+
+ await waitForFinishedFrames(4);
+
+ isBlank(frames[0], "Should not be able to navigate off-domain sibling by setting location.");
+ isBlank(frames[1], "Should not be able to navigate off-domain sibling by calling window.open.");
+ isBlank(frames[2], "Should not be able to navigate off-domain sibling by submitting form.");
+ isBlank(frames[3], "Should not be able to navigate off-domain sibling by targeted hyperlink.");
+
+ await cleanupWindows();
+ SimpleTest.finish();
+};
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=408052">Mozilla Bug 408052</a>
+<div id="frames">
+<iframe name="child0" src="blank.html"></iframe>
+<iframe name="child1" src="blank.html"></iframe>
+<iframe name="child2" src="blank.html"></iframe>
+<iframe name="child3" src="blank.html"></iframe>
+</div>
+<div id="active"></div>
+<pre id="test">
+<script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_state_size.html b/docshell/test/navigation/test_state_size.html
new file mode 100644
index 0000000000..f089a460ec
--- /dev/null
+++ b/docshell/test/navigation/test_state_size.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test the max size of the data parameter of push/replaceState</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+ function test() {
+ let tooLarge = SpecialPowers.getIntPref("browser.history.maxStateObjectSize");
+ let allowed = Math.floor(tooLarge / 2);
+
+ history.pushState(new Array(allowed).join("a"), "");
+ ok(true, "Adding a state should succeed.");
+
+ try {
+ history.pushState(new Array(tooLarge).join("a"), "");
+ ok(false, "Adding a too large state object should fail.");
+ } catch(ex) {
+ ok(true, "Adding a too large state object should fail.");
+ }
+ SimpleTest.finish();
+ }
+ </script>
+</head>
+<body onload="test()">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_static_and_dynamic.html b/docshell/test/navigation/test_static_and_dynamic.html
new file mode 100644
index 0000000000..ff72a8188c
--- /dev/null
+++ b/docshell/test/navigation/test_static_and_dynamic.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+ <title>Test for static and dynamic frames and forward-back </title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ var testCount = 0; // Used by the test files.
+
+ SimpleTest.waitForExplicitFinish();
+
+ var testWindow;
+ function runTest() {
+ testWindow = window.open("file_static_and_dynamic_1.html", "", "width=360,height=480");
+ testWindow.onunload = function() { }; // to prevent bfcache
+ }
+
+ function finishTest() {
+ testWindow.close();
+ SimpleTest.finish();
+ }
+ </script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_triggeringprincipal_frame_nav.html b/docshell/test/navigation/test_triggeringprincipal_frame_nav.html
new file mode 100644
index 0000000000..d7046e9236
--- /dev/null
+++ b/docshell/test/navigation/test_triggeringprincipal_frame_nav.html
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1181370 - Test triggeringPrincipal for iframe navigations</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe1"></iframe>
+<iframe style="width:100%;" id="testframe2"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+/* Description of the test:
+ *
+ * +------------------------------------+
+ * | +----------+ +--------------+ |
+ * | | Frame 1 | | Frame 2 | |
+ * | +----------+ | | |
+ * | | +----------+ | |
+ * | | | Subframe | | |
+ * | | +----------+ | |
+ * | +--------------+ |
+ * +------------------------------------+
+ *
+ * Frame1: test1.mochi.test
+ * Frame2: test2.mochi.test
+ * Subframe: test2.mochi.test
+ *
+ * (*) Frame1 and Subframe set their document.domain to mochi.test
+ * (*) Frame1 navigates the Subframe
+ * (*) TriggeringPrincipal for the Subframe navigation should be
+ * ==> test1.mochi.test
+ * (*) LoadingPrincipal for the Subframe navigation should be
+ * ==> test2.mochi.test
+ */
+
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+const BASEDOMAIN1 = "http://test1.mochi.test:8888/";
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+const BASEDOMAIN2 = "http://test2.mochi.test:8888/";
+const PATH = "tests/docshell/test/navigation/";
+const BASEURL1 = BASEDOMAIN1 + PATH;
+const BASEURL2 = BASEDOMAIN2 + PATH;
+const TRIGGERINGPRINCIPALURI = BASEURL1 + "file_triggeringprincipal_frame_1.html";
+const LOADINGPRINCIPALURI = BASEURL2 + "file_triggeringprincipal_frame_2.html";
+
+SimpleTest.waitForExplicitFinish();
+
+window.addEventListener("message", receiveMessage);
+
+function receiveMessage(event) {
+ is(event.data.triggeringPrincipalURI, TRIGGERINGPRINCIPALURI,
+ "TriggeringPrincipal should be the navigating iframe (Frame 1)");
+ is(event.data.loadingPrincipalURI, LOADINGPRINCIPALURI,
+ "LoadingPrincipal should be the enclosing iframe (Frame 2)");
+ is(event.data.referrerURI, BASEDOMAIN1,
+ "The path of Referrer should be trimmed (Frame 1)");
+
+ window.removeEventListener("message", receiveMessage);
+ SimpleTest.finish();
+}
+
+var frame1 = document.getElementById("testframe1");
+frame1.src = BASEURL1 + "file_triggeringprincipal_frame_1.html";
+
+var frame2 = document.getElementById("testframe2");
+frame2.src = BASEURL2 + "file_triggeringprincipal_frame_2.html";
+
+</script>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_triggeringprincipal_frame_same_origin_nav.html b/docshell/test/navigation/test_triggeringprincipal_frame_same_origin_nav.html
new file mode 100644
index 0000000000..4483efb13e
--- /dev/null
+++ b/docshell/test/navigation/test_triggeringprincipal_frame_same_origin_nav.html
@@ -0,0 +1,63 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1639195 - Test triggeringPrincipal for iframe same-origin navigations</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe" src="http://example.com/"></iframe>
+
+<script type="text/javascript">
+/* We load an third-party iframe which then gets navigated by the iframe's
+ * parent by calling iframe.setAttribute("src", same-origin url) later in the
+ * test. We then verify the TriggeringPrincipal and LoadingPrincipal of the
+ * navigated iframe.
+ *
+ * +------------------------------------------+
+ * | |
+ * | +------------------+ |
+ * | | testframe | |
+ * | +------------------+ |
+ * | |
+ * | iframe.setAttribute("src", |
+ * | same-origin url) |
+ * | |
+ * +------------------------------------------+
+ */
+
+var testframe = document.getElementById("testframe");
+
+window.addEventListener("message", receiveMessage);
+
+const TRIGGERING_PRINCIPAL_URI =
+ "http://mochi.test:8888/tests/docshell/test/navigation/test_triggeringprincipal_frame_same_origin_nav.html";
+
+const LOADING_PRINCIPAL_URI = TRIGGERING_PRINCIPAL_URI;
+
+function receiveMessage(event) {
+ is(event.data.triggeringPrincipalURI.split("?")[0], TRIGGERING_PRINCIPAL_URI,
+ "TriggeringPrincipal should be the parent iframe");
+ is(event.data.loadingPrincipalURI.split("?")[0], TRIGGERING_PRINCIPAL_URI,
+ "LoadingPrincipal should be the parent iframe");
+
+ window.removeEventListener("message", receiveMessage);
+ SimpleTest.finish();
+}
+
+function performNavigation() {
+ testframe.removeEventListener("load", performNavigation);
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ testframe.setAttribute("src", "http://example.com/tests/docshell/test/navigation/file_triggeringprincipal_subframe_same_origin_nav.html");
+}
+
+// start the test
+SimpleTest.waitForExplicitFinish();
+
+testframe.addEventListener("load", performNavigation);
+</script>
+
+</body>
+</html>
diff --git a/docshell/test/navigation/test_triggeringprincipal_iframe_iframe_window_open.html b/docshell/test/navigation/test_triggeringprincipal_iframe_iframe_window_open.html
new file mode 100644
index 0000000000..115c5f4462
--- /dev/null
+++ b/docshell/test/navigation/test_triggeringprincipal_iframe_iframe_window_open.html
@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="NavigationUtils.js"></script>
+</head>
+<body>
+
+<iframe name="framea" id="framea" src="file_triggeringprincipal_iframe_iframe_window_open_frame_a.html"></iframe>
+<iframe name="frameb" id="frameb"></iframe>
+
+<script type="text/javascript">
+
+/* We load an iframe (Frame A) which then gets navigated by another iframe (Frame B)
+ * by calling window.open("http://", "Frame A") later in the test. We then verify the
+ * TriggeringPrincipal and LoadingPrincipal of the navigated iframe (Frame A).
+ *
+ * +---------------------------------------+
+ * | Parent |
+ * | |
+ * | +----------------------------+ |
+ * | | Frame A | |
+ * | | | |
+ * | | | |
+ * | +----------------------------+ |
+ * | |
+ * | +----------------------------+ |
+ * | | Frame B | |
+ * | | | |
+ * | | win.open("http://", "A") | |
+ * | +----------------------------+ |
+ * | |
+ * +---------------------------------------+
+ *
+ * Sequence of the test:
+ * [1] load Frame A
+ * [2] load Frame B which navigates A
+ * [3] load navigated Frame A and check triggeringPrincipal and loadingPrincipal
+ */
+
+const TRIGGERING_PRINCIPAL_URI =
+ "http://mochi.test:8888/tests/docshell/test/navigation/file_triggeringprincipal_iframe_iframe_window_open_frame_b.html";
+
+const LOADING_PRINCIPAL_URI =
+ "http://mochi.test:8888/tests/docshell/test/navigation/test_triggeringprincipal_iframe_iframe_window_open.html";
+
+var frameA = document.getElementById("framea");
+
+function checkResults() {
+ frameA.removeEventListener("load", checkResults);
+
+ var channel = SpecialPowers.wrap(frameA.contentWindow).docShell.currentDocumentChannel;
+ var triggeringPrincipal = channel.loadInfo.triggeringPrincipal.asciiSpec;
+ var loadingPrincipal = channel.loadInfo.loadingPrincipal.asciiSpec;
+
+ is(triggeringPrincipal, TRIGGERING_PRINCIPAL_URI,
+ "TriggeringPrincipal for targeted window.open() should be the iframe triggering the load");
+
+ is(frameA.contentDocument.referrer, TRIGGERING_PRINCIPAL_URI,
+ "Referrer for targeted window.open() should be the principal of the iframe triggering the load");
+
+ is(loadingPrincipal.split("?")[0], LOADING_PRINCIPAL_URI,
+ "LoadingPrincipal for targeted window.open() should be the containing document");
+
+ SimpleTest.finish();
+}
+
+function performNavigation() {
+ frameA.removeEventListener("load", performNavigation);
+ frameA.addEventListener("load", checkResults);
+
+ // load Frame B which then navigates Frame A
+ var frameB = document.getElementById("frameb");
+ frameB.src = "file_triggeringprincipal_iframe_iframe_window_open_frame_b.html";
+}
+
+// start the test
+SimpleTest.waitForExplicitFinish();
+
+frameA.addEventListener("load", performNavigation);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_triggeringprincipal_parent_iframe_window_open.html b/docshell/test/navigation/test_triggeringprincipal_parent_iframe_window_open.html
new file mode 100644
index 0000000000..1611ebf479
--- /dev/null
+++ b/docshell/test/navigation/test_triggeringprincipal_parent_iframe_window_open.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="NavigationUtils.js"></script>
+</head>
+<body>
+
+<iframe name="testframe" id="testframe" src="file_triggeringprincipal_iframe_iframe_window_open_base.html"></iframe>
+
+<script type="text/javascript">
+
+/* We load an iframe which then gets navigated by the iframe's parent by calling
+ * window.open("http://", iframe) later in the test. We then verify the
+ * TriggeringPrincipal and LoadingPrincipal of the navigated iframe.
+ *
+ * +------------------------------------------+
+ * | |
+ * | +------------------+ |
+ * | | testframe | |
+ * | +------------------+ |
+ * | |
+ * | window.open("http://", "testframe"); |
+ * | |
+ * +------------------------------------------+
+ */
+
+const TRIGGERING_PRINCIPAL_URI =
+ "http://mochi.test:8888/tests/docshell/test/navigation/test_triggeringprincipal_parent_iframe_window_open.html";
+
+const LOADING_PRINCIPAL_URI = TRIGGERING_PRINCIPAL_URI;
+
+var testframe = document.getElementById("testframe");
+
+function checkResults() {
+ testframe.removeEventListener("load", checkResults);
+
+ var channel = SpecialPowers.wrap(testframe.contentWindow).docShell.currentDocumentChannel;
+ var triggeringPrincipal = channel.loadInfo.triggeringPrincipal.asciiSpec.split("?")[0];
+ var loadingPrincipal = channel.loadInfo.loadingPrincipal.asciiSpec.split("?")[0];
+
+ is(triggeringPrincipal, TRIGGERING_PRINCIPAL_URI,
+ "TriggeringPrincipal for targeted window.open() should be the principal of the document");
+
+ is(testframe.contentDocument.referrer.split("?")[0], TRIGGERING_PRINCIPAL_URI,
+ "Referrer for targeted window.open() should be the principal of the document");
+
+ is(loadingPrincipal, LOADING_PRINCIPAL_URI,
+ "LoadingPrincipal for targeted window.open() should be the <iframe>.ownerDocument");
+
+ SimpleTest.finish();
+}
+
+function performNavigation() {
+ testframe.removeEventListener("load", performNavigation);
+ testframe.addEventListener("load", checkResults);
+ window.open("file_triggeringprincipal_parent_iframe_window_open_nav.html", "testframe");
+}
+
+// start the test
+SimpleTest.waitForExplicitFinish();
+
+testframe.addEventListener("load", performNavigation);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/navigation/test_triggeringprincipal_window_open.html b/docshell/test/navigation/test_triggeringprincipal_window_open.html
new file mode 100644
index 0000000000..439a125f97
--- /dev/null
+++ b/docshell/test/navigation/test_triggeringprincipal_window_open.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="NavigationUtils.js"></script>
+</head>
+<body>
+
+<script type="text/javascript">
+
+/* We call window.open() using different URIs and make sure the triggeringPrincipal
+ * loadingPrincipal are correct.
+ * Test1: window.open(http:)
+ * Test2: window.open(javascript:)
+ */
+
+const TRIGGERING_PRINCIPAL_URI =
+ "http://mochi.test:8888/tests/docshell/test/navigation/test_triggeringprincipal_window_open.html";
+
+SimpleTest.waitForExplicitFinish();
+
+const NUM_TESTS = 2;
+var test_counter = 0;
+
+function checkFinish() {
+ test_counter++;
+ if (test_counter === NUM_TESTS) {
+ SimpleTest.finish();
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Test 1: window.open(http:)
+var httpWin = window.open("file_triggeringprincipal_window_open.html", "_blank", "width=10,height=10");
+httpWin.onload = function() {
+ var httpChannel = SpecialPowers.wrap(httpWin).docShell.currentDocumentChannel;
+ var httpTriggeringPrincipal = httpChannel.loadInfo.triggeringPrincipal.asciiSpec;
+ var httpLoadingPrincipal = httpChannel.loadInfo.loadingPrincipal;
+
+ is(httpTriggeringPrincipal.split("?")[0], TRIGGERING_PRINCIPAL_URI,
+ "TriggeringPrincipal for window.open(http:) should be the principal of the document");
+
+ is(httpWin.document.referrer.split("?")[0], TRIGGERING_PRINCIPAL_URI,
+ "Referrer for window.open(http:) should be the principal of the document");
+
+ is(httpLoadingPrincipal, null,
+ "LoadingPrincipal for window.open(http:) should be null");
+
+ httpWin.close();
+ checkFinish();
+};
+
+// ----------------------------------------------------------------------------
+// Test 2: window.open(javascript:)
+var jsWin = window.open("javascript:'<html><body>js</body></html>';", "_blank", "width=10,height=10");
+jsWin.onload = function() {
+ var jsChannel = SpecialPowers.wrap(jsWin).docShell.currentDocumentChannel;
+ var jsTriggeringPrincipal = jsChannel.loadInfo.triggeringPrincipal.asciiSpec;
+ var jsLoadingPrincipal = jsChannel.loadInfo.loadingPrincipal;
+
+ is(jsTriggeringPrincipal.split("?")[0], TRIGGERING_PRINCIPAL_URI,
+ "TriggeringPrincipal for window.open(javascript:) should be the principal of the document");
+
+ is(jsWin.document.referrer, "",
+ "Referrer for window.open(javascript:) should be empty");
+
+ is(jsLoadingPrincipal, null,
+ "LoadingPrincipal for window.open(javascript:) should be null");
+
+ jsWin.close();
+ checkFinish();
+};
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/docshell/test/unit/AllowJavascriptChild.sys.mjs b/docshell/test/unit/AllowJavascriptChild.sys.mjs
new file mode 100644
index 0000000000..a7c3fa6172
--- /dev/null
+++ b/docshell/test/unit/AllowJavascriptChild.sys.mjs
@@ -0,0 +1,41 @@
+export class AllowJavascriptChild extends JSWindowActorChild {
+ async receiveMessage(msg) {
+ switch (msg.name) {
+ case "CheckScriptsAllowed":
+ return this.checkScriptsAllowed();
+ case "CheckFiredLoadEvent":
+ return this.contentWindow.wrappedJSObject.gFiredOnload;
+ case "CreateIframe":
+ return this.createIframe(msg.data.url);
+ }
+ return null;
+ }
+
+ handleEvent(event) {
+ if (event.type === "load") {
+ this.sendAsyncMessage("LoadFired");
+ }
+ }
+
+ checkScriptsAllowed() {
+ let win = this.contentWindow;
+
+ win.wrappedJSObject.gFiredOnclick = false;
+ win.document.body.click();
+ return win.wrappedJSObject.gFiredOnclick;
+ }
+
+ async createIframe(url) {
+ let doc = this.contentWindow.document;
+
+ let iframe = doc.createElement("iframe");
+ iframe.src = url;
+ doc.body.appendChild(iframe);
+
+ await new Promise(resolve => {
+ iframe.addEventListener("load", resolve, { once: true });
+ });
+
+ return iframe.browsingContext;
+ }
+}
diff --git a/docshell/test/unit/AllowJavascriptParent.sys.mjs b/docshell/test/unit/AllowJavascriptParent.sys.mjs
new file mode 100644
index 0000000000..5631fcdb09
--- /dev/null
+++ b/docshell/test/unit/AllowJavascriptParent.sys.mjs
@@ -0,0 +1,28 @@
+let loadPromises = new WeakMap();
+
+export class AllowJavascriptParent extends JSWindowActorParent {
+ async receiveMessage(msg) {
+ switch (msg.name) {
+ case "LoadFired":
+ let bc = this.browsingContext;
+ let deferred = loadPromises.get(bc);
+ if (deferred) {
+ loadPromises.delete(bc);
+ deferred.resolve(this);
+ }
+ break;
+ }
+ }
+
+ static promiseLoad(bc) {
+ let deferred = loadPromises.get(bc);
+ if (!deferred) {
+ deferred = {};
+ deferred.promise = new Promise(resolve => {
+ deferred.resolve = resolve;
+ });
+ loadPromises.set(bc, deferred);
+ }
+ return deferred.promise;
+ }
+}
diff --git a/docshell/test/unit/data/engine.xml b/docshell/test/unit/data/engine.xml
new file mode 100644
index 0000000000..3a2bd85c1b
--- /dev/null
+++ b/docshell/test/unit/data/engine.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>test_urifixup_search_engine</ShortName>
+<Description>test_urifixup_search_engine</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Url type="text/html" method="GET" template="https://www.example.org/">
+ <Param name="search" value="{searchTerms}"/>
+</Url>
+<SearchForm>https://www.example.org/</SearchForm>
+</SearchPlugin>
diff --git a/docshell/test/unit/data/enginePost.xml b/docshell/test/unit/data/enginePost.xml
new file mode 100644
index 0000000000..14775b6f0a
--- /dev/null
+++ b/docshell/test/unit/data/enginePost.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>test_urifixup_search_engine_post</ShortName>
+<Description>test_urifixup_search_engine_post</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Url type="text/html" method="POST" template="https://www.example.org/">
+ <Param name="q" value="{searchTerms}"/>
+</Url>
+<SearchForm>https://www.example.org/</SearchForm>
+</SearchPlugin>
diff --git a/docshell/test/unit/data/enginePrivate.xml b/docshell/test/unit/data/enginePrivate.xml
new file mode 100644
index 0000000000..7d87de98fa
--- /dev/null
+++ b/docshell/test/unit/data/enginePrivate.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
+<ShortName>test_urifixup_search_engine_private</ShortName>
+<Description>test_urifixup_search_engine_private</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Url type="text/html" method="GET" template="https://www.example.org/">
+ <Param name="private" value="{searchTerms}"/>
+</Url>
+<SearchForm>https://www.example.org/</SearchForm>
+</SearchPlugin>
diff --git a/docshell/test/unit/head_docshell.js b/docshell/test/unit/head_docshell.js
new file mode 100644
index 0000000000..9e2af04b6b
--- /dev/null
+++ b/docshell/test/unit/head_docshell.js
@@ -0,0 +1,106 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var { XPCOMUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/XPCOMUtils.sys.mjs"
+);
+
+ChromeUtils.defineESModuleGetters(this, {
+ SearchTestUtils: "resource://testing-common/SearchTestUtils.sys.mjs",
+ SearchUtils: "resource://gre/modules/SearchUtils.sys.mjs",
+ TestUtils: "resource://testing-common/TestUtils.sys.mjs",
+});
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+ AddonTestUtils: "resource://testing-common/AddonTestUtils.jsm",
+ HttpServer: "resource://testing-common/httpd.js",
+ NetUtil: "resource://gre/modules/NetUtil.jsm",
+});
+
+var profileDir = do_get_profile();
+
+const kSearchEngineID = "test_urifixup_search_engine";
+const kSearchEngineURL = "https://www.example.org/?search={searchTerms}";
+const kPrivateSearchEngineID = "test_urifixup_search_engine_private";
+const kPrivateSearchEngineURL =
+ "https://www.example.org/?private={searchTerms}";
+const kPostSearchEngineID = "test_urifixup_search_engine_post";
+const kPostSearchEngineURL = "https://www.example.org/";
+const kPostSearchEngineData = "q={searchTerms}";
+
+const SEARCH_CONFIG = [
+ {
+ appliesTo: [
+ {
+ included: {
+ everywhere: true,
+ },
+ },
+ ],
+ default: "yes",
+ webExtension: {
+ id: "fixup_search@search.mozilla.org",
+ },
+ },
+];
+
+async function setupSearchService() {
+ SearchTestUtils.init(this);
+
+ AddonTestUtils.init(this);
+ AddonTestUtils.overrideCertDB();
+ AddonTestUtils.createAppInfo(
+ "xpcshell@tests.mozilla.org",
+ "XPCShell",
+ "1",
+ "42"
+ );
+
+ await SearchTestUtils.useTestEngines(".", null, SEARCH_CONFIG);
+ await AddonTestUtils.promiseStartupManager();
+ await Services.search.init();
+}
+
+/**
+ * After useHttpServer() is called, this string contains the URL of the "data"
+ * directory, including the final slash.
+ */
+var gDataUrl;
+
+/**
+ * Initializes the HTTP server and ensures that it is terminated when tests end.
+ *
+ * @param {string} dir
+ * The test sub-directory to use for the engines.
+ * @returns {HttpServer}
+ * The HttpServer object in case further customization is needed.
+ */
+function useHttpServer(dir = "data") {
+ let httpServer = new HttpServer();
+ httpServer.start(-1);
+ httpServer.registerDirectory("/", do_get_cwd());
+ gDataUrl = `http://localhost:${httpServer.identity.primaryPort}/${dir}/`;
+ registerCleanupFunction(async function cleanup_httpServer() {
+ await new Promise(resolve => {
+ httpServer.stop(resolve);
+ });
+ });
+ return httpServer;
+}
+
+async function addTestEngines() {
+ useHttpServer();
+ // This is a hack, ideally we should be setting up a configuration with
+ // built-in engines, but the `chrome_settings_overrides` section that
+ // WebExtensions need is only defined for browser/
+ await SearchTestUtils.promiseNewSearchEngine({
+ url: `${gDataUrl}/engine.xml`,
+ });
+ await SearchTestUtils.promiseNewSearchEngine({
+ url: `${gDataUrl}/enginePrivate.xml`,
+ });
+ await SearchTestUtils.promiseNewSearchEngine({
+ url: `${gDataUrl}/enginePost.xml`,
+ });
+}
diff --git a/docshell/test/unit/test_URIFixup.js b/docshell/test/unit/test_URIFixup.js
new file mode 100644
index 0000000000..7967933b56
--- /dev/null
+++ b/docshell/test/unit/test_URIFixup.js
@@ -0,0 +1,123 @@
+var pref = "browser.fixup.typo.scheme";
+
+var data = [
+ {
+ // ttp -> http.
+ wrong: "ttp://www.example.com/",
+ fixed: "http://www.example.com/",
+ },
+ {
+ // htp -> http.
+ wrong: "htp://www.example.com/",
+ fixed: "http://www.example.com/",
+ },
+ {
+ // ttps -> https.
+ wrong: "ttps://www.example.com/",
+ fixed: "https://www.example.com/",
+ },
+ {
+ // tps -> https.
+ wrong: "tps://www.example.com/",
+ fixed: "https://www.example.com/",
+ },
+ {
+ // ps -> https.
+ wrong: "ps://www.example.com/",
+ fixed: "https://www.example.com/",
+ },
+ {
+ // htps -> https.
+ wrong: "htps://www.example.com/",
+ fixed: "https://www.example.com/",
+ },
+ {
+ // ile -> file.
+ wrong: "ile:///this/is/a/test.html",
+ fixed: "file:///this/is/a/test.html",
+ },
+ {
+ // le -> file.
+ wrong: "le:///this/is/a/test.html",
+ fixed: "file:///this/is/a/test.html",
+ },
+ {
+ // Replace ';' with ':'.
+ wrong: "http;//www.example.com/",
+ fixed: "http://www.example.com/",
+ noPrefValue: "http://http;//www.example.com/",
+ },
+ {
+ // Missing ':'.
+ wrong: "https//www.example.com/",
+ fixed: "https://www.example.com/",
+ noPrefValue: "http://https//www.example.com/",
+ },
+ {
+ // Missing ':' for file scheme.
+ wrong: "file///this/is/a/test.html",
+ fixed: "file:///this/is/a/test.html",
+ noPrefValue: "http://file///this/is/a/test.html",
+ },
+ {
+ // Valid should not be changed.
+ wrong: "https://example.com/this/is/a/test.html",
+ fixed: "https://example.com/this/is/a/test.html",
+ },
+ {
+ // Unmatched should not be changed.
+ wrong: "whatever://this/is/a/test.html",
+ fixed: "whatever://this/is/a/test.html",
+ },
+];
+
+var len = data.length;
+
+add_task(async function setup() {
+ await setupSearchService();
+ // Now we've initialised the search service, we force remove the engines
+ // it has, so they don't interfere with this test.
+ // Search engine integration is tested in test_URIFixup_search.js.
+ Services.search.wrappedJSObject._engines.clear();
+});
+
+// Make sure we fix what needs fixing when there is no pref set.
+add_task(function test_unset_pref_fixes_typos() {
+ Services.prefs.clearUserPref(pref);
+ for (let i = 0; i < len; ++i) {
+ let item = data[i];
+ let { preferredURI } = Services.uriFixup.getFixupURIInfo(
+ item.wrong,
+ Services.uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS
+ );
+ Assert.equal(preferredURI.spec, item.fixed);
+ }
+});
+
+// Make sure we don't do anything when the pref is explicitly
+// set to false.
+add_task(function test_false_pref_keeps_typos() {
+ Services.prefs.setBoolPref(pref, false);
+ for (let i = 0; i < len; ++i) {
+ let item = data[i];
+ let { preferredURI } = Services.uriFixup.getFixupURIInfo(
+ item.wrong,
+ Services.uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS
+ );
+ Assert.equal(preferredURI.spec, item.noPrefValue || item.wrong);
+ }
+});
+
+// Finally, make sure we still fix what needs fixing if the pref is
+// explicitly set to true.
+add_task(function test_true_pref_fixes_typos() {
+ Services.prefs.setBoolPref(pref, true);
+ for (let i = 0; i < len; ++i) {
+ let item = data[i];
+ let { preferredURI } = Services.uriFixup.getFixupURIInfo(
+ item.wrong,
+ Services.uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS
+ );
+ Assert.equal(preferredURI.spec, item.fixed);
+ }
+});
diff --git a/docshell/test/unit/test_URIFixup_check_host.js b/docshell/test/unit/test_URIFixup_check_host.js
new file mode 100644
index 0000000000..3331176082
--- /dev/null
+++ b/docshell/test/unit/test_URIFixup_check_host.js
@@ -0,0 +1,183 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const lazy = {};
+
+// Ensure DNS lookups don't hit the server
+XPCOMUtils.defineLazyServiceGetter(
+ lazy,
+ "gDNSOverride",
+ "@mozilla.org/network/native-dns-override;1",
+ "nsINativeDNSResolverOverride"
+);
+
+add_task(async function setup() {
+ Services.prefs.setStringPref("browser.fixup.alternate.prefix", "www.");
+ Services.prefs.setStringPref("browser.fixup.alternate.suffix", ".com");
+ Services.prefs.setStringPref("browser.fixup.alternate.protocol", "https");
+ Services.prefs.setBoolPref(
+ "browser.urlbar.dnsResolveFullyQualifiedNames",
+ true
+ );
+ registerCleanupFunction(function() {
+ Services.prefs.clearUserPref("browser.fixup.alternate.prefix");
+ Services.prefs.clearUserPref("browser.fixup.alternate.suffix");
+ Services.prefs.clearUserPref("browser.fixup.alternate.protocol");
+ Services.prefs.clearUserPref(
+ "browser.urlbar.dnsResolveFullyQualifiedNames"
+ );
+ });
+});
+
+// TODO: Export Listener from test_dns_override instead of duping
+class Listener {
+ constructor() {
+ this.promise = new Promise(resolve => {
+ this.resolve = resolve;
+ });
+ }
+
+ onLookupComplete(inRequest, inRecord, inStatus) {
+ this.resolve([inRequest, inRecord, inStatus]);
+ }
+
+ async firstAddress() {
+ let all = await this.addresses();
+ if (all.length) {
+ return all[0];
+ }
+ return undefined;
+ }
+
+ async addresses() {
+ let [, inRecord] = await this.promise;
+ let addresses = [];
+ if (!inRecord) {
+ return addresses; // returns []
+ }
+ inRecord.QueryInterface(Ci.nsIDNSAddrRecord);
+ while (inRecord.hasMore()) {
+ addresses.push(inRecord.getNextAddrAsString());
+ }
+ return addresses;
+ }
+
+ then() {
+ return this.promise.then.apply(this.promise, arguments);
+ }
+}
+
+const FAKE_IP = "::1";
+const FAKE_INTRANET_IP = "::2";
+const ORIGIN_ATTRIBUTE = {};
+
+add_task(async function test_uri_with_force_fixup() {
+ let listener = new Listener();
+ let { fixedURI } = Services.uriFixup.forceHttpFixup("http://www.example.com");
+
+ lazy.gDNSOverride.addIPOverride(fixedURI.displayHost, FAKE_IP);
+
+ Services.uriFixup.checkHost(fixedURI, listener, ORIGIN_ATTRIBUTE);
+ Assert.equal(
+ await listener.firstAddress(),
+ FAKE_IP,
+ "Should've received fake IP"
+ );
+
+ lazy.gDNSOverride.clearHostOverride(fixedURI.displayHost);
+ Services.dns.clearCache(false);
+});
+
+add_task(async function test_uri_with_get_fixup() {
+ let listener = new Listener();
+ let uri = Services.io.newURI("http://www.example.com");
+
+ lazy.gDNSOverride.addIPOverride(uri.displayHost, FAKE_IP);
+
+ Services.uriFixup.checkHost(uri, listener, ORIGIN_ATTRIBUTE);
+ Assert.equal(
+ await listener.firstAddress(),
+ FAKE_IP,
+ "Should've received fake IP"
+ );
+
+ lazy.gDNSOverride.clearHostOverride(uri.displayHost);
+ Services.dns.clearCache(false);
+});
+
+add_task(async function test_intranet_like_uri() {
+ let listener = new Listener();
+ let uri = Services.io.newURI("http://someintranet");
+
+ lazy.gDNSOverride.addIPOverride(uri.displayHost, FAKE_IP);
+ // Hosts without periods should end with a period to make them FQN
+ lazy.gDNSOverride.addIPOverride(uri.displayHost + ".", FAKE_INTRANET_IP);
+
+ Services.uriFixup.checkHost(uri, listener, ORIGIN_ATTRIBUTE);
+ Assert.deepEqual(
+ await listener.addresses(),
+ FAKE_INTRANET_IP,
+ "Should've received fake intranet IP"
+ );
+
+ lazy.gDNSOverride.clearHostOverride(uri.displayHost);
+ lazy.gDNSOverride.clearHostOverride(uri.displayHost + ".");
+ Services.dns.clearCache(false);
+});
+
+add_task(async function test_intranet_like_uri_without_fixup() {
+ let listener = new Listener();
+ let uri = Services.io.newURI("http://someintranet");
+ Services.prefs.setBoolPref(
+ "browser.urlbar.dnsResolveFullyQualifiedNames",
+ false
+ );
+
+ lazy.gDNSOverride.addIPOverride(uri.displayHost, FAKE_IP);
+ // Hosts without periods should end with a period to make them FQN
+ lazy.gDNSOverride.addIPOverride(uri.displayHost + ".", FAKE_INTRANET_IP);
+
+ Services.uriFixup.checkHost(uri, listener, ORIGIN_ATTRIBUTE);
+ Assert.deepEqual(
+ await listener.addresses(),
+ FAKE_IP,
+ "Should've received non-fixed up IP"
+ );
+
+ lazy.gDNSOverride.clearHostOverride(uri.displayHost);
+ lazy.gDNSOverride.clearHostOverride(uri.displayHost + ".");
+ Services.dns.clearCache(false);
+});
+
+add_task(async function test_ip_address() {
+ let listener = new Listener();
+ let uri = Services.io.newURI("http://192.168.0.1");
+
+ // Testing IP address being passed into the method
+ // requires observing if the asyncResolve method gets called
+ let didResolve = false;
+ let topic = "uri-fixup-check-dns";
+ let observer = {
+ observe(aSubject, aTopicInner, aData) {
+ if (aTopicInner == topic) {
+ didResolve = true;
+ }
+ },
+ };
+ Services.obs.addObserver(observer, topic);
+ lazy.gDNSOverride.addIPOverride(uri.displayHost, FAKE_IP);
+
+ Services.uriFixup.checkHost(uri, listener, ORIGIN_ATTRIBUTE);
+ Assert.equal(
+ didResolve,
+ false,
+ "Passing an IP address should not conduct a host lookup"
+ );
+
+ lazy.gDNSOverride.clearHostOverride(uri.displayHost);
+ Services.dns.clearCache(false);
+ Services.obs.removeObserver(observer, topic);
+});
diff --git a/docshell/test/unit/test_URIFixup_external_protocol_fallback.js b/docshell/test/unit/test_URIFixup_external_protocol_fallback.js
new file mode 100644
index 0000000000..5d37be55fb
--- /dev/null
+++ b/docshell/test/unit/test_URIFixup_external_protocol_fallback.js
@@ -0,0 +1,107 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// Test whether fallback mechanism is working if don't trust nsIExternalProtocolService.
+
+const { AppConstants } = ChromeUtils.importESModule(
+ "resource://gre/modules/AppConstants.sys.mjs"
+);
+const { MockRegistrar } = ChromeUtils.importESModule(
+ "resource://testing-common/MockRegistrar.sys.mjs"
+);
+
+add_task(async function setup() {
+ info(
+ "Prepare mock nsIExternalProtocolService whose externalProtocolHandlerExists returns always true"
+ );
+ const externalProtocolService = Cc[
+ "@mozilla.org/uriloader/external-protocol-service;1"
+ ].getService(Ci.nsIExternalProtocolService);
+ const mockId = MockRegistrar.register(
+ "@mozilla.org/uriloader/external-protocol-service;1",
+ {
+ getProtocolHandlerInfo: scheme =>
+ externalProtocolService.getProtocolHandlerInfo(scheme),
+ externalProtocolHandlerExists: () => true,
+ QueryInterface: ChromeUtils.generateQI(["nsIExternalProtocolService"]),
+ }
+ );
+ const mockExternalProtocolService = Cc[
+ "@mozilla.org/uriloader/external-protocol-service;1"
+ ].getService(Ci.nsIExternalProtocolService);
+ Assert.ok(
+ mockExternalProtocolService.externalProtocolHandlerExists("__invalid__"),
+ "Mock service is working"
+ );
+
+ info("Register new dummy protocol");
+ const dummyProtocolHandlerInfo = externalProtocolService.getProtocolHandlerInfo(
+ "dummy"
+ );
+ const handlerService = Cc[
+ "@mozilla.org/uriloader/handler-service;1"
+ ].getService(Ci.nsIHandlerService);
+ handlerService.store(dummyProtocolHandlerInfo);
+
+ info("Prepare test search engine");
+ await setupSearchService();
+ await addTestEngines();
+ await Services.search.setDefault(
+ Services.search.getEngineByName(kSearchEngineID),
+ Ci.nsISearchService.CHANGE_REASON_UNKNOWN
+ );
+
+ registerCleanupFunction(() => {
+ handlerService.remove(dummyProtocolHandlerInfo);
+ MockRegistrar.unregister(mockId);
+ });
+});
+
+add_task(function basic() {
+ const testData = [
+ {
+ input: "mailto:test@example.com",
+ expected:
+ isSupportedInHandlerService("mailto") ||
+ // Thunderbird IS a mailto handler, it doesn't have handlers.
+ AppConstants.MOZ_APP_NAME == "thunderbird"
+ ? "mailto:test@example.com"
+ : "http://mailto:test@example.com/",
+ },
+ {
+ input: "keyword:search",
+ expected: "https://www.example.org/?search=keyword%3Asearch",
+ },
+ {
+ input: "dummy:protocol",
+ expected: "dummy:protocol",
+ },
+ ];
+
+ for (const { input, expected } of testData) {
+ assertFixup(input, expected);
+ }
+});
+
+function assertFixup(input, expected) {
+ const { preferredURI } = Services.uriFixup.getFixupURIInfo(
+ input,
+ Services.uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS
+ );
+ Assert.equal(preferredURI.spec, expected);
+}
+
+function isSupportedInHandlerService(scheme) {
+ const externalProtocolService = Cc[
+ "@mozilla.org/uriloader/external-protocol-service;1"
+ ].getService(Ci.nsIExternalProtocolService);
+ const handlerService = Cc[
+ "@mozilla.org/uriloader/handler-service;1"
+ ].getService(Ci.nsIHandlerService);
+ return handlerService.exists(
+ externalProtocolService.getProtocolHandlerInfo(scheme)
+ );
+}
diff --git a/docshell/test/unit/test_URIFixup_forced.js b/docshell/test/unit/test_URIFixup_forced.js
new file mode 100644
index 0000000000..dad24aaa99
--- /dev/null
+++ b/docshell/test/unit/test_URIFixup_forced.js
@@ -0,0 +1,162 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+var data = [
+ {
+ // singleword.
+ wrong: "http://example/",
+ fixed: "https://www.example.com/",
+ },
+ {
+ // upgrade protocol.
+ wrong: "http://www.example.com/",
+ fixed: "https://www.example.com/",
+ noAlternateURI: true,
+ },
+ {
+ // no difference.
+ wrong: "https://www.example.com/",
+ fixed: "https://www.example.com/",
+ noProtocolFixup: true,
+ noAlternateURI: true,
+ },
+ {
+ // don't add prefix when there's more than one dot.
+ wrong: "http://www.example.abc.def/",
+ fixed: "https://www.example.abc.def/",
+ noAlternateURI: true,
+ },
+ {
+ // http -> https.
+ wrong: "http://www.example/",
+ fixed: "https://www.example/",
+ noAlternateURI: true,
+ },
+ {
+ // domain.com -> https://www.domain.com.
+ wrong: "http://example.com/",
+ fixed: "https://www.example.com/",
+ },
+ {
+ // example/example... -> https://www.example.com/example/
+ wrong: "http://example/example/",
+ fixed: "https://www.example.com/example/",
+ },
+ {
+ // example/example/s#q -> www.example.com/example/s#q.
+ wrong: "http://example/example/s#q",
+ fixed: "https://www.example.com/example/s#q",
+ },
+ {
+ wrong: "http://モジラ.org",
+ fixed: "https://www.xn--yck6dwa.org/",
+ },
+ {
+ wrong: "http://モジラ",
+ fixed: "https://www.xn--yck6dwa.com/",
+ },
+ {
+ wrong: "http://xn--yck6dwa",
+ fixed: "https://www.xn--yck6dwa.com/",
+ },
+ {
+ wrong: "https://モジラ.org",
+ fixed: "https://www.xn--yck6dwa.org/",
+ noProtocolFixup: true,
+ },
+ {
+ wrong: "https://モジラ",
+ fixed: "https://www.xn--yck6dwa.com/",
+ noProtocolFixup: true,
+ },
+ {
+ wrong: "https://xn--yck6dwa",
+ fixed: "https://www.xn--yck6dwa.com/",
+ noProtocolFixup: true,
+ },
+ {
+ // Host is https -> fixup typos of protocol
+ wrong: "htp://https://mozilla.org",
+ fixed: "http://https//mozilla.org",
+ noAlternateURI: true,
+ },
+ {
+ // Host is http -> fixup typos of protocol
+ wrong: "ttp://http://mozilla.org",
+ fixed: "http://http//mozilla.org",
+ noAlternateURI: true,
+ },
+ {
+ // Host is localhost -> fixup typos of protocol
+ wrong: "htps://localhost://mozilla.org",
+ fixed: "https://localhost//mozilla.org",
+ noAlternateURI: true,
+ },
+ {
+ // view-source is not http/https -> error
+ wrong: "view-source:http://example/example/example/example",
+ reject: true,
+ comment: "Scheme should be either http or https",
+ },
+ {
+ // file is not http/https -> error
+ wrong: "file://http://example/example/example/example",
+ reject: true,
+ comment: "Scheme should be either http or https",
+ },
+ {
+ // Protocol is missing -> error
+ wrong: "example.com",
+ reject: true,
+ comment: "Scheme should be either http or https",
+ },
+ {
+ // Empty input -> error
+ wrong: "",
+ reject: true,
+ comment: "Should pass a non-null uri",
+ },
+];
+
+add_task(async function setup() {
+ Services.prefs.setStringPref("browser.fixup.alternate.prefix", "www.");
+ Services.prefs.setStringPref("browser.fixup.alternate.suffix", ".com");
+ Services.prefs.setStringPref("browser.fixup.alternate.protocol", "https");
+ registerCleanupFunction(function() {
+ Services.prefs.clearUserPref("browser.fixup.alternate.prefix");
+ Services.prefs.clearUserPref("browser.fixup.alternate.suffix");
+ Services.prefs.clearUserPref("browser.fixup.alternate.protocol");
+ });
+});
+
+add_task(function test_default_https_pref() {
+ for (let item of data) {
+ if (item.reject) {
+ Assert.throws(
+ () => Services.uriFixup.forceHttpFixup(item.wrong),
+ /NS_ERROR_FAILURE/,
+ item.comment
+ );
+ } else {
+ let {
+ fixupChangedProtocol,
+ fixupCreatedAlternateURI,
+ fixedURI,
+ } = Services.uriFixup.forceHttpFixup(item.wrong);
+ Assert.equal(fixedURI.spec, item.fixed, "Specs should be the same");
+ Assert.equal(
+ fixupChangedProtocol,
+ !item.noProtocolFixup,
+ `fixupChangedProtocol should be ${!item.noAlternateURI}`
+ );
+ Assert.equal(
+ fixupCreatedAlternateURI,
+ !item.noAlternateURI,
+ `fixupCreatedAlternateURI should be ${!item.limitedFixup}`
+ );
+ }
+ }
+});
diff --git a/docshell/test/unit/test_URIFixup_info.js b/docshell/test/unit/test_URIFixup_info.js
new file mode 100644
index 0000000000..89514cb00c
--- /dev/null
+++ b/docshell/test/unit/test_URIFixup_info.js
@@ -0,0 +1,1075 @@
+const { AppConstants } = ChromeUtils.importESModule(
+ "resource://gre/modules/AppConstants.sys.mjs"
+);
+
+const kForceDNSLookup = "browser.fixup.dns_first_for_single_words";
+const kFixupEnabled = "browser.fixup.alternate.enabled";
+
+// TODO(bug 1522134), this test should also use
+// combinations of the following flags.
+var flagInputs = [
+ Services.uriFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP,
+ Services.uriFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP |
+ Services.uriFixup.FIXUP_FLAG_PRIVATE_CONTEXT,
+ Services.uriFixup.FIXUP_FLAGS_MAKE_ALTERNATE_URI,
+ Services.uriFixup.FIXUP_FLAG_FORCE_ALTERNATE_URI,
+ Services.uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS,
+ // This should not really generate a search, but it does, see Bug 1588118.
+ Services.uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS |
+ Services.uriFixup.FIXUP_FLAG_PRIVATE_CONTEXT,
+];
+
+/*
+ The following properties are supported for these test cases:
+ {
+ input: "", // Input string, required
+ fixedURI: "", // Expected fixedURI
+ alternateURI: "", // Expected alternateURI
+ keywordLookup: false, // Whether a keyword lookup is expected
+ protocolChange: false, // Whether a protocol change is expected
+ inWhitelist: false, // Whether the input host is in the whitelist
+ affectedByDNSForSingleWordHosts: false, // Whether the input host could be a host, but is normally assumed to be a keyword query
+ }
+*/
+var testcases = [
+ {
+ input: "about:home",
+ fixedURI: "about:home",
+ },
+ {
+ input: "http://www.mozilla.org",
+ fixedURI: "http://www.mozilla.org/",
+ },
+ {
+ input: "http://127.0.0.1/",
+ fixedURI: "http://127.0.0.1/",
+ },
+ {
+ input: "file:///foo/bar",
+ fixedURI: "file:///foo/bar",
+ },
+ {
+ input: "://www.mozilla.org",
+ fixedURI: "http://www.mozilla.org/",
+ protocolChange: true,
+ },
+ {
+ input: "www.mozilla.org",
+ fixedURI: "http://www.mozilla.org/",
+ protocolChange: true,
+ },
+ {
+ input: "http://mozilla/",
+ fixedURI: "http://mozilla/",
+ alternateURI: "https://www.mozilla.com/",
+ },
+ {
+ input: "http://test./",
+ fixedURI: "http://test./",
+ alternateURI: "https://www.test./",
+ },
+ {
+ input: "127.0.0.1",
+ fixedURI: "http://127.0.0.1/",
+ protocolChange: true,
+ },
+ {
+ input: "1.2.3.4/",
+ fixedURI: "http://1.2.3.4/",
+ protocolChange: true,
+ },
+ {
+ input: "1.2.3.4/foo",
+ fixedURI: "http://1.2.3.4/foo",
+ protocolChange: true,
+ },
+ {
+ input: "1.2.3.4:8000",
+ fixedURI: "http://1.2.3.4:8000/",
+ protocolChange: true,
+ },
+ {
+ input: "1.2.3.4:8000/",
+ fixedURI: "http://1.2.3.4:8000/",
+ protocolChange: true,
+ },
+ {
+ input: "1.2.3.4:8000/foo",
+ fixedURI: "http://1.2.3.4:8000/foo",
+ protocolChange: true,
+ },
+ {
+ input: "192.168.10.110",
+ fixedURI: "http://192.168.10.110/",
+ protocolChange: true,
+ },
+ {
+ input: "192.168.10.110/123",
+ fixedURI: "http://192.168.10.110/123",
+ protocolChange: true,
+ },
+ {
+ input: "192.168.10.110/123foo",
+ fixedURI: "http://192.168.10.110/123foo",
+ protocolChange: true,
+ },
+ {
+ input: "192.168.10.110:1234/123",
+ fixedURI: "http://192.168.10.110:1234/123",
+ protocolChange: true,
+ },
+ {
+ input: "192.168.10.110:1234/123foo",
+ fixedURI: "http://192.168.10.110:1234/123foo",
+ protocolChange: true,
+ },
+ {
+ input: "1.2.3",
+ fixedURI: "http://1.2.0.3/",
+ protocolChange: true,
+ },
+ {
+ input: "1.2.3/",
+ fixedURI: "http://1.2.0.3/",
+ protocolChange: true,
+ },
+ {
+ input: "1.2.3/foo",
+ fixedURI: "http://1.2.0.3/foo",
+ protocolChange: true,
+ },
+ {
+ input: "1.2.3/123",
+ fixedURI: "http://1.2.0.3/123",
+ protocolChange: true,
+ },
+ {
+ input: "1.2.3:8000",
+ fixedURI: "http://1.2.0.3:8000/",
+ protocolChange: true,
+ },
+ {
+ input: "1.2.3:8000/",
+ fixedURI: "http://1.2.0.3:8000/",
+ protocolChange: true,
+ },
+ {
+ input: "1.2.3:8000/foo",
+ fixedURI: "http://1.2.0.3:8000/foo",
+ protocolChange: true,
+ },
+ {
+ input: "1.2.3:8000/123",
+ fixedURI: "http://1.2.0.3:8000/123",
+ protocolChange: true,
+ },
+ {
+ input: "http://1.2.3",
+ fixedURI: "http://1.2.0.3/",
+ },
+ {
+ input: "http://1.2.3/",
+ fixedURI: "http://1.2.0.3/",
+ },
+ {
+ input: "http://1.2.3/foo",
+ fixedURI: "http://1.2.0.3/foo",
+ },
+ {
+ input: "[::1]",
+ fixedURI: "http://[::1]/",
+ protocolChange: true,
+ },
+ {
+ input: "[::1]/",
+ fixedURI: "http://[::1]/",
+ protocolChange: true,
+ },
+ {
+ input: "[::1]:8000",
+ fixedURI: "http://[::1]:8000/",
+ protocolChange: true,
+ },
+ {
+ input: "[::1]:8000/",
+ fixedURI: "http://[::1]:8000/",
+ protocolChange: true,
+ },
+ {
+ input: "[[::1]]/",
+ keywordLookup: true,
+ },
+ {
+ input: "[fe80:cd00:0:cde:1257:0:211e:729c]",
+ fixedURI: "http://[fe80:cd00:0:cde:1257:0:211e:729c]/",
+ protocolChange: true,
+ },
+ {
+ input: "[64:ff9b::8.8.8.8]",
+ fixedURI: "http://[64:ff9b::808:808]/",
+ protocolChange: true,
+ },
+ {
+ input: "[64:ff9b::8.8.8.8]/~moz",
+ fixedURI: "http://[64:ff9b::808:808]/~moz",
+ protocolChange: true,
+ },
+ {
+ input: "[::1][::1]",
+ keywordLookup: true,
+ },
+ {
+ input: "[::1][100",
+ keywordLookup: true,
+ },
+ {
+ input: "[::1]]",
+ keywordLookup: true,
+ },
+ {
+ input: "1234",
+ fixedURI: "http://0.0.4.210/",
+ keywordLookup: true,
+ protocolChange: true,
+ affectedByDNSForSingleWordHosts: true,
+ },
+ {
+ input: "whitelisted/foo.txt",
+ fixedURI: "http://whitelisted/foo.txt",
+ alternateURI: "https://www.whitelisted.com/foo.txt",
+ protocolChange: true,
+ },
+ {
+ input: "mozilla",
+ fixedURI: "http://mozilla/",
+ alternateURI: "https://www.mozilla.com/",
+ keywordLookup: true,
+ protocolChange: true,
+ affectedByDNSForSingleWordHosts: true,
+ },
+ {
+ input: "test.",
+ fixedURI: "http://test./",
+ alternateURI: "https://www.test./",
+ keywordLookup: true,
+ protocolChange: true,
+ affectedByDNSForSingleWordHosts: true,
+ },
+ {
+ input: ".test",
+ fixedURI: "http://.test/",
+ alternateURI: "https://www.test/",
+ keywordLookup: true,
+ protocolChange: true,
+ affectedByDNSForSingleWordHosts: true,
+ },
+ {
+ input: "mozilla is amazing",
+ keywordLookup: true,
+ },
+ {
+ input: "search ?mozilla",
+ keywordLookup: true,
+ },
+ {
+ input: "mozilla .com",
+ keywordLookup: true,
+ },
+ {
+ input: "what if firefox?",
+ keywordLookup: true,
+ },
+ {
+ input: "london's map",
+ keywordLookup: true,
+ },
+ {
+ input: "mozilla ",
+ fixedURI: "http://mozilla/",
+ alternateURI: "https://www.mozilla.com/",
+ keywordLookup: true,
+ protocolChange: true,
+ affectedByDNSForSingleWordHosts: true,
+ },
+ {
+ input: " mozilla ",
+ fixedURI: "http://mozilla/",
+ alternateURI: "https://www.mozilla.com/",
+ keywordLookup: true,
+ protocolChange: true,
+ affectedByDNSForSingleWordHosts: true,
+ },
+ {
+ input: "mozilla \\",
+ keywordLookup: true,
+ },
+ {
+ input: "mozilla \\ foo.txt",
+ keywordLookup: true,
+ },
+ {
+ input: "mozilla \\\r foo.txt",
+ keywordLookup: true,
+ },
+ {
+ input: "mozilla\n",
+ fixedURI: "http://mozilla/",
+ alternateURI: "https://www.mozilla.com/",
+ keywordLookup: true,
+ protocolChange: true,
+ affectedByDNSForSingleWordHosts: true,
+ },
+ {
+ input: "mozilla \r\n",
+ fixedURI: "http://mozilla/",
+ alternateURI: "https://www.mozilla.com/",
+ keywordLookup: true,
+ protocolChange: true,
+ affectedByDNSForSingleWordHosts: true,
+ },
+ {
+ input: "moz\r\nfirefox\nos\r",
+ fixedURI: "http://mozfirefoxos/",
+ alternateURI: "https://www.mozfirefoxos.com/",
+ keywordLookup: true,
+ protocolChange: true,
+ affectedByDNSForSingleWordHosts: true,
+ },
+ {
+ input: "moz\r\n firefox\n",
+ keywordLookup: true,
+ },
+ {
+ input: "",
+ keywordLookup: true,
+ },
+ {
+ input: "[]",
+ keywordLookup: true,
+ },
+ {
+ input: "http://whitelisted/",
+ fixedURI: "http://whitelisted/",
+ alternateURI: "https://www.whitelisted.com/",
+ inWhitelist: true,
+ },
+ {
+ input: "whitelisted",
+ fixedURI: "http://whitelisted/",
+ alternateURI: "https://www.whitelisted.com/",
+ protocolChange: true,
+ inWhitelist: true,
+ },
+ {
+ input: "whitelisted.",
+ fixedURI: "http://whitelisted./",
+ alternateURI: "https://www.whitelisted./",
+ protocolChange: true,
+ inWhitelist: true,
+ },
+ {
+ input: "mochi.test",
+ fixedURI: "http://mochi.test/",
+ alternateURI: "https://www.mochi.test/",
+ protocolChange: true,
+ inWhitelist: true,
+ },
+ // local.domain is a whitelisted suffix...
+ {
+ input: "some.local.domain",
+ fixedURI: "http://some.local.domain/",
+ protocolChange: true,
+ inWhitelist: true,
+ },
+ // ...but .domain is not.
+ {
+ input: "some.domain",
+ fixedURI: "http://some.domain/",
+ alternateURI: "https://www.some.domain/",
+ keywordLookup: true,
+ protocolChange: true,
+ affectedByDNSForSingleWordHosts: true,
+ },
+ {
+ input: "café.com",
+ fixedURI: "http://xn--caf-dma.com/",
+ alternateURI: "https://www.xn--caf-dma.com/",
+ protocolChange: true,
+ },
+ {
+ input: "mozilla.nonexistent",
+ fixedURI: "http://mozilla.nonexistent/",
+ alternateURI: "https://www.mozilla.nonexistent/",
+ keywordLookup: true,
+ protocolChange: true,
+ affectedByDNSForSingleWordHosts: true,
+ },
+ {
+ input: "mochi.ocm",
+ fixedURI: "http://mochi.com/",
+ alternateURI: "https://www.mochi.com/",
+ protocolChange: true,
+ },
+ {
+ input: "47.6182,-122.830",
+ fixedURI: "http://47.6182,-122.830/",
+ keywordLookup: true,
+ protocolChange: true,
+ affectedByDNSForSingleWordHosts: true,
+ },
+ {
+ input: "-47.6182,-23.51",
+ fixedURI: "http://-47.6182,-23.51/",
+ keywordLookup: true,
+ protocolChange: true,
+ affectedByDNSForSingleWordHosts: true,
+ },
+ {
+ input: "-22.14,23.51-",
+ fixedURI: "http://-22.14,23.51-/",
+ keywordLookup: true,
+ protocolChange: true,
+ affectedByDNSForSingleWordHosts: true,
+ },
+ {
+ input: "32.7",
+ fixedURI: "http://32.0.0.7/",
+ keywordLookup: true,
+ protocolChange: true,
+ affectedByDNSForSingleWordHosts: true,
+ },
+ {
+ input: "5+2",
+ fixedURI: "http://5+2/",
+ alternateURI: "https://www.5+2.com/",
+ keywordLookup: true,
+ protocolChange: true,
+ affectedByDNSForSingleWordHosts: true,
+ },
+ {
+ input: "5/2",
+ fixedURI: "http://0.0.0.5/2",
+ keywordLookup: true,
+ protocolChange: true,
+ affectedByDNSForSingleWordHosts: true,
+ },
+ {
+ input: "moz ?.::%27",
+ keywordLookup: true,
+ },
+ {
+ input: "mozilla.com/?q=search",
+ fixedURI: "http://mozilla.com/?q=search",
+ alternateURI: "https://www.mozilla.com/?q=search",
+ protocolChange: true,
+ },
+ {
+ input: "mozilla.com?q=search",
+ fixedURI: "http://mozilla.com/?q=search",
+ alternateURI: "https://www.mozilla.com/?q=search",
+ protocolChange: true,
+ },
+ {
+ input: "mozilla.com ?q=search",
+ keywordLookup: true,
+ },
+ {
+ input: "mozilla.com.?q=search",
+ fixedURI: "http://mozilla.com./?q=search",
+ protocolChange: true,
+ },
+ {
+ input: "mozilla.com'?q=search",
+ fixedURI: "http://mozilla.com/?q=search",
+ alternateURI: "https://www.mozilla.com/?q=search",
+ protocolChange: true,
+ },
+ {
+ input: "mozilla.com':search",
+ keywordLookup: true,
+ },
+ {
+ input: "[mozilla]",
+ keywordLookup: true,
+ },
+ {
+ input: "':?",
+ fixedURI: "http://'/?",
+ alternateURI: "https://www.'.com/?",
+ keywordLookup: true,
+ protocolChange: true,
+ affectedByDNSForSingleWordHosts: true,
+ },
+ {
+ input: "whitelisted?.com",
+ fixedURI: "http://whitelisted/?.com",
+ alternateURI: "https://www.whitelisted.com/?.com",
+ protocolChange: true,
+ },
+ {
+ input: "?'.com",
+ keywordLookup: true,
+ },
+ {
+ input: ".com",
+ keywordLookup: true,
+ affectedByDNSForSingleWordHosts: true,
+ fixedURI: "http://.com/",
+ alternateURI: "https://www.com/",
+ protocolChange: true,
+ },
+ {
+ input: "' ?.com",
+ keywordLookup: true,
+ },
+ {
+ input: "?mozilla",
+ keywordLookup: true,
+ },
+ {
+ input: "??mozilla",
+ keywordLookup: true,
+ },
+ {
+ input: "mozilla/",
+ fixedURI: "http://mozilla/",
+ alternateURI: "https://www.mozilla.com/",
+ protocolChange: true,
+ },
+ {
+ input: "mozilla",
+ fixedURI: "http://mozilla/",
+ alternateURI: "https://www.mozilla.com/",
+ protocolChange: true,
+ keywordLookup: true,
+ affectedByDNSForSingleWordHosts: true,
+ },
+ {
+ input: "mozilla5/2",
+ fixedURI: "http://mozilla5/2",
+ alternateURI: "https://www.mozilla5.com/2",
+ protocolChange: true,
+ keywordLookup: true,
+ affectedByDNSForSingleWordHosts: true,
+ },
+ {
+ input: "mozilla/foo",
+ fixedURI: "http://mozilla/foo",
+ alternateURI: "https://www.mozilla.com/foo",
+ protocolChange: true,
+ keywordLookup: true,
+ affectedByDNSForSingleWordHosts: true,
+ },
+ {
+ input: "mozilla\\",
+ fixedURI: "http://mozilla/",
+ alternateURI: "https://www.mozilla.com/",
+ keywordLookup: true,
+ protocolChange: true,
+ affectedByDNSForSingleWordHosts: true,
+ },
+ {
+ input: "localhost",
+ fixedURI: "http://localhost/",
+ keywordLookup: true,
+ protocolChange: true,
+ affectedByDNSForSingleWordHosts: true,
+ },
+ {
+ input: "http",
+ fixedURI: "http://http/",
+ keywordLookup: true,
+ protocolChange: true,
+ affectedByDNSForSingleWordHosts: true,
+ },
+ {
+ input: "https",
+ fixedURI: "http://https/",
+ keywordLookup: true,
+ protocolChange: true,
+ affectedByDNSForSingleWordHosts: true,
+ },
+ {
+ input: "localhost:8080",
+ fixedURI: "http://localhost:8080/",
+ protocolChange: true,
+ },
+ {
+ input: "plonk:8080",
+ fixedURI: "http://plonk:8080/",
+ protocolChange: true,
+ },
+ {
+ input: "plonk:8080?test",
+ fixedURI: "http://plonk:8080/?test",
+ protocolChange: true,
+ },
+ {
+ input: "plonk:8080#test",
+ fixedURI: "http://plonk:8080/#test",
+ protocolChange: true,
+ },
+ {
+ input: "plonk/ #",
+ fixedURI: "http://plonk/%20#",
+ alternateURI: "https://www.plonk.com/%20#",
+ protocolChange: true,
+ keywordLookup: false,
+ },
+ {
+ input: "blah.com.",
+ fixedURI: "http://blah.com./",
+ protocolChange: true,
+ },
+ {
+ input:
+ "\u10E0\u10D4\u10D2\u10D8\u10E1\u10E2\u10E0\u10D0\u10EA\u10D8\u10D0.\u10D2\u10D4",
+ fixedURI: "http://xn--lodaehvb5cdik4g.xn--node/",
+ alternateURI: "https://www.xn--lodaehvb5cdik4g.xn--node/",
+ protocolChange: true,
+ },
+ {
+ input: " \t mozilla.org/\t \t ",
+ fixedURI: "http://mozilla.org/",
+ alternateURI: "https://www.mozilla.org/",
+ protocolChange: true,
+ },
+ {
+ input: " moz\ti\tlla.org ",
+ keywordLookup: true,
+ },
+ {
+ input: "mozilla/",
+ fixedURI: "http://mozilla/",
+ alternateURI: "https://www.mozilla.com/",
+ protocolChange: true,
+ },
+ {
+ input: "mozilla/ test /",
+ fixedURI: "http://mozilla/%20test%20/",
+ alternateURI: "https://www.mozilla.com/%20test%20/",
+ protocolChange: true,
+ },
+ {
+ input: "mozilla /test/",
+ keywordLookup: true,
+ },
+ {
+ input: "pserver:8080",
+ fixedURI: "http://pserver:8080/",
+ protocolChange: true,
+ },
+ {
+ input: "http;mozilla",
+ fixedURI: "http://http;mozilla/",
+ alternateURI: "https://www.http;mozilla.com/",
+ keywordLookup: true,
+ protocolChange: true,
+ affectedByDNSForSingleWordHosts: true,
+ },
+ {
+ input: "http//mozilla.org",
+ fixedURI: "http://mozilla.org/",
+ shouldRunTest: flags =>
+ flags & Services.uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS,
+ },
+ {
+ input: "http//mozilla.org",
+ fixedURI: "http://http//mozilla.org",
+ keywordLookup: true,
+ protocolChange: true,
+ affectedByDNSForSingleWordHosts: true,
+ shouldRunTest: flags =>
+ !(flags & Services.uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS),
+ },
+ {
+ input: "www.mozilla",
+ fixedURI: "http://www.mozilla/",
+ protocolChange: true,
+ },
+ {
+ input: "https://sub.www..mozilla...org/",
+ fixedURI: "https://sub.www.mozilla.org/",
+ },
+ {
+ input: "sub.www..mozilla...org/",
+ fixedURI: "http://sub.www.mozilla.org/",
+ protocolChange: true,
+ },
+ {
+ input: "sub.www..mozilla...org",
+ fixedURI: "http://sub.www.mozilla.org/",
+ protocolChange: true,
+ },
+ {
+ input: "www...mozilla",
+ fixedURI: "http://www.mozilla/",
+ keywordLookup: true,
+ protocolChange: true,
+ shouldRunTest: flags =>
+ !gSingleWordDNSLookup &&
+ flags & Services.uriFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP,
+ },
+ {
+ input: "www...mozilla",
+ fixedURI: "http://www.mozilla/",
+ protocolChange: true,
+ shouldRunTest: flags =>
+ gSingleWordDNSLookup ||
+ !(flags & Services.uriFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP),
+ },
+ {
+ input: "mozilla...org",
+ fixedURI: "http://mozilla.org/",
+ protocolChange: true,
+ },
+ {
+ input: "モジラ...org",
+ fixedURI: "http://xn--yck6dwa.org/",
+ protocolChange: true,
+ },
+ {
+ input: "user@localhost",
+ fixedURI: "http://user@localhost/",
+ protocolChange: true,
+ shouldRunTest: () => gSingleWordDNSLookup,
+ },
+ {
+ input: "user@localhost",
+ fixedURI: "http://user@localhost/",
+ keywordLookup: true,
+ protocolChange: true,
+ shouldRunTest: () => !gSingleWordDNSLookup,
+ },
+ {
+ input: "user@192.168.0.1",
+ fixedURI: "http://user@192.168.0.1/",
+ protocolChange: true,
+ },
+ {
+ input: "user@dummy-host",
+ fixedURI: "http://user@dummy-host/",
+ protocolChange: true,
+ shouldRunTest: () => gSingleWordDNSLookup,
+ },
+ {
+ input: "user@dummy-host",
+ fixedURI: "http://user@dummy-host/",
+ keywordLookup: true,
+ protocolChange: true,
+ shouldRunTest: () => !gSingleWordDNSLookup,
+ },
+ {
+ input: "user:pass@dummy-host",
+ fixedURI: "http://user:pass@dummy-host/",
+ protocolChange: true,
+ },
+ {
+ input: ":pass@dummy-host",
+ fixedURI: "http://:pass@dummy-host/",
+ protocolChange: true,
+ },
+ {
+ input: "user@dummy-host/path",
+ fixedURI: "http://user@dummy-host/path",
+ protocolChange: true,
+ shouldRunTest: () => gSingleWordDNSLookup,
+ },
+ {
+ input: "jar:file:///omni.ja!/",
+ fixedURI: "jar:file:///omni.ja!/",
+ },
+];
+
+if (AppConstants.platform == "win") {
+ testcases.push({
+ input: "C:\\some\\file.txt",
+ fixedURI: "file:///C:/some/file.txt",
+ protocolChange: true,
+ });
+ testcases.push({
+ input: "//mozilla",
+ fixedURI: "http://mozilla/",
+ alternateURI: "https://www.mozilla.com/",
+ protocolChange: true,
+ });
+ testcases.push({
+ input: "/a",
+ fixedURI: "http://a/",
+ alternateURI: "https://www.a.com/",
+ keywordLookup: true,
+ protocolChange: true,
+ affectedByDNSForSingleWordHosts: true,
+ });
+} else {
+ testcases.push({
+ input: "/some/file.txt",
+ fixedURI: "file:///some/file.txt",
+ protocolChange: true,
+ });
+ testcases.push({
+ input: "//mozilla",
+ fixedURI: "file:////mozilla",
+ protocolChange: true,
+ });
+ testcases.push({
+ input: "/a",
+ fixedURI: "file:///a",
+ protocolChange: true,
+ });
+}
+
+function sanitize(input) {
+ return input.replace(/\r|\n/g, "").trim();
+}
+
+add_task(async function setup() {
+ var prefList = [
+ "browser.fixup.typo.scheme",
+ "keyword.enabled",
+ "browser.fixup.domainwhitelist.whitelisted",
+ "browser.fixup.domainsuffixwhitelist.test",
+ "browser.fixup.domainsuffixwhitelist.local.domain",
+ "browser.search.separatePrivateDefault",
+ "browser.search.separatePrivateDefault.ui.enabled",
+ ];
+ for (let pref of prefList) {
+ Services.prefs.setBoolPref(pref, true);
+ }
+
+ await setupSearchService();
+ await addTestEngines();
+
+ await Services.search.setDefault(
+ Services.search.getEngineByName(kSearchEngineID),
+ Ci.nsISearchService.CHANGE_REASON_UNKNOWN
+ );
+ await Services.search.setDefaultPrivate(
+ Services.search.getEngineByName(kPrivateSearchEngineID),
+ Ci.nsISearchService.CHANGE_REASON_UNKNOWN
+ );
+});
+
+var gSingleWordDNSLookup = false;
+add_task(async function run_test() {
+ // Only keywordlookup things should be affected by requiring a DNS lookup for single-word hosts:
+ info(
+ "Check only keyword lookup testcases should be affected by requiring DNS for single hosts"
+ );
+ let affectedTests = testcases.filter(
+ t => !t.keywordLookup && t.affectedByDNSForSingleWordHosts
+ );
+ if (affectedTests.length) {
+ for (let testcase of affectedTests) {
+ info("Affected: " + testcase.input);
+ }
+ }
+ Assert.equal(affectedTests.length, 0);
+ await do_single_test_run();
+ await do_single_test_run(true);
+ gSingleWordDNSLookup = true;
+ await do_single_test_run();
+ await do_single_test_run(true);
+ gSingleWordDNSLookup = false;
+ await Services.search.setDefault(
+ Services.search.getEngineByName(kPostSearchEngineID),
+ Ci.nsISearchService.CHANGE_REASON_UNKNOWN
+ );
+ await do_single_test_run();
+ await do_single_test_run(true);
+});
+
+async function do_single_test_run(browserFixupAlternateEnabled = false) {
+ Services.prefs.setBoolPref(kForceDNSLookup, gSingleWordDNSLookup);
+ Services.prefs.setBoolPref(kFixupEnabled, browserFixupAlternateEnabled);
+ let relevantTests = gSingleWordDNSLookup
+ ? testcases.filter(t => t.keywordLookup)
+ : testcases;
+
+ let engine = await Services.search.getDefault();
+ let engineUrl =
+ engine.name == kPostSearchEngineID
+ ? kPostSearchEngineURL
+ : kSearchEngineURL;
+ let privateEngine = await Services.search.getDefaultPrivate();
+ let privateEngineUrl = kPrivateSearchEngineURL;
+
+ for (let {
+ input: testInput,
+ fixedURI: expectedFixedURI,
+ alternateURI: alternativeURI,
+ keywordLookup: expectKeywordLookup,
+ protocolChange: expectProtocolChange,
+ inWhitelist: inWhitelist,
+ affectedByDNSForSingleWordHosts: affectedByDNSForSingleWordHosts,
+ shouldRunTest,
+ } of relevantTests) {
+ // Explicitly force these into a boolean
+ expectKeywordLookup = !!expectKeywordLookup;
+ expectProtocolChange = !!expectProtocolChange;
+ inWhitelist = !!inWhitelist;
+ affectedByDNSForSingleWordHosts = !!affectedByDNSForSingleWordHosts;
+
+ expectKeywordLookup =
+ expectKeywordLookup &&
+ (!affectedByDNSForSingleWordHosts || !gSingleWordDNSLookup);
+
+ for (let flags of flagInputs) {
+ info(
+ 'Checking "' +
+ testInput +
+ '" with flags ' +
+ flags +
+ " (DNS lookup for single words: " +
+ (gSingleWordDNSLookup ? "yes" : "no") +
+ "," +
+ " browser fixup alt enabled: " +
+ (browserFixupAlternateEnabled ? "yes" : "no") +
+ ")"
+ );
+
+ if (shouldRunTest && !shouldRunTest(flags)) {
+ continue;
+ }
+
+ let URIInfo;
+ try {
+ URIInfo = Services.uriFixup.getFixupURIInfo(testInput, flags);
+ } catch (ex) {
+ // Both APIs should return an error in the same cases.
+ info("Caught exception: " + ex);
+ Assert.equal(expectedFixedURI, null);
+ continue;
+ }
+
+ // Check the fixedURI:
+ let fixupFlag = browserFixupAlternateEnabled
+ ? Services.uriFixup.FIXUP_FLAGS_MAKE_ALTERNATE_URI
+ : Services.uriFixup.FIXUP_FLAG_NONE;
+ let forceAlternativeURI =
+ flags & Services.uriFixup.FIXUP_FLAG_FORCE_ALTERNATE_URI;
+ let makeAlternativeURI = flags & fixupFlag || forceAlternativeURI;
+
+ if (makeAlternativeURI && alternativeURI != null) {
+ Assert.equal(
+ URIInfo.fixedURI.spec,
+ alternativeURI,
+ "should have gotten alternate URI"
+ );
+ } else {
+ Assert.equal(
+ URIInfo.fixedURI && URIInfo.fixedURI.spec,
+ expectedFixedURI,
+ "should get correct fixed URI"
+ );
+ }
+
+ // Check booleans on input:
+ let couldDoKeywordLookup =
+ flags & Services.uriFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
+ Assert.equal(
+ !!URIInfo.keywordProviderName,
+ couldDoKeywordLookup && expectKeywordLookup,
+ "keyword lookup as expected"
+ );
+
+ let expectProtocolChangeAfterAlternate = false;
+ // If alternativeURI was created, the protocol of the URI
+ // might have been changed to browser.fixup.alternate.protocol
+ // If the protocol is not the same as what was in expectedFixedURI,
+ // the protocol must've changed in the fixup process.
+ if (
+ makeAlternativeURI &&
+ alternativeURI != null &&
+ !expectedFixedURI.startsWith(URIInfo.fixedURI.scheme)
+ ) {
+ expectProtocolChangeAfterAlternate = true;
+ }
+
+ Assert.equal(
+ URIInfo.fixupChangedProtocol,
+ expectProtocolChange || expectProtocolChangeAfterAlternate,
+ "protocol change as expected"
+ );
+ Assert.equal(
+ URIInfo.fixupCreatedAlternateURI,
+ makeAlternativeURI && alternativeURI != null,
+ "alternative URI as expected"
+ );
+
+ // Check the preferred URI
+ if (couldDoKeywordLookup) {
+ if (expectKeywordLookup) {
+ if (!inWhitelist) {
+ let urlparamInput = encodeURIComponent(sanitize(testInput)).replace(
+ /%20/g,
+ "+"
+ );
+ // If the input starts with `?`, then URIInfo.preferredURI.spec will omit it
+ // In order to test this behaviour, remove `?` only if it is the first character
+ if (urlparamInput.startsWith("%3F")) {
+ urlparamInput = urlparamInput.replace("%3F", "");
+ }
+ let isPrivate =
+ flags & Services.uriFixup.FIXUP_FLAG_PRIVATE_CONTEXT;
+ let searchEngineUrl = isPrivate ? privateEngineUrl : engineUrl;
+ let searchURL = searchEngineUrl.replace(
+ "{searchTerms}",
+ urlparamInput
+ );
+ let spec = URIInfo.preferredURI.spec.replace(/%27/g, "'");
+ Assert.equal(spec, searchURL, "should get correct search URI");
+ let providerName = isPrivate ? privateEngine.name : engine.name;
+ Assert.equal(
+ URIInfo.keywordProviderName,
+ providerName,
+ "should get correct provider name"
+ );
+ // Also check keywordToURI() uses the right engine.
+ let kwInfo = Services.uriFixup.keywordToURI(
+ urlparamInput,
+ isPrivate
+ );
+ Assert.equal(kwInfo.providerName, URIInfo.providerName);
+ if (providerName == kPostSearchEngineID) {
+ Assert.ok(kwInfo.postData);
+ let submission = engine.getSubmission(urlparamInput);
+ let enginePostData = NetUtil.readInputStreamToString(
+ submission.postData,
+ submission.postData.available()
+ );
+ let postData = NetUtil.readInputStreamToString(
+ kwInfo.postData,
+ kwInfo.postData.available()
+ );
+ Assert.equal(postData, enginePostData);
+ }
+ } else {
+ Assert.equal(
+ URIInfo.preferredURI,
+ null,
+ "not expecting a preferred URI"
+ );
+ }
+ } else {
+ Assert.equal(
+ URIInfo.preferredURI.spec,
+ URIInfo.fixedURI.spec,
+ "fixed URI should match"
+ );
+ }
+ } else {
+ // In these cases, we should never be doing a keyword lookup and
+ // the fixed URI should be preferred:
+ let prefURI = URIInfo.preferredURI && URIInfo.preferredURI.spec;
+ let fixedURI = URIInfo.fixedURI && URIInfo.fixedURI.spec;
+ Assert.equal(prefURI, fixedURI, "fixed URI should be same as expected");
+ }
+ Assert.equal(
+ sanitize(testInput),
+ URIInfo.originalInput,
+ "should mirror original input"
+ );
+ }
+ }
+}
diff --git a/docshell/test/unit/test_URIFixup_search.js b/docshell/test/unit/test_URIFixup_search.js
new file mode 100644
index 0000000000..95883302f8
--- /dev/null
+++ b/docshell/test/unit/test_URIFixup_search.js
@@ -0,0 +1,144 @@
+const { AppConstants } = ChromeUtils.importESModule(
+ "resource://gre/modules/AppConstants.sys.mjs"
+);
+
+var isWin = AppConstants.platform == "win";
+
+var data = [
+ {
+ // Valid should not be changed.
+ wrong: "https://example.com/this/is/a/test.html",
+ fixed: "https://example.com/this/is/a/test.html",
+ },
+ {
+ // Unrecognized protocols should be changed.
+ wrong: "whatever://this/is/a/test.html",
+ fixed: kSearchEngineURL.replace(
+ "{searchTerms}",
+ encodeURIComponent("whatever://this/is/a/test.html")
+ ),
+ },
+
+ {
+ // Unrecognized protocols should be changed.
+ wrong: "whatever://this/is/a/test.html",
+ fixed: kPrivateSearchEngineURL.replace(
+ "{searchTerms}",
+ encodeURIComponent("whatever://this/is/a/test.html")
+ ),
+ inPrivateBrowsing: true,
+ },
+
+ // The following tests check that when a user:password is present in the URL
+ // `user:` isn't treated as an unknown protocol thus leaking the user and
+ // password to the search engine.
+ {
+ wrong: "user:pass@example.com/this/is/a/test.html",
+ fixed: "http://user:pass@example.com/this/is/a/test.html",
+ },
+ {
+ wrong: "user@example.com:8080/this/is/a/test.html",
+ fixed: "http://user@example.com:8080/this/is/a/test.html",
+ },
+ {
+ wrong: "https:pass@example.com/this/is/a/test.html",
+ fixed: "https://pass@example.com/this/is/a/test.html",
+ },
+ {
+ wrong: "user:pass@example.com:8080/this/is/a/test.html",
+ fixed: "http://user:pass@example.com:8080/this/is/a/test.html",
+ },
+ {
+ wrong: "http:user:pass@example.com:8080/this/is/a/test.html",
+ fixed: "http://user:pass@example.com:8080/this/is/a/test.html",
+ },
+ {
+ wrong: "ttp:user:pass@example.com:8080/this/is/a/test.html",
+ fixed: "http://user:pass@example.com:8080/this/is/a/test.html",
+ },
+ {
+ wrong: "gobbledygook:user:pass@example.com:8080/this/is/a/test.html",
+ fixed:
+ "http://gobbledygook:user%3Apass@example.com:8080/this/is/a/test.html",
+ },
+ {
+ wrong: "user:@example.com:8080/this/is/a/test.html",
+ fixed: "http://user@example.com:8080/this/is/a/test.html",
+ },
+ {
+ wrong: "//user:pass@example.com:8080/this/is/a/test.html",
+ fixed:
+ (isWin ? "http:" : "file://") +
+ "//user:pass@example.com:8080/this/is/a/test.html",
+ },
+ {
+ wrong: "://user:pass@example.com:8080/this/is/a/test.html",
+ fixed: "http://user:pass@example.com:8080/this/is/a/test.html",
+ },
+ {
+ wrong: "localhost:8080/?param=1",
+ fixed: "http://localhost:8080/?param=1",
+ },
+ {
+ wrong: "localhost:8080?param=1",
+ fixed: "http://localhost:8080/?param=1",
+ },
+ {
+ wrong: "localhost:8080#somewhere",
+ fixed: "http://localhost:8080/#somewhere",
+ },
+ {
+ wrong: "whatever://this/is/a@b/test.html",
+ fixed: kSearchEngineURL.replace(
+ "{searchTerms}",
+ encodeURIComponent("whatever://this/is/a@b/test.html")
+ ),
+ },
+];
+
+var extProtocolSvc = Cc[
+ "@mozilla.org/uriloader/external-protocol-service;1"
+].getService(Ci.nsIExternalProtocolService);
+
+if (extProtocolSvc && extProtocolSvc.externalProtocolHandlerExists("mailto")) {
+ data.push({
+ wrong: "mailto:foo@bar.com",
+ fixed: "mailto:foo@bar.com",
+ });
+}
+
+var len = data.length;
+
+add_task(async function setup() {
+ await setupSearchService();
+ await addTestEngines();
+
+ Services.prefs.setBoolPref("keyword.enabled", true);
+ Services.prefs.setBoolPref("browser.search.separatePrivateDefault", true);
+ Services.prefs.setBoolPref(
+ "browser.search.separatePrivateDefault.ui.enabled",
+ true
+ );
+
+ await Services.search.setDefault(
+ Services.search.getEngineByName(kSearchEngineID),
+ Ci.nsISearchService.CHANGE_REASON_UNKNOWN
+ );
+ await Services.search.setDefaultPrivate(
+ Services.search.getEngineByName(kPrivateSearchEngineID),
+ Ci.nsISearchService.CHANGE_REASON_UNKNOWN
+ );
+});
+
+// Make sure we fix what needs fixing
+add_task(function test_fix_unknown_schemes() {
+ for (let i = 0; i < len; ++i) {
+ let item = data[i];
+ let flags = Services.uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS;
+ if (item.inPrivateBrowsing) {
+ flags |= Services.uriFixup.FIXUP_FLAG_PRIVATE_CONTEXT;
+ }
+ let { preferredURI } = Services.uriFixup.getFixupURIInfo(item.wrong, flags);
+ Assert.equal(preferredURI.spec, item.fixed);
+ }
+});
diff --git a/docshell/test/unit/test_allowJavascript.js b/docshell/test/unit/test_allowJavascript.js
new file mode 100644
index 0000000000..e9d2f60fca
--- /dev/null
+++ b/docshell/test/unit/test_allowJavascript.js
@@ -0,0 +1,269 @@
+"use strict";
+
+const { XPCShellContentUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/XPCShellContentUtils.sys.mjs"
+);
+
+XPCShellContentUtils.init(this);
+
+const ACTOR = "AllowJavascript";
+
+const HTML = String.raw`<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <script type="application/javascript">
+ "use strict";
+ var gFiredOnload = false;
+ var gFiredOnclick = false;
+ </script>
+</head>
+<body onload="gFiredOnload = true;" onclick="gFiredOnclick = true;">
+</body>
+</html>`;
+
+const server = XPCShellContentUtils.createHttpServer({
+ hosts: ["example.com", "example.org"],
+});
+
+server.registerPathHandler("/", (request, response) => {
+ response.setHeader("Content-Type", "text/html");
+ response.write(HTML);
+});
+
+const { AllowJavascriptParent } = ChromeUtils.importESModule(
+ "resource://test/AllowJavascriptParent.sys.mjs"
+);
+
+async function assertScriptsAllowed(bc, expectAllowed, desc) {
+ let actor = bc.currentWindowGlobal.getActor(ACTOR);
+ let allowed = await actor.sendQuery("CheckScriptsAllowed");
+ equal(
+ allowed,
+ expectAllowed,
+ `Scripts should be ${expectAllowed ? "" : "dis"}allowed for ${desc}`
+ );
+}
+
+async function assertLoadFired(bc, expectFired, desc) {
+ let actor = bc.currentWindowGlobal.getActor(ACTOR);
+ let fired = await actor.sendQuery("CheckFiredLoadEvent");
+ equal(
+ fired,
+ expectFired,
+ `Should ${expectFired ? "" : "not "}have fired load for ${desc}`
+ );
+}
+
+function createSubframe(bc, url) {
+ let actor = bc.currentWindowGlobal.getActor(ACTOR);
+ return actor.sendQuery("CreateIframe", { url });
+}
+
+add_task(async function() {
+ Services.prefs.setBoolPref("dom.security.https_first", false);
+ ChromeUtils.registerWindowActor(ACTOR, {
+ allFrames: true,
+ child: {
+ esModuleURI: "resource://test/AllowJavascriptChild.sys.mjs",
+ events: { load: { capture: true } },
+ },
+ parent: {
+ esModuleURI: "resource://test/AllowJavascriptParent.sys.mjs",
+ },
+ });
+
+ let page = await XPCShellContentUtils.loadContentPage("http://example.com/", {
+ remote: true,
+ remoteSubframes: true,
+ });
+
+ let bc = page.browsingContext;
+
+ {
+ let oopFrame1 = await createSubframe(bc, "http://example.org/");
+ let inprocFrame1 = await createSubframe(bc, "http://example.com/");
+
+ let oopFrame1OopSub = await createSubframe(
+ oopFrame1,
+ "http://example.com/"
+ );
+ let inprocFrame1OopSub = await createSubframe(
+ inprocFrame1,
+ "http://example.org/"
+ );
+
+ equal(
+ oopFrame1.allowJavascript,
+ true,
+ "OOP BC should inherit allowJavascript from parent"
+ );
+ equal(
+ inprocFrame1.allowJavascript,
+ true,
+ "In-process BC should inherit allowJavascript from parent"
+ );
+ equal(
+ oopFrame1OopSub.allowJavascript,
+ true,
+ "OOP BC child should inherit allowJavascript from parent"
+ );
+ equal(
+ inprocFrame1OopSub.allowJavascript,
+ true,
+ "In-process child BC should inherit allowJavascript from parent"
+ );
+
+ await assertLoadFired(bc, true, "top BC");
+ await assertScriptsAllowed(bc, true, "top BC");
+
+ await assertLoadFired(oopFrame1, true, "OOP frame 1");
+ await assertScriptsAllowed(oopFrame1, true, "OOP frame 1");
+
+ await assertLoadFired(inprocFrame1, true, "In-process frame 1");
+ await assertScriptsAllowed(inprocFrame1, true, "In-process frame 1");
+
+ await assertLoadFired(oopFrame1OopSub, true, "OOP frame 1 subframe");
+ await assertScriptsAllowed(oopFrame1OopSub, true, "OOP frame 1 subframe");
+
+ await assertLoadFired(
+ inprocFrame1OopSub,
+ true,
+ "In-process frame 1 subframe"
+ );
+ await assertScriptsAllowed(
+ inprocFrame1OopSub,
+ true,
+ "In-process frame 1 subframe"
+ );
+
+ bc.allowJavascript = false;
+ await assertScriptsAllowed(bc, false, "top BC with scripts disallowed");
+ await assertScriptsAllowed(
+ oopFrame1,
+ false,
+ "OOP frame 1 with top BC with scripts disallowed"
+ );
+ await assertScriptsAllowed(
+ inprocFrame1,
+ false,
+ "In-process frame 1 with top BC with scripts disallowed"
+ );
+ await assertScriptsAllowed(
+ oopFrame1OopSub,
+ false,
+ "OOP frame 1 subframe with top BC with scripts disallowed"
+ );
+ await assertScriptsAllowed(
+ inprocFrame1OopSub,
+ false,
+ "In-process frame 1 subframe with top BC with scripts disallowed"
+ );
+
+ let oopFrame2 = await createSubframe(bc, "http://example.org/");
+ let inprocFrame2 = await createSubframe(bc, "http://example.com/");
+
+ equal(
+ oopFrame2.allowJavascript,
+ false,
+ "OOP BC 2 should inherit allowJavascript from parent"
+ );
+ equal(
+ inprocFrame2.allowJavascript,
+ false,
+ "In-process BC 2 should inherit allowJavascript from parent"
+ );
+
+ await assertLoadFired(
+ oopFrame2,
+ undefined,
+ "OOP frame 2 with top BC with scripts disallowed"
+ );
+ await assertScriptsAllowed(
+ oopFrame2,
+ false,
+ "OOP frame 2 with top BC with scripts disallowed"
+ );
+ await assertLoadFired(
+ inprocFrame2,
+ undefined,
+ "In-process frame 2 with top BC with scripts disallowed"
+ );
+ await assertScriptsAllowed(
+ inprocFrame2,
+ false,
+ "In-process frame 2 with top BC with scripts disallowed"
+ );
+
+ bc.allowJavascript = true;
+ await assertScriptsAllowed(bc, true, "top BC");
+
+ await assertScriptsAllowed(oopFrame1, true, "OOP frame 1");
+ await assertScriptsAllowed(inprocFrame1, true, "In-process frame 1");
+ await assertScriptsAllowed(oopFrame1OopSub, true, "OOP frame 1 subframe");
+ await assertScriptsAllowed(
+ inprocFrame1OopSub,
+ true,
+ "In-process frame 1 subframe"
+ );
+
+ await assertScriptsAllowed(oopFrame2, false, "OOP frame 2");
+ await assertScriptsAllowed(inprocFrame2, false, "In-process frame 2");
+
+ oopFrame1.currentWindowGlobal.allowJavascript = false;
+ inprocFrame1.currentWindowGlobal.allowJavascript = false;
+
+ await assertScriptsAllowed(
+ oopFrame1,
+ false,
+ "OOP frame 1 with second level WC scripts disallowed"
+ );
+ await assertScriptsAllowed(
+ inprocFrame1,
+ false,
+ "In-process frame 1 with second level WC scripts disallowed"
+ );
+ await assertScriptsAllowed(
+ oopFrame1OopSub,
+ false,
+ "OOP frame 1 subframe second level WC scripts disallowed"
+ );
+ await assertScriptsAllowed(
+ inprocFrame1OopSub,
+ false,
+ "In-process frame 1 subframe with second level WC scripts disallowed"
+ );
+
+ oopFrame1.reload(0);
+ inprocFrame1.reload(0);
+ await Promise.all([
+ AllowJavascriptParent.promiseLoad(oopFrame1),
+ AllowJavascriptParent.promiseLoad(inprocFrame1),
+ ]);
+
+ equal(
+ oopFrame1.currentWindowGlobal.allowJavascript,
+ true,
+ "WindowContext.allowJavascript does not persist after navigation for OOP frame 1"
+ );
+ equal(
+ inprocFrame1.currentWindowGlobal.allowJavascript,
+ true,
+ "WindowContext.allowJavascript does not persist after navigation for in-process frame 1"
+ );
+
+ await assertScriptsAllowed(oopFrame1, true, "OOP frame 1");
+ await assertScriptsAllowed(inprocFrame1, true, "In-process frame 1");
+ }
+
+ bc.allowJavascript = false;
+
+ bc.reload(0);
+ await AllowJavascriptParent.promiseLoad(bc);
+
+ await assertLoadFired(bc, undefined, "top BC with scripts disabled");
+ await assertScriptsAllowed(bc, false, "top BC with scripts disabled");
+
+ await page.close();
+ Services.prefs.clearUserPref("dom.security.https_first");
+});
diff --git a/docshell/test/unit/test_browsing_context_structured_clone.js b/docshell/test/unit/test_browsing_context_structured_clone.js
new file mode 100644
index 0000000000..d06f7aecf6
--- /dev/null
+++ b/docshell/test/unit/test_browsing_context_structured_clone.js
@@ -0,0 +1,68 @@
+"use strict";
+
+add_task(async function test_BrowsingContext_structured_clone() {
+ let browser = Services.appShell.createWindowlessBrowser(false);
+
+ let frame = browser.document.createElement("iframe");
+
+ await new Promise(r => {
+ frame.onload = () => r();
+ browser.document.body.appendChild(frame);
+ });
+
+ let { browsingContext } = frame;
+
+ let sch = new StructuredCloneHolder({ browsingContext });
+
+ let deserialize = () => sch.deserialize({}, true);
+
+ // Check that decoding a live browsing context produces the correct
+ // object.
+ equal(
+ deserialize().browsingContext,
+ browsingContext,
+ "Got correct browsing context from StructuredClone deserialize"
+ );
+
+ // Check that decoding a second time still succeeds.
+ equal(
+ deserialize().browsingContext,
+ browsingContext,
+ "Got correct browsing context from second StructuredClone deserialize"
+ );
+
+ // Destroy the browsing context and make sure that the decode fails
+ // with a DataCloneError.
+ //
+ // Making sure the BrowsingContext is actually destroyed by the time
+ // we do the second decode is a bit tricky. We obviously have clear
+ // our local references to it, and give the GC a chance to reap them.
+ // And we also, of course, have to destroy the frame that it belongs
+ // to, or its frame loader and window global would hold it alive.
+ //
+ // Beyond that, we don't *have* to reload or destroy the parent
+ // document, but we do anyway just to be safe.
+ //
+
+ frame.remove();
+ frame = null;
+ browsingContext = null;
+
+ browser.document.location.reload();
+ browser.close();
+
+ // We will schedule a precise GC and do both GC and CC a few times, to make
+ // sure we have completely destroyed the WindowGlobal actors (which keep
+ // references to their BrowsingContexts) in order
+ // to allow their (now snow-white) references to be collected.
+ await schedulePreciseGCAndForceCC(3);
+
+ // OK. We can be fairly confident that the BrowsingContext object
+ // stored in our structured clone data has been destroyed. Make sure
+ // that attempting to decode it again leads to the appropriate error.
+ Assert.throws(
+ deserialize,
+ e => e.name === "DataCloneError",
+ "Should get a DataCloneError when trying to decode a dead BrowsingContext"
+ );
+});
diff --git a/docshell/test/unit/test_bug442584.js b/docshell/test/unit/test_bug442584.js
new file mode 100644
index 0000000000..c109557f50
--- /dev/null
+++ b/docshell/test/unit/test_bug442584.js
@@ -0,0 +1,35 @@
+var prefetch = Cc["@mozilla.org/prefetch-service;1"].getService(
+ Ci.nsIPrefetchService
+);
+
+var ReferrerInfo = Components.Constructor(
+ "@mozilla.org/referrer-info;1",
+ "nsIReferrerInfo",
+ "init"
+);
+
+function run_test() {
+ // Fill up the queue
+ Services.prefs.setBoolPref("network.prefetch-next", true);
+ for (var i = 0; i < 5; i++) {
+ var uri = Services.io.newURI("http://localhost/" + i);
+ var referrerInfo = new ReferrerInfo(Ci.nsIReferrerInfo.EMPTY, true, uri);
+ prefetch.prefetchURI(uri, referrerInfo, null, true);
+ }
+
+ // Make sure the queue has items in it...
+ Assert.ok(prefetch.hasMoreElements());
+
+ // Now disable the pref to force the queue to empty...
+ Services.prefs.setBoolPref("network.prefetch-next", false);
+ Assert.ok(!prefetch.hasMoreElements());
+
+ // Now reenable the pref, and add more items to the queue.
+ Services.prefs.setBoolPref("network.prefetch-next", true);
+ for (var k = 0; k < 5; k++) {
+ var uri2 = Services.io.newURI("http://localhost/" + k);
+ var referrerInfo2 = new ReferrerInfo(Ci.nsIReferrerInfo.EMPTY, true, uri2);
+ prefetch.prefetchURI(uri2, referrerInfo2, null, true);
+ }
+ Assert.ok(prefetch.hasMoreElements());
+}
diff --git a/docshell/test/unit/test_pb_notification.js b/docshell/test/unit/test_pb_notification.js
new file mode 100644
index 0000000000..51cd3b95ff
--- /dev/null
+++ b/docshell/test/unit/test_pb_notification.js
@@ -0,0 +1,18 @@
+function destroy_transient_docshell() {
+ let windowlessBrowser = Services.appShell.createWindowlessBrowser(true);
+ windowlessBrowser.docShell.setOriginAttributes({ privateBrowsingId: 1 });
+ windowlessBrowser.close();
+ do_test_pending();
+ do_timeout(0, Cu.forceGC);
+}
+
+function run_test() {
+ var obs = {
+ observe(aSubject, aTopic, aData) {
+ Assert.equal(aTopic, "last-pb-context-exited");
+ do_test_finished();
+ },
+ };
+ Services.obs.addObserver(obs, "last-pb-context-exited");
+ destroy_transient_docshell();
+}
diff --git a/docshell/test/unit/test_privacy_transition.js b/docshell/test/unit/test_privacy_transition.js
new file mode 100644
index 0000000000..ae1bf71284
--- /dev/null
+++ b/docshell/test/unit/test_privacy_transition.js
@@ -0,0 +1,21 @@
+var gNotifications = 0;
+
+var observer = {
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIPrivacyTransitionObserver",
+ "nsISupportsWeakReference",
+ ]),
+
+ privateModeChanged(enabled) {
+ gNotifications++;
+ },
+};
+
+function run_test() {
+ let windowlessBrowser = Services.appShell.createWindowlessBrowser(true);
+ windowlessBrowser.docShell.addWeakPrivacyTransitionObserver(observer);
+ windowlessBrowser.docShell.setOriginAttributes({ privateBrowsingId: 1 });
+ windowlessBrowser.docShell.setOriginAttributes({ privateBrowsingId: 0 });
+ windowlessBrowser.close();
+ Assert.equal(gNotifications, 2);
+}
diff --git a/docshell/test/unit/test_subframe_stop_after_parent_error.js b/docshell/test/unit/test_subframe_stop_after_parent_error.js
new file mode 100644
index 0000000000..4ba80c14cd
--- /dev/null
+++ b/docshell/test/unit/test_subframe_stop_after_parent_error.js
@@ -0,0 +1,141 @@
+"use strict";
+// Tests that pending subframe requests for an initial about:blank
+// document do not delay showing load errors (and possibly result in a
+// crash at docShell destruction) for failed document loads.
+
+const { XPCShellContentUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/XPCShellContentUtils.sys.mjs"
+);
+
+XPCShellContentUtils.init(this);
+
+const server = XPCShellContentUtils.createHttpServer({
+ hosts: ["example.com"],
+});
+
+// Registers a URL with the HTTP server which will not return a response
+// until we're ready to.
+function registerSlowPage(path) {
+ let result = {
+ url: `http://example.com/${path}`,
+ };
+
+ let finishedPromise = new Promise(resolve => {
+ result.finish = resolve;
+ });
+
+ server.registerPathHandler(`/${path}`, async (request, response) => {
+ response.processAsync();
+
+ response.setHeader("Content-Type", "text/html");
+ response.write("<html><body>Hello.</body></html>");
+
+ await finishedPromise;
+
+ response.finish();
+ });
+
+ return result;
+}
+
+let topFrameRequest = registerSlowPage("top.html");
+let subFrameRequest = registerSlowPage("frame.html");
+
+let thunks = new Set();
+function promiseStateStop(webProgress) {
+ return new Promise(resolve => {
+ let listener = {
+ onStateChange(aWebProgress, request, stateFlags, status) {
+ if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
+ webProgress.removeProgressListener(listener);
+
+ thunks.delete(listener);
+ resolve();
+ }
+ },
+
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIWebProgressListener",
+ "nsISupportsWeakReference",
+ ]),
+ };
+
+ // Keep the listener alive, since it's stored as a weak reference.
+ thunks.add(listener);
+ webProgress.addProgressListener(
+ listener,
+ Ci.nsIWebProgress.NOTIFY_STATE_REQUEST
+ );
+ });
+}
+
+async function runTest(waitForErrorPage) {
+ let page = await XPCShellContentUtils.loadContentPage("about:blank", {
+ remote: false,
+ });
+ let { browser } = page;
+
+ // Watch for the HTTP request for the top frame so that we can cancel
+ // it with an error.
+ let requestPromise = TestUtils.topicObserved(
+ "http-on-modify-request",
+ subject => subject.QueryInterface(Ci.nsIRequest).name == topFrameRequest.url
+ );
+
+ // Create a frame with a source URL which will not return a response
+ // before we cancel it with an error.
+ let doc = browser.contentDocument;
+ let frame = doc.createElement("iframe");
+ frame.src = topFrameRequest.url;
+ doc.body.appendChild(frame);
+
+ // Create a subframe in the initial about:blank document for the above
+ // frame which will not return a response before we cancel the
+ // document request.
+ let frameDoc = frame.contentDocument;
+ let subframe = frameDoc.createElement("iframe");
+ subframe.src = subFrameRequest.url;
+ frameDoc.body.appendChild(subframe);
+
+ let [req] = await requestPromise;
+
+ info("Cancel request for parent frame");
+ req.cancel(Cr.NS_ERROR_PROXY_CONNECTION_REFUSED);
+
+ // Request cancelation is not synchronous, so wait for the STATE_STOP
+ // event to fire.
+ await promiseStateStop(
+ browser.docShell.nsIInterfaceRequestor.getInterface(Ci.nsIWebProgress)
+ );
+
+ // And make a trip through the event loop to give the DocLoader a
+ // chance to update its state.
+ await new Promise(executeSoon);
+
+ if (waitForErrorPage) {
+ // Make sure that canceling the request with an error code actually
+ // shows an error page without waiting for a subframe response.
+ await TestUtils.waitForCondition(() =>
+ frame.contentDocument.documentURI.startsWith("about:neterror?")
+ );
+ }
+
+ info("Remove frame");
+ frame.remove();
+
+ await page.close();
+}
+
+add_task(async function testRemoveFrameImmediately() {
+ await runTest(false);
+});
+
+add_task(async function testRemoveFrameAfterErrorPage() {
+ await runTest(true);
+});
+
+add_task(async function() {
+ // Allow the document requests for the frames to complete.
+ topFrameRequest.finish();
+ subFrameRequest.finish();
+});
diff --git a/docshell/test/unit/xpcshell.ini b/docshell/test/unit/xpcshell.ini
new file mode 100644
index 0000000000..8bd7ef4f28
--- /dev/null
+++ b/docshell/test/unit/xpcshell.ini
@@ -0,0 +1,35 @@
+[DEFAULT]
+head = head_docshell.js
+support-files =
+ data/engine.xml
+ data/enginePost.xml
+ data/enginePrivate.xml
+
+[test_allowJavascript.js]
+skip-if = os == 'android'
+support-files =
+ AllowJavascriptChild.sys.mjs
+ AllowJavascriptParent.sys.mjs
+[test_bug442584.js]
+[test_browsing_context_structured_clone.js]
+[test_URIFixup.js]
+[test_URIFixup_check_host.js]
+[test_URIFixup_external_protocol_fallback.js]
+skip-if = os == 'android'
+[test_URIFixup_forced.js]
+# Disabled for 1563343 -- URI fixup should be done at the app level in GV.
+skip-if = os == 'android'
+[test_URIFixup_search.js]
+skip-if = os == 'android'
+[test_URIFixup_info.js]
+skip-if =
+ os == 'android'
+ win10_2004 && debug # Bug 1727925
+[test_pb_notification.js]
+# Bug 751575: unrelated JS changes cause timeouts on random platforms
+skip-if = true
+[test_privacy_transition.js]
+[test_subframe_stop_after_parent_error.js]
+skip-if =
+ os == 'android'
+ appname == 'thunderbird' # Needs to run without E10s, can't do that.
diff --git a/docshell/test/unit_ipc/test_pb_notification_ipc.js b/docshell/test/unit_ipc/test_pb_notification_ipc.js
new file mode 100644
index 0000000000..ecb366a3e1
--- /dev/null
+++ b/docshell/test/unit_ipc/test_pb_notification_ipc.js
@@ -0,0 +1,15 @@
+function run_test() {
+ var notifications = 0;
+ var obs = {
+ observe(aSubject, aTopic, aData) {
+ Assert.equal(aTopic, "last-pb-context-exited");
+ notifications++;
+ },
+ };
+ Services.obs.addObserver(obs, "last-pb-context-exited");
+
+ run_test_in_child("../unit/test_pb_notification.js", function() {
+ Assert.equal(notifications, 1);
+ do_test_finished();
+ });
+}
diff --git a/docshell/test/unit_ipc/xpcshell.ini b/docshell/test/unit_ipc/xpcshell.ini
new file mode 100644
index 0000000000..30e98a270f
--- /dev/null
+++ b/docshell/test/unit_ipc/xpcshell.ini
@@ -0,0 +1,7 @@
+[DEFAULT]
+head =
+skip-if = toolkit == 'android'
+
+[test_pb_notification_ipc.js]
+# Bug 751575: Perma-fails with: command timed out: 1200 seconds without output
+skip-if = true