0

COOP: restrict-properties 7/*: tokens update after navigation.

This patch is concerned with updating the blink::Page browsing context
group when a cross browsing context group navigation takes place. This
can take two forms:

- When a local frame navigates, the BrowsingContextGroupInfo is passed
directly to the CommitNavigationParams, for an immediate update.

- When a remote frame navigates in another process, the renderer gets
updated by a message from the PageBroadcast interface. This incurs an
inevitable small delay.

DETAILS

For the local frame update, the passed BrowsingContextGroupInfo is optional and is only set for top-level navigations to a different
browsing context group. It takes the following route:
- RenderFrameImpl::CommitNavigation
- WebLocalFrameImpl::CommitNavigation
- FrameLoader::CommitNavigation
- DocumentLoader::DocumentLoader
- DocumentLoader::CommitNavigation
- Page::UpdateBrowsingContextGroup

When the browser receives the information that a page has navigated to
another browsing context group, we use the PageBroadcast interface to
tell each renderer process holding a proxy of the navigated frame, that
the blink::Page is now in another browsing context group. This goes
through:
- Navigator::DidNavigate
- RenderFrameHostManager::ExecutePageBroadcastMethod
- WebViewImpl::UpdatePageBrowsingContextGroup
- Page::UpdateBrowsingContextGroup

Testing is done in the next patch with the
CoopRestrictPropertiesAccessBrowserTest suite.

Bug: 1221127
Change-Id: Ie6300caee1c3b3e92add97a002416c8e5eddf316
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4514696
Reviewed-by: Charlie Reis <creis@chromium.org>
Commit-Queue: Daniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1152564}
This commit is contained in:
Arthur Hemery
2023-06-02 15:50:00 +00:00
committed by Chromium LUCI CQ
parent d81e47fbca
commit d51484b18b
19 changed files with 205 additions and 5 deletions

@@ -3981,7 +3981,8 @@ NavigationControllerImpl::CreateNavigationRequestFromLoadParams(
base::flat_map<::blink::mojom::RuntimeFeatureState, bool>(),
/*fenced_frame_properties=*/absl::nullopt,
/*not_restored_reasons=*/nullptr,
/*load_with_storage_access=*/false);
/*load_with_storage_access=*/false,
/*browsing_context_group_info=*/absl::nullopt);
#if BUILDFLAG(IS_ANDROID)
if (ValidateDataURLAsString(params.data_url_as_string)) {
commit_params->data_url_as_string = params.data_url_as_string->data();

@@ -59,6 +59,7 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/frame/frame_policy.h"
#include "third_party/blink/public/common/page/browsing_context_group_info.h"
#include "third_party/blink/public/common/page_state/page_state.h"
#include "third_party/blink/public/common/tokens/tokens.h"
#include "third_party/blink/public/mojom/frame/frame_owner_properties.mojom.h"
@@ -144,6 +145,12 @@ class MockPageBroadcast : public blink::mojom::PageBroadcast {
blink::mojom::RemoteMainFrameInterfacesPtr remote_main_frame_interfaces),
(override));
MOCK_METHOD(
void,
UpdatePageBrowsingContextGroup,
(const blink::BrowsingContextGroupInfo& browsing_context_group_info),
(override));
mojo::PendingAssociatedRemote<blink::mojom::PageBroadcast> GetRemote() {
return receiver_.BindNewEndpointAndPassDedicatedRemote();
}
@@ -4661,6 +4668,53 @@ TEST_F(NavigationControllerTest, NavigationApiDisposedEntries) {
EXPECT_EQ(main_frame_disposed_keys[0], "3");
}
// Once instantiated, will insert `mock_page_broadcast` as the PageBroadcast on
// a newly created RenderViewHost. This is important for listening for the
// update to a RenderViewHost which was created for a proxy, as it swaps to a
// local frame in a different browsing context group. Note that this this will
// only work once, as MockPageBroadcast does not support multiple bindings.
class PageBroadcastMockInserter : public WebContentsObserver {
public:
explicit PageBroadcastMockInserter(
content::WebContents* web_contents,
testing::NiceMock<MockPageBroadcast>* mock_page_broadcast)
: WebContentsObserver(web_contents),
mock_page_broadcast_(mock_page_broadcast) {}
void RenderViewHostChanged(RenderViewHost* old_host,
RenderViewHost* new_host) override {
static_cast<TestRenderViewHost*>(new_host)->BindPageBroadcast(
mock_page_broadcast_->GetRemote());
}
private:
raw_ptr<testing::NiceMock<MockPageBroadcast>> mock_page_broadcast_;
};
// Test that navigations across browsing context groups trigger a page broadcast
// with up to date browsing context group information.
TEST_F(NavigationControllerTest, BrowsingContextGroupUpdate) {
const GURL url1("http://a/");
const GURL url2("chrome://ukm");
// Start on a first page.
NavigateAndCommit(url1);
SiteInstanceImpl* initial_instance = main_test_rfh()->GetSiteInstance();
// Setup the page broadcast expectations. We expect no call to be made, as the
// RenderViewHost for B will get its update through the local frame commit.
testing::NiceMock<MockPageBroadcast> mock_page_broadcast;
EXPECT_CALL(mock_page_broadcast, UpdatePageBrowsingContextGroup(testing::_))
.Times(0);
PageBroadcastMockInserter mock_inserter(contents(), &mock_page_broadcast);
// Navigate to a cross browsing context group page. The update function should
// not be called.
NavigateAndCommit(url2);
SiteInstanceImpl* final_instance = main_test_rfh()->GetSiteInstance();
EXPECT_FALSE(initial_instance->IsRelatedSiteInstance(final_instance));
}
class NavigationControllerFencedFrameTest : public NavigationControllerTest {
public:
NavigationControllerFencedFrameTest() {

@@ -964,7 +964,8 @@ NavigationEntryImpl::ConstructCommitNavigationParams(
base::flat_map<::blink::mojom::RuntimeFeatureState, bool>(),
/*fenced_frame_properties=*/absl::nullopt,
/*not_restored_reasons=*/nullptr,
/*load_with_storage_access=*/false);
/*load_with_storage_access=*/false,
/*browsing_context_group_info=*/absl::nullopt);
#if BUILDFLAG(IS_ANDROID)
// `data_url_as_string` is saved in NavigationEntry but should only be used by
// main frames, because loadData* navigations can only happen on the main

@@ -1318,7 +1318,8 @@ std::unique_ptr<NavigationRequest> NavigationRequest::CreateRendererInitiated(
base::flat_map<::blink::mojom::RuntimeFeatureState, bool>(),
/*fenced_frame_properties=*/absl::nullopt,
/*not_restored_reasons=*/nullptr,
/*load_with_storage_access=*/load_with_storage_access);
/*load_with_storage_access=*/load_with_storage_access,
/*browsing_context_group_info=*/absl::nullopt);
commit_params->navigation_timing->system_entropy_at_navigation_start =
SystemEntropyUtils::ComputeSystemEntropyForFrameTreeNode(
@@ -1460,7 +1461,8 @@ NavigationRequest::CreateForSynchronousRendererCommit(
base::flat_map<::blink::mojom::RuntimeFeatureState, bool>(),
/*fenced_frame_properties=*/absl::nullopt,
/*not_restored_reasons=*/nullptr,
/*load_with_storage_access=*/false);
/*load_with_storage_access=*/false,
/*browsing_context_group_info=*/absl::nullopt);
blink::mojom::BeginNavigationParamsPtr begin_params =
blink::mojom::BeginNavigationParams::New();
std::unique_ptr<NavigationRequest> navigation_request(new NavigationRequest(

@@ -615,6 +615,31 @@ void Navigator::DidNavigate(
site_instance->group());
}
// If this was the navigation of a top-level frame to another browsing context
// group, update the browsing context group in all the renderers that have a
// representation of this page. Do not update the page in the main frame's own
// process, as it was already updated during commit.
// TODO(https://crbug.com/1446696): See if that can be consolidated with other
// similar IPCs.
if (render_frame_host->is_main_frame() &&
navigation_request->browsing_context_group_swap().ShouldSwap()) {
SiteInstanceImpl* final_site_instance =
render_frame_host->GetSiteInstance();
blink::BrowsingContextGroupInfo browsing_context_group_info(
final_site_instance->browsing_instance_token(),
final_site_instance->coop_related_group_token());
frame_tree.root()->render_manager()->ExecutePageBroadcastMethod(
base::BindRepeating(
[](const blink::BrowsingContextGroupInfo& info,
RenderViewHostImpl* rvh) {
if (auto& broadcast = rvh->GetAssociatedPageBroadcast()) {
broadcast->UpdatePageBrowsingContextGroup(info);
}
},
browsing_context_group_info),
final_site_instance->group());
}
// Store some information for recording WebPlatform security metrics. These
// metrics depends on information present in the NavigationRequest. However
// they must be recorded after the NavigationRequest has been destroyed and

@@ -13107,6 +13107,20 @@ void RenderFrameHostImpl::SendCommitNavigation(
// otherwise).
MaybeSendFencedFrameReportingBeacon(*navigation_request);
// If this commit is for a main frame in another browsing context group, warn
// the renderer that it should update the browsing context group information
// of the page if this frame successfully commits. Note that the
// BrowsingContextGroupInfo in the params should only be populated at commit
// time, and only in the case of a swap.
CHECK(!commit_params->browsing_context_group_info.has_value());
if (is_main_frame() &&
navigation_request->browsing_context_group_swap().ShouldSwap()) {
commit_params->browsing_context_group_info =
blink::BrowsingContextGroupInfo(
GetSiteInstance()->browsing_instance_token(),
GetSiteInstance()->coop_related_group_token());
}
commit_params->commit_sent = base::TimeTicks::Now();
navigation_client->CommitNavigation(
std::move(common_params), std::move(commit_params),
@@ -13146,6 +13160,20 @@ void RenderFrameHostImpl::SendCommitFailedNavigation(
DCHECK_NE(GURL(), common_params->url);
DCHECK_NE(net::OK, error_code);
IncreaseCommitNavigationCounter();
// If this commit is for a main frame in another browsing context group, warn
// the renderer that it should update the browsing context group information
// of the page. Note that the BrowsingContextGroupInfo in the params should
// only be populated at commit time, and only in the case of a swap.
CHECK(!commit_params->browsing_context_group_info.has_value());
if (is_main_frame() &&
navigation_request->browsing_context_group_swap().ShouldSwap()) {
commit_params->browsing_context_group_info =
blink::BrowsingContextGroupInfo(
GetSiteInstance()->browsing_instance_token(),
GetSiteInstance()->coop_related_group_token());
}
navigation_client->CommitFailedNavigation(
std::move(common_params), std::move(commit_params),
has_stale_copy_in_cache, error_code, extended_error_code,

@@ -1072,6 +1072,9 @@ void FillMiscNavigationParams(
navigation_params->ancestor_or_self_has_cspee =
commit_params.ancestor_or_self_has_cspee;
navigation_params->browsing_context_group_info =
commit_params.browsing_context_group_info;
}
std::string GetUniqueNameOfWebFrame(WebFrame* web_frame) {

@@ -57,4 +57,7 @@ void TestPageBroadcast::CreateRemoteMainFrame(
blink::mojom::RemoteFrameInterfacesFromBrowserPtr remote_frame_interfaces,
blink::mojom::RemoteMainFrameInterfacesPtr remote_main_frame_interfaces) {}
void TestPageBroadcast::UpdatePageBrowsingContextGroup(
const blink::BrowsingContextGroupInfo& browsing_context_group_info) {}
} // namespace content

@@ -43,6 +43,8 @@ class TestPageBroadcast : public blink::mojom::PageBroadcast {
blink::mojom::RemoteFrameInterfacesFromBrowserPtr remote_frame_interfaces,
blink::mojom::RemoteMainFrameInterfacesPtr remote_main_frame_interfaces)
override;
void UpdatePageBrowsingContextGroup(const blink::BrowsingContextGroupInfo&
browsing_context_group_info) override;
mojo::AssociatedReceiver<blink::mojom::PageBroadcast> receiver_;
};