feat: enable code cache for custom protocols (#40544)
This commit is contained in:
parent
85bc005cd6
commit
9aa73abe78
15 changed files with 526 additions and 6 deletions
|
@ -61,8 +61,9 @@ The `protocol` module has the following methods:
|
|||
module gets emitted and can be called only once.
|
||||
|
||||
Registers the `scheme` as standard, secure, bypasses content security policy for
|
||||
resources, allows registering ServiceWorker, supports fetch API, and streaming
|
||||
video/audio. Specify a privilege with the value of `true` to enable the capability.
|
||||
resources, allows registering ServiceWorker, supports fetch API, streaming
|
||||
video/audio, and V8 code cache. Specify a privilege with the value of `true` to
|
||||
enable the capability.
|
||||
|
||||
An example of registering a privileged scheme, that bypasses Content Security
|
||||
Policy:
|
||||
|
|
|
@ -1356,6 +1356,10 @@ registered.
|
|||
Sets the directory to store the generated JS [code cache](https://v8.dev/blog/code-caching-for-devs) for this session. The directory is not required to be created by the user before this call, the runtime will create if it does not exist otherwise will use the existing directory. If directory cannot be created, then code cache will not be used and all operations related to code cache will fail silently inside the runtime. By default, the directory will be `Code Cache` under the
|
||||
respective user data folder.
|
||||
|
||||
Note that by default code cache is only enabled for http(s) URLs, to enable code
|
||||
cache for custom protocols, `codeCache: true` and `standard: true` must be
|
||||
specified when registering the protocol.
|
||||
|
||||
#### `ses.clearCodeCaches(options)`
|
||||
|
||||
* `options` Object
|
||||
|
|
|
@ -9,3 +9,5 @@
|
|||
* `supportFetchAPI` boolean (optional) - Default false.
|
||||
* `corsEnabled` boolean (optional) - Default false.
|
||||
* `stream` boolean (optional) - Default false.
|
||||
* `codeCache` boolean (optional) - Enable V8 code cache for the scheme, only
|
||||
works when `standard` is also set to true. Default false.
|
||||
|
|
|
@ -128,3 +128,4 @@ feat_allow_passing_of_objecttemplate_to_objecttemplatebuilder.patch
|
|||
chore_remove_check_is_test_on_script_injection_tracker.patch
|
||||
fix_restore_original_resize_performance_on_macos.patch
|
||||
fix_font_flooding_in_dev_tools.patch
|
||||
feat_allow_code_cache_in_custom_schemes.patch
|
||||
|
|
395
patches/chromium/feat_allow_code_cache_in_custom_schemes.patch
Normal file
395
patches/chromium/feat_allow_code_cache_in_custom_schemes.patch
Normal file
|
@ -0,0 +1,395 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Cheng Zhao <zcbenz@gmail.com>
|
||||
Date: Thu, 26 Oct 2023 11:07:47 +0900
|
||||
Subject: Enable V8 code cache for custom schemes
|
||||
|
||||
Add a new category in ContentClient::AddAdditionalSchemes which allows
|
||||
embedders to make custom schemes allow V8 code cache.
|
||||
|
||||
Chromium CL: https://chromium-review.googlesource.com/c/chromium/src/+/5019665
|
||||
|
||||
diff --git a/content/browser/code_cache/generated_code_cache.cc b/content/browser/code_cache/generated_code_cache.cc
|
||||
index 65ac47b199b3f6d37fe78495eeb3d3598c4add8d..5230bf2cf1387ca73b34e0be2e0cffecd46a3653 100644
|
||||
--- a/content/browser/code_cache/generated_code_cache.cc
|
||||
+++ b/content/browser/code_cache/generated_code_cache.cc
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
+#include "base/containers/contains.h"
|
||||
#include "base/feature_list.h"
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/functional/callback_helpers.h"
|
||||
@@ -24,6 +25,7 @@
|
||||
#include "net/base/url_util.h"
|
||||
#include "net/http/http_cache.h"
|
||||
#include "url/gurl.h"
|
||||
+#include "url/url_util.h"
|
||||
|
||||
using storage::BigIOBuffer;
|
||||
|
||||
@@ -46,24 +48,37 @@ void CheckValidKeys(const GURL& resource_url,
|
||||
GeneratedCodeCache::CodeCacheType cache_type) {
|
||||
// If the resource url is invalid don't cache the code.
|
||||
DCHECK(resource_url.is_valid());
|
||||
+ bool resource_url_allows_code_cache =
|
||||
+ base::Contains(url::GetCodeCacheSchemes(), resource_url.scheme());
|
||||
bool resource_url_is_chrome_or_chrome_untrusted =
|
||||
resource_url.SchemeIs(content::kChromeUIScheme) ||
|
||||
resource_url.SchemeIs(content::kChromeUIUntrustedScheme);
|
||||
DCHECK(resource_url.SchemeIsHTTPOrHTTPS() ||
|
||||
+ resource_url_allows_code_cache ||
|
||||
resource_url_is_chrome_or_chrome_untrusted);
|
||||
|
||||
- // |origin_lock| should be either empty or should have
|
||||
- // Http/Https/chrome/chrome-untrusted schemes and it should not be a URL with
|
||||
- // opaque origin. Empty origin_locks are allowed when the renderer is not
|
||||
- // locked to an origin.
|
||||
+ // |origin_lock| should be either empty or should have code cache allowed
|
||||
+ // schemes (http/https/chrome/chrome-untrusted or other custom schemes added
|
||||
+ // by url::AddCodeCacheScheme), and it should not be a URL with opaque
|
||||
+ // origin. Empty origin_locks are allowed when the renderer is not locked to
|
||||
+ // an origin.
|
||||
+ bool origin_lock_allows_code_cache =
|
||||
+ base::Contains(url::GetCodeCacheSchemes(), origin_lock.scheme());
|
||||
bool origin_lock_is_chrome_or_chrome_untrusted =
|
||||
origin_lock.SchemeIs(content::kChromeUIScheme) ||
|
||||
origin_lock.SchemeIs(content::kChromeUIUntrustedScheme);
|
||||
DCHECK(origin_lock.is_empty() ||
|
||||
((origin_lock.SchemeIsHTTPOrHTTPS() ||
|
||||
+ origin_lock_allows_code_cache ||
|
||||
origin_lock_is_chrome_or_chrome_untrusted) &&
|
||||
!url::Origin::Create(origin_lock).opaque()));
|
||||
|
||||
+ // The custom schemes share the cache type with http(s).
|
||||
+ if (origin_lock_allows_code_cache || resource_url_allows_code_cache) {
|
||||
+ DCHECK(cache_type == GeneratedCodeCache::kJavaScript ||
|
||||
+ cache_type == GeneratedCodeCache::kWebAssembly);
|
||||
+ }
|
||||
+
|
||||
// The chrome and chrome-untrusted schemes are only used with the WebUI
|
||||
// code cache type.
|
||||
DCHECK_EQ(origin_lock_is_chrome_or_chrome_untrusted,
|
||||
diff --git a/content/browser/code_cache/generated_code_cache.h b/content/browser/code_cache/generated_code_cache.h
|
||||
index f5c5ff2c89489257003dfe3284ee9de9f517c99b..fdd2e2483171c4d43963590200817dac27d22cf9 100644
|
||||
--- a/content/browser/code_cache/generated_code_cache.h
|
||||
+++ b/content/browser/code_cache/generated_code_cache.h
|
||||
@@ -52,12 +52,14 @@ class CONTENT_EXPORT GeneratedCodeCache {
|
||||
// Cache type. Used for collecting statistics for JS and Wasm in separate
|
||||
// buckets.
|
||||
enum CodeCacheType {
|
||||
- // JavaScript from http(s) pages.
|
||||
+ // JavaScript from pages of http(s) schemes or custom schemes registered by
|
||||
+ // url::AddCodeCacheScheme.
|
||||
kJavaScript,
|
||||
|
||||
- // WebAssembly from http(s) pages. This cache allows more total size and
|
||||
- // more size per item than the JavaScript cache, since some
|
||||
- // WebAssembly programs are very large.
|
||||
+ // WebAssembly from pages of http(s) schemes or custom schemes registered by
|
||||
+ // url::AddCodeCacheScheme. This cache allows more total size and more size
|
||||
+ // per item than the JavaScript cache, since some WebAssembly programs are
|
||||
+ // very large.
|
||||
kWebAssembly,
|
||||
|
||||
// JavaScript from chrome and chrome-untrusted pages. The resource URLs are
|
||||
diff --git a/content/browser/code_cache/generated_code_cache_browsertest.cc b/content/browser/code_cache/generated_code_cache_browsertest.cc
|
||||
index 672b9bb14cd493b05d1e27019cda30c5269bf46f..f4093315dea8feb4184adbfd4c398768a6fb197d 100644
|
||||
--- a/content/browser/code_cache/generated_code_cache_browsertest.cc
|
||||
+++ b/content/browser/code_cache/generated_code_cache_browsertest.cc
|
||||
@@ -2,21 +2,32 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
+#include "base/test/test_future.h"
|
||||
#include "base/test/metrics/histogram_tester.h"
|
||||
#include "base/test/scoped_feature_list.h"
|
||||
#include "base/test/test_future.h"
|
||||
+#include "components/services/storage/storage_service_impl.h"
|
||||
#include "content/browser/code_cache/generated_code_cache.h"
|
||||
+#include "content/browser/code_cache/generated_code_cache_context.h"
|
||||
+#include "content/browser/storage_partition_impl.h"
|
||||
+#include "content/common/url_schemes.h"
|
||||
+#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/test/browser_test.h"
|
||||
#include "content/public/test/content_browser_test.h"
|
||||
#include "content/public/test/content_browser_test_utils.h"
|
||||
+#include "content/public/test/test_browser_context.h"
|
||||
#include "content/shell/browser/shell.h"
|
||||
+#include "content/test/test_content_client.h"
|
||||
#include "net/dns/mock_host_resolver.h"
|
||||
#include "third_party/blink/public/common/features.h"
|
||||
+#include "url/url_util.h"
|
||||
|
||||
namespace content {
|
||||
|
||||
namespace {
|
||||
|
||||
+const std::string kCodeCacheScheme = "test-code-cache";
|
||||
+
|
||||
bool SupportsSharedWorker() {
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
// SharedWorkers are not enabled on Android. https://crbug.com/154571
|
||||
@@ -427,4 +438,80 @@ IN_PROC_BROWSER_TEST_P(CodeCacheBrowserTest,
|
||||
}
|
||||
}
|
||||
|
||||
+class CodeCacheInCustomSchemeBrowserTest : public ContentBrowserTest {
|
||||
+ public:
|
||||
+ CodeCacheInCustomSchemeBrowserTest() {
|
||||
+ SetContentClient(&test_content_client_);
|
||||
+ ReRegisterContentSchemesForTests();
|
||||
+ }
|
||||
+
|
||||
+ private:
|
||||
+ class CustomSchemeContentClient : public TestContentClient {
|
||||
+ public:
|
||||
+ void AddAdditionalSchemes(Schemes* schemes) override {
|
||||
+ schemes->standard_schemes.push_back(kCodeCacheScheme);
|
||||
+ schemes->code_cache_schemes.push_back(kCodeCacheScheme);
|
||||
+ }
|
||||
+ };
|
||||
+
|
||||
+ CustomSchemeContentClient test_content_client_;
|
||||
+ url::ScopedSchemeRegistryForTests scheme_registry_;
|
||||
+};
|
||||
+
|
||||
+IN_PROC_BROWSER_TEST_F(CodeCacheInCustomSchemeBrowserTest,
|
||||
+ AllowedCustomSchemeCanGenerateCodeCache) {
|
||||
+ // Create browser context and get code cache context.
|
||||
+ base::ScopedAllowBlockingForTesting allow_blocking;
|
||||
+ TestBrowserContext browser_context;
|
||||
+ StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>(
|
||||
+ browser_context.GetDefaultStoragePartition());
|
||||
+ scoped_refptr<GeneratedCodeCacheContext> context =
|
||||
+ partition->GetGeneratedCodeCacheContext();
|
||||
+ EXPECT_NE(context, nullptr);
|
||||
+
|
||||
+ GURL url(kCodeCacheScheme + "://host4/script.js");
|
||||
+ GURL origin(kCodeCacheScheme + "://host1:1/");
|
||||
+ ASSERT_TRUE(url.is_valid());
|
||||
+ ASSERT_TRUE(origin.is_valid());
|
||||
+ std::string data("SomeData");
|
||||
+
|
||||
+ // Add a code cache entry for the custom scheme.
|
||||
+ base::test::TestFuture<void> add_entry_future;
|
||||
+ GeneratedCodeCacheContext::RunOrPostTask(
|
||||
+ context.get(), FROM_HERE,
|
||||
+ base::BindOnce([](scoped_refptr<GeneratedCodeCacheContext> context,
|
||||
+ const GURL& url,
|
||||
+ const GURL& origin,
|
||||
+ const std::string& data,
|
||||
+ base::OnceClosure callback) {
|
||||
+ context->generated_js_code_cache()->WriteEntry(
|
||||
+ url, origin, net::NetworkIsolationKey(),
|
||||
+ base::Time::Now(), std::vector<uint8_t>(data.begin(), data.end()));
|
||||
+ content::GetUIThreadTaskRunner({})->PostTask(
|
||||
+ FROM_HERE, std::move(callback));
|
||||
+ }, context, url, origin, data, add_entry_future.GetCallback()));
|
||||
+ ASSERT_TRUE(add_entry_future.Wait());
|
||||
+
|
||||
+ // Get the code cache entry.
|
||||
+ base::test::TestFuture<std::string> get_entry_future;
|
||||
+ GeneratedCodeCacheContext::RunOrPostTask(
|
||||
+ context.get(), FROM_HERE,
|
||||
+ base::BindOnce([](scoped_refptr<GeneratedCodeCacheContext> context,
|
||||
+ const GURL& url,
|
||||
+ const GURL& origin,
|
||||
+ base::OnceCallback<void(std::string)> callback) {
|
||||
+ context->generated_js_code_cache()->FetchEntry(
|
||||
+ url, origin, net::NetworkIsolationKey(),
|
||||
+ base::BindOnce([](base::OnceCallback<void(std::string)> callback,
|
||||
+ const base::Time& response_time,
|
||||
+ mojo_base::BigBuffer buffer) {
|
||||
+ std::string data(buffer.data(), buffer.data() + buffer.size());
|
||||
+ content::GetUIThreadTaskRunner({})->PostTask(
|
||||
+ FROM_HERE, base::BindOnce(std::move(callback), data));
|
||||
+ }, std::move(callback)));
|
||||
+ }, context, url, origin, get_entry_future.GetCallback()));
|
||||
+ ASSERT_TRUE(get_entry_future.Wait());
|
||||
+ ASSERT_EQ(data, get_entry_future.Get<0>());
|
||||
+}
|
||||
+
|
||||
} // namespace content
|
||||
diff --git a/content/browser/renderer_host/code_cache_host_impl.cc b/content/browser/renderer_host/code_cache_host_impl.cc
|
||||
index 6b9e5065dc570b506c4c2606d536319d98684e12..9d1f337b9c9890b6b7afda40bf2f829ff2a25bfd 100644
|
||||
--- a/content/browser/renderer_host/code_cache_host_impl.cc
|
||||
+++ b/content/browser/renderer_host/code_cache_host_impl.cc
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <utility>
|
||||
|
||||
+#include "base/containers/contains.h"
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/functional/callback_helpers.h"
|
||||
#include "base/metrics/histogram_functions.h"
|
||||
@@ -28,6 +29,7 @@
|
||||
#include "third_party/blink/public/common/cache_storage/cache_storage_utils.h"
|
||||
#include "url/gurl.h"
|
||||
#include "url/origin.h"
|
||||
+#include "url/url_util.h"
|
||||
|
||||
using blink::mojom::CacheStorageError;
|
||||
|
||||
@@ -40,6 +42,11 @@ enum class Operation {
|
||||
kWrite,
|
||||
};
|
||||
|
||||
+bool ProcessLockURLIsCodeCacheScheme(const ProcessLock& process_lock) {
|
||||
+ return base::Contains(url::GetCodeCacheSchemes(),
|
||||
+ process_lock.lock_url().scheme());
|
||||
+}
|
||||
+
|
||||
bool CheckSecurityForAccessingCodeCacheData(const GURL& resource_url,
|
||||
int render_process_id,
|
||||
Operation operation) {
|
||||
@@ -47,11 +54,12 @@ bool CheckSecurityForAccessingCodeCacheData(const GURL& resource_url,
|
||||
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.
|
||||
+ // Code caching is only allowed for scripts from open-web (http/https and
|
||||
+ // custom schemes registered with url::AddCodeCacheScheme) and
|
||||
+ // chrome/chrome-untrusted schemes. Furthermore, there is no way for
|
||||
+ // open-web pages to load chrome or chrome-untrusted scripts, so any
|
||||
+ // open-web 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.is_locked_to_site()) {
|
||||
@@ -60,9 +68,10 @@ bool CheckSecurityForAccessingCodeCacheData(const GURL& resource_url,
|
||||
return false;
|
||||
}
|
||||
if (process_lock.matches_scheme(url::kHttpScheme) ||
|
||||
- process_lock.matches_scheme(url::kHttpsScheme)) {
|
||||
+ process_lock.matches_scheme(url::kHttpsScheme) ||
|
||||
+ ProcessLockURLIsCodeCacheScheme(process_lock)) {
|
||||
if (operation == Operation::kWrite) {
|
||||
- mojo::ReportBadMessage("HTTP(S) pages cannot cache WebUI code");
|
||||
+ mojo::ReportBadMessage("Open-web pages cannot cache WebUI code");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -72,7 +81,16 @@ bool CheckSecurityForAccessingCodeCacheData(const GURL& resource_url,
|
||||
return process_lock.matches_scheme(content::kChromeUIScheme) ||
|
||||
process_lock.matches_scheme(content::kChromeUIUntrustedScheme);
|
||||
}
|
||||
- if (resource_url.SchemeIsHTTPOrHTTPS()) {
|
||||
+ if (base::Contains(url::GetCodeCacheSchemes(), resource_url.scheme()) &&
|
||||
+ (process_lock.matches_scheme(url::kHttpScheme) ||
|
||||
+ process_lock.matches_scheme(url::kHttpsScheme))) {
|
||||
+ // While custom schemes registered with url::AddCodeCacheScheme are
|
||||
+ // considered as open-web pages, we still do not trust http(s) pages
|
||||
+ // loading resources from custom schemes.
|
||||
+ return false;
|
||||
+ }
|
||||
+ if (resource_url.SchemeIsHTTPOrHTTPS() ||
|
||||
+ base::Contains(url::GetCodeCacheSchemes(), resource_url.scheme())) {
|
||||
if (process_lock.matches_scheme(content::kChromeUIScheme) ||
|
||||
process_lock.matches_scheme(content::kChromeUIUntrustedScheme)) {
|
||||
// It is possible for WebUI pages to include open-web content, but such
|
||||
@@ -136,15 +154,17 @@ absl::optional<GURL> GetSecondaryKeyForCodeCache(const GURL& resource_url,
|
||||
return absl::nullopt;
|
||||
|
||||
// Case 3: process_lock_url is used to enfore 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.
|
||||
+ // Code cache enabled schemes (http/https/chrome/chrome-untrusted and custom
|
||||
+ // schemes registered with url::AddCodeCacheScheme) 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.matches_scheme(url::kHttpScheme) ||
|
||||
+ // limit the cache to processes of code cache enabled schemes.
|
||||
+ if (ProcessLockURLIsCodeCacheScheme(process_lock) ||
|
||||
+ process_lock.matches_scheme(url::kHttpScheme) ||
|
||||
process_lock.matches_scheme(url::kHttpsScheme) ||
|
||||
process_lock.matches_scheme(content::kChromeUIScheme) ||
|
||||
process_lock.matches_scheme(content::kChromeUIUntrustedScheme)) {
|
||||
diff --git a/content/common/url_schemes.cc b/content/common/url_schemes.cc
|
||||
index ce9644d33fe83379127b01bf9a2b1c4badc3bc7c..532b14e013b9b65ba390f09e8bb8934bb278a0d8 100644
|
||||
--- a/content/common/url_schemes.cc
|
||||
+++ b/content/common/url_schemes.cc
|
||||
@@ -98,6 +98,9 @@ void RegisterContentSchemes(bool should_lock_registry) {
|
||||
for (auto& scheme : schemes.empty_document_schemes)
|
||||
url::AddEmptyDocumentScheme(scheme.c_str());
|
||||
|
||||
+ for (auto& scheme : schemes.code_cache_schemes)
|
||||
+ url::AddCodeCacheScheme(scheme.c_str());
|
||||
+
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
if (schemes.allow_non_standard_schemes_in_origins)
|
||||
url::EnableNonStandardSchemesForAndroidWebView();
|
||||
diff --git a/content/public/common/content_client.h b/content/public/common/content_client.h
|
||||
index 5d1484651fb8c3e03337665d3d5342ba51df3154..d4432a660d6c5a5e937dedabb7e4b71b87c9504b 100644
|
||||
--- a/content/public/common/content_client.h
|
||||
+++ b/content/public/common/content_client.h
|
||||
@@ -139,6 +139,9 @@ class CONTENT_EXPORT ContentClient {
|
||||
// Registers a URL scheme as strictly empty documents, allowing them to
|
||||
// commit synchronously.
|
||||
std::vector<std::string> empty_document_schemes;
|
||||
+ // Registers a URL scheme whose js and wasm scripts have V8 code cache
|
||||
+ // enabled.
|
||||
+ std::vector<std::string> code_cache_schemes;
|
||||
// Registers a URL scheme as extension scheme.
|
||||
std::vector<std::string> extension_schemes;
|
||||
// Registers a URL scheme with a predefined default custom handler.
|
||||
diff --git a/url/url_util.cc b/url/url_util.cc
|
||||
index 9258cfcfada47aafe6ba20c648187947fec72372..a1834e543d27d46265af0c2133acac79b6c840e2 100644
|
||||
--- a/url/url_util.cc
|
||||
+++ b/url/url_util.cc
|
||||
@@ -114,6 +114,9 @@ struct SchemeRegistry {
|
||||
kAboutScheme,
|
||||
};
|
||||
|
||||
+ // Embedder schemes that have V8 code cache enabled in js and wasm scripts.
|
||||
+ std::vector<std::string> code_cache_schemes = {};
|
||||
+
|
||||
// Schemes with a predefined default custom handler.
|
||||
std::vector<SchemeWithHandler> predefined_handler_schemes;
|
||||
|
||||
@@ -659,6 +662,15 @@ const std::vector<std::string>& GetEmptyDocumentSchemes() {
|
||||
return GetSchemeRegistry().empty_document_schemes;
|
||||
}
|
||||
|
||||
+void AddCodeCacheScheme(const char* new_scheme) {
|
||||
+ DoAddScheme(new_scheme,
|
||||
+ &GetSchemeRegistryWithoutLocking()->code_cache_schemes);
|
||||
+}
|
||||
+
|
||||
+const std::vector<std::string>& GetCodeCacheSchemes() {
|
||||
+ return GetSchemeRegistry().code_cache_schemes;
|
||||
+}
|
||||
+
|
||||
void AddPredefinedHandlerScheme(const char* new_scheme, const char* handler) {
|
||||
DoAddSchemeWithHandler(
|
||||
new_scheme, handler,
|
||||
diff --git a/url/url_util.h b/url/url_util.h
|
||||
index 8c94c7a4f6d5f653d326199e5b43452f969911d4..40dcdf9d680f9d345c09426da48b37f288234244 100644
|
||||
--- a/url/url_util.h
|
||||
+++ b/url/url_util.h
|
||||
@@ -115,6 +115,15 @@ COMPONENT_EXPORT(URL) const std::vector<std::string>& GetCSPBypassingSchemes();
|
||||
COMPONENT_EXPORT(URL) void AddEmptyDocumentScheme(const char* new_scheme);
|
||||
COMPONENT_EXPORT(URL) const std::vector<std::string>& GetEmptyDocumentSchemes();
|
||||
|
||||
+// Adds an application-defined scheme to the list of schemes that have V8 code
|
||||
+// cache enabled for the js and wasm scripts.
|
||||
+// The WebUI schemes (chrome/chrome-untrusted) do not belong to this list, as
|
||||
+// they are treated as a separate cache type for security purpose.
|
||||
+// The http(s) schemes do not belong to this list neither, they always have V8
|
||||
+// code cache enabled and can not load scripts from schemes in this list.
|
||||
+COMPONENT_EXPORT(URL) void AddCodeCacheScheme(const char* new_scheme);
|
||||
+COMPONENT_EXPORT(URL) const std::vector<std::string>& GetCodeCacheSchemes();
|
||||
+
|
||||
// Adds a scheme with a predefined default handler.
|
||||
//
|
||||
// This pair of strings must be normalized protocol handler parameters as
|
|
@ -32,6 +32,9 @@ std::vector<std::string> g_standard_schemes;
|
|||
// List of registered custom streaming schemes.
|
||||
std::vector<std::string> g_streaming_schemes;
|
||||
|
||||
// Schemes that support V8 code cache.
|
||||
std::vector<std::string> g_code_cache_schemes;
|
||||
|
||||
struct SchemeOptions {
|
||||
bool standard = false;
|
||||
bool secure = false;
|
||||
|
@ -40,6 +43,7 @@ struct SchemeOptions {
|
|||
bool supportFetchAPI = false;
|
||||
bool corsEnabled = false;
|
||||
bool stream = false;
|
||||
bool codeCache = false;
|
||||
};
|
||||
|
||||
struct CustomScheme {
|
||||
|
@ -71,6 +75,7 @@ struct Converter<CustomScheme> {
|
|||
opt.Get("supportFetchAPI", &(out->options.supportFetchAPI));
|
||||
opt.Get("corsEnabled", &(out->options.corsEnabled));
|
||||
opt.Get("stream", &(out->options.stream));
|
||||
opt.Get("codeCache", &(out->options.codeCache));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -82,10 +87,14 @@ namespace electron::api {
|
|||
|
||||
gin::WrapperInfo Protocol::kWrapperInfo = {gin::kEmbedderNativeGin};
|
||||
|
||||
std::vector<std::string> GetStandardSchemes() {
|
||||
const std::vector<std::string>& GetStandardSchemes() {
|
||||
return g_standard_schemes;
|
||||
}
|
||||
|
||||
const std::vector<std::string>& GetCodeCacheSchemes() {
|
||||
return g_code_cache_schemes;
|
||||
}
|
||||
|
||||
void AddServiceWorkerScheme(const std::string& scheme) {
|
||||
// There is no API to add service worker scheme, but there is an API to
|
||||
// return const reference to the schemes vector.
|
||||
|
@ -104,6 +113,15 @@ void RegisterSchemesAsPrivileged(gin_helper::ErrorThrower thrower,
|
|||
return;
|
||||
}
|
||||
|
||||
for (const auto& custom_scheme : custom_schemes) {
|
||||
if (custom_scheme.options.codeCache && !custom_scheme.options.standard) {
|
||||
thrower.ThrowError(
|
||||
"Code cache can only be enabled when the custom scheme is registered "
|
||||
"as standard scheme.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> secure_schemes, cspbypassing_schemes, fetch_schemes,
|
||||
service_worker_schemes, cors_schemes;
|
||||
for (const auto& custom_scheme : custom_schemes) {
|
||||
|
@ -137,10 +155,16 @@ void RegisterSchemesAsPrivileged(gin_helper::ErrorThrower thrower,
|
|||
if (custom_scheme.options.stream) {
|
||||
g_streaming_schemes.push_back(custom_scheme.scheme);
|
||||
}
|
||||
if (custom_scheme.options.codeCache) {
|
||||
g_code_cache_schemes.push_back(custom_scheme.scheme);
|
||||
url::AddCodeCacheScheme(custom_scheme.scheme.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
const auto AppendSchemesToCmdLine = [](const char* switch_name,
|
||||
std::vector<std::string> schemes) {
|
||||
if (schemes.empty())
|
||||
return;
|
||||
// Add the schemes to command line switches, so child processes can also
|
||||
// register them.
|
||||
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
|
||||
|
@ -158,6 +182,8 @@ void RegisterSchemesAsPrivileged(gin_helper::ErrorThrower thrower,
|
|||
g_standard_schemes);
|
||||
AppendSchemesToCmdLine(electron::switches::kStreamingSchemes,
|
||||
g_streaming_schemes);
|
||||
AppendSchemesToCmdLine(electron::switches::kCodeCacheSchemes,
|
||||
g_code_cache_schemes);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
|
|
@ -22,7 +22,8 @@ class ProtocolRegistry;
|
|||
|
||||
namespace api {
|
||||
|
||||
std::vector<std::string> GetStandardSchemes();
|
||||
const std::vector<std::string>& GetStandardSchemes();
|
||||
const std::vector<std::string>& GetCodeCacheSchemes();
|
||||
|
||||
void AddServiceWorkerScheme(const std::string& scheme);
|
||||
|
||||
|
|
|
@ -526,7 +526,8 @@ void ElectronBrowserClient::AppendExtraCommandLineSwitches(
|
|||
switches::kStandardSchemes, switches::kEnableSandbox,
|
||||
switches::kSecureSchemes, switches::kBypassCSPSchemes,
|
||||
switches::kCORSSchemes, switches::kFetchSchemes,
|
||||
switches::kServiceWorkerSchemes, switches::kStreamingSchemes};
|
||||
switches::kServiceWorkerSchemes, switches::kStreamingSchemes,
|
||||
switches::kCodeCacheSchemes};
|
||||
command_line->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(),
|
||||
kCommonSwitchNames);
|
||||
if (process_type == ::switches::kUtilityProcess ||
|
||||
|
@ -694,7 +695,7 @@ ElectronBrowserClient::CreateWindowForVideoPictureInPicture(
|
|||
|
||||
void ElectronBrowserClient::GetAdditionalAllowedSchemesForFileSystem(
|
||||
std::vector<std::string>* additional_schemes) {
|
||||
auto schemes_list = api::GetStandardSchemes();
|
||||
const auto& schemes_list = api::GetStandardSchemes();
|
||||
if (!schemes_list.empty())
|
||||
additional_schemes->insert(additional_schemes->end(), schemes_list.begin(),
|
||||
schemes_list.end());
|
||||
|
|
|
@ -227,6 +227,9 @@ const char kCORSSchemes[] = "cors-schemes";
|
|||
// Register schemes as streaming responses.
|
||||
const char kStreamingSchemes[] = "streaming-schemes";
|
||||
|
||||
// Register schemes as supporting V8 code cache.
|
||||
const char kCodeCacheSchemes[] = "code-cache-schemes";
|
||||
|
||||
// The browser process app model ID
|
||||
const char kAppUserModelId[] = "app-user-model-id";
|
||||
|
||||
|
|
|
@ -113,6 +113,7 @@ extern const char kBypassCSPSchemes[];
|
|||
extern const char kFetchSchemes[];
|
||||
extern const char kCORSSchemes[];
|
||||
extern const char kStreamingSchemes[];
|
||||
extern const char kCodeCacheSchemes[];
|
||||
extern const char kAppUserModelId[];
|
||||
extern const char kAppPath[];
|
||||
|
||||
|
|
|
@ -276,6 +276,13 @@ void RendererClientBase::RenderThreadStarted() {
|
|||
blink::SchemeRegistry::RegisterURLSchemeAsBypassingContentSecurityPolicy(
|
||||
WTF::String::FromUTF8(scheme.data(), scheme.length()));
|
||||
|
||||
std::vector<std::string> code_cache_schemes_list =
|
||||
ParseSchemesCLISwitch(command_line, switches::kCodeCacheSchemes);
|
||||
for (const auto& scheme : code_cache_schemes_list) {
|
||||
blink::WebSecurityPolicy::RegisterURLSchemeAsCodeCacheWithHashing(
|
||||
blink::WebString::FromASCII(scheme));
|
||||
}
|
||||
|
||||
// Allow file scheme to handle service worker by default.
|
||||
// FIXME(zcbenz): Can this be moved elsewhere?
|
||||
if (electron::fuses::IsGrantFileProtocolExtraPrivilegesEnabled()) {
|
||||
|
|
|
@ -1090,6 +1090,33 @@ describe('protocol module', () => {
|
|||
}
|
||||
});
|
||||
|
||||
describe('protocol.registerSchemesAsPrivileged codeCache', function () {
|
||||
const temp = require('temp').track();
|
||||
const appPath = path.join(fixturesPath, 'apps', 'refresh-page');
|
||||
|
||||
let w: BrowserWindow;
|
||||
let codeCachePath: string;
|
||||
beforeEach(async () => {
|
||||
w = new BrowserWindow({ show: false });
|
||||
codeCachePath = temp.path();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await closeWindow(w);
|
||||
w = null as unknown as BrowserWindow;
|
||||
});
|
||||
|
||||
it('code cache in custom protocol is disabled by default', async () => {
|
||||
ChildProcess.spawnSync(process.execPath, [appPath, 'false', codeCachePath]);
|
||||
expect(fs.readdirSync(path.join(codeCachePath, 'js')).length).to.equal(2);
|
||||
});
|
||||
|
||||
it('codeCache:true enables codeCache in custom protocol', async () => {
|
||||
ChildProcess.spawnSync(process.execPath, [appPath, 'true', codeCachePath]);
|
||||
expect(fs.readdirSync(path.join(codeCachePath, 'js')).length).to.above(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('handle', () => {
|
||||
afterEach(closeAllWindows);
|
||||
|
||||
|
|
9
spec/fixtures/apps/refresh-page/main.html
vendored
Normal file
9
spec/fixtures/apps/refresh-page/main.html
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
<html>
|
||||
<body>
|
||||
<!-- Use mocha which has a large enough js file -->
|
||||
<script src="mocha.js"></script>
|
||||
<script>
|
||||
mocha.setup('bdd');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
38
spec/fixtures/apps/refresh-page/main.js
vendored
Normal file
38
spec/fixtures/apps/refresh-page/main.js
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
const path = require('node:path');
|
||||
const { once } = require('node:events');
|
||||
const { pathToFileURL } = require('node:url');
|
||||
const { BrowserWindow, app, protocol, net, session } = require('electron');
|
||||
|
||||
if (process.argv.length < 4) {
|
||||
console.error('Must pass allow_code_cache code_cache_dir');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
protocol.registerSchemesAsPrivileged([
|
||||
{
|
||||
scheme: 'atom',
|
||||
privileges: {
|
||||
standard: true,
|
||||
codeCache: process.argv[2] === 'true'
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
app.once('ready', async () => {
|
||||
const codeCachePath = process.argv[3];
|
||||
session.defaultSession.setCodeCachePath(codeCachePath);
|
||||
|
||||
protocol.handle('atom', (request) => {
|
||||
let { pathname } = new URL(request.url);
|
||||
if (pathname === '/mocha.js') { pathname = path.resolve(__dirname, '../../../node_modules/mocha/mocha.js'); } else { pathname = path.join(__dirname, pathname); }
|
||||
return net.fetch(pathToFileURL(pathname).toString());
|
||||
});
|
||||
|
||||
const win = new BrowserWindow({ show: false });
|
||||
win.loadURL('atom://host/main.html');
|
||||
await once(win.webContents, 'did-finish-load');
|
||||
// Reload to generate code cache.
|
||||
win.reload();
|
||||
await once(win.webContents, 'did-finish-load');
|
||||
app.exit();
|
||||
});
|
4
spec/fixtures/apps/refresh-page/package.json
vendored
Normal file
4
spec/fixtures/apps/refresh-page/package.json
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"name": "electron-test-refresh-page",
|
||||
"main": "main.js"
|
||||
}
|
Loading…
Reference in a new issue