browser_SpecialPowersForProcessSpawn.js (5411B)
1 "use strict"; 2 3 // Tests SpecialPowersForProcess spawn() in browser chrome mochitests. 4 // An xpcshell version of this test exists at 5 // testing/mochitest/tests/Harness_sanity/test_SpecialPowersForProcessSpawn.js 6 7 const { SpecialPowersForProcess } = ChromeUtils.importESModule( 8 "resource://testing-common/SpecialPowersProcessActor.sys.mjs" 9 ); 10 11 const scope = this; 12 const interceptedMessages = []; 13 add_setup(() => { 14 const orig_record = SimpleTest.record; 15 SimpleTest.record = (condition, name, diag, stack, expected) => { 16 if (name?.startsWith?.("CHECK_THIS:")) { 17 interceptedMessages.push(name); 18 } 19 return orig_record(condition, name, diag, stack, expected); 20 }; 21 const orig_info = SimpleTest.info; 22 SimpleTest.info = msg => { 23 if (msg?.startsWith?.("CHECK_THIS:")) { 24 interceptedMessages.push(msg); 25 } 26 return orig_info(msg); 27 }; 28 registerCleanupFunction(() => { 29 SimpleTest.record = orig_record; 30 SimpleTest.info = orig_info; 31 }); 32 }); 33 34 // Tests that SpecialPowersForProcess can spawn() in processes that the test 35 // grabbed off a contentPage, even after the original page navigated/closed. 36 add_task(async function test_SpecialPowersForProcess_spawn() { 37 interceptedMessages.length = 0; 38 39 const tab = await BrowserTestUtils.openNewForegroundTab( 40 gBrowser, 41 "https://example.com/browser/testing/mochitest/tests/browser/file_xorigin_frames.html" 42 ); 43 await SpecialPowers.spawn(tab.linkedBrowser, [], async () => { 44 Assert.equal( 45 await this.content.wrappedJSObject.loadedPromise, 46 "frames_all_loaded", 47 "All (cross-origin) frames have finished loading" 48 ); 49 }); 50 const browsingContext = tab.linkedBrowser.browsingContext; 51 const proc1 = browsingContext.children[0].currentWindowGlobal.domProcess; 52 const proc2 = browsingContext.children[1].currentWindowGlobal.domProcess; 53 Assert.equal(proc1, proc2, "The two child frames share the same process"); 54 55 const processBoundSpecialPowers = new SpecialPowersForProcess(scope, proc1); 56 57 await SpecialPowers.spawn(tab.linkedBrowser, [], async () => { 58 info("ContentPage.spawn: Change frame1 process"); 59 const frame1 = this.content.document.getElementById("frame1"); 60 Assert.throws( 61 () => frame1.contentDocument.location.search, 62 /TypeError: (can't access property "location", )?frame1.contentDocument is null/, 63 "ContentPage.spawn: Assert, cannot read cross-origin content" 64 ); 65 await new Promise(resolve => { 66 frame1.onload = resolve; 67 frame1.src = "/dummy?3"; 68 }); 69 // Verify that it is same-origin now. 70 Assert.equal( 71 frame1.contentDocument.location.search, 72 "?3", 73 "CHECK_THIS: ContentPage.spawn: Assert, frame1 is now same-origin" 74 ); 75 info("CHECK_THIS: ContentPage.spawn: remove frame1"); 76 frame1.remove(); 77 78 // spawn() implementation has special logic to route Assert messages; 79 // Prepare to check that Assert can be called after spawn() returns. 80 this.content.assertAfterSpawnReturns = () => { 81 Assert.ok(true, "CHECK_THIS: ContentPage.spawn: asssert after return"); 82 }; 83 }); 84 85 await SpecialPowers.spawn(tab.linkedBrowser, [], () => { 86 this.content.assertAfterSpawnReturns(); 87 }); 88 89 Assert.equal(browsingContext.children.length, 1, "frame1 was removed"); 90 91 // Now frame1 has navigated (and switched processes) and removed, so if the 92 // SpecialPowers implementation were to rely on JSWindowActor, then that 93 // would break if we try to interact with it at this point. Check that we 94 // can connect just fine (because JSProcessActor should be used instead). 95 await processBoundSpecialPowers.spawn([], () => { 96 info("CHECK_THIS: process-bound spawn: still works"); 97 Assert.equal( 98 typeof content, 99 "undefined", 100 "CHECK_THIS: process-bound spawn: no content global" 101 ); 102 // Need a shared object that outlives this SpecialPowersSandbox instance: 103 const sharedGlobalObj = Cu.getGlobalForObject(Services); 104 // spawn() implementation has special logic to route Assert messages; 105 // Prepare to check that Assert can be called after spawn() returns. 106 sharedGlobalObj.assertAfterProcessBoundSpawnReturns = () => { 107 Assert.ok(true, "CHECK_THIS: process-bound spawn: asssert after return"); 108 }; 109 }); 110 await processBoundSpecialPowers.spawn([], () => { 111 // Shared object that outlived the previous SpecialPowersSandbox instance: 112 const sharedGlobalObj = Cu.getGlobalForObject(Services); 113 sharedGlobalObj.assertAfterProcessBoundSpawnReturns(); 114 delete sharedGlobalObj.assertAfterProcessBoundSpawnReturns; 115 }); 116 BrowserTestUtils.removeTab(tab); 117 await processBoundSpecialPowers.destroy(); 118 119 Assert.throws( 120 () => processBoundSpecialPowers.spawn([], () => {}), 121 /this.actor is null/, 122 "Cannot spawn after destroy()" 123 ); 124 125 const observedMessages = interceptedMessages.splice(0); 126 Assert.deepEqual( 127 observedMessages, 128 [ 129 `CHECK_THIS: ContentPage.spawn: Assert, frame1 is now same-origin - "?3" == "?3"`, 130 "CHECK_THIS: ContentPage.spawn: remove frame1", 131 "CHECK_THIS: ContentPage.spawn: asssert after return - true == true", 132 "CHECK_THIS: process-bound spawn: still works", 133 `CHECK_THIS: process-bound spawn: no content global - "undefined" == "undefined"`, 134 "CHECK_THIS: process-bound spawn: asssert after return - true == true", 135 ], 136 "Observed calls through spawn" 137 ); 138 });