blob: 88453ba0bc3d8424e101024b0c0e9e29817f619e [file] [log] [blame]
Daniel Cheng284c38942022-09-22 23:30:341// Copyright 2022 The Chromium Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/run_loop.h"
6#include "base/test/bind.h"
7#include "content/browser/renderer_host/render_frame_host_impl.h"
8#include "content/browser/web_contents/web_contents_impl.h"
9#include "content/public/browser/weak_document_ptr.h"
10#include "content/public/test/browser_test.h"
11#include "content/public/test/browser_test_utils.h"
12#include "content/public/test/content_browser_test.h"
13#include "content/public/test/content_browser_test_utils.h"
14#include "content/shell/browser/shell.h"
15#include "content/shell/common/render_frame_test_helper.mojom.h"
16#include "content/test/content_browser_test_utils_internal.h"
17#include "mojo/public/cpp/bindings/remote.h"
18#include "net/dns/mock_host_resolver.h"
19#include "testing/gtest/include/gtest/gtest.h"
20#include "third_party/abseil-cpp/absl/types/optional.h"
21#include "third_party/blink/public/common/chrome_debug_urls.h"
22#include "third_party/blink/public/common/tokens/tokens.h"
23#include "url/gurl.h"
24
25namespace content {
26
27namespace {
28
29// The general structure of all tests is to navigate A -> A -> B. A -> A will
30// reuse the same `RenderFrameHost` (without RenderDocument) while A -> B will
31// swap to a new `RenderFrameHost` (with --site-per-process).
32class DocumentTokenBrowserTest : public ContentBrowserTest {
33 protected:
34 void SetUpOnMainThread() override {
35 ContentBrowserTest::SetUpOnMainThread();
36
37 host_resolver()->AddRule("*", "127.0.0.1");
38 ASSERT_TRUE(embedded_test_server()->Start());
39 }
40
41 WebContentsImpl* web_contents() {
42 return static_cast<WebContentsImpl*>(shell()->web_contents());
43 }
44
45 blink::DocumentToken GetBrowserSideToken(ToRenderFrameHost adapter) {
46 return static_cast<RenderFrameHostImpl*>(adapter.render_frame_host())
47 ->GetDocumentToken();
48 }
49
50 // Verifies that the browser-side `DocumentToken` and the renderer-side
51 // `DocumentToken` have matching values.
52 [[nodiscard]] ::testing::AssertionResult VerifyMatchingTokens(
53 ToRenderFrameHost adapter) {
54 blink::DocumentToken token_from_browser = GetBrowserSideToken(adapter);
55
56 mojo::Remote<mojom::RenderFrameTestHelper> remote;
57 adapter.render_frame_host()->GetRemoteInterfaces()->GetInterface(
58 remote.BindNewPipeAndPassReceiver());
59 blink::DocumentToken token_from_renderer;
60 base::RunLoop run_loop;
61 remote->GetDocumentToken(
62 base::BindLambdaForTesting([&](const blink::DocumentToken& token) {
63 token_from_renderer = token;
64 run_loop.Quit();
65 }));
66 run_loop.Run();
67
68 if (token_from_browser == token_from_renderer) {
69 return ::testing::AssertionSuccess();
70 }
71 return ::testing::AssertionFailure()
72 << "browser token was " << token_from_browser
73 << " but renderer token was " << token_from_renderer;
74 }
75
76 // Whether or not `NavigateAndGetNewToken()` should wait for the response and
77 // validate document token state immediately afterwards. Most tests should
78 // expect and wait for a response; however, tests that are exercising
79 // `CommitFailedNavigation()` will probably want to specify `kNo`.
80 enum class ExpectedResponse {
81 kYes,
82 kNo,
83 };
84
85 // Navigate `adapter.render_frame_host()` to `target_url`. Verifies that the
86 // browser and renderer state are in sync, and that the document token is not
87 // updated until the navigation actually commits.
88 //
89 // Note: this helper makes IPCs to the `RenderFrame`; for the first navigation
90 // in a WebContents, it is typically more appropriate to use `NavigateToURL()`
91 // or another similar helper instead.
92 blink::DocumentToken NavigateAndGetNewToken(
93 ToRenderFrameHost adapter,
94 const GURL& target_url,
95 ExpectedResponse expect_response = ExpectedResponse::kYes) {
96 SCOPED_TRACE(target_url.spec());
97 // Capture the FrameTreeNode now; when a navigation commits, the current
98 // RenderFrameHost may change.
99 FrameTreeNode* const frame_tree_node =
100 static_cast<RenderFrameHostImpl*>(adapter.render_frame_host())
101 ->frame_tree_node();
102 const blink::DocumentToken old_token = GetBrowserSideToken(adapter);
103
104 // Start a new navigation in the main frame. The navigation is still
105 // ongoing, so `DocumentToken` should not be updated yet.
106 TestNavigationManager nav_manager(
107 WebContents::FromRenderFrameHost(adapter.render_frame_host()),
108 target_url);
109 EXPECT_TRUE(BeginNavigateToURLFromRenderer(adapter, target_url));
110 EXPECT_TRUE(VerifyMatchingTokens(adapter));
111 EXPECT_EQ(old_token, GetBrowserSideToken(adapter));
112
113 // Just before the request is actually issued, the navigation is still
114 // ongoing, so `DocumentToken` should not be updated yet.
115 EXPECT_TRUE(nav_manager.WaitForRequestStart());
116 EXPECT_TRUE(VerifyMatchingTokens(adapter));
117 EXPECT_EQ(old_token, GetBrowserSideToken(adapter));
118
119 if (ExpectedResponse::kYes == expect_response) {
120 // Just before reading the response, the navigation is still ongoing, so
121 // `DocumentToken` should not be updated yet.
122 EXPECT_TRUE(nav_manager.WaitForResponse());
123 EXPECT_TRUE(VerifyMatchingTokens(adapter));
124 EXPECT_EQ(old_token, GetBrowserSideToken(adapter));
125 }
126
127 // Once a cross-document navigation completes, the document token should be
128 // updated though.
129 nav_manager.WaitForNavigationFinished();
130 // The RenderFrameHost may have changed; use the FrameTreeNode captured
131 // above instead.
132 EXPECT_EQ(target_url,
133 frame_tree_node->current_frame_host()->GetLastCommittedURL());
134 EXPECT_TRUE(VerifyMatchingTokens(frame_tree_node));
135 const blink::DocumentToken new_token = GetBrowserSideToken(frame_tree_node);
136 EXPECT_NE(new_token, old_token);
137 return new_token;
138 }
139};
140
141IN_PROC_BROWSER_TEST_F(DocumentTokenBrowserTest, MainFrameBasic) {
142 std::vector<blink::DocumentToken> seen_tokens;
143
144 ASSERT_TRUE(NavigateToURL(
145 web_contents(), embedded_test_server()->GetURL("a.com", "/title1.html")));
146 EXPECT_TRUE(VerifyMatchingTokens(web_contents()));
147 seen_tokens.push_back(GetBrowserSideToken(web_contents()));
148
149 seen_tokens.push_back(NavigateAndGetNewToken(
150 web_contents(), embedded_test_server()->GetURL("a.com", "/title1.html")));
151
152 seen_tokens.push_back(NavigateAndGetNewToken(
153 web_contents(), embedded_test_server()->GetURL("b.com", "/title1.html")));
154
155 std::set unique_tokens(seen_tokens.begin(), seen_tokens.end());
156 EXPECT_EQ(unique_tokens.size(), seen_tokens.size());
157}
158
159IN_PROC_BROWSER_TEST_F(DocumentTokenBrowserTest, SubFrameBasic) {
160 std::vector<blink::DocumentToken> seen_tokens;
161
162 ASSERT_TRUE(NavigateToURL(
163 web_contents(), embedded_test_server()->GetURL(
164 "a.com", "/cross_site_iframe_factory.html?a(a)")));
165 EXPECT_TRUE(VerifyMatchingTokens(web_contents()));
166 EXPECT_TRUE(VerifyMatchingTokens(ChildFrameAt(web_contents(), 0)));
167 seen_tokens.push_back(GetBrowserSideToken(web_contents()));
168 seen_tokens.push_back(GetBrowserSideToken(ChildFrameAt(web_contents(), 0)));
169
170 seen_tokens.push_back(NavigateAndGetNewToken(
171 ChildFrameAt(web_contents(), 0),
172 embedded_test_server()->GetURL("a.com", "/title1.html")));
173 // Main document did not navigate so the token should be the same.
174 EXPECT_EQ(seen_tokens[0], GetBrowserSideToken(web_contents()));
175
176 seen_tokens.push_back(NavigateAndGetNewToken(
177 ChildFrameAt(web_contents(), 0),
178 embedded_test_server()->GetURL("b.com", "/title1.html")));
179 // Main document did not navigate so the token should be the same.
180 EXPECT_EQ(seen_tokens[0], GetBrowserSideToken(web_contents()));
181
182 std::set unique_tokens(seen_tokens.begin(), seen_tokens.end());
183 EXPECT_EQ(unique_tokens.size(), seen_tokens.size());
184}
185
186IN_PROC_BROWSER_TEST_F(DocumentTokenBrowserTest, NewWindowBasic) {
187 std::vector<blink::DocumentToken> seen_tokens;
188
189 ASSERT_TRUE(NavigateToURL(
190 web_contents(), embedded_test_server()->GetURL("a.com", "/title1.html")));
191 EXPECT_EQ(1u, Shell::windows().size());
192 seen_tokens.push_back(GetBrowserSideToken(web_contents()));
193
194 WebContents* new_contents = nullptr;
195 {
196 // This block is largely derived from `NavigateAndGetNewToken()`. This test
197 // cannot easily reuse that helper because:
198 //
199 // - it is important to specify an actual target URL other than about:blank
200 // for `window.open()`. Specifying no target URL and then later navigating
201 // the window has subtly different behavior (e.g. the
202 // `NewWindowSyncCommit` test below).
203 // - the helper expects the `WebContents` to already exist in order to
204 // install
205 // `TestNavigationManager`. However, in this test, a new `WebContents` is
206 // created in the process of running the test.
207 ExecuteScriptAsync(web_contents(), JsReplace("window.open($1)",
208 embedded_test_server()->GetURL(
209 "a.com", "/title1.html")));
210 ShellAddedObserver wait_for_new_shell;
211 new_contents = wait_for_new_shell.GetShell()->web_contents();
212 DCHECK_EQ(2u, Shell::windows().size());
213 DCHECK_EQ(new_contents, Shell::windows()[1]->web_contents());
214 DCHECK_NE(new_contents, web_contents());
215 seen_tokens.push_back(GetBrowserSideToken(new_contents));
216 TestNavigationManager nav_manager(
217 new_contents, embedded_test_server()->GetURL("a.com", "/title1.html"));
218
219 // Capture the FrameTreeNode now; when a navigation commits, the current
220 // RenderFrameHost may change.
221 FrameTreeNode* const frame_tree_node =
222 static_cast<RenderFrameHostImpl*>(new_contents->GetPrimaryMainFrame())
223 ->frame_tree_node();
224 const blink::DocumentToken old_token = GetBrowserSideToken(new_contents);
225
226 EXPECT_TRUE(VerifyMatchingTokens(new_contents));
227 EXPECT_EQ(old_token, GetBrowserSideToken(new_contents));
228 // Even after creating a new window, the original `WebContents` should still
229 // have the same `DocumentToken`.
230 EXPECT_EQ(seen_tokens[0], GetBrowserSideToken(web_contents()));
231
232 // Just before the request is actually issued, the navigation is still
233 // ongoing, so `DocumentToken` should not be updated yet.
234 EXPECT_TRUE(nav_manager.WaitForRequestStart());
235 EXPECT_TRUE(VerifyMatchingTokens(new_contents));
236 EXPECT_EQ(old_token, GetBrowserSideToken(new_contents));
237 // The original `WebContents` should still have the same `DocumentToken`.
238 EXPECT_EQ(seen_tokens[0], GetBrowserSideToken(web_contents()));
239
240 // Just before reading the response, the navigation is still ongoing, so
241 // `DocumentToken` should not be updated yet.
242 EXPECT_TRUE(nav_manager.WaitForResponse());
243 EXPECT_TRUE(VerifyMatchingTokens(new_contents));
244 EXPECT_EQ(old_token, GetBrowserSideToken(new_contents));
245 // The original `WebContents` should still have the same `DocumentToken`.
246 EXPECT_EQ(seen_tokens[0], GetBrowserSideToken(web_contents()));
247
248 // Once a cross-document navigation completes, the document token should be
249 // updated though.
250 nav_manager.WaitForNavigationFinished();
251 // The RenderFrameHost may have changed; use the FrameTreeNode captured
252 // above instead.
253 EXPECT_EQ(embedded_test_server()->GetURL("a.com", "/title1.html"),
254 frame_tree_node->current_frame_host()->GetLastCommittedURL());
255 EXPECT_TRUE(VerifyMatchingTokens(frame_tree_node));
256 const blink::DocumentToken new_token = GetBrowserSideToken(frame_tree_node);
257 EXPECT_NE(new_token, old_token);
258 seen_tokens.push_back(new_token);
259 // The original `WebContents` should still have the same `DocumentToken`.
260 EXPECT_EQ(seen_tokens[0], GetBrowserSideToken(web_contents()));
261 }
262
263 seen_tokens.push_back(NavigateAndGetNewToken(
264 new_contents, embedded_test_server()->GetURL("a.com", "/title1.html")));
265 // The original `WebContents` should still have the same `DocumentToken`.
266 EXPECT_EQ(seen_tokens[0], GetBrowserSideToken(web_contents()));
267
268 seen_tokens.push_back(NavigateAndGetNewToken(
269 new_contents, embedded_test_server()->GetURL("b.com", "/title1.html")));
270 // The original `WebContents` should still have the same `DocumentToken`.
271 EXPECT_EQ(seen_tokens[0], GetBrowserSideToken(web_contents()));
272
273 std::set unique_tokens(seen_tokens.begin(), seen_tokens.end());
274 EXPECT_EQ(unique_tokens.size(), seen_tokens.size());
275}
276
277IN_PROC_BROWSER_TEST_F(DocumentTokenBrowserTest, SubFrameSyncCommit) {
278 std::vector<blink::DocumentToken> seen_tokens;
279
280 // This is a basic test that the synchronous commit of about:blank reuses the
281 // same DocumentToken. See https://crbug.com/778318 for more details.
282 ASSERT_TRUE(NavigateToURL(
283 web_contents(),
284 embedded_test_server()->GetURL("a.com", "/page_with_blank_iframe.html")));
285 EXPECT_TRUE(VerifyMatchingTokens(web_contents()));
286 EXPECT_TRUE(VerifyMatchingTokens(ChildFrameAt(web_contents(), 0)));
287 seen_tokens.push_back(GetBrowserSideToken(web_contents()));
288 seen_tokens.push_back(GetBrowserSideToken(ChildFrameAt(web_contents(), 0)));
289
290 seen_tokens.push_back(NavigateAndGetNewToken(
291 ChildFrameAt(web_contents(), 0),
292 embedded_test_server()->GetURL("a.com", "/title1.html")));
293 // Main document did not navigate so the token should be the same.
294 EXPECT_EQ(seen_tokens[0], GetBrowserSideToken(web_contents()));
295
296 seen_tokens.push_back(NavigateAndGetNewToken(
297 ChildFrameAt(web_contents(), 0),
298 embedded_test_server()->GetURL("b.com", "/title1.html")));
299 // Main document did not navigate so the token should be the same.
300 EXPECT_EQ(seen_tokens[0], GetBrowserSideToken(web_contents()));
301
302 std::set unique_tokens(seen_tokens.begin(), seen_tokens.end());
303 EXPECT_EQ(unique_tokens.size(), seen_tokens.size());
304}
305
306IN_PROC_BROWSER_TEST_F(DocumentTokenBrowserTest, NewWindowSyncCommit) {
307 std::vector<blink::DocumentToken> seen_tokens;
308
309 ASSERT_TRUE(NavigateToURL(web_contents(), GURL("about:blank")));
310 EXPECT_EQ(1u, Shell::windows().size());
311 EXPECT_TRUE(VerifyMatchingTokens(web_contents()));
312 seen_tokens.push_back(GetBrowserSideToken(web_contents()));
313
314 // This is a basic test that the synchronous commit of about:blank reuses the
315 // same DocumentToken. See https://crbug.com/778318 for more details.
316 ASSERT_TRUE(ExecJs(web_contents(), "window.open()"));
317 ASSERT_EQ(2u, Shell::windows().size());
318 WebContents* new_contents = Shell::windows()[1]->web_contents();
319 DCHECK_NE(new_contents, web_contents());
320 EXPECT_TRUE(VerifyMatchingTokens(new_contents));
321 // The original `WebContents` should still have the same `DocumentToken`.
322 EXPECT_EQ(seen_tokens[0], GetBrowserSideToken(web_contents()));
323
324 seen_tokens.push_back(NavigateAndGetNewToken(
325 new_contents, embedded_test_server()->GetURL("a.com", "/title1.html")));
326 // The original `WebContents` should still have the same `DocumentToken`.
327 EXPECT_EQ(seen_tokens[0], GetBrowserSideToken(web_contents()));
328
329 seen_tokens.push_back(NavigateAndGetNewToken(
330 new_contents, embedded_test_server()->GetURL("a.com", "/title1.html")));
331 // The original `WebContents` should still have the same `DocumentToken`.
332 EXPECT_EQ(seen_tokens[0], GetBrowserSideToken(web_contents()));
333
334 seen_tokens.push_back(NavigateAndGetNewToken(
335 new_contents, embedded_test_server()->GetURL("b.com", "/title1.html")));
336 // The original `WebContents` should still have the same `DocumentToken`.
337 EXPECT_EQ(seen_tokens[0], GetBrowserSideToken(web_contents()));
338
339 std::set unique_tokens(seen_tokens.begin(), seen_tokens.end());
340 EXPECT_EQ(unique_tokens.size(), seen_tokens.size());
341}
342
343IN_PROC_BROWSER_TEST_F(DocumentTokenBrowserTest, JavascriptURL) {
344 ASSERT_TRUE(NavigateToURL(
345 web_contents(), embedded_test_server()->GetURL("a.com", "/title1.html")));
346 EXPECT_TRUE(VerifyMatchingTokens(web_contents()));
347 const blink::DocumentToken token = GetBrowserSideToken(web_contents());
348
349 // A javascript: navigation that replaces the document should not change the
350 // DocumentToken. This does not use the normal Navigate*() helpers since it
351 // does not commit a normal cross-document navigation.
352 ASSERT_TRUE(ExecJs(web_contents(),
353 JsReplace("location = $1", "javascript:'Hello world!'")));
354 EXPECT_EQ("Hello world!", EvalJs(web_contents(), "document.body.innerText"));
355 EXPECT_TRUE(VerifyMatchingTokens(web_contents()));
356 EXPECT_EQ(token, GetBrowserSideToken(web_contents()));
357}
358
359IN_PROC_BROWSER_TEST_F(DocumentTokenBrowserTest, FailedNavigation) {
360 std::vector<blink::DocumentToken> seen_tokens;
361
362 ASSERT_TRUE(NavigateToURL(
363 web_contents(), embedded_test_server()->GetURL("a.com", "/title1.html")));
364 EXPECT_TRUE(VerifyMatchingTokens(web_contents()));
365 seen_tokens.push_back(GetBrowserSideToken(web_contents()));
366
367 seen_tokens.push_back(NavigateAndGetNewToken(
368 web_contents(), embedded_test_server()->GetURL("a.com", "/close-socket"),
369 ExpectedResponse::kNo));
370
371 seen_tokens.push_back(NavigateAndGetNewToken(
372 web_contents(), embedded_test_server()->GetURL("a.com", "/close-socket"),
373 ExpectedResponse::kNo));
374
375 seen_tokens.push_back(NavigateAndGetNewToken(
376 web_contents(), embedded_test_server()->GetURL("b.com", "/close-socket"),
377 ExpectedResponse::kNo));
378
379 // Test that a regular successful navigation still updates the document token.
380 seen_tokens.push_back(NavigateAndGetNewToken(
381 web_contents(), embedded_test_server()->GetURL("a.com", "/title1.html")));
382
383 std::set unique_tokens(seen_tokens.begin(), seen_tokens.end());
384 EXPECT_EQ(unique_tokens.size(), seen_tokens.size());
385}
386
387IN_PROC_BROWSER_TEST_F(DocumentTokenBrowserTest, CrashThenReload) {
388 ASSERT_TRUE(NavigateToURL(
389 web_contents(), embedded_test_server()->GetURL("a.com", "/title1.html")));
390 EXPECT_TRUE(VerifyMatchingTokens(web_contents()));
391 const blink::DocumentToken token = GetBrowserSideToken(web_contents());
392
393 // Cause the renderer to crash.
394 RenderProcessHostWatcher crash_observer(
395 web_contents(), RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
396 EXPECT_FALSE(NavigateToURL(shell(), GURL(blink::kChromeUICrashURL)));
397 // Wait for browser to notice the renderer crash.
398 crash_observer.Wait();
399
400 // After a crash, the DocumentToken should still be the same even though the
401 // renderer process is gone..
402 EXPECT_EQ(token, GetBrowserSideToken(web_contents()));
403
404 // But when a live RenderFrame is needed again, RenderDocument should force a
405 // new RenderFrameHost, and thus, a new DocumentToken. The remainder of this
406 // test does not use `NavigateAndGetNewToken()`, which tries to use a
407 // renderer-initiated navigation (which is not possible when the renderer is
408 // not live).
409 TestNavigationManager nav_manager(
410 web_contents(), embedded_test_server()->GetURL("a.com", "/title1.html"));
411 shell()->LoadURL(embedded_test_server()->GetURL("a.com", "/title1.html"));
412 EXPECT_TRUE(VerifyMatchingTokens(web_contents()));
413 const blink::DocumentToken token_after_navigation_started =
414 GetBrowserSideToken(web_contents());
415 EXPECT_NE(token_after_navigation_started, token);
416 const WeakDocumentPtr document_weak_ptr =
417 web_contents()->GetPrimaryMainFrame()->GetWeakDocumentPtr();
418
Daniel Cheng273a2c12022-09-28 02:28:22419 // After the navigation finishes, the RenderFrameHost will still use the same
420 // DocumentToken, since no new DocumentAssociatedData was created. The latter
421 // is indirectly tested by checking if the WeakDocumentPtr is still valid
422 // after the navigation commits.
Daniel Cheng284c38942022-09-22 23:30:34423 nav_manager.WaitForNavigationFinished();
424 EXPECT_TRUE(VerifyMatchingTokens(web_contents()));
425 const blink::DocumentToken token_after_navigation_finished =
426 GetBrowserSideToken(web_contents());
427 EXPECT_NE(token_after_navigation_finished, token);
Daniel Cheng273a2c12022-09-28 02:28:22428 EXPECT_EQ(token_after_navigation_finished, token_after_navigation_started);
Daniel Cheng284c38942022-09-22 23:30:34429 EXPECT_NE(document_weak_ptr.AsRenderFrameHostIfValid(), nullptr);
430}
431
432IN_PROC_BROWSER_TEST_F(DocumentTokenBrowserTest,
433 CrashThenImmediateReinitialize) {
434 ASSERT_TRUE(NavigateToURL(
435 web_contents(), embedded_test_server()->GetURL("a.com", "/title1.html")));
436 EXPECT_TRUE(VerifyMatchingTokens(web_contents()));
437 const blink::DocumentToken token = GetBrowserSideToken(web_contents());
438 const WeakDocumentPtr document_weak_ptr =
439 web_contents()->GetPrimaryMainFrame()->GetWeakDocumentPtr();
440
441 // Cause the renderer to crash.
442 RenderProcessHostWatcher crash_observer(
443 web_contents(), RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
444 EXPECT_FALSE(NavigateToURL(shell(), GURL(blink::kChromeUICrashURL)));
445 // Wait for browser to notice the renderer crash.
446 crash_observer.Wait();
447
448 // If the main render frame is re-initialized, it also gets a new
449 // DocumentAssociatedData. Validate that the new DocumentAssociatedData is
450 // created before the renderer is re-created; a typical failure in this path
451 // will manifest as a mismatch between the browser and renderer-side document
452 // tokens.
453 web_contents()
454 ->GetPrimaryFrameTree()
455 .root()
456 ->render_manager()
457 ->InitializeMainRenderFrameForImmediateUse();
458 EXPECT_TRUE(VerifyMatchingTokens(web_contents()));
459 // The re-created RenderFrame should have a distinct document token.
460 const blink::DocumentToken new_token = GetBrowserSideToken(web_contents());
461 EXPECT_NE(new_token, token);
462 // The previous DocumentWeakPtr should be invalidated since the
463 // DocumentAssociatedData was re-created.
464 EXPECT_FALSE(document_weak_ptr.AsRenderFrameHostIfValid());
465}
466
467// TODO(https://crbug.com/1362938): Add tests for bfcache navigations and
468// prerender activations.
469
470} // namespace
471
472} // namespace content