tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_SpecialPowersForProcessSpawn.js (5666B)


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