tor-browser

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

rc-helper.js (6082B)


      1 // A collection of helper functions that make use of the `remoteContextHelper`
      2 // to test BFCache support and behavior.
      3 
      4 // Call `prepareForBFCache()` before navigating away from the page. This simply
      5 // sets a variable in window.
      6 async function prepareForBFCache(remoteContextHelper) {
      7  await remoteContextHelper.executeScript(() => {
      8    window.beforeBFCache = true;
      9  });
     10 }
     11 
     12 // Call `getBeforeCache()` after navigating back to the page. This returns the
     13 // value in window.
     14 async function getBeforeBFCache(remoteContextHelper) {
     15  return await remoteContextHelper.executeScript(() => {
     16    return window.beforeBFCache;
     17  });
     18 }
     19 
     20 // If the value in window is set to true, this means that the page was reloaded,
     21 // i.e., the page was restored from BFCache.
     22 // Call `prepareForBFCache()` before navigating away to call this function.
     23 async function assertImplementsBFCacheOptional(remoteContextHelper) {
     24  var beforeBFCache = await getBeforeBFCache(remoteContextHelper);
     25  assert_implements_optional(beforeBFCache == true, 'BFCache not supported.');
     26 }
     27 
     28 // Subtracts set `b` from set `a` and returns the result.
     29 function setMinus(a, b) {
     30  const minus = new Set();
     31  a.forEach(e => {
     32    if (!b.has(e)) {
     33      minus.add(e);
     34    }
     35  });
     36  return minus;
     37 }
     38 
     39 // Return a sorted Array from the iterable `s`.
     40 function sorted(s) {
     41  return Array.from(s).sort();
     42 }
     43 
     44 // Assert expected reasons are all present. Note that the extra reasons are allowed
     45 // as UAs might block bfcache for their specific reasons.
     46 function matchReasons(expectedNotRestoredReasonsSet, notRestoredReasonsSet) {
     47  const missing = setMinus(
     48    expectedNotRestoredReasonsSet, notRestoredReasonsSet);
     49  const extra = setMinus(
     50      notRestoredReasonsSet, expectedNotRestoredReasonsSet);
     51  assert_true(missing.size == 0, `Expected: ${sorted(expectedNotRestoredReasonsSet)}\n` +
     52    `Got: ${sorted(notRestoredReasonsSet)}\n` +
     53    `Missing: ${sorted(missing)}\n` +
     54    `Extra: ${sorted(extra)}\n`);
     55 }
     56 
     57 // This function takes a set of reasons and extracts reasons out of it and returns a set of strings.
     58 // For example, if the input is [{"reason": "error-document"}, {"reason": "masked"}],
     59 // the output is ["error-document", "masked"].
     60 function extractReason(reasonSet) {
     61  let reasonsExtracted = new Set();
     62  for (let reason of reasonSet) {
     63    reasonsExtracted.add(reason.reason);
     64  }
     65  return reasonsExtracted;
     66 }
     67 
     68 // A helper function to assert that the page is not restored from BFCache by
     69 // checking whether the `beforeBFCache` value from `window` is undefined
     70 // due to page reload.
     71 // This function also takes an optional `notRestoredReasons` list which
     72 // indicates the set of expected reasons that make the page not restored.
     73 // If the reasons list is undefined, the check will be skipped. Otherwise
     74 // this check will use the `notRestoredReasons` API, to obtain the reasons
     75 // in a tree structure, and flatten the reasons before making the order-
     76 // insensitive comparison.
     77 // If the API is not available, the function will terminate instead of marking
     78 // the assertion failed.
     79 // Call `prepareForBFCache()` before navigating away to call this function.
     80 // `preconditionFailReasons` is a set of reasons that could be reported but
     81 // should PRECONDITION_FAIL if so. If `preconditionFailReasons` are reported,
     82 // this function will not check if `notRestoredReasons` are reported.
     83 async function assertNotRestoredFromBFCache(
     84    remoteContextHelper, notRestoredReasons, preconditionFailReasons = null) {
     85  var beforeBFCache = await getBeforeBFCache(remoteContextHelper);
     86  assert_equals(beforeBFCache, undefined, 'document unexpectedly BFCached');
     87 
     88  // The reason is optional, so skip the remaining test if the
     89  // `notRestoredReasons` is not set.
     90  if (notRestoredReasons === undefined) {
     91    return;
     92  }
     93 
     94  let isFeatureEnabled = await remoteContextHelper.executeScript(() => {
     95    return 'notRestoredReasons' in
     96        performance.getEntriesByType('navigation')[0];
     97  });
     98 
     99  // Return if the `notRestoredReasons` API is not available.
    100  if (!isFeatureEnabled) {
    101    return;
    102  }
    103 
    104  let result = await remoteContextHelper.executeScript(() => {
    105    return performance.getEntriesByType('navigation')[0].notRestoredReasons;
    106  });
    107 
    108  let expectedNotRestoredReasonsSet = new Set(notRestoredReasons);
    109  let notRestoredReasonsSet = new Set();
    110 
    111  // Flatten the reasons from the main frame and all the child frames.
    112  const collectReason = (node) => {
    113    // Fenced frames do not have a 'reasons' or 'children' property.
    114    if (node.reasons) {
    115      for (let reason of node.reasons) {
    116        notRestoredReasonsSet.add(reason.reason);
    117      }
    118    }
    119    if (node.children) {
    120      for (let child of node.children) {
    121        collectReason(child);
    122      }
    123    }
    124  };
    125  collectReason(result);
    126 
    127  // Check for preconditionFailReasons if set.
    128  if (preconditionFailReasons) {
    129    let preconditionFailReasonsSet = new Set(preconditionFailReasons);
    130    const missing = setMinus(
    131        preconditionFailReasonsSet, notRestoredReasonsSet);
    132    const extra = setMinus(
    133        notRestoredReasonsSet, preconditionFailReasonsSet);
    134    // preconditionFailReasons were reported. PRECONDION_FAIL here.
    135    assert_implements_optional(
    136        !(missing.size == 0 && extra.size == 0),
    137        'Precondition fail reasons are reported.');
    138  }
    139 
    140  matchReasons(expectedNotRestoredReasonsSet, notRestoredReasonsSet);
    141 }
    142 
    143 // A helper function that combines the steps of setting window property,
    144 // navigating away and back, and making assertion on whether BFCache is
    145 // supported.
    146 // This function can be used to check if the current page is eligible for
    147 // BFCache.
    148 async function assertBFCacheEligibility(
    149    remoteContextHelper, shouldRestoreFromBFCache) {
    150  await prepareForBFCache(remoteContextHelper);
    151  // Navigate away and back.
    152  const newRemoteContextHelper = await remoteContextHelper.navigateToNew();
    153  await newRemoteContextHelper.historyBack();
    154 
    155  if (shouldRestoreFromBFCache) {
    156    await assertImplementsBFCacheOptional(remoteContextHelper);
    157  } else {
    158    await assertNotRestoredFromBFCache(remoteContextHelper);
    159  }
    160 }