tor-browser

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

browser_resources_network_events_cache.js (8136B)


      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 ResourceCommand API internal cache / ignoreExistingResources around NETWORK_EVENT
      7 
      8 const ResourceCommand = require("resource://devtools/shared/commands/resource/resource-command.js");
      9 
     10 const EXAMPLE_DOMAIN = "https://example.com/";
     11 const TEST_URI = `${URL_ROOT_SSL}network_document.html`;
     12 
     13 add_task(async function () {
     14  info("Test basic NETWORK_EVENT resources against ResourceCommand cache");
     15  await testNetworkEventResourcesWithExistingResources();
     16  await testNetworkEventResourcesWithoutExistingResources();
     17 });
     18 
     19 async function testNetworkEventResourcesWithExistingResources() {
     20  info(`Tests for network event resources with the existing resources`);
     21  await testNetworkEventResourcesWithCachedRequest({
     22    ignoreExistingResources: false,
     23    // 1 available event fired, for the existing resource in the cache.
     24    // 1 available event fired, when live request is created.
     25    expectedResourcesOnAvailable: {
     26      [`${EXAMPLE_DOMAIN}cached_post.html`]: {
     27        resourceType: ResourceCommand.TYPES.NETWORK_EVENT,
     28        method: "POST",
     29        isNavigationRequest: false,
     30      },
     31      [`${EXAMPLE_DOMAIN}live_get.html`]: {
     32        resourceType: ResourceCommand.TYPES.NETWORK_EVENT,
     33        method: "GET",
     34        isNavigationRequest: false,
     35      },
     36    },
     37    // 2 update events fired, when live request is updated.
     38    expectedResourcesOnUpdated: {
     39      [`${EXAMPLE_DOMAIN}live_get.html`]: {
     40        resourceType: ResourceCommand.TYPES.NETWORK_EVENT,
     41        method: "GET",
     42      },
     43    },
     44  });
     45 }
     46 
     47 async function testNetworkEventResourcesWithoutExistingResources() {
     48  info(`Tests for network event resources without the existing resources`);
     49  await testNetworkEventResourcesWithCachedRequest({
     50    ignoreExistingResources: true,
     51    // 1 available event fired, when live request is created.
     52    expectedResourcesOnAvailable: {
     53      [`${EXAMPLE_DOMAIN}live_get.html`]: {
     54        resourceType: ResourceCommand.TYPES.NETWORK_EVENT,
     55        method: "GET",
     56        isNavigationRequest: false,
     57      },
     58    },
     59    // 2 update events fired, when live request is updated.
     60    expectedResourcesOnUpdated: {
     61      [`${EXAMPLE_DOMAIN}live_get.html`]: {
     62        resourceType: ResourceCommand.TYPES.NETWORK_EVENT,
     63        method: "GET",
     64      },
     65    },
     66  });
     67 }
     68 
     69 /**
     70 * This test helper is slightly complex as we workaround the fact
     71 * that the server is not able to record network requests done in the past.
     72 * Because of that we have to start observer requests via ResourceCommand.watchResources
     73 * before doing a request, and before doing the actual call to watchResources
     74 * we want to assert the behavior of.
     75 */
     76 async function testNetworkEventResourcesWithCachedRequest(options) {
     77  const tab = await addTab(TEST_URI);
     78  const commands = await CommandsFactory.forTab(tab);
     79  await commands.targetCommand.startListening();
     80 
     81  const { resourceCommand } = commands;
     82 
     83  info(
     84    `Trigger some network requests *before* calling ResourceCommand.watchResources
     85     in order to assert the behavior of already existing network events.`
     86  );
     87 
     88  // Register a first empty listener in order to ensure populating ResourceCommand
     89  // internal cache of NETWORK_EVENT's. We can't retrieve past network requests
     90  // when calling the server's `watchResources`.
     91  let resolveCachedRequestAvailable;
     92  const onCachedRequestAvailable = new Promise(
     93    r => (resolveCachedRequestAvailable = r)
     94  );
     95  const onAvailableToPopulateInternalCache = () => {};
     96 
     97  // We get multiple updates, wait for all the updates
     98  const onUpdatedToPopulateInternalCache = resourceUpdates => {
     99    if (resourceUpdates[0].update.resourceUpdates.responseEndAvailable) {
    100      resolveCachedRequestAvailable();
    101    }
    102  };
    103 
    104  await resourceCommand.watchResources([resourceCommand.TYPES.NETWORK_EVENT], {
    105    ignoreExistingResources: true,
    106    onAvailable: onAvailableToPopulateInternalCache,
    107    onUpdated: onUpdatedToPopulateInternalCache,
    108  });
    109 
    110  // We can only trigger the requests once `watchResources` settles,
    111  // otherwise we might miss some events and they won't be present in the cache
    112  const cachedRequest = `await fetch("/cached_post.html", { method: "POST" });`;
    113  await triggerNetworkRequests(tab.linkedBrowser, [cachedRequest]);
    114 
    115  // We have to ensure that ResourceCommand processed the Resource for this first
    116  // cached request before calling watchResource a second time and report it.
    117  // Wait for the updated notification to avoid receiving it during the next call
    118  // to watchResources.
    119  await onCachedRequestAvailable;
    120 
    121  const actualResourcesOnAvailable = {};
    122  const actualResourcesOnUpdated = {};
    123 
    124  const {
    125    expectedResourcesOnAvailable,
    126    expectedResourcesOnUpdated,
    127 
    128    ignoreExistingResources,
    129  } = options;
    130 
    131  const onAvailable = resources => {
    132    for (const resource of resources) {
    133      is(
    134        resource.resourceType,
    135        resourceCommand.TYPES.NETWORK_EVENT,
    136        "Received a network event resource"
    137      );
    138      actualResourcesOnAvailable[resource.url] = resource;
    139    }
    140  };
    141 
    142  const onUpdated = updates => {
    143    for (const { resource } of updates) {
    144      is(
    145        resource.resourceType,
    146        resourceCommand.TYPES.NETWORK_EVENT,
    147        "Received a network update event resource"
    148      );
    149      actualResourcesOnUpdated[resource.url] = resource;
    150    }
    151  };
    152 
    153  await resourceCommand.watchResources([resourceCommand.TYPES.NETWORK_EVENT], {
    154    onAvailable,
    155    onUpdated,
    156    ignoreExistingResources,
    157  });
    158 
    159  info(
    160    `Trigger the rest of the requests *after* calling ResourceCommand.watchResources
    161     in order to assert the behavior of live network events.`
    162  );
    163  const liveRequest = `await fetch("/live_get.html", { method: "GET" });`;
    164  await triggerNetworkRequests(tab.linkedBrowser, [liveRequest]);
    165 
    166  info("Check the resources on available");
    167 
    168  await waitUntil(
    169    () =>
    170      Object.keys(actualResourcesOnAvailable).length ==
    171      Object.keys(expectedResourcesOnAvailable).length
    172  );
    173 
    174  is(
    175    Object.keys(actualResourcesOnAvailable).length,
    176    Object.keys(expectedResourcesOnAvailable).length,
    177    "Got the expected number of network events fired onAvailable"
    178  );
    179 
    180  // assert the resources emitted when the network event is created
    181  for (const key in expectedResourcesOnAvailable) {
    182    const expected = expectedResourcesOnAvailable[key];
    183    const actual = actualResourcesOnAvailable[key];
    184    assertResources(actual, expected);
    185  }
    186 
    187  info("Check the resources on updated");
    188  await waitUntil(
    189    () =>
    190      Object.keys(actualResourcesOnUpdated).length ==
    191      Object.keys(expectedResourcesOnUpdated).length
    192  );
    193 
    194  is(
    195    Object.keys(actualResourcesOnUpdated).length,
    196    Object.keys(expectedResourcesOnUpdated).length,
    197    "Got the expected number of network events fired onUpdated"
    198  );
    199 
    200  // assert the resources emitted when the network event is updated
    201  for (const key in expectedResourcesOnUpdated) {
    202    const expected = expectedResourcesOnUpdated[key];
    203    const actual = actualResourcesOnUpdated[key];
    204    assertResources(actual, expected);
    205    // assert that the resourceId for the the available and updated events match
    206    is(
    207      actual.resourceId,
    208      actualResourcesOnAvailable[key].resourceId,
    209      `Available and update resource ids for ${key} are the same`
    210    );
    211  }
    212 
    213  resourceCommand.unwatchResources([resourceCommand.TYPES.NETWORK_EVENT], {
    214    onAvailable,
    215    onUpdated,
    216    ignoreExistingResources,
    217  });
    218 
    219  resourceCommand.unwatchResources([resourceCommand.TYPES.NETWORK_EVENT], {
    220    onAvailable: onAvailableToPopulateInternalCache,
    221  });
    222 
    223  await commands.destroy();
    224 
    225  BrowserTestUtils.removeTab(tab);
    226 }
    227 
    228 function assertResources(actual, expected) {
    229  is(
    230    actual.resourceType,
    231    expected.resourceType,
    232    "The resource type is correct"
    233  );
    234  is(actual.method, expected.method, "The method is correct");
    235  if ("isNavigationRequest" in expected) {
    236    is(
    237      actual.isNavigationRequest,
    238      expected.isNavigationRequest,
    239      "The isNavigationRequest attribute is correct"
    240    );
    241  }
    242 }