blob: 63be7d4b9c4ffd1a830896dea9b7f9c58d868b03 [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/renderer_host/code_cache_host_impl.h"
#include <optional>
#include <string_view>
#include <utility>
#include "base/check_is_test.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/thread_annotations.h"
#include "base/threading/thread.h"
#include "base/types/expected_macros.h"
#include "build/build_config.h"
#include "components/persistent_cache/pending_backend.h"
#include "components/services/storage/public/cpp/buckets/bucket_locator.h"
#include "components/services/storage/public/mojom/cache_storage_control.mojom.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/code_cache/generated_code_cache.h"
#include "content/browser/code_cache/generated_code_cache_context.h"
#include "content/browser/process_lock.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/public/browser/site_isolation_policy.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/content_features.h"
#include "content/public/common/url_constants.h"
#include "mojo/public/cpp/base/big_buffer.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "net/base/features.h"
#include "net/base/io_buffer.h"
#include "third_party/blink/public/common/cache_storage/cache_storage_utils.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/scheme_registry.h"
#include "third_party/blink/public/mojom/loader/code_cache.mojom-data-view.h"
#include "url/gurl.h"
#include "url/origin.h"
using blink::mojom::CacheStorageError;
namespace content {
namespace {
bool use_empty_secondary_key_for_testing_ = false;
enum class Operation {
kRead,
kWrite,
};
bool CheckSecurityForAccessingCodeCacheData(const GURL& resource_url,
int render_process_id,
Operation operation) {
if (!resource_url.is_valid()) {
return false;
}
ProcessLock process_lock =
ChildProcessSecurityPolicyImpl::GetInstance()->GetProcessLock(
render_process_id);
// Code caching is only allowed for http(s) and chrome/chrome-untrusted
// scripts. Furthermore, there is no way for http(s) pages to load chrome or
// chrome-untrusted scripts, so any http(s) page attempting to store data
// about a chrome or chrome-untrusted script would be an indication of
// suspicious activity.
if (resource_url.SchemeIs(content::kChromeUIScheme) ||
resource_url.SchemeIs(content::kChromeUIUntrustedScheme)) {
if (!process_lock.IsLockedToSite()) {
// We can't tell for certain whether this renderer is doing something
// malicious, but we don't trust it enough to store data.
return false;
}
if (process_lock.MatchesScheme(url::kHttpScheme) ||
process_lock.MatchesScheme(url::kHttpsScheme)) {
if (operation == Operation::kWrite) {
mojo::ReportBadMessage("HTTP(S) pages cannot cache WebUI code");
}
return false;
}
// Other schemes which might successfully load chrome or chrome-untrusted
// scripts, such as the PDF viewer, are unsupported but not considered
// dangerous.
return process_lock.MatchesScheme(content::kChromeUIScheme) ||
process_lock.MatchesScheme(content::kChromeUIUntrustedScheme);
}
if (resource_url.SchemeIsHTTPOrHTTPS() ||
blink::CommonSchemeRegistry::IsExtensionScheme(
resource_url.GetScheme())) {
if (process_lock.MatchesScheme(content::kChromeUIScheme) ||
process_lock.MatchesScheme(content::kChromeUIUntrustedScheme)) {
// It is possible for WebUI pages to include open-web content, but such
// usage is rare and we've decided that reasoning about security is easier
// if the WebUI code cache includes only WebUI scripts.
return false;
}
return true;
}
if (operation == Operation::kWrite) {
mojo::ReportBadMessage("Invalid URL scheme for code cache.");
}
return false;
}
// Code caches use two keys: the URL of requested resource |resource_url|
// as the primary key and the origin lock of the renderer that requested this
// resource as secondary key. This function returns the origin lock of the
// renderer that will be used as the secondary key for the code cache.
// The secondary key is:
// Case 1. std::nullopt if the render process is locked to a WebUI page and the
// WebUICodeCache feature is not enabled.
// Case 2. an empty GURL if the render process is not locked to an origin. In
// this case:
// - local code cache: |resource_url| is used as the key.
// - code cache with PersistentCache: caching is disabled if full site isolation
// (site-per-process) is enabled; otherwise, one of two fixed keys is used so
// that there is effectively one cache shared by all renderers for the open
// web and one for WebUI.
// Case 3. a std::nullopt, if the origin lock is opaque (for ex: browser
// initiated navigation to a data: URL). In these cases, the code should not be
// cached since the serialized value of opaque origins should not be used as a
// key.
// Case 4. origin_lock if the scheme of origin_lock is
// Http/Https/chrome/chrome-untrusted.
// Case 5. std::nullopt otherwise.
std::optional<GURL> GetOriginLock(int render_process_id) {
ProcessLock process_lock =
ChildProcessSecurityPolicyImpl::GetInstance()->GetProcessLock(
render_process_id);
if (process_lock.MatchesScheme(content::kChromeUIScheme) ||
process_lock.MatchesScheme(content::kChromeUIUntrustedScheme)) {
if (!base::FeatureList::IsEnabled(features::kWebUICodeCache)) {
// Case 1: No caching for WebUI when WebUICodeCache is disabled.
return std::nullopt;
}
// No caching for WebUI when PersistentCache is used.
// TODO(374930286): Remove this condition when all other blockers for
// caching WebUI resources with PersistentCache are resolved.
if (blink::features::IsPersistentCacheForCodeCacheEnabled()) {
return std::nullopt;
}
}
// Case 2: If process is not locked to a site, it is safe to just use the
// |resource_url| of the requested resource as the key. Return an empty GURL
// as the second key.
if (!process_lock.IsLockedToSite()) {
return GURL();
}
// Case 3: Don't cache the code corresponding to opaque origins. The same
// origin checks should always fail for opaque origins but the serialized
// value of opaque origins does not ensure this.
// NOTE: HasOpaqueOrigin() will return true if the ProcessLock lock url is
// invalid, leading to a return value of std::nullopt.
if (process_lock.HasOpaqueOrigin()) {
return std::nullopt;
}
// Case 4: process_lock_url is used to enforce site-isolation in code caches.
// Http/https/chrome schemes are safe to be used as a secondary key. Other
// schemes could be enabled if they are known to be safe and if it is
// required to cache code from those origins.
//
// file:// URLs will have a "file:" process lock and would thus share a
// cache across all file:// URLs. That would likely be ok for security, but
// since this case is not performance sensitive we will keep things simple and
// limit the cache to http/https/chrome/chrome-untrusted processes.
if (process_lock.MatchesScheme(url::kHttpScheme) ||
process_lock.MatchesScheme(url::kHttpsScheme) ||
process_lock.MatchesScheme(content::kChromeUIScheme) ||
process_lock.MatchesScheme(content::kChromeUIUntrustedScheme) ||
blink::CommonSchemeRegistry::IsExtensionScheme(
process_lock.GetProcessLockURL().GetScheme())) {
return process_lock.GetProcessLockURL();
}
return std::nullopt;
}
void DidGenerateCacheableMetadataInCacheStorageOnUI(
const GURL& url,
base::Time expected_response_time,
mojo_base::BigBuffer data,
const std::string& cache_storage_cache_name,
int render_process_id,
const blink::StorageKey& code_cache_storage_key,
storage::mojom::CacheStorageControl* cache_storage_control_for_testing,
mojo::ReportBadMessageCallback bad_message_callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto* render_process_host = RenderProcessHost::FromID(render_process_id);
if (!render_process_host)
return;
int64_t trace_id = blink::cache_storage::CreateTraceId();
TRACE_EVENT_WITH_FLOW1(
"CacheStorage",
"CodeCacheHostImpl::DidGenerateCacheableMetadataInCacheStorage",
TRACE_ID_GLOBAL(trace_id), TRACE_EVENT_FLAG_FLOW_OUT, "url", url.spec());
mojo::Remote<blink::mojom::CacheStorage> remote;
network::CrossOriginEmbedderPolicy cross_origin_embedder_policy;
network::DocumentIsolationPolicy document_isolation_policy;
storage::mojom::CacheStorageControl* cache_storage_control =
cache_storage_control_for_testing
? cache_storage_control_for_testing
: render_process_host->GetStoragePartition()
->GetCacheStorageControl();
cache_storage_control->AddReceiver(
cross_origin_embedder_policy, mojo::NullRemote(),
document_isolation_policy, mojo::NullRemote(),
storage::BucketLocator::ForDefaultBucket(code_cache_storage_key),
storage::mojom::CacheStorageOwner::kCacheAPI,
remote.BindNewPipeAndPassReceiver());
// Call the remote pointer directly so we can pass the remote to the callback
// itself to preserve its lifetime.
auto* raw_remote = remote.get();
raw_remote->Open(
base::UTF8ToUTF16(cache_storage_cache_name), trace_id,
base::BindOnce(
[](const GURL& url, base::Time expected_response_time,
mojo_base::BigBuffer data, int64_t trace_id,
mojo::Remote<blink::mojom::CacheStorage> preserve_remote_lifetime,
blink::mojom::CacheStorage::OpenResult result) {
if (!result.has_value()) {
// Silently ignore errors.
return;
}
mojo::AssociatedRemote<blink::mojom::CacheStorageCache> remote;
remote.Bind(std::move(result.value()));
remote->WriteSideData(
url, expected_response_time, std::move(data), trace_id,
base::BindOnce(
[](mojo::Remote<blink::mojom::CacheStorage>
preserve_remote_lifetime,
CacheStorageError error) {
// Silently ignore errors.
},
std::move(preserve_remote_lifetime)));
},
url, expected_response_time, std::move(data), trace_id,
std::move(remote)));
}
void AddCodeCacheReceiver(
mojo::UniqueReceiverSet<blink::mojom::CodeCacheHost>* receiver_set,
scoped_refptr<GeneratedCodeCacheContext> context,
int render_process_id,
const net::NetworkIsolationKey& nik,
const blink::StorageKey& storage_key,
mojo::PendingReceiver<blink::mojom::CodeCacheHost> receiver,
CodeCacheHostImpl::ReceiverSet::CodeCacheHostReceiverHandler handler) {
auto host =
CodeCacheHostImpl::Create(render_process_id, context, nik, storage_key);
auto* raw_host = host.get();
auto id = receiver_set->Add(std::move(host), std::move(receiver));
if (handler)
std::move(handler).Run(raw_host, id, *receiver_set);
}
// NoopCodeCacheHost -----------------------------------------------------------
// An implementation of CodeCacheHostImpl that does nothing. This is used for
// cases where there is no GeneratedCodeCacheContext available, so it is not
// possible to serve any sort of cache.
class NoopCodeCacheHost : public CodeCacheHostImpl {
public:
NoopCodeCacheHost(
int render_process_id,
scoped_refptr<GeneratedCodeCacheContext> generated_code_cache_context,
const net::NetworkIsolationKey& nik,
const blink::StorageKey& storage_key)
: CodeCacheHostImpl(render_process_id,
std::move(generated_code_cache_context),
nik,
storage_key) {}
// CodeCacheHostImpl:
void GetPendingBackend(blink::mojom::CodeCacheType cache_type,
GetPendingBackendCallback callback) override {
std::move(callback).Run({});
}
void DidGenerateCacheableMetadata(blink::mojom::CodeCacheType cache_type,
const GURL& url,
base::Time expected_response_time,
mojo_base::BigBuffer data) override {}
void FetchCachedCode(blink::mojom::CodeCacheType cache_type,
const GURL& url,
FetchCachedCodeCallback callback) override {
std::move(callback).Run({}, {});
}
void ClearCodeCacheEntry(blink::mojom::CodeCacheType cache_type,
const GURL& url) override {}
};
// LocalCodeCacheHost ----------------------------------------------------------
// An implementation of CodeCacheHostImpl that uses GeneratedCodeCache locally
// for all operations.
class LocalCodeCacheHost : public CodeCacheHostImpl {
public:
LocalCodeCacheHost(
int render_process_id,
scoped_refptr<GeneratedCodeCacheContext> generated_code_cache_context,
const net::NetworkIsolationKey& nik,
const blink::StorageKey& storage_key)
: CodeCacheHostImpl(render_process_id,
std::move(generated_code_cache_context),
nik,
storage_key) {
CHECK(this->generated_code_cache_context());
}
// CodeCacheHostImpl:
void GetPendingBackend(blink::mojom::CodeCacheType cache_type,
GetPendingBackendCallback callback) override {
mojo::ReportBadMessage("Not using PersistentCache");
}
void DidGenerateCacheableMetadata(blink::mojom::CodeCacheType cache_type,
const GURL& url,
base::Time expected_response_time,
mojo_base::BigBuffer data) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ASSIGN_OR_RETURN(GURL secondary_key,
GetSecondaryKeyForCodeCache(url, render_process_id(),
Operation::kWrite),
[] {});
if (GeneratedCodeCache* code_cache = GetCodeCache(cache_type); code_cache) {
code_cache->WriteEntry(url, secondary_key, network_isolation_key(),
expected_response_time, std::move(data));
}
}
void FetchCachedCode(blink::mojom::CodeCacheType cache_type,
const GURL& url,
FetchCachedCodeCallback callback) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ASSIGN_OR_RETURN(
GURL secondary_key,
GetSecondaryKeyForCodeCache(url, render_process_id(), Operation::kRead),
[&callback] { std::move(callback).Run({}, {}); });
if (GeneratedCodeCache* code_cache = GetCodeCache(cache_type); code_cache) {
auto read_callback =
base::BindOnce(&LocalCodeCacheHost::OnReceiveCachedCode,
weak_ptr_factory_.GetWeakPtr(), cache_type,
base::TimeTicks::Now(), std::move(callback));
code_cache->FetchEntry(url, secondary_key, network_isolation_key(),
std::move(read_callback));
} else {
std::move(callback).Run(base::Time(), {});
}
}
void ClearCodeCacheEntry(blink::mojom::CodeCacheType cache_type,
const GURL& url) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ASSIGN_OR_RETURN(
GURL secondary_key,
GetSecondaryKeyForCodeCache(url, render_process_id(), Operation::kRead),
[] {});
if (GeneratedCodeCache* code_cache = GetCodeCache(cache_type); code_cache) {
code_cache->DeleteEntry(url, secondary_key, network_isolation_key());
}
}
private:
GeneratedCodeCache* GetCodeCache(blink::mojom::CodeCacheType cache_type)
VALID_CONTEXT_REQUIRED(sequence_checker_) {
ProcessLock process_lock =
ChildProcessSecurityPolicyImpl::GetInstance()->GetProcessLock(
render_process_id());
// To minimize the chance of any cache bug resulting in privilege escalation
// from an ordinary web page to trusted WebUI, we use a completely separate
// GeneratedCodeCache instance for WebUI pages.
if (process_lock.MatchesScheme(content::kChromeUIScheme) ||
process_lock.MatchesScheme(content::kChromeUIUntrustedScheme)) {
if (cache_type == blink::mojom::CodeCacheType::kJavascript) {
return generated_code_cache_context()->generated_webui_js_code_cache();
}
// WebAssembly in WebUI pages is not supported due to no current usage.
return nullptr;
}
if (cache_type == blink::mojom::CodeCacheType::kJavascript) {
return generated_code_cache_context()->generated_js_code_cache();
}
DCHECK_EQ(blink::mojom::CodeCacheType::kWebAssembly, cache_type);
return generated_code_cache_context()->generated_wasm_code_cache();
}
// Code caches use two keys: the URL of requested resource |resource_url|
// as the primary key and the origin lock of the renderer that requested this
// resource as secondary key. This function returns the origin lock of the
// renderer that will be used as the secondary key for the code cache.
// The secondary key is:
// Case 0. std::nullopt if the resource URL or origin lock have unsupported
// schemes, or if they represent potentially dangerous combinations such as
// WebUI code in an open-web page.
// Case 1. an empty GURL if the render process is not locked to an origin. In
// this case, code cache uses |resource_url| as the key.
// Case 2. a std::nullopt, if the origin lock is opaque (for ex: browser
// initiated navigation to a data: URL). In these cases, the code should not
// be cached since the serialized value of opaque origins should not be used
// as a key.
// Case 3: origin_lock if the scheme of origin_lock is
// Http/Https/chrome/chrome-untrusted.
// Case 4. std::nullopt otherwise.
static std::optional<GURL> GetSecondaryKeyForCodeCache(
const GURL& resource_url,
int render_process_id,
Operation operation) {
if (use_empty_secondary_key_for_testing_) {
return GURL();
}
// Case 0: check for invalid schemes.
if (!CheckSecurityForAccessingCodeCacheData(resource_url, render_process_id,
operation)) {
return std::nullopt;
}
return GetOriginLock(render_process_id);
}
void OnReceiveCachedCode(blink::mojom::CodeCacheType cache_type,
base::TimeTicks start_time,
FetchCachedCodeCallback callback,
const base::Time& response_time,
mojo_base::BigBuffer data) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (cache_type == blink::mojom::CodeCacheType::kJavascript &&
data.size() > 0) {
base::UmaHistogramTimes("SiteIsolatedCodeCache.JS.FetchCodeCache",
base::TimeTicks::Now() - start_time);
}
if (data.size() > 0) {
base::UmaHistogramCustomCounts("SiteIsolatedCodeCache.DataSize",
data.size(), 1, 10000000, 100);
}
std::move(callback).Run(response_time, std::move(data));
}
base::WeakPtrFactory<LocalCodeCacheHost> weak_ptr_factory_{this};
};
#if !BUILDFLAG(IS_FUCHSIA)
// CodeCacheWithPersistentCacheHost --------------------------------------------
// An implementation of CodeCacheHostImpl that uses PersistentCache. Inserts
// take place here in the browser process, while lookups take place in the
// renderer by way of read-only view of the cache.
class CodeCacheWithPersistentCacheHost : public CodeCacheHostImpl {
public:
CodeCacheWithPersistentCacheHost(
int render_process_id,
scoped_refptr<GeneratedCodeCacheContext> generated_code_cache_context,
const net::NetworkIsolationKey& nik,
const blink::StorageKey& storage_key)
: CodeCacheHostImpl(render_process_id,
std::move(generated_code_cache_context),
nik,
storage_key) {
CHECK(this->generated_code_cache_context());
}
// CodeCacheHostImpl:
void GetPendingBackend(blink::mojom::CodeCacheType cache_type,
GetPendingBackendCallback callback) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ASSIGN_OR_RETURN(std::string cache_id, GetCacheId(cache_type),
[&callback] { std::move(callback).Run(std::nullopt); });
std::move(callback).Run(
generated_code_cache_context()->ShareReadOnlyConnection(cache_id));
}
void DidGenerateCacheableMetadata(blink::mojom::CodeCacheType cache_type,
const GURL& url,
base::Time expected_response_time,
mojo_base::BigBuffer data) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Ignore insert attempts for invalid URLs.
if (!CheckSecurityForAccessingCodeCacheData(url, render_process_id(),
Operation::kWrite)) {
return;
}
ASSIGN_OR_RETURN(std::string cache_id, GetCacheId(cache_type), [] {});
std::string resource_key = GeneratedCodeCache::GetResourceKey(
url, MojoCacheTypeToCodeCacheType(cache_type));
generated_code_cache_context()->InsertIntoPersistentCacheCollection(
cache_id, resource_key, std::move(data),
persistent_cache::EntryMetadata{
.input_signature = expected_response_time.ToDeltaSinceWindowsEpoch()
.InMicroseconds()});
}
// Note: In an operational browser, `FetchCachedCode` is implemented in
// renderers in `CodeCacheWithPersistentCacheHostImpl`. Ideally, this
// implementation would be nothing more than `NOTREACHED()`. In light of the
// fact that many tests expect to be able to use this to validate inserts into
// the cache, it is implemented here. `CHECK_IS_TEST()` is used to prevent
// accidental use in the product.
void FetchCachedCode(blink::mojom::CodeCacheType cache_type,
const GURL& url,
FetchCachedCodeCallback callback) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK_IS_TEST(); // Fetch is handled directly in the client in blink.
// For simplicity's sake, the implementation here is used for tests; see
// comment above.
ASSIGN_OR_RETURN(std::string cache_id, GetCacheId(cache_type),
[&callback] { std::move(callback).Run({}, {}); });
std::string resource_key = GeneratedCodeCache::GetResourceKey(
url, MojoCacheTypeToCodeCacheType(cache_type));
if (auto metadata_and_content =
generated_code_cache_context()->FindInPersistentCacheCollection(
cache_id, resource_key);
metadata_and_content.has_value() &&
metadata_and_content->content.size() > 0) {
// Cache hit with content.
std::move(callback).Run(
base::Time::FromDeltaSinceWindowsEpoch(base::Microseconds(
metadata_and_content->metadata.input_signature)),
std::move(metadata_and_content->content));
} else {
// Cache miss or error.
std::move(callback).Run(base::Time(), mojo_base::BigBuffer());
}
}
void ClearCodeCacheEntry(blink::mojom::CodeCacheType cache_type,
const GURL& url) override {
// `PersistentCache` does not expose the ability to delete specific entries.
// This will lead to entries that are known to be unusable by renderers
// remaining in the cache. This does not lead to keys being unusable forever
// since the entries can get overwritten by valid entries. Additionally this
// does not lead to invalid values being used by renderers since the fact
// that they are unusable was detected by the clients themselves.
// User-driven requests to clear browsing data will clear caches wholesale
// rather than delete individual entries.
}
private:
// Returns the identifier by which this host's storage for data of type
// `cache_type` is known by the PersistentCacheCollection; or no value in case
// no data should be cached.
std::optional<std::string> GetCacheId(blink::mojom::CodeCacheType cache_type)
VALID_CONTEXT_REQUIRED(sequence_checker_) {
ASSIGN_OR_RETURN(
GURL origin_lock, GetOriginLock(render_process_id()),
[]() -> std::optional<std::string> { return std::nullopt; });
if (!origin_lock.is_empty()) {
return GeneratedCodeCache::GetContextKey(
origin_lock, network_isolation_key(),
MojoCacheTypeToCodeCacheType(cache_type));
}
// `origin_lock` will be empty if the renderer is not locked to an origin.
// Do not cache if site isolation is enabled and is at least as strict as
// site-per-process.
if (content::SiteIsolationPolicy::IsSitePerProcessOrStricter()) {
return std::nullopt;
}
// Alternatively, Android uses partial Site Isolation (i.e., some sites
// require dedicated processes and others do not).
//
// An empty string is not a valid context key for PersistentCacheCollection
// so a shared context key is used instead. This lets all unlocked processes
// share a context (and thus a cache) like is achieved when using
// GeneratedCodeCache through the implementation of `GetCacheKey()` which
// will construct the full cache key using only the resource URL for
// requests from unlocked processes.
//
// The context key returned by this function needs to enfcorce the "jail"
// and "citadel" concepts (see:
// https://chromium.googlesource.com/chromium/src/+/main/docs/process_model_and_site_isolation.md)
//
// 1) Locked processes are "jailed" since they cannot access shared context
// with their non-empty context key which will never equal
// `kSharedContextKeyForRelaxedIsolation'.
// 2) The "citadel" concept is upheld because unlocked processes do not have
// access to data from locked processes because locked processed store their
// data using their specific keys and not the shared context key.
//
// Use distinct cache IDs for WebUI vs. the open web to minimize the chance
// of any cache bug resulting in privilege escalation from an ordinary web
// page to trusted WebUI.
ProcessLock process_lock =
ChildProcessSecurityPolicyImpl::GetInstance()->GetProcessLock(
render_process_id());
static constexpr char kSharedContextKeyForRelaxedIsolation[] =
"_shared_context_for_relaxed_isolation";
static constexpr char kSharedContextKeyForRelaxedIsolationWebUi[] =
"_shared_context_for_relaxed_isolation_webui";
return process_lock.MatchesScheme(content::kChromeUIScheme) ||
process_lock.MatchesScheme(content::kChromeUIUntrustedScheme)
? kSharedContextKeyForRelaxedIsolationWebUi
: kSharedContextKeyForRelaxedIsolation;
}
static GeneratedCodeCache::CodeCacheType MojoCacheTypeToCodeCacheType(
blink::mojom::CodeCacheType type) {
switch (type) {
case blink::mojom::CodeCacheType::kJavascript:
return GeneratedCodeCache::CodeCacheType::kJavaScript;
case blink::mojom::CodeCacheType::kWebAssembly:
return GeneratedCodeCache::CodeCacheType::kWebAssembly;
}
}
};
#endif // !BUILDFLAG(IS_FUCHSIA)
} // namespace
CodeCacheHostImpl::ReceiverSet::ReceiverSet(
scoped_refptr<GeneratedCodeCacheContext> generated_code_cache_context)
: generated_code_cache_context_(generated_code_cache_context),
receiver_set_(
new mojo::UniqueReceiverSet<blink::mojom::CodeCacheHost>(),
base::OnTaskRunnerDeleter(GeneratedCodeCacheContext::GetTaskRunner(
generated_code_cache_context))) {}
CodeCacheHostImpl::ReceiverSet::~ReceiverSet() = default;
void CodeCacheHostImpl::ReceiverSet::Add(
int render_process_id,
const net::NetworkIsolationKey& nik,
const blink::StorageKey& storage_key,
mojo::PendingReceiver<blink::mojom::CodeCacheHost> receiver,
CodeCacheHostReceiverHandler handler) {
if (!receiver_set_) {
receiver_set_ = {
new mojo::UniqueReceiverSet<blink::mojom::CodeCacheHost>(),
base::OnTaskRunnerDeleter(GeneratedCodeCacheContext::GetTaskRunner(
generated_code_cache_context_))};
}
// |receiver_set_| will be deleted on the code cache thread, so it is safe to
// post a task to the code cache thread with the raw pointer.
GeneratedCodeCacheContext::RunOrPostTask(
generated_code_cache_context_, FROM_HERE,
base::BindOnce(&AddCodeCacheReceiver, receiver_set_.get(),
generated_code_cache_context_, render_process_id, nik,
storage_key, std::move(receiver), std::move(handler)));
}
void CodeCacheHostImpl::ReceiverSet::Add(
int render_process_id,
const net::NetworkIsolationKey& nik,
const blink::StorageKey& storage_key,
mojo::PendingReceiver<blink::mojom::CodeCacheHost> receiver) {
Add(render_process_id, nik, storage_key, std::move(receiver),
CodeCacheHostReceiverHandler());
}
void CodeCacheHostImpl::ReceiverSet::Clear() {
receiver_set_.reset();
}
// CodeCacheHostImpl -----------------------------------------------------------
std::unique_ptr<CodeCacheHostImpl> CodeCacheHostImpl::Create(
int render_process_id,
scoped_refptr<GeneratedCodeCacheContext> generated_code_cache_context,
const net::NetworkIsolationKey& nik,
const blink::StorageKey& storage_key) {
if (!generated_code_cache_context) {
// Without context, there's nothing to be done.
return std::make_unique<NoopCodeCacheHost>(
render_process_id, std::move(generated_code_cache_context), nik,
storage_key);
}
if (blink::features::IsPersistentCacheForCodeCacheEnabled()) {
#if !BUILDFLAG(IS_FUCHSIA)
return std::make_unique<CodeCacheWithPersistentCacheHost>(
render_process_id, std::move(generated_code_cache_context), nik,
storage_key);
#else
NOTREACHED();
#endif // !BUILDFLAG(IS_FUCHSIA)
}
return std::make_unique<LocalCodeCacheHost>(
render_process_id, std::move(generated_code_cache_context), nik,
storage_key);
}
CodeCacheHostImpl::~CodeCacheHostImpl() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void CodeCacheHostImpl::SetCacheStorageControlForTesting(
storage::mojom::CacheStorageControl* cache_storage_control) {
cache_storage_control_for_testing_ = cache_storage_control;
}
// static
void CodeCacheHostImpl::SetUseEmptySecondaryKeyForTesting() {
use_empty_secondary_key_for_testing_ = true;
}
CodeCacheHostImpl::CodeCacheHostImpl(
int render_process_id,
scoped_refptr<GeneratedCodeCacheContext> generated_code_cache_context,
const net::NetworkIsolationKey& nik,
const blink::StorageKey& storage_key)
: render_process_id_(render_process_id),
generated_code_cache_context_(std::move(generated_code_cache_context)),
network_isolation_key_(nik),
storage_key_(storage_key) {}
void CodeCacheHostImpl::DidGenerateCacheableMetadataInCacheStorage(
const GURL& url,
base::Time expected_response_time,
mojo_base::BigBuffer data,
const std::string& cache_storage_cache_name) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&DidGenerateCacheableMetadataInCacheStorageOnUI, url,
expected_response_time, std::move(data),
cache_storage_cache_name, render_process_id_, storage_key_,
cache_storage_control_for_testing_,
mojo::GetBadMessageCallback()));
}
} // namespace content