Skip to content

Commit

Permalink
Bug 1733861 [wpt PR 31083] - [WPT] BFCache: pushState() in BFCache/no…
Browse files Browse the repository at this point in the history
…n-BFCache cases, a=testonly

Automatic update from web-platform-tests
[WPT] BFCache: pushState() in BFCache/non-BFCache cases

When doing history navigation to an entry that's created
via pushState, the page should be:

- With BFCache: Restored from BFCache.
- Without BFCache: Loaded from the URL set by `pushState()`.

While this test contradicts the current spec but matches
the desired behavior, and the spec will be
fixed as part of whatwg/html#6315.

Tests pass on Firefox/Safari/Chrome,
except for Safari on non-BFCache case
(just because WebLock doesn't disable BFCache).

Bug: 1107415, 1298336, whatwg/html#6207
Change-Id: I609276fe865fa92409fd7a547777dba222bac36c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3199857
Reviewed-by: Kouhei Ueno <kouhei@chromium.org>
Reviewed-by: Domenic Denicola <domenic@chromium.org>
Reviewed-by: Rakina Zata Amni <rakina@chromium.org>
Commit-Queue: Hiroshige Hayashizaki <hiroshige@chromium.org>
Cr-Commit-Position: refs/heads/main@{#972799}

--

wpt-commits: a400637a6ee3b7b6f315be2a17a7b28257663b4b
wpt-pr: 31083
  • Loading branch information
hiroshige-g authored and moz-wptsync-bot committed Mar 21, 2022
1 parent c7075e0 commit 0bd0091
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 49 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<!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>
// Tests what happens when doing history navigation to an entry that's created
// via pushState, with and without BFCache:
// 1. Navigate to `urlA`.
// 2. `pushState(urlPushState)`.
// 3. Navigate to `urlB`.
// 4. Do a back navigation.
// With BFCache, the page loaded at Step 1 is restored from BFCache,
// and is not reloaded from `urlPushState` nor `urlA`.
// Without BFCache, a page is loaded from `urlPushState`, not from `urlA`.
// In both cases, `location` and `history.state` are set to those set by
// `pushState()` in Step 2.
// See https://github.com/whatwg/html/issues/6207 for more discussion on the
// specified, implemented and desired behaviors. While this test contradicts
// the current spec but matches the desired behavior, and the spec will be
// fixed as part of https://github.com/whatwg/html/pull/6315.
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 navigated 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,64 @@
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, and doesn't work on Safari. We might want to introduce a
// test-only BFCache-disabling API instead in the future.
// https://github.com/web-platform-tests/wpt/issues/16359#issuecomment-795004780
// https://crbug.com/1298336
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 0bd0091

Please sign in to comment.