Skip to content

Commit

Permalink
[WPT] BFCache: pushState() in BFCache/non-BFCache cases
Browse files Browse the repository at this point in the history
Bug: 1107415, whatwg/html#6207
Change-Id: I609276fe865fa92409fd7a547777dba222bac36c
  • Loading branch information
hiroshige-g authored and chromium-wpt-export-bot committed Feb 9, 2022
1 parent e65aefe commit 902f30b
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 49 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<!DOCTYPE HTML>
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/utils.js"></script>
<script src="/common/dispatcher/dispatcher.js"></script>
<script src="resources/helper.sub.js"></script>
<script>
// See https://github.com/whatwg/html/issues/6207 for discussion on the
// specified, implemented and desired behaviors.
for (const bfcacheDisabled of [false, true]) {
const pushStateExecutorPath =
'/html/browsers/browsing-the-web/back-forward-cache/resources/executor-pushstate.html';

runBfcacheTest({
funcBeforeNavigation: async (bfcacheDisabled, pushStateExecutorPath) => {
const urlPushState = new URL(location.href);
urlPushState.pathname = pushStateExecutorPath;
if (bfcacheDisabled) {
await disableBFCache();
}

// `pushState(..., urlPushState)` on `urlA`,
history.pushState('blue', '', urlPushState.href);
},
argsBeforeNavigation: [bfcacheDisabled, pushStateExecutorPath],
shouldBeCached: !bfcacheDisabled,
funcAfterAssertion: async (pageA) => {
// We've navigatated to `urlB` and back again
// (already done within `runBfcacheTest()`).
// After the back navigation, `location` etc. should point to
// `urlPushState` and the state that's pushed.
const urlPushState = location.origin + pushStateExecutorPath +
'?uuid=' + pageA.context_id;
assert_equals(await pageA.execute_script(() => location.href),
urlPushState, 'url');
assert_equals(await pageA.execute_script(() => history.state),
'blue', 'history.state');

if (bfcacheDisabled) {
// When the page is not restored from BFCache, the HTML page is loaded
// from `urlPushState` (not from `urlA`).
assert_true(await pageA.execute_script(() => isLoadedFromPushState),
'document should be loaded from urlPushState');
}
}
}, 'back navigation to pushState()d page (' +
(bfcacheDisabled ? 'not ' : '') + 'in BFCache)');
}
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE HTML>
<script src="/common/dispatcher/dispatcher.js"></script>
<script src="event-recorder.js" type="module"></script>
<script src="worker-helper.js" type="module"></script>
<script type="module">
// This is mostly the same as `executor.html`, except for
// `isLoadedFromPushState` is set here, in order to detect whether the page
// was loaded from `executor.html` or `executor-pushstate.html`.
// Full executor functionality is still needed to handle remote script
// execution requests etc.
window.isLoadedFromPushState = true;
</script>
<script src="executor.js" type="module"></script>
Original file line number Diff line number Diff line change
Expand Up @@ -2,52 +2,4 @@
<script src="/common/dispatcher/dispatcher.js"></script>
<script src="event-recorder.js" type="module"></script>
<script src="worker-helper.js" type="module"></script>
<script type="module">
const params = new URLSearchParams(window.location.search);
const uuid = params.get('uuid');

// Executor and BFCache detection

// When navigating out from this page, always call
// `prepareNavigation(callback)` synchronously from the script injected by
// `RemoteContext.execute_script()`, and trigger navigation on or after the
// callback is called.
// prepareNavigation() suspends task polling and avoid in-flight fetch
// requests during navigation that might evict the page from BFCache.
//
// When we navigate to the page again, task polling is resumed, either
// - (BFCache cases) when the pageshow event listener added by
// prepareNavigation() is executed, or
// - (Non-BFCache cases) when `Executor.execute()` is called again during
// non-BFCache page loading.
//
// In such scenarios, `assert_bfcached()` etc. in `helper.sub.js` can determine
// whether the page is restored from BFCache or not, by observing
// - `isPageshowFired`: whether the pageshow event listener added by the
// prepareNavigation() before navigating out, and
// - `loadCount`: whether this inline script is evaluated again.
// - `isPageshowPersisted` is used to assert that `event.persisted` is true
// when restored from BFCache.

window.isPageshowFired = false;
window.isPageshowPersisted = null;
window.loadCount = parseInt(localStorage.getItem(uuid + '.loadCount') || '0') + 1;
localStorage.setItem(uuid + '.loadCount', loadCount);

window.pageShowPromise = new Promise(resolve =>
window.addEventListener('pageshow', resolve, {once: true}));

const executor = new Executor(uuid);

window.prepareNavigation = function(callback) {
window.addEventListener(
'pageshow',
(event) => {
window.isPageshowFired = true;
window.isPageshowPersisted = event.persisted;
executor.resume();
},
{once: true});
executor.suspend(callback);
}
</script>
<script src="executor.js" type="module"></script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
const params = new URLSearchParams(window.location.search);
const uuid = params.get('uuid');

// Executor and BFCache detection

// When navigating out from this page, always call
// `prepareNavigation(callback)` synchronously from the script injected by
// `RemoteContext.execute_script()`, and trigger navigation on or after the
// callback is called.
// prepareNavigation() suspends task polling and avoid in-flight fetch
// requests during navigation that might evict the page from BFCache.
//
// When we navigate to the page again, task polling is resumed, either
// - (BFCache cases) when the pageshow event listener added by
// prepareNavigation() is executed, or
// - (Non-BFCache cases) when `Executor.execute()` is called again during
// non-BFCache page loading.
//
// In such scenarios, `assert_bfcached()` etc. in `helper.sub.js` can determine
// whether the page is restored from BFCache or not, by observing
// - `isPageshowFired`: whether the pageshow event listener added by the
// prepareNavigation() before navigating out, and
// - `loadCount`: whether this inline script is evaluated again.
// - `isPageshowPersisted` is used to assert that `event.persisted` is true
// when restored from BFCache.

window.isPageshowFired = false;
window.isPageshowPersisted = null;
window.loadCount = parseInt(localStorage.getItem(uuid + '.loadCount') || '0') + 1;
localStorage.setItem(uuid + '.loadCount', loadCount);

window.pageShowPromise = new Promise(resolve =>
window.addEventListener('pageshow', resolve, {once: true}));

const executor = new Executor(uuid);

window.prepareNavigation = function(callback) {
window.addEventListener(
'pageshow',
(event) => {
window.isPageshowFired = true;
window.isPageshowPersisted = event.persisted;
executor.resume();
},
{once: true});
executor.suspend(callback);
}

// Try to disable BFCache by acquiring and never releasing a Web Lock.
// This requires HTTPS.
// Note: This is a workaround depending on non-specified WebLock+BFCache
// behavior. We might want to introduce a test-only BFCache-disabling API
// instead in the future.
window.disableBFCache = () => {
return new Promise(resolve => {
// Use page's UUID as a unique lock name.
navigator.locks.request(uuid, () => {
resolve();
return new Promise(() => {});
});
});
};

0 comments on commit 902f30b

Please sign in to comment.