summaryrefslogtreecommitdiff
path: root/chromium/content/browser/frame_host
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/content/browser/frame_host')
-rw-r--r--chromium/content/browser/frame_host/DEPS13
-rw-r--r--chromium/content/browser/frame_host/ancestor_throttle.cc169
-rw-r--r--chromium/content/browser/frame_host/ancestor_throttle.h14
-rw-r--r--chromium/content/browser/frame_host/ancestor_throttle_browsertest.cc20
-rw-r--r--chromium/content/browser/frame_host/ancestor_throttle_unittest.cc64
-rw-r--r--chromium/content/browser/frame_host/back_forward_cache_can_store_document_result.cc7
-rw-r--r--chromium/content/browser/frame_host/back_forward_cache_impl.cc22
-rw-r--r--chromium/content/browser/frame_host/back_forward_cache_metrics.h5
-rw-r--r--chromium/content/browser/frame_host/cookie_utils.cc69
-rw-r--r--chromium/content/browser/frame_host/cookie_utils.h2
-rw-r--r--chromium/content/browser/frame_host/cross_process_frame_connector.cc13
-rw-r--r--chromium/content/browser/frame_host/debug_urls.cc11
-rw-r--r--chromium/content/browser/frame_host/embedding_token_browsertest.cc232
-rw-r--r--chromium/content/browser/frame_host/file_chooser_impl.cc194
-rw-r--r--chromium/content/browser/frame_host/file_chooser_impl.h96
-rw-r--r--chromium/content/browser/frame_host/frame_navigation_entry.cc13
-rw-r--r--chromium/content/browser/frame_host/frame_navigation_entry.h6
-rw-r--r--chromium/content/browser/frame_host/frame_tree.cc19
-rw-r--r--chromium/content/browser/frame_host/frame_tree.h16
-rw-r--r--chromium/content/browser/frame_host/frame_tree_node.cc17
-rw-r--r--chromium/content/browser/frame_host/frame_tree_node.h17
-rw-r--r--chromium/content/browser/frame_host/frame_tree_node_blame_context.cc2
-rw-r--r--chromium/content/browser/frame_host/ipc_utils.cc64
-rw-r--r--chromium/content/browser/frame_host/ipc_utils.h19
-rw-r--r--chromium/content/browser/frame_host/keep_alive_handle_factory.cc6
-rw-r--r--chromium/content/browser/frame_host/mixed_content_navigation_throttle.cc3
-rw-r--r--chromium/content/browser/frame_host/navigation_controller_impl.cc456
-rw-r--r--chromium/content/browser/frame_host/navigation_controller_impl.h57
-rw-r--r--chromium/content/browser/frame_host/navigation_controller_impl_browsertest.cc599
-rw-r--r--chromium/content/browser/frame_host/navigation_controller_impl_unittest.cc422
-rw-r--r--chromium/content/browser/frame_host/navigation_entry_impl.cc54
-rw-r--r--chromium/content/browser/frame_host/navigation_entry_impl.h5
-rw-r--r--chromium/content/browser/frame_host/navigation_request.cc954
-rw-r--r--chromium/content/browser/frame_host/navigation_request.h202
-rw-r--r--chromium/content/browser/frame_host/navigation_request_browsertest.cc21
-rw-r--r--chromium/content/browser/frame_host/navigation_request_unittest.cc5
-rw-r--r--chromium/content/browser/frame_host/navigator.cc44
-rw-r--r--chromium/content/browser/frame_host/navigator.h16
-rw-r--r--chromium/content/browser/frame_host/navigator_delegate.h1
-rw-r--r--chromium/content/browser/frame_host/navigator_unittest.cc10
-rw-r--r--chromium/content/browser/frame_host/render_document_host_user_data_browsertest.cc432
-rw-r--r--chromium/content/browser/frame_host/render_frame_host_delegate.cc15
-rw-r--r--chromium/content/browser/frame_host/render_frame_host_delegate.h61
-rw-r--r--chromium/content/browser/frame_host/render_frame_host_feature_policy_unittest.cc8
-rw-r--r--chromium/content/browser/frame_host/render_frame_host_impl.cc1652
-rw-r--r--chromium/content/browser/frame_host/render_frame_host_impl.h323
-rw-r--r--chromium/content/browser/frame_host/render_frame_host_impl_browsertest.cc190
-rw-r--r--chromium/content/browser/frame_host/render_frame_host_impl_mac_browsertest.mm5
-rw-r--r--chromium/content/browser/frame_host/render_frame_host_impl_unittest.cc4
-rw-r--r--chromium/content/browser/frame_host/render_frame_host_manager.cc495
-rw-r--r--chromium/content/browser/frame_host/render_frame_host_manager.h64
-rw-r--r--chromium/content/browser/frame_host/render_frame_host_manager_browsertest.cc539
-rw-r--r--chromium/content/browser/frame_host/render_frame_host_manager_unittest.cc257
-rw-r--r--chromium/content/browser/frame_host/render_frame_message_filter.cc5
-rw-r--r--chromium/content/browser/frame_host/render_frame_message_filter_browsertest.cc21
-rw-r--r--chromium/content/browser/frame_host/render_frame_proxy_host.cc275
-rw-r--r--chromium/content/browser/frame_host/render_frame_proxy_host.h39
-rw-r--r--chromium/content/browser/frame_host/should_swap_browsing_instance.h10
58 files changed, 4975 insertions, 3379 deletions
diff --git a/chromium/content/browser/frame_host/DEPS b/chromium/content/browser/frame_host/DEPS
index 59ac961d695..6ce53ea9e8a 100644
--- a/chromium/content/browser/frame_host/DEPS
+++ b/chromium/content/browser/frame_host/DEPS
@@ -5,6 +5,7 @@ include_rules = [
"-content/public/browser/web_contents.h",
"-content/public/browser/web_contents_delegate.h",
"-content/public/browser/web_contents_view.h",
+ "-content/public/browser/web_contents_observer.h",
]
specific_include_rules = {
@@ -12,17 +13,7 @@ specific_include_rules = {
"+content/browser/web_contents",
"+content/public/browser/web_contents.h",
"+content/public/browser/web_contents_delegate.h",
- ],
- ".*file_chooser_impl\.cc": [
- # TODO(1076947): There is a layer violation to fix in this file.
- "+content/public/browser/web_contents.h",
- ],
- ".*interstitial_page_impl\.cc": [
- # TODO(nasko): This should be removed once we remove
- # WebContentsObserver as the method of telling interstitial pages to
- # clean themselves up.
- "+content/browser/web_contents",
- "+content/public/browser/web_contents_delegate.h",
+ "+content/public/browser/web_contents_observer.h",
],
"popup_menu_helper_mac.mm": [
"+content/app_shim_remote_cocoa/render_widget_host_view_cocoa.h",
diff --git a/chromium/content/browser/frame_host/ancestor_throttle.cc b/chromium/content/browser/frame_host/ancestor_throttle.cc
index 4542a24bbd3..8630ace7189 100644
--- a/chromium/content/browser/frame_host/ancestor_throttle.cc
+++ b/chromium/content/browser/frame_host/ancestor_throttle.cc
@@ -199,69 +199,8 @@ NavigationThrottle::ThrottleCheckResult AncestorThrottle::ProcessResponseImpl(
return NavigationThrottle::PROCEED;
}
- std::string header_value;
- HeaderDisposition disposition =
- ParseHeader(request->GetResponseHeaders(), &header_value);
-
- switch (disposition) {
- case HeaderDisposition::CONFLICT:
- if (logging == LoggingDisposition::LOG_TO_CONSOLE)
- ParseError(header_value, disposition);
- RecordXFrameOptionsUsage(XFrameOptionsHistogram::CONFLICT);
- return NavigationThrottle::BLOCK_RESPONSE;
-
- case HeaderDisposition::INVALID:
- if (logging == LoggingDisposition::LOG_TO_CONSOLE)
- ParseError(header_value, disposition);
- RecordXFrameOptionsUsage(XFrameOptionsHistogram::INVALID);
- // TODO(mkwst): Consider failing here.
- break;
-
- case HeaderDisposition::DENY:
- if (logging == LoggingDisposition::LOG_TO_CONSOLE)
- ConsoleError(disposition);
- RecordXFrameOptionsUsage(XFrameOptionsHistogram::DENY);
- return NavigationThrottle::BLOCK_RESPONSE;
-
- case HeaderDisposition::SAMEORIGIN: {
- // Block the request when any ancestor is not same-origin.
- RenderFrameHostImpl* parent = request->GetParentFrame();
- url::Origin current_origin =
- url::Origin::Create(navigation_handle()->GetURL());
- while (parent) {
- if (!parent->GetLastCommittedOrigin().IsSameOriginWith(
- current_origin)) {
- RecordXFrameOptionsUsage(XFrameOptionsHistogram::SAMEORIGIN_BLOCKED);
- if (logging == LoggingDisposition::LOG_TO_CONSOLE)
- ConsoleError(disposition);
-
- // TODO(mkwst): Stop recording this metric once we convince other
- // vendors to follow our lead with XFO: SAMEORIGIN processing.
- //
- // https://crbug.com/250309
- if (parent->GetMainFrame()->GetLastCommittedOrigin().IsSameOriginWith(
- current_origin)) {
- RecordXFrameOptionsUsage(
- XFrameOptionsHistogram::SAMEORIGIN_WITH_BAD_ANCESTOR_CHAIN);
- }
-
- return NavigationThrottle::BLOCK_RESPONSE;
- }
- parent = parent->GetParent();
- }
- RecordXFrameOptionsUsage(XFrameOptionsHistogram::SAMEORIGIN);
- break;
- }
-
- case HeaderDisposition::NONE:
- RecordXFrameOptionsUsage(XFrameOptionsHistogram::NONE);
- break;
- case HeaderDisposition::BYPASS:
- RecordXFrameOptionsUsage(XFrameOptionsHistogram::BYPASS);
- break;
- case HeaderDisposition::ALLOWALL:
- RecordXFrameOptionsUsage(XFrameOptionsHistogram::ALLOWALL);
- break;
+ if (EvaluateXFrameOptions(logging) == CheckResult::BLOCK) {
+ return NavigationThrottle::BLOCK_RESPONSE;
}
// X-Frame-Option is checked on both redirect and final responses. However,
@@ -269,8 +208,15 @@ NavigationThrottle::ThrottleCheckResult AncestorThrottle::ProcessResponseImpl(
if (!is_response_check)
return NavigationThrottle::PROCEED;
- return EvaluateContentSecurityPolicy(
- request->response()->parsed_headers->content_security_policy);
+ const std::vector<network::mojom::ContentSecurityPolicyPtr>&
+ content_security_policies =
+ request->response()->parsed_headers->content_security_policy;
+
+ if (EvaluateFrameAncestors(content_security_policies) == CheckResult::BLOCK) {
+ return NavigationThrottle::BLOCK_RESPONSE;
+ }
+
+ return NavigationThrottle::PROCEED;
}
const char* AncestorThrottle::GetNameForLogging() {
@@ -280,8 +226,8 @@ const char* AncestorThrottle::GetNameForLogging() {
AncestorThrottle::AncestorThrottle(NavigationHandle* handle)
: NavigationThrottle(handle) {}
-void AncestorThrottle::ParseError(const std::string& value,
- HeaderDisposition disposition) {
+void AncestorThrottle::ParseXFrameOptionsError(const std::string& value,
+ HeaderDisposition disposition) {
DCHECK(disposition == HeaderDisposition::CONFLICT ||
disposition == HeaderDisposition::INVALID);
if (!navigation_handle()->GetRenderFrameHost())
@@ -309,7 +255,8 @@ void AncestorThrottle::ParseError(const std::string& value,
blink::mojom::ConsoleMessageLevel::kError, message);
}
-void AncestorThrottle::ConsoleError(HeaderDisposition disposition) {
+void AncestorThrottle::ConsoleErrorXFrameOptions(
+ HeaderDisposition disposition) {
DCHECK(disposition == HeaderDisposition::DENY ||
disposition == HeaderDisposition::SAMEORIGIN);
if (!navigation_handle()->GetRenderFrameHost())
@@ -329,8 +276,77 @@ void AncestorThrottle::ConsoleError(HeaderDisposition disposition) {
blink::mojom::ConsoleMessageLevel::kError, message);
}
-NavigationThrottle::ThrottleAction
-AncestorThrottle::EvaluateContentSecurityPolicy(
+AncestorThrottle::CheckResult AncestorThrottle::EvaluateXFrameOptions(
+ LoggingDisposition logging) {
+ std::string header_value;
+ NavigationRequest* request = NavigationRequest::From(navigation_handle());
+ HeaderDisposition disposition =
+ ParseXFrameOptionsHeader(request->GetResponseHeaders(), &header_value);
+
+ switch (disposition) {
+ case HeaderDisposition::CONFLICT:
+ if (logging == LoggingDisposition::LOG_TO_CONSOLE)
+ ParseXFrameOptionsError(header_value, disposition);
+ RecordXFrameOptionsUsage(XFrameOptionsHistogram::CONFLICT);
+ return CheckResult::BLOCK;
+
+ case HeaderDisposition::INVALID:
+ if (logging == LoggingDisposition::LOG_TO_CONSOLE)
+ ParseXFrameOptionsError(header_value, disposition);
+ RecordXFrameOptionsUsage(XFrameOptionsHistogram::INVALID);
+ // TODO(mkwst): Consider failing here.
+ return CheckResult::PROCEED;
+
+ case HeaderDisposition::DENY:
+ if (logging == LoggingDisposition::LOG_TO_CONSOLE)
+ ConsoleErrorXFrameOptions(disposition);
+ RecordXFrameOptionsUsage(XFrameOptionsHistogram::DENY);
+ return CheckResult::BLOCK;
+
+ case HeaderDisposition::SAMEORIGIN: {
+ // Block the request when any ancestor is not same-origin.
+ RenderFrameHostImpl* parent = ParentForAncestorThrottle(
+ request->frame_tree_node()->current_frame_host());
+ url::Origin current_origin =
+ url::Origin::Create(navigation_handle()->GetURL());
+ while (parent) {
+ if (!parent->GetLastCommittedOrigin().IsSameOriginWith(
+ current_origin)) {
+ RecordXFrameOptionsUsage(XFrameOptionsHistogram::SAMEORIGIN_BLOCKED);
+ if (logging == LoggingDisposition::LOG_TO_CONSOLE)
+ ConsoleErrorXFrameOptions(disposition);
+
+ // TODO(mkwst): Stop recording this metric once we convince other
+ // vendors to follow our lead with XFO: SAMEORIGIN processing.
+ //
+ // https://crbug.com/250309
+ if (parent->GetMainFrame()->GetLastCommittedOrigin().IsSameOriginWith(
+ current_origin)) {
+ RecordXFrameOptionsUsage(
+ XFrameOptionsHistogram::SAMEORIGIN_WITH_BAD_ANCESTOR_CHAIN);
+ }
+
+ return CheckResult::BLOCK;
+ }
+ parent = ParentForAncestorThrottle(parent);
+ }
+ RecordXFrameOptionsUsage(XFrameOptionsHistogram::SAMEORIGIN);
+ return CheckResult::PROCEED;
+ }
+
+ case HeaderDisposition::NONE:
+ RecordXFrameOptionsUsage(XFrameOptionsHistogram::NONE);
+ return CheckResult::PROCEED;
+ case HeaderDisposition::BYPASS:
+ RecordXFrameOptionsUsage(XFrameOptionsHistogram::BYPASS);
+ return CheckResult::PROCEED;
+ case HeaderDisposition::ALLOWALL:
+ RecordXFrameOptionsUsage(XFrameOptionsHistogram::ALLOWALL);
+ return CheckResult::PROCEED;
+ }
+}
+
+AncestorThrottle::CheckResult AncestorThrottle::EvaluateFrameAncestors(
const std::vector<network::mojom::ContentSecurityPolicyPtr>&
content_security_policy) {
// TODO(lfg): If the initiating document is known and correspond to the
@@ -359,15 +375,15 @@ AncestorThrottle::EvaluateContentSecurityPolicy(
true /* is_response_check */, empty_source_location,
network::CSPContext::CheckCSPDisposition::CHECK_ALL_CSP,
navigation_handle()->IsFormSubmission())) {
- return NavigationThrottle::BLOCK_RESPONSE;
+ return CheckResult::BLOCK;
}
parent = ParentForAncestorThrottle(parent);
}
- return NavigationThrottle::PROCEED;
+ return CheckResult::PROCEED;
}
-AncestorThrottle::HeaderDisposition AncestorThrottle::ParseHeader(
+AncestorThrottle::HeaderDisposition AncestorThrottle::ParseXFrameOptionsHeader(
const net::HttpResponseHeaders* headers,
std::string* header_value) {
DCHECK(header_value);
@@ -411,12 +427,13 @@ AncestorThrottle::HeaderDisposition AncestorThrottle::ParseHeader(
// https://www.w3.org/TR/CSP/#frame-ancestors-and-frame-options
if (result != HeaderDisposition::NONE &&
result != HeaderDisposition::ALLOWALL &&
+ // TODO(antoniosartori): Use the already parsed CSP header instead of the
+ // raw headers here as soon as we remove
+ // network::features::kOutOfBlinkFrameAncestors
HeadersContainFrameAncestorsCSP(headers, false)) {
- // TODO(mkwst): 'frame-ancestors' is currently handled in Blink. We should
- // handle it here instead. Until then, don't block the request, and let
- // Blink handle it. https://crbug.com/555418
return HeaderDisposition::BYPASS;
}
+
return result;
}
diff --git a/chromium/content/browser/frame_host/ancestor_throttle.h b/chromium/content/browser/frame_host/ancestor_throttle.h
index 2fdd685f822..a12995a2263 100644
--- a/chromium/content/browser/frame_host/ancestor_throttle.h
+++ b/chromium/content/browser/frame_host/ancestor_throttle.h
@@ -45,6 +45,7 @@ class CONTENT_EXPORT AncestorThrottle : public NavigationThrottle {
private:
enum class LoggingDisposition { LOG_TO_CONSOLE, DO_NOT_LOG_TO_CONSOLE };
+ enum class CheckResult { BLOCK, PROCEED };
FRIEND_TEST_ALL_PREFIXES(AncestorThrottleTest, ParsingXFrameOptions);
FRIEND_TEST_ALL_PREFIXES(AncestorThrottleTest, ErrorsParsingXFrameOptions);
@@ -55,17 +56,20 @@ class CONTENT_EXPORT AncestorThrottle : public NavigationThrottle {
NavigationThrottle::ThrottleCheckResult ProcessResponseImpl(
LoggingDisposition logging,
bool is_response_check);
- void ParseError(const std::string& value, HeaderDisposition disposition);
- void ConsoleError(HeaderDisposition disposition);
- NavigationThrottle::ThrottleAction EvaluateContentSecurityPolicy(
+ void ParseXFrameOptionsError(const std::string& value,
+ HeaderDisposition disposition);
+ void ConsoleErrorXFrameOptions(HeaderDisposition disposition);
+ CheckResult EvaluateXFrameOptions(LoggingDisposition logging);
+ CheckResult EvaluateFrameAncestors(
const std::vector<network::mojom::ContentSecurityPolicyPtr>&
content_security_policy);
// Parses an 'X-Frame-Options' header. If the result is either CONFLICT
// or INVALID, |header_value| will be populated with the value which caused
// the parse error.
- HeaderDisposition ParseHeader(const net::HttpResponseHeaders* headers,
- std::string* header_value);
+ HeaderDisposition ParseXFrameOptionsHeader(
+ const net::HttpResponseHeaders* headers,
+ std::string* header_value);
DISALLOW_COPY_AND_ASSIGN(AncestorThrottle);
};
diff --git a/chromium/content/browser/frame_host/ancestor_throttle_browsertest.cc b/chromium/content/browser/frame_host/ancestor_throttle_browsertest.cc
index 1cfaabc82c7..aa917ef6fb9 100644
--- a/chromium/content/browser/frame_host/ancestor_throttle_browsertest.cc
+++ b/chromium/content/browser/frame_host/ancestor_throttle_browsertest.cc
@@ -31,12 +31,7 @@ namespace {
class AncestorThrottleTest : public ContentBrowserTest,
public ::testing::WithParamInterface<bool> {
public:
- AncestorThrottleTest() {
- if (GetParam()) {
- feature_list_.InitAndEnableFeature(
- network::features::kOutOfBlinkFrameAncestors);
- }
- }
+ AncestorThrottleTest() = default;
protected:
void SetUpOnMainThread() override {
@@ -49,7 +44,7 @@ class AncestorThrottleTest : public ContentBrowserTest,
};
// Tests that an iframe navigation with frame-anecestors 'none' is blocked.
-IN_PROC_BROWSER_TEST_P(AncestorThrottleTest, FailedCSP) {
+IN_PROC_BROWSER_TEST_F(AncestorThrottleTest, FailedCSP) {
GURL parent_url(
embedded_test_server()->GetURL("foo.com", "/page_with_iframe.html"));
GURL iframe_url(
@@ -73,7 +68,7 @@ IN_PROC_BROWSER_TEST_P(AncestorThrottleTest, FailedCSP) {
// Tests that redirecting on a forbidden frame-ancestors will still commit if
// the final response does not have a CSP policy that prevents the navigation.
-IN_PROC_BROWSER_TEST_P(AncestorThrottleTest, RedirectCommitsIfNoCSP) {
+IN_PROC_BROWSER_TEST_F(AncestorThrottleTest, RedirectCommitsIfNoCSP) {
GURL parent_url(
embedded_test_server()->GetURL("foo.com", "/page_with_iframe.html"));
GURL iframe_url(
@@ -98,7 +93,7 @@ IN_PROC_BROWSER_TEST_P(AncestorThrottleTest, RedirectCommitsIfNoCSP) {
// Tests that redirecting on a forbidden frame-ancestors will not commit if
// the final response does have a CSP policy that prevents the navigation.
-IN_PROC_BROWSER_TEST_P(AncestorThrottleTest, RedirectFails) {
+IN_PROC_BROWSER_TEST_F(AncestorThrottleTest, RedirectFails) {
GURL parent_url(
embedded_test_server()->GetURL("foo.com", "/page_with_iframe.html"));
GURL iframe_url(embedded_test_server()->GetURL(
@@ -121,7 +116,7 @@ IN_PROC_BROWSER_TEST_P(AncestorThrottleTest, RedirectFails) {
}
// Check that we don't process CSP for 204 responses.
-IN_PROC_BROWSER_TEST_P(AncestorThrottleTest, Response204CSP) {
+IN_PROC_BROWSER_TEST_F(AncestorThrottleTest, Response204CSP) {
GURL parent_url(
embedded_test_server()->GetURL("foo.com", "/page_with_iframe.html"));
GURL iframe_url(embedded_test_server()->GetURL(
@@ -170,7 +165,7 @@ class AncestorThrottleSXGTest : public AncestorThrottleTest {
SignedExchangeBrowserTestHelper sxg_test_helper_;
};
-IN_PROC_BROWSER_TEST_P(AncestorThrottleSXGTest, SXGWithCSP) {
+IN_PROC_BROWSER_TEST_F(AncestorThrottleSXGTest, SXGWithCSP) {
sxg_test_helper_.InstallMockCert(mock_cert_verifier_.mock_cert_verifier());
sxg_test_helper_.InstallMockCertChainInterceptor();
@@ -188,9 +183,6 @@ IN_PROC_BROWSER_TEST_P(AncestorThrottleSXGTest, SXGWithCSP) {
iframe_node->current_frame_host()->GetLastCommittedOrigin().opaque());
}
-INSTANTIATE_TEST_SUITE_P(All, AncestorThrottleTest, ::testing::Bool());
-INSTANTIATE_TEST_SUITE_P(All, AncestorThrottleSXGTest, ::testing::Bool());
-
} // namespace
} // namespace content
diff --git a/chromium/content/browser/frame_host/ancestor_throttle_unittest.cc b/chromium/content/browser/frame_host/ancestor_throttle_unittest.cc
index 605d8a4e207..0acf57b221f 100644
--- a/chromium/content/browser/frame_host/ancestor_throttle_unittest.cc
+++ b/chromium/content/browser/frame_host/ancestor_throttle_unittest.cc
@@ -89,7 +89,7 @@ TEST_F(AncestorThrottleTest, ParsingXFrameOptions) {
GetAncestorHeaders(test.header, nullptr);
std::string header_value;
EXPECT_EQ(test.expected,
- throttle.ParseHeader(headers.get(), &header_value));
+ throttle.ParseXFrameOptionsHeader(headers.get(), &header_value));
EXPECT_EQ(test.value, header_value);
}
}
@@ -124,69 +124,9 @@ TEST_F(AncestorThrottleTest, ErrorsParsingXFrameOptions) {
GetAncestorHeaders(test.header, nullptr);
std::string header_value;
EXPECT_EQ(test.expected,
- throttle.ParseHeader(headers.get(), &header_value));
+ throttle.ParseXFrameOptionsHeader(headers.get(), &header_value));
EXPECT_EQ(test.failure, header_value);
}
}
-TEST_F(AncestorThrottleTest, IgnoreWhenFrameAncestorsPresent) {
- // When OutOfBlinkFrameAncestors is enabled frame-ancestors is processed in
- // the AncestorThrottle and XFO will not be parsed.
- if (base::FeatureList::IsEnabled(
- network::features::kOutOfBlinkFrameAncestors)) {
- return;
- }
-
- struct TestCase {
- const char* csp;
- AncestorThrottle::HeaderDisposition expected;
- } cases[] = {
- {"", HeaderDisposition::DENY},
- {"frame-ancestors 'none'", HeaderDisposition::BYPASS},
- {"frame-ancestors *", HeaderDisposition::BYPASS},
- {"frame-ancestors 'self'", HeaderDisposition::BYPASS},
- {"frame-ancestors https://example.com", HeaderDisposition::BYPASS},
- {"fRaMe-AnCeStOrS *", HeaderDisposition::BYPASS},
- {"directive1; frame-ancestors 'none'", HeaderDisposition::BYPASS},
- {"directive1; frame-ancestors *", HeaderDisposition::BYPASS},
- {"directive1; frame-ancestors 'self'", HeaderDisposition::BYPASS},
- {"directive1; frame-ancestors https://example.com",
- HeaderDisposition::BYPASS},
- {"directive1; fRaMe-AnCeStOrS *", HeaderDisposition::BYPASS},
- {"policy, frame-ancestors 'none'", HeaderDisposition::BYPASS},
- {"policy, frame-ancestors *", HeaderDisposition::BYPASS},
- {"policy, frame-ancestors 'self'", HeaderDisposition::BYPASS},
- {"policy, frame-ancestors https://example.com",
- HeaderDisposition::BYPASS},
- {"policy, frame-ancestors 'none'", HeaderDisposition::BYPASS},
- {"policy, directive1; frame-ancestors *", HeaderDisposition::BYPASS},
- {"policy, directive1; frame-ancestors 'self'", HeaderDisposition::BYPASS},
- {"policy, directive1; frame-ancestors https://example.com",
- HeaderDisposition::BYPASS},
- {"policy, directive1; fRaMe-AnCeStOrS *", HeaderDisposition::BYPASS},
- {"policy, directive1; fRaMe-AnCeStOrS *", HeaderDisposition::BYPASS},
-
- {"not-frame-ancestors *", HeaderDisposition::DENY},
- {"frame-ancestors-are-lovely", HeaderDisposition::DENY},
- {"directive1; not-frame-ancestors *", HeaderDisposition::DENY},
- {"directive1; frame-ancestors-are-lovely", HeaderDisposition::DENY},
- {"policy, not-frame-ancestors *", HeaderDisposition::DENY},
- {"policy, frame-ancestors-are-lovely", HeaderDisposition::DENY},
- {"policy, directive1; not-frame-ancestors *", HeaderDisposition::DENY},
- {"policy, directive1; frame-ancestors-are-lovely",
- HeaderDisposition::DENY},
- };
-
- AncestorThrottle throttle(nullptr);
- for (const auto& test : cases) {
- SCOPED_TRACE(test.csp);
- scoped_refptr<net::HttpResponseHeaders> headers =
- GetAncestorHeaders("DENY", test.csp);
- std::string header_value;
- EXPECT_EQ(test.expected,
- throttle.ParseHeader(headers.get(), &header_value));
- EXPECT_EQ("DENY", header_value);
- }
-}
-
} // namespace content
diff --git a/chromium/content/browser/frame_host/back_forward_cache_can_store_document_result.cc b/chromium/content/browser/frame_host/back_forward_cache_can_store_document_result.cc
index 9775b3c38d4..32c21da74e6 100644
--- a/chromium/content/browser/frame_host/back_forward_cache_can_store_document_result.cc
+++ b/chromium/content/browser/frame_host/back_forward_cache_can_store_document_result.cc
@@ -118,6 +118,13 @@ std::string BackForwardCacheCanStoreDocumentResult::NotRestoredReasonToString(
return "navigation entry is not the most recent one for this document";
case Reason::kServiceWorkerClaim:
return "service worker claim is called";
+ case Reason::kIgnoreEventAndEvict:
+ return "IsInactiveAndDisallowReactivation() was called for the frame in "
+ "bfcache";
+ case Reason::kHaveInnerContents:
+ return "RenderFrameHost has inner WebContents attached";
+ case Reason::kTimeoutPuttingInCache:
+ return "Timed out while waiting for page to acknowledge freezing";
}
}
diff --git a/chromium/content/browser/frame_host/back_forward_cache_impl.cc b/chromium/content/browser/frame_host/back_forward_cache_impl.cc
index 1120a52463d..aa0d3ab525c 100644
--- a/chromium/content/browser/frame_host/back_forward_cache_impl.cc
+++ b/chromium/content/browser/frame_host/back_forward_cache_impl.cc
@@ -17,6 +17,7 @@
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/common/content_navigation_policy.h"
#include "content/common/page_messages.h"
+#include "content/public/browser/browser_thread.h"
#include "content/public/browser/visibility.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_status_code.h"
@@ -152,7 +153,13 @@ uint64_t GetDisallowedFeatures(RenderFrameHostImpl* rfh) {
FeatureToBit(WebSchedulerTrackedFeature::kAppBanner) |
FeatureToBit(WebSchedulerTrackedFeature::kPrinting) |
FeatureToBit(WebSchedulerTrackedFeature::kWebDatabase) |
- FeatureToBit(WebSchedulerTrackedFeature::kPictureInPicture);
+ FeatureToBit(WebSchedulerTrackedFeature::kPictureInPicture) |
+ FeatureToBit(WebSchedulerTrackedFeature::kPortal) |
+ FeatureToBit(WebSchedulerTrackedFeature::kSpeechRecognizer) |
+ FeatureToBit(WebSchedulerTrackedFeature::kIdleManager) |
+ FeatureToBit(WebSchedulerTrackedFeature::kPaymentManager) |
+ FeatureToBit(WebSchedulerTrackedFeature::kSpeechSynthesis) |
+ FeatureToBit(WebSchedulerTrackedFeature::kKeyboardLock);
uint64_t result = kAlwaysDisallowedFeatures;
@@ -248,7 +255,6 @@ void RequestRecordTimeToVisible(RenderFrameHostImpl* rfh,
if (rfh->delegate()->GetVisibility() != Visibility::HIDDEN) {
rfh->GetView()->SetRecordContentToVisibleTimeRequest(
navigation_start, base::Optional<bool>() /* destination_is_loaded */,
- base::Optional<bool>() /* destination_is_frozen */,
false /* show_reason_tab_switching */,
false /* show_reason_unoccluded */,
true /* show_reason_bfcache_restore */);
@@ -333,8 +339,8 @@ BackForwardCacheCanStoreDocumentResult BackForwardCacheImpl::CanStoreDocument(
if (rfh->last_http_method() != net::HttpRequestHeaders::kGetMethod)
result.No(BackForwardCacheMetrics::NotRestoredReason::kHTTPMethodNotGET);
- // Do not store main document with non HTTP/HTTPS URL scheme. In particular,
- // this excludes the new tab page.
+ // Do not store main document with non HTTP/HTTPS URL scheme. Among other
+ // things, this excludes the new tab page and all WebUI pages.
if (!rfh->GetLastCommittedURL().SchemeIsHTTPOrHTTPS()) {
result.No(
BackForwardCacheMetrics::NotRestoredReason::kSchemeNotHTTPOrHTTPS);
@@ -391,6 +397,10 @@ void BackForwardCacheImpl::CanStoreRenderFrameHost(
BackForwardCacheMetrics::NotRestoredReason::kSubframeIsNavigating);
}
+ // Do not store documents if they have inner WebContents.
+ if (rfh->IsOuterDelegateFrame())
+ result->No(BackForwardCacheMetrics::NotRestoredReason::kHaveInnerContents);
+
for (size_t i = 0; i < rfh->child_count(); i++)
CanStoreRenderFrameHost(result, rfh->child_at(i)->current_frame_host());
}
@@ -492,8 +502,8 @@ void BackForwardCacheImpl::EvictFramesInRelatedSiteInstances(
}
void BackForwardCacheImpl::PostTaskToDestroyEvictedFrames() {
- base::PostTask(FROM_HERE, {BrowserThread::UI},
- base::BindOnce(&BackForwardCacheImpl::DestroyEvictedFrames,
+ GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE, base::BindOnce(&BackForwardCacheImpl::DestroyEvictedFrames,
weak_factory_.GetWeakPtr()));
}
diff --git a/chromium/content/browser/frame_host/back_forward_cache_metrics.h b/chromium/content/browser/frame_host/back_forward_cache_metrics.h
index 9c595fd2f52..16c4319bba2 100644
--- a/chromium/content/browser/frame_host/back_forward_cache_metrics.h
+++ b/chromium/content/browser/frame_host/back_forward_cache_metrics.h
@@ -72,7 +72,10 @@ class BackForwardCacheMetrics
kRenderFrameHostReused_CrossSite = 28,
kNotMostRecentNavigationEntry = 29,
kServiceWorkerClaim = 30,
- kMaxValue = kServiceWorkerClaim,
+ kIgnoreEventAndEvict = 31,
+ kHaveInnerContents = 32,
+ kTimeoutPuttingInCache = 33,
+ kMaxValue = kTimeoutPuttingInCache,
};
using NotRestoredReasons =
diff --git a/chromium/content/browser/frame_host/cookie_utils.cc b/chromium/content/browser/frame_host/cookie_utils.cc
index 6896e4ccdf8..c1ed5558c2c 100644
--- a/chromium/content/browser/frame_host/cookie_utils.cc
+++ b/chromium/content/browser/frame_host/cookie_utils.cc
@@ -10,19 +10,17 @@
#include "content/public/browser/browser_context.h"
#include "content/public/browser/cookie_access_details.h"
#include "content/public/common/content_client.h"
-#include "content/public/common/content_features.h"
-#include "net/cookies/cookie_util.h"
+#include "net/cookies/cookie_inclusion_status.h"
#include "services/metrics/public/cpp/ukm_builders.h"
namespace content {
namespace {
-void RecordContextDowngradeUKM(
- RenderFrameHost* rfh,
- CookieAccessDetails::Type access_type,
- const net::CanonicalCookie::CookieInclusionStatus& status,
- const GURL& url) {
+void RecordContextDowngradeUKM(RenderFrameHost* rfh,
+ CookieAccessDetails::Type access_type,
+ const net::CookieInclusionStatus& status,
+ const GURL& url) {
DCHECK(rfh);
ukm::SourceId source_id = rfh->GetPageUkmSourceId();
@@ -59,8 +57,7 @@ void SplitCookiesIntoAllowedAndBlocked(
for (auto& cookie_and_status : cookie_details->cookie_list) {
if (cookie_and_status.status.HasExclusionReason(
- net::CanonicalCookie::CookieInclusionStatus::
- EXCLUDE_USER_PREFERENCES)) {
+ net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES)) {
blocked->cookie_list.push_back(std::move(cookie_and_status.cookie));
} else if (cookie_and_status.status.IsInclude()) {
allowed->cookie_list.push_back(std::move(cookie_and_status.cookie));
@@ -78,53 +75,33 @@ void EmitSameSiteCookiesDeprecationWarning(
bool samesite_treated_as_lax_cookies = false;
bool samesite_none_insecure_cookies = false;
-
- bool messages_disabled_by_cmdline =
- base::FeatureList::GetInstance()->IsFeatureOverriddenFromCommandLine(
- features::kCookieDeprecationMessages.name,
- base::FeatureList::OVERRIDE_DISABLE_FEATURE);
-
bool breaking_context_downgrade = false;
for (const net::CookieWithStatus& excluded_cookie :
cookie_details->cookie_list) {
- std::string cookie_url =
- net::cookie_util::CookieOriginToURL(excluded_cookie.cookie.Domain(),
- excluded_cookie.cookie.IsSecure())
- .possibly_invalid_spec();
-
if (excluded_cookie.status.ShouldWarn()) {
- if (excluded_cookie.status.HasWarningReason(
- net::CanonicalCookie::CookieInclusionStatus::
- WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT)) {
- samesite_treated_as_lax_cookies = true;
- }
-
- if (excluded_cookie.status.HasWarningReason(
- net::CanonicalCookie::CookieInclusionStatus::
- WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE)) {
- samesite_treated_as_lax_cookies = true;
- }
-
- if (excluded_cookie.status.HasWarningReason(
- net::CanonicalCookie::CookieInclusionStatus::
- WARN_SAMESITE_NONE_INSECURE)) {
- samesite_none_insecure_cookies = true;
- }
+ samesite_treated_as_lax_cookies =
+ samesite_treated_as_lax_cookies ||
+ excluded_cookie.status.HasWarningReason(
+ net::CookieInclusionStatus::
+ WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT) ||
+ excluded_cookie.status.HasWarningReason(
+ net::CookieInclusionStatus::
+ WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE);
+
+ samesite_none_insecure_cookies =
+ samesite_none_insecure_cookies ||
+ excluded_cookie.status.HasWarningReason(
+ net::CookieInclusionStatus::WARN_SAMESITE_NONE_INSECURE);
+
devtools_instrumentation::ReportSameSiteCookieIssue(
root_frame_host, excluded_cookie, cookie_details->url,
cookie_details->site_for_cookies,
cookie_details->type == CookieAccessDetails::Type::kRead
- ? blink::mojom::SameSiteCookieOperation::ReadCookie
- : blink::mojom::SameSiteCookieOperation::SetCookie,
+ ? blink::mojom::SameSiteCookieOperation::kReadCookie
+ : blink::mojom::SameSiteCookieOperation::kSetCookie,
cookie_details->devtools_request_id);
}
- if (!messages_disabled_by_cmdline) {
- root_frame_host->AddSameSiteCookieDeprecationMessage(
- cookie_url, excluded_cookie.status,
- net::cookie_util::IsSameSiteByDefaultCookiesEnabled(),
- net::cookie_util::IsCookiesWithoutSameSiteMustBeSecureEnabled());
- }
breaking_context_downgrade = breaking_context_downgrade ||
excluded_cookie.status.HasDowngradeWarning();
@@ -136,8 +113,6 @@ void EmitSameSiteCookiesDeprecationWarning(
}
}
- // TODO(crbug.com/990439): Do we need separate UseCounter metrics for
- // Lax-allow-unsafe? We already have histograms in CanonicalCookie.
if (samesite_treated_as_lax_cookies) {
GetContentClient()->browser()->LogWebFeatureForCurrentPage(
rfh, blink::mojom::WebFeature::kCookieNoSameSite);
diff --git a/chromium/content/browser/frame_host/cookie_utils.h b/chromium/content/browser/frame_host/cookie_utils.h
index 09042230acc..f578cf59c22 100644
--- a/chromium/content/browser/frame_host/cookie_utils.h
+++ b/chromium/content/browser/frame_host/cookie_utils.h
@@ -17,6 +17,8 @@ void SplitCookiesIntoAllowedAndBlocked(
CookieAccessDetails* allowed,
CookieAccessDetails* blocked);
+// Logs SameSite cookie warnings to DevTools Issues Panel and logs event to
+// UseCounters and UKM. Does not log to the JS console.
// TODO(crbug.com/977040): Remove when no longer needed.
void EmitSameSiteCookiesDeprecationWarning(
RenderFrameHostImpl* rfh,
diff --git a/chromium/content/browser/frame_host/cross_process_frame_connector.cc b/chromium/content/browser/frame_host/cross_process_frame_connector.cc
index babd12facb8..385b190c5bf 100644
--- a/chromium/content/browser/frame_host/cross_process_frame_connector.cc
+++ b/chromium/content/browser/frame_host/cross_process_frame_connector.cc
@@ -48,12 +48,13 @@ CrossProcessFrameConnector::CrossProcessFrameConnector(
RenderFrameProxyHost* frame_proxy_in_parent_renderer)
: FrameConnectorDelegate(IsUseZoomForDSFEnabled()),
frame_proxy_in_parent_renderer_(frame_proxy_in_parent_renderer) {
- // At this point, SetView() has not been called and so the associated RenderWidgetHost doesn't
- // have a view yet. That means calling GetScreenInfo() on the associated RenderWidgetHost will
- // just default to the primary display, which may not be appropriate. So instead we call
- // GetScreenInfo() on the root RenderWidgetHost, which will be guaranteed to be on the correct
- // display. All subsequent updates to |screen_info_| ultimately come from the root, so it makes
- // sense to do it here as well.
+ // At this point, SetView() has not been called and so the associated
+ // RenderWidgetHost doesn't have a view yet. That means calling
+ // GetScreenInfo() on the associated RenderWidgetHost will just default to the
+ // primary display, which may not be appropriate. So instead we call
+ // GetScreenInfo() on the root RenderWidgetHost, which will be guaranteed to
+ // be on the correct display. All subsequent updates to |screen_info_|
+ // ultimately come from the root, so it makes sense to do it here as well.
RootRenderFrameHost(current_child_frame_host())
->GetRenderWidgetHost()
->GetScreenInfo(&screen_info_);
diff --git a/chromium/content/browser/frame_host/debug_urls.cc b/chromium/content/browser/frame_host/debug_urls.cc
index 3d2441c30e0..284233157e8 100644
--- a/chromium/content/browser/frame_host/debug_urls.cc
+++ b/chromium/content/browser/frame_host/debug_urls.cc
@@ -14,7 +14,6 @@
#include "base/sanitizer_buildflags.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/waitable_event.h"
-#include "base/task/post_task.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "cc/base/switches.h"
@@ -166,9 +165,9 @@ bool HandleDebugURL(const GURL& url,
if (url == kChromeUIDelayedBrowserUIHang) {
// Webdriver-safe url to hang the ui thread. Webdriver waits for the onload
// event in javascript which needs a little more time to fire.
- base::PostDelayedTask(FROM_HERE, {BrowserThread::UI},
- base::BindOnce(&HangCurrentThread),
- base::TimeDelta::FromSeconds(2));
+ GetUIThreadTaskRunner({})->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&HangCurrentThread),
+ base::TimeDelta::FromSeconds(2));
return true;
}
@@ -215,8 +214,8 @@ bool HandleDebugURL(const GURL& url,
}
if (url == kChromeUIPpapiFlashCrashURL || url == kChromeUIPpapiFlashHangURL) {
- base::PostTask(FROM_HERE, {BrowserThread::IO},
- base::BindOnce(&HandlePpapiFlashDebugURL, url));
+ GetIOThreadTaskRunner({})->PostTask(
+ FROM_HERE, base::BindOnce(&HandlePpapiFlashDebugURL, url));
return true;
}
diff --git a/chromium/content/browser/frame_host/embedding_token_browsertest.cc b/chromium/content/browser/frame_host/embedding_token_browsertest.cc
index e371a638fb9..103641833aa 100644
--- a/chromium/content/browser/frame_host/embedding_token_browsertest.cc
+++ b/chromium/content/browser/frame_host/embedding_token_browsertest.cc
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/test/scoped_feature_list.h"
#include "base/unguessable_token.h"
#include "content/browser/frame_host/frame_tree.h"
#include "content/browser/web_contents/web_contents_impl.h"
@@ -24,6 +25,14 @@ class EmbeddingTokenBrowserTest : public ContentBrowserTest {
EmbeddingTokenBrowserTest() = default;
void SetUpCommandLine(base::CommandLine* command_line) override {
+ scoped_feature_list_.InitAndEnableFeatureWithParameters(
+ features::kBackForwardCache,
+ {
+ // Set a very long TTL before expiration (longer than the test
+ // timeout) so tests that are expecting deletion don't pass when
+ // they shouldn't.
+ {"TimeToLiveInBackForwardCacheInSeconds", "3600"},
+ });
ContentBrowserTest::SetUpCommandLine(command_line);
IsolateAllSitesForTesting(command_line);
}
@@ -45,37 +54,41 @@ class EmbeddingTokenBrowserTest : public ContentBrowserTest {
delete;
private:
- FrameTreeVisualizer visualizer_;
+ base::test::ScopedFeatureList scoped_feature_list_;
};
-IN_PROC_BROWSER_TEST_F(EmbeddingTokenBrowserTest, NoEmbeddingTokenOnMainFrame) {
+IN_PROC_BROWSER_TEST_F(EmbeddingTokenBrowserTest, EmbeddingTokenOnMainFrame) {
GURL a_url = embedded_test_server()->GetURL("a.com", "/site_isolation/");
GURL b_url = embedded_test_server()->GetURL("b.com", "/site_isolation/");
// Starts without an embedding token.
EXPECT_FALSE(top_frame_host()->GetEmbeddingToken().has_value());
- // Embedding tokens don't get added to the main frame.
+ // Embedding tokens should get added to the main frame.
EXPECT_TRUE(NavigateToURL(shell(), a_url.Resolve("blank.html")));
- EXPECT_FALSE(top_frame_host()->GetEmbeddingToken().has_value());
+ EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
+ auto first_token = top_frame_host()->GetEmbeddingToken().value();
EXPECT_TRUE(NavigateToURL(shell(), b_url.Resolve("blank.html")));
- EXPECT_FALSE(top_frame_host()->GetEmbeddingToken().has_value());
+ EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
+ EXPECT_NE(top_frame_host()->GetEmbeddingToken().value(), first_token);
}
IN_PROC_BROWSER_TEST_F(EmbeddingTokenBrowserTest,
- EmbeddingTokensAddedToCrossSiteIFrames) {
+ EmbeddingTokensAddedToCrossDocumentIFrames) {
EXPECT_TRUE(NavigateToURL(
shell(), embedded_test_server()->GetURL(
"a.com", "/cross_site_iframe_factory.html?a(b(a),c,a)")));
ASSERT_EQ(3U, top_frame_host()->child_count());
- EXPECT_FALSE(top_frame_host()->GetEmbeddingToken().has_value());
+ EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
+ auto top_token = top_frame_host()->GetEmbeddingToken().value();
// Child 0 (b) should have an embedding token.
auto child_0_token =
top_frame_host()->child_at(0)->current_frame_host()->GetEmbeddingToken();
ASSERT_TRUE(child_0_token.has_value());
EXPECT_NE(base::UnguessableToken::Null(), child_0_token);
+ EXPECT_NE(top_token, child_0_token);
// Child 0 (a) of Child 0 (b) should have an embedding token.
ASSERT_EQ(1U, top_frame_host()->child_at(0)->child_count());
@@ -86,6 +99,7 @@ IN_PROC_BROWSER_TEST_F(EmbeddingTokenBrowserTest,
->GetEmbeddingToken();
ASSERT_TRUE(child_0_0_token.has_value());
EXPECT_NE(base::UnguessableToken::Null(), child_0_0_token);
+ EXPECT_NE(top_token, child_0_0_token);
EXPECT_NE(child_0_token, child_0_0_token);
// Child 1 (c) should have an embedding token.
@@ -93,34 +107,51 @@ IN_PROC_BROWSER_TEST_F(EmbeddingTokenBrowserTest,
top_frame_host()->child_at(1)->current_frame_host()->GetEmbeddingToken();
ASSERT_TRUE(child_1_token.has_value());
EXPECT_NE(base::UnguessableToken::Null(), child_1_token);
+ EXPECT_NE(top_token, child_1_token);
EXPECT_NE(child_0_token, child_1_token);
EXPECT_NE(child_0_0_token, child_1_token);
- // Child 2 (a) shouldn't have an embedding token as it is same site.
- EXPECT_FALSE(top_frame_host()
- ->child_at(2)
- ->current_frame_host()
- ->GetEmbeddingToken()
- .has_value());
+ // Child 2 (a) should have an embedding token.
+ auto child_2_token =
+ top_frame_host()->child_at(2)->current_frame_host()->GetEmbeddingToken();
+ ASSERT_TRUE(child_2_token.has_value());
+ EXPECT_NE(base::UnguessableToken::Null(), child_2_token);
+ EXPECT_NE(top_token, child_2_token);
+ EXPECT_NE(child_0_token, child_2_token);
+ EXPECT_NE(child_0_0_token, child_2_token);
// TODO(ckitagawa): Somehow assert that the parent and child have matching
// embedding tokens in parent HTMLOwnerElement and child LocalFrame.
}
IN_PROC_BROWSER_TEST_F(EmbeddingTokenBrowserTest,
- EmbeddingTokensSwapOnCrossSiteNavigations) {
+ EmbeddingTokenSwapsOnCrossDocumentNavigation) {
EXPECT_TRUE(NavigateToURL(
shell(), embedded_test_server()->GetURL(
"a.com", "/cross_site_iframe_factory.html?a(b)")));
ASSERT_EQ(1U, top_frame_host()->child_count());
- EXPECT_FALSE(top_frame_host()->GetEmbeddingToken().has_value());
+ EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
+ auto top_token = top_frame_host()->GetEmbeddingToken().value();
// Child 0 (b) should have an embedding token.
RenderFrameHost* target = top_frame_host()->child_at(0)->current_frame_host();
auto child_0_token = target->GetEmbeddingToken();
ASSERT_TRUE(child_0_token.has_value());
EXPECT_NE(base::UnguessableToken::Null(), child_0_token);
+ EXPECT_NE(top_token, child_0_token);
+
+ // Navigate child 0 (b) to same-site the token should swap.
+ NavigateIframeToURL(shell()->web_contents(), "child-0",
+ embedded_test_server()
+ ->GetURL("b.com", "/site_isolation/")
+ .Resolve("blank.html"));
+ auto same_site_new_child_0_token =
+ top_frame_host()->child_at(0)->current_frame_host()->GetEmbeddingToken();
+ ASSERT_TRUE(same_site_new_child_0_token.has_value());
+ EXPECT_NE(base::UnguessableToken::Null(), same_site_new_child_0_token);
+ EXPECT_NE(top_token, same_site_new_child_0_token);
+ EXPECT_NE(child_0_token, same_site_new_child_0_token);
// Navigate child 0 (b) to another site (cross-process) the token should swap.
{
@@ -131,24 +162,36 @@ IN_PROC_BROWSER_TEST_F(EmbeddingTokenBrowserTest,
.Resolve("blank.html"));
deleted_observer.WaitUntilDeleted();
}
- auto new_child_0_token =
+ auto new_site_child_0_token =
top_frame_host()->child_at(0)->current_frame_host()->GetEmbeddingToken();
- ASSERT_TRUE(new_child_0_token.has_value());
- EXPECT_NE(base::UnguessableToken::Null(), new_child_0_token);
- EXPECT_NE(child_0_token, new_child_0_token);
+ ASSERT_TRUE(same_site_new_child_0_token.has_value());
+ EXPECT_NE(base::UnguessableToken::Null(), new_site_child_0_token);
+ EXPECT_NE(top_token, new_site_child_0_token);
+ EXPECT_NE(child_0_token, new_site_child_0_token);
+ EXPECT_NE(same_site_new_child_0_token, new_site_child_0_token);
// TODO(ckitagawa): Somehow assert that the parent and child have matching
// embedding tokens in parent HTMLOwnerElement and child LocalFrame.
}
-IN_PROC_BROWSER_TEST_F(EmbeddingTokenBrowserTest,
- EmbeddingTokensDoNotSwapOnSameSiteNavigations) {
+IN_PROC_BROWSER_TEST_F(
+ EmbeddingTokenBrowserTest,
+ EmbeddingTokenNotChangedOnSubframeSameDocumentNavigation) {
EXPECT_TRUE(NavigateToURL(
shell(), embedded_test_server()->GetURL(
"a.com", "/cross_site_iframe_factory.html?a(a)")));
ASSERT_EQ(1U, top_frame_host()->child_count());
- EXPECT_FALSE(top_frame_host()->GetEmbeddingToken().has_value());
+ EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
+ auto top_token = top_frame_host()->GetEmbeddingToken().value();
+
+ // Child 0 (a) should have an embedding token.
+ RenderFrameHost* target = top_frame_host()->child_at(0)->current_frame_host();
+ auto child_0_token = target->GetEmbeddingToken();
+ ASSERT_TRUE(child_0_token.has_value());
+ EXPECT_NE(base::UnguessableToken::Null(), child_0_token);
+ EXPECT_NE(top_token, child_0_token);
+
auto b_url = embedded_test_server()->GetURL("b.com", "/site_isolation/");
// Navigate child 0 to another site (cross-process) a token should be created.
{
@@ -158,94 +201,137 @@ IN_PROC_BROWSER_TEST_F(EmbeddingTokenBrowserTest,
deleted_observer.WaitUntilDeleted();
}
- // Child 0 (b) should have an embedding token.
- auto child_0_token =
+ // Child 0 (b) should have a new embedding token.
+ auto new_child_0_token =
top_frame_host()->child_at(0)->current_frame_host()->GetEmbeddingToken();
ASSERT_TRUE(child_0_token.has_value());
- EXPECT_NE(base::UnguessableToken::Null(), child_0_token);
+ EXPECT_NE(base::UnguessableToken::Null(), new_child_0_token);
+ EXPECT_NE(top_token, new_child_0_token);
+ EXPECT_NE(child_0_token, new_child_0_token);
- // Navigate child 0 (b) to same origin the token should not swap.
- NavigateIframeToURL(web_contents(), "child-0", b_url.Resolve("valid.html"));
- auto new_child_0_token =
+ // Navigate child 0 (b) to same document the token should not swap.
+ NavigateIframeToURL(web_contents(), "child-0",
+ b_url.Resolve("blank.html#foo"));
+ auto same_document_new_child_0_token =
top_frame_host()->child_at(0)->current_frame_host()->GetEmbeddingToken();
- ASSERT_TRUE(new_child_0_token.has_value());
- // If we are creating a new frame even for same-site navigations then we would
- // expect a different token.
- if (CreateNewHostForSameSiteSubframe()) {
- EXPECT_NE(child_0_token, new_child_0_token);
- } else {
- EXPECT_EQ(child_0_token, new_child_0_token);
- }
+ ASSERT_TRUE(same_document_new_child_0_token.has_value());
+ EXPECT_EQ(new_child_0_token, same_document_new_child_0_token);
// TODO(ckitagawa): Somehow assert that the parent and child have matching
// embedding tokens in parent HTMLOwnerElement and child LocalFrame.
}
IN_PROC_BROWSER_TEST_F(EmbeddingTokenBrowserTest,
- EmbeddingTokenRemovedOnSubframeNavigationToSameOrigin) {
+ EmbeddingTokenChangedOnSubframeNavigationToNewDocument) {
auto a_url = embedded_test_server()->GetURL("a.com", "/");
EXPECT_TRUE(NavigateToURL(
shell(), a_url.Resolve("cross_site_iframe_factory.html?a(b)")));
ASSERT_EQ(1U, top_frame_host()->child_count());
- EXPECT_FALSE(top_frame_host()->GetEmbeddingToken().has_value());
+ EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
+ auto top_token = top_frame_host()->GetEmbeddingToken().value();
// Child 0 (b) should have an embedding token.
RenderFrameHost* target = top_frame_host()->child_at(0)->current_frame_host();
auto child_0_token = target->GetEmbeddingToken();
ASSERT_TRUE(child_0_token.has_value());
EXPECT_NE(base::UnguessableToken::Null(), child_0_token);
+ EXPECT_NE(top_token, child_0_token);
- // Navigate child 0 (b) to the same site as the main frame. This shouldn't
- // create an embedding token as the child is now local.
+ // Navigate child 0 (b) to the same site as the main frame. This should create
+ // an embedding token.
{
RenderFrameDeletedObserver deleted_observer(target);
NavigateIframeToURL(web_contents(), "child-0",
a_url.Resolve("site_isolation/").Resolve("blank.html"));
deleted_observer.WaitUntilDeleted();
}
- ASSERT_FALSE(top_frame_host()
- ->child_at(0)
- ->current_frame_host()
- ->GetEmbeddingToken()
- .has_value());
+
+ auto new_child_0_token =
+ top_frame_host()->child_at(0)->current_frame_host()->GetEmbeddingToken();
+ ASSERT_TRUE(new_child_0_token.has_value());
+ EXPECT_NE(base::UnguessableToken::Null(), new_child_0_token);
+ EXPECT_NE(top_token, new_child_0_token);
+ EXPECT_NE(child_0_token, new_child_0_token);
// TODO(ckitagawa): Somehow assert that the parent and child have matching
// embedding tokens in parent HTMLOwnerElement and child LocalFrame.
}
-IN_PROC_BROWSER_TEST_F(
- EmbeddingTokenBrowserTest,
- EmbeddingTokenAddedOnSubframeNavigationAwayFromSameOrigin) {
- auto a_url = embedded_test_server()->GetURL("a.com", "/");
- EXPECT_TRUE(NavigateToURL(
- shell(), a_url.Resolve("cross_site_iframe_factory.html?a(a)")));
+IN_PROC_BROWSER_TEST_F(EmbeddingTokenBrowserTest,
+ BackForwardCacheCrossDocument) {
+ auto a_url = embedded_test_server()->GetURL("a.com", "/site_isolation/");
+ auto b_url = embedded_test_server()->GetURL("b.com", "/site_isolation/");
+ EXPECT_TRUE(NavigateToURL(shell(), a_url.Resolve("blank.html")));
- ASSERT_EQ(1U, top_frame_host()->child_count());
- EXPECT_FALSE(top_frame_host()->GetEmbeddingToken().has_value());
+ EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
+ auto top_token_a = top_frame_host()->GetEmbeddingToken().value();
- // Child shouldn't have an embedding token.
- RenderFrameHost* target = top_frame_host()->child_at(0)->current_frame_host();
- auto child_0_token = target->GetEmbeddingToken();
- EXPECT_FALSE(child_0_token.has_value());
+ EXPECT_TRUE(NavigateToURL(shell(), b_url.Resolve("blank.html")));
+ EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
+ auto top_token_b = top_frame_host()->GetEmbeddingToken().value();
+ EXPECT_NE(top_token_a, top_token_b);
+
+ // Navigate back to the first origin. The back forward cache should keep
+ // the embedding token.
+ web_contents()->GetController().GoBack();
+ EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
+
+ EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
+ auto top_token_a_prime = top_frame_host()->GetEmbeddingToken().value();
+ EXPECT_EQ(top_token_a, top_token_a_prime);
+}
- // Navigate child 0 to a different site. This should now have an embedding
- // token.
- {
- RenderFrameDeletedObserver deleted_observer(target);
- NavigateIframeToURL(web_contents(), "child-0",
- embedded_test_server()
- ->GetURL("b.com", "/site_isolation/")
- .Resolve("blank.html"));
- deleted_observer.WaitUntilDeleted();
- }
- auto new_child_0_token =
- top_frame_host()->child_at(0)->current_frame_host()->GetEmbeddingToken();
- ASSERT_TRUE(new_child_0_token.has_value());
- EXPECT_NE(base::UnguessableToken::Null(), new_child_0_token);
+IN_PROC_BROWSER_TEST_F(EmbeddingTokenBrowserTest,
+ BackForwardCacheCrossDocumentAfterSameDocument) {
+ auto a_url = embedded_test_server()->GetURL("a.com", "/site_isolation/");
+ auto b_url = embedded_test_server()->GetURL("b.com", "/site_isolation/");
+ EXPECT_TRUE(NavigateToURL(shell(), a_url.Resolve("blank.html")));
- // TODO(ckitagawa): Somehow assert that the parent and child have matching
- // embedding tokens in parent HTMLOwnerElement and child LocalFrame.
+ EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
+ auto top_token_a = top_frame_host()->GetEmbeddingToken().value();
+
+ EXPECT_TRUE(NavigateToURL(shell(), a_url.Resolve("blank.html#foo")));
+ EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
+ EXPECT_EQ(top_frame_host()->GetEmbeddingToken().value(), top_token_a);
+
+ EXPECT_TRUE(NavigateToURL(shell(), b_url.Resolve("blank.html")));
+ EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
+ auto top_token_b = top_frame_host()->GetEmbeddingToken().value();
+ EXPECT_NE(top_token_a, top_token_b);
+
+ // Navigate back to the first origin. The back forward cache should keep
+ // the embedding token even when the embedding token is not present in the
+ // most recent navigation.
+ web_contents()->GetController().GoBack();
+ EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
+
+ EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
+ auto top_token_a_prime = top_frame_host()->GetEmbeddingToken().value();
+ EXPECT_EQ(top_token_a, top_token_a_prime);
+}
+
+IN_PROC_BROWSER_TEST_F(EmbeddingTokenBrowserTest,
+ SameDocumentHistoryPreservesTokens) {
+ auto a_url = embedded_test_server()->GetURL("a.com", "/site_isolation/");
+ EXPECT_TRUE(NavigateToURL(shell(), a_url.Resolve("blank.html")));
+
+ EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
+ auto top_token_a = top_frame_host()->GetEmbeddingToken().value();
+
+ EXPECT_TRUE(NavigateToURL(shell(), a_url.Resolve("blank.html#foo")));
+ EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
+ auto top_token_a_prime = top_frame_host()->GetEmbeddingToken().value();
+ EXPECT_EQ(top_token_a, top_token_a_prime);
+
+ // Navigate back to before the fragment was added. This should preserve the
+ // embedding token.
+ web_contents()->GetController().GoBack();
+ EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
+
+ EXPECT_TRUE(top_frame_host()->GetEmbeddingToken().has_value());
+ auto top_token_a_prime_prime = top_frame_host()->GetEmbeddingToken().value();
+ EXPECT_EQ(top_token_a, top_token_a_prime_prime);
}
} // namespace content
diff --git a/chromium/content/browser/frame_host/file_chooser_impl.cc b/chromium/content/browser/frame_host/file_chooser_impl.cc
deleted file mode 100644
index add104f1de3..00000000000
--- a/chromium/content/browser/frame_host/file_chooser_impl.cc
+++ /dev/null
@@ -1,194 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/frame_host/file_chooser_impl.h"
-
-#include "content/browser/child_process_security_policy_impl.h"
-#include "content/browser/frame_host/render_frame_host_delegate.h"
-#include "content/browser/frame_host/render_frame_host_impl.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/storage_partition.h"
-#include "content/public/browser/web_contents.h"
-#include "mojo/public/cpp/bindings/self_owned_receiver.h"
-
-namespace content {
-
-FileChooserImpl::FileSelectListenerImpl::~FileSelectListenerImpl() {
-#if DCHECK_IS_ON()
- DCHECK(was_file_select_listener_function_called_)
- << "Must call either FileSelectListener::FileSelected() or "
- "FileSelectListener::FileSelectionCanceled()";
- // TODO(avi): Turn on the DCHECK on the following line. This cannot yet be
- // done because I can't say for sure that I know who all the callers who bind
- // blink::mojom::FileChooser are. https://crbug.com/1054811
- /* DCHECK(was_fullscreen_block_set_) << "The fullscreen block was not set"; */
-#endif
- if (owner_)
- owner_->ResetListenerImpl();
-}
-
-void FileChooserImpl::FileSelectListenerImpl::SetFullscreenBlock(
- base::ScopedClosureRunner fullscreen_block) {
-#if DCHECK_IS_ON()
- DCHECK(!was_fullscreen_block_set_)
- << "Fullscreen block must only be set once";
- was_fullscreen_block_set_ = true;
-#endif
- fullscreen_block_ = std::move(fullscreen_block);
-}
-
-void FileChooserImpl::FileSelectListenerImpl::FileSelected(
- std::vector<blink::mojom::FileChooserFileInfoPtr> files,
- const base::FilePath& base_dir,
- blink::mojom::FileChooserParams::Mode mode) {
-#if DCHECK_IS_ON()
- DCHECK(!was_file_select_listener_function_called_)
- << "Must not call both of FileSelectListener::FileSelected() and "
- "FileSelectListener::FileSelectionCanceled()";
- was_file_select_listener_function_called_ = true;
-#endif
- if (owner_)
- owner_->FileSelected(std::move(files), base_dir, mode);
-}
-
-void FileChooserImpl::FileSelectListenerImpl::FileSelectionCanceled() {
-#if DCHECK_IS_ON()
- DCHECK(!was_file_select_listener_function_called_)
- << "Should not call both of FileSelectListener::FileSelected() and "
- "FileSelectListener::FileSelectionCanceled()";
- was_file_select_listener_function_called_ = true;
-#endif
- if (owner_)
- owner_->FileSelectionCanceled();
-}
-
-void FileChooserImpl::FileSelectListenerImpl::
- SetListenerFunctionCalledTrueForTesting() {
-#if DCHECK_IS_ON()
- was_file_select_listener_function_called_ = true;
-#endif
-}
-
-// static
-void FileChooserImpl::Create(
- RenderFrameHostImpl* render_frame_host,
- mojo::PendingReceiver<blink::mojom::FileChooser> receiver) {
- mojo::MakeSelfOwnedReceiver(
- std::make_unique<FileChooserImpl>(render_frame_host),
- std::move(receiver));
-}
-
-FileChooserImpl::FileChooserImpl(RenderFrameHostImpl* render_frame_host)
- : render_frame_host_(render_frame_host) {
- Observe(WebContents::FromRenderFrameHost(render_frame_host));
-}
-
-FileChooserImpl::~FileChooserImpl() {
- if (listener_impl_)
- listener_impl_->ResetOwner();
-}
-
-void FileChooserImpl::OpenFileChooser(blink::mojom::FileChooserParamsPtr params,
- OpenFileChooserCallback callback) {
- if (listener_impl_ || !render_frame_host_) {
- std::move(callback).Run(nullptr);
- return;
- }
- callback_ = std::move(callback);
- auto listener = std::make_unique<FileSelectListenerImpl>(this);
- listener_impl_ = listener.get();
- // Do not allow messages with absolute paths in them as this can permit a
- // renderer to coerce the browser to perform I/O on a renderer controlled
- // path.
- if (params->default_file_name != params->default_file_name.BaseName()) {
- mojo::ReportBadMessage(
- "FileChooser: The default file name must not be an absolute path.");
- listener->FileSelectionCanceled();
- return;
- }
- render_frame_host_->delegate()->RunFileChooser(render_frame_host_,
- std::move(listener), *params);
-}
-
-void FileChooserImpl::EnumerateChosenDirectory(
- const base::FilePath& directory_path,
- EnumerateChosenDirectoryCallback callback) {
- if (listener_impl_ || !render_frame_host_) {
- std::move(callback).Run(nullptr);
- return;
- }
- callback_ = std::move(callback);
- auto listener = std::make_unique<FileSelectListenerImpl>(this);
- listener_impl_ = listener.get();
- auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
- if (policy->CanReadFile(render_frame_host_->GetProcess()->GetID(),
- directory_path)) {
- render_frame_host_->delegate()->EnumerateDirectory(
- render_frame_host_, std::move(listener), directory_path);
- } else {
- listener->FileSelectionCanceled();
- }
-}
-
-void FileChooserImpl::FileSelected(
- std::vector<blink::mojom::FileChooserFileInfoPtr> files,
- const base::FilePath& base_dir,
- blink::mojom::FileChooserParams::Mode mode) {
- listener_impl_ = nullptr;
- if (!render_frame_host_)
- return;
- storage::FileSystemContext* file_system_context = nullptr;
- const int pid = render_frame_host_->GetProcess()->GetID();
- auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
- // Grant the security access requested to the given files.
- for (const auto& file : files) {
- if (mode == blink::mojom::FileChooserParams::Mode::kSave) {
- policy->GrantCreateReadWriteFile(pid, file->get_native_file()->file_path);
- } else {
- if (file->is_file_system()) {
- if (!file_system_context) {
- file_system_context =
- BrowserContext::GetStoragePartition(
- render_frame_host_->GetProcess()->GetBrowserContext(),
- render_frame_host_->GetSiteInstance())
- ->GetFileSystemContext();
- }
- policy->GrantReadFileSystem(
- pid, file_system_context->CrackURL(file->get_file_system()->url)
- .mount_filesystem_id());
- } else {
- policy->GrantReadFile(pid, file->get_native_file()->file_path);
- }
- }
- }
- std::move(callback_).Run(FileChooserResult::New(std::move(files), base_dir));
-}
-
-void FileChooserImpl::FileSelectionCanceled() {
- listener_impl_ = nullptr;
- if (!render_frame_host_)
- return;
- std::move(callback_).Run(nullptr);
-}
-
-void FileChooserImpl::ResetListenerImpl() {
- listener_impl_ = nullptr;
-}
-
-void FileChooserImpl::RenderFrameHostChanged(RenderFrameHost* old_host,
- RenderFrameHost* new_host) {
- if (old_host == render_frame_host_)
- render_frame_host_ = nullptr;
-}
-
-void FileChooserImpl::RenderFrameDeleted(RenderFrameHost* render_frame_host) {
- if (render_frame_host == render_frame_host_)
- render_frame_host_ = nullptr;
-}
-
-void FileChooserImpl::WebContentsDestroyed() {
- render_frame_host_ = nullptr;
-}
-
-} // namespace content
diff --git a/chromium/content/browser/frame_host/file_chooser_impl.h b/chromium/content/browser/frame_host/file_chooser_impl.h
deleted file mode 100644
index 223364352d6..00000000000
--- a/chromium/content/browser/frame_host/file_chooser_impl.h
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_FRAME_HOST_FILE_CHOOSER_IMPL_H_
-#define CONTENT_BROWSER_FRAME_HOST_FILE_CHOOSER_IMPL_H_
-
-#include "base/callback_helpers.h"
-#include "content/common/content_export.h"
-#include "content/public/browser/file_select_listener.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h"
-
-namespace content {
-
-class RenderFrameHostImpl;
-
-// An implementation of blink::mojom::FileChooser and FileSelectListener
-// associated to RenderFrameHost.
-class CONTENT_EXPORT FileChooserImpl : public blink::mojom::FileChooser,
- public content::WebContentsObserver {
- using FileChooserResult = blink::mojom::FileChooserResult;
-
- public:
- class CONTENT_EXPORT FileSelectListenerImpl
- : public content::FileSelectListener {
- public:
- explicit FileSelectListenerImpl(FileChooserImpl* owner) : owner_(owner) {}
- ~FileSelectListenerImpl() override;
- void SetFullscreenBlock(base::ScopedClosureRunner fullscreen_block);
- void ResetOwner() { owner_ = nullptr; }
-
- // FileSelectListener overrides:
-
- void FileSelected(std::vector<blink::mojom::FileChooserFileInfoPtr> files,
- const base::FilePath& base_dir,
- blink::mojom::FileChooserParams::Mode mode) override;
-
- void FileSelectionCanceled() override;
-
- protected:
- // This sets |was_file_select_listener_function_called_| to true so that
- // tests can pass with mocked overrides of this class.
- void SetListenerFunctionCalledTrueForTesting();
-
- private:
- FileChooserImpl* owner_;
- base::ScopedClosureRunner fullscreen_block_;
-#if DCHECK_IS_ON()
- bool was_file_select_listener_function_called_ = false;
- bool was_fullscreen_block_set_ = false;
-#endif
- };
-
- static void Create(RenderFrameHostImpl* render_frame_host,
- mojo::PendingReceiver<blink::mojom::FileChooser> receiver);
-
- explicit FileChooserImpl(RenderFrameHostImpl* render_frame_host);
-
- ~FileChooserImpl() override;
-
- void FileSelected(std::vector<blink::mojom::FileChooserFileInfoPtr> files,
- const base::FilePath& base_dir,
- blink::mojom::FileChooserParams::Mode mode);
-
- void FileSelectionCanceled();
-
- void ResetListenerImpl();
-
- // content::WebContentsObserver overrides:
-
- void OpenFileChooser(blink::mojom::FileChooserParamsPtr params,
- OpenFileChooserCallback callback) override;
-
- void EnumerateChosenDirectory(
- const base::FilePath& directory_path,
- EnumerateChosenDirectoryCallback callback) override;
-
- private:
- // content::WebContentsObserver overrides:
-
- void RenderFrameHostChanged(RenderFrameHost* old_host,
- RenderFrameHost* new_host) override;
-
- void RenderFrameDeleted(RenderFrameHost* render_frame_host) override;
-
- void WebContentsDestroyed() override;
-
- RenderFrameHostImpl* render_frame_host_;
- FileSelectListenerImpl* listener_impl_ = nullptr;
- base::OnceCallback<void(blink::mojom::FileChooserResultPtr)> callback_;
-};
-
-} // namespace content
-
-#endif // CONTENT_BROWSER_FRAME_HOST_FILE_CHOOSER_IMPL_H_
diff --git a/chromium/content/browser/frame_host/frame_navigation_entry.cc b/chromium/content/browser/frame_host/frame_navigation_entry.cc
index d2f9113b663..719526b6cc2 100644
--- a/chromium/content/browser/frame_host/frame_navigation_entry.cc
+++ b/chromium/content/browser/frame_host/frame_navigation_entry.cc
@@ -30,7 +30,8 @@ FrameNavigationEntry::FrameNavigationEntry(
const PageState& page_state,
const std::string& method,
int64_t post_id,
- scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory)
+ scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
+ std::unique_ptr<WebBundleNavigationInfo> web_bundle_navigation_info)
: frame_unique_name_(frame_unique_name),
item_sequence_number_(item_sequence_number),
document_sequence_number_(document_sequence_number),
@@ -44,7 +45,8 @@ FrameNavigationEntry::FrameNavigationEntry(
bindings_(kInvalidBindings),
method_(method),
post_id_(post_id),
- blob_url_loader_factory_(std::move(blob_url_loader_factory)) {
+ blob_url_loader_factory_(std::move(blob_url_loader_factory)),
+ web_bundle_navigation_info_(std::move(web_bundle_navigation_info)) {
if (origin)
committed_origin_ = *origin;
}
@@ -59,7 +61,8 @@ scoped_refptr<FrameNavigationEntry> FrameNavigationEntry::Clone() const {
document_sequence_number_, site_instance_.get(), nullptr,
url_, committed_origin_, referrer_, initiator_origin_,
redirect_chain_, page_state_, method_, post_id_,
- nullptr /* blob_url_loader_factory */);
+ nullptr /* blob_url_loader_factory */,
+ nullptr /* web_bundle_navigation_info */);
// |bindings_| gets only updated through the SetBindings API, not through
// UpdateEntry, so make a copy of it explicitly here as part of cloning.
copy->bindings_ = bindings_;
@@ -80,7 +83,8 @@ void FrameNavigationEntry::UpdateEntry(
const PageState& page_state,
const std::string& method,
int64_t post_id,
- scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory) {
+ scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
+ std::unique_ptr<WebBundleNavigationInfo> web_bundle_navigation_info) {
frame_unique_name_ = frame_unique_name;
item_sequence_number_ = item_sequence_number;
document_sequence_number_ = document_sequence_number;
@@ -95,6 +99,7 @@ void FrameNavigationEntry::UpdateEntry(
method_ = method;
post_id_ = post_id;
blob_url_loader_factory_ = std::move(blob_url_loader_factory);
+ web_bundle_navigation_info_ = std::move(web_bundle_navigation_info);
}
void FrameNavigationEntry::set_item_sequence_number(
diff --git a/chromium/content/browser/frame_host/frame_navigation_entry.h b/chromium/content/browser/frame_host/frame_navigation_entry.h
index f96f1dbbd0d..cbd17aa489b 100644
--- a/chromium/content/browser/frame_host/frame_navigation_entry.h
+++ b/chromium/content/browser/frame_host/frame_navigation_entry.h
@@ -54,7 +54,8 @@ class CONTENT_EXPORT FrameNavigationEntry
const PageState& page_state,
const std::string& method,
int64_t post_id,
- scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory);
+ scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
+ std::unique_ptr<WebBundleNavigationInfo> web_bundle_navigation_info);
// Creates a copy of this FrameNavigationEntry that can be modified
// independently from the original.
@@ -75,7 +76,8 @@ class CONTENT_EXPORT FrameNavigationEntry
const PageState& page_state,
const std::string& method,
int64_t post_id,
- scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory);
+ scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
+ std::unique_ptr<WebBundleNavigationInfo> web_bundle_navigation_info);
// The unique name of the frame this entry is for. This is a stable name for
// the frame based on its position in the tree and relation to other named
diff --git a/chromium/content/browser/frame_host/frame_tree.cc b/chromium/content/browser/frame_host/frame_tree.cc
index 100cd8079a0..8a21369714a 100644
--- a/chromium/content/browser/frame_host/frame_tree.cc
+++ b/chromium/content/browser/frame_host/frame_tree.cc
@@ -94,7 +94,8 @@ FrameTree::NodeRange::NodeRange(FrameTreeNode* root,
FrameTreeNode* root_of_subtree_to_skip)
: root_(root), root_of_subtree_to_skip_(root_of_subtree_to_skip) {}
-FrameTree::FrameTree(Navigator* navigator,
+FrameTree::FrameTree(NavigationControllerImpl* navigation_controller,
+ NavigatorDelegate* navigator_delegate,
RenderFrameHostDelegate* render_frame_delegate,
RenderViewHostDelegate* render_view_delegate,
RenderWidgetHostDelegate* render_widget_delegate,
@@ -103,8 +104,8 @@ FrameTree::FrameTree(Navigator* navigator,
render_view_delegate_(render_view_delegate),
render_widget_delegate_(render_widget_delegate),
manager_delegate_(manager_delegate),
+ navigator_(navigation_controller, navigator_delegate),
root_(new FrameTreeNode(this,
- navigator,
nullptr,
// The top-level frame must always be in a
// document scope.
@@ -203,9 +204,8 @@ FrameTreeNode* FrameTree::AddFrame(
return nullptr;
std::unique_ptr<FrameTreeNode> new_node = base::WrapUnique(new FrameTreeNode(
- this, parent->frame_tree_node()->navigator(), parent, scope, frame_name,
- frame_unique_name, is_created_by_script, devtools_frame_token,
- frame_owner_properties, owner_type));
+ this, parent, scope, frame_name, frame_unique_name, is_created_by_script,
+ devtools_frame_token, frame_owner_properties, owner_type));
// Set sandbox flags and container policy and make them effective immediately,
// since initial sandbox flags and feature policy should apply to the initial
@@ -237,11 +237,8 @@ FrameTreeNode* FrameTree::AddFrame(
// same |frame_unique_name|, since we don't remove FrameNavigationEntries if
// their frames are deleted. If there is a stale one, remove it to avoid
// conflicts on future updates.
- NavigationEntryImpl* last_committed_entry =
- static_cast<NavigationEntryImpl*>(parent->frame_tree_node()
- ->navigator()
- ->GetController()
- ->GetLastCommittedEntry());
+ NavigationEntryImpl* last_committed_entry = static_cast<NavigationEntryImpl*>(
+ navigator_.GetController()->GetLastCommittedEntry());
if (last_committed_entry) {
last_committed_entry->RemoveEntryForFrame(
added_node, /* only_if_different_position = */ true);
@@ -452,7 +449,7 @@ void FrameTree::UpdateLoadProgress(double progress) {
load_progress_ = progress;
// Notify the WebContents.
- root_->navigator()->GetDelegate()->DidChangeLoadProgress();
+ root_->navigator().GetDelegate()->DidChangeLoadProgress();
}
void FrameTree::ResetLoadProgress() {
diff --git a/chromium/content/browser/frame_host/frame_tree.h b/chromium/content/browser/frame_host/frame_tree.h
index b2050b910de..a843f64a7d2 100644
--- a/chromium/content/browser/frame_host/frame_tree.h
+++ b/chromium/content/browser/frame_host/frame_tree.h
@@ -16,7 +16,9 @@
#include "base/containers/queue.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
-#include "content/browser/frame_host/frame_tree_node.h"
+#include "content/browser/frame_host/navigator.h"
+#include "content/browser/frame_host/navigator_delegate.h"
+#include "content/browser/frame_host/render_frame_host_manager.h"
#include "content/common/content_export.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "services/service_manager/public/mojom/interface_provider.mojom.h"
@@ -29,7 +31,7 @@ struct FramePolicy;
namespace content {
-class Navigator;
+class NavigationControllerImpl;
class RenderFrameHostDelegate;
class RenderViewHostDelegate;
class RenderViewHostImpl;
@@ -96,7 +98,8 @@ class CONTENT_EXPORT FrameTree {
// RenderFrameHostManagers.
// TODO(creis): This set of delegates will change as we move things to
// Navigator.
- FrameTree(Navigator* navigator,
+ FrameTree(NavigationControllerImpl* navigation_controller,
+ NavigatorDelegate* navigator_delegate,
RenderFrameHostDelegate* render_frame_delegate,
RenderViewHostDelegate* render_view_delegate,
RenderWidgetHostDelegate* render_widget_delegate,
@@ -279,6 +282,9 @@ class CONTENT_EXPORT FrameTree {
const url::Origin& previously_visited_origin,
NavigationRequest* navigation_request_to_exclude);
+ NavigationControllerImpl* controller() { return navigator_.controller(); }
+ Navigator& navigator() { return navigator_; }
+
private:
friend class FrameTreeTest;
FRIEND_TEST_ALL_PREFIXES(RenderFrameHostImplBrowserTest, RemoveFocusedFrame);
@@ -295,6 +301,10 @@ class CONTENT_EXPORT FrameTree {
RenderWidgetHostDelegate* render_widget_delegate_;
RenderFrameHostManager::Delegate* manager_delegate_;
+ // The Navigator object responsible for managing navigations on this frame
+ // tree.
+ Navigator navigator_;
+
// Map of SiteInstance ID to RenderViewHost. This allows us to look up the
// RenderViewHost for a given SiteInstance when creating RenderFrameHosts.
// Each RenderViewHost maintains a refcount and is deleted when there are no
diff --git a/chromium/content/browser/frame_host/frame_tree_node.cc b/chromium/content/browser/frame_host/frame_tree_node.cc
index d4864979d3f..a1c72899b94 100644
--- a/chromium/content/browser/frame_host/frame_tree_node.cc
+++ b/chromium/content/browser/frame_host/frame_tree_node.cc
@@ -17,7 +17,6 @@
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "content/browser/devtools/devtools_instrumentation.h"
-#include "content/browser/frame_host/frame_tree.h"
#include "content/browser/frame_host/navigation_controller_impl.h"
#include "content/browser/frame_host/navigation_request.h"
#include "content/browser/frame_host/navigator.h"
@@ -106,7 +105,6 @@ FrameTreeNode* FrameTreeNode::From(RenderFrameHost* rfh) {
FrameTreeNode::FrameTreeNode(
FrameTree* frame_tree,
- Navigator* navigator,
RenderFrameHostImpl* parent,
blink::mojom::TreeScopeType scope,
const std::string& name,
@@ -116,7 +114,6 @@ FrameTreeNode::FrameTreeNode(
const blink::mojom::FrameOwnerProperties& frame_owner_properties,
blink::mojom::FrameOwnerElementType owner_type)
: frame_tree_(frame_tree),
- navigator_(navigator),
render_manager_(this, frame_tree->manager_delegate()),
frame_tree_node_id_(next_frame_tree_node_id_++),
parent_(parent),
@@ -136,7 +133,7 @@ FrameTreeNode::FrameTreeNode(
std::vector<uint32_t>()
/* hashes of hosts for insecure request upgrades */,
false /* is a potentially trustworthy unique origin */,
- false /* has received a user gesture */,
+ false /* has an active user gesture */,
false /* has received a user gesture before nav */,
owner_type),
is_created_by_script_(is_created_by_script),
@@ -164,7 +161,7 @@ FrameTreeNode::~FrameTreeNode() {
// See also https://crbug.com/784356.
if (is_created_by_script_ && parent_) {
NavigationEntryImpl* nav_entry = static_cast<NavigationEntryImpl*>(
- navigator()->GetController()->GetLastCommittedEntry());
+ navigator().GetController()->GetLastCommittedEntry());
if (nav_entry) {
nav_entry->RemoveEntryForFrame(this,
/* only_if_different_position = */ false);
@@ -515,7 +512,7 @@ void FrameTreeNode::DidStartLoading(bool to_different_document,
// Notify the WebContents.
if (!was_previously_loading)
- navigator()->GetDelegate()->DidStartLoading(this, to_different_document);
+ navigator().GetDelegate()->DidStartLoading(this, to_different_document);
// Set initial load progress and update overall progress. This will notify
// the WebContents of the load progress change.
@@ -537,7 +534,7 @@ void FrameTreeNode::DidStopLoading() {
// Notify the WebContents.
if (!frame_tree_->IsLoading())
- navigator()->GetDelegate()->DidStopLoading();
+ navigator().GetDelegate()->DidStopLoading();
}
void FrameTreeNode::DidChangeLoadProgress(double load_progress) {
@@ -595,7 +592,7 @@ bool FrameTreeNode::NotifyUserActivation() {
rfh->DidReceiveFirstUserActivation();
rfh->frame_tree_node()->user_activation_state_.Activate();
}
- replication_state_.has_received_user_gesture = true;
+ replication_state_.has_active_user_gesture = true;
// See the "Same-origin Visibility" section in |UserActivationState| class
// doc.
@@ -612,7 +609,7 @@ bool FrameTreeNode::NotifyUserActivation() {
}
NavigationControllerImpl* controller =
- static_cast<NavigationControllerImpl*>(navigator()->GetController());
+ static_cast<NavigationControllerImpl*>(navigator().GetController());
if (controller)
controller->NotifyUserActivation();
@@ -623,12 +620,14 @@ bool FrameTreeNode::ConsumeTransientUserActivation() {
bool was_active = user_activation_state_.IsActive();
for (FrameTreeNode* node : frame_tree()->Nodes())
node->user_activation_state_.ConsumeIfActive();
+ replication_state_.has_active_user_gesture = false;
return was_active;
}
bool FrameTreeNode::ClearUserActivation() {
for (FrameTreeNode* node : frame_tree()->SubtreeNodes(this))
node->user_activation_state_.Clear();
+ replication_state_.has_active_user_gesture = false;
return true;
}
diff --git a/chromium/content/browser/frame_host/frame_tree_node.h b/chromium/content/browser/frame_host/frame_tree_node.h
index 6df7a88eb74..60ba1670c9a 100644
--- a/chromium/content/browser/frame_host/frame_tree_node.h
+++ b/chromium/content/browser/frame_host/frame_tree_node.h
@@ -14,7 +14,9 @@
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
+#include "content/browser/frame_host/frame_tree.h"
#include "content/browser/frame_host/frame_tree_node_blame_context.h"
+#include "content/browser/frame_host/navigator.h"
#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/frame_host/render_frame_host_manager.h"
#include "content/common/content_export.h"
@@ -32,9 +34,7 @@
namespace content {
-class FrameTree;
class NavigationRequest;
-class Navigator;
class RenderFrameHostImpl;
class NavigationEntryImpl;
@@ -77,7 +77,6 @@ class CONTENT_EXPORT FrameTreeNode {
// calling the constructor.
FrameTreeNode(
FrameTree* frame_tree,
- Navigator* navigator,
RenderFrameHostImpl* parent,
blink::mojom::TreeScopeType scope,
const std::string& name,
@@ -102,7 +101,8 @@ class CONTENT_EXPORT FrameTreeNode {
void ResetForNavigation();
FrameTree* frame_tree() const { return frame_tree_; }
- Navigator* navigator() { return navigator_.get(); }
+ Navigator& navigator() { return frame_tree()->navigator(); }
+
RenderFrameHostManager* render_manager() { return &render_manager_; }
int frame_tree_node_id() const { return frame_tree_node_id_; }
const std::string& frame_name() const { return replication_state_.name; }
@@ -361,11 +361,6 @@ class CONTENT_EXPORT FrameTreeNode {
network::mojom::WebSandboxFlags sandbox_flags,
const blink::ParsedFeaturePolicy& parsed_header);
- // Returns whether the frame received a user gesture.
- bool has_received_user_gesture() const {
- return replication_state_.has_received_user_gesture;
- }
-
// Returns whether the frame received a user gesture on a previous navigation
// on the same eTLD+1.
bool has_received_user_gesture_before_nav() const {
@@ -443,10 +438,6 @@ class CONTENT_EXPORT FrameTreeNode {
// The FrameTree that owns us.
FrameTree* frame_tree_; // not owned.
- // The Navigator object responsible for managing navigations at this node
- // of the frame tree.
- scoped_refptr<Navigator> navigator_;
-
// Manages creation and swapping of RenderFrameHosts for this frame.
RenderFrameHostManager render_manager_;
diff --git a/chromium/content/browser/frame_host/frame_tree_node_blame_context.cc b/chromium/content/browser/frame_host/frame_tree_node_blame_context.cc
index d2b8a334503..1485f4d4f86 100644
--- a/chromium/content/browser/frame_host/frame_tree_node_blame_context.cc
+++ b/chromium/content/browser/frame_host/frame_tree_node_blame_context.cc
@@ -7,7 +7,7 @@
#include "base/process/process_handle.h"
#include "base/strings/stringprintf.h"
#include "base/trace_event/traced_value.h"
-#include "content/browser/frame_host/frame_tree.h"
+#include "content/browser/frame_host/frame_tree_node.h"
#include "url/gurl.h"
namespace content {
diff --git a/chromium/content/browser/frame_host/ipc_utils.cc b/chromium/content/browser/frame_host/ipc_utils.cc
index b3315d25e34..16a236529ce 100644
--- a/chromium/content/browser/frame_host/ipc_utils.cc
+++ b/chromium/content/browser/frame_host/ipc_utils.cc
@@ -6,6 +6,7 @@
#include <utility>
+#include "base/optional.h"
#include "content/browser/bad_message.h"
#include "content/browser/blob_storage/chrome_blob_storage_context.h"
#include "content/browser/child_process_security_policy_impl.h"
@@ -22,19 +23,14 @@ namespace content {
namespace {
-bool VerifyBlobToken(int process_id,
- mojo::MessagePipeHandle received_token,
- const GURL& received_url,
- mojo::PendingRemote<blink::mojom::BlobURLToken>*
- out_blob_url_token_remote) {
+// Validates that |received_token| is non-null iff associated with a blob: URL.
+bool VerifyBlobToken(
+ int process_id,
+ const mojo::PendingRemote<blink::mojom::BlobURLToken>& received_token,
+ const GURL& received_url) {
DCHECK_NE(ChildProcessHost::kInvalidUniqueID, process_id);
- DCHECK(out_blob_url_token_remote);
- mojo::ScopedMessagePipeHandle blob_url_token_handle(
- std::move(received_token));
- mojo::PendingRemote<blink::mojom::BlobURLToken> blob_url_token_remote(
- std::move(blob_url_token_handle), blink::mojom::BlobURLToken::Version_);
- if (blob_url_token_remote) {
+ if (received_token) {
if (!received_url.SchemeIsBlob()) {
bad_message::ReceivedBadMessage(
process_id, bad_message::BLOB_URL_TOKEN_FOR_NON_BLOB_URL);
@@ -42,7 +38,6 @@ bool VerifyBlobToken(int process_id,
}
}
- *out_blob_url_token_remote = std::move(blob_url_token_remote);
return true;
}
@@ -74,38 +69,32 @@ bool VerifyInitiatorOrigin(int process_id,
} // namespace
bool VerifyDownloadUrlParams(SiteInstance* site_instance,
- blink::mojom::DownloadURLParams* params,
- mojo::PendingRemote<blink::mojom::BlobURLToken>*
- out_blob_url_token_remote) {
+ const blink::mojom::DownloadURLParams& params) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(site_instance);
- DCHECK(out_blob_url_token_remote);
RenderProcessHost* process = site_instance->GetProcess();
int process_id = process->GetID();
- // Verify |params.blob_url_token| and populate |out_blob_url_token_remote|.
- if (params->blob_url_token &&
- !VerifyBlobToken(process_id, params->blob_url_token.release(),
- params->url, out_blob_url_token_remote)) {
+ // Verifies |params.blob_url_token| is appropriately set.
+ if (!VerifyBlobToken(process_id, params.blob_url_token, params.url))
return false;
- }
// Verify |params.initiator_origin|.
- if (params->initiator_origin &&
- !VerifyInitiatorOrigin(process_id, *params->initiator_origin))
+ if (params.initiator_origin &&
+ !VerifyInitiatorOrigin(process_id, *params.initiator_origin))
return false;
- // For large data URLs, they are passed through |params.data_url_blob| and
- // |params.url| should be empty.
- if (!params->url.is_valid())
- return params->data_url_blob.is_valid();
+ // If |params.url| is not set, this must be a large data URL being passed
+ // through |params.data_url_blob|.
+ if (!params.url.is_valid() && !params.data_url_blob.is_valid())
+ return false;
// Verification succeeded.
return true;
}
bool VerifyOpenURLParams(SiteInstance* site_instance,
- const FrameHostMsg_OpenURL_Params& params,
+ const mojom::OpenURLParamsPtr& params,
GURL* out_validated_url,
scoped_refptr<network::SharedURLLoaderFactory>*
out_blob_url_loader_factory) {
@@ -117,31 +106,32 @@ bool VerifyOpenURLParams(SiteInstance* site_instance,
int process_id = process->GetID();
// Verify |params.url| and populate |out_validated_url|.
- *out_validated_url = params.url;
+ *out_validated_url = params->url;
process->FilterURL(false, out_validated_url);
// Verify |params.blob_url_token| and populate |out_blob_url_loader_factory|.
- mojo::PendingRemote<blink::mojom::BlobURLToken> blob_url_token_remote;
- if (!VerifyBlobToken(process_id, params.blob_url_token, params.url,
- &blob_url_token_remote)) {
+ mojo::PendingRemote<blink::mojom::BlobURLToken> blob_url_token(
+ mojo::ScopedMessagePipeHandle(std::move(params->blob_url_token)),
+ blink::mojom::BlobURLToken::Version_);
+ if (!VerifyBlobToken(process_id, blob_url_token, params->url))
return false;
- }
- if (blob_url_token_remote) {
+
+ if (blob_url_token.is_valid()) {
*out_blob_url_loader_factory =
ChromeBlobStorageContext::URLLoaderFactoryForToken(
- process->GetBrowserContext(), std::move(blob_url_token_remote));
+ process->GetBrowserContext(), std::move(blob_url_token));
}
// Verify |params.post_body|.
auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
- if (!policy->CanReadRequestBody(site_instance, params.post_body)) {
+ if (!policy->CanReadRequestBody(site_instance, params->post_body)) {
bad_message::ReceivedBadMessage(process,
bad_message::ILLEGAL_UPLOAD_PARAMS);
return false;
}
// Verify |params.initiator_origin|.
- if (!VerifyInitiatorOrigin(process_id, params.initiator_origin))
+ if (!VerifyInitiatorOrigin(process_id, params->initiator_origin))
return false;
// Verification succeeded.
diff --git a/chromium/content/browser/frame_host/ipc_utils.h b/chromium/content/browser/frame_host/ipc_utils.h
index 12e2b13db09..7c57ea5d127 100644
--- a/chromium/content/browser/frame_host/ipc_utils.h
+++ b/chromium/content/browser/frame_host/ipc_utils.h
@@ -12,8 +12,6 @@
#include "third_party/blink/public/mojom/frame/frame.mojom.h"
#include "url/gurl.h"
-struct FrameHostMsg_OpenURL_Params;
-
namespace content {
class SiteInstance;
@@ -21,19 +19,14 @@ class SiteInstance;
// Verifies that |params| are valid and can be accessed by the renderer process
// associated with |site_instance|.
//
-// Returns true if the |params| are valid. In the case |params->blob_url_token|
-// is non-null, it gets deserialized and |out_blob_url_token_remote| is
-// populated. |params| is a mojo Ptr instead const& to make it clear to callees
-// of its mutable nature.
+// If the |params| are valid, returns true.
//
-// Terminates the renderer with the given |process_id| and returns false if the
-// |params| are invalid.
+// Otherwise, terminates the renderer associated with |site_instance| and
+// returns false.
//
// This function has to be called on the UI thread.
-bool VerifyDownloadUrlParams(
- SiteInstance* site_instance,
- blink::mojom::DownloadURLParams* params,
- mojo::PendingRemote<blink::mojom::BlobURLToken>* out_blob_url_token_remote);
+bool VerifyDownloadUrlParams(SiteInstance* site_instance,
+ const blink::mojom::DownloadURLParams& params);
// Verifies that |params| are valid and can be accessed by the renderer process
// associated with |site_instance|.
@@ -46,7 +39,7 @@ bool VerifyDownloadUrlParams(
//
// This function has to be called on the UI thread.
bool VerifyOpenURLParams(SiteInstance* site_instance,
- const FrameHostMsg_OpenURL_Params& params,
+ const mojom::OpenURLParamsPtr& params,
GURL* out_validated_url,
scoped_refptr<network::SharedURLLoaderFactory>*
out_blob_url_loader_factory);
diff --git a/chromium/content/browser/frame_host/keep_alive_handle_factory.cc b/chromium/content/browser/frame_host/keep_alive_handle_factory.cc
index 9d702d0356c..67b200d0d5c 100644
--- a/chromium/content/browser/frame_host/keep_alive_handle_factory.cc
+++ b/chromium/content/browser/frame_host/keep_alive_handle_factory.cc
@@ -10,7 +10,6 @@
#include "base/bind.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/field_trial_params.h"
-#include "base/task/post_task.h"
#include "content/common/frame.mojom.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
@@ -43,9 +42,8 @@ class KeepAliveHandleFactory::Context final : public base::RefCounted<Context> {
void DetachLater(base::TimeDelta timeout) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
- base::PostDelayedTask(FROM_HERE, {BrowserThread::UI},
- base::BindOnce(&Context::Detach, AsWeakPtr()),
- timeout);
+ GetUIThreadTaskRunner({})->PostDelayedTask(
+ FROM_HERE, base::BindOnce(&Context::Detach, AsWeakPtr()), timeout);
}
void AddReceiver(std::unique_ptr<mojom::KeepAliveHandle> impl,
diff --git a/chromium/content/browser/frame_host/mixed_content_navigation_throttle.cc b/chromium/content/browser/frame_host/mixed_content_navigation_throttle.cc
index f2fc465d812..b647b00c723 100644
--- a/chromium/content/browser/frame_host/mixed_content_navigation_throttle.cc
+++ b/chromium/content/browser/frame_host/mixed_content_navigation_throttle.cc
@@ -77,6 +77,8 @@ void UpdateRendererOnMixedContentFound(NavigationRequest* navigation_request,
params.request_context_type = navigation_request->request_context_type();
params.request_destination = navigation_request->request_destination();
params.was_allowed = was_allowed;
+ DCHECK(!navigation_request->GetRedirectChain().empty());
+ params.url_before_redirects = navigation_request->GetRedirectChain()[0];
params.had_redirect = for_redirect;
params.source_location =
*(navigation_request->common_params().source_location.get());
@@ -207,7 +209,6 @@ bool MixedContentNavigationThrottle::ShouldBlockNavigation(bool for_redirect) {
allowed =
should_ask_delegate &&
frame_host_delegate->ShouldAllowRunningInsecureContent(
- navigation_handle()->GetWebContents(),
prefs.allow_running_insecure_content,
mixed_content_frame->GetLastCommittedOrigin(), request->GetURL());
if (allowed) {
diff --git a/chromium/content/browser/frame_host/navigation_controller_impl.cc b/chromium/content/browser/frame_host/navigation_controller_impl.cc
index 005d9dcf49a..d6c77f00ee6 100644
--- a/chromium/content/browser/frame_host/navigation_controller_impl.cc
+++ b/chromium/content/browser/frame_host/navigation_controller_impl.cc
@@ -155,12 +155,7 @@ bool ShouldTreatNavigationAsReload(const GURL& url,
bool is_post,
bool is_reload,
bool is_navigation_to_existing_entry,
- bool has_interstitial,
NavigationEntryImpl* last_committed_entry) {
- // Don't convert when an interstitial is showing.
- if (has_interstitial)
- return false;
-
// Only convert main frame navigations to a new entry.
if (!is_main_frame || is_reload || is_navigation_to_existing_entry)
return false;
@@ -478,7 +473,8 @@ std::unique_ptr<NavigationEntry> NavigationController::CreateNavigationEntry(
return NavigationControllerImpl::CreateNavigationEntry(
url, referrer, std::move(initiator_origin),
nullptr /* source_site_instance */, transition, is_renderer_initiated,
- extra_headers, browser_context, std::move(blob_url_loader_factory));
+ extra_headers, browser_context, std::move(blob_url_loader_factory),
+ false /* should_replace_entry */);
}
// static
@@ -492,7 +488,8 @@ NavigationControllerImpl::CreateNavigationEntry(
bool is_renderer_initiated,
const std::string& extra_headers,
BrowserContext* browser_context,
- scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory) {
+ scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
+ bool should_replace_entry) {
GURL url_to_load;
GURL virtual_url;
bool reverse_on_redirect = false;
@@ -514,6 +511,7 @@ NavigationControllerImpl::CreateNavigationEntry(
entry->set_user_typed_url(virtual_url);
entry->set_update_virtual_url_with_url(reverse_on_redirect);
entry->set_extra_headers(extra_headers);
+ entry->set_should_replace_entry(should_replace_entry);
return entry;
}
@@ -538,6 +536,17 @@ base::Time NavigationControllerImpl::TimeSmoother::GetSmoothedTime(
return t;
}
+NavigationControllerImpl::ScopedShowRepostDialogForTesting::
+ ScopedShowRepostDialogForTesting()
+ : was_disallowed_(g_check_for_repost) {
+ g_check_for_repost = true;
+}
+
+NavigationControllerImpl::ScopedShowRepostDialogForTesting::
+ ~ScopedShowRepostDialogForTesting() {
+ g_check_for_repost = was_disallowed_;
+}
+
NavigationControllerImpl::NavigationControllerImpl(
NavigationControllerDelegate* delegate,
BrowserContext* browser_context)
@@ -592,18 +601,6 @@ void NavigationControllerImpl::Restore(
void NavigationControllerImpl::Reload(ReloadType reload_type,
bool check_for_repost) {
DCHECK_NE(ReloadType::NONE, reload_type);
-
- if (transient_entry_index_ != -1) {
- // If an interstitial is showing, treat a reload as a navigation to the
- // transient entry's URL.
- NavigationEntryImpl* transient_entry = GetTransientEntry();
- if (!transient_entry)
- return;
- LoadURL(transient_entry->GetURL(), Referrer(), ui::PAGE_TRANSITION_RELOAD,
- transient_entry->extra_headers());
- return;
- }
-
NavigationEntryImpl* entry = nullptr;
int current_index = -1;
@@ -722,16 +719,12 @@ void NavigationControllerImpl::SetPendingEntry(
}
NavigationEntryImpl* NavigationControllerImpl::GetActiveEntry() {
- if (transient_entry_index_ != -1)
- return entries_[transient_entry_index_].get();
if (pending_entry_)
return pending_entry_;
return GetLastCommittedEntry();
}
NavigationEntryImpl* NavigationControllerImpl::GetVisibleEntry() {
- if (transient_entry_index_ != -1)
- return entries_[transient_entry_index_].get();
// The pending entry is safe to return for new (non-history), browser-
// initiated navigations. Most renderer-initiated navigations should not
// show the pending entry, to prevent URL spoof attacks.
@@ -760,8 +753,6 @@ NavigationEntryImpl* NavigationControllerImpl::GetVisibleEntry() {
}
int NavigationControllerImpl::GetCurrentEntryIndex() {
- if (transient_entry_index_ != -1)
- return transient_entry_index_;
if (pending_entry_index_ != -1)
return pending_entry_index_;
return last_committed_entry_index_;
@@ -784,16 +775,14 @@ bool NavigationControllerImpl::CanViewSource() {
int NavigationControllerImpl::GetLastCommittedEntryIndex() {
// The last committed entry index must always be less than the number of
- // entries. If there are no entries, it must be -1. However, there may be a
- // transient entry while the last committed entry index is still -1.
+ // entries. If there are no entries, it must be -1.
DCHECK_LT(last_committed_entry_index_, GetEntryCount());
DCHECK(GetEntryCount() || last_committed_entry_index_ == -1);
return last_committed_entry_index_;
}
int NavigationControllerImpl::GetEntryCount() {
- DCHECK_LE(entries_.size(),
- max_entry_count() + (transient_entry_index_ == -1 ? 0 : 1));
+ DCHECK_LE(entries_.size(), max_entry_count());
return static_cast<int>(entries_.size());
}
@@ -906,17 +895,6 @@ void NavigationControllerImpl::GoToIndex(int index,
return;
}
- if (transient_entry_index_ != -1) {
- if (index == transient_entry_index_) {
- // Nothing to do when navigating to the transient.
- return;
- }
- if (index > transient_entry_index_) {
- // Removing the transient is goint to shift all entries by 1.
- index--;
- }
- }
-
DiscardNonCommittedEntries();
DCHECK_EQ(nullptr, pending_entry_);
@@ -1411,7 +1389,8 @@ void NavigationControllerImpl::RendererDidNavigateToNewPage(
params.document_sequence_number, rfh->GetSiteInstance(), nullptr,
params.url, (params.url_is_unreachable) ? nullptr : &params.origin,
params.referrer, initiator_origin, params.redirects, params.page_state,
- params.method, params.post_id, nullptr /* blob_url_loader_factory */);
+ params.method, params.post_id, nullptr /* blob_url_loader_factory */,
+ nullptr /* web_bundle_navigation_info */);
new_entry = GetLastCommittedEntry()->CloneAndReplace(
frame_entry, true, rfh->frame_tree_node(),
@@ -1725,7 +1704,10 @@ void NavigationControllerImpl::RendererDidNavigateToExistingPage(
params.document_sequence_number, rfh->GetSiteInstance(), nullptr,
params.url, GetCommittedOriginForFrameEntry(params), params.referrer,
initiator_origin, params.redirects, params.page_state, params.method,
- params.post_id, nullptr /* blob_url_loader_factory */);
+ params.post_id, nullptr /* blob_url_loader_factory */,
+ request->web_bundle_navigation_info()
+ ? request->web_bundle_navigation_info()->Clone()
+ : nullptr);
// The redirected to page should not inherit the favicon from the previous
// page.
@@ -1743,8 +1725,7 @@ void NavigationControllerImpl::RendererDidNavigateToExistingPage(
if (!keep_pending_entry)
DiscardNonCommittedEntries();
- // If a transient entry was removed, the indices might have changed, so we
- // have to query the entry index again.
+ // Update the last committed index to reflect the committed entry.
last_committed_entry_index_ = GetIndexOfEntry(entry);
}
@@ -1802,7 +1783,10 @@ void NavigationControllerImpl::RendererDidNavigateToSamePage(
params.document_sequence_number, rfh->GetSiteInstance(), nullptr,
params.url, GetCommittedOriginForFrameEntry(params), params.referrer,
initiator_origin, params.redirects, params.page_state, params.method,
- params.post_id, nullptr /* blob_url_loader_factory */);
+ params.post_id, nullptr /* blob_url_loader_factory */,
+ request->web_bundle_navigation_info()
+ ? request->web_bundle_navigation_info()->Clone()
+ : nullptr);
DiscardNonCommittedEntries();
}
@@ -1839,7 +1823,10 @@ void NavigationControllerImpl::RendererDidNavigateNewSubframe(
params.document_sequence_number, rfh->GetSiteInstance(), nullptr,
params.url, (params.url_is_unreachable) ? nullptr : &params.origin,
params.referrer, initiator_origin, params.redirects, params.page_state,
- params.method, params.post_id, nullptr /* blob_url_loader_factory */);
+ params.method, params.post_id, nullptr /* blob_url_loader_factory */,
+ request->web_bundle_navigation_info()
+ ? request->web_bundle_navigation_info()->Clone()
+ : nullptr);
std::unique_ptr<NavigationEntryImpl> new_entry =
GetLastCommittedEntry()->CloneAndReplace(
@@ -1917,7 +1904,10 @@ bool NavigationControllerImpl::RendererDidNavigateAutoSubframe(
params.document_sequence_number, rfh->GetSiteInstance(), nullptr,
params.url, GetCommittedOriginForFrameEntry(params), params.referrer,
initiator_origin, params.redirects, params.page_state, params.method,
- params.post_id, nullptr /* blob_url_loader_factory */);
+ params.post_id, nullptr /* blob_url_loader_factory */,
+ request->web_bundle_navigation_info()
+ ? request->web_bundle_navigation_info()->Clone()
+ : nullptr);
return send_commit_notification;
}
@@ -2037,9 +2027,8 @@ void NavigationControllerImpl::CopyStateFromAndPrune(NavigationController* temp,
if (!replace_entry)
source->PruneOldestSkippableEntryIfFull();
- // Insert the entries from source. Don't use source->GetCurrentEntryIndex as
- // we don't want to copy over the transient entry. Ignore any pending entry,
- // since it has not committed in source.
+ // Insert the entries from source. Ignore any pending entry, since it has not
+ // committed in source.
int max_source_index = source->last_committed_entry_index_;
if (max_source_index == -1)
max_source_index = source->GetEntryCount();
@@ -2075,10 +2064,6 @@ bool NavigationControllerImpl::CanPruneAllButLastCommitted() {
if (pending_entry_index_ != -1)
return false;
- // We should not prune if we are currently showing a transient entry.
- if (transient_entry_index_ != -1)
- return false;
-
return true;
}
@@ -2236,7 +2221,7 @@ bool NavigationControllerImpl::StartHistoryNavigationInNewSubframe(
request->SetNavigationClient(std::move(*navigation_client));
- render_frame_host->frame_tree_node()->navigator()->Navigate(
+ render_frame_host->frame_tree_node()->navigator().Navigate(
std::move(request), ReloadType::NONE, RestoreType::NONE);
return true;
@@ -2271,6 +2256,11 @@ void NavigationControllerImpl::NavigateFromFrameProxy(
FrameTreeNode* node = render_frame_host->frame_tree_node();
+ // Don't allow an entry replacement if there is no entry to replace.
+ // http://crbug.com/457149
+ if (GetEntryCount() == 0)
+ should_replace_current_entry = false;
+
// Create a NavigationEntry for the transfer, without making it the pending
// entry. Subframe transfers should have a clone of the last committed entry
// with a FrameNavigationEntry for the target frame. Main frame transfers
@@ -2296,31 +2286,25 @@ void NavigationControllerImpl::NavigateFromFrameProxy(
GURL(url::kAboutBlankURL), referrer, initiator_origin,
source_site_instance, page_transition, is_renderer_initiated,
extra_headers, browser_context_,
- nullptr /* blob_url_loader_factory */));
+ nullptr /* blob_url_loader_factory */, should_replace_current_entry));
}
entry->AddOrUpdateFrameEntry(
node, -1, -1, nullptr,
static_cast<SiteInstanceImpl*>(source_site_instance), url,
base::nullopt /* commit_origin */, referrer, initiator_origin,
- std::vector<GURL>(), PageState(), method, -1, blob_url_loader_factory);
+ std::vector<GURL>(), PageState(), method, -1, blob_url_loader_factory,
+ nullptr /* web_bundle_navigation_info */);
} else {
// Main frame case.
entry = NavigationEntryImpl::FromNavigationEntry(CreateNavigationEntry(
url, referrer, initiator_origin, source_site_instance, page_transition,
is_renderer_initiated, extra_headers, browser_context_,
- blob_url_loader_factory));
+ blob_url_loader_factory, should_replace_current_entry));
entry->root_node()->frame_entry->set_source_site_instance(
static_cast<SiteInstanceImpl*>(source_site_instance));
entry->root_node()->frame_entry->set_method(method);
}
- // Don't allow an entry replacement if there is no entry to replace.
- // http://crbug.com/457149
- if (GetEntryCount() == 0)
- should_replace_current_entry = false;
-
- entry->set_should_replace_entry(should_replace_current_entry);
-
bool override_user_agent = false;
if (GetLastCommittedEntry() &&
GetLastCommittedEntry()->GetIsOverridingUserAgent()) {
@@ -2340,7 +2324,8 @@ void NavigationControllerImpl::NavigateFromFrameProxy(
node->unique_name(), -1, -1, nullptr,
static_cast<SiteInstanceImpl*>(source_site_instance), url,
nullptr /* origin */, referrer, initiator_origin, std::vector<GURL>(),
- PageState(), method, -1, blob_url_loader_factory);
+ PageState(), method, -1, blob_url_loader_factory,
+ nullptr /* web_bundle_navigation_info */);
}
LoadURLParams params(url);
@@ -2386,8 +2371,8 @@ void NavigationControllerImpl::NavigateFromFrameProxy(
// remains of a cancelled browser initiated navigation to avoid URL spoofs.
DiscardNonCommittedEntries();
- node->navigator()->Navigate(std::move(request), ReloadType::NONE,
- RestoreType::NONE);
+ node->navigator().Navigate(std::move(request), ReloadType::NONE,
+ RestoreType::NONE);
}
void NavigationControllerImpl::SetSessionStorageNamespace(
@@ -2482,18 +2467,8 @@ void NavigationControllerImpl::SetNeedsReload(NeedsReloadType type) {
void NavigationControllerImpl::RemoveEntryAtIndexInternal(int index) {
DCHECK_LT(index, GetEntryCount());
DCHECK_NE(index, last_committed_entry_index_);
-
- const bool was_transient = index == transient_entry_index_;
-
DiscardNonCommittedEntries();
- if (was_transient) {
- // There's nothing left to do if the index referred to a transient entry
- // that we just discarded.
- DCHECK(!GetTransientEntry());
- return;
- }
-
entries_.erase(entries_.begin() + index);
if (last_committed_entry_index_ > index)
last_committed_entry_index_--;
@@ -2610,6 +2585,7 @@ void NavigationControllerImpl::NavigateToExistingPendingEntry(
pending_entry_ == entry_replaced_by_post_commit_error_.get());
}
DCHECK(!IsRendererDebugURL(pending_entry_->GetURL()));
+ bool is_forced_reload = needs_reload_;
needs_reload_ = false;
FrameTreeNode* root = delegate_->GetFrameTree()->root();
int nav_entry_id = pending_entry_->GetUniqueID();
@@ -2623,8 +2599,8 @@ void NavigationControllerImpl::NavigateToExistingPendingEntry(
root, pending_entry_, pending_entry_->GetFrameEntry(root),
ReloadType::NONE, false /* is_same_document_history_load */,
false /* is_history_navigation_in_new_child */);
- root->navigator()->Navigate(std::move(navigation_request), ReloadType::NONE,
- RestoreType::NONE);
+ root->navigator().Navigate(std::move(navigation_request), ReloadType::NONE,
+ RestoreType::NONE);
return;
}
@@ -2674,31 +2650,44 @@ void NavigationControllerImpl::NavigateToExistingPendingEntry(
// navigated.
std::vector<std::unique_ptr<NavigationRequest>> same_document_loads;
std::vector<std::unique_ptr<NavigationRequest>> different_document_loads;
- if (GetLastCommittedEntry()) {
- FindFramesToNavigate(root, reload_type, &same_document_loads,
- &different_document_loads);
- }
+ FindFramesToNavigate(root, reload_type, &same_document_loads,
+ &different_document_loads);
if (same_document_loads.empty() && different_document_loads.empty()) {
- // If we don't have any frames to navigate at this point, either
- // (1) there is no previous history entry to compare against, or
- // (2) we were unable to match any frames by name. In the first case,
- // doing a different document navigation to the root item is the only valid
- // thing to do. In the second case, we should have been able to find a
- // frame to navigate based on names if this were a same document
- // navigation, so we can safely assume this is the different document case.
+ // We were unable to match any frames to navigate. This can happen if a
+ // history navigation targets a subframe that no longer exists
+ // (https://crbug.com/705550). In this case, we need to update the current
+ // history entry to the pending one but keep the main document loaded. We
+ // also need to ensure that observers are informed about the updated
+ // current history entry (e.g., for greying out back/forward buttons), and
+ // that renderer processes update their history offsets. The easiest way
+ // to do all that is to schedule a "redundant" same-document navigation in
+ // the main frame.
+ //
+ // Note that we don't want to remove this history entry, as it might still
+ // be valid later, since a frame that it's targeting may be recreated.
+ //
+ // TODO(alexmos, creis): This behavior isn't ideal, as the user would
+ // need to repeat history navigations until finding the one that works.
+ // Consider changing this behavior to keep looking for the first valid
+ // history entry that finds frames to navigate.
std::unique_ptr<NavigationRequest> navigation_request =
CreateNavigationRequestFromEntry(
root, pending_entry_, pending_entry_->GetFrameEntry(root),
- reload_type, false /* is_same_document_history_load */,
+ ReloadType::NONE /* reload_type */,
+ true /* is_same_document_history_load */,
false /* is_history_navigation_in_new_child */);
if (!navigation_request) {
- // This navigation cannot start (e.g. the URL is invalid), delete the
- // pending NavigationEntry.
+ // If this navigation cannot start, delete the pending NavigationEntry.
DiscardPendingEntry(false);
return;
}
- different_document_loads.push_back(std::move(navigation_request));
+ same_document_loads.push_back(std::move(navigation_request));
+
+ // Sanity check that we never take this branch for any kinds of reloads,
+ // as those should've queued a different-document load in the main frame.
+ DCHECK(!is_forced_reload);
+ DCHECK_EQ(reload_type, ReloadType::NONE);
}
// If |sandboxed_source_frame_node_id| is valid, then track whether this
@@ -2748,83 +2737,160 @@ void NavigationControllerImpl::NavigateToExistingPendingEntry(
// Send all the same document frame loads before the different document loads.
for (auto& item : same_document_loads) {
FrameTreeNode* frame = item->frame_tree_node();
- frame->navigator()->Navigate(std::move(item), reload_type,
- pending_entry_->restore_type());
+ frame->navigator().Navigate(std::move(item), reload_type,
+ pending_entry_->restore_type());
}
for (auto& item : different_document_loads) {
FrameTreeNode* frame = item->frame_tree_node();
- frame->navigator()->Navigate(std::move(item), reload_type,
- pending_entry_->restore_type());
+ frame->navigator().Navigate(std::move(item), reload_type,
+ pending_entry_->restore_type());
}
in_navigate_to_pending_entry_ = false;
}
+NavigationControllerImpl::HistoryNavigationAction
+NavigationControllerImpl::DetermineActionForHistoryNavigation(
+ FrameTreeNode* frame,
+ ReloadType reload_type) {
+ // Only active frames can navigate:
+ // - If the frame is in pending deletion, the browser already committed to
+ // destroying this RenderFrameHost. See https://crbug.com/930278.
+ // - If the frame is in back-forward cache, it's not allowed to navigate as it
+ // should remain frozen. Ignore the request and evict the document from
+ // back-forward cache.
+ //
+ // If the frame is inactive, there's no need to recurse into subframes, which
+ // should all be inactive as well.
+ if (frame->current_frame_host()->IsInactiveAndDisallowReactivation())
+ return HistoryNavigationAction::kStopLooking;
+
+ // If there's no last committed entry, there is no previous history entry to
+ // compare against, so fall back to a different-document load. Note that we
+ // should only reach this case for the root frame and not descend further
+ // into subframes.
+ if (!GetLastCommittedEntry()) {
+ DCHECK(frame->IsMainFrame());
+ return HistoryNavigationAction::kDifferentDocument;
+ }
+
+ // Reloads should result in a different-document load. Note that reloads may
+ // also happen via the |needs_reload_| mechanism where the reload_type is
+ // NONE, so detect this by comparing whether we're going to the same
+ // entry that we're currently on. Similarly to above, only main frames
+ // should reach this. Note that subframes support reloads, but that's done
+ // via a different path that doesn't involve FindFramesToNavigate (see
+ // RenderFrameHost::Reload()).
+ if (reload_type != ReloadType::NONE ||
+ pending_entry_index_ == last_committed_entry_index_) {
+ DCHECK(frame->IsMainFrame());
+ return HistoryNavigationAction::kDifferentDocument;
+ }
+
+ // If there is no new FrameNavigationEntry for the frame, ignore the
+ // load. For example, this may happen when going back to an entry before a
+ // frame was created. Suppose we commit a same-document navigation that also
+ // results in adding a new subframe somewhere in the tree. If we go back,
+ // the new subframe will be missing a FrameNavigationEntry in the previous
+ // NavigationEntry, but we shouldn't delete or change what's loaded in
+ // it.
+ //
+ // Note that in this case, there is no need to keep looking for navigations
+ // in subframes, which would be missing FrameNavigationEntries as well.
+ //
+ // It's important to check this before checking |old_item| below, since both
+ // might be null, and in that case we still shouldn't change what's loaded in
+ // this frame. Note that scheduling any loads assumes that |new_item| is
+ // non-null. See https://crbug.com/1088354.
+ FrameNavigationEntry* new_item = pending_entry_->GetFrameEntry(frame);
+ if (!new_item)
+ return HistoryNavigationAction::kStopLooking;
+
+ // If there is no old FrameNavigationEntry, schedule a different-document
+ // load.
+ //
+ // TODO(creis): Store the last committed FrameNavigationEntry to use here,
+ // rather than assuming the NavigationEntry has up to date info on subframes.
+ // Note that this may require sharing FrameNavigationEntries between
+ // NavigationEntries (https://crbug.com/373041) as a prerequisite, since
+ // otherwise the stored FrameNavigationEntry might get stale (e.g., after
+ // subframe navigations, or in the case where we don't find any frames to
+ // navigate and ignore a back/forward navigation while moving to a different
+ // NavigationEntry, as in https://crbug.com/705550).
+ FrameNavigationEntry* old_item =
+ GetLastCommittedEntry()->GetFrameEntry(frame);
+ if (!old_item)
+ return HistoryNavigationAction::kDifferentDocument;
+
+ // If the new item is not in the same SiteInstance, schedule a
+ // different-document load. Newly restored items may not have a SiteInstance
+ // yet, in which case it will be assigned on first commit.
+ if (new_item->site_instance() &&
+ new_item->site_instance() != old_item->site_instance())
+ return HistoryNavigationAction::kDifferentDocument;
+
+ // Schedule a different-document load if the current RenderFrameHost is not
+ // live. This case can happen for Ctrl+Back or after
+ // a renderer crash.
+ if (!frame->current_frame_host()->IsRenderFrameLive())
+ return HistoryNavigationAction::kDifferentDocument;
+
+ if (new_item->item_sequence_number() != old_item->item_sequence_number()) {
+ // Same document loads happen if the previous item has the same document
+ // sequence number but different item sequence number.
+ if (new_item->document_sequence_number() ==
+ old_item->document_sequence_number())
+ return HistoryNavigationAction::kSameDocument;
+
+ // Otherwise, if both item and document sequence numbers differ, this
+ // should be a different document load.
+ return HistoryNavigationAction::kDifferentDocument;
+ }
+
+ // If the item sequence numbers match, there is no need to navigate this
+ // frame. Keep looking for navigations in this frame's children.
+ DCHECK_EQ(new_item->document_sequence_number(),
+ old_item->document_sequence_number());
+ return HistoryNavigationAction::kKeepLooking;
+}
+
void NavigationControllerImpl::FindFramesToNavigate(
FrameTreeNode* frame,
ReloadType reload_type,
std::vector<std::unique_ptr<NavigationRequest>>* same_document_loads,
std::vector<std::unique_ptr<NavigationRequest>>* different_document_loads) {
- // A frame pending deletion is not allowed to navigate anymore. It has been
- // deleted and the browser already committed to destroying this
- // RenderFrameHost. See https://crbug.com/930278.
- if (!frame->current_frame_host()->is_active())
- return;
-
DCHECK(pending_entry_);
- DCHECK_GE(last_committed_entry_index_, 0);
FrameNavigationEntry* new_item = pending_entry_->GetFrameEntry(frame);
- // TODO(creis): Store the last committed FrameNavigationEntry to use here,
- // rather than assuming the NavigationEntry has up to date info on subframes.
- FrameNavigationEntry* old_item =
- GetLastCommittedEntry()->GetFrameEntry(frame);
- if (!new_item)
- return;
- // Schedule a load in this frame if the new item isn't for the same item
- // sequence number in the same SiteInstance. Newly restored items may not have
- // a SiteInstance yet, in which case it will be assigned on first commit.
- if (!old_item ||
- new_item->item_sequence_number() != old_item->item_sequence_number() ||
- (new_item->site_instance() &&
- new_item->site_instance() != old_item->site_instance())) {
- // Same document loads happen if the previous item has the same document
- // sequence number. Note that we should treat them as different document if
- // the destination RenderFrameHost (which is necessarily the current
- // RenderFrameHost for same document navigations) doesn't have a last
- // committed page. This case can happen for Ctrl+Back or after a renderer
- // crash.
- if (old_item &&
- new_item->document_sequence_number() ==
- old_item->document_sequence_number() &&
- !frame->current_frame_host()->GetLastCommittedURL().is_empty()) {
- std::unique_ptr<NavigationRequest> navigation_request =
- CreateNavigationRequestFromEntry(
- frame, pending_entry_, new_item, reload_type,
- true /* is_same_document_history_load */,
- false /* is_history_navigation_in_new_child */);
- if (navigation_request) {
- // Only add the request if was properly created. It's possible for the
- // creation to fail in certain cases, e.g. when the URL is invalid.
- same_document_loads->push_back(std::move(navigation_request));
- }
+ auto action = DetermineActionForHistoryNavigation(frame, reload_type);
- // TODO(avi, creis): This is a bug; we should not return here. Rather, we
- // should continue on and navigate all child frames which have also
- // changed. This bug is the cause of <https://crbug.com/542299>, which is
- // a NC_IN_PAGE_NAVIGATION renderer kill.
- //
- // However, this bug is a bandaid over a deeper and worse problem. Doing a
- // pushState immediately after loading a subframe is a race, one that no
- // web page author expects. If we fix this bug, many large websites break.
- // For example, see <https://crbug.com/598043> and the spec discussion at
- // <https://github.com/whatwg/html/issues/1191>.
- //
- // For now, we accept this bug, and hope to resolve the race in a
- // different way that will one day allow us to fix this.
- return;
+ if (action == HistoryNavigationAction::kSameDocument) {
+ std::unique_ptr<NavigationRequest> navigation_request =
+ CreateNavigationRequestFromEntry(
+ frame, pending_entry_, new_item, reload_type,
+ true /* is_same_document_history_load */,
+ false /* is_history_navigation_in_new_child */);
+ if (navigation_request) {
+ // Only add the request if was properly created. It's possible for the
+ // creation to fail in certain cases, e.g. when the URL is invalid.
+ same_document_loads->push_back(std::move(navigation_request));
}
+ // TODO(avi, creis): This is a bug; we should not return here. Rather, we
+ // should continue on and navigate all child frames which have also
+ // changed. This bug is the cause of <https://crbug.com/542299>, which is
+ // a NC_IN_PAGE_NAVIGATION renderer kill.
+ //
+ // However, this bug is a bandaid over a deeper and worse problem. Doing a
+ // pushState immediately after loading a subframe is a race, one that no
+ // web page author expects. If we fix this bug, many large websites break.
+ // For example, see <https://crbug.com/598043> and the spec discussion at
+ // <https://github.com/whatwg/html/issues/1191>.
+ //
+ // For now, we accept this bug, and hope to resolve the race in a
+ // different way that will one day allow us to fix this.
+ return;
+ } else if (action == HistoryNavigationAction::kDifferentDocument) {
std::unique_ptr<NavigationRequest> navigation_request =
CreateNavigationRequestFromEntry(
frame, pending_entry_, new_item, reload_type,
@@ -2838,6 +2904,8 @@ void NavigationControllerImpl::FindFramesToNavigate(
// For a different document, the subframes will be destroyed, so there's
// no need to consider them.
return;
+ } else if (action == HistoryNavigationAction::kStopLooking) {
+ return;
}
for (size_t i = 0; i < frame->child_count(); i++) {
@@ -2935,7 +3003,6 @@ void NavigationControllerImpl::NavigateWithoutEntry(
params.load_type ==
NavigationController::LOAD_TYPE_HTTP_POST /* is_post */,
false /* is_reload */, false /* is_navigation_to_existing_entry */,
- transient_entry_index_ != -1 /* has_interstitial */,
GetLastCommittedEntry())) {
reload_type = ReloadType::NORMAL;
}
@@ -2971,8 +3038,8 @@ void NavigationControllerImpl::NavigateWithoutEntry(
// function.
std::unique_ptr<PendingEntryRef> pending_entry_ref = ReferencePendingEntry();
- node->navigator()->Navigate(std::move(request), reload_type,
- RestoreType::NONE);
+ node->navigator().Navigate(std::move(request), reload_type,
+ RestoreType::NONE);
in_navigate_to_pending_entry_ = false;
}
@@ -2988,7 +3055,12 @@ void NavigationControllerImpl::HandleRendererDebugURL(
DiscardNonCommittedEntries();
return;
}
- frame_tree_node->render_manager()->InitializeRenderFrameForImmediateUse();
+ // The current frame is always a main frame. If IsInitialNavigation() is
+ // true then there have been no navigations and any frames of this tab must
+ // be in the same renderer process. If that has crashed then the only frame
+ // that can be revived is the main frame.
+ frame_tree_node->render_manager()
+ ->InitializeMainRenderFrameForImmediateUse();
}
frame_tree_node->current_frame_host()->HandleRendererDebugURL(url);
}
@@ -3028,21 +3100,22 @@ NavigationControllerImpl::CreateNavigationEntryFromLoadParams(
GURL(url::kAboutBlankURL), params.referrer, params.initiator_origin,
params.source_site_instance.get(), params.transition_type,
params.is_renderer_initiated, extra_headers_crlf, browser_context_,
- blob_url_loader_factory));
+ blob_url_loader_factory, should_replace_current_entry));
}
entry->AddOrUpdateFrameEntry(
node, -1, -1, nullptr,
static_cast<SiteInstanceImpl*>(params.source_site_instance.get()),
params.url, base::nullopt, params.referrer, params.initiator_origin,
- params.redirect_chain, PageState(), "GET", -1, blob_url_loader_factory);
+ params.redirect_chain, PageState(), "GET", -1, blob_url_loader_factory,
+ nullptr /* web_bundle_navigation_info */);
} else {
// Otherwise, create a pending entry for the main frame.
entry = NavigationEntryImpl::FromNavigationEntry(CreateNavigationEntry(
params.url, params.referrer, params.initiator_origin,
params.source_site_instance.get(), params.transition_type,
params.is_renderer_initiated, extra_headers_crlf, browser_context_,
- blob_url_loader_factory));
+ blob_url_loader_factory, should_replace_current_entry));
entry->set_source_site_instance(
static_cast<SiteInstanceImpl*>(params.source_site_instance.get()));
entry->SetRedirectChain(params.redirect_chain);
@@ -3050,7 +3123,6 @@ NavigationControllerImpl::CreateNavigationEntryFromLoadParams(
// Set the FTN ID (only used in non-site-per-process, for tests).
entry->set_frame_tree_node_id(node->frame_tree_node_id());
- entry->set_should_replace_entry(should_replace_current_entry);
entry->set_should_clear_history_list(params.should_clear_history_list);
entry->SetIsOverridingUserAgent(override_user_agent);
entry->set_has_user_gesture(has_user_gesture);
@@ -3227,12 +3299,13 @@ NavigationControllerImpl::CreateNavigationRequestFromLoadParams(
#if defined(OS_ANDROID)
std::string(), /* data_url_as_string */
#endif
- false, /* is_browser_initiated */
+ !params.is_renderer_initiated, /* is_browser_initiated */
network::mojom::IPAddressSpace::kUnknown,
GURL() /* web_bundle_physical_url */,
GURL() /* base_url_override_for_web_bundle */,
node->pending_frame_policy(),
- std::vector<std::string>() /* force_enabled_origin_trials */);
+ std::vector<std::string>() /* force_enabled_origin_trials */,
+ false /* origin_isolation_restricted */);
#if defined(OS_ANDROID)
if (ValidateDataURLAsString(params.data_url_as_string)) {
commit_params->data_url_as_string = params.data_url_as_string->data();
@@ -3268,6 +3341,7 @@ NavigationControllerImpl::CreateNavigationRequestFromEntry(
ReloadType reload_type,
bool is_same_document_history_load,
bool is_history_navigation_in_new_child_frame) {
+ DCHECK(frame_entry);
GURL dest_url = frame_entry->url();
base::Optional<url::Origin> origin_to_commit =
frame_entry->committed_origin();
@@ -3430,10 +3504,16 @@ void NavigationControllerImpl::LoadPostCommitErrorPage(
const GURL& url,
const std::string& error_page_html,
net::Error error) {
- // A frame pending deletion is not allowed to navigate, the browser is already
- // committed to destroying this frame so ignore loading the error page.
- if (!static_cast<RenderFrameHostImpl*>(render_frame_host)->is_active())
+ // Only active frames can load post-commit error pages:
+ // - If the frame is in pending deletion, the browser already committed to
+ // destroying this RenderFrameHost so ignore loading the error page.
+ // - If the frame is in back-forward cache, it's not allowed to navigate as it
+ // should remain frozen. Ignore the request and evict the document from
+ // back-forward cache.
+ if (static_cast<RenderFrameHostImpl*>(render_frame_host)
+ ->IsInactiveAndDisallowReactivation()) {
return;
+ }
FrameTreeNode* node =
static_cast<RenderFrameHostImpl*>(render_frame_host)->frame_tree_node();
@@ -3444,6 +3524,11 @@ void NavigationControllerImpl::LoadPostCommitErrorPage(
mojom::CommitNavigationParamsPtr commit_params =
CreateCommitNavigationParams();
+ // Error pages have a fully permissive FramePolicy.
+ // TODO(arthursonzogni): Consider providing the minimal capabilities to the
+ // error pages.
+ commit_params->frame_policy = blink::FramePolicy();
+
std::unique_ptr<NavigationRequest> navigation_request =
NavigationRequest::CreateBrowserInitiated(
node, std::move(common_params), std::move(commit_params),
@@ -3478,28 +3563,14 @@ void NavigationControllerImpl::DiscardNonCommittedEntries() {
// Avoid sending a notification if there is nothing to discard.
// TODO(mthiesse): Temporarily checking failed_pending_entry_id_ to help
// diagnose https://bugs.chromium.org/p/chromium/issues/detail?id=1007570.
- if (!pending_entry_ && transient_entry_index_ == -1 &&
- failed_pending_entry_id_ == 0) {
+ if (!pending_entry_ && failed_pending_entry_id_ == 0) {
return;
}
-
DiscardPendingEntry(false);
- DiscardTransientEntry();
if (delegate_)
delegate_->NotifyNavigationStateChanged(INVALIDATE_TYPE_ALL);
}
-void NavigationControllerImpl::DiscardTransientEntry() {
- if (transient_entry_index_ == -1)
- return;
- entries_.erase(entries_.begin() + transient_entry_index_);
- if (last_committed_entry_index_ > transient_entry_index_)
- last_committed_entry_index_--;
- if (pending_entry_index_ > transient_entry_index_)
- pending_entry_index_--;
- transient_entry_index_ = -1;
-}
-
int NavigationControllerImpl::GetEntryIndexWithUniqueID(
int nav_entry_id) const {
for (int i = static_cast<int>(entries_.size()) - 1; i >= 0; --i) {
@@ -3509,27 +3580,6 @@ int NavigationControllerImpl::GetEntryIndexWithUniqueID(
return -1;
}
-NavigationEntryImpl* NavigationControllerImpl::GetTransientEntry() {
- if (transient_entry_index_ == -1)
- return nullptr;
- return entries_[transient_entry_index_].get();
-}
-
-void NavigationControllerImpl::SetTransientEntry(
- std::unique_ptr<NavigationEntry> entry) {
- // Discard any current transient entry, we can only have one at a time.
- DiscardTransientEntry();
- int index = 0;
- if (last_committed_entry_index_ != -1)
- index = last_committed_entry_index_ + 1;
- entries_.insert(entries_.begin() + index,
- NavigationEntryImpl::FromNavigationEntry(std::move(entry)));
- if (pending_entry_index_ >= index)
- pending_entry_index_++;
- transient_entry_index_ = index;
- delegate_->NotifyNavigationStateChanged(INVALIDATE_TYPE_ALL);
-}
-
void NavigationControllerImpl::InsertEntriesFrom(
NavigationControllerImpl* source,
int max_index) {
@@ -3639,8 +3689,6 @@ void NavigationControllerImpl::PendingEntryRefDeleted(PendingEntryRef* ref) {
// Do not leave the pending entry visible if it has an invalid URL, since this
// might be formatted in an unexpected or unsafe way.
// TODO(creis): Block navigations to invalid URLs in https://crbug.com/850824.
- //
- // Note: don't touch the transient entry, since an interstitial may exist.
bool should_preserve_entry =
(pending_entry_ == GetVisibleEntry()) &&
pending_entry_->GetURL().is_valid() &&
diff --git a/chromium/content/browser/frame_host/navigation_controller_impl.h b/chromium/content/browser/frame_host/navigation_controller_impl.h
index e7ec7e7e204..47e2c5a0e7b 100644
--- a/chromium/content/browser/frame_host/navigation_controller_impl.h
+++ b/chromium/content/browser/frame_host/navigation_controller_impl.h
@@ -93,8 +93,6 @@ class CONTENT_EXPORT NavigationControllerImpl : public NavigationController {
void DiscardNonCommittedEntries() override;
NavigationEntryImpl* GetPendingEntry() override;
int GetPendingEntryIndex() override;
- NavigationEntryImpl* GetTransientEntry() override;
- void SetTransientEntry(std::unique_ptr<NavigationEntry> entry) override;
void LoadURL(const GURL& url,
const Referrer& referrer,
ui::PageTransition type,
@@ -317,8 +315,8 @@ class CONTENT_EXPORT NavigationControllerImpl : public NavigationController {
// requests corresponding to the current pending entry.
std::unique_ptr<PendingEntryRef> ReferencePendingEntry();
- // Like NavigationController::CreateNavigationEntry, but takes an extra
- // |source_site_instance| argument.
+ // Like NavigationController::CreateNavigationEntry, but takes extra arguments
+ // like |source_site_instance| and |should_replace_entry|.
static std::unique_ptr<NavigationEntryImpl> CreateNavigationEntry(
const GURL& url,
Referrer referrer,
@@ -328,7 +326,8 @@ class CONTENT_EXPORT NavigationControllerImpl : public NavigationController {
bool is_renderer_initiated,
const std::string& extra_headers,
BrowserContext* browser_context,
- scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory);
+ scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
+ bool should_replace_entry);
private:
friend class RestoreHelper;
@@ -337,6 +336,17 @@ class CONTENT_EXPORT NavigationControllerImpl : public NavigationController {
FRIEND_TEST_ALL_PREFIXES(TimeSmoother, SingleDuplicate);
FRIEND_TEST_ALL_PREFIXES(TimeSmoother, ManyDuplicates);
FRIEND_TEST_ALL_PREFIXES(TimeSmoother, ClockBackwardsJump);
+ FRIEND_TEST_ALL_PREFIXES(NavigationControllerTest,
+ PostThenReplaceStateThenReload);
+
+ // Defines possible actions that are returned by
+ // DetermineActionForHistoryNavigation().
+ enum class HistoryNavigationAction {
+ kStopLooking,
+ kKeepLooking,
+ kSameDocument,
+ kDifferentDocument,
+ };
// Helper class to smooth out runs of duplicate timestamps while still
// allowing time to jump backwards.
@@ -352,6 +362,23 @@ class CONTENT_EXPORT NavigationControllerImpl : public NavigationController {
base::Time high_water_mark_;
};
+ // The repost dialog is suppressed during testing. However, it should be shown
+ // in some tests. This allows a test to elect to allow the repost dialog to
+ // show for a scoped duration.
+ class CONTENT_EXPORT ScopedShowRepostDialogForTesting {
+ public:
+ ScopedShowRepostDialogForTesting();
+ ~ScopedShowRepostDialogForTesting();
+
+ ScopedShowRepostDialogForTesting(const ScopedShowRepostDialogForTesting&) =
+ delete;
+ ScopedShowRepostDialogForTesting& operator=(
+ const ScopedShowRepostDialogForTesting&) = delete;
+
+ private:
+ const bool was_disallowed_;
+ };
+
// Navigates in session history to the given index. If
// |sandbox_frame_tree_node_id| is valid, then this request came
// from a sandboxed iframe with top level navigation disallowed. This
@@ -365,6 +392,13 @@ class CONTENT_EXPORT NavigationControllerImpl : public NavigationController {
void NavigateToExistingPendingEntry(ReloadType reload_type,
int sandboxed_source_frame_tree_node_id);
+ // Helper function used by FindFramesToNavigate to determine the appropriate
+ // action to take for a particular frame while navigating to
+ // |pending_entry_|.
+ HistoryNavigationAction DetermineActionForHistoryNavigation(
+ FrameTreeNode* frame,
+ ReloadType reload_type);
+
// Recursively identifies which frames need to be navigated for a navigation
// to |pending_entry_|, starting at |frame| and exploring its children.
// |same_document_loads| and |different_document_loads| will be filled with
@@ -507,9 +541,6 @@ class CONTENT_EXPORT NavigationControllerImpl : public NavigationController {
// Removes the entry at |index|, as long as it is not the current entry.
void RemoveEntryAtIndexInternal(int index);
- // Discards only the transient entry.
- void DiscardTransientEntry();
-
// If we have the maximum number of entries, remove the oldest entry that is
// marked to be skipped on back/forward button, in preparation to add another.
// If no entry is skippable, then the oldest entry will be pruned.
@@ -524,8 +555,7 @@ class CONTENT_EXPORT NavigationControllerImpl : public NavigationController {
// Inserts up to |max_index| entries from |source| into this. This does NOT
// adjust any of the members that reference entries_
- // (last_committed_entry_index_, pending_entry_index_ or
- // transient_entry_index_).
+ // (last_committed_entry_index_ or pending_entry_index_)
void InsertEntriesFrom(NavigationControllerImpl* source, int max_index);
// Returns the navigation index that differs from the current entry by the
@@ -603,13 +633,6 @@ class CONTENT_EXPORT NavigationControllerImpl : public NavigationController {
// pending_entry_ is a new entry (created by LoadURL).
int pending_entry_index_ = -1;
- // The index for the entry that is shown until a navigation occurs. This is
- // used for interstitial pages. -1 if there are no such entry.
- // Note that this entry really appears in the list of entries, but only
- // temporarily (until the next navigation). Any index pointing to an entry
- // after the transient entry will become invalid if you navigate forward.
- int transient_entry_index_ = -1;
-
// The delegate associated with the controller. Possibly NULL during
// setup.
NavigationControllerDelegate* delegate_;
diff --git a/chromium/content/browser/frame_host/navigation_controller_impl_browsertest.cc b/chromium/content/browser/frame_host/navigation_controller_impl_browsertest.cc
index df0eef5387b..e6e3721a6eb 100644
--- a/chromium/content/browser/frame_host/navigation_controller_impl_browsertest.cc
+++ b/chromium/content/browser/frame_host/navigation_controller_impl_browsertest.cc
@@ -19,7 +19,6 @@
#include "base/sequenced_task_runner.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/task/post_task.h"
#include "base/test/bind_test_util.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/sequenced_task_runner_handle.h"
@@ -608,10 +607,27 @@ namespace {
// current entry.
bool RendererLocationReplace(Shell* shell, const GURL& url) {
WebContents* web_contents = shell->web_contents();
+ NavigationControllerImpl& controller =
+ static_cast<NavigationControllerImpl&>(web_contents->GetController());
WaitForLoadStop(web_contents);
- TestNavigationObserver same_tab_observer(web_contents, 1);
+ TestNavigationManager navigation_manager(web_contents, url);
+ const GURL& current_url = web_contents->GetMainFrame()->GetLastCommittedURL();
EXPECT_TRUE(ExecJs(shell, JsReplace("window.location.replace($1)", url)));
- same_tab_observer.Wait();
+ // Observe pending entry if it's not a same-document navigation. We can't
+ // observe same-document navigations because it might finish in the renderer,
+ // only telling the browser side at the end.
+ if (!current_url.EqualsIgnoringRef(url)) {
+ EXPECT_TRUE(navigation_manager.WaitForRequestStart());
+ // Both should_replace_entry (in the pending NavigationEntry) and
+ // should_replace_current_entry (in NavigationRequest params) should be
+ // true.
+ EXPECT_TRUE(controller.GetPendingEntry()->should_replace_entry());
+ EXPECT_TRUE(
+ NavigationRequest::From(navigation_manager.GetNavigationHandle())
+ ->common_params()
+ .should_replace_current_entry);
+ }
+ navigation_manager.WaitForNavigationFinished();
if (!IsLastCommittedEntryOfPageType(web_contents, PAGE_TYPE_NORMAL))
return false;
return web_contents->GetLastCommittedURL() == url;
@@ -1086,8 +1102,8 @@ IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest, ErrorPageReplacement) {
NavigationController& controller = shell()->web_contents()->GetController();
GURL error_url = embedded_test_server()->GetURL("/close-socket");
- base::PostTask(FROM_HERE, {BrowserThread::IO},
- base::BindOnce(&net::URLRequestFailedJob::AddUrlHandler));
+ GetIOThreadTaskRunner({})->PostTask(
+ FROM_HERE, base::BindOnce(&net::URLRequestFailedJob::AddUrlHandler));
EXPECT_TRUE(NavigateToURL(shell(), GURL(url::kAboutBlankURL)));
EXPECT_EQ(1, controller.GetEntryCount());
@@ -6686,15 +6702,15 @@ IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest, PostInSubframe) {
}
// Tests that POST body is not lost when decidePolicyForNavigation tells the
-// renderer to route the request via FrameHostMsg_OpenURL sent to the browser.
+// renderer to route the request via OpenURL mojo method sent to the browser.
// See also https://crbug.com/344348.
IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest, PostViaOpenUrlMsg) {
GURL main_url(
embedded_test_server()->GetURL("/form_that_posts_to_echoall.html"));
EXPECT_TRUE(NavigateToURL(shell(), main_url));
- // Ask the renderer to go through OpenURL FrameHostMsg_OpenURL IPC message.
- // Without this, the test wouldn't repro https://crbug.com/344348.
+ // Ask the renderer to go through OpenURL Mojo method. Without this, the test
+ // wouldn't repro https://crbug.com/344348.
shell()
->web_contents()
->GetMutableRendererPrefs()
@@ -10597,4 +10613,571 @@ IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
}
}
+// Verify that if a history navigation only affects a subframe that was
+// removed, the main frame should not be reloaded. See
+// httos://crbug.com/705550. This test checks the case where the attempted
+// subframe navigation was same-document.
+//
+// TODO(alexmos, creis): Consider changing this behavior to auto-traverse
+// history to the first entry which finds a frame to navigate.
+IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
+ GoBackSameDocumentInRemovedSubframe) {
+ GURL main_url = embedded_test_server()->GetURL(
+ "a.com", "/cross_site_iframe_factory.html?a(b,c)");
+ ASSERT_TRUE(NavigateToURL(shell(), main_url));
+
+ NavigationControllerImpl& controller = contents()->GetController();
+ ASSERT_EQ(1, controller.GetEntryCount());
+
+ FrameTreeNode* ftn_a = contents()->GetFrameTree()->root();
+ FrameTreeNode* ftn_b = ftn_a->child_at(0);
+ FrameTreeNode* ftn_c = ftn_a->child_at(1);
+
+ // Set some state in the main frame that we can check later to make sure it
+ // wasn't reloaded.
+ EXPECT_TRUE(ExecJs(ftn_a, "window.state = 'a';"));
+
+ // history.pushState() in the main frame.
+ GURL ps2_url(embedded_test_server()->GetURL("a.com", "/ps2.html"));
+ {
+ FrameNavigateParamsCapturer capturer(ftn_a);
+ ASSERT_TRUE(
+ ExecuteScript(ftn_a, "history.pushState({}, 'page 2', 'ps2.html')"));
+ capturer.Wait();
+ EXPECT_EQ(2, controller.GetEntryCount());
+ EXPECT_EQ(ps2_url, controller.GetLastCommittedEntry()->GetURL());
+ }
+
+ // history.pushState() twice in the c subframe.
+ {
+ FrameNavigateParamsCapturer capturer(ftn_c);
+ ASSERT_TRUE(
+ ExecuteScript(ftn_c, "history.pushState({}, 'page 3', 'ps3.html')"));
+ capturer.Wait();
+ EXPECT_EQ(3, controller.GetEntryCount());
+ }
+ {
+ FrameNavigateParamsCapturer capturer(ftn_c);
+ ASSERT_TRUE(
+ ExecuteScript(ftn_c, "history.pushState({}, 'page 4', 'ps4.html')"));
+ capturer.Wait();
+ EXPECT_EQ(4, controller.GetEntryCount());
+ }
+
+ // Navigate frame b cross-document.
+ GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
+ TestNavigationObserver navigation_observer(contents());
+ EXPECT_TRUE(NavigateFrameToURL(ftn_b, url_b));
+ navigation_observer.WaitForNavigationFinished();
+ EXPECT_EQ(5, controller.GetEntryCount());
+
+ // Go back. This navigates frame b back, and we should be at next-to-last
+ // entry (of 5 total) after that's done.
+ EXPECT_EQ(4, controller.GetCurrentEntryIndex());
+ {
+ TestNavigationObserver navigation_observer(shell()->web_contents());
+ shell()->GoBackOrForward(-1);
+ navigation_observer.Wait();
+ }
+ EXPECT_EQ(5, controller.GetEntryCount());
+ EXPECT_EQ(3, controller.GetCurrentEntryIndex());
+
+ // Last committed entry's URL should be ps2.html, corresponding to latest
+ // navigation in the main frame.
+ EXPECT_EQ(ps2_url, controller.GetLastCommittedEntry()->GetURL());
+
+ // Set some state in frame b that we can check later to make sure it wasn't
+ // reloaded.
+ EXPECT_TRUE(ExecJs(ftn_b, "window.state='b';"));
+
+ // Remove the c subframe.
+ RenderFrameDeletedObserver deleted_observer(ftn_c->current_frame_host());
+ EXPECT_TRUE(ExecJs(ftn_a,
+ "var f = document.querySelectorAll('iframe')[1];"
+ "f.parentNode.removeChild(f);"));
+ deleted_observer.WaitUntilDeleted();
+
+ // Try going back. The target of this navigation had been a same-document
+ // navigation in subframe c (to ps3.html), but since c has been removed, this
+ // shouldn't reload frames a or b. Frame |a| also shouldn't fire redundant
+ // popstate events.
+ EXPECT_TRUE(ExecJs(ftn_a, "window.popstateCalled = false"));
+ EXPECT_TRUE(ExecJs(
+ ftn_a, "window.onpopstate = () => { window.popstateCalled = true; }"));
+ shell()->GoBackOrForward(-1);
+ EXPECT_TRUE(WaitForLoadStop(contents()));
+ EXPECT_EQ(ps2_url, controller.GetLastCommittedEntry()->GetURL());
+ EXPECT_EQ("a", EvalJs(ftn_a, "window.state"));
+ EXPECT_EQ("b", EvalJs(ftn_b, "window.state"));
+ EXPECT_EQ(false, EvalJs(ftn_a, "window.popstateCalled"));
+
+ // The corresponding NavigationEntry should now be the current one.
+ EXPECT_EQ(5, controller.GetEntryCount());
+ EXPECT_EQ(2, controller.GetCurrentEntryIndex());
+
+ // Try going back again. Similarly, this would've gone back to c's original
+ // URL when it was loaded from main_url, but since c is removed, this
+ // shouldn't reload frames a or b or fire popstate events.
+ shell()->GoBackOrForward(-1);
+ EXPECT_TRUE(WaitForLoadStop(contents()));
+ EXPECT_EQ(ps2_url, controller.GetLastCommittedEntry()->GetURL());
+ EXPECT_EQ("a", EvalJs(ftn_a, "window.state"));
+ EXPECT_EQ("b", EvalJs(ftn_b, "window.state"));
+ EXPECT_EQ(false, EvalJs(ftn_a, "window.popstateCalled"));
+ EXPECT_EQ(5, controller.GetEntryCount());
+ EXPECT_EQ(1, controller.GetCurrentEntryIndex());
+
+ // Going back now should result in a same-document navigation in the main
+ // frame to main_url.
+ shell()->GoBackOrForward(-1);
+ EXPECT_TRUE(WaitForLoadStop(contents()));
+ EXPECT_EQ(main_url, controller.GetLastCommittedEntry()->GetURL());
+ EXPECT_EQ("a", EvalJs(ftn_a, "window.state"));
+ EXPECT_EQ("b", EvalJs(ftn_b, "window.state"));
+ EXPECT_EQ(true, EvalJs(ftn_a, "window.popstateCalled"));
+ EXPECT_EQ(5, controller.GetEntryCount());
+ EXPECT_EQ(0, controller.GetCurrentEntryIndex());
+
+ // Go forward. This should navigate the main frame same-document to
+ // ps2.html.
+ {
+ FrameNavigateParamsCapturer capturer(ftn_a);
+ shell()->GoBackOrForward(1);
+ capturer.Wait();
+ }
+ EXPECT_EQ(5, controller.GetEntryCount());
+ EXPECT_EQ(1, controller.GetCurrentEntryIndex());
+ EXPECT_EQ(ps2_url, ftn_a->current_frame_host()->GetLastCommittedURL());
+ EXPECT_EQ("a", EvalJs(ftn_a, "window.state"));
+ EXPECT_EQ("b", EvalJs(ftn_b, "window.state"));
+
+ // Go forward three steps. This should navigate the subframe b
+ // cross-document to url_b (erasing its window.state).
+ {
+ FrameNavigateParamsCapturer capturer(ftn_b);
+ shell()->GoBackOrForward(3);
+ capturer.Wait();
+ }
+ EXPECT_EQ(url_b, ftn_b->current_frame_host()->GetLastCommittedURL());
+ EXPECT_EQ(5, controller.GetEntryCount());
+ EXPECT_EQ(4, controller.GetCurrentEntryIndex());
+ EXPECT_EQ("a", EvalJs(ftn_a, "window.state"));
+ EXPECT_EQ(nullptr, EvalJs(ftn_b, "window.state"));
+}
+
+// Verify that if a history navigation only affects a subframe that was
+// removed, the main frame should not be reloaded. See
+// httos://crbug.com/705550. This test is similar to the one above, but checks
+// the case where the attempted subframe navigation was cross-document rather
+// than same-document.
+//
+// TODO(alexmos, creis): Consider changing this behavior to auto-traverse
+// history to the first entry which finds a frame to navigate.
+IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
+ GoBackCrossDocumentInRemovedSubframe) {
+ GURL main_url = embedded_test_server()->GetURL(
+ "a.com", "/cross_site_iframe_factory.html?a(b)");
+ ASSERT_TRUE(NavigateToURL(shell(), main_url));
+
+ NavigationControllerImpl& controller = contents()->GetController();
+ ASSERT_EQ(1, controller.GetEntryCount());
+
+ FrameTreeNode* ftn_a = contents()->GetFrameTree()->root();
+ FrameTreeNode* ftn_b = ftn_a->child_at(0);
+
+ // Set some state in the main frame that we can check later to make sure it
+ // wasn't reloaded.
+ EXPECT_TRUE(ExecJs(ftn_a, "window.state = 'a';"));
+
+ // Navigate frame b cross-document.
+ GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
+ {
+ TestNavigationObserver navigation_observer(contents());
+ EXPECT_TRUE(NavigateFrameToURL(ftn_b, url_b));
+ navigation_observer.WaitForNavigationFinished();
+ }
+ EXPECT_EQ(2, controller.GetEntryCount());
+ EXPECT_EQ(1, controller.GetCurrentEntryIndex());
+
+ // Delete frame b.
+ RenderFrameDeletedObserver deleted_observer(
+ ftn_a->child_at(0)->current_frame_host());
+ EXPECT_TRUE(ExecJs(ftn_a,
+ "var f = document.querySelector('iframe');"
+ "f.parentNode.removeChild(f);"));
+ deleted_observer.WaitUntilDeleted();
+ EXPECT_EQ(2, controller.GetEntryCount());
+ EXPECT_EQ(1, controller.GetCurrentEntryIndex());
+
+ // Go back. Since this history navigation targets a non-existent subframe,
+ // the main frame shouldn't be reloaded, and the corresponding
+ // NavigationEntry should become the current one.
+ shell()->GoBackOrForward(-1);
+ EXPECT_TRUE(WaitForLoadStop(contents()));
+ EXPECT_EQ(main_url, controller.GetLastCommittedEntry()->GetURL());
+ EXPECT_EQ("a", EvalJs(ftn_a, "window.state"));
+ EXPECT_EQ(2, controller.GetEntryCount());
+ EXPECT_EQ(0, controller.GetCurrentEntryIndex());
+ EXPECT_FALSE(controller.CanGoBack());
+ EXPECT_TRUE(controller.CanGoForward());
+
+ // Go forward and expect similar behavior.
+ shell()->GoBackOrForward(1);
+ EXPECT_TRUE(WaitForLoadStop(contents()));
+ EXPECT_EQ(main_url, controller.GetLastCommittedEntry()->GetURL());
+ EXPECT_EQ("a", EvalJs(ftn_a, "window.state"));
+ EXPECT_EQ(2, controller.GetEntryCount());
+ EXPECT_EQ(1, controller.GetCurrentEntryIndex());
+ EXPECT_TRUE(controller.CanGoBack());
+ EXPECT_FALSE(controller.CanGoForward());
+}
+
+// This test is similar to the one above, but checks the case where the first
+// attempted navigation after subframe removal is a forward navigation
+// rather than a back navigation.
+IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
+ GoForwardCrossDocumentInRemovedSubframe) {
+ GURL main_url = embedded_test_server()->GetURL(
+ "a.com", "/cross_site_iframe_factory.html?a(b)");
+ ASSERT_TRUE(NavigateToURL(shell(), main_url));
+
+ NavigationControllerImpl& controller = contents()->GetController();
+ ASSERT_EQ(1, controller.GetEntryCount());
+
+ FrameTreeNode* ftn_a = contents()->GetFrameTree()->root();
+ FrameTreeNode* ftn_b = contents()->GetFrameTree()->root()->child_at(0);
+ GURL orig_subframe_url(ftn_b->current_frame_host()->GetLastCommittedURL());
+
+ // Set some state in the main frame that we can check later to make sure it
+ // wasn't reloaded.
+ EXPECT_TRUE(ExecJs(ftn_a, "window.state = 'a';"));
+
+ // Navigate frame b cross-document twice.
+ GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
+ {
+ TestNavigationObserver navigation_observer(contents());
+ EXPECT_TRUE(NavigateFrameToURL(ftn_b, url_b));
+ navigation_observer.WaitForNavigationFinished();
+ }
+ EXPECT_EQ(2, controller.GetEntryCount());
+ EXPECT_EQ(1, controller.GetCurrentEntryIndex());
+ GURL url_c(embedded_test_server()->GetURL("c.com", "/title2.html"));
+ {
+ TestNavigationObserver navigation_observer(contents());
+ EXPECT_TRUE(NavigateFrameToURL(ftn_b, url_c));
+ navigation_observer.WaitForNavigationFinished();
+ }
+ EXPECT_EQ(3, controller.GetEntryCount());
+ EXPECT_EQ(2, controller.GetCurrentEntryIndex());
+
+ // history.pushState() in the main frame.
+ GURL ps_url(embedded_test_server()->GetURL("a.com", "/ps.html"));
+ {
+ FrameNavigateParamsCapturer capturer(ftn_a);
+ ASSERT_TRUE(
+ ExecuteScript(ftn_a, "history.pushState({}, 'push state', 'ps.html')"));
+ capturer.Wait();
+ EXPECT_EQ(4, controller.GetEntryCount());
+ EXPECT_EQ(ps_url, controller.GetLastCommittedEntry()->GetURL());
+ }
+
+ // Go back three stops, bringing back the original URL in the subframe.
+ // (Note that going back by three steps all at once won't work as expected
+ // due to https://crbug.com/542299, where finding that the main frame needs
+ // to navigate same-document ignores any subframe navigations that should
+ // also be part of the history entry navigation.)
+ shell()->GoBackOrForward(-1);
+ EXPECT_TRUE(WaitForLoadStop(contents()));
+ EXPECT_EQ(4, controller.GetEntryCount());
+ EXPECT_EQ(2, controller.GetCurrentEntryIndex());
+ EXPECT_EQ(main_url, controller.GetLastCommittedEntry()->GetURL());
+ shell()->GoBackOrForward(-2);
+ EXPECT_TRUE(WaitForLoadStop(contents()));
+ EXPECT_EQ(4, controller.GetEntryCount());
+ EXPECT_EQ(0, controller.GetCurrentEntryIndex());
+ EXPECT_EQ(orig_subframe_url,
+ ftn_b->current_frame_host()->GetLastCommittedURL());
+
+ // Delete frame b.
+ RenderFrameDeletedObserver deleted_observer(
+ ftn_a->child_at(0)->current_frame_host());
+ EXPECT_TRUE(ExecJs(ftn_a,
+ "var f = document.querySelector('iframe');"
+ "f.parentNode.removeChild(f);"));
+ deleted_observer.WaitUntilDeleted();
+ EXPECT_EQ(4, controller.GetEntryCount());
+ EXPECT_EQ(0, controller.GetCurrentEntryIndex());
+
+ // Go forward. Since this navigation attempt targets a non-existent
+ // subframe, the main frame shouldn't be reloaded, and the corresponding
+ // NavigationEntry should become current. There should be no redundant
+ // popstate events.
+ EXPECT_TRUE(ExecJs(ftn_a, "window.popstateCalled = false"));
+ EXPECT_TRUE(ExecJs(
+ ftn_a, "window.onpopstate = () => { window.popstateCalled = true; }"));
+ shell()->GoBackOrForward(1);
+ EXPECT_TRUE(WaitForLoadStop(contents()));
+ EXPECT_EQ("a", EvalJs(ftn_a, "window.state"));
+ EXPECT_EQ(false, EvalJs(ftn_a, "window.popstateCalled"));
+ EXPECT_EQ(4, controller.GetEntryCount());
+ EXPECT_EQ(1, controller.GetCurrentEntryIndex());
+
+ // Go forward again and expect similar behavior.
+ shell()->GoBackOrForward(1);
+ EXPECT_TRUE(WaitForLoadStop(contents()));
+ EXPECT_EQ("a", EvalJs(ftn_a, "window.state"));
+ EXPECT_EQ(false, EvalJs(ftn_a, "window.popstateCalled"));
+ EXPECT_EQ(4, controller.GetEntryCount());
+ EXPECT_EQ(2, controller.GetCurrentEntryIndex());
+
+ // Go forward yet again. This should result in a same-document navigation in
+ // the main frame to ps.html.
+ shell()->GoBackOrForward(1);
+ EXPECT_TRUE(WaitForLoadStop(contents()));
+ EXPECT_EQ(true, EvalJs(ftn_a, "window.popstateCalled"));
+ EXPECT_EQ(4, controller.GetEntryCount());
+ EXPECT_EQ(3, controller.GetCurrentEntryIndex());
+ EXPECT_TRUE(controller.CanGoBack());
+ EXPECT_FALSE(controller.CanGoForward());
+ EXPECT_EQ(ps_url, controller.GetLastCommittedEntry()->GetURL());
+}
+
+// Check that if we ignore a history entry that targets a removed subframe, the
+// entry still stays around and is used properly when the subframe gets
+// recreated.
+IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
+ RestoreRemovedSubframe) {
+ // Start on a page with a same-site iframe. It's important that this iframe
+ // isn't dynamically inserted for history navigations in this test.
+ GURL main_url =
+ embedded_test_server()->GetURL("a.com", "/page_with_iframe.html");
+ ASSERT_TRUE(NavigateToURL(shell(), main_url));
+ NavigationControllerImpl& controller = contents()->GetController();
+ FrameTreeNode* ftn_a = contents()->GetFrameTree()->root();
+ FrameTreeNode* ftn_b = ftn_a->child_at(0);
+ EXPECT_EQ(embedded_test_server()->GetURL("a.com", "/title1.html"),
+ ftn_b->current_frame_host()->GetLastCommittedURL());
+
+ // Set some state in the main frame that we can check later to make sure it
+ // wasn't reloaded.
+ EXPECT_TRUE(ExecJs(ftn_a, "window.state = 'a';"));
+
+ // Navigate subframe cross-site twice.
+ GURL url_b(embedded_test_server()->GetURL("b.com", "/title2.html"));
+ {
+ TestNavigationObserver navigation_observer(contents());
+ EXPECT_TRUE(NavigateFrameToURL(ftn_b, url_b));
+ navigation_observer.WaitForNavigationFinished();
+ }
+
+ GURL url_c(embedded_test_server()->GetURL("c.com", "/title3.html"));
+ {
+ TestNavigationObserver navigation_observer(contents());
+ EXPECT_TRUE(NavigateFrameToURL(ftn_b, url_c));
+ navigation_observer.WaitForNavigationFinished();
+ }
+ EXPECT_EQ(3, controller.GetEntryCount());
+ EXPECT_EQ(2, controller.GetCurrentEntryIndex());
+
+ // Remove subframe.
+ RenderFrameDeletedObserver deleted_observer(
+ ftn_a->child_at(0)->current_frame_host());
+ EXPECT_TRUE(ExecJs(ftn_a,
+ "var f = document.querySelector('iframe');"
+ "f.parentNode.removeChild(f);"));
+ deleted_observer.WaitUntilDeleted();
+
+ // Go back. This normally attempts to navigate the subframe from url_c to
+ // url_b, but the subframe no longer exists. Check that the main frame isn't
+ // reloaded.
+ controller.GoBack();
+ EXPECT_TRUE(WaitForLoadStop(contents()));
+ EXPECT_EQ("a", EvalJs(ftn_a, "window.state"));
+ EXPECT_EQ(3, controller.GetEntryCount());
+ EXPECT_EQ(1, controller.GetCurrentEntryIndex());
+
+ // Navigate main frame to another url.
+ EXPECT_TRUE(NavigateToURL(
+ shell(), embedded_test_server()->GetURL("a.com", "/title2.html")));
+ EXPECT_EQ(3, controller.GetEntryCount());
+ EXPECT_EQ(2, controller.GetCurrentEntryIndex());
+
+ // Now navigate back. This should go back to |main_url|, reloading the
+ // subframe at |url_b|, which is its URL in the history entry that we ignored
+ // during the last back navigation above.
+ controller.GoBack();
+ EXPECT_TRUE(WaitForLoadStop(contents()));
+ EXPECT_EQ(3, controller.GetEntryCount());
+ EXPECT_EQ(1, controller.GetCurrentEntryIndex());
+ EXPECT_EQ(url_b,
+ ftn_a->child_at(0)->current_frame_host()->GetLastCommittedURL());
+}
+
+// Check that when we go back in a subframe on a page that contains another
+// frame which is crashed, we not only go back in the subframe but also reload
+// the sad frame. This restores restore the state covered by the corresponding
+// NavigationEntry more faithfully.
+IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
+ ReloadSadFrameWithSubframeHistoryNavigation) {
+ // Ensure this test runs in full site-per-process mode so that we can get a
+ // sad frame on Android.
+ IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
+
+ // Start on a page with two iframes.
+ GURL main_url = embedded_test_server()->GetURL(
+ "a.com", "/cross_site_iframe_factory.html?a(b,c)");
+ ASSERT_TRUE(NavigateToURL(shell(), main_url));
+ NavigationControllerImpl& controller = contents()->GetController();
+ FrameTreeNode* ftn_a = contents()->GetFrameTree()->root();
+ FrameTreeNode* ftn_b = ftn_a->child_at(0);
+ FrameTreeNode* ftn_c = ftn_a->child_at(1);
+ GURL url_b(ftn_b->current_frame_host()->GetLastCommittedURL());
+ GURL url_c(ftn_c->current_frame_host()->GetLastCommittedURL());
+
+ // Navigate first subframe cross-site.
+ GURL url_d(embedded_test_server()->GetURL("d.com", "/title2.html"));
+ {
+ TestNavigationObserver navigation_observer(contents());
+ EXPECT_TRUE(NavigateFrameToURL(ftn_b, url_d));
+ navigation_observer.WaitForNavigationFinished();
+ }
+ EXPECT_EQ(2, controller.GetEntryCount());
+ EXPECT_EQ(1, controller.GetCurrentEntryIndex());
+ EXPECT_EQ(url_d, ftn_b->current_frame_host()->GetLastCommittedURL());
+
+ // Crash second subframe.
+ RenderProcessHost* process_c = ftn_c->current_frame_host()->GetProcess();
+ RenderProcessHostWatcher crash_observer(
+ process_c, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
+ process_c->Shutdown(0);
+ crash_observer.Wait();
+ EXPECT_FALSE(ftn_c->current_frame_host()->IsRenderFrameLive());
+ EXPECT_TRUE(ftn_c->current_frame_host()->GetLastCommittedURL().is_empty());
+
+ // Go back. This should navigate the first subframe back to b.com, and it
+ // should also restore the subframe in c.com.
+ controller.GoBack();
+ EXPECT_TRUE(WaitForLoadStop(contents()));
+ EXPECT_EQ(2, controller.GetEntryCount());
+ EXPECT_EQ(0, controller.GetCurrentEntryIndex());
+ EXPECT_EQ(url_b, ftn_b->current_frame_host()->GetLastCommittedURL());
+ EXPECT_EQ(url_c, ftn_c->current_frame_host()->GetLastCommittedURL());
+ EXPECT_TRUE(ftn_c->current_frame_host()->IsRenderFrameLive());
+}
+
+// Regression test for https://crbug.com/1088354, where a different-document
+// load was incorrectly scheduled for a history navigation in a subframe that
+// had no existing and no target FrameNavigationEntry.
+IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
+ SubframeGoesBackAndSiblingHasNoFrameEntry) {
+ // Start on a page with a same-site iframe.
+ GURL main_url =
+ embedded_test_server()->GetURL("a.com", "/page_with_iframe.html");
+ ASSERT_TRUE(NavigateToURL(shell(), main_url));
+ NavigationControllerImpl& controller = contents()->GetController();
+ FrameTreeNode* ftn_a = contents()->GetFrameTree()->root();
+ FrameTreeNode* ftn_b = ftn_a->child_at(0);
+ EXPECT_EQ(embedded_test_server()->GetURL("a.com", "/title1.html"),
+ ftn_b->current_frame_host()->GetLastCommittedURL());
+
+ // Add a second subframe dynamically and set some state on it to ensure it's
+ // not reloaded. Using a javascript: URL results in the renderer not sending
+ // a DidCommitNavigation IPC back for the new frame, leaving it without a
+ // FrameNavigationEntry.
+ EXPECT_TRUE(ExecJs(ftn_a,
+ "var f = document.createElement('iframe');"
+ "f.src = 'javascript:void(0)';"
+ "document.body.appendChild(f);"));
+ FrameTreeNode* ftn_c = ftn_a->child_at(1);
+ EXPECT_TRUE(ExecJs(ftn_c, "window.state='c';"));
+
+ // Navigate first subframe same-document.
+ {
+ FrameNavigateParamsCapturer capturer(ftn_b);
+ EXPECT_TRUE(ExecJs(ftn_b, "location.hash = 'foo'"));
+ capturer.Wait();
+ EXPECT_TRUE(capturer.is_same_document());
+ }
+
+ // Go back in the first subframe. This should navigate the first subframe
+ // back same-document, while the second subframe shouldn't be reloaded, and
+ // the history navigation shouldn't crash while processing it.
+ {
+ FrameNavigateParamsCapturer capturer(ftn_b);
+ controller.GoBack();
+ capturer.Wait();
+ EXPECT_TRUE(capturer.is_same_document());
+ EXPECT_TRUE(WaitForLoadStop(contents()));
+ EXPECT_EQ("c", EvalJs(ftn_c, "window.state"));
+ }
+}
+
+// Checks that a browser-initiated same-document navigation on a page which has
+// a valid base URL preserves the base URL.
+// See https://crbug.com/1082141.
+IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest,
+ LoadDataWithBaseURLSameDocumentNavigation) {
+ // LoadDataWithBaseURL is never subject to --site-per-process policy today
+ // (this API is only used by Android WebView [where OOPIFs have not shipped
+ // yet] and GuestView cases [which always hosts guests inside a renderer
+ // without an origin lock]). Therefore, skip the test in --site-per-process
+ // mode to avoid renderer kills which won't happen in practice as described
+ // above.
+ //
+ // TODO(https://crbug.com/962643): Consider enabling this test once Android
+ // Webview or WebView guests support OOPIFs and/or origin locks.
+ if (AreAllSitesIsolatedForTesting())
+ return;
+
+ const GURL base_url("http://baseurl");
+ const GURL history_url("http://history");
+ const std::string data = "<html><title>One</title><body>foo</body></html>";
+ const GURL data_url = GURL("data:text/html;charset=utf-8," + data);
+
+ NavigationControllerImpl& controller = static_cast<NavigationControllerImpl&>(
+ shell()->web_contents()->GetController());
+
+ {
+ TestNavigationObserver same_tab_observer(shell()->web_contents(), 1);
+ shell()->LoadDataWithBaseURL(history_url, data, base_url);
+ same_tab_observer.Wait();
+ }
+
+ // Verify the last committed NavigationEntry.
+ NavigationEntryImpl* entry = controller.GetLastCommittedEntry();
+ EXPECT_EQ(base_url, entry->GetBaseURLForDataURL());
+ EXPECT_EQ(history_url, entry->GetVirtualURL());
+ EXPECT_EQ(history_url, entry->GetHistoryURLForDataURL());
+ EXPECT_EQ(data_url, entry->GetURL());
+
+ {
+ // Make a same-document navigation via history.pushState.
+ TestNavigationObserver same_tab_observer(shell()->web_contents(), 1);
+ EXPECT_TRUE(ExecuteScript(shell(), "history.pushState('', 'test', '#')"));
+ same_tab_observer.Wait();
+ }
+
+ // Verify the last committed NavigationEntry.
+ entry = controller.GetLastCommittedEntry();
+ EXPECT_EQ(base_url, entry->GetBaseURLForDataURL());
+ EXPECT_EQ(history_url, entry->GetVirtualURL());
+ EXPECT_EQ(history_url, entry->GetHistoryURLForDataURL());
+ EXPECT_EQ(data_url, entry->GetURL());
+
+ {
+ // Go back.
+ TestNavigationObserver back_load_observer(shell()->web_contents());
+ controller.GoBack();
+ back_load_observer.Wait();
+ }
+
+ // Verify the last committed NavigationEntry.
+ entry = controller.GetLastCommittedEntry();
+ EXPECT_EQ(base_url, entry->GetBaseURLForDataURL());
+ EXPECT_EQ(history_url, entry->GetVirtualURL());
+ EXPECT_EQ(history_url, entry->GetHistoryURLForDataURL());
+ EXPECT_EQ(data_url, entry->GetURL());
+ EXPECT_EQ(base_url, EvalJs(shell(), "document.URL"));
+}
+
} // namespace content
diff --git a/chromium/content/browser/frame_host/navigation_controller_impl_unittest.cc b/chromium/content/browser/frame_host/navigation_controller_impl_unittest.cc
index ea263eebfa0..2df43b5f120 100644
--- a/chromium/content/browser/frame_host/navigation_controller_impl_unittest.cc
+++ b/chromium/content/browser/frame_host/navigation_controller_impl_unittest.cc
@@ -2573,269 +2573,6 @@ TEST_F(NavigationControllerTest, RemoveEntryWithPending) {
EXPECT_FALSE(controller.GetPendingEntry());
}
-// Tests the transient entry, making sure it goes away with all navigations.
-TEST_F(NavigationControllerTest, TransientEntry) {
- NavigationControllerImpl& controller = controller_impl();
-
- const GURL url0("http://foo/0");
- const GURL url1("http://foo/1");
- const GURL url2("http://foo/2");
- const GURL url3("http://foo/3");
- const GURL url3_ref("http://foo/3#bar");
- const GURL url4("http://foo/4");
- const GURL transient_url("http://foo/transient");
-
- NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url0);
- NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url1);
-
- navigation_entry_changed_counter_ = 0;
- navigation_list_pruned_counter_ = 0;
-
- // Adding a transient with no pending entry.
- std::unique_ptr<NavigationEntry> transient_entry(new NavigationEntryImpl);
- transient_entry->SetURL(transient_url);
- controller.SetTransientEntry(std::move(transient_entry));
-
- // We should not have received any notifications.
- EXPECT_EQ(0U, navigation_entry_changed_counter_);
- EXPECT_EQ(0U, navigation_list_pruned_counter_);
-
- // Check our state.
- EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
- EXPECT_EQ(controller.GetEntryCount(), 3);
- EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
- EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
- EXPECT_TRUE(controller.GetLastCommittedEntry());
- EXPECT_FALSE(controller.GetPendingEntry());
- EXPECT_TRUE(controller.CanGoBack());
- EXPECT_FALSE(controller.CanGoForward());
-
- // Navigate.
- NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url2);
-
- // We should have navigated, transient entry should be gone.
- EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
- EXPECT_EQ(controller.GetEntryCount(), 3);
-
- // Add a transient again, then navigate with no pending entry this time.
- transient_entry.reset(new NavigationEntryImpl);
- transient_entry->SetURL(transient_url);
- controller.SetTransientEntry(std::move(transient_entry));
- EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
- NavigationSimulator::NavigateAndCommitFromDocument(url3, main_test_rfh());
- // Transient entry should be gone.
- EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL());
- EXPECT_EQ(controller.GetEntryCount(), 4);
-
- // Initiate a navigation, add a transient then commit navigation.
- auto navigation =
- NavigationSimulator::CreateBrowserInitiated(url4, contents());
- navigation->Start();
- transient_entry.reset(new NavigationEntryImpl);
- transient_entry->SetURL(transient_url);
- controller.SetTransientEntry(std::move(transient_entry));
- EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
- navigation->Commit();
- EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL());
- EXPECT_EQ(controller.GetEntryCount(), 5);
-
- // Add a transient and go back. This should simply remove the transient.
- transient_entry.reset(new NavigationEntryImpl);
- transient_entry->SetURL(transient_url);
- controller.SetTransientEntry(std::move(transient_entry));
- EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
- EXPECT_TRUE(controller.CanGoBack());
- EXPECT_FALSE(controller.CanGoForward());
- controller.GoBack();
- // Transient entry should be gone.
- EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL());
- EXPECT_EQ(controller.GetEntryCount(), 5);
-
- // Suppose the page requested a history navigation backward.
- NavigationSimulator::GoBack(contents());
-
- // Add a transient and go to an entry before the current one.
- transient_entry.reset(new NavigationEntryImpl);
- transient_entry->SetURL(transient_url);
- controller.SetTransientEntry(std::move(transient_entry));
- EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
- controller.GoToIndex(1);
- auto history_navigation1 = NavigationSimulator::CreateFromPending(contents());
- // The navigation should have been initiated, transient entry should be gone.
- EXPECT_FALSE(controller.GetTransientEntry());
- EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
- // Visible entry does not update for history navigations until commit.
- EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL());
- history_navigation1->Commit();
- EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
-
- // Add a transient and go to an entry after the current one.
- transient_entry.reset(new NavigationEntryImpl);
- transient_entry->SetURL(transient_url);
- controller.SetTransientEntry(std::move(transient_entry));
- EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
- controller.GoToIndex(3);
- auto history_navigation2 = NavigationSimulator::CreateFromPending(contents());
- // The navigation should have been initiated, transient entry should be gone.
- // Because of the transient entry that is removed, going to index 3 makes us
- // land on url2 (which is visible after the commit).
- EXPECT_EQ(url2, controller.GetPendingEntry()->GetURL());
- EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
- history_navigation2->Commit();
- EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
-
- // Add a transient and go forward.
- transient_entry.reset(new NavigationEntryImpl);
- transient_entry->SetURL(transient_url);
- controller.SetTransientEntry(std::move(transient_entry));
- EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
- EXPECT_TRUE(controller.CanGoForward());
- auto forward_navigation =
- NavigationSimulator::CreateHistoryNavigation(1, contents());
- forward_navigation->Start();
- // We should have navigated, transient entry should be gone.
- EXPECT_FALSE(controller.GetTransientEntry());
- EXPECT_EQ(url3, controller.GetPendingEntry()->GetURL());
- EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
- forward_navigation->Commit();
- EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL());
-
- // Add a transient and do an in-page navigation, replacing the current entry.
- transient_entry.reset(new NavigationEntryImpl);
- transient_entry->SetURL(transient_url);
- controller.SetTransientEntry(std::move(transient_entry));
- EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
-
- main_test_rfh()->SendNavigate(0, false, url3_ref);
- // Transient entry should be gone.
- EXPECT_FALSE(controller.GetTransientEntry());
- EXPECT_EQ(url3_ref, controller.GetVisibleEntry()->GetURL());
-
- // Ensure the URLs are correct.
- EXPECT_EQ(controller.GetEntryCount(), 5);
- EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0);
- EXPECT_EQ(controller.GetEntryAtIndex(1)->GetURL(), url1);
- EXPECT_EQ(controller.GetEntryAtIndex(2)->GetURL(), url2);
- EXPECT_EQ(controller.GetEntryAtIndex(3)->GetURL(), url3_ref);
- EXPECT_EQ(controller.GetEntryAtIndex(4)->GetURL(), url4);
-}
-
-// Test that RemoveEntryAtIndex can handle an index that refers to a transient
-// entry.
-TEST_F(NavigationControllerTest, RemoveTransientByIndex) {
- NavigationControllerImpl& controller = controller_impl();
- const GURL url0("http://foo/0");
- const GURL transient_url("http://foo/transient");
-
- NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url0);
-
- std::unique_ptr<NavigationEntry> transient_entry =
- std::make_unique<NavigationEntryImpl>();
- transient_entry->SetURL(transient_url);
- controller.SetTransientEntry(std::move(transient_entry));
-
- EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
- EXPECT_EQ(controller.GetEntryCount(), 2);
- EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
- EXPECT_TRUE(controller.GetTransientEntry());
- EXPECT_EQ(controller.GetTransientEntry(), controller.GetEntryAtIndex(1));
-
- EXPECT_TRUE(controller.RemoveEntryAtIndex(1));
-
- EXPECT_EQ(controller.GetEntryCount(), 1);
- EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
- EXPECT_FALSE(controller.GetTransientEntry());
-}
-
-// Test that Reload initiates a new navigation to a transient entry's URL.
-TEST_F(NavigationControllerTest, ReloadTransient) {
- NavigationControllerImpl& controller = controller_impl();
- const GURL url0("http://foo/0");
- const GURL url1("http://foo/1");
- const GURL transient_url("http://foo/transient");
-
- // Load |url0|, and start a pending navigation to |url1|.
- NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url0);
- auto navigation =
- NavigationSimulator::CreateBrowserInitiated(url1, contents());
- navigation->Start();
-
- // A transient entry is added, interrupting the navigation.
- std::unique_ptr<NavigationEntry> transient_entry(new NavigationEntryImpl);
- transient_entry->SetURL(transient_url);
- controller.SetTransientEntry(std::move(transient_entry));
- EXPECT_TRUE(controller.GetTransientEntry());
- EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
-
- // The page is reloaded, which should remove the pending entry for |url1| and
- // the transient entry for |transient_url|, and start a navigation to
- // |transient_url|.
- controller.Reload(ReloadType::NORMAL, true);
- EXPECT_FALSE(controller.GetTransientEntry());
- EXPECT_TRUE(controller.GetPendingEntry());
- EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
- ASSERT_EQ(controller.GetEntryCount(), 1);
- EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0);
-
- // Load of |transient_url| completes.
- auto reload = NavigationSimulator::CreateFromPending(contents());
- reload->Commit();
- ASSERT_EQ(controller.GetEntryCount(), 2);
- EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0);
- EXPECT_EQ(controller.GetEntryAtIndex(1)->GetURL(), transient_url);
-}
-
-// Ensure that adding a transient entry works when history is full.
-TEST_F(NavigationControllerTest, TransientEntryWithFullHistory) {
- NavigationControllerImpl& controller = controller_impl();
-
- const GURL url0("http://foo/0");
- const GURL url1("http://foo/1");
- const GURL url2("http://foo/2");
- const GURL transient_url("http://foo/transient");
-
- // Maximum count should be at least 2 or we will not be able to perform
- // another navigation, since it would need to prune the last committed entry
- // which is not safe.
- controller.set_max_entry_count_for_testing(2);
- NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url0);
- NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url1);
-
- // Add a transient entry beyond entry count limit.
- auto transient_entry = std::make_unique<NavigationEntryImpl>();
- transient_entry->SetURL(transient_url);
- controller.SetTransientEntry(std::move(transient_entry));
-
- // Check our state.
- EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
- EXPECT_EQ(controller.GetEntryCount(), 3);
- EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
- EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
- EXPECT_TRUE(controller.GetLastCommittedEntry());
- EXPECT_FALSE(controller.GetPendingEntry());
- EXPECT_TRUE(controller.CanGoBack());
- EXPECT_FALSE(controller.CanGoForward());
-
- // Go back, removing the transient entry.
- controller.GoBack();
- EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
- EXPECT_EQ(controller.GetEntryCount(), 2);
-
- // Initiate a navigation, then add a transient entry with the pending entry
- // present.
- auto navigation =
- NavigationSimulator::CreateBrowserInitiated(url2, contents());
- navigation->Start();
- auto another_transient = std::make_unique<NavigationEntryImpl>();
- another_transient->SetURL(transient_url);
- controller.SetTransientEntry(std::move(another_transient));
- EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
- EXPECT_EQ(controller.GetEntryCount(), 3);
- navigation->Commit();
- EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
- EXPECT_EQ(controller.GetEntryCount(), 2);
-}
-
// Ensure that renderer initiated pending entries get replaced, so that we
// don't show a stale virtual URL when a navigation commits.
// See http://crbug.com/266922.
@@ -2991,8 +2728,8 @@ TEST_F(NavigationControllerTest, ShowBrowserURLAfterFailUntilModified) {
// Suppose it aborts before committing, if it's a 204 or download or due to a
// stop or a new navigation from the user. The URL should remain visible.
- static_cast<Navigator*>(main_test_rfh()->frame_tree_node()->navigator())
- ->CancelNavigation(main_test_rfh()->frame_tree_node());
+ main_test_rfh()->frame_tree_node()->navigator().CancelNavigation(
+ main_test_rfh()->frame_tree_node());
EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
// If something else later modifies the contents of the about:blank page, then
@@ -3350,25 +3087,6 @@ TEST_F(NavigationControllerTest, CloneAndReload) {
EXPECT_EQ(1, clone->GetController().GetPendingEntryIndex());
}
-// Make sure that cloning a WebContentsImpl doesn't copy interstitials.
-TEST_F(NavigationControllerTest, CloneOmitsInterstitials) {
- NavigationControllerImpl& controller = controller_impl();
- const GURL url1("http://foo1");
- const GURL url2("http://foo2");
-
- NavigateAndCommit(url1);
- NavigateAndCommit(url2);
-
- // Add an interstitial entry. Should be deleted with controller.
- NavigationEntryImpl* interstitial_entry = new NavigationEntryImpl();
- interstitial_entry->set_page_type(PAGE_TYPE_INTERSTITIAL);
- controller.SetTransientEntry(base::WrapUnique(interstitial_entry));
-
- std::unique_ptr<WebContents> clone(controller.GetWebContents()->Clone());
-
- ASSERT_EQ(2, clone->GetController().GetEntryCount());
-}
-
// Test requesting and triggering a lazy reload.
TEST_F(NavigationControllerTest, LazyReload) {
NavigationControllerImpl& controller = controller_impl();
@@ -4361,38 +4079,36 @@ TEST_F(NavigationControllerTest, PostThenReplaceStateThenReload) {
contents()->SetDelegate(delegate.get());
// Submit a form.
- GURL url("http://foo");
- FrameHostMsg_DidCommitProvisionalLoad_Params params;
- params.nav_entry_id = 0;
- params.did_create_new_entry = true;
- params.url = url;
- params.origin = url::Origin::Create(url);
- params.transition = ui::PAGE_TRANSITION_FORM_SUBMIT;
- params.gesture = NavigationGestureUser;
- params.page_state = PageState::CreateFromURL(url);
- params.method = "POST";
- params.post_id = 2;
- main_test_rfh()->SendRendererInitiatedNavigationRequest(url, false);
- main_test_rfh()->PrepareForCommit();
- contents()->GetMainFrame()->SendNavigateWithParams(&params, false);
+ auto simulator = NavigationSimulatorImpl::CreateRendererInitiated(
+ GURL("http://foo"), main_test_rfh());
+ simulator->SetIsFormSubmission(true);
+ simulator->SetIsPostWithId(123);
+ simulator->Commit();
+
+ // Now reload. We should show repost warning dialog.
+ {
+ NavigationControllerImpl::ScopedShowRepostDialogForTesting show_repost;
+ controller_impl().Reload(ReloadType::NORMAL, true);
+ }
+ const int expected_repost_form_warning_count = 1;
+ EXPECT_EQ(expected_repost_form_warning_count,
+ delegate->repost_form_warning_count());
// history.replaceState() is called.
GURL replace_url("http://foo#foo");
- params.nav_entry_id = 0;
- params.did_create_new_entry = false;
- params.url = replace_url;
- params.origin = url::Origin::Create(replace_url);
- params.transition = ui::PAGE_TRANSITION_LINK;
- params.gesture = NavigationGestureUser;
- params.page_state = PageState::CreateFromURL(replace_url);
- params.method = "GET";
- params.post_id = -1;
- contents()->GetMainFrame()->SendNavigateWithParams(&params, true);
+ simulator = NavigationSimulatorImpl::CreateRendererInitiated(
+ GURL("http://foo#foo"), main_test_rfh());
+ simulator->set_did_create_new_entry(false);
+ simulator->Commit();
// Now reload. replaceState overrides the POST, so we should not show a
// repost warning dialog.
- controller_impl().Reload(ReloadType::NORMAL, true);
- EXPECT_EQ(0, delegate->repost_form_warning_count());
+ {
+ NavigationControllerImpl::ScopedShowRepostDialogForTesting show_repost;
+ controller_impl().Reload(ReloadType::NORMAL, true);
+ }
+ EXPECT_EQ(expected_repost_form_warning_count,
+ delegate->repost_form_warning_count());
}
TEST_F(NavigationControllerTest, UnreachableURLGivesErrorPage) {
@@ -4409,6 +4125,7 @@ TEST_F(NavigationControllerTest, UnreachableURLGivesErrorPage) {
params.method = "POST";
params.post_id = 2;
params.url_is_unreachable = true;
+ params.embedding_token = base::UnguessableToken::Create();
// Navigate to new page.
{
@@ -4452,6 +4169,7 @@ TEST_F(NavigationControllerTest, UnreachableURLGivesErrorPage) {
// Navigate without changing document.
params.url = GURL("http://foo#foo");
params.transition = ui::PAGE_TRANSITION_LINK;
+ params.embedding_token = base::nullopt;
{
LoadCommittedDetailsObserver observer(contents());
main_test_rfh()->SendNavigateWithParams(&params, true);
@@ -4521,84 +4239,6 @@ TEST_F(NavigationControllerTest, StaleNavigationsResurrected) {
EXPECT_EQ(url_b, controller.GetEntryAtIndex(2)->GetURL());
}
-// Test to ensure that the pending entry index is updated when a transient entry
-// is inserted or removed.
-TEST_F(NavigationControllerTest, PendingEntryIndexUpdatedWithTransient) {
- NavigationControllerImpl& controller = controller_impl();
- const GURL url_0("http://foo/0");
- const GURL url_1("http://foo/1");
- const GURL url_transient_1("http://foo/transient_1");
- const GURL url_transient_2("http://foo/transient_2");
-
- NavigateAndCommit(url_0);
- NavigateAndCommit(url_1);
- controller.GoBack();
- contents()->CommitPendingNavigation();
- controller.GoForward();
-
- // Check the state before the insertion of the transient entry.
- // entries[0] = url_0 <- last committed entry.
- // entries[1] = url_1 <- pending entry.
- ASSERT_EQ(2, controller.GetEntryCount());
- EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
- EXPECT_EQ(1, controller.GetPendingEntryIndex());
- EXPECT_EQ(controller.GetEntryAtIndex(1), controller.GetPendingEntry());
- EXPECT_EQ(url_0, controller.GetEntryAtIndex(0)->GetURL());
- EXPECT_EQ(url_1, controller.GetEntryAtIndex(1)->GetURL());
-
- // Insert a transient entry before the pending one. It should increase the
- // pending entry index by one (1 -> 2).
- std::unique_ptr<NavigationEntry> transient_entry_1(new NavigationEntryImpl);
- transient_entry_1->SetURL(url_transient_1);
- controller.SetTransientEntry(std::move(transient_entry_1));
-
- // Check the state after the insertion of the transient entry.
- // entries[0] = url_0 <- last committed entry
- // entries[1] = url_transient_1 <- transient entry
- // entries[2] = url_1 <- pending entry
- ASSERT_EQ(3, controller.GetEntryCount());
- EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
- EXPECT_EQ(2, controller.GetPendingEntryIndex());
- EXPECT_EQ(controller.GetEntryAtIndex(1), controller.GetTransientEntry());
- EXPECT_EQ(controller.GetEntryAtIndex(2), controller.GetPendingEntry());
- EXPECT_EQ(url_0, controller.GetEntryAtIndex(0)->GetURL());
- EXPECT_EQ(url_transient_1, controller.GetEntryAtIndex(1)->GetURL());
- EXPECT_EQ(url_1, controller.GetEntryAtIndex(2)->GetURL());
-
- // Insert another transient entry. It should replace the previous one and this
- // time the pending entry index should retain its value (i.e. 2).
- std::unique_ptr<NavigationEntry> transient_entry_2(new NavigationEntryImpl);
- transient_entry_2->SetURL(url_transient_2);
- controller.SetTransientEntry(std::move(transient_entry_2));
-
- // Check the state after the second insertion of a transient entry.
- // entries[0] = url_0 <- last committed entry
- // entries[1] = url_transient_2 <- transient entry
- // entries[2] = url_1 <- pending entry
- ASSERT_EQ(3, controller.GetEntryCount());
- EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
- EXPECT_EQ(2, controller.GetPendingEntryIndex());
- EXPECT_EQ(controller.GetEntryAtIndex(1), controller.GetTransientEntry());
- EXPECT_EQ(controller.GetEntryAtIndex(2), controller.GetPendingEntry());
- EXPECT_EQ(url_0, controller.GetEntryAtIndex(0)->GetURL());
- EXPECT_EQ(url_transient_2, controller.GetEntryAtIndex(1)->GetURL());
- EXPECT_EQ(url_1, controller.GetEntryAtIndex(2)->GetURL());
-
- // Commit the pending entry.
- contents()->CommitPendingNavigation();
-
- // Check the final state.
- // entries[0] = url_0
- // entries[1] = url_1 <- last committed entry
- ASSERT_EQ(2, controller.GetEntryCount());
- EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
- EXPECT_EQ(-1, controller.GetPendingEntryIndex());
- EXPECT_EQ(nullptr, controller.GetPendingEntry());
- EXPECT_EQ(nullptr, controller.GetTransientEntry());
- EXPECT_EQ(url_0, controller.GetEntryAtIndex(0)->GetURL());
- EXPECT_EQ(url_1, controller.GetEntryAtIndex(1)->GetURL());
-}
-
// Tests that NavigationUIData has been passed to the NavigationHandle.
TEST_F(NavigationControllerTest, MainFrameNavigationUIData) {
LoadCommittedDetailsObserver observer(contents());
@@ -4757,7 +4397,6 @@ TEST_F(NavigationControllerTest, PruneForwardEntries) {
const GURL url_1("http://foo/1");
const GURL url_2("http://foo/2");
const GURL url_3("http://foo/3");
- const GURL url_transient("http://foo/transient");
NavigateAndCommit(url_0);
NavigateAndCommit(url_1);
@@ -4797,10 +4436,6 @@ TEST_F(NavigationControllerTest, PruneForwardEntries) {
EXPECT_EQ(1, controller.GetPendingEntryIndex());
EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
- // Insert a transient entry before the pending one.
- std::unique_ptr<NavigationEntry> transient_entry(new NavigationEntryImpl);
- transient_entry->SetURL(url_transient);
- controller.SetTransientEntry(std::move(transient_entry));
state_change_count = delegate->navigation_state_change_count();
controller.PruneForwardEntries();
@@ -4812,7 +4447,6 @@ TEST_F(NavigationControllerTest, PruneForwardEntries) {
EXPECT_EQ(0, controller.GetCurrentEntryIndex());
EXPECT_EQ(-1, controller.GetPendingEntryIndex());
EXPECT_EQ(nullptr, controller.GetPendingEntry());
- EXPECT_EQ(nullptr, controller.GetTransientEntry());
EXPECT_EQ(url_0, controller.GetVisibleEntry()->GetURL());
EXPECT_EQ(1U, navigation_list_pruned_counter_);
EXPECT_EQ(1, last_navigation_entry_pruned_details_.index);
diff --git a/chromium/content/browser/frame_host/navigation_entry_impl.cc b/chromium/content/browser/frame_host/navigation_entry_impl.cc
index 6be77fe8508..0c43acd31a4 100644
--- a/chromium/content/browser/frame_host/navigation_entry_impl.cc
+++ b/chromium/content/browser/frame_host/navigation_entry_impl.cc
@@ -20,6 +20,7 @@
#include "build/build_config.h"
#include "components/url_formatter/url_formatter.h"
#include "content/browser/frame_host/navigation_controller_impl.h"
+#include "content/browser/web_package/web_bundle_navigation_info.h"
#include "content/common/content_constants_internal.h"
#include "content/common/navigation_params.h"
#include "content/common/page_state_serialization.h"
@@ -77,7 +78,8 @@ void RecursivelyGenerateFrameEntries(
state.referrer_policy),
state.initiator_origin, std::vector<GURL>(),
PageState::CreateFromEncodedData(data), "GET", -1,
- nullptr /* blob_url_loader_factory */);
+ nullptr /* blob_url_loader_factory */,
+ nullptr /* web_bundle_navigation_info */);
// Don't pass the file list to subframes, since that would result in multiple
// copies of it ending up in the combined list in GetPageState (via
@@ -356,23 +358,24 @@ NavigationEntryImpl::NavigationEntryImpl(
ui::PageTransition transition_type,
bool is_renderer_initiated,
scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory)
- : frame_tree_(
- std::make_unique<TreeNode>(nullptr,
- base::MakeRefCounted<FrameNavigationEntry>(
- "",
- -1,
- -1,
- std::move(instance),
- nullptr,
- url,
- nullptr /* origin */,
- referrer,
- initiator_origin,
- std::vector<GURL>(),
- PageState(),
- "GET",
- -1,
- std::move(blob_url_loader_factory)))),
+ : frame_tree_(std::make_unique<TreeNode>(
+ nullptr,
+ base::MakeRefCounted<FrameNavigationEntry>(
+ "",
+ -1,
+ -1,
+ std::move(instance),
+ nullptr,
+ url,
+ nullptr /* origin */,
+ referrer,
+ initiator_origin,
+ std::vector<GURL>(),
+ PageState(),
+ "GET",
+ -1,
+ std::move(blob_url_loader_factory),
+ nullptr /* web_bundle_navigation_info */))),
unique_id_(CreateUniqueEntryID()),
page_type_(PAGE_TYPE_NORMAL),
update_virtual_url_with_url_(false),
@@ -826,7 +829,8 @@ NavigationEntryImpl::ConstructCommitNavigationParams(
false, network::mojom::IPAddressSpace::kUnknown,
GURL() /* web_bundle_physical_url */,
GURL() /* base_url_override_for_web_bundle */, frame_policy,
- std::vector<std::string>() /* force_enabled_origin_trials */);
+ std::vector<std::string>() /* force_enabled_origin_trials */,
+ false /* origin_isolation_restricted */);
#if defined(OS_ANDROID)
if (NavigationControllerImpl::ValidateDataURLAsString(GetDataURLAsString())) {
commit_params->data_url_as_string = GetDataURLAsString()->data();
@@ -885,7 +889,8 @@ void NavigationEntryImpl::AddOrUpdateFrameEntry(
const PageState& page_state,
const std::string& method,
int64_t post_id,
- scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory) {
+ scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
+ std::unique_ptr<WebBundleNavigationInfo> web_bundle_navigation_info) {
// If this is called for the main frame, the FrameNavigationEntry is
// guaranteed to exist, so just update it directly and return.
if (frame_tree_node->IsMainFrame()) {
@@ -900,7 +905,8 @@ void NavigationEntryImpl::AddOrUpdateFrameEntry(
document_sequence_number, site_instance,
std::move(source_site_instance), url, origin, referrer,
initiator_origin, redirect_chain, page_state, method, post_id,
- std::move(blob_url_loader_factory));
+ std::move(blob_url_loader_factory),
+ std::move(web_bundle_navigation_info));
return;
}
@@ -929,7 +935,8 @@ void NavigationEntryImpl::AddOrUpdateFrameEntry(
unique_name, item_sequence_number, document_sequence_number,
site_instance, std::move(source_site_instance), url, origin, referrer,
initiator_origin, redirect_chain, page_state, method, post_id,
- std::move(blob_url_loader_factory));
+ std::move(blob_url_loader_factory),
+ std::move(web_bundle_navigation_info));
return;
}
}
@@ -942,7 +949,8 @@ void NavigationEntryImpl::AddOrUpdateFrameEntry(
site_instance, std::move(source_site_instance), url,
base::OptionalOrNullptr(origin), referrer, initiator_origin,
redirect_chain, page_state, method, post_id,
- std::move(blob_url_loader_factory));
+ std::move(blob_url_loader_factory),
+ std::move(web_bundle_navigation_info));
parent_node->children.push_back(
std::make_unique<NavigationEntryImpl::TreeNode>(parent_node,
std::move(frame_entry)));
diff --git a/chromium/content/browser/frame_host/navigation_entry_impl.h b/chromium/content/browser/frame_host/navigation_entry_impl.h
index ac969a4f025..09fd6806650 100644
--- a/chromium/content/browser/frame_host/navigation_entry_impl.h
+++ b/chromium/content/browser/frame_host/navigation_entry_impl.h
@@ -36,6 +36,8 @@
namespace content {
+class WebBundleNavigationInfo;
+
class CONTENT_EXPORT NavigationEntryImpl : public NavigationEntry {
public:
// Represents a tree of FrameNavigationEntries that make up this joint session
@@ -231,7 +233,8 @@ class CONTENT_EXPORT NavigationEntryImpl : public NavigationEntry {
const PageState& page_state,
const std::string& method,
int64_t post_id,
- scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory);
+ scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
+ std::unique_ptr<WebBundleNavigationInfo> web_bundle_navigation_info);
// Returns the FrameNavigationEntry corresponding to |frame_tree_node|, if
// there is one in this NavigationEntry.
diff --git a/chromium/content/browser/frame_host/navigation_request.cc b/chromium/content/browser/frame_host/navigation_request.cc
index baf19ee3bae..5757ad84edb 100644
--- a/chromium/content/browser/frame_host/navigation_request.cc
+++ b/chromium/content/browser/frame_host/navigation_request.cc
@@ -23,7 +23,6 @@
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/system/sys_info.h"
-#include "base/task/post_task.h"
#include "build/build_config.h"
#include "content/browser/appcache/appcache_navigation_handle.h"
#include "content/browser/appcache/chrome_appcache_service.h"
@@ -47,6 +46,7 @@
#include "content/browser/loader/cached_navigation_url_loader.h"
#include "content/browser/loader/navigation_url_loader.h"
#include "content/browser/net/cross_origin_embedder_policy_reporter.h"
+#include "content/browser/net/cross_origin_opener_policy_reporter.h"
#include "content/browser/network_service_instance_impl.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/renderer_host/render_view_host_delegate.h"
@@ -66,6 +66,7 @@
#include "content/common/navigation_params.h"
#include "content/common/navigation_params_mojom_traits.h"
#include "content/common/navigation_params_utils.h"
+#include "content/common/state_transitions.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
@@ -102,6 +103,7 @@
#include "services/network/public/cpp/content_security_policy/content_security_policy.h"
#include "services/network/public/cpp/cross_origin_resource_policy.h"
#include "services/network/public/cpp/features.h"
+#include "services/network/public/cpp/is_potentially_trustworthy.h"
#include "services/network/public/cpp/resource_request_body.h"
#include "services/network/public/cpp/url_loader_completion_status.h"
#include "services/network/public/cpp/web_sandbox_flags.h"
@@ -661,6 +663,107 @@ void EnterChildTraceEvent(const char* name,
arg_value);
}
+network::mojom::RequestDestination GetDestinationFromFrameTreeNode(
+ FrameTreeNode* frame_tree_node) {
+ if (frame_tree_node->IsMainFrame()) {
+ return frame_tree_node->current_frame_host()
+ ->GetRenderViewHost()
+ ->GetDelegate()
+ ->IsPortal()
+ ? network::mojom::RequestDestination::kIframe
+ : network::mojom::RequestDestination::kDocument;
+ } else {
+ switch (frame_tree_node->frame_owner_element_type()) {
+ case blink::mojom::FrameOwnerElementType::kObject:
+ return network::mojom::RequestDestination::kObject;
+ case blink::mojom::FrameOwnerElementType::kEmbed:
+ return network::mojom::RequestDestination::kEmbed;
+ case blink::mojom::FrameOwnerElementType::kIframe:
+ return network::mojom::RequestDestination::kIframe;
+ case blink::mojom::FrameOwnerElementType::kFrame:
+ return network::mojom::RequestDestination::kFrame;
+ case blink::mojom::FrameOwnerElementType::kPortal:
+ case blink::mojom::FrameOwnerElementType::kNone:
+ NOTREACHED();
+ return network::mojom::RequestDestination::kDocument;
+ }
+ NOTREACHED();
+ return network::mojom::RequestDestination::kDocument;
+ }
+}
+
+// This function implements the COOP matching algorithm as detailed in [1].
+// Note that COEP is also provided since the COOP enum does not have a
+// "same-origin + COEP" value.
+// [1] https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e
+bool CrossOriginOpenerPolicyMatch(
+ network::mojom::CrossOriginOpenerPolicyValue initiator_coop,
+ network::mojom::CrossOriginEmbedderPolicyValue initiator_coep,
+ const url::Origin& initiator_origin,
+ network::mojom::CrossOriginOpenerPolicyValue destination_coop,
+ network::mojom::CrossOriginEmbedderPolicyValue destination_coep,
+ const url::Origin& destination_origin) {
+ if (initiator_coop != destination_coop)
+ return false;
+ if (initiator_coop ==
+ network::mojom::CrossOriginOpenerPolicyValue::kUnsafeNone) {
+ return true;
+ }
+ if (initiator_coop ==
+ network::mojom::CrossOriginOpenerPolicyValue::kSameOrigin &&
+ initiator_coep != destination_coep) {
+ return false;
+ }
+ if (!initiator_origin.IsSameOriginWith(destination_origin))
+ return false;
+ return true;
+}
+
+// This function returns whether the BrowsingInstance should change following
+// COOP rules defined in:
+// https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e#changes-to-navigation
+bool ShouldSwapBrowsingInstanceForCrossOriginOpenerPolicy(
+ network::mojom::CrossOriginOpenerPolicyValue initiator_coop,
+ network::mojom::CrossOriginEmbedderPolicyValue initiator_coep,
+ const url::Origin& initiator_origin,
+ bool is_initiator_aboutblank,
+ network::mojom::CrossOriginOpenerPolicyValue destination_coop,
+ network::mojom::CrossOriginEmbedderPolicyValue destination_coep,
+ const url::Origin& destination_origin) {
+ using network::mojom::CrossOriginEmbedderPolicyValue;
+ using network::mojom::CrossOriginOpenerPolicyValue;
+
+ if (!base::FeatureList::IsEnabled(
+ network::features::kCrossOriginOpenerPolicy))
+ return false;
+
+ // If policies match there is no reason to switch BrowsingInstances.
+ if (CrossOriginOpenerPolicyMatch(initiator_coop, initiator_coep,
+ initiator_origin, destination_coop,
+ destination_coep, destination_origin)) {
+ return false;
+ }
+
+ // "same-origin-allow-popups" is used to stay in the same BrowsingInstance
+ // despite COOP mismatch. This case is defined in the spec [1] as follow.
+ // ```
+ // If the result of matching currentCOOP, currentOrigin, potentialCOOP, and
+ // potentialOrigin is false and one of the following is false:
+ // - doc is the initial about:blank document
+ // - currentCOOP is "same-origin-allow-popups"
+ // - potentialCOOP is "unsafe-none"
+ // Then create a new browsing context group.
+ // ```
+ // [1]
+ // https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e#changes-to-navigation
+ if (is_initiator_aboutblank &&
+ initiator_coop == CrossOriginOpenerPolicyValue::kSameOriginAllowPopups &&
+ destination_coop == CrossOriginOpenerPolicyValue::kUnsafeNone) {
+ return false;
+ }
+ return true;
+}
+
} // namespace
// static
@@ -680,11 +783,13 @@ std::unique_ptr<NavigationRequest> NavigationRequest::CreateBrowserInitiated(
// This is not currently handled here.
bool is_form_submission = !!post_body;
+ network::mojom::RequestDestination destination =
+ GetDestinationFromFrameTreeNode(frame_tree_node);
+
auto navigation_params = mojom::BeginNavigationParams::New(
initiator_routing_id.frame_routing_id /* initiator_routing_id */,
extra_headers, net::LOAD_NORMAL, false /* skip_service_worker */,
- blink::mojom::RequestContextType::LOCATION,
- network::mojom::RequestDestination::kDocument,
+ blink::mojom::RequestContextType::LOCATION, destination,
blink::WebMixedContentContextType::kBlockable, is_form_submission,
false /* was_initiated_by_link_click */, GURL() /* searchable_form_url */,
std::string() /* searchable_form_encoding */,
@@ -704,7 +809,7 @@ std::unique_ptr<NavigationRequest> NavigationRequest::CreateBrowserInitiated(
if (entry) {
NavigationControllerImpl* controller =
static_cast<NavigationControllerImpl*>(
- frame_tree_node->navigator()->GetController());
+ frame_tree_node->navigator().GetController());
BackForwardCacheImpl::Entry* restored_entry =
controller->GetBackForwardCache().GetEntry(entry->GetUniqueID());
if (restored_entry) {
@@ -718,15 +823,14 @@ std::unique_ptr<NavigationRequest> NavigationRequest::CreateBrowserInitiated(
std::move(commit_params), browser_initiated,
false /* from_begin_navigation */, false /* is_for_commit */, frame_entry,
entry, std::move(navigation_ui_data), mojo::NullAssociatedRemote(),
- mojo::NullRemote(), rfh_restored_from_back_forward_cache));
+ mojo::NullRemote(), rfh_restored_from_back_forward_cache,
+ initiator_routing_id));
if (frame_entry) {
navigation_request->blob_url_loader_factory_ =
frame_entry->blob_url_loader_factory();
}
- navigation_request->initiator_routing_id_ = initiator_routing_id;
-
if (navigation_request->common_params().url.SchemeIsBlob() &&
!navigation_request->blob_url_loader_factory_) {
// If this navigation entry came from session history then the blob factory
@@ -736,7 +840,7 @@ std::unique_ptr<NavigationRequest> NavigationRequest::CreateBrowserInitiated(
// alive.
navigation_request->blob_url_loader_factory_ =
ChromeBlobStorageContext::URLLoaderFactoryForUrl(
- frame_tree_node->navigator()->GetController()->GetBrowserContext(),
+ frame_tree_node->navigator().GetController()->GetBrowserContext(),
navigation_request->common_params().url);
}
@@ -767,6 +871,9 @@ std::unique_ptr<NavigationRequest> NavigationRequest::CreateRendererInitiated(
common_params->navigation_type ==
mojom::NavigationType::DIFFERENT_DOCUMENT);
+ begin_params->request_destination =
+ GetDestinationFromFrameTreeNode(frame_tree_node);
+
// TODO(clamy): See if the navigation start time should be measured in the
// renderer and sent to the browser instead of being measured here.
mojom::CommitNavigationParamsPtr commit_params =
@@ -804,7 +911,17 @@ std::unique_ptr<NavigationRequest> NavigationRequest::CreateRendererInitiated(
GURL() /* web_bundle_physical_url */,
GURL() /* base_url_override_for_web_bundle */,
frame_tree_node->pending_frame_policy(),
- std::vector<std::string>() /* force_enabled_origin_trials */);
+ std::vector<std::string>() /* force_enabled_origin_trials */,
+ false /* origin_isolation_restricted */);
+
+ // CreateRendererInitiated() should only be triggered when the navigation is
+ // initiated by a frame in the same process.
+ // TODO(https://crbug.com/1074464): Find a way to DCHECK that the routing ID
+ // is from the current RFH.
+ GlobalFrameRoutingId initiator_routing_id(
+ frame_tree_node->current_frame_host()->GetProcess()->GetID(),
+ begin_params->initiator_routing_id);
+
std::unique_ptr<NavigationRequest> navigation_request(new NavigationRequest(
frame_tree_node, std::move(common_params), std::move(begin_params),
std::move(commit_params),
@@ -814,8 +931,8 @@ std::unique_ptr<NavigationRequest> NavigationRequest::CreateRendererInitiated(
nullptr, entry,
nullptr, // navigation_ui_data
std::move(navigation_client), std::move(navigation_initiator),
- nullptr // rfh_restored_from_back_forward_cache
- ));
+ nullptr, // rfh_restored_from_back_forward_cache
+ initiator_routing_id));
navigation_request->blob_url_loader_factory_ =
std::move(blob_url_loader_factory);
navigation_request->prefetched_signed_exchange_cache_ =
@@ -823,13 +940,6 @@ std::unique_ptr<NavigationRequest> NavigationRequest::CreateRendererInitiated(
navigation_request->web_bundle_handle_tracker_ =
std::move(web_bundle_handle_tracker);
- // CreateRendererInitiated() should only be triggered when the navigation is
- // initiated by a frame in the same process.
- // TODO(https://crbug.com/1074464): Find a way to DCHECK that the routing ID
- // is from the current RFH.
- navigation_request->initiator_routing_id_ = GlobalFrameRoutingId(
- frame_tree_node->current_frame_host()->GetProcess()->GetID(),
- navigation_request->begin_params()->initiator_routing_id);
return navigation_request;
}
@@ -839,7 +949,8 @@ std::unique_ptr<NavigationRequest> NavigationRequest::CreateForCommit(
RenderFrameHostImpl* render_frame_host,
const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
std::unique_ptr<CrossOriginEmbedderPolicyReporter> coep_reporter,
- bool is_same_document) {
+ bool is_same_document,
+ std::unique_ptr<WebBundleNavigationInfo> web_bundle_navigation_info) {
// TODO(clamy): Improve the *NavigationParams and *CommitParams to avoid
// copying so many parameters here.
mojom::CommonNavigationParamsPtr common_params =
@@ -887,8 +998,9 @@ std::unique_ptr<NavigationRequest> NavigationRequest::CreateForCommit(
network::mojom::IPAddressSpace::kUnknown,
GURL() /* web_bundle_physical_url */,
GURL() /* base_url_override_for_web_bundle */,
- base::nullopt /* frame policy */,
- std::vector<std::string>() /* force_enabled_origin_trials */
+ frame_tree_node->pending_frame_policy(),
+ std::vector<std::string>() /* force_enabled_origin_trials */,
+ false /* origin_isolation_restricted */
);
mojom::BeginNavigationParamsPtr begin_params =
mojom::BeginNavigationParams::New();
@@ -896,10 +1008,13 @@ std::unique_ptr<NavigationRequest> NavigationRequest::CreateForCommit(
frame_tree_node, std::move(common_params), std::move(begin_params),
std::move(commit_params), false /* browser_initiated */,
false /* from_begin_navigation */, true /* is_for_commit */,
- nullptr /* frame_navigation_entry */, nullptr /* navitation_entry */,
+ nullptr /* frame_navigation_entry */, nullptr /* navigation_entry */,
nullptr /* navigation_ui_data */, mojo::NullAssociatedRemote(),
- mojo::NullRemote(), nullptr /* rfh_restored_from_back_forward_cache */));
+ mojo::NullRemote(), nullptr, /* rfh_restored_from_back_forward_cache */
+ {} /* initiator_routing_id */));
+ navigation_request->web_bundle_navigation_info_ =
+ std::move(web_bundle_navigation_info);
navigation_request->render_frame_host_ = render_frame_host;
navigation_request->coep_reporter_ = std::move(coep_reporter);
navigation_request->StartNavigation(true);
@@ -921,7 +1036,8 @@ NavigationRequest::NavigationRequest(
std::unique_ptr<NavigationUIData> navigation_ui_data,
mojo::PendingAssociatedRemote<mojom::NavigationClient> navigation_client,
mojo::PendingRemote<blink::mojom::NavigationInitiator> navigation_initiator,
- RenderFrameHostImpl* rfh_restored_from_back_forward_cache)
+ RenderFrameHostImpl* rfh_restored_from_back_forward_cache,
+ GlobalFrameRoutingId initiator_routing_id)
: frame_tree_node_(frame_tree_node),
is_for_commit_(is_for_commit),
common_params_(std::move(common_params)),
@@ -930,24 +1046,40 @@ NavigationRequest::NavigationRequest(
browser_initiated_(browser_initiated),
navigation_ui_data_(std::move(navigation_ui_data)),
state_(NOT_STARTED),
- restore_type_(RestoreType::NONE),
+ restore_type_(entry ? entry->restore_type() : RestoreType::NONE),
+ reload_type_(entry ? entry->reload_type() : ReloadType::NONE),
+ nav_entry_id_(entry ? entry->GetUniqueID() : 0),
is_view_source_(false),
bindings_(FrameNavigationEntry::kInvalidBindings),
response_should_be_rendered_(true),
associated_site_instance_type_(AssociatedSiteInstanceType::NONE),
from_begin_navigation_(from_begin_navigation),
has_stale_copy_in_cache_(false),
- net_error_(net::OK),
expected_render_process_host_id_(ChildProcessHost::kInvalidUniqueID),
+ initiator_csp_context_(std::make_unique<InitiatorCSPContext>(
+ std::move(common_params_->initiator_csp_info->initiator_csp),
+ std::move(common_params_->initiator_csp_info->initiator_self_source),
+ std::move(navigation_initiator))),
devtools_navigation_token_(base::UnguessableToken::Create()),
request_navigation_client_(mojo::NullAssociatedRemote()),
commit_navigation_client_(mojo::NullAssociatedRemote()),
+ navigation_handle_timing_(std::make_unique<NavigationHandleTiming>()),
rfh_restored_from_back_forward_cache_(
rfh_restored_from_back_forward_cache),
- client_security_state_(network::mojom::ClientSecurityState::New()) {
+ // Store the old RenderFrameHost id at request creation to be used later.
+ previous_render_frame_host_id_(GlobalFrameRoutingId(
+ frame_tree_node->current_frame_host()->GetProcess()->GetID(),
+ frame_tree_node->current_frame_host()->GetRoutingID())),
+ initiator_routing_id_(initiator_routing_id),
+ client_security_state_(network::mojom::ClientSecurityState::New()),
+ previous_page_load_ukm_source_id_(
+ frame_tree_node_->current_frame_host()->GetPageUkmSourceId()) {
DCHECK(browser_initiated_ || common_params_->initiator_origin.has_value());
DCHECK(!IsRendererDebugURL(common_params_->url));
DCHECK(common_params_->method == "POST" || !common_params_->post_data);
+ DCHECK((IsInMainFrame() && browser_initiated) ||
+ commit_params_->frame_policy.has_value());
+
TRACE_EVENT_NESTABLE_ASYNC_BEGIN2(
"navigation", "NavigationRequest", this, "frame_tree_node",
frame_tree_node_->frame_tree_node_id(), "url",
@@ -1012,7 +1144,9 @@ NavigationRequest::NavigationRequest(
// Let the NTP override the navigation params and pretend that this is a
// browser-initiated, bookmark-like navigation.
- if (!browser_initiated_ && source_site_instance_) {
+ // TODO(crbug.com/1099431): determine why some link navigations on chrome://
+ // pages have |browser_initiated_| set to true and others set to false.
+ if (source_site_instance_) {
bool is_renderer_initiated = !browser_initiated_;
Referrer referrer(*common_params_->referrer);
GetContentClient()->browser()->OverrideNavigationParams(
@@ -1024,14 +1158,6 @@ NavigationRequest::NavigationRequest(
commit_params_->is_browser_initiated = browser_initiated_;
}
- // Store the old RenderFrameHost id at request creation to be used later.
- previous_render_frame_host_id_ = GlobalFrameRoutingId(
- frame_tree_node->current_frame_host()->GetProcess()->GetID(),
- frame_tree_node->current_frame_host()->GetRoutingID());
-
- previous_page_load_ukm_source_id_ =
- frame_tree_node_->current_frame_host()->GetPageUkmSourceId();
-
// Update the load flags with cache information.
UpdateLoadFlagsWithCacheFlags(&begin_params_->load_flags,
common_params_->navigation_type,
@@ -1040,9 +1166,6 @@ NavigationRequest::NavigationRequest(
// Add necessary headers that may not be present in the
// mojom::BeginNavigationParams.
if (entry) {
- nav_entry_id_ = entry->GetUniqueID();
- restore_type_ = entry->restore_type();
- reload_type_ = entry->reload_type();
// TODO(altimin, crbug.com/933147): Remove this logic after we are done
// with implementing back-forward cache.
if (frame_tree_node->IsMainFrame() && entry->back_forward_cache_metrics()) {
@@ -1100,11 +1223,6 @@ NavigationRequest::NavigationRequest(
begin_params_->headers = headers.ToString();
- initiator_csp_context_.reset(new InitiatorCSPContext(
- std::move(common_params_->initiator_csp_info->initiator_csp),
- std::move(common_params_->initiator_csp_info->initiator_self_source),
- std::move(navigation_initiator)));
-
navigation_entry_offset_ = EstimateHistoryOffset();
commit_params_->is_browser_initiated = browser_initiated_;
@@ -1151,6 +1269,7 @@ NavigationRequest::~NavigationRequest() {
if (IsNavigationStarted()) {
GetDelegate()->DidFinishNavigation(this);
+ ProcessOriginIsolationEndResult();
if (IsInMainFrame()) {
TRACE_EVENT_NESTABLE_ASYNC_END2(
"navigation", "Navigation StartToCommit",
@@ -1161,13 +1280,12 @@ NavigationRequest::~NavigationRequest() {
}
void NavigationRequest::BeginNavigation() {
- DCHECK(state_ == NOT_STARTED || state_ == WAITING_FOR_RENDERER_RESPONSE);
EnterChildTraceEvent("BeginNavigation", this);
DCHECK(!loader_);
DCHECK(!render_frame_host_);
ScopedNavigationRequestCrashKeys crash_keys(this);
- state_ = WILL_START_NAVIGATION;
+ SetState(WILL_START_NAVIGATION);
#if defined(OS_ANDROID)
base::WeakPtr<NavigationRequest> this_ptr(weak_factory_.GetWeakPtr());
@@ -1263,6 +1381,26 @@ void NavigationRequest::BeginNavigation() {
return;
}
+ // Try to inherit the current page COOP/COEP to have a relevant speculative
+ // RFH. The heuristic for inheriting is to have the most conservative approach
+ // towards BrowsingInstance switching. Every same-origin navigation should
+ // yield a no swap decision. This is done to work with the renderer crash
+ // optimization that instantly commits the speculative RenderFrameHost.
+ network::mojom::CrossOriginOpenerPolicyValue coop;
+ network::mojom::CrossOriginEmbedderPolicyValue coep;
+ RenderFrameHostImpl* current_rfh = frame_tree_node_->current_frame_host();
+
+ bool inherit_coop =
+ current_rfh->has_committed_any_navigation() ||
+ current_rfh->cross_origin_opener_policy().value ==
+ network::mojom::CrossOriginOpenerPolicyValue::kSameOrigin;
+ coop = inherit_coop
+ ? current_rfh->cross_origin_opener_policy().value
+ : network::mojom::CrossOriginOpenerPolicyValue::kUnsafeNone;
+ coep = current_rfh->cross_origin_embedder_policy().value;
+
+ UpdateCoopStatus(coop, coep);
+
if (!NeedsUrlLoader()) {
// The types of pages that don't need a URL Loader should never get served
// from the BackForwardCache.
@@ -1287,18 +1425,21 @@ void NavigationRequest::BeginNavigation() {
CommitNavigation();
return;
}
-
- common_params_->previews_state =
- GetContentClient()->browser()->DetermineAllowedPreviews(
- common_params_->previews_state, this, common_params_->url);
-
+ // If the navigation is served from the back-forward cache, we already know
+ // its preview type from the first time we navigated into the page, so we
+ // should only set |previews_state| when the navigation is not served from the
+ // back-forward cache.
+ if (!IsServedFromBackForwardCache()) {
+ common_params_->previews_state =
+ GetContentClient()->browser()->DetermineAllowedPreviews(
+ common_params_->previews_state, this, common_params_->url);
+ }
WillStartRequest();
}
void NavigationRequest::SetWaitingForRendererResponse() {
EnterChildTraceEvent("WaitingForRendererResponse", this);
- DCHECK(state_ == NOT_STARTED);
- state_ = WAITING_FOR_RENDERER_RESPONSE;
+ SetState(WAITING_FOR_RENDERER_RESPONSE);
}
void NavigationRequest::StartNavigation(bool is_for_commit) {
@@ -1314,7 +1455,7 @@ void NavigationRequest::StartNavigation(bool is_for_commit) {
// starting SiteInstance.
starting_site_instance_ =
frame_tree_node->current_frame_host()->GetSiteInstance();
- site_url_ = GetSiteForCommonParamsURL();
+ site_info_ = GetSiteInfoForCommonParamsURL();
// Compute the redirect chain.
// TODO(clamy): Try to simplify this and have the redirects be part of
@@ -1354,7 +1495,7 @@ void NavigationRequest::StartNavigation(bool is_for_commit) {
}
DCHECK(!IsNavigationStarted());
- state_ = WILL_START_REQUEST;
+ SetState(WILL_START_REQUEST);
navigation_handle_id_ = CreateUniqueHandleID();
modified_request_headers_.Clear();
@@ -1420,7 +1561,7 @@ void NavigationRequest::ResetForCrossDocumentRestart() {
// Reset the state of the NavigationRequest, and the navigation_handle_id.
StopCommitTimeout();
- state_ = NOT_STARTED;
+ SetState(NOT_STARTED);
processing_navigation_throttle_ = false;
navigation_handle_id_ = 0;
@@ -1437,6 +1578,9 @@ void NavigationRequest::ResetForCrossDocumentRestart() {
// Convert the navigation type to the appropriate cross-document one.
common_params_->navigation_type =
ConvertToCrossDocumentType(common_params_->navigation_type);
+
+ // Reset navigation handle timings.
+ navigation_handle_timing_ = std::make_unique<NavigationHandleTiming>();
}
void NavigationRequest::ResetStateForSiteInstanceChange() {
@@ -1508,6 +1652,37 @@ NavigationRequest::TakeCoepReporter() {
return std::move(coep_reporter_);
}
+void NavigationRequest::CreateCoopReporter(
+ StoragePartition* storage_partition) {
+ // If the flag for reporting is off, we simply don't create anything.
+ // Since this is the only place we create COOP reporters this ensure reporting
+ // is completely off.
+ // Note that "popup inheritance" also instantiate a reporter, but only if we
+ // created one here first.
+ if (!base::FeatureList::IsEnabled(
+ network::features::kCrossOriginOpenerPolicyReporting)) {
+ return;
+ }
+
+ // If the page does not have any reporting endpoints, skip creating a
+ // reporter.
+ if (!render_frame_host_->cross_origin_opener_policy().reporting_endpoint &&
+ !render_frame_host_->cross_origin_opener_policy()
+ .report_only_reporting_endpoint) {
+ return;
+ }
+
+ coop_reporter_ = std::make_unique<CrossOriginOpenerPolicyReporter>(
+ storage_partition, frame_tree_node_->current_frame_host(),
+ common_params_->url, render_frame_host_->cross_origin_opener_policy(),
+ render_frame_host_->cross_origin_embedder_policy());
+}
+
+std::unique_ptr<CrossOriginOpenerPolicyReporter>
+NavigationRequest::TakeCoopReporter() {
+ return std::move(coop_reporter_);
+}
+
ukm::SourceId NavigationRequest::GetPreviousPageUkmSourceId() {
return previous_page_load_ukm_source_id_;
}
@@ -1615,7 +1790,9 @@ void NavigationRequest::OnRequestRedirected(
return;
}
- if (const auto blocked_reason = IsBlockedByCorp()) {
+ SanitizeCoopHeaders();
+
+ if (const auto blocked_reason = IsBlockedByResponse()) {
OnRequestFailedInternal(network::URLLoaderCompletionStatus(*blocked_reason),
false /* skip_throttles */,
base::nullopt /* error_page_content */,
@@ -1630,11 +1807,8 @@ void NavigationRequest::OnRequestRedirected(
if (redirect_info.new_method != "POST")
common_params_->post_data.reset();
- // Record the first request start time and response start time.
- if (first_request_start_.is_null())
- first_request_start_ = response_head_->load_timing.send_start;
- if (first_response_start_.is_null())
- first_response_start_ = response_head_->load_timing.receive_headers_start;
+ const bool is_first_response = commit_params_->redirects.empty();
+ UpdateNavigationHandleTimingsOnResponseReceived(is_first_response);
// Mark time for the Navigation Timing API.
if (commit_params_->navigation_timing->redirect_start.is_null()) {
@@ -1696,6 +1870,13 @@ void NavigationRequest::OnRequestRedirected(
return;
}
+ if (base::FeatureList::IsEnabled(
+ network::features::kCrossOriginOpenerPolicy)) {
+ UpdateCoopStatus(
+ response_head_->parsed_headers->cross_origin_opener_policy.value,
+ response_head_->parsed_headers->cross_origin_embedder_policy.value);
+ }
+
// Compute the SiteInstance to use for the redirect and pass its
// RenderProcessHost if it has a process. Keep a reference if it has a
// process, so that the SiteInstance and its associated process aren't deleted
@@ -1754,7 +1935,7 @@ void NavigationRequest::CheckForIsolationOptIn(const GURL& url) {
// origin as non-opt-in before it gets the change to register itself as
// opted-in.
frame_tree_node_->navigator()
- ->GetDelegate()
+ .GetDelegate()
->RegisterExistingOriginToPreventOptInIsolation(
origin, this /* navigation_request_to_exclude */);
}
@@ -1800,12 +1981,8 @@ NavigationRequest::IsOptInIsolationRequested(const GURL& url) {
url, response()->headers.get(),
"OriginIsolationHeader", base::Time::Now()));
- // TODO(https://crbug.com/1066930): For now we just check the presence of the
- // header; we do not parse/validate it. When we do, that will have to be
- // outside the browser process.
const bool requests_via_header =
- header_is_enabled && response()->headers &&
- response()->headers->HasHeader("origin-isolation");
+ header_is_enabled && response_head_->parsed_headers->origin_isolation;
if (requests_via_header)
return OptInIsolationCheckResult::HEADER;
@@ -1813,6 +1990,85 @@ NavigationRequest::IsOptInIsolationRequested(const GURL& url) {
return OptInIsolationCheckResult::NONE;
}
+void NavigationRequest::DetermineOriginIsolationEndResult(
+ OptInIsolationCheckResult check_result) {
+ auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
+ const url::Origin origin = url::Origin::Create(common_params_->url);
+ const IsolationContext& isolation_context =
+ render_frame_host_->GetSiteInstance()->GetIsolationContext();
+ const bool got_isolated =
+ policy->ShouldOriginGetOptInIsolation(isolation_context, origin);
+
+ switch (check_result) {
+ case OptInIsolationCheckResult::NONE:
+ origin_isolation_end_result_ =
+ got_isolated
+ ? OptInOriginIsolationEndResult::kNotRequestedButIsolated
+ : OptInOriginIsolationEndResult::kNotRequestedAndNotIsolated;
+ break;
+ case OptInIsolationCheckResult::ORIGIN_POLICY:
+ origin_isolation_end_result_ =
+ got_isolated ? OptInOriginIsolationEndResult::
+ kRequestedViaOriginPolicyAndIsolated
+ : OptInOriginIsolationEndResult::
+ kRequestedViaOriginPolicyButNotIsolated;
+ break;
+ case OptInIsolationCheckResult::HEADER:
+ origin_isolation_end_result_ =
+ got_isolated
+ ? OptInOriginIsolationEndResult::kRequestedViaHeaderAndIsolated
+ : OptInOriginIsolationEndResult::
+ kRequestedViaHeaderButNotIsolated;
+ break;
+ }
+
+ commit_params_->origin_isolation_restricted =
+ origin_isolation_end_result_ ==
+ OptInOriginIsolationEndResult::kRequestedViaOriginPolicyAndIsolated ||
+ origin_isolation_end_result_ ==
+ OptInOriginIsolationEndResult::kRequestedViaHeaderAndIsolated ||
+ origin_isolation_end_result_ ==
+ OptInOriginIsolationEndResult::kNotRequestedButIsolated;
+}
+
+void NavigationRequest::ProcessOriginIsolationEndResult() {
+ if (!HasCommitted() || IsErrorPage() || IsSameDocument())
+ return;
+
+ if (origin_isolation_end_result_ ==
+ OptInOriginIsolationEndResult::kRequestedViaHeaderAndIsolated ||
+ origin_isolation_end_result_ ==
+ OptInOriginIsolationEndResult::kRequestedViaHeaderButNotIsolated)
+ GetContentClient()->browser()->LogWebFeatureForCurrentPage(
+ render_frame_host_, blink::mojom::WebFeature::kOriginIsolationHeader);
+
+ const url::Origin origin = url::Origin::Create(GetURL());
+
+ if (origin_isolation_end_result_ ==
+ OptInOriginIsolationEndResult::kRequestedViaHeaderButNotIsolated ||
+ origin_isolation_end_result_ ==
+ OptInOriginIsolationEndResult::
+ kRequestedViaOriginPolicyButNotIsolated)
+ render_frame_host_->AddMessageToConsole(
+ blink::mojom::ConsoleMessageLevel::kWarning,
+ base::StringPrintf(
+ "The page requested origin isolation, but could not be isolated "
+ "since the origin '%s' had previously been seen with no "
+ "isolation. Update your headers to uniformly isolate all pages "
+ "on the origin.",
+ origin.Serialize().c_str()));
+
+ if (origin_isolation_end_result_ ==
+ OptInOriginIsolationEndResult::kNotRequestedButIsolated)
+ render_frame_host_->AddMessageToConsole(
+ blink::mojom::ConsoleMessageLevel::kWarning,
+ base::StringPrintf("The page did not request origin isolation, but "
+ "was isolated anyway because the origin '%s' had "
+ "previously been isolated. Update your headers to "
+ "uniformly isolate all pages on the origin.",
+ origin.Serialize().c_str()));
+}
+
void NavigationRequest::OnResponseStarted(
network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
network::mojom::URLResponseHeadPtr response_head,
@@ -1837,7 +2093,7 @@ void NavigationRequest::OnResponseStarted(
DCHECK(response_head);
DCHECK(response_head->parsed_headers);
EnterChildTraceEvent("OnResponseStarted", this);
- state_ = WILL_PROCESS_RESPONSE;
+ SetState(WILL_PROCESS_RESPONSE);
response_head_ = std::move(response_head);
response_body_ = std::move(response_body);
ssl_info_ = response_head_->ssl_info;
@@ -1890,12 +2146,8 @@ void NavigationRequest::OnResponseStarted(
? base::make_optional(appcache_handle_->appcache_host_id())
: base::nullopt;
- // Record the first request start time and first response start time. Skip if
- // the timings are already recorded for redirection etc.
- if (first_request_start_.is_null())
- first_request_start_ = response_head_->load_timing.send_start;
- if (first_response_start_.is_null())
- first_response_start_ = response_head_->load_timing.receive_headers_start;
+ const bool is_first_response = commit_params_->redirects.empty();
+ UpdateNavigationHandleTimingsOnResponseReceived(is_first_response);
// Update fetch start timing. While NavigationRequest updates fetch start
// timing for redirects, it's not aware of service worker interception so
@@ -1928,7 +2180,7 @@ void NavigationRequest::OnResponseStarted(
commit_params_->was_activated = mojom::WasActivatedOption::kNo;
if (!browser_initiated_ &&
- (frame_tree_node_->has_received_user_gesture() ||
+ (frame_tree_node_->HasStickyUserActivation() ||
frame_tree_node_->has_received_user_gesture_before_nav()) &&
ShouldPropagateUserActivation(
frame_tree_node_->current_origin(),
@@ -1946,7 +2198,9 @@ void NavigationRequest::OnResponseStarted(
}
}
- if (const auto blocked_reason = IsBlockedByCorp()) {
+ SanitizeCoopHeaders();
+
+ if (const auto blocked_reason = IsBlockedByResponse()) {
OnRequestFailedInternal(network::URLLoaderCompletionStatus(*blocked_reason),
false /* skip_throttles */,
base::nullopt /* error_page_content */,
@@ -1960,79 +2214,75 @@ void NavigationRequest::OnResponseStarted(
response_head_->parsed_headers->cross_origin_embedder_policy;
if (base::FeatureList::IsEnabled(
network::features::kCrossOriginEmbedderPolicy)) {
- // https://mikewest.github.io/corpp/#process-navigation-response
- if (auto* const parent_frame = GetParentFrame()) {
- const auto& parent_coep = parent_frame->cross_origin_embedder_policy();
- const auto& url = common_params_->url;
- constexpr auto kRequireCorp =
- network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp;
- constexpr auto kNone =
- network::mojom::CrossOriginEmbedderPolicyValue::kNone;
-
- // Some special URLs not loaded using the network are inheriting the
- // Cross-Origin-Embedder-Policy header from their parent.
- const bool has_allowed_scheme =
- url.SchemeIsBlob() || url.SchemeIs(url::kDataScheme) ||
- GetContentClient()
- ->browser()
- ->ShouldInheritCrossOriginEmbedderPolicyImplicitly(url);
- if (parent_coep.value == kRequireCorp && has_allowed_scheme) {
- cross_origin_embedder_policy.value = kRequireCorp;
- }
+ const auto& url = common_params_->url;
+ // https://w3c.github.io/webappsec-secure-contexts/#is-url-trustworthy
+ // returns "Potentially Trustworthy" for data URLs, but
+ // network::IsUrlPotentiallyTrustworthy returns false, so we need this
+ // extra condition.
+ if (network::IsUrlPotentiallyTrustworthy(url) ||
+ url.SchemeIs(url::kDataScheme)) {
+ // https://mikewest.github.io/corpp/#process-navigation-response
+ if (auto* const parent = GetParentFrame()) {
+ const auto& parent_coep = parent->cross_origin_embedder_policy();
+ constexpr auto kRequireCorp =
+ network::mojom::CrossOriginEmbedderPolicyValue::kRequireCorp;
+ constexpr auto kNone =
+ network::mojom::CrossOriginEmbedderPolicyValue::kNone;
+
+ // Some special URLs not loaded using the network are inheriting the
+ // Cross-Origin-Embedder-Policy header from their parent.
+ const bool has_allowed_scheme =
+ url.SchemeIsBlob() || url.SchemeIs(url::kDataScheme) ||
+ GetContentClient()
+ ->browser()
+ ->ShouldInheritCrossOriginEmbedderPolicyImplicitly(url);
+ if (parent_coep.value == kRequireCorp && has_allowed_scheme) {
+ cross_origin_embedder_policy.value = kRequireCorp;
+ }
- auto* const coep_reporter = parent_frame->coep_reporter();
- if (parent_coep.report_only_value == kRequireCorp &&
- !has_allowed_scheme && cross_origin_embedder_policy.value == kNone &&
- coep_reporter) {
- coep_reporter->QueueNavigationReport(redirect_chain_[0],
- /*report_only=*/true);
- }
- if (parent_coep.value == kRequireCorp &&
- cross_origin_embedder_policy.value == kNone) {
- if (coep_reporter) {
+ auto* const coep_reporter = parent->coep_reporter();
+ if (parent_coep.report_only_value == kRequireCorp &&
+ !has_allowed_scheme &&
+ cross_origin_embedder_policy.value == kNone && coep_reporter) {
coep_reporter->QueueNavigationReport(redirect_chain_[0],
- /*report_only=*/false);
+ /*report_only=*/true);
+ }
+ if (parent_coep.value == kRequireCorp &&
+ cross_origin_embedder_policy.value == kNone) {
+ if (coep_reporter) {
+ coep_reporter->QueueNavigationReport(redirect_chain_[0],
+ /*report_only=*/false);
+ }
+ OnRequestFailedInternal(network::URLLoaderCompletionStatus(
+ network::mojom::BlockedByResponseReason::
+ kCoepFrameResourceNeedsCoepHeader),
+ false /* skip_throttles */,
+ base::nullopt /* error_page_content */,
+ false /* collapse_frame */);
+ // DO NOT ADD CODE after this. The previous call to
+ // OnRequestFailedInternal has destroyed the NavigationRequest.
+ return;
}
- OnRequestFailedInternal(network::URLLoaderCompletionStatus(
- network::mojom::BlockedByResponseReason::
- kCoepFrameResourceNeedsCoepHeader),
- false /* skip_throttles */,
- base::nullopt /* error_page_content */,
- false /* collapse_frame */);
- // DO NOT ADD CODE after this. The previous call to
- // OnRequestFailedInternal has destroyed the NavigationRequest.
- return;
}
+ } else {
+ cross_origin_embedder_policy = network::CrossOriginEmbedderPolicy();
}
}
if (base::FeatureList::IsEnabled(
network::features::kCrossOriginOpenerPolicy)) {
- // The Cross-Origin-Opener-Policy header should be ignored if delivered in
- // insecure contexts, and non-top level documents.
- if (!IsOriginSecure(common_params_->url) || !IsInMainFrame()) {
- response_head_->parsed_headers->cross_origin_opener_policy =
- network::CrossOriginOpenerPolicy();
- }
-
- // Popups with a sandboxing flag, inherited from their opener, are not
- // allowed to navigate to a document with a Cross-Origin-Opener-Policy that
- // is not "unsafe-none". This ensures a COOP document does not inherit any
- // property from an opener.
- // https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e
- if (response_head_->parsed_headers->cross_origin_opener_policy.value !=
- network::mojom::CrossOriginOpenerPolicyValue::kUnsafeNone &&
- (frame_tree_node_->pending_frame_policy().sandbox_flags !=
- network::mojom::WebSandboxFlags::kNone)) {
- OnRequestFailedInternal(
- network::URLLoaderCompletionStatus(
- network::mojom::BlockedByResponseReason::
- kCoopSandboxedIFrameCannotNavigateToCoopPage),
- false /* skip_throttles */, base::nullopt /* error_page_content */,
- false /* collapse_frame */);
- // DO NOT ADD CODE after this. The previous call to
- // OnRequestFailedInternal has destroyed the NavigationRequest.
- return;
+ UpdateCoopStatus(
+ response_head_->parsed_headers->cross_origin_opener_policy.value,
+ response_head_->parsed_headers->cross_origin_embedder_policy.value);
+
+ RenderFrameHostImpl* current_rfh = frame_tree_node_->current_frame_host();
+ if (coop_status_.require_browsing_instance_swap &&
+ coop_status_.had_opener_before_browsing_instance_swap &&
+ current_rfh->coop_reporter()) {
+ current_rfh->coop_reporter()->QueueOpenerBreakageReport(
+ current_rfh->coop_reporter()->GetNextDocumentUrlForReporting(
+ GetRedirectChain(), GetInitiatorRoutingId()),
+ true /* is_reported_from_document */, false /* is_report_only */);
}
}
@@ -2066,10 +2316,7 @@ void NavigationRequest::OnResponseStarted(
DCHECK(render_frame_host_ || !response_should_be_rendered_);
if (render_frame_host_) {
- if (opt_in_isolation == OptInIsolationCheckResult::HEADER) {
- GetContentClient()->browser()->LogWebFeatureForCurrentPage(
- render_frame_host_, blink::mojom::WebFeature::kOriginIsolationHeader);
- }
+ DetermineOriginIsolationEndResult(opt_in_isolation);
// TODO(pmeuleman, ahemery): Only set COOP and COEP values on
// RenderFrameHost when the navigation commits. In the meantime, keep them
@@ -2091,7 +2338,7 @@ void NavigationRequest::OnResponseStarted(
// Allow the embedder to cancel the cross-process commit if needed.
// TODO(clamy): Rename ShouldTransferNavigation.
- if (!frame_tree_node_->navigator()->GetDelegate()->ShouldTransferNavigation(
+ if (!frame_tree_node_->navigator().GetDelegate()->ShouldTransferNavigation(
frame_tree_node_->IsMainFrame())) {
net_error_ = net::ERR_ABORTED;
frame_tree_node_->ResetNavigationRequest(false);
@@ -2160,7 +2407,7 @@ void NavigationRequest::OnResponseStarted(
// the new SiteInstance can be used with the old entry if we return to it.
// See http://crbug.com/992198 for further context.
NavigationController* controller =
- frame_tree_node_->navigator()->GetController();
+ frame_tree_node_->navigator().GetController();
NavigationEntryImpl* nav_entry;
if (controller &&
(nav_entry = static_cast<NavigationEntryImpl*>(
@@ -2179,7 +2426,10 @@ void NavigationRequest::OnResponseStarted(
frame_entry->committed_origin(), frame_entry->referrer(),
frame_entry->initiator_origin(), frame_entry->redirect_chain(),
frame_entry->page_state(), frame_entry->method(),
- frame_entry->post_id(), frame_entry->blob_url_loader_factory());
+ frame_entry->post_id(), frame_entry->blob_url_loader_factory(),
+ frame_entry->web_bundle_navigation_info()
+ ? frame_entry->web_bundle_navigation_info()->Clone()
+ : nullptr);
}
}
@@ -2208,6 +2458,10 @@ void NavigationRequest::OnResponseStarted(
net::Error net_error = CheckContentSecurityPolicy(
was_redirected_ /* has_followed_redirect */,
false /* url_upgraded_after_redirect */, true /* is_response_check */);
+ DCHECK_NE(net_error, net::ERR_BLOCKED_BY_CLIENT);
+ // TODO(https://crbug.com/1090859): Remove this once the bug has been fixed.
+ if (net_error == net::ERR_BLOCKED_BY_CLIENT)
+ base::debug::DumpWithoutCrashing();
if (net_error != net::OK) {
OnRequestFailedInternal(network::URLLoaderCompletionStatus(net_error),
false /* skip_throttles */,
@@ -2240,10 +2494,7 @@ void NavigationRequest::OnRequestFailedInternal(
bool skip_throttles,
const base::Optional<std::string>& error_page_content,
bool collapse_frame) {
- DCHECK(state_ == WILL_START_NAVIGATION || state_ == WILL_START_REQUEST ||
- state_ == WILL_REDIRECT_REQUEST || state_ == WILL_PROCESS_RESPONSE ||
- state_ == DID_COMMIT || state_ == CANCELING ||
- state_ == WILL_FAIL_REQUEST);
+ CheckStateTransition(WILL_FAIL_REQUEST);
DCHECK(!(status.error_code == net::ERR_ABORTED &&
error_page_content.has_value()));
ScopedNavigationRequestCrashKeys crash_keys(this);
@@ -2261,7 +2512,7 @@ void NavigationRequest::OnRequestFailedInternal(
// TODO(https://crbug.com/757633): Check that ssl_info.has_value() if
// net_error is a certificate error.
EnterChildTraceEvent("OnRequestFailed", this, "error", status.error_code);
- state_ = WILL_FAIL_REQUEST;
+ SetState(WILL_FAIL_REQUEST);
processing_navigation_throttle_ = false;
// Ensure the pending entry also gets discarded if it has no other active
@@ -2356,12 +2607,12 @@ bool NavigationRequest::ShouldKeepErrorPageInCurrentProcess(int net_error) {
// URLs should be allowed to transfer away from the current process, which
// didn't request the navigation and may have a higher privilege level
// than the blocked destination.
- return net_error == net::ERR_BLOCKED_BY_CLIENT && !browser_initiated();
+ return net::IsRequestBlockedError(net_error) && !browser_initiated();
}
void NavigationRequest::OnRequestStarted(base::TimeTicks timestamp) {
- frame_tree_node_->navigator()->LogResourceRequestTime(timestamp,
- common_params_->url);
+ frame_tree_node_->navigator().LogResourceRequestTime(timestamp,
+ common_params_->url);
}
namespace {
@@ -2391,8 +2642,7 @@ void NavigationRequest::OnStartChecksComplete(
result.action() == NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE) {
#if DCHECK_IS_ON()
if (result.action() == NavigationThrottle::BLOCK_REQUEST) {
- DCHECK(result.net_error_code() == net::ERR_BLOCKED_BY_CLIENT ||
- result.net_error_code() == net::ERR_BLOCKED_BY_ADMINISTRATOR);
+ DCHECK(net::IsRequestBlockedError(result.net_error_code()));
}
// TODO(clamy): distinguish between CANCEL and CANCEL_AND_IGNORE.
else if (result.action() == NavigationThrottle::CANCEL_AND_IGNORE) {
@@ -2407,8 +2657,8 @@ void NavigationRequest::OnStartChecksComplete(
// is no onbeforeunload handler or if a NavigationThrottle cancelled it,
// then this could cause reentrancy into NavigationController. So use a
// PostTask to avoid that.
- base::PostTask(FROM_HERE, {BrowserThread::UI},
- base::BindOnce(&NavigationRequest::OnRequestFailedInternal,
+ GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE, base::BindOnce(&NavigationRequest::OnRequestFailedInternal,
weak_factory_.GetWeakPtr(),
network::URLLoaderCompletionStatus(
result.net_error_code()),
@@ -2433,7 +2683,7 @@ void NavigationRequest::OnStartChecksComplete(
SetExpectedProcess(navigating_frame_host->GetProcess());
BrowserContext* browser_context =
- frame_tree_node_->navigator()->GetController()->GetBrowserContext();
+ frame_tree_node_->navigator().GetController()->GetBrowserContext();
StoragePartition* partition = BrowserContext::GetStoragePartition(
browser_context, navigating_frame_host->GetSiteInstance());
DCHECK(partition);
@@ -2465,9 +2715,9 @@ void NavigationRequest::OnStartChecksComplete(
.application_cache_enabled) {
// The final process id won't be available until
// NavigationRequest::ReadyToCommitNavigation.
- appcache_handle_.reset(new AppCacheNavigationHandle(
+ appcache_handle_ = std::make_unique<AppCacheNavigationHandle>(
static_cast<ChromeAppCacheService*>(partition->GetAppCacheService()),
- ChildProcessHost::kInvalidUniqueID));
+ ChildProcessHost::kInvalidUniqueID);
}
}
@@ -2570,6 +2820,11 @@ void NavigationRequest::OnServiceWorkerAccessed(
GetDelegate()->OnServiceWorkerAccessed(this, scope, allowed);
}
+base::Optional<network::mojom::WebSandboxFlags>
+NavigationRequest::SandboxFlagsToCommit() {
+ return sandbox_flags_to_commit_;
+}
+
void NavigationRequest::OnRedirectChecksComplete(
NavigationThrottle::ThrottleCheckResult result) {
DCHECK(result.action() != NavigationThrottle::DEFER);
@@ -2595,8 +2850,7 @@ void NavigationRequest::OnRedirectChecksComplete(
if (result.action() == NavigationThrottle::BLOCK_REQUEST ||
result.action() == NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE) {
- DCHECK(result.net_error_code() == net::ERR_BLOCKED_BY_CLIENT ||
- result.net_error_code() == net::ERR_BLOCKED_BY_ADMINISTRATOR);
+ DCHECK(net::IsRequestBlockedError(result.net_error_code()));
OnRequestFailedInternal(
network::URLLoaderCompletionStatus(result.net_error_code()),
true /* skip_throttles */, result.error_page_content(), collapse_frame);
@@ -2616,7 +2870,7 @@ void NavigationRequest::OnRedirectChecksComplete(
// Add any required Client Hints to the current request.
BrowserContext* browser_context =
- frame_tree_node_->navigator()->GetController()->GetBrowserContext();
+ frame_tree_node_->navigator().GetController()->GetBrowserContext();
ClientHintsControllerDelegate* client_hints_delegate =
browser_context->GetClientHintsControllerDelegate();
if (client_hints_delegate) {
@@ -2695,7 +2949,7 @@ void NavigationRequest::OnWillProcessResponseChecksComplete(
resource_request->trusted_params->isolation_info = GetIsolationInfo();
BrowserContext* browser_context =
- frame_tree_node_->navigator()->GetController()->GetBrowserContext();
+ frame_tree_node_->navigator().GetController()->GetBrowserContext();
DownloadManagerImpl* download_manager = static_cast<DownloadManagerImpl*>(
BrowserContext::GetDownloadManager(browser_context));
download_manager->InterceptNavigation(
@@ -2797,6 +3051,7 @@ void NavigationRequest::CommitErrorPage(
}
}
+ sandbox_flags_to_commit_ = ComputeSandboxFlagsToCommit();
ReadyToCommitNavigation(true);
render_frame_host_->FailedNavigation(this, *common_params_, *commit_params_,
has_stale_copy_in_cache_, net_error_,
@@ -2865,10 +3120,12 @@ void NavigationRequest::CommitNavigation() {
}
}
+ sandbox_flags_to_commit_ = ComputeSandboxFlagsToCommit();
CreateCoepReporter(render_frame_host_->GetProcess()->GetStoragePartition());
+ CreateCoopReporter(render_frame_host_->GetProcess()->GetStoragePartition());
- blink::mojom::ServiceWorkerProviderInfoForClientPtr
- service_worker_provider_info;
+ blink::mojom::ServiceWorkerContainerInfoForClientPtr
+ service_worker_container_info;
if (service_worker_handle_) {
DCHECK(coep_reporter());
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
@@ -2880,12 +3137,23 @@ void NavigationRequest::CommitNavigation() {
render_frame_host_->GetProcess()->GetID(),
render_frame_host_->GetRoutingID(),
render_frame_host_->cross_origin_embedder_policy(),
- std::move(reporter_remote), &service_worker_provider_info);
+ std::move(reporter_remote), &service_worker_container_info);
}
- if (web_bundle_handle_ && web_bundle_handle_->navigation_info()) {
- web_bundle_navigation_info_ =
- web_bundle_handle_->navigation_info()->Clone();
+ if (web_bundle_handle_) {
+ // Check whether the page was served from a web bundle.
+ if (web_bundle_handle_->navigation_info()) {
+ // If the page was served from a web bundle, sets
+ // |web_bundle_navigation_info_| which will be passed to
+ // the FrameNavigationEntry of the navigation, and will be used for
+ // history navigations.
+ web_bundle_navigation_info_ =
+ web_bundle_handle_->navigation_info()->Clone();
+ } else {
+ // If the page was not served from a web bundle, clears
+ // |web_bundle_handle_| not to pass it to |render_frame_host_|.
+ web_bundle_handle_.reset();
+ }
}
auto common_params = common_params_->Clone();
@@ -2902,8 +3170,9 @@ void NavigationRequest::CommitNavigation() {
std::move(response_head), std::move(response_body_),
std::move(url_loader_client_endpoints_), is_view_source_,
std::move(subresource_loader_params_), std::move(subresource_overrides_),
- std::move(service_worker_provider_info), devtools_navigation_token_,
+ std::move(service_worker_container_info), devtools_navigation_token_,
std::move(web_bundle_handle_));
+ UpdateNavigationHandleTimingsOnCommitSent();
// Give SpareRenderProcessHostManager a heads-up about the most recently used
// BrowserContext. This is mostly needed to make sure the spare is warmed-up
@@ -2921,8 +3190,8 @@ void NavigationRequest::ResetExpectedProcess() {
RenderProcessHost::FromID(expected_render_process_host_id_);
if (process) {
RenderProcessHostImpl::RemoveExpectedNavigationToSite(
- frame_tree_node()->navigator()->GetController()->GetBrowserContext(),
- process, site_url_);
+ frame_tree_node()->navigator().GetController()->GetBrowserContext(),
+ process, site_info_);
process->RemoveObserver(this);
}
expected_render_process_host_id_ = ChildProcessHost::kInvalidUniqueID;
@@ -2943,12 +3212,12 @@ void NavigationRequest::SetExpectedProcess(
return;
// Keep track of the speculative RenderProcessHost and tell it to expect a
- // navigation to |site_url_|.
+ // navigation to |site_info_|.
expected_render_process_host_id_ = expected_process->GetID();
expected_process->AddObserver(this);
RenderProcessHostImpl::AddExpectedNavigationToSite(
- frame_tree_node()->navigator()->GetController()->GetBrowserContext(),
- expected_process, site_url_);
+ frame_tree_node()->navigator().GetController()->GetBrowserContext(),
+ expected_process, site_info_);
}
void NavigationRequest::RenderProcessHostDestroyed(RenderProcessHost* host) {
@@ -2960,23 +3229,66 @@ void NavigationRequest::RenderProcessExited(
RenderProcessHost* host,
const ChildProcessTerminationInfo& info) {}
-void NavigationRequest::UpdateSiteURL(
+void NavigationRequest::UpdateNavigationHandleTimingsOnResponseReceived(
+ bool is_first_response) {
+ base::TimeTicks loader_callback_time = base::TimeTicks::Now();
+
+ if (is_first_response) {
+ DCHECK(navigation_handle_timing_->first_request_start_time.is_null());
+ DCHECK(navigation_handle_timing_->first_response_start_time.is_null());
+ DCHECK(navigation_handle_timing_->first_loader_callback_time.is_null());
+ navigation_handle_timing_->first_request_start_time =
+ response_head_->load_timing.send_start;
+ navigation_handle_timing_->first_response_start_time =
+ response_head_->load_timing.receive_headers_start;
+ navigation_handle_timing_->first_loader_callback_time =
+ loader_callback_time;
+ }
+
+ navigation_handle_timing_->final_request_start_time =
+ response_head_->load_timing.send_start;
+ navigation_handle_timing_->final_response_start_time =
+ response_head_->load_timing.receive_headers_start;
+ navigation_handle_timing_->final_loader_callback_time = loader_callback_time;
+
+ // 103 Early Hints experiment (https://crbug.com/1093693).
+ if (is_first_response) {
+ DCHECK(navigation_handle_timing_->early_hints_for_first_request_time
+ .is_null());
+ navigation_handle_timing_->early_hints_for_first_request_time =
+ response_head_->load_timing.first_early_hints_time;
+ }
+ navigation_handle_timing_->early_hints_for_final_request_time =
+ response_head_->load_timing.first_early_hints_time;
+
+ // |navigation_commit_sent_time| will be updated by
+ // UpdateNavigationHandleTimingsOnCommitSent() later.
+ DCHECK(navigation_handle_timing_->navigation_commit_sent_time.is_null());
+}
+
+void NavigationRequest::UpdateNavigationHandleTimingsOnCommitSent() {
+ DCHECK(navigation_handle_timing_->navigation_commit_sent_time.is_null());
+ navigation_handle_timing_->navigation_commit_sent_time =
+ base::TimeTicks::Now();
+}
+
+void NavigationRequest::UpdateSiteInfo(
RenderProcessHost* post_redirect_process) {
- GURL new_site_url = GetSiteForCommonParamsURL();
+ SiteInfo new_site_info = GetSiteInfoForCommonParamsURL();
int post_redirect_process_id = post_redirect_process
? post_redirect_process->GetID()
: ChildProcessHost::kInvalidUniqueID;
- if (new_site_url == site_url_ &&
+ if (new_site_info == site_info_ &&
post_redirect_process_id == expected_render_process_host_id_) {
return;
}
- // Stop expecting a navigation to the current site URL in the current expected
+ // Stop expecting a navigation to the current SiteInfo in the current expected
// process.
ResetExpectedProcess();
- // Update the site URL and the expected process.
- site_url_ = new_site_url;
+ // Update the SiteInfo and the expected process.
+ site_info_ = new_site_info;
SetExpectedProcess(post_redirect_process);
}
@@ -3028,11 +3340,8 @@ net::Error NavigationRequest::CheckCSPDirectives(
if (navigate_to_allowed && frame_src_allowed)
return net::OK;
- // If 'frame-src' fails, ERR_BLOCKED_BY_CLIENT is used instead.
- // If both checks fail, ERR_BLOCKED_BY_CLIENT is used to keep the existing
- // behaviour before 'navigate-to' was introduced.
if (!frame_src_allowed)
- return net::ERR_BLOCKED_BY_CLIENT;
+ return net::ERR_BLOCKED_BY_CSP;
// net::ERR_ABORTED is used to ensure that the navigation is cancelled
// when the 'navigate-to' directive check is failed. This is a better user
@@ -3095,6 +3404,8 @@ net::Error NavigationRequest::CheckContentSecurityPolicy(
parent->ContentSecurityPolicies())) {
upgrade_if_insecure_ = true;
network::UpgradeInsecureRequest(&common_params_->url);
+ common_params_->referrer = Referrer::SanitizeForRequest(
+ common_params_->url, *common_params_->referrer);
commit_params_->original_url = common_params_->url;
}
}
@@ -3192,7 +3503,7 @@ NavigationRequest::AboutSrcDocCheckResult NavigationRequest::CheckAboutSrcDoc()
void NavigationRequest::UpdateCommitNavigationParamsHistory() {
NavigationController* navigation_controller =
- frame_tree_node_->navigator()->GetController();
+ frame_tree_node_->navigator().GetController();
commit_params_->current_history_list_offset =
navigation_controller->GetCurrentEntryIndex();
commit_params_->current_history_list_length =
@@ -3207,7 +3518,7 @@ void NavigationRequest::OnRendererAbortedNavigation() {
if (IsWaitingToCommit()) {
render_frame_host_->NavigationRequestCancelled(this);
} else {
- frame_tree_node_->navigator()->CancelNavigation(frame_tree_node_);
+ frame_tree_node_->navigator().CancelNavigation(frame_tree_node_);
}
// Do not add code after this, NavigationRequest has been destroyed.
@@ -3236,7 +3547,7 @@ int NavigationRequest::EstimateHistoryOffset() {
return 0;
NavigationController* controller =
- frame_tree_node_->navigator()->GetController();
+ frame_tree_node_->navigator().GetController();
if (!controller) // Interstitial page.
return 1;
@@ -3331,7 +3642,7 @@ void NavigationRequest::OnWillStartRequestProcessed(
DCHECK(processing_navigation_throttle_);
processing_navigation_throttle_ = false;
if (result.action() != NavigationThrottle::PROCEED)
- state_ = CANCELING;
+ SetState(CANCELING);
if (complete_callback_for_testing_ &&
std::move(complete_callback_for_testing_).Run(result)) {
@@ -3359,7 +3670,7 @@ void NavigationRequest::OnWillRedirectRequestProcessed(
GetDelegate()->DidRedirectNavigation(this);
}
} else {
- state_ = CANCELING;
+ SetState(CANCELING);
}
if (complete_callback_for_testing_ &&
@@ -3382,7 +3693,7 @@ void NavigationRequest::OnWillFailRequestProcessed(
result = NavigationThrottle::ThrottleCheckResult(
NavigationThrottle::PROCEED, net_error_);
} else {
- state_ = CANCELING;
+ SetState(CANCELING);
}
if (complete_callback_for_testing_ &&
@@ -3417,7 +3728,7 @@ void NavigationRequest::OnWillProcessResponseProcessed(
if (!weak_self)
return;
} else {
- state_ = CANCELING;
+ SetState(CANCELING);
}
if (complete_callback_for_testing_ &&
@@ -3431,7 +3742,7 @@ void NavigationRequest::OnWillProcessResponseProcessed(
}
NavigatorDelegate* NavigationRequest::GetDelegate() const {
- return frame_tree_node()->navigator()->GetDelegate();
+ return frame_tree_node()->navigator().GetDelegate();
}
void NavigationRequest::Resume(NavigationThrottle* resuming_throttle) {
@@ -3485,7 +3796,7 @@ void NavigationRequest::CancelDeferredNavigationInternal(
EnterChildTraceEvent("CancelDeferredNavigation", this);
NavigationState old_state = state_;
- state_ = CANCELING;
+ SetState(CANCELING);
if (complete_callback_for_testing_ &&
std::move(complete_callback_for_testing_).Run(result)) {
return;
@@ -3516,7 +3827,7 @@ void NavigationRequest::WillStartRequest() {
DCHECK_EQ(state_, WILL_START_REQUEST);
if (IsSelfReferentialURL()) {
- state_ = CANCELING;
+ SetState(CANCELING);
if (complete_callback_for_testing_ &&
std::move(complete_callback_for_testing_)
.Run(NavigationThrottle::CANCEL)) {
@@ -3552,10 +3863,10 @@ void NavigationRequest::WillRedirectRequest(
EnterChildTraceEvent("WillRedirectRequest", this, "url",
common_params_->url.possibly_invalid_spec());
UpdateStateFollowingRedirect(new_referrer_url);
- UpdateSiteURL(post_redirect_process);
+ UpdateSiteInfo(post_redirect_process);
if (IsSelfReferentialURL()) {
- state_ = CANCELING;
+ SetState(CANCELING);
if (complete_callback_for_testing_ &&
std::move(complete_callback_for_testing_)
.Run(NavigationThrottle::CANCEL)) {
@@ -3578,7 +3889,7 @@ void NavigationRequest::WillRedirectRequest(
void NavigationRequest::WillFailRequest() {
EnterChildTraceEvent("WillFailRequest", this);
- state_ = WILL_FAIL_REQUEST;
+ SetState(WILL_FAIL_REQUEST);
processing_navigation_throttle_ = true;
// Notify each throttle of the request.
@@ -3649,10 +3960,10 @@ void NavigationRequest::DidCommitNavigation(
if (params.base_url.spec() == kUnreachableWebDataURL ||
net_error_ != net::OK) {
EnterChildTraceEvent("DidCommitNavigation: error page", this);
- state_ = DID_COMMIT_ERROR_PAGE;
+ SetState(DID_COMMIT_ERROR_PAGE);
} else {
EnterChildTraceEvent("DidCommitNavigation", this);
- state_ = DID_COMMIT;
+ SetState(DID_COMMIT);
}
StopCommitTimeout();
@@ -3661,7 +3972,7 @@ void NavigationRequest::DidCommitNavigation(
// The renderer already knows locally about it because we sent an empty name
// at frame creation time. The renderer has now committed the page and we can
// safely enforce the empty name on the browser side.
- if (require_coop_browsing_instance_swap()) {
+ if (coop_status().require_browsing_instance_swap) {
std::string name, unique_name;
// "COOP swaps" only affect main frames, that have an empty unique name.
DCHECK(frame_tree_node_->unique_name().empty());
@@ -3694,12 +4005,18 @@ void NavigationRequest::DidCommitNavigation(
DCHECK(!IsSameDocument() || !frame_tree_node()->is_collapsed());
frame_tree_node()->SetCollapsed(false);
}
+
+ if (service_worker_handle_) {
+ // Notify the service worker navigation handle that the navigation finished
+ // committing.
+ service_worker_handle_->OnEndNavigationCommit();
+ }
}
-GURL NavigationRequest::GetSiteForCommonParamsURL() const {
+SiteInfo NavigationRequest::GetSiteInfoForCommonParamsURL() const {
// TODO(alexmos): Using |starting_site_instance_|'s IsolationContext may not
// be correct for cross-BrowsingInstance redirects.
- return SiteInstanceImpl::GetSiteForURL(
+ return SiteInstanceImpl::ComputeSiteInfo(
starting_site_instance_->GetIsolationContext(), common_params_->url);
}
@@ -3725,7 +4042,7 @@ void NavigationRequest::UpdateStateFollowingRedirect(
was_redirected_ = true;
redirect_chain_.push_back(common_params_->url);
- state_ = WILL_REDIRECT_REQUEST;
+ SetState(WILL_REDIRECT_REQUEST);
processing_navigation_throttle_ = true;
#if defined(OS_ANDROID)
@@ -3759,7 +4076,7 @@ bool NavigationRequest::NeedsUrlLoader() {
void NavigationRequest::ReadyToCommitNavigation(bool is_error) {
EnterChildTraceEvent("ReadyToCommitNavigation", this);
- state_ = READY_TO_COMMIT;
+ SetState(READY_TO_COMMIT);
ready_to_commit_time_ = base::TimeTicks::Now();
RestartCommitTimeout();
@@ -4034,12 +4351,8 @@ base::TimeTicks NavigationRequest::NavigationInputStart() {
return common_params().input_start;
}
-base::TimeTicks NavigationRequest::FirstRequestStart() {
- return first_request_start_;
-}
-
-base::TimeTicks NavigationRequest::FirstResponseStart() {
- return first_response_start_;
+const NavigationHandleTiming& NavigationRequest::GetNavigationHandleTiming() {
+ return *navigation_handle_timing_;
}
bool NavigationRequest::IsPost() {
@@ -4050,6 +4363,12 @@ const blink::mojom::Referrer& NavigationRequest::GetReferrer() {
return *sanitized_referrer_;
}
+void NavigationRequest::SetReferrer(blink::mojom::ReferrerPtr referrer) {
+ DCHECK(state_ == WILL_START_REQUEST || state_ == WILL_REDIRECT_REQUEST);
+ sanitized_referrer_ = std::move(referrer);
+ common_params_->referrer = sanitized_referrer_.Clone();
+}
+
bool NavigationRequest::HasUserGesture() {
return common_params().has_user_gesture;
}
@@ -4240,7 +4559,7 @@ void NavigationRequest::SetIsOverridingUserAgent(bool override_ua) {
? GetContentClient()->browser()->GetUserAgent()
: user_agent_override);
BrowserContext* browser_context =
- frame_tree_node_->navigator()->GetController()->GetBrowserContext();
+ frame_tree_node_->navigator().GetController()->GetBrowserContext();
ClientHintsControllerDelegate* client_hints_delegate =
browser_context->GetClientHintsControllerDelegate();
if (client_hints_delegate) {
@@ -4298,8 +4617,8 @@ void NavigationRequest::RestartBackForwardCachedNavigation() {
"NavigationRequest::RestartBackForwardCachedNavigation");
CHECK(IsServedFromBackForwardCache());
restarting_back_forward_cached_navigation_ = true;
- base::PostTask(
- FROM_HERE, {BrowserThread::UI},
+ GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE,
base::BindOnce(&NavigationRequest::RestartBackForwardCachedNavigationImpl,
weak_factory_.GetWeakPtr()));
}
@@ -4328,27 +4647,44 @@ void NavigationRequest::ForceEnableOriginTrials(
}
base::Optional<network::mojom::BlockedByResponseReason>
-NavigationRequest::IsBlockedByCorp() {
- if (!base::FeatureList::IsEnabled(
- network::features::kCrossOriginEmbedderPolicy)) {
- return base::nullopt;
- }
- // https://mikewest.github.io/corpp/#integration-html
- auto* parent_frame = GetParentFrame();
- if (!parent_frame) {
- return base::nullopt;
+NavigationRequest::IsBlockedByResponse() {
+ if (base::FeatureList::IsEnabled(
+ network::features::kCrossOriginOpenerPolicy)) {
+ // Popups with a sandboxing flag, inherited from their opener, are not
+ // allowed to navigate to a document with a Cross-Origin-Opener-Policy that
+ // is not "unsafe-none". This ensures a COOP document does not inherit any
+ // property from an opener.
+ // https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e
+ if (response_head_->parsed_headers->cross_origin_opener_policy.value !=
+ network::mojom::CrossOriginOpenerPolicyValue::kUnsafeNone &&
+ (frame_tree_node_->pending_frame_policy().sandbox_flags !=
+ network::mojom::WebSandboxFlags::kNone)) {
+ return network::mojom::BlockedByResponseReason::
+ kCoopSandboxedIFrameCannotNavigateToCoopPage;
+ }
}
- const auto& url = common_params_->url;
- // Some special URLs not loaded using the network are inheriting the
- // Cross-Origin-Embedder-Policy header from their parent.
- if (url.SchemeIsBlob() || url.SchemeIs(url::kDataScheme)) {
- return base::nullopt;
+
+ if (base::FeatureList::IsEnabled(
+ network::features::kCrossOriginEmbedderPolicy)) {
+ // https://mikewest.github.io/corpp/#integration-html
+ auto* parent_frame = GetParentFrame();
+ if (!parent_frame) {
+ return base::nullopt;
+ }
+ const auto& url = common_params_->url;
+ // Some special URLs not loaded using the network are inheriting the
+ // Cross-Origin-Embedder-Policy header from their parent.
+ if (url.SchemeIsBlob() || url.SchemeIs(url::kDataScheme)) {
+ return base::nullopt;
+ }
+ return network::CrossOriginResourcePolicy::IsNavigationBlocked(
+ url, redirect_chain_[0], parent_frame->GetLastCommittedOrigin(),
+ *response_head_, parent_frame->GetLastCommittedOrigin(),
+ parent_frame->cross_origin_embedder_policy(),
+ parent_frame->coep_reporter());
}
- return network::CrossOriginResourcePolicy::IsNavigationBlocked(
- url, redirect_chain_[0], parent_frame->GetLastCommittedOrigin(),
- *response_head_, parent_frame->GetLastCommittedOrigin(),
- parent_frame->cross_origin_embedder_policy(),
- parent_frame->coep_reporter());
+
+ return base::nullopt;
}
std::unique_ptr<PeakGpuMemoryTracker>
@@ -4358,7 +4694,7 @@ NavigationRequest::TakePeakGpuMemoryTracker() {
std::string NavigationRequest::GetUserAgentOverride() {
return IsOverridingUserAgent() ? frame_tree_node_->navigator()
- ->GetDelegate()
+ .GetDelegate()
->GetUserAgentOverride()
.ua_string_override
: std::string();
@@ -4366,7 +4702,7 @@ std::string NavigationRequest::GetUserAgentOverride() {
NavigationControllerImpl* NavigationRequest::GetNavigationController() {
return static_cast<NavigationControllerImpl*>(
- frame_tree_node_->navigator()->GetController());
+ frame_tree_node_->navigator().GetController());
}
mojo::PendingRemote<network::mojom::CookieAccessObserver>
@@ -4403,4 +4739,144 @@ NavigationRequest::TakeCookieObservers() {
return cookie_observers_.TakeReceivers();
}
+network::mojom::WebSandboxFlags
+NavigationRequest::ComputeSandboxFlagsToCommit() {
+ DCHECK(commit_params_);
+ DCHECK(!HasCommitted());
+ DCHECK(!IsErrorPage());
+
+ network::mojom::WebSandboxFlags out;
+ if (commit_params_->frame_policy) {
+ // This corresponds to the sandbox policy of the frame embedding the
+ // document that were active when the navigation started.
+ out = commit_params_->frame_policy->sandbox_flags;
+ } else {
+ // The document doesn't have a sandbox policy. This case should in theory
+ // contains only the navigations that are:
+ // - main frame.
+ // - browser initiated.
+ // - non-history.
+ // - non-error.
+ //
+ // TODO(arthursonzogni): In practice, a few navigations not complying with
+ // one of the 4 items above are using this path. They must be identified
+ // and removed. A set of DCHECK must be added.
+ out = network::mojom::WebSandboxFlags::kNone;
+ }
+
+ // The response can also restrict the policy further.
+ if (response_head_) {
+ for (const auto& csp :
+ response_head_->parsed_headers->content_security_policy) {
+ out |= ~(csp->sandbox);
+ }
+ }
+
+ return out;
+}
+
+const CrossOriginOpenerPolicyStatus& NavigationRequest::coop_status() const {
+ return coop_status_;
+}
+
+void NavigationRequest::CheckStateTransition(NavigationState state) const {
+#if DCHECK_IS_ON()
+ // See
+ // https://chromium.googlesource.com/chromium/src/+/HEAD/docs/navigation-request-navigation-state.png
+ // clang-format off
+ static const base::NoDestructor<StateTransitions<NavigationState>>
+ transitions(StateTransitions<NavigationState>({
+ {NOT_STARTED, {
+ WAITING_FOR_RENDERER_RESPONSE,
+ WILL_START_NAVIGATION,
+ WILL_START_REQUEST,
+ }},
+ {WAITING_FOR_RENDERER_RESPONSE, {
+ WILL_START_NAVIGATION,
+ }},
+ {WILL_START_NAVIGATION, {
+ WILL_START_REQUEST,
+ WILL_FAIL_REQUEST,
+ }},
+ {WILL_START_REQUEST, {
+ WILL_REDIRECT_REQUEST,
+ WILL_PROCESS_RESPONSE,
+ READY_TO_COMMIT,
+ DID_COMMIT,
+ CANCELING,
+ WILL_FAIL_REQUEST,
+ DID_COMMIT_ERROR_PAGE,
+ }},
+ {WILL_REDIRECT_REQUEST, {
+ WILL_REDIRECT_REQUEST,
+ WILL_PROCESS_RESPONSE,
+ CANCELING,
+ WILL_FAIL_REQUEST,
+ }},
+ {WILL_PROCESS_RESPONSE, {
+ READY_TO_COMMIT,
+ CANCELING,
+ WILL_FAIL_REQUEST,
+ }},
+ {READY_TO_COMMIT, {
+ NOT_STARTED,
+ DID_COMMIT,
+ DID_COMMIT_ERROR_PAGE,
+ }},
+ {CANCELING, {
+ READY_TO_COMMIT,
+ WILL_FAIL_REQUEST,
+ }},
+ {WILL_FAIL_REQUEST, {
+ READY_TO_COMMIT,
+ CANCELING,
+ WILL_FAIL_REQUEST,
+ }},
+ {DID_COMMIT, {}},
+ {DID_COMMIT_ERROR_PAGE, {}},
+ }));
+ // clang-format on
+ DCHECK_STATE_TRANSITION(transitions, state_, state);
+#endif // DCHECK_IS_ON()
+}
+
+void NavigationRequest::SetState(NavigationState state) {
+ CheckStateTransition(state);
+ state_ = state;
+}
+
+void NavigationRequest::SanitizeCoopHeaders() {
+ // We blank out the COOP headers in a number of situations.
+ // - When the COOP flag is not enabled.
+ // - When the headers were not sent over HTTPS.
+ // - For subframes.
+ if (!base::FeatureList::IsEnabled(
+ network::features::kCrossOriginOpenerPolicy) ||
+ !IsOriginSecure(common_params_->url) || !IsInMainFrame()) {
+ response_head_->parsed_headers->cross_origin_opener_policy =
+ network::CrossOriginOpenerPolicy();
+ }
+}
+
+void NavigationRequest::UpdateCoopStatus(
+ network::mojom::CrossOriginOpenerPolicyValue coop,
+ network::mojom::CrossOriginEmbedderPolicyValue coep) {
+ RenderFrameHostImpl* current_rfh = frame_tree_node_->current_frame_host();
+
+ bool cross_origin_policy_swap =
+ IsInMainFrame() && !common_params_->url.IsAboutBlank() &&
+ ShouldSwapBrowsingInstanceForCrossOriginOpenerPolicy(
+ current_rfh->cross_origin_opener_policy().value,
+ current_rfh->cross_origin_embedder_policy().value,
+ current_rfh->GetLastCommittedOrigin(),
+ !current_rfh->has_committed_any_navigation(), coop, coep,
+ url::Origin::Create(common_params_->url));
+
+ if (cross_origin_policy_swap) {
+ coop_status_.require_browsing_instance_swap = true;
+ if (frame_tree_node_->opener())
+ coop_status_.had_opener_before_browsing_instance_swap = true;
+ }
+}
+
} // namespace content
diff --git a/chromium/content/browser/frame_host/navigation_request.h b/chromium/content/browser/frame_host/navigation_request.h
index cd7ee0d77bb..15e241680e7 100644
--- a/chromium/content/browser/frame_host/navigation_request.h
+++ b/chromium/content/browser/frame_host/navigation_request.h
@@ -23,6 +23,7 @@
#include "content/browser/initiator_csp_context.h"
#include "content/browser/loader/navigation_url_loader_delegate.h"
#include "content/browser/navigation_subresource_loader_params.h"
+#include "content/browser/site_instance_impl.h"
#include "content/browser/web_package/web_bundle_handle.h"
#include "content/common/content_export.h"
#include "content/common/navigation_params.h"
@@ -45,6 +46,7 @@
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "services/network/public/cpp/origin_policy.h"
#include "services/network/public/mojom/blocked_by_response_reason.mojom-shared.h"
+#include "services/network/public/mojom/web_sandbox_flags.mojom-shared.h"
#if defined(OS_ANDROID)
#include "base/android/scoped_java_ref.h"
@@ -62,6 +64,7 @@ namespace content {
class AppCacheNavigationHandle;
class CrossOriginEmbedderPolicyReporter;
+class CrossOriginOpenerPolicyReporter;
class WebBundleHandleTracker;
class WebBundleNavigationInfo;
class FrameNavigationEntry;
@@ -71,9 +74,28 @@ class NavigationUIData;
class NavigatorDelegate;
class PrefetchedSignedExchangeCache;
class ServiceWorkerMainResourceHandle;
-class SiteInstanceImpl;
struct SubresourceLoaderParams;
+// A structure that groups information about how COOP has interacted with the
+// navigation. These are used to trigger a number of mechanisms such as name
+// clearing or reporting.
+struct CrossOriginOpenerPolicyStatus {
+ // Set to true whenever the Cross-Origin-Opener-Policy spec requires a
+ // "BrowsingContext group" swap:
+ // https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e
+ // This forces the new RenderFrameHost to use a different BrowsingInstance
+ // than the current one. If other pages had JavaScript references to the
+ // Window object for the frame (via window.opener, window.open(), et cetera),
+ // those references will be broken; window.name will also be reset to an empty
+ // string.
+ bool require_browsing_instance_swap = false;
+
+ // When a page has a reachable opener and COOP triggers a browsing instance
+ // swap we potentially break the page. This is one of the case that can be
+ // reported using the COOP reporting API.
+ bool had_opener_before_browsing_instance_swap = false;
+};
+
// A UI thread object that owns a navigation request until it commits. It
// ensures the UI thread can start a navigation request in the
// ResourceDispatcherHost (that lives on the IO thread).
@@ -87,6 +109,7 @@ class CONTENT_EXPORT NavigationRequest
private network::mojom::CookieAccessObserver {
public:
// Keeps track of the various stages of a NavigationRequest.
+ // To see what state transitions are allowed, see |SetState|.
enum NavigationState {
// Initial state.
NOT_STARTED = 0,
@@ -197,7 +220,8 @@ class CONTENT_EXPORT NavigationRequest
RenderFrameHostImpl* render_frame_host,
const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
std::unique_ptr<CrossOriginEmbedderPolicyReporter> coep_reporter,
- bool is_same_document);
+ bool is_same_document,
+ std::unique_ptr<WebBundleNavigationInfo> web_bundle_navigation_info);
static NavigationRequest* From(NavigationHandle* handle);
@@ -216,6 +240,21 @@ class CONTENT_EXPORT NavigationRequest
};
OptInIsolationCheckResult IsOptInIsolationRequested(const GURL& url);
+ // The origin isolation end result is determined early in the lifecycle of a
+ // NavigationRequest, but used late. In particular, we want to trigger use
+ // counters and console warnings once navigation has committed.
+ enum class OptInOriginIsolationEndResult {
+ kNotRequestedAndNotIsolated,
+ kNotRequestedButIsolated,
+ kRequestedViaOriginPolicyButNotIsolated,
+ kRequestedViaOriginPolicyAndIsolated,
+ kRequestedViaHeaderButNotIsolated,
+ kRequestedViaHeaderAndIsolated
+ };
+ void DetermineOriginIsolationEndResult(
+ OptInIsolationCheckResult check_result);
+ void ProcessOriginIsolationEndResult();
+
// NavigationHandle implementation:
int64_t GetNavigationId() override;
const GURL& GetURL() override;
@@ -230,10 +269,10 @@ class CONTENT_EXPORT NavigationRequest
RenderFrameHostImpl* GetParentFrame() override;
base::TimeTicks NavigationStart() override;
base::TimeTicks NavigationInputStart() override;
- base::TimeTicks FirstRequestStart() override;
- base::TimeTicks FirstResponseStart() override;
+ const NavigationHandleTiming& GetNavigationHandleTiming() override;
bool IsPost() override;
const blink::mojom::Referrer& GetReferrer() override;
+ void SetReferrer(blink::mojom::ReferrerPtr referrer) override;
bool HasUserGesture() override;
ui::PageTransition GetPageTransition() override;
NavigationUIData* GetNavigationUIData() override;
@@ -367,11 +406,11 @@ class CONTENT_EXPORT NavigationRequest
// url we're navigating to.
void SetExpectedProcess(RenderProcessHost* expected_process);
- // Updates the destination site URL for this navigation. This is called on
+ // Updates the destination SiteInfo for this navigation. This is called on
// redirects. |post_redirect_process| is the renderer process that should
// handle the navigation following the redirect if it can be handled by an
// existing RenderProcessHost. Otherwise, it should be null.
- void UpdateSiteURL(RenderProcessHost* post_redirect_process);
+ void UpdateSiteInfo(RenderProcessHost* post_redirect_process);
int nav_entry_id() const { return nav_entry_id_; }
@@ -580,20 +619,17 @@ class CONTENT_EXPORT NavigationRequest
}
network::mojom::ClientSecurityStatePtr TakeClientSecurityState();
- bool require_coop_browsing_instance_swap() const {
- return require_coop_browsing_instance_swap_;
- }
-
bool ua_change_requires_reload() const { return ua_change_requires_reload_; }
- void set_require_coop_browsing_instance_swap() {
- require_coop_browsing_instance_swap_ = true;
- }
CrossOriginEmbedderPolicyReporter* coep_reporter() {
return coep_reporter_.get();
}
+ CrossOriginOpenerPolicyReporter* coop_reporter() {
+ return coop_reporter_.get();
+ }
std::unique_ptr<CrossOriginEmbedderPolicyReporter> TakeCoepReporter();
+ std::unique_ptr<CrossOriginOpenerPolicyReporter> TakeCoopReporter();
// Returns UKM SourceId for the page we are navigating away from.
// Equal to GetRenderFrameHost()->GetPageUkmSourceId() for subframe
@@ -614,6 +650,22 @@ class CONTENT_EXPORT NavigationRequest
std::vector<mojo::PendingReceiver<network::mojom::CookieAccessObserver>>
TakeCookieObservers() WARN_UNUSED_RESULT;
+ // The sandbox policy of the document to be loaded. This returns nullopt for
+ // navigations that haven't reached the 'ReadyToCommit' stage yet. In
+ // particular, this returns nullopt for same-document navigations.
+ //
+ // TODO(arthursonzogni): After RenderDocument, this can be computed and stored
+ // directly into the RenderDocumentHost.
+ base::Optional<network::mojom::WebSandboxFlags> SandboxFlagsToCommit();
+
+ // Returns the coop status information relevant to the current navigation.
+ const CrossOriginOpenerPolicyStatus& coop_status() const;
+
+ // Whether the navigation was sent to be committed in a renderer by the
+ // RenderFrameHost. This can either be for the commit of a successful
+ // navigation or an error page.
+ bool IsWaitingToCommit();
+
private:
friend class NavigationRequestTest;
@@ -631,7 +683,8 @@ class CONTENT_EXPORT NavigationRequest
mojo::PendingAssociatedRemote<mojom::NavigationClient> navigation_client,
mojo::PendingRemote<blink::mojom::NavigationInitiator>
navigation_initiator,
- RenderFrameHostImpl* rfh_restored_from_back_forward_cache);
+ RenderFrameHostImpl* rfh_restored_from_back_forward_cache,
+ GlobalFrameRoutingId initiator_routing_id);
// Checks if the response requests an isolated origin (using either origin
// policy or the Origin-Isolation header), and if so opts in the origin to be
@@ -702,7 +755,7 @@ class CONTENT_EXPORT NavigationRequest
// Checks if CSP allows the navigation. This will check the frame-src and
// navigate-to directives.
// Returns net::OK if the checks pass, and net::ERR_ABORTED or
- // net::ERR_BLOCKED_BY_CLIENT depending on which checks fail.
+ // net::ERR_BLOCKED_BY_CSP depending on which checks fail.
net::Error CheckCSPDirectives(
RenderFrameHostImpl* parent,
bool has_followed_redirect,
@@ -849,11 +902,13 @@ class CONTENT_EXPORT NavigationRequest
const ChildProcessTerminationInfo& info) override;
void RenderProcessHostDestroyed(RenderProcessHost* host) override;
- void RecordNavigationMetrics() const;
+ // Updates navigation handle timings.
+ void UpdateNavigationHandleTimingsOnResponseReceived(bool is_first_response);
+ void UpdateNavigationHandleTimingsOnCommitSent();
- // Helper function that computes the site URL for |common_params_.url|.
- // Note: |site_url_| should only be updated with the result of this function.
- GURL GetSiteForCommonParamsURL() const;
+ // Helper function that computes the SiteInfo for |common_params_.url|.
+ // Note: |site_info_| should only be updated with the result of this function.
+ SiteInfo GetSiteInfoForCommonParamsURL() const;
// Updates the state of the navigation handle after encountering a server
// redirect.
@@ -890,11 +945,6 @@ class CONTENT_EXPORT NavigationRequest
// |state_| and inform the delegate.
void ReadyToCommitNavigation(bool is_error);
- // Whether the navigation was sent to be committed in a renderer by the
- // RenderFrameHost. This can either be for the commit of a successful
- // navigation or an error page.
- bool IsWaitingToCommit();
-
// Called if READY_TO_COMMIT -> COMMIT state transition takes an unusually
// long time.
void OnCommitTimeout();
@@ -933,8 +983,9 @@ class CONTENT_EXPORT NavigationRequest
void ForceEnableOriginTrials(const std::vector<std::string>& trials) override;
void CreateCoepReporter(StoragePartition* storage_partition);
+ void CreateCoopReporter(StoragePartition* storage_partition);
- base::Optional<network::mojom::BlockedByResponseReason> IsBlockedByCorp();
+ base::Optional<network::mojom::BlockedByResponseReason> IsBlockedByResponse();
bool IsOverridingUserAgent() const {
return commit_params_->is_overriding_user_agent || entry_overrides_ua_;
@@ -956,7 +1007,27 @@ class CONTENT_EXPORT NavigationRequest
// NavigationRequest is in.
NavigationControllerImpl* GetNavigationController();
- FrameTreeNode* frame_tree_node_;
+ // Compute the sandbox policy of the document to be loaded. Called once when
+ // reaching the 'ReadyToCommit' stage.
+ network::mojom::WebSandboxFlags ComputeSandboxFlagsToCommit();
+
+ // DCHECK that tranistioning from the current state to |state| valid. This
+ // does nothing in non-debug builds.
+ void CheckStateTransition(NavigationState state) const;
+
+ // Set |state_| to |state| and also DCHECK that this state transition is
+ // valid.
+ void SetState(NavigationState state);
+
+ // Make sure COOP is relevant or clear the COOP headers.
+ void SanitizeCoopHeaders();
+
+ // Updates the internal coop_status assuming the page navigated to has
+ // cross-origin-opener-policy |coop| and cross-origin-embedder-policy |coep|.
+ void UpdateCoopStatus(network::mojom::CrossOriginOpenerPolicyValue coop,
+ network::mojom::CrossOriginEmbedderPolicyValue coep);
+
+ FrameTreeNode* const frame_tree_node_;
// Value of |is_for_commit| supplied to the constructor.
const bool is_for_commit_;
@@ -1006,11 +1077,11 @@ class CONTENT_EXPORT NavigationRequest
// creation time.
scoped_refptr<SiteInstanceImpl> source_site_instance_;
scoped_refptr<SiteInstanceImpl> dest_site_instance_;
- RestoreType restore_type_ = RestoreType::NONE;
- ReloadType reload_type_ = ReloadType::NONE;
+ const RestoreType restore_type_;
+ const ReloadType reload_type_;
+ const int nav_entry_id_;
bool is_view_source_;
int bindings_;
- int nav_entry_id_ = 0;
bool entry_overrides_ua_ = false;
// Set to true if SetIsOverridingUserAgent() is called.
@@ -1035,7 +1106,7 @@ class CONTENT_EXPORT NavigationRequest
// IPC. When true, main frame navigations should not commit in a different
// process (unless asked by the content/ embedder). When true, the renderer
// process expects to be notified if the navigation is aborted.
- bool from_begin_navigation_;
+ const bool from_begin_navigation_;
// Holds objects received from OnResponseStarted while the WillProcessResponse
// checks are performed by the NavigationHandle. Once the checks have been
@@ -1064,10 +1135,11 @@ class CONTENT_EXPORT NavigationRequest
// commit.
int expected_render_process_host_id_;
- // The site URL of this navigation, as obtained from SiteInstance::GetSiteURL.
- GURL site_url_;
+ // The SiteInfo of this navigation, as obtained from
+ // SiteInstanceImpl::ComputeSiteInfo().
+ SiteInfo site_info_;
- std::unique_ptr<InitiatorCSPContext> initiator_csp_context_;
+ const std::unique_ptr<InitiatorCSPContext> initiator_csp_context_;
base::OnceClosure on_start_checks_complete_closure_;
@@ -1153,28 +1225,8 @@ class CONTENT_EXPORT NavigationRequest
// is enabled or TrustableWebBundleFileUrl switch is set.
std::unique_ptr<WebBundleHandleTracker> web_bundle_handle_tracker_;
- // The time the first HTTP request was sent. This is filled with
- // net::LoadTimingInfo::send_start during navigation.
- //
- // In some cases, this can be the time an internal request started that did
- // not go to the networking layer. For example,
- // - Service Worker: the time the fetch event was ready to be dispatched, see
- // content::ServiceWorkerNavigationLoader::DidPrepareFetchEvent()).
- // - HSTS: the time the internal redirect was handled.
- // - Signed Exchange: the time the SXG was handled.
- base::TimeTicks first_request_start_;
-
- // The time the headers of the first HTTP response were received. This is
- // filled with net::LoadTimingInfo::receive_headers_start on the first HTTP
- // response during navigation.
- //
- // In some cases, this can be the time an internal response was received that
- // did not come from the networking layer. For example,
- // - Service Worker: the time the response from the service worker was
- // received, see content::ServiceWorkerNavigationLoader::StartResponse().
- // - HSTS: the time the internal redirect was handled.
- // - Signed Exchange: the time the SXG was handled.
- base::TimeTicks first_response_start_;
+ // Timing information of loading for the navigation. Used for recording UMAs.
+ std::unique_ptr<NavigationHandleTiming> navigation_handle_timing_;
// The time this navigation was ready to commit.
base::TimeTicks ready_to_commit_time_;
@@ -1209,9 +1261,9 @@ class CONTENT_EXPORT NavigationRequest
// TrustableWebBundleFileUrl switch is set.
// For navigations to Web Bundle file, this is cloned from
// |web_bundle_handle_| in CommitNavigation(), and is passed to
- // NavigationEntry for the navigation. And for history (back / forward)
+ // FrameNavigationEntry for the navigation. And for history (back / forward)
// navigations within the Web Bundle file, this is cloned from the
- // NavigationEntry and is used to create a WebBundleHandle.
+ // FrameNavigationEntry and is used to create a WebBundleHandle.
std::unique_ptr<WebBundleNavigationInfo> web_bundle_navigation_info_;
// Which proxy server was used for this navigation, if any.
@@ -1220,8 +1272,8 @@ class CONTENT_EXPORT NavigationRequest
// The unique id to identify the NavigationHandle with.
int64_t navigation_handle_id_ = 0;
- // Manages the lifetime of a pre-created ServiceWorkerProviderHost until a
- // corresponding provider is created in the renderer.
+ // Manages the lifetime of a pre-created ServiceWorkerContainerHost until a
+ // corresponding container is created in the renderer.
std::unique_ptr<ServiceWorkerMainResourceHandle> service_worker_handle_;
// Timer for detecting an unexpectedly long time to commit a navigation.
@@ -1255,7 +1307,7 @@ class CONTENT_EXPORT NavigationRequest
// The RenderFrameHost that was restored from the back-forward cache. This
// will be null except for navigations that are restoring a page from the
// back-forward cache.
- RenderFrameHostImpl* rfh_restored_from_back_forward_cache_;
+ RenderFrameHostImpl* const rfh_restored_from_back_forward_cache_;
// These are set to the values from the FrameNavigationEntry this
// NavigationRequest is associated with (if any).
@@ -1267,14 +1319,14 @@ class CONTENT_EXPORT NavigationRequest
base::Optional<net::IsolationInfo> isolation_info_;
// This is used to store the current_frame_host id at request creation time.
- GlobalFrameRoutingId previous_render_frame_host_id_;
+ const GlobalFrameRoutingId previous_render_frame_host_id_;
// Routing id of the frame host that initiated the navigation, derived from
// |begin_params()->initiator_routing_id|. This is best effort: it is only
// defined for some renderer-initiated navigations (e.g., not drag and drop).
// The frame with the corresponding routing ID may have been deleted before
// the navigation begins.
- GlobalFrameRoutingId initiator_routing_id_;
+ const GlobalFrameRoutingId initiator_routing_id_;
// This tracks a connection between the current pending entry and this
// request, such that the pending entry can be discarded if no requests are
@@ -1296,29 +1348,20 @@ class CONTENT_EXPORT NavigationRequest
network::mojom::ClientSecurityStatePtr client_security_state_;
std::unique_ptr<CrossOriginEmbedderPolicyReporter> coep_reporter_;
+ std::unique_ptr<CrossOriginOpenerPolicyReporter> coop_reporter_;
std::unique_ptr<PeakGpuMemoryTracker> loading_mem_tracker_ = nullptr;
- // Set to true whenever we the Cross-Origin-Opener-Policy spec requires us to
- // do a "BrowsingContext group" swap:
- // https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e
- // This forces a new BrowsingInstance to be used for the RenderFrameHost the
- // navigation will commit in. If other pages had JavaScript references to the
- // Window object for the frame (via window.opener, window.open(), et cetera),
- // those references will be broken; window.name will also be reset to an empty
- // string.
- // TODO(ahemery): COOP requires that any page during the redirect chain
- // having an incompatible COOP triggers a BrowsingInstance swap. Even if the
- // end document could be put in the same BrowsingInstance as the starting
- // one. Implement the behavior.
- bool require_coop_browsing_instance_swap_ = false;
+ // Structure tracking the effects of the CrossOriginOpenerPolicy on this
+ // navigation.
+ CrossOriginOpenerPolicyStatus coop_status_;
#if DCHECK_IS_ON()
bool is_safe_to_delete_ = true;
#endif
// UKM source associated with the page we are navigated away from.
- ukm::SourceId previous_page_load_ukm_source_id_ = ukm::kInvalidSourceId;
+ const ukm::SourceId previous_page_load_ukm_source_id_;
// If true, changes to the user-agent override require a reload. If false, a
// reload is not necessary.
@@ -1328,6 +1371,13 @@ class CONTENT_EXPORT NavigationRequest
// made by this navigation.
mojo::ReceiverSet<network::mojom::CookieAccessObserver> cookie_observers_;
+ // The sandbox flags of the document to be loaded. This is computed at
+ // 'ReadyToCommit' time.
+ base::Optional<network::mojom::WebSandboxFlags> sandbox_flags_to_commit_;
+
+ OptInOriginIsolationEndResult origin_isolation_end_result_ =
+ OptInOriginIsolationEndResult::kNotRequestedAndNotIsolated;
+
base::WeakPtrFactory<NavigationRequest> weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(NavigationRequest);
diff --git a/chromium/content/browser/frame_host/navigation_request_browsertest.cc b/chromium/content/browser/frame_host/navigation_request_browsertest.cc
index 3f274d6f23c..74997532956 100644
--- a/chromium/content/browser/frame_host/navigation_request_browsertest.cc
+++ b/chromium/content/browser/frame_host/navigation_request_browsertest.cc
@@ -7,7 +7,6 @@
#include "base/files/scoped_temp_dir.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/stringprintf.h"
-#include "base/task/post_task.h"
#include "base/test/metrics/histogram_tester.h"
#include "content/browser/frame_host/debug_urls.h"
#include "content/browser/frame_host/navigation_request.h"
@@ -101,8 +100,8 @@ class TestNavigationThrottle : public NavigationThrottle {
navigation_request->request_context_type());
request_context_type_ = navigation_request->request_context_type();
- base::PostTask(FROM_HERE, {BrowserThread::UI},
- std::move(did_call_will_start_));
+ GetUIThreadTaskRunner({})->PostTask(FROM_HERE,
+ std::move(did_call_will_start_));
return will_start_result_;
}
@@ -111,8 +110,8 @@ class TestNavigationThrottle : public NavigationThrottle {
NavigationRequest::From(navigation_handle());
CHECK_EQ(request_context_type_, navigation_request->request_context_type());
- base::PostTask(FROM_HERE, {BrowserThread::UI},
- std::move(did_call_will_redirect_));
+ GetUIThreadTaskRunner({})->PostTask(FROM_HERE,
+ std::move(did_call_will_redirect_));
return will_redirect_result_;
}
@@ -121,8 +120,8 @@ class TestNavigationThrottle : public NavigationThrottle {
NavigationRequest::From(navigation_handle());
CHECK_EQ(request_context_type_, navigation_request->request_context_type());
- base::PostTask(FROM_HERE, {BrowserThread::UI},
- std::move(did_call_will_fail_));
+ GetUIThreadTaskRunner({})->PostTask(FROM_HERE,
+ std::move(did_call_will_fail_));
return will_fail_result_;
}
@@ -131,8 +130,8 @@ class TestNavigationThrottle : public NavigationThrottle {
NavigationRequest::From(navigation_handle());
CHECK_EQ(request_context_type_, navigation_request->request_context_type());
- base::PostTask(FROM_HERE, {BrowserThread::UI},
- std::move(did_call_will_process_));
+ GetUIThreadTaskRunner({})->PostTask(FROM_HERE,
+ std::move(did_call_will_process_));
return will_process_result_;
}
@@ -1909,8 +1908,8 @@ IN_PROC_BROWSER_TEST_F(NavigationRequestBrowserTest, ErrorPageNetworkError) {
GURL start_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
GURL error_url(embedded_test_server()->GetURL("/close-socket"));
EXPECT_NE(start_url.host(), error_url.host());
- base::PostTask(FROM_HERE, {BrowserThread::IO},
- base::BindOnce(&net::URLRequestFailedJob::AddUrlHandler));
+ GetIOThreadTaskRunner({})->PostTask(
+ FROM_HERE, base::BindOnce(&net::URLRequestFailedJob::AddUrlHandler));
{
NavigationHandleObserver observer(shell()->web_contents(), start_url);
diff --git a/chromium/content/browser/frame_host/navigation_request_unittest.cc b/chromium/content/browser/frame_host/navigation_request_unittest.cc
index 3a5e2050e47..654075c29e6 100644
--- a/chromium/content/browser/frame_host/navigation_request_unittest.cc
+++ b/chromium/content/browser/frame_host/navigation_request_unittest.cc
@@ -192,9 +192,12 @@ class NavigationRequestTest : public RenderViewHostImplTestHarness {
auto common_params = CreateCommonNavigationParams();
common_params->initiator_origin =
url::Origin::Create(GURL("https://initiator.example.com"));
+ auto commit_params = CreateCommitNavigationParams();
+ commit_params->frame_policy =
+ main_test_rfh()->frame_tree_node()->pending_frame_policy();
request_ = NavigationRequest::CreateBrowserInitiated(
main_test_rfh()->frame_tree_node(), std::move(common_params),
- CreateCommitNavigationParams(), false /* browser-initiated */,
+ std::move(commit_params), false /* browser-initiated */,
GlobalFrameRoutingId() /* initiator_routing_id */,
std::string() /* extra_headers */, nullptr /* frame_entry */,
nullptr /* entry */, nullptr /* post_body */,
diff --git a/chromium/content/browser/frame_host/navigator.cc b/chromium/content/browser/frame_host/navigator.cc
index e052b64302d..8bf2016ef50 100644
--- a/chromium/content/browser/frame_host/navigator.cc
+++ b/chromium/content/browser/frame_host/navigator.cc
@@ -235,8 +235,8 @@ void Navigator::DidNavigate(
frame_tree_node->render_manager()->DidNavigateFrame(
render_frame_host, params.gesture == NavigationGestureUser,
is_same_document_navigation,
- navigation_request
- ->require_coop_browsing_instance_swap() /* clear_proxies_on_commit */,
+ navigation_request->coop_status()
+ .require_browsing_instance_swap /* clear_proxies_on_commit */,
pending_frame_policy);
// Save the new page's origin and other properties, and replicate them to
@@ -258,10 +258,24 @@ void Navigator::DidNavigate(
render_frame_host->ResetContentSecurityPolicies();
frame_tree_node->ResetForNavigation();
- // Save the new document's embedding token and propagate to any parent
- // document that embeds it. A token is only assigned to cross-process
- // child frames.
- render_frame_host->SetEmbeddingToken(params.embedding_token);
+ // Back-forward cache navigations should not update the embedding token.
+ //
+ // |was_within_same_document| (controlled by the renderer) also needs
+ // to be considered: in some cases, the browser and renderer can disagree.
+ // While this is usually a bad message kill, there are some situations
+ // where this can legitimately happen. When a new frame is created (e.g.
+ // with <iframe src="...">), the initial about:blank document doesn't have
+ // a corresponding entry in the browser process. As a result, the browser
+ // process incorrectly determines that the navigation is cross-document
+ // when in reality it's same-document.
+ if (!navigation_request->IsServedFromBackForwardCache() &&
+ !was_within_same_document) {
+ DCHECK(params.embedding_token.has_value());
+ // Save the new document's embedding token and propagate to any parent
+ // document that embeds it. A token exists for all navigations creating a
+ // new document.
+ render_frame_host->SetEmbeddingToken(params.embedding_token.value());
+ }
}
// Update the site of the SiteInstance if it doesn't have one yet, unless
@@ -285,8 +299,11 @@ void Navigator::DidNavigate(
// TODO(nasko): Verify the correctness of the above comment, since some of the
// code doesn't exist anymore. Also, move this code in the
// PageTransitionIsMainFrame code block above.
- if (ui::PageTransitionIsMainFrame(params.transition) && delegate_)
- delegate_->SetMainFrameMimeType(params.contents_mime_type);
+ if (ui::PageTransitionIsMainFrame(params.transition) && delegate_) {
+ RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
+ render_frame_host->GetRenderViewHost());
+ rvh->SetContentsMimeType(params.contents_mime_type);
+ }
int old_entry_count = controller_->GetEntryCount();
LoadCommittedDetails details;
@@ -610,7 +627,7 @@ void Navigator::OnBeginNavigation(
// Try to find a FrameNavigationEntry that matches this frame instead, based
// on the frame's unique name. If this can't be found, fall back to the
// default path below.
- if (frame_tree_node->navigator()->StartHistoryNavigationInNewSubframe(
+ if (frame_tree_node->navigator().StartHistoryNavigationInNewSubframe(
frame_tree_node->current_frame_host(), &navigation_client)) {
return;
}
@@ -805,12 +822,6 @@ Navigator::GetNavigationEntryForRendererInitiatedNavigation(
if (renderer_provisional_load_to_pending_url)
return nullptr;
- // If there is a transient entry, creating a new pending entry will result
- // in deleting it, which leads to inconsistent state.
- bool has_transient_entry = !!controller_->GetTransientEntry();
- if (has_transient_entry)
- return nullptr;
-
// Since GetNavigationEntryForRendererInitiatedNavigation is called from
// OnBeginNavigation, we can assume that no frame proxies are involved and
// therefore that |current_site_instance| is also the |source_site_instance|.
@@ -826,7 +837,8 @@ Navigator::GetNavigationEntryForRendererInitiatedNavigation(
ui::PAGE_TRANSITION_LINK, true /* is_renderer_initiated */,
std::string() /* extra_headers */,
controller_->GetBrowserContext(),
- nullptr /* blob_url_loader_factory */));
+ nullptr /* blob_url_loader_factory */,
+ common_params.should_replace_current_entry));
controller_->SetPendingEntry(std::move(entry));
if (delegate_)
diff --git a/chromium/content/browser/frame_host/navigator.h b/chromium/content/browser/frame_host/navigator.h
index 19ce4447f95..a79f0106cda 100644
--- a/chromium/content/browser/frame_host/navigator.h
+++ b/chromium/content/browser/frame_host/navigator.h
@@ -46,17 +46,13 @@ class RenderFrameHostImpl;
class WebBundleHandleTracker;
struct LoadCommittedDetails;
-// Navigator is responsible for performing navigations in a node of the
-// FrameTree. Its lifetime is bound to all FrameTreeNode objects that are using
-// it and will be released once all nodes that use it are freed. The Navigator
-// is bound to a single frame tree and cannot be used by multiple instances of
-// FrameTree.
-// TODO(nasko): Move all navigation methods, such as didStartProvisionalLoad
-// from WebContentsImpl to this interface.
-class CONTENT_EXPORT Navigator : public base::RefCounted<Navigator> {
+// Navigator is responsible for performing navigations in nodes of the
+// FrameTree. Its lifetime is bound to the FrameTree.
+class CONTENT_EXPORT Navigator {
public:
Navigator(NavigationControllerImpl* navigation_controller,
NavigatorDelegate* delegate);
+ ~Navigator();
// This method verifies that a navigation to |url| doesn't commit into a WebUI
// process if it is not allowed to. Callers of this method should take one of
@@ -203,14 +199,14 @@ class CONTENT_EXPORT Navigator : public base::RefCounted<Navigator> {
const base::TimeTicks& renderer_before_unload_start_time,
const base::TimeTicks& renderer_before_unload_end_time);
+ NavigationControllerImpl* controller() { return controller_; }
+
private:
- friend class base::RefCounted<Navigator>;
friend class NavigatorTestWithBrowserSideNavigation;
// Holds data used to track browser side navigation metrics.
struct NavigationMetricsData;
- ~Navigator();
void RecordNavigationMetrics(
const LoadCommittedDetails& details,
diff --git a/chromium/content/browser/frame_host/navigator_delegate.h b/chromium/content/browser/frame_host/navigator_delegate.h
index 6ee41979d7d..a8386f7a94c 100644
--- a/chromium/content/browser/frame_host/navigator_delegate.h
+++ b/chromium/content/browser/frame_host/navigator_delegate.h
@@ -74,7 +74,6 @@ class CONTENT_EXPORT NavigatorDelegate {
const LoadCommittedDetails& details,
const FrameHostMsg_DidCommitProvisionalLoad_Params& params) = 0;
- virtual void SetMainFrameMimeType(const std::string& mime_type) = 0;
virtual bool CanOverscrollContent() const = 0;
// Notification to the Navigator embedder that navigation state has
diff --git a/chromium/content/browser/frame_host/navigator_unittest.cc b/chromium/content/browser/frame_host/navigator_unittest.cc
index 0d819dc2d49..8d04236aa9e 100644
--- a/chromium/content/browser/frame_host/navigator_unittest.cc
+++ b/chromium/content/browser/frame_host/navigator_unittest.cc
@@ -1264,7 +1264,7 @@ TEST_F(NavigatorTest, FeaturePolicySameSiteNavigation) {
contents()->NavigateAndCommit(kUrl1);
// Check the feature policy before navigation.
- blink::FeaturePolicy* original_feature_policy =
+ const blink::FeaturePolicy* original_feature_policy =
main_test_rfh()->feature_policy();
ASSERT_TRUE(original_feature_policy);
@@ -1272,7 +1272,7 @@ TEST_F(NavigatorTest, FeaturePolicySameSiteNavigation) {
contents()->NavigateAndCommit(kUrl2);
// Check the feature policy after navigation.
- blink::FeaturePolicy* final_feature_policy =
+ const blink::FeaturePolicy* final_feature_policy =
main_test_rfh()->feature_policy();
ASSERT_TRUE(final_feature_policy);
ASSERT_NE(original_feature_policy, final_feature_policy);
@@ -1287,7 +1287,7 @@ TEST_F(NavigatorTest, FeaturePolicyFragmentNavigation) {
contents()->NavigateAndCommit(kUrl1);
// Check the feature policy before navigation.
- blink::FeaturePolicy* original_feature_policy =
+ const blink::FeaturePolicy* original_feature_policy =
main_test_rfh()->feature_policy();
ASSERT_TRUE(original_feature_policy);
@@ -1295,7 +1295,7 @@ TEST_F(NavigatorTest, FeaturePolicyFragmentNavigation) {
contents()->NavigateAndCommit(kUrl2);
// Check the feature policy after navigation.
- blink::FeaturePolicy* final_feature_policy =
+ const blink::FeaturePolicy* final_feature_policy =
main_test_rfh()->feature_policy();
ASSERT_EQ(original_feature_policy, final_feature_policy);
}
@@ -1313,7 +1313,7 @@ TEST_F(NavigatorTest, FeaturePolicyNewChild) {
contents()->GetMainFrame()->AppendChild("child");
NavigationSimulator::NavigateAndCommitFromDocument(kUrl2, subframe_rfh);
- blink::FeaturePolicy* subframe_feature_policy =
+ const blink::FeaturePolicy* subframe_feature_policy =
subframe_rfh->feature_policy();
ASSERT_TRUE(subframe_feature_policy);
ASSERT_FALSE(subframe_feature_policy->GetOriginForTest().opaque());
diff --git a/chromium/content/browser/frame_host/render_document_host_user_data_browsertest.cc b/chromium/content/browser/frame_host/render_document_host_user_data_browsertest.cc
index 09dff784251..089d8e01546 100644
--- a/chromium/content/browser/frame_host/render_document_host_user_data_browsertest.cc
+++ b/chromium/content/browser/frame_host/render_document_host_user_data_browsertest.cc
@@ -65,6 +65,53 @@ class Data : public RenderDocumentHostUserData<Data> {
RENDER_DOCUMENT_HOST_USER_DATA_KEY_IMPL(Data)
+// Observer class to track the creation of RenderFrameHost objects. It is used
+// in subsequent tests.
+class RenderFrameHostCreatedObserver : public WebContentsObserver {
+ public:
+ using OnRenderFrameHostCreatedCallback =
+ base::RepeatingCallback<void(RenderFrameHost*)>;
+
+ RenderFrameHostCreatedObserver(
+ WebContents* web_contents,
+ OnRenderFrameHostCreatedCallback on_rfh_created)
+ : WebContentsObserver(web_contents),
+ on_rfh_created_(std::move(on_rfh_created)) {}
+
+ void RenderFrameCreated(RenderFrameHost* render_frame_host) override {
+ on_rfh_created_.Run(std::move(render_frame_host));
+ }
+
+ private:
+ OnRenderFrameHostCreatedCallback on_rfh_created_;
+};
+
+// Observer class to track creation of new popups. It is used
+// in subsequent tests.
+class PopupCreatedObserver : public WebContentsDelegate {
+ public:
+ using WebContentsCreatedCallback =
+ base::RepeatingCallback<void(WebContents* web_contents)>;
+
+ explicit PopupCreatedObserver(WebContentsCreatedCallback callback)
+ : callback_(std::move(callback)) {}
+
+ void AddNewContents(WebContents* source_contents,
+ std::unique_ptr<WebContents> new_contents,
+ const GURL& target_url,
+ WindowOpenDisposition disposition,
+ const gfx::Rect& initial_rect,
+ bool user_gesture,
+ bool* was_blocked) override {
+ callback_.Run(new_contents.get());
+ web_contents_.push_back(std::move(new_contents));
+ }
+
+ private:
+ WebContentsCreatedCallback callback_;
+ std::vector<std::unique_ptr<WebContents>> web_contents_;
+};
+
} // namespace
class RenderDocumentHostUserDataTest : public ContentBrowserTest {
@@ -114,6 +161,32 @@ IN_PROC_BROWSER_TEST_F(RenderDocumentHostUserDataTest,
EXPECT_FALSE(Data::GetForCurrentDocument(rfh_a));
}
+// Test GetOrCreateForCurrentDocument API of RenderDocumentHostUserData.
+IN_PROC_BROWSER_TEST_F(RenderDocumentHostUserDataTest,
+ GetOrCreateForCurrentDocument) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+ GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
+
+ // 1) Navigate to A.
+ EXPECT_TRUE(NavigateToURL(shell(), url_a));
+ RenderFrameHostImpl* rfh_a = top_frame_host();
+
+ // 2) Get the Data associated with this RenderFrameHost. It should be null
+ // before creation.
+ Data* data = Data::GetForCurrentDocument(rfh_a);
+ EXPECT_FALSE(data);
+
+ // 3) |GetOrCreateForCurrentDocument| should create Data.
+ base::WeakPtr<Data> created_data =
+ Data::GetOrCreateForCurrentDocument(rfh_a)->GetWeakPtr();
+ EXPECT_TRUE(created_data);
+
+ // 4) Another call to |GetOrCreateForCurrentDocument| should not create the
+ // new data and the previous data created in 3) should be preserved.
+ Data::GetOrCreateForCurrentDocument(rfh_a);
+ EXPECT_TRUE(created_data);
+}
+
// Tests that RenderDocumentHostUserData objects are different for each
// RenderFrameHost in FrameTree when there are multiple frames.
IN_PROC_BROWSER_TEST_F(RenderDocumentHostUserDataTest,
@@ -141,9 +214,12 @@ IN_PROC_BROWSER_TEST_F(RenderDocumentHostUserDataTest,
EXPECT_NE(data_a->unique_id(), data_b->unique_id());
}
-// Tests that RenderDocumentHostUserData objects are cleared when the renderer
-// process crashes even when having RenderFrameHost that still exists.
-IN_PROC_BROWSER_TEST_F(RenderDocumentHostUserDataTest, CheckForCrashedFrame) {
+// Tests that RenderDocumentHostUserData object is preserved when the renderer
+// process crashes even when RenderFrameHost still exists, but the RDHUD object
+// is cleared if the RenderFrameHost is reused for the new RenderFrame creation
+// when the previous renderer process crashes.
+IN_PROC_BROWSER_TEST_F(RenderDocumentHostUserDataTest,
+ CrashedFrameUserDataIsPreservedAndDeletedOnReset) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
@@ -163,11 +239,168 @@ IN_PROC_BROWSER_TEST_F(RenderDocumentHostUserDataTest, CheckForCrashedFrame) {
renderer_process->Shutdown(0);
crash_observer.Wait();
- // 4) Check if the RDHUD object is deleted after renderer process crashes even
- // when RFH is around.
- EXPECT_EQ(top_frame_host(), rfh_a);
+ // 4) RDHUD shouldn't be cleared after the renderer crash.
EXPECT_FALSE(rfh_a->IsRenderFrameLive());
+ EXPECT_TRUE(data);
+
+ // 5) Register an observer which observes created RenderFrameHost and checks
+ // if the data is cleared when callback was run.
+ bool did_clear_user_data = false;
+ RenderFrameHostCreatedObserver observer(
+ web_contents(), base::BindRepeating(
+ [](bool* did_clear_user_data, RenderFrameHost* rfh) {
+ if (!Data::GetForCurrentDocument(rfh))
+ *did_clear_user_data = true;
+ },
+ &did_clear_user_data));
+
+ // 6) Re-initialize RenderFrame, now RDHUD should be cleared on new
+ // RenderFrame creation after crash when
+ // RenderFrameHostImpl::SetRenderFrameCreated was called.
+ FrameTreeNode* root = web_contents()->GetFrameTree()->root();
+ root->render_manager()->InitializeMainRenderFrameForImmediateUse();
+ EXPECT_TRUE(did_clear_user_data);
+
+ // With RenderDocument, on a reload renderer crashes would give a new
+ // RenderFrameHost different from rfh_a.
+ RenderFrameHostImpl* new_rfh_a = top_frame_host();
+ EXPECT_TRUE(new_rfh_a->IsRenderFrameLive());
+
+ // 7) RDHUD should be cleared after initialization.
EXPECT_FALSE(data);
+
+ // 8) Check RDHUD object creation after new RenderFrame creation.
+ Data::CreateForCurrentDocument(new_rfh_a);
+ base::WeakPtr<Data> new_data =
+ Data::GetForCurrentDocument(new_rfh_a)->GetWeakPtr();
+ EXPECT_TRUE(new_data);
+}
+
+// Tests that RenderDocumentHostUserData object is not cleared when speculative
+// RFH commits after the renderer hosting the current RFH (of old URL) crashes
+// i.e., while navigating to a new URL (using speculative RFH) and having the
+// current RFH (of old URL) not alive.
+IN_PROC_BROWSER_TEST_F(RenderDocumentHostUserDataTest,
+ CheckWithFrameCrashDuringNavigation) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+ GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
+ GURL url_b(embedded_test_server()->GetURL("b.com", "/title2.html"));
+
+ // Isolate "b.com" so we are guaranteed to get a different process
+ // for navigations to this origin on Android. Doing this ensures that a
+ // speculative RenderFrameHost is used.
+ IsolateOriginsForTesting(embedded_test_server(), shell()->web_contents(),
+ {"b.com"});
+
+ // 1) Navigate to A.
+ EXPECT_TRUE(NavigateToURL(shell(), url_a));
+ RenderFrameHostImpl* rfh_a = top_frame_host();
+
+ // 2) Start navigation to B, but don't commit yet.
+ TestNavigationManager manager(shell()->web_contents(), url_b);
+ shell()->LoadURL(url_b);
+ EXPECT_TRUE(manager.WaitForRequestStart());
+
+ FrameTreeNode* root = web_contents()->GetFrameTree()->root();
+ RenderFrameHostImpl* pending_rfh =
+ root->render_manager()->speculative_frame_host();
+ NavigationRequest* navigation_request = root->navigation_request();
+ EXPECT_EQ(navigation_request->associated_site_instance_type(),
+ NavigationRequest::AssociatedSiteInstanceType::SPECULATIVE);
+ EXPECT_TRUE(pending_rfh);
+
+ // 3) Get the RenderDocumentHostUserData associated with the speculative
+ // RenderFrameHost.
+ Data::CreateForCurrentDocument(pending_rfh);
+ base::WeakPtr<Data> data =
+ Data::GetForCurrentDocument(pending_rfh)->GetWeakPtr();
+ EXPECT_TRUE(data);
+
+ // 4) Crash the renderer hosting current RFH.
+ RenderProcessHost* renderer_process = rfh_a->GetProcess();
+ RenderProcessHostWatcher crash_observer(
+ renderer_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
+ renderer_process->Shutdown(0);
+ crash_observer.Wait();
+
+ // 5) Check that the RDHUD object is not cleared after renderer process
+ // crashes.
+ EXPECT_EQ(top_frame_host(), rfh_a);
+ EXPECT_FALSE(pending_rfh->IsCurrent());
+ EXPECT_FALSE(rfh_a->IsRenderFrameLive());
+ EXPECT_TRUE(pending_rfh->IsRenderFrameLive());
+ EXPECT_TRUE(data);
+
+ // 6) Let the navigation finish and make sure it has succeeded.
+ manager.WaitForNavigationFinished();
+ EXPECT_EQ(url_b, web_contents()->GetMainFrame()->GetLastCommittedURL());
+
+ // 7) Data shouldn't be cleared in this case, as state
+ // |committed_speculative_rfh_before_navigation_commit_| is true during the
+ // check in DidCommitInternalNavigation as the speculative RFH swaps with the
+ // crashed RFH and performs commit before navigation commit happens.
+ EXPECT_TRUE(data);
+}
+
+// Tests that RenderDocumentHostUserData object is not cleared when speculative
+// RFH commits after renderer hosting the current RFH (of old URL) which happens
+// before navigating to a new URL (using Speculative RFH) and having the current
+// RenderFrameHost (of old URL) not alive.
+IN_PROC_BROWSER_TEST_F(RenderDocumentHostUserDataTest,
+ CheckWithFrameCrashBeforeNavigation) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+ GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
+ GURL url_b(embedded_test_server()->GetURL("b.com", "/title2.html"));
+
+ // Isolate "b.com" so we are guaranteed to get a different process
+ // for navigations to this origin on Android. Doing this ensures that a
+ // speculative RenderFrameHost is used.
+ IsolateOriginsForTesting(embedded_test_server(), shell()->web_contents(),
+ {"b.com"});
+
+ // 1) Navigate to A.
+ EXPECT_TRUE(NavigateToURL(shell(), url_a));
+ RenderFrameHostImpl* rfh_a = top_frame_host();
+
+ // 2) Crash the renderer hosting current RFH.
+ RenderProcessHost* renderer_process = rfh_a->GetProcess();
+ RenderProcessHostWatcher crash_observer(
+ renderer_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
+ renderer_process->Shutdown(0);
+ crash_observer.Wait();
+
+ // 3) Start navigation to B, but don't commit yet.
+ TestNavigationManager manager(shell()->web_contents(), url_b);
+ shell()->LoadURL(url_b);
+ EXPECT_TRUE(manager.WaitForRequestStart());
+
+ FrameTreeNode* root = web_contents()->GetFrameTree()->root();
+ // Speculative RenderFrameHost for B will commit early because current rfh_a
+ // is not alive after the crash in step (2).
+ RenderFrameHostImpl* current_rfh =
+ root->render_manager()->current_frame_host();
+ NavigationRequest* navigation_request = root->navigation_request();
+ EXPECT_EQ(navigation_request->associated_site_instance_type(),
+ NavigationRequest::AssociatedSiteInstanceType::CURRENT);
+ EXPECT_TRUE(current_rfh);
+ EXPECT_TRUE(current_rfh->IsCurrent());
+
+ // 4) Get the RenderDocumentHostUserData associated with speculative
+ // RenderFrameHost.
+ Data::CreateForCurrentDocument(current_rfh);
+ base::WeakPtr<Data> data =
+ Data::GetForCurrentDocument(current_rfh)->GetWeakPtr();
+ EXPECT_TRUE(data);
+
+ // 5) Let the navigation finish and make sure it has succeeded.
+ manager.WaitForNavigationFinished();
+ EXPECT_EQ(url_b, web_contents()->GetMainFrame()->GetLastCommittedURL());
+
+ // 6) Data shouldn't be cleared in this case, as state
+ // |committed_speculative_rfh_before_navigation_commit_| is true during the
+ // check in DidCommitInternalNavigation as the speculative RFH swaps with the
+ // crashed RFH and performs commit before navigation commit happens.
+ EXPECT_TRUE(data);
}
// Tests that RenderDocumentHostUserData object is created for speculative
@@ -234,11 +467,8 @@ IN_PROC_BROWSER_TEST_F(RenderDocumentHostUserDataTest, SpeculativeRFHDeleted) {
RenderFrameHostImpl* rfh_a = web_contents()->GetMainFrame();
RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
- // RFH B has an unload handler.
- auto detach_filter_b = base::MakeRefCounted<DropMessageFilter>(
- FrameMsgStart, FrameHostMsg_Detach::ID);
- rfh_b->GetProcess()->AddFilter(detach_filter_b.get());
- EXPECT_TRUE(ExecJs(rfh_b, "onunload=function(){}"));
+ // Leave rfh_b in pending deletion state.
+ LeaveInPendingDeletionState(rfh_b);
// 2) Navigation from B to C. The server is slow to respond.
TestNavigationManager navigation_observer(web_contents(), url_c);
@@ -303,7 +533,6 @@ IN_PROC_BROWSER_TEST_F(RenderDocumentHostUserDataTest,
CheckInPendingDeletionState) {
ASSERT_TRUE(embedded_test_server()->Start());
IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
- std::string onunload_script = "window.onunload = function(){ while(1); }";
GURL url_ab(embedded_test_server()->GetURL(
"a.com", "/cross_site_iframe_factory.html?a(b)"));
GURL url_c(embedded_test_server()->GetURL("c.com", "/title1.html"));
@@ -313,13 +542,9 @@ IN_PROC_BROWSER_TEST_F(RenderDocumentHostUserDataTest,
RenderFrameHostImpl* rfh_a = top_frame_host();
RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
- // 2) Act as if there is a slow unload handler on rfh_a and rfh_b.
+ // 2) Leave both rfh_a and rfh_b in pending deletion state.
LeaveInPendingDeletionState(rfh_a);
- rfh_b->SetSubframeUnloadTimeoutForTesting(base::TimeDelta::FromSeconds(30));
- auto detach_filter = base::MakeRefCounted<DropMessageFilter>(
- FrameMsgStart, FrameHostMsg_Detach::ID);
- rfh_b->GetProcess()->AddFilter(detach_filter.get());
- EXPECT_TRUE(ExecuteScript(rfh_b->frame_tree_node(), onunload_script));
+ LeaveInPendingDeletionState(rfh_b);
// 3) Create RDHUD object for both rfh_a and rfh_b before running unload
// handlers.
@@ -504,6 +729,177 @@ IN_PROC_BROWSER_TEST_F(RenderDocumentHostUserDataTest, SameSiteNavigation) {
EXPECT_FALSE(data);
}
+// This test ensures that the data created during the new WebContents
+// initialisation is not lost during the initial "navigation" triggered by the
+// window.open.
+IN_PROC_BROWSER_TEST_F(RenderDocumentHostUserDataTest, WindowOpen) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+
+ int popup_data_id = -1;
+ WebContents* new_tab = nullptr;
+
+ // 1) Navigate to A.
+ GURL url(embedded_test_server()->GetURL("a.com", "/title1.html"));
+ EXPECT_TRUE(NavigateToURL(shell(), url));
+
+ // 2) Register WebContentsDelegate to get notified about new popups.
+ PopupCreatedObserver observer(base::BindRepeating(
+ [](int* popup_data_id, WebContents** new_tab, WebContents* web_contents) {
+ EXPECT_EQ(*popup_data_id, -1);
+ EXPECT_FALSE(*new_tab);
+ *new_tab = web_contents;
+
+ *popup_data_id =
+ Data::GetOrCreateForCurrentDocument(web_contents->GetMainFrame())
+ ->unique_id();
+ },
+ &popup_data_id, &new_tab));
+ web_contents()->SetDelegate(&observer);
+
+ // 3) Invoke a window.open() without parameters. The delegate should capture
+ // the ownership of the new WebContents and attach Data to its main frame.
+ EXPECT_TRUE(ExecJs(top_frame_host(), "window.open()"));
+ EXPECT_TRUE(new_tab);
+ // Do not check for the success status because we haven't committed a
+ // navigation yet, which causes checks to fail.
+ WaitForLoadStopWithoutSuccessCheck(new_tab);
+
+ // 4) Expect the data to the preserved (it might have been deleted due to us
+ // having a fake initial navigation for blank window.open).
+ Data* new_tab_data = Data::GetForCurrentDocument(new_tab->GetMainFrame());
+ EXPECT_TRUE(new_tab_data);
+ EXPECT_EQ(new_tab_data->unique_id(), popup_data_id);
+
+ web_contents()->SetDelegate(nullptr);
+}
+
+IN_PROC_BROWSER_TEST_F(RenderDocumentHostUserDataTest, BlankIframe) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+
+ GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
+ GURL url_b(
+ embedded_test_server()->GetURL("b.com", "/page_with_blank_iframe.html"));
+
+ // 0) Navigate to A to ensure that we have a live frame (see a comment in
+ // AttachOnCreatingInitialFrame).
+ EXPECT_TRUE(NavigateToURL(shell(), url_a));
+
+ int starting_id = next_id + 1;
+
+ std::vector<RenderFrameHost*> created_rfhs;
+
+ // 1) Register an observer which observes all created RenderFrameHosts
+ // and attaches user data to them.
+ RenderFrameHostCreatedObserver observer(
+ web_contents(), base::BindRepeating(
+ [](std::vector<RenderFrameHost*>* created_rfhs,
+ RenderFrameHost* rfh) {
+ created_rfhs->push_back(rfh);
+ Data::GetOrCreateForCurrentDocument(rfh);
+ },
+ &created_rfhs));
+
+ // 2) Navigate to a page with a blank iframe.
+ EXPECT_TRUE(NavigateToURL(shell(), url_b));
+
+ // 3) Expect that we have two frames with valid user data.
+ // The danger here lies in the perculiar properties of the initial navigation
+ // for the blank iframes, which does not create a new document, but goes via
+ // DidCommitProvisionalLoad.
+ std::vector<int> current_ids;
+ for (RenderFrameHost* rfh : created_rfhs) {
+ if (auto* data = Data::GetForCurrentDocument(rfh)) {
+ current_ids.push_back(data->unique_id());
+ }
+ }
+
+ EXPECT_EQ(2u, created_rfhs.size());
+ EXPECT_THAT(current_ids, testing::ElementsAre(starting_id, starting_id + 1));
+}
+
+IN_PROC_BROWSER_TEST_F(RenderDocumentHostUserDataTest, SrcDocIframe) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+
+ GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
+ GURL url_b(embedded_test_server()->GetURL(
+ "b.com", "/frame_tree/page_with_srcdoc_frame.html"));
+
+ // 0) Navigate to A to ensure that we have a live frame (see a comment in
+ // AttachOnCreatingInitialFrame).
+ EXPECT_TRUE(NavigateToURL(shell(), url_a));
+
+ int starting_id = next_id + 1;
+
+ std::vector<RenderFrameHost*> created_rfhs;
+
+ // 1) Register an observer which observes all created RenderFrameHosts
+ // and attaches user data to them.
+ RenderFrameHostCreatedObserver observer(
+ web_contents(), base::BindRepeating(
+ [](std::vector<RenderFrameHost*>* created_rfhs,
+ RenderFrameHost* rfh) {
+ created_rfhs->push_back(rfh);
+ Data::GetOrCreateForCurrentDocument(rfh);
+ },
+ &created_rfhs));
+
+ // 2) Navigate to a page with a srcdoc iframe.
+ EXPECT_TRUE(NavigateToURL(shell(), url_b));
+
+ // 3) Expect that we have one frame with valid user data.
+ // For the subframe, the user data was attached to the initial blank document
+ // and should have been deleted when the document was navigated to
+ // about:srcdoc.
+ std::vector<int> current_ids;
+ for (RenderFrameHost* rfh : created_rfhs) {
+ if (auto* data = Data::GetForCurrentDocument(rfh)) {
+ current_ids.push_back(data->unique_id());
+ }
+ }
+
+ EXPECT_EQ(2u, created_rfhs.size());
+ EXPECT_THAT(current_ids, testing::ElementsAre(starting_id));
+}
+
+// This test doesn't actually work at the moment and documents the current
+// behaviour rather than intended one.
+// TODO(sreejakshetty): Fix it.
+IN_PROC_BROWSER_TEST_F(RenderDocumentHostUserDataTest,
+ AttachOnCreatingInitialFrame) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+
+ GURL url(embedded_test_server()->GetURL("a.com", "/title1.html"));
+
+ bool observed_frame_creation = false;
+
+ // 1) Register an observer which observes all created RenderFrameHosts
+ // and attaches user data to them.
+ RenderFrameHostCreatedObserver observer(
+ web_contents(),
+ base::BindRepeating(
+ [](bool* observed_frame_creation, RenderFrameHost* rfh) {
+ Data::GetOrCreateForCurrentDocument(rfh);
+ *observed_frame_creation = true;
+ },
+ &observed_frame_creation));
+
+ // 2) Navigate to a new page.
+ EXPECT_FALSE(shell()->web_contents()->GetMainFrame()->IsRenderFrameLive());
+ EXPECT_TRUE(NavigateToURL(shell(), url));
+
+ // 3) Unfortunately, user data will not be there – the reason is that we
+ // actually reuse the initial RenderFrameHost, which wasn't fully initialised
+ // when we started a navigation (IsRenderFrameLive == false). It will complete
+ // its initialisation, dispatching RenderFrameCreated, but during the
+ // navigation commit we will consider the document in it to be reset later
+ // when we commit the navigation. In the short term, we should not reset RDHUD
+ // in that case. In the long term, we probably should create a new
+ // RenderFrameHost here.
+ EXPECT_TRUE(observed_frame_creation);
+ EXPECT_FALSE(
+ Data::GetForCurrentDocument(shell()->web_contents()->GetMainFrame()));
+}
+
// Test RenderDocumentHostUserData with BackForwardCache feature enabled.
class RenderDocumentHostUserDataWithBackForwardCacheTest
: public RenderDocumentHostUserDataTest {
diff --git a/chromium/content/browser/frame_host/render_frame_host_delegate.cc b/chromium/content/browser/frame_host/render_frame_host_delegate.cc
index b6a4bd21bf5..6db3cc869ff 100644
--- a/chromium/content/browser/frame_host/render_frame_host_delegate.cc
+++ b/chromium/content/browser/frame_host/render_frame_host_delegate.cc
@@ -38,20 +38,6 @@ bool RenderFrameHostDelegate::DidAddMessageToConsole(
return false;
}
-void RenderFrameHostDelegate::RunFileChooser(
- RenderFrameHost* render_frame_host,
- std::unique_ptr<FileChooserImpl::FileSelectListenerImpl> listener,
- const blink::mojom::FileChooserParams& params) {
- listener->FileSelectionCanceled();
-}
-
-void RenderFrameHostDelegate::EnumerateDirectory(
- RenderFrameHost* render_frame_host,
- std::unique_ptr<FileChooserImpl::FileSelectListenerImpl> listener,
- const base::FilePath& path) {
- listener->FileSelectionCanceled();
-}
-
WebContents* RenderFrameHostDelegate::GetAsWebContents() {
return nullptr;
}
@@ -135,7 +121,6 @@ RenderFrameHostDelegate* RenderFrameHostDelegate::CreateNewWindow(
}
bool RenderFrameHostDelegate::ShouldAllowRunningInsecureContent(
- WebContents* web_contents,
bool allowed_per_prefs,
const url::Origin& origin,
const GURL& resource_url) {
diff --git a/chromium/content/browser/frame_host/render_frame_host_delegate.h b/chromium/content/browser/frame_host/render_frame_host_delegate.h
index f20bbc780e7..e70d6104c82 100644
--- a/chromium/content/browser/frame_host/render_frame_host_delegate.h
+++ b/chromium/content/browser/frame_host/render_frame_host_delegate.h
@@ -15,7 +15,6 @@
#include "base/optional.h"
#include "build/build_config.h"
#include "components/viz/common/surfaces/surface_id.h"
-#include "content/browser/frame_host/file_chooser_impl.h"
#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/webui/web_ui_impl.h"
#include "content/common/content_export.h"
@@ -71,7 +70,6 @@ class Origin;
namespace blink {
namespace mojom {
-class FileChooserParams;
class FullscreenOptions;
}
} // namespace blink
@@ -184,26 +182,6 @@ class CONTENT_EXPORT RenderFrameHostDelegate {
RenderFrameHost* source,
std::vector<blink::mojom::FaviconURLPtr> candidates) {}
- // Called when a file selection is to be done.
- //
- // Overrides of this function must call either listener->FileSelected() or
- // listener->FileSelectionCanceled().
- virtual void RunFileChooser(
- RenderFrameHost* render_frame_host,
- std::unique_ptr<FileChooserImpl::FileSelectListenerImpl> listener,
- const blink::mojom::FileChooserParams& params);
-
- // Request to enumerate a directory. This is equivalent to running the file
- // chooser in directory-enumeration mode and having the user select the given
- // directory.
- //
- // Overrides of this function must call either listener->FileSelected() or
- // listener->FileSelectionCanceled().
- virtual void EnumerateDirectory(
- RenderFrameHost* render_frame_host,
- std::unique_ptr<FileChooserImpl::FileSelectListenerImpl> listener,
- const base::FilePath& directory_path);
-
// The pending page load was canceled, so the address bar should be updated.
virtual void DidCancelLoading() {}
@@ -298,11 +276,10 @@ class CONTENT_EXPORT RenderFrameHostDelegate {
// Returns whether entering fullscreen with EnterFullscreenMode() is allowed.
virtual bool CanEnterFullscreenMode();
- // Notification that the frame wants to go into fullscreen mode.
- // |origin| represents the origin of the frame that requests fullscreen. Must
- // only be called if CanEnterFullscreenMode returns true.
+ // Notification that the frame with the given host wants to enter fullscreen
+ // mode. Must only be called if CanEnterFullscreenMode returns true.
virtual void EnterFullscreenMode(
- const GURL& origin,
+ RenderFrameHost* requesting_frame,
const blink::mojom::FullscreenOptions& options) {}
// Notification that the frame wants to go out of fullscreen mode.
@@ -430,10 +407,8 @@ class CONTENT_EXPORT RenderFrameHostDelegate {
// Reports that passive mixed content was found at the specified url.
virtual void PassiveInsecureContentFound(const GURL& resource_url) {}
- // Checks if running of active mixed content is allowed for the specified
- // WebContents/tab.
- virtual bool ShouldAllowRunningInsecureContent(WebContents* web_contents,
- bool allowed_per_prefs,
+ // Checks if running of active mixed content is allowed in the current tab.
+ virtual bool ShouldAllowRunningInsecureContent(bool allowed_per_prefs,
const url::Origin& origin,
const GURL& resource_url);
@@ -496,6 +471,10 @@ class CONTENT_EXPORT RenderFrameHostDelegate {
virtual void AudioContextPlaybackStopped(RenderFrameHost* host,
int context_id) {}
+ // Notifies observers if the frame has changed audible state.
+ virtual void OnFrameAudioStateChanged(RenderFrameHost* host,
+ bool is_audible) {}
+
// Returns the main frame of the inner delegate that is attached to this
// delegate using |frame_tree_node|. Returns nullptr if no such inner delegate
// exists.
@@ -571,7 +550,6 @@ class CONTENT_EXPORT RenderFrameHostDelegate {
virtual void CreateNewWidget(
int32_t render_process_id,
int32_t widget_route_id,
- mojo::PendingRemote<mojom::Widget> widget,
mojo::PendingAssociatedReceiver<blink::mojom::WidgetHost>
blink_widget_host,
mojo::PendingAssociatedRemote<blink::mojom::Widget> blink_widget) {}
@@ -580,7 +558,6 @@ class CONTENT_EXPORT RenderFrameHostDelegate {
virtual void CreateNewFullscreenWidget(
int32_t render_process_id,
int32_t widget_route_id,
- mojo::PendingRemote<mojom::Widget> widget,
mojo::PendingAssociatedReceiver<blink::mojom::WidgetHost>
blink_widget_host,
mojo::PendingAssociatedRemote<blink::mojom::Widget> blink_widget) {}
@@ -614,6 +591,26 @@ class CONTENT_EXPORT RenderFrameHostDelegate {
virtual void OnCookiesAccessed(RenderFrameHostImpl* render_frame_host,
const CookieAccessDetails& details) {}
+ // Notified that the renderer responded after calling GetSavableResourceLinks.
+ virtual void SavableResourceLinksResponse(
+ RenderFrameHostImpl* source,
+ const std::vector<GURL>& resources_list,
+ blink::mojom::ReferrerPtr referrer,
+ const std::vector<blink::mojom::SavableSubframePtr>& subframes) {}
+
+ // Notified that the renderer returned an error after calling
+ // GetSavableResourceLinks in case the frame contains non-savable content
+ // (i.e. from a non-savable scheme) or if there were errors gathering the
+ // links.
+ virtual void SavableResourceLinksError(RenderFrameHostImpl* source) {}
+
+ // Called when |RenderFrameHostImpl::lifecycle_state()| changes i.e., when
+ // RenderFrameHost LifecycleState changes from old_state to new_state.
+ virtual void RenderFrameHostStateChanged(
+ RenderFrameHost* host,
+ RenderFrameHostImpl::LifecycleState old_state,
+ RenderFrameHostImpl::LifecycleState new_state) {}
+
protected:
virtual ~RenderFrameHostDelegate() = default;
};
diff --git a/chromium/content/browser/frame_host/render_frame_host_feature_policy_unittest.cc b/chromium/content/browser/frame_host/render_frame_host_feature_policy_unittest.cc
index cecab23e41f..913ce4a535c 100644
--- a/chromium/content/browser/frame_host/render_frame_host_feature_policy_unittest.cc
+++ b/chromium/content/browser/frame_host/render_frame_host_feature_policy_unittest.cc
@@ -65,10 +65,10 @@ class RenderFrameHostFeaturePolicyTest
RenderFrameHost* child,
blink::mojom::FeaturePolicyFeature feature,
const std::vector<std::string>& origins) {
- static_cast<TestRenderFrameHost*>(parent)->OnDidChangeFramePolicy(
- child->GetRoutingID(), {network::mojom::WebSandboxFlags::kNone,
- CreateFPHeader(feature, origins),
- {} /* required_document_policy */});
+ static_cast<TestRenderFrameHost*>(parent)->DidChangeFramePolicy(
+ child->GetFrameToken(), {network::mojom::WebSandboxFlags::kNone,
+ CreateFPHeader(feature, origins),
+ {} /* required_document_policy */});
}
void SimulateNavigation(RenderFrameHost** rfh, const GURL& url) {
diff --git a/chromium/content/browser/frame_host/render_frame_host_impl.cc b/chromium/content/browser/frame_host/render_frame_host_impl.cc
index 115673d129d..54cc5135341 100644
--- a/chromium/content/browser/frame_host/render_frame_host_impl.cc
+++ b/chromium/content/browser/frame_host/render_frame_host_impl.cc
@@ -14,8 +14,8 @@
#include "base/command_line.h"
#include "base/containers/queue.h"
#include "base/debug/alias.h"
+#include "base/debug/crash_logging.h"
#include "base/debug/dump_without_crashing.h"
-#include "base/hash/hash.h"
#include "base/i18n/character_encoding.h"
#include "base/lazy_instance.h"
#include "base/memory/ptr_util.h"
@@ -46,6 +46,7 @@
#include "content/browser/contacts/contacts_manager_impl.h"
#include "content/browser/data_url_loader_factory.h"
#include "content/browser/devtools/devtools_instrumentation.h"
+#include "content/browser/devtools/protocol/audits.h"
#include "content/browser/dom_storage/dom_storage_context_wrapper.h"
#include "content/browser/download/data_url_blob_reader.h"
#include "content/browser/download/mhtml_generation_manager.h"
@@ -55,7 +56,6 @@
#include "content/browser/frame_host/cookie_utils.h"
#include "content/browser/frame_host/cross_process_frame_connector.h"
#include "content/browser/frame_host/debug_urls.h"
-#include "content/browser/frame_host/file_chooser_impl.h"
#include "content/browser/frame_host/frame_tree.h"
#include "content/browser/frame_host/frame_tree_node.h"
#include "content/browser/frame_host/input/input_injector_impl.h"
@@ -82,6 +82,7 @@
#include "content/browser/native_file_system/native_file_system_manager_impl.h"
#include "content/browser/navigation_subresource_loader_params.h"
#include "content/browser/net/cross_origin_embedder_policy_reporter.h"
+#include "content/browser/net/cross_origin_opener_policy_reporter.h"
#include "content/browser/payments/payment_app_context_impl.h"
#include "content/browser/permissions/permission_controller_impl.h"
#include "content/browser/permissions/permission_service_context.h"
@@ -122,22 +123,21 @@
#include "content/browser/webui/url_data_manager_backend.h"
#include "content/browser/webui/web_ui_controller_factory_registry.h"
#include "content/browser/webui/web_ui_url_loader_factory_internal.h"
-#include "content/browser/worker_host/dedicated_worker_host.h"
+#include "content/browser/worker_host/dedicated_worker_host_factory_impl.h"
#include "content/browser/worker_host/shared_worker_service_impl.h"
#include "content/common/associated_interfaces.mojom.h"
#include "content/common/content_constants_internal.h"
#include "content/common/content_navigation_policy.h"
#include "content/common/frame.mojom.h"
#include "content/common/frame_messages.h"
-#include "content/common/input/input_handler.mojom.h"
#include "content/common/inter_process_time_ticks_converter.h"
#include "content/common/navigation_params.h"
#include "content/common/navigation_params_mojom_traits.h"
#include "content/common/navigation_params_utils.h"
#include "content/common/render_message_filter.mojom.h"
#include "content/common/renderer.mojom.h"
+#include "content/common/state_transitions.h"
#include "content/common/unfreezable_frame_messages.h"
-#include "content/common/widget.mojom.h"
#include "content/public/browser/ax_event_notification_details.h"
#include "content/public/browser/browser_accessibility_state.h"
#include "content/public/browser/browser_context.h"
@@ -192,10 +192,12 @@
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/is_potentially_trustworthy.h"
+#include "services/network/public/cpp/trust_token_operation_authorization.h"
#include "services/network/public/cpp/web_sandbox_flags.h"
#include "services/network/public/cpp/wrapper_shared_url_loader_factory.h"
#include "services/network/public/mojom/cookie_access_observer.mojom.h"
#include "services/network/public/mojom/network_service.mojom.h"
+#include "services/network/public/mojom/url_loader.mojom-shared.h"
#include "services/network/public/mojom/web_sandbox_flags.mojom-shared.h"
#include "services/service_manager/public/cpp/connector.h"
#include "services/service_manager/public/cpp/interface_provider.h"
@@ -232,6 +234,7 @@
#include "ui/accessibility/ax_tree.h"
#include "ui/accessibility/ax_tree_id_registry.h"
#include "ui/accessibility/ax_tree_update.h"
+#include "ui/events/event_constants.h"
#include "ui/gfx/geometry/quad_f.h"
#include "url/gurl.h"
#include "url/origin.h"
@@ -422,7 +425,7 @@ void ForEachFrame(RenderFrameHostImpl* root_frame_host,
const FrameCallback& frame_callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
- FrameTree* frame_tree = root_frame_host->frame_tree_node()->frame_tree();
+ FrameTree* frame_tree = root_frame_host->frame_tree();
DCHECK_EQ(root_frame_host, frame_tree->GetMainFrame());
for (FrameTreeNode* node : frame_tree->Nodes()) {
@@ -474,11 +477,10 @@ base::Histogram::Sample HashInterfaceNameToHistogramSample(
// Set crash keys that will help understand the circumstances of a renderer
// kill. Note that the commit URL is already reported in a crash key, and
// additional keys are logged in RenderProcessHostImpl::ShutdownForBadMessage.
-void LogRendererKillCrashKeys(const GURL& site_url) {
+void LogRendererKillCrashKeys(const SiteInfo& site_info) {
static auto* site_url_key = base::debug::AllocateCrashKeyString(
"current_site_url", base::debug::CrashKeySize::Size64);
- base::debug::SetCrashKeyString(site_url_key,
- site_url.possibly_invalid_spec());
+ base::debug::SetCrashKeyString(site_url_key, site_info.GetDebugString());
}
void LogCanCommitOriginAndUrlFailureReason(const std::string& failure_reason) {
@@ -487,18 +489,43 @@ void LogCanCommitOriginAndUrlFailureReason(const std::string& failure_reason) {
base::debug::SetCrashKeyString(failure_reason_key, failure_reason);
}
+bool ShouldBypassChecksForErrorPage(
+ RenderFrameHostImpl* frame,
+ NavigationRequest* navigation_request,
+ bool* should_commit_unreachable_url = nullptr) {
+ DCHECK(frame);
+
+ if (should_commit_unreachable_url)
+ *should_commit_unreachable_url = false;
+
+ bool is_main_frame = !frame->GetParent();
+ if (SiteIsolationPolicy::IsErrorPageIsolationEnabled(is_main_frame)) {
+ if (frame->GetSiteInstance()->GetSiteInfo() ==
+ SiteInfo::CreateForErrorPage()) {
+ if (should_commit_unreachable_url)
+ *should_commit_unreachable_url = true;
+
+ // With error page isolation, any URL can commit in an error page process.
+ return true;
+ }
+ } else {
+ // Without error page isolation, a blocked navigation is expected to
+ // commit in the old renderer process. This may be true for subframe
+ // navigations even when error page isolation is enabled for main frames.
+ if (navigation_request &&
+ net::IsRequestBlockedError(navigation_request->GetNetErrorCode())) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
url::Origin GetOriginForURLLoaderFactoryUnchecked(
+ RenderFrameHostImpl* target_frame,
NavigationRequest* navigation_request) {
- // Return a safe opaque origin when there is no |navigation_request| (e.g.
- // when RFHI::CommitNavigation is called via RFHI::NavigateToInterstitialURL).
- if (!navigation_request)
- return url::Origin();
-
- // GetOriginForURLLoaderFactory should only be called at the ready-to-commit
- // time, when the RFHI to commit the navigation is already known.
- DCHECK_LE(NavigationRequest::READY_TO_COMMIT, navigation_request->state());
- RenderFrameHostImpl* target_frame = navigation_request->GetRenderFrameHost();
DCHECK(target_frame);
+ DCHECK(navigation_request);
// Check if this is loadDataWithBaseUrl (which needs special treatment).
auto& common_params = navigation_request->common_params();
@@ -547,18 +574,26 @@ url::Origin GetOriginForURLLoaderFactoryUnchecked(
url::Origin GetOriginForURLLoaderFactory(
NavigationRequest* navigation_request) {
- url::Origin result =
- GetOriginForURLLoaderFactoryUnchecked(navigation_request);
+ DCHECK(navigation_request);
- // |result| must be an origin that is allowed to be accessed from the process
- // that is the target of this navigation.
- if (navigation_request) {
- int process_id =
- navigation_request->GetRenderFrameHost()->GetProcess()->GetID();
- auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
- CHECK(policy->CanAccessDataForOrigin(process_id, result));
- }
+ // GetOriginForURLLoaderFactory should only be called at the ReadyToCommit
+ // time, when the RFHI to commit the navigation is already known.
+ DCHECK_EQ(NavigationRequest::READY_TO_COMMIT, navigation_request->state());
+ RenderFrameHostImpl* target_frame = navigation_request->GetRenderFrameHost();
+ DCHECK(target_frame);
+
+ // Calculate an approximation (sandbox/csp is ignored) of the origin that will
+ // be committed because of |navigation_request|.
+ url::Origin result =
+ GetOriginForURLLoaderFactoryUnchecked(target_frame, navigation_request);
+ // Check that |result| origin is allowed to be accessed from the process that
+ // is the target of this navigation.
+ if (ShouldBypassChecksForErrorPage(target_frame, navigation_request))
+ return result;
+ int process_id = target_frame->GetProcess()->GetID();
+ auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
+ CHECK(policy->CanAccessDataForOrigin(process_id, result));
return result;
}
@@ -607,9 +642,6 @@ void OnDataURLRetrieved(
StartDownload(std::move(parameters), mojo::NullRemote());
}
-// TODO(crbug.com/977040): Remove when no longer needed.
-const uint32_t kMaxCookieSameSiteDeprecationUrls = 20;
-
void RecordCrossOriginIsolationMetrics(RenderFrameHostImpl* rfh) {
ContentBrowserClient* client = GetContentClient()->browser();
if (rfh->cross_origin_opener_policy().value ==
@@ -648,15 +680,77 @@ bool ParentNeedsTrustTokenFeaturePolicy(
if (!begin_params.trust_token_params)
return false;
- switch (begin_params.trust_token_params->type) {
- case network::mojom::TrustTokenOperationType::kRedemption:
- case network::mojom::TrustTokenOperationType::kSigning:
- return true;
- case network::mojom::TrustTokenOperationType::kIssuance:
- return false;
+ return network::DoesTrustTokenOperationRequireFeaturePolicy(
+ begin_params.trust_token_params->type);
+}
+
+// Analyzes trusted sources of a frame's trust-token-redemption Feature Policy
+// feature to see if the feature is definitely disabled or potentially enabled.
+//
+// This information will be bound to a URLLoaderFactory; if the answer is
+// "definitely disabled," the network service will report a bad message if it
+// receives a request from the renderer to execute a Trust Tokens redemption or
+// signing operation in the frame.
+//
+// A return value of kForbid denotes that the feature is disabled for the
+// frame. A return value of kPotentiallyPermit means that all trusted
+// information sources say that the policy is enabled.
+network::mojom::TrustTokenRedemptionPolicy
+DetermineWhetherToForbidTrustTokenRedemption(
+ const RenderFrameHostImpl* parent,
+ const mojom::CommitNavigationParams& commit_params,
+ const url::Origin& subframe_origin) {
+ // For main frame loads, the frame's feature policy is determined entirely by
+ // response headers, which are provided by the renderer.
+ if (!parent || !commit_params.frame_policy)
+ return network::mojom::TrustTokenRedemptionPolicy::kPotentiallyPermit;
+
+ const blink::FeaturePolicy* parent_policy = parent->feature_policy();
+ blink::ParsedFeaturePolicy container_policy =
+ commit_params.frame_policy->container_policy;
+
+ auto subframe_policy = blink::FeaturePolicy::CreateFromParentPolicy(
+ parent_policy, container_policy, subframe_origin);
+
+ if (subframe_policy->IsFeatureEnabled(
+ blink::mojom::FeaturePolicyFeature::kTrustTokenRedemption)) {
+ return network::mojom::TrustTokenRedemptionPolicy::kPotentiallyPermit;
+ }
+ return network::mojom::TrustTokenRedemptionPolicy::kForbid;
+}
+
+// When a frame creates its initial subresource loaders, it needs to know
+// whether the trust-token-redemption Feature Policy feature will be enabled
+// after the commit finishes, which is a little involved (see
+// DetermineWhetherToForbidTrustTokenRedemption). In contrast, if it needs to
+// make this decision once the frame has committted---for instance, to create
+// more loaders after the network service crashes---it can directly consult the
+// current Feature Policy state to determine whether the feature is enabled.
+network::mojom::TrustTokenRedemptionPolicy
+DetermineAfterCommitWhetherToForbidTrustTokenRedemption(
+ RenderFrameHostImpl* impl) {
+ return impl->IsFeatureEnabled(
+ blink::mojom::FeaturePolicyFeature::kTrustTokenRedemption)
+ ? network::mojom::TrustTokenRedemptionPolicy::kPotentiallyPermit
+ : network::mojom::TrustTokenRedemptionPolicy::kForbid;
+}
+
+// Returns the string corresponding to LifecycleState, used for logging crash
+// keys.
+const char* LifecycleStateToString(RenderFrameHostImpl::LifecycleState state) {
+ using LifecycleState = RenderFrameHostImpl::LifecycleState;
+ switch (state) {
+ case LifecycleState::kSpeculative:
+ return "Speculative";
+ case LifecycleState::kActive:
+ return "Active";
+ case LifecycleState::kInBackForwardCache:
+ return "InBackForwardCache";
+ case LifecycleState::kRunningUnloadHandlers:
+ return "RunningUnloadHandlers";
+ case LifecycleState::kReadyToBeDeleted:
+ return "ReadyToDeleted";
}
- NOTREACHED();
- return false;
}
} // namespace
@@ -757,6 +851,23 @@ RenderFrameHostImpl* RenderFrameHostImpl::FromID(int render_process_id,
return RenderFrameHostImpl::FromID(
GlobalFrameRoutingId(render_process_id, render_frame_id));
}
+
+// static
+RenderFrameHostImpl* RenderFrameHostImpl::FromFrameToken(
+ int process_id,
+ const base::UnguessableToken& frame_token) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ auto it = g_token_frame_map.Get().find(frame_token);
+ if (it == g_token_frame_map.Get().end())
+ return nullptr;
+
+ // TODO(tonikitoo): Consider killing the renderer when this happens
+ if (it->second->GetProcess()->GetID() != process_id)
+ return nullptr;
+
+ return it->second;
+}
+
// static
RenderFrameHost* RenderFrameHost::FromAXTreeID(ui::AXTreeID ax_tree_id) {
return RenderFrameHostImpl::FromAXTreeID(ax_tree_id);
@@ -822,6 +933,7 @@ RenderFrameHostImpl::RenderFrameHostImpl(
web_ui_type_(WebUI::kNoWebUI),
has_selection_(false),
is_audible_(false),
+ should_virtual_keyboard_overlay_content_(false),
last_navigation_previews_state_(PREVIEWS_UNSPECIFIED),
waiting_for_init_(renderer_initiated_creation),
has_focused_editable_element_(false),
@@ -872,10 +984,6 @@ RenderFrameHostImpl::RenderFrameHostImpl(
//
// Local roots require a RenderWidget for input/layout/painting.
if (!parent_ || IsCrossProcessSubframe()) {
- mojo::PendingRemote<mojom::Widget> widget;
- GetRemoteInterfaces()->GetInterface(
- widget.InitWithNewPipeAndPassReceiver());
-
if (!parent_) {
// For main frames, the RenderWidgetHost is owned by the RenderViewHost.
// TODO(https://crbug.com/545684): Once RenderViewHostImpl has-a
@@ -883,10 +991,6 @@ RenderFrameHostImpl::RenderFrameHostImpl(
// owning the RenderWidgetHostImpl itself.
DCHECK(GetLocalRenderWidgetHost());
DCHECK(!GetLocalRenderWidgetHost()->owned_by_render_frame_host());
-
- // Make the RenderWidgetHostImpl able to call the mojo Widget interface
- // (implemented by the RenderWidgetImpl).
- GetLocalRenderWidgetHost()->SetWidget(std::move(widget));
} else {
// For local child roots, the RenderFrameHost directly creates and owns
// its RenderWidgetHost.
@@ -895,7 +999,7 @@ RenderFrameHostImpl::RenderFrameHostImpl(
DCHECK_EQ(nullptr, GetLocalRenderWidgetHost());
owned_render_widget_host_ = RenderWidgetHostFactory::Create(
frame_tree_->render_widget_delegate(), GetProcess(),
- widget_routing_id, std::move(widget), /*hidden=*/true);
+ widget_routing_id, /*hidden=*/true);
owned_render_widget_host_->set_owned_by_render_frame_host(true);
#if defined(OS_ANDROID)
owned_render_widget_host_->SetForceEnableZoom(
@@ -906,8 +1010,6 @@ RenderFrameHostImpl::RenderFrameHostImpl(
if (is_main_frame())
GetLocalRenderWidgetHost()->SetIntersectsViewport(true);
GetLocalRenderWidgetHost()->SetFrameDepth(frame_tree_node_->depth());
- GetLocalRenderWidgetHost()->SetFrameInputHandler(
- frame_input_handler_.get());
GetLocalRenderWidgetHost()->input_router()->SetFrameTreeNodeId(
frame_tree_node_->frame_tree_node_id());
}
@@ -948,7 +1050,7 @@ RenderFrameHostImpl::~RenderFrameHostImpl() {
// RenderFrameHost during cleanup.
ClearWebUI();
- SetLastCommittedSiteUrl(GURL());
+ SetLastCommittedSiteInfo(GURL());
if (last_committed_document_priority_) {
GetProcess()->UpdateFrameWithPriority(last_committed_document_priority_,
base::nullopt);
@@ -968,7 +1070,8 @@ RenderFrameHostImpl::~RenderFrameHostImpl() {
// streams from this frame have terminated. This is required to ensure the
// process host has the correct media stream count, which affects its
// background priority.
- OnAudibleStateChanged(false);
+ if (is_audible_)
+ OnAudibleStateChanged(false);
// If this was the last active frame in the SiteInstance, the
// DecrementActiveFrameCount call will trigger the deletion of the
@@ -1027,7 +1130,7 @@ RenderFrameHostImpl::~RenderFrameHostImpl() {
// follows that |GetMainFrame()| will never return the speculative main frame
// being deleted, since it must have already been unset.
if (was_created && render_view_host_->GetMainFrame() != this)
- CHECK(!is_active());
+ CHECK(IsPendingDeletion() || IsInBackForwardCache());
GetProcess()->RemoveRoute(routing_id_);
g_routing_id_frame_map.Get().erase(
@@ -1050,9 +1153,6 @@ RenderFrameHostImpl::~RenderFrameHostImpl() {
if (owned_render_widget_host_)
owned_render_widget_host_->ShutdownAndDestroyWidget(false);
- // This needs to be deleted before |frame_input_handler_| so associated
- // remotes can send messages during shutdown. See crbug.com/1010478 for
- // details.
render_view_host_.reset();
// If another frame is waiting for a beforeunload completion callback from
@@ -1074,16 +1174,20 @@ int RenderFrameHostImpl::GetRoutingID() {
return routing_id_;
}
+const base::UnguessableToken& RenderFrameHostImpl::GetFrameToken() {
+ return frame_token_;
+}
+
ui::AXTreeID RenderFrameHostImpl::GetAXTreeID() {
return ax_tree_id();
}
-const base::UnguessableToken& RenderFrameHostImpl::GetTopFrameToken() const {
- const RenderFrameHostImpl* frame = this;
+const base::UnguessableToken& RenderFrameHostImpl::GetTopFrameToken() {
+ RenderFrameHostImpl* frame = this;
while (frame->parent_) {
frame = frame->parent_;
}
- return frame->frame_token();
+ return frame->GetFrameToken();
}
void RenderFrameHostImpl::AudioContextPlaybackStarted(int audio_context_id) {
@@ -1160,11 +1264,9 @@ void RenderFrameHostImpl::StartBackForwardCacheEvictionTimer() {
DCHECK(IsInBackForwardCache());
base::TimeDelta evict_after =
BackForwardCacheImpl::GetTimeToLiveInBackForwardCache();
- NavigationControllerImpl* controller = static_cast<NavigationControllerImpl*>(
- frame_tree_node_->navigator()->GetController());
back_forward_cache_eviction_timer_.SetTaskRunner(
- controller->GetBackForwardCache().GetTaskRunner());
+ frame_tree()->controller()->GetBackForwardCache().GetTaskRunner());
back_forward_cache_eviction_timer_.Start(
FROM_HERE, evict_after,
@@ -1184,17 +1286,11 @@ void RenderFrameHostImpl::OnGrantedMediaStreamAccess() {
}
void RenderFrameHostImpl::OnPortalActivated(
- std::unique_ptr<WebContents> predecessor_web_contents,
+ std::unique_ptr<Portal> predecessor,
+ mojo::PendingAssociatedRemote<blink::mojom::Portal> pending_portal,
+ mojo::PendingAssociatedReceiver<blink::mojom::PortalClient> client_receiver,
blink::TransferableMessage data,
base::OnceCallback<void(blink::mojom::PortalActivateResult)> callback) {
- mojo::PendingAssociatedRemote<blink::mojom::Portal> pending_portal;
- auto portal_receiver = pending_portal.InitWithNewEndpointAndPassReceiver();
- mojo::PendingAssociatedRemote<blink::mojom::PortalClient> pending_client;
- auto client_receiver = pending_client.InitWithNewEndpointAndPassReceiver();
-
- auto predecessor =
- std::make_unique<Portal>(this, std::move(predecessor_web_contents));
- predecessor->Bind(std::move(portal_receiver), std::move(pending_client));
auto it = portals_.insert(std::move(predecessor)).first;
GetNavigationControl()->OnPortalActivated(
@@ -1361,15 +1457,19 @@ void RenderFrameHostImpl::GetCanonicalUrlForSharing(
}
}
-mojo::Remote<blink::mojom::PauseSubresourceLoadingHandle>
-RenderFrameHostImpl::PauseSubresourceLoading() {
- DCHECK(frame_);
- mojo::Remote<blink::mojom::PauseSubresourceLoadingHandle>
- pause_subresource_loading_handle;
- GetRemoteInterfaces()->GetInterface(
- pause_subresource_loading_handle.BindNewPipeAndPassReceiver());
+void RenderFrameHostImpl::GetSerializedHtmlWithLocalLinks(
+ const base::flat_map<GURL, base::FilePath>& url_map,
+ const base::flat_map<base::UnguessableToken, base::FilePath>&
+ frame_token_map,
+ bool save_with_empty_url,
+ mojo::PendingRemote<mojom::FrameHTMLSerializerHandler> serializer_handler) {
+ // TODO(https://crbug.com/859110): Remove once frame_ can no longer be null.
+ if (!IsRenderFrameLive())
+ return;
- return pause_subresource_loading_handle;
+ frame_->GetSerializedHtmlWithLocalLinks(url_map, frame_token_map,
+ save_with_empty_url,
+ std::move(serializer_handler));
}
void RenderFrameHostImpl::ExecuteMediaPlayerActionAtLocation(
@@ -1403,7 +1503,8 @@ bool RenderFrameHostImpl::CreateNetworkServiceDefaultFactory(
CreateURLLoaderFactoryParamsForMainWorld(
last_committed_origin_,
mojo::Clone(last_committed_client_security_state_),
- std::move(coep_reporter_remote)),
+ std::move(coep_reporter_remote),
+ DetermineAfterCommitWhetherToForbidTrustTokenRedemption(this)),
std::move(default_factory_receiver));
}
@@ -1436,7 +1537,8 @@ void RenderFrameHostImpl::MarkIsolatedWorldsAsRequiringSeparateURLLoaderFactory(
CreateURLLoaderFactoriesForIsolatedWorlds(
GetExpectedMainWorldOriginForUrlLoaderFactory(),
isolated_world_origins,
- mojo::Clone(last_committed_client_security_state_));
+ mojo::Clone(last_committed_client_security_state_),
+ DetermineAfterCommitWhetherToForbidTrustTokenRedemption(this));
GetNavigationControl()->UpdateSubresourceLoaderFactories(
std::move(subresource_loader_factories));
}
@@ -1456,7 +1558,8 @@ blink::PendingURLLoaderFactoryBundle::OriginMap
RenderFrameHostImpl::CreateURLLoaderFactoriesForIsolatedWorlds(
const url::Origin& main_world_origin,
const base::flat_set<url::Origin>& isolated_world_origins,
- network::mojom::ClientSecurityStatePtr client_security_state) {
+ network::mojom::ClientSecurityStatePtr client_security_state,
+ network::mojom::TrustTokenRedemptionPolicy trust_token_redemption_policy) {
WebPreferences preferences = GetRenderViewHost()->GetWebkitPreferences();
blink::PendingURLLoaderFactoryBundle::OriginMap result;
@@ -1464,7 +1567,7 @@ RenderFrameHostImpl::CreateURLLoaderFactoriesForIsolatedWorlds(
network::mojom::URLLoaderFactoryParamsPtr factory_params =
URLLoaderFactoryParamsHelper::CreateForIsolatedWorld(
this, isolated_world_origin, main_world_origin,
- mojo::Clone(client_security_state));
+ mojo::Clone(client_security_state), trust_token_redemption_policy);
mojo::PendingRemote<network::mojom::URLLoaderFactory> factory_remote;
CreateNetworkServiceDefaultFactoryAndObserve(
@@ -1637,17 +1740,11 @@ bool RenderFrameHostImpl::OnMessageReceived(const IPC::Message& msg) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(RenderFrameHostImpl, msg)
IPC_MESSAGE_HANDLER(FrameHostMsg_Detach, OnDetach)
- IPC_MESSAGE_HANDLER(FrameHostMsg_UpdateState, OnUpdateState)
- IPC_MESSAGE_HANDLER(FrameHostMsg_OpenURL, OnOpenURL)
IPC_MESSAGE_HANDLER(FrameHostMsg_Unload_ACK, OnUnloadACK)
IPC_MESSAGE_HANDLER(FrameHostMsg_ContextMenu, OnContextMenu)
IPC_MESSAGE_HANDLER(FrameHostMsg_VisualStateResponse, OnVisualStateResponse)
- IPC_MESSAGE_HANDLER(FrameHostMsg_DidChangeOpener, OnDidChangeOpener)
- IPC_MESSAGE_HANDLER(FrameHostMsg_DidChangeFramePolicy,
- OnDidChangeFramePolicy)
IPC_MESSAGE_HANDLER(FrameHostMsg_DidStopLoading, OnDidStopLoading)
IPC_MESSAGE_HANDLER(FrameHostMsg_SelectionChanged, OnSelectionChanged)
- IPC_MESSAGE_HANDLER(FrameHostMsg_FrameDidCallFocus, OnFrameDidCallFocus)
IPC_END_MESSAGE_MAP()
// No further actions here, since we may have been deleted.
@@ -1668,26 +1765,29 @@ void RenderFrameHostImpl::OnAssociatedInterfaceRequest(
void RenderFrameHostImpl::AccessibilityPerformAction(
const ui::AXActionData& action_data) {
- if (!is_active() || !render_accessibility_)
+ // Don't perform any Accessibility action on an inactive frame.
+ if (IsInactiveAndDisallowReactivation() || !render_accessibility_)
return;
- // Use the dedicated HitTest method so that we can handle its response via
- // mojo callback once it's been handled in the renderer process.
if (action_data.action == ax::mojom::Action::kHitTest) {
- render_accessibility_->HitTest(
- action_data,
- base::BindOnce(&RenderFrameHostImpl::RequestAXHitTestCallback,
- weak_ptr_factory_.GetWeakPtr(), action_data.request_id));
+ AccessibilityHitTest(action_data.target_point,
+ action_data.hit_test_event_to_fire,
+ action_data.request_id, {});
return;
}
+ // Set the input modality in RenderWidgetHostViewAura to touch so the
+ // VK shows up.
+ if (action_data.action == ax::mojom::Action::kDoDefault) {
+ RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
+ render_view_host_->GetWidget()->GetView());
+ if (view)
+ view->SetLastPointerType(ui::EventPointerType::kTouch);
+ }
render_accessibility_->PerformAction(action_data);
}
bool RenderFrameHostImpl::AccessibilityViewHasFocus() {
- if (!is_active())
- return false;
-
RenderWidgetHostView* view = render_view_host_->GetWidget()->GetView();
if (view)
return view->HasFocus();
@@ -1695,7 +1795,8 @@ bool RenderFrameHostImpl::AccessibilityViewHasFocus() {
}
void RenderFrameHostImpl::AccessibilityViewSetFocus() {
- if (!is_active())
+ // Don't update Accessibility for inactive frames.
+ if (IsInactiveAndDisallowReactivation())
return;
RenderWidgetHostView* view = render_view_host_->GetWidget()->GetView();
@@ -1704,9 +1805,6 @@ void RenderFrameHostImpl::AccessibilityViewSetFocus() {
}
gfx::Rect RenderFrameHostImpl::AccessibilityGetViewBounds() {
- if (!is_active())
- return gfx::Rect();
-
RenderWidgetHostView* view = render_view_host_->GetWidget()->GetView();
if (view)
return view->GetViewBounds();
@@ -1714,9 +1812,6 @@ gfx::Rect RenderFrameHostImpl::AccessibilityGetViewBounds() {
}
float RenderFrameHostImpl::AccessibilityGetDeviceScaleFactor() {
- if (!is_active())
- return 1.0f;
-
RenderWidgetHostView* view = render_view_host_->GetWidget()->GetView();
if (view)
return GetScaleFactorForView(view);
@@ -1749,7 +1844,7 @@ RenderFrameHostImpl::AccessibilityGetAcceleratedWidget() {
// Only the main frame's current frame host is connected to the native
// widget tree for accessibility, so return null if this is queried on
// any other frame.
- if (!is_active() || !is_main_frame() || !IsCurrent())
+ if (!is_main_frame() || !IsCurrent())
return gfx::kNullAcceleratedWidget;
RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
@@ -1761,7 +1856,10 @@ RenderFrameHostImpl::AccessibilityGetAcceleratedWidget() {
gfx::NativeViewAccessible
RenderFrameHostImpl::AccessibilityGetNativeViewAccessible() {
- if (!is_active())
+ // If this method is called when the document is in BackForwardCache, evict
+ // the document to avoid ignoring any accessibility related events which the
+ // document might not expect.
+ if (IsInactiveAndDisallowReactivation())
return nullptr;
RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
@@ -1773,7 +1871,10 @@ RenderFrameHostImpl::AccessibilityGetNativeViewAccessible() {
gfx::NativeViewAccessible
RenderFrameHostImpl::AccessibilityGetNativeViewAccessibleForWindow() {
- if (!is_active())
+ // If this method is called when the frame is in BackForwardCache, evict
+ // the frame to avoid ignoring any accessibility related events which are not
+ // expected.
+ if (IsInactiveAndDisallowReactivation())
return nullptr;
RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
@@ -1784,15 +1885,38 @@ RenderFrameHostImpl::AccessibilityGetNativeViewAccessibleForWindow() {
}
WebContents* RenderFrameHostImpl::AccessibilityWebContents() {
- if (!is_active())
+ // If this method is called when the frame is in BackForwardCache, evict
+ // the frame to avoid ignoring any accessibility related events which are not
+ // expected.
+ if (IsInactiveAndDisallowReactivation())
return nullptr;
return delegate()->GetAsWebContents();
}
+void RenderFrameHostImpl::AccessibilityHitTest(
+ const gfx::Point& point_in_frame_pixels,
+ ax::mojom::Event opt_event_to_fire,
+ int opt_request_id,
+ base::OnceCallback<void(BrowserAccessibilityManager* hit_manager,
+ int hit_node_id)> opt_callback) {
+ // This is called by BrowserAccessibilityManager. During teardown it's
+ // possible that render_accessibility_ is null but the corresponding
+ // BrowserAccessibilityManager still exists and could call this.
+ if (IsInactiveAndDisallowReactivation() || !render_accessibility_) {
+ if (opt_callback)
+ std::move(opt_callback).Run(nullptr, 0);
+ return;
+ }
+
+ render_accessibility_->HitTest(
+ point_in_frame_pixels, opt_event_to_fire, opt_request_id,
+ base::BindOnce(&RenderFrameHostImpl::AccessibilityHitTestCallback,
+ weak_ptr_factory_.GetWeakPtr(), opt_request_id,
+ opt_event_to_fire, std::move(opt_callback)));
+}
+
bool RenderFrameHostImpl::AccessibilityIsMainFrame() {
- if (!is_active())
- return false;
- return frame_tree_node()->IsMainFrame();
+ return is_main_frame();
}
void RenderFrameHostImpl::RenderProcessExited(
@@ -1886,7 +2010,8 @@ void RenderFrameHostImpl::RenderProcessGone(
// process should be ignored until the next commit.
set_nav_entry_id(0);
- OnAudibleStateChanged(false);
+ if (is_audible_)
+ OnAudibleStateChanged(false);
}
void RenderFrameHostImpl::ReportContentSecurityPolicyViolation(
@@ -1954,16 +2079,11 @@ bool RenderFrameHostImpl::SchemeShouldBypassCSP(
return base::Contains(bypassing_schemes, scheme);
}
-mojom::FrameInputHandler* RenderFrameHostImpl::GetFrameInputHandler() {
- if (!frame_input_handler_)
- return nullptr;
- return frame_input_handler_.get();
-}
-
-bool RenderFrameHostImpl::CreateRenderFrame(int previous_routing_id,
- int opener_routing_id,
- int parent_routing_id,
- int previous_sibling_routing_id) {
+bool RenderFrameHostImpl::CreateRenderFrame(
+ int previous_routing_id,
+ const base::Optional<base::UnguessableToken>& opener_frame_token,
+ int parent_routing_id,
+ int previous_sibling_routing_id) {
TRACE_EVENT0("navigation", "RenderFrameHostImpl::CreateRenderFrame");
DCHECK(!IsRenderFrameLive()) << "Creating frame twice";
@@ -1992,7 +2112,7 @@ bool RenderFrameHostImpl::CreateRenderFrame(int previous_routing_id,
params->routing_id = routing_id_;
params->previous_routing_id = previous_routing_id;
- params->opener_routing_id = opener_routing_id;
+ params->opener_frame_token = opener_frame_token;
params->parent_routing_id = parent_routing_id;
params->previous_sibling_routing_id = previous_sibling_routing_id;
params->replication_state = frame_tree_node()->current_replication_state();
@@ -2013,7 +2133,7 @@ bool RenderFrameHostImpl::CreateRenderFrame(int previous_routing_id,
NavigationRequest* navigation_request =
frame_tree_node()->navigation_request();
if (navigation_request &&
- navigation_request->require_coop_browsing_instance_swap()) {
+ navigation_request->coop_status().require_browsing_instance_swap) {
params->replication_state.name = "";
// "COOP swaps" only affect main frames, that have an empty unique name.
DCHECK(params->replication_state.unique_name.empty());
@@ -2053,12 +2173,10 @@ bool RenderFrameHostImpl::CreateRenderFrame(int previous_routing_id,
rwh->BindNewFrameWidgetInterfaces();
}
- // TODO(https://crbug.com/1006814): Remove this.
- if (params->previous_routing_id == MSG_ROUTING_NONE &&
- params->parent_routing_id == MSG_ROUTING_NONE) {
- base::debug::DumpWithoutCrashing();
- NOTREACHED();
- }
+ // https://crbug.com/1006814. The renderer needs at least one of these IDs to
+ // be able to insert the new frame in the frame tree.
+ DCHECK(params->previous_routing_id != MSG_ROUTING_NONE ||
+ params->parent_routing_id != MSG_ROUTING_NONE);
GetProcess()->GetRendererInterface()->CreateFrame(std::move(params));
if (previous_routing_id != MSG_ROUTING_NONE) {
@@ -2124,6 +2242,22 @@ void RenderFrameHostImpl::SetRenderFrameCreated(bool created) {
bool was_created = render_frame_created_;
render_frame_created_ = created;
+ // Clear all the user data associated with this RenderFrameHost when its
+ // RenderFrame is recreated after a crash. Checking
+ // |was_render_frame_ever_created_| guarantees that the user data isn't
+ // cleared for the initial RenderFrame creation. Note that the user data is
+ // intentionally not cleared at the time of crash. Please refer to
+ // https://crbug.com/1099237 for more details.
+ //
+ // Clearing of user data should be called before RenderFrameCreated to ensure:
+ // - a) new new state set in RenderFrameCreated doesn't get deleted.
+ // - b) the old state is not leaked to a new RenderFrameHost.
+ if (!was_created && created && was_render_frame_ever_created_)
+ document_associated_data_.ClearAllUserData();
+
+ if (created)
+ was_render_frame_ever_created_ = true;
+
// If the current status is different than the new status, the delegate
// needs to be notified.
if (created != was_created) {
@@ -2139,12 +2273,6 @@ void RenderFrameHostImpl::SetRenderFrameCreated(bool created) {
CHECK(frame_);
if (created && GetLocalRenderWidgetHost()) {
- mojo::PendingRemote<mojom::Widget> widget;
- GetRemoteInterfaces()->GetInterface(
- widget.InitWithNewPipeAndPassReceiver());
- GetLocalRenderWidgetHost()->SetWidget(std::move(widget));
- GetLocalRenderWidgetHost()->SetFrameInputHandler(
- frame_input_handler_.get());
GetLocalRenderWidgetHost()->input_router()->SetFrameTreeNodeId(
frame_tree_node_->frame_tree_node_id());
mojo::Remote<viz::mojom::InputTargetClient> input_target_client;
@@ -2161,11 +2289,6 @@ void RenderFrameHostImpl::SetRenderFrameCreated(bool created) {
GetRemoteAssociatedInterfaces()->GetInterface(&frame_bindings_control_);
frame_bindings_control_->AllowBindings(enabled_bindings_);
}
-
- // Clear all the user data associated with this RenderFrameHost in case if
- // the renderer crashes and the RenderFrameHost still stays alive.
- if (!created)
- document_associated_data_.ClearAllUserData();
}
void RenderFrameHostImpl::SwapIn() {
@@ -2179,7 +2302,7 @@ void RenderFrameHostImpl::Init() {
waiting_for_init_ = false;
if (pending_navigate_) {
- frame_tree_node()->navigator()->OnBeginNavigation(
+ frame_tree_node()->navigator().OnBeginNavigation(
frame_tree_node(), std::move(pending_navigate_->common_params),
std::move(pending_navigate_->begin_navigation_params),
std::move(pending_navigate_->blob_url_loader_factory),
@@ -2192,13 +2315,14 @@ void RenderFrameHostImpl::Init() {
}
void RenderFrameHostImpl::OnAudibleStateChanged(bool is_audible) {
- if (is_audible_ == is_audible)
- return;
- if (is_audible)
+ DCHECK_NE(is_audible_, is_audible);
+ if (is_audible) {
GetProcess()->OnMediaStreamAdded();
- else
+ } else {
GetProcess()->OnMediaStreamRemoved();
+ }
is_audible_ = is_audible;
+ delegate_->OnFrameAudioStateChanged(this, is_audible_);
}
void RenderFrameHostImpl::DidAddMessageToConsole(
@@ -2252,10 +2376,10 @@ void RenderFrameHostImpl::OnCreateChildFrame(
}
// The RenderFrame corresponding to this host sent an IPC message to create a
- // child, but by the time we get here, it's possible for the host to have been
- // swapped out, or for its process to have disconnected (maybe due to browser
- // shutdown). Ignore such messages.
- if (!is_active() || !IsCurrent() || !render_frame_created_)
+ // child, but by the time we get here, it's possible for the RenderFrameHost
+ // to become pending deletion, or for its process to have disconnected (maybe
+ // due to browser shutdown). Ignore such messages.
+ if (IsInactiveAndDisallowReactivation() || !render_frame_created_)
return;
// |new_routing_id|, |new_interface_provider_provider_receiver|,
@@ -2391,7 +2515,7 @@ net::IsolationInfo RenderFrameHostImpl::ComputeIsolationInfoInternal(
frame_origin, candidate_site_for_cookies);
}
-void RenderFrameHostImpl::SetOriginAndIsolationInfoOfNewFrame(
+void RenderFrameHostImpl::SetOriginDependentStateOfNewFrame(
const url::Origin& new_frame_creator) {
// This method should only be called for *new* frames, that haven't committed
// a navigation yet.
@@ -2410,6 +2534,13 @@ void RenderFrameHostImpl::SetOriginAndIsolationInfoOfNewFrame(
isolation_info_ = ComputeIsolationInfoInternal(
new_frame_origin, net::IsolationInfo::RedirectMode::kUpdateNothing);
SetLastCommittedOrigin(new_frame_origin);
+
+ // Construct the frame's feature policy only once we know its initial
+ // committed origin. It's necessary to wait for the origin because the feature
+ // policy's state depends on the origin, so the FeaturePolicy object could be
+ // configured incorrectly if it were initialized before knowing the value of
+ // |last_committed_origin_|. More at crbug.com/1112959.
+ ResetFeaturePolicy();
}
FrameTreeNode* RenderFrameHostImpl::AddChild(
@@ -2435,7 +2566,7 @@ FrameTreeNode* RenderFrameHostImpl::AddChild(
// When the child is added, it hasn't committed any navigation yet - its
// initial empty document should inherit the origin of its parent (the origin
// may change after the first commit). See also https://crbug.com/932067.
- child->current_frame_host()->SetOriginAndIsolationInfoOfNewFrame(
+ child->current_frame_host()->SetOriginDependentStateOfNewFrame(
GetLastCommittedOrigin());
children_.push_back(std::move(child));
@@ -2508,6 +2639,11 @@ void RenderFrameHostImpl::OnDetach() {
return;
}
+ // Ignore FrameHostMsg_Detach IPC message, if the RenderFrameHost should be
+ // left in pending deletion state.
+ if (do_not_delete_for_testing_)
+ return;
+
if (IsPendingDeletion()) {
// The frame is pending deletion. FrameHostMsg_Detach is used to confirm
// its unload handlers ran. Note that it is possible for a frame to already
@@ -2529,7 +2665,7 @@ void RenderFrameHostImpl::OnDetach() {
// descendant frames to execute unload handlers. Start executing those
// handlers now.
StartPendingDeletionOnSubtree();
- frame_tree_node_->frame_tree()->FrameUnloading(frame_tree_node_);
+ frame_tree()->FrameUnloading(frame_tree_node_);
// Some children with no unload handler may be eligible for immediate
// deletion. Cut the dead branches now. This is a performance optimization.
@@ -2545,12 +2681,17 @@ void RenderFrameHostImpl::DidFailLoadWithError(const GURL& url,
GURL validated_url(url);
GetProcess()->FilterURL(false, &validated_url);
- frame_tree_node_->navigator()->DidFailLoadWithError(this, validated_url,
- error_code);
+ frame_tree_node_->navigator().DidFailLoadWithError(this, validated_url,
+ error_code);
}
void RenderFrameHostImpl::DidFocusFrame() {
- if (!is_active())
+ // We don't handle this IPC signal for non-active RenderFrameHost.
+ //
+ // For RenderFrameHost in BackForwardCache, it is safe to ignore this IPC as
+ // there is a renderer side check (see Document::IsFocusedAllowed) which
+ // returns false.
+ if (lifecycle_state_ != LifecycleState::kActive)
return;
// We need to handle receiving this IPC from a frame that is inside a portal
@@ -2565,6 +2706,10 @@ void RenderFrameHostImpl::DidFocusFrame() {
delegate_->SetFocusedFrame(frame_tree_node_, GetSiteInstance());
}
+void RenderFrameHostImpl::DidCallFocus() {
+ delegate_->DidCallFocus();
+}
+
void RenderFrameHostImpl::DidAddContentSecurityPolicies(
std::vector<network::mojom::ContentSecurityPolicyPtr> policies) {
TRACE_EVENT1("navigation",
@@ -2579,27 +2724,6 @@ void RenderFrameHostImpl::DidAddContentSecurityPolicies(
frame_tree_node()->AddContentSecurityPolicies(std::move(headers));
}
-void RenderFrameHostImpl::OnOpenURL(const FrameHostMsg_OpenURL_Params& params) {
- // Verify and unpack IPC payload.
- GURL validated_url;
- scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory;
- if (!VerifyOpenURLParams(GetSiteInstance(), params, &validated_url,
- &blob_url_loader_factory)) {
- return;
- }
-
- TRACE_EVENT1("navigation", "RenderFrameHostImpl::OpenURL", "url",
- validated_url.possibly_invalid_spec());
-
- frame_tree_node_->navigator()->RequestOpenURL(
- this, validated_url,
- GlobalFrameRoutingId(GetProcess()->GetID(), params.initiator_routing_id),
- params.initiator_origin, params.post_body, params.extra_headers,
- params.referrer, params.disposition, params.should_replace_current_entry,
- params.user_gesture, params.triggering_event_info, params.href_translate,
- std::move(blob_url_loader_factory), params.impression);
-}
-
void RenderFrameHostImpl::CancelInitialHistoryLoad() {
// A Javascript navigation interrupted the initial history load. Check if an
// initial subframe cross-process navigation needs to be canceled as a result.
@@ -2617,13 +2741,14 @@ void RenderFrameHostImpl::DidChangeActiveSchedulerTrackedFeatures(
void RenderFrameHostImpl::OnSchedulerTrackedFeatureUsed(
blink::scheduler::WebSchedulerTrackedFeature feature) {
browser_reported_scheduler_tracked_features_ |=
- 1 << static_cast<uint64_t>(feature);
+ 1ull << static_cast<uint64_t>(feature);
MaybeEvictFromBackForwardCache();
}
bool RenderFrameHostImpl::IsFrozen() {
- return frame_lifecycle_state_ != blink::mojom::FrameLifecycleState::kRunning;
+ // TODO(crbug.com/1081920): Account for non-bfcache freezing here as well.
+ return lifecycle_state_ == LifecycleState::kInBackForwardCache;
}
void RenderFrameHostImpl::DidCommitProvisionalLoad(
@@ -2663,9 +2788,17 @@ void RenderFrameHostImpl::DidCommitBackForwardCacheNavigation(
}
void RenderFrameHostImpl::SetEmbeddingToken(
- const base::Optional<base::UnguessableToken>& embedding_token) {
+ const base::UnguessableToken& embedding_token) {
embedding_token_ = embedding_token;
- if (!embedding_token_.has_value())
+
+ // We only need to propagate the token to the parent frame if it's
+ // remote. For local parents the propagation occurs within the renderer
+ // process. The token is also present on the main frame for generalization
+ // when the main frame in embedded in another context (e.g. browser UI).
+ // The main frame is not embedded in the context of the frame tree so it
+ // is not propagated here. See RenderFrameHost::GetEmbeddingToken for more
+ // details.
+ if (!IsCrossProcessSubframe())
return;
// Only non-null tokens are propagated to the parent document. The token is
@@ -2703,8 +2836,8 @@ void RenderFrameHostImpl::DidCommitPerNavigationMojoInterfaceNavigation(
void RenderFrameHostImpl::DidCommitSameDocumentNavigation(
std::unique_ptr<FrameHostMsg_DidCommitProvisionalLoad_Params> params) {
- ScopedActiveURL scoped_active_url(
- params->url, frame_tree_node()->frame_tree()->root()->current_origin());
+ ScopedActiveURL scoped_active_url(params->url,
+ frame_tree()->root()->current_origin());
ScopedCommitStateResetter commit_state_resetter(this);
// When the frame is pending deletion, the browser is waiting for it to unload
@@ -2715,7 +2848,10 @@ void RenderFrameHostImpl::DidCommitSameDocumentNavigation(
// See https://crbug.com/805705 and https://crbug.com/930132.
// TODO(ahemery): Investigate to see if this can be removed when the
// NavigationClient interface is implemented.
- if (!is_active())
+ // If this is called when the frame is in BackForwardCache, evict the frame
+ // to avoid ignoring the renderer-initiated navigation, which the frame
+ // might not expect.
+ if (IsInactiveAndDisallowReactivation())
return;
TRACE_EVENT2("navigation",
@@ -2740,20 +2876,6 @@ void RenderFrameHostImpl::DidCommitSameDocumentNavigation(
commit_state_resetter.disable();
}
-void RenderFrameHostImpl::OnUpdateState(const PageState& state) {
- // TODO(creis): Verify the state's ISN matches the last committed FNE.
-
- // Without this check, the renderer can trick the browser into using
- // filenames it can't access in a future session restore.
- if (!CanAccessFilesOfPageState(state)) {
- bad_message::ReceivedBadMessage(
- GetProcess(), bad_message::RFH_CAN_ACCESS_FILES_OF_PAGE_STATE);
- return;
- }
-
- delegate_->UpdateStateForFrame(this, state);
-}
-
RenderWidgetHostImpl* RenderFrameHostImpl::GetRenderWidgetHost() {
RenderFrameHostImpl* frame = this;
while (frame) {
@@ -2864,7 +2986,7 @@ void RenderFrameHostImpl::DetachFromProxy() {
// Start pending deletion on this frame and its children.
DeleteRenderFrame(FrameDeleteIntention::kNotMainFrame);
StartPendingDeletionOnSubtree();
- frame_tree_node_->frame_tree()->FrameUnloading(frame_tree_node_);
+ frame_tree()->FrameUnloading(frame_tree_node_);
// Some children with no unload handler may be eligible for immediate
// deletion. Cut the dead branches now. This is a performance optimization.
@@ -2960,7 +3082,7 @@ void RenderFrameHostImpl::ProcessBeforeUnloadCompletedFromFrame(
UMA_HISTOGRAM_TIMES("Navigation.OnBeforeUnloadOverheadTime",
on_before_unload_overhead_time);
- frame_tree_node_->navigator()->LogBeforeUnloadTime(
+ frame_tree_node_->navigator().LogBeforeUnloadTime(
renderer_before_unload_start_time, renderer_before_unload_end_time);
}
@@ -2975,7 +3097,7 @@ void RenderFrameHostImpl::ProcessBeforeUnloadCompletedFromFrame(
// current navigation stop/proceed. Otherwise, send it to the
// RenderFrameHostManager which handles closing.
if (unload_ack_is_for_navigation_) {
- frame_tree_node_->navigator()->BeforeUnloadCompleted(
+ frame_tree_node_->navigator().BeforeUnloadCompleted(
frame_tree_node_, proceed, before_unload_end_time);
} else {
// We could reach this from a subframe destructor for |frame| while we're
@@ -3066,7 +3188,7 @@ void RenderFrameHostImpl::SetSubframeUnloadTimeoutForTesting(
void RenderFrameHostImpl::OnContextMenu(
const UntrustworthyContextMenuParams& params) {
- if (!is_active())
+ if (IsInactiveAndDisallowReactivation())
return;
// Validate the URLs in |params|. If the renderer can't request the URLs
@@ -3178,9 +3300,10 @@ void RenderFrameHostImpl::RunJavaScriptDialog(
const base::string16& default_prompt,
JavaScriptDialogType dialog_type,
JavaScriptDialogCallback ipc_response_callback) {
- // Don't show the dialog if it's triggered on a frame that's pending deletion
- // (e.g., from an unload handler), or when the tab is being closed.
- if (!is_active()) {
+ // Don't show the dialog if it's triggered on a non-active RenderFrameHost.
+ // This happens when the RenderFrameHost is pending deletion or in the
+ // back-forward cache.
+ if (lifecycle_state_ != LifecycleState::kActive) {
std::move(ipc_response_callback).Run(true, base::string16());
return;
}
@@ -3261,13 +3384,6 @@ void RenderFrameHostImpl::RunBeforeUnloadConfirm(
std::move(dialog_closed_callback));
}
-void RenderFrameHostImpl::Are3DAPIsBlocked(Are3DAPIsBlockedCallback callback) {
- bool blocked = GpuDataManagerImpl::GetInstance()->Are3DAPIsBlocked(
- GetMainFrame()->GetLastCommittedURL(), GetProcess()->GetID(),
- GetRoutingID(), THREE_D_API_TYPE_WEBGL);
- std::move(callback).Run(blocked);
-}
-
void RenderFrameHostImpl::ScaleFactorChanged(float scale) {
delegate_->OnPageScaleFactorChanged(this, scale);
}
@@ -3282,6 +3398,10 @@ void RenderFrameHostImpl::TextAutosizerPageInfoChanged(
delegate_->OnTextAutosizerPageInfoChanged(this, std::move(page_info));
}
+void RenderFrameHostImpl::FocusPage() {
+ render_view_host_->OnFocus();
+}
+
void RenderFrameHostImpl::UpdateFaviconURL(
std::vector<blink::mojom::FaviconURLPtr> favicon_urls) {
delegate_->UpdateFaviconURL(this, std::move(favicon_urls));
@@ -3289,14 +3409,9 @@ void RenderFrameHostImpl::UpdateFaviconURL(
void RenderFrameHostImpl::DownloadURL(
blink::mojom::DownloadURLParamsPtr blink_parameters) {
- mojo::PendingRemote<blink::mojom::BlobURLToken> blob_url_token;
- if (!VerifyDownloadUrlParams(GetSiteInstance(), blink_parameters.get(),
- &blob_url_token))
+ if (!VerifyDownloadUrlParams(GetSiteInstance(), *blink_parameters))
return;
- mojo::PendingRemote<blink::mojom::Blob> blob_data_remote(
- std::move(blink_parameters->data_url_blob), blink::mojom::Blob::Version_);
-
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("renderer_initiated_download", R"(
semantics {
@@ -3342,14 +3457,15 @@ void RenderFrameHostImpl::DownloadURL(
blink_parameters->initiator_origin.value_or(url::Origin()));
parameters->set_download_source(download::DownloadSource::FROM_RENDERER);
- if (blob_data_remote) {
+ if (blink_parameters->data_url_blob) {
DataURLBlobReader::ReadDataURLFromBlob(
- std::move(blob_data_remote),
+ std::move(blink_parameters->data_url_blob),
base::BindOnce(&OnDataURLRetrieved, std::move(parameters)));
return;
}
- StartDownload(std::move(parameters), std::move(blob_url_token));
+ StartDownload(std::move(parameters),
+ std::move(blink_parameters->blob_url_token));
}
void RenderFrameHostImpl::ReportNoBinderForInterface(const std::string& error) {
@@ -3369,6 +3485,16 @@ BrowserContext* RenderFrameHostImpl::GetBrowserContext() {
return GetProcess()->GetBrowserContext();
}
+// TODO(crbug.com/1091720): Would be better to do this directly in the chrome
+// layer. See referenced bug for further details.
+void RenderFrameHostImpl::ReportHeavyAdIssue(
+ blink::mojom::HeavyAdResolutionStatus resolution,
+ blink::mojom::HeavyAdReason reason) {
+ auto issue =
+ devtools_instrumentation::GetHeavyAdIssue(this, resolution, reason);
+ devtools_instrumentation::ReportBrowserInitiatedIssue(this, issue.get());
+}
+
StoragePartition* RenderFrameHostImpl::GetStoragePartition() {
return BrowserContext::GetStoragePartition(GetBrowserContext(),
GetSiteInstance());
@@ -3489,6 +3615,10 @@ void RenderFrameHostImpl::DoNotDeleteForTesting() {
do_not_delete_for_testing_ = true;
}
+void RenderFrameHostImpl::ResumeDeletionForTesting() {
+ do_not_delete_for_testing_ = false;
+}
+
bool RenderFrameHostImpl::IsFeatureEnabled(
blink::mojom::FeaturePolicyFeature feature) {
return feature_policy_ && feature_policy_->IsFeatureEnabledForOrigin(
@@ -3555,7 +3685,8 @@ void RenderFrameHostImpl::UpdateSubresourceLoaderFactories() {
CreateURLLoaderFactoryParamsForMainWorld(
last_committed_origin_,
mojo::Clone(last_committed_client_security_state_),
- std::move(coep_reporter_remote)),
+ std::move(coep_reporter_remote),
+ DetermineAfterCommitWhetherToForbidTrustTokenRedemption(this)),
default_factory_remote.InitWithNewPipeAndPassReceiver());
}
@@ -3567,7 +3698,9 @@ void RenderFrameHostImpl::UpdateSubresourceLoaderFactories() {
CreateURLLoaderFactoriesForIsolatedWorlds(
GetExpectedMainWorldOriginForUrlLoaderFactory(),
isolated_worlds_requiring_separate_url_loader_factory_,
- mojo::Clone(last_committed_client_security_state_)),
+ mojo::Clone(last_committed_client_security_state_),
+ DetermineAfterCommitWhetherToForbidTrustTokenRedemption(
+ this)),
bypass_redirect_checks);
GetNavigationControl()->UpdateSubresourceLoaderFactories(
std::move(subresource_loader_factories));
@@ -3586,11 +3719,6 @@ void RenderFrameHostImpl::DidAccessInitialDocument() {
delegate_->DidAccessInitialDocument();
}
-void RenderFrameHostImpl::OnDidChangeOpener(int32_t opener_routing_id) {
- frame_tree_node_->render_manager()->DidChangeOpener(opener_routing_id,
- GetSiteInstance());
-}
-
void RenderFrameHostImpl::DidChangeName(const std::string& name,
const std::string& unique_name) {
if (GetParent() != nullptr) {
@@ -3612,8 +3740,22 @@ void RenderFrameHostImpl::DidSetFramePolicyHeaders(
network::mojom::WebSandboxFlags sandbox_flags,
const blink::ParsedFeaturePolicy& feature_policy_header,
const blink::DocumentPolicy::FeatureState& document_policy_header) {
- if (!is_active())
+ // TODO(https://crbug.com/1093268): Investigate why this IPC can be received
+ // before the navigation commit. This can be triggered when loading an error
+ // page using the test:
+ // CrossOriginOpenerPolicyBrowserTest.NetworkErrorOnSandboxedPopups.
+ if (lifecycle_state() == LifecycleState::kSpeculative)
+ return;
+
+ // We should not be updating policy headers when the RenderFrameHost is in
+ // BackForwardCache. If this is called when the RenderFrameHost is in
+ // BackForwardCache, evict the document.
+ if (IsInactiveAndDisallowReactivation())
return;
+
+ // We shouldn't update policy headers for non-current frames.
+ DCHECK(IsCurrent());
+
// Rebuild |feature_policy_| for this frame.
ResetFeaturePolicy();
feature_policy_->SetHeaderPolicy(feature_policy_header);
@@ -3651,6 +3793,8 @@ void RenderFrameHostImpl::DidSetFramePolicyHeaders(
// Save a copy of the now-active sandbox flags on this RFHI.
active_sandbox_flags_ = frame_tree_node()->active_sandbox_flags();
+
+ CheckSandboxFlags();
}
void RenderFrameHostImpl::EnforceInsecureRequestPolicy(
@@ -3687,8 +3831,7 @@ RenderFrameHostImpl* RenderFrameHostImpl::FindAndVerifyChildInternal(
if (!child_frame_or_proxy)
return nullptr;
- if (child_frame_or_proxy.GetFrameTreeNode()->frame_tree() !=
- frame_tree_node()->frame_tree()) {
+ if (child_frame_or_proxy.GetFrameTreeNode()->frame_tree() != frame_tree()) {
// Ignore the cases when the child lives in a different frame tree.
// This is possible when we create a proxy for inner WebContents (e.g.
// for portals) so the |child_frame_or_proxy| points to the root frame
@@ -3707,30 +3850,6 @@ RenderFrameHostImpl* RenderFrameHostImpl::FindAndVerifyChildInternal(
: child_frame_or_proxy.frame;
}
-void RenderFrameHostImpl::OnDidChangeFramePolicy(
- int32_t frame_routing_id,
- const blink::FramePolicy& frame_policy) {
- // Ensure that a frame can only update sandbox flags or feature policy for its
- // immediate children. If this is not the case, the renderer is considered
- // malicious and is killed.
- RenderFrameHostImpl* child = FindAndVerifyChild(
- // TODO(iclelland): Rename this message
- frame_routing_id, bad_message::RFH_SANDBOX_FLAGS);
- if (!child)
- return;
-
- child->frame_tree_node()->SetPendingFramePolicy(frame_policy);
-
- // Notify the RenderFrame if it lives in a different process from its parent.
- // The frame's proxies in other processes also need to learn about the updated
- // flags and policy, but these notifications are sent later in
- // RenderFrameHostManager::CommitPendingFramePolicy(), when the frame
- // navigates and the new policies take effect.
- if (child->GetSiteInstance() != GetSiteInstance()) {
- child->GetAssociatedLocalFrame()->DidUpdateFramePolicy(frame_policy);
- }
-}
-
void RenderFrameHostImpl::UpdateTitle(
const base::Optional<::base::string16>& title,
base::i18n::TextDirection title_direction) {
@@ -3767,7 +3886,7 @@ void RenderFrameHostImpl::FrameSizeChanged(const gfx::Size& frame_size) {
}
void RenderFrameHostImpl::FullscreenStateChanged(bool is_fullscreen) {
- if (!is_active())
+ if (IsInactiveAndDisallowReactivation())
return;
delegate_->FullscreenStateChanged(this, is_fullscreen);
}
@@ -3815,9 +3934,9 @@ void RenderFrameHostImpl::DocumentAvailableInMainFrame(
}
void RenderFrameHostImpl::SetNeedsOcclusionTracking(bool needs_tracking) {
- // Don't process the IPC if this RFH is pending deletion. See also
+ // Do not update the parent on behalf of inactive RenderFrameHost. See also
// https://crbug.com/972566.
- if (!is_active())
+ if (IsInactiveAndDisallowReactivation())
return;
RenderFrameProxyHost* proxy =
@@ -3831,9 +3950,63 @@ void RenderFrameHostImpl::SetNeedsOcclusionTracking(bool needs_tracking) {
proxy->GetAssociatedRemoteFrame()->SetNeedsOcclusionTracking(needs_tracking);
}
-void RenderFrameHostImpl::LifecycleStateChanged(
- blink::mojom::FrameLifecycleState state) {
- frame_lifecycle_state_ = state;
+void RenderFrameHostImpl::SetVirtualKeyboardOverlayPolicy(
+ bool vk_overlays_content) {
+ should_virtual_keyboard_overlay_content_ = vk_overlays_content;
+}
+
+bool RenderFrameHostImpl::ShouldVirtualKeyboardOverlayContent() const {
+ RenderFrameHostImpl* root_frame_host =
+ frame_tree_->root()->current_frame_host();
+ return root_frame_host->should_virtual_keyboard_overlay_content_;
+}
+
+void RenderFrameHostImpl::NotifyVirtualKeyboardOverlayRect(
+ const gfx::Rect& keyboard_rect) {
+ DCHECK(ShouldVirtualKeyboardOverlayContent());
+
+ RenderFrameHostImpl* root_frame_host =
+ frame_tree_->root()->current_frame_host();
+ RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
+ root_frame_host->render_view_host_->GetWidget()->GetView());
+ if (!view)
+ return;
+
+ gfx::PointF root_widget_origin(0.f, 0.f);
+ view->TransformPointToRootSurface(&root_widget_origin);
+
+ gfx::Rect root_widget_rect;
+ if (!keyboard_rect.IsEmpty()) {
+ // If the rect is non-empty, we need to transform it to be widget-relative
+ // window (DIP coordinates). The input is client coordinates for the root
+ // window.
+ // Transform the widget rect origin to root relative coords.
+ root_widget_rect = gfx::Rect(root_widget_origin.x(), root_widget_origin.y(),
+ view->GetViewBounds().width(),
+ view->GetViewBounds().height());
+
+ // Intersect with the keyboard rect and transform back to widget-relative
+ // coordinates, which will be sent to the renderer.
+ root_widget_rect.Intersect(keyboard_rect);
+ root_widget_rect.Offset(-root_widget_origin.x(), -root_widget_origin.y());
+ }
+
+ // Notify each SiteInstance a single time. Renderer will take care of ensuring
+ // the event is dispatched to all relevant listeners in the grouping of frames
+ // for the SiteInstance.
+ // TODO(snianu): Transform from the main frame's coordinates to each
+ // individual frame client coordinates so that these are more usable from
+ // within iframes.
+ std::set<SiteInstance*> notified_instances;
+ for (RenderFrameHostImpl* node = this; node; node = node->GetParent()) {
+ SiteInstance* site_instance = node->GetSiteInstance();
+ if (base::Contains(notified_instances, site_instance))
+ continue;
+
+ node->GetAssociatedLocalFrame()->NotifyVirtualKeyboardOverlayRect(
+ root_widget_rect);
+ notified_instances.insert(site_instance);
+ }
}
#if defined(OS_ANDROID)
@@ -3845,7 +4018,6 @@ void RenderFrameHostImpl::UpdateUserGestureCarryoverInfo() {
void RenderFrameHostImpl::VisibilityChanged(
blink::mojom::FrameVisibility visibility) {
visibility_ = visibility;
- UpdateFrameFrozenState();
}
void RenderFrameHostImpl::DidChangeThemeColor(
@@ -3880,10 +4052,11 @@ void RenderFrameHostImpl::DispatchLoad() {
TRACE_EVENT1("navigation", "RenderFrameHostImpl::DispatchLoad",
"frame_tree_node", frame_tree_node_->frame_tree_node_id());
- // Don't forward the load event if this RFH is pending deletion. This can
- // happen in a race where this RenderFrameHost finishes loading just after
- // the frame navigates away. See https://crbug.com/626802.
- if (!is_active())
+ // Don't forward the load event to the parent on behalf of inactive
+ // RenderFrameHost. This can happen in a race where this inactive
+ // RenderFrameHost finishes loading just after the frame navigates away.
+ // See https://crbug.com/626802.
+ if (IsInactiveAndDisallowReactivation())
return;
// We should never be receiving this message from a speculative RFH.
@@ -3909,8 +4082,14 @@ void RenderFrameHostImpl::GoToEntryAtOffset(int32_t offset,
void RenderFrameHostImpl::HandleAccessibilityFindInPageResult(
blink::mojom::FindInPageResultAXParamsPtr params) {
+ // Only update FindInPageResult on active RenderFrameHost. Note that, it is
+ // safe to ignore this call for BackForwardCache, as we terminate the
+ // FindInPage session once the page enters BackForwardCache.
+ if (lifecycle_state_ != LifecycleState::kActive)
+ return;
+
ui::AXMode accessibility_mode = delegate_->GetAccessibilityMode();
- if (accessibility_mode.has_mode(ui::AXMode::kNativeAPIs) && is_active()) {
+ if (accessibility_mode.has_mode(ui::AXMode::kNativeAPIs)) {
BrowserAccessibilityManager* manager =
GetOrCreateBrowserAccessibilityManager();
if (manager) {
@@ -3922,8 +4101,14 @@ void RenderFrameHostImpl::HandleAccessibilityFindInPageResult(
}
void RenderFrameHostImpl::HandleAccessibilityFindInPageTermination() {
+ // Only update FindInPageTermination on active RenderFrameHost. Note that, it
+ // is safe to ignore this call for BackForwardCache, as we terminate the
+ // FindInPage session once the page enters BackForwardCache.
+ if (lifecycle_state_ != LifecycleState::kActive)
+ return;
+
ui::AXMode accessibility_mode = delegate_->GetAccessibilityMode();
- if (accessibility_mode.has_mode(ui::AXMode::kNativeAPIs) && is_active()) {
+ if (accessibility_mode.has_mode(ui::AXMode::kNativeAPIs)) {
BrowserAccessibilityManager* manager =
GetOrCreateBrowserAccessibilityManager();
if (manager)
@@ -3941,10 +4126,11 @@ void RenderFrameHostImpl::DocumentOnLoadCompleted() {
void RenderFrameHostImpl::ForwardResourceTimingToParent(
blink::mojom::ResourceTimingInfoPtr timing) {
- // Don't forward the resource timing if this RFH is pending deletion. This can
- // happen in a race where this RenderFrameHost finishes loading just after
- // the frame navigates away. See https://crbug.com/626802.
- if (!is_active())
+ // Don't forward the resource timing of the parent on behalf of inactive
+ // RenderFrameHost. This can happen in a race where this RenderFrameHost
+ // finishes loading just after the frame navigates away. See
+ // https://crbug.com/626802.
+ if (IsInactiveAndDisallowReactivation())
return;
// We should never be receiving this message from a speculative RFH.
@@ -3995,6 +4181,28 @@ void RenderFrameHostImpl::SendAccessibilityEventsToManager(
}
}
+bool RenderFrameHostImpl::IsInactiveAndDisallowReactivation() {
+ switch (lifecycle_state_) {
+ case LifecycleState::kRunningUnloadHandlers:
+ case LifecycleState::kReadyToBeDeleted:
+ return true;
+ case LifecycleState::kInBackForwardCache:
+ EvictFromBackForwardCacheWithReason(
+ BackForwardCacheMetrics::NotRestoredReason::kIgnoreEventAndEvict);
+ return true;
+ case LifecycleState::kSpeculative:
+ // TODO(sreejakshetty): Upgrade NOTREACHED to a CHECK(false) after
+ // monitoring the DumpWithoutCrashing reports and ensuring this doesn't
+ // happen in practice.
+ base::debug::DumpWithoutCrashing();
+ NOTREACHED() << "We should not try to ignore events for a speculative "
+ "RenderFrameHost\n";
+ return false;
+ case LifecycleState::kActive:
+ return false;
+ }
+}
+
void RenderFrameHostImpl::EvictFromBackForwardCache() {
// TODO(hajimehoshi): This function should take the reason from the renderer
// side.
@@ -4042,8 +4250,7 @@ void RenderFrameHostImpl::EvictFromBackForwardCacheWithReasons(
// A document is evicted from the BackForwardCache, but it has already been
// restored. The current document should be reloaded, because it is not
// salvageable.
- frame_tree_node_->navigator()->GetController()->Reload(ReloadType::NORMAL,
- false);
+ frame_tree()->controller()->Reload(ReloadType::NORMAL, false);
return;
}
@@ -4069,14 +4276,14 @@ void RenderFrameHostImpl::EvictFromBackForwardCacheWithReasons(
in_flight_navigation_request->RestartBackForwardCachedNavigation();
}
- NavigationControllerImpl* controller = static_cast<NavigationControllerImpl*>(
- frame_tree_node_->navigator()->GetController());
-
// Evict the frame and schedule it to be destroyed. Eviction happens
// immediately, but destruction is delayed, so that callers don't have to
// worry about use-after-free of |this|.
top_document->is_evicted_from_back_forward_cache_ = true;
- controller->GetBackForwardCache().PostTaskToDestroyEvictedFrames();
+ frame_tree()
+ ->controller()
+ ->GetBackForwardCache()
+ .PostTaskToDestroyEvictedFrames();
}
bool RenderFrameHostImpl::HasSeenRecentXrOverlaySetup() {
@@ -4156,8 +4363,7 @@ void RenderFrameHostImpl::EnterFullscreen(
notified_instances.insert(parent_site_instance);
}
- // TODO(alexmos): See if this can use the last committed origin instead.
- delegate_->EnterFullscreenMode(GetLastCommittedURL().GetOrigin(), *options);
+ delegate_->EnterFullscreenMode(this, *options);
delegate_->FullscreenStateChanged(this, true /* is_fullscreen */);
// The previous call might change the fullscreen state. We need to make sure
@@ -4244,7 +4450,7 @@ void RenderFrameHostImpl::OnDidStopLoading() {
// Only inform the FrameTreeNode of a change in load state if the load state
// of this RenderFrameHost is being tracked.
- if (is_active())
+ if (!IsPendingDeletion())
frame_tree_node_->DidStopLoading();
}
@@ -4275,8 +4481,12 @@ void RenderFrameHostImpl::DidReceiveFirstUserActivation() {
void RenderFrameHostImpl::UpdateUserActivationState(
blink::mojom::UserActivationUpdateType update_type) {
- if (!is_active())
+ // Don't update UserActivationState for non-active RenderFrameHost. In case
+ // of BackForwardCache, this is only called for tests and it is safe to ignore
+ // such requests.
+ if (lifecycle_state_ != LifecycleState::kActive)
return;
+
frame_tree_node_->UpdateUserActivationState(update_type);
}
@@ -4298,7 +4508,8 @@ void RenderFrameHostImpl::ScrollRectToVisibleInParentFrame(
void RenderFrameHostImpl::BubbleLogicalScrollInParentFrame(
blink::mojom::ScrollDirection direction,
ui::ScrollGranularity granularity) {
- if (!is_active())
+ // Do not update the parent on behalf of inactive RenderFrameHost.
+ if (IsInactiveAndDisallowReactivation())
return;
RenderFrameProxyHost* proxy =
@@ -4315,10 +4526,6 @@ void RenderFrameHostImpl::BubbleLogicalScrollInParentFrame(
granularity);
}
-void RenderFrameHostImpl::OnFrameDidCallFocus() {
- delegate_->DidCallFocus();
-}
-
void RenderFrameHostImpl::RenderFallbackContentInParentProcess() {
bool is_object_type =
frame_tree_node()->current_replication_state().frame_owner_element_type ==
@@ -4410,6 +4617,36 @@ void RenderFrameHostImpl::DidChangeFrameOwnerProperties(
}
}
+void RenderFrameHostImpl::DidChangeOpener(
+ const base::Optional<base::UnguessableToken>& opener_frame_token) {
+ frame_tree_node_->render_manager()->DidChangeOpener(
+ opener_frame_token.value_or(base::UnguessableToken()), GetSiteInstance());
+}
+
+void RenderFrameHostImpl::DidChangeFramePolicy(
+ const base::UnguessableToken& child_frame_token,
+ const blink::FramePolicy& frame_policy) {
+ // Ensure that a frame can only update sandbox flags or feature policy for its
+ // immediate children. If this is not the case, the renderer is considered
+ // malicious and is killed.
+ RenderFrameHostImpl* child = FindAndVerifyChild(
+ // TODO(iclelland): Rename this message
+ child_frame_token, bad_message::RFH_SANDBOX_FLAGS);
+ if (!child)
+ return;
+
+ child->frame_tree_node()->SetPendingFramePolicy(frame_policy);
+
+ // Notify the RenderFrame if it lives in a different process from its parent.
+ // The frame's proxies in other processes also need to learn about the updated
+ // flags and policy, but these notifications are sent later in
+ // RenderFrameHostManager::CommitPendingFramePolicy(), when the frame
+ // navigates and the new policies take effect.
+ if (child->GetSiteInstance() != GetSiteInstance()) {
+ child->GetAssociatedLocalFrame()->DidUpdateFramePolicy(frame_policy);
+ }
+}
+
void RenderFrameHostImpl::BindInterfaceProviderReceiver(
mojo::PendingReceiver<service_manager::mojom::InterfaceProvider>
interface_provider_receiver) {
@@ -4457,6 +4694,54 @@ void RenderFrameHostImpl::RequestOverlayRoutingToken(
std::move(callback).Run(frame_token_);
}
+void RenderFrameHostImpl::UpdateState(const PageState& state) {
+ // TODO(creis): Verify the state's ISN matches the last committed FNE.
+
+ // Without this check, the renderer can trick the browser into using
+ // filenames it can't access in a future session restore.
+ if (!CanAccessFilesOfPageState(state)) {
+ bad_message::ReceivedBadMessage(
+ GetProcess(), bad_message::RFH_CAN_ACCESS_FILES_OF_PAGE_STATE);
+ return;
+ }
+
+ delegate_->UpdateStateForFrame(this, state);
+}
+
+void RenderFrameHostImpl::OpenURL(mojom::OpenURLParamsPtr params) {
+ // Verify and unpack the Mojo payload.
+ GURL validated_url;
+ scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory;
+ if (!VerifyOpenURLParams(GetSiteInstance(), params, &validated_url,
+ &blob_url_loader_factory)) {
+ return;
+ }
+
+ TRACE_EVENT1("navigation", "RenderFrameHostImpl::OpenURL", "url",
+ validated_url.possibly_invalid_spec());
+
+ frame_tree_node_->navigator().RequestOpenURL(
+ this, validated_url,
+ GlobalFrameRoutingId(GetProcess()->GetID(), params->initiator_routing_id),
+ params->initiator_origin, params->post_body, params->extra_headers,
+ params->referrer.To<content::Referrer>(), params->disposition,
+ params->should_replace_current_entry, params->user_gesture,
+ params->triggering_event_info, params->href_translate,
+ std::move(blob_url_loader_factory), params->impression);
+}
+
+void RenderFrameHostImpl::GetSavableResourceLinksCallback(
+ blink::mojom::GetSavableResourceLinksReplyPtr reply) {
+ if (!reply) {
+ delegate_->SavableResourceLinksError(this);
+ return;
+ }
+
+ delegate_->SavableResourceLinksResponse(this, reply->resources_list,
+ std::move(reply->referrer),
+ reply->subframes);
+}
+
void RenderFrameHostImpl::DomOperationResponse(const std::string& json_string) {
delegate_->DomOperationResponse(json_string);
}
@@ -4483,7 +4768,8 @@ RenderFrameHostImpl::CreateCrossOriginPrefetchLoaderFactoryBundle() {
CreateURLLoaderFactoriesForIsolatedWorlds(
GetExpectedMainWorldOriginForUrlLoaderFactory(),
isolated_worlds_requiring_separate_url_loader_factory_,
- mojo::Clone(last_committed_client_security_state_)),
+ mojo::Clone(last_committed_client_security_state_),
+ DetermineAfterCommitWhetherToForbidTrustTokenRedemption(this)),
bypass_redirect_checks);
}
@@ -4644,10 +4930,20 @@ void RenderFrameHostImpl::CreateNewWindow(
// Checking sandbox flags of the new frame should be safe at this point,
// because the flags should be already inherited by the CreateNewWindow call
// above.
- main_frame->SetOriginAndIsolationInfoOfNewFrame(GetLastCommittedOrigin());
+ main_frame->SetOriginDependentStateOfNewFrame(GetLastCommittedOrigin());
main_frame->cross_origin_opener_policy_ = popup_coop;
main_frame->cross_origin_embedder_policy_ = popup_coep;
+ // If inheriting coop (checking this via |opener_suppressed|) and the original
+ // coop page has a reporter we make sure the the newly created popup also has
+ // a reporter.
+ if (!params->opener_suppressed && GetMainFrame()->coop_reporter()) {
+ main_frame->set_coop_reporter(
+ std::make_unique<CrossOriginOpenerPolicyReporter>(
+ GetProcess()->GetStoragePartition(), this, GetLastCommittedURL(),
+ popup_coop, popup_coep));
+ }
+
if (main_frame->waiting_for_init_) {
// Need to check |waiting_for_init_| as some paths inside CreateNewWindow
// call above (eg if WebContentsDelegate::IsWebContentsCreationOverridden()
@@ -4685,6 +4981,14 @@ void RenderFrameHostImpl::CreateNewWindow(
blink_widget_host_receiver =
blink_widget_host.InitWithNewEndpointAndPassReceiver();
+ // With this path, RenderViewHostImpl::CreateRenderView is never called
+ // because RenderView is already created on the renderer side. Thus we need to
+ // establish the connection here.
+ mojo::PendingAssociatedRemote<blink::mojom::PageBroadcast> page_broadcast;
+ mojo::PendingAssociatedReceiver<blink::mojom::PageBroadcast>
+ page_broadcast_receiver =
+ page_broadcast.InitWithNewEndpointAndPassReceiver();
+
// TODO(danakj): The main frame's RenderWidgetHost has no RenderWidgetHostView
// yet here. It seems like it should though? In the meantime we send some
// nonsense with a semi-valid but incorrect ScreenInfo (it needs a
@@ -4698,16 +5002,17 @@ void RenderFrameHostImpl::CreateNewWindow(
std::move(blink_frame_widget));
main_frame->GetLocalRenderWidgetHost()->BindWidgetInterfaces(
std::move(blink_widget_host_receiver), std::move(blink_widget));
+ main_frame->render_view_host()->BindPageBroadcast(std::move(page_broadcast));
bool wait_for_debugger =
devtools_instrumentation::ShouldWaitForDebuggerInWindowOpen();
mojom::CreateNewWindowReplyPtr reply = mojom::CreateNewWindowReply::New(
main_frame->GetRenderViewHost()->GetRoutingID(),
- main_frame->GetRoutingID(), main_frame->frame_token(),
+ main_frame->GetRoutingID(), main_frame->GetFrameToken(),
main_frame->GetLocalRenderWidgetHost()->GetRoutingID(), visual_properties,
std::move(blink_frame_widget_host),
std::move(blink_frame_widget_receiver), std::move(blink_widget_host),
- std::move(blink_widget_receiver),
+ std::move(blink_widget_receiver), std::move(page_broadcast_receiver),
mojom::DocumentScopedInterfaceBundle::New(
std::move(main_frame_interface_provider_info),
std::move(browser_interface_broker)),
@@ -4738,6 +5043,15 @@ void RenderFrameHostImpl::CreatePortal(
return;
}
+ // TODO(crbug.com/1051639): We need to find a long term solution to when/how
+ // portals should work in sandboxed documents.
+ if (active_sandbox_flags_ != network::mojom::WebSandboxFlags::kNone) {
+ mojo::ReportBadMessage(
+ "RFHI::CreatePortal called in a sandboxed browsing context");
+ frame_host_associated_receiver_.reset();
+ return;
+ }
+
// Note that we don't check |GetLastCommittedOrigin|, since that is inherited
// by the initial empty document of a new frame.
// TODO(1008989): Once issue 1008989 is fixed we could move this check into
@@ -4787,27 +5101,25 @@ void RenderFrameHostImpl::AdoptPortal(
}
void RenderFrameHostImpl::CreateNewWidget(
- mojo::PendingRemote<mojom::Widget> widget,
mojo::PendingAssociatedReceiver<blink::mojom::WidgetHost> blink_widget_host,
mojo::PendingAssociatedRemote<blink::mojom::Widget> blink_widget,
CreateNewWidgetCallback callback) {
int32_t widget_route_id = GetProcess()->GetNextRoutingID();
std::move(callback).Run(widget_route_id);
delegate_->CreateNewWidget(GetProcess()->GetID(), widget_route_id,
- std::move(widget), std::move(blink_widget_host),
+ std::move(blink_widget_host),
std::move(blink_widget));
}
void RenderFrameHostImpl::CreateNewFullscreenWidget(
- mojo::PendingRemote<mojom::Widget> widget,
mojo::PendingAssociatedReceiver<blink::mojom::WidgetHost> blink_widget_host,
mojo::PendingAssociatedRemote<blink::mojom::Widget> blink_widget,
CreateNewFullscreenWidgetCallback callback) {
int32_t widget_route_id = GetProcess()->GetNextRoutingID();
std::move(callback).Run(widget_route_id);
- delegate_->CreateNewFullscreenWidget(
- GetProcess()->GetID(), widget_route_id, std::move(widget),
- std::move(blink_widget_host), std::move(blink_widget));
+ delegate_->CreateNewFullscreenWidget(GetProcess()->GetID(), widget_route_id,
+ std::move(blink_widget_host),
+ std::move(blink_widget));
}
void RenderFrameHostImpl::IssueKeepAliveHandle(
@@ -4842,7 +5154,7 @@ void RenderFrameHostImpl::BeginNavigation(
return;
}
- if (!is_active())
+ if (IsInactiveAndDisallowReactivation())
return;
TRACE_EVENT2("navigation", "RenderFrameHostImpl::BeginNavigation",
@@ -4913,7 +5225,7 @@ void RenderFrameHostImpl::BeginNavigation(
return;
}
- frame_tree_node()->navigator()->OnBeginNavigation(
+ frame_tree_node()->navigator().OnBeginNavigation(
frame_tree_node(), std::move(validated_params), std::move(begin_params),
std::move(blob_url_loader_factory), std::move(navigation_client),
std::move(navigation_initiator), EnsurePrefetchedSignedExchangeCache(),
@@ -4961,7 +5273,8 @@ void RenderFrameHostImpl::HandleAXEvents(
RenderWidgetHostViewBase* view = GetViewForAccessibility();
ui::AXMode accessibility_mode = delegate_->GetAccessibilityMode();
- if (accessibility_mode.is_mode_off() || !view || !is_active()) {
+ if (accessibility_mode.is_mode_off() || !view ||
+ IsInactiveAndDisallowReactivation()) {
std::move(callback).Run();
return;
}
@@ -4985,6 +5298,7 @@ void RenderFrameHostImpl::HandleAXEvents(
dst_update->root_id = src_update.root_id;
dst_update->node_id_to_clear = src_update.node_id_to_clear;
dst_update->event_from = src_update.event_from;
+ dst_update->event_intents = src_update.event_intents;
dst_update->nodes.resize(src_update.nodes.size());
for (size_t j = 0; j < src_update.nodes.size(); ++j) {
AXContentNodeDataToAXNodeData(src_update.nodes[j], &dst_update->nodes[j]);
@@ -5019,7 +5333,7 @@ void RenderFrameHostImpl::HandleAXEvents(
void RenderFrameHostImpl::HandleAXLocationChanges(
std::vector<mojom::LocationChangesPtr> changes) {
- if (accessibility_reset_token_ || !is_active())
+ if (accessibility_reset_token_ || IsInactiveAndDisallowReactivation())
return;
RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
@@ -5053,7 +5367,11 @@ RenderFrameHostImpl::GetRecordAggregateWatchTimeCallback() {
}
void RenderFrameHostImpl::ResetWaitingState() {
- DCHECK(is_active());
+ // We don't allow resetting waiting state when the RenderFrameHost is either
+ // in BackForwardCache or in pending deletion state, as we don't allow
+ // navigations from either of these two states.
+ DCHECK(!IsInBackForwardCache());
+ DCHECK(!IsPendingDeletion());
// Whenever we reset the RFH state, we should not be waiting for beforeunload
// or close acks. We clear them here to be safe, since they can cause
@@ -5161,32 +5479,6 @@ CanCommitStatus RenderFrameHostImpl::CanCommitOriginAndUrl(
return CanCommitStatus::CAN_COMMIT_ORIGIN_AND_URL;
}
-void RenderFrameHostImpl::NavigateToInterstitialURL(const GURL& data_url) {
- TRACE_EVENT1("navigation", "RenderFrameHostImpl::NavigateToInterstitialURL",
- "frame_tree_node", frame_tree_node_->frame_tree_node_id());
- DCHECK(data_url.SchemeIs(url::kDataScheme));
- NavigationDownloadPolicy download_policy;
- download_policy.SetDisallowed(NavigationDownloadType::kInterstitial);
-
- auto common_params = mojom::CommonNavigationParams::New(
- data_url, base::nullopt, blink::mojom::Referrer::New(),
- ui::PAGE_TRANSITION_LINK, mojom::NavigationType::DIFFERENT_DOCUMENT,
- download_policy, false, GURL(), GURL(), PREVIEWS_OFF,
- base::TimeTicks::Now(), "GET", nullptr,
- network::mojom::SourceLocation::New(),
- false /* started_from_context_menu */, false /* has_user_gesture */,
- CreateInitiatorCSPInfo(), std::vector<int>(), std::string(),
- false /* is_history_navigation_in_new_child_frame */, base::TimeTicks());
- CommitNavigation(nullptr /* navigation_request */, std::move(common_params),
- CreateCommitNavigationParams(), nullptr /* response_head */,
- mojo::ScopedDataPipeConsumerHandle(),
- network::mojom::URLLoaderClientEndpointsPtr(), false,
- base::nullopt, base::nullopt /* subresource_overrides */,
- nullptr /* provider_info */,
- base::UnguessableToken::Create() /* not traced */,
- nullptr /* web_bundle_factory */);
-}
-
void RenderFrameHostImpl::Stop() {
TRACE_EVENT1("navigation", "RenderFrameHostImpl::Stop", "frame_tree_node",
frame_tree_node_->frame_tree_node_id());
@@ -5293,8 +5585,7 @@ bool RenderFrameHostImpl::CheckOrDispatchBeforeUnloadForSubtree(
bool send_ipc,
bool is_reload) {
bool found_beforeunload = false;
- for (FrameTreeNode* node :
- frame_tree_node_->frame_tree()->SubtreeNodes(frame_tree_node_)) {
+ for (FrameTreeNode* node : frame_tree()->SubtreeNodes(frame_tree_node_)) {
RenderFrameHostImpl* rfh = node->current_frame_host();
// If |subframes_only| is true, skip this frame and its same-site
@@ -5398,8 +5689,7 @@ void RenderFrameHostImpl::StartPendingDeletionOnSubtree() {
DCHECK(IsPendingDeletion());
for (std::unique_ptr<FrameTreeNode>& child_frame : children_) {
- for (FrameTreeNode* node :
- frame_tree_node_->frame_tree()->SubtreeNodes(child_frame.get())) {
+ for (FrameTreeNode* node : frame_tree()->SubtreeNodes(child_frame.get())) {
RenderFrameHostImpl* child = node->current_frame_host();
if (child->IsPendingDeletion())
continue;
@@ -5477,9 +5767,10 @@ void RenderFrameHostImpl::UpdateOpener() {
GetSiteInstance(), frame_tree_node_);
}
- int opener_routing_id =
- frame_tree_node_->render_manager()->GetOpenerRoutingID(GetSiteInstance());
- Send(new FrameMsg_UpdateOpener(GetRoutingID(), opener_routing_id));
+ auto opener_frame_token =
+ frame_tree_node_->render_manager()->GetOpenerFrameToken(
+ GetSiteInstance());
+ GetAssociatedLocalFrame()->UpdateOpener(opener_frame_token);
}
void RenderFrameHostImpl::SetFocusedFrame() {
@@ -5526,24 +5817,23 @@ void RenderFrameHostImpl::CommitNavigation(
base::Optional<SubresourceLoaderParams> subresource_loader_params,
base::Optional<std::vector<mojom::TransferrableURLLoaderPtr>>
subresource_overrides,
- blink::mojom::ServiceWorkerProviderInfoForClientPtr provider_info,
+ blink::mojom::ServiceWorkerContainerInfoForClientPtr container_info,
const base::UnguessableToken& devtools_navigation_token,
std::unique_ptr<WebBundleHandle> web_bundle_handle) {
- web_bundle_handle_ = std::move(web_bundle_handle);
-
TRACE_EVENT2("navigation", "RenderFrameHostImpl::CommitNavigation",
"frame_tree_node", frame_tree_node_->frame_tree_node_id(), "url",
common_params->url.possibly_invalid_spec());
DCHECK(!IsRendererDebugURL(common_params->url));
+ DCHECK(navigation_request);
- bool is_mhtml_iframe =
- navigation_request && navigation_request->IsForMhtmlSubframe();
+ bool is_same_document =
+ NavigationTypeUtils::IsSameDocument(common_params->navigation_type);
+ bool is_mhtml_iframe = navigation_request->IsForMhtmlSubframe();
// A |response| and a |url_loader_client_endpoints| must always be provided,
// except for edge cases, where another way to load the document exist.
DCHECK((response_head && url_loader_client_endpoints) ||
- common_params->url.SchemeIs(url::kDataScheme) ||
- NavigationTypeUtils::IsSameDocument(common_params->navigation_type) ||
+ common_params->url.SchemeIs(url::kDataScheme) || is_same_document ||
!IsURLHandledByNetworkStack(common_params->url) || is_mhtml_iframe);
// All children of MHTML documents must be MHTML documents.
@@ -5575,6 +5865,8 @@ void RenderFrameHostImpl::CommitNavigation(
CHECK_EQ(GetSiteInstance(), parent_->GetSiteInstance());
}
+ // TODO(https://crbug.com/888079): Compute the Origin to commit here.
+
// If this is an attempt to commit a URL in an incompatible process, capture a
// crash dump to diagnose why it is occurring.
// TODO(creis): Remove this check after we've gathered enough information to
@@ -5606,6 +5898,13 @@ void RenderFrameHostImpl::CommitNavigation(
const bool is_first_navigation = !has_committed_any_navigation_;
has_committed_any_navigation_ = true;
+ // If this is NOT for same-document navigation, existing |web_bundle_handle_|
+ // should be reset to the new one. Otherwise the existing one should be kept
+ // around so that the subresource requests keep being served from the
+ // WebBundleURLLoaderFactory held by the handle.
+ if (!is_same_document)
+ web_bundle_handle_ = std::move(web_bundle_handle);
+
UpdatePermissionsForNavigation(*common_params, *commit_params);
// Get back to a clean state, in case we start a new navigation without
@@ -5625,8 +5924,6 @@ void RenderFrameHostImpl::CommitNavigation(
network::mojom::URLResponseHeadPtr head =
response_head ? std::move(response_head)
: network::mojom::URLResponseHead::New();
- const bool is_same_document =
- NavigationTypeUtils::IsSameDocument(common_params->navigation_type);
// TODO(crbug.com/979296): Consider changing this code to copy an origin
// instead of creating one from a URL which lacks opacity information.
@@ -5644,7 +5941,7 @@ void RenderFrameHostImpl::CommitNavigation(
}
DCHECK(!isolation_info_.IsEmpty());
- if (navigation_request && navigation_request->appcache_handle()) {
+ if (navigation_request->appcache_handle()) {
// AppCache may create a subresource URLLoaderFactory later, so make sure it
// has the correct origin to use when calling
// ContentBrowserClient::WillCreateURLLoaderFactory().
@@ -5743,21 +6040,23 @@ void RenderFrameHostImpl::CommitNavigation(
// NavigationRequest in this function.
recreate_default_url_loader_factory_after_network_service_crash_ = true;
CrossOriginEmbedderPolicyReporter* const coep_reporter =
- navigation_request ? navigation_request->coep_reporter() : nullptr;
+ navigation_request->coep_reporter();
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
coep_reporter_remote;
if (coep_reporter) {
coep_reporter->Clone(
coep_reporter_remote.InitWithNewPipeAndPassReceiver());
}
+
bool bypass_redirect_checks =
CreateNetworkServiceDefaultFactoryAndObserve(
CreateURLLoaderFactoryParamsForMainWorld(
main_world_origin_for_url_loader_factory,
- navigation_request
- ? mojo::Clone(navigation_request->client_security_state())
- : network::mojom::ClientSecurityState::New(),
- std::move(coep_reporter_remote)),
+ mojo::Clone(navigation_request->client_security_state()),
+ std::move(coep_reporter_remote),
+ DetermineWhetherToForbidTrustTokenRedemption(
+ GetParent(), *commit_params,
+ main_world_origin_for_url_loader_factory)),
pending_default_factory.InitWithNewPipeAndPassReceiver());
subresource_loader_factories->set_bypass_redirect_checks(
bypass_redirect_checks);
@@ -5827,19 +6126,15 @@ void RenderFrameHostImpl::CommitNavigation(
StoragePartition* partition =
BrowserContext::GetStoragePartition(browser_context, GetSiteInstance());
- std::string storage_domain;
- if (site_instance_) {
- std::string partition_name;
- bool in_memory;
- GetContentClient()->browser()->GetStoragePartitionConfigForSite(
- browser_context, site_instance_->GetSiteURL(), true, &storage_domain,
- &partition_name, &in_memory);
- }
+ auto storage_partition_config =
+ GetContentClient()->browser()->GetStoragePartitionConfigForSite(
+ browser_context, site_instance_->GetSiteInfo().site_url());
non_network_url_loader_factories_.emplace(
url::kFileSystemScheme,
content::CreateFileSystemURLLoaderFactory(
process_->GetID(), GetFrameTreeNodeId(),
- partition->GetFileSystemContext(), storage_domain));
+ partition->GetFileSystemContext(),
+ storage_partition_config.partition_domain()));
non_network_url_loader_factories_.emplace(
url::kDataScheme, std::make_unique<DataURLLoaderFactory>());
@@ -5877,9 +6172,10 @@ void RenderFrameHostImpl::CommitNavigation(
CreateURLLoaderFactoriesForIsolatedWorlds(
main_world_origin_for_url_loader_factory,
isolated_worlds_requiring_separate_url_loader_factory_,
- navigation_request
- ? mojo::Clone(navigation_request->client_security_state())
- : network::mojom::ClientSecurityState::New());
+ mojo::Clone(navigation_request->client_security_state()),
+ DetermineWhetherToForbidTrustTokenRedemption(
+ GetParent(), *commit_params,
+ main_world_origin_for_url_loader_factory));
}
// It is imperative that cross-document navigations always provide a set of
@@ -5949,9 +6245,8 @@ void RenderFrameHostImpl::CommitNavigation(
EnsurePrefetchedSignedExchangeCache());
}
- mojom::NavigationClient* navigation_client = nullptr;
- if (navigation_request)
- navigation_client = navigation_request->GetCommitNavigationClient();
+ mojom::NavigationClient* navigation_client =
+ navigation_request->GetCommitNavigationClient();
// Record the metrics about the state of the old main frame at the moment
// when we navigate away from it as it matters for whether the page
@@ -5971,10 +6266,7 @@ void RenderFrameHostImpl::CommitNavigation(
if (!GetParent() && frame_tree_node()->current_frame_host() == this) {
if (NavigationEntryImpl* last_committed_entry =
NavigationEntryImpl::FromNavigationEntry(
- frame_tree_node()
- ->navigator()
- ->GetController()
- ->GetLastCommittedEntry())) {
+ frame_tree()->controller()->GetLastCommittedEntry())) {
if (last_committed_entry->back_forward_cache_metrics()) {
last_committed_entry->back_forward_cache_metrics()
->RecordFeatureUsage(this);
@@ -6011,7 +6303,7 @@ void RenderFrameHostImpl::CommitNavigation(
std::move(url_loader_client_endpoints),
std::move(subresource_loader_factories),
std::move(subresource_overrides), std::move(controller),
- std::move(provider_info), std::move(prefetch_loader_factory),
+ std::move(container_info), std::move(prefetch_loader_factory),
devtools_navigation_token);
// |remote_object| is an associated interface ptr, so calls can't be made on
@@ -6053,9 +6345,20 @@ void RenderFrameHostImpl::FailedNavigation(
// Error page will commit in an opaque origin.
//
+ // The precursor of the opaque origin can be set arbitrarily, because:
+ // 1) we expect that the error page will not issue network requests
+ // 2) network::VerifyRequestInitiatorLock doesn't compare precursors
+ // This observation lets us improve debuggability by using a hardcoded
+ // precursor below.
+ // TODO(lukasza): https://crbug.com/1056949: Stop using error.page.invalid as
+ // the precursor (once https://crbug.com/1056949 is debugged OR once
+ // network::VerifyRequestInitiatorLock starts to compare precursors).
+ //
// TODO(lukasza): https://crbug.com/888079: Use this origin when committing
// later on.
- url::Origin origin = url::Origin();
+ url::Origin error_page_origin =
+ url::Origin::Create(GURL("https://error.page.invalid"))
+ .DeriveNewOpaqueOrigin();
isolation_info_ = net::IsolationInfo::CreateTransient();
std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
@@ -6063,8 +6366,10 @@ void RenderFrameHostImpl::FailedNavigation(
mojo::PendingRemote<network::mojom::URLLoaderFactory> default_factory_remote;
bool bypass_redirect_checks = CreateNetworkServiceDefaultFactoryAndObserve(
CreateURLLoaderFactoryParamsForMainWorld(
- origin, mojo::Clone(navigation_request->client_security_state()),
- /*coep_reporter=*/mojo::NullRemote()),
+ error_page_origin,
+ mojo::Clone(navigation_request->client_security_state()),
+ /*coep_reporter=*/mojo::NullRemote(),
+ network::mojom::TrustTokenRedemptionPolicy::kForbid),
default_factory_remote.InitWithNewPipeAndPassReceiver());
subresource_loader_factories =
std::make_unique<blink::PendingURLLoaderFactoryBundle>(
@@ -6096,7 +6401,7 @@ void RenderFrameHostImpl::HandleRendererDebugURL(const GURL& url) {
// the renderer process is done handling the URL.
// TODO(clamy): Remove the test dependency on this behavior.
if (!url.SchemeIs(url::kJavaScriptScheme)) {
- bool was_loading = frame_tree_node()->frame_tree()->IsLoading();
+ bool was_loading = frame_tree()->IsLoading();
is_loading_ = true;
frame_tree_node()->DidStartLoading(true, was_loading);
}
@@ -6203,9 +6508,6 @@ void RenderFrameHostImpl::SetUpMojoIfNeeded() {
remote_interfaces.InitWithNewPipeAndPassReceiver());
remote_interfaces_.reset(new service_manager::InterfaceProvider);
remote_interfaces_->Bind(std::move(remote_interfaces));
-
- remote_interfaces_->GetInterface(
- frame_input_handler_.BindNewPipeAndPassReceiver());
}
void RenderFrameHostImpl::InvalidateMojoConnection() {
@@ -6215,7 +6517,6 @@ void RenderFrameHostImpl::InvalidateMojoConnection() {
local_frame_.reset();
local_main_frame_.reset();
navigation_control_.reset();
- frame_input_handler_.reset();
find_in_page_.reset();
render_accessibility_.reset();
@@ -6246,7 +6547,7 @@ bool RenderFrameHostImpl::IsFocused() {
bool RenderFrameHostImpl::CreateWebUI(const GURL& dest_url,
int entry_bindings) {
// Verify expectation that WebUI should not be created for error pages.
- DCHECK_NE(GetSiteInstance()->GetSiteURL(), GURL(kUnreachableWebDataURL));
+ DCHECK_NE(GetSiteInstance()->GetSiteInfo(), SiteInfo::CreateForErrorPage());
WebUI::TypeID new_web_ui_type =
WebUIControllerFactoryRegistry::GetInstance()->GetWebUIType(
@@ -6337,7 +6638,7 @@ void RenderFrameHostImpl::ResetLoadingState() {
// When pending deletion, just set the loading state to not loading.
// Otherwise, OnDidStopLoading will take care of that, as well as sending
// notification to the FrameTreeNode about the change in loading state.
- if (!is_active())
+ if (IsPendingDeletion() || IsInBackForwardCache())
is_loading_ = false;
else
OnDidStopLoading();
@@ -6424,6 +6725,15 @@ void RenderFrameHostImpl::RequestAXTreeSnapshot(AXTreeSnapshotCallback callback,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
+void RenderFrameHostImpl::GetSavableResourceLinksFromRenderer() {
+ if (!IsRenderFrameLive())
+ return;
+
+ GetAssociatedLocalFrame()->GetSavableResourceLinks(
+ base::BindOnce(&RenderFrameHostImpl::GetSavableResourceLinksCallback,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
void RenderFrameHostImpl::SetAccessibilityCallbackForTesting(
const AccessibilityCallbackForTesting& callback) {
accessibility_testing_callback_ = callback;
@@ -6431,7 +6741,7 @@ void RenderFrameHostImpl::SetAccessibilityCallbackForTesting(
void RenderFrameHostImpl::UpdateAXTreeData() {
ui::AXMode accessibility_mode = delegate_->GetAccessibilityMode();
- if (accessibility_mode.is_mode_off() || !is_active()) {
+ if (accessibility_mode.is_mode_off() || IsInactiveAndDisallowReactivation()) {
return;
}
@@ -6517,7 +6827,7 @@ bool RenderFrameHostImpl::HasSelection() {
RenderFrameHostImpl* RenderFrameHostImpl::GetMainFrame() {
// Iteration over the GetParent() chain is used below, because returning
- // |frame_tree_node()->frame_tree()->root()->current_frame_host()| might
+ // |frame_tree()->root()->current_frame_host()| might
// give an incorrect result after |this| has been detached from the frame
// tree.
RenderFrameHostImpl* main_frame = this;
@@ -6628,10 +6938,11 @@ RenderFrameHostImpl::CreateURLLoaderFactoryParamsForMainWorld(
const url::Origin& main_world_origin,
network::mojom::ClientSecurityStatePtr client_security_state,
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
- coep_reporter) {
+ coep_reporter,
+ network::mojom::TrustTokenRedemptionPolicy trust_token_redemption_policy) {
return URLLoaderFactoryParamsHelper::CreateForFrame(
this, main_world_origin, std::move(client_security_state),
- std::move(coep_reporter), GetProcess());
+ std::move(coep_reporter), GetProcess(), trust_token_redemption_policy);
}
bool RenderFrameHostImpl::CreateNetworkServiceDefaultFactoryAndObserve(
@@ -6788,34 +7099,46 @@ void RenderFrameHostImpl::AXContentTreeDataToAXTreeData(ui::AXTreeData* dst) {
dst->focused_tree_id = focused_frame->GetAXTreeID();
}
-void RenderFrameHostImpl::RequestAXHitTestCallback(
- int action_request_id,
- mojom::ChildFrameHitTestInfoPtr child_frame_hit_test_info) {
- // Not receiving a child_frame_hit_test_info means that the renderer has
- // already handled this by emitting the requested event over the object found
- // as the result of the hit testing process, so nothing to do.
- if (!child_frame_hit_test_info)
+void RenderFrameHostImpl::AccessibilityHitTestCallback(
+ int request_id,
+ ax::mojom::Event event_to_fire,
+ base::OnceCallback<void(BrowserAccessibilityManager* hit_manager,
+ int hit_node_id)> opt_callback,
+ mojom::HitTestResponsePtr hit_test_response) {
+ if (!hit_test_response) {
+ if (opt_callback)
+ std::move(opt_callback).Run(nullptr, 0);
return;
+ }
- // A child frame was found while hit testing on this frame, and so we need to
- // request a new hit test over such child frame now.
auto frame_or_proxy = LookupRenderFrameHostOrProxy(
- GetProcess()->GetID(), child_frame_hit_test_info->child_frame_routing_id);
- RenderFrameHostImpl* child_frame =
+ GetProcess()->GetID(), hit_test_response->hit_frame_token);
+ RenderFrameHostImpl* hit_frame =
frame_or_proxy.proxy
? frame_or_proxy.proxy->frame_tree_node()->current_frame_host()
: frame_or_proxy.frame;
- if (!child_frame || !child_frame->is_active())
+ if (!hit_frame || hit_frame->IsInactiveAndDisallowReactivation()) {
+ if (opt_callback)
+ std::move(opt_callback).Run(nullptr, 0);
return;
+ }
- ui::AXActionData action_data;
- action_data.request_id = action_request_id;
- action_data.target_point = child_frame_hit_test_info->transformed_point;
- action_data.action = ax::mojom::Action::kHitTest;
- action_data.hit_test_event_to_fire = child_frame_hit_test_info->event_to_fire;
+ // If the hit node's routing ID is the same frame, we're done. If a
+ // callback was provided, call it with the information about the hit node.
+ if (hit_frame->GetFrameToken() == frame_token_) {
+ if (opt_callback) {
+ std::move(opt_callback)
+ .Run(hit_frame->browser_accessibility_manager(),
+ hit_test_response->hit_node_id);
+ }
+ return;
+ }
- child_frame->AccessibilityPerformAction(action_data);
+ // The hit node has a child frame. Do a hit test in that frame's renderer.
+ hit_frame->AccessibilityHitTest(
+ hit_test_response->hit_frame_transformed_point, event_to_fire, request_id,
+ std::move(opt_callback));
}
void RenderFrameHostImpl::RequestAXTreeSnapshotCallback(
@@ -6843,6 +7166,12 @@ void RenderFrameHostImpl::CreatePaymentManager(
}
GetProcess()->CreatePaymentManagerForOrigin(GetLastCommittedOrigin(),
std::move(receiver));
+
+ // Blocklist PaymentManager from the back-forward cache as at the moment we
+ // don't cancel pending payment requests when the RenderFrameHost is stored
+ // in back-forward cache.
+ OnSchedulerTrackedFeatureUsed(
+ blink::scheduler::WebSchedulerTrackedFeature::kPaymentManager);
}
void RenderFrameHostImpl::CreateWebBluetoothService(
@@ -6944,12 +7273,10 @@ void RenderFrameHostImpl::BindScreenEnumerationReceiver(
void RenderFrameHostImpl::BindMediaInterfaceFactoryReceiver(
mojo::PendingReceiver<media::mojom::InterfaceFactory> receiver) {
- DCHECK(!media_interface_proxy_);
- media_interface_proxy_.reset(new MediaInterfaceProxy(
- this, std::move(receiver),
- base::BindOnce(
- &RenderFrameHostImpl::OnMediaInterfaceFactoryConnectionError,
- base::Unretained(this))));
+ if (!media_interface_proxy_) {
+ media_interface_proxy_ = std::make_unique<MediaInterfaceProxy>(this);
+ }
+ media_interface_proxy_->Bind(std::move(receiver));
}
void RenderFrameHostImpl::BindMediaMetricsProviderReceiver(
@@ -7054,17 +7381,14 @@ void RenderFrameHostImpl::CreateDedicatedWorkerHostFactory(
// When a dedicated worker is created from the frame script, the frame is both
// the creator and the ancestor.
- content::CreateDedicatedWorkerHostFactory(
- worker_process_id,
- /*creator_render_frame_host_id=*/GetGlobalFrameRoutingId(),
- /*ancestor_render_frame_host_id=*/GetGlobalFrameRoutingId(),
- last_committed_origin_, cross_origin_embedder_policy_,
- std::move(coep_reporter), std::move(receiver));
-}
-
-void RenderFrameHostImpl::OnMediaInterfaceFactoryConnectionError() {
- DCHECK(media_interface_proxy_);
- media_interface_proxy_.reset();
+ mojo::MakeSelfOwnedReceiver(
+ std::make_unique<DedicatedWorkerHostFactoryImpl>(
+ worker_process_id,
+ /*creator_render_frame_host_id=*/GetGlobalFrameRoutingId(),
+ /*ancestor_render_frame_host_id=*/GetGlobalFrameRoutingId(),
+ last_committed_origin_, cross_origin_embedder_policy_,
+ std::move(coep_reporter)),
+ std::move(receiver));
}
#if defined(OS_ANDROID)
@@ -7112,6 +7436,8 @@ void RenderFrameHostImpl::GetIdleManager(
->GetIdleManager()
->CreateService(std::move(receiver),
GetMainFrame()->GetLastCommittedOrigin());
+ OnSchedulerTrackedFeatureUsed(
+ blink::scheduler::WebSchedulerTrackedFeature::kIdleManager);
}
void RenderFrameHostImpl::GetPresentationService(
@@ -7131,14 +7457,15 @@ void RenderFrameHostImpl::GetSpeechSynthesis(
mojo::PendingReceiver<blink::mojom::SpeechSynthesis> receiver) {
if (!speech_synthesis_impl_) {
speech_synthesis_impl_ = std::make_unique<SpeechSynthesisImpl>(
- GetProcess()->GetBrowserContext());
+ GetProcess()->GetBrowserContext(), delegate_->GetAsWebContents());
}
speech_synthesis_impl_->AddReceiver(std::move(receiver));
-}
-void RenderFrameHostImpl::GetFileChooser(
- mojo::PendingReceiver<blink::mojom::FileChooser> receiver) {
- FileChooserImpl::Create(this, std::move(receiver));
+ // Blocklist SpeechSynthesis for BackForwardCache, because currently we do not
+ // handle speech synthesis after placing the page in BackForwardCache.
+ // TODO(sreejakshetty): Make SpeechSynthesis compatible with BackForwardCache.
+ OnSchedulerTrackedFeatureUsed(
+ blink::scheduler::WebSchedulerTrackedFeature::kSpeechSynthesis);
}
void RenderFrameHostImpl::GetSensorProvider(
@@ -7152,13 +7479,6 @@ void RenderFrameHostImpl::GetSensorProvider(
sensor_provider_proxy_->Bind(std::move(receiver));
}
-mojo::Remote<blink::mojom::FileChooser>
-RenderFrameHostImpl::BindFileChooserForTesting() {
- mojo::Remote<blink::mojom::FileChooser> chooser;
- FileChooserImpl::Create(this, chooser.BindNewPipeAndPassReceiver());
- return chooser;
-}
-
void RenderFrameHostImpl::BindCacheStorage(
mojo::PendingReceiver<blink::mojom::CacheStorage> receiver) {
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
@@ -7253,6 +7573,15 @@ void RenderFrameHostImpl::GetInterface(
void RenderFrameHostImpl::CreateAppCacheBackend(
mojo::PendingReceiver<blink::mojom::AppCacheBackend> receiver) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ static auto* crash_key = base::debug::AllocateCrashKeyString(
+ "CreateAppCacheBackend-data", base::debug::CrashKeySize::Size64);
+ std::string data = base::StringPrintf(
+ "f=%d br=%d irfl=%d iiand=%d fid=%d", frame_.is_bound(),
+ broker_receiver_.is_bound(), IsRenderFrameLive(),
+ GetProcess()->IsInitializedAndNotDead(),
+ RenderProcessHost::FromID(GetProcess()->GetID()) != nullptr);
+ base::debug::ScopedCrashKeyString scoped_crash_key(crash_key, data);
+
auto* storage_partition_impl =
static_cast<StoragePartitionImpl*>(GetProcess()->GetStoragePartition());
storage_partition_impl->GetAppCacheService()->CreateBackend(
@@ -7272,8 +7601,8 @@ void RenderFrameHostImpl::GetContactsManager(
void RenderFrameHostImpl::GetFileSystemManager(
mojo::PendingReceiver<blink::mojom::FileSystemManager> receiver) {
// This is safe because file_system_manager_ is deleted on the IO thread
- base::PostTask(FROM_HERE, {BrowserThread::IO},
- base::BindOnce(&FileSystemManagerImpl::BindReceiver,
+ GetIOThreadTaskRunner({})->PostTask(
+ FROM_HERE, base::BindOnce(&FileSystemManagerImpl::BindReceiver,
base::Unretained(file_system_manager_.get()),
std::move(receiver)));
}
@@ -7298,7 +7627,7 @@ void RenderFrameHostImpl::GetNativeFileSystemManager(
auto* manager = storage_partition->GetNativeFileSystemManager();
manager->BindReceiver(NativeFileSystemManagerImpl::BindingContext(
GetLastCommittedOrigin(), GetLastCommittedURL(),
- GetProcess()->GetID(), routing_id_),
+ GetGlobalFrameRoutingId()),
std::move(receiver));
}
@@ -7352,7 +7681,9 @@ void RenderFrameHostImpl::GetVirtualAuthenticatorManager(
mojo::PendingReceiver<blink::test::mojom::VirtualAuthenticatorManager>
receiver) {
#if !defined(OS_ANDROID)
- if (base::FeatureList::IsEnabled(features::kWebAuth)) {
+ if (base::FeatureList::IsEnabled(features::kWebAuth) &&
+ base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableWebAuthDeprecatedMojoTestingApi)) {
auto* environment_singleton = AuthenticatorEnvironmentImpl::GetInstance();
environment_singleton->EnableVirtualAuthenticatorFor(frame_tree_node_);
environment_singleton->AddVirtualAuthenticatorReceiver(frame_tree_node_,
@@ -7374,9 +7705,17 @@ RenderFrameHostImpl::CreateNavigationRequestForCommit(
cross_origin_embedder_policy_.reporting_endpoint,
cross_origin_embedder_policy_.report_only_reporting_endpoint);
}
- return NavigationRequest::CreateForCommit(frame_tree_node_, this, params,
- std::move(coep_reporter),
- is_same_document);
+ std::unique_ptr<WebBundleNavigationInfo> web_bundle_navigation_info;
+ if (is_same_document && web_bundle_handle_ &&
+ web_bundle_handle_->navigation_info()) {
+ // Need to set |web_bundle_navigation_info| of NavigationRequest. This
+ // will be passed to FrameNavigationEntry, and will be used for subsequent
+ // history navigations.
+ web_bundle_navigation_info = web_bundle_handle_->navigation_info()->Clone();
+ }
+ return NavigationRequest::CreateForCommit(
+ frame_tree_node_, this, params, std::move(coep_reporter),
+ is_same_document, std::move(web_bundle_navigation_info));
}
bool RenderFrameHostImpl::NavigationRequestWasIntendedForPendingEntry(
@@ -7384,7 +7723,7 @@ bool RenderFrameHostImpl::NavigationRequestWasIntendedForPendingEntry(
const FrameHostMsg_DidCommitProvisionalLoad_Params& params,
bool same_document) {
NavigationEntryImpl* pending_entry = NavigationEntryImpl::FromNavigationEntry(
- frame_tree_node()->navigator()->GetController()->GetPendingEntry());
+ frame_tree()->controller()->GetPendingEntry());
if (!pending_entry)
return false;
if (request->nav_entry_id() != pending_entry->GetUniqueID())
@@ -7409,27 +7748,27 @@ void RenderFrameHostImpl::BeforeUnloadTimeout() {
SimulateBeforeUnloadCompleted(true /* proceed */);
}
-void RenderFrameHostImpl::SetLastCommittedSiteUrl(const GURL& url) {
- GURL site_url = url.is_empty()
- ? GURL()
- : SiteInstanceImpl::GetSiteForURL(
- GetSiteInstance()->GetIsolationContext(), url);
+void RenderFrameHostImpl::SetLastCommittedSiteInfo(const GURL& url) {
+ SiteInfo site_info = url.is_empty()
+ ? SiteInfo()
+ : SiteInstanceImpl::ComputeSiteInfo(
+ GetSiteInstance()->GetIsolationContext(), url);
- if (last_committed_site_url_ == site_url)
+ if (last_committed_site_info_ == site_info)
return;
- if (!last_committed_site_url_.is_empty()) {
+ if (!last_committed_site_info_.site_url().is_empty()) {
RenderProcessHostImpl::RemoveFrameWithSite(
- frame_tree_node_->navigator()->GetController()->GetBrowserContext(),
- GetProcess(), last_committed_site_url_);
+ frame_tree()->controller()->GetBrowserContext(), GetProcess(),
+ last_committed_site_info_);
}
- last_committed_site_url_ = site_url;
+ last_committed_site_info_ = site_info;
- if (!last_committed_site_url_.is_empty()) {
+ if (!last_committed_site_info_.site_url().is_empty()) {
RenderProcessHostImpl::AddFrameWithSite(
- frame_tree_node_->navigator()->GetController()->GetBrowserContext(),
- GetProcess(), last_committed_site_url_);
+ frame_tree()->controller()->GetBrowserContext(), GetProcess(),
+ last_committed_site_info_);
}
}
@@ -7544,30 +7883,18 @@ bool RenderFrameHostImpl::ValidateDidCommitParams(
// Error pages may sometimes commit a URL in the wrong process, which requires
// an exception for the CanCommitOriginAndUrl() checks. This is ok as long
// as the origin is opaque.
- bool bypass_checks_for_error_page = false;
- if (SiteIsolationPolicy::IsErrorPageIsolationEnabled(
- frame_tree_node_->IsMainFrame())) {
- if (site_instance_->GetSiteURL() == GURL(content::kUnreachableWebDataURL)) {
- // Commits in the error page process must only be failures, otherwise
- // successful navigations could commit documents from origins different
- // than the chrome-error://chromewebdata/ one and violate expectations.
- if (!params->url_is_unreachable) {
- DEBUG_ALIAS_FOR_ORIGIN(origin_debug_alias, params->origin);
- bad_message::ReceivedBadMessage(
- process, bad_message::RFH_ERROR_PROCESS_NON_ERROR_COMMIT);
- return false;
- }
- // With error page isolation, any URL can commit in an error page process.
- bypass_checks_for_error_page = true;
- }
- } else {
- // Without error page isolation, a blocked navigation is expected to
- // commit in the old renderer process. This may be true for subframe
- // navigations even when error page isolation is enabled for main frames.
- if (navigation_request &&
- navigation_request->GetNetErrorCode() == net::ERR_BLOCKED_BY_CLIENT) {
- bypass_checks_for_error_page = true;
- }
+ bool should_commit_unreachable_url = false;
+ bool bypass_checks_for_error_page = ShouldBypassChecksForErrorPage(
+ this, navigation_request, &should_commit_unreachable_url);
+
+ // Commits in the error page process must only be failures, otherwise
+ // successful navigations could commit documents from origins different
+ // than the chrome-error://chromewebdata/ one and violate expectations.
+ if (should_commit_unreachable_url && !params->url_is_unreachable) {
+ DEBUG_ALIAS_FOR_ORIGIN(origin_debug_alias, params->origin);
+ bad_message::ReceivedBadMessage(
+ process, bad_message::RFH_ERROR_PROCESS_NON_ERROR_COMMIT);
+ return false;
}
// Error pages must commit in a opaque origin. Terminate the renderer
@@ -7650,12 +7977,6 @@ bool RenderFrameHostImpl::ValidateDidCommitParams(
// renderer to load the URL and grant the renderer the privileges to request
// the URL. To prevent this attack, we block the renderer from inserting
// banned URLs into the navigation controller in the first place.
- //
- // TODO(https://crbug.com/172694): Currently, when FilterURL detects a bad URL
- // coming from the renderer, it overwrites that URL to about:blank, which
- // requires |params| to be mutable. Once we kill the renderer
- // instead, the signature of RenderFrameHostImpl::DidCommitProvisionalLoad can
- // be modified to take |params| by const reference.
process->FilterURL(false, &params->url);
process->FilterURL(true, &params->referrer.url);
for (auto it(params->redirects.begin()); it != params->redirects.end();
@@ -7671,25 +7992,22 @@ bool RenderFrameHostImpl::ValidateDidCommitParams(
return false;
}
- // A cross-document navigation requires an embedding token for all embedded
- // frames (a child frame to a remote parent). Embedding tokens should not
- // exist for other cases.
- if (!is_same_document_navigation) {
- if (frame_tree_node()->IsMainFrame() &&
- params->embedding_token.has_value()) {
- bad_message::ReceivedBadMessage(
- process, bad_message::RFH_UNEXPECTED_EMBEDDING_TOKEN);
- return false;
- } else if (IsCrossProcessSubframe() &&
- !params->embedding_token.has_value()) {
+ // A cross-document navigation requires an embedding token. Navigations
+ // served from the back-forward cache do not require new embedding tokens as
+ // the token is already set.
+ bool is_served_from_bfcache =
+ navigation_request && navigation_request->IsServedFromBackForwardCache();
+ if (!is_served_from_bfcache) {
+ if (!is_same_document_navigation && !params->embedding_token.has_value()) {
bad_message::ReceivedBadMessage(process,
bad_message::RFH_MISSING_EMBEDDING_TOKEN);
return false;
+ } else if (is_same_document_navigation &&
+ params->embedding_token.has_value()) {
+ bad_message::ReceivedBadMessage(
+ process, bad_message::RFH_UNEXPECTED_EMBEDDING_TOKEN);
+ return false;
}
- } else if (params->embedding_token.has_value()) {
- bad_message::ReceivedBadMessage(
- process, bad_message::RFH_UNEXPECTED_EMBEDDING_TOKEN);
- return false;
}
return true;
@@ -7698,9 +8016,9 @@ bool RenderFrameHostImpl::ValidateDidCommitParams(
void RenderFrameHostImpl::UpdateSiteURL(const GURL& url,
bool url_is_unreachable) {
if (url_is_unreachable) {
- SetLastCommittedSiteUrl(GURL());
+ SetLastCommittedSiteInfo(GURL());
} else {
- SetLastCommittedSiteUrl(url);
+ SetLastCommittedSiteInfo(url);
}
}
@@ -7769,7 +8087,7 @@ bool RenderFrameHostImpl::DidCommitNavigationInternal(
// racy DidStopLoading IPC resets the loading state that was set to true in
// CommitNavigation.
if (!is_loading()) {
- bool was_loading = frame_tree_node()->frame_tree()->IsLoading();
+ bool was_loading = frame_tree()->IsLoading();
is_loading_ = true;
frame_tree_node()->DidStartLoading(!is_same_document_navigation,
was_loading);
@@ -7815,18 +8133,39 @@ bool RenderFrameHostImpl::DidCommitNavigationInternal(
accessibility_reset_count_ = 0;
appcache_handle_ = navigation_request->TakeAppCacheHandle();
- if (!is_same_document_navigation &&
- !navigation_request->IsServedFromBackForwardCache()) {
+ bool created_new_document =
+ !is_same_document_navigation &&
+ !navigation_request->IsServedFromBackForwardCache();
+
+ if (created_new_document) {
+ // IsWaitingToCommit can be false inside DidCommitNavigationInternal only in
+ // specific circumstances listed above, and specifically for the fake
+ // initial navigations triggered by the blank window.open() and creating a
+ // blank iframe. In that case we do not want to reset the per-document
+ // states as we are not really creating a new Document and we want to
+ // preserve the states set by WebContentsCreated delegate notification
+ // (which among other things create tab helpers) or RenderFrameCreated.
+ if (!navigation_request->IsWaitingToCommit())
+ created_new_document = false;
+ }
+
+ // TODO(crbug.com/936696): Remove this after we have RenderDocument.
+ if (created_new_document) {
+ TRACE_EVENT1("content", "DidCommitProvisionalLoad_StateResetForNewDocument",
+ "render_frame_host", this);
if (navigation_request->IsInMainFrame()) {
render_view_host_->ResetPerPageState();
}
last_committed_cross_document_navigation_id_ =
navigation_request->GetNavigationId();
- if (IsCurrent()) {
+ if (IsCurrent() && !committed_speculative_rfh_before_navigation_commit_) {
// Clear all the user data associated with the non speculative
// RenderFrameHost when the navigation is a cross-document navigation not
- // served from the back-forward cache.
+ // served from the back-forward cache. Make sure the data doesn't get
+ // cleared for the cases when the RenderFrameHost commits before the
+ // navigation commits. This happens when the current RenderFrameHost
+ // crashes before navigating to a new URL.
document_associated_data_.ClearAllUserData();
}
@@ -7836,6 +8175,11 @@ bool RenderFrameHostImpl::DidCommitNavigationInternal(
}
}
+ // Keep track of the sandbox policy of the document that has just committed.
+ // It will be compared with the value computed from the renderer. The latter
+ // is expected to be received in DidSetFramePolicyHeaders(..).
+ active_sandbox_flags_control_ = navigation_request->SandboxFlagsToCommit();
+
// If we still have a PeakGpuMemoryTracker, then the loading it was observing
// never completed. Cancel it's callback so that we don't report partial
// loads to UMA.
@@ -7862,9 +8206,27 @@ bool RenderFrameHostImpl::DidCommitNavigationInternal(
weak_ptr_factory_.GetWeakPtr(), std::move(receiver)));
}
- frame_tree_node()->navigator()->DidNavigate(this, *params,
- std::move(navigation_request),
- is_same_document_navigation);
+ std::unique_ptr<CrossOriginOpenerPolicyReporter> coop_reporter =
+ navigation_request->TakeCoopReporter();
+
+ // If this navigation had a COOP BrowsingInstance swap that severed an opener,
+ // and we have a reporter on the page we're going to, report it here.
+ if (navigation_request->coop_status()
+ .had_opener_before_browsing_instance_swap &&
+ coop_reporter) {
+ coop_reporter->QueueOpenerBreakageReport(
+ coop_reporter->GetPreviousDocumentUrlForReporting(
+ navigation_request->GetRedirectChain(),
+ navigation_request->common_params().referrer->url),
+ false /* is_reported_from_document */, false /* is_report_only */);
+ }
+
+ frame_tree_node()->navigator().DidNavigate(this, *params,
+ std::move(navigation_request),
+ is_same_document_navigation);
+
+ // Reset back the state to false after navigation commits.
+ committed_speculative_rfh_before_navigation_commit_ = false;
if (IsBackForwardCacheEnabled()) {
// Store the Commit params so they can be reused if the page is ever
@@ -7873,12 +8235,11 @@ bool RenderFrameHostImpl::DidCommitNavigationInternal(
}
if (!is_same_document_navigation) {
- cookie_no_samesite_deprecation_url_hashes_.clear();
- cookie_samesite_none_insecure_deprecation_url_hashes_.clear();
renderer_reported_scheduler_tracked_features_ = 0;
browser_reported_scheduler_tracked_features_ = 0;
last_committed_client_security_state_ = std::move(client_security_state);
coep_reporter_ = std::move(coep_reporter);
+ coop_reporter_ = std::move(coop_reporter);
}
RecordCrossOriginIsolationMetrics(this);
@@ -7900,7 +8261,7 @@ void RenderFrameHostImpl::OnSameDocumentCommitProcessed(
if (result == blink::mojom::CommitResult::RestartCrossDocument) {
// The navigation could not be committed as a same-document navigation.
// Restart the navigation cross-document.
- frame_tree_node_->navigator()->RestartNavigationAsCrossDocument(
+ frame_tree_node_->navigator().RestartNavigationAsCrossDocument(
std::move(same_document_navigation_request_));
}
@@ -8016,36 +8377,18 @@ void RenderFrameHostImpl::SendCommitNavigation(
base::Optional<std::vector<::content::mojom::TransferrableURLLoaderPtr>>
subresource_overrides,
blink::mojom::ControllerServiceWorkerInfoPtr controller,
- blink::mojom::ServiceWorkerProviderInfoForClientPtr provider_info,
+ blink::mojom::ServiceWorkerContainerInfoForClientPtr container_info,
mojo::PendingRemote<network::mojom::URLLoaderFactory>
prefetch_loader_factory,
const base::UnguessableToken& devtools_navigation_token) {
- // For committed interstitials, we do not have a NavigationRequest and use the
- // old NavigationControl mojo interface. For anything else we should have a
- // NavigationRequest, containing a NavigationClient, and commit through it.
- // TODO(ahemery): Update when https://crbug.com/448486 is done.
- if (navigation_client) {
- navigation_client->CommitNavigation(
- std::move(common_params), std::move(commit_params),
- std::move(response_head), std::move(response_body),
- std::move(url_loader_client_endpoints),
- std::move(subresource_loader_factories),
- std::move(subresource_overrides), std::move(controller),
- std::move(provider_info), std::move(prefetch_loader_factory),
- devtools_navigation_token,
- BuildCommitNavigationCallback(navigation_request));
- } else {
- DCHECK(!navigation_request);
- GetNavigationControl()->CommitNavigation(
- std::move(common_params), std::move(commit_params),
- std::move(response_head), std::move(response_body),
- std::move(url_loader_client_endpoints),
- std::move(subresource_loader_factories),
- std::move(subresource_overrides), std::move(controller),
- std::move(provider_info), std::move(prefetch_loader_factory),
- devtools_navigation_token,
- mojom::FrameNavigationControl::CommitNavigationCallback());
- }
+ navigation_client->CommitNavigation(
+ std::move(common_params), std::move(commit_params),
+ std::move(response_head), std::move(response_body),
+ std::move(url_loader_client_endpoints),
+ std::move(subresource_loader_factories), std::move(subresource_overrides),
+ std::move(controller), std::move(container_info),
+ std::move(prefetch_loader_factory), devtools_navigation_token,
+ BuildCommitNavigationCallback(navigation_request));
}
void RenderFrameHostImpl::SendCommitFailedNavigation(
@@ -8073,6 +8416,15 @@ void RenderFrameHostImpl::DidCommitNavigation(
std::unique_ptr<NavigationRequest> committing_navigation_request,
std::unique_ptr<FrameHostMsg_DidCommitProvisionalLoad_Params> params,
mojom::DidCommitProvisionalLoadInterfaceParamsPtr interface_params) {
+ // BackForwardCacheImpl::CanStoreRenderFrameHost prevents placing the pages
+ // with in-flight navigation requests in the back-forward cache and it's not
+ // possible to start/commit a new one after the RenderFrameHost is in the
+ // BackForwardCache (see the check IsInactiveAndDisallowReactivation in
+ // RFH::DidCommitSameDocumentNavigation() and RFH::BeginNavigation()) so it
+ // isn't possible to get a DidCommitNavigation IPC from the renderer in
+ // kInBackForwardCache state.
+ DCHECK(!IsInBackForwardCache());
+
NavigationRequest* request;
if (committing_navigation_request) {
request = committing_navigation_request.get();
@@ -8089,8 +8441,8 @@ void RenderFrameHostImpl::DidCommitNavigation(
// DidCommitProvisionalLoad IPC should be associated with the URL being
// committed (not with the *last* committed URL that most other IPCs are
// associated with).
- ScopedActiveURL scoped_active_url(
- params->url, frame_tree_node()->frame_tree()->root()->current_origin());
+ ScopedActiveURL scoped_active_url(params->url,
+ frame_tree()->root()->current_origin());
ScopedCommitStateResetter commit_state_resetter(this);
RenderProcessHost* process = GetProcess();
@@ -8122,7 +8474,7 @@ void RenderFrameHostImpl::DidCommitNavigation(
// destroying this RenderFrameHost. Note that we intentionally do not ignore
// commits that happen while the current tab is being closed - see
// https://crbug.com/805705.
- if (!is_active())
+ if (IsPendingDeletion())
return;
// Retroactive sanity check:
@@ -8253,24 +8605,6 @@ void RenderFrameHostImpl::RemoveServiceWorkerContainerHost(
service_worker_container_hosts_.erase(uuid);
}
-void RenderFrameHostImpl::UpdateFrameFrozenState() {
- // TODO(http://crbug.com/1014212): remove this.
- CHECK(frame_);
- if (!IsFeatureEnabled(
- blink::mojom::FeaturePolicyFeature::kExecutionWhileNotRendered) &&
- visibility_ == blink::mojom::FrameVisibility::kNotRendered) {
- frame_->SetLifecycleState(blink::mojom::FrameLifecycleState::kFrozen);
- } else if (!IsFeatureEnabled(blink::mojom::FeaturePolicyFeature::
- kExecutionWhileOutOfViewport) &&
- visibility_ ==
- blink::mojom::FrameVisibility::kRenderedOutOfViewport) {
- frame_->SetLifecycleState(
- blink::mojom::FrameLifecycleState::kFrozenAutoResumeMedia);
- } else {
- frame_->SetLifecycleState(blink::mojom::FrameLifecycleState::kRunning);
- }
-}
-
bool RenderFrameHostImpl::MaybeInterceptCommitCallback(
NavigationRequest* navigation_request,
FrameHostMsg_DidCommitProvisionalLoad_Params* params,
@@ -8368,108 +8702,16 @@ void RenderFrameHostImpl::AddMessageToConsoleImpl(
discard_duplicates);
}
-void RenderFrameHostImpl::AddSameSiteCookieDeprecationMessage(
- const std::string& cookie_url,
- net::CanonicalCookie::CookieInclusionStatus status,
- bool is_lax_by_default_enabled,
- bool is_none_requires_secure_enabled) {
- std::string deprecation_message;
- // The status will have, at most, one of these warning messages at any given
- // time.
- if (status.HasWarningReason(
- net::CanonicalCookie::CookieInclusionStatus::WarningReason::
- WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT)) {
- if (!ShouldAddCookieSameSiteDeprecationMessage(
- cookie_url, &cookie_no_samesite_deprecation_url_hashes_)) {
- return;
- }
- std::string warning_or_blocked_message =
- (is_lax_by_default_enabled
- ? "It has been blocked, as Chrome now only delivers "
- : "A future release of Chrome will only deliver ");
- deprecation_message =
- "A cookie associated with a cross-site resource at " + cookie_url +
- " was set without the `SameSite` attribute. " +
- warning_or_blocked_message +
- "cookies with "
- "cross-site requests if they are set with `SameSite=None` and "
- "`Secure`. You can review cookies in developer tools under "
- "Application>Storage>Cookies and see more details at "
- "https://www.chromestatus.com/feature/5088147346030592 and "
- "https://www.chromestatus.com/feature/5633521622188032.";
- } else if (status.HasWarningReason(
- net::CanonicalCookie::CookieInclusionStatus::WarningReason::
- WARN_SAMESITE_NONE_INSECURE)) {
- if (!ShouldAddCookieSameSiteDeprecationMessage(
- cookie_url,
- &cookie_samesite_none_insecure_deprecation_url_hashes_)) {
- return;
- }
- std::string warning_or_blocked_message =
- (is_none_requires_secure_enabled
- ? "It has been blocked, as Chrome now only delivers "
- : "A future release of Chrome will only deliver ");
- deprecation_message =
- "A cookie associated with a resource at " + cookie_url +
- " was set with `SameSite=None` but without `Secure`. " +
- warning_or_blocked_message +
- "cookies marked "
- "`SameSite=None` if they are also marked `Secure`. You "
- "can review cookies in developer tools under "
- "Application>Storage>Cookies and see more details at "
- "https://www.chromestatus.com/feature/5633521622188032.";
- } else if (status.HasWarningReason(
- net::CanonicalCookie::CookieInclusionStatus::WarningReason::
- WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE)) {
- if (!ShouldAddCookieSameSiteDeprecationMessage(
- cookie_url, &cookie_lax_allow_unsafe_deprecation_url_hashes_)) {
- return;
- }
- deprecation_message =
- "A cookie associated with a resource at " + cookie_url +
- " set without a `SameSite` attribute was sent with a non-idempotent "
- "top-level cross-site request because it was less than " +
- base::NumberToString(net::kLaxAllowUnsafeMaxAge.InMinutes()) +
- " minutes old. A future release of Chrome will treat such cookies as "
- "if they were set with `SameSite=Lax` and will only allow them to be "
- "sent with top-level cross-site requests if the HTTP method is safe. "
- "See more details at "
- "https://www.chromestatus.com/feature/5088147346030592.";
- }
-
- if (deprecation_message.empty())
- return;
-
- AddUniqueMessageToConsole(blink::mojom::ConsoleMessageLevel::kWarning,
- deprecation_message);
-}
-
void RenderFrameHostImpl::AddInspectorIssue(
blink::mojom::InspectorIssueInfoPtr info) {
GetAssociatedLocalFrame()->AddInspectorIssue(std::move(info));
}
-bool RenderFrameHostImpl::ShouldAddCookieSameSiteDeprecationMessage(
- const std::string& cookie_url,
- base::circular_deque<size_t>* already_seen_url_hashes) {
- DCHECK_LE(already_seen_url_hashes->size(), kMaxCookieSameSiteDeprecationUrls);
- size_t cookie_url_hash = base::FastHash(cookie_url);
- if (base::Contains(*already_seen_url_hashes, cookie_url_hash))
- return false;
-
- // Put most recent ones at the front because we are likely to have multiple
- // consecutive cookies with the same URL.
- if (already_seen_url_hashes->size() == kMaxCookieSameSiteDeprecationUrls)
- already_seen_url_hashes->pop_back();
- already_seen_url_hashes->push_front(cookie_url_hash);
- return true;
-}
-
void RenderFrameHostImpl::LogCannotCommitUrlCrashKeys(
const GURL& url,
bool is_same_document_navigation,
NavigationRequest* navigation_request) {
- LogRendererKillCrashKeys(GetSiteInstance()->GetSiteURL());
+ LogRendererKillCrashKeys(GetSiteInstance()->GetSiteInfo());
// Temporary instrumentation to debug the root cause of renderer process
// terminations. See https://crbug.com/931895.
@@ -8558,8 +8800,8 @@ void RenderFrameHostImpl::LogCannotCommitUrlCrashKeys(
base::debug::AllocateCrashKeyString("starting_site_instance",
base::debug::CrashKeySize::Size64),
navigation_request->GetStartingSiteInstance()
- ->GetSiteURL()
- .possibly_invalid_spec());
+ ->GetSiteInfo()
+ .GetDebugString());
// Recompute the target SiteInstance to see if it matches the current
// one at commit time.
@@ -8582,10 +8824,9 @@ void RenderFrameHostImpl::MaybeEvictFromBackForwardCache() {
while (RenderFrameHostImpl* parent = top_document->GetParent())
top_document = parent;
- NavigationControllerImpl* controller = static_cast<NavigationControllerImpl*>(
- frame_tree_node_->navigator()->GetController());
auto can_store =
- controller->GetBackForwardCache().CanStoreDocument(top_document);
+ frame_tree()->controller()->GetBackForwardCache().CanStoreDocument(
+ top_document);
TRACE_EVENT1("navigation",
"RenderFrameHostImpl::MaybeEvictFromBackForwardCache",
"can_store", can_store.ToString());
@@ -8599,7 +8840,7 @@ void RenderFrameHostImpl::MaybeEvictFromBackForwardCache() {
void RenderFrameHostImpl::LogCannotCommitOriginCrashKeys(
bool is_same_document_navigation,
NavigationRequest* navigation_request) {
- LogRendererKillCrashKeys(GetSiteInstance()->GetSiteURL());
+ LogRendererKillCrashKeys(GetSiteInstance()->GetSiteInfo());
// Temporary instrumentation to debug the root cause of
// https://crbug.com/923144.
@@ -8615,9 +8856,9 @@ void RenderFrameHostImpl::LogCannotCommitOriginCrashKeys(
bool_to_crash_key(!frame_tree_node_->IsMainFrame()));
base::debug::SetCrashKeyString(
- base::debug::AllocateCrashKeyString("is_active",
+ base::debug::AllocateCrashKeyString("lifecycle_state",
base::debug::CrashKeySize::Size32),
- bool_to_crash_key(is_active()));
+ LifecycleStateToString(lifecycle_state_));
base::debug::SetCrashKeyString(
base::debug::AllocateCrashKeyString("is_current",
@@ -8661,8 +8902,8 @@ void RenderFrameHostImpl::LogCannotCommitOriginCrashKeys(
base::debug::AllocateCrashKeyString("starting_site_instance",
base::debug::CrashKeySize::Size64),
navigation_request->GetStartingSiteInstance()
- ->GetSiteURL()
- .possibly_invalid_spec());
+ ->GetSiteInfo()
+ .GetDebugString());
}
}
@@ -8671,7 +8912,7 @@ void RenderFrameHostImpl::EnableMojoJsBindings() {
DCHECK_NE(WebUI::kNoWebUI,
WebUIControllerFactoryRegistry::GetInstance()->GetWebUIType(
GetSiteInstance()->GetBrowserContext(),
- site_instance_->GetSiteURL()));
+ site_instance_->GetSiteInfo().site_url()));
if (!frame_bindings_control_)
GetRemoteAssociatedInterfaces()->GetInterface(&frame_bindings_control_);
@@ -8680,9 +8921,7 @@ void RenderFrameHostImpl::EnableMojoJsBindings() {
BackForwardCacheMetrics* RenderFrameHostImpl::GetBackForwardCacheMetrics() {
NavigationEntryImpl* navigation_entry =
- static_cast<NavigationControllerImpl*>(
- frame_tree_node_->navigator()->GetController())
- ->GetEntryWithUniqueID(nav_entry_id());
+ frame_tree()->controller()->GetEntryWithUniqueID(nav_entry_id());
if (!navigation_entry)
return nullptr;
return navigation_entry->back_forward_cache_metrics();
@@ -8804,32 +9043,43 @@ void RenderFrameHostImpl::SetLifecycleStateToActive() {
}
void RenderFrameHostImpl::SetLifecycleState(LifecycleState state) {
- // Cross-verify that |lifecycle_state_| transition happens correctly.
- switch (state) {
- case LifecycleState::kSpeculative:
- // RenderFrameHost is only set speculative during its creation and no
- // transitions happen to this state during its lifetime.
- NOTREACHED();
- break;
- case LifecycleState::kActive:
- DCHECK(lifecycle_state_ == LifecycleState::kSpeculative ||
- lifecycle_state_ == LifecycleState::kInBackForwardCache)
- << "Unexpected LifeCycleState " << int(lifecycle_state_);
- break;
- case LifecycleState::kInBackForwardCache:
- DCHECK_EQ(lifecycle_state_, LifecycleState::kActive)
- << "Unexpected LifeCycleState " << int(lifecycle_state_);
- break;
- case LifecycleState::kRunningUnloadHandlers:
- DCHECK_EQ(lifecycle_state_, LifecycleState::kActive)
- << "Unexpected LifeCycleState " << int(lifecycle_state_);
- break;
- case LifecycleState::kReadyToBeDeleted:
- DCHECK_NE(lifecycle_state_, LifecycleState::kReadyToBeDeleted)
- << "Unexpected LifeCycleState " << int(lifecycle_state_);
- break;
- }
+ TRACE_EVENT2("content", "RenderFrameHostImpl::SetLifecycleState",
+ "render_frame_host", this, "state",
+ LifecycleStateToString(state));
+#if DCHECK_IS_ON()
+ static const base::NoDestructor<StateTransitions<LifecycleState>>
+ allowed_transitions(
+ // For a graph of state transitions, see
+ // https://chromium.googlesource.com/chromium/src/+/HEAD/docs/render-frame-host-lifecycle-state.png
+ // To update the graph, see the corresponding .gv file.
+
+ // RenderFrameHost is only set speculative during its creation and no
+ // transitions happen to this state during its lifetime.
+ StateTransitions<LifecycleState>({
+ {LifecycleState::kSpeculative,
+ {LifecycleState::kActive, LifecycleState::kReadyToBeDeleted}},
+
+ {LifecycleState::kActive,
+ {LifecycleState::kInBackForwardCache,
+ LifecycleState::kRunningUnloadHandlers,
+ LifecycleState::kReadyToBeDeleted}},
+
+ {LifecycleState::kInBackForwardCache,
+ {LifecycleState::kActive, LifecycleState::kReadyToBeDeleted}},
+
+ {LifecycleState::kRunningUnloadHandlers,
+ {LifecycleState::kReadyToBeDeleted}},
+
+ {LifecycleState::kReadyToBeDeleted, {}},
+ }));
+ DCHECK_STATE_TRANSITION(allowed_transitions, /*old_state=*/lifecycle_state_,
+ /*new_state*/ state);
+#endif // DCHECK_IS_ON()
+
+ LifecycleState old_state = lifecycle_state_;
lifecycle_state_ = state;
+ // Notify the delegate about change in |lifecycle_state_|.
+ delegate_->RenderFrameHostStateChanged(this, old_state, lifecycle_state_);
}
void RenderFrameHostImpl::BindReportingObserver(
@@ -8862,4 +9112,22 @@ void RenderFrameHostImpl::OnCookiesAccessed(
delegate_->OnCookiesAccessed(this, blocked);
}
+void RenderFrameHostImpl::CheckSandboxFlags() {
+ if (is_mhtml_document_)
+ return;
+
+ if (!active_sandbox_flags_control_)
+ return;
+
+ if (active_sandbox_flags_ == *active_sandbox_flags_control_)
+ return;
+
+ DCHECK(false);
+}
+
+std::ostream& operator<<(std::ostream& o,
+ const RenderFrameHostImpl::LifecycleState& s) {
+ return o << LifecycleStateToString(s);
+}
+
} // namespace content
diff --git a/chromium/content/browser/frame_host/render_frame_host_impl.h b/chromium/content/browser/frame_host/render_frame_host_impl.h
index 513fca6f861..d2af86857f5 100644
--- a/chromium/content/browser/frame_host/render_frame_host_impl.h
+++ b/chromium/content/browser/frame_host/render_frame_host_impl.h
@@ -39,18 +39,19 @@
#include "content/browser/feature_observer.h"
#include "content/browser/frame_host/back_forward_cache_metrics.h"
#include "content/browser/frame_host/should_swap_browsing_instance.h"
+#include "content/browser/net/cross_origin_opener_policy_reporter.h"
#include "content/browser/renderer_host/media/render_frame_audio_input_stream_factory.h"
#include "content/browser/renderer_host/media/render_frame_audio_output_stream_factory.h"
#include "content/browser/site_instance_impl.h"
#include "content/browser/webui/web_ui_impl.h"
#include "content/common/ax_content_node_data.h"
+#include "content/common/ax_content_tree_update.h"
#include "content/common/buildflags.h"
#include "content/common/content_export.h"
#include "content/common/dom_automation_controller.mojom.h"
#include "content/common/frame.mojom.h"
#include "content/common/frame_delete_intention.h"
#include "content/common/frame_replication_state.h"
-#include "content/common/input/input_handler.mojom.h"
#include "content/common/input/input_injector.mojom-forward.h"
#include "content/common/navigation_params.mojom.h"
#include "content/common/render_accessibility.mojom.h"
@@ -78,6 +79,7 @@
#include "net/base/isolation_info.h"
#include "net/base/network_isolation_key.h"
#include "net/cookies/canonical_cookie.h"
+#include "net/cookies/cookie_inclusion_status.h"
#include "net/http/http_response_headers.h"
#include "services/device/public/mojom/sensor_provider.mojom.h"
#include "services/device/public/mojom/wake_lock_context.mojom.h"
@@ -111,6 +113,7 @@
#include "third_party/blink/public/mojom/idle/idle_manager.mojom.h"
#include "third_party/blink/public/mojom/image_downloader/image_downloader.mojom.h"
#include "third_party/blink/public/mojom/input/focus_type.mojom-forward.h"
+#include "third_party/blink/public/mojom/input/input_handler.mojom.h"
#include "third_party/blink/public/mojom/installedapp/installed_app_provider.mojom.h"
#include "third_party/blink/public/mojom/loader/resource_load_info.mojom-forward.h"
#include "third_party/blink/public/mojom/native_file_system/native_file_system_manager.mojom-forward.h"
@@ -152,7 +155,6 @@
#endif
class GURL;
-struct FrameHostMsg_OpenURL_Params;
namespace blink {
class AssociatedInterfaceProvider;
@@ -262,6 +264,9 @@ class CONTENT_EXPORT RenderFrameHostImpl
static RenderFrameHostImpl* FromID(GlobalFrameRoutingId id);
static RenderFrameHostImpl* FromID(int process_id, int routing_id);
+ static RenderFrameHostImpl* FromFrameToken(
+ int process_id,
+ const base::UnguessableToken& frame_token);
static RenderFrameHostImpl* FromAXTreeID(ui::AXTreeID ax_tree_id);
static RenderFrameHostImpl* FromOverlayRoutingToken(
const base::UnguessableToken& token);
@@ -273,6 +278,7 @@ class CONTENT_EXPORT RenderFrameHostImpl
// RenderFrameHost
int GetRoutingID() override;
+ const base::UnguessableToken& GetFrameToken() override;
ui::AXTreeID GetAXTreeID() override;
SiteInstanceImpl* GetSiteInstance() override;
RenderProcessHost* GetProcess() override;
@@ -341,8 +347,6 @@ class CONTENT_EXPORT RenderFrameHostImpl
bool IsFeatureEnabled(blink::mojom::DocumentPolicyFeature feature,
blink::PolicyValue threshold_value) override;
void ViewSource() override;
- mojo::Remote<blink::mojom::PauseSubresourceLoadingHandle>
- PauseSubresourceLoading() override;
void ExecuteMediaPlayerActionAtLocation(
const gfx::Point&,
const blink::mojom::MediaPlayerAction& action) override;
@@ -376,6 +380,8 @@ class CONTENT_EXPORT RenderFrameHostImpl
ukm::SourceId GetPageUkmSourceId() override;
StoragePartition* GetStoragePartition() override;
BrowserContext* GetBrowserContext() override;
+ void ReportHeavyAdIssue(blink::mojom::HeavyAdResolutionStatus resolution,
+ blink::mojom::HeavyAdReason reason) override;
// Determines if a clipboard paste using |data| of type |data_type| is allowed
// in this renderer frame. The implementation delegates to
@@ -388,6 +394,28 @@ class CONTENT_EXPORT RenderFrameHostImpl
void SendAccessibilityEventsToManager(
const AXEventNotificationDetails& details);
+ // Returns true iff the RenderFrameHost is inactive i.e., when the
+ // |lifecycle_state_| of this RenderFrameHost is in either kInBackForwardCache
+ // or kRunningUnloadHandlers or kReadyToBeDeleted states. This function should
+ // be used when we are unsure if inactive RenderFrameHost can be properly
+ // handled and their processing shouldn't be deferred until the
+ // RenderFrameHost becomes active again.
+ //
+ // This method additionally has a side effect for back-forward cache: it
+ // disallows reactivating by evicting the document from the cache and
+ // triggering deletion. This avoids reactivating the frame as restoring would
+ // be unsafe after dropping an event, which means that the frame will never be
+ // shown to the user again and the event can be safely ignored.
+ //
+ // Note that if |IsInactiveAndDisallowReactivation()| returns false, then
+ // IsCurrent() returns false as well.
+ // We shouldn't be calling this for a speculative RenderFrameHost as we don't
+ // want to support disallowing activation before the document became active
+ // for the first time. In that case |IsInactiveAndDisallowReactivation()|
+ // returns false along with logging a DumpWithoutCrashing to understand the
+ // root cause.
+ bool IsInactiveAndDisallowReactivation();
+
void EvictFromBackForwardCacheWithReason(
BackForwardCacheMetrics::NotRestoredReason reason);
void EvictFromBackForwardCacheWithReasons(
@@ -414,6 +442,12 @@ class CONTENT_EXPORT RenderFrameHostImpl
gfx::NativeViewAccessible AccessibilityGetNativeViewAccessibleForWindow()
override;
WebContents* AccessibilityWebContents() override;
+ void AccessibilityHitTest(
+ const gfx::Point& point_in_frame_pixels,
+ ax::mojom::Event opt_event_to_fire,
+ int opt_request_id,
+ base::OnceCallback<void(BrowserAccessibilityManager* hit_manager,
+ int hit_node_id)> opt_callback) override;
bool AccessibilityIsMainFrame() override;
// RenderProcessHostObserver implementation.
@@ -438,20 +472,19 @@ class CONTENT_EXPORT RenderFrameHostImpl
void PerformAction(const ui::AXActionData& data) override;
bool RequiresPerformActionPointInPixels() const override;
- mojom::FrameInputHandler* GetFrameInputHandler();
-
viz::mojom::InputTargetClient* GetInputTargetClient() {
return input_target_client_;
}
// Creates a RenderFrame in the renderer process.
- bool CreateRenderFrame(int previous_routing_id,
- int opener_routing_id,
- int parent_routing_id,
- int previous_sibling_routing_id);
+ bool CreateRenderFrame(
+ int previous_routing_id,
+ const base::Optional<base::UnguessableToken>& opener_frame_token,
+ int parent_routing_id,
+ int previous_sibling_routing_id);
// Deletes the RenderFrame in the renderer process.
- // Postcondition: |is_active()| will return false.
+ // Postcondition: |IsPendingDeletion()| is true.
void DeleteRenderFrame(FrameDeleteIntention intent);
// Tracks whether the RenderFrame for this RenderFrameHost has been created in
@@ -464,6 +497,10 @@ class CONTENT_EXPORT RenderFrameHostImpl
// Returns true if the frame recently plays an audio.
bool is_audible() const { return is_audible_; }
+
+ // Toggles the audible state of this render frame. This should only be called
+ // from AudioStreamMonitor, and should not be invoked with the same value
+ // successively.
void OnAudibleStateChanged(bool is_audible);
int routing_id() const { return routing_id_; }
@@ -505,6 +542,7 @@ class CONTENT_EXPORT RenderFrameHostImpl
RenderViewHostImpl* render_view_host() { return render_view_host_.get(); }
RenderFrameHostDelegate* delegate() { return delegate_; }
+ FrameTree* frame_tree() const { return frame_tree_; }
FrameTreeNode* frame_tree_node() const { return frame_tree_node_; }
// Methods to add/remove/reset/query child FrameTreeNodes of this frame.
@@ -560,6 +598,17 @@ class CONTENT_EXPORT RenderFrameHostImpl
void GetCanonicalUrlForSharing(
mojom::Frame::GetCanonicalUrlForSharingCallback callback);
+ // Get HTML data for this RenderFrame by serializing contents on the renderer
+ // side and replacing all links to both same-site and cross-site resources
+ // with paths to local copies as specified by |url_map| and |frame_token_map|.
+ void GetSerializedHtmlWithLocalLinks(
+ const base::flat_map<GURL, base::FilePath>& url_map,
+ const base::flat_map<base::UnguessableToken, base::FilePath>&
+ frame_token_map,
+ bool save_with_empty_url,
+ mojo::PendingRemote<mojom::FrameHTMLSerializerHandler>
+ serializer_handler);
+
// Returns the associated WebUI or null if none applies.
WebUIImpl* web_ui() const { return web_ui_.get(); }
WebUI::TypeID web_ui_type() const { return web_ui_type_; }
@@ -659,7 +708,7 @@ class CONTENT_EXPORT RenderFrameHostImpl
// Remove this frame and its children. This happens asynchronously, an IPC
// round trip with the renderer process is needed to ensure children's unload
// handlers are run.
- // Postcondition: is_active() is false.
+ // Postcondition: |IsPendingDeletion()| is true.
void DetachFromProxy();
// Whether an ongoing navigation in this frame is waiting for a BeforeUnload
@@ -681,24 +730,12 @@ class CONTENT_EXPORT RenderFrameHostImpl
// out.
void OnUnloaded();
- // This method returns true from the time this RenderFrameHost is created
- // until it is pending deletion. Pending deletion starts when Unload() is
- // called on the frame or one of its ancestors.
- // Returns false when the frame is in the BackForwardCache.
- // TODO(https://crbug.com/1073449): Replace all occurrences of is_active.
- bool is_active() const {
- return lifecycle_state_ == LifecycleState::kActive ||
- lifecycle_state_ == LifecycleState::kSpeculative;
- }
-
- // Navigates to an interstitial page represented by the provided data URL.
- void NavigateToInterstitialURL(const GURL& data_url);
// Stop the load in progress.
void Stop();
// Defines different states the RenderFrameHost can be in during its lifetime
- // i.e., from point of creation to deletion.
+ // i.e., from point of creation to deletion. See |SetLifecycleState|.
enum class LifecycleState {
// This state corresponds to when a speculative RenderFrameHost is created
// for an ongoing navigation (to new URL) but hasn't been swapped in the
@@ -878,7 +915,7 @@ class CONTENT_EXPORT RenderFrameHostImpl
base::Optional<SubresourceLoaderParams> subresource_loader_params,
base::Optional<std::vector<mojom::TransferrableURLLoaderPtr>>
subresource_overrides,
- blink::mojom::ServiceWorkerProviderInfoForClientPtr provider_info,
+ blink::mojom::ServiceWorkerContainerInfoForClientPtr container_info,
const base::UnguessableToken& devtools_navigation_token,
std::unique_ptr<WebBundleHandle> web_bundle_handle);
@@ -938,7 +975,9 @@ class CONTENT_EXPORT RenderFrameHostImpl
void ResetLoadingState();
// Returns the feature policy which should be enforced on this RenderFrame.
- blink::FeaturePolicy* feature_policy() { return feature_policy_.get(); }
+ const blink::FeaturePolicy* feature_policy() const {
+ return feature_policy_.get();
+ }
void ClearFocusedElement();
@@ -984,8 +1023,7 @@ class CONTENT_EXPORT RenderFrameHostImpl
// or speculative RenderFrameHost (that has not committed) should be avoided.
void SetVisibilityForChildViews(bool visible);
- const base::UnguessableToken& frame_token() const { return frame_token_; }
- const base::UnguessableToken& GetTopFrameToken() const;
+ const base::UnguessableToken& GetTopFrameToken();
// Returns an unguessable token for this RFHI. This provides a temporary way
// to identify a RenderFrameHost that's compatible with IPC. Else, one needs
@@ -1062,8 +1100,6 @@ class CONTENT_EXPORT RenderFrameHostImpl
// for unload handler processing.
void SetSubframeUnloadTimeoutForTesting(const base::TimeDelta& timeout);
- mojo::Remote<blink::mojom::FileChooser> BindFileChooserForTesting();
-
// Called when the WebAudio AudioContext given by |audio_context_id| has
// started (or stopped) playing audible audio.
void AudioContextPlaybackStarted(int audio_context_id);
@@ -1124,12 +1160,14 @@ class CONTENT_EXPORT RenderFrameHostImpl
void NavigationRequestCancelled(NavigationRequest* navigation_request);
// Called on the main frame of a page embedded in a Portal when it is
- // activated. The frame has the option to adopt the previous page as a portal
- // containing the contents |predecessor_web_contents|. The activation
- // can optionally include a message |data| dispatched with the
- // PortalActivateEvent.
+ // activated. The frame has the option to adopt the previous page,
+ // |predecessor|, as a portal. The activation can optionally include a message
+ // |data| dispatched with the PortalActivateEvent.
void OnPortalActivated(
- std::unique_ptr<WebContents> predecessor_web_contents,
+ std::unique_ptr<Portal> predecessor,
+ mojo::PendingAssociatedRemote<blink::mojom::Portal> pending_portal,
+ mojo::PendingAssociatedReceiver<blink::mojom::PortalClient>
+ client_receiver,
blink::TransferableMessage data,
base::OnceCallback<void(blink::mojom::PortalActivateResult)> callback);
@@ -1152,6 +1190,9 @@ class CONTENT_EXPORT RenderFrameHostImpl
// Returns true if the frame is embedded in a Portal.
bool InsidePortal();
+ bool ShouldVirtualKeyboardOverlayContent() const;
+ void NotifyVirtualKeyboardOverlayRect(const gfx::Rect& keyboard_rect);
+
blink::mojom::FrameVisibility visibility() const { return visibility_; }
// A CommitCallbackInterceptor is used to modify parameters for or cancel a
@@ -1214,21 +1255,13 @@ class CONTENT_EXPORT RenderFrameHostImpl
blink::mojom::ConsoleMessageLevel level,
const std::string& message);
- // Add cookie SameSite deprecation messages to the DevTools console.
- // TODO(crbug.com/977040): Remove when no longer needed.
- void AddSameSiteCookieDeprecationMessage(
- const std::string& cookie_url,
- net::CanonicalCookie::CookieInclusionStatus status,
- bool is_lax_by_default_enabled,
- bool is_none_requires_secure_enabled);
-
// Notify the scheduler that this frame used a feature which impacts the
// scheduling policy (e.g. whether the frame can be frozen or put into the
// back-forward cache).
void OnSchedulerTrackedFeatureUsed(
blink::scheduler::WebSchedulerTrackedFeature feature);
- // Returns true if frame is frozen.
+ // Returns true if the frame is frozen.
bool IsFrozen();
void CreateAppCacheBackend(
@@ -1273,9 +1306,6 @@ class CONTENT_EXPORT RenderFrameHostImpl
void CreateIDBFactory(
mojo::PendingReceiver<blink::mojom::IDBFactory> receiver);
- void GetFileChooser(
- mojo::PendingReceiver<blink::mojom::FileChooser> receiver);
-
void GetSensorProvider(
mojo::PendingReceiver<device::mojom::SensorProvider> receiver);
@@ -1380,6 +1410,10 @@ class CONTENT_EXPORT RenderFrameHostImpl
CrossOriginEmbedderPolicyReporter* coep_reporter() {
return coep_reporter_.get();
}
+ void set_coop_reporter(
+ std::unique_ptr<CrossOriginOpenerPolicyReporter>&& reporter) {
+ coop_reporter_ = std::move(reporter);
+ }
// Semi-formal definition of COOP:
// https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e
@@ -1389,6 +1423,9 @@ class CONTENT_EXPORT RenderFrameHostImpl
void set_cross_origin_opener_policy(network::CrossOriginOpenerPolicy policy) {
cross_origin_opener_policy_ = policy;
}
+ CrossOriginOpenerPolicyReporter* coop_reporter() {
+ return coop_reporter_.get();
+ }
const network::mojom::ClientSecurityStatePtr&
last_committed_client_security_state() const {
@@ -1407,8 +1444,7 @@ class CONTENT_EXPORT RenderFrameHostImpl
// Sets the embedding token corresponding to the document in this
// RenderFrameHost.
- void SetEmbeddingToken(
- const base::Optional<base::UnguessableToken>& embedding_token);
+ void SetEmbeddingToken(const base::UnguessableToken& embedding_token);
// Return true if the process this RenderFrameHost is using has crashed and we
// are replacing RenderFrameHosts for crashed frames rather than reusing them.
@@ -1419,7 +1455,7 @@ class CONTENT_EXPORT RenderFrameHostImpl
bool must_be_replaced() const { return must_be_replaced_; }
// Resets the must_be_replaced after the RFH has been reinitialized. Do not
// add any more usages of this.
- // TODO(https://crbug.com/1006814): Remove this.
+ // TODO(https://crbug.com/936696): Remove this.
void reset_must_be_replaced() { must_be_replaced_ = false; }
std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
@@ -1459,6 +1495,11 @@ class CONTENT_EXPORT RenderFrameHostImpl
// WebContents.
RenderFrameHostImpl* ParentOrOuterDelegateFrame();
+ void SetIsOuterDelegateFrame(bool is_outer_frame) {
+ is_outer_delegate_frame_ = is_outer_frame;
+ }
+ bool IsOuterDelegateFrame() { return is_outer_delegate_frame_; }
+
scoped_refptr<WebAuthRequestSecurityChecker>
GetWebAuthRequestSecurityChecker();
@@ -1480,12 +1521,13 @@ class CONTENT_EXPORT RenderFrameHostImpl
void DidContainInsecureFormAction() override;
void DocumentAvailableInMainFrame(bool uses_temporary_zoom_level) override;
void SetNeedsOcclusionTracking(bool needs_tracking) override;
- void LifecycleStateChanged(blink::mojom::FrameLifecycleState state) override;
+ void SetVirtualKeyboardOverlayPolicy(bool vk_overlays_content) override;
void EvictFromBackForwardCache() override;
void VisibilityChanged(blink::mojom::FrameVisibility) override;
void DidChangeThemeColor(const base::Optional<SkColor>& theme_color) override;
void DidFailLoadWithError(const GURL& url, int32_t error_code) override;
void DidFocusFrame() override;
+ void DidCallFocus() override;
void DidAddContentSecurityPolicies(
std::vector<network::mojom::ContentSecurityPolicyPtr> policies) override;
void EnforceInsecureRequestPolicy(
@@ -1532,7 +1574,6 @@ class CONTENT_EXPORT RenderFrameHostImpl
RunModalPromptDialogCallback callback) override;
void RunBeforeUnloadConfirm(bool is_reload,
RunBeforeUnloadConfirmCallback callback) override;
- void Are3DAPIsBlocked(Are3DAPIsBlockedCallback callback) override;
void UpdateFaviconURL(
std::vector<blink::mojom::FaviconURLPtr> favicon_urls) override;
void DownloadURL(blink::mojom::DownloadURLParamsPtr params) override;
@@ -1555,12 +1596,17 @@ class CONTENT_EXPORT RenderFrameHostImpl
void DidChangeFrameOwnerProperties(
const base::UnguessableToken& child_frame_token,
blink::mojom::FrameOwnerPropertiesPtr frame_owner_properties) override;
+ void DidChangeOpener(
+ const base::Optional<base::UnguessableToken>& opener_frame) override;
+ void DidChangeFramePolicy(const base::UnguessableToken& child_frame_token,
+ const blink::FramePolicy& frame_policy) override;
// blink::LocalMainFrameHost overrides:
void ScaleFactorChanged(float scale) override;
void ContentsPreferredSizeChanged(const gfx::Size& pref_size) override;
void TextAutosizerPageInfoChanged(
blink::mojom::TextAutosizerPageInfoPtr page_info) override;
+ void FocusPage() override;
void ReportNoBinderForInterface(const std::string& error);
@@ -1582,6 +1628,13 @@ class CONTENT_EXPORT RenderFrameHostImpl
// - Ignore any OnUnloadACK sent by the renderer process.
void DoNotDeleteForTesting();
+ // This method will unset the flag |do_not_delete_for_testing_| to resume
+ // deletion on the RenderFrameHost. Deletion will only be triggered if
+ // RenderFrameHostImpl::OnDetach() is called for the RenderFrameHost. This is
+ // a counterpart for DoNotDeleteForTesting() which sets the flag
+ // |do_not_delete_for_testing_|.
+ void ResumeDeletionForTesting();
+
// Document-associated data. This is cleared whenever a new document is hosted
// by this RenderFrameHost. Please refer to the description at
// content/public/browser/render_document_host_user_data.h for more details.
@@ -1600,6 +1653,15 @@ class CONTENT_EXPORT RenderFrameHostImpl
document_associated_data_.RemoveUserData(key);
}
+ // Called when we commit speculative RFH early due to not having an alive
+ // current frame. This happens when the renderer crashes before navigating to
+ // a new URL using speculative RenderFrameHost.
+ // TODO(https://crbug.com/1072817): Undo this plumbing after removing the
+ // early post-crash CommitPending() call.
+ void OnCommittedSpeculativeBeforeNavigationCommit() {
+ committed_speculative_rfh_before_navigation_commit_ = true;
+ }
+
// Returns the child RenderFrameHostImpl if |child_frame_routing_id| is an
// immediate child of this FrameTreeNode. |child_frame_routing_id| is
// considered untrusted, so the renderer process is killed if it refers to a
@@ -1622,6 +1684,11 @@ class CONTENT_EXPORT RenderFrameHostImpl
void OnCookiesAccessed(
network::mojom::CookieAccessDetailsPtr details) override;
+ // mojom::FrameHost:
+ void OpenURL(mojom::OpenURLParamsPtr params) override;
+
+ void GetSavableResourceLinksFromRenderer();
+
protected:
friend class RenderFrameHostFactory;
@@ -1642,7 +1709,7 @@ class CONTENT_EXPORT RenderFrameHostImpl
LifecycleState lifecycle_state);
// The SendCommit* functions below are wrappers for commit calls
- // made to mojom::FrameNavigationControl and mojom::NavigationClient.
+ // made to mojom::NavigationClient.
// These exist to be overridden in tests to retain mojo callbacks.
// Note: |navigation_id| is used in test overrides, but is unused otherwise.
virtual void SendCommitNavigation(
@@ -1659,7 +1726,7 @@ class CONTENT_EXPORT RenderFrameHostImpl
subresource_overrides,
blink::mojom::ControllerServiceWorkerInfoPtr
controller_service_worker_info,
- blink::mojom::ServiceWorkerProviderInfoForClientPtr provider_info,
+ blink::mojom::ServiceWorkerContainerInfoForClientPtr container_info,
mojo::PendingRemote<network::mojom::URLLoaderFactory>
prefetch_loader_factory,
const base::UnguessableToken& devtools_navigation_token);
@@ -1782,15 +1849,10 @@ class CONTENT_EXPORT RenderFrameHostImpl
// IPC Message handlers.
void OnDetach();
- void OnOpenURL(const FrameHostMsg_OpenURL_Params& params);
- void OnUpdateState(const PageState& state);
void OnUnloadACK();
void OnContextMenu(const UntrustworthyContextMenuParams& params);
void OnVisualStateResponse(uint64_t id);
- void OnDidChangeOpener(int32_t opener_routing_id);
- void OnDidChangeFramePolicy(int32_t frame_routing_id,
- const blink::FramePolicy& frame_policy);
void OnForwardResourceTimingToParent(
const ResourceTimingInfo& resource_timing);
void OnDidStopLoading();
@@ -1798,7 +1860,6 @@ class CONTENT_EXPORT RenderFrameHostImpl
uint32_t offset,
const gfx::Range& range);
void OnSetNeedsOcclusionTracking(bool needs_tracking);
- void OnFrameDidCallFocus();
void OnSaveImageFromDataURL(const std::string& url_str);
// Computes the IsolationInfo for both navigations and subresources.
@@ -1824,13 +1885,11 @@ class CONTENT_EXPORT RenderFrameHostImpl
void AdoptPortal(const base::UnguessableToken& portal_token,
AdoptPortalCallback callback) override;
void CreateNewWidget(
- mojo::PendingRemote<mojom::Widget> widget,
mojo::PendingAssociatedReceiver<blink::mojom::WidgetHost>
blink_widget_host,
mojo::PendingAssociatedRemote<blink::mojom::Widget> blink_widget,
CreateNewWidgetCallback callback) override;
void CreateNewFullscreenWidget(
- mojo::PendingRemote<mojom::Widget> widget,
mojo::PendingAssociatedReceiver<blink::mojom::WidgetHost>
blink_widget_host,
mojo::PendingAssociatedRemote<blink::mojom::Widget> blink_widget,
@@ -1886,6 +1945,7 @@ class CONTENT_EXPORT RenderFrameHostImpl
bool user_gesture) override;
void RequestOverlayRoutingToken(
RequestOverlayRoutingTokenCallback callback) override;
+ void UpdateState(const PageState& state) override;
#if defined(OS_ANDROID)
void UpdateUserGestureCarryoverInfo() override;
#endif
@@ -1963,7 +2023,8 @@ class CONTENT_EXPORT RenderFrameHostImpl
const url::Origin& main_world_origin,
network::mojom::ClientSecurityStatePtr client_security_state,
mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter>
- coep_reporter);
+ coep_reporter,
+ network::mojom::TrustTokenRedemptionPolicy trust_token_redemption_policy);
// Creates a Network Service-backed factory from appropriate |NetworkContext|
// and sets a connection error handler to trigger
@@ -1995,11 +2056,14 @@ class CONTENT_EXPORT RenderFrameHostImpl
// AXTreeData structure.
void AXContentTreeDataToAXTreeData(ui::AXTreeData* dst);
- // Callback that will be called as a response to the request to perform a hit
- // test over the accessibility object at given point.
- void RequestAXHitTestCallback(
+ // Callback in response to an accessibility hit test triggered by
+ // AccessibilityHitTest.
+ void AccessibilityHitTestCallback(
int action_request_id,
- mojom::ChildFrameHitTestInfoPtr child_frame_hit_test_result);
+ ax::mojom::Event event_to_fire,
+ base::OnceCallback<void(BrowserAccessibilityManager* hit_manager,
+ int hit_node_id)> opt_callback,
+ mojom::HitTestResponsePtr hit_test_response);
// Callback that will be called as a response to the call to the method
// content::mojom::RenderAccessibility::SnapshotAccessibilityTree(). The
@@ -2008,6 +2072,12 @@ class CONTENT_EXPORT RenderFrameHostImpl
void RequestAXTreeSnapshotCallback(AXTreeSnapshotCallback callback,
const AXContentTreeUpdate& snapshot);
+ // Callback that will be called as a response to the call to the method
+ // blink::mojom::LocalFrame::GetSavableResourceLinks(). The |reply| passed
+ // will be a nullptr when the url is not the savable URLs or valid.
+ void GetSavableResourceLinksCallback(
+ blink::mojom::GetSavableResourceLinksReplyPtr reply);
+
// Returns the RenderWidgetHostView used for accessibility. For subframes,
// this function will return the platform view on the main frame; for main
// frames, it will return the current frame's view.
@@ -2021,9 +2091,6 @@ class CONTENT_EXPORT RenderFrameHostImpl
void DeleteWebBluetoothService(
WebBluetoothServiceImpl* web_bluetooth_service);
- // Callback for connection error on the media::mojom::InterfaceFactory client.
- void OnMediaInterfaceFactoryConnectionError();
-
#if !defined(OS_ANDROID)
void BindAuthenticatorReceiver(
mojo::PendingReceiver<blink::mojom::Authenticator> receiver);
@@ -2106,22 +2173,21 @@ class CONTENT_EXPORT RenderFrameHostImpl
// Update this frame's last committed origin.
void SetLastCommittedOrigin(const url::Origin& origin);
- // Set the |last_committed_origin_| and |isolation_info_| of |this| frame,
- // inheriting the origin from |new_frame_creator| as appropriate (e.g.
- // depending on whether |this| frame should be sandboxed / should have an
- // opaque origin instead).
- void SetOriginAndIsolationInfoOfNewFrame(
- const url::Origin& new_frame_creator);
-
- // Called when a navigation commits succesfully to |url|. This will update
- // |last_committed_site_url_| with the site URL corresponding to |url|.
- // Note that this will recompute the site URL from |url| rather than using
- // GetSiteInstance()->GetSiteURL(), so that |last_committed_site_url_| is
+ // Set the |last_committed_origin_|, |isolation_info_|, and |feature_policy_|
+ // of |this| frame, inheriting the origin from |new_frame_creator| as
+ // appropriate (e.g. depending on whether |this| frame should be sandboxed /
+ // should have an opaque origin instead).
+ void SetOriginDependentStateOfNewFrame(const url::Origin& new_frame_creator);
+
+ // Called when a navigation commits successfully to |url|. This will update
+ // |last_committed_site_info_| with the SiteInfo corresponding to |url|.
+ // Note that this will recompute the SiteInfo from |url| rather than using
+ // GetSiteInstance()->GetSiteInfo(), so that |last_committed_site_info_| is
// always meaningful: e.g., without site isolation, b.com could commit in a
// SiteInstance for a.com, but this function will still compute the last
- // committed site URL as b.com. For example, this can be used to track which
+ // committed SiteInfo as b.com. For example, this can be used to track which
// sites have committed in which process.
- void SetLastCommittedSiteUrl(const GURL& url);
+ void SetLastCommittedSiteInfo(const GURL& url);
// Clears any existing policy and constructs a new policy for this frame,
// based on its parent frame.
@@ -2191,6 +2257,8 @@ class CONTENT_EXPORT RenderFrameHostImpl
// Called by the renderer process when it is done processing a cross-document
// commit request.
+ // TODO(https://crbug.com/1020175): this is only called with
+ // blink::mojom::CommitResult::Aborted.
void OnCrossDocumentCommitProcessed(NavigationRequest* navigation_request,
blink::mojom::CommitResult result);
@@ -2204,7 +2272,8 @@ class CONTENT_EXPORT RenderFrameHostImpl
CreateURLLoaderFactoriesForIsolatedWorlds(
const url::Origin& main_world_origin,
const base::flat_set<url::Origin>& isolated_world_origins,
- network::mojom::ClientSecurityStatePtr client_security_state);
+ network::mojom::ClientSecurityStatePtr client_security_state,
+ network::mojom::TrustTokenRedemptionPolicy trust_token_redemption_policy);
// Based on the termination |status| and |exit_code|, may generate a crash
// report to be routed to the Reporting API.
@@ -2237,10 +2306,6 @@ class CONTENT_EXPORT RenderFrameHostImpl
// immediately deletes the RenderFrameHost.
void OnUnloadTimeout();
- // Update the frozen state of the frame applying current inputs (visibility,
- // loaded state) to determine the new state.
- void UpdateFrameFrozenState();
-
// Runs interception set up in testing code, if any.
// Returns true if we should proceed to the Commit callback, false otherwise.
bool MaybeInterceptCommitCallback(
@@ -2270,13 +2335,6 @@ class CONTENT_EXPORT RenderFrameHostImpl
const std::string& message,
bool discard_duplicates);
- // Returns whether a cookie SameSite deprecation message should be sent for
- // the given cookie url.
- // TODO(crbug.com/977040): Remove when no longer needed.
- bool ShouldAddCookieSameSiteDeprecationMessage(
- const std::string& cookie_url,
- base::circular_deque<size_t>* already_seen_url_hashes);
-
// Helper functions for logging crash keys when ValidateDidCommitParams()
// determines it cannot commit a URL or origin.
void LogCannotCommitUrlCrashKeys(const GURL& url,
@@ -2319,6 +2377,13 @@ class CONTENT_EXPORT RenderFrameHostImpl
mojo::PendingReceiver<blink::mojom::ReportingObserver>
reporting_observer_receiver);
+ // Check the renderer provided sandbox flags matches with what the browser
+ // process computed on its own. This triggers DCHECK and DumpWithoutCrashing()
+ //
+ // TODO(https://crbug.com/1041376) Remove this when we are confident the value
+ // computed from the browser is always matching.
+ void CheckSandboxFlags();
+
// The RenderViewHost that this RenderFrameHost is associated with.
//
// It is kept alive as long as any RenderFrameHosts or RenderFrameProxyHosts
@@ -2381,9 +2446,9 @@ class CONTENT_EXPORT RenderFrameHostImpl
network::CrossOriginOpenerPolicy cross_origin_opener_policy_;
- // Track the site URL of the last site we committed successfully, as obtained
- // from SiteInstance::GetSiteURL.
- GURL last_committed_site_url_;
+ // Track the SiteInfo of the last site we committed successfully, as obtained
+ // from SiteInstanceImpl::GetSiteInfoForURL().
+ SiteInfo last_committed_site_info_;
// The most recent non-error URL to commit in this frame.
// TODO(clamy): Remove this in favor of GetLastCommittedURL().
@@ -2414,6 +2479,11 @@ class CONTENT_EXPORT RenderFrameHostImpl
// the renderer process.
bool render_frame_created_;
+ // Tracks whether the RenderFrame has ever been created for this
+ // RenderFrameHost or not. This starts out as false, becomes true after the
+ // first call to SetRenderFrameCreated(true), and stays true thereafter.
+ bool was_render_frame_ever_created_ = false;
+
// When the last BeforeUnload message was sent.
base::TimeTicks send_before_unload_start_time_;
@@ -2588,6 +2658,12 @@ class CONTENT_EXPORT RenderFrameHostImpl
// audio streams).
bool is_audible_;
+ // If true, then the Virtual keyboard rectangle that occludes the content is
+ // sent to the VirtualKeyboard API where it fires overlaygeometrychange JS
+ // event notifying the web authors that Virtual keyboard has occluded the
+ // content.
+ bool should_virtual_keyboard_overlay_content_;
+
// Used for tracking the latest size of the RenderFrame.
base::Optional<gfx::Size> frame_size_;
@@ -2686,6 +2762,16 @@ class CONTENT_EXPORT RenderFrameHostImpl
// deletion.
network::mojom::WebSandboxFlags active_sandbox_flags_;
+ // Same as |active_sandbox_flags_|, except this is computed:
+ // - outside of the renderer process.
+ // - before loading the document.
+ //
+ // For now, this is simply used to double check this matches the renderer
+ // computation. Later this will be used as the source of truth.
+ //
+ // [OutOfBlinkSandbox](https://crbug.com/1041376)
+ base::Optional<network::mojom::WebSandboxFlags> active_sandbox_flags_control_;
+
// Tracks the document policy which has been set on this frame.
std::unique_ptr<blink::DocumentPolicy> document_policy_;
@@ -2756,7 +2842,6 @@ class CONTENT_EXPORT RenderFrameHostImpl
const base::UnguessableToken frame_token_;
viz::mojom::InputTargetClient* input_target_client_ = nullptr;
- mojo::Remote<mojom::FrameInputHandler> frame_input_handler_;
// Binding to remote implementation of mojom::RenderAccessibility. Note that
// this binding is done on-demand (in UpdateAccessibilityMode()) and will only
@@ -2866,20 +2951,6 @@ class CONTENT_EXPORT RenderFrameHostImpl
// committed origin.
net::IsolationInfo isolation_info_;
- // Hold onto hashes of the last |kMaxCookieSameSiteDeprecationUrls| cookie
- // URLs that we have seen since the last committed navigation, in order to
- // partially deduplicate the corresponding cookie SameSite deprecation
- // messages.
- // TODO(crbug.com/977040): Remove when no longer needed.
- base::circular_deque<size_t> cookie_no_samesite_deprecation_url_hashes_;
- base::circular_deque<size_t>
- cookie_samesite_none_insecure_deprecation_url_hashes_;
- base::circular_deque<size_t> cookie_lax_allow_unsafe_deprecation_url_hashes_;
-
- // The lifecycle state of the frame.
- blink::mojom::FrameLifecycleState frame_lifecycle_state_ =
- blink::mojom::FrameLifecycleState::kRunning;
-
// The factory to load resources from the WebBundle source bound to
// this file.
std::unique_ptr<WebBundleHandle> web_bundle_handle_;
@@ -2936,10 +3007,19 @@ class CONTENT_EXPORT RenderFrameHostImpl
};
DocumentAssociatedData document_associated_data_;
+ // Keeps track of the scenario when RenderFrameHostManager::CommitPending is
+ // called before the navigation commits. This becomes true if the previous
+ // RenderFrameHost is not alive and the speculative RenderFrameHost is
+ // committed early (see RenderFrameHostManager::GetFrameHostForNavigation for
+ // more details). While |committed_speculative_rfh_before_navigation_commit_|
+ // is true the RenderFrameHost which we commit early will be live.
+ bool committed_speculative_rfh_before_navigation_commit_ = false;
+
// This time is used to record the last WebXR DOM Overlay setup request.
base::TimeTicks last_xr_overlay_setup_time_;
std::unique_ptr<CrossOriginEmbedderPolicyReporter> coep_reporter_;
+ std::unique_ptr<CrossOriginOpenerPolicyReporter> coop_reporter_;
// Navigation ID for the last committed cross-document non-bfcached navigation
// in this RenderFrameHost.
@@ -2954,7 +3034,11 @@ class CONTENT_EXPORT RenderFrameHostImpl
// stuck in pending deletion.
bool do_not_delete_for_testing_ = false;
- // Embedding token for the document in this RenderFrameHost.
+ // Embedding token for the document in this RenderFrameHost. This differs from
+ // |frame_token_| in that |frame_token_| has a lifetime matching that of the
+ // corresponding RenderFrameHostImpl, and is intended to be used for IPCs for
+ // identifying frames just like routing IDs. |embedding_token_| has a document
+ // scoped lifetime and changes on cross-document navigations.
base::Optional<base::UnguessableToken> embedding_token_;
// Observers listening to cookie access notifications for the current document
@@ -2967,12 +3051,21 @@ class CONTENT_EXPORT RenderFrameHostImpl
// TODO(crbug.com/936696): Remove this warning after the RDH ships.
mojo::ReceiverSet<network::mojom::CookieAccessObserver> cookie_observers_;
+ // Indicates whether this frame is an outer delegate frame for some other
+ // RenderFrameHost.
+ bool is_outer_delegate_frame_ = false;
+
// NOTE: This must be the last member.
base::WeakPtrFactory<RenderFrameHostImpl> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(RenderFrameHostImpl);
};
+// Used when DCHECK_STATE_TRANSITION triggers.
+CONTENT_EXPORT std::ostream& operator<<(
+ std::ostream& o,
+ const RenderFrameHostImpl::LifecycleState& s);
+
} // namespace content
#endif // CONTENT_BROWSER_FRAME_HOST_RENDER_FRAME_HOST_IMPL_H_
diff --git a/chromium/content/browser/frame_host/render_frame_host_impl_browsertest.cc b/chromium/content/browser/frame_host/render_frame_host_impl_browsertest.cc
index fb399217074..089a0f08c96 100644
--- a/chromium/content/browser/frame_host/render_frame_host_impl_browsertest.cc
+++ b/chromium/content/browser/frame_host/render_frame_host_impl_browsertest.cc
@@ -71,7 +71,6 @@
#include "services/service_manager/public/cpp/interface_provider.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/blink/public/mojom/browser_interface_broker.mojom-test-utils.h"
-#include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h"
#include "third_party/blink/public/mojom/frame/frame.mojom-test-utils.h"
#include "url/gurl.h"
#include "url/origin.h"
@@ -2434,17 +2433,10 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
// process. Currently, this is done by the renderer process, which commits an
// empty document with success instead.
EXPECT_TRUE(navigation_observer.has_committed());
- if (base::FeatureList::IsEnabled(
- network::features::kOutOfBlinkFrameAncestors)) {
EXPECT_TRUE(navigation_observer.is_error());
EXPECT_EQ(blocked_url, frame->GetLastCommittedURL());
EXPECT_EQ(net::ERR_BLOCKED_BY_RESPONSE,
navigation_observer.net_error_code());
- } else {
- EXPECT_FALSE(navigation_observer.is_error());
- EXPECT_EQ(GURL("data:,"), navigation_observer.last_committed_url());
- EXPECT_EQ(net::Error::OK, navigation_observer.net_error_code());
- }
}
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
@@ -2866,39 +2858,6 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
// Pass if this didn't crash.
}
-void FileChooserCallback(base::RunLoop* run_loop,
- blink::mojom::FileChooserResultPtr result) {
- run_loop->Quit();
-}
-
-IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
- FileChooserAfterRfhDeath) {
- EXPECT_TRUE(NavigateToURL(shell(), GURL(url::kAboutBlankURL)));
- auto* rfh = static_cast<RenderFrameHostImpl*>(
- shell()->web_contents()->GetMainFrame());
- mojo::Remote<blink::mojom::FileChooser> chooser =
- rfh->BindFileChooserForTesting();
-
- // Kill the renderer process.
- RenderProcessHostWatcher crash_observer(
- rfh->GetProcess(), RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
- rfh->GetProcess()->Shutdown(0);
- crash_observer.Wait();
-
- // Call FileChooser methods. The browser process should not crash.
- base::RunLoop run_loop1;
- chooser->OpenFileChooser(blink::mojom::FileChooserParams::New(),
- base::BindOnce(FileChooserCallback, &run_loop1));
- run_loop1.Run();
-
- base::RunLoop run_loop2;
- chooser->EnumerateChosenDirectory(
- base::FilePath(), base::BindOnce(FileChooserCallback, &run_loop2));
- run_loop2.Run();
-
- // Pass if this didn't crash.
-}
-
// Verify that adding an <object> tag which resource is blocked by the network
// stack does not result in terminating the renderer process.
// See https://crbug.com/955777.
@@ -2946,142 +2905,6 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
EXPECT_TRUE(crash_observer.did_exit_normally());
}
-// Test deduplication of SameSite cookie deprecation messages.
-// TODO(crbug.com/976475): This test is flaky.
-IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
- DISABLED_DeduplicateSameSiteCookieDeprecationMessages) {
-#if defined(OS_ANDROID)
- // TODO(crbug.com/974701): This test is broken on Android that is
- // Marshmallow or older.
- if (base::android::BuildInfo::GetInstance()->sdk_int() <=
- base::android::SDK_VERSION_MARSHMALLOW) {
- return;
- }
-#endif // defined(OS_ANDROID)
-
- base::test::ScopedFeatureList feature_list;
- feature_list.InitAndEnableFeature(features::kCookieDeprecationMessages);
-
- WebContentsConsoleObserver console_observer(shell()->web_contents());
-
- // Test deprecation messages for SameSiteByDefault.
- // Set a cookie without SameSite on b.com, then access it in a cross-site
- // context.
- GURL url =
- embedded_test_server()->GetURL("b.com", "/set-cookie?nosamesite=1");
- EXPECT_TRUE(NavigateToURL(shell(), url));
- ASSERT_EQ(0u, console_observer.messages().size());
- url = embedded_test_server()->GetURL(
- "a.com", "/cross_site_iframe_factory.html?a(b(),b())");
- EXPECT_TRUE(NavigateToURL(shell(), url));
- // Only 1 message even though there are 2 cross-site iframes.
- EXPECT_EQ(1u, console_observer.messages().size());
-
- // Test deprecation messages for CookiesWithoutSameSiteMustBeSecure.
- // Set a cookie with SameSite=None but without Secure.
- url = embedded_test_server()->GetURL(
- "c.com", "/set-cookie?samesitenoneinsecure=1;SameSite=None");
- EXPECT_TRUE(NavigateToURL(shell(), url));
- // The 1 message from before, plus the (different) message for setting the
- // SameSite=None insecure cookie.
- EXPECT_EQ(2u, console_observer.messages().size());
- // Another copy of the message appears because we have navigated.
- EXPECT_TRUE(NavigateToURL(shell(), url));
- EXPECT_EQ(3u, console_observer.messages().size());
- EXPECT_EQ(console_observer.messages()[1].message,
- console_observer.messages()[2].message);
-}
-
-// Enable SameSiteByDefaultCookies to test deprecation messages for
-// Lax-allow-unsafe.
-class RenderFrameHostImplSameSiteByDefaultCookiesBrowserTest
- : public RenderFrameHostImplBrowserTest {
- public:
- void SetUp() override {
- feature_list_.InitWithFeatures({features::kCookieDeprecationMessages,
- net::features::kSameSiteByDefaultCookies},
- {});
- RenderFrameHostImplBrowserTest::SetUp();
- }
-
- private:
- base::test::ScopedFeatureList feature_list_;
-};
-
-IN_PROC_BROWSER_TEST_F(RenderFrameHostImplSameSiteByDefaultCookiesBrowserTest,
- DisplaySameSiteCookieDeprecationMessages) {
- WebContentsConsoleObserver console_observer(shell()->web_contents());
-
- // Test deprecation messages for SameSiteByDefault.
- // Set a cookie without SameSite on b.com, then access it in a cross-site
- // context.
- base::Time set_cookie_time = base::Time::Now();
- GURL url =
- embedded_test_server()->GetURL("x.com", "/set-cookie?nosamesite=1");
- EXPECT_TRUE(NavigateToURL(shell(), url));
- // Message does not appear in same-site context (main frame is x).
- ASSERT_EQ(0u, console_observer.messages().size());
- url = embedded_test_server()->GetURL(
- "a.com", "/cross_site_iframe_factory.html?a(x())");
- EXPECT_TRUE(NavigateToURL(shell(), url));
- // Message appears in cross-site context (a framing x).
- EXPECT_EQ(1u, console_observer.messages().size());
-
- // Test deprecation messages for CookiesWithoutSameSiteMustBeSecure.
- // Set a cookie with SameSite=None but without Secure.
- url = embedded_test_server()->GetURL(
- "c.com", "/set-cookie?samesitenoneinsecure=1;SameSite=None");
- EXPECT_TRUE(NavigateToURL(shell(), url));
- // The 1 message from before, plus the (different) message for setting the
- // SameSite=None insecure cookie.
- EXPECT_EQ(2u, console_observer.messages().size());
-
- // Test deprecation messages for Lax-allow-unsafe.
- url = embedded_test_server()->GetURL("a.com",
- "/form_that_posts_cross_site.html");
- EXPECT_TRUE(NavigateToURL(shell(), url));
- // Submit the form to make a cross-site POST request to x.com.
- TestNavigationObserver form_post_observer(shell()->web_contents(), 1);
- EXPECT_TRUE(ExecJs(shell(), "document.getElementById('text-form').submit()"));
- form_post_observer.Wait();
-
- // The test should not take more than 2 minutes.
- ASSERT_LT(base::Time::Now() - set_cookie_time, net::kLaxAllowUnsafeMaxAge);
- EXPECT_EQ(3u, console_observer.messages().size());
-
- // Check that the messages were all distinct.
- EXPECT_NE(console_observer.messages()[0].message,
- console_observer.messages()[1].message);
- EXPECT_NE(console_observer.messages()[0].message,
- console_observer.messages()[2].message);
- EXPECT_NE(console_observer.messages()[1].message,
- console_observer.messages()[2].message);
-}
-
-// Test that the SameSite-by-default console warnings are not emitted
-// if the cookie would have been rejected for other reasons.
-// Regression test for https://crbug.com/1027318.
-IN_PROC_BROWSER_TEST_F(RenderFrameHostImplSameSiteByDefaultCookiesBrowserTest,
- NoMessagesIfCookieWouldBeRejectedForOtherReasons) {
- WebContentsConsoleObserver console_observer(shell()->web_contents());
-
- GURL url = embedded_test_server()->GetURL(
- "x.com", "/set-cookie?cookiewithpath=1;path=/set-cookie");
- EXPECT_TRUE(NavigateToURL(shell(), url));
- url = embedded_test_server()->GetURL("sub.x.com",
- "/set-cookie?cookieforsubdomain=1");
- EXPECT_TRUE(NavigateToURL(shell(), url));
-
- ASSERT_EQ(0u, console_observer.messages().size());
- url = embedded_test_server()->GetURL(
- "a.com", "/cross_site_iframe_factory.html?a(x())");
- EXPECT_TRUE(NavigateToURL(shell(), url));
- // No messages appear even though x.com is accessed in a cross-site
- // context, because the cookies would have been rejected for mismatching path
- // or domain anyway.
- EXPECT_EQ(0u, console_observer.messages().size());
-}
-
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
SchedulerTrackedFeatures) {
EXPECT_TRUE(
@@ -3764,14 +3587,8 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
RenderFrameDeletedObserver delete_rfh_b(rfh_b);
EXPECT_EQ(LifecycleState::kActive, rfh_b->lifecycle_state());
- // 2) Disable the unload timer to ensure that the unload timer doesn't fire
- // before we call OnDetach(). Act as if there was a slow unload handler on
- // rfh_b. The non navigating frames are waiting for FrameHostMsg_Detach.
- rfh_b->DisableUnloadTimerForTesting();
- auto detach_filter = base::MakeRefCounted<DropMessageFilter>(
- FrameMsgStart, FrameHostMsg_Detach::ID);
- rfh_b->GetProcess()->AddFilter(detach_filter.get());
- EXPECT_TRUE(ExecuteScript(rfh_b->frame_tree_node(), onunload_script));
+ // 2) Leave rfh_b in pending deletion state.
+ LeaveInPendingDeletionState(rfh_b);
// 3) Check the IsCurrent state of rfh_a, rfh_b before navigating to C.
EXPECT_TRUE(rfh_a->IsCurrent());
@@ -3788,8 +3605,9 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
EXPECT_FALSE(rfh_b->IsCurrent());
EXPECT_TRUE(rfh_c->IsCurrent());
- // 6) Run detach on rfh_b to delete its frame.
+ // 6) Resume deletion on rfh_b and run detach on rfh_b to delete its frame.
EXPECT_FALSE(delete_rfh_b.deleted());
+ rfh_b->ResumeDeletionForTesting();
rfh_b->OnDetach();
EXPECT_TRUE(delete_rfh_b.deleted());
}
diff --git a/chromium/content/browser/frame_host/render_frame_host_impl_mac_browsertest.mm b/chromium/content/browser/frame_host/render_frame_host_impl_mac_browsertest.mm
index 6c74902c88d..5bc2610df21 100644
--- a/chromium/content/browser/frame_host/render_frame_host_impl_mac_browsertest.mm
+++ b/chromium/content/browser/frame_host/render_frame_host_impl_mac_browsertest.mm
@@ -6,6 +6,7 @@
#include "base/mac/scoped_nsobject.h"
#include "base/run_loop.h"
+#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/content_browser_test.h"
@@ -33,8 +34,8 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserMacTest,
[pboard setFindText:@"test"];
EXPECT_NSEQ(@"test", [pboard findText]);
- auto* rfhi = static_cast<RenderFrameHostImpl*>(web_contents->GetMainFrame());
- auto* input_handler = rfhi->GetFrameInputHandler();
+ auto* input_handler = static_cast<WebContentsImpl*>(web_contents)
+ ->GetFocusedFrameWidgetInputHandler();
input_handler->SelectAll();
input_handler->CopyToFindPboard();
diff --git a/chromium/content/browser/frame_host/render_frame_host_impl_unittest.cc b/chromium/content/browser/frame_host/render_frame_host_impl_unittest.cc
index f35049157c9..174044481da 100644
--- a/chromium/content/browser/frame_host/render_frame_host_impl_unittest.cc
+++ b/chromium/content/browser/frame_host/render_frame_host_impl_unittest.cc
@@ -82,11 +82,7 @@ TEST_F(RenderFrameHostImplTest, ExpectedMainWorldOrigin) {
// Create a full screen popup RenderWidgetHost and View.
TEST_F(RenderFrameHostImplTest, CreateFullscreenWidget) {
- mojo::PendingRemote<mojom::Widget> widget;
- std::unique_ptr<MockWidgetImpl> widget_impl =
- std::make_unique<MockWidgetImpl>(widget.InitWithNewPipeAndPassReceiver());
main_test_rfh()->CreateNewFullscreenWidget(
- std::move(widget),
mojo::PendingAssociatedReceiver<blink::mojom::WidgetHost>(),
mojo::PendingAssociatedRemote<blink::mojom::Widget>(), base::DoNothing());
}
diff --git a/chromium/content/browser/frame_host/render_frame_host_manager.cc b/chromium/content/browser/frame_host/render_frame_host_manager.cc
index 73050ff98a7..72ecbed1a29 100644
--- a/chromium/content/browser/frame_host/render_frame_host_manager.cc
+++ b/chromium/content/browser/frame_host/render_frame_host_manager.cc
@@ -35,6 +35,7 @@
#include "content/browser/frame_host/render_frame_host_factory.h"
#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/frame_host/render_frame_proxy_host.h"
+#include "content/browser/net/cross_origin_opener_policy_reporter.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/renderer_host/render_view_host_factory.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
@@ -46,6 +47,7 @@
#include "content/common/page_messages.h"
#include "content/common/unfreezable_frame_messages.h"
#include "content/common/view_messages.h"
+#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_security_policy.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/render_process_host_observer.h"
@@ -118,140 +120,6 @@ bool ShouldSwapBrowsingInstancesForDynamicIsolation(
future_isolation_context, destination_effective_url);
}
-ShouldSwapBrowsingInstance ShouldProactivelySwapBrowsingInstance(
- RenderFrameHostImpl* current_rfh,
- const GURL& destination_effective_url) {
- // Back-forward cache triggers proactive swap when the current url can be
- // stored in the back-forward cache.
- if (!IsProactivelySwapBrowsingInstanceEnabled() &&
- !IsBackForwardCacheEnabled())
- return ShouldSwapBrowsingInstance::kNo_ProactiveSwapDisabled;
-
- // Only main frames are eligible to swap BrowsingInstances.
- if (!current_rfh->frame_tree_node()->IsMainFrame())
- return ShouldSwapBrowsingInstance::kNo_NotMainFrame;
-
- // Skip cases when there are other windows that might script this one.
- SiteInstanceImpl* current_instance = current_rfh->GetSiteInstance();
- if (current_instance->GetRelatedActiveContentsCount() > 1u)
- return ShouldSwapBrowsingInstance::kNo_HasRelatedActiveContents;
-
- // "about:blank" and chrome-native-URL do not "use" a SiteInstance. This
- // allows the SiteInstance to be reused cross-site. Starting a new
- // BrowsingInstance would prevent the SiteInstance to be reused, that's why
- // this case is excluded here.
- if (!current_instance->HasSite())
- return ShouldSwapBrowsingInstance::kNo_DoesNotHaveSite;
-
- // Exclude non http(s) schemes. Some tests don't expect navigations to
- // data-URL or to about:blank to switch to a different BrowsingInstance.
- const GURL& current_url = current_rfh->GetLastCommittedURL();
- if (!current_url.SchemeIsHTTPOrHTTPS())
- return ShouldSwapBrowsingInstance::kNo_SourceURLSchemeIsNotHTTPOrHTTPS;
-
- if (!destination_effective_url.SchemeIsHTTPOrHTTPS())
- return ShouldSwapBrowsingInstance::kNo_DestinationURLSchemeIsNotHTTPOrHTTPS;
-
- // Nothing prevents two pages with the same website to live in different
- // BrowsingInstance. However many tests are making this assumption. The scope
- // of ProactivelySwapBrowsingInstance experiment doesn't include them. The
- // cost of getting a new process on same-site navigation would (probably?) be
- // too high.
- if (SiteInstanceImpl::IsSameSite(current_instance->GetIsolationContext(),
- current_url, destination_effective_url,
- true)) {
- return ShouldSwapBrowsingInstance::kNo_SameSiteNavigation;
- }
-
- if (IsProactivelySwapBrowsingInstanceEnabled())
- return ShouldSwapBrowsingInstance::kYes_ProactiveSwap;
-
- // If BackForwardCache is enabled, swap BrowsingInstances only when needed
- // for back-forward cache.
- DCHECK(IsBackForwardCacheEnabled());
- NavigationControllerImpl* controller = static_cast<NavigationControllerImpl*>(
- current_rfh->frame_tree_node()->navigator()->GetController());
- if (controller->GetBackForwardCache().IsAllowed(
- current_rfh->GetLastCommittedURL())) {
- return ShouldSwapBrowsingInstance::kYes_ProactiveSwap;
- } else {
- return ShouldSwapBrowsingInstance::kNo_NotNeededForBackForwardCache;
- }
-}
-
-// This function implements the COOP matching algorithm as detailed in [1].
-// Note that COEP is also provided since the COOP enum does not have a
-// "same-origin + COEP" value.
-// [1] https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e
-bool CrossOriginOpenerPolicyMatch(
- network::mojom::CrossOriginOpenerPolicyValue initiator_coop,
- network::mojom::CrossOriginEmbedderPolicyValue initiator_coep,
- const url::Origin& initiator_origin,
- network::mojom::CrossOriginOpenerPolicyValue destination_coop,
- network::mojom::CrossOriginEmbedderPolicyValue destination_coep,
- const url::Origin& destination_origin) {
- if (initiator_coop != destination_coop)
- return false;
- if (initiator_coop ==
- network::mojom::CrossOriginOpenerPolicyValue::kUnsafeNone) {
- return true;
- }
- if (initiator_coop ==
- network::mojom::CrossOriginOpenerPolicyValue::kSameOrigin &&
- initiator_coep != destination_coep) {
- return false;
- }
- if (!initiator_origin.IsSameOriginWith(destination_origin))
- return false;
- return true;
-}
-
-// This function returns whether the BrowsingInstance should change following
-// COOP rules defined in:
-// https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e#changes-to-navigation
-// TODO(ahemery): This should be a member of NavigationRequest.
-bool ShouldSwapBrowsingInstanceForCrossOriginOpenerPolicy(
- network::mojom::CrossOriginOpenerPolicyValue initiator_coop,
- network::mojom::CrossOriginEmbedderPolicyValue initiator_coep,
- const url::Origin& initiator_origin,
- bool is_initiator_aboutblank,
- network::mojom::CrossOriginOpenerPolicyValue destination_coop,
- network::mojom::CrossOriginEmbedderPolicyValue destination_coep,
- const url::Origin& destination_origin) {
- using network::mojom::CrossOriginEmbedderPolicyValue;
- using network::mojom::CrossOriginOpenerPolicyValue;
-
- if (!base::FeatureList::IsEnabled(
- network::features::kCrossOriginOpenerPolicy))
- return false;
-
- // If policies match there is no reason to switch BrowsingInstances.
- if (CrossOriginOpenerPolicyMatch(initiator_coop, initiator_coep,
- initiator_origin, destination_coop,
- destination_coep, destination_origin)) {
- return false;
- }
-
- // "same-origin-allow-popups" is used to stay in the same BrowsingInstance
- // despite COOP mismatch. This case is defined in the spec [1] as follow.
- // ```
- // If the result of matching currentCOOP, currentOrigin, potentialCOOP, and
- // potentialOrigin is false and one of the following is false:
- // - doc is the initial about:blank document
- // - currentCOOP is "same-origin-allow-popups"
- // - potentialCOOP is "unsafe-none"
- // Then create a new browsing context group.
- // ```
- // [1]
- // https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e#changes-to-navigation
- if (is_initiator_aboutblank &&
- initiator_coop == CrossOriginOpenerPolicyValue::kSameOriginAllowPopups &&
- destination_coop == CrossOriginOpenerPolicyValue::kUnsafeNone) {
- return false;
- }
- return true;
-}
-
bool IsSiteInstanceCompatibleWithErrorIsolation(SiteInstance* site_instance,
bool is_main_frame,
bool is_failure) {
@@ -266,7 +134,8 @@ bool IsSiteInstanceCompatibleWithErrorIsolation(SiteInstance* site_instance,
// SiteInstance but the navigation will fail and actually need an error page
// SiteInstance.
bool is_site_instance_for_failures =
- site_instance->GetSiteURL() == GURL(kUnreachableWebDataURL);
+ static_cast<SiteInstanceImpl*>(site_instance)->GetSiteInfo() ==
+ SiteInfo::CreateForErrorPage();
return is_site_instance_for_failures == is_failure;
}
@@ -489,12 +358,12 @@ void RenderFrameHostManager::CommitPendingIfNecessary(
}
void RenderFrameHostManager::DidChangeOpener(
- int opener_routing_id,
+ const base::UnguessableToken& opener_frame_token,
SiteInstance* source_site_instance) {
FrameTreeNode* opener = nullptr;
- if (opener_routing_id != MSG_ROUTING_NONE) {
- RenderFrameHostImpl* opener_rfhi = RenderFrameHostImpl::FromID(
- source_site_instance->GetProcess()->GetID(), opener_routing_id);
+ if (opener_frame_token) {
+ RenderFrameHostImpl* opener_rfhi = RenderFrameHostImpl::FromFrameToken(
+ source_site_instance->GetProcess()->GetID(), opener_frame_token);
// If |opener_rfhi| is null, the opener RFH has already disappeared. In
// this case, clear the opener rather than keeping the old opener around.
if (opener_rfhi)
@@ -637,8 +506,9 @@ void RenderFrameHostManager::UnloadOldFrame(
DeleteRenderFrameProxyHost(it.second->GetSiteInstance());
// Ensures RenderViewHosts are not reused while they are in the cache.
- for (RenderViewHostImpl* rvh : old_render_view_hosts)
+ for (RenderViewHostImpl* rvh : old_render_view_hosts) {
rvh->EnterBackForwardCache();
+ }
auto entry = std::make_unique<BackForwardCacheImpl::Entry>(
std::move(old_render_frame_host), std::move(old_proxy_hosts),
@@ -666,10 +536,10 @@ void RenderFrameHostManager::UnloadOldFrame(
// |old_render_frame_host| will be deleted when its unload ACK is received,
// or when the timer times out, or when the RFHM itself is deleted (whichever
// comes first).
- pending_delete_hosts_.push_back(std::move(old_render_frame_host));
-
+ auto insertion =
+ pending_delete_hosts_.insert(std::move(old_render_frame_host));
// Tell the old RenderFrameHost to swap out and be replaced by the proxy.
- pending_delete_hosts_.back()->Unload(proxy, true);
+ (*insertion.first)->Unload(proxy, true);
}
void RenderFrameHostManager::DiscardUnusedFrame(
@@ -721,14 +591,11 @@ void RenderFrameHostManager::DiscardUnusedFrame(
bool RenderFrameHostManager::DeleteFromPendingList(
RenderFrameHostImpl* render_frame_host) {
- for (auto iter = pending_delete_hosts_.begin();
- iter != pending_delete_hosts_.end(); iter++) {
- if (iter->get() == render_frame_host) {
- pending_delete_hosts_.erase(iter);
- return true;
- }
- }
- return false;
+ auto it = pending_delete_hosts_.find(render_frame_host);
+ if (it == pending_delete_hosts_.end())
+ return false;
+ pending_delete_hosts_.erase(it);
+ return true;
}
void RenderFrameHostManager::RestoreFromBackForwardCache(
@@ -793,10 +660,10 @@ RenderFrameHostImpl* RenderFrameHostManager::GetFrameHostForNavigation(
<< "Don't call this method for JavaScript URLs as those create a "
"temporary NavigationRequest and we don't want to reset an ongoing "
"navigation's speculative RFH.";
- // A frame that's pending deletion should never be navigated. If this happens,
- // log a DumpWithoutCrashing to understand the root cause.
- // See https://crbug.com/926820 and https://crbug.com/927705.
- if (!current_frame_host()->is_active()) {
+ // Inactive frames should never be navigated. If this happens, log a
+ // DumpWithoutCrashing to understand the root cause. See
+ // https://crbug.com/926820 and https://crbug.com/927705.
+ if (current_frame_host()->IsInactiveAndDisallowReactivation()) {
NOTREACHED() << "Navigation in an inactive frame";
DEBUG_ALIAS_FOR_GURL(url, request->common_params().url);
base::debug::DumpWithoutCrashing();
@@ -820,23 +687,8 @@ RenderFrameHostImpl* RenderFrameHostManager::GetFrameHostForNavigation(
// If a crashed RenderFrameHost must not be reused, replace it by a
// new one immediately.
- if (render_frame_host_->must_be_replaced()) {
+ if (render_frame_host_->must_be_replaced())
use_current_rfh = false;
- // TODO(https://crbug.com/1006814): Remove this.
- if (render_frame_host_->render_view_host()
- ->GetWidget()
- ->renderer_initialized()) {
- static auto* crash_key = base::debug::AllocateCrashKeyString(
- "IsRenderFrameLive", base::debug::CrashKeySize::Size32);
- std::string message = base::StringPrintf(
- "rdu=%d", IsRendererDebugURL(request->common_params().url));
- // This string is whitelisted for collection from Android Webview. It must
- // only contain booleans to avoid leaking any PII.
- base::debug::SetCrashKeyString(crash_key, message);
- base::debug::DumpWithoutCrashing();
- NOTREACHED();
- }
- }
// Force using a different RenderFrameHost when RenderDocument is enabled.
// TODO(arthursonzogni, fergal): Add support for the main frame.
@@ -955,10 +807,13 @@ RenderFrameHostImpl* RenderFrameHostManager::GetFrameHostForNavigation(
// done earlier to keep browser and renderer state in sync. This is
// important to do before CommitPending(), which destroys the
// corresponding proxy. See https://crbug.com/487872.
+ // TODO(https://crbug.com/1072817): Make this logic more robust to
+ // consider the case for failed navigations after CommitPending.
if (GetRenderFrameProxyHost(dest_site_instance.get()))
navigation_rfh->SwapIn();
+ navigation_rfh->OnCommittedSpeculativeBeforeNavigationCommit();
CommitPending(std::move(speculative_render_frame_host_), nullptr,
- request->require_coop_browsing_instance_swap());
+ request->coop_status().require_browsing_instance_swap);
}
}
DCHECK(navigation_rfh &&
@@ -1222,12 +1077,13 @@ void RenderFrameHostManager::TransferUserActivationFrom(
for (const auto& pair : proxy_hosts_) {
SiteInstance* site_instance = pair.second->GetSiteInstance();
if (site_instance != source_rfh->GetSiteInstance()) {
- int32_t source_routing_id =
+ base::Optional<base::UnguessableToken> source_frame_token =
source_rfh->frame_tree_node()
->render_manager()
- ->GetRoutingIdForSiteInstance(site_instance);
- pair.second->Send(new FrameMsg_TransferUserActivationFrom(
- pair.second->GetRoutingID(), source_routing_id));
+ ->GetFrameTokenForSiteInstance(site_instance);
+ DCHECK(source_frame_token.has_value());
+ pair.second->GetAssociatedRemoteFrame()->TransferUserActivationToRenderer(
+ source_frame_token.value());
}
}
}
@@ -1281,12 +1137,17 @@ RenderFrameHostManager::ShouldSwapBrowsingInstancesForNavigation(
ui::PageTransition transition,
bool is_failure,
bool is_reload,
+ bool is_same_document,
bool cross_origin_opener_policy_mismatch,
- bool was_server_redirect) {
+ bool was_server_redirect,
+ bool should_replace_current_entry) {
// A subframe must stay in the same BrowsingInstance as its parent.
if (!frame_tree_node_->IsMainFrame())
return ShouldSwapBrowsingInstance::kNo_NotMainFrame;
+ if (is_same_document)
+ return ShouldSwapBrowsingInstance::kNo_SameDocumentNavigation;
+
// If this navigation is reloading an error page, do not swap BrowsingInstance
// and keep the error page in a related SiteInstance. If later a reload of
// this navigation is successful, it will correctly create a new
@@ -1425,10 +1286,101 @@ RenderFrameHostManager::ShouldSwapBrowsingInstancesForNavigation(
return ShouldSwapBrowsingInstance::kYes_ForceSwap;
}
- // Experimental mode to swap BrowsingInstances on most cross-site navigations
- // when there are no other windows in the BrowsingInstance.
- return ShouldProactivelySwapBrowsingInstance(render_frame_host_.get(),
- destination_effective_url);
+ // Experimental mode to swap BrowsingInstances on most navigations when there
+ // are no other windows in the BrowsingInstance.
+ return ShouldProactivelySwapBrowsingInstance(destination_url, is_reload,
+ should_replace_current_entry);
+}
+
+ShouldSwapBrowsingInstance
+RenderFrameHostManager::ShouldProactivelySwapBrowsingInstance(
+ const GURL& destination_url,
+ bool is_reload,
+ bool should_replace_current_entry) {
+ // We should only do proactive swap when either the flag is enabled, or if
+ // it's needed for the back-forward cache (and the bfcache flag is enabled).
+ if (!IsProactivelySwapBrowsingInstanceEnabled() &&
+ !IsBackForwardCacheEnabled())
+ return ShouldSwapBrowsingInstance::kNo_ProactiveSwapDisabled;
+
+ // Only main frames are eligible to swap BrowsingInstances.
+ if (!render_frame_host_->frame_tree_node()->IsMainFrame())
+ return ShouldSwapBrowsingInstance::kNo_NotMainFrame;
+
+ // Skip cases when there are other windows that might script this one.
+ SiteInstanceImpl* current_instance = render_frame_host_->GetSiteInstance();
+ if (current_instance->GetRelatedActiveContentsCount() > 1u)
+ return ShouldSwapBrowsingInstance::kNo_HasRelatedActiveContents;
+
+ // "about:blank" and chrome-native-URL do not "use" a SiteInstance. This
+ // allows the SiteInstance to be reused cross-site. Starting a new
+ // BrowsingInstance would prevent the SiteInstance to be reused, that's why
+ // this case is excluded here.
+ if (!current_instance->HasSite())
+ return ShouldSwapBrowsingInstance::kNo_DoesNotHaveSite;
+
+ // Exclude non http(s) schemes. Some tests don't expect navigations to
+ // data-URL or to about:blank to switch to a different BrowsingInstance.
+ const GURL& current_url = render_frame_host_->GetLastCommittedURL();
+ if (!current_url.SchemeIsHTTPOrHTTPS())
+ return ShouldSwapBrowsingInstance::kNo_SourceURLSchemeIsNotHTTPOrHTTPS;
+
+ const GURL& destination_effective_url = SiteInstanceImpl::GetEffectiveURL(
+ current_instance->GetBrowserContext(), destination_url);
+ if (!destination_effective_url.SchemeIsHTTPOrHTTPS())
+ return ShouldSwapBrowsingInstance::kNo_DestinationURLSchemeIsNotHTTPOrHTTPS;
+
+ // WebView guests currently need to stay in the same SiteInstance and
+ // BrowsingInstance.
+ if (current_instance->IsGuest())
+ return ShouldSwapBrowsingInstance::kNo_Guest;
+
+ // We should check whether the new page will result in adding a new history
+ // entry or not. If not, we should not do a proactive BrowsingInstance swap,
+ // because these navigations are not interesting for bfcache (the old page
+ // will not get into the bfcache). Cases include:
+ // 1) When we know we're going to replace the history entry.
+ if (should_replace_current_entry)
+ return ShouldSwapBrowsingInstance::kNo_WillReplaceEntry;
+ // Navigations where we will reuse the history entry:
+ // 2) Different-document but same-page navigations. These navigations are
+ // not classified as same-document (which got filtered earlier) so they will
+ // use a different document, but they will later on be classified as
+ // SAME_PAGE and will reuse the history entry.
+ // TODO(crbug.com/536102): When the SAME_PAGE navigation type gets removed,
+ // we should remove this part as well.
+ bool is_same_page = current_url.EqualsIgnoringRef(destination_url);
+ if (is_same_page)
+ return ShouldSwapBrowsingInstance::kNo_SamePageNavigation;
+ // 3) Reloads. Note that most reloads will not actually reach this part, as
+ // ShouldSwapBrowsingInstancesForNavigation will return early if the reload
+ // has a destination SiteInstance. Reloads that don't have a destination
+ // SiteInstance include: doing reload after a replaceState call, reloading a
+ // URL for which we've just installed a hosted app, and duplicating a tab.
+ if (is_reload)
+ return ShouldSwapBrowsingInstance::kNo_Reload;
+
+ if (IsCurrentlySameSite(
+ static_cast<RenderFrameHostImpl*>(render_frame_host_.get()),
+ destination_url)) {
+ if (IsProactivelySwapBrowsingInstanceOnSameSiteNavigationEnabled())
+ return ShouldSwapBrowsingInstance::kYes_SameSiteProactiveSwap;
+ return ShouldSwapBrowsingInstance::kNo_SameSiteNavigation;
+ }
+
+ if (IsProactivelySwapBrowsingInstanceEnabled())
+ return ShouldSwapBrowsingInstance::kYes_CrossSiteProactiveSwap;
+
+ // If BackForwardCache is enabled, swap BrowsingInstances only when needed
+ // for back-forward cache.
+ DCHECK(IsBackForwardCacheEnabled());
+ NavigationControllerImpl* controller = static_cast<NavigationControllerImpl*>(
+ render_frame_host_->frame_tree_node()->navigator().GetController());
+ if (controller->GetBackForwardCache().IsAllowed(current_url)) {
+ return ShouldSwapBrowsingInstance::kYes_CrossSiteProactiveSwap;
+ } else {
+ return ShouldSwapBrowsingInstance::kNo_NotNeededForBackForwardCache;
+ }
}
scoped_refptr<SiteInstance>
@@ -1440,10 +1392,12 @@ RenderFrameHostManager::GetSiteInstanceForNavigation(
ui::PageTransition transition,
bool is_failure,
bool is_reload,
+ bool is_same_document,
bool dest_is_restore,
bool dest_is_view_source_mode,
bool was_server_redirect,
- bool cross_origin_opener_policy_mismatch) {
+ bool cross_origin_opener_policy_mismatch,
+ bool should_replace_current_entry) {
// On renderer-initiated navigations, when the frame initiating the navigation
// and the frame being navigated differ, |source_instance| is set to the
// SiteInstance of the initiating frame. |dest_instance| is present on session
@@ -1476,7 +1430,7 @@ RenderFrameHostManager::GetSiteInstanceForNavigation(
!render_frame_host_->last_successful_url().is_empty()
? SiteInstanceImpl::GetEffectiveURL(
browser_context, render_frame_host_->last_successful_url())
- : render_frame_host_->GetSiteInstance()->GetSiteURL();
+ : render_frame_host_->GetSiteInstance()->GetSiteInfo().site_url();
// Determine if the current RenderFrameHost is in view source mode.
// TODO(clamy): If the current_effective_url doesn't match the last committed
@@ -1495,9 +1449,13 @@ RenderFrameHostManager::GetSiteInstanceForNavigation(
current_effective_url, current_is_view_source_mode, source_instance,
static_cast<SiteInstanceImpl*>(current_instance), dest_instance,
dest_url, dest_is_view_source_mode, transition, is_failure, is_reload,
- cross_origin_opener_policy_mismatch, was_server_redirect);
+ is_same_document, cross_origin_opener_policy_mismatch,
+ was_server_redirect, should_replace_current_entry);
bool proactive_swap =
- (should_swap_result == ShouldSwapBrowsingInstance::kYes_ProactiveSwap);
+ (should_swap_result ==
+ ShouldSwapBrowsingInstance::kYes_CrossSiteProactiveSwap ||
+ should_swap_result ==
+ ShouldSwapBrowsingInstance::kYes_SameSiteProactiveSwap);
bool should_swap =
(should_swap_result == ShouldSwapBrowsingInstance::kYes_ForceSwap) ||
proactive_swap;
@@ -1553,11 +1511,18 @@ RenderFrameHostManager::GetSiteInstanceForNavigation(
REUSE_PENDING_OR_COMMITTED_SITE);
}
}
-
+ bool is_same_site_proactive_swap =
+ (should_swap_result ==
+ ShouldSwapBrowsingInstance::kYes_SameSiteProactiveSwap);
// If we're doing a proactive BI swap, we should try to reuse the current
// SiteInstance's process for the new SiteInstance if possible.
+ // It might not be possible to reuse the process in some cases, including when
+ // the current SiteInstance needs a dedicated process (unless this is a
+ // same-site navigation).
if (IsProactivelySwapBrowsingInstanceWithProcessReuseEnabled() &&
- proactive_swap && !current_instance->RequiresDedicatedProcess()) {
+ proactive_swap &&
+ (!current_instance->RequiresDedicatedProcess() ||
+ is_same_site_proactive_swap)) {
DCHECK(frame_tree_node_->IsMainFrame());
new_instance_impl->ReuseCurrentProcessIfPossible(
current_instance->GetProcess());
@@ -1565,11 +1530,12 @@ RenderFrameHostManager::GetSiteInstanceForNavigation(
return new_instance;
}
-bool RenderFrameHostManager::InitializeRenderFrameForImmediateUse() {
+bool RenderFrameHostManager::InitializeMainRenderFrameForImmediateUse() {
// TODO(jam): this copies some logic inside GetFrameHostForNavigation, which
// also duplicates logic in Navigate. They should all use this method, but
// that involves slight reordering.
// http://crbug.com/794229
+ DCHECK(frame_tree_node_->IsMainFrame());
if (render_frame_host_->IsRenderFrameLive())
return true;
@@ -1590,10 +1556,8 @@ bool RenderFrameHostManager::InitializeRenderFrameForImmediateUse() {
// RenderFrameHostManager are completely initialized. This should be
// removed once the process manager moves away from NotificationService.
// See https://crbug.com/462682.
- if (frame_tree_node_->IsMainFrame()) {
- delegate_->NotifyMainFrameSwappedFromRenderManager(
- nullptr, render_frame_host_.get());
- }
+ delegate_->NotifyMainFrameSwappedFromRenderManager(nullptr,
+ render_frame_host_.get());
return true;
}
@@ -1716,11 +1680,11 @@ RenderFrameHostManager::DetermineSiteInstanceForURL(
// thus use the correct process.
DCHECK_EQ(controller.GetBrowserContext(),
current_instance_impl->GetBrowserContext());
- const GURL dest_site_url = SiteInstanceImpl::GetSiteForURL(
+ const SiteInfo dest_site_info = SiteInstanceImpl::ComputeSiteInfo(
current_instance_impl->GetIsolationContext(), dest_url);
bool use_process_per_site =
- RenderProcessHost::ShouldUseProcessPerSite(
- current_instance_impl->GetBrowserContext(), dest_site_url) &&
+ RenderProcessHostImpl::ShouldUseProcessPerSite(
+ current_instance_impl->GetBrowserContext(), dest_site_info) &&
RenderProcessHostImpl::GetSoleProcessHostForURL(
current_instance_impl->GetIsolationContext(), dest_url);
if (current_instance_impl->HasRelatedSiteInstance(dest_url) ||
@@ -1799,11 +1763,14 @@ RenderFrameHostManager::DetermineSiteInstanceForURL(
}
// Keep subframes in the parent's SiteInstance unless a dedicated process is
- // required for either the parent or the subframe's destination URL. This
- // isn't a strict invariant but rather a heuristic to avoid unnecessary
- // OOPIFs; see https://crbug.com/711006.
- //
- // TODO(alexmos): Remove this check after fixing https://crbug.com/787576.
+ // required for either the parent or the subframe's destination URL. Although
+ // this consolidation is usually handled by default SiteInstances, there are
+ // some corner cases in which default SiteInstances cannot currently be used,
+ // such as file: URLs. This logic prevents unneeded OOPIFs in those cases.
+ // This turns out to be important for correctness on Android Webview, which
+ // does not yet support OOPIFs (https://crbug.com/1101214).
+ // TODO(https://crbug.com/1103352): Remove this block when default
+ // SiteInstances support file: URLs.
//
// Also if kProcessSharingWithStrictSiteInstances is enabled, don't lump the
// subframe into the same SiteInstance as the parent. These separate
@@ -2188,9 +2155,6 @@ RenderFrameHostManager::CreateSpeculativeRenderFrame(SiteInstance* instance) {
RenderViewHostImpl* render_view_host =
new_render_frame_host->render_view_host();
- // TODO(https://crbug.com/1006814): Remove this.
- bool recreated_main_frame = false;
- bool widget_renderer_initialized = false;
if (frame_tree_node_->IsMainFrame()) {
if (render_view_host == render_frame_host_->render_view_host()) {
// We are replacing the main frame's host with |new_render_frame_host|.
@@ -2200,9 +2164,6 @@ RenderFrameHostManager::CreateSpeculativeRenderFrame(SiteInstance* instance) {
// GetFrameHostForNavigation() before yielding to other tasks.
render_view_host->SetMainFrameRoutingId(
new_render_frame_host->GetRoutingID());
- recreated_main_frame = true;
- widget_renderer_initialized =
- render_view_host->GetWidget()->renderer_initialized();
}
if (!InitRenderView(render_view_host, GetRenderFrameProxyHost(instance)))
@@ -2219,32 +2180,6 @@ RenderFrameHostManager::CreateSpeculativeRenderFrame(SiteInstance* instance) {
}
DCHECK(render_view_host->IsRenderViewLive());
- // TODO(https://crbug.com/1006814): Remove this.
- if (recreated_main_frame && !new_render_frame_host->IsRenderFrameLive()) {
- static auto* crash_key = base::debug::AllocateCrashKeyString(
- "IsRenderFrameLive", base::debug::CrashKeySize::Size256);
- int main_rfh_routing_id =
- render_view_host->GetMainFrameRoutingIdForCrbug1006814();
- std::string message = base::StringPrintf(
- "created=%d,process=%d,proxy=%d,widget=%d,main_rfh=%d,new_rfh=%d,"
- "in_pdo=%d,in_cpdo=%d,wi=%d",
- new_render_frame_host->IsRenderFrameCreated(),
- new_render_frame_host->GetProcess()->IsInitializedAndNotDead(),
- !!GetRenderFrameProxyHost(instance), widget_renderer_initialized,
- main_rfh_routing_id, new_render_frame_host->routing_id(),
- static_cast<RenderProcessHostImpl*>(
- render_view_host->GetWidget()->GetProcess())
- ->GetWithinProcessDiedObserverForCrbug1006814(),
- static_cast<RenderProcessHostImpl*>(
- render_view_host->GetWidget()->GetProcess())
- ->GetWithinCleanupProcessDiedObserverForCrbug1006814(),
- render_view_host->GetWidget()->get_initializer_for_crbug_1006814());
- // This string is whitelisted for collection from Android Webview. It must
- // only contain booleans to avoid leaking any PII.
- base::debug::SetCrashKeyString(crash_key, message);
- base::debug::DumpWithoutCrashing();
- NOTREACHED() << message;
- }
// RenderViewHost for |instance| might exist prior to calling
// CreateRenderFrame. In such a case, InitRenderView will not create the
// RenderFrame in the renderer process and it needs to be done
@@ -2373,18 +2308,12 @@ bool RenderFrameHostManager::InitRenderView(
if (render_view_host->IsRenderViewLive())
return true;
- int opener_frame_routing_id =
- GetOpenerRoutingID(render_view_host->GetSiteInstance());
+ auto opener_frame_token =
+ GetOpenerFrameToken(render_view_host->GetSiteInstance());
bool created = delegate_->CreateRenderViewForRenderManager(
- render_view_host, opener_frame_routing_id,
- proxy ? proxy->GetRoutingID() : MSG_ROUTING_NONE,
- proxy
- ? proxy->GetFrameToken()
- : static_cast<RenderFrameHostImpl*>(render_view_host->GetMainFrame())
- ->frame_token(),
- frame_tree_node_->devtools_frame_token(),
- frame_tree_node_->current_replication_state());
+ render_view_host, opener_frame_token,
+ proxy ? proxy->GetRoutingID() : MSG_ROUTING_NONE);
if (created && proxy) {
proxy->SetRenderFrameProxyCreated(true);
@@ -2436,50 +2365,15 @@ RenderFrameHostManager::GetSiteInstanceForNavigationRequest(
request->common_params().navigation_type ==
mojom::NavigationType::RELOAD_ORIGINAL_REQUEST_URL;
- // Retrieve COOP and COEP from the response headers. If we don't have the
- // headers yet we try to inherit the current page COOP/COEP to have a
- // relevant speculative RFH.
- network::mojom::CrossOriginOpenerPolicyValue coop;
- network::mojom::CrossOriginEmbedderPolicyValue coep;
- if (auto* response = request->response()) {
- coop = response->parsed_headers->cross_origin_opener_policy.value;
- coep = response->parsed_headers->cross_origin_embedder_policy.value;
- } else {
- // The heuristic for inheriting is to have the most conservative approach
- // towards BrowsingInstance switching. Every same-origin navigation should
- // yield a no swap decision. This is done to work with the renderer crash
- // optimization that instantly commits the speculative RenderFrameHost.
- bool inherit_coop =
- render_frame_host_->has_committed_any_navigation() ||
- render_frame_host_->cross_origin_opener_policy().value ==
- network::mojom::CrossOriginOpenerPolicyValue::kSameOrigin;
- coop = inherit_coop
- ? render_frame_host_->cross_origin_opener_policy().value
- : network::mojom::CrossOriginOpenerPolicyValue::kUnsafeNone;
- coep = render_frame_host_->cross_origin_embedder_policy().value;
- }
-
- bool cross_origin_policy_swap =
- frame_tree_node_->IsMainFrame() &&
- !request->common_params().url.IsAboutBlank() &&
- ShouldSwapBrowsingInstanceForCrossOriginOpenerPolicy(
- render_frame_host_->cross_origin_opener_policy().value,
- render_frame_host_->cross_origin_embedder_policy().value,
- render_frame_host_->GetLastCommittedOrigin(),
- !render_frame_host_->has_committed_any_navigation(), coop, coep,
- url::Origin::Create(request->common_params().url));
-
- if (cross_origin_policy_swap)
- request->set_require_coop_browsing_instance_swap();
-
scoped_refptr<SiteInstance> dest_site_instance = GetSiteInstanceForNavigation(
request->common_params().url, request->GetSourceSiteInstance(),
request->dest_site_instance(), candidate_site_instance,
request->common_params().transition,
request->state() >= NavigationRequest::CANCELING, is_reload,
- request->GetRestoreType() != RestoreType::NONE, request->is_view_source(),
- request->WasServerRedirect(),
- request->require_coop_browsing_instance_swap());
+ request->IsSameDocument(), request->GetRestoreType() != RestoreType::NONE,
+ request->is_view_source(), request->WasServerRedirect(),
+ request->coop_status().require_browsing_instance_swap,
+ request->common_params().should_replace_current_entry);
// If the NavigationRequest's dest_site_instance was present but incorrect,
// then ensure no sensitive state is kept on the request. This can happen for
@@ -2499,9 +2393,9 @@ bool RenderFrameHostManager::InitRenderFrame(
SiteInstance* site_instance = render_frame_host->GetSiteInstance();
- int opener_routing_id = MSG_ROUTING_NONE;
+ base::Optional<base::UnguessableToken> opener_frame_token;
if (frame_tree_node_->opener())
- opener_routing_id = GetOpenerRoutingID(site_instance);
+ opener_frame_token = GetOpenerFrameToken(site_instance);
int parent_routing_id = MSG_ROUTING_NONE;
if (frame_tree_node_->parent()) {
@@ -2548,7 +2442,7 @@ bool RenderFrameHostManager::InitRenderFrame(
}
return delegate_->CreateRenderFrameForRenderManager(
- render_frame_host, previous_routing_id, opener_routing_id,
+ render_frame_host, previous_routing_id, opener_frame_token,
parent_routing_id, previous_sibling_routing_id);
}
@@ -2614,6 +2508,19 @@ int RenderFrameHostManager::GetRoutingIdForSiteInstance(
return MSG_ROUTING_NONE;
}
+base::Optional<base::UnguessableToken>
+RenderFrameHostManager::GetFrameTokenForSiteInstance(
+ SiteInstance* site_instance) {
+ if (render_frame_host_->GetSiteInstance() == site_instance)
+ return render_frame_host_->GetFrameToken();
+
+ RenderFrameProxyHost* proxy = GetRenderFrameProxyHost(site_instance);
+ if (proxy)
+ return proxy->GetFrameToken();
+
+ return base::nullopt;
+}
+
void RenderFrameHostManager::CommitPending(
std::unique_ptr<RenderFrameHostImpl> pending_rfh,
std::unique_ptr<BackForwardCacheImpl::Entry> pending_bfcache_entry,
@@ -3004,11 +2911,10 @@ void RenderFrameHostManager::CreateOpenerProxies(
if (!proxy)
continue;
- int opener_routing_id =
- node->render_manager()->GetOpenerRoutingID(instance);
- DCHECK_NE(opener_routing_id, MSG_ROUTING_NONE);
- proxy->Send(
- new FrameMsg_UpdateOpener(proxy->GetRoutingID(), opener_routing_id));
+ auto opener_frame_token =
+ node->render_manager()->GetOpenerFrameToken(instance);
+ DCHECK(opener_frame_token);
+ proxy->GetAssociatedRemoteFrame()->UpdateOpener(opener_frame_token);
}
}
@@ -3030,13 +2936,14 @@ void RenderFrameHostManager::CreateOpenerProxiesForFrameTree(
frame_tree->CreateProxiesForSiteInstance(skip_this_node, instance);
}
-int RenderFrameHostManager::GetOpenerRoutingID(SiteInstance* instance) {
+base::Optional<base::UnguessableToken>
+RenderFrameHostManager::GetOpenerFrameToken(SiteInstance* instance) {
if (!frame_tree_node_->opener())
- return MSG_ROUTING_NONE;
+ return base::nullopt;
return frame_tree_node_->opener()
->render_manager()
- ->GetRoutingIdForSiteInstance(instance);
+ ->GetFrameTokenForSiteInstance(instance);
}
void RenderFrameHostManager::SendPageMessage(IPC::Message* msg,
@@ -3166,8 +3073,8 @@ void RenderFrameHostManager::NotifyPrepareForInnerDelegateAttachComplete(
int32_t routing_id =
success ? render_frame_host_->GetRoutingID() : MSG_ROUTING_NONE;
// Invoking the callback asynchronously to meet the APIs promise.
- base::PostTask(
- FROM_HERE, {BrowserThread::UI},
+ GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE,
base::BindOnce(
[](RenderFrameHost::PrepareForInnerWebContentsAttachCallback callback,
int32_t process_id, int32_t routing_id) {
diff --git a/chromium/content/browser/frame_host/render_frame_host_manager.h b/chromium/content/browser/frame_host/render_frame_host_manager.h
index a7fa84f147d..9d03c5a3f42 100644
--- a/chromium/content/browser/frame_host/render_frame_host_manager.h
+++ b/chromium/content/browser/frame_host/render_frame_host_manager.h
@@ -10,12 +10,14 @@
#include <list>
#include <map>
#include <memory>
+#include <set>
#include <unordered_map>
#include <unordered_set>
-#include "base/logging.h"
+#include "base/containers/unique_ptr_adapters.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
#include "content/browser/frame_host/back_forward_cache_impl.h"
#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/frame_host/should_swap_browsing_instance.h"
@@ -31,6 +33,7 @@
#include "url/origin.h"
namespace content {
+class FrameTree;
class FrameTreeNode;
class NavigationControllerImpl;
class NavigationEntry;
@@ -42,7 +45,6 @@ class RenderViewHost;
class RenderViewHostImpl;
class RenderWidgetHostView;
class TestWebContents;
-struct FrameReplicationState;
// Manages RenderFrameHosts for a FrameTreeNode. It maintains a
// current_frame_host() which is the content currently visible to the user. When
@@ -116,17 +118,14 @@ class CONTENT_EXPORT RenderFrameHostManager
// automatically called from LoadURL.
virtual bool CreateRenderViewForRenderManager(
RenderViewHost* render_view_host,
- int opener_frame_routing_id,
- int proxy_routing_id,
- const base::UnguessableToken& frame_token,
- const base::UnguessableToken& devtools_frame_token,
- const FrameReplicationState& replicated_frame_state) = 0;
+ const base::Optional<base::UnguessableToken>& opener_frame_token,
+ int proxy_routing_id) = 0;
virtual void CreateRenderWidgetHostViewForRenderManager(
RenderViewHost* render_view_host) = 0;
virtual bool CreateRenderFrameForRenderManager(
RenderFrameHost* render_frame_host,
int proxy_routing_id,
- int opener_routing_id,
+ const base::Optional<base::UnguessableToken>& opener_frame_token,
int parent_routing_id,
int previous_sibling_routing_id) = 0;
virtual void BeforeUnloadFiredFromRenderManager(
@@ -259,12 +258,12 @@ class CONTENT_EXPORT RenderFrameHostManager
const blink::FramePolicy& frame_policy);
// Called when this frame's opener is changed to the frame specified by
- // |opener_routing_id| in |source_site_instance|'s process. This change
+ // |opener_frame_token| in |source_site_instance|'s process. This change
// could come from either the current RenderFrameHost or one of the
// proxies (e.g., window.open that targets a RemoteFrame by name). The
// updated opener will be forwarded to any other RenderFrameProxies and
// RenderFrames for this FrameTreeNode.
- void DidChangeOpener(int opener_routing_id,
+ void DidChangeOpener(const base::UnguessableToken& opener_frame_token,
SiteInstance* source_site_instance);
// Creates and initializes a RenderFrameHost.
@@ -305,6 +304,12 @@ class CONTENT_EXPORT RenderFrameHostManager
// RenderFrameHostManager. Returns MSG_ROUTING_NONE if none is found.
int GetRoutingIdForSiteInstance(SiteInstance* site_instance);
+ // Returns the frame token for a RenderFrameHost or RenderFrameProxyHost
+ // that has the given SiteInstance and is associated with this
+ // RenderFrameHostManager. Returns base::nullopt if none is found.
+ base::Optional<base::UnguessableToken> GetFrameTokenForSiteInstance(
+ SiteInstance* site_instance);
+
// Notifies the RenderFrameHostManager that a new NavigationRequest has been
// created and set in the FrameTreeNode so that it can speculatively create a
// new RenderFrameHost (and potentially a new process) if needed.
@@ -396,12 +401,13 @@ class CONTENT_EXPORT RenderFrameHostManager
// https://crbug.com/511474.
void CreateProxiesForNewNamedFrame();
- // Returns a routing ID for the current FrameTreeNode's opener node in the
- // given SiteInstance. May return a routing ID of either a RenderFrameHost
- // (if opener's current or pending RFH has SiteInstance |instance|) or a
- // RenderFrameProxyHost. Returns MSG_ROUTING_NONE if there is no opener, or
- // if the opener node doesn't have a proxy for |instance|.
- int GetOpenerRoutingID(SiteInstance* instance);
+ // Returns a base::UnguessableToken for the current FrameTreeNode's opener
+ // node in the given SiteInstance. May return a frame token of either a
+ // RenderFrameHost (if opener's current or pending RFH has SiteInstance
+ // |instance|) or a RenderFrameProxyHost. Returns base::nullopt if there is
+ // no opener, or if the opener node doesn't have a proxy for |instance|.
+ base::Optional<base::UnguessableToken> GetOpenerFrameToken(
+ SiteInstance* instance);
// Called on the RFHM of the inner WebContents to create a
// RenderFrameProxyHost in its outer WebContents's SiteInstance,
@@ -482,11 +488,11 @@ class CONTENT_EXPORT RenderFrameHostManager
scoped_refptr<SiteInstance> GetSiteInstanceForNavigationRequest(
NavigationRequest* navigation_request);
- // Helper to initialize the current RenderFrame if it's not initialized.
- // TODO(https://crbug.com/1006814): Remove this. For now debug URLs and
+ // Helper to initialize the main RenderFrame if it's not initialized.
+ // TODO(https://crbug.com/936696): Remove this. For now debug URLs and
// WebView JS execution are an exception to replacing all crashed frames for
// RenderDocument. This is a no-op if the frame is already initialized.
- bool InitializeRenderFrameForImmediateUse();
+ bool InitializeMainRenderFrameForImmediateUse();
// Prepares the FrameTreeNode for attaching an inner WebContents. This step
// may involve replacing |current_frame_host()| with a new RenderFrameHost
@@ -599,8 +605,15 @@ class CONTENT_EXPORT RenderFrameHostManager
ui::PageTransition transition,
bool is_failure,
bool is_reload,
+ bool is_same_document,
bool cross_origin_opener_policy_mismatch,
- bool was_server_redirect);
+ bool was_server_redirect,
+ bool should_replace_current_entry);
+
+ ShouldSwapBrowsingInstance ShouldProactivelySwapBrowsingInstance(
+ const GURL& destination_url,
+ bool is_reload,
+ bool should_replace_current_entry);
// Returns the SiteInstance to use for the navigation.
scoped_refptr<SiteInstance> GetSiteInstanceForNavigation(
@@ -611,10 +624,12 @@ class CONTENT_EXPORT RenderFrameHostManager
ui::PageTransition transition,
bool is_failure,
bool is_reload,
+ bool is_same_document,
bool dest_is_restore,
bool dest_is_view_source_mode,
bool was_server_redirect,
- bool cross_origin_opener_policy_mismatch);
+ bool cross_origin_opener_policy_mismatch,
+ bool should_replace_current_entry);
// Returns a descriptor of the appropriate SiteInstance object for the given
// |dest_url|, possibly reusing the current, source or destination
@@ -821,9 +836,10 @@ class CONTENT_EXPORT RenderFrameHostManager
// Proxy hosts, indexed by site instance ID.
RenderFrameProxyHostMap proxy_hosts_;
- // A list of RenderFrameHosts waiting to shut down after swapping out.
- using RFHPendingDeleteList = std::list<std::unique_ptr<RenderFrameHostImpl>>;
- RFHPendingDeleteList pending_delete_hosts_;
+ // A set of RenderFrameHosts waiting to shut down after swapping out.
+ using RFHPendingDeleteSet =
+ std::set<std::unique_ptr<RenderFrameHostImpl>, base::UniquePtrComparator>;
+ RFHPendingDeleteSet pending_delete_hosts_;
// Stores a speculative RenderFrameHost which is created early in a navigation
// so a renderer process can be started in parallel, if needed.
diff --git a/chromium/content/browser/frame_host/render_frame_host_manager_browsertest.cc b/chromium/content/browser/frame_host/render_frame_host_manager_browsertest.cc
index b0028e5be49..8be9a86361d 100644
--- a/chromium/content/browser/frame_host/render_frame_host_manager_browsertest.cc
+++ b/chromium/content/browser/frame_host/render_frame_host_manager_browsertest.cc
@@ -5660,7 +5660,7 @@ IN_PROC_BROWSER_TEST_P(
static_cast<SiteInstanceImpl*>(
web_contents->GetMainFrame()->GetSiteInstance());
- // Check that A and B are in different SiteInstances (both are in default
+ // Check that A and B are in different BrowsingInstances (both are in default
// SiteInstances of different BrowsingInstances) but have the same renderer
// process.
EXPECT_FALSE(a_site_instance->IsRelatedSiteInstance(b_site_instance.get()));
@@ -5695,7 +5695,7 @@ IN_PROC_BROWSER_TEST_P(
static_cast<SiteInstanceImpl*>(
web_contents->GetMainFrame()->GetSiteInstance());
- // Check that A and B are in different SiteInstances (both are in default
+ // Check that A and B are in different BrowsingInstances (both are in default
// SiteInstances of different BrowsingInstances) and renderer processes.
EXPECT_FALSE(a_site_instance->IsRelatedSiteInstance(b_site_instance.get()));
EXPECT_TRUE(a_site_instance->IsDefaultSiteInstance());
@@ -5755,7 +5755,7 @@ IN_PROC_BROWSER_TEST_P(
static_cast<SiteInstanceImpl*>(
web_contents->GetMainFrame()->GetSiteInstance());
- // Check that A and B are in different SiteInstances (both are in default
+ // Check that A and B are in different BrowsingInstances (both are in default
// SiteInstances of different BrowsingInstances) but have the same renderer
// process.
EXPECT_FALSE(a_site_instance->IsRelatedSiteInstance(b_site_instance.get()));
@@ -5775,7 +5775,7 @@ IN_PROC_BROWSER_TEST_P(
static_cast<SiteInstanceImpl*>(
web_contents->GetMainFrame()->GetSiteInstance());
- // Check that B and C are in different SiteInstances (both are in default
+ // Check that B and C are in different BrowsingInstances (both are in default
// SiteInstances of different BrowsingInstances) and renderer processes.
EXPECT_FALSE(b_site_instance->IsRelatedSiteInstance(c_site_instance.get()));
EXPECT_TRUE(c_site_instance->IsDefaultSiteInstance());
@@ -5852,7 +5852,7 @@ IN_PROC_BROWSER_TEST_P(
static_cast<SiteInstanceImpl*>(
web_contents->GetMainFrame()->GetSiteInstance());
- // Check that A and B are in different SiteInstances (both are in default
+ // Check that A and B are in different BrowsingInstances (both are in default
// SiteInstances of different BrowsingInstances) but B should use the sole
// process assigned to site B.
EXPECT_FALSE(a_site_instance->IsRelatedSiteInstance(b_site_instance.get()));
@@ -5868,6 +5868,529 @@ IN_PROC_BROWSER_TEST_P(
SetBrowserClientForTesting(old_client);
}
+// We should not reuse the current process on renderer-initiated navigations to
+// sites that require a dedicated process.
+IN_PROC_BROWSER_TEST_P(
+ ProactivelySwapBrowsingInstancesCrossSiteReuseProcessTest,
+ NavigationToSiteThatRequiresDedicatedProcess) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+ GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
+ GURL b_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
+
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+ // The client will make sure b.com require a dedicated process.
+ EffectiveURLContentBrowserClient modified_client(
+ b_url /* url_to_modify */, b_url, true /* requires_dedicated_process */);
+ ContentBrowserClient* old_client =
+ SetBrowserClientForTesting(&modified_client);
+ // 1) Navigate to A.
+ EXPECT_TRUE(NavigateToURL(shell(), a_url));
+ scoped_refptr<SiteInstanceImpl> a_site_instance =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+ EXPECT_FALSE(a_site_instance->RequiresDedicatedProcess());
+
+ // 2) Navigate cross-site to B. The navigation is document/renderer initiated.
+ EXPECT_TRUE(NavigateToURLFromRenderer(shell(), b_url));
+ scoped_refptr<SiteInstanceImpl> b_site_instance =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+ EXPECT_TRUE(b_site_instance->RequiresDedicatedProcess());
+
+ // Check that A and B are in different BrowsingInstances and processes.
+ EXPECT_FALSE(a_site_instance->IsRelatedSiteInstance(b_site_instance.get()));
+ EXPECT_NE(a_site_instance->GetProcess(), b_site_instance->GetProcess());
+ SetBrowserClientForTesting(old_client);
+}
+
+// We should not reuse the current process on renderer-initiated navigations to
+// sites that require a dedicated process.
+IN_PROC_BROWSER_TEST_P(
+ ProactivelySwapBrowsingInstancesCrossSiteReuseProcessTest,
+ NavigationFromSiteThatRequiresDedicatedProcess) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+ GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
+ GURL b_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
+
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+ // The client will make sure a.com require a dedicated process.
+ EffectiveURLContentBrowserClient modified_client(
+ a_url /* url_to_modify */, a_url, true /* requires_dedicated_process */);
+ ContentBrowserClient* old_client =
+ SetBrowserClientForTesting(&modified_client);
+ // 1) Navigate to A.
+ EXPECT_TRUE(NavigateToURL(shell(), a_url));
+ scoped_refptr<SiteInstanceImpl> a_site_instance =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+ EXPECT_TRUE(a_site_instance->RequiresDedicatedProcess());
+
+ // 2) Navigate cross-site to B. The navigation is document/renderer initiated.
+ EXPECT_TRUE(NavigateToURLFromRenderer(shell(), b_url));
+ scoped_refptr<SiteInstanceImpl> b_site_instance =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+ EXPECT_FALSE(b_site_instance->RequiresDedicatedProcess());
+
+ // Check that A and B are in different BrowsingInstances and processes.
+ EXPECT_FALSE(a_site_instance->IsRelatedSiteInstance(b_site_instance.get()));
+ EXPECT_NE(a_site_instance->GetProcess(), b_site_instance->GetProcess());
+ SetBrowserClientForTesting(old_client);
+}
+
+class ProactivelySwapBrowsingInstancesSameSiteTest
+ : public RenderFrameHostManagerTest {
+ public:
+ ProactivelySwapBrowsingInstancesSameSiteTest() {
+ std::map<std::string, std::string> parameters;
+ parameters[kProactivelySwapBrowsingInstanceLevelParameterName] = "SameSite";
+ feature_list_.InitAndEnableFeatureWithParameters(
+ features::kProactivelySwapBrowsingInstance, parameters);
+ }
+
+ ~ProactivelySwapBrowsingInstancesSameSiteTest() override = default;
+
+ private:
+ base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesSameSiteTest,
+ RendererInitiatedSameSiteNavigationReusesProcess) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+ GURL url_1(embedded_test_server()->GetURL("/title1.html"));
+ GURL url_2(embedded_test_server()->GetURL("/title2.html"));
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+
+ // Navigate to title1.html.
+ EXPECT_TRUE(NavigateToURL(shell(), url_1));
+ scoped_refptr<SiteInstanceImpl> site_instance_1 =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+ // Navigate to title2.html. The navigation is document/renderer initiated.
+ EXPECT_TRUE(NavigateToURLFromRenderer(shell(), url_2));
+ scoped_refptr<SiteInstanceImpl> site_instance_2 =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+
+ // Check that title1.html and title2.html are in different BrowsingInstances
+ // but have the same renderer process.
+ EXPECT_FALSE(site_instance_1->IsRelatedSiteInstance(site_instance_2.get()));
+ EXPECT_EQ(site_instance_1->GetProcess(), site_instance_2->GetProcess());
+}
+
+IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesSameSiteTest,
+ BrowserInitiatedSameSiteNavigationReusesProcess) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+ GURL url_1(embedded_test_server()->GetURL("/title1.html"));
+ GURL url_2(embedded_test_server()->GetURL("/title2.html"));
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+
+ // 1) Navigate to title1.html.
+ EXPECT_TRUE(NavigateToURL(shell(), url_1));
+ scoped_refptr<SiteInstanceImpl> site_instance_1 =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+ // 2) Navigate to title2.html. The navigation is browser initiated.
+ EXPECT_TRUE(NavigateToURL(shell(), url_2));
+ scoped_refptr<SiteInstanceImpl> site_instance_2 =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+
+ // Check that title1.html and title2.html are in different BrowsingInstances
+ // but have the same renderer process.
+ EXPECT_FALSE(site_instance_1->IsRelatedSiteInstance(site_instance_2.get()));
+ EXPECT_EQ(site_instance_1->GetProcess(), site_instance_2->GetProcess());
+
+ // 3) Do a back navigation to title1.html.
+ shell()->web_contents()->GetController().GoBack();
+ EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
+ EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), url_1);
+ scoped_refptr<SiteInstanceImpl> site_instance_1_history_nav =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+
+ // We will reuse the SiteInstance and renderer process of |site_instance_1|.
+ EXPECT_EQ(site_instance_1_history_nav, site_instance_1);
+ EXPECT_EQ(site_instance_1_history_nav->GetProcess(),
+ site_instance_1->GetProcess());
+}
+
+// If the navigation is classified as NAVIGATION_TYPE_SAME_PAGE, or is a same
+// document navigation, we should not do a proactive BrowsingInstance swap.
+// TODO(crbug.com/536102): NAVIGATION_TYPE_SAME_PAGE will be removed in the
+// future, so we should update this test.
+IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesSameSiteTest,
+ SamePageAndSameDocumentNavigationDoesNotSwap) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+
+ // 1) Navigate to title1.html#foo.
+ EXPECT_TRUE(NavigateToURL(
+ shell(), embedded_test_server()->GetURL("/title1.html#foo")));
+ scoped_refptr<SiteInstanceImpl> site_instance_1 =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+
+ // 2) Navigate from title1.html#foo to title1.html.
+ // This is a different-document, same-page navigation.
+ EXPECT_TRUE(
+ NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
+ scoped_refptr<SiteInstanceImpl> site_instance_2 =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+
+ // Check that #1 and #2 are in the same SiteInstance.
+ EXPECT_EQ(site_instance_1, site_instance_2);
+
+ // 3) Navigate from title1.html to title1.html.
+ // This is a different-document, same-page navigation.
+ EXPECT_TRUE(
+ NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
+ scoped_refptr<SiteInstanceImpl> site_instance_3 =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+
+ // We should keep the same SiteInstance again.
+ EXPECT_EQ(site_instance_2, site_instance_3);
+
+ // 4) Navigate from title1.html to title1.html#foo.
+ // This is a same document navigation.
+ EXPECT_TRUE(NavigateToURL(
+ shell(), embedded_test_server()->GetURL("/title1.html#foo")));
+ scoped_refptr<SiteInstanceImpl> site_instance_4 =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+
+ // We should keep the same SiteInstance again.
+ EXPECT_EQ(site_instance_3, site_instance_4);
+
+ // 5) Navigate from title1.html#foo to title1.html#foo.
+ // This is a different-document, same page navigation.
+ EXPECT_TRUE(NavigateToURL(
+ shell(), embedded_test_server()->GetURL("/title1.html#foo")));
+ scoped_refptr<SiteInstanceImpl> site_instance_5 =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+
+ // We should keep the same SiteInstance again.
+ EXPECT_EQ(site_instance_4, site_instance_5);
+
+ // 6) Navigate from title1.html#foo to title1.html#bar.
+ // This is a same document navigation.
+ EXPECT_TRUE(NavigateToURL(
+ shell(), embedded_test_server()->GetURL("/title1.html#bar")));
+ scoped_refptr<SiteInstanceImpl> site_instance_6 =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+
+ // We should keep the same SiteInstance again.
+ EXPECT_EQ(site_instance_5, site_instance_6);
+
+ // 7) Do a history navigation from title1.html#bar to title1.html#foo.
+ // This is a different-document, same-page history navigation.
+ shell()->web_contents()->GetController().GoBack();
+ EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
+ scoped_refptr<SiteInstanceImpl> site_instance_7 =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+
+ // We should keep the same SiteInstance again.
+ EXPECT_EQ(site_instance_6, site_instance_7);
+}
+
+IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesSameSiteTest,
+ ReloadDoesNotSwap) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+ GURL url(embedded_test_server()->GetURL("/title1.html"));
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+ FrameTreeNode* root = web_contents->GetFrameTree()->root();
+
+ // 1) Navigate to title1.html.
+ EXPECT_TRUE(NavigateToURL(shell(), url));
+ scoped_refptr<SiteInstanceImpl> site_instance_1 =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+
+ // 2) Request a reload to happen when the controller becomes active (e.g.
+ // after the renderer gets killed in background on Android).
+ NavigationControllerImpl& controller = static_cast<NavigationControllerImpl&>(
+ shell()->web_contents()->GetController());
+ ASSERT_FALSE(controller.NeedsReload());
+ controller.SetNeedsReload();
+ ASSERT_TRUE(controller.NeedsReload());
+
+ // Set the controller as active, triggering the requested reload.
+ controller.SetActive(true);
+ EXPECT_TRUE(WaitForLoadStop(web_contents));
+ ASSERT_FALSE(controller.NeedsReload());
+ scoped_refptr<SiteInstanceImpl> site_instance_2 =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+ // Check that we're still in the same SiteInstance.
+ EXPECT_EQ(site_instance_1, site_instance_2);
+
+ // 3) Trigger reload using Reload().
+ {
+ TestNavigationObserver reload_observer(shell()->web_contents());
+ shell()->web_contents()->GetController().Reload(ReloadType::NORMAL, false);
+ reload_observer.Wait();
+ EXPECT_TRUE(reload_observer.last_navigation_succeeded());
+ }
+ scoped_refptr<SiteInstanceImpl> site_instance_3 =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+ // Check that we're still in the same SiteInstance.
+ EXPECT_EQ(site_instance_2, site_instance_3);
+
+ // 4) Trigger reload using location.reload().
+ {
+ TestNavigationObserver reload_observer(shell()->web_contents());
+ EXPECT_TRUE(ExecuteScript(shell(), "location.reload();"));
+ reload_observer.Wait();
+ EXPECT_TRUE(reload_observer.last_navigation_succeeded());
+ }
+ scoped_refptr<SiteInstanceImpl> site_instance_4 =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+ // Check that we're still in the same SiteInstance.
+ EXPECT_EQ(site_instance_3, site_instance_4);
+
+ // 5) Do a replaceState to another URL.
+ {
+ GURL url_2(embedded_test_server()->GetURL("/title2.html"));
+ TestNavigationObserver observer(web_contents);
+ std::string script =
+ "history.replaceState({}, '', '/" + url_2.spec() + "')";
+ EXPECT_TRUE(ExecJs(root, script));
+ observer.Wait();
+ }
+ scoped_refptr<SiteInstanceImpl> site_instance_5 =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+ // Check that we're still in the same SiteInstance.
+ EXPECT_EQ(site_instance_4, site_instance_5);
+
+ // 6) Reload after a replaceState by simulating the user hitting Enter in the
+ // omnibox without changing the URL.
+ {
+ TestNavigationObserver observer(web_contents);
+ web_contents->GetController().LoadURL(web_contents->GetLastCommittedURL(),
+ Referrer(), ui::PAGE_TRANSITION_LINK,
+ std::string());
+ observer.Wait();
+ }
+ scoped_refptr<SiteInstanceImpl> site_instance_6 =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+ // Check that we're still in the same SiteInstance.
+ EXPECT_EQ(site_instance_5, site_instance_6);
+}
+
+IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesSameSiteTest,
+ SwapOnNavigationToPageThatRedirects) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+ GURL url_1(embedded_test_server()->GetURL("/title1.html"));
+ GURL url_2(embedded_test_server()->GetURL("/title2.html"));
+ // This is a same-site URL, and will redirect to another same-site URL.
+ GURL same_site_redirector_url(
+ embedded_test_server()->GetURL("/server-redirect?" + url_2.spec()));
+ GURL url_3(embedded_test_server()->GetURL("/title3.html"));
+ // This is a cross-site URL, but will redirect to a same-site URL.
+ GURL cross_site_redirector_url(embedded_test_server()->GetURL(
+ "b.com", "/server-redirect?" + url_3.spec()));
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+
+ // 1) Navigate to title1.html.
+ EXPECT_TRUE(NavigateToURL(shell(), url_1));
+ scoped_refptr<SiteInstanceImpl> site_instance_1 =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+
+ // 2) Go to a same-site URL that will redirect us same-site to /title2.html.
+ EXPECT_TRUE(NavigateToURL(shell(), same_site_redirector_url,
+ url_2 /* expected_commit_url */));
+ scoped_refptr<SiteInstanceImpl> site_instance_2 =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+
+ // Check that we are using a different BrowsingInstance but still using the
+ // same renderer process.
+ EXPECT_FALSE(site_instance_1->IsRelatedSiteInstance(site_instance_2.get()));
+ EXPECT_EQ(site_instance_1->GetProcess(), site_instance_2->GetProcess());
+
+ // 3) Go to a cross-site URL that will redirect us same-site to /title3.html.
+ // Note that we're using a renderer-initiated navigation here. If we do a
+ // browser-initiated navigation, it will hit the case at crbug.com/1094147
+ // where we can't reuse |url_2|'s process even though |url_3| is same-site.
+ // TODO(crbug.com/1094147): Test with browser-initiated navigation too once
+ // the issue is fixed.
+ EXPECT_TRUE(NavigateToURLFromRenderer(shell(), cross_site_redirector_url,
+ url_3 /* expected_commit_url */));
+ scoped_refptr<SiteInstanceImpl> site_instance_3 =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+
+ // Check that we are using a different BrowsingInstance but still using the
+ // same renderer process.
+ EXPECT_FALSE(site_instance_2->IsRelatedSiteInstance(site_instance_3.get()));
+ EXPECT_EQ(site_instance_2->GetProcess(), site_instance_3->GetProcess());
+}
+
+IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesSameSiteTest,
+ DoNotSwapWhenReplacingHistoryEntry) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+ GURL url_1(embedded_test_server()->GetURL("/title1.html"));
+ GURL url_2(embedded_test_server()->GetURL("/title2.html"));
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+
+ // 1) Navigate to title1.html.
+ EXPECT_TRUE(NavigateToURL(shell(), url_1));
+ scoped_refptr<SiteInstanceImpl> site_instance_1 =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+
+ // 2) Do a location.replace() to title2.html.
+ {
+ TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
+ EXPECT_TRUE(
+ ExecJs(shell(), JsReplace("window.location.replace($1)", url_2)));
+ navigation_observer.Wait();
+ EXPECT_TRUE(navigation_observer.last_navigation_succeeded());
+ EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), url_2);
+ }
+ scoped_refptr<SiteInstanceImpl> site_instance_2 =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+ EXPECT_EQ(site_instance_1, site_instance_2);
+}
+
+// When we do a same-document navigation from A to A#foo then a navigation that
+// does replacement (e.g., cross-process reload, or location.replace, or other
+// client redirects) such that B takes the place of A#foo, we can go back to A
+// with the back navigation. In this case, we might want to do a proactive BI
+// swap so that page A can be bfcached.
+// However, this test is currently disabled because we won't swap on any
+// navigation that will replace the current history entry.
+// TODO(rakina): Support this case.
+IN_PROC_BROWSER_TEST_P(
+ ProactivelySwapBrowsingInstancesSameSiteTest,
+ DISABLED_ShouldSwapWhenReplacingEntryWithSameDocumentPreviousEntry) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+ GURL url_1(embedded_test_server()->GetURL("/title1.html"));
+ GURL url_1_anchor(embedded_test_server()->GetURL("/title1.html#foo"));
+ GURL url_2(embedded_test_server()->GetURL("/title2.html"));
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+
+ // 1) Navigate to title1.html.
+ EXPECT_TRUE(NavigateToURL(shell(), url_1));
+ scoped_refptr<SiteInstanceImpl> site_instance_1 =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+
+ // 2) Navigate same-document to title1.html#foo.
+ EXPECT_TRUE(NavigateToURL(shell(), url_1_anchor));
+ scoped_refptr<SiteInstanceImpl> site_instance_2 =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+ EXPECT_EQ(site_instance_1, site_instance_2);
+
+ // 3) Do a location.replace() to title2.html.
+ {
+ TestNavigationObserver navigation_observer(web_contents, 1);
+ EXPECT_TRUE(
+ ExecJs(shell(), JsReplace("window.location.replace($1)", url_2)));
+ navigation_observer.Wait();
+ EXPECT_TRUE(navigation_observer.last_navigation_succeeded());
+ EXPECT_EQ(web_contents->GetLastCommittedURL(), url_2);
+ }
+ scoped_refptr<SiteInstanceImpl> site_instance_3 =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+ // We should swap BrowsingInstance here so that the page at url_1 (which is
+ // now the previous history entry) can be bfcached.
+ EXPECT_NE(site_instance_2, site_instance_3);
+
+ // Assert that a back navigation will go to |url_1|.
+ {
+ TestNavigationObserver navigation_observer(web_contents);
+ web_contents->GetController().GoBack();
+ navigation_observer.Wait();
+ EXPECT_TRUE(navigation_observer.last_navigation_succeeded());
+ EXPECT_EQ(web_contents->GetLastCommittedURL(), url_1);
+ }
+}
+
+IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesSameSiteTest,
+ DoNotSwapWhenRelatedContentsPresent) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+ GURL url_1(embedded_test_server()->GetURL("/title1.html"));
+ GURL url_2(embedded_test_server()->GetURL("/title2.html"));
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+
+ // 1) Navigate and open a new window.
+ EXPECT_TRUE(NavigateToURL(shell(), url_1));
+ OpenPopup(shell(), url_1, "foo");
+ scoped_refptr<SiteInstanceImpl> site_instance_1 =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+
+ // 2) Navigate to title2.html.
+ EXPECT_TRUE(NavigateToURL(shell(), url_2));
+ scoped_refptr<SiteInstanceImpl> site_instance_2 =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+
+ // Check that title1.html and title2.html are using the same SiteInstance.
+ EXPECT_EQ(site_instance_1, site_instance_2);
+}
+
+// We should reuse the current process on same-site navigations even if the
+// site requires a dedicated process (because we are still in the same site).
+IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesSameSiteTest,
+ NavigationToSiteThatRequiresDedicatedProcess) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+ GURL url_1(embedded_test_server()->GetURL("/title1.html"));
+ GURL url_2(embedded_test_server()->GetURL("/title2.html"));
+
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+ // Make sure the site require a dedicated process.
+ EffectiveURLContentBrowserClient modified_client(
+ url_1 /* url_to_modify */, url_1, /* requires_dedicated_process */ true);
+ ContentBrowserClient* old_client =
+ SetBrowserClientForTesting(&modified_client);
+
+ // 1) Navigate to A.
+ EXPECT_TRUE(NavigateToURL(shell(), url_1));
+ scoped_refptr<SiteInstanceImpl> site_instance_1 =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+ EXPECT_TRUE(site_instance_1->RequiresDedicatedProcess());
+
+ // 2) Navigate cross-site to B. The navigation is document/renderer initiated.
+ EXPECT_TRUE(NavigateToURLFromRenderer(shell(), url_2));
+ scoped_refptr<SiteInstanceImpl> site_instance_2 =
+ static_cast<SiteInstanceImpl*>(
+ web_contents->GetMainFrame()->GetSiteInstance());
+ EXPECT_TRUE(site_instance_2->RequiresDedicatedProcess());
+
+ // Check that A and B are in different BrowsingInstances but reuse the same
+ // process.
+ EXPECT_FALSE(site_instance_1->IsRelatedSiteInstance(site_instance_2.get()));
+ EXPECT_EQ(site_instance_1->GetProcess(), site_instance_2->GetProcess());
+ SetBrowserClientForTesting(old_client);
+}
+
// Helper class to simplify testing of unload handlers. It allows waiting for
// particular HTTP requests to be made to the embedded_test_server(); the tests
// use this to wait for termination pings (e.g., navigator.sendBeacon()) made
@@ -6879,6 +7402,9 @@ IN_PROC_BROWSER_TEST_P(RenderFrameHostManagerTest,
// anything without SiteIsolation.
if (!AreAllSitesIsolatedForTesting())
return;
+ // TODO(https://crbug.com/1064944): Fix this test and remove this.
+ if (CreateNewHostForSameSiteSubframe())
+ return;
// 1. Navigate to A1(B2, B3(B4), C5).
StartEmbeddedServer();
@@ -7076,6 +7602,9 @@ INSTANTIATE_TEST_SUITE_P(
ProactivelySwapBrowsingInstancesCrossSiteReuseProcessTest,
testing::ValuesIn(RenderDocumentFeatureLevelValues()));
INSTANTIATE_TEST_SUITE_P(All,
+ ProactivelySwapBrowsingInstancesSameSiteTest,
+ testing::ValuesIn(RenderDocumentFeatureLevelValues()));
+INSTANTIATE_TEST_SUITE_P(All,
RenderFrameHostManagerUnloadBrowserTest,
testing::ValuesIn(RenderDocumentFeatureLevelValues()));
INSTANTIATE_TEST_SUITE_P(All,
diff --git a/chromium/content/browser/frame_host/render_frame_host_manager_unittest.cc b/chromium/content/browser/frame_host/render_frame_host_manager_unittest.cc
index 7c72abc9193..d7388d6997f 100644
--- a/chromium/content/browser/frame_host/render_frame_host_manager_unittest.cc
+++ b/chromium/content/browser/frame_host/render_frame_host_manager_unittest.cc
@@ -251,6 +251,7 @@ class PluginFaviconMessageObserver : public WebContentsObserver {
}
void DidUpdateFaviconURL(
+ RenderFrameHost* render_frame_host,
const std::vector<blink::mojom::FaviconURLPtr>& candidates) override {
favicon_received_ = true;
}
@@ -265,6 +266,16 @@ class PluginFaviconMessageObserver : public WebContentsObserver {
DISALLOW_COPY_AND_ASSIGN(PluginFaviconMessageObserver);
};
+// A shorter version for RenderFrameHostManager::DidNavigateFrame(rfh, ...).
+// This provides all the arguments that aren't tested in this file.
+void DidNavigateFrame(RenderFrameHostManager* rfh_manager,
+ RenderFrameHostImpl* rfh) {
+ rfh_manager->DidNavigateFrame(rfh, true /* was_caused_by_user_gesture */,
+ false /* is_same_document_navigation */,
+ false /* clear_proxies_on_commit */,
+ blink::FramePolicy());
+}
+
} // namespace
// Test that the "level" feature param has the expected effect.
@@ -377,7 +388,7 @@ class RenderFrameHostManagerTest
static_cast<NavigationControllerImpl*>(manager->current_frame_host()
->frame_tree_node()
->navigator()
- ->GetController());
+ .GetController());
mojom::NavigationType navigate_type =
entry->restore_type() == RestoreType::NONE
? mojom::NavigationType::DIFFERENT_DOCUMENT
@@ -899,10 +910,7 @@ TEST_P(RenderFrameHostManagerTest, Navigate) {
EXPECT_FALSE(GetPendingFrameHost(manager));
// Commit.
- manager->DidNavigateFrame(host, true /* was_caused_by_user_gesture */,
- false /* is_same_document_navigation */,
- false /* clear_proxies_on_commit */,
- blink::FramePolicy());
+ DidNavigateFrame(manager, host);
// Commit to SiteInstance should be delayed until RenderFrame commit.
EXPECT_TRUE(host == manager->current_frame_host());
ASSERT_TRUE(host);
@@ -927,10 +935,7 @@ TEST_P(RenderFrameHostManagerTest, Navigate) {
EXPECT_FALSE(GetPendingFrameHost(manager));
// Commit.
- manager->DidNavigateFrame(host, true /* was_caused_by_user_gesture */,
- false /* is_same_document_navigation */,
- false /* clear_proxies_on_commit */,
- blink::FramePolicy());
+ DidNavigateFrame(manager, host);
EXPECT_TRUE(host == manager->current_frame_host());
ASSERT_TRUE(host);
EXPECT_TRUE(host->GetSiteInstance()->HasSite());
@@ -955,10 +960,7 @@ TEST_P(RenderFrameHostManagerTest, Navigate) {
change_observer.Reset();
// Commit.
- manager->DidNavigateFrame(
- GetPendingFrameHost(manager), true /* was_caused_by_user_gesture */,
- false /* is_same_document_navigation */,
- false /* clear_proxies_on_commit */, blink::FramePolicy());
+ DidNavigateFrame(manager, GetPendingFrameHost(manager));
EXPECT_TRUE(host == manager->current_frame_host());
ASSERT_TRUE(host);
EXPECT_TRUE(host->GetSiteInstance()->HasSite());
@@ -1015,10 +1017,7 @@ TEST_P(RenderFrameHostManagerTest, WebUI) {
EXPECT_TRUE(manager->current_frame_host()->web_ui());
// Commit.
- manager->DidNavigateFrame(host, true /* was_caused_by_user_gesture */,
- false /* is_same_document_navigation */,
- false /* clear_proxies_on_commit */,
- blink::FramePolicy());
+ DidNavigateFrame(manager, host);
EXPECT_TRUE(host->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
}
@@ -1035,9 +1034,8 @@ TEST_P(RenderFrameHostManagerTest, WebUIInNewTab) {
RenderFrameHostManager* manager1 =
web_contents1->GetRenderManagerForTesting();
// Test the case that new RVH is considered live.
- manager1->current_host()->CreateRenderView(
- -1, MSG_ROUTING_NONE, base::UnguessableToken::Create(),
- base::UnguessableToken::Create(), FrameReplicationState(), false);
+ manager1->current_host()->CreateRenderView(base::nullopt, MSG_ROUTING_NONE,
+ false);
EXPECT_TRUE(manager1->current_host()->IsRenderViewLive());
EXPECT_TRUE(manager1->current_frame_host()->IsRenderFrameLive());
@@ -1056,10 +1054,7 @@ TEST_P(RenderFrameHostManagerTest, WebUIInNewTab) {
EXPECT_TRUE(host1->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
// Commit and ensure we still have bindings.
- manager1->DidNavigateFrame(host1, true /* was_caused_by_user_gesture */,
- false /* is_same_document_navigation */,
- false /* clear_proxies_on_commit */,
- blink::FramePolicy());
+ DidNavigateFrame(manager1, host1);
SiteInstance* webui_instance = host1->GetSiteInstance();
EXPECT_EQ(host1, manager1->current_frame_host());
EXPECT_TRUE(host1->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
@@ -1071,9 +1066,8 @@ TEST_P(RenderFrameHostManagerTest, WebUIInNewTab) {
web_contents2->GetRenderManagerForTesting();
// Make sure the new RVH is considered live. This is usually done in
// RenderWidgetHost::Init when opening a new tab from a link.
- manager2->current_host()->CreateRenderView(
- -1, MSG_ROUTING_NONE, base::UnguessableToken::Create(),
- base::UnguessableToken::Create(), FrameReplicationState(), false);
+ manager2->current_host()->CreateRenderView(base::nullopt, MSG_ROUTING_NONE,
+ false);
EXPECT_TRUE(manager2->current_host()->IsRenderViewLive());
const GURL kUrl2(GetWebUIURL("foo/bar"));
@@ -1091,10 +1085,7 @@ TEST_P(RenderFrameHostManagerTest, WebUIInNewTab) {
EXPECT_TRUE(host2->web_ui());
EXPECT_TRUE(host2->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
- manager2->DidNavigateFrame(host2, true /* was_caused_by_user_gesture */,
- false /* is_same_document_navigation */,
- false /* clear_proxies_on_commit */,
- blink::FramePolicy());
+ DidNavigateFrame(manager2, host2);
}
// Tests that a WebUI is correctly reused between chrome:// pages.
@@ -1264,7 +1255,7 @@ TEST_P(RenderFrameHostManagerTest, DisownOpener) {
EXPECT_NE(site_instance1, rfh2->GetSiteInstance());
// Disown the opener from rfh2.
- rfh2->DidChangeOpener(MSG_ROUTING_NONE);
+ rfh2->SimulateDidChangeOpener(base::UnguessableToken());
// Ensure the opener is cleared.
EXPECT_FALSE(contents()->HasOpener());
@@ -1285,7 +1276,7 @@ TEST_P(RenderFrameHostManagerTest, DisownSameSiteOpener) {
EXPECT_TRUE(contents()->HasOpener());
// Disown the opener from rfh1.
- rfh1->DidChangeOpener(MSG_ROUTING_NONE);
+ rfh1->SimulateDidChangeOpener(base::UnguessableToken());
// Ensure the opener is cleared even if it is in the same process.
EXPECT_FALSE(contents()->HasOpener());
@@ -1321,7 +1312,7 @@ TEST_P(RenderFrameHostManagerTest, DisownOpenerDuringNavigation) {
contents()->GetMainFrame()->PrepareForCommit();
// Disown the opener from rfh2.
- rfh2->DidChangeOpener(MSG_ROUTING_NONE);
+ rfh2->SimulateDidChangeOpener(base::UnguessableToken());
// Ensure the opener is cleared.
EXPECT_FALSE(contents()->HasOpener());
@@ -1370,7 +1361,7 @@ TEST_P(RenderFrameHostManagerTest, DisownOpenerAfterNavigation) {
entry1->GetTransitionType());
// Disown the opener from rfh2.
- rfh2->DidChangeOpener(MSG_ROUTING_NONE);
+ rfh2->SimulateDidChangeOpener(base::UnguessableToken());
EXPECT_FALSE(contents()->HasOpener());
}
@@ -1392,9 +1383,8 @@ TEST_P(RenderFrameHostManagerTest, CleanUpProxiesOnProcessCrash) {
contents()->SetOpener(opener1.get());
// Make sure the new opener RVH is considered live.
- opener1_manager->current_host()->CreateRenderView(
- -1, MSG_ROUTING_NONE, base::UnguessableToken::Create(),
- base::UnguessableToken::Create(), FrameReplicationState(), false);
+ opener1_manager->current_host()->CreateRenderView(base::nullopt,
+ MSG_ROUTING_NONE, false);
EXPECT_TRUE(opener1_manager->current_host()->IsRenderViewLive());
EXPECT_TRUE(opener1_manager->current_frame_host()->IsRenderFrameLive());
@@ -1431,8 +1421,8 @@ TEST_P(RenderFrameHostManagerTest, CleanUpProxiesOnProcessCrash) {
->GetRenderViewHost()
->IsRenderViewLive());
EXPECT_EQ(
- opener1_manager->GetRoutingIdForSiteInstance(rfh2->GetSiteInstance()),
- rfh2->GetRenderViewHost()->opener_frame_route_id());
+ opener1_manager->GetFrameTokenForSiteInstance(rfh2->GetSiteInstance()),
+ rfh2->GetRenderViewHost()->opener_frame_token());
}
// Test that we reuse the same guest SiteInstance if we navigate across sites.
@@ -1467,10 +1457,7 @@ TEST_P(RenderFrameHostManagerTest, NoSwapOnGuestNavigations) {
EXPECT_EQ(manager->current_frame_host()->GetSiteInstance(), instance);
// Commit.
- manager->DidNavigateFrame(host, true /* was_caused_by_user_gesture */,
- false /* is_same_document_navigation */,
- false /* clear_proxies_on_commit */,
- blink::FramePolicy());
+ DidNavigateFrame(manager, host);
// Commit to SiteInstance should be delayed until RenderFrame commit.
EXPECT_EQ(host, manager->current_frame_host());
ASSERT_TRUE(host);
@@ -1493,10 +1480,7 @@ TEST_P(RenderFrameHostManagerTest, NoSwapOnGuestNavigations) {
EXPECT_FALSE(manager->speculative_frame_host());
// Commit.
- manager->DidNavigateFrame(host, true /* was_caused_by_user_gesture */,
- false /* is_same_document_navigation */,
- false /* clear_proxies_on_commit */,
- blink::FramePolicy());
+ DidNavigateFrame(manager, host);
EXPECT_EQ(host, manager->current_frame_host());
ASSERT_TRUE(host);
EXPECT_EQ(host->GetSiteInstance(), instance);
@@ -1551,10 +1535,7 @@ TEST_P(RenderFrameHostManagerTest, NavigateWithEarlyClose) {
EXPECT_TRUE(change_observer.DidHostChange());
// Commit.
- manager->DidNavigateFrame(host, true /* was_caused_by_user_gesture */,
- false /* is_same_document_navigation */,
- false /* clear_proxies_on_commit */,
- blink::FramePolicy());
+ DidNavigateFrame(manager, host);
// Commit to SiteInstance should be delayed until RenderFrame commits.
EXPECT_EQ(host, manager->current_frame_host());
@@ -1866,10 +1847,7 @@ TEST_P(RenderFrameHostManagerTestWithSiteIsolation, DetachPendingChild) {
EXPECT_FALSE(GetPendingFrameHost(iframe1));
// Commit.
- iframe1->DidNavigateFrame(host1, true /* was_caused_by_user_gesture */,
- false /* is_same_document_navigation */,
- false /* clear_proxies_on_commit */,
- blink::FramePolicy());
+ DidNavigateFrame(iframe1, host1);
// Commit to SiteInstance should be delayed until RenderFrame commit.
EXPECT_TRUE(host1 == iframe1->current_frame_host());
ASSERT_TRUE(host1);
@@ -2019,10 +1997,7 @@ TEST_P(RenderFrameHostManagerTestWithSiteIsolation,
base::string16() /* title */, ui::PAGE_TRANSITION_LINK,
false /* is_renderer_init */, nullptr /* blob_url_loader_factory */);
RenderFrameHostImpl* cross_site = NavigateToEntry(iframe, &entry);
- iframe->DidNavigateFrame(cross_site, true /* was_caused_by_user_gesture */,
- false /* is_same_document_navigation */,
- false /* clear_proxies_on_commit */,
- blink::FramePolicy());
+ DidNavigateFrame(iframe, cross_site);
// A proxy to the iframe should now exist in the SiteInstance of the main
// frames.
@@ -2092,9 +2067,55 @@ TEST_P(RenderFrameHostManagerTestWithSiteIsolation,
EXPECT_FALSE(bar_rfh->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI);
}
+// This class intercepts RenderFrameProxyHost creations, and overrides their
+// respective blink::mojom::RemoteFrame instances, so that it can watch the
+// updates of opener frames.
+class UpdateOpenerProxyObserver {
+ public:
+ UpdateOpenerProxyObserver() {
+ RenderFrameProxyHost::SetCreatedCallbackForTesting(base::BindRepeating(
+ &UpdateOpenerProxyObserver::RenderFrameProxyHostCreatedCallback,
+ base::Unretained(this)));
+ }
+ ~UpdateOpenerProxyObserver() {
+ RenderFrameProxyHost::SetCreatedCallbackForTesting(
+ RenderFrameProxyHost::CreatedCallback());
+ }
+ base::Optional<base::UnguessableToken> OpenerFrameToken(
+ RenderFrameProxyHost* proxy) {
+ return remote_frames_[proxy]->opener_frame_token();
+ }
+
+ private:
+ class Remote : public content::FakeRemoteFrame {
+ public:
+ explicit Remote(RenderFrameProxyHost* proxy) {
+ Init(proxy->GetRemoteAssociatedInterfacesTesting());
+ }
+ void UpdateOpener(
+ const base::Optional<base::UnguessableToken>& frame_token) override {
+ frame_token_ = frame_token;
+ }
+ base::Optional<base::UnguessableToken> opener_frame_token() {
+ return frame_token_;
+ }
+
+ private:
+ base::Optional<base::UnguessableToken> frame_token_;
+ };
+
+ void RenderFrameProxyHostCreatedCallback(RenderFrameProxyHost* proxy_host) {
+ remote_frames_[proxy_host] = std::make_unique<Remote>(proxy_host);
+ }
+
+ std::map<RenderFrameProxyHost*, std::unique_ptr<Remote>> remote_frames_;
+};
+
// Test that opener proxies are created properly with a cycle on the opener
// chain.
TEST_P(RenderFrameHostManagerTest, CreateOpenerProxiesWithCycleOnOpenerChain) {
+ UpdateOpenerProxyObserver proxy_observers;
+
const GURL kUrl1("http://www.google.com/");
const GURL kUrl2 = isolated_cross_site_url();
@@ -2138,28 +2159,26 @@ TEST_P(RenderFrameHostManagerTest, CreateOpenerProxiesWithCycleOnOpenerChain) {
EXPECT_TRUE(tab2_proxy);
// Verify that the proxies' openers point to each other.
- int tab1_opener_routing_id =
- tab1_manager->GetOpenerRoutingID(rfh2->GetSiteInstance());
- int tab2_opener_routing_id =
- tab2_manager->GetOpenerRoutingID(rfh2->GetSiteInstance());
- EXPECT_EQ(tab2_proxy->GetRoutingID(), tab1_opener_routing_id);
- EXPECT_EQ(tab1_proxy->GetRoutingID(), tab2_opener_routing_id);
+ auto tab1_opener_frame_token =
+ tab1_manager->GetOpenerFrameToken(rfh2->GetSiteInstance());
+ auto tab2_opener_frame_token =
+ tab2_manager->GetOpenerFrameToken(rfh2->GetSiteInstance());
+ EXPECT_EQ(tab2_proxy->GetFrameToken(), tab1_opener_frame_token);
+ EXPECT_EQ(tab1_proxy->GetFrameToken(), tab2_opener_frame_token);
// Setting tab2_proxy's opener required an extra IPC message to be set, since
- // the opener's routing ID wasn't available when tab2_proxy was created.
- // Verify that this IPC was sent and that it passed correct routing ID.
- const IPC::Message* message =
- rfh2->GetProcess()->sink().GetUniqueMessageMatching(
- FrameMsg_UpdateOpener::ID);
- EXPECT_TRUE(message);
- FrameMsg_UpdateOpener::Param params;
- EXPECT_TRUE(FrameMsg_UpdateOpener::Read(message, &params));
- EXPECT_EQ(tab2_opener_routing_id, std::get<0>(params));
+ // the opener's frame token wasn't available when tab2_proxy was created.
+ // Verify that this IPC was sent and that it passed correct frame token.
+ base::RunLoop().RunUntilIdle();
+ DCHECK(proxy_observers.OpenerFrameToken(tab2_proxy) ==
+ tab2_manager->GetOpenerFrameToken(rfh2->GetSiteInstance()));
}
// Test that opener proxies are created properly when the opener points
// to itself.
TEST_P(RenderFrameHostManagerTest, CreateOpenerProxiesWhenOpenerPointsToSelf) {
+ UpdateOpenerProxyObserver proxy_observers;
+
const GURL kUrl1("http://www.google.com/");
const GURL kUrl2 = isolated_cross_site_url();
@@ -2190,20 +2209,16 @@ TEST_P(RenderFrameHostManagerTest, CreateOpenerProxiesWhenOpenerPointsToSelf) {
EXPECT_TRUE(opener_proxy);
// Verify that the proxy's opener points to itself.
- int opener_routing_id =
- opener_manager->GetOpenerRoutingID(rfh2->GetSiteInstance());
- EXPECT_EQ(opener_proxy->GetRoutingID(), opener_routing_id);
+ auto opener_frame_token =
+ opener_manager->GetOpenerFrameToken(rfh2->GetSiteInstance());
+ EXPECT_EQ(opener_proxy->GetFrameToken(), opener_frame_token);
// Setting the opener in opener_proxy required an extra IPC message, since
- // the opener's routing ID wasn't available when opener_proxy was created.
- // Verify that this IPC was sent and that it passed correct routing ID.
- const IPC::Message* message =
- rfh2->GetProcess()->sink().GetUniqueMessageMatching(
- FrameMsg_UpdateOpener::ID);
- EXPECT_TRUE(message);
- FrameMsg_UpdateOpener::Param params;
- EXPECT_TRUE(FrameMsg_UpdateOpener::Read(message, &params));
- EXPECT_EQ(opener_routing_id, std::get<0>(params));
+ // the opener's frame_token wasn't available when opener_proxy was created.
+ // Verify that this IPC was sent and that it passed correct frame token.
+ base::RunLoop().RunUntilIdle();
+ DCHECK(proxy_observers.OpenerFrameToken(opener_proxy) ==
+ opener_manager->GetOpenerFrameToken(rfh2->GetSiteInstance()));
}
// Build the following frame opener graph and see that it can be properly
@@ -2393,14 +2408,8 @@ TEST_P(RenderFrameHostManagerTest, PageFocusPropagatesToSubframeProcesses) {
TestRenderFrameHost* host2 =
static_cast<TestRenderFrameHost*>(NavigateToEntry(child2, &entryB));
- child1->DidNavigateFrame(host1, true /* was_caused_by_user_gesture */,
- false /* is_same_document_navigation */,
- false /* clear_proxies_on_commit */,
- blink::FramePolicy());
- child2->DidNavigateFrame(host2, true /* was_caused_by_user_gesture */,
- false /* is_same_document_navigation */,
- false /* clear_proxies_on_commit */,
- blink::FramePolicy());
+ DidNavigateFrame(child1, host1);
+ DidNavigateFrame(child2, host2);
// Navigate the third subframe to C.
NavigationEntryImpl entryC(
@@ -2419,10 +2428,7 @@ TEST_P(RenderFrameHostManagerTest, PageFocusPropagatesToSubframeProcesses) {
// Create PageFocusRemoteFrame to intercept SetPageFocus to RemoteFrame.
PageFocusRemoteFrame remote_frame3(proxyC);
- child3->DidNavigateFrame(host3, true /* was_caused_by_user_gesture */,
- false /* is_same_document_navigation */,
- false /* clear_proxies_on_commit */,
- blink::FramePolicy());
+ DidNavigateFrame(child3, host3);
// Make sure the first two subframes and the third subframe are placed in
// distinct processes.
@@ -2495,10 +2501,7 @@ TEST_P(RenderFrameHostManagerTest,
false /* is_renderer_init */, nullptr /* blob_url_loader_factory */);
TestRenderFrameHost* hostB =
static_cast<TestRenderFrameHost*>(NavigateToEntry(child, &entryB));
- child->DidNavigateFrame(hostB, true /* was_caused_by_user_gesture */,
- false /* is_same_document_navigation */,
- false /* clear_proxies_on_commit */,
- blink::FramePolicy());
+ DidNavigateFrame(child, hostB);
// Ensure that the main page is focused.
main_test_rfh()->GetView()->Focus();
@@ -2521,10 +2524,7 @@ TEST_P(RenderFrameHostManagerTest,
// Create PageFocusRemoteFrame to intercept SetPageFocus to RemoteFrame.
PageFocusRemoteFrame remote_frame(proxyC);
- child->DidNavigateFrame(hostC, true /* was_caused_by_user_gesture */,
- false /* is_same_document_navigation */,
- false /* clear_proxies_on_commit */,
- blink::FramePolicy());
+ DidNavigateFrame(child, hostC);
base::RunLoop().RunUntilIdle();
@@ -2581,10 +2581,7 @@ TEST_P(RenderFrameHostManagerTest, RestoreNavigationToWebUI) {
EXPECT_TRUE(current_host->web_ui());
// The RenderFrameHost committed.
- manager->DidNavigateFrame(current_host, true /* was_caused_by_user_gesture */,
- false /* is_same_document_navigation */,
- false /* clear_proxies_on_commit */,
- blink::FramePolicy());
+ DidNavigateFrame(manager, current_host);
EXPECT_EQ(current_host, manager->current_frame_host());
EXPECT_TRUE(current_host->web_ui());
}
@@ -2900,10 +2897,7 @@ TEST_P(RenderFrameHostManagerTest, NavigateFromDeadRendererToWebUI) {
EXPECT_EQ(web_ui, host->web_ui());
// The RenderFrameHost committed.
- manager->DidNavigateFrame(host, true /* was_caused_by_user_gesture */,
- false /* is_same_document_navigation */,
- false /* clear_proxies_on_commit */,
- blink::FramePolicy());
+ DidNavigateFrame(manager, host);
EXPECT_EQ(host, manager->current_frame_host());
EXPECT_FALSE(GetPendingFrameHost(manager));
EXPECT_EQ(web_ui, host->web_ui());
@@ -3244,31 +3238,26 @@ TEST_P(RenderFrameHostManagerTest, BeginNavigationIgnoredWhenNotActive) {
// Tests that sandbox flags received after a navigation away has started do not
// affect the document being navigated to.
TEST_P(RenderFrameHostManagerTest, ReceivedFramePolicyAfterNavigationStarted) {
- const GURL kUrl1("http://www.google.com");
- const GURL kUrl2("http://www.chromium.org");
-
- contents()->NavigateAndCommit(kUrl1);
- TestRenderFrameHost* initial_rfh = main_test_rfh();
-
- // The RFH should start out with an empty frame policy.
+ // The RFH should start out with an fully permissive sandbox policy.
EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
- initial_rfh->frame_tree_node()->active_sandbox_flags());
+ main_test_rfh()->frame_tree_node()->active_sandbox_flags());
- // Navigate cross-site but don't commit the navigation.
- auto navigation_to_kUrl2 =
- NavigationSimulator::CreateBrowserInitiated(kUrl2, contents());
- navigation_to_kUrl2->ReadyToCommit();
+ // Navigate, but don't commit the navigation.
+ auto navigation = NavigationSimulator::CreateBrowserInitiated(
+ GURL("http://a.com"), contents());
+ navigation->ReadyToCommit();
// Now send the frame policy for the initial page.
- initial_rfh->SendFramePolicy(network::mojom::WebSandboxFlags::kAll,
- {} /* feature_policy_header */,
- {} /* document_policy_header */);
- // Verify that the policy landed in the frame tree.
+ main_test_rfh()->SendFramePolicy(network::mojom::WebSandboxFlags::kAll,
+ {} /* feature_policy_header */,
+ {} /* document_policy_header */);
+
+ // Check 'SendFramePolicy' updated the active sandbox flags.
EXPECT_EQ(network::mojom::WebSandboxFlags::kAll,
- initial_rfh->frame_tree_node()->active_sandbox_flags());
+ main_test_rfh()->frame_tree_node()->active_sandbox_flags());
- // Commit the naviagation; the new frame should have a clear frame policy.
- navigation_to_kUrl2->Commit();
+ // Commit the navigation. The new frame should have a clear frame policy.
+ navigation->Commit();
EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
main_test_rfh()->frame_tree_node()->active_sandbox_flags());
}
diff --git a/chromium/content/browser/frame_host/render_frame_message_filter.cc b/chromium/content/browser/frame_host/render_frame_message_filter.cc
index 33e2c5f972d..75e5dbaa10c 100644
--- a/chromium/content/browser/frame_host/render_frame_message_filter.cc
+++ b/chromium/content/browser/frame_host/render_frame_message_filter.cc
@@ -16,7 +16,6 @@
#include "base/macros.h"
#include "base/strings/string_util.h"
#include "base/syslog_logging.h"
-#include "base/task/post_task.h"
#include "base/unguessable_token.h"
#include "build/build_config.h"
#include "content/browser/bad_message.h"
@@ -283,8 +282,8 @@ void RenderFrameMessageFilter::OnCreateChildFrame(
params_reply->frame_token = base::UnguessableToken::Create();
params_reply->devtools_frame_token = base::UnguessableToken::Create();
- base::PostTask(
- FROM_HERE, {BrowserThread::UI},
+ GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE,
base::BindOnce(
&CreateChildFrameOnUI, render_process_id_, params.parent_routing_id,
params.scope, params.frame_name, params.frame_unique_name,
diff --git a/chromium/content/browser/frame_host/render_frame_message_filter_browsertest.cc b/chromium/content/browser/frame_host/render_frame_message_filter_browsertest.cc
index c64a7286604..e2f185d49ae 100644
--- a/chromium/content/browser/frame_host/render_frame_message_filter_browsertest.cc
+++ b/chromium/content/browser/frame_host/render_frame_message_filter_browsertest.cc
@@ -35,6 +35,7 @@
#include "ipc/ipc_security_test_util.h"
#include "net/base/features.h"
#include "net/cookies/canonical_cookie.h"
+#include "net/cookies/cookie_inclusion_status.h"
#include "net/cookies/cookie_util.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
@@ -78,9 +79,7 @@ void SetCookieDirect(WebContentsImpl* tab,
->SetCanonicalCookie(
*cookie_obj, url, options,
base::BindLambdaForTesting(
- [&](net::CanonicalCookie::CookieInclusionStatus status) {
- run_loop.Quit();
- }));
+ [&](net::CookieInclusionStatus status) { run_loop.Quit(); }));
run_loop.Run();
}
@@ -93,14 +92,14 @@ std::string GetCookiesDirect(WebContentsImpl* tab, const GURL& url) {
base::RunLoop run_loop;
BrowserContext::GetDefaultStoragePartition(tab->GetBrowserContext())
->GetCookieManagerForBrowserProcess()
- ->GetCookieList(url, options,
- base::BindLambdaForTesting(
- [&](const net::CookieStatusList& cookie_list,
- const net::CookieStatusList& excluded_cookies) {
- result =
- net::cookie_util::StripStatuses(cookie_list);
- run_loop.Quit();
- }));
+ ->GetCookieList(
+ url, options,
+ base::BindLambdaForTesting(
+ [&](const net::CookieAccessResultList& cookie_list,
+ const net::CookieAccessResultList& excluded_cookies) {
+ result = net::cookie_util::StripAccessResults(cookie_list);
+ run_loop.Quit();
+ }));
run_loop.Run();
return net::CanonicalCookie::BuildCookieLine(result);
}
diff --git a/chromium/content/browser/frame_host/render_frame_proxy_host.cc b/chromium/content/browser/frame_host/render_frame_proxy_host.cc
index 425b0b4f6e1..ba3aa600341 100644
--- a/chromium/content/browser/frame_host/render_frame_proxy_host.cc
+++ b/chromium/content/browser/frame_host/render_frame_proxy_host.cc
@@ -30,11 +30,13 @@
#include "content/public/browser/browser_thread.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_features.h"
+#include "content/public/common/referrer_type_converters.h"
#include "ipc/ipc_message.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
#include "services/network/public/cpp/web_sandbox_flags.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/mojom/frame/frame_owner_properties.mojom.h"
+#include "third_party/blink/public/mojom/messaging/transferable_message.mojom.h"
#include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom.h"
namespace content {
@@ -198,10 +200,6 @@ bool RenderFrameProxyHost::OnMessageReceived(const IPC::Message& msg) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(RenderFrameProxyHost, msg)
IPC_MESSAGE_HANDLER(FrameHostMsg_Detach, OnDetach)
- IPC_MESSAGE_HANDLER(FrameHostMsg_OpenURL, OnOpenURL)
- IPC_MESSAGE_HANDLER(FrameHostMsg_RouteMessageEvent, OnRouteMessageEvent)
- IPC_MESSAGE_HANDLER(FrameHostMsg_DidChangeOpener, OnDidChangeOpener)
- IPC_MESSAGE_HANDLER(FrameHostMsg_AdvanceFocus, OnAdvanceFocus)
IPC_MESSAGE_HANDLER(FrameHostMsg_PrintCrossProcessSubframe,
OnPrintCrossProcessSubframe)
IPC_MESSAGE_UNHANDLED(handled = false)
@@ -211,12 +209,15 @@ bool RenderFrameProxyHost::OnMessageReceived(const IPC::Message& msg) {
bool RenderFrameProxyHost::InitRenderFrameProxy() {
DCHECK(!render_frame_proxy_created_);
+ // We shouldn't be creating proxies for subframes of frames in
+ // BackForwardCache.
+ DCHECK(!frame_tree_node_->current_frame_host()->IsInBackForwardCache());
// If the current RenderFrameHost is pending deletion, no new proxies should
// be created for it, since this frame should no longer be visible from other
// processes. We can get here with postMessage while trying to recreate
// proxies for the sender.
- if (!frame_tree_node_->current_frame_host()->is_active())
+ if (frame_tree_node_->current_frame_host()->IsPendingDeletion())
return false;
// It is possible to reach this when the process is dead (in particular, when
@@ -252,17 +253,18 @@ bool RenderFrameProxyHost::InitRenderFrameProxy() {
CHECK_NE(parent_routing_id, MSG_ROUTING_NONE);
}
- int opener_routing_id = MSG_ROUTING_NONE;
+ base::Optional<base::UnguessableToken> opener_frame_token;
if (frame_tree_node_->opener()) {
- opener_routing_id = frame_tree_node_->render_manager()->GetOpenerRoutingID(
- site_instance_.get());
+ opener_frame_token =
+ frame_tree_node_->render_manager()->GetOpenerFrameToken(
+ site_instance_.get());
}
int view_routing_id = frame_tree_node_->frame_tree()
->GetRenderViewHost(site_instance_.get())
->GetRoutingID();
GetProcess()->GetRendererInterface()->CreateFrameProxy(
- routing_id_, view_routing_id, opener_routing_id, parent_routing_id,
+ routing_id_, view_routing_id, opener_frame_token, parent_routing_id,
frame_tree_node_->current_replication_state(), frame_token_,
frame_tree_node_->devtools_frame_token());
@@ -300,6 +302,10 @@ void RenderFrameProxyHost::OnAssociatedInterfaceRequest(
remote_frame_host_receiver_.Bind(
mojo::PendingAssociatedReceiver<blink::mojom::RemoteFrameHost>(
std::move(handle)));
+ } else if (interface_name == blink::mojom::RemoteMainFrameHost::Name_) {
+ remote_main_frame_host_receiver_.Bind(
+ mojo::PendingAssociatedReceiver<blink::mojom::RemoteMainFrameHost>(
+ std::move(handle)));
}
}
@@ -377,9 +383,10 @@ void RenderFrameProxyHost::UpdateOpener() {
GetSiteInstance(), frame_tree_node_);
}
- int opener_routing_id =
- frame_tree_node_->render_manager()->GetOpenerRoutingID(GetSiteInstance());
- Send(new FrameMsg_UpdateOpener(GetRoutingID(), opener_routing_id));
+ auto opener_frame_token =
+ frame_tree_node_->render_manager()->GetOpenerFrameToken(
+ GetSiteInstance());
+ GetAssociatedRemoteFrame()->UpdateOpener(opener_frame_token);
}
void RenderFrameProxyHost::SetFocusedFrame() {
@@ -416,65 +423,6 @@ void RenderFrameProxyHost::OnDetach() {
frame_tree_node_->current_frame_host()->DetachFromProxy();
}
-void RenderFrameProxyHost::OnOpenURL(
- const FrameHostMsg_OpenURL_Params& params) {
- // Verify and unpack IPC payload.
- GURL validated_url;
- scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory;
- if (!VerifyOpenURLParams(GetSiteInstance(), params, &validated_url,
- &blob_url_loader_factory)) {
- return;
- }
-
- RenderFrameHostImpl* current_rfh = frame_tree_node_->current_frame_host();
-
- // The current_rfh may be pending deletion. In this case, ignore the
- // navigation, because the frame is going to disappear soon anyway.
- if (!current_rfh->is_active())
- return;
-
- // Verify that we are in the same BrowsingInstance as the current
- // RenderFrameHost.
- if (!site_instance_->IsRelatedSiteInstance(current_rfh->GetSiteInstance()))
- return;
-
- // Since this navigation targeted a specific RenderFrameProxy, it should stay
- // in the current tab.
- DCHECK_EQ(WindowOpenDisposition::CURRENT_TAB, params.disposition);
-
- // Augment |download_policy| for situations that were not covered on the
- // renderer side, e.g. status not available on remote frame, etc.
- NavigationDownloadPolicy download_policy = params.download_policy;
- GetContentClient()->browser()->AugmentNavigationDownloadPolicy(
- frame_tree_node_->navigator()->GetController()->GetWebContents(),
- current_rfh, params.user_gesture, &download_policy);
-
- if ((frame_tree_node_->pending_frame_policy().sandbox_flags &
- network::mojom::WebSandboxFlags::kDownloads) !=
- network::mojom::WebSandboxFlags::kNone) {
- if (download_policy.blocking_downloads_in_sandbox_enabled) {
- download_policy.SetDisallowed(content::NavigationDownloadType::kSandbox);
- } else {
- download_policy.SetAllowed(content::NavigationDownloadType::kSandbox);
- }
- }
-
- // TODO(lfg, lukasza): Remove |extra_headers| parameter from
- // RequestTransferURL method once both RenderFrameProxyHost and
- // RenderFrameHostImpl call RequestOpenURL from their OnOpenURL handlers.
- // See also https://crbug.com/647772.
- // TODO(clamy): The transition should probably be changed for POST navigations
- // to PAGE_TRANSITION_FORM_SUBMIT. See https://crbug.com/829827.
- frame_tree_node_->navigator()->NavigateFromFrameProxy(
- current_rfh, validated_url,
- GlobalFrameRoutingId(GetProcess()->GetID(), params.initiator_routing_id),
- params.initiator_origin, site_instance_.get(), params.referrer,
- ui::PAGE_TRANSITION_LINK, params.should_replace_current_entry,
- download_policy, params.post_body ? "POST" : "GET", params.post_body,
- params.extra_headers, std::move(blob_url_loader_factory),
- params.user_gesture, params.impression);
-}
-
void RenderFrameProxyHost::CheckCompleted() {
RenderFrameHostImpl* target_rfh = frame_tree_node()->current_frame_host();
target_rfh->GetAssociatedLocalFrame()->CheckCompleted();
@@ -498,8 +446,39 @@ void RenderFrameProxyHost::ChildProcessGone() {
GetAssociatedRenderFrameProxy()->ChildProcessGone();
}
-void RenderFrameProxyHost::OnRouteMessageEvent(
- const FrameMsg_PostMessage_Params& params) {
+void RenderFrameProxyHost::DidFocusFrame() {
+ RenderFrameHostImpl* render_frame_host =
+ frame_tree_node_->current_frame_host();
+
+ // We need to handle this case due to a race, see documentation in
+ // RenderFrameHostImpl::DidFocusFrame for more details.
+ if (render_frame_host->InsidePortal())
+ return;
+
+ render_frame_host->delegate()->SetFocusedFrame(frame_tree_node_,
+ GetSiteInstance());
+}
+
+void RenderFrameProxyHost::CapturePaintPreviewOfCrossProcessSubframe(
+ const gfx::Rect& clip_rect,
+ const base::UnguessableToken& guid) {
+ RenderFrameHostImpl* rfh = frame_tree_node_->current_frame_host();
+ // Do not capture paint on behalf of inactive RenderFrameHost.
+ if (rfh->IsInactiveAndDisallowReactivation())
+ return;
+ rfh->delegate()->CapturePaintPreviewOfCrossProcessSubframe(clip_rect, guid,
+ rfh);
+}
+
+void RenderFrameProxyHost::SetIsInert(bool inert) {
+ cross_process_frame_connector_->SetIsInert(inert);
+}
+
+void RenderFrameProxyHost::RouteMessageEvent(
+ const base::Optional<base::UnguessableToken>& source_frame_token,
+ const base::string16& source_origin,
+ const base::string16& target_origin,
+ blink::TransferableMessage message) {
RenderFrameHostImpl* target_rfh = frame_tree_node()->current_frame_host();
if (!target_rfh->IsRenderFrameLive()) {
// Check if there is an inner delegate involved; if so target its main
@@ -511,34 +490,34 @@ void RenderFrameProxyHost::OnRouteMessageEvent(
return;
}
- // |targetOrigin| argument of postMessage is already checked by
+ // |target_origin| argument of postMessage is already checked by
// blink::LocalDOMWindow::DispatchMessageEventWithOriginCheck (needed for
// messages sent within the same process - e.g. same-site, cross-origin),
// but this check needs to be duplicated below in case the recipient renderer
// process got compromised (i.e. in case the renderer-side check may be
// bypassed).
- if (!params.target_origin.empty()) {
- url::Origin target_origin =
- url::Origin::Create(GURL(base::UTF16ToUTF8(params.target_origin)));
+ if (!target_origin.empty()) {
+ url::Origin target_url_origin =
+ url::Origin::Create(GURL(base::UTF16ToUTF8(target_origin)));
// Renderer should send either an empty string (this is how "*" is expressed
// in the IPC) or a valid, non-opaque origin. OTOH, there are no security
// implications here - the message payload needs to be protected from an
// unintended recipient, not from the sender.
- DCHECK(!target_origin.opaque());
+ DCHECK(!target_url_origin.opaque());
// While the postMessage was in flight, the target might have navigated away
// to another origin. In this case, the postMessage should be silently
// dropped.
- if (target_origin != target_rfh->GetLastCommittedOrigin())
+ if (target_url_origin != target_rfh->GetLastCommittedOrigin())
return;
}
// TODO(lukasza): Move opaque-ness check into ChildProcessSecurityPolicyImpl.
auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
- if (params.source_origin != base::UTF8ToUTF16("null") &&
+ if (source_origin != base::UTF8ToUTF16("null") &&
!policy->CanAccessDataForOrigin(GetProcess()->GetID(),
- GURL(params.source_origin))) {
+ GURL(source_origin))) {
bad_message::ReceivedBadMessage(
GetProcess(), bad_message::RFPH_POST_MESSAGE_INVALID_SOURCE_ORIGIN);
return;
@@ -557,16 +536,12 @@ void RenderFrameProxyHost::OnRouteMessageEvent(
GetSiteInstance()))
return;
- base::Optional<base::UnguessableToken> translated_source_token;
- base::string16 source_origin = params.source_origin;
- base::string16 target_origin = params.target_origin;
- blink::TransferableMessage message = std::move(params.message->data);
-
- // If there is a source_routing_id, translate it to the frame token of the
+ // If there is a |source_frame_token|, translate it to the frame token of the
// equivalent RenderFrameProxyHost in the target process.
- if (params.source_routing_id != MSG_ROUTING_NONE) {
- RenderFrameHostImpl* source_rfh = RenderFrameHostImpl::FromID(
- GetProcess()->GetID(), params.source_routing_id);
+ base::Optional<base::UnguessableToken> translated_source_token;
+ if (source_frame_token) {
+ RenderFrameHostImpl* source_rfh = RenderFrameHostImpl::FromFrameToken(
+ GetProcess()->GetID(), source_frame_token.value());
if (source_rfh) {
// https://crbug.com/822958: If the postMessage is going to a descendant
// frame, ensure that any pending visual properties such as size are sent
@@ -609,8 +584,9 @@ void RenderFrameProxyHost::OnRouteMessageEvent(
}
// If the message source is a cross-process subframe, its proxy will only
- // be created in --site-per-process mode. If the proxy wasn't created,
- // set the source routing ID to MSG_ROUTING_NONE (see
+ // be created in --site-per-process mode, which is the case when we set an
+ // actual non-empty value for |translated_source_token|. Otherwise (if the
+ // proxy wasn't created), use an empty |translated_source_token| (see
// https://crbug.com/485520 for discussion on why this is ok).
RenderFrameProxyHost* source_proxy_in_target_site_instance =
source_rfh->frame_tree_node()
@@ -627,15 +603,83 @@ void RenderFrameProxyHost::OnRouteMessageEvent(
target_origin, std::move(message));
}
-void RenderFrameProxyHost::OnDidChangeOpener(int32_t opener_routing_id) {
- frame_tree_node_->render_manager()->DidChangeOpener(opener_routing_id,
- GetSiteInstance());
+void RenderFrameProxyHost::FocusPage() {
+ frame_tree_node_->current_frame_host()->FocusPage();
+}
+
+void RenderFrameProxyHost::OpenURL(mojom::OpenURLParamsPtr params) {
+ // Verify and unpack IPC payload.
+ GURL validated_url;
+ scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory;
+ if (!VerifyOpenURLParams(GetSiteInstance(), params, &validated_url,
+ &blob_url_loader_factory)) {
+ return;
+ }
+
+ RenderFrameHostImpl* current_rfh = frame_tree_node_->current_frame_host();
+
+ // Only active frames can navigate:
+ // - If the frame is in pending deletion, ignore the navigation, because the
+ // frame is going to disappear soon anyway.
+ // - If the frame is in back-forward cache, it's not allowed to navigate as it
+ // should remain frozen. Ignore the request and evict the document from
+ // back-forward cache.
+ if (current_rfh->IsInactiveAndDisallowReactivation())
+ return;
+
+ // Verify that we are in the same BrowsingInstance as the current
+ // RenderFrameHost.
+ if (!site_instance_->IsRelatedSiteInstance(current_rfh->GetSiteInstance()))
+ return;
+
+ // Since this navigation targeted a specific RenderFrameProxy, it should stay
+ // in the current tab.
+ DCHECK_EQ(WindowOpenDisposition::CURRENT_TAB, params->disposition);
+
+ // Augment |download_policy| for situations that were not covered on the
+ // renderer side, e.g. status not available on remote frame, etc.
+ NavigationDownloadPolicy download_policy = params->download_policy;
+ GetContentClient()->browser()->AugmentNavigationDownloadPolicy(
+ frame_tree_node_->navigator().GetController()->GetWebContents(),
+ current_rfh, params->user_gesture, &download_policy);
+
+ if ((frame_tree_node_->pending_frame_policy().sandbox_flags &
+ network::mojom::WebSandboxFlags::kDownloads) !=
+ network::mojom::WebSandboxFlags::kNone) {
+ if (download_policy.blocking_downloads_in_sandbox_enabled) {
+ download_policy.SetDisallowed(content::NavigationDownloadType::kSandbox);
+ } else {
+ download_policy.SetAllowed(content::NavigationDownloadType::kSandbox);
+ }
+ }
+
+ // TODO(lfg, lukasza): Remove |extra_headers| parameter from
+ // RequestTransferURL method once both RenderFrameProxyHost and
+ // RenderFrameHostImpl call RequestOpenURL from their OnOpenURL handlers.
+ // See also https://crbug.com/647772.
+ // TODO(clamy): The transition should probably be changed for POST navigations
+ // to PAGE_TRANSITION_FORM_SUBMIT. See https://crbug.com/829827.
+ frame_tree_node_->navigator().NavigateFromFrameProxy(
+ current_rfh, validated_url,
+ GlobalFrameRoutingId(GetProcess()->GetID(), params->initiator_routing_id),
+ params->initiator_origin, site_instance_.get(),
+ params->referrer.To<content::Referrer>(), ui::PAGE_TRANSITION_LINK,
+ params->should_replace_current_entry, download_policy,
+ params->post_body ? "POST" : "GET", params->post_body,
+ params->extra_headers, std::move(blob_url_loader_factory),
+ params->user_gesture, params->impression);
+}
+
+void RenderFrameProxyHost::DidChangeOpener(
+ const base::Optional<base::UnguessableToken>& opener_frame_token) {
+ frame_tree_node_->render_manager()->DidChangeOpener(
+ opener_frame_token.value_or(base::UnguessableToken()), GetSiteInstance());
}
-void RenderFrameProxyHost::OnAdvanceFocus(blink::mojom::FocusType type,
- int32_t source_routing_id) {
- RenderFrameHostImpl* target_rfh =
- frame_tree_node_->render_manager()->current_frame_host();
+void RenderFrameProxyHost::AdvanceFocus(
+ blink::mojom::FocusType focus_type,
+ const base::UnguessableToken& source_frame_token) {
+ RenderFrameHostImpl* target_rfh = frame_tree_node_->current_frame_host();
if (target_rfh->InsidePortal()) {
bad_message::ReceivedBadMessage(
GetProcess(), bad_message::RFPH_ADVANCE_FOCUS_INTO_PORTAL);
@@ -646,46 +690,19 @@ void RenderFrameProxyHost::OnAdvanceFocus(blink::mojom::FocusType type,
// RenderFrameProxyHost in the target process. This is needed for continuing
// the focus traversal from correct place in a parent frame after one of its
// child frames finishes its traversal.
- RenderFrameHostImpl* source_rfh =
- RenderFrameHostImpl::FromID(GetProcess()->GetID(), source_routing_id);
+ RenderFrameHostImpl* source_rfh = RenderFrameHostImpl::FromFrameToken(
+ GetProcess()->GetID(), source_frame_token);
RenderFrameProxyHost* source_proxy =
source_rfh ? source_rfh->frame_tree_node()
->render_manager()
->GetRenderFrameProxyHost(target_rfh->GetSiteInstance())
: nullptr;
- target_rfh->AdvanceFocus(type, source_proxy);
+ target_rfh->AdvanceFocus(focus_type, source_proxy);
frame_tree_node_->current_frame_host()->delegate()->OnAdvanceFocus(
source_rfh);
}
-void RenderFrameProxyHost::DidFocusFrame() {
- RenderFrameHostImpl* render_frame_host =
- frame_tree_node_->current_frame_host();
-
- // We need to handle this case due to a race, see documentation in
- // RenderFrameHostImpl::DidFocusFrame for more details.
- if (render_frame_host->InsidePortal())
- return;
-
- render_frame_host->delegate()->SetFocusedFrame(frame_tree_node_,
- GetSiteInstance());
-}
-
-void RenderFrameProxyHost::CapturePaintPreviewOfCrossProcessSubframe(
- const gfx::Rect& clip_rect,
- const base::UnguessableToken& guid) {
- RenderFrameHostImpl* rfh = frame_tree_node_->current_frame_host();
- if (!rfh->is_active())
- return;
- rfh->delegate()->CapturePaintPreviewOfCrossProcessSubframe(clip_rect, guid,
- rfh);
-}
-
-void RenderFrameProxyHost::SetIsInert(bool inert) {
- cross_process_frame_connector_->SetIsInert(inert);
-}
-
bool RenderFrameProxyHost::IsInertForTesting() {
return cross_process_frame_connector_->IsInert();
}
diff --git a/chromium/content/browser/frame_host/render_frame_proxy_host.h b/chromium/content/browser/frame_host/render_frame_proxy_host.h
index 4f9511a5ddf..695f18320b6 100644
--- a/chromium/content/browser/frame_host/render_frame_proxy_host.h
+++ b/chromium/content/browser/frame_host/render_frame_proxy_host.h
@@ -12,17 +12,16 @@
#include "base/callback_forward.h"
#include "base/macros.h"
#include "content/browser/site_instance_impl.h"
+#include "content/common/frame.mojom.h"
#include "content/common/frame_proxy.mojom.h"
#include "ipc/ipc_listener.h"
#include "ipc/ipc_sender.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "third_party/blink/public/mojom/frame/frame.mojom.h"
#include "third_party/blink/public/mojom/input/focus_type.mojom-forward.h"
+#include "third_party/blink/public/mojom/messaging/transferable_message.mojom-forward.h"
#include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom-forward.h"
-struct FrameHostMsg_OpenURL_Params;
-struct FrameMsg_PostMessage_Params;
-
namespace blink {
class AssociatedInterfaceProvider;
}
@@ -70,7 +69,8 @@ class CONTENT_EXPORT RenderFrameProxyHost
: public IPC::Listener,
public IPC::Sender,
public mojom::RenderFrameProxyHost,
- public blink::mojom::RemoteFrameHost {
+ public blink::mojom::RemoteFrameHost,
+ public blink::mojom::RemoteMainFrameHost {
public:
using CreatedCallback = base::RepeatingCallback<void(RenderFrameProxyHost*)>;
@@ -160,6 +160,21 @@ class CONTENT_EXPORT RenderFrameProxyHost
const gfx::Rect& clip_rect,
const base::UnguessableToken& guid) override;
void SetIsInert(bool inert) override;
+ void DidChangeOpener(const base::Optional<base::UnguessableToken>&
+ opener_frame_token) override;
+ void AdvanceFocus(blink::mojom::FocusType focus_type,
+ const base::UnguessableToken& source_frame_token) override;
+ void RouteMessageEvent(
+ const base::Optional<base::UnguessableToken>& source_frame_token,
+ const base::string16& source_origin,
+ const base::string16& target_origin,
+ blink::TransferableMessage message) override;
+
+ // blink::mojom::RemoteMainFrameHost overrides:
+ void FocusPage() override;
+
+ // mojom::RenderFrameProxyHost:
+ void OpenURL(mojom::OpenURLParamsPtr params) override;
// Returns associated remote for the content::mojom::RenderFrameProxy Mojo
// interface.
@@ -180,12 +195,11 @@ class CONTENT_EXPORT RenderFrameProxyHost
const base::UnguessableToken& GetFrameToken() const { return frame_token_; }
private:
+ // The interceptor needs access to frame_host_receiver_for_testing().
+ friend class RouteMessageEventInterceptor;
+
// IPC Message handlers.
void OnDetach();
- void OnOpenURL(const FrameHostMsg_OpenURL_Params& params);
- void OnRouteMessageEvent(const FrameMsg_PostMessage_Params& params);
- void OnDidChangeOpener(int32_t opener_routing_id);
- void OnAdvanceFocus(blink::mojom::FocusType type, int32_t source_routing_id);
void OnPrintCrossProcessSubframe(const gfx::Rect& rect, int document_cookie);
// IPC::Listener
@@ -195,6 +209,12 @@ class CONTENT_EXPORT RenderFrameProxyHost
blink::AssociatedInterfaceProvider* GetRemoteAssociatedInterfaces();
+ // Needed for tests to be able to swap the implementation and intercept calls.
+ mojo::AssociatedReceiver<blink::mojom::RemoteFrameHost>&
+ frame_host_receiver_for_testing() {
+ return remote_frame_host_receiver_;
+ }
+
// This RenderFrameProxyHost's routing id.
int routing_id_;
@@ -244,6 +264,9 @@ class CONTENT_EXPORT RenderFrameProxyHost
mojo::AssociatedReceiver<blink::mojom::RemoteFrameHost>
remote_frame_host_receiver_{this};
+ mojo::AssociatedReceiver<blink::mojom::RemoteMainFrameHost>
+ remote_main_frame_host_receiver_{this};
+
base::UnguessableToken frame_token_ = base::UnguessableToken::Create();
DISALLOW_COPY_AND_ASSIGN(RenderFrameProxyHost);
diff --git a/chromium/content/browser/frame_host/should_swap_browsing_instance.h b/chromium/content/browser/frame_host/should_swap_browsing_instance.h
index b593e950b6a..daf1dfaa02d 100644
--- a/chromium/content/browser/frame_host/should_swap_browsing_instance.h
+++ b/chromium/content/browser/frame_host/should_swap_browsing_instance.h
@@ -24,9 +24,15 @@ enum class ShouldSwapBrowsingInstance {
kNo_AlreadyHasMatchingBrowsingInstance = 9,
kNo_RendererDebugURL = 10,
kNo_NotNeededForBackForwardCache = 11,
- kYes_ProactiveSwap = 12,
+ kYes_CrossSiteProactiveSwap = 12,
+ kYes_SameSiteProactiveSwap = 13,
+ kNo_SameDocumentNavigation = 14,
+ kNo_SamePageNavigation = 15,
+ kNo_WillReplaceEntry = 16,
+ kNo_Reload = 17,
+ kNo_Guest = 18,
- kMaxValue = kYes_ProactiveSwap
+ kMaxValue = kNo_Guest
};
} // namespace content