browser_windowProxy_transplant.js (6799B)
1 "use strict"; 2 3 const DIRPATH = getRootDirectory(gTestPath).replace( 4 "chrome://mochitests/content/", 5 "" 6 ); 7 const PATH = DIRPATH + "file_postMessage_parent.html"; 8 9 const URL1 = `http://mochi.test:8888/${PATH}`; 10 const URL2 = `http://example.com/${PATH}`; 11 const URL3 = `http://example.org/${PATH}`; 12 13 // A bunch of boilerplate which needs to be dealt with. 14 add_task(async function () { 15 // Open a window with fission force-enabled in it. 16 let win = await BrowserTestUtils.openNewBrowserWindow({ 17 fission: true, 18 remote: true, 19 }); 20 try { 21 // Get the tab & browser to perform the test in. 22 let tab = win.gBrowser.selectedTab; 23 let browser = tab.linkedBrowser; 24 25 // Start loading the original URI, then wait until it is loaded. 26 BrowserTestUtils.startLoadingURIString(browser, URL1); 27 await BrowserTestUtils.browserLoaded(browser, false, URL1); 28 29 info("Chrome script has loaded initial URI."); 30 await SpecialPowers.spawn( 31 browser, 32 [{ URL1, URL2, URL3 }], 33 async ({ URL1, URL2, URL3 }) => { 34 let iframe = content.document.createElement("iframe"); 35 content.document.body.appendChild(iframe); 36 37 info("Chrome script created iframe"); 38 39 // Here and below, we have to store references to things in the 40 // iframes on the content window, because all chrome references 41 // to content will be turned into dead wrappers when the iframes 42 // are closed. 43 content.win0 = iframe.contentWindow; 44 content.bc0 = iframe.browsingContext; 45 46 ok( 47 !Cu.isDeadWrapper(content.win0), 48 "win0 shouldn't be a dead wrapper before navigation" 49 ); 50 51 // Helper for waiting for a load. 52 function waitLoad() { 53 return new Promise(resolve => { 54 iframe.addEventListener( 55 "load", 56 () => { 57 info("Got an iframe load event!"); 58 resolve(); 59 }, 60 { once: true } 61 ); 62 }); 63 } 64 65 function askLoad(url) { 66 info("Chrome script asking for load of " + url); 67 iframe.contentWindow.postMessage( 68 { 69 action: "navigate", 70 location: url, 71 }, 72 "*" 73 ); 74 info("Chrome script done calling PostMessage"); 75 } 76 77 // Check that BC and WindowProxy are preserved across navigations. 78 iframe.contentWindow.location = URL1; 79 await waitLoad(); 80 81 content.win1 = iframe.contentWindow; 82 let chromeWin1 = iframe.contentWindow; 83 let chromeWin1x = Cu.waiveXrays(iframe.contentWindow); 84 content.win1x = Cu.waiveXrays(iframe.contentWindow); 85 86 Assert.notEqual( 87 chromeWin1, 88 chromeWin1x, 89 "waiving xrays creates a new thing?" 90 ); 91 92 content.bc1 = iframe.browsingContext; 93 94 is( 95 content.bc0, 96 content.bc1, 97 "same to same-origin BrowsingContext match" 98 ); 99 is(content.win0, content.win1, "same to same-origin WindowProxy match"); 100 101 ok( 102 !Cu.isDeadWrapper(content.win1), 103 "win1 shouldn't be a dead wrapper before navigation" 104 ); 105 ok( 106 !Cu.isDeadWrapper(chromeWin1), 107 "chromeWin1 shouldn't be a dead wrapper before navigation" 108 ); 109 110 askLoad(URL2); 111 await waitLoad(); 112 113 content.win2 = iframe.contentWindow; 114 content.bc2 = iframe.browsingContext; 115 116 // When chrome accesses a remote window proxy in content, the result 117 // should be a remote outer window proxy in the chrome compartment, not an 118 // Xray wrapper around the content remote window proxy. The former will 119 // throw a security error, because @@toPrimitive can't be called cross 120 // process, while the latter will result in an opaque wrapper, because 121 // XPConnect doesn't know what to do when trying to create an Xray wrapper 122 // around a remote outer window proxy. See bug 1556845. 123 Assert.throws( 124 () => { 125 dump("content.win1 " + content.win1 + "\n"); 126 }, 127 /SecurityError: Permission denied to access property Symbol.toPrimitive on cross-origin object/, 128 "Should get a remote outer window proxy when accessing old window proxy" 129 ); 130 Assert.throws( 131 () => { 132 dump("content.win2 " + content.win2 + "\n"); 133 }, 134 /SecurityError: Permission denied to access property Symbol.toPrimitive on cross-origin object/, 135 "Should get a remote outer window proxy when accessing new window proxy" 136 ); 137 138 // If we fail to transplant existing non-remote outer window proxies, then 139 // after we navigate the iframe existing chrome references to the window will 140 // become dead wrappers. Also check content.win1 for thoroughness, though 141 // we don't nuke content-content references. 142 ok( 143 !Cu.isDeadWrapper(content.win1), 144 "win1 shouldn't be a dead wrapper after navigation" 145 ); 146 ok( 147 !Cu.isDeadWrapper(chromeWin1), 148 "chromeWin1 shouldn't be a dead wrapper after navigation" 149 ); 150 ok( 151 Cu.isDeadWrapper(chromeWin1x), 152 "chromeWin1x should be a dead wrapper after navigation" 153 ); 154 ok( 155 Cu.isDeadWrapper(content.win1x), 156 "content.win1x should be a dead wrapper after navigation" 157 ); 158 159 is( 160 content.bc1, 161 content.bc2, 162 "same to cross-origin navigation BrowsingContext match" 163 ); 164 is( 165 content.win1, 166 content.win2, 167 "same to cross-origin navigation WindowProxy match" 168 ); 169 170 ok( 171 !Cu.isDeadWrapper(content.win1), 172 "win1 shouldn't be a dead wrapper after navigation" 173 ); 174 175 askLoad(URL3); 176 await waitLoad(); 177 178 content.win3 = iframe.contentWindow; 179 content.bc3 = iframe.browsingContext; 180 181 is( 182 content.bc2, 183 content.bc3, 184 "cross to cross-origin navigation BrowsingContext match" 185 ); 186 is( 187 content.win2, 188 content.win3, 189 "cross to cross-origin navigation WindowProxy match" 190 ); 191 192 askLoad(URL1); 193 await waitLoad(); 194 195 content.win4 = iframe.contentWindow; 196 content.bc4 = iframe.browsingContext; 197 198 is( 199 content.bc3, 200 content.bc4, 201 "cross to same-origin navigation BrowsingContext match" 202 ); 203 is( 204 content.win3, 205 content.win4, 206 "cross to same-origin navigation WindowProxy match" 207 ); 208 } 209 ); 210 } finally { 211 await BrowserTestUtils.closeWindow(win); 212 } 213 });