summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/html/browsers/browsing-the-web/back-forward-cache/resources/rc-helper.js
blob: ad2119b7385684dff539d80b5f3a37aefae10816 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// A collection of helper functions that make use of the `remoteContextHelper`
// to test BFCache support and behavior.

// Call `prepareForBFCache()` before navigating away from the page. This simply
// sets a variable in window.
async function prepareForBFCache(remoteContextHelper) {
  await remoteContextHelper.executeScript(() => {
    window.beforeBFCache = true;
  });
}

// Call `getBeforeCache()` after navigating back to the page. This returns the
// value in window.
async function getBeforeBFCache(remoteContextHelper) {
  return await remoteContextHelper.executeScript(() => {
    return window.beforeBFCache;
  });
}

// If the value in window is set to true, this means that the page was reloaded,
// i.e., the page was restored from BFCache.
// Call `prepareForBFCache()` before navigating away to call this function.
async function assertImplementsBFCacheOptional(remoteContextHelper) {
  var beforeBFCache = await getBeforeBFCache(remoteContextHelper);
  assert_implements_optional(beforeBFCache == true, 'BFCache not supported.');
}

// Subtracts set `b` from set `a` and returns the result.
function setMinus(a, b) {
  const minus = new Set();
  a.forEach(e => {
    if (!b.has(e)) {
      minus.add(e);
    }
  });
  return minus;
}

// Return a sorted Array from the iterable `s`.
function sorted(s) {
  return Array.from(s).sort();
}

// Assert expected reasons are all present. Note that the extra reasons are allowed
// as UAs might block bfcache for their specific reasons.
function matchReasons(expectedNotRestoredReasonsSet, notRestoredReasonsSet) {
  const missing = setMinus(
    expectedNotRestoredReasonsSet, notRestoredReasonsSet, 'Missing reasons');
  const extra = setMinus(
      notRestoredReasonsSet, expectedNotRestoredReasonsSet, 'Extra reasons');
  assert_true(missing.size == 0, `Expected: ${sorted(expectedNotRestoredReasonsSet)}\n` +
    `Got: ${sorted(notRestoredReasonsSet)}\n` +
    `Missing: ${sorted(missing)}\n` +
    `Extra: ${sorted(extra)}\n`);
}

// This function takes a set of reasons and extracts reasons out of it and returns a set of strings.
// For example, if the input is [{"reason": "error-document"}, {"reason": "masked"}],
// the output is ["error-document", "masked"].
function extractReason(reasonSet) {
  let reasonsExtracted = new Set();
  for (let reason of reasonSet) {
    reasonsExtracted.add(reason.reason);
  }
  return reasonsExtracted;
}

// A helper function to assert that the page is not restored from BFCache by
// checking whether the `beforeBFCache` value from `window` is undefined
// due to page reload.
// This function also takes an optional `notRestoredReasons` list which
// indicates the set of expected reasons that make the page not restored.
// If the reasons list is undefined, the check will be skipped. Otherwise
// this check will use the `notRestoredReasons` API, to obtain the reasons
// in a tree structure, and flatten the reasons before making the order-
// insensitive comparison.
// If the API is not available, the function will terminate instead of marking
// the assertion failed.
// Call `prepareForBFCache()` before navigating away to call this function.
async function assertNotRestoredFromBFCache(
    remoteContextHelper, notRestoredReasons) {
  var beforeBFCache = await getBeforeBFCache(remoteContextHelper);
  assert_equals(beforeBFCache, undefined, 'document unexpectedly BFCached');

  // The reason is optional, so skip the remaining test if the
  // `notRestoredReasons` is not set.
  if (notRestoredReasons === undefined) {
    return;
  }

  let isFeatureEnabled = await remoteContextHelper.executeScript(() => {
    return 'notRestoredReasons' in
        performance.getEntriesByType('navigation')[0];
  });

  // Return if the `notRestoredReasons` API is not available.
  if (!isFeatureEnabled) {
    return;
  }

  let result = await remoteContextHelper.executeScript(() => {
    return performance.getEntriesByType('navigation')[0].notRestoredReasons;
  });

  let expectedNotRestoredReasonsSet = new Set(notRestoredReasons);
  let notRestoredReasonsSet = new Set();

  // Flatten the reasons from the main frame and all the child frames.
  const collectReason = (node) => {
    for (let reason of node.reasons) {
      notRestoredReasonsSet.add(reason.reason);
    }
    for (let child of node.children) {
      collectReason(child);
    }
  };
  collectReason(result);
  matchReasons(expectedNotRestoredReasonsSet, notRestoredReasonsSet);
}

// A helper function that combines the steps of setting window property,
// navigating away and back, and making assertion on whether BFCache is
// supported.
// This function can be used to check if the current page is eligible for
// BFCache.
async function assertBFCacheEligibility(
    remoteContextHelper, shouldRestoreFromBFCache) {
  await prepareForBFCache(remoteContextHelper);
  // Navigate away and back.
  const newRemoteContextHelper = await remoteContextHelper.navigateToNew();
  await newRemoteContextHelper.historyBack();

  if (shouldRestoreFromBFCache) {
    await assertImplementsBFCacheOptional(remoteContextHelper);
  } else {
    await assertNotRestoredFromBFCache(remoteContextHelper);
  }
}