browser_isInitialDocument.js (11370B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 // Tag every new WindowGlobalParent with an expando indicating whether or not 7 // they were an initial document when they were created for the duration of this 8 // test. 9 function wasInitialDocumentObserver(subject) { 10 subject._test_wasInitialDocument = subject.isInitialDocument; 11 } 12 Services.obs.addObserver(wasInitialDocumentObserver, "window-global-created"); 13 SimpleTest.registerCleanupFunction(function () { 14 Services.obs.removeObserver( 15 wasInitialDocumentObserver, 16 "window-global-created" 17 ); 18 }); 19 20 add_task(async function new_about_blank_tab() { 21 await BrowserTestUtils.withNewTab("about:blank", async browser => { 22 is( 23 browser.browsingContext.currentWindowGlobal.isInitialDocument, 24 true, 25 "After the initial about:blank fires its load event, the field is still true" 26 ); 27 }); 28 }); 29 30 add_task(async function iframe_initial_about_blank() { 31 await BrowserTestUtils.withNewTab( 32 // eslint-disable-next-line @microsoft/sdl/no-insecure-url 33 "http://example.com/document-builder.sjs?html=com", 34 async browser => { 35 info("Create an iframe without any explicit location"); 36 await SpecialPowers.spawn(browser, [], async () => { 37 const iframe = content.document.createElement("iframe"); 38 let loadPromise = new Promise(resolve => { 39 iframe.addEventListener("load", resolve, { once: true }); 40 }); 41 // Add the iframe to the DOM tree in order to be able to have its browsingContext 42 content.document.body.appendChild(iframe); 43 const { browsingContext } = iframe; 44 45 is( 46 iframe.contentDocument.isInitialDocument, 47 true, 48 "The field is true on just-created iframes" 49 ); 50 let beforeLoadPromise = SpecialPowers.spawnChrome( 51 [browsingContext], 52 bc => [ 53 bc.currentWindowGlobal.isInitialDocument, 54 bc.currentWindowGlobal._test_wasInitialDocument, 55 ] 56 ); 57 58 await loadPromise; 59 is( 60 iframe.contentDocument.isInitialDocument, 61 true, 62 "The field remains true when the iframe stays at about:blank" 63 ); 64 let afterLoadPromise = SpecialPowers.spawnChrome( 65 [browsingContext], 66 bc => [ 67 bc.currentWindowGlobal.isInitialDocument, 68 bc.currentWindowGlobal._test_wasInitialDocument, 69 ] 70 ); 71 72 // Wait to await the parent process promises, so we can't miss the "load" event. 73 let [beforeIsInitial, beforeWasInitial] = await beforeLoadPromise; 74 is(beforeIsInitial, true, "before load is initial in parent"); 75 is(beforeWasInitial, true, "before load was initial in parent"); 76 let [afterIsInitial, afterWasInitial] = await afterLoadPromise; 77 is(afterIsInitial, true, "after load is initial in parent"); 78 is(afterWasInitial, true, "after load was initial in parent"); 79 iframe.remove(); 80 }); 81 82 info("Create an iframe with a cross origin location"); 83 const iframeBC = await SpecialPowers.spawn(browser, [], async () => { 84 const iframe = content.document.createElement("iframe"); 85 await new Promise(resolve => { 86 iframe.addEventListener("load", resolve, { once: true }); 87 iframe.src = 88 // eslint-disable-next-line @microsoft/sdl/no-insecure-url 89 "http://example.org/document-builder.sjs?html=org-iframe"; 90 content.document.body.appendChild(iframe); 91 }); 92 93 return iframe.browsingContext; 94 }); 95 96 is( 97 iframeBC.currentWindowGlobal.isInitialDocument, 98 false, 99 "The field is true after having loaded the final document" 100 ); 101 } 102 ); 103 }); 104 105 add_task(async function window_open() { 106 async function testWindowOpen({ browser, args, isCrossOrigin, willLoad }) { 107 info(`Open popup with ${JSON.stringify(args)}`); 108 let url = args[0] || "about:blank"; 109 const onNewTab = BrowserTestUtils.waitForNewTab(gBrowser, url); 110 await SpecialPowers.spawn( 111 browser, 112 [url, args, isCrossOrigin, willLoad], 113 async (url, args, crossOrigin, willLoad) => { 114 const win = content.window.open(...args); 115 is( 116 win.document.isInitialDocument, 117 true, 118 "The field is true right after calling window.open()" 119 ); 120 let beforeLoadPromise = SpecialPowers.spawnChrome( 121 [win.browsingContext], 122 bc => [ 123 bc.currentWindowGlobal.isInitialDocument, 124 bc.currentWindowGlobal._test_wasInitialDocument, 125 ] 126 ); 127 128 // In cross origin, it is harder to watch for new document load, and if 129 // no argument is passed no load will happen. 130 if (!crossOrigin && willLoad && url != "about:blank") { 131 await new Promise(r => 132 win.addEventListener("load", r, { once: true }) 133 ); 134 is( 135 win.document.isInitialDocument, 136 false, 137 "The field becomes false right after the popup document is loaded" 138 ); 139 } 140 141 // Perform the await after the load to avoid missing it. 142 let [beforeIsInitial, beforeWasInitial] = await beforeLoadPromise; 143 is(beforeIsInitial, true, "before load is initial in parent"); 144 is(beforeWasInitial, true, "before load was initial in parent"); 145 } 146 ); 147 const newTab = await onNewTab; 148 const windowGlobal = 149 newTab.linkedBrowser.browsingContext.currentWindowGlobal; 150 if (willLoad) { 151 if (url == "about:blank") { 152 is( 153 windowGlobal.isInitialDocument, 154 true, 155 "The field is true in the parent process after having loaded about:blank" 156 ); 157 } else { 158 is( 159 windowGlobal.isInitialDocument, 160 false, 161 "The field is false in the parent process after having loaded the final document" 162 ); 163 } 164 } else { 165 is( 166 windowGlobal.isInitialDocument, 167 true, 168 "The field remains true in the parent process as nothing will be loaded" 169 ); 170 } 171 BrowserTestUtils.removeTab(newTab); 172 } 173 174 await BrowserTestUtils.withNewTab( 175 // eslint-disable-next-line @microsoft/sdl/no-insecure-url 176 "http://example.com/document-builder.sjs?html=com", 177 async browser => { 178 info("Use window.open() with cross-origin document"); 179 await testWindowOpen({ 180 browser, 181 // eslint-disable-next-line @microsoft/sdl/no-insecure-url 182 args: ["http://example.org/document-builder.sjs?html=org-popup"], 183 isCrossOrigin: true, 184 willLoad: true, 185 }); 186 187 info("Use window.open() with same-origin document"); 188 await testWindowOpen({ 189 browser, 190 // eslint-disable-next-line @microsoft/sdl/no-insecure-url 191 args: ["http://example.com/document-builder.sjs?html=com-popup"], 192 isCrossOrigin: false, 193 willLoad: true, 194 }); 195 196 info("Use window.open() with final about:blank document"); 197 await testWindowOpen({ 198 browser, 199 args: ["about:blank"], 200 isCrossOrigin: false, 201 willLoad: true, 202 }); 203 204 info("Use window.open() with no argument"); 205 await testWindowOpen({ 206 browser, 207 args: [], 208 isCrossOrigin: false, 209 willLoad: false, 210 }); 211 } 212 ); 213 }); 214 215 add_task(async function document_open() { 216 await BrowserTestUtils.withNewTab( 217 // eslint-disable-next-line @microsoft/sdl/no-insecure-url 218 "http://example.com/document-builder.sjs?html=com", 219 async browser => { 220 is(browser.browsingContext.currentWindowGlobal.isInitialDocument, false); 221 await SpecialPowers.spawn(browser, [], async () => { 222 const iframe = content.document.createElement("iframe"); 223 // Add the iframe to the DOM tree in order to be able to have its browsingContext 224 content.document.body.appendChild(iframe); 225 const { browsingContext } = iframe; 226 227 // Check the state before the call in both parent and content. 228 is( 229 iframe.contentDocument.isInitialDocument, 230 true, 231 "Is an initial document before calling document.open" 232 ); 233 let beforeOpenParentPromise = SpecialPowers.spawnChrome( 234 [browsingContext], 235 bc => [ 236 bc.currentWindowGlobal.isInitialDocument, 237 bc.currentWindowGlobal._test_wasInitialDocument, 238 bc.currentWindowGlobal.innerWindowId, 239 ] 240 ); 241 242 // Run the `document.open` call with reduced permissions. 243 iframe.contentWindow.eval(` 244 document.open(); 245 document.write("new document"); 246 document.close(); 247 `); 248 249 is( 250 iframe.contentDocument.isInitialDocument, 251 false, 252 "Is no longer an initial document after calling document.open" 253 ); 254 let [afterIsInitial, afterWasInitial, afterID] = 255 await SpecialPowers.spawnChrome([browsingContext], bc => [ 256 bc.currentWindowGlobal.isInitialDocument, 257 bc.currentWindowGlobal._test_wasInitialDocument, 258 bc.currentWindowGlobal.innerWindowId, 259 ]); 260 let [beforeIsInitial, beforeWasInitial, beforeID] = 261 await beforeOpenParentPromise; 262 is(beforeIsInitial, true, "Should be initial before in the parent"); 263 is(beforeWasInitial, true, "Was initial before in the parent"); 264 is(afterIsInitial, false, "Should not be initial after in the parent"); 265 is(afterWasInitial, true, "Was initial after in the parent"); 266 is(beforeID, afterID, "Should be the same WindowGlobalParent"); 267 }); 268 } 269 ); 270 }); 271 272 add_task(async function windowless_browser() { 273 info("Create a Windowless browser"); 274 const browser = Services.appShell.createWindowlessBrowser(false); 275 const { browsingContext } = browser; 276 is( 277 browsingContext.currentWindowGlobal.isInitialDocument, 278 true, 279 "The field is true for a freshly created WindowlessBrowser" 280 ); 281 is( 282 browser.currentURI.spec, 283 "about:blank", 284 "The location is immediately set to about:blank" 285 ); 286 287 const principal = Services.scriptSecurityManager.getSystemPrincipal(); 288 browser.docShell.createAboutBlankDocumentViewer(principal, principal); 289 is( 290 browsingContext.currentWindowGlobal.isInitialDocument, 291 false, 292 "The field becomes false when creating an artificial blank document" 293 ); 294 295 info("Load a final about:blank document in it"); 296 const onLocationChange = new Promise(resolve => { 297 let wpl = { 298 QueryInterface: ChromeUtils.generateQI([ 299 "nsIWebProgressListener", 300 "nsISupportsWeakReference", 301 ]), 302 onLocationChange() { 303 browsingContext.webProgress.removeProgressListener( 304 wpl, 305 Ci.nsIWebProgress.NOTIFY_ALL 306 ); 307 resolve(); 308 }, 309 }; 310 browsingContext.webProgress.addProgressListener( 311 wpl, 312 Ci.nsIWebProgress.NOTIFY_ALL 313 ); 314 }); 315 browser.loadURI(Services.io.newURI("about:blank"), { 316 triggeringPrincipal: principal, 317 }); 318 info("Wait for the location change"); 319 await onLocationChange; 320 is( 321 browsingContext.currentWindowGlobal.isInitialDocument, 322 false, 323 "The field is false after the location change event" 324 ); 325 browser.close(); 326 });