Update navigation.entries() and navigation.activation on prerender activation
We currently have plumbing for updating entries() on bfcache restore.
This reuses that plumbing for prerender activation.
This also ensures that we set `navigation.activation.from` correctly
in a prerendered document before activation (it should be the entry
that initiated the preload).
Bug: 1212819
Change-Id: I5835313b0d713e10802b3df442bb16e4d813f464
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5013956
Reviewed-by: Rakina Zata Amni <[email protected]>
Reviewed-by: Nasko Oskov <[email protected]>
Reviewed-by: Domenic Denicola <[email protected]>
Commit-Queue: Nate Chapin <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1226462}
diff --git a/content/browser/renderer_host/navigation_controller_impl.cc b/content/browser/renderer_host/navigation_controller_impl.cc
index eb0a0559..3e96978 100644
--- a/content/browser/renderer_host/navigation_controller_impl.cc
+++ b/content/browser/renderer_host/navigation_controller_impl.cc
@@ -62,6 +62,7 @@
#include "content/browser/browser_url_handler_impl.h"
#include "content/browser/dom_storage/dom_storage_context_wrapper.h"
#include "content/browser/dom_storage/session_storage_namespace_impl.h"
+#include "content/browser/preloading/prerender/prerender_host.h"
#include "content/browser/process_lock.h"
#include "content/browser/renderer_host/back_forward_cache_impl.h"
#include "content/browser/renderer_host/debug_urls.h"
@@ -4696,23 +4697,36 @@
// If the previous entry is within the block of contiguous entries being
// provided, then report it as the `previous_entry`.
- if (GetLastCommittedEntryIndex() != -1 &&
- GetLastCommittedEntryIndex() >= backmost_index &&
- GetLastCommittedEntryIndex() <= forwardmost_index) {
- if (auto* frame_entry = GetLastCommittedEntry()->GetFrameEntry(node)) {
- url::Origin frame_entry_origin =
- frame_entry->committed_origin().value_or(url::Origin::Resolve(
- frame_entry->url(),
- frame_entry->initiator_origin().value_or(url::Origin())));
- // TODO(crbug.com/1209092): Move this into ToNavigationApiHistoryEntry()
- // once we can be sure that entries with the same ISN will never be
- // cross-origin.
- if (pending_origin.IsSameOriginWith(frame_entry_origin)) {
- entry_arrays->previous_entry = ToNavigationApiHistoryEntry(
- frame_entry, pending_document_sequence_number);
- }
+ FrameNavigationEntry* previous_entry = nullptr;
+ if (frame_tree_->is_prerendering()) {
+ int initiator_id = PrerenderHost::GetFromFrameTreeNode(*node)
+ .initiator_frame_tree_node_id();
+ if (initiator_id != RenderFrameHost::kNoFrameTreeNodeId) {
+ auto* initiator_node = FrameTreeNode::GloballyFindByID(initiator_id);
+ previous_entry = initiator_node->frame_tree()
+ .controller()
+ .GetLastCommittedEntry()
+ ->GetFrameEntry(initiator_node);
+ }
+ } else if (GetLastCommittedEntryIndex() != -1 &&
+ GetLastCommittedEntryIndex() >= backmost_index &&
+ GetLastCommittedEntryIndex() <= forwardmost_index) {
+ previous_entry = GetLastCommittedEntry()->GetFrameEntry(node);
+ }
+ if (previous_entry) {
+ url::Origin previous_entry_origin =
+ previous_entry->committed_origin().value_or(url::Origin::Resolve(
+ previous_entry->url(),
+ previous_entry->initiator_origin().value_or(url::Origin())));
+ // TODO(crbug.com/1209092): Move this into ToNavigationApiHistoryEntry()
+ // once we can be sure that entries with the same ISN will never be
+ // cross-origin.
+ if (pending_origin.IsSameOriginWith(previous_entry_origin)) {
+ entry_arrays->previous_entry = ToNavigationApiHistoryEntry(
+ previous_entry, pending_document_sequence_number);
}
}
+
return entry_arrays;
}
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index 84ffb00..a50bb963 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -5930,7 +5930,8 @@
: nullptr);
rfh->GetAssociatedLocalFrame()
->SetNavigationApiHistoryEntriesForRestore(
- std::move(entry_arrays));
+ std::move(entry_arrays),
+ blink::mojom::NavigationApiEntryRestoreReason::kBFCache);
return RenderFrameHost::FrameIterationAction::kContinue;
});
@@ -6022,6 +6023,33 @@
stored_page->SetViewTransitionState(
std::exchange(commit_params_->view_transition_state, {}));
+ // Update navigation API entries. A prerendered page has only a single
+ // history entry, but now it has access to a full back/forward list.
+ rfh->ForEachRenderFrameHostWithAction([this, &stored_page](
+ RenderFrameHostImpl* rfh) {
+ // Currently, prerender activation only happens for DIFFERENT_DOCUMENT
+ // navigations. If that ever changes, `reason` calculation will need to be
+ // updated (and new NavigationApiEntryRestoreReason values added).
+ DCHECK_EQ(common_params_->navigation_type,
+ blink::mojom::NavigationType::DIFFERENT_DOCUMENT);
+ blink::mojom::NavigationApiEntryRestoreReason reason =
+ common_params_->should_replace_current_entry
+ ? blink::mojom::NavigationApiEntryRestoreReason::
+ kPrerenderActivationReplace
+ : blink::mojom::NavigationApiEntryRestoreReason::
+ kPrerenderActivationPush;
+ // |this| is given as a parameter to
+ // GetNavigationApiHistoryEntryVectors() only for the frame being
+ // committed (i.e., the top frame).
+ auto entry_arrays =
+ rfh->frame_tree()->controller().GetNavigationApiHistoryEntryVectors(
+ rfh->frame_tree_node(),
+ stored_page->render_frame_host() == rfh ? this : nullptr);
+ rfh->GetAssociatedLocalFrame()->SetNavigationApiHistoryEntriesForRestore(
+ std::move(entry_arrays), reason);
+ return RenderFrameHost::FrameIterationAction::kContinue;
+ });
+
// Move the StoredPage into RenderFrameHostManager, in
// preparation for committing. This entry may be used for prerendering.
frame_tree_node_->render_manager()->ActivatePrerender(
diff --git a/content/public/test/fake_local_frame.cc b/content/public/test/fake_local_frame.cc
index ad5be22..f0ee1ae1 100644
--- a/content/public/test/fake_local_frame.cc
+++ b/content/public/test/fake_local_frame.cc
@@ -186,7 +186,8 @@
base::OnceCallback<void(blink::mojom::OpenGraphMetadataPtr)>) {}
void FakeLocalFrame::SetNavigationApiHistoryEntriesForRestore(
- blink::mojom::NavigationApiHistoryEntryArraysPtr entry_arrays) {}
+ blink::mojom::NavigationApiHistoryEntryArraysPtr entry_arrays,
+ blink::mojom::NavigationApiEntryRestoreReason restore_reason) {}
void FakeLocalFrame::NotifyNavigationApiOfDisposedEntries(
const std::vector<std::string>& keys) {}
diff --git a/content/public/test/fake_local_frame.h b/content/public/test/fake_local_frame.h
index 6cc4e78..c00d9ffa 100644
--- a/content/public/test/fake_local_frame.h
+++ b/content/public/test/fake_local_frame.h
@@ -141,7 +141,8 @@
void GetOpenGraphMetadata(
base::OnceCallback<void(blink::mojom::OpenGraphMetadataPtr)>) override;
void SetNavigationApiHistoryEntriesForRestore(
- blink::mojom::NavigationApiHistoryEntryArraysPtr entry_arrays) override;
+ blink::mojom::NavigationApiHistoryEntryArraysPtr entry_arrays,
+ blink::mojom::NavigationApiEntryRestoreReason restore_reason) override;
void NotifyNavigationApiOfDisposedEntries(
const std::vector<std::string>& keys) override;
void TraverseCancelled(const std::string& navigation_api_key,
diff --git a/third_party/blink/public/mojom/frame/frame.mojom b/third_party/blink/public/mojom/frame/frame.mojom
index 0c95a61e..9dde108 100644
--- a/third_party/blink/public/mojom/frame/frame.mojom
+++ b/third_party/blink/public/mojom/frame/frame.mojom
@@ -177,6 +177,12 @@
kFencedframe
};
+enum NavigationApiEntryRestoreReason {
+ kBFCache,
+ kPrerenderActivationPush,
+ kPrerenderActivationReplace
+};
+
// The maximum number of characters of the document's title that we're willing
// to accept in the browser process.
const uint16 kMaxTitleChars = 4096; // 4 * 1024;
@@ -1080,7 +1086,8 @@
// to this frame, and that subset may have changed while the page was in
// bfcache.
SetNavigationApiHistoryEntriesForRestore(
- NavigationApiHistoryEntryArrays entry_arrays);
+ NavigationApiHistoryEntryArrays entry_arrays,
+ NavigationApiEntryRestoreReason restore_reason);
// Updates navigation.entries() when entries are disposed from the session
// history.
diff --git a/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc b/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc
index f6bf6ab..1f23f15 100644
--- a/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_mojo_handler.cc
@@ -1138,8 +1138,10 @@
}
void LocalFrameMojoHandler::SetNavigationApiHistoryEntriesForRestore(
- mojom::blink::NavigationApiHistoryEntryArraysPtr entry_arrays) {
- frame_->DomWindow()->navigation()->SetEntriesForRestore(entry_arrays);
+ mojom::blink::NavigationApiHistoryEntryArraysPtr entry_arrays,
+ mojom::blink::NavigationApiEntryRestoreReason restore_reason) {
+ frame_->DomWindow()->navigation()->SetEntriesForRestore(entry_arrays,
+ restore_reason);
}
void LocalFrameMojoHandler::NotifyNavigationApiOfDisposedEntries(
diff --git a/third_party/blink/renderer/core/frame/local_frame_mojo_handler.h b/third_party/blink/renderer/core/frame/local_frame_mojo_handler.h
index 1804ed1b..88dc4a3 100644
--- a/third_party/blink/renderer/core/frame/local_frame_mojo_handler.h
+++ b/third_party/blink/renderer/core/frame/local_frame_mojo_handler.h
@@ -195,7 +195,8 @@
void GetOpenGraphMetadata(GetOpenGraphMetadataCallback callback) final;
void SetNavigationApiHistoryEntriesForRestore(
- mojom::blink::NavigationApiHistoryEntryArraysPtr) final;
+ mojom::blink::NavigationApiHistoryEntryArraysPtr,
+ mojom::blink::NavigationApiEntryRestoreReason) final;
void NotifyNavigationApiOfDisposedEntries(
const WTF::Vector<WTF::String>&) final;
void TraverseCancelled(const String& navigation_api_key,
diff --git a/third_party/blink/renderer/core/navigation_api/navigation_api.cc b/third_party/blink/renderer/core/navigation_api/navigation_api.cc
index 89f9d0c..14cdd73b 100644
--- a/third_party/blink/renderer/core/navigation_api/navigation_api.cc
+++ b/third_party/blink/renderer/core/navigation_api/navigation_api.cc
@@ -38,6 +38,7 @@
#include "third_party/blink/renderer/core/navigation_api/navigation_destination.h"
#include "third_party/blink/renderer/core/navigation_api/navigation_history_entry.h"
#include "third_party/blink/renderer/core/navigation_api/navigation_transition.h"
+#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/timing/soft_navigation_heuristics.h"
#include "third_party/blink/renderer/platform/bindings/exception_context.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
@@ -127,8 +128,10 @@
previous_history_entry = MakeEntryFromItem(*previous_item);
}
}
- activation_->Update(currentEntry(), previous_history_entry,
- DetermineNavigationType(load_type));
+ String navigation_type = window_->GetFrame()->GetPage()->IsPrerendering()
+ ? "push"
+ : DetermineNavigationType(load_type);
+ activation_->Update(currentEntry(), previous_history_entry, navigation_type);
}
NavigationHistoryEntry* NavigationApi::GetExistingEntryFor(const String& key,
@@ -304,7 +307,8 @@
}
void NavigationApi::SetEntriesForRestore(
- const mojom::blink::NavigationApiHistoryEntryArraysPtr& entry_arrays) {
+ const mojom::blink::NavigationApiHistoryEntryArraysPtr& entry_arrays,
+ mojom::blink::NavigationApiEntryRestoreReason restore_reason) {
// If this window HasEntriesAndEventsDisabled(), we shouldn't attempt to
// restore anything.
if (HasEntriesAndEventsDisabled())
@@ -325,9 +329,27 @@
base::checked_cast<wtf_size_t>(entry_arrays->back_entries.size());
keys_to_indices_.clear();
PopulateKeySet();
+
+ String navigation_type;
+ switch (restore_reason) {
+ case mojom::blink::NavigationApiEntryRestoreReason::kBFCache:
+ navigation_type = "traverse";
+ break;
+ case mojom::blink::NavigationApiEntryRestoreReason::
+ kPrerenderActivationPush:
+ navigation_type = "push";
+ break;
+ case mojom::blink::NavigationApiEntryRestoreReason::
+ kPrerenderActivationReplace:
+ navigation_type = "replace";
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
activation_->Update(currentEntry(),
GetEntryForRestore(entry_arrays->previous_entry),
- "traverse");
+ navigation_type);
// |new_entries| now contains the previous entries_. Find the ones that are no
// longer in entries_ so they can be disposed.
diff --git a/third_party/blink/renderer/core/navigation_api/navigation_api.h b/third_party/blink/renderer/core/navigation_api/navigation_api.h
index c9f8be4..19e9c38 100644
--- a/third_party/blink/renderer/core/navigation_api/navigation_api.h
+++ b/third_party/blink/renderer/core/navigation_api/navigation_api.h
@@ -53,7 +53,8 @@
HistoryItem* previous_entry);
void UpdateForNavigation(HistoryItem&, WebFrameLoadType);
void SetEntriesForRestore(
- const mojom::blink::NavigationApiHistoryEntryArraysPtr&);
+ const mojom::blink::NavigationApiHistoryEntryArraysPtr&,
+ mojom::blink::NavigationApiEntryRestoreReason);
// The entries indicated by |keys| have been removed from the session history
// in the browser process and should be disposed. In many cases, this won't
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/navigation-api-location-replace.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/navigation-api-location-replace.html
new file mode 100644
index 0000000..e3ecf1b7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/navigation-api-location-replace.html
@@ -0,0 +1,61 @@
+<!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="/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"></script>
+<script src="../resources/utils.js"></script>
+<script src="resources/utils.js"></script>
+
+<body>
+<script>
+setup(() => assertSpeculationRulesIsSupported());
+
+promise_test(async t => {
+ const rcHelper = new RemoteContextHelper();
+ const referrerRC = await rcHelper.addWindow(undefined, { features: 'noopener' });
+ assert_equals(await referrerRC.executeScript(() => navigation.entries().length), 1);
+ let referrerRCCurrentId = await referrerRC.executeScript(() => navigation.currentEntry.id);
+
+ const prerenderedRC = await addPrerenderRC(referrerRC);
+ let activationStateBeforeActivation = await prerenderedRC.executeScript(() => {
+ return {
+ entries: navigation.entries().map(e => ({ id: e.id, })),
+ activationEntryId: navigation.activation.entry?.id,
+ activationFromId: navigation.activation.from?.id,
+ activationNavigationType : navigation.activation.navigationType,
+ }
+ });
+ assert_equals(activationStateBeforeActivation.entries.length, 1);
+ assert_equals(activationStateBeforeActivation.activationFromId, referrerRCCurrentId);
+ assert_equals(activationStateBeforeActivation.activationEntryId, activationStateBeforeActivation.entries[0].id);
+ assert_equals(activationStateBeforeActivation.activationNavigationType, "push");
+
+ // Save the current entry before activation.
+ await prerenderedRC.executeScript(() => window.currentEntryBeforeActivation = navigation.currentEntry);
+
+ await activatePrerenderRC(referrerRC, prerenderedRC, url => {
+ location.replace(url);
+ });
+
+ let activationStateAfterActivation = await prerenderedRC.executeScript(() => {
+ return {
+ entries: navigation.entries().map(e => ({ id: e.id, })),
+ activationEntryId: navigation.activation.entry?.id,
+ activationFromId: navigation.activation.from?.id,
+ activationNavigationType : navigation.activation.navigationType,
+ }
+ });
+ assert_equals(activationStateAfterActivation.entries.length, 1);
+ assert_equals(activationStateAfterActivation.activationFromId, referrerRCCurrentId);
+ assert_equals(activationStateAfterActivation.activationEntryId, activationStateAfterActivation.entries[0].id);
+ assert_equals(activationStateAfterActivation.activationNavigationType, "replace");
+
+ let currentEntryIdentity = await prerenderedRC.executeScript(() => {
+ return window.currentEntryBeforeActivation === navigation.currentEntry &&
+ navigation.currentEntry === navigation.entries()[navigation.entries().length - 1];
+ });
+ assert_true(currentEntryIdentity);
+},`navigation.entries() and navigation.activation should be updated on activation and handle replacing correctly`);
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/navigation-api-multiple-entries.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/navigation-api-multiple-entries.html
new file mode 100644
index 0000000..e8e0f87
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/navigation-api-multiple-entries.html
@@ -0,0 +1,54 @@
+<!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="/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"></script>
+<script src="../resources/utils.js"></script>
+<script src="resources/utils.js"></script>
+
+<body>
+<script>
+setup(() => assertSpeculationRulesIsSupported());
+
+promise_test(async t => {
+ const rcHelper = new RemoteContextHelper();
+
+ const referrerRC1 = await rcHelper.addWindow(undefined, { features: 'noopener' });
+ const referrerRC2 = await referrerRC1.navigateToNew();
+ const referrerRC3 = await referrerRC2.navigateToNew();
+ assert_equals(await referrerRC3.executeScript(() => navigation.entries().length), 3);
+
+ let referrerRC3CurrentId = await referrerRC3.executeScript(() => navigation.currentEntry.id);
+
+ const prerenderedRC = await addPrerenderRC(referrerRC3);
+ let activationStateBeforeActivation = await prerenderedRC.executeScript(() => {
+ return {
+ entries: navigation.entries().map(e => ({ id: e.id, })),
+ activationEntryId: navigation.activation.entry?.id,
+ activationFromId: navigation.activation.from?.id,
+ activationNavigationType : navigation.activation.navigationType,
+ }
+ });
+ assert_equals(activationStateBeforeActivation.entries.length, 1);
+ assert_equals(activationStateBeforeActivation.activationFromId, referrerRC3CurrentId);
+ assert_equals(activationStateBeforeActivation.activationEntryId, activationStateBeforeActivation.entries[0].id);
+ assert_equals(activationStateBeforeActivation.activationNavigationType, "push");
+
+ await activatePrerenderRC(referrerRC3, prerenderedRC);
+
+ let activationStateAfterActivation = await prerenderedRC.executeScript(() => {
+ return {
+ entries: navigation.entries().map(e => ({ id: e.id, })),
+ activationEntryId: navigation.activation.entry?.id,
+ activationFromId: navigation.activation.from?.id,
+ activationNavigationType : navigation.activation.navigationType,
+ }
+ });
+ assert_equals(activationStateAfterActivation.entries.length, 4);
+ assert_equals(activationStateAfterActivation.activationFromId, activationStateAfterActivation.entries[2].id);
+ assert_equals(activationStateAfterActivation.activationEntryId, activationStateAfterActivation.entries[3].id);
+ assert_equals(activationStateAfterActivation.activationNavigationType, "push");
+},`navigation.entries() and navigation.activation should be updated on activation - multiple entries`);
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/navigation-api.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/navigation-api.html
new file mode 100644
index 0000000..fd25e94
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prerender/navigation-api.html
@@ -0,0 +1,60 @@
+<!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="/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"></script>
+<script src="../resources/utils.js"></script>
+<script src="resources/utils.js"></script>
+
+<body>
+<script>
+setup(() => assertSpeculationRulesIsSupported());
+
+promise_test(async t => {
+ const rcHelper = new RemoteContextHelper();
+ const referrerRC = await rcHelper.addWindow(undefined, { features: 'noopener' });
+ assert_equals(await referrerRC.executeScript(() => navigation.entries().length), 1);
+ let referrerRCCurrentId = await referrerRC.executeScript(() => navigation.currentEntry.id);
+
+ const prerenderedRC = await addPrerenderRC(referrerRC);
+ let activationStateBeforeActivation = await prerenderedRC.executeScript(() => {
+ return {
+ entries: navigation.entries().map(e => ({ id: e.id, })),
+ activationEntryId: navigation.activation.entry?.id,
+ activationFromId: navigation.activation.from?.id,
+ activationNavigationType : navigation.activation.navigationType,
+ }
+ });
+ assert_equals(activationStateBeforeActivation.entries.length, 1);
+ assert_equals(activationStateBeforeActivation.activationFromId, referrerRCCurrentId);
+ assert_equals(activationStateBeforeActivation.activationEntryId, activationStateBeforeActivation.entries[0].id);
+ assert_equals(activationStateBeforeActivation.activationNavigationType, "push");
+
+ // Save the current entry before activation.
+ await prerenderedRC.executeScript(() => window.currentEntryBeforeActivation = navigation.currentEntry);
+
+ await activatePrerenderRC(referrerRC, prerenderedRC);
+
+ let activationStateAfterActivation = await prerenderedRC.executeScript(() => {
+ return {
+ entries: navigation.entries().map(e => ({ id: e.id, })),
+ activationEntryId: navigation.activation.entry?.id,
+ activationFromId: navigation.activation.from?.id,
+ activationNavigationType : navigation.activation.navigationType,
+ }
+ });
+ assert_equals(activationStateAfterActivation.entries.length, 2);
+ assert_equals(activationStateAfterActivation.activationFromId, activationStateAfterActivation.entries[0].id);
+ assert_equals(activationStateAfterActivation.activationFromId, referrerRCCurrentId);
+ assert_equals(activationStateAfterActivation.activationEntryId, activationStateAfterActivation.entries[1].id);
+ assert_equals(activationStateAfterActivation.activationNavigationType, "push");
+
+ let currentEntryIdentity = await prerenderedRC.executeScript(() => {
+ return window.currentEntryBeforeActivation === navigation.currentEntry &&
+ navigation.currentEntry === navigation.entries()[navigation.entries().length - 1];
+ });
+ assert_true(currentEntryIdentity);
+},`navigation.entries() and navigation.activation should be updated on activation`);
+</script>