Skip to content

Commit

Permalink
[BFCache] Add basic event tests + helpers for BFCache WPT
Browse files Browse the repository at this point in the history
back-forward-cache/resources/helper.sub.js:
Helper for A->B->A navigation scenarios.
BFCache state is detected by observing `pageshow` events.
We might want to use more explicit APIs like
`isPreviousPageInBFCache` discussed at
#16359
in the future / in more complicated scenarios, but so far
`pageshow`-based detection seems to work without hurting ergonomics.

back-forward-cache/resources/events.html:
The file that loads `helper.sub.js` and
contains test logic and assertions.
We navigate to `back-forward-cache/resources/back.html` and then
back-navigate to `back-forward-cache/resources/events.html`.
In the case of BFCache is not used, two async_test objects are
created:
- The first one during the initial navigation,
  which never completes, and
- The second one during the back navigation, which will execute
  test assertions and then complete.
This doesn't affect the behavior (the first one seems just ignored),
but might look awkward.

back-forward-cache/events.html:
The main test Document that opens
`back-forward-cache/resources/events.html` using
`window.open()` with 'noopener' option.
This is because (at least) Chromium requires top-level navigations
to trigger BFCache and thus `back-forward-cache/resources/events.html`
is navigated away during the test, but the WPT test infrastructure
doesn't support navigating the main test Document.

testharness.js:
`fetch_tests_from_prefixed_local_storage()` is introduced
to communicate test results from
`back-forward-cache/resources/events.html`
in a similar way to `fetch_tests_from_window()`.

PrefixedLocalStorage.js:
Some basic utility methods for `helper.sub.js` are added.

Bug: 1107415
Change-Id: I034f9f5376dc3f9f32ca0b936dbd06e458c9160b
  • Loading branch information
hiroshige-g authored and chromium-wpt-export-bot committed May 24, 2021
1 parent 3d3e869 commit 467bf8a
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 0 deletions.
18 changes: 18 additions & 0 deletions common/PrefixedLocalStorage.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,24 @@ PrefixedLocalStorage.prototype.setItem = function (baseKey, value) {
localStorage.setItem(this.prefixedKey(baseKey), value);
};

PrefixedLocalStorage.prototype.getItem = function (baseKey) {
return localStorage.getItem(this.prefixedKey(baseKey));
};

PrefixedLocalStorage.prototype.pushItem = function (baseKey, value) {
const array = this.getPushedItems(baseKey);
array.push(value);
this.setItem(baseKey, JSON.stringify(array));
};

PrefixedLocalStorage.prototype.getPushedItems = function (baseKey) {
const value = this.getItem(baseKey);
if (!value) {
return [];
}
return JSON.parse(value);
};

/**
* Listen for `storage` events pertaining to a particular key,
* prefixed with this object's prefix. Ignore when value is being set to null
Expand Down
12 changes: 12 additions & 0 deletions html/browsers/browsing-the-web/back-forward-cache/events.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE HTML>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/PrefixedLocalStorage.js"></script>
<title>Events fired during BFCached back navigation (cross-site)</title>
<script>
const prefixedLocalStorage = new PrefixedLocalStorageTest();
fetch_tests_from_prefixed_local_storage(prefixedLocalStorage);
window.open(prefixedLocalStorage.url('resources/events.html'),
'_blank',
'noopener');
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<!DOCTYPE HTML>
<script>
window.onload = () => history.back();
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!DOCTYPE HTML>
<script src="/resources/testharness.js"></script>
<script src="/common/PrefixedLocalStorage.js"></script>
<script src="helper.sub.js"></script>
<script>
startRecordingEvents(['visibilitychange', 'pagehide', 'pageshow', 'load']);

const t = async_test('Events');
runTest(
t,
() => location.href = backUrl,
(isBFCached, observedEvents) => {
assert_implements_optional(isBFCached, 'Should be BFCached');
assert_array_equals(observedEvents, [
'window.load',
'window.pageshow',
'window.pagehide.persisted',
'document.visibilitychange.hidden',
'window.visibilitychange.hidden',
'document.visibilitychange.visible',
'window.visibilitychange.visible',
'window.pageshow.persisted']);
t.done();
}
);
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// A helper script for simple A->B->A navigation scenarios like:
// 1. Initial navigation to `A.html`.
// 2. Navigation to `B.html`.
// 3. Back navigation to `A.html`, assuming `A.html` is (or is not) in BFCache.

// This script is loaded from `A.html`.

// `A.html` should be opened using `PrefixedLocalStorage.url()`, because
// `/common/PrefixedLocalStorage.js` is used to save states across navigations.

window.prefixedLocalStorage = new PrefixedLocalStorageResource({
close_on_cleanup: true
});

// Starts an A->B->A navigation test. This should be called on `A.html`.
// `onStart()` is called on the initial navigation, which is expected to
// initiate a navigation to a page (`B.html`) that will eventually back
// navigate to `A.html`.
// `onBackNavigated(isBFCached, observedEvents)` is called on back navigation.
// - `isBFCached` indicates whether the back navigation is from BFCache or not,
// based on events fired.
// - `observedEvents` is an array of event labels fired on `A.html`,
// if `startRecordingEvents()` is called.
function runTest(test, onStart, onBackNavigated) {
window.addEventListener('load', () => {
if (prefixedLocalStorage.getItem('state') === null) {
// Initial navigation.
prefixedLocalStorage.setItem('state', 'started');

// Call `onStart()` (and thus starting navigation) after this document
// is fully loaded.
// `step_timeout()` is used here because starting the navigation
// synchronously inside the window load event handler seems to
// cause back navigation to this page to fail on Firefox.
test.step_timeout(() => {
window.addEventListener('pageshow', (() => {
// Back navigation, from BFCache.
test.step(
onBackNavigated,
undefined,
true,
prefixedLocalStorage.getPushedItems('observedEvents'));
}));
test.step(onStart);
}, 0);
} else {
// Back navigation, not from BFCache.
test.step(
onBackNavigated,
undefined,
false,
prefixedLocalStorage.getPushedItems('observedEvents'));
}
});
}

// Records events fired on `window` and `document`, with names listed in
// `eventNames`.
// The recorded events are stored in localStorage and used later in the
// runTest() callback.
function startRecordingEvents(eventNames) {
window.testObservedEvents = [];
for (const eventName of eventNames) {
window.addEventListener(eventName, event => {
let result = eventName;
if (event.persisted) {
result += '.persisted';
}
if (eventName === 'visibilitychange') {
result += '.' + document.visibilityState;
}
prefixedLocalStorage.pushItem('observedEvents', 'window.' + result);
});
document.addEventListener(eventName, () => {
let result = eventName;
if (eventName === 'visibilitychange') {
result += '.' + document.visibilityState;
}
prefixedLocalStorage.pushItem('observedEvents', 'document.' + result);
});
}
}

const origin =
'http://{{hosts[alt][www]}}:{{ports[http][0]}}'; // cross-site

const backUrl =
origin +
'/html/browsers/browsing-the-web/back-forward-cache/resources/back.html';
41 changes: 41 additions & 0 deletions resources/testharness.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@
w.postMessage(message_arg, "*");
}
});
if (window.prefixedLocalStorage) {
window.prefixedLocalStorage.setItem('dispatched_messages.' + Math.random(),
JSON.stringify(message_arg));
}
};

WindowTestEnvironment.prototype._forEach_windows = function(callback) {
Expand Down Expand Up @@ -3168,6 +3172,25 @@
);
};

/*
* Constructs a RemoteContext that tracks tests from prefixed local storage.
* Test results from a window where
* - `window.prefixedLocalStorage` is defined using `/common/PrefixedLocalStorage.js`
* like `window.prefixedLocalStorage = new PrefixedLocalStorageResource();` and
* - testharness.js is loaded
* are received via `prefixedLocalStorage`.
*/
Tests.prototype.create_remote_prefixed_local_storage = function(prefixedLocalStorage) {
const channel = new MessageChannel();
// This receives the random keys prefixed by 'dispatched_messages' sent by the remote
// window's `WindowTestEnvironment.prototype._dispatch`.
prefixedLocalStorage.onSet('dispatched_messages', e => {
channel.port1.postMessage(JSON.parse(e.newValue));
});
channel.port2.start();
return new RemoteContext(null, channel.port2);
};

Tests.prototype.fetch_tests_from_worker = function(worker) {
if (this.phase >= this.phases.COMPLETE) {
return;
Expand Down Expand Up @@ -3196,6 +3219,24 @@
}
expose(fetch_tests_from_window, 'fetch_tests_from_window');


Tests.prototype.fetch_tests_from_prefixed_local_storage = function(prefixedLocalStorage) {
if (this.phase >= this.phases.COMPLETE) {
return;
}

var remoteContext = this.create_remote_prefixed_local_storage(prefixedLocalStorage);
this.pending_remotes.push(remoteContext);
return remoteContext.done.then(() => {
prefixedLocalStorage.cleanup();
});
};

function fetch_tests_from_prefixed_local_storage(prefixedLocalStorage) {
return tests.fetch_tests_from_prefixed_local_storage(prefixedLocalStorage);
}
expose(fetch_tests_from_prefixed_local_storage, 'fetch_tests_from_prefixed_local_storage');

function timeout() {
if (tests.timeout_length === null) {
tests.timeout();
Expand Down

0 comments on commit 467bf8a

Please sign in to comment.