tor-browser

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

browser_target_command_watchTargets.js (6516B)


      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's `watchTargets` function
      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  await testWatchTargets();
     21  await testThrowingInOnAvailable();
     22 });
     23 
     24 async function testWatchTargets() {
     25  info("Test TargetCommand watchTargets function");
     26 
     27  const commands = await CommandsFactory.forMainProcess();
     28  const targetCommand = commands.targetCommand;
     29  const { TYPES } = targetCommand;
     30 
     31  await targetCommand.startListening();
     32 
     33  // Note that ppmm also includes the parent process, which is considered as a frame rather than a process
     34  const originalProcessesCount = Services.ppmm.childCount - 1;
     35 
     36  info(
     37    "Check that onAvailable is called for processes already created *before* the call to watchTargets"
     38  );
     39  const targets = new Set();
     40  const topLevelTarget = targetCommand.targetFront;
     41  const onAvailable = ({ targetFront }) => {
     42    if (targets.has(targetFront)) {
     43      ok(false, "The same target is notified multiple times via onAvailable");
     44    }
     45    is(
     46      targetFront.targetType,
     47      TYPES.PROCESS,
     48      "We are only notified about process targets"
     49    );
     50    ok(
     51      targetFront == topLevelTarget
     52        ? targetFront.isTopLevel
     53        : !targetFront.isTopLevel,
     54      "isTopLevel property is correct"
     55    );
     56    targets.add(targetFront);
     57  };
     58  const onDestroyed = ({ targetFront }) => {
     59    if (!targets.has(targetFront)) {
     60      ok(
     61        false,
     62        "A target is declared destroyed via onDestroyed without being notified via onAvailable"
     63      );
     64    }
     65    is(
     66      targetFront.targetType,
     67      TYPES.PROCESS,
     68      "We are only notified about process targets"
     69    );
     70    ok(
     71      !targetFront.isTopLevel,
     72      "We are not notified about the top level target destruction"
     73    );
     74    targets.delete(targetFront);
     75  };
     76  await targetCommand.watchTargets({
     77    types: [TYPES.PROCESS],
     78    onAvailable,
     79    onDestroyed,
     80  });
     81  is(
     82    targets.size,
     83    originalProcessesCount,
     84    "retrieved the expected number of processes via watchTargets"
     85  );
     86  // Start from 1 in order to ignore the parent process target, which is considered as a frame rather than a process
     87  for (let i = 1; i < Services.ppmm.childCount; i++) {
     88    const process = Services.ppmm.getChildAt(i);
     89    const hasTargetWithSamePID = [...targets].find(
     90      processTarget => processTarget.targetForm.processID == process.osPid
     91    );
     92    ok(
     93      hasTargetWithSamePID,
     94      `Process with PID ${process.osPid} has been reported via onAvailable`
     95    );
     96  }
     97 
     98  info(
     99    "Check that onAvailable is called for processes created *after* the call to watchTargets"
    100  );
    101  const previousTargets = new Set(targets);
    102  const onProcessCreated = new Promise(resolve => {
    103    const onAvailable2 = ({ targetFront }) => {
    104      if (previousTargets.has(targetFront)) {
    105        return;
    106      }
    107      targetCommand.unwatchTargets({
    108        types: [TYPES.PROCESS],
    109        onAvailable: onAvailable2,
    110      });
    111      resolve(targetFront);
    112    };
    113    targetCommand.watchTargets({
    114      types: [TYPES.PROCESS],
    115      onAvailable: onAvailable2,
    116    });
    117  });
    118  const tab1 = await BrowserTestUtils.openNewForegroundTab({
    119    gBrowser,
    120    url: TEST_URL,
    121    forceNewProcess: true,
    122  });
    123  const createdTarget = await onProcessCreated;
    124 
    125  // For some reason, creating a new tab purges processes created from previous tests
    126  // so it is not reasonable to assert the side of `targets` as it may be lower than expected.
    127  ok(targets.has(createdTarget), "The new tab process is in the list");
    128 
    129  const processCountAfterTabOpen = targets.size;
    130 
    131  // Assert that onDestroyed is called for destroyed processes
    132  const onProcessDestroyed = new Promise(resolve => {
    133    const onAvailable3 = () => {};
    134    const onDestroyed3 = ({ targetFront }) => {
    135      resolve(targetFront);
    136      targetCommand.unwatchTargets({
    137        types: [TYPES.PROCESS],
    138        onAvailable: onAvailable3,
    139        onDestroyed: onDestroyed3,
    140      });
    141    };
    142    targetCommand.watchTargets({
    143      types: [TYPES.PROCESS],
    144      onAvailable: onAvailable3,
    145      onDestroyed: onDestroyed3,
    146    });
    147  });
    148 
    149  BrowserTestUtils.removeTab(tab1);
    150 
    151  const destroyedTarget = await onProcessDestroyed;
    152  is(
    153    targets.size,
    154    processCountAfterTabOpen - 1,
    155    "The closed tab's process has been reported as destroyed"
    156  );
    157  ok(
    158    !targets.has(destroyedTarget),
    159    "The destroyed target is no longer in the list"
    160  );
    161  is(
    162    destroyedTarget,
    163    createdTarget,
    164    "The destroyed target is the one that has been reported as created"
    165  );
    166 
    167  targetCommand.unwatchTargets({
    168    types: [TYPES.PROCESS],
    169    onAvailable,
    170    onDestroyed,
    171  });
    172 
    173  targetCommand.destroy();
    174 
    175  await commands.destroy();
    176 }
    177 
    178 async function testThrowingInOnAvailable() {
    179  info(
    180    "Test TargetCommand watchTargets function when an exception is thrown in onAvailable callback"
    181  );
    182 
    183  const commands = await CommandsFactory.forMainProcess();
    184  const targetCommand = commands.targetCommand;
    185  const { TYPES } = targetCommand;
    186 
    187  await targetCommand.startListening();
    188 
    189  // Note that ppmm also includes the parent process, which is considered as a frame rather than a process
    190  const originalProcessesCount = Services.ppmm.childCount - 1;
    191 
    192  info(
    193    "Check that onAvailable is called for processes already created *before* the call to watchTargets"
    194  );
    195  const targets = new Set();
    196  let thrown = false;
    197  const onAvailable = ({ targetFront }) => {
    198    if (!thrown) {
    199      thrown = true;
    200      throw new Error("Force an exception when processing the first target");
    201    }
    202    targets.add(targetFront);
    203  };
    204  await targetCommand.watchTargets({ types: [TYPES.PROCESS], onAvailable });
    205  is(
    206    targets.size,
    207    originalProcessesCount - 1,
    208    "retrieved the expected number of processes via onAvailable. All but the first one where we have thrown."
    209  );
    210 
    211  targetCommand.destroy();
    212 
    213  await commands.destroy();
    214 }