Add new BFCache level for terminated DBSC sessions
A terminated Device Bound Session Credentials session can lead to authorization changes similar to the way an expired cookie can. This CL adds a new BFCache CacheControlNoStoreLevel for this, which will evict a page from the cache if a DBSC session is terminated. Bug: 353774923 Change-Id: I48e2daddb33d1102b5ccaf946eb0c541b5a07cda Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6198936 Reviewed-by: Dave Tapuska <dtapuska@chromium.org> Reviewed-by: Eric Seckler <eseckler@chromium.org> Commit-Queue: Daniel Rubery <drubery@chromium.org> Reviewed-by: Fergal Daly <fergal@chromium.org> Reviewed-by: Chris Thompson <cthomp@chromium.org> Cr-Commit-Position: refs/heads/main@{#1422196}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
4143a049e5
commit
03e719955f
base/tracing/protos
content
third_party/blink/public/devtools_protocol
tools/metrics/histograms/metadata/navigation
@ -700,6 +700,7 @@ message BackForwardCacheCanStoreDocumentResult {
|
||||
WEBVIEW_MESSAGE_LISTENER_INJECTED = 62;
|
||||
WEBVIEW_SAFE_BROWSING_ALLOWLIST_CHANGED = 63;
|
||||
WEBVIEW_DOCUMENT_START_JAVASCRIPT_CHANGED = 64;
|
||||
CACHE_CONTROL_NO_STORE_DEVICE_BOUND_SESSION_TERMINATED = 65;
|
||||
}
|
||||
|
||||
optional BackForwardCacheNotRestoredReason
|
||||
|
@ -37,6 +37,7 @@ include_rules = [
|
||||
"+components/system_media_controls",
|
||||
"+components/tracing",
|
||||
"+components/ukm",
|
||||
"+components/unexportable_keys/features.h",
|
||||
"+components/url_formatter",
|
||||
"+components/viz",
|
||||
"+components/web_package",
|
||||
|
@ -2,13 +2,14 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "content/browser/back_forward_cache_browsertest.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/test/test_future.h"
|
||||
#include "build/build_config.h"
|
||||
#include "build/buildflag.h"
|
||||
#include "build/chromecast_buildflags.h"
|
||||
#include "components/unexportable_keys/features.h"
|
||||
#include "content/browser/back_forward_cache_browsertest.h"
|
||||
#include "content/browser/renderer_host/navigation_request.h"
|
||||
#include "content/browser/renderer_host/render_frame_host_impl.h"
|
||||
#include "content/browser/web_contents/web_contents_impl.h"
|
||||
@ -19,9 +20,12 @@
|
||||
#include "content/public/test/content_browser_test_utils.h"
|
||||
#include "content/public/test/test_navigation_observer.h"
|
||||
#include "content/shell/browser/shell.h"
|
||||
#include "net/device_bound_sessions/test_support.h"
|
||||
#include "net/net_buildflags.h"
|
||||
#include "net/test/embedded_test_server/controllable_http_response.h"
|
||||
#include "net/test/spawned_test_server/spawned_test_server.h"
|
||||
#include "net/test/test_data_directory.h"
|
||||
#include "services/network/public/mojom/clear_data_filter.mojom.h"
|
||||
#include "third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h"
|
||||
|
||||
// This file contains back-/forward-cache tests for the
|
||||
@ -1930,4 +1934,256 @@ IN_PROC_BROWSER_TEST_F(
|
||||
BlockListedFeatures()));
|
||||
}
|
||||
|
||||
#if BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
|
||||
|
||||
class DeviceBoundSessionAccessObserver : public content::WebContentsObserver {
|
||||
public:
|
||||
DeviceBoundSessionAccessObserver(
|
||||
content::WebContents* web_contents,
|
||||
base::RepeatingCallback<void(
|
||||
const net::device_bound_sessions::SessionAccess&)> on_access_callback)
|
||||
: WebContentsObserver(web_contents),
|
||||
on_access_callback_(std::move(on_access_callback)) {}
|
||||
|
||||
void OnDeviceBoundSessionAccessed(
|
||||
content::NavigationHandle* navigation,
|
||||
const net::device_bound_sessions::SessionAccess& access) override {
|
||||
on_access_callback_.Run(access);
|
||||
}
|
||||
void OnDeviceBoundSessionAccessed(
|
||||
content::RenderFrameHost* rfh,
|
||||
const net::device_bound_sessions::SessionAccess& access) override {
|
||||
on_access_callback_.Run(access);
|
||||
}
|
||||
|
||||
private:
|
||||
base::RepeatingCallback<void(
|
||||
const net::device_bound_sessions::SessionAccess&)>
|
||||
on_access_callback_;
|
||||
};
|
||||
|
||||
class BackForwardCacheBrowserTestRestoreUnlessDeviceBoundSessionTerminated
|
||||
: public BackForwardCacheBrowserTest {
|
||||
protected:
|
||||
void SetUpCommandLine(base::CommandLine* command_line) override {
|
||||
EnableFeatureAndSetParams(features::kBackForwardCache, "", "");
|
||||
EnableFeatureAndSetParams(
|
||||
features::kDeviceBoundSessionTerminationEvictBackForwardCache, "", "");
|
||||
EnableFeatureAndSetParams(net::features::kDeviceBoundSessions, "", "");
|
||||
EnableFeatureAndSetParams(
|
||||
unexportable_keys::
|
||||
kEnableBoundSessionCredentialsSoftwareKeysForManualTesting,
|
||||
"", "");
|
||||
BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
|
||||
}
|
||||
};
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(
|
||||
BackForwardCacheBrowserTestRestoreUnlessDeviceBoundSessionTerminated,
|
||||
NoCacheControlNoStoreButSessionTerminated) {
|
||||
EXPECT_TRUE(embedded_test_server()->InitializeAndListen());
|
||||
embedded_test_server()->RegisterRequestHandler(
|
||||
net::device_bound_sessions::GetTestRequestHandler(
|
||||
embedded_test_server()->base_url()));
|
||||
embedded_test_server()->StartAcceptingConnections();
|
||||
|
||||
Shell* tab_to_be_bfcached = shell();
|
||||
Shell* tab_to_create_session = CreateBrowser();
|
||||
|
||||
// 1) Load the document without cache-control:no-store
|
||||
EXPECT_TRUE(NavigateToURL(tab_to_be_bfcached,
|
||||
embedded_test_server()->GetURL("/set-header")));
|
||||
RenderFrameHostImplWrapper cached_rfh(current_frame_host());
|
||||
|
||||
// 2) Navigate away. `cached_rfh` should enter bfcache.
|
||||
EXPECT_TRUE(NavigateToURL(tab_to_be_bfcached, embedded_test_server()->GetURL(
|
||||
"b.com", "/set-header")));
|
||||
EXPECT_TRUE(cached_rfh->IsInBackForwardCache());
|
||||
|
||||
// 3) Create a device bound session in another tab
|
||||
base::test::TestFuture<net::device_bound_sessions::SessionAccess> future;
|
||||
DeviceBoundSessionAccessObserver observer(
|
||||
tab_to_create_session->web_contents(),
|
||||
future.GetRepeatingCallback<
|
||||
const net::device_bound_sessions::SessionAccess&>());
|
||||
EXPECT_TRUE(NavigateToURL(tab_to_create_session,
|
||||
embedded_test_server()->GetURL("/dbsc_required")));
|
||||
EXPECT_EQ(future.Take().access_type,
|
||||
net::device_bound_sessions::SessionAccess::AccessType::kCreation);
|
||||
|
||||
// 4) Terminate the session. This could happen through natural
|
||||
// navigation, but it's simpler to test it through the
|
||||
// `DeviceBoundSessionManager` interface.
|
||||
network::mojom::DeviceBoundSessionManager* device_bound_session_manager =
|
||||
tab_to_create_session->web_contents()
|
||||
->GetBrowserContext()
|
||||
->GetStoragePartitionForUrl(
|
||||
embedded_test_server()->GetURL("/set-header"),
|
||||
/*can_create=*/true)
|
||||
->GetDeviceBoundSessionManager();
|
||||
ASSERT_TRUE(device_bound_session_manager);
|
||||
|
||||
base::RunLoop run_loop;
|
||||
device_bound_session_manager->DeleteAllSessions(
|
||||
/*created_after_time=*/std::nullopt,
|
||||
/*created_before_time=*/std::nullopt,
|
||||
/*filter=*/nullptr, run_loop.QuitClosure());
|
||||
run_loop.Run();
|
||||
|
||||
// 5) Go back. `cached_rfh` should be restored from bfcache.
|
||||
ASSERT_TRUE(HistoryGoBack(tab_to_be_bfcached->web_contents()));
|
||||
ExpectRestored(FROM_HERE);
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(
|
||||
BackForwardCacheBrowserTestRestoreUnlessDeviceBoundSessionTerminated,
|
||||
CacheControlNoStoreSessionTerminated) {
|
||||
EXPECT_TRUE(embedded_test_server()->InitializeAndListen());
|
||||
embedded_test_server()->RegisterRequestHandler(
|
||||
net::device_bound_sessions::GetTestRequestHandler(
|
||||
embedded_test_server()->base_url()));
|
||||
embedded_test_server()->StartAcceptingConnections();
|
||||
|
||||
Shell* tab_to_be_bfcached = shell();
|
||||
Shell* tab_to_create_session = CreateBrowser();
|
||||
|
||||
// 1) Load the document with cache-control:no-store
|
||||
EXPECT_TRUE(NavigateToURL(
|
||||
tab_to_be_bfcached,
|
||||
embedded_test_server()->GetURL("/set-header?Cache-Control: no-store")));
|
||||
RenderFrameHostImplWrapper cached_rfh(current_frame_host());
|
||||
cached_rfh->GetBackForwardCacheMetrics()->SetObserverForTesting(this);
|
||||
|
||||
// 2) Navigate away. `cached_rfh` should enter bfcache.
|
||||
EXPECT_TRUE(NavigateToURL(tab_to_be_bfcached, embedded_test_server()->GetURL(
|
||||
"b.com", "/set-header")));
|
||||
EXPECT_TRUE(cached_rfh->IsInBackForwardCache());
|
||||
|
||||
// 3) Create a device bound session in another tab
|
||||
base::test::TestFuture<net::device_bound_sessions::SessionAccess> future;
|
||||
DeviceBoundSessionAccessObserver observer(
|
||||
tab_to_create_session->web_contents(),
|
||||
future.GetRepeatingCallback<
|
||||
const net::device_bound_sessions::SessionAccess&>());
|
||||
EXPECT_TRUE(NavigateToURL(tab_to_create_session,
|
||||
embedded_test_server()->GetURL("/dbsc_required")));
|
||||
EXPECT_EQ(future.Take().access_type,
|
||||
net::device_bound_sessions::SessionAccess::AccessType::kCreation);
|
||||
|
||||
// 4) Terminate the session. This could happen through natural
|
||||
// navigation, but it's simpler to test it through the
|
||||
// `DeviceBoundSessionManager` interface.
|
||||
network::mojom::DeviceBoundSessionManager* device_bound_session_manager =
|
||||
tab_to_create_session->web_contents()
|
||||
->GetBrowserContext()
|
||||
->GetStoragePartitionForUrl(
|
||||
embedded_test_server()->GetURL("/set-header"),
|
||||
/*can_create=*/true)
|
||||
->GetDeviceBoundSessionManager();
|
||||
ASSERT_TRUE(device_bound_session_manager);
|
||||
|
||||
base::RunLoop run_loop;
|
||||
device_bound_session_manager->DeleteAllSessions(
|
||||
/*created_after_time=*/std::nullopt,
|
||||
/*created_before_time=*/std::nullopt,
|
||||
/*filter=*/nullptr, run_loop.QuitClosure());
|
||||
run_loop.Run();
|
||||
|
||||
// 5) Go back. `cached_rfh` should not be restored from bfcache.
|
||||
ASSERT_TRUE(HistoryGoBack(tab_to_be_bfcached->web_contents()));
|
||||
ExpectNotRestored(
|
||||
{NotRestoredReason::kCacheControlNoStoreDeviceBoundSessionTerminated}, {},
|
||||
{}, {}, {}, FROM_HERE);
|
||||
EXPECT_THAT(GetTreeResult()->GetDocumentResult(),
|
||||
MatchesDocumentResult(
|
||||
NotRestoredReasons(
|
||||
{NotRestoredReason::
|
||||
kCacheControlNoStoreDeviceBoundSessionTerminated}),
|
||||
BlockListedFeatures()));
|
||||
}
|
||||
|
||||
std::unique_ptr<net::test_server::HttpResponse> RedirectToUrl(
|
||||
const GURL& gurl,
|
||||
const net::test_server::HttpRequest& request) {
|
||||
auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
|
||||
http_response->set_code(net::HTTP_PERMANENT_REDIRECT);
|
||||
http_response->AddCustomHeader("Location", gurl.spec());
|
||||
return http_response;
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(
|
||||
BackForwardCacheBrowserTestRestoreUnlessDeviceBoundSessionTerminated,
|
||||
CacheControlNoStoreSessionTerminatedOnRedirectedPage) {
|
||||
EXPECT_TRUE(embedded_test_server()->InitializeAndListen());
|
||||
embedded_test_server()->RegisterRequestHandler(
|
||||
net::device_bound_sessions::GetTestRequestHandler(
|
||||
embedded_test_server()->base_url()));
|
||||
embedded_test_server()->StartAcceptingConnections();
|
||||
|
||||
GURL redirected_url =
|
||||
embedded_test_server()->GetURL("/set-header?Cache-Control: no-store");
|
||||
CreateHttpsServer();
|
||||
https_server()->RegisterRequestHandler(
|
||||
base::BindRepeating(&RedirectToUrl, redirected_url));
|
||||
ASSERT_TRUE(https_server()->Start());
|
||||
|
||||
Shell* tab_to_be_bfcached = shell();
|
||||
Shell* tab_to_create_session = CreateBrowser();
|
||||
|
||||
// 1) Load the document with cache-control:no-store.
|
||||
EXPECT_TRUE(NavigateToURL(tab_to_be_bfcached, https_server()->GetURL("/"),
|
||||
redirected_url));
|
||||
RenderFrameHostImplWrapper cached_rfh(current_frame_host());
|
||||
cached_rfh->GetBackForwardCacheMetrics()->SetObserverForTesting(this);
|
||||
|
||||
// 2) Navigate away. `cached_rfh` should enter bfcache.
|
||||
EXPECT_TRUE(NavigateToURL(tab_to_be_bfcached, embedded_test_server()->GetURL(
|
||||
"b.com", "/set-header")));
|
||||
EXPECT_TRUE(cached_rfh->IsInBackForwardCache());
|
||||
|
||||
// 3) Create a device bound session in another tab
|
||||
base::test::TestFuture<net::device_bound_sessions::SessionAccess> future;
|
||||
DeviceBoundSessionAccessObserver observer(
|
||||
tab_to_create_session->web_contents(),
|
||||
future.GetRepeatingCallback<
|
||||
const net::device_bound_sessions::SessionAccess&>());
|
||||
EXPECT_TRUE(NavigateToURL(tab_to_create_session,
|
||||
embedded_test_server()->GetURL("/dbsc_required")));
|
||||
EXPECT_EQ(future.Take().access_type,
|
||||
net::device_bound_sessions::SessionAccess::AccessType::kCreation);
|
||||
|
||||
// 4) Terminate the session. This could happen through natural
|
||||
// navigation, but it's simpler to test it through the
|
||||
// `DeviceBoundSessionManager` interface.
|
||||
network::mojom::DeviceBoundSessionManager* device_bound_session_manager =
|
||||
tab_to_create_session->web_contents()
|
||||
->GetBrowserContext()
|
||||
->GetStoragePartitionForUrl(
|
||||
embedded_test_server()->GetURL("/set-header"),
|
||||
/*can_create=*/true)
|
||||
->GetDeviceBoundSessionManager();
|
||||
ASSERT_TRUE(device_bound_session_manager);
|
||||
|
||||
base::RunLoop run_loop;
|
||||
device_bound_session_manager->DeleteAllSessions(
|
||||
/*created_after_time=*/std::nullopt,
|
||||
/*created_before_time=*/std::nullopt,
|
||||
/*filter=*/nullptr, run_loop.QuitClosure());
|
||||
run_loop.Run();
|
||||
|
||||
// 5) Go back. `cached_rfh` should not be restored from bfcache.
|
||||
ASSERT_TRUE(HistoryGoBack(tab_to_be_bfcached->web_contents()));
|
||||
ExpectNotRestored(
|
||||
{NotRestoredReason::kCacheControlNoStoreDeviceBoundSessionTerminated}, {},
|
||||
{}, {}, {}, FROM_HERE);
|
||||
EXPECT_THAT(GetTreeResult()->GetDocumentResult(),
|
||||
MatchesDocumentResult(
|
||||
NotRestoredReasons(
|
||||
{NotRestoredReason::
|
||||
kCacheControlNoStoreDeviceBoundSessionTerminated}),
|
||||
BlockListedFeatures()));
|
||||
}
|
||||
|
||||
#endif // BUILDFLAG(ENABLE_DEVICE_BOUND_SESSIONS)
|
||||
|
||||
} // namespace content
|
||||
|
@ -1813,6 +1813,9 @@ Page::BackForwardCacheNotRestoredReason NotRestoredReasonToProtocol(
|
||||
NOTREACHED();
|
||||
case Reason::kUnknown:
|
||||
return Page::BackForwardCacheNotRestoredReasonEnum::Unknown;
|
||||
case Reason::kCacheControlNoStoreDeviceBoundSessionTerminated:
|
||||
return Page::BackForwardCacheNotRestoredReasonEnum::
|
||||
CacheControlNoStoreDeviceBoundSessionTerminated;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2109,6 +2112,7 @@ Page::BackForwardCacheNotRestoredReasonType MapNotRestoredReasonToType(
|
||||
case Reason::kCacheControlNoStoreHTTPOnlyCookieModified:
|
||||
case Reason::kUnloadHandlerExistsInMainFrame:
|
||||
case Reason::kUnloadHandlerExistsInSubFrame:
|
||||
case Reason::kCacheControlNoStoreDeviceBoundSessionTerminated:
|
||||
return Page::BackForwardCacheNotRestoredReasonTypeEnum::PageSupportNeeded;
|
||||
case Reason::kNetworkRequestDatapipeDrainedAsBytesConsumer:
|
||||
case Reason::kUnknown:
|
||||
|
@ -204,6 +204,8 @@ ProtoEnum::BackForwardCacheNotRestoredReason NotRestoredReasonToTraceEnum(
|
||||
return ProtoEnum::BLOCKLISTED_FEATURES;
|
||||
case Reason::kUnknown:
|
||||
return ProtoEnum::UNKNOWN;
|
||||
case Reason::kCacheControlNoStoreDeviceBoundSessionTerminated:
|
||||
return ProtoEnum::CACHE_CONTROL_NO_STORE_DEVICE_BOUND_SESSION_TERMINATED;
|
||||
}
|
||||
NOTREACHED();
|
||||
}
|
||||
@ -463,6 +465,9 @@ std::string BackForwardCacheCanStoreDocumentResult::NotRestoredReasonToString(
|
||||
return "Android WebView safe browsing allowlist changed";
|
||||
case Reason::kWebViewDocumentStartJavascriptChanged:
|
||||
return "Android WebView document start script changed";
|
||||
case Reason::kCacheControlNoStoreDeviceBoundSessionTerminated:
|
||||
return "A device bound session was terminated on a cached page with "
|
||||
"Cache-Control: no-store";
|
||||
}
|
||||
}
|
||||
|
||||
@ -542,6 +547,7 @@ BackForwardCacheCanStoreDocumentResult::NotRestoredReasonToReportString(
|
||||
case Reason::kCacheControlNoStore:
|
||||
case Reason::kCacheControlNoStoreCookieModified:
|
||||
case Reason::kCacheControlNoStoreHTTPOnlyCookieModified:
|
||||
case Reason::kCacheControlNoStoreDeviceBoundSessionTerminated:
|
||||
return "response-cache-control-no-store";
|
||||
case Reason::kCookieDisabled:
|
||||
return base::FeatureList::IsEnabled(
|
||||
|
@ -700,8 +700,13 @@ void BackForwardCacheImpl::UpdateCanStoreToIncludeCacheControlNoStore(
|
||||
// Note that kCacheControlNoStoreHTTPOnlyCookieModified,
|
||||
// kCacheControlNoStoreCookieModified and kCacheControlNoStore are mutually
|
||||
// exclusive.
|
||||
if (render_frame_host->GetCookieChangeInfo()
|
||||
.http_only_cookie_modification_count_ > 0) {
|
||||
if (render_frame_host->IsDeviceBoundSessionTerminated() &&
|
||||
base::FeatureList::IsEnabled(
|
||||
features::kDeviceBoundSessionTerminationEvictBackForwardCache)) {
|
||||
result.No(BackForwardCacheMetrics::NotRestoredReason::
|
||||
kCacheControlNoStoreDeviceBoundSessionTerminated);
|
||||
} else if (render_frame_host->GetCookieChangeInfo()
|
||||
.http_only_cookie_modification_count_ > 0) {
|
||||
result.No(BackForwardCacheMetrics::NotRestoredReason::
|
||||
kCacheControlNoStoreHTTPOnlyCookieModified);
|
||||
} else if (render_frame_host->GetCookieChangeInfo()
|
||||
|
@ -2959,6 +2959,12 @@ bool NavigationRequest::ShouldAddCookieChangeListener() {
|
||||
common_params_->url.SchemeIsHTTPOrHTTPS();
|
||||
}
|
||||
|
||||
bool NavigationRequest::ShouldAddDeviceBoundSessionObserver() {
|
||||
// Device bound session expiry should evict pages from the BFCache in
|
||||
// the exact same circumstances as cookie expiry.
|
||||
return ShouldAddCookieChangeListener();
|
||||
}
|
||||
|
||||
void NavigationRequest::StartNavigation() {
|
||||
TRACE_EVENT_WITH_FLOW0("navigation", "NavigationRequest::StartNavigation",
|
||||
TRACE_ID_WITH_SCOPE(kNavigationRequestScope,
|
||||
@ -2999,6 +3005,12 @@ void NavigationRequest::StartNavigation() {
|
||||
GetStoragePartitionWithCurrentSiteInfo(), common_params_->url);
|
||||
}
|
||||
|
||||
if (ShouldAddDeviceBoundSessionObserver()) {
|
||||
device_bound_session_observer_ =
|
||||
std::make_unique<RenderFrameHostImpl::DeviceBoundSessionObserver>(
|
||||
GetStoragePartitionWithCurrentSiteInfo(), common_params_->url);
|
||||
}
|
||||
|
||||
// Compute the redirect chain.
|
||||
// TODO(clamy): Try to simplify this and have the redirects be part of
|
||||
// CommonNavigationParams.
|
||||
@ -3552,6 +3564,14 @@ void NavigationRequest::OnRequestRedirected(
|
||||
cookie_change_listener_.reset();
|
||||
}
|
||||
|
||||
if (ShouldAddDeviceBoundSessionObserver()) {
|
||||
device_bound_session_observer_ =
|
||||
std::make_unique<RenderFrameHostImpl::DeviceBoundSessionObserver>(
|
||||
GetStoragePartitionWithCurrentSiteInfo(), common_params_->url);
|
||||
} else {
|
||||
device_bound_session_observer_.reset();
|
||||
}
|
||||
|
||||
// Check Content Security Policy before the NavigationThrottles run. This
|
||||
// gives CSP a chance to modify requests that NavigationThrottles would
|
||||
// otherwise block.
|
||||
|
@ -1292,6 +1292,11 @@ class CONTENT_EXPORT NavigationRequest
|
||||
return std::move(cookie_change_listener_);
|
||||
}
|
||||
|
||||
std::unique_ptr<RenderFrameHostImpl::DeviceBoundSessionObserver>
|
||||
TakeDeviceBoundSessionObserver() {
|
||||
return std::move(device_bound_session_observer_);
|
||||
}
|
||||
|
||||
// Returns true if there is a speculative RFH that has a pending commit
|
||||
// cross-document navigation, and this NavigationRequest is not a pending
|
||||
// commit NavigationRequest itself. This means that this navigation should be
|
||||
@ -2259,6 +2264,10 @@ class CONTENT_EXPORT NavigationRequest
|
||||
// navigation.
|
||||
bool ShouldAddCookieChangeListener();
|
||||
|
||||
// Returns if we should add/reset the `DeviceBoundSessionObserver` for
|
||||
// the current navigation.
|
||||
bool ShouldAddDeviceBoundSessionObserver();
|
||||
|
||||
// Returns the `StoragePartition` based on the config from the `site_info_`.
|
||||
StoragePartition* GetStoragePartitionWithCurrentSiteInfo();
|
||||
|
||||
@ -3104,6 +3113,17 @@ class CONTENT_EXPORT NavigationRequest
|
||||
std::unique_ptr<RenderFrameHostImpl::CookieChangeListener>
|
||||
cookie_change_listener_;
|
||||
|
||||
// The observer that receives device bound session events and
|
||||
// maintains device bound session information for the domain of the
|
||||
// URL that this `NavigationRequest` is navigating to. The observer
|
||||
// will observe all device bound session changes starting from the
|
||||
// navigation/redirection, and it will be moved to the
|
||||
// `RenderFrameHostImpl` when the navigation is committed and
|
||||
// continues observing until the destructoin of the document.
|
||||
// See `RenderFrameHostImpl::DeviceBoundSessionObserver`.
|
||||
std::unique_ptr<RenderFrameHostImpl::DeviceBoundSessionObserver>
|
||||
device_bound_session_observer_;
|
||||
|
||||
// LCP Critical Path Predictor managed hint data those were already available
|
||||
// at the time of navigation. The hint is passed along to the renderer process
|
||||
// on commit along with the other navigation params.
|
||||
|
@ -15275,10 +15275,13 @@ bool RenderFrameHostImpl::DidCommitNavigationInternal(
|
||||
RecordDocumentCreatedUkmEvent(params->origin, document_ukm_source_id,
|
||||
ukm_recorder);
|
||||
|
||||
// We only replace the `CookieChangeListener` with the one initialized by
|
||||
// the navigation request when navigating to a new document. Otherwise, the
|
||||
// existing `CookieChangeListener` will be reused.
|
||||
// We only replace the `CookieChangeListener` and
|
||||
// `DeviceBoundSessionObserver` with the one initialized by the
|
||||
// navigation request when navigating to a new document. Otherwise,
|
||||
// the existing observers will be reused.
|
||||
cookie_change_listener_ = navigation_request->TakeCookieChangeListener();
|
||||
device_bound_session_observer_ =
|
||||
navigation_request->TakeDeviceBoundSessionObserver();
|
||||
}
|
||||
|
||||
// Note: The renderer never sets |params->is_overriding_user_agent| to true
|
||||
@ -18223,6 +18226,43 @@ RenderFrameHostImpl::GetCookieChangeInfo() {
|
||||
: CookieChangeListener::CookieChangeInfo{};
|
||||
}
|
||||
|
||||
RenderFrameHostImpl::DeviceBoundSessionObserver::DeviceBoundSessionObserver(
|
||||
StoragePartition* storage_partition,
|
||||
GURL& url) {
|
||||
DCHECK(storage_partition);
|
||||
auto* device_bound_session_manager =
|
||||
storage_partition->GetDeviceBoundSessionManager();
|
||||
if (device_bound_session_manager) {
|
||||
device_bound_session_manager->AddObserver(
|
||||
url, receiver_.BindNewPipeAndPassRemote());
|
||||
}
|
||||
}
|
||||
|
||||
RenderFrameHostImpl::DeviceBoundSessionObserver::~DeviceBoundSessionObserver() =
|
||||
default;
|
||||
|
||||
void RenderFrameHostImpl::DeviceBoundSessionObserver::
|
||||
OnDeviceBoundSessionAccessed(
|
||||
const net::device_bound_sessions::SessionAccess& access) {
|
||||
is_terminated_ |=
|
||||
access.access_type ==
|
||||
net::device_bound_sessions::SessionAccess::AccessType::kTermination;
|
||||
}
|
||||
|
||||
void RenderFrameHostImpl::DeviceBoundSessionObserver::Clone(
|
||||
mojo::PendingReceiver<network::mojom::DeviceBoundSessionAccessObserver>
|
||||
observer) {
|
||||
// The `Clone` method is only called for the observers that are part
|
||||
// of network requests, so it is not expected to be called here.
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
bool RenderFrameHostImpl::IsDeviceBoundSessionTerminated() {
|
||||
return device_bound_session_observer_
|
||||
? device_bound_session_observer_->IsTerminated()
|
||||
: false;
|
||||
}
|
||||
|
||||
bool RenderFrameHostImpl::LoadedWithCacheControlNoStoreHeader() {
|
||||
return GetBackForwardCacheDisablingFeatures().Has(
|
||||
blink::scheduler::WebSchedulerTrackedFeature::
|
||||
|
@ -1568,6 +1568,31 @@ class CONTENT_EXPORT RenderFrameHostImpl
|
||||
CookieChangeInfo cookie_change_info_;
|
||||
};
|
||||
|
||||
class DeviceBoundSessionObserver
|
||||
: public network::mojom::DeviceBoundSessionAccessObserver {
|
||||
public:
|
||||
DeviceBoundSessionObserver(StoragePartition* storage_partition, GURL& url);
|
||||
~DeviceBoundSessionObserver() override;
|
||||
DeviceBoundSessionObserver(const DeviceBoundSessionObserver&) = delete;
|
||||
DeviceBoundSessionObserver& operator=(const DeviceBoundSessionObserver&) =
|
||||
delete;
|
||||
|
||||
bool IsTerminated() const { return is_terminated_; }
|
||||
|
||||
private:
|
||||
// network::mojom::DeviceBoundSessionAccessObserver
|
||||
void OnDeviceBoundSessionAccessed(
|
||||
const net::device_bound_sessions::SessionAccess& access) override;
|
||||
void Clone(
|
||||
mojo::PendingReceiver<network::mojom::DeviceBoundSessionAccessObserver>
|
||||
observer) override;
|
||||
|
||||
mojo::Receiver<network::mojom::DeviceBoundSessionAccessObserver> receiver_{
|
||||
this};
|
||||
|
||||
bool is_terminated_ = false;
|
||||
};
|
||||
|
||||
// Indicates that a navigation is ready to commit and can be
|
||||
// handled by this RenderFrame.
|
||||
// |subresource_loader_params| is used in network service land to pass
|
||||
@ -3079,6 +3104,10 @@ class CONTENT_EXPORT RenderFrameHostImpl
|
||||
// last committed document.
|
||||
CookieChangeListener::CookieChangeInfo GetCookieChangeInfo();
|
||||
|
||||
// Retrieves the whether a device bound session on the last committed
|
||||
// document has been terminated.
|
||||
bool IsDeviceBoundSessionTerminated();
|
||||
|
||||
// Records metrics on sudden termination handlers found in this frame and
|
||||
// subframes.
|
||||
void RecordNavigationSuddenTerminationHandlers();
|
||||
@ -5398,6 +5427,9 @@ class CONTENT_EXPORT RenderFrameHostImpl
|
||||
// `cookie_change_listener_` in `NavigationRequest`.
|
||||
std::unique_ptr<CookieChangeListener> cookie_change_listener_;
|
||||
|
||||
// Listens for changes to DeviceBoundSessions on this page.
|
||||
std::unique_ptr<DeviceBoundSessionObserver> device_bound_session_observer_;
|
||||
|
||||
// If true, the renderer side widget is created after the navigation is
|
||||
// committed.
|
||||
bool waiting_for_renderer_widget_creation_after_commit_ = false;
|
||||
|
@ -116,7 +116,8 @@ class CONTENT_EXPORT BackForwardCache {
|
||||
kWebViewMessageListenerInjected = 66,
|
||||
kWebViewSafeBrowsingAllowlistChanged = 67,
|
||||
kWebViewDocumentStartJavascriptChanged = 68,
|
||||
kMaxValue = kWebViewDocumentStartJavascriptChanged,
|
||||
kCacheControlNoStoreDeviceBoundSessionTerminated = 69,
|
||||
kMaxValue = kCacheControlNoStoreDeviceBoundSessionTerminated,
|
||||
};
|
||||
// LINT.ThenChange(//tools/metrics/histograms/metadata/navigation/enums.xml:BackForwardCacheNotRestoredReason)
|
||||
|
||||
|
@ -233,11 +233,6 @@ const char kCookieDeprecationTestingDisableAdsAPIsName[] = "disable_ads_apis";
|
||||
// Adiitional FeatureParams for CookieDeprecationFacilitatedTesting are defined
|
||||
// in chrome/browser/tpcd/experiment/tpcd_experiment_features.cc.
|
||||
|
||||
// When enabled, the DevTools Privacy UI is displayed.
|
||||
BASE_FEATURE(kDevToolsPrivacyUI,
|
||||
"DevToolsPrivacyUI",
|
||||
base::FEATURE_ENABLED_BY_DEFAULT);
|
||||
|
||||
// Enables deferring the creation of the speculative RFH when the navigation
|
||||
// starts. The creation of a speculative RFH consumes about 2ms and is blocking
|
||||
// the network request. With this feature the creation will be deferred until
|
||||
@ -273,6 +268,20 @@ BASE_FEATURE(kDeleteStaleSessionCookiesOnStartup,
|
||||
"DeleteStaleSessionCookiesOnStartup",
|
||||
base::FEATURE_DISABLED_BY_DEFAULT);
|
||||
|
||||
// When a device bound session
|
||||
// (https://github.com/w3c/webappsec-dbsc/blob/main/README.md) is
|
||||
// terminated, evict pages with cache-control:no-store from the
|
||||
// BFCache. Note that if `kCacheControlNoStoreEnterBackForwardCache` is
|
||||
// disabled, no such pages will be in the cache.
|
||||
BASE_FEATURE(kDeviceBoundSessionTerminationEvictBackForwardCache,
|
||||
"DeviceBoundSessionTerminationEvictBackForwardCache",
|
||||
base::FEATURE_DISABLED_BY_DEFAULT);
|
||||
|
||||
// When enabled, the DevTools Privacy UI is displayed.
|
||||
BASE_FEATURE(kDevToolsPrivacyUI,
|
||||
"DevToolsPrivacyUI",
|
||||
base::FEATURE_ENABLED_BY_DEFAULT);
|
||||
|
||||
// Controls whether the Digital Goods API is enabled.
|
||||
// https://github.com/WICG/digital-goods/
|
||||
BASE_FEATURE(kDigitalGoodsApi,
|
||||
|
@ -82,6 +82,8 @@ CONTENT_EXPORT extern const base::FeatureParam<bool>
|
||||
CONTENT_EXPORT extern const base::FeatureParam<int>
|
||||
kCreateSpeculativeRFHDelayMs;
|
||||
CONTENT_EXPORT BASE_DECLARE_FEATURE(kDeleteStaleSessionCookiesOnStartup);
|
||||
CONTENT_EXPORT BASE_DECLARE_FEATURE(
|
||||
kDeviceBoundSessionTerminationEvictBackForwardCache);
|
||||
CONTENT_EXPORT BASE_DECLARE_FEATURE(kDevToolsPrivacyUI);
|
||||
CONTENT_EXPORT BASE_DECLARE_FEATURE(kDigitalGoodsApi);
|
||||
// TODO(crbug.com/364900088): Refactor BTM feature flags and parameters into
|
||||
|
@ -882,6 +882,8 @@ void ShellContentBrowserClient::ConfigureNetworkContextParamsForShell(
|
||||
"cors_exempt_header_list");
|
||||
if (!exempt_header.empty())
|
||||
context_params->cors_exempt_header_list.push_back(exempt_header);
|
||||
context_params->device_bound_sessions_enabled =
|
||||
base::FeatureList::IsEnabled(net::features::kDeviceBoundSessions);
|
||||
}
|
||||
|
||||
void ShellContentBrowserClient::GetHyphenationDictionary(
|
||||
|
@ -1809,6 +1809,7 @@ test("content_browsertests") {
|
||||
"//base/test:test_support",
|
||||
"//build:chromecast_buildflags",
|
||||
"//cc/slim",
|
||||
"//components//unexportable_keys:unexportable_keys",
|
||||
"//components/attribution_reporting:mojom",
|
||||
"//components/cbor",
|
||||
"//components/discardable_memory/client",
|
||||
|
@ -9751,6 +9751,7 @@ domain Page
|
||||
EmbedderExtensionSentMessageToCachedFrame
|
||||
RequestedByWebViewClient
|
||||
PostMessageByWebViewClient
|
||||
CacheControlNoStoreDeviceBoundSessionTerminated
|
||||
|
||||
# Types of not restored reasons for back-forward cache.
|
||||
experimental type BackForwardCacheNotRestoredReasonType extends string
|
||||
|
@ -223,6 +223,9 @@ chromium-metrics-reviews@google.com.
|
||||
<int value="66" label="WebView injected message listener"/>
|
||||
<int value="67" label="WebView safe browsing allowlist changed"/>
|
||||
<int value="68" label="WebView added document start javascript"/>
|
||||
<int value="69"
|
||||
label="CacheControlNoStore is present and Device Bound Session
|
||||
terminated"/>
|
||||
</enum>
|
||||
|
||||
<!-- LINT.ThenChange(//content/public/browser/back_forward_cache.h:NotRestoredReason) -->
|
||||
|
Reference in New Issue
Block a user