summaryrefslogtreecommitdiffstats
path: root/docshell/test/browser/browser_isInitialDocument.js
blob: 7b62be8f6fac88debd619e2b1f5ebdd01c529c09 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
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();
});