summaryrefslogtreecommitdiffstats
path: root/dom/tests/browser/browser_windowProxy_transplant.js
blob: 8e6e0f8413a699e10468150ca85ab6bfffeaed09 (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
"use strict";

const DIRPATH = getRootDirectory(gTestPath).replace(
  "chrome://mochitests/content/",
  ""
);
const PATH = DIRPATH + "file_postMessage_parent.html";

const URL1 = `http://mochi.test:8888/${PATH}`;
const URL2 = `http://example.com/${PATH}`;
const URL3 = `http://example.org/${PATH}`;

// A bunch of boilerplate which needs to be dealt with.
add_task(async function () {
  // Turn on BC preservation and frameloader rebuilding to ensure that the
  // BrowsingContext is preserved.
  await SpecialPowers.pushPrefEnv({
    set: [["fission.preserve_browsing_contexts", true]],
  });

  // Open a window with fission force-enabled in it.
  let win = await BrowserTestUtils.openNewBrowserWindow({
    fission: true,
    remote: true,
  });
  try {
    // Get the tab & browser to perform the test in.
    let tab = win.gBrowser.selectedTab;
    let browser = tab.linkedBrowser;

    // Start loading the original URI, then wait until it is loaded.
    BrowserTestUtils.startLoadingURIString(browser, URL1);
    await BrowserTestUtils.browserLoaded(browser, false, URL1);

    info("Chrome script has loaded initial URI.");
    await SpecialPowers.spawn(
      browser,
      [{ URL1, URL2, URL3 }],
      async ({ URL1, URL2, URL3 }) => {
        let iframe = content.document.createElement("iframe");
        content.document.body.appendChild(iframe);

        info("Chrome script created iframe");

        // Here and below, we have to store references to things in the
        // iframes on the content window, because all chrome references
        // to content will be turned into dead wrappers when the iframes
        // are closed.
        content.win0 = iframe.contentWindow;
        content.bc0 = iframe.browsingContext;

        ok(
          !Cu.isDeadWrapper(content.win0),
          "win0 shouldn't be a dead wrapper before navigation"
        );

        // Helper for waiting for a load.
        function waitLoad() {
          return new Promise(resolve => {
            iframe.addEventListener(
              "load",
              () => {
                info("Got an iframe load event!");
                resolve();
              },
              { once: true }
            );
          });
        }

        function askLoad(url) {
          info("Chrome script asking for load of " + url);
          iframe.contentWindow.postMessage(
            {
              action: "navigate",
              location: url,
            },
            "*"
          );
          info("Chrome script done calling PostMessage");
        }

        // Check that BC and WindowProxy are preserved across navigations.
        iframe.contentWindow.location = URL1;
        await waitLoad();

        content.win1 = iframe.contentWindow;
        let chromeWin1 = iframe.contentWindow;
        let chromeWin1x = Cu.waiveXrays(iframe.contentWindow);
        content.win1x = Cu.waiveXrays(iframe.contentWindow);

        Assert.notEqual(
          chromeWin1,
          chromeWin1x,
          "waiving xrays creates a new thing?"
        );

        content.bc1 = iframe.browsingContext;

        is(
          content.bc0,
          content.bc1,
          "same to same-origin BrowsingContext match"
        );
        is(content.win0, content.win1, "same to same-origin WindowProxy match");

        ok(
          !Cu.isDeadWrapper(content.win1),
          "win1 shouldn't be a dead wrapper before navigation"
        );
        ok(
          !Cu.isDeadWrapper(chromeWin1),
          "chromeWin1 shouldn't be a dead wrapper before navigation"
        );

        askLoad(URL2);
        await waitLoad();

        content.win2 = iframe.contentWindow;
        content.bc2 = iframe.browsingContext;

        // When chrome accesses a remote window proxy in content, the result
        // should be a remote outer window proxy in the chrome compartment, not an
        // Xray wrapper around the content remote window proxy. The former will
        // throw a security error, because @@toPrimitive can't be called cross
        // process, while the latter will result in an opaque wrapper, because
        // XPConnect doesn't know what to do when trying to create an Xray wrapper
        // around a remote outer window proxy. See bug 1556845.
        Assert.throws(
          () => {
            dump("content.win1 " + content.win1 + "\n");
          },
          /SecurityError: Permission denied to access property Symbol.toPrimitive on cross-origin object/,
          "Should get a remote outer window proxy when accessing old window proxy"
        );
        Assert.throws(
          () => {
            dump("content.win2 " + content.win2 + "\n");
          },
          /SecurityError: Permission denied to access property Symbol.toPrimitive on cross-origin object/,
          "Should get a remote outer window proxy when accessing new window proxy"
        );

        // If we fail to transplant existing non-remote outer window proxies, then
        // after we navigate the iframe existing chrome references to the window will
        // become dead wrappers. Also check content.win1 for thoroughness, though
        // we don't nuke content-content references.
        ok(
          !Cu.isDeadWrapper(content.win1),
          "win1 shouldn't be a dead wrapper after navigation"
        );
        ok(
          !Cu.isDeadWrapper(chromeWin1),
          "chromeWin1 shouldn't be a dead wrapper after navigation"
        );
        ok(
          Cu.isDeadWrapper(chromeWin1x),
          "chromeWin1x should be a dead wrapper after navigation"
        );
        ok(
          Cu.isDeadWrapper(content.win1x),
          "content.win1x should be a dead wrapper after navigation"
        );

        is(
          content.bc1,
          content.bc2,
          "same to cross-origin navigation BrowsingContext match"
        );
        is(
          content.win1,
          content.win2,
          "same to cross-origin navigation WindowProxy match"
        );

        ok(
          !Cu.isDeadWrapper(content.win1),
          "win1 shouldn't be a dead wrapper after navigation"
        );

        askLoad(URL3);
        await waitLoad();

        content.win3 = iframe.contentWindow;
        content.bc3 = iframe.browsingContext;

        is(
          content.bc2,
          content.bc3,
          "cross to cross-origin navigation BrowsingContext match"
        );
        is(
          content.win2,
          content.win3,
          "cross to cross-origin navigation WindowProxy match"
        );

        askLoad(URL1);
        await waitLoad();

        content.win4 = iframe.contentWindow;
        content.bc4 = iframe.browsingContext;

        is(
          content.bc3,
          content.bc4,
          "cross to same-origin navigation BrowsingContext match"
        );
        is(
          content.win3,
          content.win4,
          "cross to same-origin navigation WindowProxy match"
        );
      }
    );
  } finally {
    await BrowserTestUtils.closeWindow(win);
  }
});