diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/resources/test-helper.js b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/resources/test-helper.js
index 95d516e275280..5f8e07711cbca 100644
--- a/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/resources/test-helper.js
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/remote-context-helper-tests/resources/test-helper.js
@@ -46,3 +46,40 @@ async function assertHeaderIsAsExpected(
return res.headers.get(headerName);
}, [headerName]), 'header is set');
}
+
+async function assertNotRestoredReasonsEquals(
+ remoteContextHelper, blocked, url, src, id, name, reasons, children) {
+ let result = await remoteContextHelper.executeScript(() => {
+ return performance.getEntriesByType('navigation')[0].notRestoredReasons;
+ });
+ assertReasonsStructEquals(result, blocked, url, src, id, name, reasons, children);
+}
+
+function assertReasonsStructEquals(result, blocked, url, src, id, name, reasons, children) {
+ assert_equals(result.blocked, blocked);
+ assert_equals(result.url, url);
+ assert_equals(result.src, src);
+ assert_equals(result.id, id);
+ assert_equals(result.name, name);
+ // Reasons should match.
+ assert_equals(result.reasons.length, reasons.length);
+ reasons.sort();
+ result.reasons.sort();
+ for (let i=0; i {
+ const rcHelper = new RemoteContextHelper();
+
+ // Open a window with noopener so that BFCache will work.
+ const rc1 = await rcHelper.addWindow(
+ /*config=*/ null, /*options=*/ {features: 'noopener'});
+
+ // Navigate away.
+ const rc2 = await rc1.navigateToNew();
+
+ // Navigate back.
+ await rc2.historyBack();
+
+ // Verify that no reasons are recorded for successful restore.
+ assert_true(await rc1.executeScript(() => {
+ let reasons = performance.getEntriesByType('navigation')[0].notRestoredReasons;
+ return reasons == null;
+ }));
+});
\ No newline at end of file
diff --git a/testing/web-platform/tests/performance-timeline/not-restored-reasons/performance-navigation-timing-cross-origin-bfcache.window.js b/testing/web-platform/tests/performance-timeline/not-restored-reasons/performance-navigation-timing-cross-origin-bfcache.window.js
new file mode 100644
index 0000000000000..f906b4b0735af
--- /dev/null
+++ b/testing/web-platform/tests/performance-timeline/not-restored-reasons/performance-navigation-timing-cross-origin-bfcache.window.js
@@ -0,0 +1,70 @@
+// META: title=RemoteContextHelper navigation using BFCache
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/resources/testharness.js
+// META: script=/resources/testharnessreport.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper-tests/resources/test-helper.js
+// META: script=/websockets/constants.sub.js
+
+'use strict';
+
+// Ensure that cross-origin subtree's reasons are not exposed to notRestoredReasons.
+promise_test(async t => {
+ const rcHelper = new RemoteContextHelper();
+ // Open a window with noopener so that BFCache will work.
+ const rc1 = await rcHelper.addWindow(
+ /*config=*/ null, /*options=*/ {features: 'noopener'});
+ const rc1_url = await rc1.executeScript(() => {
+ return location.href;
+ });
+ // Add a cross-origin iframe and use BroadcastChannel.
+ const rc1_child = await rc1.addIframe(
+ /*extraConfig=*/ {
+ origin: 'HTTP_REMOTE_ORIGIN',
+ scripts: [],
+ headers: [],
+ },
+ /*attributes=*/ {id: 'test-id'},
+ );
+
+ const domainPort = SCHEME_DOMAIN_PORT;
+ await rc1_child.executeScript((domain) => {
+ var ws = new WebSocket(domain + '/echo');
+ }, [domainPort]);
+
+ const rc1_child_url = await rc1_child.executeScript(() => {
+ return location.href;
+ });
+ // Add a child to the iframe.
+ const rc1_grand_child = await rc1_child.addIframe();
+ const rc1_grand_child_url = await rc1_grand_child.executeScript(() => {
+ return location.href;
+ });
+
+ // Navigate away.
+ const rc2 = await rc1.navigateToNew();
+
+ // Navigate back.
+ await rc2.historyBack();
+
+ // Check the reported reasons.
+ await assertNotRestoredReasonsEquals(
+ rc1,
+ /*blocked=*/false,
+ /*url=*/rc1_url,
+ /*src=*/ "",
+ /*id=*/"",
+ /*name=*/"",
+ /*reasons=*/[],
+ /*children=*/[{
+ "blocked": true,
+ "url": "",
+ "src": "",
+ "id": "",
+ "name": "",
+ "reasons": [],
+ "children": []
+ }]);
+});
\ No newline at end of file
diff --git a/testing/web-platform/tests/performance-timeline/not-restored-reasons/performance-navigation-timing-not-bfcached.window.js b/testing/web-platform/tests/performance-timeline/not-restored-reasons/performance-navigation-timing-not-bfcached.window.js
new file mode 100644
index 0000000000000..ac8af0e88c8a4
--- /dev/null
+++ b/testing/web-platform/tests/performance-timeline/not-restored-reasons/performance-navigation-timing-not-bfcached.window.js
@@ -0,0 +1,44 @@
+// META: title=RemoteContextHelper navigation using BFCache
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/resources/testharness.js
+// META: script=/resources/testharnessreport.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper-tests/resources/test-helper.js
+// META: script=/websockets/constants.sub.js
+
+'use strict';
+
+// Ensure that notRestoredReasons is populated when not restored.
+promise_test(async t => {
+ const rcHelper = new RemoteContextHelper();
+ // Open a window with noopener so that BFCache will work.
+ const rc1 = await rcHelper.addWindow(
+ /*config=*/ null, /*options=*/ {features: 'noopener'});
+
+ const domainPort = SCHEME_DOMAIN_PORT;
+ await rc1.executeScript((domain) => {
+ var ws = new WebSocket(domain + '/echo');
+ }, [domainPort]);
+
+ const rc1_url = await rc1.executeScript(() => {
+ return location.href;
+ });
+
+ // Navigate away.
+ const rc2 = await rc1.navigateToNew();
+
+ // Navigate back.
+ await rc2.historyBack();
+ // Check the reported reasons.
+ await assertNotRestoredReasonsEquals(
+ rc1,
+ /*blocked=*/true,
+ /*url=*/rc1_url,
+ /*src=*/ "",
+ /*id=*/"",
+ /*name=*/"",
+ /*reasons=*/["WebSocket"],
+ /*children=*/[]);
+});
\ No newline at end of file
diff --git a/testing/web-platform/tests/performance-timeline/not-restored-reasons/performance-navigation-timing-same-origin-bfcache.window.js b/testing/web-platform/tests/performance-timeline/not-restored-reasons/performance-navigation-timing-same-origin-bfcache.window.js
new file mode 100644
index 0000000000000..fb5ec50400e5a
--- /dev/null
+++ b/testing/web-platform/tests/performance-timeline/not-restored-reasons/performance-navigation-timing-same-origin-bfcache.window.js
@@ -0,0 +1,73 @@
+// META: title=RemoteContextHelper navigation using BFCache
+// META: script=/common/dispatcher/dispatcher.js
+// META: script=/common/get-host-info.sub.js
+// META: script=/common/utils.js
+// META: script=/resources/testharness.js
+// META: script=/resources/testharnessreport.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
+// META: script=/html/browsers/browsing-the-web/remote-context-helper-tests/resources/test-helper.js
+// META: script=/websockets/constants.sub.js
+
+'use strict';
+
+// Ensure that same-origin subtree's reasons are exposed to notRestoredReasons.
+promise_test(async t => {
+ const rcHelper = new RemoteContextHelper();
+ // Open a window with noopener so that BFCache will work.
+ const rc1 = await rcHelper.addWindow(
+ /*config=*/ null, /*options=*/ {features: 'noopener'});
+ const rc1_url = await rc1.executeScript(() => {
+ return location.href;
+ });
+ // Add a same-origin iframe and use WebSocket.
+ const rc1_child = await rc1.addIframe(/*extra_config=*/{}, /*attributes=*/ {id: 'test-id'});
+
+ const domainPort = SCHEME_DOMAIN_PORT;
+ await rc1_child.executeScript((domain) => {
+ var ws = new WebSocket(domain + '/echo');
+ }, [domainPort]);
+
+ const rc1_child_url = await rc1_child.executeScript(() => {
+ return location.href;
+ });
+ // Add a child to the iframe.
+ const rc1_grand_child = await rc1_child.addIframe();
+ const rc1_grand_child_url = await rc1_grand_child.executeScript(() => {
+ return location.href;
+ });
+
+ // Navigate away.
+ const rc2 = await rc1.navigateToNew();
+
+ // Navigate back.
+ await rc2.historyBack();
+
+ // Check the reported reasons.
+ await assertNotRestoredReasonsEquals(
+ rc1,
+ /*blocked=*/false,
+ /*url=*/rc1_url,
+ /*src=*/ "",
+ /*id=*/"",
+ /*name=*/"",
+ /*reasons=*/[],
+ /*children=*/[{
+ "blocked": true,
+ "url": rc1_child_url,
+ "src": rc1_child_url,
+ "id": "test-id",
+ "name": "",
+ "reasons": ["WebSocket"],
+ "children": [
+ {
+ "blocked": false,
+ "url": rc1_grand_child_url,
+ "src": rc1_grand_child_url,
+ "id": "",
+ "name": "",
+ "reasons": [],
+ "children": []
+ }
+ ]
+ }]);
+});
\ No newline at end of file