BFCache: Do not check IsDOMContentLoaded of a frame if last url
in the frame is invalid IsDOMContentLoaded can be completed when the url is valid. If a subframe with an invalid url exists in a page, bfcache does not work because IsDOMContentLoaded of the frame is always false. This required test updates: - ChildFrameCaptureContentFirst disable BFCache because the test requires that the frame be evicted - the data= version of embedded-not-found-expected.txt now passes This adds WebContentsWaiter a base class for making test-waiters that wait for a WebContentObserver event to occur. R=toyoshim@chromium.org Bug: 1366707 Change-Id: I371cd9ad55f342d9c71ba24eaf5b8b5981d818ba Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3912822 Reviewed-by: Xianzhu Wang <wangxianzhu@chromium.org> Reviewed-by: Rakina Zata Amni <rakina@chromium.org> Commit-Queue: Fergal Daly <fergal@chromium.org> Cr-Commit-Position: refs/heads/main@{#1107116}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
d708718c80
commit
7e0c67bd9f
AUTHORS
components/content_capture/browser
content
third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/bfcache
1
AUTHORS
1
AUTHORS
@ -644,6 +644,7 @@ Julien Isorce <j.isorce@samsung.com>
|
||||
Julien Racle <jracle@logitech.com>
|
||||
Jun Fang <jun_fang@foxitsoftware.com>
|
||||
Jun Jiang <jun.a.jiang@intel.com>
|
||||
Junbong Eom <jb.eom@samsung.com>
|
||||
Jungchang Park <valley84265@gmail.com>
|
||||
Junchao Han <junchao.han@intel.com>
|
||||
Junghoon Lee <sjh836@gmail.com>
|
||||
|
@ -9,7 +9,9 @@
|
||||
#include "base/test/test_mock_time_task_runner.h"
|
||||
#include "build/build_config.h"
|
||||
#include "components/content_capture/browser/content_capture_test_helper.h"
|
||||
#include "content/public/browser/back_forward_cache.h"
|
||||
#include "content/public/common/content_features.h"
|
||||
#include "content/public/test/back_forward_cache_util.h"
|
||||
#include "content/public/test/test_renderer_host.h"
|
||||
#include "content/public/test/test_utils.h"
|
||||
#include "third_party/blink/public/mojom/favicon/favicon_url.mojom.h"
|
||||
@ -371,6 +373,9 @@ TEST_P(ContentCaptureReceiverTest, TitleUpdateTaskDelay) {
|
||||
#define MAYBE_ChildFrameCaptureContentFirst ChildFrameCaptureContentFirst
|
||||
#endif
|
||||
TEST_P(ContentCaptureReceiverTest, MAYBE_ChildFrameCaptureContentFirst) {
|
||||
// This test performs navigations, expecting the frames to be destroyed.
|
||||
content::DisableBackForwardCacheForTesting(
|
||||
web_contents(), content::BackForwardCache::TEST_REQUIRES_NO_CACHING);
|
||||
// Simulate add child frame.
|
||||
SetupChildFrame();
|
||||
// Simulate to capture the content from child frame.
|
||||
|
@ -5,9 +5,12 @@
|
||||
#include "content/browser/back_forward_cache_browsertest.h"
|
||||
|
||||
#include "content/browser/web_contents/web_contents_impl.h"
|
||||
#include "content/public/browser/render_frame_host.h"
|
||||
#include "content/public/browser/site_isolation_policy.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "content/public/common/url_constants.h"
|
||||
#include "content/public/test/browser_test.h"
|
||||
#include "content/public/test/browser_test_utils.h"
|
||||
#include "content/public/test/content_browser_test_utils.h"
|
||||
#include "content/public/test/navigation_handle_observer.h"
|
||||
#include "content/public/test/test_navigation_observer.h"
|
||||
@ -32,6 +35,19 @@ using testing::UnorderedElementsAreArray;
|
||||
|
||||
namespace content {
|
||||
|
||||
namespace {
|
||||
void InsertSubFrameWithUrl(RenderFrameHost* rfh, std::string url) {
|
||||
std::string insert_script = base::StringPrintf(
|
||||
R"(
|
||||
const iframeElement = document.createElement("iframe");
|
||||
iframeElement.src = "%s";
|
||||
document.body.appendChild(iframeElement);
|
||||
)",
|
||||
url.c_str());
|
||||
ASSERT_TRUE(ExecJs(rfh, insert_script));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
using NotRestoredReason = BackForwardCacheMetrics::NotRestoredReason;
|
||||
using NotRestoredReasons =
|
||||
BackForwardCacheCanStoreDocumentResult::NotRestoredReasons;
|
||||
@ -575,125 +591,172 @@ IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
|
||||
}
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
|
||||
DoesNotCacheIfMainFrameStillLoading) {
|
||||
class BackForwardCacheStillLoadingBrowserTest
|
||||
: public BackForwardCacheBrowserTest,
|
||||
public ::testing::WithParamInterface<TestFrameType> {
|
||||
protected:
|
||||
std::string GetMainFramePath() {
|
||||
switch (GetParam()) {
|
||||
case TestFrameType::kMainFrame:
|
||||
return "/controlled";
|
||||
case TestFrameType::kSubFrame:
|
||||
return "/back_forward_cache/controllable_subframe.html";
|
||||
case TestFrameType::kSubFrameOfSubframe:
|
||||
return "/back_forward_cache/controllable_subframe_of_subframe.html";
|
||||
}
|
||||
}
|
||||
|
||||
int GetNavigationCount() {
|
||||
switch (GetParam()) {
|
||||
case TestFrameType::kMainFrame:
|
||||
return 1;
|
||||
case TestFrameType::kSubFrame:
|
||||
return 2;
|
||||
case TestFrameType::kSubFrameOfSubframe:
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(All,
|
||||
BackForwardCacheStillLoadingBrowserTest,
|
||||
::testing::Values(TestFrameType::kMainFrame,
|
||||
TestFrameType::kSubFrame,
|
||||
TestFrameType::kSubFrameOfSubframe));
|
||||
|
||||
IN_PROC_BROWSER_TEST_P(BackForwardCacheStillLoadingBrowserTest,
|
||||
DoesNotCacheIfFrameStillLoading) {
|
||||
std::string controlled_path("/controlled");
|
||||
net::test_server::ControllableHttpResponse response(embedded_test_server(),
|
||||
"/main_document");
|
||||
controlled_path);
|
||||
ASSERT_TRUE(embedded_test_server()->Start());
|
||||
|
||||
// 1) Navigate to a page that doesn't finish loading.
|
||||
GURL url(embedded_test_server()->GetURL("a.com", "/main_document"));
|
||||
TestNavigationManager navigation_manager(shell()->web_contents(), url);
|
||||
shell()->LoadURL(url);
|
||||
bool testing_main_frame = GetParam() == TestFrameType::kMainFrame;
|
||||
|
||||
// The navigation starts.
|
||||
EXPECT_TRUE(navigation_manager.WaitForRequestStart());
|
||||
navigation_manager.ResumeNavigation();
|
||||
GURL main_frame_url(
|
||||
embedded_test_server()->GetURL("a.com", GetMainFramePath()));
|
||||
|
||||
// The server sends the first part of the response and waits.
|
||||
// 1) Navigate to a page with a frame that loads partially but never
|
||||
// completes. We need the navigation of the partial frame to complete to avoid
|
||||
// extra blocking reasons from occurring.
|
||||
TestNavigationObserver observer(web_contents(), GetNavigationCount());
|
||||
observer.set_wait_event(
|
||||
TestNavigationObserver::WaitEvent::kNavigationFinished);
|
||||
shell()->LoadURL(main_frame_url);
|
||||
response.WaitForRequest();
|
||||
response.Send(
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Content-Type: text/html; charset=utf-8\r\n"
|
||||
"\r\n"
|
||||
"<html><body> ... ");
|
||||
|
||||
// The navigation finishes while the body is still loading.
|
||||
ASSERT_TRUE(navigation_manager.WaitForNavigationFinished());
|
||||
RenderFrameDeletedObserver delete_observer_rfh_a(current_frame_host());
|
||||
"<html><body>...");
|
||||
observer.WaitForNavigationFinished();
|
||||
|
||||
// 2) Navigate away.
|
||||
RenderFrameHostImplWrapper rfh_a(current_frame_host());
|
||||
shell()->LoadURL(embedded_test_server()->GetURL("b.com", "/title1.html"));
|
||||
ASSERT_TRUE(WaitForLoadStop(web_contents()));
|
||||
|
||||
// The page was still loading when we navigated away, so it shouldn't have
|
||||
// been cached.
|
||||
delete_observer_rfh_a.WaitUntilDeleted();
|
||||
// The page should not have been added to cache, since it had a subframe
|
||||
// that was still loading at the time it was navigated away from.
|
||||
ASSERT_TRUE(rfh_a.WaitUntilRenderFrameDeleted());
|
||||
|
||||
// 3) Go back.
|
||||
web_contents()->GetController().GoBack();
|
||||
EXPECT_FALSE(WaitForLoadStop(shell()->web_contents()));
|
||||
// 3) Go back. If this is the main frame, then going back will get a 404.
|
||||
ASSERT_NE(HistoryGoBack(web_contents()), testing_main_frame);
|
||||
ExpectNotRestored({NotRestoredReason::kLoading}, {}, {}, {}, {}, FROM_HERE);
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
|
||||
DoesNotCacheLoadingSubframe) {
|
||||
class BackForwardCacheStillNavigatingBrowserTest
|
||||
: public BackForwardCacheBrowserTest,
|
||||
public ::testing::WithParamInterface<TestFrameType> {
|
||||
protected:
|
||||
std::string GetMainFramePath() {
|
||||
switch (GetParam()) {
|
||||
case TestFrameType::kMainFrame:
|
||||
NOTREACHED();
|
||||
return "";
|
||||
case TestFrameType::kSubFrame:
|
||||
return "/back_forward_cache/controllable_subframe.html";
|
||||
case TestFrameType::kSubFrameOfSubframe:
|
||||
return "/back_forward_cache/controllable_subframe_of_subframe.html";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(All,
|
||||
BackForwardCacheStillNavigatingBrowserTest,
|
||||
::testing::Values(TestFrameType::kSubFrame,
|
||||
TestFrameType::kSubFrameOfSubframe));
|
||||
|
||||
IN_PROC_BROWSER_TEST_P(BackForwardCacheStillNavigatingBrowserTest,
|
||||
DoesNotCacheNavigatingSubframe) {
|
||||
net::test_server::ControllableHttpResponse response(embedded_test_server(),
|
||||
"/controlled");
|
||||
ASSERT_TRUE(embedded_test_server()->Start());
|
||||
|
||||
// 1) Navigate to a page with an iframe that loads forever.
|
||||
GURL url(embedded_test_server()->GetURL(
|
||||
"a.com", "/back_forward_cache/controllable_subframe.html"));
|
||||
TestNavigationManager navigation_manager(shell()->web_contents(), url);
|
||||
GURL url(embedded_test_server()->GetURL("a.com", GetMainFramePath()));
|
||||
|
||||
shell()->LoadURL(url);
|
||||
|
||||
// The navigation finishes while the iframe is still loading.
|
||||
ASSERT_TRUE(navigation_manager.WaitForNavigationFinished());
|
||||
|
||||
// Wait for the iframe request to arrive, and leave it hanging with no
|
||||
// response.
|
||||
response.WaitForRequest();
|
||||
|
||||
RenderFrameHostImpl* rfh_a = current_frame_host();
|
||||
RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
|
||||
RenderFrameHostImplWrapper rfh_a(current_frame_host());
|
||||
// If the "DOMContentLoaded" event has not fired, it will cause BFCache to be
|
||||
// blocked. We are not
|
||||
ASSERT_EQ(42, EvalJs(rfh_a.get(), "domContentLoaded"));
|
||||
|
||||
// 2) Navigate away.
|
||||
shell()->LoadURL(embedded_test_server()->GetURL("b.com", "/title1.html"));
|
||||
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
|
||||
EXPECT_TRUE(WaitForLoadStop(web_contents()));
|
||||
|
||||
// The page should not have been added to cache, since it had a subframe that
|
||||
// was still loading at the time it was navigated away from.
|
||||
delete_observer_rfh_a.WaitUntilDeleted();
|
||||
ASSERT_TRUE(rfh_a.WaitUntilRenderFrameDeleted());
|
||||
|
||||
// 3) Go back.
|
||||
ASSERT_TRUE(HistoryGoBack(web_contents()));
|
||||
ExpectNotRestored(
|
||||
{
|
||||
NotRestoredReason::kLoading,
|
||||
NotRestoredReason::kSubframeIsNavigating,
|
||||
},
|
||||
{}, {}, {}, {}, FROM_HERE);
|
||||
ExpectNotRestored({NotRestoredReason::kSubframeIsNavigating}, {}, {}, {}, {},
|
||||
FROM_HERE);
|
||||
}
|
||||
|
||||
// Check that a frame with an invalid url doesn't affect the back-forward cache
|
||||
// usage.
|
||||
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
|
||||
DoesNotCacheLoadingSubframeOfSubframe) {
|
||||
net::test_server::ControllableHttpResponse response(embedded_test_server(),
|
||||
"/controlled");
|
||||
FrameWithInvalidURLDoesntAffectCache) {
|
||||
ASSERT_TRUE(embedded_test_server()->Start());
|
||||
|
||||
// 1) Navigate to a page with an iframe that contains yet another iframe, that
|
||||
// hangs while loading.
|
||||
GURL url(embedded_test_server()->GetURL(
|
||||
"a.com", "/back_forward_cache/controllable_subframe_of_subframe.html"));
|
||||
TestNavigationManager navigation_manager(shell()->web_contents(), url);
|
||||
shell()->LoadURL(url);
|
||||
|
||||
// The navigation finishes while the iframe within an iframe is still loading.
|
||||
ASSERT_TRUE(navigation_manager.WaitForNavigationFinished());
|
||||
|
||||
// Wait for the innermost iframe request to arrive, and leave it hanging with
|
||||
// no response.
|
||||
response.WaitForRequest();
|
||||
GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
|
||||
GURL url_b(embedded_test_server()->GetURL("b.com", "/title2.html"));
|
||||
|
||||
// 1) Navigate to A.
|
||||
EXPECT_TRUE(NavigateToURL(shell(), url_a));
|
||||
RenderFrameHostImpl* rfh_a = current_frame_host();
|
||||
RenderFrameDeletedObserver delete_rfh_a(rfh_a);
|
||||
|
||||
// 2) Navigate away.
|
||||
shell()->LoadURL(embedded_test_server()->GetURL("b.com", "/title1.html"));
|
||||
EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
|
||||
// 2) Create some subframes which have an invalid URL
|
||||
// and thus won't commit a document.
|
||||
InsertSubFrameWithUrl(rfh_a, "javascript:false");
|
||||
InsertSubFrameWithUrl(rfh_a, "blob:");
|
||||
InsertSubFrameWithUrl(rfh_a, "file:///");
|
||||
// wrongly typed scheme
|
||||
InsertSubFrameWithUrl(rfh_a, "htt://");
|
||||
for (size_t i = 0; i < rfh_a->child_count(); i++) {
|
||||
RenderFrameHostImpl* rfh_subframe =
|
||||
rfh_a->child_at(i)->current_frame_host();
|
||||
EXPECT_FALSE(rfh_subframe->IsDOMContentLoaded());
|
||||
EXPECT_FALSE(rfh_subframe->has_committed_any_navigation());
|
||||
}
|
||||
|
||||
// The page should not have been added to the cache, since it had an iframe
|
||||
// that was still loading at the time it was navigated away from.
|
||||
delete_rfh_a.WaitUntilDeleted();
|
||||
// 3) Navigate to B.
|
||||
EXPECT_TRUE(NavigateToURL(shell(), url_b));
|
||||
// The page A should be stored in the back-forward cache.
|
||||
EXPECT_TRUE(rfh_a->IsInBackForwardCache());
|
||||
|
||||
// 3) Go back.
|
||||
// 4) Go back.
|
||||
ASSERT_TRUE(HistoryGoBack(web_contents()));
|
||||
ExpectNotRestored(
|
||||
{
|
||||
NotRestoredReason::kLoading,
|
||||
NotRestoredReason::kSubframeIsNavigating,
|
||||
},
|
||||
{}, {}, {}, {}, FROM_HERE);
|
||||
|
||||
// The page A should be restored from the back-forward cache.
|
||||
ExpectRestored(FROM_HERE);
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, DoesNotCacheIfHttpError) {
|
||||
@ -998,11 +1061,6 @@ IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
|
||||
EXPECT_EQ(num_messages_received, 2);
|
||||
}
|
||||
|
||||
enum class TestFrameType {
|
||||
kMainFrame,
|
||||
kSubFrame,
|
||||
};
|
||||
|
||||
enum class StickinessType {
|
||||
kSticky,
|
||||
kNonSticky,
|
||||
|
@ -52,6 +52,12 @@ MATCHER(Deleted, "") {
|
||||
std::initializer_list<RenderFrameHostImpl*> Elements(
|
||||
std::initializer_list<RenderFrameHostImpl*> t);
|
||||
|
||||
enum class TestFrameType {
|
||||
kMainFrame,
|
||||
kSubFrame,
|
||||
kSubFrameOfSubframe,
|
||||
};
|
||||
|
||||
// Test about the BackForwardCache.
|
||||
class BackForwardCacheBrowserTest
|
||||
: public ContentBrowserTest,
|
||||
|
@ -940,8 +940,11 @@ void BackForwardCacheImpl::NotRestoredReasonBuilder::
|
||||
RenderFrameHostImpl* rfh,
|
||||
RequestedFeatures requested_features) {
|
||||
DCHECK_NE(requested_features, RequestedFeatures::kOnlySticky);
|
||||
if (!rfh->IsDOMContentLoaded())
|
||||
// The DOM content must have finished loading, except when there is
|
||||
// no DOM content to load when the RFH has not committed any navigation.
|
||||
if (!rfh->IsDOMContentLoaded() && rfh->has_committed_any_navigation()) {
|
||||
result.No(BackForwardCacheMetrics::NotRestoredReason::kLoading);
|
||||
}
|
||||
|
||||
// Check for non-sticky features that are present at the moment.
|
||||
WebSchedulerTrackedFeatures banned_features =
|
||||
|
@ -130,6 +130,7 @@
|
||||
//content/test/data/back_forward_cache/controllable_subframe_of_subframe.html
|
||||
//content/test/data/back_forward_cache/dedicated_worker_in_subframe.html
|
||||
//content/test/data/back_forward_cache/disallowed_path.html
|
||||
//content/test/data/back_forward_cache/dom_content_loaded_promise.js
|
||||
//content/test/data/back_forward_cache/empty.html
|
||||
//content/test/data/back_forward_cache/empty.js
|
||||
//content/test/data/back_forward_cache/page_with_broadcastchannel.html
|
||||
|
@ -1,2 +1,3 @@
|
||||
<!-- /controlled routes to a ControllableHttpResponse driven by the tests. -->
|
||||
<script src="dom_content_loaded_promise.js"></script>
|
||||
<iframe src="/controlled"></iframe>
|
||||
|
@ -1 +1,2 @@
|
||||
<script src="dom_content_loaded_promise.js"></script>
|
||||
<iframe src="/back_forward_cache/controllable_subframe.html"></iframe>
|
||||
|
@ -0,0 +1,8 @@
|
||||
// Copyright 2023 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Set up a promise that will be resolve when DOMContentLoaded is fired.
|
||||
var domContentLoaded = new Promise(resolve => {
|
||||
document.addEventListener("DOMContentLoaded", () => resolve(42));
|
||||
});
|
@ -1,5 +0,0 @@
|
||||
This is a testharness.js-based test.
|
||||
PASS Page with <embed type=image/png src=/404.png>
|
||||
NOTRUN Page with <object type=image/png data=/404.png> Could have been BFCached but actually wasn't
|
||||
Harness: the test ran to completion.
|
||||
|
Reference in New Issue
Block a user