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