tor-browser

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

browser_target_command_processes.js (7362B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 // Test the TargetCommand API around processes
      7 
      8 const TEST_URL =
      9  "data:text/html;charset=utf-8," + encodeURIComponent(`<div id="test"></div>`);
     10 
     11 add_task(async function () {
     12  // Enabled fission's pref as the TargetCommand is almost disabled without it
     13  await pushPref("devtools.browsertoolbox.scope", "everything");
     14  // Disable the preloaded process as it gets created lazily and may interfere
     15  // with process count assertions
     16  await pushPref("dom.ipc.processPrelaunch.enabled", false);
     17  // This preference helps destroying the content process when we close the tab
     18  await pushPref("dom.ipc.keepProcessesAlive.web", 1);
     19 
     20  const commands = await CommandsFactory.forMainProcess();
     21  const targetCommand = commands.targetCommand;
     22  await targetCommand.startListening();
     23 
     24  await testProcesses(targetCommand, targetCommand.targetFront);
     25 
     26  targetCommand.destroy();
     27  // Wait for all the targets to be fully attached so we don't have pending requests.
     28  await Promise.all(
     29    targetCommand.getAllTargets(targetCommand.ALL_TYPES).map(t => t.initialized)
     30  );
     31 
     32  await commands.destroy();
     33 });
     34 
     35 add_task(async function () {
     36  const commands = await CommandsFactory.forMainProcess();
     37  const targetCommand = commands.targetCommand;
     38  await targetCommand.startListening();
     39 
     40  const created = [];
     41  const destroyed = [];
     42  const onAvailable = ({ targetFront }) => {
     43    created.push(targetFront);
     44  };
     45  const onDestroyed = ({ targetFront }) => {
     46    destroyed.push(targetFront);
     47  };
     48  await targetCommand.watchTargets({
     49    types: [targetCommand.TYPES.PROCESS],
     50    onAvailable,
     51    onDestroyed,
     52  });
     53  Assert.greater(created.length, 1, "We get many content process targets");
     54 
     55  targetCommand.stopListening();
     56 
     57  await waitFor(
     58    () => created.length == destroyed.length,
     59    "Wait for the destruction of all content process targets when calling stopListening"
     60  );
     61  is(
     62    created.length,
     63    destroyed.length,
     64    "Got notification of destruction for all previously reported targets"
     65  );
     66 
     67  targetCommand.destroy();
     68  // Wait for all the targets to be fully attached so we don't have pending requests.
     69  await Promise.all(
     70    targetCommand.getAllTargets(targetCommand.ALL_TYPES).map(t => t.initialized)
     71  );
     72 
     73  await commands.destroy();
     74 });
     75 
     76 async function testProcesses(targetCommand, target) {
     77  info("Test TargetCommand against processes");
     78  const { TYPES } = targetCommand;
     79 
     80  // Note that ppmm also includes the parent process, which is considered as a frame rather than a process
     81  const originalProcessesCount = Services.ppmm.childCount - 1;
     82  const processes = await targetCommand.getAllTargets([TYPES.PROCESS]);
     83  is(
     84    processes.length,
     85    originalProcessesCount,
     86    "Get a target for all content processes"
     87  );
     88 
     89  const processes2 = await targetCommand.getAllTargets([TYPES.PROCESS]);
     90  is(
     91    processes2.length,
     92    originalProcessesCount,
     93    "retrieved the same number of processes"
     94  );
     95  function sortFronts(f1, f2) {
     96    return f1.actorID < f2.actorID;
     97  }
     98  processes.sort(sortFronts);
     99  processes2.sort(sortFronts);
    100  for (let i = 0; i < processes.length; i++) {
    101    is(processes[i], processes2[i], `process ${i} targets are the same`);
    102  }
    103 
    104  // Assert that watchTargets will call the create callback for all existing frames
    105  const targets = new Set();
    106 
    107  const pidRegExp = /^\d+$/;
    108 
    109  const onAvailable = ({ targetFront }) => {
    110    if (targets.has(targetFront)) {
    111      ok(false, "The same target is notified multiple times via onAvailable");
    112    }
    113    is(
    114      targetFront.targetType,
    115      TYPES.PROCESS,
    116      "We are only notified about process targets"
    117    );
    118    ok(
    119      targetFront == target ? targetFront.isTopLevel : !targetFront.isTopLevel,
    120      "isTopLevel property is correct"
    121    );
    122    ok(
    123      pidRegExp.test(targetFront.processID),
    124      `Target has processID of expected shape (${targetFront.processID})`
    125    );
    126    targets.add(targetFront);
    127  };
    128  const onDestroyed = ({ targetFront }) => {
    129    if (!targets.has(targetFront)) {
    130      ok(
    131        false,
    132        "A target is declared destroyed via onDestroy without being notified via onAvailable"
    133      );
    134    }
    135    is(
    136      targetFront.targetType,
    137      TYPES.PROCESS,
    138      "We are only notified about process targets"
    139    );
    140    ok(
    141      !targetFront.isTopLevel,
    142      "We are never notified about the top level target destruction"
    143    );
    144    targets.delete(targetFront);
    145  };
    146  await targetCommand.watchTargets({
    147    types: [TYPES.PROCESS],
    148    onAvailable,
    149    onDestroyed,
    150  });
    151  is(
    152    targets.size,
    153    originalProcessesCount,
    154    "retrieved the same number of processes via watchTargets"
    155  );
    156  for (let i = 0; i < processes.length; i++) {
    157    ok(
    158      targets.has(processes[i]),
    159      `process ${i} targets are the same via watchTargets`
    160    );
    161  }
    162 
    163  const previousTargets = new Set(targets);
    164  // Assert that onAvailable is called for processes created *after* the call to watchTargets
    165  const onProcessCreated = new Promise(resolve => {
    166    const onAvailable2 = ({ targetFront }) => {
    167      if (previousTargets.has(targetFront)) {
    168        return;
    169      }
    170      targetCommand.unwatchTargets({
    171        types: [TYPES.PROCESS],
    172        onAvailable: onAvailable2,
    173      });
    174      resolve(targetFront);
    175    };
    176    targetCommand.watchTargets({
    177      types: [TYPES.PROCESS],
    178      onAvailable: onAvailable2,
    179    });
    180  });
    181  info("open new tab in new process");
    182  const tab1 = await BrowserTestUtils.openNewForegroundTab({
    183    gBrowser,
    184    url: TEST_URL,
    185    forceNewProcess: true,
    186  });
    187  info("wait for process target to be created");
    188  const createdTarget = await onProcessCreated;
    189  // For some reason, creating a new tab purges processes created from previous tests
    190  // so it is not reasonable to assert the size of `targets` as it may be lower than expected.
    191  ok(targets.has(createdTarget), "The new tab process is in the list");
    192 
    193  const processCountAfterTabOpen = targets.size;
    194 
    195  // Assert that onDestroy is called for destroyed processes
    196  const onProcessDestroyed = new Promise(resolve => {
    197    const onAvailable3 = () => {};
    198    const onDestroyed3 = ({ targetFront }) => {
    199      resolve(targetFront);
    200      targetCommand.unwatchTargets({
    201        types: [TYPES.PROCESS],
    202        onAvailable: onAvailable3,
    203        onDestroyed: onDestroyed3,
    204      });
    205    };
    206    targetCommand.watchTargets({
    207      types: [TYPES.PROCESS],
    208      onAvailable: onAvailable3,
    209      onDestroyed: onDestroyed3,
    210    });
    211  });
    212 
    213  BrowserTestUtils.removeTab(tab1);
    214 
    215  const destroyedTarget = await onProcessDestroyed;
    216  is(
    217    targets.size,
    218    processCountAfterTabOpen - 1,
    219    "The closed tab's process has been reported as destroyed"
    220  );
    221  ok(
    222    !targets.has(destroyedTarget),
    223    "The destroyed target is no longer in the list"
    224  );
    225  is(
    226    destroyedTarget,
    227    createdTarget,
    228    "The destroyed target is the one that has been reported as created"
    229  );
    230 
    231  targetCommand.unwatchTargets({
    232    types: [TYPES.PROCESS],
    233    onAvailable,
    234    onDestroyed,
    235  });
    236 
    237  // Ensure that getAllTargets still works after the call to unwatchTargets
    238  const processes3 = await targetCommand.getAllTargets([TYPES.PROCESS]);
    239  is(
    240    processes3.length,
    241    processCountAfterTabOpen - 1,
    242    "getAllTargets reports a new target"
    243  );
    244 }