Skip to content

Commit

Permalink
Bug 1862325 [wpt PR 42873] - NavigationActivation prototype, a=testonly
Browse files Browse the repository at this point in the history
Automatic update from web-platform-tests
NavigationActivation prototype (#42873)

NavigationActivation is a new object (exposed as
`navigation.activation`). It is updated when a new Document is
"activated": initial Document creation, restore from bfcache, or
(in a future CL) prerender activation. It has three pieces of state:
* `activation.entry`: The current NavigationHistoryEntry at the time
  of activation.
* `activation.from`: The current NavigationHistoryEntry immediately
  before activation (i.e., the entry we came from). This will be
  null if the navigation was cross-origin, or if the previous entry
  was not in the same-origin contiguous region of the back/forward
  list that is available in `navigation.entries()`.
* `activation.navigationType`: The navigationType ('push', 'replace',
  'reload', or 'traverse') of the navigation that activated the
  current Document.

Note that `entry` or `from` may be a NavigationHistoryEntry that is
no longer present in `navigation.entries()` (e.g., when doing a
replace navigation, `activation.from` will be the replaced entry).
In that case, the entry will remain visible, but because it is
a "disposed" entry, its index will be -1 and attempting to
`navigation.traverseTo()` its key will be rejected.

Initial discussion: whatwg/html#9760
Draft spec PR: whatwg/html#9856
I2p: https://groups.google.com/a/chromium.org/g/blink-dev/c/EfqxeH3Iwh4

Bug: 1492932
Change-Id: I2e090a8366906e4cb6778893ad9ffdca18d6ec37
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4990011
Reviewed-by: Domenic Denicola <domenic@chromium.org>
Commit-Queue: Nate Chapin <japhet@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1220983}

Co-authored-by: Nate Chapin <japhet@chromium.org>
--

wpt-commits: c2ca6ebdc7f92fbaf7308c91dda05bcaf24ef455
wpt-pr: 42873
  • Loading branch information
chromium-wpt-export-bot authored and moz-wptsync-bot committed Nov 22, 2023
1 parent 8c106fb commit 32c76c8
Show file tree
Hide file tree
Showing 17 changed files with 415 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<!doctype html>
<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="/html/browsers/browsing-the-web/back-forward-cache/resources/helper.sub.js"></script>
<script src="resources/is_uuid.js"></script>

<script>
// This test ensures that navigation.activation is properly updated when a page
// is restored from bfcache.
runBfcacheTest({
targetOrigin: originSameSite,
async funcAfterAssertion(pageA, pageB) {
const activationData = await pageA.execute_script(() => {
return { entryURL : navigation.activation.entry.url,
fromIsNull : navigation.activation.from === null,
navigationType : navigation.activation.navigationType }
});

// activation.entry should be the currently activated page, and
// activation.from should be omitted because it is a different origin.
assert_equals(activationData.entryURL, pageA.url);
assert_true(activationData.fromIsNull);
assert_equals(activationData.navigationType, "traverse");
}
}, "navigation.activation must be updated when restored from bfcache");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<!doctype html>
<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="/html/browsers/browsing-the-web/back-forward-cache/resources/helper.sub.js"></script>
<script src="resources/is_uuid.js"></script>

<script>
// This test ensures that navigation.activation is properly updated when a page
// is restored from bfcache.
runBfcacheTest({
targetOrigin: originSameOrigin,
async funcAfterAssertion(pageA, pageB) {
const activationData = await pageA.execute_script(() => {
return { entryURL : navigation.activation.entry.url,
fromURL : navigation.activation.from.url,
navigationType : navigation.activation.navigationType }
});

// activation.entry should be the currently activated page, and
// activation.from should be the page before restored from bfcache.
assert_equals(activationData.entryURL, pageA.url);
assert_equals(activationData.fromURL, pageB.url);
assert_equals(activationData.navigationType, "traverse");

const entryAndCurrentMatch = await pageA.execute_script(() => navigation.activation.entry === navigation.currentEntry);
assert_true(entryAndCurrentMatch);

const fromAndEntriesArrayMatch = await pageA.execute_script(() => navigation.activation.from === navigation.entries()[navigation.currentEntry.index + 1]);
assert_true(fromAndEntriesArrayMatch);
}
}, "navigation.activation must be updated when restored from bfcache");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
promise_test(async t => {
// Wait for after the load event so that the navigation doesn't get converted
// into a replace navigation.
await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0));

assert_equals(navigation.activation.entry, navigation.currentEntry);
let activationEntry = navigation.activation.entry;
let entryIndexBeforePush = activationEntry.index;
history.pushState("#fragment", "", "");
assert_not_equals(navigation.activation.entry, navigation.currentEntry);
assert_equals(navigation.activation.entry, activationEntry);
assert_equals(navigation.activation.entry.index, entryIndexBeforePush);
}, "navigation.activation.entry should not change due to history.pushState()");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
promise_test(async t => {
// Wait for after the load event so that the navigation doesn't get converted
// into a replace navigation.
await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0));

assert_equals(navigation.activation.entry, navigation.currentEntry);
let activationEntry = navigation.activation.entry;
history.replaceState("#fragment", "", "");
assert_not_equals(navigation.activation.entry, navigation.currentEntry);
assert_equals(navigation.activation.entry, activationEntry);
assert_equals(navigation.activation.entry.index, -1);
}, "navigation.activation.entry should be orphaned by history.replaceState()");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<iframe id="i"></iframe>
<script>
promise_test(async t => {
// Wait for after the load event so that the navigation doesn't get converted
// into a replace navigation.
await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0));

assert_equals(i.contentWindow.navigation.activation, null);

i.contentWindow.navigation.navigate("/common/blank.html?a");
await new Promise(resolve => i.onload = () => t.step_timeout(resolve, 0));

assert_equals(i.contentWindow.navigation.entries().length, 1);
assert_equals(i.contentWindow.navigation.activation.entry,
i.contentWindow.navigation.currentEntry);
assert_equals(i.contentWindow.navigation.activation.from.url, "about:blank");
assert_equals(i.contentWindow.navigation.activation.from.index, -1);
assert_equals(i.contentWindow.navigation.activation.navigationType, "replace");
}, "navigation.activation interaction with initial about:blank");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script>
promise_test(async t => {
// Wait for after the load event so that the navigation doesn't get converted
// into a replace navigation.
await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0));

let i = document.createElement("iframe");
i.src = get_host_info().HTTP_ORIGIN_WITH_DIFFERENT_PORT + "/common/blank.html";
document.body.appendChild(i);
await new Promise(resolve => i.onload = () => t.step_timeout(resolve, 0));

i.contentWindow.location = "/common/blank.html";
await new Promise(resolve => i.onload = () => t.step_timeout(resolve, 0));
let current_entry_after_nav = i.contentWindow.navigation.currentEntry;

assert_equals(i.contentWindow.navigation.entries().length, 1);
assert_equals(i.contentWindow.navigation.activation.entry, current_entry_after_nav);
assert_equals(i.contentWindow.navigation.activation.from, null);
assert_equals(i.contentWindow.navigation.activation.navigationType, "push");
}, "navigation.activation after push cross-origin");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<iframe id="i" src="/common/blank.html"></iframe>
<script>
promise_test(async t => {
// Wait for after the load event so that the navigation doesn't get converted
// into a replace navigation.
await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0));

i.contentWindow.navigation.navigate("/common/blank.html?a");
await new Promise(resolve => i.onload = () => t.step_timeout(resolve, 0));
let current_entry_after_nav = i.contentWindow.navigation.currentEntry;

assert_equals(i.contentWindow.navigation.entries().length, 2);
assert_equals(i.contentWindow.navigation.activation.entry, current_entry_after_nav);
assert_equals(i.contentWindow.navigation.activation.from,
i.contentWindow.navigation.entries()[0]);
assert_equals(i.contentWindow.navigation.activation.navigationType, "push");

// Same-document navigation doesn't change navigation.activation.
await i.contentWindow.navigation.navigate("/common/blank.html?a#fragment").finished;

assert_equals(i.contentWindow.navigation.entries().length, 3);
assert_equals(i.contentWindow.navigation.activation.entry, current_entry_after_nav);
assert_equals(i.contentWindow.navigation.activation.from,
i.contentWindow.navigation.entries()[0]);
assert_equals(i.contentWindow.navigation.activation.navigationType, "push");
}, "navigation.activation after push");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<iframe id="i" src="/common/blank.html"></iframe>
<script>
promise_test(async t => {
// Wait for after the load event so that the navigation doesn't get converted
// into a replace navigation.
await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0));

i.contentWindow.navigation.reload();
await new Promise(resolve => i.onload = () => t.step_timeout(resolve, 0));

// activation.entry and activation.from are equal after reload
assert_equals(i.contentWindow.navigation.entries().length, 1);
assert_equals(i.contentWindow.navigation.activation.entry.index, 0);
assert_equals(i.contentWindow.navigation.activation.entry,
i.contentWindow.navigation.activation.from);
assert_equals(i.contentWindow.navigation.activation.navigationType, "reload");
}, "navigation.activation after reload");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script>
promise_test(async t => {
// Wait for after the load event so that the navigation doesn't get converted
// into a replace navigation.
await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0));

let i = document.createElement("iframe");
i.src = get_host_info().HTTP_ORIGIN_WITH_DIFFERENT_PORT + "/common/blank.html";
document.body.appendChild(i);
await new Promise(resolve => i.onload = () => t.step_timeout(resolve, 0));

i.contentWindow.location.replace("/common/blank.html");
await new Promise(resolve => i.onload = () => t.step_timeout(resolve, 0));
let current_entry_after_nav = i.contentWindow.navigation.currentEntry;

assert_equals(i.contentWindow.navigation.entries().length, 1);
assert_equals(i.contentWindow.navigation.activation.entry, current_entry_after_nav);
assert_equals(i.contentWindow.navigation.activation.from, null);
assert_equals(i.contentWindow.navigation.activation.navigationType, "replace");
}, "navigation.activation after replace cross-origin");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<iframe id="i" src="/common/blank.html"></iframe>
<script>
promise_test(async t => {
// Wait for after the load event so that the navigation doesn't get converted
// into a replace navigation.
await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0));

let before_key = i.contentWindow.navigation.currentEntry.key;
let before_id = i.contentWindow.navigation.currentEntry.id;
let before_url = i.contentWindow.navigation.currentEntry.url;
i.contentWindow.navigation.navigate("/common/blank.html?a", { history: "replace" });
await new Promise(resolve => i.onload = () => t.step_timeout(resolve, 0));
let current_entry_after_nav = i.contentWindow.navigation.currentEntry;
let from_entry_after_nav = i.contentWindow.navigation.activation.from;

// activation.entry is the current entry. activation.from is a new
// NavigationHistoryEntry object (because there is a new window), with the
// same key/id/url as before the navigation, and an index of -1 because it is
// not present in the entries array.
assert_equals(i.contentWindow.navigation.entries().length, 1);
assert_equals(i.contentWindow.navigation.activation.entry, current_entry_after_nav);
assert_equals(i.contentWindow.navigation.activation.entry.index, 0);
assert_equals(from_entry_after_nav.key, before_key);
assert_equals(from_entry_after_nav.id, before_id);
assert_equals(from_entry_after_nav.url, before_url);
assert_equals(from_entry_after_nav.index, -1);
assert_equals(i.contentWindow.navigation.activation.navigationType, "replace");

await i.contentWindow.navigation.navigate("/common/blank.html?a#fragment", { history: "replace" }).finished;

// activation.entry is no longer navigation.currentEntry and is disposed.
// activation.from has not changed.
assert_equals(i.contentWindow.navigation.entries().length, 1);
assert_equals(i.contentWindow.navigation.activation.entry, current_entry_after_nav);
assert_not_equals(i.contentWindow.navigation.activation.entry,
i.contentWindow.navigation.currentEntry);
assert_equals(i.contentWindow.navigation.activation.entry.index, -1);
assert_equals(i.contentWindow.navigation.activation.from, from_entry_after_nav);
assert_equals(i.contentWindow.navigation.activation.navigationType, "replace");
}, "navigation.activation after replace");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<iframe id="i" src="/common/blank.html"></iframe>
<script>
promise_test(async t => {
// Wait for after the load event so that the navigation doesn't get converted
// into a replace navigation.
await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0));
await i.contentWindow.navigation.navigate("/common/blank.html#fragment").finished;
assert_equals(i.contentWindow.navigation.entries().length, 2);

let urlAfterSameDoc = i.contentWindow.location.href;

i.contentWindow.navigation.navigate("/common/blank.html?a");
await new Promise(resolve => i.onload = () => t.step_timeout(resolve, 0));
assert_equals(i.contentWindow.navigation.entries().length, 3);

assert_equals(i.contentWindow.navigation.activation.from,
i.contentWindow.navigation.entries()[1]);
assert_equals(i.contentWindow.navigation.activation.from.url, urlAfterSameDoc);
assert_equals(i.contentWindow.navigation.activation.navigationType, "push");
}, "navigation.activation same-document then cross-document");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<iframe id="i" src="/common/blank.html"></iframe>
<script src="/common/get-host-info.sub.js"></script>
<script>
promise_test(async t => {
// Wait for after the load event so that the navigation doesn't get converted
// into a replace navigation.
await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0));

i.contentWindow.location = get_host_info().HTTP_ORIGIN_WITH_DIFFERENT_PORT + "/common/blank.html";
await new Promise(resolve => i.onload = () => t.step_timeout(resolve, 0));

i.contentWindow.location = get_host_info().HTTP_ORIGIN + "/common/blank.html";
await new Promise(resolve => i.onload = () => t.step_timeout(resolve, 0));

// Go back to the first entry in the iframe. Because the navigation API only
// exposes same-origin *contiguous* entries, and there's a cross-origin entry
// in between, we have to use history.go().
history.go(-2);
await new Promise(resolve => i.onload = () => t.step_timeout(resolve, 0));
assert_equals(i.contentWindow.navigation.entries().length, 1);

// activation.from will be null because the entry that triggered the traversal
// is in a different same-origin contiguous region.
assert_equals(i.contentWindow.navigation.activation.entry, i.contentWindow.navigation.currentEntry);
assert_equals(i.contentWindow.navigation.activation.entry.index, 0);
assert_equals(i.contentWindow.navigation.activation.from, null);
assert_equals(i.contentWindow.navigation.activation.navigationType, "traverse");
}, "navigation.activation - traverse from a non-contiguous same-origin url");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<iframe id="i" src="/common/blank.html"></iframe>
<script>
promise_test(async t => {
// Wait for after the load event so that the navigation doesn't get converted
// into a replace navigation.
await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0));

i.contentWindow.navigation.navigate("/common/blank.html?a");
await new Promise(resolve => i.onload = () => t.step_timeout(resolve, 0));

assert_equals(i.contentWindow.navigation.entries().length, 2);

i.contentWindow.navigation.back();
await new Promise(resolve => i.onload = () => t.step_timeout(resolve, 0));

// activation.entry is the current entry. activation.from is the entry the
// traverse came from.
assert_equals(i.contentWindow.navigation.entries().length, 2);
assert_equals(i.contentWindow.navigation.activation.entry, i.contentWindow.navigation.currentEntry);
assert_equals(i.contentWindow.navigation.activation.entry.index, 0);
assert_equals(i.contentWindow.navigation.activation.from,
i.contentWindow.navigation.entries()[1]);
assert_equals(i.contentWindow.navigation.activation.navigationType, "traverse");

let from_key_before_push = i.contentWindow.navigation.activation.from.key;
let from_id_before_push = i.contentWindow.navigation.activation.from.id;
let from_url_before_push = i.contentWindow.navigation.activation.from.url;
await i.contentWindow.navigation.navigate("/common/blank.html#fragment").finished;

// pushing same document will not change activation.entry, but it will cut
// activation.from out of the entries array. Its parameters should not change
//except for its index, which becomes -1.
assert_equals(i.contentWindow.navigation.entries().length, 2);
assert_equals(i.contentWindow.navigation.activation.entry, i.contentWindow.navigation.entries()[0]);
assert_equals(i.contentWindow.navigation.activation.entry.index, 0);
assert_not_equals(i.contentWindow.navigation.activation.from,
i.contentWindow.navigation.entries()[1]);
assert_equals(i.contentWindow.navigation.activation.from.key, from_key_before_push);
assert_equals(i.contentWindow.navigation.activation.from.id, from_id_before_push);
assert_equals(i.contentWindow.navigation.activation.from.url, from_url_before_push);
assert_equals(i.contentWindow.navigation.activation.from.index, -1);
assert_equals(i.contentWindow.navigation.activation.navigationType, "traverse");
}, "navigation.activation - traverse, then push same-document");
</script>
Loading

0 comments on commit 32c76c8

Please sign in to comment.