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 }