head.js (7048B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 var { ContextDescriptorType } = ChromeUtils.importESModule( 8 "chrome://remote/content/shared/messagehandler/MessageHandler.sys.mjs" 9 ); 10 11 var { WindowGlobalMessageHandler } = ChromeUtils.importESModule( 12 "chrome://remote/content/shared/messagehandler/WindowGlobalMessageHandler.sys.mjs" 13 ); 14 15 var contextDescriptorAll = { 16 type: ContextDescriptorType.All, 17 }; 18 19 function createRootMessageHandler(sessionId) { 20 const { RootMessageHandlerRegistry } = ChromeUtils.importESModule( 21 "chrome://remote/content/shared/messagehandler/RootMessageHandlerRegistry.sys.mjs" 22 ); 23 return RootMessageHandlerRegistry.getOrCreateMessageHandler(sessionId); 24 } 25 26 /** 27 * Load the provided url in an existing browser. 28 * Returns a promise which will resolve when the page is loaded. 29 * 30 * @param {Browser} browser 31 * The browser element where the URL should be loaded. 32 * @param {string} url 33 * The URL to load in the new tab 34 */ 35 async function loadURL(browser, url) { 36 const loaded = BrowserTestUtils.browserLoaded(browser); 37 BrowserTestUtils.startLoadingURIString(browser, url); 38 return loaded; 39 } 40 41 /** 42 * Create a new foreground tab loading the provided url. 43 * Returns a promise which will resolve when the page is loaded. 44 * 45 * @param {string} url 46 * The URL to load in the new tab 47 */ 48 async function addTab(url) { 49 const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url); 50 registerCleanupFunction(() => { 51 gBrowser.removeTab(tab); 52 }); 53 return tab; 54 } 55 56 /** 57 * Create inline markup for a simple iframe that can be used with 58 * document-builder.sjs. The iframe will be served under the provided domain. 59 * 60 * @param {string} domain 61 * A domain (eg "example.com"), compatible with build/pgo/server-locations.txt 62 */ 63 function createFrame(domain) { 64 return createFrameForUri( 65 `https://${domain}/document-builder.sjs?html=frame-${domain}` 66 ); 67 } 68 69 function createFrameForUri(uri) { 70 return `<iframe src="${encodeURI(uri)}"></iframe>`; 71 } 72 73 /** 74 * Create a XUL browser element in the provided XUL tab, with the provided type. 75 * 76 * @param {XULTab} tab 77 * The XUL tab in which the browser element should be inserted. 78 * @param {string} type 79 * The type attribute of the browser element, "chrome" or "content". 80 * @returns {XULBrowser} 81 * The created browser element. 82 */ 83 function createParentBrowserElement(tab, type) { 84 const parentBrowser = gBrowser.ownerDocument.createXULElement("browser"); 85 parentBrowser.setAttribute("type", type); 86 const container = gBrowser.getBrowserContainer(tab.linkedBrowser); 87 container.appendChild(parentBrowser); 88 89 return parentBrowser; 90 } 91 92 // Create a test page with 2 iframes: 93 // - one with a different eTLD+1 (example.com) 94 // - one with a nested iframe on a different eTLD+1 (example.net) 95 // 96 // Overall the document structure should look like: 97 // 98 // html (example.org) 99 // iframe (example.org) 100 // iframe (example.net) 101 // iframe(example.com) 102 // 103 // Which means we should have 4 browsing contexts in total. 104 function createTestMarkupWithFrames() { 105 // Create the markup for an example.net frame nested in an example.com frame. 106 const NESTED_FRAME_MARKUP = createFrameForUri( 107 `https://example.org/document-builder.sjs?html=${createFrame( 108 "example.net" 109 )}` 110 ); 111 112 // Combine the nested frame markup created above with an example.com frame. 113 const TEST_URI_MARKUP = `${NESTED_FRAME_MARKUP}${createFrame("example.com")}`; 114 115 // Create the test page URI on example.org. 116 return `https://example.org/document-builder.sjs?html=${encodeURI( 117 TEST_URI_MARKUP 118 )}`; 119 } 120 121 const hasPromiseResolved = async function (promise) { 122 let resolved = false; 123 124 // Note that the catch() is only here to avoid leaking promise rejections. 125 // In all cases the resolved flag should be successfully flipped in finally(). 126 promise.finally(() => (resolved = true)).catch(() => {}); 127 // Make sure microtasks have time to run. 128 await new Promise(resolve => Services.tm.dispatchToMainThread(resolve)); 129 return resolved; 130 }; 131 132 /** 133 * Install a sidebar extension. 134 * 135 * @returns {object} 136 * Return value with two properties: 137 * - extension: test wrapper as returned by SpecialPowers.loadExtension. 138 * Make sure to explicitly call extension.unload() before the end of the test. 139 * - sidebarBrowser: the browser element containing the extension sidebar. 140 */ 141 async function installSidebarExtension() { 142 info("Load the test extension"); 143 let extension = ExtensionTestUtils.loadExtension({ 144 manifest: { 145 sidebar_action: { 146 default_panel: "sidebar.html", 147 }, 148 }, 149 useAddonManager: "temporary", 150 151 files: { 152 "sidebar.html": ` 153 <!DOCTYPE html> 154 <html> 155 Test extension 156 <script src="sidebar.js"></script> 157 </html> 158 `, 159 "sidebar.js": function () { 160 const { browser } = this; 161 browser.test.sendMessage("sidebar-loaded", { 162 bcId: SpecialPowers.wrap(window).browsingContext.id, 163 }); 164 }, 165 "tab.html": ` 166 <!DOCTYPE html> 167 <html> 168 Test extension (tab) 169 <script src="tab.js"></script> 170 </html> 171 `, 172 "tab.js": function () { 173 const { browser } = this; 174 browser.test.sendMessage("tab-loaded", { 175 bcId: SpecialPowers.wrap(window).browsingContext.id, 176 }); 177 }, 178 }, 179 }); 180 181 info("Wait for the extension to start"); 182 await extension.startup(); 183 184 info("Wait for the extension browsing context"); 185 const { bcId } = await extension.awaitMessage("sidebar-loaded"); 186 const sidebarBrowser = BrowsingContext.get(bcId).top.embedderElement; 187 ok(sidebarBrowser, "Got a browser element for the extension sidebar"); 188 189 return { 190 extension, 191 sidebarBrowser, 192 }; 193 } 194 195 const SessionDataUpdateHelpers = { 196 getUpdates(rootMessageHandler, browsingContext) { 197 return rootMessageHandler.handleCommand({ 198 moduleName: "sessiondataupdate", 199 commandName: "getSessionDataUpdates", 200 destination: { 201 id: browsingContext.id, 202 type: WindowGlobalMessageHandler.type, 203 }, 204 }); 205 }, 206 207 createSessionDataUpdate( 208 values, 209 method, 210 category, 211 descriptor = { type: ContextDescriptorType.All } 212 ) { 213 return { 214 method, 215 values, 216 moduleName: "sessiondataupdate", 217 category, 218 contextDescriptor: descriptor, 219 }; 220 }, 221 222 assertUpdate(update, expectedValues, expectedCategory) { 223 is( 224 update.length, 225 expectedValues.length, 226 "Update has the expected number of values" 227 ); 228 229 for (const item of update) { 230 info(`Check session data update item '${item.value}'`); 231 is(item.category, expectedCategory, "Item has the expected category"); 232 is( 233 expectedValues[update.indexOf(item)], 234 item.value, 235 "Item has the expected value" 236 ); 237 } 238 }, 239 };