tor-browser

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

browser_target_parents.js (5438B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 // Test a given Target's parentFront attribute returns the correct parent front.
      7 
      8 const {
      9  DevToolsClient,
     10 } = require("resource://devtools/client/devtools-client.js");
     11 const {
     12  DevToolsServer,
     13 } = require("resource://devtools/server/devtools-server.js");
     14 const {
     15  createCommandsDictionary,
     16 } = require("resource://devtools/shared/commands/index.js");
     17 
     18 const TEST_URL = `data:text/html;charset=utf-8,<div id="test"></div>`;
     19 
     20 // Test against Tab targets
     21 add_task(async function () {
     22  const tab = await addTab(TEST_URL);
     23 
     24  const client = await setupDebuggerClient();
     25  const mainRoot = client.mainRoot;
     26 
     27  const tabDescriptors = await mainRoot.listTabs();
     28 
     29  const concurrentCommands = [];
     30  for (const descriptor of tabDescriptors) {
     31    concurrentCommands.push(
     32      (async () => {
     33        const commands = await createCommandsDictionary(descriptor);
     34        // Descriptor's getTarget will only work if the TargetCommand watches for the first top target
     35        await commands.targetCommand.startListening();
     36      })()
     37    );
     38  }
     39  info("Instantiate all tab's commands and initialize their TargetCommand");
     40  await Promise.all(concurrentCommands);
     41 
     42  await testGetTargetWithConcurrentCalls(tabDescriptors, tabTarget => {
     43    // We only call BrowsingContextTargetFront.attach and not TargetMixin.attachAndInitThread.
     44    // So very few things are done.
     45    return !!tabTarget.targetForm?.traits;
     46  });
     47 
     48  await client.close();
     49  await removeTab(tab);
     50 });
     51 
     52 // Test against Process targets
     53 add_task(async function () {
     54  const client = await setupDebuggerClient();
     55  const mainRoot = client.mainRoot;
     56 
     57  const processes = await mainRoot.listProcesses();
     58 
     59  // Assert that concurrent calls to getTarget resolves the same target and that it is already attached
     60  // With that, we were chasing a precise race, where a second call to ProcessDescriptor.getTarget()
     61  // happens between the instantiation of ContentProcessTarget and its call to attach() from getTarget
     62  // function.
     63  await testGetTargetWithConcurrentCalls(processes, () => {
     64    // We only call ContentProcessTargetFront.attach and not TargetMixin.attachAndInitThread.
     65    // So nothing is done for content process targets.
     66    return true;
     67  });
     68 
     69  await client.close();
     70 });
     71 
     72 // Test against worker targets on parent process
     73 add_task(async function () {
     74  const client = await setupDebuggerClient();
     75 
     76  const mainRoot = client.mainRoot;
     77 
     78  const { workers } = await mainRoot.listWorkers();
     79 
     80  ok(!!workers.length, "list workers returned a non-empty list of workers");
     81 
     82  for (const workerDescriptorFront of workers) {
     83    let targetFront;
     84    try {
     85      targetFront = await workerDescriptorFront.getTarget();
     86    } catch (e) {
     87      // Ignore race condition where we are trying to connect to a worker
     88      // related to a previous test which is being destroyed.
     89      if (
     90        e.message.includes("nsIWorkerDebugger.initialize") ||
     91        workerDescriptorFront.isDestroyed() ||
     92        !workerDescriptorFront.name
     93      ) {
     94        info("Failed to connect to " + workerDescriptorFront.url);
     95        continue;
     96      }
     97      throw e;
     98    }
     99    // Bug 1767760: name might be null on some worker which are probably initializing or destroying.
    100    if (!workerDescriptorFront.name) {
    101      info("Failed to connect to " + workerDescriptorFront.url);
    102      continue;
    103    }
    104 
    105    is(
    106      workerDescriptorFront,
    107      targetFront,
    108      "For now, worker descriptors and targets are the same object (see bug 1667404)"
    109    );
    110    // Check that accessing descriptor#name getter doesn't throw (See Bug 1714974).
    111    ok(
    112      workerDescriptorFront.name.includes(".js") ||
    113        workerDescriptorFront.name.includes(".mjs"),
    114      `worker descriptor front holds the worker file name (${workerDescriptorFront.name})`
    115    );
    116    is(
    117      workerDescriptorFront.isWorkerDescriptor,
    118      true,
    119      "isWorkerDescriptor is true"
    120    );
    121  }
    122 
    123  await client.close();
    124 });
    125 
    126 async function setupDebuggerClient() {
    127  // Instantiate a minimal server
    128  DevToolsServer.init();
    129  DevToolsServer.allowChromeProcess = true;
    130  if (!DevToolsServer.createRootActor) {
    131    DevToolsServer.registerAllActors();
    132  }
    133  const transport = DevToolsServer.connectPipe();
    134  const client = new DevToolsClient(transport);
    135  await client.connect();
    136  return client;
    137 }
    138 
    139 async function testGetTargetWithConcurrentCalls(descriptors, isTargetAttached) {
    140  // Assert that concurrent calls to getTarget resolves the same target and that it is already attached
    141  await Promise.all(
    142    descriptors.map(async descriptor => {
    143      const promises = [];
    144      const concurrentCalls = 10;
    145      for (let i = 0; i < concurrentCalls; i++) {
    146        const targetPromise = descriptor.getTarget();
    147        // Every odd runs, wait for a tick to introduce some more randomness
    148        if (i % 2 == 0) {
    149          await wait(0);
    150        }
    151        promises.push(
    152          targetPromise.then(target => {
    153            ok(isTargetAttached(target), "The target is attached");
    154            return target;
    155          })
    156        );
    157      }
    158      const targets = await Promise.all(promises);
    159      for (let i = 1; i < concurrentCalls; i++) {
    160        is(
    161          targets[0],
    162          targets[i],
    163          "All the targets returned by concurrent calls to getTarget are the same"
    164        );
    165      }
    166    })
    167  );
    168 }