Introduce PrefetchedSignedExchangeManager in Blink
This class is created if the page was loaded from a signed exchage which has "allowed-alt-sxg" link headers in the inner response and PrefetchedSignedExchanges were passed from the previous page. It holds the subresource signed exchange related headers ("alternate" link header in the outer response and "allowed-alt-sxg" link header in the inner response of the page's signed exchange), and the passed PrefetchedSignedExchanges. While dispatching link header preloads, LoaderFactoryForFrame::CreateURLLoader() will call PrefetchedSignedExchangeManager::MaybeCreateURLLoader() which creates a PrefetchedSignedExchangeLoader if there is a matching "allowed-alt-sxg" link header in the inner response of the page's signed exchange. After all link header preloads were dispatched, PrefetchedSignedExchangeManager ::StartLoad() is called from HTMLDocumentParser::EnqueueTokenizedChunk(). This method checks whether the all PrefetchedSignedExchangeLoaders has a matching "alternate" outer link header and a matching subresource signed exchange was already prefetched. If the check passes, starts loading the prefetched subresource signed exchange. Otherwise, starts normal loading. This check is intended to prevent the signed exchange distributor from sending arbitrary information to the publisher of the signed exchange. Bug: 935267 Change-Id: I3c27d2ad510f1622e5bb8f6e07e30623b5a92d83 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1610273 Commit-Queue: Tsuyoshi Horo <horo@chromium.org> Reviewed-by: Kinuko Yasuda <kinuko@chromium.org> Reviewed-by: Kunihiko Sakamoto <ksakamoto@chromium.org> Cr-Commit-Position: refs/heads/master@{#660822}
This commit is contained in:

committed by
Commit Bot

parent
79e824b447
commit
1438527860
content
browser
loader
web_package
renderer
third_party/blink
@ -119,7 +119,8 @@ class InnerResponseURLLoader : public network::mojom::URLLoader {
|
||||
const network::ResourceResponseHead& inner_response,
|
||||
std::unique_ptr<const storage::BlobDataHandle> blob_data_handle,
|
||||
const network::URLLoaderCompletionStatus& completion_status,
|
||||
network::mojom::URLLoaderClientPtr client)
|
||||
network::mojom::URLLoaderClientPtr client,
|
||||
bool is_navigation_request)
|
||||
: blob_data_handle_(std::move(blob_data_handle)),
|
||||
completion_status_(completion_status),
|
||||
client_(std::move(client)),
|
||||
@ -128,10 +129,14 @@ class InnerResponseURLLoader : public network::mojom::URLLoader {
|
||||
UpdateRequestResponseStartTime(&response);
|
||||
response.encoded_data_length = 0;
|
||||
client_->OnReceiveResponse(response);
|
||||
// When Network Service is not enabled, we need to wait ProceedWithResponse.
|
||||
// When Network Service is not enabled, we need to wait ProceedWithResponse
|
||||
// for navigation request.
|
||||
// See https://crbug.com/791049.
|
||||
if (base::FeatureList::IsEnabled(network::features::kNetworkService))
|
||||
SendResponseBody();
|
||||
if (is_navigation_request &&
|
||||
!base::FeatureList::IsEnabled(network::features::kNetworkService)) {
|
||||
return;
|
||||
}
|
||||
SendResponseBody();
|
||||
}
|
||||
~InnerResponseURLLoader() override {}
|
||||
|
||||
@ -235,13 +240,13 @@ class SubresourceSignedExchangeURLLoaderFactory
|
||||
traffic_annotation) override {
|
||||
// TODO(crbug.com/935267): Implement CORS check.
|
||||
DCHECK_EQ(request.url, entry_->inner_url());
|
||||
mojo::MakeStrongBinding(
|
||||
std::make_unique<InnerResponseURLLoader>(
|
||||
*entry_->inner_response(),
|
||||
std::make_unique<const storage::BlobDataHandle>(
|
||||
*entry_->blob_data_handle()),
|
||||
*entry_->completion_status(), std::move(client)),
|
||||
std::move(loader));
|
||||
mojo::MakeStrongBinding(std::make_unique<InnerResponseURLLoader>(
|
||||
*entry_->inner_response(),
|
||||
std::make_unique<const storage::BlobDataHandle>(
|
||||
*entry_->blob_data_handle()),
|
||||
*entry_->completion_status(), std::move(client),
|
||||
false /* is_navigation_request */),
|
||||
std::move(loader));
|
||||
}
|
||||
void Clone(network::mojom::URLLoaderFactoryRequest request) override {
|
||||
bindings_.AddBinding(this, std::move(request));
|
||||
@ -338,7 +343,8 @@ class PrefetchedNavigationLoaderInterceptor
|
||||
*exchange_->inner_response(),
|
||||
std::make_unique<const storage::BlobDataHandle>(
|
||||
*exchange_->blob_data_handle()),
|
||||
*exchange_->completion_status(), std::move(client)),
|
||||
*exchange_->completion_status(), std::move(client),
|
||||
true /* is_navigation_request */),
|
||||
std::move(request));
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,17 @@
|
||||
|
||||
namespace content {
|
||||
|
||||
namespace {
|
||||
|
||||
std::string GetHeaderIntegrityString(const net::SHA256HashValue& hash) {
|
||||
std::string header_integrity_string = net::HashValue(hash).ToString();
|
||||
// Change "sha256/" to "sha256-".
|
||||
header_integrity_string[6] = '-';
|
||||
return header_integrity_string;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
struct SignedExchangeSubresourcePrefetchBrowserTestParam {
|
||||
SignedExchangeSubresourcePrefetchBrowserTestParam(
|
||||
bool network_service_enabled)
|
||||
@ -274,15 +285,19 @@ IN_PROC_BROWSER_TEST_P(SignedExchangeSubresourcePrefetchBrowserTest,
|
||||
IN_PROC_BROWSER_TEST_P(SignedExchangeSubresourcePrefetchBrowserTest,
|
||||
PrefetchAlternativeSubresourceSXG) {
|
||||
int script_sxg_fetch_count = 0;
|
||||
int script_fetch_count = 0;
|
||||
const char* prefetch_path = "/prefetch.html";
|
||||
const char* target_sxg_path = "/target.sxg";
|
||||
const char* target_path = "/target.html";
|
||||
const char* script_path_in_sxg = "/script.js";
|
||||
const char* script_sxg_path = "/script_js.sxg";
|
||||
const char* script_path = "/script.js";
|
||||
|
||||
base::RunLoop script_sxg_prefetch_waiter;
|
||||
RegisterRequestMonitor(embedded_test_server(), script_sxg_path,
|
||||
&script_sxg_fetch_count, &script_sxg_prefetch_waiter);
|
||||
base::RunLoop script_prefetch_waiter;
|
||||
RegisterRequestMonitor(embedded_test_server(), script_path,
|
||||
&script_fetch_count, &script_prefetch_waiter);
|
||||
RegisterRequestHandler(embedded_test_server());
|
||||
ASSERT_TRUE(embedded_test_server()->Start());
|
||||
EXPECT_EQ(0, prefetch_url_loader_called_);
|
||||
@ -290,7 +305,12 @@ IN_PROC_BROWSER_TEST_P(SignedExchangeSubresourcePrefetchBrowserTest,
|
||||
const GURL target_sxg_url = embedded_test_server()->GetURL(target_sxg_path);
|
||||
const GURL target_url = embedded_test_server()->GetURL(target_path);
|
||||
const GURL script_sxg_url = embedded_test_server()->GetURL(script_sxg_path);
|
||||
const GURL script_url = embedded_test_server()->GetURL(script_path_in_sxg);
|
||||
const GURL script_url = embedded_test_server()->GetURL(script_path);
|
||||
|
||||
const net::SHA256HashValue target_header_integrity = {{0x01}};
|
||||
const net::SHA256HashValue script_header_integrity = {{0x02}};
|
||||
const std::string script_header_integrity_string =
|
||||
GetHeaderIntegrityString(script_header_integrity);
|
||||
|
||||
const std::string outer_link_header = base::StringPrintf(
|
||||
"<%s>;"
|
||||
@ -299,37 +319,32 @@ IN_PROC_BROWSER_TEST_P(SignedExchangeSubresourcePrefetchBrowserTest,
|
||||
"anchor=\"%s\"",
|
||||
script_sxg_url.spec().c_str(), script_url.spec().c_str());
|
||||
const std::string inner_link_headers = base::StringPrintf(
|
||||
"Link: <%s>;"
|
||||
"rel=\"allowed-alt-sxg\";header-integrity=\"%s\","
|
||||
"Link: "
|
||||
"<%s>;rel=\"allowed-alt-sxg\";header-integrity=\"%s\","
|
||||
"<%s>;rel=\"preload\";as=\"script\"",
|
||||
script_url.spec().c_str(),
|
||||
// This is just a dummy data as of now.
|
||||
// TODO(crbug.com/935267): When we will implement the header integrity
|
||||
// checking logic, add tests for it.
|
||||
"sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
|
||||
script_url.spec().c_str(), script_header_integrity_string.c_str(),
|
||||
script_url.spec().c_str());
|
||||
|
||||
RegisterResponse(
|
||||
prefetch_path,
|
||||
ResponseEntry(base::StringPrintf(
|
||||
"<body><link rel='prefetch' href='%s'></body>", target_sxg_path)));
|
||||
RegisterResponse(
|
||||
target_sxg_path,
|
||||
// We mock the SignedExchangeHandler, so just return a HTML content
|
||||
// as "application/signed-exchange;v=b3".
|
||||
ResponseEntry("<head><title>Prefetch Target (SXG)</title><script "
|
||||
"src=\"./preload.js\"></script></head>",
|
||||
"application/signed-exchange;v=b3",
|
||||
{{"x-content-type-options", "nosniff"},
|
||||
{"link", outer_link_header}}));
|
||||
RegisterResponse(script_path, ResponseEntry("document.title=\"from server\";",
|
||||
"text/javascript"));
|
||||
RegisterResponse(target_sxg_path,
|
||||
// We mock the SignedExchangeHandler, so just return a HTML
|
||||
// content as "application/signed-exchange;v=b3".
|
||||
ResponseEntry("<head><title>Prefetch Target (SXG)</title>"
|
||||
"<script src=\"./script.js\"></script></head>",
|
||||
"application/signed-exchange;v=b3",
|
||||
{{"x-content-type-options", "nosniff"},
|
||||
{"link", outer_link_header}}));
|
||||
RegisterResponse(script_sxg_path,
|
||||
// We mock the SignedExchangeHandler, so just return a JS
|
||||
// content as "application/signed-exchange;v=b3".
|
||||
ResponseEntry("document.title=\"done\";",
|
||||
"application/signed-exchange;v=b3",
|
||||
{{"x-content-type-options", "nosniff"}}));
|
||||
const net::SHA256HashValue target_header_integrity = {{0x01}};
|
||||
const net::SHA256HashValue script_header_integrity = {{0x02}};
|
||||
MockSignedExchangeHandlerFactory factory(
|
||||
{MockSignedExchangeHandlerParams(
|
||||
target_sxg_url, SignedExchangeLoadResult::kSuccess, net::OK,
|
||||
@ -343,6 +358,7 @@ IN_PROC_BROWSER_TEST_P(SignedExchangeSubresourcePrefetchBrowserTest,
|
||||
NavigateToURL(shell(), embedded_test_server()->GetURL(prefetch_path));
|
||||
script_sxg_prefetch_waiter.Run();
|
||||
EXPECT_EQ(1, script_sxg_fetch_count);
|
||||
EXPECT_EQ(0, script_fetch_count);
|
||||
|
||||
WaitUntilLoaded(target_sxg_url);
|
||||
WaitUntilLoaded(script_sxg_url);
|
||||
@ -361,6 +377,500 @@ IN_PROC_BROWSER_TEST_P(SignedExchangeSubresourcePrefetchBrowserTest,
|
||||
EXPECT_EQ(script_sxg_url, script_it->second->outer_url());
|
||||
EXPECT_EQ(script_url, script_it->second->inner_url());
|
||||
EXPECT_EQ(script_header_integrity, *script_it->second->header_integrity());
|
||||
|
||||
// Subsequent navigation to the target URL wouldn't hit the network for
|
||||
// the target URL. The target content should still be read correctly.
|
||||
// The content is loaded from PrefetchedSignedExchangeCache. And the script
|
||||
// is also loaded from PrefetchedSignedExchangeCache.
|
||||
NavigateToURLAndWaitTitle(target_sxg_url, "done");
|
||||
|
||||
EXPECT_EQ(1, script_sxg_fetch_count);
|
||||
EXPECT_EQ(0, script_fetch_count);
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_P(SignedExchangeSubresourcePrefetchBrowserTest,
|
||||
PrefetchAlternativeSubresourceSXG_MultipleResources) {
|
||||
int script1_sxg_fetch_count = 0;
|
||||
int script1_fetch_count = 0;
|
||||
int script2_sxg_fetch_count = 0;
|
||||
int script2_fetch_count = 0;
|
||||
const char* prefetch_path = "/prefetch.html";
|
||||
const char* target_sxg_path = "/target.sxg";
|
||||
const char* target_path = "/target.html";
|
||||
const char* script1_sxg_path = "/script1_js.sxg";
|
||||
const char* script1_path = "/script1.js";
|
||||
const char* script2_sxg_path = "/script2_js.sxg";
|
||||
const char* script2_path = "/script2.js";
|
||||
|
||||
base::RunLoop script1_sxg_prefetch_waiter;
|
||||
RegisterRequestMonitor(embedded_test_server(), script1_sxg_path,
|
||||
&script1_sxg_fetch_count,
|
||||
&script1_sxg_prefetch_waiter);
|
||||
base::RunLoop script1_prefetch_waiter;
|
||||
RegisterRequestMonitor(embedded_test_server(), script1_path,
|
||||
&script1_fetch_count, &script1_prefetch_waiter);
|
||||
base::RunLoop script2_sxg_prefetch_waiter;
|
||||
RegisterRequestMonitor(embedded_test_server(), script2_sxg_path,
|
||||
&script2_sxg_fetch_count,
|
||||
&script2_sxg_prefetch_waiter);
|
||||
base::RunLoop script2_prefetch_waiter;
|
||||
RegisterRequestMonitor(embedded_test_server(), script2_path,
|
||||
&script2_fetch_count, &script2_prefetch_waiter);
|
||||
RegisterRequestHandler(embedded_test_server());
|
||||
ASSERT_TRUE(embedded_test_server()->Start());
|
||||
EXPECT_EQ(0, prefetch_url_loader_called_);
|
||||
|
||||
const GURL target_sxg_url = embedded_test_server()->GetURL(target_sxg_path);
|
||||
const GURL target_url = embedded_test_server()->GetURL(target_path);
|
||||
const GURL script1_sxg_url = embedded_test_server()->GetURL(script1_sxg_path);
|
||||
const GURL script1_url = embedded_test_server()->GetURL(script1_path);
|
||||
const GURL script2_sxg_url = embedded_test_server()->GetURL(script2_sxg_path);
|
||||
const GURL script2_url = embedded_test_server()->GetURL(script2_path);
|
||||
|
||||
const net::SHA256HashValue target_header_integrity = {{0x01}};
|
||||
const net::SHA256HashValue script1_header_integrity = {{0x02}};
|
||||
const std::string script1_header_integrity_string =
|
||||
GetHeaderIntegrityString(script1_header_integrity);
|
||||
const net::SHA256HashValue script2_header_integrity = {{0x03}};
|
||||
const std::string script2_header_integrity_string =
|
||||
GetHeaderIntegrityString(script2_header_integrity);
|
||||
|
||||
const std::string outer_link_header = base::StringPrintf(
|
||||
"<%s>;rel=\"alternate\";type=\"application/signed-exchange;v=b3\";"
|
||||
"anchor=\"%s\","
|
||||
"<%s>;rel=\"alternate\";type=\"application/signed-exchange;v=b3\";"
|
||||
"anchor=\"%s\"",
|
||||
script1_sxg_url.spec().c_str(), script1_url.spec().c_str(),
|
||||
script2_sxg_url.spec().c_str(), script2_url.spec().c_str());
|
||||
const std::string inner_link_headers = base::StringPrintf(
|
||||
"Link: "
|
||||
"<%s>;rel=\"allowed-alt-sxg\";header-integrity=\"%s\","
|
||||
"<%s>;rel=\"preload\";as=\"script\","
|
||||
"<%s>;rel=\"allowed-alt-sxg\";header-integrity=\"%s\","
|
||||
"<%s>;rel=\"preload\";as=\"script\"",
|
||||
script1_url.spec().c_str(), script1_header_integrity_string.c_str(),
|
||||
script1_url.spec().c_str(), script2_url.spec().c_str(),
|
||||
script2_header_integrity_string.c_str(), script2_url.spec().c_str());
|
||||
|
||||
RegisterResponse(
|
||||
prefetch_path,
|
||||
ResponseEntry(base::StringPrintf(
|
||||
"<body><link rel='prefetch' href='%s'></body>", target_sxg_path)));
|
||||
RegisterResponse(script1_path, ResponseEntry("var test_title=\"from\";",
|
||||
"text/javascript"));
|
||||
RegisterResponse(script2_path,
|
||||
ResponseEntry("document.title=test_title+\"server\";",
|
||||
"text/javascript"));
|
||||
RegisterResponse(
|
||||
target_sxg_path,
|
||||
// We mock the SignedExchangeHandler, so just return a HTML
|
||||
// content as "application/signed-exchange;v=b3".
|
||||
ResponseEntry("<head><title>Prefetch Target (SXG)</title>"
|
||||
"<script src=\"./script1.js\"></script>"
|
||||
"<script src=\"./script2.js\"></script></head>",
|
||||
"application/signed-exchange;v=b3",
|
||||
{{"x-content-type-options", "nosniff"},
|
||||
{"link", outer_link_header}}));
|
||||
RegisterResponse(script1_sxg_path,
|
||||
// We mock the SignedExchangeHandler, so just return a JS
|
||||
// content as "application/signed-exchange;v=b3".
|
||||
ResponseEntry("var test_title=\"done\";",
|
||||
"application/signed-exchange;v=b3",
|
||||
{{"x-content-type-options", "nosniff"}}));
|
||||
RegisterResponse(script2_sxg_path,
|
||||
// We mock the SignedExchangeHandler, so just return a JS
|
||||
// content as "application/signed-exchange;v=b3".
|
||||
ResponseEntry("document.title=test_title;",
|
||||
"application/signed-exchange;v=b3",
|
||||
{{"x-content-type-options", "nosniff"}}));
|
||||
MockSignedExchangeHandlerFactory factory({
|
||||
MockSignedExchangeHandlerParams(
|
||||
target_sxg_url, SignedExchangeLoadResult::kSuccess, net::OK,
|
||||
target_url, "text/html", {inner_link_headers},
|
||||
target_header_integrity),
|
||||
MockSignedExchangeHandlerParams(
|
||||
script1_sxg_url, SignedExchangeLoadResult::kSuccess, net::OK,
|
||||
script1_url, "text/javascript", {}, script1_header_integrity),
|
||||
MockSignedExchangeHandlerParams(
|
||||
script2_sxg_url, SignedExchangeLoadResult::kSuccess, net::OK,
|
||||
script2_url, "text/javascript", {}, script2_header_integrity),
|
||||
});
|
||||
ScopedSignedExchangeHandlerFactory scoped_factory(&factory);
|
||||
|
||||
NavigateToURL(shell(), embedded_test_server()->GetURL(prefetch_path));
|
||||
script1_sxg_prefetch_waiter.Run();
|
||||
script2_sxg_prefetch_waiter.Run();
|
||||
EXPECT_EQ(1, script1_sxg_fetch_count);
|
||||
EXPECT_EQ(0, script1_fetch_count);
|
||||
EXPECT_EQ(1, script2_sxg_fetch_count);
|
||||
EXPECT_EQ(0, script2_fetch_count);
|
||||
|
||||
WaitUntilLoaded(target_sxg_url);
|
||||
WaitUntilLoaded(script1_sxg_url);
|
||||
WaitUntilLoaded(script2_sxg_url);
|
||||
|
||||
const auto& cached_exchanges = GetCachedExchanges();
|
||||
EXPECT_EQ(3u, cached_exchanges.size());
|
||||
|
||||
const auto target_it = cached_exchanges.find(target_sxg_url);
|
||||
ASSERT_TRUE(target_it != cached_exchanges.end());
|
||||
EXPECT_EQ(target_sxg_url, target_it->second->outer_url());
|
||||
EXPECT_EQ(target_url, target_it->second->inner_url());
|
||||
EXPECT_EQ(target_header_integrity, *target_it->second->header_integrity());
|
||||
|
||||
const auto script1_it = cached_exchanges.find(script1_sxg_url);
|
||||
ASSERT_TRUE(script1_it != cached_exchanges.end());
|
||||
EXPECT_EQ(script1_sxg_url, script1_it->second->outer_url());
|
||||
EXPECT_EQ(script1_url, script1_it->second->inner_url());
|
||||
EXPECT_EQ(script1_header_integrity, *script1_it->second->header_integrity());
|
||||
|
||||
const auto script2_it = cached_exchanges.find(script2_sxg_url);
|
||||
ASSERT_TRUE(script2_it != cached_exchanges.end());
|
||||
EXPECT_EQ(script2_sxg_url, script2_it->second->outer_url());
|
||||
EXPECT_EQ(script2_url, script2_it->second->inner_url());
|
||||
EXPECT_EQ(script2_header_integrity, *script2_it->second->header_integrity());
|
||||
|
||||
// Subsequent navigation to the target URL wouldn't hit the network for
|
||||
// the target URL. The target content should still be read correctly.
|
||||
// The content is loaded from PrefetchedSignedExchangeCache. And the scripts
|
||||
// are also loaded from PrefetchedSignedExchangeCache.
|
||||
NavigateToURLAndWaitTitle(target_sxg_url, "done");
|
||||
|
||||
EXPECT_EQ(1, script1_sxg_fetch_count);
|
||||
EXPECT_EQ(0, script1_fetch_count);
|
||||
EXPECT_EQ(1, script2_sxg_fetch_count);
|
||||
EXPECT_EQ(0, script2_fetch_count);
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_P(SignedExchangeSubresourcePrefetchBrowserTest,
|
||||
PrefetchAlternativeSubresourceSXG_SameUrl) {
|
||||
int script_sxg_fetch_count = 0;
|
||||
int script_fetch_count = 0;
|
||||
const char* prefetch_path = "/prefetch.html";
|
||||
const char* target_sxg_path = "/target.html";
|
||||
const char* target_path = "/target.html";
|
||||
const char* script_sxg_path = "/script.js";
|
||||
const char* script_path = "/script.js";
|
||||
|
||||
base::RunLoop script_sxg_prefetch_waiter;
|
||||
RegisterRequestMonitor(embedded_test_server(), script_sxg_path,
|
||||
&script_sxg_fetch_count, &script_sxg_prefetch_waiter);
|
||||
RegisterRequestHandler(embedded_test_server());
|
||||
ASSERT_TRUE(embedded_test_server()->Start());
|
||||
EXPECT_EQ(0, prefetch_url_loader_called_);
|
||||
|
||||
const GURL target_sxg_url = embedded_test_server()->GetURL(target_sxg_path);
|
||||
const GURL target_url = embedded_test_server()->GetURL(target_path);
|
||||
const GURL script_sxg_url = embedded_test_server()->GetURL(script_sxg_path);
|
||||
const GURL script_url = embedded_test_server()->GetURL(script_path);
|
||||
|
||||
const net::SHA256HashValue target_header_integrity = {{0x01}};
|
||||
const net::SHA256HashValue script_header_integrity = {{0x02}};
|
||||
const std::string script_header_integrity_string =
|
||||
GetHeaderIntegrityString(script_header_integrity);
|
||||
|
||||
const std::string outer_link_header = base::StringPrintf(
|
||||
"<%s>;"
|
||||
"rel=\"alternate\";"
|
||||
"type=\"application/signed-exchange;v=b3\";"
|
||||
"anchor=\"%s\"",
|
||||
script_sxg_url.spec().c_str(), script_url.spec().c_str());
|
||||
const std::string inner_link_headers = base::StringPrintf(
|
||||
"Link: "
|
||||
"<%s>;rel=\"allowed-alt-sxg\";header-integrity=\"%s\","
|
||||
"<%s>;rel=\"preload\";as=\"script\"",
|
||||
script_url.spec().c_str(), script_header_integrity_string.c_str(),
|
||||
script_url.spec().c_str());
|
||||
|
||||
RegisterResponse(
|
||||
prefetch_path,
|
||||
ResponseEntry(base::StringPrintf(
|
||||
"<body><link rel='prefetch' href='%s'></body>", target_sxg_path)));
|
||||
RegisterResponse(script_path, ResponseEntry("document.title=\"from server\";",
|
||||
"text/javascript"));
|
||||
RegisterResponse(target_sxg_path,
|
||||
// We mock the SignedExchangeHandler, so just return a HTML
|
||||
// content as "application/signed-exchange;v=b3".
|
||||
ResponseEntry("<head><title>Prefetch Target (SXG)</title>"
|
||||
"<script src=\"./script.js\"></script></head>",
|
||||
"application/signed-exchange;v=b3",
|
||||
{{"x-content-type-options", "nosniff"},
|
||||
{"link", outer_link_header}}));
|
||||
RegisterResponse(script_sxg_path,
|
||||
// We mock the SignedExchangeHandler, so just return a JS
|
||||
// content as "application/signed-exchange;v=b3".
|
||||
ResponseEntry("document.title=\"done\";",
|
||||
"application/signed-exchange;v=b3",
|
||||
{{"x-content-type-options", "nosniff"}}));
|
||||
MockSignedExchangeHandlerFactory factory(
|
||||
{MockSignedExchangeHandlerParams(
|
||||
target_sxg_url, SignedExchangeLoadResult::kSuccess, net::OK,
|
||||
target_url, "text/html", {inner_link_headers},
|
||||
target_header_integrity),
|
||||
MockSignedExchangeHandlerParams(
|
||||
script_sxg_url, SignedExchangeLoadResult::kSuccess, net::OK,
|
||||
script_url, "text/javascript", {}, script_header_integrity)});
|
||||
ScopedSignedExchangeHandlerFactory scoped_factory(&factory);
|
||||
|
||||
NavigateToURL(shell(), embedded_test_server()->GetURL(prefetch_path));
|
||||
script_sxg_prefetch_waiter.Run();
|
||||
EXPECT_EQ(1, script_sxg_fetch_count);
|
||||
EXPECT_EQ(0, script_fetch_count);
|
||||
|
||||
WaitUntilLoaded(target_sxg_url);
|
||||
WaitUntilLoaded(script_sxg_url);
|
||||
|
||||
const auto& cached_exchanges = GetCachedExchanges();
|
||||
EXPECT_EQ(2u, cached_exchanges.size());
|
||||
|
||||
const auto target_it = cached_exchanges.find(target_sxg_url);
|
||||
ASSERT_TRUE(target_it != cached_exchanges.end());
|
||||
EXPECT_EQ(target_sxg_url, target_it->second->outer_url());
|
||||
EXPECT_EQ(target_url, target_it->second->inner_url());
|
||||
EXPECT_EQ(target_header_integrity, *target_it->second->header_integrity());
|
||||
|
||||
const auto script_it = cached_exchanges.find(script_sxg_url);
|
||||
ASSERT_TRUE(script_it != cached_exchanges.end());
|
||||
EXPECT_EQ(script_sxg_url, script_it->second->outer_url());
|
||||
EXPECT_EQ(script_url, script_it->second->inner_url());
|
||||
EXPECT_EQ(script_header_integrity, *script_it->second->header_integrity());
|
||||
|
||||
// Subsequent navigation to the target URL wouldn't hit the network for
|
||||
// the target URL. The target content should still be read correctly.
|
||||
// The content is loaded from PrefetchedSignedExchangeCache. And the script
|
||||
// is also loaded from PrefetchedSignedExchangeCache.
|
||||
NavigateToURLAndWaitTitle(target_sxg_url, "done");
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_P(SignedExchangeSubresourcePrefetchBrowserTest,
|
||||
PrefetchAlternativeSubresourceSXG_IntegrityMismatch) {
|
||||
int script_sxg_fetch_count = 0;
|
||||
int script_fetch_count = 0;
|
||||
const char* prefetch_path = "/prefetch.html";
|
||||
const char* target_sxg_path = "/target.sxg";
|
||||
const char* target_path = "/target.html";
|
||||
const char* script_path = "/script.js";
|
||||
const char* script_sxg_path = "/script_js.sxg";
|
||||
|
||||
base::RunLoop script_sxg_prefetch_waiter;
|
||||
RegisterRequestMonitor(embedded_test_server(), script_sxg_path,
|
||||
&script_sxg_fetch_count, &script_sxg_prefetch_waiter);
|
||||
base::RunLoop script_prefetch_waiter;
|
||||
RegisterRequestMonitor(embedded_test_server(), script_path,
|
||||
&script_fetch_count, &script_prefetch_waiter);
|
||||
RegisterRequestHandler(embedded_test_server());
|
||||
ASSERT_TRUE(embedded_test_server()->Start());
|
||||
EXPECT_EQ(0, prefetch_url_loader_called_);
|
||||
|
||||
const GURL target_sxg_url = embedded_test_server()->GetURL(target_sxg_path);
|
||||
const GURL target_url = embedded_test_server()->GetURL(target_path);
|
||||
const GURL script_sxg_url = embedded_test_server()->GetURL(script_sxg_path);
|
||||
const GURL script_url = embedded_test_server()->GetURL(script_path);
|
||||
|
||||
const net::SHA256HashValue target_header_integrity = {{0x01}};
|
||||
const net::SHA256HashValue script_header_integrity = {{0x02}};
|
||||
const net::SHA256HashValue wrong_script_header_integrity = {{0x03}};
|
||||
// Use the wrong header integrity value for "allowed-alt-sxg" link header to
|
||||
// trigger the integrity mismatch fallback logic.
|
||||
const std::string script_header_integrity_string =
|
||||
GetHeaderIntegrityString(wrong_script_header_integrity);
|
||||
|
||||
const std::string outer_link_header = base::StringPrintf(
|
||||
"<%s>;"
|
||||
"rel=\"alternate\";"
|
||||
"type=\"application/signed-exchange;v=b3\";"
|
||||
"anchor=\"%s\"",
|
||||
script_sxg_url.spec().c_str(), script_url.spec().c_str());
|
||||
const std::string inner_link_headers = base::StringPrintf(
|
||||
"Link: "
|
||||
"<%s>;rel=\"allowed-alt-sxg\";header-integrity=\"%s\","
|
||||
"<%s>;rel=\"preload\";as=\"script\"",
|
||||
script_url.spec().c_str(), script_header_integrity_string.c_str(),
|
||||
script_url.spec().c_str());
|
||||
|
||||
RegisterResponse(
|
||||
prefetch_path,
|
||||
ResponseEntry(base::StringPrintf(
|
||||
"<body><link rel='prefetch' href='%s'></body>", target_sxg_path)));
|
||||
RegisterResponse(script_path, ResponseEntry("document.title=\"from server\";",
|
||||
"text/javascript"));
|
||||
RegisterResponse(target_sxg_path,
|
||||
// We mock the SignedExchangeHandler, so just return a HTML
|
||||
// content as "application/signed-exchange;v=b3".
|
||||
ResponseEntry("<head><title>Prefetch Target (SXG)</title>"
|
||||
"<script src=\"./script.js\"></script></head>",
|
||||
"application/signed-exchange;v=b3",
|
||||
{{"x-content-type-options", "nosniff"},
|
||||
{"link", outer_link_header}}));
|
||||
RegisterResponse(script_sxg_path,
|
||||
// We mock the SignedExchangeHandler, so just return a JS
|
||||
// content as "application/signed-exchange;v=b3".
|
||||
ResponseEntry("document.title=\"done\";",
|
||||
"application/signed-exchange;v=b3",
|
||||
{{"x-content-type-options", "nosniff"}}));
|
||||
MockSignedExchangeHandlerFactory factory(
|
||||
{MockSignedExchangeHandlerParams(
|
||||
target_sxg_url, SignedExchangeLoadResult::kSuccess, net::OK,
|
||||
target_url, "text/html", {inner_link_headers},
|
||||
target_header_integrity),
|
||||
MockSignedExchangeHandlerParams(
|
||||
script_sxg_url, SignedExchangeLoadResult::kSuccess, net::OK,
|
||||
script_url, "text/javascript", {}, script_header_integrity)});
|
||||
ScopedSignedExchangeHandlerFactory scoped_factory(&factory);
|
||||
|
||||
NavigateToURL(shell(), embedded_test_server()->GetURL(prefetch_path));
|
||||
script_sxg_prefetch_waiter.Run();
|
||||
EXPECT_EQ(1, script_sxg_fetch_count);
|
||||
EXPECT_EQ(0, script_fetch_count);
|
||||
|
||||
WaitUntilLoaded(target_sxg_url);
|
||||
WaitUntilLoaded(script_sxg_url);
|
||||
|
||||
// The value of "header-integrity" in "allowed-alt-sxg" link header of the
|
||||
// inner response doesn't match the actual header integrity of script_js.sxg.
|
||||
// So the script request must go to the server.
|
||||
NavigateToURLAndWaitTitle(target_sxg_url, "from server");
|
||||
|
||||
EXPECT_EQ(1, script_fetch_count);
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_P(
|
||||
SignedExchangeSubresourcePrefetchBrowserTest,
|
||||
PrefetchAlternativeSubresourceSXG_MultipleResources_IntegrityMismatch) {
|
||||
int script1_sxg_fetch_count = 0;
|
||||
int script1_fetch_count = 0;
|
||||
int script2_sxg_fetch_count = 0;
|
||||
int script2_fetch_count = 0;
|
||||
const char* prefetch_path = "/prefetch.html";
|
||||
const char* target_sxg_path = "/target.sxg";
|
||||
const char* target_path = "/target.html";
|
||||
const char* script1_sxg_path = "/script1_js.sxg";
|
||||
const char* script1_path = "/script1.js";
|
||||
const char* script2_sxg_path = "/script2_js.sxg";
|
||||
const char* script2_path = "/script2.js";
|
||||
|
||||
base::RunLoop script1_sxg_prefetch_waiter;
|
||||
RegisterRequestMonitor(embedded_test_server(), script1_sxg_path,
|
||||
&script1_sxg_fetch_count,
|
||||
&script1_sxg_prefetch_waiter);
|
||||
base::RunLoop script1_prefetch_waiter;
|
||||
RegisterRequestMonitor(embedded_test_server(), script1_path,
|
||||
&script1_fetch_count, &script1_prefetch_waiter);
|
||||
base::RunLoop script2_sxg_prefetch_waiter;
|
||||
RegisterRequestMonitor(embedded_test_server(), script2_sxg_path,
|
||||
&script2_sxg_fetch_count,
|
||||
&script2_sxg_prefetch_waiter);
|
||||
base::RunLoop script2_prefetch_waiter;
|
||||
RegisterRequestMonitor(embedded_test_server(), script2_path,
|
||||
&script2_fetch_count, &script2_prefetch_waiter);
|
||||
RegisterRequestHandler(embedded_test_server());
|
||||
ASSERT_TRUE(embedded_test_server()->Start());
|
||||
EXPECT_EQ(0, prefetch_url_loader_called_);
|
||||
|
||||
const GURL target_sxg_url = embedded_test_server()->GetURL(target_sxg_path);
|
||||
const GURL target_url = embedded_test_server()->GetURL(target_path);
|
||||
const GURL script1_sxg_url = embedded_test_server()->GetURL(script1_sxg_path);
|
||||
const GURL script1_url = embedded_test_server()->GetURL(script1_path);
|
||||
const GURL script2_sxg_url = embedded_test_server()->GetURL(script2_sxg_path);
|
||||
const GURL script2_url = embedded_test_server()->GetURL(script2_path);
|
||||
|
||||
const net::SHA256HashValue target_header_integrity = {{0x01}};
|
||||
const net::SHA256HashValue script1_header_integrity = {{0x02}};
|
||||
const std::string script1_header_integrity_string =
|
||||
GetHeaderIntegrityString(script1_header_integrity);
|
||||
const net::SHA256HashValue script2_header_integrity = {{0x03}};
|
||||
const net::SHA256HashValue wrong_script2_header_integrity = {{0x04}};
|
||||
// Use the wrong header integrity value for "allowed-alt-sxg" link header to
|
||||
// trigger the integrity mismatch fallback logic.
|
||||
const std::string script2_header_integrity_string =
|
||||
GetHeaderIntegrityString(wrong_script2_header_integrity);
|
||||
|
||||
const std::string outer_link_header = base::StringPrintf(
|
||||
"<%s>;rel=\"alternate\";type=\"application/signed-exchange;v=b3\";"
|
||||
"anchor=\"%s\","
|
||||
"<%s>;rel=\"alternate\";type=\"application/signed-exchange;v=b3\";"
|
||||
"anchor=\"%s\"",
|
||||
script1_sxg_url.spec().c_str(), script1_url.spec().c_str(),
|
||||
script2_sxg_url.spec().c_str(), script2_url.spec().c_str());
|
||||
const std::string inner_link_headers = base::StringPrintf(
|
||||
"Link: "
|
||||
"<%s>;rel=\"allowed-alt-sxg\";header-integrity=\"%s\","
|
||||
"<%s>;rel=\"preload\";as=\"script\","
|
||||
"<%s>;rel=\"allowed-alt-sxg\";header-integrity=\"%s\","
|
||||
"<%s>;rel=\"preload\";as=\"script\"",
|
||||
script1_url.spec().c_str(), script1_header_integrity_string.c_str(),
|
||||
script1_url.spec().c_str(), script2_url.spec().c_str(),
|
||||
script2_header_integrity_string.c_str(), script2_url.spec().c_str());
|
||||
|
||||
RegisterResponse(
|
||||
prefetch_path,
|
||||
ResponseEntry(base::StringPrintf(
|
||||
"<body><link rel='prefetch' href='%s'></body>", target_sxg_path)));
|
||||
RegisterResponse(script1_path, ResponseEntry("var test_title=\"from\";",
|
||||
"text/javascript"));
|
||||
RegisterResponse(script2_path,
|
||||
ResponseEntry("document.title=test_title+\" server\";",
|
||||
"text/javascript"));
|
||||
RegisterResponse(
|
||||
target_sxg_path,
|
||||
// We mock the SignedExchangeHandler, so just return a HTML
|
||||
// content as "application/signed-exchange;v=b3".
|
||||
ResponseEntry("<head><title>Prefetch Target (SXG)</title>"
|
||||
"<script src=\"./script1.js\"></script>"
|
||||
"<script src=\"./script2.js\"></script></head>",
|
||||
"application/signed-exchange;v=b3",
|
||||
{{"x-content-type-options", "nosniff"},
|
||||
{"link", outer_link_header}}));
|
||||
RegisterResponse(script1_sxg_path,
|
||||
// We mock the SignedExchangeHandler, so just return a JS
|
||||
// content as "application/signed-exchange;v=b3".
|
||||
ResponseEntry("var test_title=\"done\";",
|
||||
"application/signed-exchange;v=b3",
|
||||
{{"x-content-type-options", "nosniff"}}));
|
||||
RegisterResponse(script2_sxg_path,
|
||||
// We mock the SignedExchangeHandler, so just return a JS
|
||||
// content as "application/signed-exchange;v=b3".
|
||||
ResponseEntry("document.title=test_title;",
|
||||
"application/signed-exchange;v=b3",
|
||||
{{"x-content-type-options", "nosniff"}}));
|
||||
MockSignedExchangeHandlerFactory factory({
|
||||
MockSignedExchangeHandlerParams(
|
||||
target_sxg_url, SignedExchangeLoadResult::kSuccess, net::OK,
|
||||
target_url, "text/html", {inner_link_headers},
|
||||
target_header_integrity),
|
||||
MockSignedExchangeHandlerParams(
|
||||
script1_sxg_url, SignedExchangeLoadResult::kSuccess, net::OK,
|
||||
script1_url, "text/javascript", {}, script1_header_integrity),
|
||||
MockSignedExchangeHandlerParams(
|
||||
script2_sxg_url, SignedExchangeLoadResult::kSuccess, net::OK,
|
||||
script2_url, "text/javascript", {}, script2_header_integrity),
|
||||
});
|
||||
ScopedSignedExchangeHandlerFactory scoped_factory(&factory);
|
||||
|
||||
NavigateToURL(shell(), embedded_test_server()->GetURL(prefetch_path));
|
||||
script1_sxg_prefetch_waiter.Run();
|
||||
script2_sxg_prefetch_waiter.Run();
|
||||
EXPECT_EQ(1, script1_sxg_fetch_count);
|
||||
EXPECT_EQ(0, script1_fetch_count);
|
||||
EXPECT_EQ(1, script2_sxg_fetch_count);
|
||||
EXPECT_EQ(0, script2_fetch_count);
|
||||
|
||||
WaitUntilLoaded(target_sxg_url);
|
||||
WaitUntilLoaded(script1_sxg_url);
|
||||
WaitUntilLoaded(script2_sxg_url);
|
||||
|
||||
const auto& cached_exchanges = GetCachedExchanges();
|
||||
EXPECT_EQ(3u, cached_exchanges.size());
|
||||
|
||||
// The value of "header-integrity" in "allowed-alt-sxg" link header of the
|
||||
// inner response doesn't match the actual header integrity of script2_js.sxg.
|
||||
// So the all script requests must go to the server.
|
||||
NavigateToURLAndWaitTitle(target_sxg_url, "from server");
|
||||
|
||||
EXPECT_EQ(1, script1_sxg_fetch_count);
|
||||
EXPECT_EQ(1, script1_fetch_count);
|
||||
EXPECT_EQ(1, script2_sxg_fetch_count);
|
||||
EXPECT_EQ(1, script2_fetch_count);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "base/auto_reset.h"
|
||||
#include "base/base64.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/bind_helpers.h"
|
||||
#include "base/command_line.h"
|
||||
@ -448,7 +449,7 @@ void FillNavigationParamsRequest(
|
||||
navigation_params->url = !commit_params.original_url.is_empty()
|
||||
? commit_params.original_url
|
||||
: common_params.url;
|
||||
navigation_params->http_method = WebString::FromLatin1(
|
||||
navigation_params->http_method = WebString::FromASCII(
|
||||
!commit_params.original_method.empty() ? commit_params.original_method
|
||||
: common_params.method);
|
||||
|
||||
@ -493,8 +494,31 @@ void FillNavigationParamsRequest(
|
||||
if (!commit_params.prefetched_signed_exchanges.empty()) {
|
||||
DCHECK(base::FeatureList::IsEnabled(
|
||||
features::kSignedExchangeSubresourcePrefetch));
|
||||
// TODO(crbug.com/935267): Pass the prefetched signed exchanges to Blink.
|
||||
navigation_params->prefetched_signed_exchanges = WebVector<std::unique_ptr<
|
||||
blink::WebNavigationParams::PrefetchedSignedExchange>>();
|
||||
|
||||
for (const auto& exchange : commit_params.prefetched_signed_exchanges) {
|
||||
blink::WebURLResponse web_response;
|
||||
WebURLLoaderImpl::PopulateURLResponse(
|
||||
exchange.inner_url, exchange.inner_response, &web_response,
|
||||
false /* report_security_info*/, -1 /* request_id */);
|
||||
std::string header_integrity_base64;
|
||||
base::Base64Encode(
|
||||
base::StringPiece(
|
||||
reinterpret_cast<const char*>(exchange.header_integrity.data),
|
||||
sizeof(exchange.header_integrity.data)),
|
||||
&header_integrity_base64);
|
||||
navigation_params->prefetched_signed_exchanges.emplace_back(
|
||||
std::make_unique<
|
||||
blink::WebNavigationParams::PrefetchedSignedExchange>(
|
||||
exchange.outer_url,
|
||||
WebString::FromLatin1(std::string("sha256-") +
|
||||
header_integrity_base64),
|
||||
exchange.inner_url, web_response,
|
||||
mojo::ScopedMessagePipeHandle(exchange.loader_factory_handle)));
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
navigation_params->had_transient_activation = common_params.has_user_gesture;
|
||||
#endif
|
||||
|
@ -295,6 +295,27 @@ struct BLINK_EXPORT WebNavigationParams {
|
||||
// document.
|
||||
std::unique_ptr<blink::WebServiceWorkerNetworkProvider>
|
||||
service_worker_network_provider;
|
||||
|
||||
// Used for SignedExchangeSubresourcePrefetch.
|
||||
// This struct keeps the information about a prefetched signed exchange.
|
||||
struct BLINK_EXPORT PrefetchedSignedExchange {
|
||||
PrefetchedSignedExchange();
|
||||
PrefetchedSignedExchange(
|
||||
const WebURL& outer_url,
|
||||
const WebString& header_integrity,
|
||||
const WebURL& inner_url,
|
||||
const WebURLResponse& inner_response,
|
||||
mojo::ScopedMessagePipeHandle loader_factory_handle);
|
||||
~PrefetchedSignedExchange();
|
||||
|
||||
WebURL outer_url;
|
||||
WebString header_integrity;
|
||||
WebURL inner_url;
|
||||
WebURLResponse inner_response;
|
||||
mojo::ScopedMessagePipeHandle loader_factory_handle;
|
||||
};
|
||||
WebVector<std::unique_ptr<PrefetchedSignedExchange>>
|
||||
prefetched_signed_exchanges;
|
||||
};
|
||||
|
||||
} // namespace blink
|
||||
|
@ -116,4 +116,20 @@ void WebNavigationParams::FillStaticResponse(WebNavigationParams* params,
|
||||
FillBodyLoader(params, data);
|
||||
}
|
||||
|
||||
WebNavigationParams::PrefetchedSignedExchange::PrefetchedSignedExchange() =
|
||||
default;
|
||||
WebNavigationParams::PrefetchedSignedExchange::~PrefetchedSignedExchange() =
|
||||
default;
|
||||
WebNavigationParams::PrefetchedSignedExchange::PrefetchedSignedExchange(
|
||||
const WebURL& outer_url,
|
||||
const WebString& header_integrity,
|
||||
const WebURL& inner_url,
|
||||
const WebURLResponse& inner_response,
|
||||
mojo::ScopedMessagePipeHandle loader_factory_handle)
|
||||
: outer_url(outer_url),
|
||||
header_integrity(header_integrity),
|
||||
inner_url(inner_url),
|
||||
inner_response(inner_response),
|
||||
loader_factory_handle(std::move(loader_factory_handle)) {}
|
||||
|
||||
} // namespace blink
|
||||
|
@ -48,6 +48,7 @@
|
||||
#include "third_party/blink/renderer/core/html_names.h"
|
||||
#include "third_party/blink/renderer/core/inspector/inspector_trace_events.h"
|
||||
#include "third_party/blink/renderer/core/loader/document_loader.h"
|
||||
#include "third_party/blink/renderer/core/loader/prefetched_signed_exchange_manager.h"
|
||||
#include "third_party/blink/renderer/core/loader/preload_helper.h"
|
||||
#include "third_party/blink/renderer/core/probe/core_probes.h"
|
||||
#include "third_party/blink/renderer/core/script/html_parser_script_runner.h"
|
||||
@ -325,6 +326,15 @@ void HTMLDocumentParser::EnqueueTokenizedChunk(
|
||||
GetDocument()->Loader()->DispatchLinkHeaderPreloads(
|
||||
&chunk->viewport, PreloadHelper::kOnlyLoadMedia);
|
||||
tried_loading_link_headers_ = true;
|
||||
if (GetDocument()->Loader()->GetPrefetchedSignedExchangeManager()) {
|
||||
// Link header preloads for prefetched signed exchanges won't be started
|
||||
// until StartPrefetchedLinkHeaderPreloads() is called. See the header
|
||||
// comment of PrefetchedSignedExchangeManager.
|
||||
GetDocument()
|
||||
->Loader()
|
||||
->GetPrefetchedSignedExchangeManager()
|
||||
->StartPrefetchedLinkHeaderPreloads();
|
||||
}
|
||||
}
|
||||
|
||||
// Defer preloads if any of the chunks contains a <meta> csp tag.
|
||||
|
@ -89,6 +89,8 @@ blink_core_sources("loader") {
|
||||
"navigation_policy.h",
|
||||
"ping_loader.cc",
|
||||
"ping_loader.h",
|
||||
"prefetched_signed_exchange_manager.cc",
|
||||
"prefetched_signed_exchange_manager.h",
|
||||
"preload_helper.cc",
|
||||
"preload_helper.h",
|
||||
"prerenderer_client.cc",
|
||||
|
@ -68,6 +68,7 @@
|
||||
#include "third_party/blink/renderer/core/loader/idleness_detector.h"
|
||||
#include "third_party/blink/renderer/core/loader/interactive_detector.h"
|
||||
#include "third_party/blink/renderer/core/loader/mixed_content_checker.h"
|
||||
#include "third_party/blink/renderer/core/loader/prefetched_signed_exchange_manager.h"
|
||||
#include "third_party/blink/renderer/core/loader/preload_helper.h"
|
||||
#include "third_party/blink/renderer/core/loader/private/frame_client_hints_preferences_context.h"
|
||||
#include "third_party/blink/renderer/core/loader/progress_tracker.h"
|
||||
@ -263,6 +264,7 @@ void DocumentLoader::Trace(blink::Visitor* visitor) {
|
||||
visitor->Trace(application_cache_host_);
|
||||
visitor->Trace(content_security_policy_);
|
||||
visitor->Trace(cached_metadata_handler_);
|
||||
visitor->Trace(prefetched_signed_exchange_manager_);
|
||||
visitor->Trace(use_counter_);
|
||||
}
|
||||
|
||||
@ -1234,6 +1236,8 @@ void DocumentLoader::StartLoadingInternal() {
|
||||
return;
|
||||
}
|
||||
|
||||
InitializePrefetchedSignedExchangeManager();
|
||||
|
||||
if (defers_loading_)
|
||||
body_loader_->SetDefersLoading(true);
|
||||
|
||||
@ -1764,6 +1768,31 @@ void DocumentLoader::ParseAndPersistClientHints(
|
||||
client_hints_preferences_.GetPersistDuration(), url);
|
||||
}
|
||||
|
||||
void DocumentLoader::InitializePrefetchedSignedExchangeManager() {
|
||||
if (params_->prefetched_signed_exchanges.empty())
|
||||
return;
|
||||
DCHECK(RuntimeEnabledFeatures::SignedExchangeSubresourcePrefetchEnabled());
|
||||
// |prefetched_signed_exchanges| is set only when the page is loaded from a
|
||||
// signed exchange.
|
||||
DCHECK(GetResponse().IsSignedExchangeInnerResponse());
|
||||
// When the page is loaded from a signed exchange, |last_redirect| must be the
|
||||
// synthesized redirect for the signed exchange.
|
||||
DCHECK(params_->redirects.size());
|
||||
const WebNavigationParams::RedirectInfo& last_redirect =
|
||||
params_->redirects[params_->redirects.size() - 1];
|
||||
prefetched_signed_exchange_manager_ =
|
||||
PrefetchedSignedExchangeManager::MaybeCreate(
|
||||
GetFrame(),
|
||||
last_redirect.redirect_response.HttpHeaderField(http_names::kLink),
|
||||
GetResponse().HttpHeaderField(http_names::kLink),
|
||||
std::move(params_->prefetched_signed_exchanges));
|
||||
}
|
||||
|
||||
PrefetchedSignedExchangeManager*
|
||||
DocumentLoader::GetPrefetchedSignedExchangeManager() const {
|
||||
return prefetched_signed_exchange_manager_;
|
||||
}
|
||||
|
||||
DEFINE_WEAK_IDENTIFIER_MAP(DocumentLoader)
|
||||
|
||||
} // namespace blink
|
||||
|
@ -76,6 +76,7 @@ class HistoryItem;
|
||||
class LocalFrame;
|
||||
class LocalFrameClient;
|
||||
class MHTMLArchive;
|
||||
class PrefetchedSignedExchangeManager;
|
||||
class ResourceTimingInfo;
|
||||
class SerializedScriptValue;
|
||||
class SubresourceFilter;
|
||||
@ -267,6 +268,8 @@ class CORE_EXPORT DocumentLoader
|
||||
|
||||
int ErrorCode() const { return error_code_; }
|
||||
|
||||
PrefetchedSignedExchangeManager* GetPrefetchedSignedExchangeManager() const;
|
||||
|
||||
protected:
|
||||
bool had_transient_activation() const { return had_transient_activation_; }
|
||||
|
||||
@ -363,6 +366,17 @@ class CORE_EXPORT DocumentLoader
|
||||
// persistence duration.
|
||||
void ParseAndPersistClientHints(const ResourceResponse&);
|
||||
|
||||
// For SignedExchangeSubresourcePrefetch feature. If the page was loaded from
|
||||
// a signed exchage which has "allowed-alt-sxg" link headers in the inner
|
||||
// response and PrefetchedSignedExchanges were passed from the previous page,
|
||||
// initializes a PrefetchedSignedExchangeManager which will hold the
|
||||
// subresource signed exchange related headers ("alternate" link header in the
|
||||
// outer response and "allowed-alt-sxg" link header in the inner response of
|
||||
// the page's signed exchange), and the passed PrefetchedSignedExchanges.
|
||||
// The created PrefetchedSignedExchangeManager will be used to load the
|
||||
// prefetched signed exchanges for matching requests.
|
||||
void InitializePrefetchedSignedExchangeManager();
|
||||
|
||||
// These fields are copied from WebNavigationParams, see there for definition.
|
||||
KURL url_;
|
||||
AtomicString http_method_;
|
||||
@ -457,6 +471,7 @@ class CORE_EXPORT DocumentLoader
|
||||
bool report_timing_info_to_parent_ = false;
|
||||
WebScopedVirtualTimePauser virtual_time_pauser_;
|
||||
Member<SourceKeyedCachedMetadataHandler> cached_metadata_handler_;
|
||||
Member<PrefetchedSignedExchangeManager> prefetched_signed_exchange_manager_;
|
||||
|
||||
// This UseCounter tracks feature usage associated with the lifetime of the
|
||||
// document load. Features recorded prior to commit will be recorded locally.
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "third_party/blink/renderer/core/frame/local_frame.h"
|
||||
#include "third_party/blink/renderer/core/loader/document_loader.h"
|
||||
#include "third_party/blink/renderer/core/loader/frame_or_imported_document.h"
|
||||
#include "third_party/blink/renderer/core/loader/prefetched_signed_exchange_manager.h"
|
||||
#include "third_party/blink/renderer/platform/exported/wrapped_resource_request.h"
|
||||
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
|
||||
|
||||
@ -21,10 +22,16 @@ namespace blink {
|
||||
|
||||
LoaderFactoryForFrame::LoaderFactoryForFrame(
|
||||
const FrameOrImportedDocument& frame_or_imported_document)
|
||||
: frame_or_imported_document_(frame_or_imported_document) {}
|
||||
: frame_or_imported_document_(frame_or_imported_document),
|
||||
prefetched_signed_exchange_manager_(
|
||||
frame_or_imported_document_->GetDocumentLoader()
|
||||
? frame_or_imported_document_->GetDocumentLoader()
|
||||
->GetPrefetchedSignedExchangeManager()
|
||||
: nullptr) {}
|
||||
|
||||
void LoaderFactoryForFrame::Trace(Visitor* visitor) {
|
||||
visitor->Trace(frame_or_imported_document_);
|
||||
visitor->Trace(prefetched_signed_exchange_manager_);
|
||||
LoaderFactory::Trace(visitor);
|
||||
}
|
||||
|
||||
@ -86,6 +93,14 @@ std::unique_ptr<WebURLLoader> LoaderFactoryForFrame::CreateURLLoader(
|
||||
if (loader)
|
||||
return loader;
|
||||
}
|
||||
|
||||
if (prefetched_signed_exchange_manager_) {
|
||||
DCHECK(RuntimeEnabledFeatures::SignedExchangeSubresourcePrefetchEnabled());
|
||||
auto loader =
|
||||
prefetched_signed_exchange_manager_->MaybeCreateURLLoader(webreq);
|
||||
if (loader)
|
||||
return loader;
|
||||
}
|
||||
return frame.GetURLLoaderFactory()->CreateURLLoader(
|
||||
webreq, frame_scheduler->CreateResourceLoadingTaskRunnerHandle());
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
namespace blink {
|
||||
|
||||
class FrameOrImportedDocument;
|
||||
class PrefetchedSignedExchangeManager;
|
||||
|
||||
class LoaderFactoryForFrame final : public ResourceFetcher::LoaderFactory {
|
||||
public:
|
||||
@ -29,6 +30,8 @@ class LoaderFactoryForFrame final : public ResourceFetcher::LoaderFactory {
|
||||
|
||||
private:
|
||||
const Member<const FrameOrImportedDocument> frame_or_imported_document_;
|
||||
const Member<PrefetchedSignedExchangeManager>
|
||||
prefetched_signed_exchange_manager_;
|
||||
};
|
||||
|
||||
} // namespace blink
|
||||
|
295
third_party/blink/renderer/core/loader/prefetched_signed_exchange_manager.cc
vendored
Normal file
295
third_party/blink/renderer/core/loader/prefetched_signed_exchange_manager.cc
vendored
Normal file
@ -0,0 +1,295 @@
|
||||
// Copyright 2019 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 "third_party/blink/renderer/core/loader/prefetched_signed_exchange_manager.h"
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/optional.h"
|
||||
#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
|
||||
#include "third_party/blink/public/platform/platform.h"
|
||||
#include "third_party/blink/public/platform/task_type.h"
|
||||
#include "third_party/blink/public/platform/web_url_load_timing.h"
|
||||
#include "third_party/blink/public/platform/web_url_loader.h"
|
||||
#include "third_party/blink/public/platform/web_url_loader_client.h"
|
||||
#include "third_party/blink/public/platform/web_url_loader_factory.h"
|
||||
#include "third_party/blink/public/platform/web_url_response.h"
|
||||
#include "third_party/blink/renderer/core/dom/document.h"
|
||||
#include "third_party/blink/renderer/core/frame/local_frame.h"
|
||||
#include "third_party/blink/renderer/core/inspector/console_message.h"
|
||||
#include "third_party/blink/renderer/core/loader/alternate_signed_exchange_resource_info.h"
|
||||
#include "third_party/blink/renderer/platform/loader/link_header.h"
|
||||
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
|
||||
#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
|
||||
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
|
||||
#include "third_party/blink/renderer/platform/weborigin/kurl_hash.h"
|
||||
#include "third_party/blink/renderer/platform/wtf/functional.h"
|
||||
#include "third_party/blink/renderer/platform/wtf/hash_map.h"
|
||||
#include "third_party/blink/renderer/platform/wtf/hash_set.h"
|
||||
#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
|
||||
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
|
||||
#include "third_party/blink/renderer/platform/wtf/vector.h"
|
||||
|
||||
namespace blink {
|
||||
|
||||
class PrefetchedSignedExchangeManager::PrefetchedSignedExchangeLoader
|
||||
: public WebURLLoader {
|
||||
public:
|
||||
PrefetchedSignedExchangeLoader(
|
||||
const WebURLRequest& request,
|
||||
scoped_refptr<base::SingleThreadTaskRunner> task_runner)
|
||||
: request_(request),
|
||||
task_runner_(std::move(task_runner)),
|
||||
weak_ptr_factory_(this) {}
|
||||
|
||||
~PrefetchedSignedExchangeLoader() override {}
|
||||
|
||||
base::WeakPtr<PrefetchedSignedExchangeLoader> GetWeakPtr() {
|
||||
return weak_ptr_factory_.GetWeakPtr();
|
||||
}
|
||||
|
||||
void SetURLLoader(std::unique_ptr<WebURLLoader> url_loader) {
|
||||
DCHECK(!url_loader_);
|
||||
url_loader_ = std::move(url_loader);
|
||||
ExecutePendingMethodCalls();
|
||||
}
|
||||
|
||||
const WebURLRequest& request() const { return request_; }
|
||||
|
||||
// WebURLLoader methods:
|
||||
void LoadSynchronously(const WebURLRequest& request,
|
||||
WebURLLoaderClient* client,
|
||||
WebURLResponse& response,
|
||||
base::Optional<WebURLError>& error,
|
||||
WebData& data,
|
||||
int64_t& encoded_data_length,
|
||||
int64_t& encoded_body_length,
|
||||
WebBlobInfo& downloaded_blob) override {
|
||||
NOTREACHED();
|
||||
}
|
||||
void LoadAsynchronously(const WebURLRequest& request,
|
||||
WebURLLoaderClient* client) override {
|
||||
if (url_loader_) {
|
||||
url_loader_->LoadAsynchronously(request, client);
|
||||
return;
|
||||
}
|
||||
// It is safe to use Unretained(client), because |client| is a
|
||||
// ResourceLoader which owns |this|, and we are binding with weak ptr of
|
||||
// |this| here.
|
||||
pending_method_calls_.push(
|
||||
WTF::Bind(&PrefetchedSignedExchangeLoader::LoadAsynchronously,
|
||||
GetWeakPtr(), request, WTF::Unretained(client)));
|
||||
}
|
||||
void SetDefersLoading(bool value) override {
|
||||
if (url_loader_) {
|
||||
url_loader_->SetDefersLoading(value);
|
||||
return;
|
||||
}
|
||||
pending_method_calls_.push(
|
||||
WTF::Bind(&PrefetchedSignedExchangeLoader::SetDefersLoading,
|
||||
GetWeakPtr(), value));
|
||||
}
|
||||
void DidChangePriority(WebURLRequest::Priority new_priority,
|
||||
int intra_priority_value) override {
|
||||
if (url_loader_) {
|
||||
url_loader_->DidChangePriority(new_priority, intra_priority_value);
|
||||
return;
|
||||
}
|
||||
pending_method_calls_.push(
|
||||
WTF::Bind(&PrefetchedSignedExchangeLoader::DidChangePriority,
|
||||
GetWeakPtr(), new_priority, intra_priority_value));
|
||||
}
|
||||
scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() override {
|
||||
return task_runner_;
|
||||
}
|
||||
|
||||
private:
|
||||
void ExecutePendingMethodCalls() {
|
||||
std::queue<base::OnceClosure> pending_calls =
|
||||
std::move(pending_method_calls_);
|
||||
while (!pending_calls.empty()) {
|
||||
std::move(pending_calls.front()).Run();
|
||||
pending_calls.pop();
|
||||
}
|
||||
}
|
||||
|
||||
const WebURLRequest request_;
|
||||
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
|
||||
std::unique_ptr<WebURLLoader> url_loader_;
|
||||
std::queue<base::OnceClosure> pending_method_calls_;
|
||||
|
||||
base::WeakPtrFactory<PrefetchedSignedExchangeLoader> weak_ptr_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(PrefetchedSignedExchangeLoader);
|
||||
};
|
||||
|
||||
// static
|
||||
PrefetchedSignedExchangeManager* PrefetchedSignedExchangeManager::MaybeCreate(
|
||||
LocalFrame* frame,
|
||||
const String& outer_link_header,
|
||||
const String& inner_link_header,
|
||||
WebVector<std::unique_ptr<WebNavigationParams::PrefetchedSignedExchange>>
|
||||
prefetched_signed_exchanges) {
|
||||
DCHECK(RuntimeEnabledFeatures::SignedExchangeSubresourcePrefetchEnabled());
|
||||
if (prefetched_signed_exchanges.empty())
|
||||
return nullptr;
|
||||
std::unique_ptr<AlternateSignedExchangeResourceInfo> alternative_resources =
|
||||
AlternateSignedExchangeResourceInfo::CreateIfValid(outer_link_header,
|
||||
inner_link_header);
|
||||
if (!alternative_resources) {
|
||||
// There is no "allowed-alt-sxg" link header for this resource.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HashMap<KURL, std::unique_ptr<WebNavigationParams::PrefetchedSignedExchange>>
|
||||
prefetched_exchanges_map;
|
||||
for (auto& exchange : prefetched_signed_exchanges) {
|
||||
const KURL outer_url = exchange->outer_url;
|
||||
prefetched_exchanges_map.Set(outer_url, std::move(exchange));
|
||||
}
|
||||
|
||||
return MakeGarbageCollected<PrefetchedSignedExchangeManager>(
|
||||
frame, std::move(alternative_resources),
|
||||
std::move(prefetched_exchanges_map));
|
||||
}
|
||||
|
||||
PrefetchedSignedExchangeManager::PrefetchedSignedExchangeManager(
|
||||
LocalFrame* frame,
|
||||
std::unique_ptr<AlternateSignedExchangeResourceInfo> alternative_resources,
|
||||
HashMap<KURL,
|
||||
std::unique_ptr<WebNavigationParams::PrefetchedSignedExchange>>
|
||||
prefetched_exchanges_map)
|
||||
: frame_(frame),
|
||||
alternative_resources_(std::move(alternative_resources)),
|
||||
prefetched_exchanges_map_(std::move(prefetched_exchanges_map)) {
|
||||
DCHECK(RuntimeEnabledFeatures::SignedExchangeSubresourcePrefetchEnabled());
|
||||
}
|
||||
|
||||
PrefetchedSignedExchangeManager::~PrefetchedSignedExchangeManager() {}
|
||||
|
||||
void PrefetchedSignedExchangeManager::Trace(blink::Visitor* visitor) {
|
||||
visitor->Trace(frame_);
|
||||
}
|
||||
|
||||
void PrefetchedSignedExchangeManager::StartPrefetchedLinkHeaderPreloads() {
|
||||
DCHECK(!started_);
|
||||
started_ = true;
|
||||
TriggerLoad();
|
||||
// Clears |prefetched_exchanges_map_| to release URLLoaderFactory in the
|
||||
// browser process.
|
||||
prefetched_exchanges_map_.clear();
|
||||
// Clears |alternative_resources_| which will not be used forever.
|
||||
alternative_resources_.reset();
|
||||
}
|
||||
|
||||
std::unique_ptr<WebURLLoader>
|
||||
PrefetchedSignedExchangeManager::MaybeCreateURLLoader(
|
||||
const WebURLRequest& request) {
|
||||
if (started_)
|
||||
return nullptr;
|
||||
const auto* matching_resource =
|
||||
alternative_resources_->FindMatchingEntry(request.Url());
|
||||
if (!matching_resource)
|
||||
return nullptr;
|
||||
|
||||
std::unique_ptr<PrefetchedSignedExchangeLoader> loader =
|
||||
std::make_unique<PrefetchedSignedExchangeLoader>(
|
||||
request, frame_->GetFrameScheduler()->GetTaskRunner(
|
||||
TaskType::kInternalLoading));
|
||||
loaders_.emplace_back(loader->GetWeakPtr());
|
||||
return loader;
|
||||
}
|
||||
|
||||
std::unique_ptr<WebURLLoader>
|
||||
PrefetchedSignedExchangeManager::CreateDefaultURLLoader(
|
||||
const WebURLRequest& request) {
|
||||
return frame_->GetURLLoaderFactory()->CreateURLLoader(
|
||||
request,
|
||||
frame_->GetFrameScheduler()->CreateResourceLoadingTaskRunnerHandle());
|
||||
}
|
||||
|
||||
std::unique_ptr<WebURLLoader>
|
||||
PrefetchedSignedExchangeManager::CreatePrefetchedSignedExchangeURLLoader(
|
||||
const WebURLRequest& request,
|
||||
network::mojom::blink::URLLoaderFactoryPtr loader_factory) {
|
||||
return Platform::Current()
|
||||
->WrapURLLoaderFactory(loader_factory.PassInterface().PassHandle())
|
||||
->CreateURLLoader(
|
||||
request,
|
||||
frame_->GetFrameScheduler()->CreateResourceLoadingTaskRunnerHandle());
|
||||
}
|
||||
|
||||
void PrefetchedSignedExchangeManager::TriggerLoad() {
|
||||
std::vector<WebNavigationParams::PrefetchedSignedExchange*>
|
||||
maching_prefetched_exchanges;
|
||||
for (auto loader : loaders_) {
|
||||
if (!loader) {
|
||||
// The loader has been canceled.
|
||||
maching_prefetched_exchanges.emplace_back(nullptr);
|
||||
// We can continue the matching, because the distributor can't send
|
||||
// arbitrary information to the publisher using this resource.
|
||||
continue;
|
||||
}
|
||||
const auto* matching_resource =
|
||||
alternative_resources_->FindMatchingEntry(loader->request().Url());
|
||||
const auto alternative_url = matching_resource->alternative_url();
|
||||
if (!alternative_url.IsValid()) {
|
||||
// There is no matching "alternate" link header in outer response header.
|
||||
break;
|
||||
}
|
||||
const auto exchange_it = prefetched_exchanges_map_.find(alternative_url);
|
||||
if (exchange_it == prefetched_exchanges_map_.end()) {
|
||||
// There is no matching prefetched exchange.
|
||||
break;
|
||||
}
|
||||
if (String(exchange_it->value->header_integrity) !=
|
||||
matching_resource->header_integrity()) {
|
||||
// The header integrity doesn't match.
|
||||
break;
|
||||
}
|
||||
if (KURL(exchange_it->value->inner_url) !=
|
||||
matching_resource->anchor_url()) {
|
||||
// The inner URL doesn't match.
|
||||
break;
|
||||
}
|
||||
maching_prefetched_exchanges.emplace_back(exchange_it->value.get());
|
||||
}
|
||||
if (loaders_.size() != maching_prefetched_exchanges.size()) {
|
||||
// Need to load the all original resources in this case to prevent the
|
||||
// distributor from sending arbitrary information to the publisher.
|
||||
String message =
|
||||
"Failed to match prefetched alternative signed exchange subresources. "
|
||||
"Requesting the all original resources ignoreing all alternative signed"
|
||||
" exchange responses.";
|
||||
frame_->GetDocument()->AddConsoleMessage(
|
||||
ConsoleMessage::Create(mojom::ConsoleMessageSource::kNetwork,
|
||||
mojom::ConsoleMessageLevel::kError, message));
|
||||
for (auto loader : loaders_) {
|
||||
if (!loader)
|
||||
continue;
|
||||
loader->SetURLLoader(CreateDefaultURLLoader(loader->request()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (wtf_size_t i = 0; i < loaders_.size(); ++i) {
|
||||
auto loader = loaders_.at(i);
|
||||
if (!loader)
|
||||
continue;
|
||||
auto* prefetched_exchange = maching_prefetched_exchanges.at(i);
|
||||
network::mojom::blink::URLLoaderFactoryPtr loader_factory =
|
||||
network::mojom::blink::URLLoaderFactoryPtr(
|
||||
network::mojom::blink::URLLoaderFactoryPtrInfo(
|
||||
std::move(prefetched_exchange->loader_factory_handle),
|
||||
network::mojom::URLLoaderFactory::Version_));
|
||||
network::mojom::blink::URLLoaderFactoryPtr loader_factory_clone;
|
||||
loader_factory->Clone(MakeRequest(&loader_factory_clone));
|
||||
// Reset loader_factory_handle to support loading the same resource again.
|
||||
prefetched_exchange->loader_factory_handle =
|
||||
loader_factory_clone.PassInterface().PassHandle();
|
||||
loader->SetURLLoader(CreatePrefetchedSignedExchangeURLLoader(
|
||||
loader->request(), std::move(loader_factory)));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blink
|
102
third_party/blink/renderer/core/loader/prefetched_signed_exchange_manager.h
vendored
Normal file
102
third_party/blink/renderer/core/loader/prefetched_signed_exchange_manager.h
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
// Copyright 2019 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 THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_PREFETCHED_SIGNED_EXCHANGE_MANAGER_H_
|
||||
#define THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_PREFETCHED_SIGNED_EXCHANGE_MANAGER_H_
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "services/network/public/mojom/url_loader_factory.mojom-shared.h"
|
||||
#include "third_party/blink/public/web/web_navigation_params.h"
|
||||
#include "third_party/blink/renderer/core/loader/preload_helper.h"
|
||||
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
|
||||
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
|
||||
#include "third_party/blink/renderer/platform/weborigin/kurl_hash.h"
|
||||
#include "third_party/blink/renderer/platform/wtf/hash_map.h"
|
||||
#include "third_party/blink/renderer/platform/wtf/vector.h"
|
||||
|
||||
namespace blink {
|
||||
|
||||
class AlternateSignedExchangeResourceInfo;
|
||||
class LocalFrame;
|
||||
class WebURLLoader;
|
||||
class WebURLRequest;
|
||||
|
||||
// For SignedExchangeSubresourcePrefetch feature. This class holds the
|
||||
// prefetched signed exchange info and will returns loaders for matching
|
||||
// requests.
|
||||
class PrefetchedSignedExchangeManager
|
||||
: public GarbageCollectedFinalized<PrefetchedSignedExchangeManager> {
|
||||
public:
|
||||
// If threre are no "allowed-alt-sxg" link headers in |inner_link_header|,
|
||||
// or |prefetched_signed_exchanges| is empty, returns null.
|
||||
static PrefetchedSignedExchangeManager* MaybeCreate(
|
||||
LocalFrame* frame,
|
||||
const String& outer_link_header,
|
||||
const String& inner_link_header,
|
||||
WebVector<std::unique_ptr<WebNavigationParams::PrefetchedSignedExchange>>
|
||||
prefetched_signed_exchanges);
|
||||
|
||||
PrefetchedSignedExchangeManager(
|
||||
LocalFrame* frame,
|
||||
std::unique_ptr<AlternateSignedExchangeResourceInfo>
|
||||
alternative_resources,
|
||||
HashMap<KURL,
|
||||
std::unique_ptr<WebNavigationParams::PrefetchedSignedExchange>>
|
||||
prefetched_exchanges_map);
|
||||
~PrefetchedSignedExchangeManager();
|
||||
|
||||
void Trace(blink::Visitor* visitor);
|
||||
|
||||
// Returns a loader if there is a matching resource in
|
||||
// |alternative_resources_|, otherwise returns null. This only checks the
|
||||
// existence of matching "allowed-alt-sxg" link header in the inner response.
|
||||
// This doesn't check the existence of matching "alternate" link header in the
|
||||
// outer response nor the existence of the matching prefetched signed exchange
|
||||
// in |prefetched_signed_exchanges|. This check is done in
|
||||
// StartPrefetchedLinkHeaderPreloads().
|
||||
//
|
||||
// The returned loader doesn't start loading until
|
||||
// StartPrefetchedLinkHeaderPreloads() will be called.
|
||||
std::unique_ptr<WebURLLoader> MaybeCreateURLLoader(
|
||||
const WebURLRequest& request);
|
||||
|
||||
// If the all loaders which have been created by MaybeCreateURLLoader() have
|
||||
// a matching "alternate" link header in the outer response and the matching
|
||||
// prefetched signed exchange in |prefetched_exchanges_map_|, they will start
|
||||
// loading the prefetched signed exchange using the loader_factory_handle in
|
||||
// PrefetchedSignedExchange. Otherwise they will start loading using the
|
||||
// default loader. This check is intended to prevent the signed exchange
|
||||
// distributor from sending arbitrary information to the publisher of the
|
||||
// signed exchange by choosing arbitrary subresources to be loaded from
|
||||
// signed exchange.
|
||||
//
|
||||
// This method must be called when all link header preload requests have been
|
||||
// dispatched. After this method is called, MaybeCreateURLLoader() will always
|
||||
// return null.
|
||||
void StartPrefetchedLinkHeaderPreloads();
|
||||
|
||||
private:
|
||||
class PrefetchedSignedExchangeLoader;
|
||||
|
||||
void TriggerLoad();
|
||||
std::unique_ptr<WebURLLoader> CreateDefaultURLLoader(
|
||||
const WebURLRequest& request);
|
||||
std::unique_ptr<WebURLLoader> CreatePrefetchedSignedExchangeURLLoader(
|
||||
const WebURLRequest& request,
|
||||
network::mojom::blink::URLLoaderFactoryPtr loader_factory);
|
||||
|
||||
Member<LocalFrame> frame_;
|
||||
std::unique_ptr<AlternateSignedExchangeResourceInfo> alternative_resources_;
|
||||
HashMap<KURL, std::unique_ptr<WebNavigationParams::PrefetchedSignedExchange>>
|
||||
prefetched_exchanges_map_;
|
||||
bool started_ = false;
|
||||
|
||||
WTF::Vector<base::WeakPtr<PrefetchedSignedExchangeLoader>> loaders_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(PrefetchedSignedExchangeManager);
|
||||
};
|
||||
|
||||
} // namespace blink
|
||||
|
||||
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_PREFETCHED_SIGNED_EXCHANGE_MANAGER_H_
|
Reference in New Issue
Block a user