summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/platform/loader
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2018-05-15 10:20:33 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2018-05-15 10:28:57 +0000
commitd17ea114e5ef69ad5d5d7413280a13e6428098aa (patch)
tree2c01a75df69f30d27b1432467cfe7c1467a498da /chromium/third_party/blink/renderer/platform/loader
parent8c5c43c7b138c9b4b0bf56d946e61d3bbc111bec (diff)
downloadqtwebengine-chromium-d17ea114e5ef69ad5d5d7413280a13e6428098aa.tar.gz
BASELINE: Update Chromium to 67.0.3396.47
Change-Id: Idcb1341782e417561a2473eeecc82642dafda5b7 Reviewed-by: Michal Klocek <michal.klocek@qt.io>
Diffstat (limited to 'chromium/third_party/blink/renderer/platform/loader')
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/BUILD.gn164
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/DEPS13
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/OWNERS7
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/README.md22
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/cors/cors.cc215
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/cors/cors.h71
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/cors/cors_error_string.cc344
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/cors/cors_error_string.h112
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/cors/cors_status.h27
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/README.md6
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/access_control_status.h18
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/buffering_data_pipe_writer.cc109
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/buffering_data_pipe_writer.h46
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/buffering_data_pipe_writer_test.cc78
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata.cc54
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata.h95
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h70
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.cc150
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.h77
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences_test.cc158
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/fetch_context.cc126
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/fetch_context.h292
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/fetch_initiator_info.h82
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/fetch_initiator_type_names.json522
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.cc176
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h251
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/fetch_utils.cc97
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/fetch_utils.h38
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/fetch_utils_test.cc40
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/integrity_metadata.cc33
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h69
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache.cc476
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache.h220
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc564
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc419
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/preload_key.h75
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc470
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource.h247
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource_test.cc230
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource.cc1207
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource.h596
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_client.h113
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_client_walker.h69
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_error.cc190
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_error.h120
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc1700
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h345
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc852
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_finish_observer.h38
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_info.h48
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_priority.h37
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.cc722
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h318
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler_test.cc520
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.cc126
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h115
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc873
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader.h205
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h209
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_options_test.cc108
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc155
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_loading_log.h21
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_priority.h52
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_request.cc469
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_request.h498
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_request_test.cc161
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.cc625
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.h625
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_response_test.cc87
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_status.h20
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_test.cc424
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_timing_info.cc61
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h165
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.cc69
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.h82
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.cc218
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.h81
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler_test.cc459
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/substitute_data.h70
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.cc65
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.h99
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/unique_identifier.cc43
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/fetch/unique_identifier.h41
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/link_header.cc102
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/link_header.h87
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/link_header_test.cc287
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/subresource_integrity.cc503
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/subresource_integrity.h152
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/subresource_integrity_test.cc713
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/testing/crypto_testing_platform_support.h51
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.cc48
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.h39
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h166
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/testing/mock_resource.cc103
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/testing/mock_resource.h60
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/testing/mock_resource_client.h61
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/testing/web_url_loader_factory_with_mock.cc24
-rw-r--r--chromium/third_party/blink/renderer/platform/loader/testing/web_url_loader_factory_with_mock.h32
98 files changed, 21292 insertions, 0 deletions
diff --git a/chromium/third_party/blink/renderer/platform/loader/BUILD.gn b/chromium/third_party/blink/renderer/platform/loader/BUILD.gn
new file mode 100644
index 00000000000..0ce462242b6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/BUILD.gn
@@ -0,0 +1,164 @@
+# Copyright 2017 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.
+
+import("//build/config/jumbo.gni")
+import("//third_party/blink/renderer/build/scripts/scripts.gni")
+import("//third_party/blink/renderer/platform/platform.gni")
+import("//third_party/blink/renderer/platform/platform_generated.gni")
+
+make_names("make_platform_loader_generated_fetch_initiator_type_names") {
+ in_files = [ "fetch/fetch_initiator_type_names.json5" ]
+ output_dir = "$blink_platform_output_dir/loader/fetch"
+}
+
+blink_platform_sources("loader") {
+ sources = [
+ "cors/cors.cc",
+ "cors/cors.h",
+ "cors/cors_error_string.cc",
+ "cors/cors_error_string.h",
+ "fetch/access_control_status.h",
+ "fetch/buffering_data_pipe_writer.cc",
+ "fetch/buffering_data_pipe_writer.h",
+ "fetch/cached_metadata.cc",
+ "fetch/cached_metadata.h",
+ "fetch/cached_metadata_handler.h",
+ "fetch/client_hints_preferences.cc",
+ "fetch/client_hints_preferences.h",
+ "fetch/fetch_context.cc",
+ "fetch/fetch_context.h",
+ "fetch/fetch_initiator_info.h",
+ "fetch/fetch_parameters.cc",
+ "fetch/fetch_parameters.h",
+ "fetch/fetch_utils.cc",
+ "fetch/fetch_utils.h",
+ "fetch/integrity_metadata.cc",
+ "fetch/integrity_metadata.h",
+ "fetch/memory_cache.cc",
+ "fetch/memory_cache.h",
+ "fetch/preload_key.h",
+ "fetch/raw_resource.cc",
+ "fetch/raw_resource.h",
+ "fetch/resource.cc",
+ "fetch/resource.h",
+ "fetch/resource_client.h",
+ "fetch/resource_client_walker.h",
+ "fetch/resource_error.cc",
+ "fetch/resource_error.h",
+ "fetch/resource_fetcher.cc",
+ "fetch/resource_fetcher.h",
+ "fetch/resource_finish_observer.h",
+ "fetch/resource_load_info.h",
+ "fetch/resource_load_priority.h",
+ "fetch/resource_load_scheduler.cc",
+ "fetch/resource_load_scheduler.h",
+ "fetch/resource_load_timing.cc",
+ "fetch/resource_load_timing.h",
+ "fetch/resource_loader.cc",
+ "fetch/resource_loader.h",
+ "fetch/resource_loader_options.h",
+ "fetch/resource_loading_log.h",
+ "fetch/resource_priority.h",
+ "fetch/resource_request.cc",
+ "fetch/resource_request.h",
+ "fetch/resource_response.cc",
+ "fetch/resource_response.h",
+ "fetch/resource_status.h",
+ "fetch/resource_timing_info.cc",
+ "fetch/resource_timing_info.h",
+ "fetch/script_fetch_options.cc",
+ "fetch/script_fetch_options.h",
+ "fetch/source_keyed_cached_metadata_handler.cc",
+ "fetch/source_keyed_cached_metadata_handler.h",
+ "fetch/substitute_data.h",
+ "fetch/text_resource_decoder_options.cc",
+ "fetch/text_resource_decoder_options.h",
+ "fetch/unique_identifier.cc",
+ "fetch/unique_identifier.h",
+ "link_header.cc",
+ "link_header.h",
+ "subresource_integrity.cc",
+ "subresource_integrity.h",
+ ]
+
+ sources += get_target_outputs(
+ ":make_platform_loader_generated_fetch_initiator_type_names")
+
+ deps = [
+ ":make_platform_loader_generated_fetch_initiator_type_names",
+ "//components/link_header_util",
+ "//services/network/public/cpp",
+ "//services/network/public/mojom:mojom_blink",
+ ]
+}
+
+jumbo_source_set("unit_tests") {
+ # This target defines test files for blink_platform_unittests and only the
+ # blink_platform_unittests target should depend on it.
+ visibility = [ "//third_party/blink/renderer/platform:*" ]
+ testonly = true
+
+ # Source files for blink_platform_unittests.
+ sources = [
+ "fetch/buffering_data_pipe_writer_test.cc",
+ "fetch/client_hints_preferences_test.cc",
+ "fetch/fetch_utils_test.cc",
+ "fetch/memory_cache_correctness_test.cc",
+ "fetch/memory_cache_test.cc",
+ "fetch/raw_resource_test.cc",
+ "fetch/resource_fetcher_test.cc",
+ "fetch/resource_load_scheduler_test.cc",
+ "fetch/resource_loader_options_test.cc",
+ "fetch/resource_loader_test.cc",
+ "fetch/resource_request_test.cc",
+ "fetch/resource_response_test.cc",
+ "fetch/resource_test.cc",
+ "fetch/source_keyed_cached_metadata_handler_test.cc",
+ "link_header_test.cc",
+ "subresource_integrity_test.cc",
+ ]
+
+ configs += [ "//third_party/blink/renderer/platform:blink_platform_config" ]
+
+ deps = [
+ "//testing/gmock",
+ "//testing/gtest",
+ "//third_party/blink/renderer/platform:platform",
+ ]
+}
+
+jumbo_source_set("test_support") {
+ # This target defines test files for platform:test_support that
+ # blink_platform_unittests and webkit_unit_tests can use.
+ visibility = [ "//third_party/blink/renderer/platform:test_support" ]
+ testonly = true
+
+ # Source files that can be called from blink_platform_unittests and
+ # webkit_unit_tests.
+ sources = [
+ "testing/crypto_testing_platform_support.h",
+ "testing/fetch_testing_platform_support.cc",
+ "testing/fetch_testing_platform_support.h",
+ "testing/mock_fetch_context.h",
+ "testing/mock_resource.cc",
+ "testing/mock_resource.h",
+ "testing/mock_resource_client.h",
+ "testing/web_url_loader_factory_with_mock.cc",
+ "testing/web_url_loader_factory_with_mock.h",
+ ]
+
+ configs += [
+ "//third_party/blink/renderer:non_test_config",
+ "//third_party/blink/renderer/platform:blink_platform_config",
+ ]
+
+ public_deps = [
+ "//net",
+ "//skia",
+ "//third_party/blink/public:blink_headers",
+ "//third_party/blink/renderer/platform:platform",
+ "//third_party/blink/renderer/platform/blob:generator",
+ "//third_party/icu",
+ ]
+}
diff --git a/chromium/third_party/blink/renderer/platform/loader/DEPS b/chromium/third_party/blink/renderer/platform/loader/DEPS
new file mode 100644
index 00000000000..3577746624c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/DEPS
@@ -0,0 +1,13 @@
+include_rules = [
+ "+base/metrics/field_trial_params.h", # for fetch/ResourceLoadScheduler.cpp
+ "+base/strings/string_number_conversions.h", # for fetch/ResourceLoadScheduler.cpp
+ "+components/link_header_util", # for LinkHeader.cpp
+ "+services/network/public", # for Fetch API and CORS
+ "+third_party/boringssl/src/include/openssl/curve25519.h" # for SubresourceIntegrity.cpp
+]
+
+specific_include_rules = {
+ "resource_error\.cc": [
+ "+net/base/net_errors.h"
+ ]
+}
diff --git a/chromium/third_party/blink/renderer/platform/loader/OWNERS b/chromium/third_party/blink/renderer/platform/loader/OWNERS
new file mode 100644
index 00000000000..fa3423b99fb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/OWNERS
@@ -0,0 +1,7 @@
+japhet@chromium.org
+mkwst@chromium.org
+yhirano@chromium.org
+yoav@yoav.ws
+
+# TEAM: loading-dev@chromium.org
+# COMPONENT: Blink>Loader
diff --git a/chromium/third_party/blink/renderer/platform/loader/README.md b/chromium/third_party/blink/renderer/platform/loader/README.md
new file mode 100644
index 00000000000..83fa16c4885
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/README.md
@@ -0,0 +1,22 @@
+# platform/loader/
+
+This document describes how files under `platform/loader/` are organized.
+
+## cors
+
+Contains Cross-Origin Resource Sharing (CORS) related files. Some functions
+in this directory will be removed once CORS support is moved to
+//services/network. Please contact {kinuko,tyoshino,toyoshim}@chromium.org when
+you need to depend on this directory from new code.
+
+## fetch
+
+Contains files for low-level loading APIs. The `PLATFORM_EXPORT` macro is
+needed to use them from `core/`. Otherwise they can be used only in
+`platform/`.
+
+## testing
+
+Contains helper files for testing that are available in both
+`blink_platform_unittests` and `webkit_unit_tests`.
+These files are built as a part of the `platform:test_support` static library.
diff --git a/chromium/third_party/blink/renderer/platform/loader/cors/cors.cc b/chromium/third_party/blink/renderer/platform/loader/cors/cors.cc
new file mode 100644
index 00000000000..370971d02b8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/cors/cors.cc
@@ -0,0 +1,215 @@
+// Copyright 2018 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/platform/loader/cors/cors.h"
+
+#include <string>
+
+#include "services/network/public/cpp/cors/cors.h"
+#include "services/network/public/cpp/cors/preflight_cache.h"
+#include "third_party/blink/renderer/platform/loader/cors/cors_error_string.h"
+#include "third_party/blink/renderer/platform/network/http_header_map.h"
+#include "third_party/blink/renderer/platform/network/http_names.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/thread_specific.h"
+#include "url/gurl.h"
+
+namespace blink {
+
+namespace {
+
+base::Optional<std::string> GetHeaderValue(const HTTPHeaderMap& header_map,
+ const AtomicString& header_name) {
+ if (header_map.Contains(header_name)) {
+ const AtomicString& atomic_value = header_map.Get(header_name);
+ CString string_value = atomic_value.GetString().Utf8();
+ return std::string(string_value.data(), string_value.length());
+ }
+ return base::nullopt;
+}
+
+network::cors::PreflightCache& GetPerThreadPreflightCache() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<network::cors::PreflightCache>,
+ cache, ());
+ return *cache;
+}
+
+base::Optional<std::string> GetOptionalHeaderValue(
+ const HTTPHeaderMap& header_map,
+ const AtomicString& header_name) {
+ const AtomicString& result = header_map.Get(header_name);
+ if (result.IsNull())
+ return base::nullopt;
+
+ return std::string(result.Ascii().data());
+}
+
+std::unique_ptr<net::HttpRequestHeaders> CreateNetHttpRequestHeaders(
+ const HTTPHeaderMap& header_map) {
+ std::unique_ptr<net::HttpRequestHeaders> request_headers =
+ std::make_unique<net::HttpRequestHeaders>();
+ for (HTTPHeaderMap::const_iterator i = header_map.begin(),
+ end = header_map.end();
+ i != end; ++i) {
+ DCHECK(!i->key.IsNull());
+ DCHECK(!i->value.IsNull());
+ request_headers->SetHeader(std::string(i->key.Ascii().data()),
+ std::string(i->value.Ascii().data()));
+ }
+ return request_headers;
+}
+
+} // namespace
+
+namespace CORS {
+
+WTF::Optional<network::mojom::CORSError> CheckAccess(
+ const KURL& response_url,
+ const int response_status_code,
+ const HTTPHeaderMap& response_header,
+ network::mojom::FetchCredentialsMode credentials_mode,
+ const SecurityOrigin& origin) {
+ std::unique_ptr<SecurityOrigin::PrivilegeData> privilege =
+ origin.CreatePrivilegeData();
+ return network::cors::CheckAccess(
+ response_url, response_status_code,
+ GetHeaderValue(response_header, HTTPNames::Access_Control_Allow_Origin),
+ GetHeaderValue(response_header,
+ HTTPNames::Access_Control_Allow_Credentials),
+ credentials_mode, origin.ToUrlOrigin(),
+ !privilege->block_local_access_from_local_origin_);
+}
+
+WTF::Optional<network::mojom::CORSError> CheckRedirectLocation(
+ const KURL& url) {
+ static const bool run_blink_side_scheme_check =
+ !RuntimeEnabledFeatures::OutOfBlinkCORSEnabled();
+ // TODO(toyoshim): Deprecate Blink side scheme check when we enable
+ // out-of-renderer CORS support. This will need to deprecate Blink APIs that
+ // are currently used by an embedder. See https://crbug.com/800669.
+ if (run_blink_side_scheme_check &&
+ !SchemeRegistry::ShouldTreatURLSchemeAsCORSEnabled(url.Protocol())) {
+ return network::mojom::CORSError::kRedirectDisallowedScheme;
+ }
+ return network::cors::CheckRedirectLocation(url, run_blink_side_scheme_check);
+}
+
+WTF::Optional<network::mojom::CORSError> CheckPreflight(
+ const int preflight_response_status_code) {
+ return network::cors::CheckPreflight(preflight_response_status_code);
+}
+
+WTF::Optional<network::mojom::CORSError> CheckExternalPreflight(
+ const HTTPHeaderMap& response_header) {
+ return network::cors::CheckExternalPreflight(GetHeaderValue(
+ response_header, HTTPNames::Access_Control_Allow_External));
+}
+
+bool IsCORSEnabledRequestMode(network::mojom::FetchRequestMode request_mode) {
+ return network::cors::IsCORSEnabledRequestMode(request_mode);
+}
+
+bool EnsurePreflightResultAndCacheOnSuccess(
+ const HTTPHeaderMap& response_header_map,
+ const String& origin,
+ const KURL& request_url,
+ const String& request_method,
+ const HTTPHeaderMap& request_header_map,
+ network::mojom::FetchCredentialsMode request_credentials_mode,
+ String* error_description) {
+ DCHECK(!origin.IsNull());
+ DCHECK(!request_method.IsNull());
+ DCHECK(error_description);
+
+ base::Optional<network::mojom::CORSError> error;
+
+ std::unique_ptr<network::cors::PreflightResult> result =
+ network::cors::PreflightResult::Create(
+ request_credentials_mode,
+ GetOptionalHeaderValue(response_header_map,
+ HTTPNames::Access_Control_Allow_Methods),
+ GetOptionalHeaderValue(response_header_map,
+ HTTPNames::Access_Control_Allow_Headers),
+ GetOptionalHeaderValue(response_header_map,
+ HTTPNames::Access_Control_Max_Age),
+ &error);
+ if (error) {
+ *error_description = CORS::GetErrorString(
+ CORS::ErrorParameter::CreateForPreflightResponseCheck(*error,
+ String()));
+ return false;
+ }
+
+ error = result->EnsureAllowedCrossOriginMethod(
+ std::string(request_method.Ascii().data()));
+ if (error) {
+ *error_description = CORS::GetErrorString(
+ CORS::ErrorParameter::CreateForPreflightResponseCheck(*error,
+ request_method));
+ return false;
+ }
+
+ std::string detected_error_header;
+ error = result->EnsureAllowedCrossOriginHeaders(
+ *CreateNetHttpRequestHeaders(request_header_map), &detected_error_header);
+ if (error) {
+ *error_description = CORS::GetErrorString(
+ CORS::ErrorParameter::CreateForPreflightResponseCheck(
+ *error, String(detected_error_header.data(),
+ detected_error_header.length())));
+ return false;
+ }
+
+ DCHECK(!error);
+
+ GetPerThreadPreflightCache().AppendEntry(std::string(origin.Ascii().data()),
+ request_url, std::move(result));
+ return true;
+}
+
+bool CheckIfRequestCanSkipPreflight(
+ const String& origin,
+ const KURL& url,
+ network::mojom::FetchCredentialsMode credentials_mode,
+ const String& method,
+ const HTTPHeaderMap& request_header_map) {
+ DCHECK(!origin.IsNull());
+ DCHECK(!method.IsNull());
+
+ return GetPerThreadPreflightCache().CheckIfRequestCanSkipPreflight(
+ std::string(origin.Ascii().data()), url, credentials_mode,
+ std::string(method.Ascii().data()),
+ *CreateNetHttpRequestHeaders(request_header_map));
+}
+
+bool IsCORSSafelistedMethod(const String& method) {
+ DCHECK(!method.IsNull());
+ CString utf8_method = method.Utf8();
+ return network::cors::IsCORSSafelistedMethod(
+ std::string(utf8_method.data(), utf8_method.length()));
+}
+
+bool IsCORSSafelistedContentType(const String& media_type) {
+ CString utf8_media_type = media_type.Utf8();
+ return network::cors::IsCORSSafelistedContentType(
+ std::string(utf8_media_type.data(), utf8_media_type.length()));
+}
+
+bool IsCORSSafelistedHeader(const String& name, const String& value) {
+ DCHECK(!name.IsNull());
+ DCHECK(!value.IsNull());
+ CString utf8_name = name.Utf8();
+ CString utf8_value = value.Utf8();
+ return network::cors::IsCORSSafelistedHeader(
+ std::string(utf8_name.data(), utf8_name.length()),
+ std::string(utf8_value.data(), utf8_value.length()));
+}
+
+} // namespace CORS
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/cors/cors.h b/chromium/third_party/blink/renderer/platform/loader/cors/cors.h
new file mode 100644
index 00000000000..0ba71c770f7
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/cors/cors.h
@@ -0,0 +1,71 @@
+// Copyright 2018 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_PLATFORM_LOADER_CORS_CORS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_CORS_CORS_H_
+
+#include "services/network/public/mojom/cors.mojom-shared.h"
+#include "services/network/public/mojom/fetch_api.mojom-shared.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class HTTPHeaderMap;
+class KURL;
+class SecurityOrigin;
+
+// CORS related utility functions.
+namespace CORS {
+
+// Thin wrapper functions below are for calling ::network::cors functions from
+// Blink core. Once Out-of-renderer CORS is enabled, following functions will
+// be removed.
+PLATFORM_EXPORT WTF::Optional<network::mojom::CORSError> CheckAccess(
+ const KURL&,
+ const int response_status_code,
+ const HTTPHeaderMap&,
+ network::mojom::FetchCredentialsMode,
+ const SecurityOrigin&);
+
+PLATFORM_EXPORT WTF::Optional<network::mojom::CORSError> CheckRedirectLocation(
+ const KURL&);
+
+PLATFORM_EXPORT WTF::Optional<network::mojom::CORSError> CheckPreflight(
+ const int preflight_response_status_code);
+
+PLATFORM_EXPORT WTF::Optional<network::mojom::CORSError> CheckExternalPreflight(
+ const HTTPHeaderMap&);
+
+PLATFORM_EXPORT bool IsCORSEnabledRequestMode(network::mojom::FetchRequestMode);
+
+PLATFORM_EXPORT bool EnsurePreflightResultAndCacheOnSuccess(
+ const HTTPHeaderMap& response_header_map,
+ const String& origin,
+ const KURL& request_url,
+ const String& request_method,
+ const HTTPHeaderMap& request_header_map,
+ network::mojom::FetchCredentialsMode request_credentials_mode,
+ String* error_description);
+
+PLATFORM_EXPORT bool CheckIfRequestCanSkipPreflight(
+ const String& origin,
+ const KURL&,
+ network::mojom::FetchCredentialsMode,
+ const String& method,
+ const HTTPHeaderMap& request_header_map);
+
+// Thin wrapper functions that will not be removed even after out-of-renderer
+// CORS is enabled.
+PLATFORM_EXPORT bool IsCORSSafelistedMethod(const String& method);
+PLATFORM_EXPORT bool IsCORSSafelistedContentType(const String&);
+PLATFORM_EXPORT bool IsCORSSafelistedHeader(const String& name,
+ const String& value);
+
+} // namespace CORS
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_CORS_CORS_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/cors/cors_error_string.cc b/chromium/third_party/blink/renderer/platform/loader/cors/cors_error_string.cc
new file mode 100644
index 00000000000..e0d653ff462
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/cors/cors_error_string.cc
@@ -0,0 +1,344 @@
+// Copyright 2018 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/platform/loader/cors/cors_error_string.h"
+
+#include "third_party/blink/renderer/platform/network/http_header_map.h"
+#include "third_party/blink/renderer/platform/network/http_names.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+
+namespace blink {
+
+namespace CORS {
+
+namespace {
+
+const KURL& GetInvalidURL() {
+ DEFINE_THREAD_SAFE_STATIC_LOCAL(KURL, invalid_url, ());
+ return invalid_url;
+}
+
+bool IsInterestingStatusCode(int status_code) {
+ // Predicate that gates what status codes should be included in console error
+ // messages for responses containing no access control headers.
+ return status_code >= 400;
+}
+
+ErrorParameter CreateWrongParameter(network::mojom::CORSError error) {
+ return ErrorParameter(
+ error, GetInvalidURL(), GetInvalidURL(), 0 /* status_code */,
+ HTTPHeaderMap(), *SecurityOrigin::CreateUnique(),
+ WebURLRequest::kRequestContextUnspecified, String(), true);
+}
+
+} // namespace
+
+// static
+ErrorParameter ErrorParameter::Create(
+ const network::mojom::CORSError error,
+ const KURL& first_url,
+ const KURL& second_url,
+ const int status_code,
+ const HTTPHeaderMap& header_map,
+ const SecurityOrigin& origin,
+ const WebURLRequest::RequestContext context) {
+ return ErrorParameter(error, first_url, second_url, status_code, header_map,
+ origin, context, String(), false);
+}
+
+// static
+ErrorParameter ErrorParameter::CreateForDisallowedByMode(
+ const KURL& request_url) {
+ return ErrorParameter(network::mojom::CORSError::kDisallowedByMode,
+ request_url, GetInvalidURL(), 0 /* status_code */,
+ HTTPHeaderMap(), *SecurityOrigin::CreateUnique(),
+ WebURLRequest::kRequestContextUnspecified, String(),
+ false);
+}
+
+// static
+ErrorParameter ErrorParameter::CreateForInvalidResponse(
+ const KURL& request_url,
+ const SecurityOrigin& origin) {
+ return ErrorParameter(
+ network::mojom::CORSError::kInvalidResponse, request_url, GetInvalidURL(),
+ 0 /* status_code */, HTTPHeaderMap(), origin,
+ WebURLRequest::kRequestContextUnspecified, String(), false);
+}
+
+// static
+ErrorParameter ErrorParameter::CreateForAccessCheck(
+ const network::mojom::CORSError error,
+ const KURL& request_url,
+ int response_status_code,
+ const HTTPHeaderMap& response_header_map,
+ const SecurityOrigin& origin,
+ const WebURLRequest::RequestContext context,
+ const KURL& redirect_url) {
+ switch (error) {
+ case network::mojom::CORSError::kInvalidResponse:
+ case network::mojom::CORSError::kWildcardOriginNotAllowed:
+ case network::mojom::CORSError::kMissingAllowOriginHeader:
+ case network::mojom::CORSError::kMultipleAllowOriginValues:
+ case network::mojom::CORSError::kInvalidAllowOriginValue:
+ case network::mojom::CORSError::kAllowOriginMismatch:
+ case network::mojom::CORSError::kDisallowCredentialsNotSetToTrue:
+ return ErrorParameter(error, request_url, redirect_url,
+ response_status_code, response_header_map, origin,
+ context, String(), false);
+ default:
+ NOTREACHED();
+ }
+ return CreateWrongParameter(error);
+}
+
+// static
+ErrorParameter ErrorParameter::CreateForPreflightStatusCheck(
+ int response_status_code) {
+ return ErrorParameter(network::mojom::CORSError::kPreflightInvalidStatus,
+ GetInvalidURL(), GetInvalidURL(), response_status_code,
+ HTTPHeaderMap(), *SecurityOrigin::CreateUnique(),
+ WebURLRequest::kRequestContextUnspecified, String(),
+ false);
+}
+
+// static
+ErrorParameter ErrorParameter::CreateForExternalPreflightCheck(
+ const network::mojom::CORSError error,
+ const HTTPHeaderMap& response_header_map) {
+ switch (error) {
+ case network::mojom::CORSError::kPreflightMissingAllowExternal:
+ case network::mojom::CORSError::kPreflightInvalidAllowExternal:
+ return ErrorParameter(
+ error, GetInvalidURL(), GetInvalidURL(), 0 /* status_code */,
+ response_header_map, *SecurityOrigin::CreateUnique(),
+ WebURLRequest::kRequestContextUnspecified, String(), false);
+ default:
+ NOTREACHED();
+ }
+ return CreateWrongParameter(error);
+}
+
+// static
+ErrorParameter ErrorParameter::CreateForPreflightResponseCheck(
+ const network::mojom::CORSError error,
+ const String& hint) {
+ switch (error) {
+ case network::mojom::CORSError::kInvalidAllowMethodsPreflightResponse:
+ case network::mojom::CORSError::kInvalidAllowHeadersPreflightResponse:
+ case network::mojom::CORSError::kMethodDisallowedByPreflightResponse:
+ case network::mojom::CORSError::kHeaderDisallowedByPreflightResponse:
+ return ErrorParameter(
+ error, GetInvalidURL(), GetInvalidURL(), 0 /* status_code */,
+ HTTPHeaderMap(), *SecurityOrigin::CreateUnique(),
+ WebURLRequest::kRequestContextUnspecified, hint, false);
+ default:
+ NOTREACHED();
+ }
+ return CreateWrongParameter(error);
+}
+
+// static
+ErrorParameter ErrorParameter::CreateForRedirectCheck(
+ network::mojom::CORSError error,
+ const KURL& request_url,
+ const KURL& redirect_url) {
+ switch (error) {
+ case network::mojom::CORSError::kRedirectDisallowedScheme:
+ case network::mojom::CORSError::kRedirectContainsCredentials:
+ return ErrorParameter(
+ error, request_url, redirect_url, 0 /* status_code */,
+ HTTPHeaderMap(), *SecurityOrigin::CreateUnique(),
+ WebURLRequest::kRequestContextUnspecified, String(), false);
+ default:
+ NOTREACHED();
+ }
+ return CreateWrongParameter(error);
+}
+
+ErrorParameter::ErrorParameter(const network::mojom::CORSError error,
+ const KURL& first_url,
+ const KURL& second_url,
+ const int status_code,
+ const HTTPHeaderMap& header_map,
+ const SecurityOrigin& origin,
+ const WebURLRequest::RequestContext context,
+ const String& hint,
+ bool unknown)
+ : error(error),
+ first_url(first_url),
+ second_url(second_url),
+ status_code(status_code),
+ header_map(header_map),
+ origin(origin),
+ context(context),
+ hint(hint),
+ unknown(unknown) {}
+
+String GetErrorString(const ErrorParameter& param) {
+ static const char kNoCorsInformation[] =
+ " Have the server send the header with a valid value, or, if an opaque "
+ "response serves your needs, set the request's mode to 'no-cors' to "
+ "fetch the resource with CORS disabled.";
+
+ if (param.unknown)
+ return String::Format("CORS error, code %d", static_cast<int>(param.error));
+
+ String redirect_denied =
+ param.second_url.IsValid()
+ ? String::Format(
+ "Redirect from '%s' to '%s' has been blocked by CORS policy: ",
+ param.first_url.GetString().Utf8().data(),
+ param.second_url.GetString().Utf8().data())
+ : String();
+
+ switch (param.error) {
+ case network::mojom::CORSError::kDisallowedByMode:
+ return String::Format(
+ "Failed to load '%s': Cross origin requests are not allowed by "
+ "request mode.",
+ param.first_url.GetString().Utf8().data());
+ case network::mojom::CORSError::kInvalidResponse:
+ return String::Format(
+ "%sInvalid response. Origin '%s' is therefore not allowed access.",
+ redirect_denied.Utf8().data(), param.origin.ToString().Utf8().data());
+ case network::mojom::CORSError::kWildcardOriginNotAllowed:
+ return String::Format(
+ "%sThe value of the 'Access-Control-Allow-Origin' header in the "
+ "response must not be the wildcard '*' when the request's "
+ "credentials mode is 'include'. Origin '%s' is therefore not allowed "
+ "access.%s",
+ redirect_denied.Utf8().data(), param.origin.ToString().Utf8().data(),
+ param.context == WebURLRequest::kRequestContextXMLHttpRequest
+ ? " The credentials mode of requests initiated by the "
+ "XMLHttpRequest is controlled by the withCredentials attribute."
+ : "");
+ case network::mojom::CORSError::kMissingAllowOriginHeader:
+ return String::Format(
+ "%sNo 'Access-Control-Allow-Origin' header is present on the "
+ "requested resource. Origin '%s' is therefore not allowed access."
+ "%s%s",
+ redirect_denied.Utf8().data(), param.origin.ToString().Utf8().data(),
+ IsInterestingStatusCode(param.status_code)
+ ? String::Format(" The response had HTTP status code %d.",
+ param.status_code)
+ .Utf8()
+ .data()
+ : "",
+ param.context == WebURLRequest::kRequestContextFetch
+ ? " If an opaque response serves your needs, set the request's "
+ "mode to 'no-cors' to fetch the resource with CORS disabled."
+ : "");
+ case network::mojom::CORSError::kMultipleAllowOriginValues:
+ return String::Format(
+ "%sThe 'Access-Control-Allow-Origin' header contains multiple values "
+ "'%s', but only one is allowed. Origin '%s' is therefore not allowed "
+ "access.%s",
+ redirect_denied.Utf8().data(),
+ param.header_map.Get(HTTPNames::Access_Control_Allow_Origin)
+ .Utf8()
+ .data(),
+ param.origin.ToString().Utf8().data(),
+ param.context == WebURLRequest::kRequestContextFetch
+ ? kNoCorsInformation
+ : "");
+ case network::mojom::CORSError::kInvalidAllowOriginValue:
+ return String::Format(
+ "%sThe 'Access-Control-Allow-Origin' header contains the invalid "
+ "value '%s'. Origin '%s' is therefore not allowed access.%s",
+ redirect_denied.Utf8().data(),
+ param.header_map.Get(HTTPNames::Access_Control_Allow_Origin)
+ .Utf8()
+ .data(),
+ param.origin.ToString().Utf8().data(),
+ param.context == WebURLRequest::kRequestContextFetch
+ ? kNoCorsInformation
+ : "");
+ case network::mojom::CORSError::kAllowOriginMismatch:
+ return String::Format(
+ "%sThe 'Access-Control-Allow-Origin' header has a value '%s' that is "
+ "not equal to the supplied origin. Origin '%s' is therefore not "
+ "allowed access.%s",
+ redirect_denied.Utf8().data(),
+ param.header_map.Get(HTTPNames::Access_Control_Allow_Origin)
+ .Utf8()
+ .data(),
+ param.origin.ToString().Utf8().data(),
+ param.context == WebURLRequest::kRequestContextFetch
+ ? kNoCorsInformation
+ : "");
+ case network::mojom::CORSError::kDisallowCredentialsNotSetToTrue:
+ return String::Format(
+ "%sThe value of the 'Access-Control-Allow-Credentials' header in "
+ "the response is '%s' which must be 'true' when the request's "
+ "credentials mode is 'include'. Origin '%s' is therefore not allowed "
+ "access.%s",
+ redirect_denied.Utf8().data(),
+ param.header_map.Get(HTTPNames::Access_Control_Allow_Credentials)
+ .Utf8()
+ .data(),
+ param.origin.ToString().Utf8().data(),
+ (param.context == WebURLRequest::kRequestContextXMLHttpRequest
+ ? " The credentials mode of requests initiated by the "
+ "XMLHttpRequest is controlled by the withCredentials "
+ "attribute."
+ : ""));
+ case network::mojom::CORSError::kPreflightInvalidStatus:
+ return String::Format(
+ "Response for preflight has invalid HTTP status code %d.",
+ param.status_code);
+ case network::mojom::CORSError::kPreflightMissingAllowExternal:
+ return String(
+ "No 'Access-Control-Allow-External' header was present in the "
+ "preflight response for this external request (This is an "
+ "experimental header which is defined in "
+ "'https://wicg.github.io/cors-rfc1918/').");
+ case network::mojom::CORSError::kPreflightInvalidAllowExternal:
+ return String::Format(
+ "The 'Access-Control-Allow-External' header in the preflight "
+ "response for this external request had a value of '%s', not 'true' "
+ "(This is an experimental header which is defined in "
+ "'https://wicg.github.io/cors-rfc1918/').",
+ param.header_map.Get(HTTPNames::Access_Control_Allow_External)
+ .Utf8()
+ .data());
+ case network::mojom::CORSError::kInvalidAllowMethodsPreflightResponse:
+ return String(
+ "Cannot parse Access-Control-Allow-Methods response header field in "
+ "preflight response.");
+ case network::mojom::CORSError::kInvalidAllowHeadersPreflightResponse:
+ return String(
+ "Cannot parse Access-Control-Allow-Headers response header field in "
+ "preflight response.");
+ case network::mojom::CORSError::kMethodDisallowedByPreflightResponse:
+ return String::Format(
+ "Method %s is not allowed by Access-Control-Allow-Methods in "
+ "preflight response.",
+ param.hint.Utf8().data());
+ case network::mojom::CORSError::kHeaderDisallowedByPreflightResponse:
+ return String::Format(
+ "Request header field %s is not allowed by "
+ "Access-Control-Allow-Headers in preflight response.",
+ param.hint.Utf8().data());
+ case network::mojom::CORSError::kRedirectDisallowedScheme:
+ return String::Format(
+ "%sRedirect location '%s' has a disallowed scheme for cross-origin "
+ "requests.",
+ redirect_denied.Utf8().data(),
+ param.second_url.GetString().Utf8().data());
+ case network::mojom::CORSError::kRedirectContainsCredentials:
+ return String::Format(
+ "%sRedirect location '%s' contains a username and password, which is "
+ "disallowed for cross-origin requests.",
+ redirect_denied.Utf8().data(),
+ param.second_url.GetString().Utf8().data());
+ }
+ NOTREACHED();
+ return String();
+}
+
+} // namespace CORS
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/cors/cors_error_string.h b/chromium/third_party/blink/renderer/platform/loader/cors/cors_error_string.h
new file mode 100644
index 00000000000..73bd0f9e07d
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/cors/cors_error_string.h
@@ -0,0 +1,112 @@
+// Copyright 2018 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_PLATFORM_LOADER_CORS_CORS_ERROR_STRING_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_CORS_CORS_ERROR_STRING_H_
+
+#include "base/macros.h"
+#include "services/network/public/mojom/cors.mojom-shared.h"
+#include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+
+namespace blink {
+
+class HTTPHeaderMap;
+class SecurityOrigin;
+
+// CORS error strings related utility functions.
+namespace CORS {
+
+// A struct to pass error dependent arguments for |GetErrorString|.
+struct PLATFORM_EXPORT ErrorParameter {
+ // Creates an ErrorParameter for generic cases. Use this function if |error|
+ // can contain any.
+ static ErrorParameter Create(const network::mojom::CORSError,
+ const KURL& first_url,
+ const KURL& second_url,
+ const int status_code,
+ const HTTPHeaderMap&,
+ const SecurityOrigin&,
+ const WebURLRequest::RequestContext);
+
+ // Creates an ErrorParameter for kDisallowedByMode.
+ static ErrorParameter CreateForDisallowedByMode(const KURL& request_url);
+
+ // Creates an ErrorParameter for kInvalidResponse.
+ static ErrorParameter CreateForInvalidResponse(const KURL& request_url,
+ const SecurityOrigin&);
+
+ // Creates an ErrorParameter for an error that CORS::CheckAccess() returns.
+ // |error| for redirect check needs to specify a valid |redirect_url|. The
+ // |redirect_url| can be omitted not to include redirect related information.
+ static ErrorParameter CreateForAccessCheck(
+ const network::mojom::CORSError,
+ const KURL& request_url,
+ int response_status_code,
+ const HTTPHeaderMap& response_header_map,
+ const SecurityOrigin&,
+ const WebURLRequest::RequestContext,
+ const KURL& redirect_url = KURL());
+
+ // Creates an ErrorParameter for kPreflightInvalidStatus that
+ // CORS::CheckPreflight() returns.
+ static ErrorParameter CreateForPreflightStatusCheck(int response_status_code);
+
+ // Creates an ErrorParameter for an error that CORS::CheckExternalPreflight()
+ // returns.
+ static ErrorParameter CreateForExternalPreflightCheck(
+ const network::mojom::CORSError,
+ const HTTPHeaderMap& response_header_map);
+
+ // Creates an ErrorParameter for an error that is related to CORS-preflight
+ // response checks.
+ // |hint| should contain a banned request method for
+ // kMethodDisallowedByPreflightResponse, a banned request header name for
+ // kHeaderDisallowedByPreflightResponse, or can be omitted for others.
+ static ErrorParameter CreateForPreflightResponseCheck(
+ const network::mojom::CORSError,
+ const String& hint);
+
+ // Creates an ErrorParameter for CORS::CheckRedirectLocation() returns.
+ static ErrorParameter CreateForRedirectCheck(network::mojom::CORSError,
+ const KURL& request_url,
+ const KURL& redirect_url);
+
+ // Should not be used directly by external callers. Use Create functions
+ // above.
+ ErrorParameter(const network::mojom::CORSError,
+ const KURL& first_url,
+ const KURL& second_url,
+ const int status_code,
+ const HTTPHeaderMap&,
+ const SecurityOrigin&,
+ const WebURLRequest::RequestContext,
+ const String& hint,
+ bool unknown);
+
+ // Members that this struct carries.
+ const network::mojom::CORSError error;
+ const KURL& first_url;
+ const KURL& second_url;
+ const int status_code;
+ const HTTPHeaderMap& header_map;
+ const SecurityOrigin& origin;
+ const WebURLRequest::RequestContext context;
+ const String& hint;
+
+ // Set to true when an ErrorParameter was created in a wrong way. Used in
+ // GetErrorString() to be robust for coding errors.
+ const bool unknown;
+};
+
+// Stringify CORSError mainly for inspector messages. Generated string should
+// not be exposed to JavaScript for security reasons.
+PLATFORM_EXPORT String GetErrorString(const ErrorParameter&);
+
+} // namespace CORS
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_CORS_CORS_ERROR_STRING_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/cors/cors_status.h b/chromium/third_party/blink/renderer/platform/loader/cors/cors_status.h
new file mode 100644
index 00000000000..d6ffdc89e3f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/cors/cors_status.h
@@ -0,0 +1,27 @@
+// Copyright 2017 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_PLATFORM_LOADER_CORS_CORS_STATUS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_CORS_CORS_STATUS_H_
+
+namespace blink {
+
+enum class CORSStatus {
+ kUnknown, // Status not determined - not supposed to be seen by users.
+ kNotApplicable, // E.g. for main resources or if not in fetch mode CORS.
+
+ // Response not handled by service worker:
+ kSameOrigin, // Request was same origin.
+ kSuccessful, // Request was cross origin and CORS checks passed.
+ kFailed, // Request was cross origin and CORS checks failed.
+
+ // Response handled by service worker:
+ kServiceWorkerSuccessful, // ResponseType other than opaque (including
+ // error).
+ kServiceWorkerOpaque, // ResponseType was opaque.
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/README.md b/chromium/third_party/blink/renderer/platform/loader/fetch/README.md
new file mode 100644
index 00000000000..5e6da7211f2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/README.md
@@ -0,0 +1,6 @@
+Low-level fetching code.
+
+Fetching/loading code is divided into:
+- core/fetch: Fetch API
+- core/loader: high-level fetching
+- platform/loader/fetch: low-level fetching
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/access_control_status.h b/chromium/third_party/blink/renderer/platform/loader/fetch/access_control_status.h
new file mode 100644
index 00000000000..bd8d9454dc5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/access_control_status.h
@@ -0,0 +1,18 @@
+// Copyright 2015 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_PLATFORM_LOADER_FETCH_ACCESS_CONTROL_STATUS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_ACCESS_CONTROL_STATUS_H_
+
+namespace blink {
+
+enum AccessControlStatus {
+ kNotSharableCrossOrigin,
+ kSharableCrossOrigin,
+ kOpaqueResource
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_ACCESS_CONTROL_STATUS_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_data_pipe_writer.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_data_pipe_writer.cc
new file mode 100644
index 00000000000..e7f1ee3bfcb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_data_pipe_writer.cc
@@ -0,0 +1,109 @@
+// Copyright 2017 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/platform/loader/fetch/buffering_data_pipe_writer.h"
+
+#include "base/single_thread_task_runner.h"
+
+namespace blink {
+
+namespace {
+
+const auto kNone = MOJO_WRITE_DATA_FLAG_NONE;
+
+} // namespace
+
+BufferingDataPipeWriter::BufferingDataPipeWriter(
+ mojo::ScopedDataPipeProducerHandle handle,
+ base::SingleThreadTaskRunner* runner)
+ : handle_(std::move(handle)),
+ watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL, runner) {
+ watcher_.Watch(handle_.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
+ MOJO_WATCH_CONDITION_SATISFIED,
+ base::BindRepeating(&BufferingDataPipeWriter::OnWritable,
+ base::Unretained(this)));
+}
+
+bool BufferingDataPipeWriter::Write(const char* buffer, uint32_t num_bytes) {
+ DCHECK(!finished_);
+ if (!handle_.is_valid())
+ return false;
+
+ if (buffer_.empty()) {
+ while (num_bytes > 0) {
+ uint32_t size = num_bytes;
+ MojoResult result = handle_->WriteData(buffer, &size, kNone);
+ if (result == MOJO_RESULT_SHOULD_WAIT)
+ break;
+ if (result != MOJO_RESULT_OK) {
+ Clear();
+ return false;
+ }
+ num_bytes -= size;
+ buffer += size;
+ }
+ }
+ if (num_bytes == 0)
+ return true;
+
+ buffer_.push_back(Vector<char>());
+ buffer_.back().Append(buffer, num_bytes);
+ if (!waiting_) {
+ waiting_ = true;
+ watcher_.ArmOrNotify();
+ }
+ return true;
+}
+
+void BufferingDataPipeWriter::Finish() {
+ finished_ = true;
+ ClearIfNeeded();
+}
+
+void BufferingDataPipeWriter::OnWritable(MojoResult,
+ const mojo::HandleSignalsState&) {
+ if (!handle_.is_valid())
+ return;
+ waiting_ = false;
+ while (!buffer_.empty()) {
+ WTF::Vector<char>& front = buffer_.front();
+
+ uint32_t size = front.size() - front_written_size_;
+
+ MojoResult result =
+ handle_->WriteData(front.data() + front_written_size_, &size, kNone);
+ if (result == MOJO_RESULT_SHOULD_WAIT) {
+ waiting_ = true;
+ watcher_.ArmOrNotify();
+ return;
+ }
+ if (result != MOJO_RESULT_OK) {
+ Clear();
+ return;
+ }
+ front_written_size_ += size;
+
+ if (front_written_size_ == front.size()) {
+ front_written_size_ = 0;
+ buffer_.TakeFirst();
+ }
+ }
+ ClearIfNeeded();
+}
+
+void BufferingDataPipeWriter::Clear() {
+ handle_.reset();
+ watcher_.Cancel();
+ buffer_.clear();
+}
+
+void BufferingDataPipeWriter::ClearIfNeeded() {
+ if (!finished_)
+ return;
+
+ if (buffer_.empty())
+ Clear();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_data_pipe_writer.h b/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_data_pipe_writer.h
new file mode 100644
index 00000000000..8b3175fd537
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_data_pipe_writer.h
@@ -0,0 +1,46 @@
+// Copyright 2017 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_PLATFORM_LOADER_FETCH_BUFFERING_DATA_PIPE_WRITER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_BUFFERING_DATA_PIPE_WRITER_H_
+
+#include "base/single_thread_task_runner.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "mojo/public/cpp/system/simple_watcher.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/deque.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+// A writer to a mojo data pipe which has a buffer to store contents. As a
+// result, it is possible for a caller to miss write failures.
+class PLATFORM_EXPORT BufferingDataPipeWriter {
+ public:
+ BufferingDataPipeWriter(mojo::ScopedDataPipeProducerHandle,
+ base::SingleThreadTaskRunner*);
+
+ // Writes buffer[0:num_bytes] to the data pipe. Returns true if there is no
+ // error.
+ bool Write(const char* buffer, uint32_t num_bytes);
+
+ // Finishes writing. After calling this function, Write must not be called.
+ void Finish();
+
+ private:
+ void OnWritable(MojoResult, const mojo::HandleSignalsState&);
+ void ClearIfNeeded();
+ void Clear();
+
+ mojo::ScopedDataPipeProducerHandle handle_;
+ mojo::SimpleWatcher watcher_;
+ Deque<Vector<char>> buffer_;
+ size_t front_written_size_ = 0;
+ bool waiting_ = false;
+ bool finished_ = false;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_data_pipe_writer_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_data_pipe_writer_test.cc
new file mode 100644
index 00000000000..0788a286d23
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_data_pipe_writer_test.cc
@@ -0,0 +1,78 @@
+// Copyright 2017 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/platform/loader/fetch/buffering_data_pipe_writer.h"
+
+#include <memory>
+#include <random>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+
+namespace blink {
+namespace {
+
+TEST(BufferingDataPipeWriterTest, WriteMany) {
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform;
+ constexpr int kCapacity = 4096;
+
+ std::minstd_rand engine(99);
+
+ mojo::ScopedDataPipeProducerHandle producer;
+ mojo::ScopedDataPipeConsumerHandle consumer;
+ MojoCreateDataPipeOptions options;
+ options.struct_size = sizeof(MojoCreateDataPipeOptions);
+ options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE;
+ options.element_num_bytes = 1;
+ options.capacity_num_bytes = kCapacity;
+
+ MojoResult result = mojo::CreateDataPipe(&options, &producer, &consumer);
+ ASSERT_EQ(MOJO_RESULT_OK, result);
+
+ constexpr size_t total = kCapacity * 3;
+ constexpr size_t writing_chunk_size = 5;
+ constexpr size_t reading_chunk_size = 7;
+ Vector<char> input, output;
+
+ for (size_t i = 0; i < total; ++i)
+ input.push_back(static_cast<char>(engine() % 26 + 'A'));
+
+ auto writer = std::make_unique<BufferingDataPipeWriter>(
+ std::move(producer), platform->CurrentThread()->GetTaskRunner().get());
+
+ for (size_t i = 0; i < total;) {
+ // We use a temporary buffer to check that the buffer is copied immediately.
+ char temp[writing_chunk_size] = {};
+ size_t size = std::min(total - i, writing_chunk_size);
+
+ std::copy(input.data() + i, input.data() + i + size, temp);
+ ASSERT_TRUE(writer->Write(temp, size));
+
+ i += size;
+ }
+
+ writer->Finish();
+
+ while (true) {
+ constexpr auto kNone = MOJO_READ_DATA_FLAG_NONE;
+ char buffer[reading_chunk_size] = {};
+ uint32_t size = reading_chunk_size;
+ result = consumer->ReadData(buffer, &size, kNone);
+
+ if (result == MOJO_RESULT_SHOULD_WAIT) {
+ platform->RunUntilIdle();
+ continue;
+ }
+ if (result == MOJO_RESULT_FAILED_PRECONDITION)
+ break;
+
+ ASSERT_EQ(MOJO_RESULT_OK, result);
+ output.Append(buffer, size);
+ }
+ EXPECT_EQ(output, input);
+}
+
+} // namespace
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata.cc
new file mode 100644
index 00000000000..563a6742750
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata.cc
@@ -0,0 +1,54 @@
+// Copyright 2016 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/platform/loader/fetch/cached_metadata.h"
+
+#include "third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h"
+
+namespace blink {
+
+scoped_refptr<CachedMetadata> CachedMetadata::CreateFromSerializedData(
+ const char* data,
+ size_t size) {
+ // Ensure the data is big enough, otherwise discard the data.
+ if (size < kCachedMetaDataStart) {
+ return nullptr;
+ }
+ // Ensure the marker matches, otherwise discard the data.
+ if (*reinterpret_cast<const uint32_t*>(data) !=
+ CachedMetadataHandler::kSingleEntry) {
+ return nullptr;
+ }
+ return base::AdoptRef(new CachedMetadata(data, size));
+}
+
+CachedMetadata::CachedMetadata(const char* data, size_t size) {
+ // Serialized metadata should have non-empty data.
+ DCHECK_GT(size, kCachedMetaDataStart);
+ DCHECK(data);
+ // Make sure that the first int in the data is the single entry marker.
+ CHECK_EQ(*reinterpret_cast<const uint32_t*>(data),
+ CachedMetadataHandler::kSingleEntry);
+
+ serialized_data_.ReserveInitialCapacity(size);
+ serialized_data_.Append(data, size);
+}
+
+CachedMetadata::CachedMetadata(uint32_t data_type_id,
+ const char* data,
+ size_t size) {
+ // Don't allow an ID of 0, it is used internally to indicate errors.
+ DCHECK(data_type_id);
+ DCHECK(data);
+
+ serialized_data_.ReserveInitialCapacity(kCachedMetaDataStart + size);
+ uint32_t marker = CachedMetadataHandler::kSingleEntry;
+ serialized_data_.Append(reinterpret_cast<const char*>(&marker),
+ sizeof(uint32_t));
+ serialized_data_.Append(reinterpret_cast<const char*>(&data_type_id),
+ sizeof(uint32_t));
+ serialized_data_.Append(data, size);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata.h b/chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata.h
new file mode 100644
index 00000000000..1763b23fd42
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_CACHED_METADATA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_CACHED_METADATA_H_
+
+#include <stdint.h>
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+// |m_serializedData| consists of a 32 bit marker, 32 bits type ID, and actual
+// data.
+constexpr size_t kCacheDataTypeStart = sizeof(uint32_t);
+constexpr size_t kCachedMetaDataStart = kCacheDataTypeStart + sizeof(uint32_t);
+
+// Metadata retrieved from the embedding application's cache.
+//
+// Serialized data is NOT portable across architectures. However, reading the
+// data type ID will reject data generated with a different byte-order.
+class PLATFORM_EXPORT CachedMetadata : public RefCounted<CachedMetadata> {
+ public:
+ static scoped_refptr<CachedMetadata> Create(uint32_t data_type_id,
+ const char* data,
+ size_t size) {
+ return base::AdoptRef(new CachedMetadata(data_type_id, data, size));
+ }
+
+ static scoped_refptr<CachedMetadata> CreateFromSerializedData(
+ const char* data,
+ size_t);
+
+ ~CachedMetadata() = default;
+
+ const Vector<char>& SerializedData() const { return serialized_data_; }
+
+ uint32_t DataTypeID() const {
+ DCHECK_GE(serialized_data_.size(), kCachedMetaDataStart);
+ return *reinterpret_cast_ptr<uint32_t*>(
+ const_cast<char*>(serialized_data_.data() + kCacheDataTypeStart));
+ }
+
+ const char* Data() const {
+ DCHECK_GE(serialized_data_.size(), kCachedMetaDataStart);
+ return serialized_data_.data() + kCachedMetaDataStart;
+ }
+
+ size_t size() const {
+ DCHECK_GE(serialized_data_.size(), kCachedMetaDataStart);
+ return serialized_data_.size() - kCachedMetaDataStart;
+ }
+
+ private:
+ CachedMetadata(const char* data, size_t);
+ CachedMetadata(uint32_t data_type_id, const char* data, size_t);
+
+ // Since the serialization format supports random access, storing it in
+ // serialized form avoids need for a copy during serialization.
+ Vector<char> serialized_data_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h b/chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h
new file mode 100644
index 00000000000..626544fddef
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h
@@ -0,0 +1,70 @@
+// Copyright 2015 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_PLATFORM_LOADER_FETCH_CACHED_METADATA_HANDLER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_CACHED_METADATA_HANDLER_H_
+
+#include <stdint.h>
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+class CachedMetadata;
+
+// Handler class for caching operations.
+class CachedMetadataHandler
+ : public GarbageCollectedFinalized<CachedMetadataHandler> {
+ public:
+ enum CacheType {
+ kSendToPlatform, // send cache data to blink::Platform::cacheMetadata
+ kCacheLocally // cache only in Resource's member variables
+ };
+
+ // Enum for marking serialized cached metadatas so that the deserializers
+ // do not conflict.
+ enum CachedMetadataType : uint32_t {
+ kSingleEntry, // the metadata is a single CachedMetadata entry
+ kSourceKeyedMap // the metadata is multiple CachedMetadata entries keyed by
+ // a source string.
+ };
+
+ virtual ~CachedMetadataHandler() = default;
+ virtual void Trace(blink::Visitor* visitor) {}
+
+ // Reset existing metadata, to allow setting new data.
+ virtual void ClearCachedMetadata(CacheType = kCacheLocally) = 0;
+
+ // Returns the encoding to which the cache is specific.
+ virtual String Encoding() const = 0;
+
+ virtual bool IsServedFromCacheStorage() const = 0;
+
+ protected:
+ CachedMetadataHandler() = default;
+};
+
+// A CachedMetadataHandler which stores one piece of metadata.
+class SingleCachedMetadataHandler : public CachedMetadataHandler {
+ public:
+ // Caches the given metadata in association with this resource and suggests
+ // that the platform persist it. The dataTypeID is a pseudo-randomly chosen
+ // identifier that is used to distinguish data generated by the caller.
+ virtual void SetCachedMetadata(uint32_t data_type_id,
+ const char*,
+ size_t,
+ CacheType = kSendToPlatform) = 0;
+
+ // Returns cached metadata of the given type associated with this resource.
+ // This cached metadata can be pruned at any time.
+ virtual scoped_refptr<CachedMetadata> GetCachedMetadata(
+ uint32_t data_type_id) const = 0;
+
+ protected:
+ SingleCachedMetadataHandler() = default;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_CACHED_METADATA_HANDLER_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.cc
new file mode 100644
index 00000000000..6a3d226f8d3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.cc
@@ -0,0 +1,150 @@
+// Copyright 2015 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/platform/loader/fetch/client_hints_preferences.h"
+
+#include "base/macros.h"
+#include "third_party/blink/public/common/client_hints/client_hints.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+#include "third_party/blink/renderer/platform/network/http_names.h"
+#include "third_party/blink/renderer/platform/network/http_parsers.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+
+namespace blink {
+
+namespace {
+
+void ParseAcceptChHeader(const String& header_value,
+ WebEnabledClientHints& enabled_hints) {
+ CommaDelimitedHeaderSet accept_client_hints_header;
+ ParseCommaDelimitedHeader(header_value, accept_client_hints_header);
+
+ for (size_t i = 0;
+ i < static_cast<int>(mojom::WebClientHintsType::kMaxValue) + 1; ++i) {
+ enabled_hints.SetIsEnabled(
+ static_cast<mojom::WebClientHintsType>(i),
+ accept_client_hints_header.Contains(kClientHintsHeaderMapping[i]));
+ }
+
+ enabled_hints.SetIsEnabled(
+ mojom::WebClientHintsType::kDeviceMemory,
+ enabled_hints.IsEnabled(mojom::WebClientHintsType::kDeviceMemory) &&
+ RuntimeEnabledFeatures::DeviceMemoryHeaderEnabled());
+
+ enabled_hints.SetIsEnabled(
+ mojom::WebClientHintsType::kRtt,
+ enabled_hints.IsEnabled(mojom::WebClientHintsType::kRtt) &&
+ RuntimeEnabledFeatures::NetInfoRttHeaderEnabled());
+
+ enabled_hints.SetIsEnabled(
+ mojom::WebClientHintsType::kDownlink,
+ enabled_hints.IsEnabled(mojom::WebClientHintsType::kDownlink) &&
+ RuntimeEnabledFeatures::NetInfoDownlinkHeaderEnabled());
+
+ enabled_hints.SetIsEnabled(
+ mojom::WebClientHintsType::kEct,
+ enabled_hints.IsEnabled(mojom::WebClientHintsType::kEct) &&
+ RuntimeEnabledFeatures::NetInfoEffectiveTypeHeaderEnabled());
+}
+
+} // namespace
+
+ClientHintsPreferences::ClientHintsPreferences() {
+ DCHECK_EQ(static_cast<size_t>(mojom::WebClientHintsType::kMaxValue) + 1,
+ kClientHintsHeaderMappingCount);
+}
+
+void ClientHintsPreferences::UpdateFrom(
+ const ClientHintsPreferences& preferences) {
+ for (size_t i = 0;
+ i < static_cast<int>(mojom::WebClientHintsType::kMaxValue) + 1; ++i) {
+ mojom::WebClientHintsType type = static_cast<mojom::WebClientHintsType>(i);
+ enabled_hints_.SetIsEnabled(type, preferences.ShouldSend(type));
+ }
+}
+
+void ClientHintsPreferences::UpdateFromAcceptClientHintsHeader(
+ const String& header_value,
+ const KURL& url,
+ Context* context) {
+ if (header_value.IsEmpty())
+ return;
+
+ // If the persistent client hint feature is enabled, then client hints
+ // should be allowed only on secure URLs.
+ if (blink::RuntimeEnabledFeatures::ClientHintsPersistentEnabled() &&
+ !IsClientHintsAllowed(url)) {
+ return;
+ }
+
+ WebEnabledClientHints new_enabled_types;
+
+ ParseAcceptChHeader(header_value, new_enabled_types);
+
+ for (size_t i = 0;
+ i < static_cast<int>(mojom::WebClientHintsType::kMaxValue) + 1; ++i) {
+ mojom::WebClientHintsType type = static_cast<mojom::WebClientHintsType>(i);
+ enabled_hints_.SetIsEnabled(type, enabled_hints_.IsEnabled(type) ||
+ new_enabled_types.IsEnabled(type));
+ }
+
+ if (context) {
+ for (size_t i = 0;
+ i < static_cast<int>(mojom::WebClientHintsType::kMaxValue) + 1; ++i) {
+ mojom::WebClientHintsType type =
+ static_cast<mojom::WebClientHintsType>(i);
+ if (enabled_hints_.IsEnabled(type))
+ context->CountClientHints(type);
+ }
+ }
+}
+
+// static
+void ClientHintsPreferences::UpdatePersistentHintsFromHeaders(
+ const ResourceResponse& response,
+ Context* context,
+ WebEnabledClientHints& enabled_hints,
+ TimeDelta* persist_duration) {
+ *persist_duration = base::TimeDelta();
+
+ if (response.WasCached())
+ return;
+
+ String accept_ch_header_value =
+ response.HttpHeaderField(HTTPNames::Accept_CH);
+ String accept_ch_lifetime_header_value =
+ response.HttpHeaderField(HTTPNames::Accept_CH_Lifetime);
+
+ if (!RuntimeEnabledFeatures::ClientHintsPersistentEnabled() ||
+ accept_ch_header_value.IsEmpty() ||
+ accept_ch_lifetime_header_value.IsEmpty()) {
+ return;
+ }
+
+ const KURL url = response.Url();
+ if (!IsClientHintsAllowed(url))
+ return;
+
+ bool conversion_ok = false;
+ int64_t persist_duration_seconds =
+ accept_ch_lifetime_header_value.ToInt64Strict(&conversion_ok);
+ if (!conversion_ok || persist_duration_seconds <= 0)
+ return;
+
+ *persist_duration = TimeDelta::FromSeconds(persist_duration_seconds);
+ if (context)
+ context->CountPersistentClientHintHeaders();
+
+ ParseAcceptChHeader(accept_ch_header_value, enabled_hints);
+}
+
+// static
+bool ClientHintsPreferences::IsClientHintsAllowed(const KURL& url) {
+ return (url.ProtocolIs("http") || url.ProtocolIs("https")) &&
+ (SecurityOrigin::IsSecure(url) ||
+ SecurityOrigin::Create(url)->IsLocalhost());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.h b/chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.h
new file mode 100644
index 00000000000..6900745d56e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.h
@@ -0,0 +1,77 @@
+// Copyright 2015 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_PLATFORM_LOADER_FETCH_CLIENT_HINTS_PREFERENCES_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_CLIENT_HINTS_PREFERENCES_H_
+
+#include "third_party/blink/public/platform/web_client_hints_type.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+class KURL;
+class ResourceResponse;
+
+// TODO (tbansal): Remove PLATFORM_EXPORT, and pass WebClientHintsType
+// everywhere.
+class PLATFORM_EXPORT ClientHintsPreferences {
+ DISALLOW_NEW();
+
+ public:
+ class Context {
+ public:
+ virtual void CountClientHints(mojom::WebClientHintsType) = 0;
+ virtual void CountPersistentClientHintHeaders() = 0;
+
+ protected:
+ virtual ~Context() = default;
+ };
+
+ ClientHintsPreferences();
+
+ void UpdateFrom(const ClientHintsPreferences&);
+
+ // Parses the client hints headers, and populates |this| with the client hint
+ // preferences. |url| is the URL of the resource whose response included the
+ // |header_value|. |context| may be null. If client hints are not allowed for
+ // |url|, then |this| would not be updated.
+ void UpdateFromAcceptClientHintsHeader(const String& header_value,
+ const KURL&,
+ Context*);
+
+ bool ShouldSend(mojom::WebClientHintsType type) const {
+ return enabled_hints_.IsEnabled(type);
+ }
+ void SetShouldSendForTesting(mojom::WebClientHintsType type) {
+ enabled_hints_.SetIsEnabled(type, true);
+ }
+
+ // Parses the client hints headers, and populates |enabled_hints| with the
+ // client hint preferences that should be persisted for |persist_duration|.
+ // |persist_duration| should be non-null.
+ // If there are no client hints that need to be persisted,
+ // |persist_duration| is not set, otherwise it is set to the duration for
+ // which the client hint preferences should be persisted.
+ // UpdatePersistentHintsFromHeaders may be called for all responses
+ // received (including subresources). |context| may be null.
+ static void UpdatePersistentHintsFromHeaders(
+ const ResourceResponse&,
+ Context*,
+ WebEnabledClientHints& enabled_hints,
+ TimeDelta* persist_duration);
+
+ // Returns true if client hints are allowed for the provided KURL. Client
+ // hints are allowed only on HTTP URLs that belong to secure contexts.
+ static bool IsClientHintsAllowed(const KURL&);
+
+ private:
+ WebEnabledClientHints enabled_hints_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences_test.cc
new file mode 100644
index 00000000000..dde2ec04ca4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences_test.cc
@@ -0,0 +1,158 @@
+// Copyright 2014 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/platform/loader/fetch/client_hints_preferences.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/web_runtime_features.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+#include "third_party/blink/renderer/platform/network/http_names.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+class ClientHintsPreferencesTest : public testing::Test {};
+
+TEST_F(ClientHintsPreferencesTest, BasicSecure) {
+ struct TestCase {
+ const char* header_value;
+ bool expectation_resource_width;
+ bool expectation_dpr;
+ bool expectation_viewport_width;
+ bool expectation_rtt;
+ bool expectation_downlink;
+ bool expectation_ect;
+ } cases[] = {
+ {"width, dpr, viewportWidth", true, true, false, false, false, false},
+ {"WiDtH, dPr, viewport-width, rtt, downlink, ect", true, true, true, true,
+ true, true},
+ {"WiDtH, dPr, viewport-width, rtt, downlink, effective-connection-type",
+ true, true, true, true, true, false},
+ {"WIDTH, DPR, VIWEPROT-Width", true, true, false, false, false, false},
+ {"VIewporT-Width, wutwut, width", true, false, true, false, false, false},
+ {"dprw", false, false, false, false, false, false},
+ {"DPRW", false, false, false, false, false, false},
+ };
+
+ for (const auto& test_case : cases) {
+ ClientHintsPreferences preferences;
+ const KURL kurl(String::FromUTF8("https://www.google.com/"));
+ preferences.UpdateFromAcceptClientHintsHeader(test_case.header_value, kurl,
+ nullptr);
+ EXPECT_EQ(
+ test_case.expectation_resource_width,
+ preferences.ShouldSend(mojom::WebClientHintsType::kResourceWidth));
+ EXPECT_EQ(test_case.expectation_dpr,
+ preferences.ShouldSend(mojom::WebClientHintsType::kDpr));
+ EXPECT_EQ(
+ test_case.expectation_viewport_width,
+ preferences.ShouldSend(mojom::WebClientHintsType::kViewportWidth));
+ EXPECT_EQ(test_case.expectation_rtt,
+ preferences.ShouldSend(mojom::WebClientHintsType::kRtt));
+ EXPECT_EQ(test_case.expectation_downlink,
+ preferences.ShouldSend(mojom::WebClientHintsType::kDownlink));
+ EXPECT_EQ(test_case.expectation_ect,
+ preferences.ShouldSend(mojom::WebClientHintsType::kEct));
+
+ // Calling UpdateFromAcceptClientHintsHeader with empty header should have
+ // no impact on client hint preferences.
+ preferences.UpdateFromAcceptClientHintsHeader("", kurl, nullptr);
+ EXPECT_EQ(
+ test_case.expectation_resource_width,
+ preferences.ShouldSend(mojom::WebClientHintsType::kResourceWidth));
+ EXPECT_EQ(test_case.expectation_dpr,
+ preferences.ShouldSend(mojom::WebClientHintsType::kDpr));
+ EXPECT_EQ(
+ test_case.expectation_viewport_width,
+ preferences.ShouldSend(mojom::WebClientHintsType::kViewportWidth));
+
+ // Calling UpdateFromAcceptClientHintsHeader with an invalid header should
+ // have no impact on client hint preferences.
+ preferences.UpdateFromAcceptClientHintsHeader("foobar", kurl, nullptr);
+ EXPECT_EQ(
+ test_case.expectation_resource_width,
+ preferences.ShouldSend(mojom::WebClientHintsType::kResourceWidth));
+ EXPECT_EQ(test_case.expectation_dpr,
+ preferences.ShouldSend(mojom::WebClientHintsType::kDpr));
+ EXPECT_EQ(
+ test_case.expectation_viewport_width,
+ preferences.ShouldSend(mojom::WebClientHintsType::kViewportWidth));
+ }
+}
+
+TEST_F(ClientHintsPreferencesTest, Insecure) {
+ for (const auto& use_secure_url : {false, true}) {
+ ClientHintsPreferences preferences;
+ const KURL kurl = use_secure_url
+ ? KURL(String::FromUTF8("https://www.google.com/"))
+ : KURL(String::FromUTF8("http://www.google.com/"));
+ preferences.UpdateFromAcceptClientHintsHeader("dpr", kurl, nullptr);
+ EXPECT_EQ(use_secure_url,
+ preferences.ShouldSend(mojom::WebClientHintsType::kDpr));
+ }
+}
+
+TEST_F(ClientHintsPreferencesTest, PersistentHints) {
+ struct TestCase {
+ bool enable_persistent_runtime_feature;
+ const char* accept_ch_header_value;
+ const char* accept_lifetime_header_value;
+ int64_t expect_persist_duration_seconds;
+ } test_cases[] = {
+ {true, "width, dpr, viewportWidth", "", 0},
+ {true, "width, dpr, viewportWidth", "-1000", 0},
+ {true, "width, dpr, viewportWidth", "1000s", 0},
+ {true, "width, dpr, viewportWidth", "1000.5", 0},
+ {false, "width, dpr, viewportWidth", "1000", 0},
+ {true, "width, dpr, rtt, downlink, ect", "1000", 1000},
+ };
+
+ for (const auto& test : test_cases) {
+ WebRuntimeFeatures::EnableClientHintsPersistent(
+ test.enable_persistent_runtime_feature);
+ WebEnabledClientHints enabled_types;
+ TimeDelta persist_duration;
+
+ const KURL kurl(String::FromUTF8("https://www.google.com/"));
+
+ ResourceResponse response(kurl);
+ response.SetHTTPHeaderField(HTTPNames::Accept_CH,
+ test.accept_ch_header_value);
+ response.SetHTTPHeaderField(HTTPNames::Accept_CH_Lifetime,
+ test.accept_lifetime_header_value);
+
+ ClientHintsPreferences::UpdatePersistentHintsFromHeaders(
+ response, nullptr, enabled_types, &persist_duration);
+ EXPECT_EQ(test.expect_persist_duration_seconds,
+ persist_duration.InSeconds());
+ if (test.expect_persist_duration_seconds > 0) {
+ EXPECT_FALSE(
+ enabled_types.IsEnabled(mojom::WebClientHintsType::kDeviceMemory));
+ EXPECT_TRUE(enabled_types.IsEnabled(mojom::WebClientHintsType::kDpr));
+ EXPECT_TRUE(
+ enabled_types.IsEnabled(mojom::WebClientHintsType::kResourceWidth));
+ EXPECT_FALSE(
+ enabled_types.IsEnabled(mojom::WebClientHintsType::kViewportWidth));
+ EXPECT_TRUE(enabled_types.IsEnabled(mojom::WebClientHintsType::kRtt));
+ EXPECT_TRUE(
+ enabled_types.IsEnabled(mojom::WebClientHintsType::kDownlink));
+ EXPECT_TRUE(enabled_types.IsEnabled(mojom::WebClientHintsType::kEct));
+ } else {
+ EXPECT_FALSE(
+ enabled_types.IsEnabled(mojom::WebClientHintsType::kDeviceMemory));
+ EXPECT_FALSE(enabled_types.IsEnabled(mojom::WebClientHintsType::kDpr));
+ EXPECT_FALSE(
+ enabled_types.IsEnabled(mojom::WebClientHintsType::kResourceWidth));
+ EXPECT_FALSE(
+ enabled_types.IsEnabled(mojom::WebClientHintsType::kViewportWidth));
+ EXPECT_FALSE(enabled_types.IsEnabled(mojom::WebClientHintsType::kRtt));
+ EXPECT_FALSE(
+ enabled_types.IsEnabled(mojom::WebClientHintsType::kDownlink));
+ EXPECT_FALSE(enabled_types.IsEnabled(mojom::WebClientHintsType::kEct));
+ }
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_context.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_context.cc
new file mode 100644
index 00000000000..ab5e1ae5900
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_context.cc
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_context.h"
+
+#include "third_party/blink/renderer/platform/PlatformProbeSink.h"
+#include "third_party/blink/renderer/platform/probe/PlatformTraceEventsAgent.h"
+
+namespace blink {
+
+FetchContext& FetchContext::NullInstance() {
+ return *(new FetchContext);
+}
+
+FetchContext::FetchContext() : platform_probe_sink_(new PlatformProbeSink) {
+ platform_probe_sink_->addPlatformTraceEventsAgent(
+ new PlatformTraceEventsAgent);
+}
+
+void FetchContext::Trace(blink::Visitor* visitor) {
+ visitor->Trace(platform_probe_sink_);
+}
+
+void FetchContext::DispatchDidChangeResourcePriority(unsigned long,
+ ResourceLoadPriority,
+ int) {}
+
+void FetchContext::AddAdditionalRequestHeaders(ResourceRequest&,
+ FetchResourceType) {}
+
+mojom::FetchCacheMode FetchContext::ResourceRequestCachePolicy(
+ const ResourceRequest&,
+ Resource::Type,
+ FetchParameters::DeferOption defer) const {
+ return mojom::FetchCacheMode::kDefault;
+}
+
+void FetchContext::PrepareRequest(ResourceRequest&, RedirectType) {}
+
+void FetchContext::DispatchWillSendRequest(unsigned long,
+ ResourceRequest&,
+ const ResourceResponse&,
+ Resource::Type,
+ const FetchInitiatorInfo&) {}
+
+void FetchContext::DispatchDidLoadResourceFromMemoryCache(
+ unsigned long,
+ const ResourceRequest&,
+ const ResourceResponse&) {}
+
+void FetchContext::DispatchDidReceiveResponse(
+ unsigned long,
+ const ResourceResponse&,
+ network::mojom::RequestContextFrameType FrameType,
+ WebURLRequest::RequestContext,
+ Resource*,
+ ResourceResponseType) {}
+
+void FetchContext::DispatchDidReceiveData(unsigned long, const char*, int) {}
+
+void FetchContext::DispatchDidReceiveEncodedData(unsigned long, int) {}
+
+void FetchContext::DispatchDidDownloadData(unsigned long, int, int) {}
+
+void FetchContext::DispatchDidDownloadToBlob(unsigned long identifier,
+ BlobDataHandle*) {}
+
+void FetchContext::DispatchDidFinishLoading(unsigned long,
+ double,
+ int64_t,
+ int64_t,
+ bool) {}
+
+void FetchContext::DispatchDidFail(const KURL&,
+ unsigned long,
+ const ResourceError&,
+ int64_t,
+ bool) {}
+
+void FetchContext::RecordLoadingActivity(
+ const ResourceRequest&,
+ Resource::Type,
+ const AtomicString& fetch_initiator_name) {}
+
+void FetchContext::DidLoadResource(Resource*) {}
+
+void FetchContext::AddResourceTiming(const ResourceTimingInfo&) {}
+
+void FetchContext::AddWarningConsoleMessage(const String&, LogSource) const {}
+
+void FetchContext::AddErrorConsoleMessage(const String&, LogSource) const {}
+
+void FetchContext::PopulateResourceRequest(
+ Resource::Type,
+ const ClientHintsPreferences&,
+ const FetchParameters::ResourceWidth&,
+ ResourceRequest&) {}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_context.h b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_context.h
new file mode 100644
index 00000000000..600b89d2900
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_context.h
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_FETCH_CONTEXT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_FETCH_CONTEXT_H_
+
+#include "services/network/public/mojom/request_context_frame_type.mojom-shared.h"
+#include "third_party/blink/public/platform/modules/fetch/fetch_api_request.mojom-shared.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_application_cache_host.h"
+#include "third_party/blink/public/platform/web_url_loader.h"
+#include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_info.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_load_priority.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+#include "third_party/blink/renderer/platform/network/content_security_policy_parsers.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
+#include "third_party/blink/renderer/platform/weborigin/security_violation_reporting_policy.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+
+namespace blink {
+
+class ClientHintsPreferences;
+class KURL;
+class MHTMLArchive;
+class PlatformProbeSink;
+class ResourceError;
+class ResourceResponse;
+class ResourceTimingInfo;
+
+enum FetchResourceType { kFetchMainResource, kFetchSubresource };
+
+// The FetchContext is an interface for performing context specific processing
+// in response to events in the ResourceFetcher. The ResourceFetcher or its job
+// class, ResourceLoader, may call the methods on a FetchContext.
+//
+// Any processing that depends on components outside platform/loader/fetch/
+// should be implemented on a subclass of this interface, and then exposed to
+// the ResourceFetcher via this interface.
+class PLATFORM_EXPORT FetchContext
+ : public GarbageCollectedFinalized<FetchContext> {
+ WTF_MAKE_NONCOPYABLE(FetchContext);
+
+ public:
+ // This enum corresponds to blink::MessageSource. We have this not to
+ // introduce any dependency to core/.
+ //
+ // Currently only kJSMessageSource is used, but not to impress readers that
+ // AddConsoleMessage() call from FetchContext() should always use it, which is
+ // not true, we ask users of the Add.*ConsoleMessage() methods to explicitly
+ // specify the MessageSource to use.
+ //
+ // Extend this when needed.
+ enum LogSource { kJSSource };
+
+ static FetchContext& NullInstance();
+
+ virtual ~FetchContext() = default;
+
+ virtual void Trace(blink::Visitor*);
+
+ virtual bool IsFrameFetchContext() { return false; }
+
+ virtual void AddAdditionalRequestHeaders(ResourceRequest&, FetchResourceType);
+
+ // Called when the ResourceFetcher observes a data: URI load that contains an
+ // octothorpe ('#') character. This is a temporary method to support an Intent
+ // to Deprecate for spec incompliant handling of '#' characters in data URIs.
+ //
+ // TODO(crbug.com/123004): Remove once we have enough data for the I2D.
+ virtual void RecordDataUriWithOctothorpe() {}
+
+ // Returns the cache policy for the resource. ResourceRequest is not passed as
+ // a const reference as a header needs to be added for doc.write blocking
+ // intervention.
+ virtual mojom::FetchCacheMode ResourceRequestCachePolicy(
+ const ResourceRequest&,
+ Resource::Type,
+ FetchParameters::DeferOption) const;
+
+ virtual void DispatchDidChangeResourcePriority(unsigned long identifier,
+ ResourceLoadPriority,
+ int intra_priority_value);
+
+ // This internally dispatches WebFrameClient::willSendRequest and hooks
+ // request interceptors like ServiceWorker and ApplicationCache.
+ // This may modify the request.
+ enum class RedirectType { kForRedirect, kNotForRedirect };
+ virtual void PrepareRequest(ResourceRequest&, RedirectType);
+
+ // The last callback before a request is actually sent to the browser process.
+ // TODO(https://crbug.com/632580): make this take const ResourceRequest&.
+ virtual void DispatchWillSendRequest(
+ unsigned long identifier,
+ ResourceRequest&,
+ const ResourceResponse& redirect_response,
+ Resource::Type,
+ const FetchInitiatorInfo& = FetchInitiatorInfo());
+ virtual void DispatchDidLoadResourceFromMemoryCache(unsigned long identifier,
+ const ResourceRequest&,
+ const ResourceResponse&);
+ enum class ResourceResponseType { kNotFromMemoryCache, kFromMemoryCache };
+ virtual void DispatchDidReceiveResponse(
+ unsigned long identifier,
+ const ResourceResponse&,
+ network::mojom::RequestContextFrameType,
+ WebURLRequest::RequestContext,
+ Resource*,
+ ResourceResponseType);
+ virtual void DispatchDidReceiveData(unsigned long identifier,
+ const char* data,
+ int data_length);
+ virtual void DispatchDidReceiveEncodedData(unsigned long identifier,
+ int encoded_data_length);
+ virtual void DispatchDidDownloadData(unsigned long identifier,
+ int data_length,
+ int encoded_data_length);
+ virtual void DispatchDidDownloadToBlob(unsigned long identifier,
+ BlobDataHandle*);
+ virtual void DispatchDidFinishLoading(unsigned long identifier,
+ double finish_time,
+ int64_t encoded_data_length,
+ int64_t decoded_body_length,
+ bool blocked_cross_site_document);
+ virtual void DispatchDidFail(const KURL&,
+ unsigned long identifier,
+ const ResourceError&,
+ int64_t encoded_data_length,
+ bool is_internal_request);
+
+ virtual bool ShouldLoadNewResource(Resource::Type) const { return false; }
+
+ // Called when a resource load is first requested, which may not be when the
+ // load actually begins.
+ virtual void RecordLoadingActivity(const ResourceRequest&,
+ Resource::Type,
+ const AtomicString& fetch_initiator_name);
+
+ virtual void DidLoadResource(Resource*);
+
+ virtual void AddResourceTiming(const ResourceTimingInfo&);
+ virtual bool AllowImage(bool, const KURL&) const { return false; }
+ virtual ResourceRequestBlockedReason CanRequest(
+ Resource::Type,
+ const ResourceRequest&,
+ const KURL&,
+ const ResourceLoaderOptions&,
+ SecurityViolationReportingPolicy,
+ FetchParameters::OriginRestriction,
+ ResourceRequest::RedirectStatus) const {
+ return ResourceRequestBlockedReason::kOther;
+ }
+ virtual ResourceRequestBlockedReason CheckCSPForRequest(
+ WebURLRequest::RequestContext,
+ const KURL&,
+ const ResourceLoaderOptions&,
+ SecurityViolationReportingPolicy,
+ ResourceRequest::RedirectStatus) const {
+ return ResourceRequestBlockedReason::kOther;
+ }
+ virtual ResourceRequestBlockedReason CheckResponseNosniff(
+ WebURLRequest::RequestContext,
+ const ResourceResponse&) const {
+ return ResourceRequestBlockedReason::kOther;
+ }
+
+ virtual bool IsControlledByServiceWorker() const { return false; }
+ virtual int64_t ServiceWorkerID() const { return -1; }
+ virtual int ApplicationCacheHostID() const {
+ return WebApplicationCacheHost::kAppCacheNoHostId;
+ }
+
+ virtual bool IsMainFrame() const { return true; }
+ virtual bool DefersLoading() const { return false; }
+ virtual bool IsLoadComplete() const { return false; }
+ virtual bool UpdateTimingInfoForIFrameNavigation(ResourceTimingInfo*) {
+ return false;
+ }
+
+ virtual void AddWarningConsoleMessage(const String&, LogSource) const;
+ virtual void AddErrorConsoleMessage(const String&, LogSource) const;
+
+ virtual const SecurityOrigin* GetSecurityOrigin() const { return nullptr; }
+
+ // Populates the ResourceRequest using the given values and information
+ // stored in the FetchContext implementation. Used by ResourceFetcher to
+ // prepare a ResourceRequest instance at the start of resource loading.
+ virtual void PopulateResourceRequest(Resource::Type,
+ const ClientHintsPreferences&,
+ const FetchParameters::ResourceWidth&,
+ ResourceRequest&);
+
+ virtual MHTMLArchive* Archive() const { return nullptr; }
+
+ PlatformProbeSink* GetPlatformProbeSink() const {
+ return platform_probe_sink_;
+ }
+
+ virtual std::unique_ptr<WebURLLoader> CreateURLLoader(
+ const ResourceRequest&,
+ scoped_refptr<base::SingleThreadTaskRunner>,
+ const ResourceLoaderOptions&) {
+ NOTREACHED();
+ return nullptr;
+ }
+
+ // Returns the initial throttling policy used by the associated
+ // ResourceLoadScheduler.
+ virtual ResourceLoadScheduler::ThrottlingPolicy InitialLoadThrottlingPolicy()
+ const {
+ return ResourceLoadScheduler::ThrottlingPolicy::kNormal;
+ }
+
+ virtual bool IsDetached() const { return false; }
+
+ // Obtains FrameScheduler instance that is used in the attached frame.
+ // May return nullptr if a frame is not attached or detached.
+ virtual FrameScheduler* GetFrameScheduler() const { return nullptr; }
+
+ // Returns a task runner intended for loading tasks. Should work even in a
+ // worker context, where FrameScheduler doesn't exist, but the returned
+ // base::SingleThreadTaskRunner will not work after the context detaches
+ // (after Detach() is called, this will return a generic timer suitable for
+ // post-detach actions like keepalive requests.
+ virtual scoped_refptr<base::SingleThreadTaskRunner> GetLoadingTaskRunner() {
+ return Platform::Current()->CurrentThread()->GetTaskRunner();
+ }
+
+ // Called when the underlying context is detached. Note that some
+ // FetchContexts continue working after detached (e.g., for fetch() operations
+ // with "keepalive" specified).
+ // Returns a "detached" fetch context which can be null.
+ virtual FetchContext* Detach() { return nullptr; }
+
+ // Returns the updated priority of the resource based on the experiments that
+ // may be currently enabled.
+ virtual ResourceLoadPriority ModifyPriorityForExperiments(
+ ResourceLoadPriority priority) const {
+ return priority;
+ }
+
+ // Returns if the |resource_url| is identified as ad.
+ virtual bool IsAdResource(
+ const KURL& resource_url,
+ Resource::Type type,
+ WebURLRequest::RequestContext request_context) const {
+ return false;
+ }
+
+ protected:
+ FetchContext();
+
+ private:
+ Member<PlatformProbeSink> platform_probe_sink_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_initiator_info.h b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_initiator_info.h
new file mode 100644
index 00000000000..8d471afcb18
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_initiator_info.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2013 Google, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_FETCH_INITIATOR_INFO_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_FETCH_INITIATOR_INFO_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_position.h"
+
+namespace blink {
+
+struct FetchInitiatorInfo {
+ DISALLOW_NEW();
+ FetchInitiatorInfo()
+ : name(),
+ position(TextPosition::BelowRangePosition()),
+ start_time(0.0),
+ is_link_preload(false) {}
+
+ // ATTENTION: When adding members, update CrossThreadFetchInitiatorInfoData,
+ // too.
+ AtomicString name;
+ TextPosition position;
+ double start_time;
+ bool is_link_preload;
+ String imported_module_referrer;
+};
+
+// Encode AtomicString as String to cross threads.
+struct CrossThreadFetchInitiatorInfoData {
+ DISALLOW_NEW();
+ explicit CrossThreadFetchInitiatorInfoData(const FetchInitiatorInfo& info)
+ : name(info.name.GetString().IsolatedCopy()),
+ position(info.position),
+ start_time(info.start_time),
+ is_link_preload(info.is_link_preload),
+ imported_module_referrer(info.imported_module_referrer.IsolatedCopy()) {
+ }
+
+ operator FetchInitiatorInfo() const {
+ FetchInitiatorInfo info;
+ info.name = AtomicString(name);
+ info.position = position;
+ info.start_time = start_time;
+ info.is_link_preload = is_link_preload;
+ info.imported_module_referrer = imported_module_referrer;
+ return info;
+ }
+
+ String name;
+ TextPosition position;
+ double start_time;
+ bool is_link_preload;
+ String imported_module_referrer;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_initiator_type_names.json5 b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_initiator_type_names.json5
new file mode 100644
index 00000000000..d2d2aac3a55
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_initiator_type_names.json5
@@ -0,0 +1,22 @@
+{
+ metadata: {
+ namespace: "FetchInitiatorType",
+ export: "PLATFORM_EXPORT",
+ },
+
+ data: [
+ "beacon",
+ "css",
+ "document",
+ "icon",
+ "internal",
+ "link",
+ "ping",
+ "processinginstruction",
+ "texttrack",
+ "uacss",
+ "violationreport",
+ "xml",
+ "xmlhttprequest",
+ ],
+}
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.cc
new file mode 100644
index 00000000000..88a956d43af
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.cc
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2012 Google, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
+
+#include <memory>
+
+#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+
+namespace blink {
+
+FetchParameters::FetchParameters(const ResourceRequest& resource_request)
+ : resource_request_(resource_request),
+ decoder_options_(TextResourceDecoderOptions::kPlainTextContent),
+ speculative_preload_type_(SpeculativePreloadType::kNotSpeculative),
+ preload_discovery_time_(0.0),
+ defer_(kNoDefer),
+ origin_restriction_(kUseDefaultOriginRestrictionForType),
+ placeholder_image_request_type_(kDisallowPlaceholder) {}
+
+FetchParameters::FetchParameters(
+ std::unique_ptr<CrossThreadFetchParametersData> data)
+ : resource_request_(data->resource_request.get()),
+ decoder_options_(data->decoder_options),
+ options_(data->options),
+ speculative_preload_type_(data->speculative_preload_type),
+ preload_discovery_time_(data->preload_discovery_time),
+ defer_(data->defer),
+ origin_restriction_(data->origin_restriction),
+ resource_width_(data->resource_width),
+ client_hint_preferences_(data->client_hint_preferences),
+ placeholder_image_request_type_(data->placeholder_image_request_type) {}
+
+FetchParameters::FetchParameters(const ResourceRequest& resource_request,
+ const ResourceLoaderOptions& options)
+ : resource_request_(resource_request),
+ decoder_options_(TextResourceDecoderOptions::kPlainTextContent),
+ options_(options),
+ speculative_preload_type_(SpeculativePreloadType::kNotSpeculative),
+ preload_discovery_time_(0.0),
+ defer_(kNoDefer),
+ origin_restriction_(kUseDefaultOriginRestrictionForType),
+ placeholder_image_request_type_(kDisallowPlaceholder) {}
+
+FetchParameters::~FetchParameters() = default;
+
+void FetchParameters::SetCrossOriginAccessControl(
+ const SecurityOrigin* origin,
+ CrossOriginAttributeValue cross_origin) {
+ switch (cross_origin) {
+ case kCrossOriginAttributeNotSet:
+ NOTREACHED();
+ break;
+ case kCrossOriginAttributeAnonymous:
+ SetCrossOriginAccessControl(
+ origin, network::mojom::FetchCredentialsMode::kSameOrigin);
+ break;
+ case kCrossOriginAttributeUseCredentials:
+ SetCrossOriginAccessControl(
+ origin, network::mojom::FetchCredentialsMode::kInclude);
+ break;
+ }
+}
+
+void FetchParameters::SetCrossOriginAccessControl(
+ const SecurityOrigin* origin,
+ network::mojom::FetchCredentialsMode credentials_mode) {
+ // Currently FetchParametersMode is only used when the request goes to
+ // Service Worker.
+ resource_request_.SetFetchRequestMode(
+ network::mojom::FetchRequestMode::kCORS);
+ resource_request_.SetFetchCredentialsMode(credentials_mode);
+
+ options_.security_origin = origin;
+
+ // TODO: Credentials should be removed only when the request is cross origin.
+ resource_request_.RemoveUserAndPassFromURL();
+
+ if (origin)
+ resource_request_.SetHTTPOrigin(origin);
+}
+
+void FetchParameters::SetResourceWidth(ResourceWidth resource_width) {
+ if (resource_width.is_set) {
+ resource_width_.width = resource_width.width;
+ resource_width_.is_set = true;
+ }
+}
+
+void FetchParameters::SetSpeculativePreloadType(
+ SpeculativePreloadType speculative_preload_type,
+ double discovery_time) {
+ speculative_preload_type_ = speculative_preload_type;
+ preload_discovery_time_ = discovery_time;
+}
+
+void FetchParameters::MakeSynchronous() {
+ // Synchronous requests should always be max priority, lest they hang the
+ // renderer.
+ resource_request_.SetPriority(ResourceLoadPriority::kHighest);
+ if (resource_request_.TimeoutInterval() == INT_MAX) {
+ resource_request_.SetTimeoutInterval(10);
+ }
+ // Skip ServiceWorker for synchronous loads from the main thread to avoid
+ // deadlocks.
+ if (IsMainThread())
+ resource_request_.SetSkipServiceWorker(true);
+ options_.synchronous_policy = kRequestSynchronously;
+}
+
+void FetchParameters::SetAllowImagePlaceholder() {
+ DCHECK_EQ(kDisallowPlaceholder, placeholder_image_request_type_);
+ if (!resource_request_.Url().ProtocolIsInHTTPFamily() ||
+ resource_request_.HttpMethod() != "GET" ||
+ !resource_request_.HttpHeaderField("range").IsNull()) {
+ // Make sure that the request isn't marked as using Client Lo-Fi, since
+ // without loading an image placeholder, Client Lo-Fi isn't really in use.
+ resource_request_.SetPreviewsState(resource_request_.GetPreviewsState() &
+ ~(WebURLRequest::kClientLoFiOn));
+ return;
+ }
+
+ placeholder_image_request_type_ = kAllowPlaceholder;
+
+ // Fetch the first few bytes of the image. This number is tuned to both (a)
+ // likely capture the entire image for small images and (b) likely contain
+ // the dimensions for larger images.
+ // TODO(sclittle): Calculate the optimal value for this number.
+ resource_request_.SetHTTPHeaderField("range", "bytes=0-2047");
+
+ // TODO(sclittle): Indicate somehow (e.g. through a new request bit) to the
+ // embedder that it should return the full resource if the entire resource is
+ // fresh in the cache.
+}
+
+std::unique_ptr<CrossThreadFetchParametersData> FetchParameters::CopyData()
+ const {
+ auto data = std::make_unique<CrossThreadFetchParametersData>();
+ data->resource_request = resource_request_.CopyData();
+ data->decoder_options = decoder_options_;
+ data->options = CrossThreadResourceLoaderOptionsData(options_);
+ data->speculative_preload_type = speculative_preload_type_;
+ data->preload_discovery_time = preload_discovery_time_;
+ data->defer = defer_;
+ data->origin_restriction = origin_restriction_;
+ data->resource_width = resource_width_;
+ data->client_hint_preferences = client_hint_preferences_;
+ data->placeholder_image_request_type = placeholder_image_request_type_;
+ return data;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h
new file mode 100644
index 00000000000..4ed9aba1b11
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2012 Google, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_FETCH_PARAMETERS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_FETCH_PARAMETERS_H_
+
+#include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/renderer/platform/cross_origin_attribute_value.h"
+#include "third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.h"
+#include "third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+#include "third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
+
+namespace blink {
+
+class SecurityOrigin;
+struct CrossThreadFetchParametersData;
+
+// A FetchParameters is a "parameter object" for
+// ResourceFetcher::requestResource to avoid the method having too many
+// arguments.
+//
+// There are cases where we need to copy a FetchParameters across threads, and
+// CrossThreadFetchParametersData is a struct for the purpose. When you add a
+// member variable to this class, do not forget to add the corresponding
+// one in CrossThreadFetchParametersData and write copying logic.
+class PLATFORM_EXPORT FetchParameters {
+ DISALLOW_NEW();
+
+ public:
+ enum DeferOption { kNoDefer, kLazyLoad, kIdleLoad };
+ enum class SpeculativePreloadType {
+ kNotSpeculative,
+ kInDocument, // The request was discovered in the main document
+ kInserted // The request was discovered in a document.write()
+ };
+ enum OriginRestriction {
+ kUseDefaultOriginRestrictionForType,
+ kRestrictToSameOrigin,
+ kNoOriginRestriction
+ };
+ enum PlaceholderImageRequestType {
+ kDisallowPlaceholder = 0, // The requested image must not be a placeholder.
+ kAllowPlaceholder, // The image is allowed to be a placeholder.
+ };
+ struct ResourceWidth {
+ DISALLOW_NEW();
+ float width;
+ bool is_set;
+
+ ResourceWidth() : width(0), is_set(false) {}
+ };
+
+ explicit FetchParameters(const ResourceRequest&);
+ explicit FetchParameters(std::unique_ptr<CrossThreadFetchParametersData>);
+ FetchParameters(const ResourceRequest&, const ResourceLoaderOptions&);
+ ~FetchParameters();
+
+ ResourceRequest& MutableResourceRequest() { return resource_request_; }
+ const ResourceRequest& GetResourceRequest() const {
+ return resource_request_;
+ }
+ const KURL& Url() const { return resource_request_.Url(); }
+
+ void SetRequestContext(WebURLRequest::RequestContext context) {
+ resource_request_.SetRequestContext(context);
+ }
+
+ const TextResourceDecoderOptions& DecoderOptions() const {
+ return decoder_options_;
+ }
+ void SetDecoderOptions(const TextResourceDecoderOptions& decoder_options) {
+ decoder_options_ = decoder_options;
+ }
+ void OverrideContentType(
+ TextResourceDecoderOptions::ContentType content_type) {
+ decoder_options_.OverrideContentType(content_type);
+ }
+ void SetCharset(const WTF::TextEncoding& charset) {
+ SetDecoderOptions(TextResourceDecoderOptions(
+ TextResourceDecoderOptions::kPlainTextContent, charset));
+ }
+
+ ResourceLoaderOptions& MutableOptions() { return options_; }
+ const ResourceLoaderOptions& Options() const { return options_; }
+
+ DeferOption Defer() const { return defer_; }
+ void SetDefer(DeferOption defer) { defer_ = defer; }
+
+ ResourceWidth GetResourceWidth() const { return resource_width_; }
+ void SetResourceWidth(ResourceWidth);
+
+ ClientHintsPreferences& GetClientHintsPreferences() {
+ return client_hint_preferences_;
+ }
+
+ bool IsSpeculativePreload() const {
+ return speculative_preload_type_ != SpeculativePreloadType::kNotSpeculative;
+ }
+ SpeculativePreloadType GetSpeculativePreloadType() const {
+ return speculative_preload_type_;
+ }
+ void SetSpeculativePreloadType(SpeculativePreloadType,
+ double discovery_time = 0);
+
+ double PreloadDiscoveryTime() const { return preload_discovery_time_; }
+
+ bool IsLinkPreload() const { return options_.initiator_info.is_link_preload; }
+ void SetLinkPreload(bool is_link_preload) {
+ options_.initiator_info.is_link_preload = is_link_preload;
+ }
+
+ void SetContentSecurityCheck(
+ ContentSecurityPolicyDisposition content_security_policy_option) {
+ options_.content_security_policy_option = content_security_policy_option;
+ }
+ // Configures the request to use the "cors" mode and the credentials mode
+ // specified by the crossOrigin attribute.
+ void SetCrossOriginAccessControl(const SecurityOrigin*,
+ CrossOriginAttributeValue);
+ // Configures the request to use the "cors" mode and the specified
+ // credentials mode.
+ void SetCrossOriginAccessControl(const SecurityOrigin*,
+ network::mojom::FetchCredentialsMode);
+ OriginRestriction GetOriginRestriction() const { return origin_restriction_; }
+ void SetOriginRestriction(OriginRestriction restriction) {
+ origin_restriction_ = restriction;
+ }
+ const IntegrityMetadataSet IntegrityMetadata() const {
+ return options_.integrity_metadata;
+ }
+ void SetIntegrityMetadata(const IntegrityMetadataSet& metadata) {
+ options_.integrity_metadata = metadata;
+ }
+
+ String ContentSecurityPolicyNonce() const {
+ return options_.content_security_policy_nonce;
+ }
+ void SetContentSecurityPolicyNonce(const String& nonce) {
+ options_.content_security_policy_nonce = nonce;
+ }
+
+ void SetParserDisposition(ParserDisposition parser_disposition) {
+ options_.parser_disposition = parser_disposition;
+ }
+
+ void SetCacheAwareLoadingEnabled(
+ CacheAwareLoadingEnabled cache_aware_loading_enabled) {
+ options_.cache_aware_loading_enabled = cache_aware_loading_enabled;
+ }
+
+ void MakeSynchronous();
+
+ PlaceholderImageRequestType GetPlaceholderImageRequestType() const {
+ return placeholder_image_request_type_;
+ }
+
+ // Configures the request to load an image placeholder if the request is
+ // eligible (e.g. the url's protocol is HTTP, etc.). If this request is
+ // non-eligible, this method doesn't modify the ResourceRequest. Calling this
+ // method sets m_placeholderImageRequestType to the appropriate value.
+ void SetAllowImagePlaceholder();
+
+ // Gets a copy of the data suitable for passing to another thread.
+ std::unique_ptr<CrossThreadFetchParametersData> CopyData() const;
+
+ private:
+ ResourceRequest resource_request_;
+ // |decoder_options_|'s ContentType is set to |kPlainTextContent| in
+ // FetchParameters but is later overridden by ResourceFactory::ContentType()
+ // in ResourceFetcher::PrepareRequest() before actual use.
+ TextResourceDecoderOptions decoder_options_;
+ ResourceLoaderOptions options_;
+ SpeculativePreloadType speculative_preload_type_;
+ double preload_discovery_time_;
+ DeferOption defer_;
+ OriginRestriction origin_restriction_;
+ ResourceWidth resource_width_;
+ ClientHintsPreferences client_hint_preferences_;
+ PlaceholderImageRequestType placeholder_image_request_type_;
+};
+
+// This class is needed to copy a FetchParameters across threads, because it
+// has some members which cannot be transferred across threads (AtomicString
+// for example).
+// There are some rules / restrictions:
+// - This struct cannot contain an object that cannot be transferred across
+// threads (e.g., AtomicString)
+// - Non-simple members need explicit copying (e.g., String::IsolatedCopy,
+// KURL::Copy) rather than the copy constructor or the assignment operator.
+struct CrossThreadFetchParametersData {
+ WTF_MAKE_NONCOPYABLE(CrossThreadFetchParametersData);
+ USING_FAST_MALLOC(CrossThreadFetchParametersData);
+
+ public:
+ CrossThreadFetchParametersData()
+ : decoder_options(TextResourceDecoderOptions::kPlainTextContent),
+ options(ResourceLoaderOptions()) {}
+
+ std::unique_ptr<CrossThreadResourceRequestData> resource_request;
+ TextResourceDecoderOptions decoder_options;
+ CrossThreadResourceLoaderOptionsData options;
+ FetchParameters::SpeculativePreloadType speculative_preload_type;
+ double preload_discovery_time;
+ FetchParameters::DeferOption defer;
+ FetchParameters::OriginRestriction origin_restriction;
+ FetchParameters::ResourceWidth resource_width;
+ ClientHintsPreferences client_hint_preferences;
+ FetchParameters::PlaceholderImageRequestType placeholder_image_request_type;
+};
+
+template <>
+struct CrossThreadCopier<FetchParameters> {
+ STATIC_ONLY(CrossThreadCopier);
+ using Type =
+ WTF::PassedWrapper<std::unique_ptr<CrossThreadFetchParametersData>>;
+ static Type Copy(const FetchParameters& fetch_params) {
+ return WTF::Passed(fetch_params.CopyData());
+ }
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_utils.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_utils.cc
new file mode 100644
index 00000000000..643ed618ac3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_utils.cc
@@ -0,0 +1,97 @@
+// Copyright 2014 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/platform/loader/fetch/fetch_utils.h"
+
+#include "services/network/public/cpp/cors/cors.h"
+#include "third_party/blink/renderer/platform/loader/cors/cors.h"
+#include "third_party/blink/renderer/platform/network/http_header_map.h"
+#include "third_party/blink/renderer/platform/network/http_names.h"
+#include "third_party/blink/renderer/platform/network/http_parsers.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/threading.h"
+
+namespace blink {
+
+namespace {
+
+bool IsHTTPWhitespace(UChar chr) {
+ return chr == ' ' || chr == '\n' || chr == '\t' || chr == '\r';
+}
+
+} // namespace
+
+bool FetchUtils::IsForbiddenMethod(const String& method) {
+ // http://fetch.spec.whatwg.org/#forbidden-method
+ // "A forbidden method is a method that is a byte case-insensitive match"
+ // for one of `CONNECT`, `TRACE`, and `TRACK`."
+ return EqualIgnoringASCIICase(method, "TRACE") ||
+ EqualIgnoringASCIICase(method, "TRACK") ||
+ EqualIgnoringASCIICase(method, "CONNECT");
+}
+
+bool FetchUtils::IsForbiddenHeaderName(const String& name) {
+ const CString utf8_name = name.Utf8();
+ return network::cors::IsForbiddenHeader(
+ std::string(utf8_name.data(), utf8_name.length()));
+}
+
+bool FetchUtils::IsForbiddenResponseHeaderName(const String& name) {
+ // http://fetch.spec.whatwg.org/#forbidden-response-header-name
+ // "A forbidden response header name is a header name that is one of:
+ // `Set-Cookie`, `Set-Cookie2`"
+
+ return EqualIgnoringASCIICase(name, "set-cookie") ||
+ EqualIgnoringASCIICase(name, "set-cookie2");
+}
+
+AtomicString FetchUtils::NormalizeMethod(const AtomicString& method) {
+ // https://fetch.spec.whatwg.org/#concept-method-normalize
+
+ // We place GET and POST first because they are more commonly used than
+ // others.
+ const char* const kMethods[] = {
+ "GET", "POST", "DELETE", "HEAD", "OPTIONS", "PUT",
+ };
+
+ for (auto* const known : kMethods) {
+ if (EqualIgnoringASCIICase(method, known)) {
+ // Don't bother allocating a new string if it's already all
+ // uppercase.
+ return method == known ? method : known;
+ }
+ }
+ return method;
+}
+
+String FetchUtils::NormalizeHeaderValue(const String& value) {
+ // https://fetch.spec.whatwg.org/#concept-header-value-normalize
+ // Strip leading and trailing whitespace from header value.
+ // HTTP whitespace bytes are 0x09, 0x0A, 0x0D, and 0x20.
+
+ return value.StripWhiteSpace(IsHTTPWhitespace);
+}
+
+bool FetchUtils::ContainsOnlyCORSSafelistedHeaders(
+ const HTTPHeaderMap& header_map) {
+ for (const auto& header : header_map) {
+ if (!CORS::IsCORSSafelistedHeader(header.key, header.value))
+ return false;
+ }
+ return true;
+}
+
+bool FetchUtils::ContainsOnlyCORSSafelistedOrForbiddenHeaders(
+ const HTTPHeaderMap& header_map) {
+ for (const auto& header : header_map) {
+ if (!CORS::IsCORSSafelistedHeader(header.key, header.value) &&
+ !IsForbiddenHeaderName(header.key))
+ return false;
+ }
+ return true;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_utils.h b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_utils.h
new file mode 100644
index 00000000000..991ffcce318
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_utils.h
@@ -0,0 +1,38 @@
+// Copyright 2014 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_PLATFORM_LOADER_FETCH_FETCH_UTILS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_FETCH_UTILS_H_
+
+#include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+class HTTPHeaderMap;
+
+class PLATFORM_EXPORT FetchUtils {
+ STATIC_ONLY(FetchUtils);
+
+ public:
+ static bool IsForbiddenMethod(const String& method);
+ static bool IsForbiddenHeaderName(const String& name);
+ static bool IsForbiddenResponseHeaderName(const String& name);
+ static AtomicString NormalizeMethod(const AtomicString& method);
+ static String NormalizeHeaderValue(const String& value);
+ static bool ContainsOnlyCORSSafelistedHeaders(const HTTPHeaderMap&);
+ static bool ContainsOnlyCORSSafelistedOrForbiddenHeaders(
+ const HTTPHeaderMap&);
+
+ // https://fetch.spec.whatwg.org/#ok-status aka a successful 2xx status
+ // code, https://tools.ietf.org/html/rfc7231#section-6.3 . We opt to use
+ // the Fetch term in naming the predicate.
+ static bool IsOkStatus(int status) { return status >= 200 && status < 300; }
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_utils_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_utils_test.cc
new file mode 100644
index 00000000000..c7619245046
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_utils_test.cc
@@ -0,0 +1,40 @@
+// Copyright 2015 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/platform/loader/fetch/fetch_utils.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+namespace {
+
+TEST(FetchUtilsTest, NormalizeHeaderValue) {
+ EXPECT_EQ("t", FetchUtils::NormalizeHeaderValue(" t"));
+ EXPECT_EQ("t", FetchUtils::NormalizeHeaderValue("t "));
+ EXPECT_EQ("t", FetchUtils::NormalizeHeaderValue(" t "));
+ EXPECT_EQ("test", FetchUtils::NormalizeHeaderValue("test\r"));
+ EXPECT_EQ("test", FetchUtils::NormalizeHeaderValue("test\n"));
+ EXPECT_EQ("test", FetchUtils::NormalizeHeaderValue("test\r\n"));
+ EXPECT_EQ("test", FetchUtils::NormalizeHeaderValue("test\t"));
+ EXPECT_EQ("t t", FetchUtils::NormalizeHeaderValue("t t"));
+ EXPECT_EQ("t\tt", FetchUtils::NormalizeHeaderValue("t\tt"));
+ EXPECT_EQ("t\rt", FetchUtils::NormalizeHeaderValue("t\rt"));
+ EXPECT_EQ("t\nt", FetchUtils::NormalizeHeaderValue("t\nt"));
+ EXPECT_EQ("t\r\nt", FetchUtils::NormalizeHeaderValue("t\r\nt"));
+ EXPECT_EQ("test", FetchUtils::NormalizeHeaderValue("\rtest"));
+ EXPECT_EQ("test", FetchUtils::NormalizeHeaderValue("\ntest"));
+ EXPECT_EQ("test", FetchUtils::NormalizeHeaderValue("\r\ntest"));
+ EXPECT_EQ("test", FetchUtils::NormalizeHeaderValue("\ttest"));
+ EXPECT_EQ("", FetchUtils::NormalizeHeaderValue(""));
+ EXPECT_EQ("", FetchUtils::NormalizeHeaderValue(" "));
+ EXPECT_EQ("", FetchUtils::NormalizeHeaderValue("\r\n\r\n\r\n"));
+ EXPECT_EQ("\xd0\xa1", FetchUtils::NormalizeHeaderValue("\xd0\xa1"));
+ EXPECT_EQ("test", FetchUtils::NormalizeHeaderValue("test"));
+}
+
+} // namespace
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/integrity_metadata.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/integrity_metadata.cc
new file mode 100644
index 00000000000..864e5db6a10
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/integrity_metadata.cc
@@ -0,0 +1,33 @@
+// Copyright 2015 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/platform/loader/fetch/integrity_metadata.h"
+
+namespace blink {
+
+IntegrityMetadata::IntegrityMetadata(WTF::String digest,
+ IntegrityAlgorithm algorithm)
+ : digest_(digest), algorithm_(algorithm) {}
+
+IntegrityMetadata::IntegrityMetadata(IntegrityMetadataPair pair)
+ : digest_(pair.first), algorithm_(pair.second) {}
+
+IntegrityMetadataPair IntegrityMetadata::ToPair() const {
+ return IntegrityMetadataPair(digest_, algorithm_);
+}
+
+bool IntegrityMetadata::SetsEqual(const IntegrityMetadataSet& set1,
+ const IntegrityMetadataSet& set2) {
+ if (set1.size() != set2.size())
+ return false;
+
+ for (const IntegrityMetadataPair& metadata : set1) {
+ if (!set2.Contains(metadata))
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h b/chromium/third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h
new file mode 100644
index 00000000000..5854670d344
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h
@@ -0,0 +1,69 @@
+// Copyright 2015 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_PLATFORM_LOADER_FETCH_INTEGRITY_METADATA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_INTEGRITY_METADATA_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/string_hasher.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class IntegrityMetadata;
+enum class IntegrityAlgorithm : uint8_t;
+
+using IntegrityMetadataPair = std::pair<String, IntegrityAlgorithm>;
+using IntegrityMetadataSet = WTF::HashSet<IntegrityMetadataPair>;
+
+class PLATFORM_EXPORT IntegrityMetadata {
+ public:
+ IntegrityMetadata() = default;
+ IntegrityMetadata(String digest, IntegrityAlgorithm);
+ IntegrityMetadata(IntegrityMetadataPair);
+
+ String Digest() const { return digest_; }
+ void SetDigest(const String& digest) { digest_ = digest; }
+ IntegrityAlgorithm Algorithm() const { return algorithm_; }
+ void SetAlgorithm(IntegrityAlgorithm algorithm) { algorithm_ = algorithm; }
+
+ IntegrityMetadataPair ToPair() const;
+
+ static bool SetsEqual(const IntegrityMetadataSet& set1,
+ const IntegrityMetadataSet& set2);
+
+ private:
+ String digest_;
+ IntegrityAlgorithm algorithm_;
+};
+
+enum class ResourceIntegrityDisposition : uint8_t {
+ kNotChecked = 0,
+ kFailed,
+ kPassed
+};
+
+enum class IntegrityAlgorithm : uint8_t { kSha256, kSha384, kSha512, kEd25519 };
+
+} // namespace blink
+
+namespace WTF {
+
+template <>
+struct DefaultHash<blink::IntegrityAlgorithm> {
+ STATIC_ONLY(DefaultHash);
+ typedef IntHash<blink::IntegrityAlgorithm> Hash;
+};
+
+template <>
+struct HashTraits<blink::IntegrityAlgorithm>
+ : UnsignedWithZeroKeyHashTraits<blink::IntegrityAlgorithm> {
+ STATIC_ONLY(HashTraits);
+};
+
+} // namespace WTF
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache.cc
new file mode 100644
index 00000000000..20be134ae12
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache.cc
@@ -0,0 +1,476 @@
+/*
+ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2001 Dirk Mueller (mueller@kde.org)
+ Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
+ Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
+
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loading_log.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin_hash.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/auto_reset.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+static Persistent<MemoryCache>* g_memory_cache;
+
+static const unsigned kCDefaultCacheCapacity = 8192 * 1024;
+static const int kCMinDelayBeforeLiveDecodedPrune = 1; // Seconds.
+static const double kCMaxPruneDeferralDelay = 0.5; // Seconds.
+
+// Percentage of capacity toward which we prune, to avoid immediately pruning
+// again.
+static const float kCTargetPrunePercentage = .95f;
+
+MemoryCache* GetMemoryCache() {
+ DCHECK(WTF::IsMainThread());
+ if (!g_memory_cache)
+ g_memory_cache = new Persistent<MemoryCache>(MemoryCache::Create());
+ return g_memory_cache->Get();
+}
+
+MemoryCache* ReplaceMemoryCacheForTesting(MemoryCache* cache) {
+ GetMemoryCache();
+ MemoryCache* old_cache = g_memory_cache->Release();
+ *g_memory_cache = cache;
+ MemoryCacheDumpProvider::Instance()->SetMemoryCache(cache);
+ return old_cache;
+}
+
+void MemoryCacheEntry::Trace(blink::Visitor* visitor) {
+ visitor->template RegisterWeakMembers<MemoryCacheEntry,
+ &MemoryCacheEntry::ClearResourceWeak>(
+ this);
+}
+
+void MemoryCacheEntry::ClearResourceWeak(Visitor* visitor) {
+ if (!resource_ || ThreadHeap::IsHeapObjectAlive(resource_))
+ return;
+ GetMemoryCache()->Remove(resource_.Get());
+ resource_.Clear();
+}
+
+inline MemoryCache::MemoryCache()
+ : in_prune_resources_(false),
+ prune_pending_(false),
+ max_prune_deferral_delay_(kCMaxPruneDeferralDelay),
+ prune_time_stamp_(0.0),
+ prune_frame_time_stamp_(0.0),
+ last_frame_paint_time_stamp_(0.0),
+ capacity_(kCDefaultCacheCapacity),
+ delay_before_live_decoded_prune_(kCMinDelayBeforeLiveDecodedPrune),
+ size_(0) {
+ MemoryCacheDumpProvider::Instance()->SetMemoryCache(this);
+ if (MemoryCoordinator::IsLowEndDevice())
+ MemoryCoordinator::Instance().RegisterClient(this);
+}
+
+MemoryCache* MemoryCache::Create() {
+ return new MemoryCache;
+}
+
+MemoryCache::~MemoryCache() {
+ if (prune_pending_)
+ Platform::Current()->CurrentThread()->RemoveTaskObserver(this);
+}
+
+void MemoryCache::Trace(blink::Visitor* visitor) {
+ visitor->Trace(resource_maps_);
+ MemoryCacheDumpClient::Trace(visitor);
+ MemoryCoordinatorClient::Trace(visitor);
+}
+
+KURL MemoryCache::RemoveFragmentIdentifierIfNeeded(const KURL& original_url) {
+ if (!original_url.HasFragmentIdentifier())
+ return original_url;
+ // Strip away fragment identifier from HTTP URLs. Data URLs must be
+ // unmodified. For file and custom URLs clients may expect resources to be
+ // unique even when they differ by the fragment identifier only.
+ if (!original_url.ProtocolIsInHTTPFamily())
+ return original_url;
+ KURL url = original_url;
+ url.RemoveFragmentIdentifier();
+ return url;
+}
+
+String MemoryCache::DefaultCacheIdentifier() {
+ return g_empty_string;
+}
+
+MemoryCache::ResourceMap* MemoryCache::EnsureResourceMap(
+ const String& cache_identifier) {
+ if (!resource_maps_.Contains(cache_identifier)) {
+ ResourceMapIndex::AddResult result =
+ resource_maps_.insert(cache_identifier, new ResourceMap);
+ CHECK(result.is_new_entry);
+ }
+ return resource_maps_.at(cache_identifier);
+}
+
+void MemoryCache::Add(Resource* resource) {
+ DCHECK(resource);
+ ResourceMap* resources = EnsureResourceMap(resource->CacheIdentifier());
+ AddInternal(resources, MemoryCacheEntry::Create(resource));
+ RESOURCE_LOADING_DVLOG(1)
+ << "MemoryCache::add Added " << resource->Url().GetString()
+ << ", resource " << resource;
+}
+
+void MemoryCache::AddInternal(ResourceMap* resource_map,
+ MemoryCacheEntry* entry) {
+ DCHECK(WTF::IsMainThread());
+ DCHECK(resource_map);
+
+ Resource* resource = entry->GetResource();
+ if (!resource)
+ return;
+ DCHECK(resource->Url().IsValid());
+
+ KURL url = RemoveFragmentIdentifierIfNeeded(resource->Url());
+ ResourceMap::iterator it = resource_map->find(url);
+ if (it != resource_map->end()) {
+ Resource* old_resource = it->value->GetResource();
+ CHECK_NE(old_resource, resource);
+ Update(old_resource, old_resource->size(), 0);
+ }
+ resource_map->Set(url, entry);
+ Update(resource, 0, resource->size());
+}
+
+void MemoryCache::Remove(Resource* resource) {
+ DCHECK(WTF::IsMainThread());
+ DCHECK(resource);
+ // Resources can be created with garbage urls in error cases. These Resources
+ // should never be added to the cache (AddInternal() DCHECKs that the url is
+ // valid). Null urls will crash if we attempt to hash them, so early exit.
+ if (resource->Url().IsNull())
+ return;
+
+ RESOURCE_LOADING_DVLOG(1) << "Evicting resource " << resource << " for "
+ << resource->Url().GetString() << " from cache";
+ TRACE_EVENT1("blink", "MemoryCache::evict", "resource",
+ resource->Url().GetString().Utf8());
+
+ ResourceMap* resources = resource_maps_.at(resource->CacheIdentifier());
+ if (!resources)
+ return;
+
+ KURL url = RemoveFragmentIdentifierIfNeeded(resource->Url());
+ ResourceMap::iterator it = resources->find(url);
+ if (it == resources->end() || it->value->GetResource() != resource)
+ return;
+ RemoveInternal(resources, it);
+}
+
+void MemoryCache::RemoveInternal(ResourceMap* resource_map,
+ const ResourceMap::iterator& it) {
+ DCHECK(WTF::IsMainThread());
+ DCHECK(resource_map);
+
+ Resource* resource = it->value->GetResource();
+ DCHECK(resource);
+
+ Update(resource, resource->size(), 0);
+ resource_map->erase(it);
+}
+
+bool MemoryCache::Contains(const Resource* resource) const {
+ if (!resource || resource->Url().IsEmpty())
+ return false;
+ const ResourceMap* resources = resource_maps_.at(resource->CacheIdentifier());
+ if (!resources)
+ return false;
+ KURL url = RemoveFragmentIdentifierIfNeeded(resource->Url());
+ MemoryCacheEntry* entry = resources->at(url);
+ return entry && resource == entry->GetResource();
+}
+
+Resource* MemoryCache::ResourceForURL(const KURL& resource_url) const {
+ return ResourceForURL(resource_url, DefaultCacheIdentifier());
+}
+
+Resource* MemoryCache::ResourceForURL(const KURL& resource_url,
+ const String& cache_identifier) const {
+ DCHECK(WTF::IsMainThread());
+ if (!resource_url.IsValid() || resource_url.IsNull())
+ return nullptr;
+ DCHECK(!cache_identifier.IsNull());
+ const ResourceMap* resources = resource_maps_.at(cache_identifier);
+ if (!resources)
+ return nullptr;
+ MemoryCacheEntry* entry =
+ resources->at(RemoveFragmentIdentifierIfNeeded(resource_url));
+ if (!entry)
+ return nullptr;
+ return entry->GetResource();
+}
+
+HeapVector<Member<Resource>> MemoryCache::ResourcesForURL(
+ const KURL& resource_url) const {
+ DCHECK(WTF::IsMainThread());
+ KURL url = RemoveFragmentIdentifierIfNeeded(resource_url);
+ HeapVector<Member<Resource>> results;
+ for (const auto& resource_map_iter : resource_maps_) {
+ if (MemoryCacheEntry* entry = resource_map_iter.value->at(url)) {
+ Resource* resource = entry->GetResource();
+ DCHECK(resource);
+ results.push_back(resource);
+ }
+ }
+ return results;
+}
+
+void MemoryCache::PruneResources(PruneStrategy strategy) {
+ DCHECK(!prune_pending_);
+ const size_t size_limit = (strategy == kMaximalPrune) ? 0 : Capacity();
+ if (size_ <= size_limit)
+ return;
+
+ // Cut by a percentage to avoid immediately pruning again.
+ size_t target_size =
+ static_cast<size_t>(size_limit * kCTargetPrunePercentage);
+
+ for (const auto& resource_map_iter : resource_maps_) {
+ for (const auto& resource_iter : *resource_map_iter.value) {
+ Resource* resource = resource_iter.value->GetResource();
+ DCHECK(resource);
+ if (resource->IsLoaded() && resource->DecodedSize()) {
+ // Check to see if the remaining resources are too new to prune.
+ double elapsed_time = prune_frame_time_stamp_ -
+ resource_iter.value->last_decoded_access_time_;
+ if (strategy == kAutomaticPrune &&
+ elapsed_time < delay_before_live_decoded_prune_)
+ continue;
+ resource->Prune();
+ if (size_ <= target_size)
+ return;
+ }
+ }
+ }
+}
+
+void MemoryCache::SetCapacity(size_t total_bytes) {
+ capacity_ = total_bytes;
+ Prune();
+}
+
+void MemoryCache::Update(Resource* resource, size_t old_size, size_t new_size) {
+ if (!Contains(resource))
+ return;
+ ptrdiff_t delta = new_size - old_size;
+ DCHECK(delta >= 0 || size_ >= static_cast<size_t>(-delta));
+ size_ += delta;
+}
+
+void MemoryCache::RemoveURLFromCache(const KURL& url) {
+ HeapVector<Member<Resource>> resources = ResourcesForURL(url);
+ for (Resource* resource : resources)
+ Remove(resource);
+}
+
+void MemoryCache::TypeStatistic::AddResource(Resource* o) {
+ count++;
+ size += o->size();
+ decoded_size += o->DecodedSize();
+ encoded_size += o->EncodedSize();
+ overhead_size += o->OverheadSize();
+ encoded_size_duplicated_in_data_urls +=
+ o->Url().ProtocolIsData() ? o->EncodedSize() : 0;
+}
+
+MemoryCache::Statistics MemoryCache::GetStatistics() const {
+ Statistics stats;
+ for (const auto& resource_map_iter : resource_maps_) {
+ for (const auto& resource_iter : *resource_map_iter.value) {
+ Resource* resource = resource_iter.value->GetResource();
+ DCHECK(resource);
+ switch (resource->GetType()) {
+ case Resource::kImage:
+ stats.images.AddResource(resource);
+ break;
+ case Resource::kCSSStyleSheet:
+ stats.css_style_sheets.AddResource(resource);
+ break;
+ case Resource::kScript:
+ stats.scripts.AddResource(resource);
+ break;
+ case Resource::kXSLStyleSheet:
+ stats.xsl_style_sheets.AddResource(resource);
+ break;
+ case Resource::kFont:
+ stats.fonts.AddResource(resource);
+ break;
+ default:
+ stats.other.AddResource(resource);
+ break;
+ }
+ }
+ }
+ return stats;
+}
+
+void MemoryCache::EvictResources(EvictResourcePolicy policy) {
+ for (auto resource_map_iter = resource_maps_.begin();
+ resource_map_iter != resource_maps_.end();) {
+ ResourceMap* resources = resource_map_iter->value.Get();
+ HeapVector<Member<MemoryCacheEntry>> unused_preloads;
+ for (auto resource_iter = resources->begin();
+ resource_iter != resources->end();
+ resource_iter = resources->begin()) {
+ DCHECK(resource_iter.Get());
+ DCHECK(resource_iter->value.Get());
+ DCHECK(resource_iter->value->GetResource());
+ Resource* resource = resource_iter->value->GetResource();
+ DCHECK(resource);
+ if (policy != kEvictAllResources && resource->IsUnusedPreload()) {
+ // Store unused preloads aside, so they could be added back later.
+ // That is in order to avoid the performance impact of iterating over
+ // the same resource multiple times.
+ unused_preloads.push_back(resource_iter->value.Get());
+ }
+ RemoveInternal(resources, resource_iter);
+ }
+ for (const auto& unused_preload : unused_preloads) {
+ AddInternal(resources, unused_preload);
+ }
+ // We may iterate multiple times over resourceMaps with unused preloads.
+ // That's extremely unlikely to have any real-life performance impact.
+ if (!resources->size()) {
+ resource_maps_.erase(resource_map_iter);
+ resource_map_iter = resource_maps_.begin();
+ } else {
+ ++resource_map_iter;
+ }
+ }
+}
+
+void MemoryCache::Prune() {
+ TRACE_EVENT0("renderer", "MemoryCache::prune()");
+
+ if (in_prune_resources_)
+ return;
+ if (size_ <= capacity_) // Fast path.
+ return;
+
+ // To avoid burdening the current thread with repetitive pruning jobs, pruning
+ // is postponed until the end of the current task. If it has been more than
+ // m_maxPruneDeferralDelay since the last prune, then we prune immediately. If
+ // the current thread's run loop is not active, then pruning will happen
+ // immediately only if it has been over m_maxPruneDeferralDelay since the last
+ // prune.
+ double current_time = WTF::CurrentTime();
+ if (prune_pending_) {
+ if (current_time - prune_time_stamp_ >= max_prune_deferral_delay_) {
+ PruneNow(current_time, kAutomaticPrune);
+ }
+ } else {
+ if (current_time - prune_time_stamp_ >= max_prune_deferral_delay_) {
+ PruneNow(current_time, kAutomaticPrune); // Delay exceeded, prune now.
+ } else {
+ // Defer.
+ Platform::Current()->CurrentThread()->AddTaskObserver(this);
+ prune_pending_ = true;
+ }
+ }
+}
+
+void MemoryCache::WillProcessTask() {}
+
+void MemoryCache::DidProcessTask() {
+ // Perform deferred pruning
+ DCHECK(prune_pending_);
+ PruneNow(WTF::CurrentTime(), kAutomaticPrune);
+}
+
+void MemoryCache::PruneAll() {
+ double current_time = WTF::CurrentTime();
+ PruneNow(current_time, kMaximalPrune);
+}
+
+void MemoryCache::PruneNow(double current_time, PruneStrategy strategy) {
+ if (prune_pending_) {
+ prune_pending_ = false;
+ Platform::Current()->CurrentThread()->RemoveTaskObserver(this);
+ }
+
+ AutoReset<bool> reentrancy_protector(&in_prune_resources_, true);
+
+ PruneResources(strategy);
+ prune_frame_time_stamp_ = last_frame_paint_time_stamp_;
+ prune_time_stamp_ = current_time;
+}
+
+void MemoryCache::UpdateFramePaintTimestamp() {
+ last_frame_paint_time_stamp_ = CurrentTime();
+}
+
+bool MemoryCache::OnMemoryDump(WebMemoryDumpLevelOfDetail level_of_detail,
+ WebProcessMemoryDump* memory_dump) {
+ if (level_of_detail == WebMemoryDumpLevelOfDetail::kBackground) {
+ Statistics stats = GetStatistics();
+ WebMemoryAllocatorDump* dump1 =
+ memory_dump->CreateMemoryAllocatorDump("web_cache/Image_resources");
+ dump1->AddScalar("size", "bytes",
+ stats.images.encoded_size + stats.images.overhead_size);
+ WebMemoryAllocatorDump* dump2 = memory_dump->CreateMemoryAllocatorDump(
+ "web_cache/CSS stylesheet_resources");
+ dump2->AddScalar("size", "bytes",
+ stats.css_style_sheets.encoded_size +
+ stats.css_style_sheets.overhead_size);
+ WebMemoryAllocatorDump* dump3 =
+ memory_dump->CreateMemoryAllocatorDump("web_cache/Script_resources");
+ dump3->AddScalar("size", "bytes",
+ stats.scripts.encoded_size + stats.scripts.overhead_size);
+ WebMemoryAllocatorDump* dump4 = memory_dump->CreateMemoryAllocatorDump(
+ "web_cache/XSL stylesheet_resources");
+ dump4->AddScalar("size", "bytes",
+ stats.xsl_style_sheets.encoded_size +
+ stats.xsl_style_sheets.overhead_size);
+ WebMemoryAllocatorDump* dump5 =
+ memory_dump->CreateMemoryAllocatorDump("web_cache/Font_resources");
+ dump5->AddScalar("size", "bytes",
+ stats.fonts.encoded_size + stats.fonts.overhead_size);
+ WebMemoryAllocatorDump* dump6 =
+ memory_dump->CreateMemoryAllocatorDump("web_cache/Other_resources");
+ dump6->AddScalar("size", "bytes",
+ stats.other.encoded_size + stats.other.overhead_size);
+ return true;
+ }
+
+ for (const auto& resource_map_iter : resource_maps_) {
+ for (const auto& resource_iter : *resource_map_iter.value) {
+ Resource* resource = resource_iter.value->GetResource();
+ resource->OnMemoryDump(level_of_detail, memory_dump);
+ }
+ }
+ return true;
+}
+
+void MemoryCache::OnMemoryPressure(WebMemoryPressureLevel level) {
+ PruneAll();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache.h b/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache.h
new file mode 100644
index 00000000000..bd63614edc1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache.h
@@ -0,0 +1,220 @@
+/*
+ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2001 Dirk Mueller <mueller@kde.org>
+ Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ This class provides all functionality needed for loading images, style
+ sheets and html
+ pages from the web. It has a memory cache for these objects.
+*/
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_MEMORY_CACHE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_MEMORY_CACHE_H_
+
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/memory_cache_dump_provider.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+#include "third_party/blink/renderer/platform/memory_coordinator.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.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 KURL;
+
+// Member<MemoryCacheEntry> + MemoryCacheEntry::clearResourceWeak() monitors
+// eviction from MemoryCache due to Resource garbage collection.
+// WeakMember<Resource> + Resource's prefinalizer cannot determine whether the
+// Resource was on MemoryCache or not, because WeakMember is already cleared
+// when the prefinalizer is executed.
+class MemoryCacheEntry final : public GarbageCollected<MemoryCacheEntry> {
+ public:
+ static MemoryCacheEntry* Create(Resource* resource) {
+ return new MemoryCacheEntry(resource);
+ }
+ void Trace(blink::Visitor*);
+ Resource* GetResource() const { return resource_; }
+
+ double last_decoded_access_time_; // Used as a thrash guard
+
+ private:
+ explicit MemoryCacheEntry(Resource* resource)
+ : last_decoded_access_time_(0.0), resource_(resource) {}
+
+ void ClearResourceWeak(Visitor*);
+
+ WeakMember<Resource> resource_;
+};
+
+WILL_NOT_BE_EAGERLY_TRACED_CLASS(MemoryCacheEntry);
+
+// This cache holds subresources used by Web pages: images, scripts,
+// stylesheets, etc.
+class PLATFORM_EXPORT MemoryCache final
+ : public GarbageCollectedFinalized<MemoryCache>,
+ public WebThread::TaskObserver,
+ public MemoryCacheDumpClient,
+ public MemoryCoordinatorClient {
+ USING_GARBAGE_COLLECTED_MIXIN(MemoryCache);
+ WTF_MAKE_NONCOPYABLE(MemoryCache);
+
+ public:
+ static MemoryCache* Create();
+ ~MemoryCache() override;
+ void Trace(blink::Visitor*) override;
+
+ struct TypeStatistic {
+ STACK_ALLOCATED();
+ size_t count;
+ size_t size;
+ size_t decoded_size;
+ size_t encoded_size;
+ size_t overhead_size;
+ size_t encoded_size_duplicated_in_data_urls;
+
+ TypeStatistic()
+ : count(0),
+ size(0),
+ decoded_size(0),
+ encoded_size(0),
+ overhead_size(0),
+ encoded_size_duplicated_in_data_urls(0) {}
+
+ void AddResource(Resource*);
+ };
+
+ struct Statistics {
+ STACK_ALLOCATED();
+ TypeStatistic images;
+ TypeStatistic css_style_sheets;
+ TypeStatistic scripts;
+ TypeStatistic xsl_style_sheets;
+ TypeStatistic fonts;
+ TypeStatistic other;
+ };
+
+ Resource* ResourceForURL(const KURL&) const;
+ Resource* ResourceForURL(const KURL&, const String& cache_identifier) const;
+ HeapVector<Member<Resource>> ResourcesForURL(const KURL&) const;
+
+ void Add(Resource*);
+ void Remove(Resource*);
+ bool Contains(const Resource*) const;
+
+ static KURL RemoveFragmentIdentifierIfNeeded(const KURL& original_url);
+
+ static String DefaultCacheIdentifier();
+
+ // Sets the cache's memory capacities, in bytes. These will hold only
+ // approximately, since the decoded cost of resources like scripts and
+ // stylesheets is not known.
+ // - totalBytes: The maximum number of bytes that the cache should consume
+ // overall.
+ void SetCapacity(size_t total_bytes);
+ void SetDelayBeforeLiveDecodedPrune(double seconds) {
+ delay_before_live_decoded_prune_ = seconds;
+ }
+ void SetMaxPruneDeferralDelay(double seconds) {
+ max_prune_deferral_delay_ = seconds;
+ }
+
+ enum EvictResourcePolicy { kEvictAllResources, kDoNotEvictUnusedPreloads };
+ void EvictResources(EvictResourcePolicy = kEvictAllResources);
+
+ void Prune();
+
+ // Called to update MemoryCache::size().
+ void Update(Resource*, size_t old_size, size_t new_size);
+
+ void RemoveURLFromCache(const KURL&);
+
+ Statistics GetStatistics() const;
+
+ size_t Capacity() const { return capacity_; }
+ size_t size() const { return size_; }
+
+ // TaskObserver implementation
+ void WillProcessTask() override;
+ void DidProcessTask() override;
+
+ void PruneAll();
+
+ void UpdateFramePaintTimestamp();
+
+ // Take memory usage snapshot for tracing.
+ bool OnMemoryDump(WebMemoryDumpLevelOfDetail, WebProcessMemoryDump*) override;
+
+ void OnMemoryPressure(WebMemoryPressureLevel) override;
+
+ private:
+ enum PruneStrategy {
+ // Automatically decide how much to prune.
+ kAutomaticPrune,
+ // Maximally prune resources.
+ kMaximalPrune
+ };
+
+ // A URL-based map of all resources that are in the cache (including the
+ // freshest version of objects that are currently being referenced by a Web
+ // page). removeFragmentIdentifierIfNeeded() should be called for the url
+ // before using it as a key for the map.
+ using ResourceMap = HeapHashMap<String, Member<MemoryCacheEntry>>;
+ using ResourceMapIndex = HeapHashMap<String, Member<ResourceMap>>;
+ ResourceMap* EnsureResourceMap(const String& cache_identifier);
+ ResourceMapIndex resource_maps_;
+
+ MemoryCache();
+
+ void AddInternal(ResourceMap*, MemoryCacheEntry*);
+ void RemoveInternal(ResourceMap*, const ResourceMap::iterator&);
+
+ void PruneResources(PruneStrategy);
+ void PruneNow(double current_time, PruneStrategy);
+
+ bool in_prune_resources_;
+ bool prune_pending_;
+ double max_prune_deferral_delay_;
+ double prune_time_stamp_;
+ double prune_frame_time_stamp_;
+ double last_frame_paint_time_stamp_; // used for detecting decoded resource
+ // thrash in the cache
+
+ size_t capacity_;
+ double delay_before_live_decoded_prune_;
+
+ // The number of bytes currently consumed by resources in the cache.
+ size_t size_;
+
+ friend class MemoryCacheTest;
+};
+
+// Returns the global cache.
+PLATFORM_EXPORT MemoryCache* GetMemoryCache();
+
+// Sets the global cache, used to swap in a test instance. Returns the old
+// MemoryCache object.
+PLATFORM_EXPORT MemoryCache* ReplaceMemoryCacheForTesting(MemoryCache*);
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc
new file mode 100644
index 00000000000..75a0e6c10dd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache_correctness_test.cc
@@ -0,0 +1,564 @@
+/*
+ * Copyright (c) 2014, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_context.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
+#include "third_party/blink/renderer/platform/loader/fetch/raw_resource.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+#include "third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h"
+#include "third_party/blink/renderer/platform/loader/testing/mock_resource.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+
+namespace blink {
+
+namespace {
+
+// An URL for the original request.
+constexpr char kResourceURL[] = "http://resource.com/";
+
+// The origin time of our first request.
+constexpr char kOriginalRequestDateAsString[] = "Thu, 25 May 1977 18:30:00 GMT";
+constexpr char kOneDayBeforeOriginalRequest[] = "Wed, 24 May 1977 18:30:00 GMT";
+constexpr char kOneDayAfterOriginalRequest[] = "Fri, 26 May 1977 18:30:00 GMT";
+
+} // namespace
+
+class MemoryCacheCorrectnessTest : public testing::Test {
+ protected:
+ MockResource* ResourceFromResourceResponse(ResourceResponse response) {
+ if (response.Url().IsNull())
+ response.SetURL(KURL(kResourceURL));
+ ResourceRequest request(response.Url());
+ MockResource* resource = MockResource::Create(request);
+ resource->SetResponse(response);
+ resource->FinishForTest();
+ AddResourceToMemoryCache(resource);
+
+ return resource;
+ }
+ MockResource* ResourceFromResourceRequest(ResourceRequest request) {
+ if (request.Url().IsNull())
+ request.SetURL(KURL(kResourceURL));
+ request.SetFetchCredentialsMode(
+ network::mojom::FetchCredentialsMode::kOmit);
+ MockResource* resource = MockResource::Create(request);
+ resource->SetResponse(ResourceResponse(KURL(kResourceURL), "text/html"));
+ resource->FinishForTest();
+ AddResourceToMemoryCache(resource);
+
+ return resource;
+ }
+ void AddResourceToMemoryCache(Resource* resource) {
+ resource->SetSourceOrigin(security_origin_);
+ GetMemoryCache()->Add(resource);
+ }
+ // TODO(toyoshim): Consider to use MockResource for all tests instead of
+ // RawResource.
+ RawResource* FetchRawResource() {
+ ResourceRequest resource_request{KURL(kResourceURL)};
+ resource_request.SetRequestContext(WebURLRequest::kRequestContextInternal);
+ FetchParameters fetch_params(resource_request);
+ return RawResource::Fetch(fetch_params, Fetcher(), nullptr);
+ }
+ MockResource* FetchMockResource() {
+ ResourceRequest resource_request{KURL(kResourceURL)};
+ FetchParameters fetch_params(resource_request);
+ return MockResource::Fetch(fetch_params, Fetcher(), nullptr);
+ }
+ ResourceFetcher* Fetcher() const { return fetcher_.Get(); }
+ void AdvanceClock(double seconds) { platform_->AdvanceClockSeconds(seconds); }
+
+ private:
+ // Overrides testing::Test.
+ void SetUp() override {
+ // Save the global memory cache to restore it upon teardown.
+ global_memory_cache_ = ReplaceMemoryCacheForTesting(MemoryCache::Create());
+
+ MockFetchContext* context =
+ MockFetchContext::Create(MockFetchContext::kShouldNotLoadNewResource);
+ security_origin_ = SecurityOrigin::CreateUnique();
+ context->SetSecurityOrigin(security_origin_);
+
+ fetcher_ = ResourceFetcher::Create(context);
+ }
+ void TearDown() override {
+ GetMemoryCache()->EvictResources();
+
+ // Yield the ownership of the global memory cache back.
+ ReplaceMemoryCacheForTesting(global_memory_cache_.Release());
+ }
+
+ Persistent<MemoryCache> global_memory_cache_;
+ scoped_refptr<const SecurityOrigin> security_origin_;
+ Persistent<ResourceFetcher> fetcher_;
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform_;
+};
+
+TEST_F(MemoryCacheCorrectnessTest, FreshFromLastModified) {
+ ResourceResponse fresh200_response;
+ fresh200_response.SetHTTPStatusCode(200);
+ fresh200_response.SetHTTPHeaderField("Date", kOriginalRequestDateAsString);
+ fresh200_response.SetHTTPHeaderField("Last-Modified",
+ kOneDayBeforeOriginalRequest);
+
+ MockResource* fresh200 = ResourceFromResourceResponse(fresh200_response);
+
+ // Advance the clock within the implicit freshness period of this resource
+ // before we make a request.
+ AdvanceClock(600.);
+
+ MockResource* fetched = FetchMockResource();
+ EXPECT_EQ(fresh200, fetched);
+}
+
+TEST_F(MemoryCacheCorrectnessTest, FreshFromExpires) {
+ ResourceResponse fresh200_response;
+ fresh200_response.SetHTTPStatusCode(200);
+ fresh200_response.SetHTTPHeaderField("Date", kOriginalRequestDateAsString);
+ fresh200_response.SetHTTPHeaderField("Expires", kOneDayAfterOriginalRequest);
+
+ MockResource* fresh200 = ResourceFromResourceResponse(fresh200_response);
+
+ // Advance the clock within the freshness period of this resource before we
+ // make a request.
+ AdvanceClock(24. * 60. * 60. - 15.);
+
+ MockResource* fetched = FetchMockResource();
+ EXPECT_EQ(fresh200, fetched);
+}
+
+TEST_F(MemoryCacheCorrectnessTest, FreshFromMaxAge) {
+ ResourceResponse fresh200_response;
+ fresh200_response.SetHTTPStatusCode(200);
+ fresh200_response.SetHTTPHeaderField("Date", kOriginalRequestDateAsString);
+ fresh200_response.SetHTTPHeaderField("Cache-Control", "max-age=600");
+
+ MockResource* fresh200 = ResourceFromResourceResponse(fresh200_response);
+
+ // Advance the clock within the freshness period of this resource before we
+ // make a request.
+ AdvanceClock(500.);
+
+ MockResource* fetched = FetchMockResource();
+ EXPECT_EQ(fresh200, fetched);
+}
+
+// The strong validator causes a revalidation to be launched, and the proxy and
+// original resources leak because of their reference loop.
+TEST_F(MemoryCacheCorrectnessTest, DISABLED_ExpiredFromLastModified) {
+ ResourceResponse expired200_response;
+ expired200_response.SetHTTPStatusCode(200);
+ expired200_response.SetHTTPHeaderField("Date", kOriginalRequestDateAsString);
+ expired200_response.SetHTTPHeaderField("Last-Modified",
+ kOneDayBeforeOriginalRequest);
+
+ MockResource* expired200 = ResourceFromResourceResponse(expired200_response);
+
+ // Advance the clock beyond the implicit freshness period.
+ AdvanceClock(24. * 60. * 60. * 0.2);
+
+ MockResource* fetched = FetchMockResource();
+ EXPECT_NE(expired200, fetched);
+}
+
+TEST_F(MemoryCacheCorrectnessTest, ExpiredFromExpires) {
+ ResourceResponse expired200_response;
+ expired200_response.SetHTTPStatusCode(200);
+ expired200_response.SetHTTPHeaderField("Date", kOriginalRequestDateAsString);
+ expired200_response.SetHTTPHeaderField("Expires",
+ kOneDayAfterOriginalRequest);
+
+ MockResource* expired200 = ResourceFromResourceResponse(expired200_response);
+
+ // Advance the clock within the expiredness period of this resource before we
+ // make a request.
+ AdvanceClock(24. * 60. * 60. + 15.);
+
+ MockResource* fetched = FetchMockResource();
+ EXPECT_NE(expired200, fetched);
+}
+
+// If the resource hasn't been loaded in this "document" before, then it
+// shouldn't have list of available resources logic.
+TEST_F(MemoryCacheCorrectnessTest, NewMockResourceExpiredFromExpires) {
+ ResourceResponse expired200_response;
+ expired200_response.SetHTTPStatusCode(200);
+ expired200_response.SetHTTPHeaderField("Date", kOriginalRequestDateAsString);
+ expired200_response.SetHTTPHeaderField("Expires",
+ kOneDayAfterOriginalRequest);
+
+ MockResource* expired200 = ResourceFromResourceResponse(expired200_response);
+
+ // Advance the clock within the expiredness period of this resource before we
+ // make a request.
+ AdvanceClock(24. * 60. * 60. + 15.);
+
+ MockResource* fetched = FetchMockResource();
+ EXPECT_NE(expired200, fetched);
+}
+
+// If the resource has been loaded in this "document" before, then it should
+// have list of available resources logic, and so normal cache testing should be
+// bypassed.
+TEST_F(MemoryCacheCorrectnessTest, ReuseMockResourceExpiredFromExpires) {
+ ResourceResponse expired200_response;
+ expired200_response.SetHTTPStatusCode(200);
+ expired200_response.SetHTTPHeaderField("Date", kOriginalRequestDateAsString);
+ expired200_response.SetHTTPHeaderField("Expires",
+ kOneDayAfterOriginalRequest);
+
+ MockResource* expired200 = ResourceFromResourceResponse(expired200_response);
+
+ // Advance the clock within the freshness period, and make a request to add
+ // this resource to the document resources.
+ AdvanceClock(15.);
+ MockResource* first_fetched = FetchMockResource();
+ EXPECT_EQ(expired200, first_fetched);
+
+ // Advance the clock within the expiredness period of this resource before we
+ // make a request.
+ AdvanceClock(24. * 60. * 60. + 15.);
+
+ MockResource* fetched = FetchMockResource();
+ EXPECT_EQ(expired200, fetched);
+}
+
+TEST_F(MemoryCacheCorrectnessTest, ExpiredFromMaxAge) {
+ ResourceResponse expired200_response;
+ expired200_response.SetHTTPStatusCode(200);
+ expired200_response.SetHTTPHeaderField("Date", kOriginalRequestDateAsString);
+ expired200_response.SetHTTPHeaderField("Cache-Control", "max-age=600");
+
+ MockResource* expired200 = ResourceFromResourceResponse(expired200_response);
+
+ // Advance the clock within the expiredness period of this resource before we
+ // make a request.
+ AdvanceClock(700.);
+
+ MockResource* fetched = FetchMockResource();
+ EXPECT_NE(expired200, fetched);
+}
+
+TEST_F(MemoryCacheCorrectnessTest, FreshButNoCache) {
+ ResourceResponse fresh200_nocache_response;
+ fresh200_nocache_response.SetHTTPStatusCode(200);
+ fresh200_nocache_response.SetHTTPHeaderField(HTTPNames::Date,
+ kOriginalRequestDateAsString);
+ fresh200_nocache_response.SetHTTPHeaderField(HTTPNames::Expires,
+ kOneDayAfterOriginalRequest);
+ fresh200_nocache_response.SetHTTPHeaderField(HTTPNames::Cache_Control,
+ "no-cache");
+
+ MockResource* fresh200_nocache =
+ ResourceFromResourceResponse(fresh200_nocache_response);
+
+ // Advance the clock within the freshness period of this resource before we
+ // make a request.
+ AdvanceClock(24. * 60. * 60. - 15.);
+
+ MockResource* fetched = FetchMockResource();
+ EXPECT_NE(fresh200_nocache, fetched);
+}
+
+TEST_F(MemoryCacheCorrectnessTest, RequestWithNoCache) {
+ ResourceRequest no_cache_request;
+ no_cache_request.SetHTTPHeaderField(HTTPNames::Cache_Control, "no-cache");
+ MockResource* no_cache_resource =
+ ResourceFromResourceRequest(no_cache_request);
+ MockResource* fetched = FetchMockResource();
+ EXPECT_NE(no_cache_resource, fetched);
+}
+
+TEST_F(MemoryCacheCorrectnessTest, FreshButNoStore) {
+ ResourceResponse fresh200_nostore_response;
+ fresh200_nostore_response.SetHTTPStatusCode(200);
+ fresh200_nostore_response.SetHTTPHeaderField(HTTPNames::Date,
+ kOriginalRequestDateAsString);
+ fresh200_nostore_response.SetHTTPHeaderField(HTTPNames::Expires,
+ kOneDayAfterOriginalRequest);
+ fresh200_nostore_response.SetHTTPHeaderField(HTTPNames::Cache_Control,
+ "no-store");
+
+ MockResource* fresh200_nostore =
+ ResourceFromResourceResponse(fresh200_nostore_response);
+
+ // Advance the clock within the freshness period of this resource before we
+ // make a request.
+ AdvanceClock(24. * 60. * 60. - 15.);
+
+ MockResource* fetched = FetchMockResource();
+ EXPECT_NE(fresh200_nostore, fetched);
+}
+
+TEST_F(MemoryCacheCorrectnessTest, RequestWithNoStore) {
+ ResourceRequest no_store_request;
+ no_store_request.SetHTTPHeaderField(HTTPNames::Cache_Control, "no-store");
+ MockResource* no_store_resource =
+ ResourceFromResourceRequest(no_store_request);
+ MockResource* fetched = FetchMockResource();
+ EXPECT_NE(no_store_resource, fetched);
+}
+
+// FIXME: Determine if ignoring must-revalidate for blink is correct behaviour.
+// See crbug.com/340088 .
+TEST_F(MemoryCacheCorrectnessTest, DISABLED_FreshButMustRevalidate) {
+ ResourceResponse fresh200_must_revalidate_response;
+ fresh200_must_revalidate_response.SetHTTPStatusCode(200);
+ fresh200_must_revalidate_response.SetHTTPHeaderField(
+ HTTPNames::Date, kOriginalRequestDateAsString);
+ fresh200_must_revalidate_response.SetHTTPHeaderField(
+ HTTPNames::Expires, kOneDayAfterOriginalRequest);
+ fresh200_must_revalidate_response.SetHTTPHeaderField(HTTPNames::Cache_Control,
+ "must-revalidate");
+
+ MockResource* fresh200_must_revalidate =
+ ResourceFromResourceResponse(fresh200_must_revalidate_response);
+
+ // Advance the clock within the freshness period of this resource before we
+ // make a request.
+ AdvanceClock(24. * 60. * 60. - 15.);
+
+ MockResource* fetched = FetchMockResource();
+ EXPECT_NE(fresh200_must_revalidate, fetched);
+}
+
+TEST_F(MemoryCacheCorrectnessTest, FreshWithFreshRedirect) {
+ KURL redirect_url(kResourceURL);
+ const char kRedirectTargetUrlString[] = "http://redirect-target.com";
+ KURL redirect_target_url(kRedirectTargetUrlString);
+
+ ResourceRequest request(redirect_url);
+ MockResource* first_resource = MockResource::Create(request);
+
+ ResourceResponse fresh301_response(redirect_url);
+ fresh301_response.SetHTTPStatusCode(301);
+ fresh301_response.SetHTTPHeaderField(HTTPNames::Date,
+ kOriginalRequestDateAsString);
+ fresh301_response.SetHTTPHeaderField(HTTPNames::Location,
+ kRedirectTargetUrlString);
+ fresh301_response.SetHTTPHeaderField(HTTPNames::Cache_Control, "max-age=600");
+
+ // Add the redirect to our request.
+ ResourceRequest redirect_request = ResourceRequest(redirect_target_url);
+ first_resource->WillFollowRedirect(redirect_request, fresh301_response);
+
+ // Add the final response to our request.
+ ResourceResponse fresh200_response(redirect_target_url);
+ fresh200_response.SetHTTPStatusCode(200);
+ fresh200_response.SetHTTPHeaderField(HTTPNames::Date,
+ kOriginalRequestDateAsString);
+ fresh200_response.SetHTTPHeaderField(HTTPNames::Expires,
+ kOneDayAfterOriginalRequest);
+
+ first_resource->SetResponse(fresh200_response);
+ first_resource->FinishForTest();
+ AddResourceToMemoryCache(first_resource);
+
+ AdvanceClock(500.);
+
+ MockResource* fetched = FetchMockResource();
+ EXPECT_EQ(first_resource, fetched);
+}
+
+TEST_F(MemoryCacheCorrectnessTest, FreshWithStaleRedirect) {
+ KURL redirect_url(kResourceURL);
+ const char kRedirectTargetUrlString[] = "http://redirect-target.com";
+ KURL redirect_target_url(kRedirectTargetUrlString);
+
+ ResourceRequest request(redirect_url);
+ request.SetFetchCredentialsMode(network::mojom::FetchCredentialsMode::kOmit);
+ MockResource* first_resource = MockResource::Create(request);
+
+ ResourceResponse stale301_response(redirect_url);
+ stale301_response.SetHTTPStatusCode(301);
+ stale301_response.SetHTTPHeaderField(HTTPNames::Date,
+ kOriginalRequestDateAsString);
+ stale301_response.SetHTTPHeaderField(HTTPNames::Location,
+ kRedirectTargetUrlString);
+
+ // Add the redirect to our request.
+ ResourceRequest redirect_request = ResourceRequest(redirect_target_url);
+ first_resource->WillFollowRedirect(redirect_request, stale301_response);
+
+ // Add the final response to our request.
+ ResourceResponse fresh200_response(redirect_target_url);
+ fresh200_response.SetHTTPStatusCode(200);
+ fresh200_response.SetHTTPHeaderField(HTTPNames::Date,
+ kOriginalRequestDateAsString);
+ fresh200_response.SetHTTPHeaderField(HTTPNames::Expires,
+ kOneDayAfterOriginalRequest);
+
+ first_resource->SetResponse(fresh200_response);
+ first_resource->FinishForTest();
+ AddResourceToMemoryCache(first_resource);
+
+ AdvanceClock(500.);
+
+ MockResource* fetched = FetchMockResource();
+ EXPECT_NE(first_resource, fetched);
+}
+
+TEST_F(MemoryCacheCorrectnessTest, PostToSameURLTwice) {
+ ResourceRequest request1{KURL(kResourceURL)};
+ request1.SetHTTPMethod(HTTPNames::POST);
+ RawResource* resource1 = RawResource::CreateForTest(request1, Resource::kRaw);
+ resource1->SetStatus(ResourceStatus::kPending);
+ AddResourceToMemoryCache(resource1);
+
+ ResourceRequest request2{KURL(kResourceURL)};
+ request2.SetHTTPMethod(HTTPNames::POST);
+ FetchParameters fetch2(request2);
+ RawResource* resource2 = RawResource::FetchSynchronously(fetch2, Fetcher());
+ EXPECT_NE(resource1, resource2);
+}
+
+TEST_F(MemoryCacheCorrectnessTest, 302RedirectNotImplicitlyFresh) {
+ KURL redirect_url(kResourceURL);
+ const char kRedirectTargetUrlString[] = "http://redirect-target.com";
+ KURL redirect_target_url(kRedirectTargetUrlString);
+
+ RawResource* first_resource =
+ RawResource::CreateForTest(redirect_url, Resource::kRaw);
+
+ ResourceResponse fresh302_response(redirect_url);
+ fresh302_response.SetHTTPStatusCode(302);
+ fresh302_response.SetHTTPHeaderField(HTTPNames::Date,
+ kOriginalRequestDateAsString);
+ fresh302_response.SetHTTPHeaderField(HTTPNames::Last_Modified,
+ kOneDayBeforeOriginalRequest);
+ fresh302_response.SetHTTPHeaderField(HTTPNames::Location,
+ kRedirectTargetUrlString);
+
+ // Add the redirect to our request.
+ ResourceRequest redirect_request = ResourceRequest(redirect_target_url);
+ first_resource->WillFollowRedirect(redirect_request, fresh302_response);
+
+ // Add the final response to our request.
+ ResourceResponse fresh200_response(redirect_target_url);
+ fresh200_response.SetHTTPStatusCode(200);
+ fresh200_response.SetHTTPHeaderField(HTTPNames::Date,
+ kOriginalRequestDateAsString);
+ fresh200_response.SetHTTPHeaderField(HTTPNames::Expires,
+ kOneDayAfterOriginalRequest);
+
+ first_resource->SetResponse(fresh200_response);
+ first_resource->FinishForTest();
+ AddResourceToMemoryCache(first_resource);
+
+ AdvanceClock(500.);
+
+ RawResource* fetched = FetchRawResource();
+ EXPECT_NE(first_resource, fetched);
+}
+
+TEST_F(MemoryCacheCorrectnessTest, 302RedirectExplicitlyFreshMaxAge) {
+ KURL redirect_url(kResourceURL);
+ const char kRedirectTargetUrlString[] = "http://redirect-target.com";
+ KURL redirect_target_url(kRedirectTargetUrlString);
+
+ ResourceRequest request(redirect_url);
+ MockResource* first_resource = MockResource::Create(request);
+
+ ResourceResponse fresh302_response(redirect_url);
+ fresh302_response.SetHTTPStatusCode(302);
+ fresh302_response.SetHTTPHeaderField(HTTPNames::Date,
+ kOriginalRequestDateAsString);
+ fresh302_response.SetHTTPHeaderField(HTTPNames::Cache_Control, "max-age=600");
+ fresh302_response.SetHTTPHeaderField(HTTPNames::Location,
+ kRedirectTargetUrlString);
+
+ // Add the redirect to our request.
+ ResourceRequest redirect_request = ResourceRequest(redirect_target_url);
+ first_resource->WillFollowRedirect(redirect_request, fresh302_response);
+
+ // Add the final response to our request.
+ ResourceResponse fresh200_response(redirect_target_url);
+ fresh200_response.SetHTTPStatusCode(200);
+ fresh200_response.SetHTTPHeaderField(HTTPNames::Date,
+ kOriginalRequestDateAsString);
+ fresh200_response.SetHTTPHeaderField(HTTPNames::Expires,
+ kOneDayAfterOriginalRequest);
+
+ first_resource->SetResponse(fresh200_response);
+ first_resource->FinishForTest();
+ AddResourceToMemoryCache(first_resource);
+
+ AdvanceClock(500.);
+
+ MockResource* fetched = FetchMockResource();
+ EXPECT_EQ(first_resource, fetched);
+}
+
+TEST_F(MemoryCacheCorrectnessTest, 302RedirectExplicitlyFreshExpires) {
+ KURL redirect_url(kResourceURL);
+ const char kRedirectTargetUrlString[] = "http://redirect-target.com";
+ KURL redirect_target_url(kRedirectTargetUrlString);
+
+ ResourceRequest request(redirect_url);
+ MockResource* first_resource = MockResource::Create(request);
+
+ ResourceResponse fresh302_response(redirect_url);
+ fresh302_response.SetHTTPStatusCode(302);
+ fresh302_response.SetHTTPHeaderField(HTTPNames::Date,
+ kOriginalRequestDateAsString);
+ fresh302_response.SetHTTPHeaderField(HTTPNames::Expires,
+ kOneDayAfterOriginalRequest);
+ fresh302_response.SetHTTPHeaderField(HTTPNames::Location,
+ kRedirectTargetUrlString);
+
+ // Add the redirect to our request.
+ ResourceRequest redirect_request = ResourceRequest(redirect_target_url);
+ first_resource->WillFollowRedirect(redirect_request, fresh302_response);
+
+ // Add the final response to our request.
+ ResourceResponse fresh200_response(redirect_target_url);
+ fresh200_response.SetHTTPStatusCode(200);
+ fresh200_response.SetHTTPHeaderField(HTTPNames::Date,
+ kOriginalRequestDateAsString);
+ fresh200_response.SetHTTPHeaderField(HTTPNames::Expires,
+ kOneDayAfterOriginalRequest);
+
+ first_resource->SetResponse(fresh200_response);
+ first_resource->FinishForTest();
+ AddResourceToMemoryCache(first_resource);
+
+ AdvanceClock(500.);
+
+ MockResource* fetched = FetchMockResource();
+ EXPECT_EQ(first_resource, fetched);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc
new file mode 100644
index 00000000000..d107d869f93
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc
@@ -0,0 +1,419 @@
+/*
+ * Copyright (c) 2013, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/loader/fetch/raw_resource.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+#include "third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h"
+#include "third_party/blink/renderer/platform/loader/testing/mock_resource_client.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+
+namespace blink {
+
+class FakeDecodedResource final : public Resource {
+ public:
+ static FakeDecodedResource* Fetch(FetchParameters& params,
+ ResourceFetcher* fetcher,
+ ResourceClient* client) {
+ return static_cast<FakeDecodedResource*>(
+ fetcher->RequestResource(params, Factory(), client));
+ }
+
+ void AppendData(const char* data, size_t len) override {
+ Resource::AppendData(data, len);
+ SetDecodedSize(this->size());
+ }
+
+ void FakeEncodedSize(size_t size) { SetEncodedSize(size); }
+
+ private:
+ class Factory final : public NonTextResourceFactory {
+ public:
+ Factory() : NonTextResourceFactory(kMock) {}
+
+ Resource* Create(const ResourceRequest& request,
+ const ResourceLoaderOptions& options) const override {
+ return new FakeDecodedResource(request, options);
+ }
+ };
+
+ FakeDecodedResource(const ResourceRequest& request,
+ const ResourceLoaderOptions& options)
+ : Resource(request, kMock, options) {}
+
+ void DestroyDecodedDataIfPossible() override { SetDecodedSize(0); }
+};
+
+class MemoryCacheTest : public testing::Test {
+ public:
+ class FakeResource final : public Resource {
+ public:
+ static FakeResource* Create(const char* url, Type type) {
+ return Create(KURL(url), type);
+ }
+ static FakeResource* Create(const KURL& url, Type type) {
+ ResourceRequest request(url);
+ request.SetFetchCredentialsMode(
+ network::mojom::FetchCredentialsMode::kOmit);
+
+ ResourceLoaderOptions options;
+
+ return new FakeResource(request, type, options);
+ }
+
+ private:
+ FakeResource(const ResourceRequest& request,
+ Type type,
+ const ResourceLoaderOptions& options)
+ : Resource(request, type, options) {}
+ };
+
+ protected:
+ void SetUp() override {
+ // Save the global memory cache to restore it upon teardown.
+ global_memory_cache_ = ReplaceMemoryCacheForTesting(MemoryCache::Create());
+ fetcher_ = ResourceFetcher::Create(
+ MockFetchContext::Create(MockFetchContext::kShouldLoadNewResource));
+ }
+
+ void TearDown() override {
+ ReplaceMemoryCacheForTesting(global_memory_cache_.Release());
+ }
+
+ Persistent<MemoryCache> global_memory_cache_;
+ Persistent<ResourceFetcher> fetcher_;
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform_;
+};
+
+// Verifies that setters and getters for cache capacities work correcty.
+TEST_F(MemoryCacheTest, CapacityAccounting) {
+ const size_t kSizeMax = ~static_cast<size_t>(0);
+ const size_t kTotalCapacity = kSizeMax / 4;
+ GetMemoryCache()->SetCapacity(kTotalCapacity);
+ EXPECT_EQ(kTotalCapacity, GetMemoryCache()->Capacity());
+}
+
+TEST_F(MemoryCacheTest, VeryLargeResourceAccounting) {
+ const size_t kSizeMax = ~static_cast<size_t>(0);
+ const size_t kTotalCapacity = kSizeMax / 4;
+ const size_t kResourceSize1 = kSizeMax / 16;
+ const size_t kResourceSize2 = kSizeMax / 20;
+ GetMemoryCache()->SetCapacity(kTotalCapacity);
+ Persistent<MockResourceClient> client = new MockResourceClient;
+ FetchParameters params(ResourceRequest("data:text/html,"));
+ FakeDecodedResource* cached_resource =
+ FakeDecodedResource::Fetch(params, fetcher_, client);
+ cached_resource->FakeEncodedSize(kResourceSize1);
+
+ EXPECT_TRUE(GetMemoryCache()->Contains(cached_resource));
+ EXPECT_EQ(cached_resource->size(), GetMemoryCache()->size());
+
+ client->RemoveAsClient();
+ EXPECT_EQ(cached_resource->size(), GetMemoryCache()->size());
+
+ cached_resource->FakeEncodedSize(kResourceSize2);
+ EXPECT_EQ(cached_resource->size(), GetMemoryCache()->size());
+}
+
+static void RunTask1(Resource* resource1, Resource* resource2) {
+ // The resource size has to be nonzero for this test to be meaningful, but
+ // we do not rely on it having any particular value.
+ EXPECT_GT(resource1->size(), 0u);
+ EXPECT_GT(resource2->size(), 0u);
+
+ EXPECT_EQ(0u, GetMemoryCache()->size());
+
+ GetMemoryCache()->Add(resource1);
+ GetMemoryCache()->Add(resource2);
+
+ size_t total_size = resource1->size() + resource2->size();
+ EXPECT_EQ(total_size, GetMemoryCache()->size());
+ EXPECT_GT(resource1->DecodedSize(), 0u);
+ EXPECT_GT(resource2->DecodedSize(), 0u);
+
+ // We expect actual pruning doesn't occur here synchronously but deferred
+ // to the end of this task, due to the previous pruning invoked in
+ // testResourcePruningAtEndOfTask().
+ GetMemoryCache()->Prune();
+ EXPECT_EQ(total_size, GetMemoryCache()->size());
+ EXPECT_GT(resource1->DecodedSize(), 0u);
+ EXPECT_GT(resource2->DecodedSize(), 0u);
+}
+
+static void RunTask2(unsigned size_without_decode) {
+ // Next task: now, the resources was pruned.
+ EXPECT_EQ(size_without_decode, GetMemoryCache()->size());
+}
+
+static void TestResourcePruningAtEndOfTask(ResourceFetcher* fetcher,
+ const String& identifier1,
+ const String& identifier2) {
+ GetMemoryCache()->SetDelayBeforeLiveDecodedPrune(0);
+
+ // Enforce pruning by adding |dummyResource| and then call prune().
+ Resource* dummy_resource =
+ RawResource::CreateForTest("http://dummy", Resource::kRaw);
+ GetMemoryCache()->Add(dummy_resource);
+ EXPECT_GT(GetMemoryCache()->size(), 1u);
+ const unsigned kTotalCapacity = 1;
+ GetMemoryCache()->SetCapacity(kTotalCapacity);
+ GetMemoryCache()->Prune();
+ GetMemoryCache()->Remove(dummy_resource);
+ EXPECT_EQ(0u, GetMemoryCache()->size());
+
+ const char kData[6] = "abcde";
+ FetchParameters params1(ResourceRequest("data:text/html,resource1"));
+ Resource* resource1 = FakeDecodedResource::Fetch(params1, fetcher, nullptr);
+ GetMemoryCache()->Remove(resource1);
+ if (!identifier1.IsEmpty())
+ resource1->SetCacheIdentifier(identifier1);
+ resource1->AppendData(kData, 3u);
+ resource1->FinishForTest();
+ FetchParameters params2(ResourceRequest("data:text/html,resource2"));
+ Persistent<MockResourceClient> client = new MockResourceClient;
+ Resource* resource2 = FakeDecodedResource::Fetch(params2, fetcher, client);
+ GetMemoryCache()->Remove(resource2);
+ if (!identifier2.IsEmpty())
+ resource2->SetCacheIdentifier(identifier2);
+ resource2->AppendData(kData, 4u);
+ resource2->FinishForTest();
+
+ Platform::Current()->CurrentThread()->GetTaskRunner()->PostTask(
+ FROM_HERE, WTF::Bind(&RunTask1, WrapPersistent(resource1),
+ WrapPersistent(resource2)));
+ Platform::Current()->CurrentThread()->GetTaskRunner()->PostTask(
+ FROM_HERE,
+ WTF::Bind(&RunTask2,
+ resource1->EncodedSize() + resource1->OverheadSize() +
+ resource2->EncodedSize() + resource2->OverheadSize()));
+ static_cast<TestingPlatformSupportWithMockScheduler*>(Platform::Current())
+ ->RunUntilIdle();
+}
+
+// Verified that when ordering a prune in a runLoop task, the prune
+// is deferred to the end of the task.
+TEST_F(MemoryCacheTest, ResourcePruningAtEndOfTask_Basic) {
+ TestResourcePruningAtEndOfTask(fetcher_, "", "");
+}
+
+TEST_F(MemoryCacheTest, ResourcePruningAtEndOfTask_MultipleResourceMaps) {
+ {
+ TestResourcePruningAtEndOfTask(fetcher_, "foo", "");
+ GetMemoryCache()->EvictResources();
+ }
+ {
+ TestResourcePruningAtEndOfTask(fetcher_, "foo", "bar");
+ GetMemoryCache()->EvictResources();
+ }
+}
+
+// Verifies that
+// - Resources are not pruned synchronously when ResourceClient is removed.
+// - size() is updated appropriately when Resources are added to MemoryCache
+// and garbage collected.
+static void TestClientRemoval(ResourceFetcher* fetcher,
+ const String& identifier1,
+ const String& identifier2) {
+ GetMemoryCache()->SetCapacity(0);
+ const char kData[6] = "abcde";
+ Persistent<MockResourceClient> client1 = new MockResourceClient;
+ Persistent<MockResourceClient> client2 = new MockResourceClient;
+ FetchParameters params1(ResourceRequest("data:text/html,foo"));
+ Resource* resource1 = FakeDecodedResource::Fetch(params1, fetcher, client1);
+ FetchParameters params2(ResourceRequest("data:text/html,bar"));
+ Resource* resource2 = FakeDecodedResource::Fetch(params2, fetcher, client2);
+ resource1->AppendData(kData, 4u);
+ resource2->AppendData(kData, 4u);
+
+ GetMemoryCache()->SetCapacity(0);
+ // Remove and re-Add the resources, with proper cache identifiers.
+ GetMemoryCache()->Remove(resource1);
+ GetMemoryCache()->Remove(resource2);
+ if (!identifier1.IsEmpty())
+ resource1->SetCacheIdentifier(identifier1);
+ if (!identifier2.IsEmpty())
+ resource2->SetCacheIdentifier(identifier2);
+ GetMemoryCache()->Add(resource1);
+ GetMemoryCache()->Add(resource2);
+
+ size_t original_total_size = resource1->size() + resource2->size();
+
+ // Call prune. There is nothing to prune, but this will initialize
+ // the prune timestamp, allowing future prunes to be deferred.
+ GetMemoryCache()->Prune();
+ EXPECT_GT(resource1->DecodedSize(), 0u);
+ EXPECT_GT(resource2->DecodedSize(), 0u);
+ EXPECT_EQ(original_total_size, GetMemoryCache()->size());
+
+ // Removing the client from resource1 should not trigger pruning.
+ client1->RemoveAsClient();
+ EXPECT_GT(resource1->DecodedSize(), 0u);
+ EXPECT_GT(resource2->DecodedSize(), 0u);
+ EXPECT_EQ(original_total_size, GetMemoryCache()->size());
+ EXPECT_TRUE(GetMemoryCache()->Contains(resource1));
+ EXPECT_TRUE(GetMemoryCache()->Contains(resource2));
+
+ // Removing the client from resource2 should not trigger pruning.
+ client2->RemoveAsClient();
+ EXPECT_GT(resource1->DecodedSize(), 0u);
+ EXPECT_GT(resource2->DecodedSize(), 0u);
+ EXPECT_EQ(original_total_size, GetMemoryCache()->size());
+ EXPECT_TRUE(GetMemoryCache()->Contains(resource1));
+ EXPECT_TRUE(GetMemoryCache()->Contains(resource2));
+
+ WeakPersistent<Resource> resource1_weak = resource1;
+ WeakPersistent<Resource> resource2_weak = resource2;
+
+ ThreadState::Current()->CollectGarbage(
+ BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
+ BlinkGC::kEagerSweeping, BlinkGC::kForcedGC);
+ // Resources are garbage-collected (WeakMemoryCache) and thus removed
+ // from MemoryCache.
+ EXPECT_FALSE(resource1_weak);
+ EXPECT_FALSE(resource2_weak);
+ EXPECT_EQ(0u, GetMemoryCache()->size());
+}
+
+TEST_F(MemoryCacheTest, ClientRemoval_Basic) {
+ TestClientRemoval(fetcher_, "", "");
+}
+
+TEST_F(MemoryCacheTest, ClientRemoval_MultipleResourceMaps) {
+ {
+ TestClientRemoval(fetcher_, "foo", "");
+ GetMemoryCache()->EvictResources();
+ }
+ {
+ TestClientRemoval(fetcher_, "", "foo");
+ GetMemoryCache()->EvictResources();
+ }
+ {
+ TestClientRemoval(fetcher_, "foo", "bar");
+ GetMemoryCache()->EvictResources();
+ }
+}
+
+TEST_F(MemoryCacheTest, RemoveDuringRevalidation) {
+ FakeResource* resource1 =
+ FakeResource::Create("http://test/resource", Resource::kRaw);
+ GetMemoryCache()->Add(resource1);
+
+ FakeResource* resource2 =
+ FakeResource::Create("http://test/resource", Resource::kRaw);
+ GetMemoryCache()->Remove(resource1);
+ GetMemoryCache()->Add(resource2);
+ EXPECT_TRUE(GetMemoryCache()->Contains(resource2));
+ EXPECT_FALSE(GetMemoryCache()->Contains(resource1));
+
+ FakeResource* resource3 =
+ FakeResource::Create("http://test/resource", Resource::kRaw);
+ GetMemoryCache()->Remove(resource2);
+ GetMemoryCache()->Add(resource3);
+ EXPECT_TRUE(GetMemoryCache()->Contains(resource3));
+ EXPECT_FALSE(GetMemoryCache()->Contains(resource2));
+}
+
+TEST_F(MemoryCacheTest, ResourceMapIsolation) {
+ FakeResource* resource1 =
+ FakeResource::Create("http://test/resource", Resource::kRaw);
+ GetMemoryCache()->Add(resource1);
+
+ FakeResource* resource2 =
+ FakeResource::Create("http://test/resource", Resource::kRaw);
+ resource2->SetCacheIdentifier("foo");
+ GetMemoryCache()->Add(resource2);
+ EXPECT_TRUE(GetMemoryCache()->Contains(resource1));
+ EXPECT_TRUE(GetMemoryCache()->Contains(resource2));
+
+ const KURL url = KURL("http://test/resource");
+ EXPECT_EQ(resource1, GetMemoryCache()->ResourceForURL(url));
+ EXPECT_EQ(resource1, GetMemoryCache()->ResourceForURL(
+ url, GetMemoryCache()->DefaultCacheIdentifier()));
+ EXPECT_EQ(resource2, GetMemoryCache()->ResourceForURL(url, "foo"));
+ EXPECT_EQ(nullptr, GetMemoryCache()->ResourceForURL(NullURL()));
+
+ FakeResource* resource3 =
+ FakeResource::Create("http://test/resource", Resource::kRaw);
+ resource3->SetCacheIdentifier("foo");
+ GetMemoryCache()->Remove(resource2);
+ GetMemoryCache()->Add(resource3);
+ EXPECT_TRUE(GetMemoryCache()->Contains(resource1));
+ EXPECT_FALSE(GetMemoryCache()->Contains(resource2));
+ EXPECT_TRUE(GetMemoryCache()->Contains(resource3));
+
+ HeapVector<Member<Resource>> resources =
+ GetMemoryCache()->ResourcesForURL(url);
+ EXPECT_EQ(2u, resources.size());
+
+ GetMemoryCache()->EvictResources();
+ EXPECT_FALSE(GetMemoryCache()->Contains(resource1));
+ EXPECT_FALSE(GetMemoryCache()->Contains(resource3));
+}
+
+TEST_F(MemoryCacheTest, FragmentIdentifier) {
+ const KURL url1 = KURL("http://test/resource#foo");
+ FakeResource* resource = FakeResource::Create(url1, Resource::kRaw);
+ GetMemoryCache()->Add(resource);
+ EXPECT_TRUE(GetMemoryCache()->Contains(resource));
+
+ EXPECT_EQ(resource, GetMemoryCache()->ResourceForURL(url1));
+
+ const KURL url2 = MemoryCache::RemoveFragmentIdentifierIfNeeded(url1);
+ EXPECT_EQ(resource, GetMemoryCache()->ResourceForURL(url2));
+}
+
+TEST_F(MemoryCacheTest, RemoveURLFromCache) {
+ const KURL url1 = KURL("http://test/resource1");
+ Persistent<FakeResource> resource1 =
+ FakeResource::Create(url1, Resource::kRaw);
+ GetMemoryCache()->Add(resource1);
+ EXPECT_TRUE(GetMemoryCache()->Contains(resource1));
+
+ GetMemoryCache()->RemoveURLFromCache(url1);
+ EXPECT_FALSE(GetMemoryCache()->Contains(resource1));
+
+ const KURL url2 = KURL("http://test/resource2#foo");
+ FakeResource* resource2 = FakeResource::Create(url2, Resource::kRaw);
+ GetMemoryCache()->Add(resource2);
+ EXPECT_TRUE(GetMemoryCache()->Contains(resource2));
+
+ GetMemoryCache()->RemoveURLFromCache(url2);
+ EXPECT_FALSE(GetMemoryCache()->Contains(resource2));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/preload_key.h b/chromium/third_party/blink/renderer/platform/loader/fetch/preload_key.h
new file mode 100644
index 00000000000..81273b6808a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/preload_key.h
@@ -0,0 +1,75 @@
+// Copyright 2017 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_PLATFORM_LOADER_FETCH_PRELOAD_KEY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_PRELOAD_KEY_H_
+
+#include "third_party/blink/renderer/platform/loader/fetch/resource.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_traits.h"
+
+namespace blink {
+
+// PreloadKey is a key type of the preloads map in a fetch group (a.k.a.
+// blink::ResourceFetcher).
+struct PreloadKey final {
+ public:
+ struct Hash {
+ STATIC_ONLY(Hash);
+
+ public:
+ static unsigned GetHash(const PreloadKey& key) {
+ return KURLHash::GetHash(key.url);
+ }
+ static bool Equal(const PreloadKey& x, const PreloadKey& y) {
+ return x == y;
+ }
+ static constexpr bool safe_to_compare_to_empty_or_deleted = false;
+ };
+
+ PreloadKey() = default;
+ PreloadKey(const KURL& url, Resource::Type type)
+ : url(RemoveFragmentFromUrl(url)), type(type) {}
+
+ bool operator==(const PreloadKey& x) const {
+ return url == x.url && type == x.type;
+ }
+
+ static KURL RemoveFragmentFromUrl(const KURL& src) {
+ if (!src.HasFragmentIdentifier())
+ return src;
+ KURL url = src;
+ url.RemoveFragmentIdentifier();
+ return url;
+ }
+
+ KURL url;
+ Resource::Type type = Resource::kMainResource;
+};
+
+} // namespace blink
+
+namespace WTF {
+
+template <>
+struct DefaultHash<blink::PreloadKey> {
+ using Hash = blink::PreloadKey::Hash;
+};
+
+template <>
+struct HashTraits<blink::PreloadKey>
+ : public SimpleClassHashTraits<blink::PreloadKey> {
+ static bool IsDeletedValue(const blink::PreloadKey& value) {
+ return HashTraits<blink::KURL>::IsDeletedValue(value.url);
+ }
+
+ static void ConstructDeletedValue(blink::PreloadKey& slot, bool zero_value) {
+ HashTraits<blink::KURL>::ConstructDeletedValue(slot.url, zero_value);
+ }
+};
+
+} // namespace WTF
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_PRELOAD_KEY_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc
new file mode 100644
index 00000000000..083f95fa3f8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc
@@ -0,0 +1,470 @@
+/*
+ * Copyright (C) 2011 Google Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "third_party/blink/renderer/platform/loader/fetch/raw_resource.h"
+
+#include <memory>
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "services/network/public/mojom/request_context_frame_type.mojom-shared.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
+#include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_client_walker.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
+#include "third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.h"
+#include "third_party/blink/renderer/platform/network/http_names.h"
+#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler.h"
+
+namespace blink {
+
+RawResource* RawResource::FetchSynchronously(FetchParameters& params,
+ ResourceFetcher* fetcher) {
+ params.MakeSynchronous();
+ return ToRawResource(fetcher->RequestResource(
+ params, RawResourceFactory(Resource::kRaw), nullptr));
+}
+
+RawResource* RawResource::FetchImport(FetchParameters& params,
+ ResourceFetcher* fetcher,
+ RawResourceClient* client) {
+ DCHECK_EQ(params.GetResourceRequest().GetFrameType(),
+ network::mojom::RequestContextFrameType::kNone);
+ params.SetRequestContext(WebURLRequest::kRequestContextImport);
+ return ToRawResource(fetcher->RequestResource(
+ params, RawResourceFactory(Resource::kImportResource), client));
+}
+
+RawResource* RawResource::Fetch(FetchParameters& params,
+ ResourceFetcher* fetcher,
+ RawResourceClient* client) {
+ DCHECK_EQ(params.GetResourceRequest().GetFrameType(),
+ network::mojom::RequestContextFrameType::kNone);
+ DCHECK_NE(params.GetResourceRequest().GetRequestContext(),
+ WebURLRequest::kRequestContextUnspecified);
+ return ToRawResource(fetcher->RequestResource(
+ params, RawResourceFactory(Resource::kRaw), client));
+}
+
+RawResource* RawResource::FetchMainResource(
+ FetchParameters& params,
+ ResourceFetcher* fetcher,
+ RawResourceClient* client,
+ const SubstituteData& substitute_data) {
+ DCHECK_NE(params.GetResourceRequest().GetFrameType(),
+ network::mojom::RequestContextFrameType::kNone);
+ DCHECK(params.GetResourceRequest().GetRequestContext() ==
+ WebURLRequest::kRequestContextForm ||
+ params.GetResourceRequest().GetRequestContext() ==
+ WebURLRequest::kRequestContextFrame ||
+ params.GetResourceRequest().GetRequestContext() ==
+ WebURLRequest::kRequestContextHyperlink ||
+ params.GetResourceRequest().GetRequestContext() ==
+ WebURLRequest::kRequestContextIframe ||
+ params.GetResourceRequest().GetRequestContext() ==
+ WebURLRequest::kRequestContextInternal ||
+ params.GetResourceRequest().GetRequestContext() ==
+ WebURLRequest::kRequestContextLocation);
+
+ return ToRawResource(fetcher->RequestResource(
+ params, RawResourceFactory(Resource::kMainResource), client,
+ substitute_data));
+}
+
+RawResource* RawResource::FetchMedia(FetchParameters& params,
+ ResourceFetcher* fetcher,
+ RawResourceClient* client) {
+ DCHECK_EQ(params.GetResourceRequest().GetFrameType(),
+ network::mojom::RequestContextFrameType::kNone);
+ auto context = params.GetResourceRequest().GetRequestContext();
+ DCHECK(context == WebURLRequest::kRequestContextAudio ||
+ context == WebURLRequest::kRequestContextVideo);
+ Resource::Type type = (context == WebURLRequest::kRequestContextAudio)
+ ? Resource::kAudio
+ : Resource::kVideo;
+ return ToRawResource(
+ fetcher->RequestResource(params, RawResourceFactory(type), client));
+}
+
+RawResource* RawResource::FetchTextTrack(FetchParameters& params,
+ ResourceFetcher* fetcher,
+ RawResourceClient* client) {
+ DCHECK_EQ(params.GetResourceRequest().GetFrameType(),
+ network::mojom::RequestContextFrameType::kNone);
+ params.SetRequestContext(WebURLRequest::kRequestContextTrack);
+ return ToRawResource(fetcher->RequestResource(
+ params, RawResourceFactory(Resource::kTextTrack), client));
+}
+
+RawResource* RawResource::FetchManifest(FetchParameters& params,
+ ResourceFetcher* fetcher,
+ RawResourceClient* client) {
+ DCHECK_EQ(params.GetResourceRequest().GetFrameType(),
+ network::mojom::RequestContextFrameType::kNone);
+ DCHECK_EQ(params.GetResourceRequest().GetRequestContext(),
+ WebURLRequest::kRequestContextManifest);
+ return ToRawResource(fetcher->RequestResource(
+ params, RawResourceFactory(Resource::kManifest), client));
+}
+
+RawResource::RawResource(const ResourceRequest& resource_request,
+ Type type,
+ const ResourceLoaderOptions& options)
+ : Resource(resource_request, type, options) {}
+
+void RawResource::AppendData(const char* data, size_t length) {
+ if (data_pipe_writer_) {
+ DCHECK_EQ(kDoNotBufferData, GetDataBufferingPolicy());
+ data_pipe_writer_->Write(data, length);
+ } else {
+ Resource::AppendData(data, length);
+ }
+}
+
+void RawResource::DidAddClient(ResourceClient* c) {
+ // CHECK()/RevalidationStartForbiddenScope are for
+ // https://crbug.com/640960#c24.
+ CHECK(!IsCacheValidator());
+ if (!HasClient(c))
+ return;
+ DCHECK(RawResourceClient::IsExpectedType(c));
+ RevalidationStartForbiddenScope revalidation_start_forbidden_scope(this);
+ RawResourceClient* client = static_cast<RawResourceClient*>(c);
+ for (const auto& redirect : RedirectChain()) {
+ ResourceRequest request(redirect.request_);
+ client->RedirectReceived(this, request, redirect.redirect_response_);
+ if (!HasClient(c))
+ return;
+ }
+
+ if (!GetResponse().IsNull()) {
+ client->ResponseReceived(this, GetResponse(),
+ std::move(data_consumer_handle_));
+ }
+ if (!HasClient(c))
+ return;
+ Resource::DidAddClient(client);
+}
+
+bool RawResource::WillFollowRedirect(
+ const ResourceRequest& new_request,
+ const ResourceResponse& redirect_response) {
+ bool follow = Resource::WillFollowRedirect(new_request, redirect_response);
+ // The base class method takes a const reference of a ResourceRequest and
+ // returns bool just for allowing RawResource to reject redirect. It must
+ // always return true.
+ DCHECK(follow);
+
+ DCHECK(!redirect_response.IsNull());
+ ResourceClientWalker<RawResourceClient> w(Clients());
+ while (RawResourceClient* c = w.Next()) {
+ if (!c->RedirectReceived(this, new_request, redirect_response))
+ follow = false;
+ }
+
+ return follow;
+}
+
+void RawResource::WillNotFollowRedirect() {
+ ResourceClientWalker<RawResourceClient> w(Clients());
+ while (RawResourceClient* c = w.Next())
+ c->RedirectBlocked();
+}
+
+SourceKeyedCachedMetadataHandler* RawResource::CacheHandler() {
+ return static_cast<SourceKeyedCachedMetadataHandler*>(
+ Resource::CacheHandler());
+}
+
+void RawResource::ResponseReceived(
+ const ResourceResponse& response,
+ std::unique_ptr<WebDataConsumerHandle> handle) {
+ if (response.WasFallbackRequiredByServiceWorker()) {
+ // The ServiceWorker asked us to re-fetch the request. This resource must
+ // not be reused.
+ // Note: This logic is needed here because DocumentThreadableLoader handles
+ // CORS independently from ResourceLoader. Fix it.
+ if (IsMainThread())
+ GetMemoryCache()->Remove(this);
+ }
+
+ Resource::ResponseReceived(response, nullptr);
+
+ DCHECK(!handle || !data_consumer_handle_);
+ if (!handle && Clients().size() > 0)
+ handle = std::move(data_consumer_handle_);
+ ResourceClientWalker<RawResourceClient> w(Clients());
+ DCHECK(Clients().size() <= 1 || !handle);
+ while (RawResourceClient* c = w.Next()) {
+ // |handle| is cleared when passed, but it's not a problem because |handle|
+ // is null when there are two or more clients, as asserted.
+ c->ResponseReceived(this, this->GetResponse(), std::move(handle));
+ }
+}
+
+CachedMetadataHandler* RawResource::CreateCachedMetadataHandler(
+ std::unique_ptr<CachedMetadataSender> send_callback) {
+ return new SourceKeyedCachedMetadataHandler(Encoding(),
+ std::move(send_callback));
+}
+
+void RawResource::SetSerializedCachedMetadata(const char* data, size_t size) {
+ Resource::SetSerializedCachedMetadata(data, size);
+
+ SourceKeyedCachedMetadataHandler* cache_handler = CacheHandler();
+ if (cache_handler) {
+ cache_handler->SetSerializedCachedMetadata(data, size);
+ }
+
+ ResourceClientWalker<RawResourceClient> w(Clients());
+ while (RawResourceClient* c = w.Next())
+ c->SetSerializedCachedMetadata(this, data, size);
+}
+
+void RawResource::DidSendData(unsigned long long bytes_sent,
+ unsigned long long total_bytes_to_be_sent) {
+ ResourceClientWalker<RawResourceClient> w(Clients());
+ while (RawResourceClient* c = w.Next())
+ c->DataSent(this, bytes_sent, total_bytes_to_be_sent);
+}
+
+void RawResource::DidDownloadData(int data_length) {
+ downloaded_file_length_ =
+ (downloaded_file_length_ ? *downloaded_file_length_ : 0) + data_length;
+ ResourceClientWalker<RawResourceClient> w(Clients());
+ while (RawResourceClient* c = w.Next())
+ c->DataDownloaded(this, data_length);
+}
+
+void RawResource::DidDownloadToBlob(scoped_refptr<BlobDataHandle> blob) {
+ downloaded_blob_ = blob;
+ ResourceClientWalker<RawResourceClient> w(Clients());
+ while (RawResourceClient* c = w.Next())
+ c->DidDownloadToBlob(this, blob);
+}
+
+void RawResource::ReportResourceTimingToClients(
+ const ResourceTimingInfo& info) {
+ ResourceClientWalker<RawResourceClient> w(Clients());
+ while (RawResourceClient* c = w.Next())
+ c->DidReceiveResourceTiming(this, info);
+}
+
+bool RawResource::MatchPreload(const FetchParameters& params,
+ base::SingleThreadTaskRunner* task_runner) {
+ if (!Resource::MatchPreload(params, task_runner))
+ return false;
+
+ // This is needed to call Platform::Current() below. Remove this branch
+ // when the calls are removed.
+ if (!IsMainThread())
+ return false;
+
+ if (!params.GetResourceRequest().UseStreamOnResponse())
+ return true;
+
+ if (ErrorOccurred())
+ return true;
+
+ // A preloaded resource is not for streaming.
+ DCHECK(!GetResourceRequest().UseStreamOnResponse());
+ DCHECK_EQ(GetDataBufferingPolicy(), kBufferData);
+
+ // Preloading for raw resources are not cached.
+ DCHECK(!IsMainThread() || !GetMemoryCache()->Contains(this));
+
+ constexpr auto kCapacity = 32 * 1024;
+ mojo::ScopedDataPipeProducerHandle producer;
+ mojo::ScopedDataPipeConsumerHandle consumer;
+ MojoCreateDataPipeOptions options;
+ options.struct_size = sizeof(MojoCreateDataPipeOptions);
+ options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE;
+ options.element_num_bytes = 1;
+ options.capacity_num_bytes = kCapacity;
+
+ MojoResult result = mojo::CreateDataPipe(&options, &producer, &consumer);
+ if (result != MOJO_RESULT_OK)
+ return false;
+
+ data_consumer_handle_ =
+ Platform::Current()->CreateDataConsumerHandle(std::move(consumer));
+ data_pipe_writer_ = std::make_unique<BufferingDataPipeWriter>(
+ std::move(producer), task_runner);
+
+ if (Data()) {
+ Data()->ForEachSegment(
+ [this](const char* segment, size_t size, size_t offset) -> bool {
+ return data_pipe_writer_->Write(segment, size);
+ });
+ }
+ SetDataBufferingPolicy(kDoNotBufferData);
+
+ if (IsLoaded())
+ data_pipe_writer_->Finish();
+ return true;
+}
+
+void RawResource::NotifyFinished() {
+ if (data_pipe_writer_)
+ data_pipe_writer_->Finish();
+ Resource::NotifyFinished();
+}
+
+static bool ShouldIgnoreHeaderForCacheReuse(AtomicString header_name) {
+ // FIXME: This list of headers that don't affect cache policy almost certainly
+ // isn't complete.
+ DEFINE_STATIC_LOCAL(
+ HashSet<AtomicString>, headers,
+ ({"Cache-Control", "If-Modified-Since", "If-None-Match", "Origin",
+ "Pragma", "Purpose", "Referer", "User-Agent",
+ HTTPNames::X_DevTools_Emulate_Network_Conditions_Client_Id}));
+ return headers.Contains(header_name);
+}
+
+static bool IsCacheableHTTPMethod(const AtomicString& method) {
+ // Per http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.10,
+ // these methods always invalidate the cache entry.
+ return method != HTTPNames::POST && method != HTTPNames::PUT &&
+ method != "DELETE";
+}
+
+bool RawResource::CanReuse(
+ const FetchParameters& new_fetch_parameters,
+ scoped_refptr<const SecurityOrigin> new_source_origin) const {
+ const ResourceRequest& new_request =
+ new_fetch_parameters.GetResourceRequest();
+
+ if (GetDataBufferingPolicy() == kDoNotBufferData)
+ return false;
+
+ if (!IsCacheableHTTPMethod(GetResourceRequest().HttpMethod()))
+ return false;
+ if (GetResourceRequest().HttpMethod() != new_request.HttpMethod())
+ return false;
+
+ if (GetResourceRequest().HttpBody() != new_request.HttpBody())
+ return false;
+
+ if (GetResourceRequest().AllowStoredCredentials() !=
+ new_request.AllowStoredCredentials())
+ return false;
+
+ // Ensure most headers match the existing headers before continuing. Note that
+ // the list of ignored headers includes some headers explicitly related to
+ // caching. A more detailed check of caching policy will be performed later,
+ // this is simply a list of headers that we might permit to be different and
+ // still reuse the existing Resource.
+ const HTTPHeaderMap& new_headers = new_request.HttpHeaderFields();
+ const HTTPHeaderMap& old_headers = GetResourceRequest().HttpHeaderFields();
+
+ for (const auto& header : new_headers) {
+ AtomicString header_name = header.key;
+ if (!ShouldIgnoreHeaderForCacheReuse(header_name) &&
+ header.value != old_headers.Get(header_name))
+ return false;
+ }
+
+ for (const auto& header : old_headers) {
+ AtomicString header_name = header.key;
+ if (!ShouldIgnoreHeaderForCacheReuse(header_name) &&
+ header.value != new_headers.Get(header_name))
+ return false;
+ }
+
+ return Resource::CanReuse(new_fetch_parameters, std::move(new_source_origin));
+}
+
+RawResourceClientStateChecker::RawResourceClientStateChecker()
+ : state_(kNotAddedAsClient) {}
+
+RawResourceClientStateChecker::~RawResourceClientStateChecker() = default;
+
+NEVER_INLINE void RawResourceClientStateChecker::WillAddClient() {
+ SECURITY_CHECK(state_ == kNotAddedAsClient);
+ state_ = kStarted;
+}
+
+NEVER_INLINE void RawResourceClientStateChecker::WillRemoveClient() {
+ SECURITY_CHECK(state_ != kNotAddedAsClient);
+ state_ = kNotAddedAsClient;
+}
+
+NEVER_INLINE void RawResourceClientStateChecker::RedirectReceived() {
+ SECURITY_CHECK(state_ == kStarted);
+}
+
+NEVER_INLINE void RawResourceClientStateChecker::RedirectBlocked() {
+ SECURITY_CHECK(state_ == kStarted);
+ state_ = kRedirectBlocked;
+}
+
+NEVER_INLINE void RawResourceClientStateChecker::DataSent() {
+ SECURITY_CHECK(state_ == kStarted);
+}
+
+NEVER_INLINE void RawResourceClientStateChecker::ResponseReceived() {
+ SECURITY_CHECK(state_ == kStarted);
+ state_ = kResponseReceived;
+}
+
+NEVER_INLINE void RawResourceClientStateChecker::SetSerializedCachedMetadata() {
+ SECURITY_CHECK(state_ == kResponseReceived);
+ state_ = kSetSerializedCachedMetadata;
+}
+
+NEVER_INLINE void RawResourceClientStateChecker::DataReceived() {
+ SECURITY_CHECK(state_ == kResponseReceived ||
+ state_ == kSetSerializedCachedMetadata ||
+ state_ == kDataReceived);
+ state_ = kDataReceived;
+}
+
+NEVER_INLINE void RawResourceClientStateChecker::DataDownloaded() {
+ SECURITY_CHECK(state_ == kResponseReceived ||
+ state_ == kSetSerializedCachedMetadata ||
+ state_ == kDataDownloaded);
+ state_ = kDataDownloaded;
+}
+
+NEVER_INLINE void RawResourceClientStateChecker::DidDownloadToBlob() {
+ SECURITY_CHECK(state_ == kResponseReceived ||
+ state_ == kSetSerializedCachedMetadata ||
+ state_ == kDataDownloaded);
+ state_ = kDidDownloadToBlob;
+}
+
+NEVER_INLINE void RawResourceClientStateChecker::NotifyFinished(
+ Resource* resource) {
+ SECURITY_CHECK(state_ != kNotAddedAsClient);
+ SECURITY_CHECK(state_ != kNotifyFinished);
+ SECURITY_CHECK(resource->ErrorOccurred() ||
+ (state_ == kResponseReceived ||
+ state_ == kSetSerializedCachedMetadata ||
+ state_ == kDataReceived || state_ == kDataDownloaded ||
+ state_ == kDidDownloadToBlob));
+ state_ = kNotifyFinished;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource.h b/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource.h
new file mode 100644
index 00000000000..88c232ccea0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource.h
@@ -0,0 +1,247 @@
+/*
+ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2001 Dirk Mueller <mueller@kde.org>
+ Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
+ Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RAW_RESOURCE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RAW_RESOURCE_H_
+
+#include <memory>
+
+#include "third_party/blink/public/platform/web_data_consumer_handle.h"
+#include "third_party/blink/renderer/platform/loader/fetch/buffering_data_pipe_writer.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_client.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+
+namespace blink {
+class WebDataConsumerHandle;
+class FetchParameters;
+class RawResourceClient;
+class ResourceFetcher;
+class SubstituteData;
+class SourceKeyedCachedMetadataHandler;
+
+class PLATFORM_EXPORT RawResource final : public Resource {
+ public:
+ static RawResource* FetchSynchronously(FetchParameters&, ResourceFetcher*);
+ static RawResource* Fetch(FetchParameters&,
+ ResourceFetcher*,
+ RawResourceClient*);
+ static RawResource* FetchMainResource(FetchParameters&,
+ ResourceFetcher*,
+ RawResourceClient*,
+ const SubstituteData&);
+ static RawResource* FetchImport(FetchParameters&,
+ ResourceFetcher*,
+ RawResourceClient*);
+ static RawResource* FetchMedia(FetchParameters&,
+ ResourceFetcher*,
+ RawResourceClient*);
+ static RawResource* FetchTextTrack(FetchParameters&,
+ ResourceFetcher*,
+ RawResourceClient*);
+ static RawResource* FetchManifest(FetchParameters&,
+ ResourceFetcher*,
+ RawResourceClient*);
+
+ // Exposed for testing
+ static RawResource* CreateForTest(ResourceRequest request, Type type) {
+ ResourceLoaderOptions options;
+ return new RawResource(request, type, options);
+ }
+ static RawResource* CreateForTest(const KURL& url, Type type) {
+ ResourceRequest request(url);
+ return CreateForTest(request, type);
+ }
+ static RawResource* CreateForTest(const char* url, Type type) {
+ return CreateForTest(KURL(url), type);
+ }
+
+ // Resource implementation
+ bool CanReuse(
+ const FetchParameters&,
+ scoped_refptr<const SecurityOrigin> new_source_origin) const override;
+ bool WillFollowRedirect(const ResourceRequest&,
+ const ResourceResponse&) override;
+
+ void SetSerializedCachedMetadata(const char*, size_t) override;
+
+ // Used for code caching of scripts with source code inline in the HTML.
+ // Returns a cache handler which can store multiple cache metadata entries,
+ // keyed by the source code of the script.
+ SourceKeyedCachedMetadataHandler* CacheHandler();
+
+ WTF::Optional<int64_t> DownloadedFileLength() const {
+ return downloaded_file_length_;
+ }
+ scoped_refptr<BlobDataHandle> DownloadedBlob() const {
+ return downloaded_blob_;
+ }
+
+ protected:
+ CachedMetadataHandler* CreateCachedMetadataHandler(
+ std::unique_ptr<CachedMetadataSender> send_callback) override;
+
+ private:
+ class RawResourceFactory : public NonTextResourceFactory {
+ public:
+ explicit RawResourceFactory(Resource::Type type)
+ : NonTextResourceFactory(type) {}
+
+ Resource* Create(const ResourceRequest& request,
+ const ResourceLoaderOptions& options) const override {
+ return new RawResource(request, type_, options);
+ }
+ };
+
+ RawResource(const ResourceRequest&, Type, const ResourceLoaderOptions&);
+
+ // Resource implementation
+ void DidAddClient(ResourceClient*) override;
+ void AppendData(const char*, size_t) override;
+ bool ShouldIgnoreHTTPStatusCodeErrors() const override {
+ return !IsLinkPreload();
+ }
+ void WillNotFollowRedirect() override;
+ void ResponseReceived(const ResourceResponse&,
+ std::unique_ptr<WebDataConsumerHandle>) override;
+ void DidSendData(unsigned long long bytes_sent,
+ unsigned long long total_bytes_to_be_sent) override;
+ void DidDownloadData(int) override;
+ void DidDownloadToBlob(scoped_refptr<BlobDataHandle>) override;
+ void ReportResourceTimingToClients(const ResourceTimingInfo&) override;
+ bool MatchPreload(const FetchParameters&,
+ base::SingleThreadTaskRunner*) override;
+ void NotifyFinished() override;
+
+ WTF::Optional<int64_t> downloaded_file_length_;
+ scoped_refptr<BlobDataHandle> downloaded_blob_;
+
+ // Used for preload matching.
+ std::unique_ptr<BufferingDataPipeWriter> data_pipe_writer_;
+ std::unique_ptr<WebDataConsumerHandle> data_consumer_handle_;
+};
+
+// TODO(yhirano): Recover #if ENABLE_SECURITY_ASSERT when we stop adding
+// RawResources to MemoryCache.
+inline bool IsRawResource(const Resource& resource) {
+ Resource::Type type = resource.GetType();
+ return type == Resource::kMainResource || type == Resource::kRaw ||
+ type == Resource::kTextTrack || type == Resource::kAudio ||
+ type == Resource::kVideo || type == Resource::kManifest ||
+ type == Resource::kImportResource;
+}
+inline RawResource* ToRawResource(Resource* resource) {
+ SECURITY_DCHECK(!resource || IsRawResource(*resource));
+ return static_cast<RawResource*>(resource);
+}
+
+class PLATFORM_EXPORT RawResourceClient : public ResourceClient {
+ public:
+ static bool IsExpectedType(ResourceClient* client) {
+ return client->GetResourceClientType() == kRawResourceType;
+ }
+ ResourceClientType GetResourceClientType() const final {
+ return kRawResourceType;
+ }
+
+ // The order of the callbacks is as follows:
+ // [Case 1] A successful load:
+ // 0+ redirectReceived() and/or dataSent()
+ // 1 responseReceived()
+ // 0-1 setSerializedCachedMetadata()
+ // 0+ dataReceived() or dataDownloaded(), but never both
+ // 1 notifyFinished() with errorOccurred() = false
+ // [Case 2] When redirect is blocked:
+ // 0+ redirectReceived() and/or dataSent()
+ // 1 redirectBlocked()
+ // 1 notifyFinished() with errorOccurred() = true
+ // [Case 3] Other failures:
+ // notifyFinished() with errorOccurred() = true is called at any time
+ // (unless notifyFinished() is already called).
+ // In all cases:
+ // No callbacks are made after notifyFinished() or
+ // removeClient() is called.
+ virtual void DataSent(Resource*,
+ unsigned long long /* bytesSent */,
+ unsigned long long /* totalBytesToBeSent */) {}
+ virtual void ResponseReceived(Resource*,
+ const ResourceResponse&,
+ std::unique_ptr<WebDataConsumerHandle>) {}
+ virtual void SetSerializedCachedMetadata(Resource*, const char*, size_t) {}
+ virtual bool RedirectReceived(Resource*,
+ const ResourceRequest&,
+ const ResourceResponse&) {
+ return true;
+ }
+ virtual void RedirectBlocked() {}
+ virtual void DataDownloaded(Resource*, int) {}
+ virtual void DidReceiveResourceTiming(Resource*, const ResourceTimingInfo&) {}
+ // Called for requests that had DownloadToBlob set to true. Can be called with
+ // null if creating the blob failed for some reason (but the download itself
+ // otherwise succeeded). Could also not be called at all if the downloaded
+ // resource ended up being zero bytes.
+ virtual void DidDownloadToBlob(Resource*, scoped_refptr<BlobDataHandle>) {}
+};
+
+// Checks the sequence of callbacks of RawResourceClient. This can be used only
+// when a RawResourceClient is added as a client to at most one RawResource.
+class PLATFORM_EXPORT RawResourceClientStateChecker final {
+ public:
+ RawResourceClientStateChecker();
+ ~RawResourceClientStateChecker();
+
+ // Call before addClient()/removeClient() is called.
+ void WillAddClient();
+ void WillRemoveClient();
+
+ // Call RawResourceClientStateChecker::f() at the beginning of
+ // RawResourceClient::f().
+ void RedirectReceived();
+ void RedirectBlocked();
+ void DataSent();
+ void ResponseReceived();
+ void SetSerializedCachedMetadata();
+ void DataReceived();
+ void DataDownloaded();
+ void DidDownloadToBlob();
+ void NotifyFinished(Resource*);
+
+ private:
+ enum State {
+ kNotAddedAsClient,
+ kStarted,
+ kRedirectBlocked,
+ kResponseReceived,
+ kSetSerializedCachedMetadata,
+ kDataReceived,
+ kDataDownloaded,
+ kDidDownloadToBlob,
+ kNotifyFinished
+ };
+ State state_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RAW_RESOURCE_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource_test.cc
new file mode 100644
index 00000000000..3a973b9476e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource_test.cc
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2013, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "third_party/blink/renderer/platform/loader/fetch/raw_resource.h"
+
+#include <memory>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/public/platform/web_url_response.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h"
+#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+
+namespace blink {
+
+class RawResourceTest : public testing::Test {
+ public:
+ RawResourceTest() = default;
+ ~RawResourceTest() override = default;
+
+ protected:
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RawResourceTest);
+};
+
+TEST_F(RawResourceTest, DontIgnoreAcceptForCacheReuse) {
+ ResourceRequest jpeg_request;
+ jpeg_request.SetHTTPAccept("image/jpeg");
+
+ scoped_refptr<const SecurityOrigin> source_origin =
+ SecurityOrigin::CreateUnique();
+
+ RawResource* jpeg_resource(
+ RawResource::CreateForTest(jpeg_request, Resource::kRaw));
+ jpeg_resource->SetSourceOrigin(source_origin);
+
+ ResourceRequest png_request;
+ png_request.SetHTTPAccept("image/png");
+
+ EXPECT_FALSE(
+ jpeg_resource->CanReuse(FetchParameters(png_request), source_origin));
+}
+
+class DummyClient final : public GarbageCollectedFinalized<DummyClient>,
+ public RawResourceClient {
+ USING_GARBAGE_COLLECTED_MIXIN(DummyClient);
+
+ public:
+ DummyClient() : called_(false), number_of_redirects_received_(0) {}
+ ~DummyClient() override = default;
+
+ // ResourceClient implementation.
+ void NotifyFinished(Resource* resource) override { called_ = true; }
+ String DebugName() const override { return "DummyClient"; }
+
+ void DataReceived(Resource*, const char* data, size_t length) override {
+ data_.Append(data, length);
+ }
+
+ bool RedirectReceived(Resource*,
+ const ResourceRequest&,
+ const ResourceResponse&) override {
+ ++number_of_redirects_received_;
+ return true;
+ }
+
+ bool Called() { return called_; }
+ int NumberOfRedirectsReceived() const {
+ return number_of_redirects_received_;
+ }
+ const Vector<char>& Data() { return data_; }
+ void Trace(blink::Visitor* visitor) override {
+ RawResourceClient::Trace(visitor);
+ }
+
+ private:
+ bool called_;
+ int number_of_redirects_received_;
+ Vector<char> data_;
+};
+
+// This client adds another client when notified.
+class AddingClient final : public GarbageCollectedFinalized<AddingClient>,
+ public RawResourceClient {
+ USING_GARBAGE_COLLECTED_MIXIN(AddingClient);
+
+ public:
+ AddingClient(DummyClient* client, Resource* resource)
+ : dummy_client_(client), resource_(resource) {}
+
+ ~AddingClient() override = default;
+
+ // ResourceClient implementation.
+ void NotifyFinished(Resource* resource) override {
+ // First schedule an asynchronous task to remove the client.
+ // We do not expect a client to be called if the client is removed before
+ // a callback invocation task queued inside addClient() is scheduled.
+ Platform::Current()->CurrentThread()->GetTaskRunner()->PostTask(
+ FROM_HERE,
+ WTF::Bind(&AddingClient::RemoveClient, WrapPersistent(this)));
+ resource->AddClient(
+ dummy_client_,
+ Platform::Current()->CurrentThread()->GetTaskRunner().get());
+ }
+ String DebugName() const override { return "AddingClient"; }
+
+ void RemoveClient() { resource_->RemoveClient(dummy_client_); }
+
+ void Trace(blink::Visitor* visitor) override {
+ visitor->Trace(dummy_client_);
+ visitor->Trace(resource_);
+ RawResourceClient::Trace(visitor);
+ }
+
+ private:
+ Member<DummyClient> dummy_client_;
+ Member<Resource> resource_;
+};
+
+TEST_F(RawResourceTest, AddClientDuringCallback) {
+ Resource* raw = RawResource::CreateForTest("data:text/html,", Resource::kRaw);
+ raw->SetResponse(ResourceResponse(KURL("http://600.613/")));
+ raw->FinishForTest();
+ EXPECT_FALSE(raw->GetResponse().IsNull());
+
+ Persistent<DummyClient> dummy_client = new DummyClient();
+ Persistent<AddingClient> adding_client =
+ new AddingClient(dummy_client.Get(), raw);
+ raw->AddClient(adding_client,
+ Platform::Current()->CurrentThread()->GetTaskRunner().get());
+ platform_->RunUntilIdle();
+ raw->RemoveClient(adding_client);
+ EXPECT_FALSE(dummy_client->Called());
+ EXPECT_FALSE(raw->IsAlive());
+}
+
+// This client removes another client when notified.
+class RemovingClient : public GarbageCollectedFinalized<RemovingClient>,
+ public RawResourceClient {
+ USING_GARBAGE_COLLECTED_MIXIN(RemovingClient);
+
+ public:
+ explicit RemovingClient(DummyClient* client) : dummy_client_(client) {}
+
+ ~RemovingClient() override = default;
+
+ // ResourceClient implementation.
+ void NotifyFinished(Resource* resource) override {
+ resource->RemoveClient(dummy_client_);
+ resource->RemoveClient(this);
+ }
+ String DebugName() const override { return "RemovingClient"; }
+ void Trace(blink::Visitor* visitor) override {
+ visitor->Trace(dummy_client_);
+ RawResourceClient::Trace(visitor);
+ }
+
+ private:
+ Member<DummyClient> dummy_client_;
+};
+
+TEST_F(RawResourceTest, RemoveClientDuringCallback) {
+ Resource* raw = RawResource::CreateForTest("data:text/html,", Resource::kRaw);
+ raw->SetResponse(ResourceResponse(KURL("http://600.613/")));
+ raw->FinishForTest();
+ EXPECT_FALSE(raw->GetResponse().IsNull());
+
+ Persistent<DummyClient> dummy_client = new DummyClient();
+ Persistent<RemovingClient> removing_client =
+ new RemovingClient(dummy_client.Get());
+ raw->AddClient(dummy_client,
+ Platform::Current()->CurrentThread()->GetTaskRunner().get());
+ raw->AddClient(removing_client,
+ Platform::Current()->CurrentThread()->GetTaskRunner().get());
+ platform_->RunUntilIdle();
+ EXPECT_FALSE(raw->IsAlive());
+}
+
+TEST_F(RawResourceTest,
+ CanReuseDevToolsEmulateNetworkConditionsClientIdHeader) {
+ scoped_refptr<const SecurityOrigin> source_origin =
+ SecurityOrigin::CreateUnique();
+ ResourceRequest request("data:text/html,");
+ request.SetHTTPHeaderField(
+ HTTPNames::X_DevTools_Emulate_Network_Conditions_Client_Id, "Foo");
+ Resource* raw = RawResource::CreateForTest(request, Resource::kRaw);
+ raw->SetSourceOrigin(source_origin);
+ EXPECT_TRUE(raw->CanReuse(FetchParameters(ResourceRequest("data:text/html,")),
+ source_origin));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource.cc
new file mode 100644
index 00000000000..13a3f06f3d0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource.cc
@@ -0,0 +1,1207 @@
+/*
+ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2001 Dirk Mueller (mueller@kde.org)
+ Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
+ Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
+ Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All
+ rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+
+#include <stdint.h>
+
+#include <algorithm>
+#include <cassert>
+#include <memory>
+
+#include "base/single_thread_task_runner.h"
+#include "build/build_config.h"
+#include "services/network/public/mojom/fetch_api.mojom-blink.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_security_origin.h"
+#include "third_party/blink/renderer/platform/histogram.h"
+#include "third_party/blink/renderer/platform/instance_counters.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/loader/cors/cors.h"
+#include "third_party/blink/renderer/platform/loader/fetch/cached_metadata.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_type_names.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
+#include "third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h"
+#include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_client.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_client_walker.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_finish_observer.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader.h"
+#include "third_party/blink/renderer/platform/network/http_parsers.h"
+#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+namespace {
+
+void NotifyFinishObservers(
+ HeapHashSet<WeakMember<ResourceFinishObserver>>* observers) {
+ for (const auto& observer : *observers)
+ observer->NotifyFinished();
+}
+
+} // namespace
+
+// These response headers are not copied from a revalidated response to the
+// cached response headers. For compatibility, this list is based on Chromium's
+// net/http/http_response_headers.cc.
+const char* const kHeadersToIgnoreAfterRevalidation[] = {
+ "allow",
+ "connection",
+ "etag",
+ "expires",
+ "keep-alive",
+ "last-modified",
+ "proxy-authenticate",
+ "proxy-connection",
+ "trailer",
+ "transfer-encoding",
+ "upgrade",
+ "www-authenticate",
+ "x-frame-options",
+ "x-xss-protection",
+};
+
+// Some header prefixes mean "Don't copy this header from a 304 response.".
+// Rather than listing all the relevant headers, we can consolidate them into
+// this list, also grabbed from Chromium's net/http/http_response_headers.cc.
+const char* const kHeaderPrefixesToIgnoreAfterRevalidation[] = {
+ "content-", "x-content-", "x-webkit-"};
+
+static inline bool ShouldUpdateHeaderAfterRevalidation(
+ const AtomicString& header) {
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(kHeadersToIgnoreAfterRevalidation);
+ i++) {
+ if (DeprecatedEqualIgnoringCase(header,
+ kHeadersToIgnoreAfterRevalidation[i]))
+ return false;
+ }
+ for (size_t i = 0;
+ i < WTF_ARRAY_LENGTH(kHeaderPrefixesToIgnoreAfterRevalidation); i++) {
+ if (header.StartsWithIgnoringASCIICase(
+ kHeaderPrefixesToIgnoreAfterRevalidation[i]))
+ return false;
+ }
+ return true;
+}
+
+// This is a CachedMetadataSender implementation for normal responses.
+class CachedMetadataSenderImpl : public CachedMetadataSender {
+ public:
+ CachedMetadataSenderImpl(const Resource*);
+ ~CachedMetadataSenderImpl() override = default;
+
+ void Send(const char*, size_t) override;
+ bool IsServedFromCacheStorage() override { return false; }
+
+ private:
+ const KURL response_url_;
+ const Time response_time_;
+};
+
+CachedMetadataSenderImpl::CachedMetadataSenderImpl(const Resource* resource)
+ : response_url_(resource->GetResponse().Url()),
+ response_time_(resource->GetResponse().ResponseTime()) {
+ DCHECK(resource->GetResponse().CacheStorageCacheName().IsNull());
+}
+
+void CachedMetadataSenderImpl::Send(const char* data, size_t size) {
+ Platform::Current()->CacheMetadata(response_url_, response_time_, data, size);
+}
+
+// This is a CachedMetadataSender implementation that does nothing.
+class NullCachedMetadataSender : public CachedMetadataSender {
+ public:
+ NullCachedMetadataSender() = default;
+ ~NullCachedMetadataSender() override = default;
+
+ void Send(const char*, size_t) override {}
+ bool IsServedFromCacheStorage() override { return false; }
+};
+
+// This is a CachedMetadataSender implementation for responses that are served
+// by a ServiceWorker from cache storage.
+class ServiceWorkerCachedMetadataSender : public CachedMetadataSender {
+ public:
+ ServiceWorkerCachedMetadataSender(const Resource*, const SecurityOrigin*);
+ ~ServiceWorkerCachedMetadataSender() override = default;
+
+ void Send(const char*, size_t) override;
+ bool IsServedFromCacheStorage() override { return true; }
+
+ private:
+ const KURL response_url_;
+ const Time response_time_;
+ const String cache_storage_cache_name_;
+ scoped_refptr<const SecurityOrigin> security_origin_;
+};
+
+ServiceWorkerCachedMetadataSender::ServiceWorkerCachedMetadataSender(
+ const Resource* resource,
+ const SecurityOrigin* security_origin)
+ : response_url_(resource->GetResponse().Url()),
+ response_time_(resource->GetResponse().ResponseTime()),
+ cache_storage_cache_name_(
+ resource->GetResponse().CacheStorageCacheName()),
+ security_origin_(security_origin) {
+ DCHECK(!cache_storage_cache_name_.IsNull());
+}
+
+void ServiceWorkerCachedMetadataSender::Send(const char* data, size_t size) {
+ Platform::Current()->CacheMetadataInCacheStorage(
+ response_url_, response_time_, data, size,
+ WebSecurityOrigin(security_origin_), cache_storage_cache_name_);
+}
+
+Resource::Resource(const ResourceRequest& request,
+ Type type,
+ const ResourceLoaderOptions& options)
+ : type_(type),
+ status_(ResourceStatus::kNotStarted),
+ load_finish_time_(0),
+ identifier_(0),
+ preload_discovery_time_(0.0),
+ encoded_size_(0),
+ encoded_size_memory_usage_(0),
+ decoded_size_(0),
+ overhead_size_(CalculateOverheadSize()),
+ cache_identifier_(MemoryCache::DefaultCacheIdentifier()),
+ link_preload_(false),
+ is_revalidating_(false),
+ is_alive_(false),
+ is_add_remove_client_prohibited_(false),
+ integrity_disposition_(ResourceIntegrityDisposition::kNotChecked),
+ options_(options),
+ response_timestamp_(CurrentTime()),
+ resource_request_(request) {
+ InstanceCounters::IncrementCounter(InstanceCounters::kResourceCounter);
+
+ if (IsMainThread())
+ MemoryCoordinator::Instance().RegisterClient(this);
+}
+
+Resource::~Resource() {
+ InstanceCounters::DecrementCounter(InstanceCounters::kResourceCounter);
+}
+
+void Resource::Trace(blink::Visitor* visitor) {
+ visitor->Trace(loader_);
+ visitor->Trace(cache_handler_);
+ visitor->Trace(clients_);
+ visitor->Trace(clients_awaiting_callback_);
+ visitor->Trace(finished_clients_);
+ visitor->Trace(finish_observers_);
+ MemoryCoordinatorClient::Trace(visitor);
+}
+
+void Resource::SetLoader(ResourceLoader* loader) {
+ CHECK(!loader_);
+ DCHECK(StillNeedsLoad());
+ loader_ = loader;
+ status_ = ResourceStatus::kPending;
+}
+
+void Resource::CheckResourceIntegrity() {
+ // Skip the check and reuse the previous check result, especially on
+ // successful revalidation.
+ if (IntegrityDisposition() != ResourceIntegrityDisposition::kNotChecked)
+ return;
+
+ // Loading error occurred? Then result is uncheckable.
+ integrity_report_info_.Clear();
+ if (ErrorOccurred()) {
+ CHECK(!Data());
+ integrity_disposition_ = ResourceIntegrityDisposition::kFailed;
+ return;
+ }
+
+ // No integrity attributes to check? Then we're passing.
+ if (IntegrityMetadata().IsEmpty()) {
+ integrity_disposition_ = ResourceIntegrityDisposition::kPassed;
+ return;
+ }
+
+ const char* data = nullptr;
+ size_t data_length = 0;
+
+ // Edge case: If a resource actually has zero bytes then it will not
+ // typically have a resource buffer, but we still need to check integrity
+ // because people might want to assert a zero-length resource.
+ CHECK(DecodedSize() == 0 || Data());
+ if (Data()) {
+ data = Data()->Data();
+ data_length = Data()->size();
+ }
+
+ if (SubresourceIntegrity::CheckSubresourceIntegrity(IntegrityMetadata(), data,
+ data_length, Url(), *this,
+ integrity_report_info_))
+ integrity_disposition_ = ResourceIntegrityDisposition::kPassed;
+ else
+ integrity_disposition_ = ResourceIntegrityDisposition::kFailed;
+ DCHECK_NE(IntegrityDisposition(), ResourceIntegrityDisposition::kNotChecked);
+}
+
+void Resource::NotifyFinished() {
+ DCHECK(IsLoaded());
+
+ ResourceClientWalker<ResourceClient> w(clients_);
+ while (ResourceClient* c = w.Next()) {
+ MarkClientFinished(c);
+ c->NotifyFinished(this);
+ }
+}
+
+void Resource::MarkClientFinished(ResourceClient* client) {
+ if (clients_.Contains(client)) {
+ finished_clients_.insert(client);
+ clients_.erase(client);
+ }
+}
+
+void Resource::AppendData(const char* data, size_t length) {
+ TRACE_EVENT0("blink", "Resource::appendData");
+ DCHECK(!is_revalidating_);
+ DCHECK(!ErrorOccurred());
+ if (options_.data_buffering_policy == kBufferData) {
+ if (data_)
+ data_->Append(data, length);
+ else
+ data_ = SharedBuffer::Create(data, length);
+ SetEncodedSize(data_->size());
+ }
+ ResourceClientWalker<ResourceClient> w(Clients());
+ while (ResourceClient* c = w.Next())
+ c->DataReceived(this, data, length);
+}
+
+void Resource::SetResourceBuffer(scoped_refptr<SharedBuffer> resource_buffer) {
+ DCHECK(!is_revalidating_);
+ DCHECK(!ErrorOccurred());
+ DCHECK_EQ(options_.data_buffering_policy, kBufferData);
+ data_ = std::move(resource_buffer);
+ SetEncodedSize(data_->size());
+}
+
+void Resource::ClearData() {
+ data_ = nullptr;
+ encoded_size_memory_usage_ = 0;
+}
+
+void Resource::TriggerNotificationForFinishObservers(
+ base::SingleThreadTaskRunner* task_runner) {
+ if (finish_observers_.IsEmpty())
+ return;
+
+ auto* new_collections = new HeapHashSet<WeakMember<ResourceFinishObserver>>(
+ std::move(finish_observers_));
+ finish_observers_.clear();
+
+ task_runner->PostTask(FROM_HERE, WTF::Bind(&NotifyFinishObservers,
+ WrapPersistent(new_collections)));
+
+ DidRemoveClientOrObserver();
+}
+
+void Resource::SetDataBufferingPolicy(
+ DataBufferingPolicy data_buffering_policy) {
+ options_.data_buffering_policy = data_buffering_policy;
+ ClearData();
+ SetEncodedSize(0);
+}
+
+void Resource::FinishAsError(const ResourceError& error,
+ base::SingleThreadTaskRunner* task_runner) {
+ error_ = error;
+ is_revalidating_ = false;
+
+ if (IsMainThread())
+ GetMemoryCache()->Remove(this);
+
+ if (!ErrorOccurred())
+ SetStatus(ResourceStatus::kLoadError);
+ DCHECK(ErrorOccurred());
+ ClearData();
+ loader_ = nullptr;
+ CheckResourceIntegrity();
+ TriggerNotificationForFinishObservers(task_runner);
+ NotifyFinished();
+}
+
+void Resource::Finish(double load_finish_time,
+ base::SingleThreadTaskRunner* task_runner) {
+ DCHECK(!is_revalidating_);
+ load_finish_time_ = load_finish_time;
+ if (!ErrorOccurred())
+ status_ = ResourceStatus::kCached;
+ loader_ = nullptr;
+ CheckResourceIntegrity();
+ TriggerNotificationForFinishObservers(task_runner);
+ NotifyFinished();
+}
+
+AtomicString Resource::HttpContentType() const {
+ return GetResponse().HttpContentType();
+}
+
+bool Resource::PassesAccessControlCheck(
+ const SecurityOrigin& security_origin) const {
+ WTF::Optional<network::mojom::CORSError> cors_error = CORS::CheckAccess(
+ GetResponse().Url(), GetResponse().HttpStatusCode(),
+ GetResponse().HttpHeaderFields(),
+ LastResourceRequest().GetFetchCredentialsMode(), security_origin);
+
+ return !cors_error;
+}
+
+bool Resource::MustRefetchDueToIntegrityMetadata(
+ const FetchParameters& params) const {
+ if (params.IntegrityMetadata().IsEmpty())
+ return false;
+
+ return !IntegrityMetadata::SetsEqual(IntegrityMetadata(),
+ params.IntegrityMetadata());
+}
+
+static double CurrentAge(const ResourceResponse& response,
+ double response_timestamp) {
+ // RFC2616 13.2.3
+ // No compensation for latency as that is not terribly important in practice
+ double date_value = response.Date();
+ double apparent_age = std::isfinite(date_value)
+ ? std::max(0., response_timestamp - date_value)
+ : 0;
+ double age_value = response.Age();
+ double corrected_received_age = std::isfinite(age_value)
+ ? std::max(apparent_age, age_value)
+ : apparent_age;
+ double resident_time = CurrentTime() - response_timestamp;
+ return corrected_received_age + resident_time;
+}
+
+static double FreshnessLifetime(const ResourceResponse& response,
+ double response_timestamp) {
+#if !defined(OS_ANDROID)
+ // On desktop, local files should be reloaded in case they change.
+ if (response.Url().IsLocalFile())
+ return 0;
+#endif
+
+ // Cache other non-http / non-filesystem resources liberally.
+ if (!response.Url().ProtocolIsInHTTPFamily() &&
+ !response.Url().ProtocolIs("filesystem"))
+ return std::numeric_limits<double>::max();
+
+ // RFC2616 13.2.4
+ double max_age_value = response.CacheControlMaxAge();
+ if (std::isfinite(max_age_value))
+ return max_age_value;
+ double expires_value = response.Expires();
+ double date_value = response.Date();
+ double creation_time =
+ std::isfinite(date_value) ? date_value : response_timestamp;
+ if (std::isfinite(expires_value))
+ return expires_value - creation_time;
+ double last_modified_value = response.LastModified();
+ if (std::isfinite(last_modified_value))
+ return (creation_time - last_modified_value) * 0.1;
+ // If no cache headers are present, the specification leaves the decision to
+ // the UA. Other browsers seem to opt for 0.
+ return 0;
+}
+
+static bool CanUseResponse(const ResourceResponse& response,
+ double response_timestamp) {
+ if (response.IsNull())
+ return false;
+
+ if (response.CacheControlContainsNoCache() ||
+ response.CacheControlContainsNoStore())
+ return false;
+
+ if (response.HttpStatusCode() == 303) {
+ // Must not be cached.
+ return false;
+ }
+
+ if (response.HttpStatusCode() == 302 || response.HttpStatusCode() == 307) {
+ // Default to not cacheable unless explicitly allowed.
+ bool has_max_age = std::isfinite(response.CacheControlMaxAge());
+ bool has_expires = std::isfinite(response.Expires());
+ // TODO: consider catching Cache-Control "private" and "public" here.
+ if (!has_max_age && !has_expires)
+ return false;
+ }
+
+ return CurrentAge(response, response_timestamp) <=
+ FreshnessLifetime(response, response_timestamp);
+}
+
+const ResourceRequest& Resource::LastResourceRequest() const {
+ if (!redirect_chain_.size())
+ return GetResourceRequest();
+ return redirect_chain_.back().request_;
+}
+
+void Resource::SetRevalidatingRequest(const ResourceRequest& request) {
+ SECURITY_CHECK(redirect_chain_.IsEmpty());
+ SECURITY_CHECK(!is_unused_preload_);
+ DCHECK(!request.IsNull());
+ CHECK(!is_revalidation_start_forbidden_);
+ is_revalidating_ = true;
+ resource_request_ = request;
+ status_ = ResourceStatus::kNotStarted;
+}
+
+bool Resource::WillFollowRedirect(const ResourceRequest& new_request,
+ const ResourceResponse& redirect_response) {
+ if (is_revalidating_)
+ RevalidationFailed();
+ redirect_chain_.push_back(RedirectPair(new_request, redirect_response));
+ return true;
+}
+
+void Resource::SetResponse(const ResourceResponse& response) {
+ response_ = response;
+
+ // Currently we support the metadata caching only for HTTP family.
+ if (!GetResourceRequest().Url().ProtocolIsInHTTPFamily() ||
+ !GetResponse().Url().ProtocolIsInHTTPFamily()) {
+ cache_handler_.Clear();
+ return;
+ }
+
+ cache_handler_ = CreateCachedMetadataHandler(CreateCachedMetadataSender());
+}
+
+std::unique_ptr<CachedMetadataSender> Resource::CreateCachedMetadataSender()
+ const {
+ if (GetResponse().WasFetchedViaServiceWorker()) {
+ // TODO(leszeks): Check whether it's correct that the source_origin can be
+ // null.
+ if (!source_origin_ || GetResponse().CacheStorageCacheName().IsNull()) {
+ return std::make_unique<NullCachedMetadataSender>();
+ }
+ return std::make_unique<ServiceWorkerCachedMetadataSender>(
+ this, source_origin_.get());
+ }
+ return std::make_unique<CachedMetadataSenderImpl>(this);
+}
+
+void Resource::ResponseReceived(const ResourceResponse& response,
+ std::unique_ptr<WebDataConsumerHandle>) {
+ response_timestamp_ = CurrentTime();
+ if (preload_discovery_time_) {
+ int time_since_discovery = static_cast<int>(
+ 1000 * (CurrentTimeTicksInSeconds() - preload_discovery_time_));
+ DEFINE_STATIC_LOCAL(CustomCountHistogram,
+ preload_discovery_to_first_byte_histogram,
+ ("PreloadScanner.TTFB", 0, 10000, 50));
+ preload_discovery_to_first_byte_histogram.Count(time_since_discovery);
+ }
+
+ if (is_revalidating_) {
+ if (response.HttpStatusCode() == 304) {
+ RevalidationSucceeded(response);
+ return;
+ }
+ RevalidationFailed();
+ }
+ SetResponse(response);
+ String encoding = response.TextEncodingName();
+ if (!encoding.IsNull())
+ SetEncoding(encoding);
+}
+
+void Resource::SetSerializedCachedMetadata(const char* data, size_t size) {
+ DCHECK(!is_revalidating_);
+ DCHECK(!GetResponse().IsNull());
+}
+
+String Resource::ReasonNotDeletable() const {
+ StringBuilder builder;
+ if (HasClientsOrObservers()) {
+ builder.Append("hasClients(");
+ builder.AppendNumber(clients_.size());
+ if (!clients_awaiting_callback_.IsEmpty()) {
+ builder.Append(", AwaitingCallback=");
+ builder.AppendNumber(clients_awaiting_callback_.size());
+ }
+ if (!finished_clients_.IsEmpty()) {
+ builder.Append(", Finished=");
+ builder.AppendNumber(finished_clients_.size());
+ }
+ builder.Append(')');
+ }
+ if (loader_) {
+ if (!builder.IsEmpty())
+ builder.Append(' ');
+ builder.Append("loader_");
+ }
+ if (IsMainThread() && GetMemoryCache()->Contains(this)) {
+ if (!builder.IsEmpty())
+ builder.Append(' ');
+ builder.Append("in_memory_cache");
+ }
+ return builder.ToString();
+}
+
+void Resource::DidAddClient(ResourceClient* c) {
+ if (scoped_refptr<SharedBuffer> data = Data()) {
+ data->ForEachSegment([this, &c](const char* segment, size_t segment_size,
+ size_t segment_offset) -> bool {
+ c->DataReceived(this, segment, segment_size);
+
+ // Stop pushing data if the client removed itself.
+ return HasClient(c);
+ });
+ }
+ if (!HasClient(c))
+ return;
+ if (IsLoaded()) {
+ c->NotifyFinished(this);
+ if (clients_.Contains(c)) {
+ finished_clients_.insert(c);
+ clients_.erase(c);
+ }
+ }
+}
+
+static bool TypeNeedsSynchronousCacheHit(Resource::Type type) {
+ // Some resources types default to return data synchronously. For most of
+ // these, it's because there are layout tests that expect data to return
+ // synchronously in case of cache hit. In the case of fonts, there was a
+ // performance regression.
+ // FIXME: Get to the point where we don't need to special-case sync/async
+ // behavior for different resource types.
+ if (type == Resource::kCSSStyleSheet)
+ return true;
+ if (type == Resource::kScript)
+ return true;
+ if (type == Resource::kFont)
+ return true;
+ return false;
+}
+
+void Resource::WillAddClientOrObserver() {
+ if (!HasClientsOrObservers()) {
+ is_alive_ = true;
+ }
+}
+
+void Resource::AddClient(ResourceClient* client,
+ base::SingleThreadTaskRunner* task_runner) {
+ CHECK(!is_add_remove_client_prohibited_);
+
+ WillAddClientOrObserver();
+
+ if (is_revalidating_) {
+ clients_.insert(client);
+ return;
+ }
+
+ // If an error has occurred or we have existing data to send to the new client
+ // and the resource type supports it, send it asynchronously.
+ if ((ErrorOccurred() || !GetResponse().IsNull()) &&
+ !TypeNeedsSynchronousCacheHit(GetType())) {
+ clients_awaiting_callback_.insert(client);
+ if (!async_finish_pending_clients_task_.IsActive()) {
+ async_finish_pending_clients_task_ = PostCancellableTask(
+ *task_runner, FROM_HERE,
+ WTF::Bind(&Resource::FinishPendingClients, WrapWeakPersistent(this)));
+ }
+ return;
+ }
+
+ clients_.insert(client);
+ DidAddClient(client);
+ return;
+}
+
+void Resource::RemoveClient(ResourceClient* client) {
+ CHECK(!is_add_remove_client_prohibited_);
+
+ // This code may be called in a pre-finalizer, where weak members in the
+ // HashCountedSet are already swept out.
+
+ if (finished_clients_.Contains(client))
+ finished_clients_.erase(client);
+ else if (clients_awaiting_callback_.Contains(client))
+ clients_awaiting_callback_.erase(client);
+ else
+ clients_.erase(client);
+
+ if (clients_awaiting_callback_.IsEmpty() &&
+ async_finish_pending_clients_task_.IsActive()) {
+ async_finish_pending_clients_task_.Cancel();
+ }
+
+ DidRemoveClientOrObserver();
+}
+
+void Resource::AddFinishObserver(ResourceFinishObserver* client,
+ base::SingleThreadTaskRunner* task_runner) {
+ CHECK(!is_add_remove_client_prohibited_);
+ DCHECK(!finish_observers_.Contains(client));
+
+ WillAddClientOrObserver();
+ finish_observers_.insert(client);
+ if (IsLoaded())
+ TriggerNotificationForFinishObservers(task_runner);
+}
+
+void Resource::RemoveFinishObserver(ResourceFinishObserver* client) {
+ CHECK(!is_add_remove_client_prohibited_);
+
+ finish_observers_.erase(client);
+ DidRemoveClientOrObserver();
+}
+
+void Resource::DidRemoveClientOrObserver() {
+ if (!HasClientsOrObservers() && is_alive_) {
+ is_alive_ = false;
+ AllClientsAndObserversRemoved();
+
+ // RFC2616 14.9.2:
+ // "no-store: ... MUST make a best-effort attempt to remove the information
+ // from volatile storage as promptly as possible"
+ // "... History buffers MAY store such responses as part of their normal
+ // operation."
+ // We allow non-secure content to be reused in history, but we do not allow
+ // secure content to be reused.
+ if (HasCacheControlNoStoreHeader() && Url().ProtocolIs("https") &&
+ IsMainThread())
+ GetMemoryCache()->Remove(this);
+ }
+}
+
+void Resource::AllClientsAndObserversRemoved() {
+ if (loader_ && !detachable_)
+ loader_->ScheduleCancel();
+}
+
+void Resource::SetDecodedSize(size_t decoded_size) {
+ if (decoded_size == decoded_size_)
+ return;
+ size_t old_size = size();
+ decoded_size_ = decoded_size;
+ if (IsMainThread())
+ GetMemoryCache()->Update(this, old_size, size());
+}
+
+void Resource::SetEncodedSize(size_t encoded_size) {
+ if (encoded_size == encoded_size_ &&
+ encoded_size == encoded_size_memory_usage_)
+ return;
+ size_t old_size = size();
+ encoded_size_ = encoded_size;
+ encoded_size_memory_usage_ = encoded_size;
+ if (IsMainThread())
+ GetMemoryCache()->Update(this, old_size, size());
+}
+
+void Resource::FinishPendingClients() {
+ // We're going to notify clients one by one. It is simple if the client does
+ // nothing. However there are a couple other things that can happen.
+ //
+ // 1. Clients can be added during the loop. Make sure they are not processed.
+ // 2. Clients can be removed during the loop. Make sure they are always
+ // available to be removed. Also don't call removed clients or add them
+ // back.
+ //
+ // Handle case (1) by saving a list of clients to notify. A separate list also
+ // ensure a client is either in cliens_ or clients_awaiting_callback_.
+ HeapVector<Member<ResourceClient>> clients_to_notify;
+ CopyToVector(clients_awaiting_callback_, clients_to_notify);
+
+ for (const auto& client : clients_to_notify) {
+ // Handle case (2) to skip removed clients.
+ if (!clients_awaiting_callback_.erase(client))
+ continue;
+ clients_.insert(client);
+
+ // When revalidation starts after waiting clients are scheduled and
+ // before they are added here. In such cases, we just add the clients
+ // to |clients_| without DidAddClient(), as in Resource::AddClient().
+ if (!is_revalidating_)
+ DidAddClient(client);
+ }
+
+ // It is still possible for the above loop to finish a new client
+ // synchronously. If there's no client waiting we should deschedule.
+ bool scheduled = async_finish_pending_clients_task_.IsActive();
+ if (scheduled && clients_awaiting_callback_.IsEmpty())
+ async_finish_pending_clients_task_.Cancel();
+
+ // Prevent the case when there are clients waiting but no callback scheduled.
+ DCHECK(clients_awaiting_callback_.IsEmpty() || scheduled);
+}
+
+bool Resource::CanReuse(
+ const FetchParameters& params,
+ scoped_refptr<const SecurityOrigin> new_source_origin) const {
+ const ResourceRequest& new_request = params.GetResourceRequest();
+ const ResourceLoaderOptions& new_options = params.Options();
+
+ // Never reuse opaque responses from a service worker for requests that are
+ // not no-cors. https://crbug.com/625575
+ // TODO(yhirano): Remove this.
+ if (GetResponse().WasFetchedViaServiceWorker() &&
+ GetResponse().ResponseTypeViaServiceWorker() ==
+ network::mojom::FetchResponseType::kOpaque &&
+ new_request.GetFetchRequestMode() !=
+ network::mojom::FetchRequestMode::kNoCORS) {
+ return false;
+ }
+
+ // If credentials were sent with the previous request and won't be with this
+ // one, or vice versa, re-fetch the resource.
+ //
+ // This helps with the case where the server sends back
+ // "Access-Control-Allow-Origin: *" all the time, but some of the client's
+ // requests are made without CORS and some with.
+ if (GetResourceRequest().AllowStoredCredentials() !=
+ new_request.AllowStoredCredentials())
+ return false;
+
+ // Certain requests (e.g., XHRs) might have manually set headers that require
+ // revalidation. In theory, this should be a Revalidate case. In practice, the
+ // MemoryCache revalidation path assumes a whole bunch of things about how
+ // revalidation works that manual headers violate, so punt to Reload instead.
+ //
+ // Similarly, a request with manually added revalidation headers can lead to a
+ // 304 response for a request that wasn't flagged as a revalidation attempt.
+ // Normally, successful revalidation will maintain the original response's
+ // status code, but for a manual revalidation the response code remains 304.
+ // In this case, the Resource likely has insufficient context to provide a
+ // useful cache hit or revalidation. See http://crbug.com/643659
+ if (new_request.IsConditional() || response_.HttpStatusCode() == 304)
+ return false;
+
+ // Answers the question "can a separate request with different options be
+ // re-used" (e.g. preload request). The safe (but possibly slow) answer is
+ // always false.
+ //
+ // Data buffering policy differences are believed to be safe for re-use.
+ //
+ // TODO: Check content_security_policy_option.
+ //
+ // initiator_info is purely informational and should be benign for re-use.
+ //
+ // request_initiator_context is benign (indicates document vs. worker).
+
+ // Reuse only if both the existing Resource and the new request are
+ // asynchronous. Particularly,
+ // 1. Sync and async Resource/requests shouldn't be mixed (crbug.com/652172),
+ // 2. Sync existing Resources shouldn't be revalidated, and
+ // 3. Sync new requests shouldn't revalidate existing Resources.
+ //
+ // 2. and 3. are because SyncResourceHandler handles redirects without
+ // calling WillFollowRedirect, and causes response URL mismatch
+ // (crbug.com/618967) and bypassing redirect restriction around revalidation
+ // (crbug.com/613971 for 2. and crbug.com/614989 for 3.).
+ if (new_options.synchronous_policy == kRequestSynchronously ||
+ options_.synchronous_policy == kRequestSynchronously)
+ return false;
+
+ if (resource_request_.GetKeepalive() || new_request.GetKeepalive()) {
+ return false;
+ }
+
+ DCHECK(source_origin_);
+ DCHECK(new_source_origin);
+
+ // Don't reuse an existing resource when the source origin is different.
+ if (!source_origin_->IsSameSchemeHostPort(new_source_origin.get()))
+ return false;
+
+ // securityOrigin has more complicated checks which callers are responsible
+ // for.
+
+ if (new_request.GetFetchCredentialsMode() !=
+ resource_request_.GetFetchCredentialsMode())
+ return false;
+
+ const auto new_mode = new_request.GetFetchRequestMode();
+ const auto existing_mode = resource_request_.GetFetchRequestMode();
+
+ if (new_mode != existing_mode)
+ return false;
+
+ switch (new_mode) {
+ case network::mojom::FetchRequestMode::kNoCORS:
+ case network::mojom::FetchRequestMode::kNavigate:
+ break;
+
+ case network::mojom::FetchRequestMode::kCORS:
+ case network::mojom::FetchRequestMode::kSameOrigin:
+ case network::mojom::FetchRequestMode::kCORSWithForcedPreflight:
+ // We have two separate CORS handling logics in DocumentThreadableLoader
+ // and ResourceLoader and sharing resources is difficult when they are
+ // handled differently.
+ if (options_.cors_handling_by_resource_fetcher !=
+ new_options.cors_handling_by_resource_fetcher) {
+ // If the existing one is handled in DocumentThreadableLoader and the
+ // new one is handled in ResourceLoader, reusing the existing one will
+ // lead to CORS violations.
+ if (!options_.cors_handling_by_resource_fetcher)
+ return false;
+
+ // Otherwise (i.e., if the existing one is handled in ResourceLoader
+ // and the new one is handled in DocumentThreadableLoader), reusing
+ // the existing one will lead to double check which is harmless.
+ }
+ break;
+ }
+
+ return true;
+}
+
+void Resource::Prune() {
+ DestroyDecodedDataIfPossible();
+}
+
+void Resource::OnPurgeMemory() {
+ Prune();
+ if (!cache_handler_)
+ return;
+ cache_handler_->ClearCachedMetadata(CachedMetadataHandler::kCacheLocally);
+}
+
+void Resource::OnMemoryDump(WebMemoryDumpLevelOfDetail level_of_detail,
+ WebProcessMemoryDump* memory_dump) const {
+ static const size_t kMaxURLReportLength = 128;
+ static const int kMaxResourceClientToShowInMemoryInfra = 10;
+
+ const String dump_name = GetMemoryDumpName();
+ WebMemoryAllocatorDump* dump =
+ memory_dump->CreateMemoryAllocatorDump(dump_name);
+ dump->AddScalar("encoded_size", "bytes", encoded_size_memory_usage_);
+ if (HasClientsOrObservers())
+ dump->AddScalar("live_size", "bytes", encoded_size_memory_usage_);
+ else
+ dump->AddScalar("dead_size", "bytes", encoded_size_memory_usage_);
+
+ if (data_)
+ data_->OnMemoryDump(dump_name, memory_dump);
+
+ if (level_of_detail == WebMemoryDumpLevelOfDetail::kDetailed) {
+ String url_to_report = Url().GetString();
+ if (url_to_report.length() > kMaxURLReportLength) {
+ url_to_report.Truncate(kMaxURLReportLength);
+ url_to_report = url_to_report + "...";
+ }
+ dump->AddString("url", "", url_to_report);
+
+ dump->AddString("reason_not_deletable", "", ReasonNotDeletable());
+
+ Vector<String> client_names;
+ ResourceClientWalker<ResourceClient> walker(clients_);
+ while (ResourceClient* client = walker.Next())
+ client_names.push_back(client->DebugName());
+ ResourceClientWalker<ResourceClient> walker2(clients_awaiting_callback_);
+ while (ResourceClient* client = walker2.Next())
+ client_names.push_back("(awaiting) " + client->DebugName());
+ ResourceClientWalker<ResourceClient> walker3(finished_clients_);
+ while (ResourceClient* client = walker3.Next())
+ client_names.push_back("(finished) " + client->DebugName());
+ std::sort(client_names.begin(), client_names.end(),
+ WTF::CodePointCompareLessThan);
+
+ StringBuilder builder;
+ for (size_t i = 0;
+ i < client_names.size() && i < kMaxResourceClientToShowInMemoryInfra;
+ ++i) {
+ if (i > 0)
+ builder.Append(" / ");
+ builder.Append(client_names[i]);
+ }
+ if (client_names.size() > kMaxResourceClientToShowInMemoryInfra) {
+ builder.Append(" / and ");
+ builder.AppendNumber(client_names.size() -
+ kMaxResourceClientToShowInMemoryInfra);
+ builder.Append(" more");
+ }
+ dump->AddString("ResourceClient", "", builder.ToString());
+ }
+
+ const String overhead_name = dump_name + "/metadata";
+ WebMemoryAllocatorDump* overhead_dump =
+ memory_dump->CreateMemoryAllocatorDump(overhead_name);
+ overhead_dump->AddScalar("size", "bytes", OverheadSize());
+ memory_dump->AddSuballocation(
+ overhead_dump->Guid(), String(WTF::Partitions::kAllocatedObjectPoolName));
+}
+
+String Resource::GetMemoryDumpName() const {
+ return String::Format(
+ "web_cache/%s_resources/%ld",
+ ResourceTypeToString(GetType(), Options().initiator_info.name),
+ identifier_);
+}
+
+void Resource::SetCachePolicyBypassingCache() {
+ resource_request_.SetCacheMode(mojom::FetchCacheMode::kBypassCache);
+}
+
+void Resource::SetPreviewsState(WebURLRequest::PreviewsState previews_state) {
+ resource_request_.SetPreviewsState(previews_state);
+}
+
+void Resource::ClearRangeRequestHeader() {
+ resource_request_.ClearHTTPHeaderField("range");
+}
+
+void Resource::RevalidationSucceeded(
+ const ResourceResponse& validating_response) {
+ SECURITY_CHECK(redirect_chain_.IsEmpty());
+ SECURITY_CHECK(EqualIgnoringFragmentIdentifier(validating_response.Url(),
+ GetResponse().Url()));
+ response_.SetResourceLoadTiming(validating_response.GetResourceLoadTiming());
+
+ // RFC2616 10.3.5
+ // Update cached headers from the 304 response
+ const HTTPHeaderMap& new_headers = validating_response.HttpHeaderFields();
+ for (const auto& header : new_headers) {
+ // Entity headers should not be sent by servers when generating a 304
+ // response; misconfigured servers send them anyway. We shouldn't allow such
+ // headers to update the original request. We'll base this on the list
+ // defined by RFC2616 7.1, with a few additions for extension headers we
+ // care about.
+ if (!ShouldUpdateHeaderAfterRevalidation(header.key))
+ continue;
+ response_.SetHTTPHeaderField(header.key, header.value);
+ }
+
+ is_revalidating_ = false;
+}
+
+void Resource::RevalidationFailed() {
+ SECURITY_CHECK(redirect_chain_.IsEmpty());
+ ClearData();
+ cache_handler_.Clear();
+ integrity_disposition_ = ResourceIntegrityDisposition::kNotChecked;
+ integrity_report_info_.Clear();
+ DestroyDecodedDataForFailedRevalidation();
+ is_revalidating_ = false;
+}
+
+void Resource::MarkAsPreload() {
+ DCHECK(!is_unused_preload_);
+ is_unused_preload_ = true;
+}
+
+bool Resource::MatchPreload(const FetchParameters& params,
+ base::SingleThreadTaskRunner*) {
+ DCHECK(is_unused_preload_);
+ is_unused_preload_ = false;
+
+ if (preload_discovery_time_) {
+ int time_since_discovery = static_cast<int>(
+ 1000 * (CurrentTimeTicksInSeconds() - preload_discovery_time_));
+ DEFINE_STATIC_LOCAL(CustomCountHistogram, preload_discovery_histogram,
+ ("PreloadScanner.ReferenceTime", 0, 10000, 50));
+ preload_discovery_histogram.Count(time_since_discovery);
+ }
+ return true;
+}
+
+bool Resource::CanReuseRedirectChain() const {
+ for (auto& redirect : redirect_chain_) {
+ if (!CanUseResponse(redirect.redirect_response_, response_timestamp_))
+ return false;
+ if (redirect.request_.CacheControlContainsNoCache() ||
+ redirect.request_.CacheControlContainsNoStore())
+ return false;
+ }
+ return true;
+}
+
+bool Resource::HasCacheControlNoStoreHeader() const {
+ return GetResponse().CacheControlContainsNoStore() ||
+ GetResourceRequest().CacheControlContainsNoStore();
+}
+
+bool Resource::MustReloadDueToVaryHeader(
+ const ResourceRequest& new_request) const {
+ const AtomicString& vary = GetResponse().HttpHeaderField(HTTPNames::Vary);
+ if (vary.IsNull())
+ return false;
+ if (vary == "*")
+ return true;
+
+ CommaDelimitedHeaderSet vary_headers;
+ ParseCommaDelimitedHeader(vary, vary_headers);
+ for (const String& header : vary_headers) {
+ AtomicString atomic_header(header);
+ if (GetResourceRequest().HttpHeaderField(atomic_header) !=
+ new_request.HttpHeaderField(atomic_header)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Resource::MustRevalidateDueToCacheHeaders() const {
+ return !CanUseResponse(GetResponse(), response_timestamp_) ||
+ GetResourceRequest().CacheControlContainsNoCache() ||
+ GetResourceRequest().CacheControlContainsNoStore();
+}
+
+bool Resource::CanUseCacheValidator() const {
+ if (IsLoading() || ErrorOccurred())
+ return false;
+
+ if (HasCacheControlNoStoreHeader())
+ return false;
+
+ // Do not revalidate Resource with redirects. https://crbug.com/613971
+ if (!RedirectChain().IsEmpty())
+ return false;
+
+ return GetResponse().HasCacheValidatorFields() ||
+ GetResourceRequest().HasCacheValidatorFields();
+}
+
+size_t Resource::CalculateOverheadSize() const {
+ static const int kAverageClientsHashMapSize = 384;
+ return sizeof(Resource) + GetResponse().MemoryUsage() +
+ kAverageClientsHashMapSize +
+ GetResourceRequest().Url().GetString().length() * 2;
+}
+
+void Resource::DidChangePriority(ResourceLoadPriority load_priority,
+ int intra_priority_value) {
+ resource_request_.SetPriority(load_priority, intra_priority_value);
+ if (loader_)
+ loader_->DidChangePriority(load_priority, intra_priority_value);
+}
+
+// TODO(toyoshim): Consider to generate automatically. https://crbug.com/675515.
+static const char* InitiatorTypeNameToString(
+ const AtomicString& initiator_type_name) {
+ if (initiator_type_name == FetchInitiatorTypeNames::css)
+ return "CSS resource";
+ if (initiator_type_name == FetchInitiatorTypeNames::document)
+ return "Document";
+ if (initiator_type_name == FetchInitiatorTypeNames::icon)
+ return "Icon";
+ if (initiator_type_name == FetchInitiatorTypeNames::internal)
+ return "Internal resource";
+ if (initiator_type_name == FetchInitiatorTypeNames::link)
+ return "Link element resource";
+ if (initiator_type_name == FetchInitiatorTypeNames::processinginstruction)
+ return "Processing instruction";
+ if (initiator_type_name == FetchInitiatorTypeNames::texttrack)
+ return "Text track";
+ if (initiator_type_name == FetchInitiatorTypeNames::uacss)
+ return "User Agent CSS resource";
+ if (initiator_type_name == FetchInitiatorTypeNames::xml)
+ return "XML resource";
+ if (initiator_type_name == FetchInitiatorTypeNames::xmlhttprequest)
+ return "XMLHttpRequest";
+
+ static_assert(
+ FetchInitiatorTypeNames::FetchInitiatorTypeNamesCount == 13,
+ "New FetchInitiatorTypeNames should be handled correctly here.");
+
+ return "Resource";
+}
+
+const char* Resource::ResourceTypeToString(
+ Type type,
+ const AtomicString& fetch_initiator_name) {
+ switch (type) {
+ case Resource::kMainResource:
+ return "Main resource";
+ case Resource::kImage:
+ return "Image";
+ case Resource::kCSSStyleSheet:
+ return "CSS stylesheet";
+ case Resource::kScript:
+ return "Script";
+ case Resource::kFont:
+ return "Font";
+ case Resource::kRaw:
+ return InitiatorTypeNameToString(fetch_initiator_name);
+ case Resource::kSVGDocument:
+ return "SVG document";
+ case Resource::kXSLStyleSheet:
+ return "XSL stylesheet";
+ case Resource::kLinkPrefetch:
+ return "Link prefetch resource";
+ case Resource::kTextTrack:
+ return "Text track";
+ case Resource::kImportResource:
+ return "Imported resource";
+ case Resource::kAudio:
+ return "Audio";
+ case Resource::kVideo:
+ return "Video";
+ case Resource::kManifest:
+ return "Manifest";
+ case Resource::kMock:
+ return "Mock";
+ }
+ NOTREACHED();
+ return InitiatorTypeNameToString(fetch_initiator_name);
+}
+
+bool Resource::ShouldBlockLoadEvent() const {
+ return !link_preload_ && IsLoadEventBlockingResourceType();
+}
+
+bool Resource::IsLoadEventBlockingResourceType() const {
+ switch (type_) {
+ case Resource::kMainResource:
+ case Resource::kImage:
+ case Resource::kCSSStyleSheet:
+ case Resource::kScript:
+ case Resource::kFont:
+ case Resource::kSVGDocument:
+ case Resource::kXSLStyleSheet:
+ case Resource::kImportResource:
+ return true;
+ case Resource::kRaw:
+ case Resource::kLinkPrefetch:
+ case Resource::kTextTrack:
+ case Resource::kAudio:
+ case Resource::kVideo:
+ case Resource::kManifest:
+ case Resource::kMock:
+ return false;
+ }
+ NOTREACHED();
+ return false;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource.h
new file mode 100644
index 00000000000..b68b9c5abb0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource.h
@@ -0,0 +1,596 @@
+/*
+ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2001 Dirk Mueller <mueller@kde.org>
+ Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
+ Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All
+ rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_H_
+
+#include <memory>
+#include "base/single_thread_task_runner.h"
+#include "third_party/blink/public/platform/web_data_consumer_handle.h"
+#include "third_party/blink/public/platform/web_scoped_virtual_time_pauser.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump.h"
+#include "third_party/blink/renderer/platform/loader/cors/cors_status.h"
+#include "third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h"
+#include "third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_error.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_load_priority.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_priority.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_status.h"
+#include "third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.h"
+#include "third_party/blink/renderer/platform/loader/subresource_integrity.h"
+#include "third_party/blink/renderer/platform/memory_coordinator.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/timer.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/auto_reset.h"
+#include "third_party/blink/renderer/platform/wtf/hash_counted_set.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class FetchParameters;
+class ResourceClient;
+class ResourceFetcher;
+class ResourceFinishObserver;
+class ResourceTimingInfo;
+class ResourceLoader;
+class SecurityOrigin;
+
+// A callback for sending the serialized data of cached metadata back to the
+// platform.
+class CachedMetadataSender {
+ public:
+ virtual ~CachedMetadataSender() = default;
+ virtual void Send(const char*, size_t) = 0;
+
+ // IsServedFromCacheStorage is used to alter caching strategy to be more
+ // aggressive. See ScriptController.cpp CacheOptions() for an example.
+ virtual bool IsServedFromCacheStorage() = 0;
+};
+
+// A resource that is held in the cache. Classes who want to use this object
+// should derive from ResourceClient, to get the function calls in case the
+// requested data has arrived. This class also does the actual communication
+// with the loader to obtain the resource from the network.
+class PLATFORM_EXPORT Resource : public GarbageCollectedFinalized<Resource>,
+ public MemoryCoordinatorClient {
+ USING_GARBAGE_COLLECTED_MIXIN(Resource);
+ WTF_MAKE_NONCOPYABLE(Resource);
+
+ public:
+ // |Type| enum values are used in UMAs, so do not change the values of
+ // existing |Type|. When adding a new |Type|, append it at the end and update
+ // |kLastResourceType|.
+ enum Type : uint8_t {
+ kMainResource,
+ kImage,
+ kCSSStyleSheet,
+ kScript,
+ kFont,
+ kRaw,
+ kSVGDocument,
+ kXSLStyleSheet,
+ kLinkPrefetch,
+ kTextTrack,
+ kImportResource,
+ kAudio,
+ kVideo,
+ kManifest,
+ kMock // Only for testing
+ };
+ static const int kLastResourceType = kMock + 1;
+
+ // Used by reloadIfLoFiOrPlaceholderImage().
+ enum ReloadLoFiOrPlaceholderPolicy {
+ kReloadIfNeeded,
+ kReloadAlways,
+ };
+
+ ~Resource() override;
+
+ void Trace(blink::Visitor*) override;
+
+ virtual WTF::TextEncoding Encoding() const { return WTF::TextEncoding(); }
+ virtual void AppendData(const char*, size_t);
+ virtual void FinishAsError(const ResourceError&,
+ base::SingleThreadTaskRunner*);
+
+ void SetLinkPreload(bool is_link_preload) { link_preload_ = is_link_preload; }
+ bool IsLinkPreload() const { return link_preload_; }
+
+ void SetPreloadDiscoveryTime(double preload_discovery_time) {
+ preload_discovery_time_ = preload_discovery_time;
+ }
+
+ const ResourceError& GetResourceError() const {
+ DCHECK(error_);
+ return *error_;
+ }
+
+ void SetIdentifier(unsigned long identifier) { identifier_ = identifier; }
+ unsigned long Identifier() const { return identifier_; }
+
+ virtual bool ShouldIgnoreHTTPStatusCodeErrors() const { return false; }
+
+ const ResourceRequest& GetResourceRequest() const {
+ return resource_request_;
+ }
+ const ResourceRequest& LastResourceRequest() const;
+
+ virtual void SetRevalidatingRequest(const ResourceRequest&);
+
+ // This url can have a fragment, but it can match resources that differ by the
+ // fragment only.
+ const KURL& Url() const { return GetResourceRequest().Url(); }
+ Type GetType() const { return static_cast<Type>(type_); }
+ const ResourceLoaderOptions& Options() const { return options_; }
+ ResourceLoaderOptions& MutableOptions() { return options_; }
+
+ void DidChangePriority(ResourceLoadPriority, int intra_priority_value);
+ virtual ResourcePriority PriorityFromObservers() {
+ return ResourcePriority();
+ }
+
+ // If this Resource is already finished when AddClient is called, the
+ // ResourceClient will be notified asynchronously by a task scheduled
+ // on the given base::SingleThreadTaskRunner. Otherwise, the given
+ // base::SingleThreadTaskRunner is unused.
+ void AddClient(ResourceClient*, base::SingleThreadTaskRunner*);
+ void RemoveClient(ResourceClient*);
+ // Once called, this resource will not be canceled until load finishes
+ // even if associated with no client.
+ void SetDetachable() { detachable_ = true; }
+
+ // If this Resource is already finished when AddFinishObserver is called, the
+ // ResourceFinishObserver will be notified asynchronously by a task scheduled
+ // on the given base::SingleThreadTaskRunner. Otherwise, the given
+ // base::SingleThreadTaskRunner is unused.
+ void AddFinishObserver(ResourceFinishObserver*,
+ base::SingleThreadTaskRunner*);
+ void RemoveFinishObserver(ResourceFinishObserver*);
+
+ bool IsUnusedPreload() const { return is_unused_preload_; }
+
+ ResourceStatus GetStatus() const { return status_; }
+ void SetStatus(ResourceStatus status) { status_ = status; }
+
+ size_t size() const { return EncodedSize() + DecodedSize() + OverheadSize(); }
+
+ // Returns the size of content (response body) before decoding. Adding a new
+ // usage of this function is not recommended (See the TODO below).
+ //
+ // TODO(hiroshige): Now EncodedSize/DecodedSize states are inconsistent and
+ // need to be refactored (crbug/643135).
+ size_t EncodedSize() const { return encoded_size_; }
+
+ // Returns the current memory usage for the encoded data. Adding a new usage
+ // of this function is not recommended as the same reason as |EncodedSize()|.
+ //
+ // |EncodedSize()| and |EncodedSizeMemoryUsageForTesting()| can return
+ // different values, e.g., when ImageResource purges encoded image data after
+ // finishing loading.
+ size_t EncodedSizeMemoryUsageForTesting() const {
+ return encoded_size_memory_usage_;
+ }
+
+ size_t DecodedSize() const { return decoded_size_; }
+ size_t OverheadSize() const { return overhead_size_; }
+
+ bool IsLoaded() const { return status_ > ResourceStatus::kPending; }
+
+ bool IsLoading() const { return status_ == ResourceStatus::kPending; }
+ bool StillNeedsLoad() const { return status_ < ResourceStatus::kPending; }
+
+ void SetLoader(ResourceLoader*);
+ ResourceLoader* Loader() const { return loader_.Get(); }
+
+ bool ShouldBlockLoadEvent() const;
+ bool IsLoadEventBlockingResourceType() const;
+
+ // Computes the status of an object after loading. Updates the expire date on
+ // the cache entry file
+ virtual void Finish(double finish_time, base::SingleThreadTaskRunner*);
+ void FinishForTest() { Finish(0.0, nullptr); }
+
+ bool PassesAccessControlCheck(const SecurityOrigin&) const;
+
+ virtual scoped_refptr<const SharedBuffer> ResourceBuffer() const {
+ return data_;
+ }
+ void SetResourceBuffer(scoped_refptr<SharedBuffer>);
+
+ virtual bool WillFollowRedirect(const ResourceRequest&,
+ const ResourceResponse&);
+
+ // Called when a redirect response was received but a decision has already
+ // been made to not follow it.
+ virtual void WillNotFollowRedirect() {}
+
+ virtual void ResponseReceived(const ResourceResponse&,
+ std::unique_ptr<WebDataConsumerHandle>);
+ void SetResponse(const ResourceResponse&);
+ const ResourceResponse& GetResponse() const { return response_; }
+
+ virtual void ReportResourceTimingToClients(const ResourceTimingInfo&) {}
+
+ // Sets the serialized metadata retrieved from the platform's cache.
+ // Subclasses of Resource that support cached metadata should override this
+ // method with one that fills the current CachedMetadataHandler.
+ virtual void SetSerializedCachedMetadata(const char*, size_t);
+
+ AtomicString HttpContentType() const;
+
+ bool WasCanceled() const { return error_ && error_->IsCancellation(); }
+ bool ErrorOccurred() const {
+ return status_ == ResourceStatus::kLoadError ||
+ status_ == ResourceStatus::kDecodeError;
+ }
+ bool LoadFailedOrCanceled() const { return !!error_; }
+
+ DataBufferingPolicy GetDataBufferingPolicy() const {
+ return options_.data_buffering_policy;
+ }
+ void SetDataBufferingPolicy(DataBufferingPolicy);
+
+ void MarkAsPreload();
+ // Returns true if |this| resource is matched with the given parameters.
+ virtual bool MatchPreload(const FetchParameters&,
+ base::SingleThreadTaskRunner*);
+
+ bool CanReuseRedirectChain() const;
+ bool MustRevalidateDueToCacheHeaders() const;
+ virtual bool CanUseCacheValidator() const;
+ bool IsCacheValidator() const { return is_revalidating_; }
+ bool HasCacheControlNoStoreHeader() const;
+ bool MustReloadDueToVaryHeader(const ResourceRequest& new_request) const;
+
+ const IntegrityMetadataSet& IntegrityMetadata() const {
+ return options_.integrity_metadata;
+ }
+ ResourceIntegrityDisposition IntegrityDisposition() const {
+ return integrity_disposition_;
+ }
+ const SubresourceIntegrity::ReportInfo& IntegrityReportInfo() const {
+ return integrity_report_info_;
+ }
+ bool MustRefetchDueToIntegrityMetadata(const FetchParameters&) const;
+
+ bool IsAlive() const { return is_alive_; }
+
+ CORSStatus GetCORSStatus() const { return cors_status_; }
+
+ bool IsSameOriginOrCORSSuccessful() const {
+ return cors_status_ == CORSStatus::kSameOrigin ||
+ cors_status_ == CORSStatus::kSuccessful ||
+ cors_status_ == CORSStatus::kServiceWorkerSuccessful;
+ }
+
+ void SetCacheIdentifier(const String& cache_identifier) {
+ cache_identifier_ = cache_identifier;
+ }
+ String CacheIdentifier() const { return cache_identifier_; }
+
+ void SetSourceOrigin(scoped_refptr<const SecurityOrigin> source_origin) {
+ source_origin_ = source_origin;
+ }
+
+ virtual void DidSendData(unsigned long long /* bytesSent */,
+ unsigned long long /* totalBytesToBeSent */) {}
+ virtual void DidDownloadData(int) {}
+ virtual void DidDownloadToBlob(scoped_refptr<BlobDataHandle>) {}
+
+ double LoadFinishTime() const { return load_finish_time_; }
+
+ void SetEncodedDataLength(int64_t value) {
+ response_.SetEncodedDataLength(value);
+ }
+ void SetEncodedBodyLength(int value) {
+ response_.SetEncodedBodyLength(value);
+ }
+ void SetDecodedBodyLength(int value) {
+ response_.SetDecodedBodyLength(value);
+ }
+
+ virtual bool CanReuse(
+ const FetchParameters&,
+ scoped_refptr<const SecurityOrigin> new_source_origin) const;
+
+ // If cache-aware loading is activated, this callback is called when the first
+ // disk-cache-only request failed due to cache miss. After this callback,
+ // cache-aware loading is deactivated and a reload with original request will
+ // be triggered right away in ResourceLoader.
+ virtual void WillReloadAfterDiskCacheMiss() {}
+
+ // TODO(shaochuan): This is for saving back the actual ResourceRequest sent
+ // in ResourceFetcher::StartLoad() for retry in cache-aware loading, remove
+ // once ResourceRequest is not modified in StartLoad(). crbug.com/632580
+ void SetResourceRequest(const ResourceRequest& resource_request) {
+ resource_request_ = resource_request;
+ }
+
+ // Used by the MemoryCache to reduce the memory consumption of the entry.
+ void Prune();
+
+ virtual void OnMemoryDump(WebMemoryDumpLevelOfDetail,
+ WebProcessMemoryDump*) const;
+
+ // If this Resource is ImageResource and has the Lo-Fi response headers or is
+ // a placeholder, reload the full original image with the Lo-Fi state set to
+ // off and optionally bypassing the cache.
+ virtual void ReloadIfLoFiOrPlaceholderImage(ResourceFetcher*,
+ ReloadLoFiOrPlaceholderPolicy) {}
+
+ // Used to notify ImageResourceContent of the start of actual loading.
+ // JavaScript calls or client/observer notifications are disallowed inside
+ // NotifyStartLoad().
+ virtual void NotifyStartLoad() {}
+
+ static const char* ResourceTypeToString(
+ Type,
+ const AtomicString& fetch_initiator_name);
+
+ class ProhibitAddRemoveClientInScope : public AutoReset<bool> {
+ public:
+ ProhibitAddRemoveClientInScope(Resource* resource)
+ : AutoReset(&resource->is_add_remove_client_prohibited_, true) {}
+ };
+
+ class RevalidationStartForbiddenScope : public AutoReset<bool> {
+ public:
+ RevalidationStartForbiddenScope(Resource* resource)
+ : AutoReset(&resource->is_revalidation_start_forbidden_, true) {}
+ };
+
+ WebScopedVirtualTimePauser& VirtualTimePauser() {
+ return virtual_time_pauser_;
+ }
+
+ protected:
+ Resource(const ResourceRequest&, Type, const ResourceLoaderOptions&);
+
+ virtual void NotifyFinished();
+
+ void MarkClientFinished(ResourceClient*);
+
+ virtual bool HasClientsOrObservers() const {
+ return !clients_.IsEmpty() || !clients_awaiting_callback_.IsEmpty() ||
+ !finished_clients_.IsEmpty() || !finish_observers_.IsEmpty();
+ }
+ virtual void DestroyDecodedDataForFailedRevalidation() {}
+
+ void SetEncodedSize(size_t);
+ void SetDecodedSize(size_t);
+
+ void FinishPendingClients();
+
+ virtual void DidAddClient(ResourceClient*);
+ void WillAddClientOrObserver();
+
+ void DidRemoveClientOrObserver();
+ virtual void AllClientsAndObserversRemoved();
+
+ bool HasClient(ResourceClient* client) const {
+ return clients_.Contains(client) ||
+ clients_awaiting_callback_.Contains(client) ||
+ finished_clients_.Contains(client);
+ }
+
+ struct RedirectPair {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ explicit RedirectPair(const ResourceRequest& request,
+ const ResourceResponse& redirect_response)
+ : request_(request), redirect_response_(redirect_response) {}
+
+ ResourceRequest request_;
+ ResourceResponse redirect_response_;
+ };
+ const Vector<RedirectPair>& RedirectChain() const { return redirect_chain_; }
+
+ virtual void DestroyDecodedDataIfPossible() {}
+
+ // Returns the memory dump name used for tracing. See Resource::OnMemoryDump.
+ String GetMemoryDumpName() const;
+
+ const HeapHashCountedSet<WeakMember<ResourceClient>>& Clients() const {
+ return clients_;
+ }
+
+ void SetCachePolicyBypassingCache();
+ void SetPreviewsState(WebURLRequest::PreviewsState);
+ void ClearRangeRequestHeader();
+
+ SharedBuffer* Data() const { return data_.get(); }
+ void ClearData();
+
+ virtual void SetEncoding(const String&) {}
+
+ // Create a handler for the cached metadata of this resource. Subclasses of
+ // Resource that support cached metadata should override this method with one
+ // that creates an appropriate CachedMetadataHandler implementation, and
+ // override SetSerializedCachedMetadata with an implementation that fills the
+ // cache handler.
+ virtual CachedMetadataHandler* CreateCachedMetadataHandler(
+ std::unique_ptr<CachedMetadataSender> send_callback) {
+ return nullptr;
+ }
+
+ CachedMetadataHandler* CacheHandler() { return cache_handler_.Get(); }
+
+ private:
+ // To allow access to SetCORSStatus
+ friend class ResourceLoader;
+ friend class SubresourceIntegrityTest;
+
+ void RevalidationSucceeded(const ResourceResponse&);
+ void RevalidationFailed();
+
+ size_t CalculateOverheadSize() const;
+
+ String ReasonNotDeletable() const;
+
+ void SetCORSStatus(const CORSStatus cors_status) {
+ cors_status_ = cors_status;
+ }
+
+ // MemoryCoordinatorClient overrides:
+ void OnPurgeMemory() override;
+
+ void CheckResourceIntegrity();
+ void TriggerNotificationForFinishObservers(base::SingleThreadTaskRunner*);
+
+ // Helper for creating the send callback function for the cached metadata
+ // handler.
+ std::unique_ptr<CachedMetadataSender> CreateCachedMetadataSender() const;
+
+ Type type_;
+ ResourceStatus status_;
+
+ // A SecurityOrigin representing the origin from which the loading of the
+ // Resource was initiated. This is calculated and set on Resource creation.
+ //
+ // Unlike |security_origin| on |options_|, which:
+ // - holds a SecurityOrigin to override the FetchContext's SecurityOrigin
+ // (in case of e.g. that the script initiated the loading is in an isolated
+ // world)
+ //
+ // Used for isolating resources for different origins in the MemoryCache.
+ //
+ // Note: A Resource returned from the memory cache has an origin for the first
+ // initiator that fetched the Resource. It may be different from the origin
+ // that you need for any runtime security check in Blink.
+ scoped_refptr<const SecurityOrigin> source_origin_;
+
+ CORSStatus cors_status_;
+
+ Member<CachedMetadataHandler> cache_handler_;
+
+ Optional<ResourceError> error_;
+
+ double load_finish_time_;
+
+ unsigned long identifier_;
+
+ double preload_discovery_time_;
+
+ size_t encoded_size_;
+ size_t encoded_size_memory_usage_;
+ size_t decoded_size_;
+
+ // Resource::CalculateOverheadSize() is affected by changes in
+ // |m_resourceRequest.url()|, but |m_overheadSize| is not updated after
+ // initial |m_resourceRequest| is given, to reduce MemoryCache manipulation
+ // and thus potential bugs. crbug.com/594644
+ const size_t overhead_size_;
+
+ String cache_identifier_;
+
+ bool link_preload_;
+ bool is_revalidating_;
+ bool is_alive_;
+ bool is_add_remove_client_prohibited_;
+ bool is_revalidation_start_forbidden_ = false;
+ bool is_unused_preload_ = false;
+ bool detachable_ = false;
+
+ ResourceIntegrityDisposition integrity_disposition_;
+ SubresourceIntegrity::ReportInfo integrity_report_info_;
+
+ // Ordered list of all redirects followed while fetching this resource.
+ Vector<RedirectPair> redirect_chain_;
+
+ HeapHashCountedSet<WeakMember<ResourceClient>> clients_;
+ HeapHashCountedSet<WeakMember<ResourceClient>> clients_awaiting_callback_;
+ HeapHashCountedSet<WeakMember<ResourceClient>> finished_clients_;
+ HeapHashSet<WeakMember<ResourceFinishObserver>> finish_observers_;
+
+ ResourceLoaderOptions options_;
+
+ double response_timestamp_;
+
+ TaskHandle async_finish_pending_clients_task_;
+
+ ResourceRequest resource_request_;
+ Member<ResourceLoader> loader_;
+ ResourceResponse response_;
+
+ scoped_refptr<SharedBuffer> data_;
+
+ WebScopedVirtualTimePauser virtual_time_pauser_;
+};
+
+class ResourceFactory {
+ STACK_ALLOCATED();
+
+ public:
+ virtual Resource* Create(const ResourceRequest&,
+ const ResourceLoaderOptions&,
+ const TextResourceDecoderOptions&) const = 0;
+
+ Resource::Type GetType() const { return type_; }
+ TextResourceDecoderOptions::ContentType ContentType() const {
+ return content_type_;
+ }
+
+ protected:
+ explicit ResourceFactory(Resource::Type type,
+ TextResourceDecoderOptions::ContentType content_type)
+ : type_(type), content_type_(content_type) {}
+
+ Resource::Type type_;
+ TextResourceDecoderOptions::ContentType content_type_;
+};
+
+class NonTextResourceFactory : public ResourceFactory {
+ protected:
+ explicit NonTextResourceFactory(Resource::Type type)
+ : ResourceFactory(type, TextResourceDecoderOptions::kPlainTextContent) {}
+
+ virtual Resource* Create(const ResourceRequest&,
+ const ResourceLoaderOptions&) const = 0;
+
+ Resource* Create(const ResourceRequest& request,
+ const ResourceLoaderOptions& options,
+ const TextResourceDecoderOptions&) const final {
+ return Create(request, options);
+ }
+};
+
+#define DEFINE_RESOURCE_TYPE_CASTS(typeName) \
+ DEFINE_TYPE_CASTS(typeName##Resource, Resource, resource, \
+ resource->GetType() == Resource::k##typeName, \
+ resource.GetType() == Resource::k##typeName);
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_client.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_client.h
new file mode 100644
index 00000000000..f4476b61496
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_client.h
@@ -0,0 +1,113 @@
+/*
+ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2001 Dirk Mueller <mueller@kde.org>
+ Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All
+ rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ This class provides all functionality needed for loading images, style
+ sheets and html pages from the web. It has a memory cache for these objects.
+*/
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_CLIENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_CLIENT_H_
+
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT ResourceClient : public GarbageCollectedMixin {
+ USING_PRE_FINALIZER(ResourceClient, ClearResource);
+
+ public:
+ enum ResourceClientType {
+ kBaseResourceType,
+ kFontType,
+ kRawResourceType
+ };
+
+ virtual ~ResourceClient() = default;
+
+ // DataReceived() is called each time a chunk of data is received.
+ // For cache hits, the data is replayed before NotifyFinished() is called.
+ // For successful revalidation responses, the data is NOT replayed, because
+ // the Resource may not be in an entirely consistent state in the middle of
+ // completing the revalidation, when DataReceived() would have to be called.
+ // Some RawResourceClients depends on receiving all bytes via DataReceived(),
+ // but RawResources forbid revalidation attempts, so they still are guaranteed
+ // to get all data via DataReceived().
+ virtual void DataReceived(Resource*,
+ const char* /* data */,
+ size_t /* length */) {}
+ virtual void NotifyFinished(Resource*) {}
+
+ static bool IsExpectedType(ResourceClient*) { return true; }
+ virtual ResourceClientType GetResourceClientType() const {
+ return kBaseResourceType;
+ }
+
+ Resource* GetResource() const { return resource_; }
+
+ // Name for debugging, e.g. shown in memory-infra.
+ virtual String DebugName() const = 0;
+
+ void Trace(blink::Visitor* visitor) override { visitor->Trace(resource_); }
+
+ protected:
+ ResourceClient() = default;
+
+ void ClearResource() { SetResource(nullptr, nullptr); }
+
+ private:
+ // ResourceFetcher is primarily responsible for calling SetResource() with a
+ // non-null Resource*. ResourceClient subclasses are responsible for calling
+ // ClearResource().
+ friend class ResourceFetcher;
+ // TODO(japhet): There isn't a clean way for SVGResourceClients to determine
+ // whether SVGElementProxy is holding a Resource that it should register with,
+ // so SVGElementProxy handles it for those clients. SVGResourceClients should
+ // have a better way to register themselves as clients. crbug.com/789198
+ friend class SVGElementProxy;
+ // CSSFontFaceSrcValue only ever requests a Resource once, and acts as an
+ // intermediate caching layer of sorts. It needs to be able to register
+ // additional clients.
+ friend class CSSFontFaceSrcValue;
+
+ void SetResource(Resource* new_resource,
+ base::SingleThreadTaskRunner* task_runner) {
+ if (new_resource == resource_)
+ return;
+
+ // Some ResourceClient implementations reenter this so
+ // we need to prevent double removal.
+ if (Resource* old_resource = resource_.Release())
+ old_resource->RemoveClient(this);
+ resource_ = new_resource;
+ if (resource_)
+ resource_->AddClient(this, task_runner);
+ }
+
+ Member<Resource> resource_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_client_walker.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_client_walker.h
new file mode 100644
index 00000000000..33e7c8c8368
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_client_walker.h
@@ -0,0 +1,69 @@
+/*
+ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2001 Dirk Mueller <mueller@kde.org>
+ Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ This class provides all functionality needed for loading images, style
+ sheets and html pages from the web. It has a memory cache for these objects.
+*/
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_CLIENT_WALKER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_CLIENT_WALKER_H_
+
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_client.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+// Call this "walker" instead of iterator so people won't expect Qt or STL-style
+// iterator interface. Just keep calling next() on this. It's safe from
+// deletions of items.
+template <typename T>
+class ResourceClientWalker {
+ STACK_ALLOCATED();
+
+ public:
+ explicit ResourceClientWalker(
+ const HeapHashCountedSet<WeakMember<ResourceClient>>& set)
+ : client_set_(set) {
+ CopyToVector(client_set_, client_vector_);
+ }
+
+ T* Next() {
+ size_t size = client_vector_.size();
+ while (index_ < size) {
+ ResourceClient* next = client_vector_[index_++];
+ DCHECK(next);
+ if (client_set_.Contains(next)) {
+ DCHECK(T::IsExpectedType(next));
+ return static_cast<T*>(next);
+ }
+ }
+ return nullptr;
+ }
+
+ private:
+ const HeapHashCountedSet<WeakMember<ResourceClient>>& client_set_;
+ HeapVector<Member<ResourceClient>> client_vector_;
+ size_t index_ = 0;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_error.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_error.cc
new file mode 100644
index 00000000000..4a4c0e1f55f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_error.cc
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "third_party/blink/renderer/platform/loader/fetch/resource_error.h"
+
+#include "net/base/net_errors.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/public/platform/web_url_error.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+
+namespace blink {
+
+namespace {
+constexpr char kThrottledErrorDescription[] =
+ "Request throttled. Visit http://dev.chromium.org/throttling for more "
+ "information.";
+} // namespace
+
+int ResourceError::BlockedByXSSAuditorErrorCode() {
+ return net::ERR_BLOCKED_BY_XSS_AUDITOR;
+}
+
+ResourceError ResourceError::CancelledError(const KURL& url) {
+ return ResourceError(net::ERR_ABORTED, url, WTF::nullopt);
+}
+
+ResourceError ResourceError::CancelledDueToAccessCheckError(
+ const KURL& url,
+ ResourceRequestBlockedReason blocked_reason) {
+ ResourceError error = CancelledError(url);
+ error.is_access_check_ = true;
+ error.should_collapse_initiator_ =
+ blocked_reason == ResourceRequestBlockedReason::kSubresourceFilter;
+ return error;
+}
+
+ResourceError ResourceError::CancelledDueToAccessCheckError(
+ const KURL& url,
+ ResourceRequestBlockedReason blocked_reason,
+ const String& localized_description) {
+ ResourceError error = CancelledDueToAccessCheckError(url, blocked_reason);
+ error.localized_description_ = localized_description;
+ return error;
+}
+
+ResourceError ResourceError::CacheMissError(const KURL& url) {
+ return ResourceError(net::ERR_CACHE_MISS, url, WTF::nullopt);
+}
+
+ResourceError ResourceError::TimeoutError(const KURL& url) {
+ return ResourceError(net::ERR_TIMED_OUT, url, WTF::nullopt);
+}
+
+ResourceError ResourceError::Failure(const KURL& url) {
+ return ResourceError(net::ERR_FAILED, url, WTF::nullopt);
+}
+
+ResourceError::ResourceError(
+ int error_code,
+ const KURL& url,
+ WTF::Optional<network::CORSErrorStatus> cors_error_status)
+ : error_code_(error_code),
+ failing_url_(url),
+ cors_error_status_(cors_error_status) {
+ DCHECK_NE(error_code_, 0);
+ InitializeDescription();
+}
+
+ResourceError::ResourceError(const WebURLError& error)
+ : error_code_(error.reason()),
+ extended_error_code_(error.extended_reason()),
+ failing_url_(error.url()),
+ is_access_check_(error.is_web_security_violation()),
+ has_copy_in_cache_(error.has_copy_in_cache()),
+ cors_error_status_(error.cors_error_status()) {
+ DCHECK_NE(error_code_, 0);
+ InitializeDescription();
+}
+
+ResourceError ResourceError::Copy() const {
+ ResourceError error_copy(error_code_, failing_url_.Copy(),
+ cors_error_status_);
+ error_copy.extended_error_code_ = extended_error_code_;
+ error_copy.has_copy_in_cache_ = has_copy_in_cache_;
+ error_copy.localized_description_ = localized_description_.IsolatedCopy();
+ error_copy.is_access_check_ = is_access_check_;
+ return error_copy;
+}
+
+ResourceError::operator WebURLError() const {
+ WebURLError::HasCopyInCache has_copy_in_cache =
+ has_copy_in_cache_ ? WebURLError::HasCopyInCache::kTrue
+ : WebURLError::HasCopyInCache::kFalse;
+
+ if (cors_error_status_) {
+ DCHECK_EQ(net::ERR_FAILED, error_code_);
+ return WebURLError(*cors_error_status_, has_copy_in_cache, failing_url_);
+ }
+
+ return WebURLError(error_code_, extended_error_code_, has_copy_in_cache,
+ is_access_check_
+ ? WebURLError::IsWebSecurityViolation::kTrue
+ : WebURLError::IsWebSecurityViolation::kFalse,
+ failing_url_);
+}
+
+bool ResourceError::Compare(const ResourceError& a, const ResourceError& b) {
+ if (a.ErrorCode() != b.ErrorCode())
+ return false;
+
+ if (a.FailingURL() != b.FailingURL())
+ return false;
+
+ if (a.LocalizedDescription() != b.LocalizedDescription())
+ return false;
+
+ if (a.IsAccessCheck() != b.IsAccessCheck())
+ return false;
+
+ if (a.HasCopyInCache() != b.HasCopyInCache())
+ return false;
+
+ if (a.CORSErrorStatus() != b.CORSErrorStatus())
+ return false;
+
+ return true;
+}
+
+bool ResourceError::IsTimeout() const {
+ return error_code_ == net::ERR_TIMED_OUT;
+}
+
+bool ResourceError::IsCancellation() const {
+ return error_code_ == net::ERR_ABORTED;
+}
+
+bool ResourceError::IsCacheMiss() const {
+ return error_code_ == net::ERR_CACHE_MISS;
+}
+
+bool ResourceError::WasBlockedByResponse() const {
+ return error_code_ == net::ERR_BLOCKED_BY_RESPONSE;
+}
+
+void ResourceError::InitializeDescription() {
+ if (error_code_ == net::ERR_TEMPORARILY_THROTTLED) {
+ localized_description_ = WebString::FromASCII(kThrottledErrorDescription);
+ } else {
+ localized_description_ = WebString::FromASCII(
+ net::ExtendedErrorToString(error_code_, extended_error_code_));
+ }
+}
+
+std::ostream& operator<<(std::ostream& os, const ResourceError& error) {
+ return os << ", ErrorCode = " << error.ErrorCode()
+ << ", FailingURL = " << error.FailingURL()
+ << ", LocalizedDescription = " << error.LocalizedDescription()
+ << ", IsCancellation = " << error.IsCancellation()
+ << ", IsAccessCheck = " << error.IsAccessCheck()
+ << ", IsTimeout = " << error.IsTimeout()
+ << ", HasCopyInCache = " << error.HasCopyInCache()
+ << ", IsCacheMiss = " << error.IsCacheMiss();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_error.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_error.h
new file mode 100644
index 00000000000..ea6e0f6acb2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_error.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_ERROR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_ERROR_H_
+
+#include <iosfwd>
+#include "services/network/public/cpp/cors/cors_error_status.h"
+#include "third_party/blink/public/platform/web_url_error.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+enum class ResourceRequestBlockedReason;
+
+// ResourceError represents an error for loading a resource. There is no
+// "no-error" instance. Use Optional for nullable errors.
+class PLATFORM_EXPORT ResourceError final {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ static ResourceError CancelledError(const KURL&);
+ static ResourceError CancelledDueToAccessCheckError(
+ const KURL&,
+ ResourceRequestBlockedReason);
+ static ResourceError CancelledDueToAccessCheckError(
+ const KURL&,
+ ResourceRequestBlockedReason,
+ const String& localized_description);
+
+ static ResourceError CacheMissError(const KURL&);
+ static ResourceError TimeoutError(const KURL&);
+ static ResourceError Failure(const KURL&);
+
+ ResourceError() = delete;
+ // |error_code| must not be 0.
+ ResourceError(int error_code,
+ const KURL& failing_url,
+ WTF::Optional<network::CORSErrorStatus>);
+ ResourceError(const WebURLError&);
+
+ // Makes a deep copy. Useful for when you need to use a ResourceError on
+ // another thread.
+ ResourceError Copy() const;
+
+ int ErrorCode() const { return error_code_; }
+ const String& FailingURL() const { return failing_url_; }
+ const String& LocalizedDescription() const { return localized_description_; }
+
+ bool IsCancellation() const;
+ bool IsAccessCheck() const { return is_access_check_; }
+ bool HasCopyInCache() const { return has_copy_in_cache_; }
+ bool IsTimeout() const;
+ bool IsCacheMiss() const;
+ bool WasBlockedByResponse() const;
+ bool ShouldCollapseInitiator() const { return should_collapse_initiator_; }
+
+ WTF::Optional<network::CORSErrorStatus> CORSErrorStatus() const {
+ return cors_error_status_;
+ }
+
+ operator WebURLError() const;
+
+ static bool Compare(const ResourceError&, const ResourceError&);
+
+ // Net error code getters are here to avoid unpreferred header inclusion.
+ static int BlockedByXSSAuditorErrorCode();
+
+ private:
+ void InitializeDescription();
+
+ int error_code_;
+ int extended_error_code_;
+ KURL failing_url_;
+ String localized_description_;
+ bool is_access_check_ = false;
+ bool has_copy_in_cache_ = false;
+ bool should_collapse_initiator_ = false;
+ WTF::Optional<network::CORSErrorStatus> cors_error_status_;
+};
+
+inline bool operator==(const ResourceError& a, const ResourceError& b) {
+ return ResourceError::Compare(a, b);
+}
+inline bool operator!=(const ResourceError& a, const ResourceError& b) {
+ return !(a == b);
+}
+
+PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, const ResourceError&);
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_ERROR_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
new file mode 100644
index 00000000000..7a5b12a467f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
@@ -0,0 +1,1700 @@
+/*
+ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2001 Dirk Mueller (mueller@kde.org)
+ Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
+ Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All
+ rights reserved.
+ Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ This class provides all functionality needed for loading images, style
+ sheets and html pages from the web. It has a memory cache for these objects.
+*/
+
+#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
+
+#include <algorithm>
+#include <limits>
+#include <utility>
+
+#include "base/time/time.h"
+#include "third_party/blink/public/platform/modules/fetch/fetch_api_request.mojom-blink.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
+#include "third_party/blink/renderer/platform/histogram.h"
+#include "third_party/blink/renderer/platform/instance_counters.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_context.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_type_names.h"
+#include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
+#include "third_party/blink/renderer/platform/loader/fetch/raw_resource.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loading_log.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h"
+#include "third_party/blink/renderer/platform/loader/fetch/unique_identifier.h"
+#include "third_party/blink/renderer/platform/mhtml/archive_resource.h"
+#include "third_party/blink/renderer/platform/mhtml/mhtml_archive.h"
+#include "third_party/blink/renderer/platform/network/network_instrumentation.h"
+#include "third_party/blink/renderer/platform/network/network_utils.h"
+#include "third_party/blink/renderer/platform/probe/platform_probes.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler.h"
+#include "third_party/blink/renderer/platform/weborigin/known_ports.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/weborigin/security_policy.h"
+#include "third_party/blink/renderer/platform/weborigin/security_violation_reporting_policy.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+using blink::WebURLRequest;
+
+namespace blink {
+
+constexpr uint32_t ResourceFetcher::kKeepaliveInflightBytesQuota;
+
+namespace {
+
+constexpr base::TimeDelta kKeepaliveLoadersTimeout =
+ base::TimeDelta::FromSeconds(30);
+
+#define DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, name) \
+ case Resource::k##name: { \
+ DEFINE_THREAD_SAFE_STATIC_LOCAL( \
+ EnumerationHistogram, resource_histogram, \
+ ("Blink.MemoryCache.RevalidationPolicy." prefix #name, kLoad + 1)); \
+ resource_histogram.Count(policy); \
+ break; \
+ }
+
+#define DEFINE_RESOURCE_HISTOGRAM(prefix) \
+ switch (factory.GetType()) { \
+ DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, CSSStyleSheet) \
+ DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, Font) \
+ DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, Image) \
+ DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, ImportResource) \
+ DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, LinkPrefetch) \
+ DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, MainResource) \
+ DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, Manifest) \
+ DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, Audio) \
+ DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, Video) \
+ DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, Mock) \
+ DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, Raw) \
+ DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, Script) \
+ DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, SVGDocument) \
+ DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, TextTrack) \
+ DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, XSLStyleSheet) \
+ }
+
+void AddRedirectsToTimingInfo(Resource* resource, ResourceTimingInfo* info) {
+ // Store redirect responses that were packed inside the final response.
+ const auto& responses = resource->GetResponse().RedirectResponses();
+ for (size_t i = 0; i < responses.size(); ++i) {
+ const KURL& new_url = i + 1 < responses.size()
+ ? KURL(responses[i + 1].Url())
+ : resource->GetResourceRequest().Url();
+ bool cross_origin =
+ !SecurityOrigin::AreSameSchemeHostPort(responses[i].Url(), new_url);
+ info->AddRedirect(responses[i], cross_origin);
+ }
+}
+
+ResourceLoadPriority TypeToPriority(Resource::Type type) {
+ switch (type) {
+ case Resource::kMainResource:
+ case Resource::kCSSStyleSheet:
+ case Resource::kFont:
+ // Also parser-blocking scripts (set explicitly in loadPriority)
+ return ResourceLoadPriority::kVeryHigh;
+ case Resource::kXSLStyleSheet:
+ DCHECK(RuntimeEnabledFeatures::XSLTEnabled());
+ FALLTHROUGH;
+ case Resource::kRaw:
+ case Resource::kImportResource:
+ case Resource::kScript:
+ // Also visible resources/images (set explicitly in loadPriority)
+ return ResourceLoadPriority::kHigh;
+ case Resource::kManifest:
+ case Resource::kMock:
+ // Also late-body scripts discovered by the preload scanner (set
+ // explicitly in loadPriority)
+ return ResourceLoadPriority::kMedium;
+ case Resource::kImage:
+ case Resource::kTextTrack:
+ case Resource::kAudio:
+ case Resource::kVideo:
+ case Resource::kSVGDocument:
+ // Also async scripts (set explicitly in loadPriority)
+ return ResourceLoadPriority::kLow;
+ case Resource::kLinkPrefetch:
+ return ResourceLoadPriority::kVeryLow;
+ }
+
+ NOTREACHED();
+ return ResourceLoadPriority::kUnresolved;
+}
+
+bool ShouldResourceBeAddedToMemoryCache(const FetchParameters& params,
+ Resource* resource) {
+ if (!IsMainThread())
+ return false;
+ if (params.Options().data_buffering_policy == kDoNotBufferData)
+ return false;
+ if (IsRawResource(*resource))
+ return false;
+ return true;
+}
+
+} // namespace
+
+ResourceLoadPriority ResourceFetcher::ComputeLoadPriority(
+ Resource::Type type,
+ const ResourceRequest& resource_request,
+ ResourcePriority::VisibilityStatus visibility,
+ FetchParameters::DeferOption defer_option,
+ FetchParameters::SpeculativePreloadType speculative_preload_type,
+ bool is_link_preload) {
+ ResourceLoadPriority priority = TypeToPriority(type);
+
+ // Visible resources (images in practice) get a boost to High priority.
+ if (visibility == ResourcePriority::kVisible)
+ priority = ResourceLoadPriority::kHigh;
+
+ // Resources before the first image are considered "early" in the document and
+ // resources after the first image are "late" in the document. Important to
+ // note that this is based on when the preload scanner discovers a resource
+ // for the most part so the main parser may not have reached the image element
+ // yet.
+ if (type == Resource::kImage && !is_link_preload)
+ image_fetched_ = true;
+
+ // A preloaded font should not take precedence over critical CSS or
+ // parser-blocking scripts.
+ if (type == Resource::kFont && is_link_preload)
+ priority = ResourceLoadPriority::kHigh;
+
+ if (FetchParameters::kIdleLoad == defer_option) {
+ priority = ResourceLoadPriority::kVeryLow;
+ } else if (type == Resource::kScript) {
+ // Special handling for scripts.
+ // Default/Parser-Blocking/Preload early in document: High (set in
+ // typeToPriority)
+ // Async/Defer: Low Priority (applies to both preload and parser-inserted)
+ // Preload late in document: Medium
+ if (FetchParameters::kLazyLoad == defer_option) {
+ priority = ResourceLoadPriority::kLow;
+ } else if (speculative_preload_type ==
+ FetchParameters::SpeculativePreloadType::kInDocument &&
+ image_fetched_) {
+ // Speculative preload is used as a signal for scripts at the bottom of
+ // the document.
+ priority = ResourceLoadPriority::kMedium;
+ }
+ } else if (FetchParameters::kLazyLoad == defer_option) {
+ priority = ResourceLoadPriority::kVeryLow;
+ } else if (resource_request.GetRequestContext() ==
+ WebURLRequest::kRequestContextBeacon ||
+ resource_request.GetRequestContext() ==
+ WebURLRequest::kRequestContextPing ||
+ resource_request.GetRequestContext() ==
+ WebURLRequest::kRequestContextCSPReport) {
+ priority = ResourceLoadPriority::kVeryLow;
+ }
+
+ // A manually set priority acts as a floor. This is used to ensure that
+ // synchronous requests are always given the highest possible priority, as
+ // well as to ensure that there isn't priority churn if images move in and out
+ // of the viewport, or are displayed more than once, both in and out of the
+ // viewport.
+ return std::max(Context().ModifyPriorityForExperiments(priority),
+ resource_request.Priority());
+}
+
+static void PopulateTimingInfo(ResourceTimingInfo* info, Resource* resource) {
+ KURL initial_url = resource->GetResponse().RedirectResponses().IsEmpty()
+ ? resource->GetResourceRequest().Url()
+ : resource->GetResponse().RedirectResponses()[0].Url();
+ info->SetInitialURL(initial_url);
+ info->SetFinalResponse(resource->GetResponse());
+}
+
+WebURLRequest::RequestContext ResourceFetcher::DetermineRequestContext(
+ Resource::Type type,
+ IsImageSet is_image_set,
+ bool is_main_frame) {
+ DCHECK((is_image_set == kImageNotImageSet) ||
+ (type == Resource::kImage && is_image_set == kImageIsImageSet));
+ switch (type) {
+ case Resource::kMainResource:
+ if (!is_main_frame)
+ return WebURLRequest::kRequestContextIframe;
+ // FIXME: Change this to a context frame type (once we introduce them):
+ // http://fetch.spec.whatwg.org/#concept-request-context-frame-type
+ return WebURLRequest::kRequestContextHyperlink;
+ case Resource::kXSLStyleSheet:
+ DCHECK(RuntimeEnabledFeatures::XSLTEnabled());
+ FALLTHROUGH;
+ case Resource::kCSSStyleSheet:
+ return WebURLRequest::kRequestContextStyle;
+ case Resource::kScript:
+ return WebURLRequest::kRequestContextScript;
+ case Resource::kFont:
+ return WebURLRequest::kRequestContextFont;
+ case Resource::kImage:
+ if (is_image_set == kImageIsImageSet)
+ return WebURLRequest::kRequestContextImageSet;
+ return WebURLRequest::kRequestContextImage;
+ case Resource::kRaw:
+ return WebURLRequest::kRequestContextSubresource;
+ case Resource::kImportResource:
+ return WebURLRequest::kRequestContextImport;
+ case Resource::kLinkPrefetch:
+ return WebURLRequest::kRequestContextPrefetch;
+ case Resource::kTextTrack:
+ return WebURLRequest::kRequestContextTrack;
+ case Resource::kSVGDocument:
+ return WebURLRequest::kRequestContextImage;
+ case Resource::kAudio:
+ return WebURLRequest::kRequestContextAudio;
+ case Resource::kVideo:
+ return WebURLRequest::kRequestContextVideo;
+ case Resource::kManifest:
+ return WebURLRequest::kRequestContextManifest;
+ case Resource::kMock:
+ return WebURLRequest::kRequestContextSubresource;
+ }
+ NOTREACHED();
+ return WebURLRequest::kRequestContextSubresource;
+}
+
+ResourceFetcher::ResourceFetcher(FetchContext* new_context)
+ : context_(new_context),
+ scheduler_(ResourceLoadScheduler::Create(&Context())),
+ archive_(Context().IsMainFrame() ? nullptr : Context().Archive()),
+ resource_timing_report_timer_(
+ Context().GetLoadingTaskRunner(),
+ this,
+ &ResourceFetcher::ResourceTimingReportTimerFired),
+ auto_load_images_(true),
+ images_enabled_(true),
+ allow_stale_resources_(false),
+ image_fetched_(false) {
+ InstanceCounters::IncrementCounter(InstanceCounters::kResourceFetcherCounter);
+}
+
+ResourceFetcher::~ResourceFetcher() {
+ InstanceCounters::DecrementCounter(InstanceCounters::kResourceFetcherCounter);
+}
+
+Resource* ResourceFetcher::CachedResource(const KURL& resource_url) const {
+ KURL url = MemoryCache::RemoveFragmentIdentifierIfNeeded(resource_url);
+ const WeakMember<Resource>& resource = cached_resources_map_.at(url);
+ return resource.Get();
+}
+
+void ResourceFetcher::HoldResourcesFromPreviousFetcher(
+ ResourceFetcher* old_fetcher) {
+ DCHECK(resources_from_previous_fetcher_.IsEmpty());
+ for (Resource* resource : old_fetcher->document_resources_) {
+ if (GetMemoryCache()->Contains(resource))
+ resources_from_previous_fetcher_.insert(resource);
+ }
+}
+
+void ResourceFetcher::ClearResourcesFromPreviousFetcher() {
+ resources_from_previous_fetcher_.clear();
+}
+
+bool ResourceFetcher::IsControlledByServiceWorker() const {
+ return Context().IsControlledByServiceWorker();
+}
+
+bool ResourceFetcher::ResourceNeedsLoad(Resource* resource,
+ const FetchParameters& params,
+ RevalidationPolicy policy) {
+ // Defer a font load until it is actually needed unless this is a link
+ // preload.
+ if (resource->GetType() == Resource::kFont && !params.IsLinkPreload())
+ return false;
+
+ // Defer loading images either when:
+ // - images are disabled
+ // - instructed to defer loading images from network
+ if (resource->GetType() == Resource::kImage &&
+ ShouldDeferImageLoad(resource->Url()))
+ return false;
+
+ return policy != kUse || resource->StillNeedsLoad();
+}
+
+void ResourceFetcher::RequestLoadStarted(unsigned long identifier,
+ Resource* resource,
+ const FetchParameters& params,
+ RevalidationPolicy policy,
+ bool is_static_data) {
+ KURL url = MemoryCache::RemoveFragmentIdentifierIfNeeded(params.Url());
+ if (policy == kUse && resource->GetStatus() == ResourceStatus::kCached &&
+ !cached_resources_map_.Contains(url)) {
+ // Loaded from MemoryCache.
+ DidLoadResourceFromMemoryCache(identifier, resource,
+ params.GetResourceRequest());
+ }
+
+ if (is_static_data)
+ return;
+
+ if (policy == kUse && !resource->StillNeedsLoad() &&
+ !cached_resources_map_.Contains(url)) {
+ // Resources loaded from memory cache should be reported the first time
+ // they're used.
+ scoped_refptr<ResourceTimingInfo> info = ResourceTimingInfo::Create(
+ params.Options().initiator_info.name, CurrentTimeTicksInSeconds(),
+ resource->GetType() == Resource::kMainResource);
+ PopulateTimingInfo(info.get(), resource);
+ info->ClearLoadTimings();
+ info->SetLoadFinishTime(info->InitialTime());
+ scheduled_resource_timing_reports_.push_back(std::move(info));
+ if (!resource_timing_report_timer_.IsActive())
+ resource_timing_report_timer_.StartOneShot(TimeDelta(), FROM_HERE);
+ }
+}
+
+void ResourceFetcher::DidLoadResourceFromMemoryCache(
+ unsigned long identifier,
+ Resource* resource,
+ const ResourceRequest& original_resource_request) {
+ ResourceRequest resource_request(resource->Url());
+ resource_request.SetFrameType(original_resource_request.GetFrameType());
+ resource_request.SetRequestContext(
+ original_resource_request.GetRequestContext());
+ if (original_resource_request.IsAdResource())
+ resource_request.SetIsAdResource();
+
+ Context().DispatchDidLoadResourceFromMemoryCache(identifier, resource_request,
+ resource->GetResponse());
+ Context().DispatchWillSendRequest(
+ identifier, resource_request, ResourceResponse() /* redirects */,
+ resource->GetType(), resource->Options().initiator_info);
+ Context().DispatchDidReceiveResponse(
+ identifier, resource->GetResponse(), resource_request.GetFrameType(),
+ resource_request.GetRequestContext(), resource,
+ FetchContext::ResourceResponseType::kFromMemoryCache);
+
+ if (resource->EncodedSize() > 0) {
+ Context().DispatchDidReceiveData(identifier, nullptr,
+ resource->EncodedSize());
+ }
+
+ Context().DispatchDidFinishLoading(
+ identifier, 0, 0, resource->GetResponse().DecodedBodyLength(), false);
+}
+
+static std::unique_ptr<TracedValue> UrlForTraceEvent(const KURL& url) {
+ std::unique_ptr<TracedValue> value = TracedValue::Create();
+ value->SetString("url", url.GetString());
+ return value;
+}
+
+Resource* ResourceFetcher::ResourceForStaticData(
+ const FetchParameters& params,
+ const ResourceFactory& factory,
+ const SubstituteData& substitute_data) {
+ const KURL& url = params.GetResourceRequest().Url();
+ DCHECK(url.ProtocolIsData() || substitute_data.IsValid() || archive_);
+
+ // TODO(japhet): We only send main resource data: urls through WebURLLoader
+ // for the benefit of a service worker test
+ // (RenderViewImplTest.ServiceWorkerNetworkProviderSetup), which is at a layer
+ // where it isn't easy to mock out a network load. It uses data: urls to
+ // emulate the behavior it wants to test, which would otherwise be reserved
+ // for network loads.
+ if (!archive_ && !substitute_data.IsValid() &&
+ (factory.GetType() == Resource::kMainResource ||
+ factory.GetType() == Resource::kRaw))
+ return nullptr;
+
+ const String cache_identifier = GetCacheIdentifier();
+ if (Resource* old_resource =
+ GetMemoryCache()->ResourceForURL(url, cache_identifier)) {
+ // There's no reason to re-parse if we saved the data from the previous
+ // parse.
+ if (params.Options().data_buffering_policy != kDoNotBufferData)
+ return old_resource;
+ GetMemoryCache()->Remove(old_resource);
+ }
+
+ ResourceResponse response;
+ scoped_refptr<SharedBuffer> data;
+ if (substitute_data.IsValid()) {
+ data = substitute_data.Content();
+ response.SetURL(url);
+ response.SetMimeType(substitute_data.MimeType());
+ response.SetExpectedContentLength(data->size());
+ response.SetTextEncodingName(substitute_data.TextEncoding());
+ } else if (url.ProtocolIsData()) {
+ data = NetworkUtils::ParseDataURLAndPopulateResponse(url, response);
+ if (!data)
+ return nullptr;
+ // |response| is modified by parseDataURLAndPopulateResponse() and is
+ // ready to be used.
+ } else {
+ ArchiveResource* archive_resource =
+ archive_->SubresourceForURL(params.Url());
+ // The archive doesn't contain the resource, the request must be aborted.
+ if (!archive_resource)
+ return nullptr;
+ data = archive_resource->Data();
+ response.SetURL(url);
+ response.SetMimeType(archive_resource->MimeType());
+ response.SetExpectedContentLength(data->size());
+ response.SetTextEncodingName(archive_resource->TextEncoding());
+ }
+
+ Resource* resource = factory.Create(
+ params.GetResourceRequest(), params.Options(), params.DecoderOptions());
+ // FIXME: We should provide a body stream here.
+ resource->SetStatus(ResourceStatus::kPending);
+ resource->NotifyStartLoad();
+ resource->ResponseReceived(response, nullptr);
+ resource->SetDataBufferingPolicy(kBufferData);
+ if (data->size())
+ resource->SetResourceBuffer(data);
+ resource->SetIdentifier(CreateUniqueIdentifier());
+ resource->SetCacheIdentifier(cache_identifier);
+ resource->SetSourceOrigin(GetSourceOrigin(params.Options()));
+ resource->Finish(0.0, Context().GetLoadingTaskRunner().get());
+
+ if (!substitute_data.IsValid())
+ AddToMemoryCacheIfNeeded(params, resource);
+
+ return resource;
+}
+
+Resource* ResourceFetcher::ResourceForBlockedRequest(
+ const FetchParameters& params,
+ const ResourceFactory& factory,
+ ResourceRequestBlockedReason blocked_reason) {
+ Resource* resource = factory.Create(
+ params.GetResourceRequest(), params.Options(), params.DecoderOptions());
+ resource->SetStatus(ResourceStatus::kPending);
+ resource->NotifyStartLoad();
+ resource->SetSourceOrigin(GetSourceOrigin(params.Options()));
+ resource->FinishAsError(ResourceError::CancelledDueToAccessCheckError(
+ params.Url(), blocked_reason),
+ Context().GetLoadingTaskRunner().get());
+ return resource;
+}
+
+void ResourceFetcher::MakePreloadedResourceBlockOnloadIfNeeded(
+ Resource* resource,
+ const FetchParameters& params) {
+ // TODO(yoav): Test that non-blocking resources (video/audio/track) continue
+ // to not-block even after being preloaded and discovered.
+ if (resource && resource->Loader() &&
+ resource->IsLoadEventBlockingResourceType() &&
+ resource->IsLinkPreload() && !params.IsLinkPreload() &&
+ non_blocking_loaders_.Contains(resource->Loader())) {
+ non_blocking_loaders_.erase(resource->Loader());
+ loaders_.insert(resource->Loader());
+ }
+}
+
+void ResourceFetcher::UpdateMemoryCacheStats(Resource* resource,
+ RevalidationPolicy policy,
+ const FetchParameters& params,
+ const ResourceFactory& factory,
+ bool is_static_data) const {
+ if (is_static_data)
+ return;
+
+ if (params.IsSpeculativePreload() || params.IsLinkPreload()) {
+ DEFINE_RESOURCE_HISTOGRAM("Preload.");
+ } else {
+ DEFINE_RESOURCE_HISTOGRAM("");
+ }
+
+ // Aims to count Resource only referenced from MemoryCache (i.e. what would be
+ // dead if MemoryCache holds weak references to Resource). Currently we check
+ // references to Resource from ResourceClient and |m_preloads| only, because
+ // they are major sources of references.
+ if (resource && !resource->IsAlive() && !ContainsAsPreload(resource)) {
+ DEFINE_RESOURCE_HISTOGRAM("Dead.");
+ }
+}
+
+bool ResourceFetcher::ContainsAsPreload(Resource* resource) const {
+ auto it = preloads_.find(PreloadKey(resource->Url(), resource->GetType()));
+ return it != preloads_.end() && it->value == resource;
+}
+
+void ResourceFetcher::RemovePreload(Resource* resource) {
+ auto it = preloads_.find(PreloadKey(resource->Url(), resource->GetType()));
+ if (it == preloads_.end())
+ return;
+ if (it->value == resource)
+ preloads_.erase(it);
+}
+
+ResourceRequestBlockedReason ResourceFetcher::PrepareRequest(
+ FetchParameters& params,
+ const ResourceFactory& factory,
+ const SubstituteData& substitute_data,
+ unsigned long identifier) {
+ ResourceRequest& resource_request = params.MutableResourceRequest();
+ Resource::Type resource_type = factory.GetType();
+ const ResourceLoaderOptions& options = params.Options();
+
+ DCHECK(options.synchronous_policy == kRequestAsynchronously ||
+ resource_type == Resource::kRaw ||
+ resource_type == Resource::kXSLStyleSheet);
+
+ params.OverrideContentType(factory.ContentType());
+
+ // Don't send security violation reports for speculative preloads.
+ SecurityViolationReportingPolicy reporting_policy =
+ params.IsSpeculativePreload()
+ ? SecurityViolationReportingPolicy::kSuppressReporting
+ : SecurityViolationReportingPolicy::kReport;
+
+ // Note that resource_request.GetRedirectStatus() may return kFollowedRedirect
+ // here since e.g. DocumentThreadableLoader may create a new Resource from
+ // a ResourceRequest that originates from the ResourceRequest passed to
+ // the redirect handling callback.
+
+ // Before modifying the request for CSP, evaluate report-only headers. This
+ // allows site owners to learn about requests that are being modified
+ // (e.g. mixed content that is being upgraded by upgrade-insecure-requests).
+ Context().CheckCSPForRequest(
+ resource_request.GetRequestContext(),
+ MemoryCache::RemoveFragmentIdentifierIfNeeded(params.Url()), options,
+ reporting_policy, resource_request.GetRedirectStatus());
+
+ // This may modify params.Url() (via the resource_request argument).
+ Context().PopulateResourceRequest(
+ resource_type, params.GetClientHintsPreferences(),
+ params.GetResourceWidth(), resource_request);
+
+ if (!params.Url().IsValid())
+ return ResourceRequestBlockedReason::kOther;
+
+ resource_request.SetPriority(ComputeLoadPriority(
+ resource_type, params.GetResourceRequest(), ResourcePriority::kNotVisible,
+ params.Defer(), params.GetSpeculativePreloadType(),
+ params.IsLinkPreload()));
+ if (resource_request.GetCacheMode() == mojom::FetchCacheMode::kDefault) {
+ resource_request.SetCacheMode(Context().ResourceRequestCachePolicy(
+ resource_request, resource_type, params.Defer()));
+ }
+ if (resource_request.GetRequestContext() ==
+ WebURLRequest::kRequestContextUnspecified) {
+ resource_request.SetRequestContext(DetermineRequestContext(
+ resource_type, kImageNotImageSet, Context().IsMainFrame()));
+ }
+ if (resource_type == Resource::kLinkPrefetch)
+ resource_request.SetHTTPHeaderField(HTTPNames::Purpose, "prefetch");
+
+ Context().AddAdditionalRequestHeaders(
+ resource_request, (resource_type == Resource::kMainResource)
+ ? kFetchMainResource
+ : kFetchSubresource);
+
+ network_instrumentation::ResourcePrioritySet(identifier,
+ resource_request.Priority());
+
+ KURL url = MemoryCache::RemoveFragmentIdentifierIfNeeded(params.Url());
+ ResourceRequestBlockedReason blocked_reason = Context().CanRequest(
+ resource_type, resource_request, url, options, reporting_policy,
+ params.GetOriginRestriction(), resource_request.GetRedirectStatus());
+
+ if (Context().IsAdResource(url, resource_type,
+ resource_request.GetRequestContext())) {
+ resource_request.SetIsAdResource();
+ }
+
+ if (blocked_reason != ResourceRequestBlockedReason::kNone)
+ return blocked_reason;
+
+ const scoped_refptr<const SecurityOrigin>& origin = options.security_origin;
+ if (origin && !origin->IsUnique() &&
+ !origin->IsSameSchemeHostPort(Context().GetSecurityOrigin())) {
+ // |options.security_origin| may differ from the document's origin if
+ // this is a fetch initiated by an isolated world execution context, with a
+ // different SecurityOrigin. In this case, plumb it through as the
+ // RequestorOrigin so that the browser process can make policy decisions for
+ // this request, based on any special permissions the isolated world may
+ // have been granted.
+ // TODO(nick, dcheng): Find a way to formalize the isolated world origin
+ // check in https://crbug.com/792154.
+ resource_request.SetRequestorOrigin(origin);
+ }
+
+ // For initial requests, call prepareRequest() here before revalidation
+ // policy is determined.
+ Context().PrepareRequest(resource_request,
+ FetchContext::RedirectType::kNotForRedirect);
+
+ if (!params.Url().IsValid())
+ return ResourceRequestBlockedReason::kOther;
+
+ params.MutableOptions().cors_flag =
+ !origin || !origin->CanRequest(params.Url());
+
+ if (options.cors_handling_by_resource_fetcher ==
+ kEnableCORSHandlingByResourceFetcher) {
+ bool allow_stored_credentials = false;
+ switch (resource_request.GetFetchCredentialsMode()) {
+ case network::mojom::FetchCredentialsMode::kOmit:
+ break;
+ case network::mojom::FetchCredentialsMode::kSameOrigin:
+ allow_stored_credentials = !params.Options().cors_flag;
+ break;
+ case network::mojom::FetchCredentialsMode::kInclude:
+ allow_stored_credentials = true;
+ break;
+ }
+ resource_request.SetAllowStoredCredentials(allow_stored_credentials);
+ }
+
+ return ResourceRequestBlockedReason::kNone;
+}
+
+Resource* ResourceFetcher::RequestResource(
+ FetchParameters& params,
+ const ResourceFactory& factory,
+ ResourceClient* client,
+ const SubstituteData& substitute_data) {
+ // Only async requests get ResourceClient callbacks, so sync requests
+ // shouldn't provide a client.
+ DCHECK(!client ||
+ params.Options().synchronous_policy == kRequestAsynchronously);
+ Resource* resource =
+ RequestResourceInternal(params, factory, substitute_data);
+ DCHECK(resource);
+ if (client)
+ client->SetResource(resource, Context().GetLoadingTaskRunner().get());
+ return resource;
+}
+
+Resource* ResourceFetcher::RequestResourceInternal(
+ FetchParameters& params,
+ const ResourceFactory& factory,
+ const SubstituteData& substitute_data) {
+ unsigned long identifier = CreateUniqueIdentifier();
+ ResourceRequest& resource_request = params.MutableResourceRequest();
+ network_instrumentation::ScopedResourceLoadTracker
+ scoped_resource_load_tracker(identifier, resource_request);
+ SCOPED_BLINK_UMA_HISTOGRAM_TIMER_THREAD_SAFE(
+ "Blink.Fetch.RequestResourceTime");
+ // TODO(dproy): Remove this. http://crbug.com/659666
+ TRACE_EVENT1("blink", "ResourceFetcher::requestResource", "url",
+ UrlForTraceEvent(params.Url()));
+
+ // TODO(crbug.com/123004): Remove once we have enough stats on data URIs that
+ // contain fragments ('#' characters).
+ //
+ // TODO(crbug.com/796173): This call happens before commit for iframes that
+ // have data URI sources, which causes UKM to miss the metric recording.
+ if (context_) {
+ const KURL& url = params.Url();
+ if (url.HasFragmentIdentifier() && url.ProtocolIsData()) {
+ context_->RecordDataUriWithOctothorpe();
+ }
+ }
+
+ ResourceRequestBlockedReason blocked_reason =
+ PrepareRequest(params, factory, substitute_data, identifier);
+ if (blocked_reason != ResourceRequestBlockedReason::kNone)
+ return ResourceForBlockedRequest(params, factory, blocked_reason);
+
+ Resource::Type resource_type = factory.GetType();
+
+ if (!params.IsSpeculativePreload()) {
+ // Only log if it's not for speculative preload.
+ Context().RecordLoadingActivity(resource_request, resource_type,
+ params.Options().initiator_info.name);
+ }
+
+ Resource* resource = nullptr;
+ RevalidationPolicy policy = kLoad;
+
+ bool is_data_url = resource_request.Url().ProtocolIsData();
+ bool is_static_data = is_data_url || substitute_data.IsValid() || archive_;
+ if (is_static_data) {
+ resource = ResourceForStaticData(params, factory, substitute_data);
+ if (resource) {
+ policy =
+ DetermineRevalidationPolicy(resource_type, params, *resource, true);
+ } else if (!is_data_url && archive_) {
+ // Abort the request if the archive doesn't contain the resource, except
+ // in the case of data URLs which might have resources such as fonts that
+ // need to be decoded only on demand. These data URLs are allowed to be
+ // processed using the normal ResourceFetcher machinery.
+ return ResourceForBlockedRequest(params, factory,
+ ResourceRequestBlockedReason::kOther);
+ }
+ }
+
+ if (!resource) {
+ resource = MatchPreload(params, resource_type);
+ if (resource) {
+ policy = kUse;
+ // If |params| is for a blocking resource and a preloaded resource is
+ // found, we may need to make it block the onload event.
+ MakePreloadedResourceBlockOnloadIfNeeded(resource, params);
+ } else if (IsMainThread()) {
+ resource =
+ GetMemoryCache()->ResourceForURL(params.Url(), GetCacheIdentifier());
+ if (resource) {
+ policy = DetermineRevalidationPolicy(resource_type, params, *resource,
+ is_static_data);
+ }
+ }
+ }
+
+ UpdateMemoryCacheStats(resource, policy, params, factory, is_static_data);
+
+ switch (policy) {
+ case kReload:
+ GetMemoryCache()->Remove(resource);
+ FALLTHROUGH;
+ case kLoad:
+ resource = CreateResourceForLoading(params, factory);
+ break;
+ case kRevalidate:
+ InitializeRevalidation(resource_request, resource);
+ break;
+ case kUse:
+ if (resource->IsLinkPreload() && !params.IsLinkPreload())
+ resource->SetLinkPreload(false);
+ break;
+ }
+ DCHECK(resource);
+ // TODO(yoav): turn to a DCHECK. See https://crbug.com/690632
+ CHECK_EQ(resource->GetType(), resource_type);
+
+ if (policy != kUse)
+ resource->SetIdentifier(identifier);
+
+ // TODO(yoav): It is not clear why preloads are exempt from this check. Can we
+ // remove the exemption?
+ if (!params.IsSpeculativePreload() || policy != kUse) {
+ // When issuing another request for a resource that is already in-flight
+ // make sure to not demote the priority of the in-flight request. If the new
+ // request isn't at the same priority as the in-flight request, only allow
+ // promotions. This can happen when a visible image's priority is increased
+ // and then another reference to the image is parsed (which would be at a
+ // lower priority).
+ if (resource_request.Priority() > resource->GetResourceRequest().Priority())
+ resource->DidChangePriority(resource_request.Priority(), 0);
+ // TODO(yoav): I'd expect the stated scenario to not go here, as its policy
+ // would be Use.
+ }
+
+ // If only the fragment identifiers differ, it is the same resource.
+ DCHECK(EqualIgnoringFragmentIdentifier(resource->Url(), params.Url()));
+ RequestLoadStarted(identifier, resource, params, policy, is_static_data);
+ cached_resources_map_.Set(
+ MemoryCache::RemoveFragmentIdentifierIfNeeded(params.Url()), resource);
+ document_resources_.insert(resource);
+
+ // Returns with an existing resource if the resource does not need to start
+ // loading immediately. If revalidation policy was determined as |Revalidate|,
+ // the resource was already initialized for the revalidation here, but won't
+ // start loading.
+ if (ResourceNeedsLoad(resource, params, policy)) {
+ if (StartLoad(resource)) {
+ scoped_resource_load_tracker.ResourceLoadContinuesBeyondScope();
+ } else {
+ resource->FinishAsError(ResourceError::CancelledError(params.Url()),
+ Context().GetLoadingTaskRunner().get());
+ }
+ }
+
+ if (policy != kUse)
+ InsertAsPreloadIfNecessary(resource, params, resource_type);
+
+ return resource;
+}
+
+void ResourceFetcher::ResourceTimingReportTimerFired(TimerBase* timer) {
+ DCHECK_EQ(timer, &resource_timing_report_timer_);
+ Vector<scoped_refptr<ResourceTimingInfo>> timing_reports;
+ timing_reports.swap(scheduled_resource_timing_reports_);
+ for (const auto& timing_info : timing_reports)
+ Context().AddResourceTiming(*timing_info);
+}
+
+void ResourceFetcher::InitializeRevalidation(
+ ResourceRequest& revalidating_request,
+ Resource* resource) {
+ DCHECK(resource);
+ DCHECK(GetMemoryCache()->Contains(resource));
+ DCHECK(resource->IsLoaded());
+ DCHECK(resource->CanUseCacheValidator());
+ DCHECK(!resource->IsCacheValidator());
+ DCHECK(!Context().IsControlledByServiceWorker());
+ // RawResource doesn't support revalidation.
+ CHECK(!IsRawResource(*resource));
+
+ const AtomicString& last_modified =
+ resource->GetResponse().HttpHeaderField(HTTPNames::Last_Modified);
+ const AtomicString& e_tag =
+ resource->GetResponse().HttpHeaderField(HTTPNames::ETag);
+ if (!last_modified.IsEmpty() || !e_tag.IsEmpty()) {
+ DCHECK_NE(mojom::FetchCacheMode::kBypassCache,
+ revalidating_request.GetCacheMode());
+ if (revalidating_request.GetCacheMode() ==
+ mojom::FetchCacheMode::kValidateCache) {
+ revalidating_request.SetHTTPHeaderField(HTTPNames::Cache_Control,
+ "max-age=0");
+ }
+ }
+ if (!last_modified.IsEmpty()) {
+ revalidating_request.SetHTTPHeaderField(HTTPNames::If_Modified_Since,
+ last_modified);
+ }
+ if (!e_tag.IsEmpty())
+ revalidating_request.SetHTTPHeaderField(HTTPNames::If_None_Match, e_tag);
+
+ resource->SetRevalidatingRequest(revalidating_request);
+}
+
+scoped_refptr<const SecurityOrigin> ResourceFetcher::GetSourceOrigin(
+ const ResourceLoaderOptions& options) const {
+ if (options.security_origin)
+ return options.security_origin;
+
+ return Context().GetSecurityOrigin();
+}
+
+void ResourceFetcher::AddToMemoryCacheIfNeeded(const FetchParameters& params,
+ Resource* resource) {
+ if (!ShouldResourceBeAddedToMemoryCache(params, resource))
+ return;
+
+ GetMemoryCache()->Add(resource);
+}
+
+Resource* ResourceFetcher::CreateResourceForLoading(
+ const FetchParameters& params,
+ const ResourceFactory& factory) {
+ const String cache_identifier = GetCacheIdentifier();
+ DCHECK(!IsMainThread() ||
+ !GetMemoryCache()->ResourceForURL(params.GetResourceRequest().Url(),
+ cache_identifier));
+
+ RESOURCE_LOADING_DVLOG(1) << "Loading Resource for "
+ << params.GetResourceRequest().Url().ElidedString();
+
+ Resource* resource = factory.Create(
+ params.GetResourceRequest(), params.Options(), params.DecoderOptions());
+ resource->SetLinkPreload(params.IsLinkPreload());
+ if (params.IsSpeculativePreload()) {
+ resource->SetPreloadDiscoveryTime(params.PreloadDiscoveryTime());
+ }
+ resource->SetCacheIdentifier(cache_identifier);
+ resource->SetSourceOrigin(GetSourceOrigin(params.Options()));
+
+ AddToMemoryCacheIfNeeded(params, resource);
+ return resource;
+}
+
+void ResourceFetcher::StorePerformanceTimingInitiatorInformation(
+ Resource* resource) {
+ const AtomicString& fetch_initiator = resource->Options().initiator_info.name;
+ if (fetch_initiator == FetchInitiatorTypeNames::internal)
+ return;
+
+ bool is_main_resource = resource->GetType() == Resource::kMainResource;
+
+ // The request can already be fetched in a previous navigation. Thus
+ // startTime must be set accordingly.
+ double start_time = resource->GetResourceRequest().NavigationStartTime()
+ ? resource->GetResourceRequest().NavigationStartTime()
+ : CurrentTimeTicksInSeconds();
+
+ // This buffer is created and populated for providing transferSize
+ // and redirect timing opt-in information.
+ if (is_main_resource) {
+ DCHECK(!navigation_timing_info_);
+ navigation_timing_info_ = ResourceTimingInfo::Create(
+ fetch_initiator, start_time, is_main_resource);
+ }
+
+ scoped_refptr<ResourceTimingInfo> info =
+ ResourceTimingInfo::Create(fetch_initiator, start_time, is_main_resource);
+
+ if (resource->IsCacheValidator()) {
+ const AtomicString& timing_allow_origin =
+ resource->GetResponse().HttpHeaderField(HTTPNames::Timing_Allow_Origin);
+ if (!timing_allow_origin.IsEmpty())
+ info->SetOriginalTimingAllowOrigin(timing_allow_origin);
+ }
+
+ if (!is_main_resource ||
+ Context().UpdateTimingInfoForIFrameNavigation(info.get())) {
+ resource_timing_info_map_.insert(resource, std::move(info));
+ }
+}
+
+void ResourceFetcher::RecordResourceTimingOnRedirect(
+ Resource* resource,
+ const ResourceResponse& redirect_response,
+ bool cross_origin) {
+ ResourceTimingInfoMap::iterator it = resource_timing_info_map_.find(resource);
+ if (it != resource_timing_info_map_.end()) {
+ it->value->AddRedirect(redirect_response, cross_origin);
+ }
+
+ if (resource->GetType() == Resource::kMainResource) {
+ DCHECK(navigation_timing_info_);
+ navigation_timing_info_->AddRedirect(redirect_response, cross_origin);
+ }
+}
+
+static bool IsDownloadOrStreamRequest(const ResourceRequest& request) {
+ // Never use cache entries for DownloadToFile / UseStreamOnResponse requests.
+ // The data will be delivered through other paths.
+ return request.DownloadToFile() || request.DownloadToBlob() ||
+ request.UseStreamOnResponse();
+}
+
+Resource* ResourceFetcher::MatchPreload(const FetchParameters& params,
+ Resource::Type type) {
+ auto it = preloads_.find(PreloadKey(params.Url(), type));
+ if (it == preloads_.end())
+ return nullptr;
+
+ Resource* resource = it->value;
+
+ if (resource->MustRefetchDueToIntegrityMetadata(params))
+ return nullptr;
+
+ if (params.IsSpeculativePreload())
+ return resource;
+ if (params.IsLinkPreload()) {
+ resource->SetLinkPreload(true);
+ return resource;
+ }
+
+ const ResourceRequest& request = params.GetResourceRequest();
+ if (request.DownloadToFile() || request.DownloadToBlob())
+ return nullptr;
+
+ if (IsImageResourceDisallowedToBeReused(*resource) ||
+ !resource->CanReuse(params, GetSourceOrigin(params.Options())))
+ return nullptr;
+
+ if (!resource->MatchPreload(params, Context().GetLoadingTaskRunner().get()))
+ return nullptr;
+ preloads_.erase(it);
+ matched_preloads_.push_back(resource);
+ return resource;
+}
+
+void ResourceFetcher::InsertAsPreloadIfNecessary(Resource* resource,
+ const FetchParameters& params,
+ Resource::Type type) {
+ if (!params.IsSpeculativePreload() && !params.IsLinkPreload())
+ return;
+ // CSP layout tests verify that preloads are subject to access checks by
+ // seeing if they are in the `preload started` list. Therefore do not add
+ // them to the list if the load is immediately denied.
+ if (resource->LoadFailedOrCanceled() &&
+ resource->GetResourceError().IsAccessCheck()) {
+ return;
+ }
+ PreloadKey key(params.Url(), type);
+ if (preloads_.find(key) != preloads_.end())
+ return;
+
+ preloads_.insert(key, resource);
+ resource->MarkAsPreload();
+ if (preloaded_urls_for_test_)
+ preloaded_urls_for_test_->insert(resource->Url().GetString());
+}
+
+bool ResourceFetcher::IsImageResourceDisallowedToBeReused(
+ const Resource& existing_resource) const {
+ // When images are disabled, don't ever load images, even if the image is
+ // cached or it is a data: url. In this case:
+ // - remove the image from the memory cache, and
+ // - create a new resource but defer loading (this is done by
+ // ResourceNeedsLoad()).
+ //
+ // This condition must be placed before the condition on |is_static_data| to
+ // prevent loading a data: URL.
+ //
+ // TODO(japhet): Can we get rid of one of these settings?
+
+ if (existing_resource.GetType() != Resource::kImage)
+ return false;
+
+ return !Context().AllowImage(images_enabled_, existing_resource.Url());
+}
+
+ResourceFetcher::RevalidationPolicy
+ResourceFetcher::DetermineRevalidationPolicy(
+ Resource::Type type,
+ const FetchParameters& fetch_params,
+ const Resource& existing_resource,
+ bool is_static_data) const {
+ RevalidationPolicy policy = DetermineRevalidationPolicyInternal(
+ type, fetch_params, existing_resource, is_static_data);
+
+ TRACE_EVENT_INSTANT1("blink", "ResourceFetcher::DetermineRevalidationPolicy",
+ TRACE_EVENT_SCOPE_THREAD, "revalidationPolicy", policy);
+
+ return policy;
+}
+
+ResourceFetcher::RevalidationPolicy
+ResourceFetcher::DetermineRevalidationPolicyInternal(
+ Resource::Type type,
+ const FetchParameters& fetch_params,
+ const Resource& existing_resource,
+ bool is_static_data) const {
+ const ResourceRequest& request = fetch_params.GetResourceRequest();
+
+ if (IsDownloadOrStreamRequest(request))
+ return kReload;
+
+ if (IsImageResourceDisallowedToBeReused(existing_resource))
+ return kReload;
+
+ // If the existing resource is loading and the associated fetcher is not equal
+ // to |this|, we must not use the resource. Otherwise, CSP violation may
+ // happen in redirect handling.
+ if (existing_resource.Loader() &&
+ existing_resource.Loader()->Fetcher() != this) {
+ return kReload;
+ }
+
+ // It's hard to share a not-yet-referenced preloads via MemoryCache correctly.
+ // A not-yet-matched preloads made by a foreign ResourceFetcher and stored in
+ // the memory cache could be used without this block.
+ if ((fetch_params.IsLinkPreload() || fetch_params.IsSpeculativePreload()) &&
+ existing_resource.IsUnusedPreload()) {
+ return kReload;
+ }
+
+ // Checks if the resource has an explicit policy about integrity metadata.
+ //
+ // This is necessary because ScriptResource and CSSStyleSheetResource objects
+ // do not keep the raw data around after the source is accessed once, so if
+ // the resource is accessed from the MemoryCache for a second time, there is
+ // no way to redo an integrity check.
+ //
+ // Thus, Blink implements a scheme where it caches the integrity information
+ // for those resources after the first time it is checked, and if there is
+ // another request for that resource, with the same integrity metadata, Blink
+ // skips the integrity calculation. However, if the integrity metadata is a
+ // mismatch, the MemoryCache must be skipped here, and a new request for the
+ // resource must be made to get the raw data. This is expected to be an
+ // uncommon case, however, as it implies two same-origin requests to the same
+ // resource, but with different integrity metadata.
+ if (existing_resource.MustRefetchDueToIntegrityMetadata(fetch_params)) {
+ return kReload;
+ }
+
+ // If the same URL has been loaded as a different type, we need to reload.
+ if (existing_resource.GetType() != type) {
+ // FIXME: If existingResource is a Preload and the new type is LinkPrefetch
+ // We really should discard the new prefetch since the preload has more
+ // specific type information! crbug.com/379893
+ // fast/dom/HTMLLinkElement/link-and-subresource-test hits this case.
+ RESOURCE_LOADING_DVLOG(1) << "ResourceFetcher::DetermineRevalidationPolicy "
+ "reloading due to type mismatch.";
+ return kReload;
+ }
+
+ // If resource was populated from a SubstituteData load or data: url, use it.
+ // This doesn't necessarily mean that |resource| was just created by using
+ // ResourceForStaticData().
+ if (is_static_data)
+ return kUse;
+
+ if (!existing_resource.CanReuse(fetch_params,
+ GetSourceOrigin(fetch_params.Options()))) {
+ RESOURCE_LOADING_DVLOG(1) << "ResourceFetcher::DetermineRevalidationPolicy "
+ "reloading due to Resource::CanReuse() "
+ "returning false.";
+ return kReload;
+ }
+
+ // Don't reload resources while pasting.
+ if (allow_stale_resources_)
+ return kUse;
+
+ // FORCE_CACHE uses the cache no matter what.
+ if (request.GetCacheMode() == mojom::FetchCacheMode::kForceCache)
+ return kUse;
+
+ // Don't reuse resources with Cache-control: no-store.
+ if (existing_resource.HasCacheControlNoStoreHeader()) {
+ RESOURCE_LOADING_DVLOG(1) << "ResourceFetcher::DetermineRevalidationPolicy "
+ "reloading due to Cache-control: no-store.";
+ return kReload;
+ }
+
+ // During the initial load, avoid loading the same resource multiple times for
+ // a single document, even if the cache policies would tell us to. We also
+ // group loads of the same resource together. Raw resources are exempted, as
+ // XHRs fall into this category and may have user-set Cache-Control: headers
+ // or other factors that require separate requests.
+ if (type != Resource::kRaw) {
+ if (!Context().IsLoadComplete() &&
+ cached_resources_map_.Contains(
+ MemoryCache::RemoveFragmentIdentifierIfNeeded(
+ existing_resource.Url())))
+ return kUse;
+ if (existing_resource.IsLoading())
+ return kUse;
+ }
+
+ // RELOAD always reloads
+ if (request.GetCacheMode() == mojom::FetchCacheMode::kBypassCache) {
+ RESOURCE_LOADING_DVLOG(1) << "ResourceFetcher::DetermineRevalidationPolicy "
+ "reloading due to "
+ "FetchCacheMode::kBypassCache";
+ return kReload;
+ }
+
+ // We'll try to reload the resource if it failed last time.
+ if (existing_resource.ErrorOccurred()) {
+ RESOURCE_LOADING_DVLOG(1) << "ResourceFetcher::DetermineRevalidationPolicy "
+ "reloading due to resource being in the error "
+ "state";
+ return kReload;
+ }
+
+ // List of available images logic allows images to be re-used without cache
+ // validation. We restrict this only to images from memory cache which are the
+ // same as the version in the current document.
+ if (type == Resource::kImage &&
+ &existing_resource == CachedResource(request.Url())) {
+ return kUse;
+ }
+
+ if (existing_resource.MustReloadDueToVaryHeader(request))
+ return kReload;
+
+ // If any of the redirects in the chain to loading the resource were not
+ // cacheable, we cannot reuse our cached resource.
+ if (!existing_resource.CanReuseRedirectChain()) {
+ RESOURCE_LOADING_DVLOG(1) << "ResourceFetcher::DetermineRevalidationPolicy "
+ "reloading due to an uncacheable redirect";
+ return kReload;
+ }
+
+ // Check if the cache headers requires us to revalidate (cache expiration for
+ // example).
+ if (request.GetCacheMode() == mojom::FetchCacheMode::kValidateCache ||
+ existing_resource.MustRevalidateDueToCacheHeaders() ||
+ request.CacheControlContainsNoCache()) {
+ // Revalidation is harmful for non-matched preloads because it may lead to
+ // sharing one preloaded resource among multiple ResourceFetchers.
+ if (existing_resource.IsUnusedPreload())
+ return kReload;
+
+ // See if the resource has usable ETag or Last-modified headers. If the page
+ // is controlled by the ServiceWorker, we choose the Reload policy because
+ // the revalidation headers should not be exposed to the
+ // ServiceWorker.(crbug.com/429570)
+ if (existing_resource.CanUseCacheValidator() &&
+ !Context().IsControlledByServiceWorker()) {
+ // If the resource is already a cache validator but not started yet, the
+ // |Use| policy should be applied to subsequent requests.
+ if (existing_resource.IsCacheValidator()) {
+ DCHECK(existing_resource.StillNeedsLoad());
+ return kUse;
+ }
+ return kRevalidate;
+ }
+
+ // No, must reload.
+ RESOURCE_LOADING_DVLOG(1) << "ResourceFetcher::DetermineRevalidationPolicy "
+ "reloading due to missing cache validators.";
+ return kReload;
+ }
+
+ return kUse;
+}
+
+void ResourceFetcher::SetAutoLoadImages(bool enable) {
+ if (enable == auto_load_images_)
+ return;
+
+ auto_load_images_ = enable;
+
+ if (!auto_load_images_)
+ return;
+
+ ReloadImagesIfNotDeferred();
+}
+
+void ResourceFetcher::SetImagesEnabled(bool enable) {
+ if (enable == images_enabled_)
+ return;
+
+ images_enabled_ = enable;
+
+ if (!images_enabled_)
+ return;
+
+ ReloadImagesIfNotDeferred();
+}
+
+bool ResourceFetcher::ShouldDeferImageLoad(const KURL& url) const {
+ return !Context().AllowImage(images_enabled_, url) || !auto_load_images_;
+}
+
+void ResourceFetcher::ReloadImagesIfNotDeferred() {
+ for (Resource* resource : document_resources_) {
+ if (resource->GetType() == Resource::kImage && resource->StillNeedsLoad() &&
+ !ShouldDeferImageLoad(resource->Url()))
+ StartLoad(resource);
+ }
+}
+
+void ResourceFetcher::ClearContext() {
+ DCHECK(resources_from_previous_fetcher_.IsEmpty());
+ scheduler_->Shutdown();
+ ClearPreloads(ResourceFetcher::kClearAllPreloads);
+ context_ = Context().Detach();
+
+ // Make sure the only requests still going are keepalive requests.
+ // Callers of ClearContext() should be calling StopFetching() prior
+ // to this, but it's possible for additional requests to start during
+ // StopFetching() (e.g., fallback fonts that only trigger when the
+ // first choice font failed to load).
+ StopFetching();
+
+ if (!loaders_.IsEmpty() || !non_blocking_loaders_.IsEmpty()) {
+ // There are some keepalive requests.
+ // The use of WrapPersistent creates a reference cycle intentionally,
+ // to keep the ResourceFetcher and ResourceLoaders alive until the requests
+ // complete or the timer fires.
+ keepalive_loaders_task_handle_ = PostDelayedCancellableTask(
+ *Context().GetLoadingTaskRunner(), FROM_HERE,
+ WTF::Bind(&ResourceFetcher::StopFetchingIncludingKeepaliveLoaders,
+ WrapPersistent(this)),
+ kKeepaliveLoadersTimeout);
+ }
+}
+
+int ResourceFetcher::BlockingRequestCount() const {
+ return loaders_.size();
+}
+
+int ResourceFetcher::NonblockingRequestCount() const {
+ return non_blocking_loaders_.size();
+}
+
+int ResourceFetcher::ActiveRequestCount() const {
+ return loaders_.size() + non_blocking_loaders_.size();
+}
+
+void ResourceFetcher::EnableIsPreloadedForTest() {
+ if (preloaded_urls_for_test_)
+ return;
+ preloaded_urls_for_test_ = std::make_unique<HashSet<String>>();
+
+ for (const auto& pair : preloads_) {
+ Resource* resource = pair.value;
+ preloaded_urls_for_test_->insert(resource->Url().GetString());
+ }
+}
+
+bool ResourceFetcher::IsPreloadedForTest(const KURL& url) const {
+ DCHECK(preloaded_urls_for_test_);
+ return preloaded_urls_for_test_->Contains(url.GetString());
+}
+
+void ResourceFetcher::ClearPreloads(ClearPreloadsPolicy policy) {
+ Vector<PreloadKey> keys_to_be_removed;
+ for (const auto& pair : preloads_) {
+ Resource* resource = pair.value;
+ if (policy == kClearAllPreloads || !resource->IsLinkPreload()) {
+ GetMemoryCache()->Remove(resource);
+ keys_to_be_removed.push_back(pair.key);
+ }
+ }
+ preloads_.RemoveAll(keys_to_be_removed);
+
+ matched_preloads_.clear();
+}
+
+void ResourceFetcher::WarnUnusedPreloads() {
+ for (const auto& pair : preloads_) {
+ Resource* resource = pair.value;
+ if (resource && resource->IsLinkPreload() && resource->IsUnusedPreload()) {
+ Context().AddWarningConsoleMessage(
+ "The resource " + resource->Url().GetString() +
+ " was preloaded using link preload but not used within a few " +
+ "seconds from the window's load event. Please make sure it has " +
+ "an appropriate `as` value and it is preloaded intentionally.",
+ FetchContext::kJSSource);
+ }
+ }
+}
+
+ArchiveResource* ResourceFetcher::CreateArchive(Resource* resource) {
+ // Only the top-frame can load MHTML.
+ if (!Context().IsMainFrame()) {
+ Context().AddErrorConsoleMessage(
+ "Attempted to load a multipart archive into an subframe: " +
+ resource->Url().GetString(),
+ FetchContext::kJSSource);
+ return nullptr;
+ }
+
+ archive_ = MHTMLArchive::Create(resource->Url(), resource->ResourceBuffer());
+ if (!archive_) {
+ // Log if attempting to load an invalid archive resource.
+ Context().AddErrorConsoleMessage(
+ "Malformed multipart archive: " + resource->Url().GetString(),
+ FetchContext::kJSSource);
+ return nullptr;
+ }
+
+ return archive_->MainResource();
+}
+
+ResourceTimingInfo* ResourceFetcher::GetNavigationTimingInfo() {
+ return navigation_timing_info_.get();
+}
+
+void ResourceFetcher::HandleLoadCompletion(Resource* resource) {
+ Context().DidLoadResource(resource);
+
+ resource->ReloadIfLoFiOrPlaceholderImage(this, Resource::kReloadIfNeeded);
+}
+
+void ResourceFetcher::HandleLoaderFinish(Resource* resource,
+ double finish_time,
+ LoaderFinishType type,
+ uint32_t inflight_keepalive_bytes,
+ bool blocked_cross_site_document) {
+ DCHECK(resource);
+
+ DCHECK_LE(inflight_keepalive_bytes, inflight_keepalive_bytes_);
+ inflight_keepalive_bytes_ -= inflight_keepalive_bytes;
+
+ ResourceLoader* loader = resource->Loader();
+ if (type == kDidFinishFirstPartInMultipart) {
+ // When loading a multipart resource, make the loader non-block when
+ // finishing loading the first part.
+ MoveResourceLoaderToNonBlocking(loader);
+ } else {
+ RemoveResourceLoader(loader);
+ DCHECK(!non_blocking_loaders_.Contains(loader));
+ }
+ DCHECK(!loaders_.Contains(loader));
+
+ const int64_t encoded_data_length =
+ resource->GetResponse().EncodedDataLength();
+
+ if (resource->GetType() == Resource::kMainResource) {
+ DCHECK(navigation_timing_info_);
+ // Store redirect responses that were packed inside the final response.
+ AddRedirectsToTimingInfo(resource, navigation_timing_info_.get());
+ if (resource->GetResponse().IsHTTP()) {
+ PopulateTimingInfo(navigation_timing_info_.get(), resource);
+ navigation_timing_info_->AddFinalTransferSize(
+ encoded_data_length == -1 ? 0 : encoded_data_length);
+ }
+ }
+ if (scoped_refptr<ResourceTimingInfo> info =
+ resource_timing_info_map_.Take(resource)) {
+ // Store redirect responses that were packed inside the final response.
+ AddRedirectsToTimingInfo(resource, info.get());
+
+ if (resource->GetResponse().IsHTTP() &&
+ resource->GetResponse().HttpStatusCode() < 400) {
+ PopulateTimingInfo(info.get(), resource);
+ info->SetLoadFinishTime(finish_time);
+ // encodedDataLength == -1 means "not available".
+ // TODO(ricea): Find cases where it is not available but the
+ // PerformanceResourceTiming spec requires it to be available and fix
+ // them.
+ info->AddFinalTransferSize(
+ encoded_data_length == -1 ? 0 : encoded_data_length);
+
+ if (resource->Options().request_initiator_context == kDocumentContext)
+ Context().AddResourceTiming(*info);
+ resource->ReportResourceTimingToClients(*info);
+ }
+ }
+
+ resource->VirtualTimePauser().UnpauseVirtualTime();
+ Context().DispatchDidFinishLoading(
+ resource->Identifier(), finish_time, encoded_data_length,
+ resource->GetResponse().DecodedBodyLength(), blocked_cross_site_document);
+
+ if (type == kDidFinishLoading)
+ resource->Finish(finish_time, Context().GetLoadingTaskRunner().get());
+
+ HandleLoadCompletion(resource);
+}
+
+void ResourceFetcher::HandleLoaderError(Resource* resource,
+ const ResourceError& error,
+ uint32_t inflight_keepalive_bytes) {
+ DCHECK(resource);
+
+ DCHECK_LE(inflight_keepalive_bytes, inflight_keepalive_bytes_);
+ inflight_keepalive_bytes_ -= inflight_keepalive_bytes;
+
+ RemoveResourceLoader(resource->Loader());
+
+ resource_timing_info_map_.Take(resource);
+
+ bool is_internal_request = resource->Options().initiator_info.name ==
+ FetchInitiatorTypeNames::internal;
+
+ resource->VirtualTimePauser().UnpauseVirtualTime();
+ Context().DispatchDidFail(
+ resource->LastResourceRequest().Url(), resource->Identifier(), error,
+ resource->GetResponse().EncodedDataLength(), is_internal_request);
+
+ if (error.IsCancellation())
+ RemovePreload(resource);
+ resource->FinishAsError(error, Context().GetLoadingTaskRunner().get());
+
+ HandleLoadCompletion(resource);
+}
+
+void ResourceFetcher::MoveResourceLoaderToNonBlocking(ResourceLoader* loader) {
+ DCHECK(loader);
+ // TODO(yoav): Convert CHECK to DCHECK if no crash reports come in.
+ CHECK(loaders_.Contains(loader));
+ non_blocking_loaders_.insert(loader);
+ loaders_.erase(loader);
+}
+
+bool ResourceFetcher::StartLoad(Resource* resource) {
+ DCHECK(resource);
+ DCHECK(resource->StillNeedsLoad());
+
+ ResourceRequest request(resource->GetResourceRequest());
+ ResourceLoader* loader = nullptr;
+
+ {
+ // Forbids JavaScript/revalidation until start()
+ // to prevent unintended state transitions.
+ Resource::RevalidationStartForbiddenScope
+ revalidation_start_forbidden_scope(resource);
+ ScriptForbiddenScope script_forbidden_scope;
+
+ if (!Context().ShouldLoadNewResource(resource->GetType()) &&
+ IsMainThread()) {
+ GetMemoryCache()->Remove(resource);
+ return false;
+ }
+
+ ResourceResponse response;
+
+ blink::probe::PlatformSendRequest probe(&Context(), resource->Identifier(),
+ request, response,
+ resource->Options().initiator_info);
+
+ if (Context().GetFrameScheduler()) {
+ WebScopedVirtualTimePauser virtual_time_pauser =
+ Context().GetFrameScheduler()->CreateWebScopedVirtualTimePauser(
+ resource->Url().GetString(),
+ WebScopedVirtualTimePauser::VirtualTaskDuration::kNonInstant);
+ virtual_time_pauser.PauseVirtualTime();
+ resource->VirtualTimePauser() = std::move(virtual_time_pauser);
+ }
+ Context().DispatchWillSendRequest(resource->Identifier(), request, response,
+ resource->GetType(),
+ resource->Options().initiator_info);
+
+ // TODO(shaochuan): Saving modified ResourceRequest back to |resource|,
+ // remove once dispatchWillSendRequest() takes const ResourceRequest.
+ // crbug.com/632580
+ resource->SetResourceRequest(request);
+
+ using QuotaType = decltype(inflight_keepalive_bytes_);
+ QuotaType size = 0;
+ if (request.GetKeepalive() && request.HttpBody()) {
+ auto original_size = request.HttpBody()->SizeInBytes();
+ DCHECK_LE(inflight_keepalive_bytes_, kKeepaliveInflightBytesQuota);
+ if (original_size > std::numeric_limits<QuotaType>::max())
+ return false;
+ size = static_cast<QuotaType>(original_size);
+ if (kKeepaliveInflightBytesQuota - inflight_keepalive_bytes_ < size)
+ return false;
+
+ inflight_keepalive_bytes_ += size;
+ }
+
+ loader = ResourceLoader::Create(this, scheduler_, resource, size);
+ if (resource->ShouldBlockLoadEvent())
+ loaders_.insert(loader);
+ else
+ non_blocking_loaders_.insert(loader);
+
+ StorePerformanceTimingInitiatorInformation(resource);
+
+ // NotifyStartLoad() shouldn't cause AddClient/RemoveClient().
+ Resource::ProhibitAddRemoveClientInScope
+ prohibit_add_remove_client_in_scope(resource);
+
+ resource->NotifyStartLoad();
+ }
+
+ loader->Start();
+ return true;
+}
+
+void ResourceFetcher::RemoveResourceLoader(ResourceLoader* loader) {
+ DCHECK(loader);
+ if (loaders_.Contains(loader))
+ loaders_.erase(loader);
+ else if (non_blocking_loaders_.Contains(loader))
+ non_blocking_loaders_.erase(loader);
+ else
+ NOTREACHED();
+
+ if (loaders_.IsEmpty() && non_blocking_loaders_.IsEmpty())
+ keepalive_loaders_task_handle_.Cancel();
+}
+
+void ResourceFetcher::StopFetching() {
+ StopFetchingInternal(StopFetchingTarget::kExcludingKeepaliveLoaders);
+}
+
+void ResourceFetcher::SetDefersLoading(bool defers) {
+ for (const auto& loader : non_blocking_loaders_)
+ loader->SetDefersLoading(defers);
+ for (const auto& loader : loaders_)
+ loader->SetDefersLoading(defers);
+}
+
+void ResourceFetcher::UpdateAllImageResourcePriorities() {
+ TRACE_EVENT0(
+ "blink",
+ "ResourceLoadPriorityOptimizer::updateAllImageResourcePriorities");
+ for (Resource* resource : document_resources_) {
+ if (!resource || resource->GetType() != Resource::kImage ||
+ !resource->IsLoading())
+ continue;
+
+ ResourcePriority resource_priority = resource->PriorityFromObservers();
+ ResourceLoadPriority resource_load_priority =
+ ComputeLoadPriority(Resource::kImage, resource->GetResourceRequest(),
+ resource_priority.visibility);
+ if (resource_load_priority == resource->GetResourceRequest().Priority())
+ continue;
+
+ resource->DidChangePriority(resource_load_priority,
+ resource_priority.intra_priority_value);
+ network_instrumentation::ResourcePrioritySet(resource->Identifier(),
+ resource_load_priority);
+ Context().DispatchDidChangeResourcePriority(
+ resource->Identifier(), resource_load_priority,
+ resource_priority.intra_priority_value);
+ }
+}
+
+void ResourceFetcher::ReloadLoFiImages() {
+ for (Resource* resource : document_resources_) {
+ if (resource)
+ resource->ReloadIfLoFiOrPlaceholderImage(this, Resource::kReloadAlways);
+ }
+}
+
+String ResourceFetcher::GetCacheIdentifier() const {
+ if (Context().IsControlledByServiceWorker())
+ return String::Number(Context().ServiceWorkerID());
+ return MemoryCache::DefaultCacheIdentifier();
+}
+
+void ResourceFetcher::EmulateLoadStartedForInspector(
+ Resource* resource,
+ const KURL& url,
+ WebURLRequest::RequestContext request_context,
+ const AtomicString& initiator_name) {
+ if (CachedResource(url))
+ return;
+ ResourceRequest resource_request(url);
+ resource_request.SetRequestContext(request_context);
+ ResourceLoaderOptions options = resource->Options();
+ options.initiator_info.name = initiator_name;
+ FetchParameters params(resource_request, options);
+ Context().CanRequest(resource->GetType(), resource->LastResourceRequest(),
+ resource->LastResourceRequest().Url(), params.Options(),
+ SecurityViolationReportingPolicy::kReport,
+ params.GetOriginRestriction(),
+ resource->LastResourceRequest().GetRedirectStatus());
+ RequestLoadStarted(resource->Identifier(), resource, params, kUse);
+}
+
+void ResourceFetcher::PrepareForLeakDetection() {
+ // Stop loaders including keepalive ones that may persist after page
+ // navigation and thus affect instance counters of leak detection.
+ StopFetchingIncludingKeepaliveLoaders();
+}
+
+void ResourceFetcher::StopFetchingInternal(StopFetchingTarget target) {
+ // TODO(toyoshim): May want to suspend scheduler while canceling loaders so
+ // that the cancellations below do not awake unnecessary scheduling.
+
+ HeapVector<Member<ResourceLoader>> loaders_to_cancel;
+ for (const auto& loader : non_blocking_loaders_) {
+ if (target == StopFetchingTarget::kIncludingKeepaliveLoaders ||
+ !loader->ShouldBeKeptAliveWhenDetached()) {
+ loaders_to_cancel.push_back(loader);
+ }
+ }
+ for (const auto& loader : loaders_) {
+ if (target == StopFetchingTarget::kIncludingKeepaliveLoaders ||
+ !loader->ShouldBeKeptAliveWhenDetached()) {
+ loaders_to_cancel.push_back(loader);
+ }
+ }
+
+ for (const auto& loader : loaders_to_cancel) {
+ if (loaders_.Contains(loader) || non_blocking_loaders_.Contains(loader))
+ loader->Cancel();
+ }
+}
+
+void ResourceFetcher::StopFetchingIncludingKeepaliveLoaders() {
+ StopFetchingInternal(StopFetchingTarget::kIncludingKeepaliveLoaders);
+}
+
+void ResourceFetcher::Trace(blink::Visitor* visitor) {
+ visitor->Trace(context_);
+ visitor->Trace(scheduler_);
+ visitor->Trace(archive_);
+ visitor->Trace(loaders_);
+ visitor->Trace(non_blocking_loaders_);
+ visitor->Trace(cached_resources_map_);
+ visitor->Trace(document_resources_);
+ visitor->Trace(resources_from_previous_fetcher_);
+ visitor->Trace(preloads_);
+ visitor->Trace(matched_preloads_);
+ visitor->Trace(resource_timing_info_map_);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
new file mode 100644
index 00000000000..134349299fb
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
@@ -0,0 +1,345 @@
+/*
+ Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
+ Copyright (C) 2001 Dirk Mueller <mueller@kde.org>
+ Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All
+ rights reserved.
+ Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ This class provides all functionality needed for loading images, style
+ sheets and html pages from the web. It has a memory cache for these objects.
+*/
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_FETCHER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_FETCHER_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_context.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_info.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
+#include "third_party/blink/renderer/platform/loader/fetch/preload_key.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_error.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_load_priority.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
+#include "third_party/blink/renderer/platform/loader/fetch/substitute_data.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/timer.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"
+
+namespace blink {
+
+class ArchiveResource;
+class MHTMLArchive;
+class KURL;
+class ResourceTimingInfo;
+
+// The ResourceFetcher provides a per-context interface to the MemoryCache and
+// enforces a bunch of security checks and rules for resource revalidation. Its
+// lifetime is roughly per-DocumentLoader, in that it is generally created in
+// the DocumentLoader constructor and loses its ability to generate network
+// requests when the DocumentLoader is destroyed. Documents also hold a pointer
+// to ResourceFetcher for their lifetime (and will create one if they are
+// initialized without a LocalFrame), so a Document can keep a ResourceFetcher
+// alive past detach if scripts still reference the Document.
+class PLATFORM_EXPORT ResourceFetcher
+ : public GarbageCollectedFinalized<ResourceFetcher> {
+ WTF_MAKE_NONCOPYABLE(ResourceFetcher);
+ USING_PRE_FINALIZER(ResourceFetcher, ClearPreloads);
+
+ public:
+ static ResourceFetcher* Create(FetchContext* context) {
+ return new ResourceFetcher(context);
+ }
+ virtual ~ResourceFetcher();
+ virtual void Trace(blink::Visitor*);
+
+ // Triggers a fetch based on the given FetchParameters (if there isn't a
+ // suitable Resource already cached) and registers the given ResourceClient
+ // with the Resource. Guaranteed to return a non-null Resource of the subtype
+ // specified by ResourceFactory::GetType().
+ Resource* RequestResource(FetchParameters&,
+ const ResourceFactory&,
+ ResourceClient*,
+ const SubstituteData& = SubstituteData());
+
+ Resource* CachedResource(const KURL&) const;
+
+ using DocumentResourceMap = HeapHashMap<String, WeakMember<Resource>>;
+ const DocumentResourceMap& AllResources() const {
+ return cached_resources_map_;
+ }
+
+ void HoldResourcesFromPreviousFetcher(ResourceFetcher*);
+ void ClearResourcesFromPreviousFetcher();
+
+ // Binds the given Resource instance to this ResourceFetcher instance to
+ // start loading the Resource actually.
+ // Usually, RequestResource() calls this method internally, but needs to
+ // call this method explicitly on cases such as ResourceNeedsLoad() returning
+ // false.
+ bool StartLoad(Resource*);
+
+ void SetAutoLoadImages(bool);
+ void SetImagesEnabled(bool);
+
+ FetchContext& Context() const {
+ return context_ ? *context_.Get() : FetchContext::NullInstance();
+ }
+ void ClearContext();
+
+ int BlockingRequestCount() const;
+ int NonblockingRequestCount() const;
+ int ActiveRequestCount() const;
+
+ enum ClearPreloadsPolicy {
+ kClearAllPreloads,
+ kClearSpeculativeMarkupPreloads
+ };
+
+ void EnableIsPreloadedForTest();
+ bool IsPreloadedForTest(const KURL&) const;
+
+ int CountPreloads() const { return preloads_.size(); }
+ void ClearPreloads(ClearPreloadsPolicy = kClearAllPreloads);
+ void LogPreloadStats(ClearPreloadsPolicy);
+ void WarnUnusedPreloads();
+
+ MHTMLArchive* Archive() const { return archive_.Get(); }
+ ArchiveResource* CreateArchive(Resource*);
+
+ void SetDefersLoading(bool);
+ void StopFetching();
+
+ bool ShouldDeferImageLoad(const KURL&) const;
+
+ void RecordResourceTimingOnRedirect(Resource*, const ResourceResponse&, bool);
+
+ enum LoaderFinishType { kDidFinishLoading, kDidFinishFirstPartInMultipart };
+ void HandleLoaderFinish(Resource*,
+ double finish_time,
+ LoaderFinishType,
+ uint32_t inflight_keepalive_bytes,
+ bool blocked_cross_site_document);
+ void HandleLoaderError(Resource*,
+ const ResourceError&,
+ uint32_t inflight_keepalive_bytes);
+ bool IsControlledByServiceWorker() const;
+
+ String GetCacheIdentifier() const;
+
+ enum IsImageSet { kImageNotImageSet, kImageIsImageSet };
+
+ WARN_UNUSED_RESULT static WebURLRequest::RequestContext
+ DetermineRequestContext(Resource::Type, IsImageSet, bool is_main_frame);
+
+ void UpdateAllImageResourcePriorities();
+
+ void ReloadLoFiImages();
+
+ // Calling this method before main document resource is fetched is invalid.
+ ResourceTimingInfo* GetNavigationTimingInfo();
+
+ // Returns whether the given resource is contained as a preloaded resource.
+ bool ContainsAsPreload(Resource*) const;
+
+ void RemovePreload(Resource*);
+
+ void LoosenLoadThrottlingPolicy() { scheduler_->LoosenThrottlingPolicy(); }
+ void OnNetworkQuiet() { scheduler_->OnNetworkQuiet(); }
+
+ // Workaround for https://crbug.com/666214.
+ // TODO(hiroshige): Remove this hack.
+ void EmulateLoadStartedForInspector(Resource*,
+ const KURL&,
+ WebURLRequest::RequestContext,
+ const AtomicString& initiator_name);
+
+ // This is called from leak detectors (Real-world leak detector & layout test
+ // leak detector) to clean up loaders after page navigation before instance
+ // counting.
+ void PrepareForLeakDetection();
+
+ private:
+ friend class ResourceCacheValidationSuppressor;
+ enum class StopFetchingTarget {
+ kExcludingKeepaliveLoaders,
+ kIncludingKeepaliveLoaders,
+ };
+
+ ResourceFetcher(FetchContext*);
+
+ void InitializeRevalidation(ResourceRequest&, Resource*);
+ // When |security_origin| of the ResourceLoaderOptions is not a nullptr, it'll
+ // be used instead of the associated FetchContext's SecurityOrigin.
+ scoped_refptr<const SecurityOrigin> GetSourceOrigin(
+ const ResourceLoaderOptions&) const;
+ void AddToMemoryCacheIfNeeded(const FetchParameters&, Resource*);
+ Resource* CreateResourceForLoading(const FetchParameters&,
+ const ResourceFactory&);
+ void StorePerformanceTimingInitiatorInformation(Resource*);
+ ResourceLoadPriority ComputeLoadPriority(
+ Resource::Type,
+ const ResourceRequest&,
+ ResourcePriority::VisibilityStatus,
+ FetchParameters::DeferOption = FetchParameters::kNoDefer,
+ FetchParameters::SpeculativePreloadType =
+ FetchParameters::SpeculativePreloadType::kNotSpeculative,
+ bool is_link_preload = false);
+
+ Resource* RequestResourceInternal(FetchParameters&,
+ const ResourceFactory&,
+ const SubstituteData&);
+ ResourceRequestBlockedReason PrepareRequest(FetchParameters&,
+ const ResourceFactory&,
+ const SubstituteData&,
+ unsigned long identifier);
+
+ Resource* ResourceForStaticData(const FetchParameters&,
+ const ResourceFactory&,
+ const SubstituteData&);
+ Resource* ResourceForBlockedRequest(const FetchParameters&,
+ const ResourceFactory&,
+ ResourceRequestBlockedReason);
+
+ Resource* MatchPreload(const FetchParameters& params, Resource::Type);
+ void InsertAsPreloadIfNecessary(Resource*,
+ const FetchParameters& params,
+ Resource::Type);
+
+ bool IsImageResourceDisallowedToBeReused(const Resource&) const;
+
+ void StopFetchingInternal(StopFetchingTarget);
+ void StopFetchingIncludingKeepaliveLoaders();
+
+ // RevalidationPolicy enum values are used in UMAs https://crbug.com/579496.
+ enum RevalidationPolicy { kUse, kRevalidate, kReload, kLoad };
+
+ // A wrapper just for placing a trace_event macro.
+ RevalidationPolicy DetermineRevalidationPolicy(
+ Resource::Type,
+ const FetchParameters&,
+ const Resource& existing_resource,
+ bool is_static_data) const;
+ // Determines a RevalidationPolicy given a FetchParameters and an existing
+ // resource retrieved from the memory cache (can be a newly constructed one
+ // for a static data).
+ RevalidationPolicy DetermineRevalidationPolicyInternal(
+ Resource::Type,
+ const FetchParameters&,
+ const Resource& existing_resource,
+ bool is_static_data) const;
+
+ void MakePreloadedResourceBlockOnloadIfNeeded(Resource*,
+ const FetchParameters&);
+ void MoveResourceLoaderToNonBlocking(ResourceLoader*);
+ void RemoveResourceLoader(ResourceLoader*);
+ void HandleLoadCompletion(Resource*);
+
+ void RequestLoadStarted(unsigned long identifier,
+ Resource*,
+ const FetchParameters&,
+ RevalidationPolicy,
+ bool is_static_data = false);
+
+ void DidLoadResourceFromMemoryCache(unsigned long identifier,
+ Resource*,
+ const ResourceRequest&);
+
+ bool ResourceNeedsLoad(Resource*, const FetchParameters&, RevalidationPolicy);
+
+ void ResourceTimingReportTimerFired(TimerBase*);
+
+ void ReloadImagesIfNotDeferred();
+
+ void UpdateMemoryCacheStats(Resource*,
+ RevalidationPolicy,
+ const FetchParameters&,
+ const ResourceFactory&,
+ bool is_static_data) const;
+
+ Member<FetchContext> context_;
+ Member<ResourceLoadScheduler> scheduler_;
+
+ DocumentResourceMap cached_resources_map_;
+ HeapHashSet<WeakMember<Resource>> document_resources_;
+
+ // When populated, forces Resources to remain alive across a navigation, to
+ // increase the odds the next document will be able to reuse resources from
+ // the previous page. Unpopulated unless experiment is enabled.
+ HeapHashSet<Member<Resource>> resources_from_previous_fetcher_;
+
+ HeapHashMap<PreloadKey, Member<Resource>> preloads_;
+ HeapVector<Member<Resource>> matched_preloads_;
+ Member<MHTMLArchive> archive_;
+
+ TaskRunnerTimer<ResourceFetcher> resource_timing_report_timer_;
+
+ using ResourceTimingInfoMap =
+ HeapHashMap<Member<Resource>, scoped_refptr<ResourceTimingInfo>>;
+ ResourceTimingInfoMap resource_timing_info_map_;
+
+ scoped_refptr<ResourceTimingInfo> navigation_timing_info_;
+
+ Vector<scoped_refptr<ResourceTimingInfo>> scheduled_resource_timing_reports_;
+
+ HeapHashSet<Member<ResourceLoader>> loaders_;
+ HeapHashSet<Member<ResourceLoader>> non_blocking_loaders_;
+
+ std::unique_ptr<HashSet<String>> preloaded_urls_for_test_;
+
+ // Timeout timer for keepalive requests.
+ TaskHandle keepalive_loaders_task_handle_;
+
+ uint32_t inflight_keepalive_bytes_ = 0;
+
+ // 28 bits left
+ bool auto_load_images_ : 1;
+ bool images_enabled_ : 1;
+ bool allow_stale_resources_ : 1;
+ bool image_fetched_ : 1;
+
+ static constexpr uint32_t kKeepaliveInflightBytesQuota = 64 * 1024;
+};
+
+class ResourceCacheValidationSuppressor {
+ WTF_MAKE_NONCOPYABLE(ResourceCacheValidationSuppressor);
+ STACK_ALLOCATED();
+
+ public:
+ explicit ResourceCacheValidationSuppressor(ResourceFetcher* loader)
+ : loader_(loader), previous_state_(false) {
+ if (loader_) {
+ previous_state_ = loader_->allow_stale_resources_;
+ loader_->allow_stale_resources_ = true;
+ }
+ }
+ ~ResourceCacheValidationSuppressor() {
+ if (loader_)
+ loader_->allow_stale_resources_ = previous_state_;
+ }
+
+ private:
+ Member<ResourceFetcher> loader_;
+ bool previous_state_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_FETCHER_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
new file mode 100644
index 00000000000..679e35ddf20
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
@@ -0,0 +1,852 @@
+/*
+ * Copyright (c) 2013, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
+
+#include <memory>
+#include "services/network/public/mojom/request_context_frame_type.mojom-shared.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/modules/fetch/fetch_api_request.mojom-shared.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_url_loader.h"
+#include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
+#include "third_party/blink/public/platform/web_url_response.h"
+#include "third_party/blink/renderer/platform/exported/wrapped_resource_response.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/heap/heap_allocator.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_info.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_type_names.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
+#include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
+#include "third_party/blink/renderer/platform/loader/fetch/raw_resource.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_error.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h"
+#include "third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.h"
+#include "third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h"
+#include "third_party/blink/renderer/platform/loader/testing/mock_resource.h"
+#include "third_party/blink/renderer/platform/loader/testing/mock_resource_client.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/weburl_loader_mock.h"
+#include "third_party/blink/renderer/platform/testing/weburl_loader_mock_factory_impl.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+
+namespace blink {
+
+namespace {
+
+constexpr char kTestResourceFilename[] = "white-1x1.png";
+constexpr char kTestResourceMimeType[] = "image/png";
+constexpr int kTestResourceSize = 103; // size of white-1x1.png
+
+void RegisterMockedURLLoadWithCustomResponse(const KURL& url,
+ const ResourceResponse& response) {
+ URLTestHelpers::RegisterMockedURLLoadWithCustomResponse(
+ url, test::PlatformTestDataPath(kTestResourceFilename),
+ WrappedResourceResponse(response));
+}
+
+void RegisterMockedURLLoad(const KURL& url) {
+ URLTestHelpers::RegisterMockedURLLoad(
+ url, test::PlatformTestDataPath(kTestResourceFilename),
+ kTestResourceMimeType);
+}
+
+} // namespace
+
+class ResourceFetcherTest : public testing::Test {
+ public:
+ ResourceFetcherTest() = default;
+ ~ResourceFetcherTest() override { GetMemoryCache()->EvictResources(); }
+
+ protected:
+ MockFetchContext* Context() { return platform_->Context(); }
+ void AddResourceToMemoryCache(
+ Resource* resource,
+ scoped_refptr<const SecurityOrigin> source_origin) {
+ resource->SetSourceOrigin(source_origin);
+ GetMemoryCache()->Add(resource);
+ }
+
+ ScopedTestingPlatformSupport<FetchTestingPlatformSupport> platform_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ResourceFetcherTest);
+};
+
+TEST_F(ResourceFetcherTest, StartLoadAfterFrameDetach) {
+ KURL secure_url("https://secureorigin.test/image.png");
+ // Try to request a url. The request should fail, and a resource in an error
+ // state should be returned, and no resource should be present in the cache.
+ ResourceFetcher* fetcher =
+ ResourceFetcher::Create(&FetchContext::NullInstance());
+
+ ResourceRequest resource_request(secure_url);
+ resource_request.SetRequestContext(WebURLRequest::kRequestContextInternal);
+ FetchParameters fetch_params(resource_request);
+ Resource* resource = RawResource::Fetch(fetch_params, fetcher, nullptr);
+ ASSERT_TRUE(resource);
+ EXPECT_TRUE(resource->ErrorOccurred());
+ EXPECT_TRUE(resource->GetResourceError().IsAccessCheck());
+ EXPECT_FALSE(GetMemoryCache()->ResourceForURL(secure_url));
+
+ // Start by calling StartLoad() directly, rather than via RequestResource().
+ // This shouldn't crash.
+ fetcher->StartLoad(RawResource::CreateForTest(secure_url, Resource::kRaw));
+}
+
+TEST_F(ResourceFetcherTest, UseExistingResource) {
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+
+ KURL url("http://127.0.0.1:8000/foo.html");
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+ response.SetHTTPHeaderField(HTTPNames::Cache_Control, "max-age=3600");
+ RegisterMockedURLLoadWithCustomResponse(url, response);
+
+ FetchParameters fetch_params{ResourceRequest(url)};
+ Resource* resource = MockResource::Fetch(fetch_params, fetcher, nullptr);
+ ASSERT_TRUE(resource);
+ platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+ EXPECT_TRUE(resource->IsLoaded());
+ EXPECT_TRUE(GetMemoryCache()->Contains(resource));
+
+ Resource* new_resource = MockResource::Fetch(fetch_params, fetcher, nullptr);
+ EXPECT_EQ(resource, new_resource);
+}
+
+// Verify that the ad bit is copied to WillSendRequest's request when the
+// response is served from the memory cache.
+TEST_F(ResourceFetcherTest, WillSendRequestAdBit) {
+ // Add a resource to the memory cache.
+ scoped_refptr<const SecurityOrigin> source_origin =
+ SecurityOrigin::CreateUnique();
+ Context()->SetSecurityOrigin(source_origin);
+ KURL url("http://127.0.0.1:8000/foo.html");
+ Resource* resource = RawResource::CreateForTest(url, Resource::kRaw);
+ AddResourceToMemoryCache(resource, source_origin);
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+ response.SetHTTPHeaderField(HTTPNames::Cache_Control, "max-age=3600");
+ resource->ResponseReceived(response, nullptr);
+ resource->FinishForTest();
+
+ // Fetch the cached resource. The request to DispatchWillSendRequest should
+ // preserve the ad bit.
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+ ResourceRequest resource_request(url);
+ resource_request.SetIsAdResource();
+ resource_request.SetRequestContext(WebURLRequest::kRequestContextInternal);
+ FetchParameters fetch_params(resource_request);
+ platform_->GetURLLoaderMockFactory()->RegisterURL(url, WebURLResponse(), "");
+ Resource* new_resource = RawResource::Fetch(fetch_params, fetcher, nullptr);
+
+ EXPECT_EQ(resource, new_resource);
+ WTF::Optional<ResourceRequest> new_request =
+ Context()->RequestFromWillSendRequest();
+ EXPECT_TRUE(new_request.has_value());
+ EXPECT_TRUE(new_request.value().IsAdResource());
+}
+
+TEST_F(ResourceFetcherTest, Vary) {
+ scoped_refptr<const SecurityOrigin> source_origin =
+ SecurityOrigin::CreateUnique();
+ Context()->SetSecurityOrigin(source_origin);
+
+ KURL url("http://127.0.0.1:8000/foo.html");
+ Resource* resource = RawResource::CreateForTest(url, Resource::kRaw);
+ AddResourceToMemoryCache(resource, source_origin);
+
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+ response.SetHTTPHeaderField(HTTPNames::Cache_Control, "max-age=3600");
+ response.SetHTTPHeaderField(HTTPNames::Vary, "*");
+ resource->ResponseReceived(response, nullptr);
+ resource->FinishForTest();
+ ASSERT_TRUE(resource->MustReloadDueToVaryHeader(ResourceRequest(url)));
+
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+ ResourceRequest resource_request(url);
+ resource_request.SetRequestContext(WebURLRequest::kRequestContextInternal);
+ FetchParameters fetch_params(resource_request);
+ platform_->GetURLLoaderMockFactory()->RegisterURL(url, WebURLResponse(), "");
+ Resource* new_resource = RawResource::Fetch(fetch_params, fetcher, nullptr);
+ EXPECT_NE(resource, new_resource);
+ new_resource->Loader()->Cancel();
+}
+
+TEST_F(ResourceFetcherTest, NavigationTimingInfo) {
+ KURL url("http://127.0.0.1:8000/foo.html");
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+ ResourceRequest resource_request(url);
+ resource_request.SetFrameType(
+ network::mojom::RequestContextFrameType::kNested);
+ resource_request.SetRequestContext(WebURLRequest::kRequestContextForm);
+ FetchParameters fetch_params(resource_request);
+ platform_->GetURLLoaderMockFactory()->RegisterURL(url, WebURLResponse(), "");
+ Resource* resource = RawResource::FetchMainResource(
+ fetch_params, fetcher, nullptr, SubstituteData());
+ resource->ResponseReceived(response, nullptr);
+ EXPECT_EQ(resource->GetType(), Resource::kMainResource);
+
+ ResourceTimingInfo* navigation_timing_info =
+ fetcher->GetNavigationTimingInfo();
+ ASSERT_TRUE(navigation_timing_info);
+ long long encoded_data_length = 123;
+ resource->Loader()->DidFinishLoading(0.0, encoded_data_length, 0, 0, false);
+ EXPECT_EQ(navigation_timing_info->TransferSize(), encoded_data_length);
+
+ // When there are redirects.
+ KURL redirect_url("http://127.0.0.1:8000/redirect.html");
+ ResourceResponse redirect_response(redirect_url);
+ redirect_response.SetHTTPStatusCode(200);
+ long long redirect_encoded_data_length = 123;
+ redirect_response.SetEncodedDataLength(redirect_encoded_data_length);
+ ResourceRequest redirect_resource_request(url);
+ fetcher->RecordResourceTimingOnRedirect(resource, redirect_response, false);
+ EXPECT_EQ(navigation_timing_info->TransferSize(),
+ encoded_data_length + redirect_encoded_data_length);
+}
+
+TEST_F(ResourceFetcherTest, VaryOnBack) {
+ scoped_refptr<const SecurityOrigin> source_origin =
+ SecurityOrigin::CreateUnique();
+ Context()->SetSecurityOrigin(source_origin);
+
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+
+ KURL url("http://127.0.0.1:8000/foo.html");
+ Resource* resource = RawResource::CreateForTest(url, Resource::kRaw);
+ AddResourceToMemoryCache(resource, source_origin);
+
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+ response.SetHTTPHeaderField(HTTPNames::Cache_Control, "max-age=3600");
+ response.SetHTTPHeaderField(HTTPNames::Vary, "*");
+ resource->ResponseReceived(response, nullptr);
+ resource->FinishForTest();
+ ASSERT_TRUE(resource->MustReloadDueToVaryHeader(ResourceRequest(url)));
+
+ ResourceRequest resource_request(url);
+ resource_request.SetCacheMode(mojom::FetchCacheMode::kForceCache);
+ resource_request.SetRequestContext(WebURLRequest::kRequestContextInternal);
+ FetchParameters fetch_params(resource_request);
+ Resource* new_resource = RawResource::Fetch(fetch_params, fetcher, nullptr);
+ EXPECT_EQ(resource, new_resource);
+}
+
+TEST_F(ResourceFetcherTest, VaryResource) {
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+
+ KURL url("http://127.0.0.1:8000/foo.html");
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+ response.SetHTTPHeaderField(HTTPNames::Cache_Control, "max-age=3600");
+ response.SetHTTPHeaderField(HTTPNames::Vary, "*");
+ RegisterMockedURLLoadWithCustomResponse(url, response);
+
+ FetchParameters fetch_params_original{ResourceRequest(url)};
+ Resource* resource =
+ MockResource::Fetch(fetch_params_original, fetcher, nullptr);
+ ASSERT_TRUE(resource);
+ platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+ ASSERT_TRUE(resource->MustReloadDueToVaryHeader(ResourceRequest(url)));
+
+ FetchParameters fetch_params{ResourceRequest(url)};
+ Resource* new_resource = MockResource::Fetch(fetch_params, fetcher, nullptr);
+ EXPECT_EQ(resource, new_resource);
+}
+
+class RequestSameResourceOnComplete
+ : public GarbageCollectedFinalized<RequestSameResourceOnComplete>,
+ public RawResourceClient {
+ USING_GARBAGE_COLLECTED_MIXIN(RequestSameResourceOnComplete);
+
+ public:
+ explicit RequestSameResourceOnComplete(FetchParameters& params,
+ ResourceFetcher* fetcher)
+ : notify_finished_called_(false),
+ source_origin_(fetcher->Context().GetSecurityOrigin()) {
+ MockResource::Fetch(params, fetcher, this);
+ }
+
+ void NotifyFinished(Resource* resource) override {
+ EXPECT_EQ(GetResource(), resource);
+ MockFetchContext* context =
+ MockFetchContext::Create(MockFetchContext::kShouldLoadNewResource);
+ context->SetSecurityOrigin(source_origin_);
+ ResourceFetcher* fetcher2 = ResourceFetcher::Create(context);
+ ResourceRequest resource_request2(GetResource()->Url());
+ resource_request2.SetCacheMode(mojom::FetchCacheMode::kValidateCache);
+ FetchParameters fetch_params2(resource_request2);
+ Resource* resource2 = MockResource::Fetch(fetch_params2, fetcher2, nullptr);
+ EXPECT_EQ(GetResource(), resource2);
+ notify_finished_called_ = true;
+ ClearResource();
+ }
+ bool NotifyFinishedCalled() const { return notify_finished_called_; }
+
+ void Trace(blink::Visitor* visitor) override {
+ RawResourceClient::Trace(visitor);
+ }
+
+ String DebugName() const override { return "RequestSameResourceOnComplete"; }
+
+ private:
+ bool notify_finished_called_;
+ scoped_refptr<const SecurityOrigin> source_origin_;
+};
+
+TEST_F(ResourceFetcherTest, RevalidateWhileFinishingLoading) {
+ scoped_refptr<const SecurityOrigin> source_origin =
+ SecurityOrigin::CreateUnique();
+ Context()->SetSecurityOrigin(source_origin);
+
+ KURL url("http://127.0.0.1:8000/foo.png");
+
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+ response.SetHTTPHeaderField(HTTPNames::Cache_Control, "max-age=3600");
+ response.SetHTTPHeaderField(HTTPNames::ETag, "1234567890");
+ RegisterMockedURLLoadWithCustomResponse(url, response);
+
+ ResourceFetcher* fetcher1 = ResourceFetcher::Create(Context());
+ ResourceRequest request1(url);
+ request1.SetHTTPHeaderField(HTTPNames::Cache_Control, "no-cache");
+ FetchParameters fetch_params1(request1);
+ Persistent<RequestSameResourceOnComplete> client =
+ new RequestSameResourceOnComplete(fetch_params1, fetcher1);
+ platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+ EXPECT_TRUE(client->NotifyFinishedCalled());
+}
+
+TEST_F(ResourceFetcherTest, DontReuseMediaDataUrl) {
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+ ResourceRequest request(KURL("data:text/html,foo"));
+ request.SetRequestContext(WebURLRequest::kRequestContextVideo);
+ request.SetFetchCredentialsMode(network::mojom::FetchCredentialsMode::kOmit);
+ ResourceLoaderOptions options;
+ options.data_buffering_policy = kDoNotBufferData;
+ options.initiator_info.name = FetchInitiatorTypeNames::internal;
+ FetchParameters fetch_params(request, options);
+ Resource* resource1 = RawResource::FetchMedia(fetch_params, fetcher, nullptr);
+ Resource* resource2 = RawResource::FetchMedia(fetch_params, fetcher, nullptr);
+ EXPECT_NE(resource1, resource2);
+}
+
+class ServeRequestsOnCompleteClient final
+ : public GarbageCollectedFinalized<ServeRequestsOnCompleteClient>,
+ public RawResourceClient {
+ USING_GARBAGE_COLLECTED_MIXIN(ServeRequestsOnCompleteClient);
+
+ public:
+ void NotifyFinished(Resource*) override {
+ Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+ ClearResource();
+ }
+
+ // No callbacks should be received except for the NotifyFinished() triggered
+ // by ResourceLoader::Cancel().
+ void DataSent(Resource*, unsigned long long, unsigned long long) override {
+ ASSERT_TRUE(false);
+ }
+ void ResponseReceived(Resource*,
+ const ResourceResponse&,
+ std::unique_ptr<WebDataConsumerHandle>) override {
+ ASSERT_TRUE(false);
+ }
+ void SetSerializedCachedMetadata(Resource*, const char*, size_t) override {
+ ASSERT_TRUE(false);
+ }
+ void DataReceived(Resource*, const char*, size_t) override {
+ ASSERT_TRUE(false);
+ }
+ bool RedirectReceived(Resource*,
+ const ResourceRequest&,
+ const ResourceResponse&) override {
+ ADD_FAILURE();
+ return true;
+ }
+ void DataDownloaded(Resource*, int) override { ASSERT_TRUE(false); }
+ void DidReceiveResourceTiming(Resource*, const ResourceTimingInfo&) override {
+ ASSERT_TRUE(false);
+ }
+
+ void Trace(blink::Visitor* visitor) override {
+ RawResourceClient::Trace(visitor);
+ }
+
+ String DebugName() const override { return "ServeRequestsOnCompleteClient"; }
+};
+
+// Regression test for http://crbug.com/594072.
+// This emulates a modal dialog triggering a nested run loop inside
+// ResourceLoader::Cancel(). If the ResourceLoader doesn't promptly cancel its
+// WebURLLoader before notifying its clients, a nested run loop may send a
+// network response, leading to an invalid state transition in ResourceLoader.
+TEST_F(ResourceFetcherTest, ResponseOnCancel) {
+ KURL url("http://127.0.0.1:8000/foo.png");
+ RegisterMockedURLLoad(url);
+
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+ ResourceRequest resource_request(url);
+ resource_request.SetRequestContext(WebURLRequest::kRequestContextInternal);
+ FetchParameters fetch_params(resource_request);
+ Persistent<ServeRequestsOnCompleteClient> client =
+ new ServeRequestsOnCompleteClient();
+ Resource* resource = RawResource::Fetch(fetch_params, fetcher, client);
+ resource->Loader()->Cancel();
+}
+
+class ScopedMockRedirectRequester {
+ STACK_ALLOCATED();
+ WTF_MAKE_NONCOPYABLE(ScopedMockRedirectRequester);
+
+ public:
+ explicit ScopedMockRedirectRequester(MockFetchContext* context)
+ : context_(context) {}
+
+ void RegisterRedirect(const WebString& from_url, const WebString& to_url) {
+ KURL redirect_url(from_url);
+ WebURLResponse redirect_response;
+ redirect_response.SetURL(redirect_url);
+ redirect_response.SetHTTPStatusCode(301);
+ redirect_response.SetHTTPHeaderField(HTTPNames::Location, to_url);
+ redirect_response.SetEncodedDataLength(kRedirectResponseOverheadBytes);
+ Platform::Current()->GetURLLoaderMockFactory()->RegisterURL(
+ redirect_url, redirect_response, "");
+ }
+
+ void RegisterFinalResource(const WebString& url) {
+ KURL final_url(url);
+ RegisterMockedURLLoad(final_url);
+ }
+
+ void Request(const WebString& url) {
+ ResourceFetcher* fetcher = ResourceFetcher::Create(context_);
+ ResourceRequest resource_request(url);
+ resource_request.SetRequestContext(WebURLRequest::kRequestContextInternal);
+ FetchParameters fetch_params(resource_request);
+ RawResource::Fetch(fetch_params, fetcher, nullptr);
+ Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+ }
+
+ private:
+ Member<MockFetchContext> context_;
+};
+
+TEST_F(ResourceFetcherTest, SameOriginRedirect) {
+ const char kRedirectURL[] = "http://127.0.0.1:8000/redirect.html";
+ const char kFinalURL[] = "http://127.0.0.1:8000/final.html";
+ ScopedMockRedirectRequester requester(Context());
+ requester.RegisterRedirect(kRedirectURL, kFinalURL);
+ requester.RegisterFinalResource(kFinalURL);
+ requester.Request(kRedirectURL);
+
+ EXPECT_EQ(kRedirectResponseOverheadBytes + kTestResourceSize,
+ Context()->GetTransferSize());
+}
+
+TEST_F(ResourceFetcherTest, CrossOriginRedirect) {
+ const char kRedirectURL[] = "http://otherorigin.test/redirect.html";
+ const char kFinalURL[] = "http://127.0.0.1:8000/final.html";
+ ScopedMockRedirectRequester requester(Context());
+ requester.RegisterRedirect(kRedirectURL, kFinalURL);
+ requester.RegisterFinalResource(kFinalURL);
+ requester.Request(kRedirectURL);
+
+ EXPECT_EQ(kTestResourceSize, Context()->GetTransferSize());
+}
+
+TEST_F(ResourceFetcherTest, ComplexCrossOriginRedirect) {
+ const char kRedirectURL1[] = "http://127.0.0.1:8000/redirect1.html";
+ const char kRedirectURL2[] = "http://otherorigin.test/redirect2.html";
+ const char kRedirectURL3[] = "http://127.0.0.1:8000/redirect3.html";
+ const char kFinalURL[] = "http://127.0.0.1:8000/final.html";
+ ScopedMockRedirectRequester requester(Context());
+ requester.RegisterRedirect(kRedirectURL1, kRedirectURL2);
+ requester.RegisterRedirect(kRedirectURL2, kRedirectURL3);
+ requester.RegisterRedirect(kRedirectURL3, kFinalURL);
+ requester.RegisterFinalResource(kFinalURL);
+ requester.Request(kRedirectURL1);
+
+ EXPECT_EQ(kTestResourceSize, Context()->GetTransferSize());
+}
+
+TEST_F(ResourceFetcherTest, SynchronousRequest) {
+ KURL url("http://127.0.0.1:8000/foo.png");
+ RegisterMockedURLLoad(url);
+
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+ ResourceRequest resource_request(url);
+ resource_request.SetRequestContext(WebURLRequest::kRequestContextInternal);
+ FetchParameters fetch_params(resource_request);
+ fetch_params.MakeSynchronous();
+ Resource* resource = RawResource::Fetch(fetch_params, fetcher, nullptr);
+ EXPECT_TRUE(resource->IsLoaded());
+ EXPECT_EQ(ResourceLoadPriority::kHighest,
+ resource->GetResourceRequest().Priority());
+}
+
+TEST_F(ResourceFetcherTest, PingPriority) {
+ KURL url("http://127.0.0.1:8000/foo.png");
+ RegisterMockedURLLoad(url);
+
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+ ResourceRequest resource_request(url);
+ resource_request.SetRequestContext(WebURLRequest::kRequestContextPing);
+ FetchParameters fetch_params(resource_request);
+ Resource* resource = RawResource::Fetch(fetch_params, fetcher, nullptr);
+ EXPECT_EQ(ResourceLoadPriority::kVeryLow,
+ resource->GetResourceRequest().Priority());
+}
+
+TEST_F(ResourceFetcherTest, PreloadResourceTwice) {
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+
+ KURL url("http://127.0.0.1:8000/foo.png");
+ RegisterMockedURLLoad(url);
+
+ FetchParameters fetch_params_original{ResourceRequest(url)};
+ fetch_params_original.SetLinkPreload(true);
+ Resource* resource =
+ MockResource::Fetch(fetch_params_original, fetcher, nullptr);
+ ASSERT_TRUE(resource);
+ EXPECT_TRUE(resource->IsLinkPreload());
+ EXPECT_TRUE(fetcher->ContainsAsPreload(resource));
+ platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+
+ FetchParameters fetch_params{ResourceRequest(url)};
+ fetch_params.SetLinkPreload(true);
+ Resource* new_resource = MockResource::Fetch(fetch_params, fetcher, nullptr);
+ EXPECT_EQ(resource, new_resource);
+ EXPECT_TRUE(fetcher->ContainsAsPreload(resource));
+
+ fetcher->ClearPreloads(ResourceFetcher::kClearAllPreloads);
+ EXPECT_FALSE(fetcher->ContainsAsPreload(resource));
+ EXPECT_FALSE(GetMemoryCache()->Contains(resource));
+ EXPECT_TRUE(resource->IsUnusedPreload());
+}
+
+TEST_F(ResourceFetcherTest, LinkPreloadResourceAndUse) {
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+
+ KURL url("http://127.0.0.1:8000/foo.png");
+ RegisterMockedURLLoad(url);
+
+ // Link preload preload scanner
+ FetchParameters fetch_params_original{ResourceRequest(url)};
+ fetch_params_original.SetLinkPreload(true);
+ Resource* resource =
+ MockResource::Fetch(fetch_params_original, fetcher, nullptr);
+ ASSERT_TRUE(resource);
+ EXPECT_TRUE(resource->IsLinkPreload());
+ platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+
+ // Resource created by preload scanner
+ FetchParameters fetch_params_preload_scanner{ResourceRequest(url)};
+ Resource* preload_scanner_resource =
+ MockResource::Fetch(fetch_params_preload_scanner, fetcher, nullptr);
+ EXPECT_EQ(resource, preload_scanner_resource);
+ EXPECT_FALSE(resource->IsLinkPreload());
+
+ // Resource created by parser
+ FetchParameters fetch_params{ResourceRequest(url)};
+ Persistent<MockResourceClient> client = new MockResourceClient;
+ Resource* new_resource = MockResource::Fetch(fetch_params, fetcher, client);
+ EXPECT_EQ(resource, new_resource);
+ EXPECT_FALSE(resource->IsLinkPreload());
+
+ // DCL reached
+ fetcher->ClearPreloads(ResourceFetcher::kClearSpeculativeMarkupPreloads);
+ EXPECT_TRUE(GetMemoryCache()->Contains(resource));
+ EXPECT_FALSE(resource->IsUnusedPreload());
+}
+
+TEST_F(ResourceFetcherTest, PreloadMatchWithBypassingCache) {
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+ KURL url("http://127.0.0.1:8000/foo.png");
+ RegisterMockedURLLoad(url);
+
+ FetchParameters fetch_params_original{ResourceRequest(url)};
+ fetch_params_original.SetLinkPreload(true);
+ Resource* resource =
+ MockResource::Fetch(fetch_params_original, fetcher, nullptr);
+ ASSERT_TRUE(resource);
+ EXPECT_TRUE(resource->IsLinkPreload());
+ platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+
+ FetchParameters fetch_params_second{ResourceRequest(url)};
+ fetch_params_second.MutableResourceRequest().SetCacheMode(
+ mojom::FetchCacheMode::kBypassCache);
+ Resource* second_resource =
+ MockResource::Fetch(fetch_params_second, fetcher, nullptr);
+ EXPECT_EQ(resource, second_resource);
+ EXPECT_FALSE(resource->IsLinkPreload());
+}
+
+TEST_F(ResourceFetcherTest, CrossFramePreloadMatchIsNotAllowed) {
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+ ResourceFetcher* fetcher2 = ResourceFetcher::Create(Context());
+
+ KURL url("http://127.0.0.1:8000/foo.png");
+ RegisterMockedURLLoad(url);
+
+ FetchParameters fetch_params_original{ResourceRequest(url)};
+ fetch_params_original.SetLinkPreload(true);
+ Resource* resource =
+ MockResource::Fetch(fetch_params_original, fetcher, nullptr);
+ ASSERT_TRUE(resource);
+ EXPECT_TRUE(resource->IsLinkPreload());
+ platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+
+ FetchParameters fetch_params_second{ResourceRequest(url)};
+ fetch_params_second.MutableResourceRequest().SetCacheMode(
+ mojom::FetchCacheMode::kBypassCache);
+ Resource* second_resource =
+ MockResource::Fetch(fetch_params_second, fetcher2, nullptr);
+
+ EXPECT_NE(resource, second_resource);
+ EXPECT_TRUE(resource->IsLinkPreload());
+}
+
+TEST_F(ResourceFetcherTest, RepetitiveLinkPreloadShouldBeMerged) {
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+
+ KURL url("http://127.0.0.1:8000/foo.png");
+ RegisterMockedURLLoad(url);
+
+ FetchParameters fetch_params_for_request{ResourceRequest(url)};
+ FetchParameters fetch_params_for_preload{ResourceRequest(url)};
+ fetch_params_for_preload.SetLinkPreload(true);
+
+ Resource* resource1 =
+ MockResource::Fetch(fetch_params_for_preload, fetcher, nullptr);
+ ASSERT_TRUE(resource1);
+ EXPECT_TRUE(resource1->IsUnusedPreload());
+ EXPECT_TRUE(fetcher->ContainsAsPreload(resource1));
+ platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+
+ // The second preload fetch returns the first preload.
+ Resource* resource2 =
+ MockResource::Fetch(fetch_params_for_preload, fetcher, nullptr);
+ EXPECT_TRUE(fetcher->ContainsAsPreload(resource1));
+ EXPECT_TRUE(resource1->IsUnusedPreload());
+ EXPECT_EQ(resource1, resource2);
+
+ // preload matching
+ Resource* resource3 =
+ MockResource::Fetch(fetch_params_for_request, fetcher, nullptr);
+ EXPECT_EQ(resource1, resource3);
+ EXPECT_FALSE(fetcher->ContainsAsPreload(resource1));
+ EXPECT_FALSE(resource1->IsUnusedPreload());
+}
+
+TEST_F(ResourceFetcherTest, RepetitiveSpeculativePreloadShouldBeMerged) {
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+
+ KURL url("http://127.0.0.1:8000/foo.png");
+ RegisterMockedURLLoad(url);
+
+ FetchParameters fetch_params_for_request{ResourceRequest(url)};
+ FetchParameters fetch_params_for_preload{ResourceRequest(url)};
+ fetch_params_for_preload.SetSpeculativePreloadType(
+ FetchParameters::SpeculativePreloadType::kInDocument);
+
+ Resource* resource1 =
+ MockResource::Fetch(fetch_params_for_preload, fetcher, nullptr);
+ ASSERT_TRUE(resource1);
+ EXPECT_TRUE(resource1->IsUnusedPreload());
+ EXPECT_TRUE(fetcher->ContainsAsPreload(resource1));
+ platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+
+ // The second preload fetch returns the first preload.
+ Resource* resource2 =
+ MockResource::Fetch(fetch_params_for_preload, fetcher, nullptr);
+ EXPECT_TRUE(fetcher->ContainsAsPreload(resource1));
+ EXPECT_TRUE(resource1->IsUnusedPreload());
+ EXPECT_EQ(resource1, resource2);
+
+ // preload matching
+ Resource* resource3 =
+ MockResource::Fetch(fetch_params_for_request, fetcher, nullptr);
+ EXPECT_EQ(resource1, resource3);
+ EXPECT_FALSE(fetcher->ContainsAsPreload(resource1));
+ EXPECT_FALSE(resource1->IsUnusedPreload());
+}
+
+TEST_F(ResourceFetcherTest, SpeculativePreloadShouldBePromotedToLinkePreload) {
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+
+ KURL url("http://127.0.0.1:8000/foo.png");
+ RegisterMockedURLLoad(url);
+
+ FetchParameters fetch_params_for_request{ResourceRequest(url)};
+ FetchParameters fetch_params_for_speculative_preload{ResourceRequest(url)};
+ fetch_params_for_speculative_preload.SetSpeculativePreloadType(
+ FetchParameters::SpeculativePreloadType::kInDocument);
+ FetchParameters fetch_params_for_link_preload{ResourceRequest(url)};
+ fetch_params_for_link_preload.SetLinkPreload(true);
+
+ Resource* resource1 = MockResource::Fetch(
+ fetch_params_for_speculative_preload, fetcher, nullptr);
+ ASSERT_TRUE(resource1);
+ EXPECT_TRUE(resource1->IsUnusedPreload());
+ EXPECT_FALSE(resource1->IsLinkPreload());
+ EXPECT_TRUE(fetcher->ContainsAsPreload(resource1));
+ platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+
+ // The second preload fetch returns the first preload.
+ Resource* resource2 =
+ MockResource::Fetch(fetch_params_for_link_preload, fetcher, nullptr);
+ EXPECT_TRUE(fetcher->ContainsAsPreload(resource1));
+ EXPECT_TRUE(resource1->IsUnusedPreload());
+ EXPECT_TRUE(resource1->IsLinkPreload());
+ EXPECT_EQ(resource1, resource2);
+
+ // preload matching
+ Resource* resource3 =
+ MockResource::Fetch(fetch_params_for_request, fetcher, nullptr);
+ EXPECT_EQ(resource1, resource3);
+ EXPECT_FALSE(fetcher->ContainsAsPreload(resource1));
+ EXPECT_FALSE(resource1->IsUnusedPreload());
+ EXPECT_FALSE(resource1->IsLinkPreload());
+}
+
+TEST_F(ResourceFetcherTest, Revalidate304) {
+ scoped_refptr<const SecurityOrigin> source_origin =
+ SecurityOrigin::CreateUnique();
+ Context()->SetSecurityOrigin(source_origin);
+
+ KURL url("http://127.0.0.1:8000/foo.html");
+ Resource* resource = RawResource::CreateForTest(url, Resource::kRaw);
+ AddResourceToMemoryCache(resource, source_origin);
+
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(304);
+ response.SetHTTPHeaderField("etag", "1234567890");
+ resource->ResponseReceived(response, nullptr);
+ resource->FinishForTest();
+
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+ ResourceRequest resource_request(url);
+ resource_request.SetRequestContext(WebURLRequest::kRequestContextInternal);
+ FetchParameters fetch_params(resource_request);
+ platform_->GetURLLoaderMockFactory()->RegisterURL(url, WebURLResponse(), "");
+ Resource* new_resource = RawResource::Fetch(fetch_params, fetcher, nullptr);
+ fetcher->StopFetching();
+
+ EXPECT_NE(resource, new_resource);
+}
+
+TEST_F(ResourceFetcherTest, LinkPreloadResourceMultipleFetchersAndMove) {
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+ ResourceFetcher* fetcher2 = ResourceFetcher::Create(Context());
+
+ KURL url("http://127.0.0.1:8000/foo.png");
+ RegisterMockedURLLoad(url);
+
+ FetchParameters fetch_params_original{ResourceRequest(url)};
+ fetch_params_original.SetLinkPreload(true);
+ Resource* resource =
+ MockResource::Fetch(fetch_params_original, fetcher, nullptr);
+ ASSERT_TRUE(resource);
+ EXPECT_TRUE(resource->IsLinkPreload());
+ EXPECT_EQ(0, fetcher->BlockingRequestCount());
+
+ // Resource created by parser on the second fetcher
+ FetchParameters fetch_params2{ResourceRequest(url)};
+ Persistent<MockResourceClient> client2 = new MockResourceClient;
+ Resource* new_resource2 =
+ MockResource::Fetch(fetch_params2, fetcher2, client2);
+ EXPECT_NE(resource, new_resource2);
+ EXPECT_EQ(0, fetcher2->BlockingRequestCount());
+ platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests();
+}
+
+TEST_F(ResourceFetcherTest, ContentTypeDataURL) {
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+ FetchParameters fetch_params{ResourceRequest("data:text/testmimetype,foo")};
+ Resource* resource = MockResource::Fetch(fetch_params, fetcher, nullptr);
+ ASSERT_TRUE(resource);
+ EXPECT_EQ(ResourceStatus::kCached, resource->GetStatus());
+ EXPECT_EQ("text/testmimetype", resource->GetResponse().MimeType());
+ EXPECT_EQ("text/testmimetype", resource->GetResponse().HttpContentType());
+}
+
+// Request with the Content-ID scheme must not be canceled, even if there is no
+// MHTMLArchive to serve them.
+// Note: Not blocking it is important because there are some embedders of
+// Android WebView that are intercepting Content-ID URLs and serve their own
+// resources. Please see https://crbug.com/739658.
+TEST_F(ResourceFetcherTest, ContentIdURL) {
+ KURL url("cid:0123456789@example.com");
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+ RegisterMockedURLLoadWithCustomResponse(url, response);
+
+ ResourceFetcher* fetcher = ResourceFetcher::Create(Context());
+
+ // Main resource case.
+ {
+ ResourceRequest resource_request(url);
+ resource_request.SetRequestContext(WebURLRequest::kRequestContextIframe);
+ resource_request.SetFrameType(
+ network::mojom::RequestContextFrameType::kNested);
+ FetchParameters fetch_params(resource_request);
+ RawResource* resource = RawResource::FetchMainResource(
+ fetch_params, fetcher, nullptr, SubstituteData());
+ ASSERT_NE(nullptr, resource);
+ EXPECT_FALSE(resource->ErrorOccurred());
+ }
+
+ // Subresource case.
+ {
+ ResourceRequest resource_request(url);
+ resource_request.SetRequestContext(WebURLRequest::kRequestContextVideo);
+ FetchParameters fetch_params(resource_request);
+ RawResource* resource =
+ RawResource::FetchMedia(fetch_params, fetcher, nullptr);
+ ASSERT_NE(nullptr, resource);
+ EXPECT_FALSE(resource->ErrorOccurred());
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_finish_observer.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_finish_observer.h
new file mode 100644
index 00000000000..72cfbda8334
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_finish_observer.h
@@ -0,0 +1,38 @@
+// Copyright 2017 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_PLATFORM_LOADER_FETCH_RESOURCE_FINISH_OBSERVER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_FINISH_OBSERVER_H_
+
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+// ResourceFinishObserver is different from ResourceClient in several ways.
+// - NotifyFinished is dispatched asynchronously.
+// - ResourceFinishObservers will be removed from Resource when the load
+// finishes. - This class is not intended to be "subclassed" per each Resource
+// subclass.
+// There is no ImageResourceFinishObserver, for example.
+// ResourceFinishObserver should be quite simple. All notifications must be
+// notified AFTER the loading finishes.
+class PLATFORM_EXPORT ResourceFinishObserver : public GarbageCollectedMixin {
+ public:
+ virtual ~ResourceFinishObserver() = default;
+
+ // Called asynchronously when loading finishes.
+ // Note that this can be dispatched after removing |this| client from a
+ // Resource, because of the asynchronicity.
+ virtual void NotifyFinished() = 0;
+ // Name for debugging
+ virtual String DebugName() const = 0;
+
+ void Trace(blink::Visitor* visitor) override {}
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_FINISH_OBSERVER_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_info.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_info.h
new file mode 100644
index 00000000000..db23bdd7a80
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_info.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010 Google, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOAD_INFO_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOAD_INFO_H_
+
+#include "third_party/blink/renderer/platform/network/http_header_map.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+
+namespace blink {
+
+struct ResourceLoadInfo : RefCounted<ResourceLoadInfo> {
+ ResourceLoadInfo() : http_status_code(0) {}
+
+ int http_status_code;
+ String http_status_text;
+ HTTPHeaderMap request_headers;
+ HTTPHeaderMap response_headers;
+ String request_headers_text;
+ String response_headers_text;
+ String npn_negotiated_protocol;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_priority.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_priority.h
new file mode 100644
index 00000000000..e5806041157
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_priority.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOAD_PRIORITY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOAD_PRIORITY_H_
+
+#include "third_party/blink/public/platform/web_url_request.h"
+
+namespace blink {
+
+using ResourceLoadPriority = WebURLRequest::Priority;
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.cc
new file mode 100644
index 00000000000..5e0f33f2aa3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.cc
@@ -0,0 +1,722 @@
+// Copyright 2017 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/platform/loader/fetch/resource_load_scheduler.h"
+
+#include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram.h"
+#include "base/strings/string_number_conversions.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/histogram.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_context.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/scheduler/renderer/frame_status.h"
+#include "third_party/blink/renderer/platform/scheduler/util/aggregated_metric_reporter.h"
+
+namespace blink {
+
+namespace {
+
+// Field trial name.
+const char kResourceLoadSchedulerTrial[] = "ResourceLoadScheduler";
+
+// Field trial parameter names.
+// Note: bg_limit is supported on m61+, but bg_sub_limit is only on m63+.
+// If bg_sub_limit param is not found, we should use bg_limit to make the
+// study result statistically correct.
+const char kOutstandingLimitForBackgroundMainFrameName[] = "bg_limit";
+const char kOutstandingLimitForBackgroundSubFrameName[] = "bg_sub_limit";
+
+// Field trial default parameters.
+constexpr size_t kOutstandingLimitForBackgroundFrameDefault = 16u;
+
+// Maximum request count that request count metrics assume.
+constexpr base::HistogramBase::Sample kMaximumReportSize10K = 10000;
+
+// Maximum traffic bytes that traffic metrics assume.
+constexpr base::HistogramBase::Sample kMaximumReportSize1G =
+ 1 * 1000 * 1000 * 1000;
+
+// Bucket count for metrics.
+constexpr int32_t kReportBucketCount = 25;
+
+constexpr char kRendererSideResourceScheduler[] =
+ "RendererSideResourceScheduler";
+
+// These values are copied from resource_scheduler.cc, but the meaning is a bit
+// different because ResourceScheduler counts the running delayable requests
+// while ResourceLoadScheduler counts all the running requests.
+constexpr size_t kTightLimitForRendererSideResourceScheduler = 1u;
+constexpr size_t kLimitForRendererSideResourceScheduler = 10u;
+
+constexpr char kTightLimitForRendererSideResourceSchedulerName[] =
+ "tight_limit";
+constexpr char kLimitForRendererSideResourceSchedulerName[] = "limit";
+
+// Represents a resource load circumstance, e.g. from main frame vs sub-frames,
+// or on throttled state vs on not-throttled state.
+// Used to report histograms. Do not reorder or insert new items.
+enum class ReportCircumstance {
+ kMainframeThrottled,
+ kMainframeNotThrottled,
+ kSubframeThrottled,
+ kSubframeNotThrottled,
+ // Append new items here.
+ kNumOfCircumstances,
+};
+
+base::HistogramBase::Sample ToSample(ReportCircumstance circumstance) {
+ return static_cast<base::HistogramBase::Sample>(circumstance);
+}
+
+uint32_t GetFieldTrialUint32Param(const char* trial_name,
+ const char* parameter_name,
+ uint32_t default_param) {
+ std::map<std::string, std::string> trial_params;
+ bool result = base::GetFieldTrialParams(trial_name, &trial_params);
+ if (!result)
+ return default_param;
+
+ const auto& found = trial_params.find(parameter_name);
+ if (found == trial_params.end())
+ return default_param;
+
+ uint32_t param;
+ if (!base::StringToUint(found->second, &param))
+ return default_param;
+
+ return param;
+}
+
+size_t GetOutstandingThrottledLimit(FetchContext* context) {
+ DCHECK(context);
+
+ if (!RuntimeEnabledFeatures::ResourceLoadSchedulerEnabled())
+ return ResourceLoadScheduler::kOutstandingUnlimited;
+
+ uint32_t main_frame_limit = GetFieldTrialUint32Param(
+ kResourceLoadSchedulerTrial, kOutstandingLimitForBackgroundMainFrameName,
+ kOutstandingLimitForBackgroundFrameDefault);
+ if (context->IsMainFrame())
+ return main_frame_limit;
+
+ // We do not have a fixed default limit for sub-frames, but use the limit for
+ // the main frame so that it works as how previous versions that haven't
+ // consider sub-frames' specific limit work.
+ return GetFieldTrialUint32Param(kResourceLoadSchedulerTrial,
+ kOutstandingLimitForBackgroundSubFrameName,
+ main_frame_limit);
+}
+
+int TakeWholeKilobytes(int64_t& bytes) {
+ int kilobytes = bytes / 1024;
+ bytes %= 1024;
+ return kilobytes;
+}
+
+} // namespace
+
+// A class to gather throttling and traffic information to report histograms.
+class ResourceLoadScheduler::TrafficMonitor {
+ public:
+ explicit TrafficMonitor(FetchContext*);
+ ~TrafficMonitor();
+
+ // Notified when the ThrottlingState is changed.
+ void OnThrottlingStateChanged(FrameScheduler::ThrottlingState);
+
+ // Reports resource request completion.
+ void Report(const ResourceLoadScheduler::TrafficReportHints&);
+
+ // Reports per-frame reports.
+ void ReportAll();
+
+ private:
+ const bool is_main_frame_;
+
+ const WeakPersistent<FetchContext> context_; // NOT OWNED
+
+ FrameScheduler::ThrottlingState current_state_ =
+ FrameScheduler::ThrottlingState::kStopped;
+
+ size_t total_throttled_request_count_ = 0;
+ size_t total_throttled_traffic_bytes_ = 0;
+ size_t total_throttled_decoded_bytes_ = 0;
+ size_t total_not_throttled_request_count_ = 0;
+ size_t total_not_throttled_traffic_bytes_ = 0;
+ size_t total_not_throttled_decoded_bytes_ = 0;
+ size_t throttling_state_change_count_ = 0;
+ bool report_all_is_called_ = false;
+
+ scheduler::AggregatedMetricReporter<scheduler::FrameStatus, int64_t>
+ traffic_kilobytes_per_frame_status_;
+ scheduler::AggregatedMetricReporter<scheduler::FrameStatus, int64_t>
+ decoded_kilobytes_per_frame_status_;
+};
+
+ResourceLoadScheduler::TrafficMonitor::TrafficMonitor(FetchContext* context)
+ : is_main_frame_(context->IsMainFrame()),
+ context_(context),
+ traffic_kilobytes_per_frame_status_(
+ "Blink.ResourceLoadScheduler.TrafficBytes.KBPerFrameStatus",
+ &TakeWholeKilobytes),
+ decoded_kilobytes_per_frame_status_(
+ "Blink.ResourceLoadScheduler.DecodedBytes.KBPerFrameStatus",
+ &TakeWholeKilobytes) {
+ DCHECK(context_);
+}
+
+ResourceLoadScheduler::TrafficMonitor::~TrafficMonitor() {
+ ReportAll();
+}
+
+void ResourceLoadScheduler::TrafficMonitor::OnThrottlingStateChanged(
+ FrameScheduler::ThrottlingState state) {
+ current_state_ = state;
+ throttling_state_change_count_++;
+}
+
+void ResourceLoadScheduler::TrafficMonitor::Report(
+ const ResourceLoadScheduler::TrafficReportHints& hints) {
+ // Currently we only care about stats from frames.
+ if (!IsMainThread())
+ return;
+ if (!hints.IsValid())
+ return;
+
+ DEFINE_STATIC_LOCAL(EnumerationHistogram, request_count_by_circumstance,
+ ("Blink.ResourceLoadScheduler.RequestCount",
+ ToSample(ReportCircumstance::kNumOfCircumstances)));
+
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, main_frame_throttled_traffic_bytes,
+ ("Blink.ResourceLoadScheduler.TrafficBytes.MainframeThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, main_frame_not_throttled_traffic_bytes,
+ ("Blink.ResourceLoadScheduler.TrafficBytes.MainframeNotThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, sub_frame_throttled_traffic_bytes,
+ ("Blink.ResourceLoadScheduler.TrafficBytes.SubframeThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, sub_frame_not_throttled_traffic_bytes,
+ ("Blink.ResourceLoadScheduler.TrafficBytes.SubframeNotThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, main_frame_throttled_decoded_bytes,
+ ("Blink.ResourceLoadScheduler.DecodedBytes.MainframeThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, main_frame_not_throttled_decoded_bytes,
+ ("Blink.ResourceLoadScheduler.DecodedBytes.MainframeNotThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, sub_frame_throttled_decoded_bytes,
+ ("Blink.ResourceLoadScheduler.DecodedBytes.SubframeThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, sub_frame_not_throttled_decoded_bytes,
+ ("Blink.ResourceLoadScheduler.DecodedBytes.SubframeNotThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+
+ switch (current_state_) {
+ case FrameScheduler::ThrottlingState::kThrottled:
+ if (is_main_frame_) {
+ request_count_by_circumstance.Count(
+ ToSample(ReportCircumstance::kMainframeThrottled));
+ main_frame_throttled_traffic_bytes.Count(hints.encoded_data_length());
+ main_frame_throttled_decoded_bytes.Count(hints.decoded_body_length());
+ } else {
+ request_count_by_circumstance.Count(
+ ToSample(ReportCircumstance::kSubframeThrottled));
+ sub_frame_throttled_traffic_bytes.Count(hints.encoded_data_length());
+ sub_frame_throttled_decoded_bytes.Count(hints.decoded_body_length());
+ }
+ total_throttled_request_count_++;
+ total_throttled_traffic_bytes_ += hints.encoded_data_length();
+ total_throttled_decoded_bytes_ += hints.decoded_body_length();
+ break;
+ case FrameScheduler::ThrottlingState::kNotThrottled:
+ if (is_main_frame_) {
+ request_count_by_circumstance.Count(
+ ToSample(ReportCircumstance::kMainframeNotThrottled));
+ main_frame_not_throttled_traffic_bytes.Count(
+ hints.encoded_data_length());
+ main_frame_not_throttled_decoded_bytes.Count(
+ hints.decoded_body_length());
+ } else {
+ request_count_by_circumstance.Count(
+ ToSample(ReportCircumstance::kSubframeNotThrottled));
+ sub_frame_not_throttled_traffic_bytes.Count(
+ hints.encoded_data_length());
+ sub_frame_not_throttled_decoded_bytes.Count(
+ hints.decoded_body_length());
+ }
+ total_not_throttled_request_count_++;
+ total_not_throttled_traffic_bytes_ += hints.encoded_data_length();
+ total_not_throttled_decoded_bytes_ += hints.decoded_body_length();
+ break;
+ case FrameScheduler::ThrottlingState::kStopped:
+ break;
+ }
+
+ // Report kilobytes instead of bytes to avoid overflows.
+ size_t encoded_kilobytes = hints.encoded_data_length() / 1024;
+ size_t decoded_kilobytes = hints.decoded_body_length() / 1024;
+
+ if (encoded_kilobytes) {
+ traffic_kilobytes_per_frame_status_.RecordTask(
+ scheduler::GetFrameStatus(context_->GetFrameScheduler()),
+ encoded_kilobytes);
+ }
+ if (decoded_kilobytes) {
+ decoded_kilobytes_per_frame_status_.RecordTask(
+ scheduler::GetFrameStatus(context_->GetFrameScheduler()),
+ decoded_kilobytes);
+ }
+}
+
+void ResourceLoadScheduler::TrafficMonitor::ReportAll() {
+ // Currently we only care about stats from frames.
+ if (!IsMainThread())
+ return;
+
+ // Blink has several cases to create DocumentLoader not for an actual page
+ // load use. I.e., per a XMLHttpRequest in "document" type response.
+ // We just ignore such uninteresting cases in following metrics.
+ if (!total_throttled_request_count_ && !total_not_throttled_request_count_)
+ return;
+
+ if (report_all_is_called_)
+ return;
+ report_all_is_called_ = true;
+
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, main_frame_total_throttled_request_count,
+ ("Blink.ResourceLoadScheduler.TotalRequestCount.MainframeThrottled", 0,
+ kMaximumReportSize10K, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, main_frame_total_not_throttled_request_count,
+ ("Blink.ResourceLoadScheduler.TotalRequestCount.MainframeNotThrottled", 0,
+ kMaximumReportSize10K, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, sub_frame_total_throttled_request_count,
+ ("Blink.ResourceLoadScheduler.TotalRequestCount.SubframeThrottled", 0,
+ kMaximumReportSize10K, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, sub_frame_total_not_throttled_request_count,
+ ("Blink.ResourceLoadScheduler.TotalRequestCount.SubframeNotThrottled", 0,
+ kMaximumReportSize10K, kReportBucketCount));
+
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, main_frame_total_throttled_traffic_bytes,
+ ("Blink.ResourceLoadScheduler.TotalTrafficBytes.MainframeThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, main_frame_total_not_throttled_traffic_bytes,
+ ("Blink.ResourceLoadScheduler.TotalTrafficBytes.MainframeNotThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, sub_frame_total_throttled_traffic_bytes,
+ ("Blink.ResourceLoadScheduler.TotalTrafficBytes.SubframeThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, sub_frame_total_not_throttled_traffic_bytes,
+ ("Blink.ResourceLoadScheduler.TotalTrafficBytes.SubframeNotThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, main_frame_total_throttled_decoded_bytes,
+ ("Blink.ResourceLoadScheduler.TotalDecodedBytes.MainframeThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, main_frame_total_not_throttled_decoded_bytes,
+ ("Blink.ResourceLoadScheduler.TotalDecodedBytes.MainframeNotThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, sub_frame_total_throttled_decoded_bytes,
+ ("Blink.ResourceLoadScheduler.TotalDecodedBytes.SubframeThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, sub_frame_total_not_throttled_decoded_bytes,
+ ("Blink.ResourceLoadScheduler.TotalDecodedBytes.SubframeNotThrottled", 0,
+ kMaximumReportSize1G, kReportBucketCount));
+
+ DEFINE_STATIC_LOCAL(CustomCountHistogram, throttling_state_change_count,
+ ("Blink.ResourceLoadScheduler.ThrottlingStateChangeCount",
+ 0, 100, kReportBucketCount));
+
+ if (is_main_frame_) {
+ main_frame_total_throttled_request_count.Count(
+ total_throttled_request_count_);
+ main_frame_total_not_throttled_request_count.Count(
+ total_not_throttled_request_count_);
+ main_frame_total_throttled_traffic_bytes.Count(
+ total_throttled_traffic_bytes_);
+ main_frame_total_not_throttled_traffic_bytes.Count(
+ total_not_throttled_traffic_bytes_);
+ main_frame_total_throttled_decoded_bytes.Count(
+ total_throttled_decoded_bytes_);
+ main_frame_total_not_throttled_decoded_bytes.Count(
+ total_not_throttled_decoded_bytes_);
+ } else {
+ sub_frame_total_throttled_request_count.Count(
+ total_throttled_request_count_);
+ sub_frame_total_not_throttled_request_count.Count(
+ total_not_throttled_request_count_);
+ sub_frame_total_throttled_traffic_bytes.Count(
+ total_throttled_traffic_bytes_);
+ sub_frame_total_not_throttled_traffic_bytes.Count(
+ total_not_throttled_traffic_bytes_);
+ sub_frame_total_throttled_decoded_bytes.Count(
+ total_throttled_decoded_bytes_);
+ sub_frame_total_not_throttled_decoded_bytes.Count(
+ total_not_throttled_decoded_bytes_);
+ }
+
+ throttling_state_change_count.Count(throttling_state_change_count_);
+}
+
+constexpr ResourceLoadScheduler::ClientId
+ ResourceLoadScheduler::kInvalidClientId;
+
+ResourceLoadScheduler::ResourceLoadScheduler(FetchContext* context)
+ : outstanding_limit_for_throttled_frame_scheduler_(
+ GetOutstandingThrottledLimit(context)),
+ context_(context) {
+ traffic_monitor_ =
+ std::make_unique<ResourceLoadScheduler::TrafficMonitor>(context_);
+
+ if (!RuntimeEnabledFeatures::ResourceLoadSchedulerEnabled() &&
+ !Platform::Current()->IsRendererSideResourceSchedulerEnabled()) {
+ // Initialize TrafficMonitor's state to be |kNotThrottled| so that it
+ // reports metrics in a reasonable state group.
+ traffic_monitor_->OnThrottlingStateChanged(
+ FrameScheduler::ThrottlingState::kNotThrottled);
+ return;
+ }
+
+ auto* scheduler = context->GetFrameScheduler();
+ if (!scheduler)
+ return;
+
+ if (Platform::Current()->IsRendererSideResourceSchedulerEnabled()) {
+ policy_ = context->InitialLoadThrottlingPolicy();
+ normal_outstanding_limit_ =
+ GetFieldTrialUint32Param(kRendererSideResourceScheduler,
+ kLimitForRendererSideResourceSchedulerName,
+ kLimitForRendererSideResourceScheduler);
+ tight_outstanding_limit_ = GetFieldTrialUint32Param(
+ kRendererSideResourceScheduler,
+ kTightLimitForRendererSideResourceSchedulerName,
+ kTightLimitForRendererSideResourceScheduler);
+ }
+
+ is_enabled_ = true;
+ scheduler_observer_handle_ = scheduler->AddThrottlingObserver(
+ FrameScheduler::ObserverType::kLoader, this);
+}
+
+ResourceLoadScheduler* ResourceLoadScheduler::Create(FetchContext* context) {
+ return new ResourceLoadScheduler(context ? context
+ : &FetchContext::NullInstance());
+}
+
+ResourceLoadScheduler::~ResourceLoadScheduler() = default;
+
+void ResourceLoadScheduler::Trace(blink::Visitor* visitor) {
+ visitor->Trace(pending_request_map_);
+ visitor->Trace(context_);
+}
+
+void ResourceLoadScheduler::LoosenThrottlingPolicy() {
+ switch (policy_) {
+ case ThrottlingPolicy::kTight:
+ break;
+ case ThrottlingPolicy::kNormal:
+ return;
+ }
+ policy_ = ThrottlingPolicy::kNormal;
+ MaybeRun();
+}
+
+void ResourceLoadScheduler::Shutdown() {
+ // Do nothing if the feature is not enabled, or Shutdown() was already called.
+ if (is_shutdown_)
+ return;
+ is_shutdown_ = true;
+
+ if (traffic_monitor_)
+ traffic_monitor_.reset();
+
+ scheduler_observer_handle_.reset();
+}
+
+void ResourceLoadScheduler::Request(ResourceLoadSchedulerClient* client,
+ ThrottleOption option,
+ ResourceLoadPriority priority,
+ int intra_priority,
+ ResourceLoadScheduler::ClientId* id) {
+ *id = GenerateClientId();
+ if (is_shutdown_)
+ return;
+
+ if (!Platform::Current()->IsRendererSideResourceSchedulerEnabled()) {
+ // Prioritization is effectively disabled as we use the constant priority.
+ priority = ResourceLoadPriority::kMedium;
+ intra_priority = 0;
+ }
+
+ if (!is_enabled_ || option == ThrottleOption::kCanNotBeThrottled ||
+ !IsThrottablePriority(priority)) {
+ Run(*id, client, false);
+ return;
+ }
+
+ pending_requests_.emplace(*id, priority, intra_priority);
+ pending_request_map_.insert(
+ *id, new ClientWithPriority(client, priority, intra_priority));
+ MaybeRun();
+}
+
+void ResourceLoadScheduler::SetPriority(ClientId client_id,
+ ResourceLoadPriority priority,
+ int intra_priority) {
+ if (!Platform::Current()->IsRendererSideResourceSchedulerEnabled())
+ return;
+
+ auto client_it = pending_request_map_.find(client_id);
+ if (client_it == pending_request_map_.end())
+ return;
+
+ auto it = pending_requests_.find(ClientIdWithPriority(
+ client_id, client_it->value->priority, client_it->value->intra_priority));
+
+ DCHECK(it != pending_requests_.end());
+ pending_requests_.erase(it);
+
+ client_it->value->priority = priority;
+ client_it->value->intra_priority = intra_priority;
+
+ pending_requests_.emplace(client_id, priority, intra_priority);
+ MaybeRun();
+}
+
+bool ResourceLoadScheduler::Release(
+ ResourceLoadScheduler::ClientId id,
+ ResourceLoadScheduler::ReleaseOption option,
+ const ResourceLoadScheduler::TrafficReportHints& hints) {
+ // Check kInvalidClientId that can not be passed to the HashSet.
+ if (id == kInvalidClientId)
+ return false;
+
+ if (running_requests_.find(id) != running_requests_.end()) {
+ running_requests_.erase(id);
+ running_throttlable_requests_.erase(id);
+
+ if (traffic_monitor_)
+ traffic_monitor_->Report(hints);
+
+ if (option == ReleaseOption::kReleaseAndSchedule)
+ MaybeRun();
+ return true;
+ }
+ auto found = pending_request_map_.find(id);
+ if (found != pending_request_map_.end()) {
+ pending_request_map_.erase(found);
+ // Intentionally does not remove it from |pending_requests_|.
+
+ // Didn't release any running requests, but the outstanding limit might be
+ // changed to allow another request.
+ if (option == ReleaseOption::kReleaseAndSchedule)
+ MaybeRun();
+ return true;
+ }
+ return false;
+}
+
+void ResourceLoadScheduler::SetOutstandingLimitForTesting(size_t tight_limit,
+ size_t normal_limit) {
+ tight_outstanding_limit_ = tight_limit;
+ normal_outstanding_limit_ = normal_limit;
+ MaybeRun();
+}
+
+void ResourceLoadScheduler::OnNetworkQuiet() {
+ DCHECK(IsMainThread());
+
+ // Flush out all traffic reports here for safety.
+ traffic_monitor_->ReportAll();
+
+ if (maximum_running_requests_seen_ == 0)
+ return;
+
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, main_frame_throttled,
+ ("Blink.ResourceLoadScheduler.PeakRequests.MainframeThrottled", 0,
+ kMaximumReportSize10K, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, main_frame_not_throttled,
+ ("Blink.ResourceLoadScheduler.PeakRequests.MainframeNotThrottled", 0,
+ kMaximumReportSize10K, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, sub_frame_throttled,
+ ("Blink.ResourceLoadScheduler.PeakRequests.SubframeThrottled", 0,
+ kMaximumReportSize10K, kReportBucketCount));
+ DEFINE_STATIC_LOCAL(
+ CustomCountHistogram, sub_frame_not_throttled,
+ ("Blink.ResourceLoadScheduler.PeakRequests.SubframeNotThrottled", 0,
+ kMaximumReportSize10K, kReportBucketCount));
+
+ switch (throttling_history_) {
+ case ThrottlingHistory::kInitial:
+ case ThrottlingHistory::kNotThrottled:
+ if (context_->IsMainFrame())
+ main_frame_not_throttled.Count(maximum_running_requests_seen_);
+ else
+ sub_frame_not_throttled.Count(maximum_running_requests_seen_);
+ break;
+ case ThrottlingHistory::kThrottled:
+ if (context_->IsMainFrame())
+ main_frame_throttled.Count(maximum_running_requests_seen_);
+ else
+ sub_frame_throttled.Count(maximum_running_requests_seen_);
+ break;
+ case ThrottlingHistory::kPartiallyThrottled:
+ break;
+ case ThrottlingHistory::kStopped:
+ break;
+ }
+}
+
+bool ResourceLoadScheduler::IsThrottablePriority(
+ ResourceLoadPriority priority) const {
+ if (!Platform::Current()->IsRendererSideResourceSchedulerEnabled())
+ return true;
+
+ if (RuntimeEnabledFeatures::ResourceLoadSchedulerEnabled()) {
+ // If this scheduler is throttled by the associated FrameScheduler,
+ // consider every prioritiy as throttlable.
+ const auto state = frame_scheduler_throttling_state_;
+ if (state == FrameScheduler::ThrottlingState::kThrottled ||
+ state == FrameScheduler::ThrottlingState::kStopped) {
+ return true;
+ }
+ }
+
+ return priority < ResourceLoadPriority::kHigh;
+}
+
+void ResourceLoadScheduler::OnThrottlingStateChanged(
+ FrameScheduler::ThrottlingState state) {
+ if (traffic_monitor_)
+ traffic_monitor_->OnThrottlingStateChanged(state);
+
+ frame_scheduler_throttling_state_ = state;
+
+ switch (state) {
+ case FrameScheduler::ThrottlingState::kThrottled:
+ if (throttling_history_ == ThrottlingHistory::kInitial)
+ throttling_history_ = ThrottlingHistory::kThrottled;
+ else if (throttling_history_ == ThrottlingHistory::kNotThrottled)
+ throttling_history_ = ThrottlingHistory::kPartiallyThrottled;
+ break;
+ case FrameScheduler::ThrottlingState::kNotThrottled:
+ if (throttling_history_ == ThrottlingHistory::kInitial)
+ throttling_history_ = ThrottlingHistory::kNotThrottled;
+ else if (throttling_history_ == ThrottlingHistory::kThrottled)
+ throttling_history_ = ThrottlingHistory::kPartiallyThrottled;
+ break;
+ case FrameScheduler::ThrottlingState::kStopped:
+ throttling_history_ = ThrottlingHistory::kStopped;
+ break;
+ }
+ MaybeRun();
+}
+
+ResourceLoadScheduler::ClientId ResourceLoadScheduler::GenerateClientId() {
+ ClientId id = ++current_id_;
+ CHECK_NE(0u, id);
+ return id;
+}
+
+void ResourceLoadScheduler::MaybeRun() {
+ // Requests for keep-alive loaders could be remained in the pending queue,
+ // but ignore them once Shutdown() is called.
+ if (is_shutdown_)
+ return;
+
+ while (!pending_requests_.empty()) {
+ // TODO(yhirano): Consider using a unified value.
+ const auto num_requests =
+ frame_scheduler_throttling_state_ ==
+ FrameScheduler::ThrottlingState::kNotThrottled
+ ? running_throttlable_requests_.size()
+ : running_requests_.size();
+
+ const bool has_enough_running_requets =
+ num_requests >= GetOutstandingLimit();
+
+ if (IsThrottablePriority(pending_requests_.begin()->priority) &&
+ has_enough_running_requets) {
+ break;
+ }
+ if (IsThrottablePriority(pending_requests_.begin()->priority) &&
+ has_enough_running_requets) {
+ break;
+ }
+
+ ClientId id = pending_requests_.begin()->client_id;
+ pending_requests_.erase(pending_requests_.begin());
+ auto found = pending_request_map_.find(id);
+ if (found == pending_request_map_.end())
+ continue; // Already released.
+ ResourceLoadSchedulerClient* client = found->value->client;
+ pending_request_map_.erase(found);
+ Run(id, client, true);
+ }
+}
+
+void ResourceLoadScheduler::Run(ResourceLoadScheduler::ClientId id,
+ ResourceLoadSchedulerClient* client,
+ bool throttlable) {
+ running_requests_.insert(id);
+ if (throttlable)
+ running_throttlable_requests_.insert(id);
+ if (running_requests_.size() > maximum_running_requests_seen_) {
+ maximum_running_requests_seen_ = running_requests_.size();
+ }
+ client->Run();
+}
+
+size_t ResourceLoadScheduler::GetOutstandingLimit() const {
+ size_t limit = kOutstandingUnlimited;
+
+ switch (frame_scheduler_throttling_state_) {
+ case FrameScheduler::ThrottlingState::kThrottled:
+ limit = std::min(limit, outstanding_limit_for_throttled_frame_scheduler_);
+ break;
+ case FrameScheduler::ThrottlingState::kNotThrottled:
+ break;
+ case FrameScheduler::ThrottlingState::kStopped:
+ if (RuntimeEnabledFeatures::ResourceLoadSchedulerEnabled())
+ limit = 0;
+ break;
+ }
+
+ switch (policy_) {
+ case ThrottlingPolicy::kTight:
+ limit = std::min(limit, tight_outstanding_limit_);
+ break;
+ case ThrottlingPolicy::kNormal:
+ limit = std::min(limit, normal_outstanding_limit_);
+ break;
+ }
+ return limit;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h
new file mode 100644
index 00000000000..403276a26e4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h
@@ -0,0 +1,318 @@
+// Copyright 2017 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_PLATFORM_LOADER_FETCH_RESOURCE_LOAD_SCHEDULER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOAD_SCHEDULER_H_
+
+#include <set>
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/heap/heap_allocator.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
+#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+
+namespace blink {
+
+class FetchContext;
+
+// Client interface to use the throttling/scheduling functionality that
+// ResourceLoadScheduler provides.
+class PLATFORM_EXPORT ResourceLoadSchedulerClient
+ : public GarbageCollectedMixin {
+ public:
+ // Called when the request is granted to run.
+ virtual void Run() = 0;
+
+ void Trace(blink::Visitor* visitor) override {}
+};
+
+// ResourceLoadScheduler provides a unified per-frame infrastructure to schedule
+// loading requests. When Request() is called with a
+// ResourceLoadSchedulerClient |client|, it calls |client|'s Run() method
+// synchronously or asynchronously to notify that |client| can start loading.
+//
+// A ResourceLoadScheduler may initiate a new resource loading in the following
+// cases:
+// - When Request() is called
+// - When LoosenThrottlingPolicy() is called
+// - When SetPriority() is called
+// - When Release() is called with kReleaseAndSchedule
+// - When OnThrottlingStateChanged() is called
+//
+// A ResourceLoadScheduler determines if a request can be throttable or not, and
+// keeps track of pending throttable requests with priority information (i.e.,
+// ResourceLoadPriority accompanied with an integer called "intra-priority").
+// Here are the general principles:
+// - A ResourceLoadScheduler does not throttle requests that cannot be
+// throttable. It will call client's Run() method as soon as possible.
+// - A ResourceLoadScheduler determines whether a request can be throttable by
+// seeing Request()'s ThrottleOption argument and requests' priority
+// information. Requests' priority information can be modified via
+// SetPriority().
+// - A ResourceLoadScheulder won't initiate a new resource loading which can
+// be throttable when there are active resource loading activities more than
+// its internal threshold (i.e., what GetOutstandingLimit() returns)".
+//
+// By default, ResourceLoadScheduler is disabled, which means it doesn't
+// throttle any resource loading requests.
+//
+// Here are running experiments (as of M65):
+// - "ResourceLoadScheduler"
+// - Resource loading requests are not at throttled when the frame is in
+// the foreground tab.
+// - Resource loading requests are throttled when the frame is in a
+// background tab. It has different thresholds for the main frame
+// and sub frames. When the frame has been background for more than five
+// minutes, all throttable resource loading requests are throttled
+// indefinitely (i.e., threshold is zero in such a circumstance).
+// - RendererSideResourceScheduler
+// ResourceLoadScheduler has two modes each of which has its own threshold.
+// - Tight mode (used until the frame sees a <body> element):
+// ResourceLoadScheduler considers a request throttable if its priority
+// is less than |kHigh|.
+// - Normal mode:
+// ResourceLoadScheduler considers a request throttable if its priority
+// is less than |kMedium|.
+class PLATFORM_EXPORT ResourceLoadScheduler final
+ : public GarbageCollectedFinalized<ResourceLoadScheduler>,
+ public FrameScheduler::Observer {
+ WTF_MAKE_NONCOPYABLE(ResourceLoadScheduler);
+
+ public:
+ // An option to use in calling Request(). If kCanNotBeThrottled is specified,
+ // the request should be granted and Run() should be called synchronously.
+ // Otherwise, OnRequestGranted() could be called later when other outstanding
+ // requests are finished.
+ enum class ThrottleOption { kCanBeThrottled, kCanNotBeThrottled };
+
+ // An option to use in calling Release(). If kReleaseOnly is specified,
+ // the specified request should be released, but no other requests should
+ // be scheduled within the call.
+ enum class ReleaseOption { kReleaseOnly, kReleaseAndSchedule };
+
+ // A class to pass traffic report hints on calling Release().
+ class TrafficReportHints {
+ public:
+ // |encoded_data_length| is payload size in bytes sent over the network.
+ // |decoded_body_length| is received resource data size in bytes.
+ TrafficReportHints(int64_t encoded_data_length, int64_t decoded_body_length)
+ : valid_(true),
+ encoded_data_length_(encoded_data_length),
+ decoded_body_length_(decoded_body_length) {}
+
+ // Returns the instance that represents an invalid report, which can be
+ // used when a caller don't want to report traffic, i.e. on a failure.
+ static PLATFORM_EXPORT TrafficReportHints InvalidInstance() {
+ return TrafficReportHints();
+ }
+
+ bool IsValid() const { return valid_; }
+
+ int64_t encoded_data_length() const {
+ DCHECK(valid_);
+ return encoded_data_length_;
+ }
+ int64_t decoded_body_length() const {
+ DCHECK(valid_);
+ return decoded_body_length_;
+ }
+
+ private:
+ // Default constructor makes an invalid instance that won't be recorded.
+ TrafficReportHints() = default;
+
+ bool valid_ = false;
+ int64_t encoded_data_length_ = 0;
+ int64_t decoded_body_length_ = 0;
+ };
+
+ // ResourceLoadScheduler has two policies: |kTight| and |kNormal|. Currently
+ // this is used to support aggressive throttling while the corresponding frame
+ // is in layout-blocking phase. There is only one state transition,
+ // |kTight| => |kNormal|, which is done by |LoosenThrottlingPolicy|.
+ enum class ThrottlingPolicy { kTight, kNormal };
+
+ // Returned on Request(). Caller should need to return it via Release().
+ using ClientId = uint64_t;
+
+ static constexpr ClientId kInvalidClientId = 0u;
+
+ static constexpr size_t kOutstandingUnlimited =
+ std::numeric_limits<size_t>::max();
+
+ static ResourceLoadScheduler* Create(FetchContext* = nullptr);
+ ~ResourceLoadScheduler();
+
+ void Trace(blink::Visitor*);
+
+ // Changes the policy from |kTight| to |kNormal|. This function can be called
+ // multiple times, and does nothing when the scheduler is already working with
+ // the normal policy. This function may initiate a new resource loading.
+ void LoosenThrottlingPolicy();
+
+ // Stops all operations including observing throttling signals.
+ // ResourceLoadSchedulerClient::Run() will not be called once this method is
+ // called. This method can be called multiple times safely.
+ void Shutdown();
+
+ // Makes a request. This may synchronously call
+ // ResourceLoadSchedulerClient::Run(), but it is guaranteed that ClientId is
+ // populated before ResourceLoadSchedulerClient::Run() is called, so that the
+ // caller can call Release() with the assigned ClientId correctly.
+ void Request(ResourceLoadSchedulerClient*,
+ ThrottleOption,
+ ResourceLoadPriority,
+ int intra_priority,
+ ClientId*);
+
+ // Updates the priority information of the given client. This function may
+ // initiate a new resource loading.
+ void SetPriority(ClientId, ResourceLoadPriority, int intra_priority);
+
+ // ResourceLoadSchedulerClient should call this method when the loading is
+ // finished, or canceled. This method can be called in a pre-finalization
+ // step, bug the ReleaseOption must be kReleaseOnly in such a case.
+ // TrafficReportHints is for reporting histograms.
+ // TrafficReportHints::InvalidInstance() can be used to omit reporting.
+ bool Release(ClientId, ReleaseOption, const TrafficReportHints&);
+
+ // Checks if the specified client was already scheduled to call Run(), but
+ // haven't call Release() yet.
+ bool IsRunning(ClientId id) { return running_requests_.Contains(id); }
+
+ // Sets outstanding limit for testing.
+ void SetOutstandingLimitForTesting(size_t limit) {
+ SetOutstandingLimitForTesting(limit, limit);
+ }
+ void SetOutstandingLimitForTesting(size_t tight_limit, size_t normal_limit);
+
+ void OnNetworkQuiet();
+
+ // Returns whether we can throttle a request with the given priority.
+ // This function returns false when RendererSideResourceScheduler is disabled.
+ bool IsThrottablePriority(ResourceLoadPriority) const;
+
+ // FrameScheduler::Observer overrides:
+ void OnThrottlingStateChanged(FrameScheduler::ThrottlingState) override;
+
+ private:
+ class TrafficMonitor;
+
+ class ClientIdWithPriority {
+ public:
+ struct Compare {
+ bool operator()(const ClientIdWithPriority& x,
+ const ClientIdWithPriority& y) const {
+ if (x.priority != y.priority)
+ return x.priority > y.priority;
+ if (x.intra_priority != y.intra_priority)
+ return x.intra_priority > y.intra_priority;
+ return x.client_id < y.client_id;
+ }
+ };
+
+ ClientIdWithPriority(ClientId client_id,
+ WebURLRequest::Priority priority,
+ int intra_priority)
+ : client_id(client_id),
+ priority(priority),
+ intra_priority(intra_priority) {}
+
+ const ClientId client_id;
+ const WebURLRequest::Priority priority;
+ const int intra_priority;
+ };
+
+ struct ClientWithPriority : public GarbageCollected<ClientWithPriority> {
+ ClientWithPriority(ResourceLoadSchedulerClient* client,
+ ResourceLoadPriority priority,
+ int intra_priority)
+ : client(client), priority(priority), intra_priority(intra_priority) {}
+
+ void Trace(blink::Visitor* visitor) { visitor->Trace(client); }
+
+ Member<ResourceLoadSchedulerClient> client;
+ ResourceLoadPriority priority;
+ int intra_priority;
+ };
+
+ ResourceLoadScheduler(FetchContext*);
+
+ // Generates the next ClientId.
+ ClientId GenerateClientId();
+
+ // Picks up one client if there is a budget and route it to run.
+ void MaybeRun();
+
+ // Grants a client to run,
+ void Run(ClientId, ResourceLoadSchedulerClient*, bool throttlable);
+
+ size_t GetOutstandingLimit() const;
+
+ // A flag to indicate an internal running state.
+ // TODO(toyoshim): We may want to use enum once we start to have more states.
+ bool is_shutdown_ = false;
+
+ // A mutable flag to indicate if the throttling and scheduling are enabled.
+ // Can be modified by field trial flags or for testing.
+ bool is_enabled_ = false;
+
+ ThrottlingPolicy policy_ = ThrottlingPolicy::kNormal;
+
+ // ResourceLoadScheduler threshold values for various circumstances. Some
+ // conditions can overlap, and ResourceLoadScheduler chooses the smallest
+ // value in such cases.
+
+ // Used when |policy_| is |kTight|.
+ size_t tight_outstanding_limit_ = kOutstandingUnlimited;
+
+ // Used when |policy_| is |kNormal|.
+ size_t normal_outstanding_limit_ = kOutstandingUnlimited;
+
+ // Used when |frame_scheduler_throttling_state_| is |kThrottled|.
+ const size_t outstanding_limit_for_throttled_frame_scheduler_;
+
+ // The last used ClientId to calculate the next.
+ ClientId current_id_ = kInvalidClientId;
+
+ // Holds clients that were granted and are running.
+ HashSet<ClientId> running_requests_;
+
+ HashSet<ClientId> running_throttlable_requests_;
+
+ // Largest number of running requests seen so far.
+ unsigned maximum_running_requests_seen_ = 0;
+
+ enum class ThrottlingHistory {
+ kInitial,
+ kThrottled,
+ kNotThrottled,
+ kPartiallyThrottled,
+ kStopped,
+ };
+ ThrottlingHistory throttling_history_ = ThrottlingHistory::kInitial;
+ FrameScheduler::ThrottlingState frame_scheduler_throttling_state_ =
+ FrameScheduler::ThrottlingState::kNotThrottled;
+
+ // Holds clients that haven't been granted, and are waiting for a grant.
+ HeapHashMap<ClientId, Member<ClientWithPriority>> pending_request_map_;
+ // We use std::set here because WTF doesn't have its counterpart.
+ std::set<ClientIdWithPriority, ClientIdWithPriority::Compare>
+ pending_requests_;
+
+ // Holds an internal class instance to monitor and report traffic.
+ std::unique_ptr<TrafficMonitor> traffic_monitor_;
+
+ // Holds FetchContext reference to contact FrameScheduler.
+ Member<FetchContext> context_;
+
+ // Handle to throttling observer.
+ std::unique_ptr<FrameScheduler::ThrottlingObserverHandle>
+ scheduler_observer_handle_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler_test.cc
new file mode 100644
index 00000000000..059d772e417
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler_test.cc
@@ -0,0 +1,520 @@
+// Copyright 2017 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/platform/loader/fetch/resource_load_scheduler.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
+
+namespace blink {
+namespace {
+
+class MockClient final : public GarbageCollectedFinalized<MockClient>,
+ public ResourceLoadSchedulerClient {
+ USING_GARBAGE_COLLECTED_MIXIN(MockClient);
+
+ public:
+ ~MockClient() = default;
+
+ void Run() override {
+ EXPECT_FALSE(was_run_);
+ was_run_ = true;
+ }
+ bool WasRun() { return was_run_; }
+
+ void Trace(blink::Visitor* visitor) override {
+ ResourceLoadSchedulerClient::Trace(visitor);
+ }
+
+ private:
+ bool was_run_ = false;
+};
+
+class ResourceLoadSchedulerTest : public testing::Test {
+ public:
+ using ThrottleOption = ResourceLoadScheduler::ThrottleOption;
+ void SetUp() override {
+ DCHECK(RuntimeEnabledFeatures::ResourceLoadSchedulerEnabled());
+ scheduler_ = ResourceLoadScheduler::Create(
+ MockFetchContext::Create(MockFetchContext::kShouldNotLoadNewResource));
+ Scheduler()->SetOutstandingLimitForTesting(1);
+ }
+ void TearDown() override { Scheduler()->Shutdown(); }
+
+ ResourceLoadScheduler* Scheduler() { return scheduler_; }
+
+ bool Release(ResourceLoadScheduler::ClientId client) {
+ return Scheduler()->Release(
+ client, ResourceLoadScheduler::ReleaseOption::kReleaseOnly,
+ ResourceLoadScheduler::TrafficReportHints::InvalidInstance());
+ }
+ bool ReleaseAndSchedule(ResourceLoadScheduler::ClientId client) {
+ return Scheduler()->Release(
+ client, ResourceLoadScheduler::ReleaseOption::kReleaseAndSchedule,
+ ResourceLoadScheduler::TrafficReportHints::InvalidInstance());
+ }
+
+ private:
+ Persistent<ResourceLoadScheduler> scheduler_;
+};
+
+class RendererSideResourceSchedulerTest : public testing::Test {
+ public:
+ using ThrottleOption = ResourceLoadScheduler::ThrottleOption;
+ class TestingPlatformSupport : public ::blink::TestingPlatformSupport {
+ public:
+ bool IsRendererSideResourceSchedulerEnabled() const override {
+ return true;
+ }
+ };
+
+ void SetUp() override {
+ DCHECK(RuntimeEnabledFeatures::ResourceLoadSchedulerEnabled());
+ scheduler_ = ResourceLoadScheduler::Create(
+ MockFetchContext::Create(MockFetchContext::kShouldNotLoadNewResource));
+ Scheduler()->SetOutstandingLimitForTesting(1);
+ }
+ void TearDown() override { Scheduler()->Shutdown(); }
+
+ ResourceLoadScheduler* Scheduler() { return scheduler_; }
+
+ bool Release(ResourceLoadScheduler::ClientId client) {
+ return Scheduler()->Release(
+ client, ResourceLoadScheduler::ReleaseOption::kReleaseOnly,
+ ResourceLoadScheduler::TrafficReportHints::InvalidInstance());
+ }
+
+ private:
+ ScopedTestingPlatformSupport<TestingPlatformSupport>
+ testing_platform_support_;
+ Persistent<ResourceLoadScheduler> scheduler_;
+};
+
+TEST_F(ResourceLoadSchedulerTest, Bypass) {
+ // A request that disallows throttling should be ran synchronously.
+ MockClient* client1 = new MockClient;
+ ResourceLoadScheduler::ClientId id1 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client1, ThrottleOption::kCanNotBeThrottled,
+ ResourceLoadPriority::kMedium, 0 /* intra_priority */,
+ &id1);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id1);
+ EXPECT_TRUE(client1->WasRun());
+
+ // Another request that disallows throttling also should be ran even it makes
+ // the outstanding number reaches to the limit.
+ MockClient* client2 = new MockClient;
+ ResourceLoadScheduler::ClientId id2 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client2, ThrottleOption::kCanNotBeThrottled,
+ ResourceLoadPriority::kMedium, 0 /* intra_priority */,
+ &id2);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id2);
+ EXPECT_TRUE(client2->WasRun());
+
+ // Call Release() with different options just in case.
+ EXPECT_TRUE(Release(id1));
+ EXPECT_TRUE(ReleaseAndSchedule(id2));
+
+ // Should not succeed to call with the same ID twice.
+ EXPECT_FALSE(Release(id1));
+
+ // Should not succeed to call with the invalid ID or unused ID.
+ EXPECT_FALSE(Release(ResourceLoadScheduler::kInvalidClientId));
+
+ EXPECT_FALSE(Release(static_cast<ResourceLoadScheduler::ClientId>(774)));
+}
+
+TEST_F(ResourceLoadSchedulerTest, Throttled) {
+ // The first request should be ran synchronously.
+ MockClient* client1 = new MockClient;
+ ResourceLoadScheduler::ClientId id1 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client1, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kMedium, 0 /* intra_priority */,
+ &id1);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id1);
+ EXPECT_TRUE(client1->WasRun());
+
+ // Another request should be throttled until the first request calls Release.
+ MockClient* client2 = new MockClient;
+ ResourceLoadScheduler::ClientId id2 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client2, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kMedium, 0 /* intra_priority */,
+ &id2);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id2);
+ EXPECT_FALSE(client2->WasRun());
+
+ // Two more requests.
+ MockClient* client3 = new MockClient;
+ ResourceLoadScheduler::ClientId id3 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client3, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kMedium, 0 /* intra_priority */,
+ &id3);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id3);
+ EXPECT_FALSE(client3->WasRun());
+
+ MockClient* client4 = new MockClient;
+ ResourceLoadScheduler::ClientId id4 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client4, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kMedium, 0 /* intra_priority */,
+ &id4);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id4);
+ EXPECT_FALSE(client4->WasRun());
+
+ // Call Release() to run the second request.
+ EXPECT_TRUE(ReleaseAndSchedule(id1));
+ EXPECT_TRUE(client2->WasRun());
+
+ // Call Release() with kReleaseOnly should not run the third and the fourth
+ // requests.
+ EXPECT_TRUE(Release(id2));
+ EXPECT_FALSE(client3->WasRun());
+ EXPECT_FALSE(client4->WasRun());
+
+ // Should be able to call Release() for a client that hasn't run yet. This
+ // should run another scheduling to run the fourth request.
+ EXPECT_TRUE(ReleaseAndSchedule(id3));
+ EXPECT_TRUE(client4->WasRun());
+}
+
+TEST_F(ResourceLoadSchedulerTest, Unthrottle) {
+ // Push three requests.
+ MockClient* client1 = new MockClient;
+ ResourceLoadScheduler::ClientId id1 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client1, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kMedium, 0 /* intra_priority */,
+ &id1);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id1);
+ EXPECT_TRUE(client1->WasRun());
+
+ MockClient* client2 = new MockClient;
+ ResourceLoadScheduler::ClientId id2 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client2, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kMedium, 0 /* intra_priority */,
+ &id2);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id2);
+ EXPECT_FALSE(client2->WasRun());
+
+ MockClient* client3 = new MockClient;
+ ResourceLoadScheduler::ClientId id3 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client3, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kMedium, 0 /* intra_priority */,
+ &id3);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id3);
+ EXPECT_FALSE(client3->WasRun());
+
+ // Allows to pass all requests.
+ Scheduler()->SetOutstandingLimitForTesting(3);
+ EXPECT_TRUE(client2->WasRun());
+ EXPECT_TRUE(client3->WasRun());
+
+ // Release all.
+ EXPECT_TRUE(Release(id3));
+ EXPECT_TRUE(Release(id2));
+ EXPECT_TRUE(Release(id1));
+}
+
+TEST_F(ResourceLoadSchedulerTest, Stopped) {
+ // Push three requests.
+ MockClient* client1 = new MockClient;
+ ResourceLoadScheduler::ClientId id1 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client1, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kMedium, 0 /* intra_priority */,
+ &id1);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id1);
+ EXPECT_TRUE(client1->WasRun());
+
+ MockClient* client2 = new MockClient;
+ ResourceLoadScheduler::ClientId id2 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client2, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kMedium, 0 /* intra_priority */,
+ &id2);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id2);
+ EXPECT_FALSE(client2->WasRun());
+
+ MockClient* client3 = new MockClient;
+ ResourceLoadScheduler::ClientId id3 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client3, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kMedium, 0 /* intra_priority */,
+ &id3);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id3);
+ EXPECT_FALSE(client3->WasRun());
+
+ // Setting outstanding_limit_ to 0 in ThrottlingState::kStopped, prevents
+ // further requests.
+ Scheduler()->SetOutstandingLimitForTesting(0);
+ EXPECT_FALSE(client2->WasRun());
+ EXPECT_FALSE(client3->WasRun());
+
+ // Calling Release() still does not run the second request.
+ EXPECT_TRUE(ReleaseAndSchedule(id1));
+ EXPECT_FALSE(client2->WasRun());
+ EXPECT_FALSE(client3->WasRun());
+
+ // Release all.
+ EXPECT_TRUE(Release(id3));
+ EXPECT_TRUE(Release(id2));
+}
+
+TEST_F(ResourceLoadSchedulerTest, PriotrityIsNotConsidered) {
+ // Push three requests.
+ MockClient* client1 = new MockClient;
+
+ Scheduler()->SetOutstandingLimitForTesting(0);
+
+ ResourceLoadScheduler::ClientId id1 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client1, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kLowest, 10 /* intra_priority */,
+ &id1);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id1);
+
+ MockClient* client2 = new MockClient;
+ ResourceLoadScheduler::ClientId id2 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client2, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kLow, 1 /* intra_priority */,
+ &id2);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id2);
+
+ MockClient* client3 = new MockClient;
+ ResourceLoadScheduler::ClientId id3 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client3, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kLow, 3 /* intra_priority */,
+ &id3);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id3);
+
+ EXPECT_FALSE(client1->WasRun());
+ EXPECT_FALSE(client2->WasRun());
+ EXPECT_FALSE(client3->WasRun());
+
+ Scheduler()->SetOutstandingLimitForTesting(1);
+
+ EXPECT_TRUE(client1->WasRun());
+ EXPECT_FALSE(client2->WasRun());
+ EXPECT_FALSE(client3->WasRun());
+
+ Scheduler()->SetOutstandingLimitForTesting(2);
+
+ EXPECT_TRUE(client1->WasRun());
+ EXPECT_TRUE(client2->WasRun());
+ EXPECT_FALSE(client3->WasRun());
+
+ // Release all.
+ EXPECT_TRUE(Release(id3));
+ EXPECT_TRUE(Release(id2));
+ EXPECT_TRUE(Release(id1));
+}
+
+TEST_F(RendererSideResourceSchedulerTest, PriorityIsConsidered) {
+ // Push three requests.
+ MockClient* client1 = new MockClient;
+
+ Scheduler()->SetOutstandingLimitForTesting(0);
+
+ ResourceLoadScheduler::ClientId id1 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client1, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kLowest, 10 /* intra_priority */,
+ &id1);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id1);
+
+ MockClient* client2 = new MockClient;
+ ResourceLoadScheduler::ClientId id2 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client2, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kLow, 1 /* intra_priority */,
+ &id2);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id2);
+
+ MockClient* client3 = new MockClient;
+ ResourceLoadScheduler::ClientId id3 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client3, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kLow, 3 /* intra_priority */,
+ &id3);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id3);
+
+ MockClient* client4 = new MockClient;
+ ResourceLoadScheduler::ClientId id4 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client4, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kHigh, 0 /* intra_priority */,
+ &id4);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id4);
+
+ EXPECT_FALSE(client1->WasRun());
+ EXPECT_FALSE(client2->WasRun());
+ EXPECT_FALSE(client3->WasRun());
+ EXPECT_TRUE(client4->WasRun());
+
+ Scheduler()->SetOutstandingLimitForTesting(1);
+
+ EXPECT_FALSE(client1->WasRun());
+ EXPECT_FALSE(client2->WasRun());
+ EXPECT_TRUE(client3->WasRun());
+ EXPECT_TRUE(client4->WasRun());
+
+ Scheduler()->SetOutstandingLimitForTesting(2);
+
+ EXPECT_FALSE(client1->WasRun());
+ EXPECT_TRUE(client2->WasRun());
+ EXPECT_TRUE(client3->WasRun());
+ EXPECT_TRUE(client4->WasRun());
+
+ // Release all.
+ EXPECT_TRUE(Release(id4));
+ EXPECT_TRUE(Release(id3));
+ EXPECT_TRUE(Release(id2));
+ EXPECT_TRUE(Release(id1));
+}
+
+TEST_F(RendererSideResourceSchedulerTest, IsThrottablePriority) {
+ EXPECT_TRUE(
+ Scheduler()->IsThrottablePriority(ResourceLoadPriority::kVeryLow));
+ EXPECT_TRUE(Scheduler()->IsThrottablePriority(ResourceLoadPriority::kLow));
+ EXPECT_TRUE(Scheduler()->IsThrottablePriority(ResourceLoadPriority::kMedium));
+ EXPECT_FALSE(Scheduler()->IsThrottablePriority(ResourceLoadPriority::kHigh));
+ EXPECT_FALSE(
+ Scheduler()->IsThrottablePriority(ResourceLoadPriority::kVeryHigh));
+
+ Scheduler()->LoosenThrottlingPolicy();
+
+ EXPECT_TRUE(
+ Scheduler()->IsThrottablePriority(ResourceLoadPriority::kVeryLow));
+ EXPECT_TRUE(Scheduler()->IsThrottablePriority(ResourceLoadPriority::kLow));
+ EXPECT_TRUE(Scheduler()->IsThrottablePriority(ResourceLoadPriority::kMedium));
+ EXPECT_FALSE(Scheduler()->IsThrottablePriority(ResourceLoadPriority::kHigh));
+ EXPECT_FALSE(
+ Scheduler()->IsThrottablePriority(ResourceLoadPriority::kVeryHigh));
+}
+
+TEST_F(RendererSideResourceSchedulerTest, SetPriority) {
+ // Start with the normal scheduling policy.
+ Scheduler()->LoosenThrottlingPolicy();
+ // Push three requests.
+ MockClient* client1 = new MockClient;
+
+ Scheduler()->SetOutstandingLimitForTesting(0);
+
+ ResourceLoadScheduler::ClientId id1 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client1, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kLowest, 0 /* intra_priority */,
+ &id1);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id1);
+
+ MockClient* client2 = new MockClient;
+ ResourceLoadScheduler::ClientId id2 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client2, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kLow, 5 /* intra_priority */,
+ &id2);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id2);
+
+ MockClient* client3 = new MockClient;
+ ResourceLoadScheduler::ClientId id3 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client3, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kLow, 10 /* intra_priority */,
+ &id3);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id3);
+
+ EXPECT_FALSE(client1->WasRun());
+ EXPECT_FALSE(client2->WasRun());
+ EXPECT_FALSE(client3->WasRun());
+
+ Scheduler()->SetPriority(id1, ResourceLoadPriority::kHigh, 0);
+
+ EXPECT_TRUE(client1->WasRun());
+ EXPECT_FALSE(client2->WasRun());
+ EXPECT_FALSE(client3->WasRun());
+
+ Scheduler()->SetPriority(id3, ResourceLoadPriority::kLow, 2);
+
+ EXPECT_TRUE(client1->WasRun());
+ EXPECT_FALSE(client2->WasRun());
+ EXPECT_FALSE(client3->WasRun());
+
+ Scheduler()->SetOutstandingLimitForTesting(2);
+
+ EXPECT_TRUE(client1->WasRun());
+ EXPECT_TRUE(client2->WasRun());
+ EXPECT_FALSE(client3->WasRun());
+
+ // Release all.
+ EXPECT_TRUE(Release(id3));
+ EXPECT_TRUE(Release(id2));
+ EXPECT_TRUE(Release(id1));
+}
+
+TEST_F(RendererSideResourceSchedulerTest, LoosenThrottlingPolicy) {
+ MockClient* client1 = new MockClient;
+
+ Scheduler()->SetOutstandingLimitForTesting(0, 0);
+
+ ResourceLoadScheduler::ClientId id1 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client1, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kLowest, 0 /* intra_priority */,
+ &id1);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id1);
+
+ MockClient* client2 = new MockClient;
+ ResourceLoadScheduler::ClientId id2 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client2, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kLowest, 0 /* intra_priority */,
+ &id2);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id2);
+
+ MockClient* client3 = new MockClient;
+ ResourceLoadScheduler::ClientId id3 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client3, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kLowest, 0 /* intra_priority */,
+ &id3);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id3);
+
+ MockClient* client4 = new MockClient;
+ ResourceLoadScheduler::ClientId id4 = ResourceLoadScheduler::kInvalidClientId;
+ Scheduler()->Request(client4, ThrottleOption::kCanBeThrottled,
+ ResourceLoadPriority::kLowest, 0 /* intra_priority */,
+ &id4);
+ EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id4);
+
+ Scheduler()->SetPriority(id2, ResourceLoadPriority::kLow, 0);
+ Scheduler()->SetPriority(id3, ResourceLoadPriority::kLow, 0);
+ Scheduler()->SetPriority(id4, ResourceLoadPriority::kMedium, 0);
+
+ // As the policy is |kTight|, |kMedium| is throttled.
+ EXPECT_FALSE(client1->WasRun());
+ EXPECT_FALSE(client2->WasRun());
+ EXPECT_FALSE(client3->WasRun());
+ EXPECT_FALSE(client4->WasRun());
+
+ Scheduler()->SetOutstandingLimitForTesting(0, 2);
+
+ // MockFetchContext's initial scheduling policy is |kTight|, setting the
+ // outstanding limit for the normal mode doesn't take effect.
+ EXPECT_FALSE(client1->WasRun());
+ EXPECT_FALSE(client2->WasRun());
+ EXPECT_FALSE(client3->WasRun());
+ EXPECT_FALSE(client4->WasRun());
+
+ // Now let's tighten the limit again.
+ Scheduler()->SetOutstandingLimitForTesting(0, 0);
+
+ // ...and change the scheduling policy to |kNormal|.
+ Scheduler()->LoosenThrottlingPolicy();
+
+ EXPECT_FALSE(client1->WasRun());
+ EXPECT_FALSE(client2->WasRun());
+ EXPECT_FALSE(client3->WasRun());
+ EXPECT_FALSE(client4->WasRun());
+
+ Scheduler()->SetOutstandingLimitForTesting(0, 2);
+
+ EXPECT_FALSE(client1->WasRun());
+ EXPECT_TRUE(client2->WasRun());
+ EXPECT_FALSE(client3->WasRun());
+ EXPECT_TRUE(client4->WasRun());
+
+ // Release all.
+ EXPECT_TRUE(Release(id4));
+ EXPECT_TRUE(Release(id3));
+ EXPECT_TRUE(Release(id2));
+ EXPECT_TRUE(Release(id1));
+}
+
+} // namespace
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.cc
new file mode 100644
index 00000000000..ac8c07f7469
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.cc
@@ -0,0 +1,126 @@
+// Copyright 2015 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/platform/loader/fetch/resource_load_timing.h"
+
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+
+namespace blink {
+
+ResourceLoadTiming::ResourceLoadTiming() {}
+
+scoped_refptr<ResourceLoadTiming> ResourceLoadTiming::Create() {
+ return base::AdoptRef(new ResourceLoadTiming);
+}
+
+scoped_refptr<ResourceLoadTiming> ResourceLoadTiming::DeepCopy() {
+ scoped_refptr<ResourceLoadTiming> timing = Create();
+ timing->request_time_ = request_time_;
+ timing->proxy_start_ = proxy_start_;
+ timing->proxy_end_ = proxy_end_;
+ timing->dns_start_ = dns_start_;
+ timing->dns_end_ = dns_end_;
+ timing->connect_start_ = connect_start_;
+ timing->connect_end_ = connect_end_;
+ timing->worker_start_ = worker_start_;
+ timing->worker_ready_ = worker_ready_;
+ timing->send_start_ = send_start_;
+ timing->send_end_ = send_end_;
+ timing->receive_headers_end_ = receive_headers_end_;
+ timing->ssl_start_ = ssl_start_;
+ timing->ssl_end_ = ssl_end_;
+ timing->push_start_ = push_start_;
+ timing->push_end_ = push_end_;
+ return timing;
+}
+
+bool ResourceLoadTiming::operator==(const ResourceLoadTiming& other) const {
+ return request_time_ == other.request_time_ &&
+ proxy_start_ == other.proxy_start_ && proxy_end_ == other.proxy_end_ &&
+ dns_start_ == other.dns_start_ && dns_end_ == other.dns_end_ &&
+ connect_start_ == other.connect_start_ &&
+ connect_end_ == other.connect_end_ &&
+ worker_start_ == other.worker_start_ &&
+ worker_ready_ == other.worker_ready_ &&
+ send_start_ == other.send_start_ && send_end_ == other.send_end_ &&
+ receive_headers_end_ == other.receive_headers_end_ &&
+ ssl_start_ == other.ssl_start_ && ssl_end_ == other.ssl_end_ &&
+ push_start_ == other.push_start_ && push_end_ == other.push_end_;
+}
+
+bool ResourceLoadTiming::operator!=(const ResourceLoadTiming& other) const {
+ return !(*this == other);
+}
+
+void ResourceLoadTiming::SetDnsStart(TimeTicks dns_start) {
+ dns_start_ = dns_start;
+}
+
+void ResourceLoadTiming::SetRequestTime(TimeTicks request_time) {
+ request_time_ = request_time;
+}
+
+void ResourceLoadTiming::SetProxyStart(TimeTicks proxy_start) {
+ proxy_start_ = proxy_start;
+}
+
+void ResourceLoadTiming::SetProxyEnd(TimeTicks proxy_end) {
+ proxy_end_ = proxy_end;
+}
+
+void ResourceLoadTiming::SetDnsEnd(TimeTicks dns_end) {
+ dns_end_ = dns_end;
+}
+
+void ResourceLoadTiming::SetConnectStart(TimeTicks connect_start) {
+ connect_start_ = connect_start;
+}
+
+void ResourceLoadTiming::SetConnectEnd(TimeTicks connect_end) {
+ connect_end_ = connect_end;
+}
+
+void ResourceLoadTiming::SetWorkerStart(TimeTicks worker_start) {
+ worker_start_ = worker_start;
+}
+
+void ResourceLoadTiming::SetWorkerReady(TimeTicks worker_ready) {
+ worker_ready_ = worker_ready;
+}
+
+void ResourceLoadTiming::SetSendStart(TimeTicks send_start) {
+ TRACE_EVENT_MARK_WITH_TIMESTAMP0("blink.user_timing", "requestStart",
+ send_start);
+ send_start_ = send_start;
+}
+
+void ResourceLoadTiming::SetSendEnd(TimeTicks send_end) {
+ send_end_ = send_end;
+}
+
+void ResourceLoadTiming::SetReceiveHeadersEnd(TimeTicks receive_headers_end) {
+ receive_headers_end_ = receive_headers_end;
+}
+
+void ResourceLoadTiming::SetSslStart(TimeTicks ssl_start) {
+ ssl_start_ = ssl_start;
+}
+
+void ResourceLoadTiming::SetSslEnd(TimeTicks ssl_end) {
+ ssl_end_ = ssl_end;
+}
+
+void ResourceLoadTiming::SetPushStart(TimeTicks push_start) {
+ push_start_ = push_start;
+}
+
+void ResourceLoadTiming::SetPushEnd(TimeTicks push_end) {
+ push_end_ = push_end;
+}
+
+double ResourceLoadTiming::CalculateMillisecondDelta(TimeTicks time) const {
+ return time.is_null() ? -1 : (time - request_time_).InMillisecondsF();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h
new file mode 100644
index 00000000000..b5016d9beb8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2010 Google, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOAD_TIMING_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOAD_TIMING_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT ResourceLoadTiming
+ : public RefCounted<ResourceLoadTiming> {
+ public:
+ static scoped_refptr<ResourceLoadTiming> Create();
+
+ scoped_refptr<ResourceLoadTiming> DeepCopy();
+ bool operator==(const ResourceLoadTiming&) const;
+ bool operator!=(const ResourceLoadTiming&) const;
+
+ void SetDnsStart(TimeTicks);
+ void SetRequestTime(TimeTicks);
+ void SetProxyStart(TimeTicks);
+ void SetProxyEnd(TimeTicks);
+ void SetDnsEnd(TimeTicks);
+ void SetConnectStart(TimeTicks);
+ void SetConnectEnd(TimeTicks);
+ void SetWorkerStart(TimeTicks);
+ void SetWorkerReady(TimeTicks);
+ void SetSendStart(TimeTicks);
+ void SetSendEnd(TimeTicks);
+ void SetReceiveHeadersEnd(TimeTicks);
+ void SetSslStart(TimeTicks);
+ void SetSslEnd(TimeTicks);
+ void SetPushStart(TimeTicks);
+ void SetPushEnd(TimeTicks);
+
+ TimeTicks DnsStart() const { return dns_start_; }
+ TimeTicks RequestTime() const { return request_time_; }
+ TimeTicks ProxyStart() const { return proxy_start_; }
+ TimeTicks ProxyEnd() const { return proxy_end_; }
+ TimeTicks DnsEnd() const { return dns_end_; }
+ TimeTicks ConnectStart() const { return connect_start_; }
+ TimeTicks ConnectEnd() const { return connect_end_; }
+ TimeTicks WorkerStart() const { return worker_start_; }
+ TimeTicks WorkerReady() const { return worker_ready_; }
+ TimeTicks SendStart() const { return send_start_; }
+ TimeTicks SendEnd() const { return send_end_; }
+ TimeTicks ReceiveHeadersEnd() const { return receive_headers_end_; }
+ TimeTicks SslStart() const { return ssl_start_; }
+ TimeTicks SslEnd() const { return ssl_end_; }
+ TimeTicks PushStart() const { return push_start_; }
+ TimeTicks PushEnd() const { return push_end_; }
+
+ double CalculateMillisecondDelta(TimeTicks) const;
+
+ private:
+ ResourceLoadTiming();
+
+ // We want to present a unified timeline to Javascript. Using walltime is
+ // problematic, because the clock may skew while resources load. To prevent
+ // that skew, we record a single reference walltime when root document
+ // navigation begins. All other times are recorded using
+ // monotonicallyIncreasingTime(). When a time needs to be presented to
+ // Javascript, we build a pseudo-walltime using the following equation
+ // (m_requestTime as example):
+ // pseudo time = document wall reference +
+ // (m_requestTime - document monotonic reference).
+
+ // All values from monotonicallyIncreasingTime(), in WTF::TimeTicks.
+ TimeTicks request_time_;
+ TimeTicks proxy_start_;
+ TimeTicks proxy_end_;
+ TimeTicks dns_start_;
+ TimeTicks dns_end_;
+ TimeTicks connect_start_;
+ TimeTicks connect_end_;
+ TimeTicks worker_start_;
+ TimeTicks worker_ready_;
+ TimeTicks send_start_;
+ TimeTicks send_end_;
+ TimeTicks receive_headers_end_;
+ TimeTicks ssl_start_;
+ TimeTicks ssl_end_;
+ TimeTicks push_start_;
+ TimeTicks push_end_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
new file mode 100644
index 00000000000..7800884770f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
@@ -0,0 +1,873 @@
+/*
+ * Copyright (C) 2006, 2007, 2010, 2011 Apple Inc. All rights reserved.
+ * (C) 2007 Graham Dennis (graham.dennis@gmail.com)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader.h"
+
+#include "services/network/public/mojom/fetch_api.mojom-blink.h"
+#include "third_party/blink/public/mojom/blob/blob_registry.mojom-blink.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_cors.h"
+#include "third_party/blink/public/platform/web_data.h"
+#include "third_party/blink/public/platform/web_url_error.h"
+#include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/public/platform/web_url_response.h"
+#include "third_party/blink/renderer/platform/exported/wrapped_resource_request.h"
+#include "third_party/blink/renderer/platform/exported/wrapped_resource_response.h"
+#include "third_party/blink/renderer/platform/loader/cors/cors.h"
+#include "third_party/blink/renderer/platform/loader/cors/cors_error_string.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_context.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_error.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
+#include "third_party/blink/renderer/platform/network/network_instrumentation.h"
+#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/weborigin/security_violation_reporting_policy.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+ResourceLoader* ResourceLoader::Create(ResourceFetcher* fetcher,
+ ResourceLoadScheduler* scheduler,
+ Resource* resource,
+ uint32_t inflight_keepalive_bytes) {
+ return new ResourceLoader(fetcher, scheduler, resource,
+ inflight_keepalive_bytes);
+}
+
+ResourceLoader::ResourceLoader(ResourceFetcher* fetcher,
+ ResourceLoadScheduler* scheduler,
+ Resource* resource,
+ uint32_t inflight_keepalive_bytes)
+ : scheduler_client_id_(ResourceLoadScheduler::kInvalidClientId),
+ fetcher_(fetcher),
+ scheduler_(scheduler),
+ resource_(resource),
+ inflight_keepalive_bytes_(inflight_keepalive_bytes),
+ is_cache_aware_loading_activated_(false),
+ progress_binding_(this),
+ cancel_timer_(Context().GetLoadingTaskRunner(),
+ this,
+ &ResourceLoader::CancelTimerFired) {
+ DCHECK(resource_);
+ DCHECK(fetcher_);
+
+ resource_->SetLoader(this);
+}
+
+ResourceLoader::~ResourceLoader() = default;
+
+void ResourceLoader::Trace(blink::Visitor* visitor) {
+ visitor->Trace(fetcher_);
+ visitor->Trace(scheduler_);
+ visitor->Trace(resource_);
+ ResourceLoadSchedulerClient::Trace(visitor);
+}
+
+void ResourceLoader::Start() {
+ const ResourceRequest& request = resource_->GetResourceRequest();
+ ActivateCacheAwareLoadingIfNeeded(request);
+ loader_ = Context().CreateURLLoader(request, Context().GetLoadingTaskRunner(),
+ resource_->Options());
+ DCHECK_EQ(ResourceLoadScheduler::kInvalidClientId, scheduler_client_id_);
+ auto throttle_option = ResourceLoadScheduler::ThrottleOption::kCanBeThrottled;
+
+ // Synchronous requests should not work with a throttling. Also, tentatively
+ // disables throttling for fetch requests that could keep on holding an active
+ // connection until data is read by JavaScript.
+ if (resource_->Options().synchronous_policy == kRequestSynchronously ||
+ request.GetRequestContext() == WebURLRequest::kRequestContextFetch) {
+ throttle_option = ResourceLoadScheduler::ThrottleOption::kCanNotBeThrottled;
+ }
+
+ scheduler_->Request(this, throttle_option, request.Priority(),
+ request.IntraPriorityValue(), &scheduler_client_id_);
+}
+
+void ResourceLoader::Run() {
+ StartWith(resource_->GetResourceRequest());
+}
+
+void ResourceLoader::StartWith(const ResourceRequest& request) {
+ DCHECK_NE(ResourceLoadScheduler::kInvalidClientId, scheduler_client_id_);
+ DCHECK(loader_);
+
+ if (resource_->Options().synchronous_policy == kRequestSynchronously &&
+ Context().DefersLoading()) {
+ Cancel();
+ return;
+ }
+
+ is_downloading_to_blob_ = request.DownloadToBlob();
+
+ loader_->SetDefersLoading(Context().DefersLoading());
+
+ if (is_cache_aware_loading_activated_) {
+ // Override cache policy for cache-aware loading. If this request fails, a
+ // reload with original request will be triggered in DidFail().
+ ResourceRequest cache_aware_request(request);
+ cache_aware_request.SetCacheMode(
+ mojom::FetchCacheMode::kUnspecifiedOnlyIfCachedStrict);
+ loader_->LoadAsynchronously(WrappedResourceRequest(cache_aware_request),
+ this);
+ return;
+ }
+
+ if (resource_->Options().synchronous_policy == kRequestSynchronously)
+ RequestSynchronously(request);
+ else
+ loader_->LoadAsynchronously(WrappedResourceRequest(request), this);
+}
+
+void ResourceLoader::Release(
+ ResourceLoadScheduler::ReleaseOption option,
+ const ResourceLoadScheduler::TrafficReportHints& hints) {
+ DCHECK_NE(ResourceLoadScheduler::kInvalidClientId, scheduler_client_id_);
+ bool released = scheduler_->Release(scheduler_client_id_, option, hints);
+ DCHECK(released);
+ scheduler_client_id_ = ResourceLoadScheduler::kInvalidClientId;
+}
+
+void ResourceLoader::Restart(const ResourceRequest& request) {
+ CHECK_EQ(resource_->Options().synchronous_policy, kRequestAsynchronously);
+
+ loader_ = Context().CreateURLLoader(request, Context().GetLoadingTaskRunner(),
+ resource_->Options());
+ StartWith(request);
+}
+
+void ResourceLoader::SetDefersLoading(bool defers) {
+ DCHECK(loader_);
+ loader_->SetDefersLoading(defers);
+ if (defers) {
+ resource_->VirtualTimePauser().UnpauseVirtualTime();
+ } else {
+ resource_->VirtualTimePauser().PauseVirtualTime();
+ }
+}
+
+void ResourceLoader::DidChangePriority(ResourceLoadPriority load_priority,
+ int intra_priority_value) {
+ if (scheduler_->IsRunning(scheduler_client_id_)) {
+ DCHECK(loader_);
+ DCHECK_NE(ResourceLoadScheduler::kInvalidClientId, scheduler_client_id_);
+ loader_->DidChangePriority(
+ static_cast<WebURLRequest::Priority>(load_priority),
+ intra_priority_value);
+ } else {
+ scheduler_->SetPriority(scheduler_client_id_, load_priority,
+ intra_priority_value);
+ }
+}
+
+void ResourceLoader::ScheduleCancel() {
+ if (!cancel_timer_.IsActive())
+ cancel_timer_.StartOneShot(TimeDelta(), FROM_HERE);
+}
+
+void ResourceLoader::CancelTimerFired(TimerBase*) {
+ if (loader_ && !resource_->HasClientsOrObservers())
+ Cancel();
+}
+
+void ResourceLoader::Cancel() {
+ HandleError(
+ ResourceError::CancelledError(resource_->LastResourceRequest().Url()));
+}
+
+void ResourceLoader::CancelForRedirectAccessCheckError(
+ const KURL& new_url,
+ ResourceRequestBlockedReason blocked_reason) {
+ resource_->WillNotFollowRedirect();
+
+ if (loader_) {
+ HandleError(
+ ResourceError::CancelledDueToAccessCheckError(new_url, blocked_reason));
+ }
+}
+
+static bool IsManualRedirectFetchRequest(const ResourceRequest& request) {
+ return request.GetFetchRedirectMode() ==
+ network::mojom::FetchRedirectMode::kManual &&
+ request.GetRequestContext() == WebURLRequest::kRequestContextFetch;
+}
+
+bool ResourceLoader::WillFollowRedirect(
+ const WebURL& new_url,
+ const WebURL& new_site_for_cookies,
+ const WebString& new_referrer,
+ WebReferrerPolicy new_referrer_policy,
+ const WebString& new_method,
+ const WebURLResponse& passed_redirect_response,
+ bool& report_raw_headers) {
+ DCHECK(!passed_redirect_response.IsNull());
+
+ if (is_cache_aware_loading_activated_) {
+ // Fail as cache miss if cached response is a redirect.
+ HandleError(
+ ResourceError::CacheMissError(resource_->LastResourceRequest().Url()));
+ return false;
+ }
+
+ std::unique_ptr<ResourceRequest> new_request =
+ resource_->LastResourceRequest().CreateRedirectRequest(
+ new_url, new_method, new_site_for_cookies, new_referrer,
+ static_cast<ReferrerPolicy>(new_referrer_policy),
+ !passed_redirect_response.WasFetchedViaServiceWorker());
+
+ Resource::Type resource_type = resource_->GetType();
+
+ const ResourceRequest& initial_request = resource_->GetResourceRequest();
+ // The following parameters never change during the lifetime of a request.
+ WebURLRequest::RequestContext request_context =
+ initial_request.GetRequestContext();
+ network::mojom::RequestContextFrameType frame_type =
+ initial_request.GetFrameType();
+ network::mojom::FetchRequestMode fetch_request_mode =
+ initial_request.GetFetchRequestMode();
+ network::mojom::FetchCredentialsMode fetch_credentials_mode =
+ initial_request.GetFetchCredentialsMode();
+
+ const ResourceLoaderOptions& options = resource_->Options();
+
+ const ResourceResponse& redirect_response(
+ passed_redirect_response.ToResourceResponse());
+
+ if (!IsManualRedirectFetchRequest(initial_request)) {
+ bool unused_preload = resource_->IsUnusedPreload();
+
+ // Don't send security violation reports for unused preloads.
+ SecurityViolationReportingPolicy reporting_policy =
+ unused_preload ? SecurityViolationReportingPolicy::kSuppressReporting
+ : SecurityViolationReportingPolicy::kReport;
+
+ // CanRequest() checks only enforced CSP, so check report-only here to
+ // ensure that violations are sent.
+ Context().CheckCSPForRequest(
+ request_context, new_url, options, reporting_policy,
+ ResourceRequest::RedirectStatus::kFollowedRedirect);
+
+ ResourceRequestBlockedReason blocked_reason = Context().CanRequest(
+ resource_type, *new_request, new_url, options, reporting_policy,
+ FetchParameters::kUseDefaultOriginRestrictionForType,
+ ResourceRequest::RedirectStatus::kFollowedRedirect);
+
+ if (Context().IsAdResource(new_url, resource_type,
+ new_request->GetRequestContext())) {
+ new_request->SetIsAdResource();
+ }
+
+ if (blocked_reason != ResourceRequestBlockedReason::kNone) {
+ CancelForRedirectAccessCheckError(new_url, blocked_reason);
+ return false;
+ }
+
+ if (options.cors_handling_by_resource_fetcher ==
+ kEnableCORSHandlingByResourceFetcher &&
+ fetch_request_mode == network::mojom::FetchRequestMode::kCORS) {
+ scoped_refptr<const SecurityOrigin> source_origin = GetSourceOrigin();
+ WebSecurityOrigin source_web_origin(source_origin.get());
+ WrappedResourceRequest new_request_wrapper(*new_request);
+ WTF::Optional<network::mojom::CORSError> cors_error =
+ WebCORS::HandleRedirect(
+ source_web_origin, new_request_wrapper, redirect_response.Url(),
+ redirect_response.HttpStatusCode(),
+ redirect_response.HttpHeaderFields(), fetch_credentials_mode,
+ resource_->MutableOptions());
+ if (cors_error) {
+ resource_->SetCORSStatus(CORSStatus::kFailed);
+
+ if (!unused_preload) {
+ Context().AddErrorConsoleMessage(
+ CORS::GetErrorString(CORS::ErrorParameter::Create(
+ *cors_error, redirect_response.Url(), new_url,
+ redirect_response.HttpStatusCode(),
+ redirect_response.HttpHeaderFields(), *source_origin.get(),
+ resource_->LastResourceRequest().GetRequestContext())),
+ FetchContext::kJSSource);
+ }
+
+ CancelForRedirectAccessCheckError(new_url,
+ ResourceRequestBlockedReason::kOther);
+ return false;
+ }
+
+ source_origin = source_web_origin;
+ }
+ if (resource_type == Resource::kImage &&
+ fetcher_->ShouldDeferImageLoad(new_url)) {
+ CancelForRedirectAccessCheckError(new_url,
+ ResourceRequestBlockedReason::kOther);
+ return false;
+ }
+ }
+
+ bool cross_origin =
+ !SecurityOrigin::AreSameSchemeHostPort(redirect_response.Url(), new_url);
+ fetcher_->RecordResourceTimingOnRedirect(resource_.Get(), redirect_response,
+ cross_origin);
+
+ if (options.cors_handling_by_resource_fetcher ==
+ kEnableCORSHandlingByResourceFetcher &&
+ fetch_request_mode == network::mojom::FetchRequestMode::kCORS) {
+ bool allow_stored_credentials = false;
+ switch (fetch_credentials_mode) {
+ case network::mojom::FetchCredentialsMode::kOmit:
+ break;
+ case network::mojom::FetchCredentialsMode::kSameOrigin:
+ allow_stored_credentials = !options.cors_flag;
+ break;
+ case network::mojom::FetchCredentialsMode::kInclude:
+ allow_stored_credentials = true;
+ break;
+ }
+ new_request->SetAllowStoredCredentials(allow_stored_credentials);
+ }
+
+ // The following two calls may rewrite the new_request.Url() to
+ // something else not for rejecting redirect but for other reasons.
+ // E.g. WebFrameTestClient::WillSendRequest() and
+ // RenderFrameImpl::WillSendRequest(). We should reflect the
+ // rewriting but currently we cannot. So, compare new_request.Url() and
+ // new_url after calling them, and return false to make the redirect fail on
+ // mismatch.
+
+ Context().PrepareRequest(*new_request,
+ FetchContext::RedirectType::kForRedirect);
+ if (Context().GetFrameScheduler()) {
+ WebScopedVirtualTimePauser virtual_time_pauser =
+ Context().GetFrameScheduler()->CreateWebScopedVirtualTimePauser(
+ resource_->Url().GetString(),
+ WebScopedVirtualTimePauser::VirtualTaskDuration::kNonInstant);
+ virtual_time_pauser.PauseVirtualTime();
+ resource_->VirtualTimePauser() = std::move(virtual_time_pauser);
+ }
+ Context().DispatchWillSendRequest(resource_->Identifier(), *new_request,
+ redirect_response, resource_->GetType(),
+ options.initiator_info);
+
+ // First-party cookie logic moved from DocumentLoader in Blink to
+ // net::URLRequest in the browser. Assert that Blink didn't try to change it
+ // to something else.
+ DCHECK(KURL(new_site_for_cookies) == new_request->SiteForCookies());
+
+ // The following parameters never change during the lifetime of a request.
+ DCHECK_EQ(new_request->GetRequestContext(), request_context);
+ DCHECK_EQ(new_request->GetFrameType(), frame_type);
+ DCHECK_EQ(new_request->GetFetchRequestMode(), fetch_request_mode);
+ DCHECK_EQ(new_request->GetFetchCredentialsMode(), fetch_credentials_mode);
+
+ if (new_request->Url() != KURL(new_url)) {
+ CancelForRedirectAccessCheckError(new_request->Url(),
+ ResourceRequestBlockedReason::kOther);
+ return false;
+ }
+
+ if (!resource_->WillFollowRedirect(*new_request, redirect_response)) {
+ CancelForRedirectAccessCheckError(new_request->Url(),
+ ResourceRequestBlockedReason::kOther);
+ return false;
+ }
+
+ report_raw_headers = new_request->ReportRawHeaders();
+
+ return true;
+}
+
+void ResourceLoader::DidReceiveCachedMetadata(const char* data, int length) {
+ resource_->SetSerializedCachedMetadata(data, length);
+}
+
+void ResourceLoader::DidSendData(unsigned long long bytes_sent,
+ unsigned long long total_bytes_to_be_sent) {
+ resource_->DidSendData(bytes_sent, total_bytes_to_be_sent);
+}
+
+FetchContext& ResourceLoader::Context() const {
+ return fetcher_->Context();
+}
+
+scoped_refptr<const SecurityOrigin> ResourceLoader::GetSourceOrigin() const {
+ scoped_refptr<const SecurityOrigin> origin =
+ resource_->Options().security_origin;
+ if (origin)
+ return origin;
+
+ return Context().GetSecurityOrigin();
+}
+
+CORSStatus ResourceLoader::DetermineCORSStatus(const ResourceResponse& response,
+ StringBuilder& error_msg) const {
+ // Service workers handle CORS separately.
+ if (response.WasFetchedViaServiceWorker()) {
+ switch (response.ResponseTypeViaServiceWorker()) {
+ case network::mojom::FetchResponseType::kBasic:
+ case network::mojom::FetchResponseType::kCORS:
+ case network::mojom::FetchResponseType::kDefault:
+ case network::mojom::FetchResponseType::kError:
+ return CORSStatus::kServiceWorkerSuccessful;
+ case network::mojom::FetchResponseType::kOpaque:
+ case network::mojom::FetchResponseType::kOpaqueRedirect:
+ return CORSStatus::kServiceWorkerOpaque;
+ }
+ NOTREACHED();
+ }
+
+ if (resource_->GetType() == Resource::Type::kMainResource)
+ return CORSStatus::kNotApplicable;
+
+ scoped_refptr<const SecurityOrigin> source_origin = GetSourceOrigin();
+ DCHECK(source_origin);
+
+ if (source_origin->CanRequest(response.Url()))
+ return CORSStatus::kSameOrigin;
+
+ // RequestContext, FetchRequestMode and FetchCredentialsMode never change
+ // during the lifetime of a request.
+ const ResourceRequest& initial_request = resource_->GetResourceRequest();
+
+ if (resource_->Options().cors_handling_by_resource_fetcher !=
+ kEnableCORSHandlingByResourceFetcher ||
+ initial_request.GetFetchRequestMode() !=
+ network::mojom::FetchRequestMode::kCORS) {
+ return CORSStatus::kNotApplicable;
+ }
+
+ // Use the original response instead of the 304 response for a successful
+ // revalidation.
+ const ResourceResponse& response_for_access_control =
+ (resource_->IsCacheValidator() && response.HttpStatusCode() == 304)
+ ? resource_->GetResponse()
+ : response;
+
+ base::Optional<network::mojom::CORSError> cors_error = CORS::CheckAccess(
+ response_for_access_control.Url(),
+ response_for_access_control.HttpStatusCode(),
+ response_for_access_control.HttpHeaderFields(),
+ initial_request.GetFetchCredentialsMode(), *source_origin);
+
+ if (!cors_error)
+ return CORSStatus::kSuccessful;
+
+ String resource_type = Resource::ResourceTypeToString(
+ resource_->GetType(), resource_->Options().initiator_info.name);
+ error_msg.Append("Access to ");
+ error_msg.Append(resource_type);
+ error_msg.Append(" at '");
+ error_msg.Append(response.Url().GetString());
+ error_msg.Append("' from origin '");
+ error_msg.Append(source_origin->ToString());
+ error_msg.Append("' has been blocked by CORS policy: ");
+ error_msg.Append(CORS::GetErrorString(CORS::ErrorParameter::Create(
+ *cors_error, initial_request.Url(), KURL(),
+ response_for_access_control.HttpStatusCode(),
+ response_for_access_control.HttpHeaderFields(), *source_origin,
+ initial_request.GetRequestContext())));
+
+ return CORSStatus::kFailed;
+}
+
+void ResourceLoader::DidReceiveResponse(
+ const WebURLResponse& web_url_response,
+ std::unique_ptr<WebDataConsumerHandle> handle) {
+ DCHECK(!web_url_response.IsNull());
+
+ if (Context().IsDetached()) {
+ // If the fetch context is already detached, we don't need further signals,
+ // so let's cancel the request.
+ HandleError(ResourceError::CancelledError(web_url_response.Url()));
+ return;
+ }
+
+ Resource::Type resource_type = resource_->GetType();
+
+ const ResourceRequest& initial_request = resource_->GetResourceRequest();
+ // The following parameters never change during the lifetime of a request.
+ WebURLRequest::RequestContext request_context =
+ initial_request.GetRequestContext();
+ network::mojom::FetchRequestMode fetch_request_mode =
+ initial_request.GetFetchRequestMode();
+
+ const ResourceLoaderOptions& options = resource_->Options();
+
+ const ResourceResponse& response = web_url_response.ToResourceResponse();
+
+ // Later, CORS results should already be in the response we get from the
+ // browser at this point.
+ StringBuilder cors_error_msg;
+ resource_->SetCORSStatus(DetermineCORSStatus(response, cors_error_msg));
+
+ // Perform 'nosniff' checks against the original response instead of the 304
+ // response for a successful revalidation.
+ const ResourceResponse& nosniffed_response =
+ (resource_->IsCacheValidator() && response.HttpStatusCode() == 304)
+ ? resource_->GetResponse()
+ : response;
+ ResourceRequestBlockedReason blocked_reason =
+ Context().CheckResponseNosniff(request_context, nosniffed_response);
+ if (blocked_reason != ResourceRequestBlockedReason::kNone) {
+ HandleError(ResourceError::CancelledDueToAccessCheckError(response.Url(),
+ blocked_reason));
+ return;
+ }
+
+ if (response.WasFetchedViaServiceWorker()) {
+ if (options.cors_handling_by_resource_fetcher ==
+ kEnableCORSHandlingByResourceFetcher &&
+ fetch_request_mode == network::mojom::FetchRequestMode::kCORS &&
+ response.WasFallbackRequiredByServiceWorker()) {
+ ResourceRequest last_request = resource_->LastResourceRequest();
+ DCHECK(!last_request.GetSkipServiceWorker());
+ // This code handles the case when a controlling service worker doesn't
+ // handle a cross origin request.
+ if (!Context().ShouldLoadNewResource(resource_type)) {
+ // Cancel the request if we should not trigger a reload now.
+ HandleError(ResourceError::CancelledError(response.Url()));
+ return;
+ }
+ last_request.SetSkipServiceWorker(true);
+ Restart(last_request);
+ return;
+ }
+
+ // If the response is fetched via ServiceWorker, the original URL of the
+ // response could be different from the URL of the request. We check the URL
+ // not to load the resources which are forbidden by the page CSP.
+ // https://w3c.github.io/webappsec-csp/#should-block-response
+ const KURL& original_url = response.OriginalURLViaServiceWorker();
+ if (!original_url.IsEmpty()) {
+ // CanRequest() below only checks enforced policies: check report-only
+ // here to ensure violations are sent.
+ Context().CheckCSPForRequest(
+ request_context, original_url, options,
+ SecurityViolationReportingPolicy::kReport,
+ ResourceRequest::RedirectStatus::kFollowedRedirect);
+
+ ResourceRequestBlockedReason blocked_reason = Context().CanRequest(
+ resource_type, initial_request, original_url, options,
+ SecurityViolationReportingPolicy::kReport,
+ FetchParameters::kUseDefaultOriginRestrictionForType,
+ ResourceRequest::RedirectStatus::kFollowedRedirect);
+ if (blocked_reason != ResourceRequestBlockedReason::kNone) {
+ HandleError(ResourceError::CancelledDueToAccessCheckError(
+ original_url, blocked_reason));
+ return;
+ }
+ }
+ } else if (options.cors_handling_by_resource_fetcher ==
+ kEnableCORSHandlingByResourceFetcher &&
+ fetch_request_mode == network::mojom::FetchRequestMode::kCORS) {
+ if (!resource_->IsSameOriginOrCORSSuccessful()) {
+ if (!resource_->IsUnusedPreload())
+ Context().AddErrorConsoleMessage(cors_error_msg.ToString(),
+ FetchContext::kJSSource);
+
+ // Redirects can change the response URL different from one of request.
+ HandleError(ResourceError::CancelledDueToAccessCheckError(
+ response.Url(), ResourceRequestBlockedReason::kOther));
+ return;
+ }
+ }
+
+ // FrameType never changes during the lifetime of a request.
+ Context().DispatchDidReceiveResponse(
+ resource_->Identifier(), response, initial_request.GetFrameType(),
+ request_context, resource_,
+ FetchContext::ResourceResponseType::kNotFromMemoryCache);
+
+ resource_->ResponseReceived(response, std::move(handle));
+ if (!resource_->Loader())
+ return;
+
+ if (response.HttpStatusCode() >= 400 &&
+ !resource_->ShouldIgnoreHTTPStatusCodeErrors())
+ HandleError(ResourceError::CancelledError(response.Url()));
+}
+
+void ResourceLoader::DidReceiveResponse(const WebURLResponse& response) {
+ DidReceiveResponse(response, nullptr);
+}
+
+void ResourceLoader::DidStartLoadingResponseBody(
+ mojo::ScopedDataPipeConsumerHandle body) {
+ DCHECK(is_downloading_to_blob_);
+ DCHECK(!blob_response_started_);
+ blob_response_started_ = true;
+
+ const ResourceResponse& response = resource_->GetResponse();
+ AtomicString mime_type = response.MimeType();
+
+ mojom::blink::ProgressClientAssociatedPtrInfo progress_client_ptr;
+ progress_binding_.Bind(MakeRequest(&progress_client_ptr));
+
+ // Callback is bound to a WeakPersistent, as ResourceLoader is kept alive by
+ // ResourceFetcher as long as we still care about the result of the load.
+ mojom::blink::BlobRegistry* blob_registry = BlobDataHandle::GetBlobRegistry();
+ blob_registry->RegisterFromStream(
+ mime_type.IsNull() ? g_empty_string : mime_type.LowerASCII(), "",
+ std::max(0ll, response.ExpectedContentLength()), std::move(body),
+ std::move(progress_client_ptr),
+ WTF::Bind(&ResourceLoader::FinishedCreatingBlob,
+ WrapWeakPersistent(this)));
+}
+
+void ResourceLoader::DidDownloadData(int length, int encoded_data_length) {
+ Context().DispatchDidDownloadData(resource_->Identifier(), length,
+ encoded_data_length);
+ resource_->DidDownloadData(length);
+}
+
+void ResourceLoader::DidReceiveData(const char* data, int length) {
+ CHECK_GE(length, 0);
+
+ Context().DispatchDidReceiveData(resource_->Identifier(), data, length);
+ resource_->AppendData(data, length);
+}
+
+void ResourceLoader::DidReceiveTransferSizeUpdate(int transfer_size_diff) {
+ DCHECK_GT(transfer_size_diff, 0);
+ Context().DispatchDidReceiveEncodedData(resource_->Identifier(),
+ transfer_size_diff);
+}
+
+void ResourceLoader::DidFinishLoadingFirstPartInMultipart() {
+ network_instrumentation::EndResourceLoad(
+ resource_->Identifier(),
+ network_instrumentation::RequestOutcome::kSuccess);
+
+ fetcher_->HandleLoaderFinish(resource_.Get(), 0,
+ ResourceFetcher::kDidFinishFirstPartInMultipart,
+ 0, false);
+}
+
+void ResourceLoader::DidFinishLoading(double finish_time,
+ int64_t encoded_data_length,
+ int64_t encoded_body_length,
+ int64_t decoded_body_length,
+ bool blocked_cross_site_document) {
+ resource_->SetEncodedDataLength(encoded_data_length);
+ resource_->SetEncodedBodyLength(encoded_body_length);
+ resource_->SetDecodedBodyLength(decoded_body_length);
+
+ if (is_downloading_to_blob_ && !blob_finished_ && blob_response_started_) {
+ load_did_finish_before_blob_ =
+ DeferedFinishLoadingInfo{finish_time, blocked_cross_site_document};
+ return;
+ }
+
+ Release(ResourceLoadScheduler::ReleaseOption::kReleaseAndSchedule,
+ ResourceLoadScheduler::TrafficReportHints(encoded_data_length,
+ decoded_body_length));
+ loader_.reset();
+
+ network_instrumentation::EndResourceLoad(
+ resource_->Identifier(),
+ network_instrumentation::RequestOutcome::kSuccess);
+
+ fetcher_->HandleLoaderFinish(
+ resource_.Get(), finish_time, ResourceFetcher::kDidFinishLoading,
+ inflight_keepalive_bytes_, blocked_cross_site_document);
+}
+
+void ResourceLoader::DidFail(const WebURLError& error,
+ int64_t encoded_data_length,
+ int64_t encoded_body_length,
+ int64_t decoded_body_length) {
+ resource_->SetEncodedDataLength(encoded_data_length);
+ resource_->SetEncodedBodyLength(encoded_body_length);
+ resource_->SetDecodedBodyLength(decoded_body_length);
+ HandleError(error);
+}
+
+void ResourceLoader::HandleError(const ResourceError& error) {
+ if (is_cache_aware_loading_activated_ && error.IsCacheMiss() &&
+ Context().ShouldLoadNewResource(resource_->GetType())) {
+ resource_->WillReloadAfterDiskCacheMiss();
+ is_cache_aware_loading_activated_ = false;
+ Restart(resource_->GetResourceRequest());
+ return;
+ }
+
+ Release(ResourceLoadScheduler::ReleaseOption::kReleaseAndSchedule,
+ ResourceLoadScheduler::TrafficReportHints::InvalidInstance());
+ loader_.reset();
+
+ network_instrumentation::EndResourceLoad(
+ resource_->Identifier(), network_instrumentation::RequestOutcome::kFail);
+
+ fetcher_->HandleLoaderError(resource_.Get(), error,
+ inflight_keepalive_bytes_);
+}
+
+void ResourceLoader::RequestSynchronously(const ResourceRequest& request) {
+ DCHECK(loader_);
+ DCHECK_EQ(request.Priority(), ResourceLoadPriority::kHighest);
+
+ WrappedResourceRequest request_in(request);
+ WebURLResponse response_out;
+ WTF::Optional<WebURLError> error_out;
+ WebData data_out;
+ int64_t encoded_data_length = WebURLLoaderClient::kUnknownEncodedDataLength;
+ int64_t encoded_body_length = 0;
+ base::Optional<int64_t> downloaded_file_length;
+ WebBlobInfo downloaded_blob;
+ loader_->LoadSynchronously(request_in, response_out, error_out, data_out,
+ encoded_data_length, encoded_body_length,
+ downloaded_file_length, downloaded_blob);
+
+ // A message dispatched while synchronously fetching the resource
+ // can bring about the cancellation of this load.
+ if (!loader_)
+ return;
+ int64_t decoded_body_length = data_out.size();
+ if (error_out) {
+ DidFail(*error_out, encoded_data_length, encoded_body_length,
+ decoded_body_length);
+ return;
+ }
+ DidReceiveResponse(response_out);
+ if (!loader_)
+ return;
+ DCHECK_GE(response_out.ToResourceResponse().EncodedBodyLength(), 0);
+
+ // Follow the async case convention of not calling DidReceiveData or
+ // appending data to m_resource if the response body is empty. Copying the
+ // empty buffer is a noop in most cases, but is destructive in the case of
+ // a 304, where it will overwrite the cached data we should be reusing.
+ if (data_out.size()) {
+ data_out.ForEachSegment([this](const char* segment, size_t segment_size,
+ size_t segment_offset) {
+ Context().DispatchDidReceiveData(resource_->Identifier(), segment,
+ segment_size);
+ return true;
+ });
+ resource_->SetResourceBuffer(data_out);
+ }
+
+ if (downloaded_file_length) {
+ DCHECK(request.DownloadToFile());
+ DidDownloadData(*downloaded_file_length, encoded_body_length);
+ }
+ if (request.DownloadToBlob()) {
+ auto blob = downloaded_blob.GetBlobHandle();
+ if (blob) {
+ Context().DispatchDidReceiveData(resource_->Identifier(), nullptr,
+ blob->size());
+ resource_->DidDownloadData(blob->size());
+ }
+ Context().DispatchDidDownloadToBlob(resource_->Identifier(), blob.get());
+ resource_->DidDownloadToBlob(blob);
+ }
+ DidFinishLoading(CurrentTimeTicksInSeconds(), encoded_data_length,
+ encoded_body_length, decoded_body_length, false);
+}
+
+void ResourceLoader::Dispose() {
+ loader_ = nullptr;
+
+ // Release() should be called to release |scheduler_client_id_| beforehand in
+ // DidFinishLoading() or DidFail(), but when a timer to call Cancel() is
+ // ignored due to GC, this case happens. We just release here because we can
+ // not schedule another request safely. See crbug.com/675947.
+ if (scheduler_client_id_ != ResourceLoadScheduler::kInvalidClientId) {
+ Release(ResourceLoadScheduler::ReleaseOption::kReleaseOnly,
+ ResourceLoadScheduler::TrafficReportHints::InvalidInstance());
+ }
+}
+
+void ResourceLoader::ActivateCacheAwareLoadingIfNeeded(
+ const ResourceRequest& request) {
+ DCHECK(!is_cache_aware_loading_activated_);
+
+ if (resource_->Options().cache_aware_loading_enabled !=
+ kIsCacheAwareLoadingEnabled)
+ return;
+
+ // Synchronous requests are not supported.
+ if (resource_->Options().synchronous_policy == kRequestSynchronously)
+ return;
+
+ // Don't activate on Resource revalidation.
+ if (resource_->IsCacheValidator())
+ return;
+
+ // Don't activate if cache policy is explicitly set.
+ if (request.GetCacheMode() != mojom::FetchCacheMode::kDefault)
+ return;
+
+ // Don't activate if the page is controlled by service worker.
+ if (fetcher_->IsControlledByServiceWorker())
+ return;
+
+ is_cache_aware_loading_activated_ = true;
+}
+
+bool ResourceLoader::ShouldBeKeptAliveWhenDetached() const {
+ return resource_->GetResourceRequest().GetKeepalive() &&
+ resource_->GetResponse().IsNull();
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+ResourceLoader::GetLoadingTaskRunner() {
+ return Context().GetLoadingTaskRunner();
+}
+
+void ResourceLoader::OnProgress(uint64_t delta) {
+ DCHECK(!blob_finished_);
+
+ if (scheduler_client_id_ == ResourceLoadScheduler::kInvalidClientId)
+ return;
+
+ Context().DispatchDidReceiveData(resource_->Identifier(), nullptr, delta);
+ resource_->DidDownloadData(delta);
+}
+
+void ResourceLoader::FinishedCreatingBlob(
+ const scoped_refptr<BlobDataHandle>& blob) {
+ DCHECK(!blob_finished_);
+
+ if (scheduler_client_id_ == ResourceLoadScheduler::kInvalidClientId)
+ return;
+
+ Context().DispatchDidDownloadToBlob(resource_->Identifier(), blob.get());
+ resource_->DidDownloadToBlob(blob);
+
+ blob_finished_ = true;
+ if (load_did_finish_before_blob_) {
+ const ResourceResponse& response = resource_->GetResponse();
+ DidFinishLoading(load_did_finish_before_blob_->finish_time,
+ response.EncodedDataLength(), response.EncodedBodyLength(),
+ response.DecodedBodyLength(),
+ load_did_finish_before_blob_->blocked_cross_site_document);
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader.h
new file mode 100644
index 00000000000..cc8c44ea9a6
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader.h
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2005, 2006, 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOADER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOADER_H_
+
+#include <memory>
+#include "base/gtest_prod_util.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
+#include "third_party/blink/public/mojom/blob/blob_registry.mojom-blink.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/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/forward.h"
+
+namespace blink {
+
+class FetchContext;
+class ResourceError;
+class ResourceFetcher;
+
+// A ResourceLoader is created for each Resource by the ResourceFetcher when it
+// needs to load the specified resource. A ResourceLoader creates a
+// WebURLLoader and loads the resource using it. Any per-load logic should be
+// implemented in this class basically.
+class PLATFORM_EXPORT ResourceLoader final
+ : public GarbageCollectedFinalized<ResourceLoader>,
+ public ResourceLoadSchedulerClient,
+ protected WebURLLoaderClient,
+ protected mojom::blink::ProgressClient {
+ USING_GARBAGE_COLLECTED_MIXIN(ResourceLoader);
+ USING_PRE_FINALIZER(ResourceLoader, Dispose);
+
+ public:
+ static ResourceLoader* Create(ResourceFetcher*,
+ ResourceLoadScheduler*,
+ Resource*,
+ uint32_t inflight_keepalive_bytes = 0);
+ ~ResourceLoader() override;
+ void Trace(blink::Visitor*) override;
+
+ void Start();
+
+ void ScheduleCancel();
+ void Cancel();
+
+ void SetDefersLoading(bool);
+
+ void DidChangePriority(ResourceLoadPriority, int intra_priority_value);
+
+ // Called before start() to activate cache-aware loading if enabled in
+ // |m_resource->options()| and applicable.
+ void ActivateCacheAwareLoadingIfNeeded(const ResourceRequest&);
+
+ bool IsCacheAwareLoadingActivated() const {
+ return is_cache_aware_loading_activated_;
+ }
+
+ ResourceFetcher* Fetcher() { return fetcher_; }
+ bool ShouldBeKeptAliveWhenDetached() const;
+
+ // WebURLLoaderClient
+ //
+ // A succesful load will consist of:
+ // 0+ WillFollowRedirect()
+ // 0+ DidSendData()
+ // 1 DidReceiveResponse()
+ // 0-1 DidReceiveCachedMetadata()
+ // 0+ DidReceiveData() or DidDownloadData(), but never both
+ // 1 DidFinishLoading()
+ // A failed load is indicated by 1 DidFail(), which can occur at any time
+ // before DidFinishLoading(), including synchronous inside one of the other
+ // callbacks via ResourceLoader::cancel()
+ bool WillFollowRedirect(const WebURL& new_url,
+ const WebURL& new_site_for_cookies,
+ const WebString& new_referrer,
+ WebReferrerPolicy new_referrer_policy,
+ const WebString& new_method,
+ const WebURLResponse& passed_redirect_response,
+ bool& report_raw_headers) override;
+ void DidSendData(unsigned long long bytes_sent,
+ unsigned long long total_bytes_to_be_sent) override;
+ void DidReceiveResponse(const WebURLResponse&) override;
+ void DidReceiveResponse(const WebURLResponse&,
+ std::unique_ptr<WebDataConsumerHandle>) override;
+ void DidReceiveCachedMetadata(const char* data, int length) override;
+ void DidReceiveData(const char*, int) override;
+ void DidReceiveTransferSizeUpdate(int transfer_size_diff) override;
+ void DidStartLoadingResponseBody(
+ mojo::ScopedDataPipeConsumerHandle body) override;
+ void DidDownloadData(int, int) override;
+ void DidFinishLoading(double finish_time,
+ int64_t encoded_data_length,
+ int64_t encoded_body_length,
+ int64_t decoded_body_length,
+ bool blocked_cross_site_document) override;
+ void DidFail(const WebURLError&,
+ int64_t encoded_data_length,
+ int64_t encoded_body_length,
+ int64_t decoded_body_length) override;
+
+ void HandleError(const ResourceError&);
+
+ void DidFinishLoadingFirstPartInMultipart();
+
+ // ResourceLoadSchedulerClient.
+ void Run() override;
+
+ scoped_refptr<base::SingleThreadTaskRunner> GetLoadingTaskRunner();
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(ResourceLoaderTest, DetermineCORSStatus);
+
+ friend class SubresourceIntegrityTest;
+
+ // Assumes ResourceFetcher and Resource are non-null.
+ ResourceLoader(ResourceFetcher*,
+ ResourceLoadScheduler*,
+ Resource*,
+ uint32_t inflight_keepalive_bytes);
+
+ void StartWith(const ResourceRequest&);
+
+ void Release(ResourceLoadScheduler::ReleaseOption,
+ const ResourceLoadScheduler::TrafficReportHints&);
+
+ // This method is currently only used for service worker fallback request and
+ // cache-aware loading, other users should be careful not to break
+ // ResourceLoader state.
+ void Restart(const ResourceRequest&);
+
+ FetchContext& Context() const;
+ scoped_refptr<const SecurityOrigin> GetSourceOrigin() const;
+
+ CORSStatus DetermineCORSStatus(const ResourceResponse&, StringBuilder&) const;
+
+ void CancelForRedirectAccessCheckError(const KURL&,
+ ResourceRequestBlockedReason);
+ void RequestSynchronously(const ResourceRequest&);
+ void Dispose();
+
+ void CancelTimerFired(TimerBase*);
+
+ void OnProgress(uint64_t delta) override;
+ void FinishedCreatingBlob(const scoped_refptr<BlobDataHandle>&);
+
+ std::unique_ptr<WebURLLoader> loader_;
+ ResourceLoadScheduler::ClientId scheduler_client_id_;
+ Member<ResourceFetcher> fetcher_;
+ Member<ResourceLoadScheduler> scheduler_;
+ Member<Resource> resource_;
+
+ uint32_t inflight_keepalive_bytes_;
+ bool is_cache_aware_loading_activated_;
+
+ bool is_downloading_to_blob_ = false;
+ mojo::AssociatedBinding<mojom::blink::ProgressClient> progress_binding_;
+ bool blob_finished_ = false;
+ bool blob_response_started_ = false;
+ // If DidFinishLoading is called while downloading to a blob before the blob
+ // is finished, we might have to defer actually handling the event. This
+ // struct is used to store the information needed to refire DidFinishLoading
+ // when the blob is finished too.
+ struct DeferedFinishLoadingInfo {
+ double finish_time;
+ bool blocked_cross_site_document;
+ };
+ Optional<DeferedFinishLoadingInfo> load_did_finish_before_blob_;
+
+ TaskRunnerTimer<ResourceLoader> cancel_timer_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h
new file mode 100644
index 00000000000..0907407eedf
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOADER_OPTIONS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOADER_OPTIONS_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "services/network/public/mojom/url_loader_factory.mojom-blink.h"
+#include "third_party/blink/renderer/platform/cross_thread_copier.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_info.h"
+#include "third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+enum DataBufferingPolicy : uint8_t { kBufferData, kDoNotBufferData };
+
+enum ContentSecurityPolicyDisposition : uint8_t {
+ kCheckContentSecurityPolicy,
+ kDoNotCheckContentSecurityPolicy
+};
+
+enum RequestInitiatorContext : uint8_t {
+ kDocumentContext,
+ kWorkerContext,
+};
+
+enum SynchronousPolicy : uint8_t {
+ kRequestSynchronously,
+ kRequestAsynchronously
+};
+
+// Used by the DocumentThreadableLoader to turn off part of the CORS handling
+// logic in the ResourceFetcher to use its own CORS handling logic.
+enum CORSHandlingByResourceFetcher {
+ kDisableCORSHandlingByResourceFetcher,
+ kEnableCORSHandlingByResourceFetcher,
+};
+
+// Was the request generated from a "parser-inserted" element?
+// https://html.spec.whatwg.org/multipage/scripting.html#parser-inserted
+enum ParserDisposition : uint8_t { kParserInserted, kNotParserInserted };
+
+enum CacheAwareLoadingEnabled : uint8_t {
+ kNotCacheAwareLoadingEnabled,
+ kIsCacheAwareLoadingEnabled
+};
+
+struct ResourceLoaderOptions {
+ USING_FAST_MALLOC(ResourceLoaderOptions);
+
+ public:
+ ResourceLoaderOptions()
+ : data_buffering_policy(kBufferData),
+ content_security_policy_option(kCheckContentSecurityPolicy),
+ request_initiator_context(kDocumentContext),
+ synchronous_policy(kRequestAsynchronously),
+ cors_handling_by_resource_fetcher(kEnableCORSHandlingByResourceFetcher),
+ cors_flag(false),
+ parser_disposition(kParserInserted),
+ cache_aware_loading_enabled(kNotCacheAwareLoadingEnabled) {}
+
+ FetchInitiatorInfo initiator_info;
+
+ // ATTENTION: When adding members, update
+ // CrossThreadResourceLoaderOptionsData, too.
+
+ DataBufferingPolicy data_buffering_policy;
+
+ ContentSecurityPolicyDisposition content_security_policy_option;
+ RequestInitiatorContext request_initiator_context;
+ SynchronousPolicy synchronous_policy;
+
+ // When set to kDisableCORSHandlingByResourceFetcher, the ResourceFetcher
+ // suppresses part of its CORS handling logic.
+ // Used by DocumentThreadableLoader which does CORS handling by itself.
+ CORSHandlingByResourceFetcher cors_handling_by_resource_fetcher;
+
+ // Corresponds to the CORS flag in the Fetch spec.
+ bool cors_flag;
+
+ scoped_refptr<const SecurityOrigin> security_origin;
+ String content_security_policy_nonce;
+ IntegrityMetadataSet integrity_metadata;
+ ParserDisposition parser_disposition;
+ CacheAwareLoadingEnabled cache_aware_loading_enabled;
+
+ // If not null, this URLLoaderFactory should be used to load this resource
+ // rather than whatever factory the system might otherwise use.
+ // Used for example for loading blob: URLs and for prefetch loading.
+ scoped_refptr<
+ base::RefCountedData<network::mojom::blink::URLLoaderFactoryPtr>>
+ url_loader_factory;
+};
+
+// Encode AtomicString (in FetchInitiatorInfo) as String to cross threads.
+struct CrossThreadResourceLoaderOptionsData {
+ DISALLOW_NEW();
+ explicit CrossThreadResourceLoaderOptionsData(
+ const ResourceLoaderOptions& options)
+ : data_buffering_policy(options.data_buffering_policy),
+ content_security_policy_option(options.content_security_policy_option),
+ initiator_info(options.initiator_info),
+ request_initiator_context(options.request_initiator_context),
+ synchronous_policy(options.synchronous_policy),
+ cors_handling_by_resource_fetcher(
+ options.cors_handling_by_resource_fetcher),
+ cors_flag(options.cors_flag),
+ security_origin(options.security_origin
+ ? options.security_origin->IsolatedCopy()
+ : nullptr),
+ content_security_policy_nonce(
+ options.content_security_policy_nonce.IsolatedCopy()),
+ integrity_metadata(options.integrity_metadata),
+ parser_disposition(options.parser_disposition),
+ cache_aware_loading_enabled(options.cache_aware_loading_enabled) {
+ if (options.url_loader_factory) {
+ DCHECK(options.url_loader_factory->data.is_bound());
+ url_loader_factory = base::MakeRefCounted<base::RefCountedData<
+ network::mojom::blink::URLLoaderFactoryPtrInfo>>();
+ options.url_loader_factory->data->Clone(
+ MakeRequest(&url_loader_factory->data));
+ }
+ }
+
+ operator ResourceLoaderOptions() const {
+ ResourceLoaderOptions options;
+ options.data_buffering_policy = data_buffering_policy;
+ options.content_security_policy_option = content_security_policy_option;
+ options.initiator_info = initiator_info;
+ options.request_initiator_context = request_initiator_context;
+ options.synchronous_policy = synchronous_policy;
+ options.cors_handling_by_resource_fetcher =
+ cors_handling_by_resource_fetcher;
+ options.cors_flag = cors_flag;
+ options.security_origin = security_origin;
+ options.content_security_policy_nonce = content_security_policy_nonce;
+ options.integrity_metadata = integrity_metadata;
+ options.parser_disposition = parser_disposition;
+ options.cache_aware_loading_enabled = cache_aware_loading_enabled;
+ if (url_loader_factory) {
+ DCHECK(url_loader_factory->data.is_valid());
+ options.url_loader_factory = base::MakeRefCounted<
+ base::RefCountedData<network::mojom::blink::URLLoaderFactoryPtr>>(
+ network::mojom::blink::URLLoaderFactoryPtr(
+ std::move(url_loader_factory->data)));
+ }
+ return options;
+ }
+
+ DataBufferingPolicy data_buffering_policy;
+ ContentSecurityPolicyDisposition content_security_policy_option;
+ CrossThreadFetchInitiatorInfoData initiator_info;
+ RequestInitiatorContext request_initiator_context;
+ SynchronousPolicy synchronous_policy;
+
+ CORSHandlingByResourceFetcher cors_handling_by_resource_fetcher;
+ bool cors_flag;
+ scoped_refptr<const SecurityOrigin> security_origin;
+
+ String content_security_policy_nonce;
+ IntegrityMetadataSet integrity_metadata;
+ ParserDisposition parser_disposition;
+ CacheAwareLoadingEnabled cache_aware_loading_enabled;
+ scoped_refptr<
+ base::RefCountedData<network::mojom::blink::URLLoaderFactoryPtrInfo>>
+ url_loader_factory;
+};
+
+template <>
+struct CrossThreadCopier<ResourceLoaderOptions> {
+ using Type = CrossThreadResourceLoaderOptionsData;
+ static Type Copy(const ResourceLoaderOptions& options) {
+ return CrossThreadResourceLoaderOptionsData(options);
+ }
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOADER_OPTIONS_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_options_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_options_test.cc
new file mode 100644
index 00000000000..9fd9edd4160
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_options_test.cc
@@ -0,0 +1,108 @@
+// Copyright 2014 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/platform/loader/fetch/resource_loader_options.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include <type_traits>
+
+namespace blink {
+
+namespace {
+
+TEST(ResourceLoaderOptionsTest, DeepCopy) {
+ // Check that the fields of ResourceLoaderOptions are enums, except for
+ // initiatorInfo and securityOrigin.
+ static_assert(std::is_enum<DataBufferingPolicy>::value,
+ "DataBufferingPolicy should be an enum");
+ static_assert(std::is_enum<ContentSecurityPolicyDisposition>::value,
+ "ContentSecurityPolicyDisposition should be an enum");
+ static_assert(std::is_enum<RequestInitiatorContext>::value,
+ "RequestInitiatorContext should be an enum");
+ static_assert(std::is_enum<SynchronousPolicy>::value,
+ "SynchronousPolicy should be an enum");
+ static_assert(std::is_enum<CORSHandlingByResourceFetcher>::value,
+ "CORSHandlingByResourceFetcher should be an enum");
+
+ ResourceLoaderOptions original;
+ scoped_refptr<const SecurityOrigin> security_origin =
+ SecurityOrigin::CreateFromString("http://www.google.com");
+ original.security_origin = security_origin;
+ original.initiator_info.name = AtomicString("xmlhttprequest");
+
+ CrossThreadResourceLoaderOptionsData copy_data =
+ CrossThreadCopier<ResourceLoaderOptions>::Copy(original);
+ ResourceLoaderOptions copy = copy_data;
+
+ // Check that contents are correctly copied to |copyData|
+ EXPECT_EQ(original.data_buffering_policy, copy_data.data_buffering_policy);
+ EXPECT_EQ(original.content_security_policy_option,
+ copy_data.content_security_policy_option);
+ EXPECT_EQ(original.initiator_info.name, copy_data.initiator_info.name);
+ EXPECT_EQ(original.initiator_info.position,
+ copy_data.initiator_info.position);
+ EXPECT_EQ(original.initiator_info.start_time,
+ copy_data.initiator_info.start_time);
+ EXPECT_EQ(original.request_initiator_context,
+ copy_data.request_initiator_context);
+ EXPECT_EQ(original.synchronous_policy, copy_data.synchronous_policy);
+ EXPECT_EQ(original.cors_handling_by_resource_fetcher,
+ copy_data.cors_handling_by_resource_fetcher);
+ EXPECT_EQ(original.security_origin->Protocol(),
+ copy_data.security_origin->Protocol());
+ EXPECT_EQ(original.security_origin->Host(),
+ copy_data.security_origin->Host());
+ EXPECT_EQ(original.security_origin->Domain(),
+ copy_data.security_origin->Domain());
+
+ // Check that pointers are different between |original| and |copyData|
+ EXPECT_NE(original.initiator_info.name.Impl(),
+ copy_data.initiator_info.name.Impl());
+ EXPECT_NE(original.security_origin.get(), copy_data.security_origin.get());
+ EXPECT_NE(original.security_origin->Protocol().Impl(),
+ copy_data.security_origin->Protocol().Impl());
+ EXPECT_NE(original.security_origin->Host().Impl(),
+ copy_data.security_origin->Host().Impl());
+ EXPECT_NE(original.security_origin->Domain().Impl(),
+ copy_data.security_origin->Domain().Impl());
+
+ // Check that contents are correctly copied to |copy|
+ EXPECT_EQ(original.data_buffering_policy, copy.data_buffering_policy);
+ EXPECT_EQ(original.content_security_policy_option,
+ copy.content_security_policy_option);
+ EXPECT_EQ(original.initiator_info.name, copy.initiator_info.name);
+ EXPECT_EQ(original.initiator_info.position, copy.initiator_info.position);
+ EXPECT_EQ(original.initiator_info.start_time, copy.initiator_info.start_time);
+ EXPECT_EQ(original.request_initiator_context, copy.request_initiator_context);
+ EXPECT_EQ(original.synchronous_policy, copy.synchronous_policy);
+ EXPECT_EQ(original.cors_handling_by_resource_fetcher,
+ copy.cors_handling_by_resource_fetcher);
+ EXPECT_EQ(original.security_origin->Protocol(),
+ copy.security_origin->Protocol());
+ EXPECT_EQ(original.security_origin->Host(), copy.security_origin->Host());
+ EXPECT_EQ(original.security_origin->Domain(), copy.security_origin->Domain());
+
+ // Check that pointers are different between |original| and |copy|
+ // FIXME: When |original| and |copy| are in different threads, then
+ // EXPECT_NE(original.initiatorInfo.name.impl(),
+ // copy.initiatorInfo.name.impl());
+ // should pass. However, in the unit test here, these two pointers are the
+ // same, because initiatorInfo.name is AtomicString.
+ EXPECT_NE(original.security_origin.get(), copy.security_origin.get());
+ EXPECT_NE(original.security_origin->Protocol().Impl(),
+ copy.security_origin->Protocol().Impl());
+ EXPECT_NE(original.security_origin->Host().Impl(),
+ copy.security_origin->Host().Impl());
+ EXPECT_NE(original.security_origin->Domain().Impl(),
+ copy.security_origin->Domain().Impl());
+
+ // FIXME: The checks for content equality/pointer inequality for
+ // securityOrigin here is not complete (i.e. m_filePath is not checked). A
+ // unit test for SecurityOrigin::isolatedCopy() that covers these checks
+ // should be added.
+}
+
+} // namespace
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc
new file mode 100644
index 00000000000..0336dc93bc3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_test.cc
@@ -0,0 +1,155 @@
+// Copyright 2017 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/platform/loader/fetch/raw_resource.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+#include "third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+class ResourceLoaderTest : public testing::Test {
+ DISALLOW_COPY_AND_ASSIGN(ResourceLoaderTest);
+
+ public:
+ ResourceLoaderTest()
+ : foo_url_("https://foo.test"), bar_url_("https://bar.test"){};
+
+ void SetUp() override {
+ context_ =
+ MockFetchContext::Create(MockFetchContext::kShouldLoadNewResource);
+ }
+
+ protected:
+ enum ServiceWorkerMode { kNoSW, kSWOpaque, kSWClear };
+
+ struct TestCase {
+ const KURL& origin;
+ const KURL& target;
+ const KURL* allow_origin_url;
+ const ServiceWorkerMode service_worker;
+ const Resource::Type resource_type;
+ const CORSStatus expectation;
+ };
+
+ Persistent<MockFetchContext> context_;
+
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform_;
+
+ const KURL foo_url_;
+ const KURL bar_url_;
+};
+
+TEST_F(ResourceLoaderTest, DetermineCORSStatus) {
+ TestCase cases[] = {
+ // No CORS status for main resources:
+ {foo_url_, foo_url_, nullptr, kNoSW, Resource::Type::kMainResource,
+ CORSStatus::kNotApplicable},
+
+ // Same origin:
+ {foo_url_, foo_url_, nullptr, kNoSW, Resource::Type::kRaw,
+ CORSStatus::kSameOrigin},
+
+ // Cross origin CORS successful:
+ {foo_url_, bar_url_, &foo_url_, kNoSW, Resource::Type::kRaw,
+ CORSStatus::kSuccessful},
+
+ // Cross origin not in CORS mode:
+ {foo_url_, bar_url_, nullptr, kNoSW, Resource::Type::kRaw,
+ CORSStatus::kNotApplicable},
+
+ // Cross origin CORS failed:
+ {foo_url_, bar_url_, &bar_url_, kNoSW, Resource::Type::kRaw,
+ CORSStatus::kFailed},
+
+ // CORS handled by service worker
+ {foo_url_, foo_url_, nullptr, kSWClear, Resource::Type::kRaw,
+ CORSStatus::kServiceWorkerSuccessful},
+ {foo_url_, foo_url_, &foo_url_, kSWClear, Resource::Type::kRaw,
+ CORSStatus::kServiceWorkerSuccessful},
+ {foo_url_, bar_url_, nullptr, kSWClear, Resource::Type::kRaw,
+ CORSStatus::kServiceWorkerSuccessful},
+ {foo_url_, bar_url_, &foo_url_, kSWClear, Resource::Type::kRaw,
+ CORSStatus::kServiceWorkerSuccessful},
+
+ // Opaque response by service worker
+ {foo_url_, foo_url_, nullptr, kSWOpaque, Resource::Type::kRaw,
+ CORSStatus::kServiceWorkerOpaque},
+ {foo_url_, bar_url_, nullptr, kSWOpaque, Resource::Type::kRaw,
+ CORSStatus::kServiceWorkerOpaque},
+ {foo_url_, bar_url_, &foo_url_, kSWOpaque, Resource::Type::kRaw,
+ CORSStatus::kServiceWorkerOpaque},
+ };
+
+ ResourceLoadScheduler* scheduler = ResourceLoadScheduler::Create();
+
+ for (const auto& test : cases) {
+ SCOPED_TRACE(testing::Message()
+ << "Origin: " << test.origin.GetString()
+ << ", target: " << test.target.GetString()
+ << ", CORS access-control-allow-origin header: "
+ << (test.allow_origin_url ? test.allow_origin_url->GetString()
+ : "-")
+ << ", service worker: "
+ << (test.service_worker == kNoSW
+ ? "no"
+ : (test.service_worker == kSWClear
+ ? "clear response"
+ : "opaque response"))
+ << ", expected CORSStatus == "
+ << static_cast<unsigned>(test.expectation));
+
+ context_->SetSecurityOrigin(SecurityOrigin::Create(test.origin));
+ ResourceFetcher* fetcher = ResourceFetcher::Create(context_);
+
+ Resource* resource =
+ RawResource::CreateForTest(test.target, test.resource_type);
+ ResourceLoader* loader =
+ ResourceLoader::Create(fetcher, scheduler, resource);
+
+ ResourceRequest request;
+ request.SetURL(test.target);
+
+ ResourceResponse response(test.target);
+ response.SetHTTPStatusCode(200);
+
+ if (test.allow_origin_url) {
+ request.SetFetchRequestMode(network::mojom::FetchRequestMode::kCORS);
+ resource->MutableOptions().cors_handling_by_resource_fetcher =
+ kEnableCORSHandlingByResourceFetcher;
+ response.SetHTTPHeaderField(
+ "access-control-allow-origin",
+ SecurityOrigin::Create(*test.allow_origin_url)->ToAtomicString());
+ response.SetHTTPHeaderField("access-control-allow-credentials", "true");
+ }
+
+ resource->SetResourceRequest(request);
+
+ if (test.service_worker != kNoSW) {
+ response.SetWasFetchedViaServiceWorker(true);
+
+ if (test.service_worker == kSWOpaque) {
+ response.SetResponseTypeViaServiceWorker(
+ network::mojom::FetchResponseType::kOpaque);
+ } else {
+ response.SetResponseTypeViaServiceWorker(
+ network::mojom::FetchResponseType::kDefault);
+ }
+ }
+
+ StringBuilder cors_error_msg;
+ CORSStatus cors_status =
+ loader->DetermineCORSStatus(response, cors_error_msg);
+
+ EXPECT_EQ(cors_status, test.expectation);
+ }
+}
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loading_log.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loading_log.h
new file mode 100644
index 00000000000..62435966a4b
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loading_log.h
@@ -0,0 +1,21 @@
+// Copyright 2016 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_PLATFORM_LOADER_FETCH_RESOURCE_LOADING_LOG_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOADING_LOG_H_
+
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+#if DCHECK_IS_ON()
+// We can see logs with |--v=N| or |--vmodule=ResourceLoadingLog=N| where N is a
+// verbose level.
+#define RESOURCE_LOADING_DVLOG(verbose_level) \
+ LAZY_STREAM( \
+ VLOG_STREAM(verbose_level), \
+ ((verbose_level) <= ::logging::GetVlogLevel("ResourceLoadingLog.h")))
+#else
+#define RESOURCE_LOADING_DVLOG(verbose_level) EAT_STREAM_PARAMETERS
+#endif
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOADING_LOG_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_priority.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_priority.h
new file mode 100644
index 00000000000..7b4ecebf224
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_priority.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_PRIORITY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_PRIORITY_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+struct ResourcePriority final {
+ STACK_ALLOCATED();
+
+ public:
+ enum VisibilityStatus {
+ kNotVisible,
+ kVisible,
+ };
+
+ ResourcePriority() : ResourcePriority(kNotVisible, 0) {}
+ ResourcePriority(VisibilityStatus status, int intra_value)
+ : visibility(status), intra_priority_value(intra_value) {}
+
+ VisibilityStatus visibility;
+ int intra_priority_value;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request.cc
new file mode 100644
index 00000000000..fab47ed61e5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request.cc
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2009, 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+
+#include <memory>
+
+#include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/renderer/platform/network/http_names.h"
+#include "third_party/blink/renderer/platform/network/network_utils.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+
+namespace blink {
+
+double ResourceRequest::default_timeout_interval_ = INT_MAX;
+
+ResourceRequest::ResourceRequest() : ResourceRequest(NullURL()) {}
+
+ResourceRequest::ResourceRequest(const String& url_string)
+ : ResourceRequest(KURL(url_string)) {}
+
+ResourceRequest::ResourceRequest(const KURL& url)
+ : url_(url),
+ timeout_interval_(default_timeout_interval_),
+ requestor_origin_(nullptr),
+ http_method_(HTTPNames::GET),
+ allow_stored_credentials_(true),
+ report_upload_progress_(false),
+ report_raw_headers_(false),
+ has_user_gesture_(false),
+ download_to_file_(false),
+ download_to_blob_(false),
+ use_stream_on_response_(false),
+ keepalive_(false),
+ should_reset_app_cache_(false),
+ cache_mode_(mojom::FetchCacheMode::kDefault),
+ skip_service_worker_(false),
+ priority_(ResourceLoadPriority::kLowest),
+ intra_priority_value_(0),
+ requestor_id_(0),
+ plugin_child_id_(-1),
+ app_cache_host_id_(0),
+ previews_state_(WebURLRequest::kPreviewsUnspecified),
+ request_context_(WebURLRequest::kRequestContextUnspecified),
+ frame_type_(network::mojom::RequestContextFrameType::kNone),
+ fetch_request_mode_(network::mojom::FetchRequestMode::kNoCORS),
+ fetch_credentials_mode_(network::mojom::FetchCredentialsMode::kInclude),
+ fetch_redirect_mode_(network::mojom::FetchRedirectMode::kFollow),
+ referrer_policy_(kReferrerPolicyDefault),
+ did_set_http_referrer_(false),
+ check_for_browser_side_navigation_(true),
+ was_discarded_(false),
+ ui_start_time_(0),
+ is_external_request_(false),
+ cors_preflight_policy_(
+ network::mojom::CORSPreflightPolicy::kConsiderPreflight),
+ is_same_document_navigation_(false),
+ input_perf_metric_report_policy_(
+ InputToLoadPerfMetricReportPolicy::kNoReport),
+ redirect_status_(RedirectStatus::kNoRedirect) {}
+
+ResourceRequest::ResourceRequest(CrossThreadResourceRequestData* data)
+ : ResourceRequest(data->url_) {
+ SetTimeoutInterval(data->timeout_interval_);
+ SetSiteForCookies(data->site_for_cookies_);
+ SetRequestorOrigin(data->requestor_origin_);
+ SetHTTPMethod(AtomicString(data->http_method_));
+ SetPriority(data->priority_, data->intra_priority_value_);
+
+ http_header_fields_.Adopt(std::move(data->http_headers_));
+
+ SetHTTPBody(data->http_body_);
+ SetAllowStoredCredentials(data->allow_stored_credentials_);
+ SetReportUploadProgress(data->report_upload_progress_);
+ SetHasUserGesture(data->has_user_gesture_);
+ SetDownloadToFile(data->download_to_file_);
+ SetDownloadToBlob(data->download_to_blob_);
+ SetUseStreamOnResponse(data->use_stream_on_response_);
+ SetKeepalive(data->keepalive_);
+ SetCacheMode(data->cache_mode_);
+ SetSkipServiceWorker(data->skip_service_worker_);
+ SetShouldResetAppCache(data->should_reset_app_cache_);
+ SetRequestorID(data->requestor_id_);
+ SetPluginChildID(data->plugin_child_id_);
+ SetAppCacheHostID(data->app_cache_host_id_);
+ SetPreviewsState(data->previews_state_);
+ SetRequestContext(data->request_context_);
+ SetFrameType(data->frame_type_);
+ SetFetchRequestMode(data->fetch_request_mode_);
+ SetFetchCredentialsMode(data->fetch_credentials_mode_);
+ SetFetchRedirectMode(data->fetch_redirect_mode_);
+ SetFetchIntegrity(data->fetch_integrity_.IsolatedCopy());
+ referrer_policy_ = data->referrer_policy_;
+ did_set_http_referrer_ = data->did_set_http_referrer_;
+ check_for_browser_side_navigation_ = data->check_for_browser_side_navigation_;
+ ui_start_time_ = data->ui_start_time_;
+ is_external_request_ = data->is_external_request_;
+ cors_preflight_policy_ = data->cors_preflight_policy_;
+ input_perf_metric_report_policy_ = data->input_perf_metric_report_policy_;
+ redirect_status_ = data->redirect_status_;
+ suggested_filename_ = data->suggested_filename_;
+ is_ad_resource_ = data->is_ad_resource_;
+}
+
+ResourceRequest::ResourceRequest(const ResourceRequest&) = default;
+
+ResourceRequest& ResourceRequest::operator=(const ResourceRequest&) = default;
+
+std::unique_ptr<ResourceRequest> ResourceRequest::CreateRedirectRequest(
+ const KURL& new_url,
+ const AtomicString& new_method,
+ const KURL& new_site_for_cookies,
+ const String& new_referrer,
+ ReferrerPolicy new_referrer_policy,
+ bool skip_service_worker) const {
+ std::unique_ptr<ResourceRequest> request =
+ std::make_unique<ResourceRequest>(new_url);
+ request->SetHTTPMethod(new_method);
+ request->SetSiteForCookies(new_site_for_cookies);
+ String referrer =
+ new_referrer.IsEmpty() ? Referrer::NoReferrer() : String(new_referrer);
+ request->SetHTTPReferrer(
+ Referrer(referrer, static_cast<ReferrerPolicy>(new_referrer_policy)));
+ request->SetSkipServiceWorker(skip_service_worker);
+ request->SetRedirectStatus(RedirectStatus::kFollowedRedirect);
+
+ // Copy from parameters for |this|.
+ request->SetDownloadToFile(DownloadToFile());
+ request->SetDownloadToBlob(DownloadToBlob());
+ request->SetUseStreamOnResponse(UseStreamOnResponse());
+ request->SetRequestContext(GetRequestContext());
+ request->SetFrameType(GetFrameType());
+ request->SetShouldResetAppCache(ShouldResetAppCache());
+ request->SetFetchRequestMode(GetFetchRequestMode());
+ request->SetFetchCredentialsMode(GetFetchCredentialsMode());
+ request->SetKeepalive(GetKeepalive());
+ request->SetPriority(Priority());
+
+ if (request->HttpMethod() == HttpMethod())
+ request->SetHTTPBody(HttpBody());
+ request->SetCheckForBrowserSideNavigation(CheckForBrowserSideNavigation());
+ request->SetWasDiscarded(WasDiscarded());
+ request->SetCORSPreflightPolicy(CORSPreflightPolicy());
+ if (IsAdResource())
+ request->SetIsAdResource();
+
+ return request;
+}
+
+std::unique_ptr<CrossThreadResourceRequestData> ResourceRequest::CopyData()
+ const {
+ std::unique_ptr<CrossThreadResourceRequestData> data =
+ std::make_unique<CrossThreadResourceRequestData>();
+ data->url_ = Url().Copy();
+ data->timeout_interval_ = TimeoutInterval();
+ data->site_for_cookies_ = SiteForCookies().Copy();
+ data->requestor_origin_ =
+ RequestorOrigin() ? RequestorOrigin()->IsolatedCopy() : nullptr;
+ data->http_method_ = HttpMethod().GetString().IsolatedCopy();
+ data->http_headers_ = HttpHeaderFields().CopyData();
+ data->priority_ = Priority();
+ data->intra_priority_value_ = intra_priority_value_;
+
+ if (http_body_)
+ data->http_body_ = http_body_->DeepCopy();
+ data->allow_stored_credentials_ = allow_stored_credentials_;
+ data->report_upload_progress_ = report_upload_progress_;
+ data->has_user_gesture_ = has_user_gesture_;
+ data->download_to_file_ = download_to_file_;
+ data->download_to_blob_ = download_to_blob_;
+ data->use_stream_on_response_ = use_stream_on_response_;
+ data->keepalive_ = keepalive_;
+ data->cache_mode_ = GetCacheMode();
+ data->skip_service_worker_ = skip_service_worker_;
+ data->should_reset_app_cache_ = should_reset_app_cache_;
+ data->requestor_id_ = requestor_id_;
+ data->plugin_child_id_ = plugin_child_id_;
+ data->app_cache_host_id_ = app_cache_host_id_;
+ data->previews_state_ = previews_state_;
+ data->request_context_ = request_context_;
+ data->frame_type_ = frame_type_;
+ data->fetch_request_mode_ = fetch_request_mode_;
+ data->fetch_credentials_mode_ = fetch_credentials_mode_;
+ data->fetch_redirect_mode_ = fetch_redirect_mode_;
+ data->fetch_integrity_ = fetch_integrity_.IsolatedCopy();
+ data->referrer_policy_ = referrer_policy_;
+ data->did_set_http_referrer_ = did_set_http_referrer_;
+ data->check_for_browser_side_navigation_ = check_for_browser_side_navigation_;
+ data->ui_start_time_ = ui_start_time_;
+ data->is_external_request_ = is_external_request_;
+ data->cors_preflight_policy_ = cors_preflight_policy_;
+ data->input_perf_metric_report_policy_ = input_perf_metric_report_policy_;
+ data->redirect_status_ = redirect_status_;
+ data->suggested_filename_ = suggested_filename_;
+ data->is_ad_resource_ = is_ad_resource_;
+ return data;
+}
+
+bool ResourceRequest::IsNull() const {
+ return url_.IsNull();
+}
+
+const KURL& ResourceRequest::Url() const {
+ return url_;
+}
+
+void ResourceRequest::SetURL(const KURL& url) {
+ url_ = url;
+}
+
+void ResourceRequest::RemoveUserAndPassFromURL() {
+ if (url_.User().IsEmpty() && url_.Pass().IsEmpty())
+ return;
+
+ url_.SetUser(String());
+ url_.SetPass(String());
+}
+
+mojom::FetchCacheMode ResourceRequest::GetCacheMode() const {
+ return cache_mode_;
+}
+
+void ResourceRequest::SetCacheMode(mojom::FetchCacheMode cache_mode) {
+ cache_mode_ = cache_mode;
+}
+
+double ResourceRequest::TimeoutInterval() const {
+ return timeout_interval_;
+}
+
+void ResourceRequest::SetTimeoutInterval(double timout_interval_seconds) {
+ timeout_interval_ = timout_interval_seconds;
+}
+
+const KURL& ResourceRequest::SiteForCookies() const {
+ return site_for_cookies_;
+}
+
+void ResourceRequest::SetSiteForCookies(const KURL& site_for_cookies) {
+ site_for_cookies_ = site_for_cookies;
+}
+
+scoped_refptr<const SecurityOrigin> ResourceRequest::RequestorOrigin() const {
+ return requestor_origin_;
+}
+
+void ResourceRequest::SetRequestorOrigin(
+ scoped_refptr<const SecurityOrigin> requestor_origin) {
+ requestor_origin_ = std::move(requestor_origin);
+}
+
+const AtomicString& ResourceRequest::HttpMethod() const {
+ return http_method_;
+}
+
+void ResourceRequest::SetHTTPMethod(const AtomicString& http_method) {
+ http_method_ = http_method;
+}
+
+const HTTPHeaderMap& ResourceRequest::HttpHeaderFields() const {
+ return http_header_fields_;
+}
+
+const AtomicString& ResourceRequest::HttpHeaderField(
+ const AtomicString& name) const {
+ return http_header_fields_.Get(name);
+}
+
+void ResourceRequest::SetHTTPHeaderField(const AtomicString& name,
+ const AtomicString& value) {
+ http_header_fields_.Set(name, value);
+}
+
+void ResourceRequest::SetHTTPReferrer(const Referrer& referrer) {
+ if (referrer.referrer.IsEmpty())
+ http_header_fields_.Remove(HTTPNames::Referer);
+ else
+ SetHTTPHeaderField(HTTPNames::Referer, referrer.referrer);
+ referrer_policy_ = referrer.referrer_policy;
+ did_set_http_referrer_ = true;
+}
+
+void ResourceRequest::ClearHTTPReferrer() {
+ http_header_fields_.Remove(HTTPNames::Referer);
+ referrer_policy_ = kReferrerPolicyDefault;
+ did_set_http_referrer_ = false;
+}
+
+void ResourceRequest::SetHTTPOrigin(const SecurityOrigin* origin) {
+ SetHTTPHeaderField(HTTPNames::Origin, origin->ToAtomicString());
+}
+
+void ResourceRequest::ClearHTTPOrigin() {
+ http_header_fields_.Remove(HTTPNames::Origin);
+}
+
+void ResourceRequest::SetHTTPOriginIfNeeded(const SecurityOrigin* origin) {
+ if (NeedsHTTPOrigin())
+ SetHTTPOrigin(origin);
+}
+
+void ResourceRequest::SetHTTPOriginToMatchReferrerIfNeeded() {
+ if (NeedsHTTPOrigin()) {
+ SetHTTPOrigin(
+ SecurityOrigin::CreateFromString(HttpHeaderField(HTTPNames::Referer))
+ .get());
+ }
+}
+
+void ResourceRequest::ClearHTTPUserAgent() {
+ http_header_fields_.Remove(HTTPNames::User_Agent);
+}
+
+EncodedFormData* ResourceRequest::HttpBody() const {
+ return http_body_.get();
+}
+
+void ResourceRequest::SetHTTPBody(scoped_refptr<EncodedFormData> http_body) {
+ http_body_ = std::move(http_body);
+}
+
+bool ResourceRequest::AllowStoredCredentials() const {
+ return allow_stored_credentials_;
+}
+
+void ResourceRequest::SetAllowStoredCredentials(bool allow_credentials) {
+ allow_stored_credentials_ = allow_credentials;
+}
+
+ResourceLoadPriority ResourceRequest::Priority() const {
+ return priority_;
+}
+
+int ResourceRequest::IntraPriorityValue() const {
+ return intra_priority_value_;
+}
+
+void ResourceRequest::SetPriority(ResourceLoadPriority priority,
+ int intra_priority_value) {
+ priority_ = priority;
+ intra_priority_value_ = intra_priority_value;
+}
+
+void ResourceRequest::AddHTTPHeaderField(const AtomicString& name,
+ const AtomicString& value) {
+ HTTPHeaderMap::AddResult result = http_header_fields_.Add(name, value);
+ if (!result.is_new_entry)
+ result.stored_value->value = result.stored_value->value + ", " + value;
+}
+
+void ResourceRequest::AddHTTPHeaderFields(const HTTPHeaderMap& header_fields) {
+ HTTPHeaderMap::const_iterator end = header_fields.end();
+ for (HTTPHeaderMap::const_iterator it = header_fields.begin(); it != end;
+ ++it)
+ AddHTTPHeaderField(it->key, it->value);
+}
+
+void ResourceRequest::ClearHTTPHeaderField(const AtomicString& name) {
+ http_header_fields_.Remove(name);
+}
+
+void ResourceRequest::SetExternalRequestStateFromRequestorAddressSpace(
+ mojom::IPAddressSpace requestor_space) {
+ static_assert(mojom::IPAddressSpace::kLocal < mojom::IPAddressSpace::kPrivate,
+ "Local is inside Private");
+ static_assert(mojom::IPAddressSpace::kLocal < mojom::IPAddressSpace::kPublic,
+ "Local is inside Public");
+ static_assert(
+ mojom::IPAddressSpace::kPrivate < mojom::IPAddressSpace::kPublic,
+ "Private is inside Public");
+
+ // TODO(mkwst): This only checks explicit IP addresses. We'll have to move all
+ // this up to //net and //content in order to have any real impact on gateway
+ // attacks. That turns out to be a TON of work. https://crbug.com/378566
+ if (!RuntimeEnabledFeatures::CorsRFC1918Enabled()) {
+ is_external_request_ = false;
+ return;
+ }
+
+ mojom::IPAddressSpace target_space = mojom::IPAddressSpace::kPublic;
+ if (NetworkUtils::IsReservedIPAddress(url_.Host()))
+ target_space = mojom::IPAddressSpace::kPrivate;
+ if (SecurityOrigin::Create(url_)->IsLocalhost())
+ target_space = mojom::IPAddressSpace::kLocal;
+
+ is_external_request_ = requestor_space > target_space;
+}
+
+void ResourceRequest::SetNavigationStartTime(double navigation_start) {
+ navigation_start_ = navigation_start;
+}
+
+bool ResourceRequest::IsConditional() const {
+ return (http_header_fields_.Contains(HTTPNames::If_Match) ||
+ http_header_fields_.Contains(HTTPNames::If_Modified_Since) ||
+ http_header_fields_.Contains(HTTPNames::If_None_Match) ||
+ http_header_fields_.Contains(HTTPNames::If_Range) ||
+ http_header_fields_.Contains(HTTPNames::If_Unmodified_Since));
+}
+
+void ResourceRequest::SetHasUserGesture(bool has_user_gesture) {
+ has_user_gesture_ |= has_user_gesture;
+}
+
+const CacheControlHeader& ResourceRequest::GetCacheControlHeader() const {
+ if (!cache_control_header_cache_.parsed) {
+ cache_control_header_cache_ = ParseCacheControlDirectives(
+ http_header_fields_.Get(HTTPNames::Cache_Control),
+ http_header_fields_.Get(HTTPNames::Pragma));
+ }
+ return cache_control_header_cache_;
+}
+
+bool ResourceRequest::CacheControlContainsNoCache() const {
+ return GetCacheControlHeader().contains_no_cache;
+}
+
+bool ResourceRequest::CacheControlContainsNoStore() const {
+ return GetCacheControlHeader().contains_no_store;
+}
+
+bool ResourceRequest::HasCacheValidatorFields() const {
+ return !http_header_fields_.Get(HTTPNames::Last_Modified).IsEmpty() ||
+ !http_header_fields_.Get(HTTPNames::ETag).IsEmpty();
+}
+
+bool ResourceRequest::NeedsHTTPOrigin() const {
+ if (!HttpOrigin().IsEmpty())
+ return false; // Request already has an Origin header.
+
+ // Don't send an Origin header for GET or HEAD to avoid privacy issues.
+ // For example, if an intranet page has a hyperlink to an external web
+ // site, we don't want to include the Origin of the request because it
+ // will leak the internal host name. Similar privacy concerns have lead
+ // to the widespread suppression of the Referer header at the network
+ // layer.
+ if (HttpMethod() == HTTPNames::GET || HttpMethod() == HTTPNames::HEAD)
+ return false;
+
+ // For non-GET and non-HEAD methods, always send an Origin header so the
+ // server knows we support this feature.
+ return true;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request.h
new file mode 100644
index 00000000000..194138f38f3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request.h
@@ -0,0 +1,498 @@
+/*
+ * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com>
+ * Copyright (C) 2009, 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_REQUEST_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_REQUEST_H_
+
+#include <memory>
+#include "services/network/public/mojom/cors.mojom-blink.h"
+#include "services/network/public/mojom/fetch_api.mojom-blink.h"
+#include "services/network/public/mojom/request_context_frame_type.mojom-shared.h"
+#include "third_party/blink/public/mojom/net/ip_address_space.mojom-blink.h"
+#include "third_party/blink/public/platform/modules/fetch/fetch_api_request.mojom-shared.h"
+#include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_load_priority.h"
+#include "third_party/blink/renderer/platform/network/encoded_form_data.h"
+#include "third_party/blink/renderer/platform/network/http_header_map.h"
+#include "third_party/blink/renderer/platform/network/http_names.h"
+#include "third_party/blink/renderer/platform/network/http_parsers.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/referrer.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+
+namespace blink {
+
+enum class ResourceRequestBlockedReason {
+ kCSP,
+ kMixedContent,
+ kOrigin,
+ kInspector,
+ kSubresourceFilter,
+ kOther,
+ kContentType,
+ kNone
+};
+
+enum InputToLoadPerfMetricReportPolicy : uint8_t {
+ kNoReport, // Don't report metrics for this ResourceRequest.
+ kReportLink, // Report metrics for this request as initiated by a link click.
+ kReportIntent, // Report metrics for this request as initiated by an intent.
+};
+
+struct CrossThreadResourceRequestData;
+
+// A ResourceRequest is a "request" object for ResourceLoader. Conceptually
+// it is https://fetch.spec.whatwg.org/#concept-request, but it contains
+// a lot of blink specific fields. WebURLRequest is the "public version"
+// of this class and WebURLLoader needs it. See WebURLRequest and
+// WrappedResourceRequest.
+//
+// There are cases where we need to copy a request across threads, and
+// CrossThreadResourceRequestData is a struct for the purpose. When you add a
+// member variable to this class, do not forget to add the corresponding
+// one in CrossThreadResourceRequestData and write copying logic.
+class PLATFORM_EXPORT ResourceRequest final {
+ USING_FAST_MALLOC(ResourceRequest);
+
+ public:
+ enum class RedirectStatus : uint8_t { kFollowedRedirect, kNoRedirect };
+
+ ResourceRequest();
+ explicit ResourceRequest(const String& url_string);
+ explicit ResourceRequest(const KURL&);
+ explicit ResourceRequest(CrossThreadResourceRequestData*);
+
+ // TODO(toyoshim): Use std::unique_ptr as much as possible, and hopefully
+ // make ResourceRequest WTF_MAKE_NONCOPYABLE. See crbug.com/787704.
+ ResourceRequest(const ResourceRequest&);
+ ResourceRequest& operator=(const ResourceRequest&);
+
+ // Constructs a new ResourceRequest for a redirect from this instance.
+ std::unique_ptr<ResourceRequest> CreateRedirectRequest(
+ const KURL& new_url,
+ const AtomicString& new_method,
+ const KURL& new_site_for_cookies,
+ const String& new_referrer,
+ ReferrerPolicy new_referrer_policy,
+ bool skip_service_worker) const;
+
+ // Gets a copy of the data suitable for passing to another thread.
+ std::unique_ptr<CrossThreadResourceRequestData> CopyData() const;
+
+ bool IsNull() const;
+
+ const KURL& Url() const;
+ void SetURL(const KURL&);
+
+ void RemoveUserAndPassFromURL();
+
+ mojom::FetchCacheMode GetCacheMode() const;
+ void SetCacheMode(mojom::FetchCacheMode);
+
+ double TimeoutInterval() const; // May return 0 when using platform default.
+ void SetTimeoutInterval(double);
+
+ const KURL& SiteForCookies() const;
+ void SetSiteForCookies(const KURL&);
+
+ scoped_refptr<const SecurityOrigin> RequestorOrigin() const;
+ void SetRequestorOrigin(scoped_refptr<const SecurityOrigin>);
+
+ const AtomicString& HttpMethod() const;
+ void SetHTTPMethod(const AtomicString&);
+
+ const HTTPHeaderMap& HttpHeaderFields() const;
+ const AtomicString& HttpHeaderField(const AtomicString& name) const;
+ void SetHTTPHeaderField(const AtomicString& name, const AtomicString& value);
+ void AddHTTPHeaderField(const AtomicString& name, const AtomicString& value);
+ void AddHTTPHeaderFields(const HTTPHeaderMap& header_fields);
+ void ClearHTTPHeaderField(const AtomicString& name);
+
+ const AtomicString& HttpContentType() const {
+ return HttpHeaderField(HTTPNames::Content_Type);
+ }
+ void SetHTTPContentType(const AtomicString& http_content_type) {
+ SetHTTPHeaderField(HTTPNames::Content_Type, http_content_type);
+ }
+
+ bool DidSetHTTPReferrer() const { return did_set_http_referrer_; }
+ const AtomicString& HttpReferrer() const {
+ return HttpHeaderField(HTTPNames::Referer);
+ }
+ ReferrerPolicy GetReferrerPolicy() const { return referrer_policy_; }
+ void SetHTTPReferrer(const Referrer&);
+ void ClearHTTPReferrer();
+
+ const AtomicString& HttpOrigin() const {
+ return HttpHeaderField(HTTPNames::Origin);
+ }
+ void SetHTTPOrigin(const SecurityOrigin*);
+ void ClearHTTPOrigin();
+ void SetHTTPOriginIfNeeded(const SecurityOrigin*);
+ void SetHTTPOriginToMatchReferrerIfNeeded();
+
+ void SetHTTPUserAgent(const AtomicString& http_user_agent) {
+ SetHTTPHeaderField(HTTPNames::User_Agent, http_user_agent);
+ }
+ void ClearHTTPUserAgent();
+
+ void SetHTTPAccept(const AtomicString& http_accept) {
+ SetHTTPHeaderField(HTTPNames::Accept, http_accept);
+ }
+
+ EncodedFormData* HttpBody() const;
+ void SetHTTPBody(scoped_refptr<EncodedFormData>);
+
+ bool AllowStoredCredentials() const;
+ void SetAllowStoredCredentials(bool allow_credentials);
+
+ // TODO(yhirano): Describe what Priority and IntraPriorityValue are.
+ ResourceLoadPriority Priority() const;
+ int IntraPriorityValue() const;
+ void SetPriority(ResourceLoadPriority, int intra_priority_value = 0);
+
+ bool IsConditional() const;
+
+ // Whether the associated ResourceHandleClient needs to be notified of
+ // upload progress made for that resource.
+ bool ReportUploadProgress() const { return report_upload_progress_; }
+ void SetReportUploadProgress(bool report_upload_progress) {
+ report_upload_progress_ = report_upload_progress;
+ }
+
+ // Whether actual headers being sent/received should be collected and reported
+ // for the request.
+ bool ReportRawHeaders() const { return report_raw_headers_; }
+ void SetReportRawHeaders(bool report_raw_headers) {
+ report_raw_headers_ = report_raw_headers;
+ }
+
+ // Allows the request to be matched up with its requestor.
+ int RequestorID() const { return requestor_id_; }
+ void SetRequestorID(int requestor_id) { requestor_id_ = requestor_id; }
+
+ // The unique child id (not PID) of the process from which this request
+ // originated. In the case of out-of-process plugins, this allows to link back
+ // the request to the plugin process (as it is processed through a render view
+ // process).
+ int GetPluginChildID() const { return plugin_child_id_; }
+ void SetPluginChildID(int plugin_child_id) {
+ plugin_child_id_ = plugin_child_id;
+ }
+
+ // Allows the request to be matched up with its app cache host.
+ int AppCacheHostID() const { return app_cache_host_id_; }
+ void SetAppCacheHostID(int id) { app_cache_host_id_ = id; }
+
+ // True if request was user initiated.
+ bool HasUserGesture() const { return has_user_gesture_; }
+ void SetHasUserGesture(bool);
+
+ // True if request should be downloaded to file.
+ bool DownloadToFile() const { return download_to_file_; }
+ void SetDownloadToFile(bool download_to_file) {
+ download_to_file_ = download_to_file;
+ }
+
+ // True if request shuold be downloaded to blob.
+ bool DownloadToBlob() const { return download_to_blob_; }
+ void SetDownloadToBlob(bool download_to_blob) {
+ download_to_blob_ = download_to_blob;
+ }
+
+ // True if the requestor wants to receive a response body as
+ // WebDataConsumerHandle.
+ bool UseStreamOnResponse() const { return use_stream_on_response_; }
+ void SetUseStreamOnResponse(bool use_stream_on_response) {
+ use_stream_on_response_ = use_stream_on_response;
+ }
+
+ // True if the request can work after the fetch group is terminated.
+ bool GetKeepalive() const { return keepalive_; }
+ void SetKeepalive(bool keepalive) { keepalive_ = keepalive; }
+
+ // True if service workers should not get events for the request.
+ bool GetSkipServiceWorker() const { return skip_service_worker_; }
+ void SetSkipServiceWorker(bool skip_service_worker) {
+ skip_service_worker_ = skip_service_worker;
+ }
+
+ // True if corresponding AppCache group should be resetted.
+ bool ShouldResetAppCache() const { return should_reset_app_cache_; }
+ void SetShouldResetAppCache(bool should_reset_app_cache) {
+ should_reset_app_cache_ = should_reset_app_cache;
+ }
+
+ // Extra data associated with this request.
+ WebURLRequest::ExtraData* GetExtraData() const {
+ return sharable_extra_data_ ? sharable_extra_data_->data.get() : nullptr;
+ }
+ void SetExtraData(std::unique_ptr<WebURLRequest::ExtraData> extra_data) {
+ if (extra_data) {
+ sharable_extra_data_ =
+ base::MakeRefCounted<SharableExtraData>(std::move(extra_data));
+ } else {
+ sharable_extra_data_ = nullptr;
+ }
+ }
+
+ WebURLRequest::RequestContext GetRequestContext() const {
+ return request_context_;
+ }
+ void SetRequestContext(WebURLRequest::RequestContext context) {
+ request_context_ = context;
+ }
+
+ network::mojom::RequestContextFrameType GetFrameType() const {
+ return frame_type_;
+ }
+ void SetFrameType(network::mojom::RequestContextFrameType frame_type) {
+ frame_type_ = frame_type;
+ }
+
+ network::mojom::FetchRequestMode GetFetchRequestMode() const {
+ return fetch_request_mode_;
+ }
+ void SetFetchRequestMode(network::mojom::FetchRequestMode mode) {
+ fetch_request_mode_ = mode;
+ }
+
+ network::mojom::FetchCredentialsMode GetFetchCredentialsMode() const {
+ return fetch_credentials_mode_;
+ }
+ void SetFetchCredentialsMode(network::mojom::FetchCredentialsMode mode) {
+ fetch_credentials_mode_ = mode;
+ }
+
+ network::mojom::FetchRedirectMode GetFetchRedirectMode() const {
+ return fetch_redirect_mode_;
+ }
+ void SetFetchRedirectMode(network::mojom::FetchRedirectMode redirect) {
+ fetch_redirect_mode_ = redirect;
+ }
+
+ const String& GetFetchIntegrity() const { return fetch_integrity_; }
+ void SetFetchIntegrity(const String& integrity) {
+ fetch_integrity_ = integrity;
+ }
+
+ WebURLRequest::PreviewsState GetPreviewsState() const {
+ return previews_state_;
+ }
+ void SetPreviewsState(WebURLRequest::PreviewsState previews_state) {
+ previews_state_ = previews_state;
+ }
+
+ bool CacheControlContainsNoCache() const;
+ bool CacheControlContainsNoStore() const;
+ bool HasCacheValidatorFields() const;
+
+ bool CheckForBrowserSideNavigation() const {
+ return check_for_browser_side_navigation_;
+ }
+ void SetCheckForBrowserSideNavigation(bool check) {
+ check_for_browser_side_navigation_ = check;
+ }
+
+ bool WasDiscarded() const { return was_discarded_; }
+ void SetWasDiscarded(bool was_discarded) { was_discarded_ = was_discarded; }
+
+ double UiStartTime() const { return ui_start_time_; }
+ void SetUIStartTime(double ui_start_time_seconds) {
+ ui_start_time_ = ui_start_time_seconds;
+ }
+
+ // https://wicg.github.io/cors-rfc1918/#external-request
+ bool IsExternalRequest() const { return is_external_request_; }
+ void SetExternalRequestStateFromRequestorAddressSpace(mojom::IPAddressSpace);
+
+ network::mojom::CORSPreflightPolicy CORSPreflightPolicy() const {
+ return cors_preflight_policy_;
+ }
+ void SetCORSPreflightPolicy(network::mojom::CORSPreflightPolicy policy) {
+ cors_preflight_policy_ = policy;
+ }
+
+ InputToLoadPerfMetricReportPolicy InputPerfMetricReportPolicy() const {
+ return input_perf_metric_report_policy_;
+ }
+ void SetInputPerfMetricReportPolicy(
+ InputToLoadPerfMetricReportPolicy input_perf_metric_report_policy) {
+ input_perf_metric_report_policy_ = input_perf_metric_report_policy;
+ }
+
+ void SetRedirectStatus(RedirectStatus status) { redirect_status_ = status; }
+ RedirectStatus GetRedirectStatus() const { return redirect_status_; }
+
+ void SetSuggestedFilename(const WTF::Optional<String>& suggested_filename) {
+ suggested_filename_ = suggested_filename;
+ }
+ const WTF::Optional<String>& GetSuggestedFilename() const {
+ return suggested_filename_;
+ }
+
+ void SetNavigationStartTime(double);
+ double NavigationStartTime() const { return navigation_start_; }
+
+ void SetIsSameDocumentNavigation(bool is_same_document) {
+ is_same_document_navigation_ = is_same_document;
+ }
+ bool IsSameDocumentNavigation() const { return is_same_document_navigation_; }
+
+ void SetIsAdResource() { is_ad_resource_ = true; }
+ bool IsAdResource() const { return is_ad_resource_; }
+
+ private:
+ using SharableExtraData =
+ base::RefCountedData<std::unique_ptr<WebURLRequest::ExtraData>>;
+
+ const CacheControlHeader& GetCacheControlHeader() const;
+
+ bool NeedsHTTPOrigin() const;
+
+ KURL url_;
+ double timeout_interval_; // 0 is a magic value for platform default on
+ // platforms that have one.
+ KURL site_for_cookies_;
+
+ // The SecurityOrigin specified by the ResourceLoaderOptions in case e.g.
+ // when the fetching was initiated in an isolated world. Set by
+ // ResourceFetcher but only when needed.
+ //
+ // TODO(crbug.com/811669): Merge with some of the other origin variables.
+ scoped_refptr<const SecurityOrigin> requestor_origin_;
+
+ AtomicString http_method_;
+ HTTPHeaderMap http_header_fields_;
+ scoped_refptr<EncodedFormData> http_body_;
+ bool allow_stored_credentials_ : 1;
+ bool report_upload_progress_ : 1;
+ bool report_raw_headers_ : 1;
+ bool has_user_gesture_ : 1;
+ bool download_to_file_ : 1;
+ bool download_to_blob_ : 1;
+ bool use_stream_on_response_ : 1;
+ bool keepalive_ : 1;
+ bool should_reset_app_cache_ : 1;
+ mojom::FetchCacheMode cache_mode_;
+ bool skip_service_worker_ : 1;
+ ResourceLoadPriority priority_;
+ int intra_priority_value_;
+ int requestor_id_;
+ int plugin_child_id_;
+ int app_cache_host_id_;
+ WebURLRequest::PreviewsState previews_state_;
+ scoped_refptr<SharableExtraData> sharable_extra_data_;
+ WebURLRequest::RequestContext request_context_;
+ network::mojom::RequestContextFrameType frame_type_;
+ network::mojom::FetchRequestMode fetch_request_mode_;
+ network::mojom::FetchCredentialsMode fetch_credentials_mode_;
+ network::mojom::FetchRedirectMode fetch_redirect_mode_;
+ String fetch_integrity_;
+ ReferrerPolicy referrer_policy_;
+ bool did_set_http_referrer_;
+ bool check_for_browser_side_navigation_;
+ bool was_discarded_;
+ double ui_start_time_;
+ bool is_external_request_;
+ network::mojom::CORSPreflightPolicy cors_preflight_policy_;
+ bool is_same_document_navigation_;
+ InputToLoadPerfMetricReportPolicy input_perf_metric_report_policy_;
+ RedirectStatus redirect_status_;
+ WTF::Optional<String> suggested_filename_;
+
+ mutable CacheControlHeader cache_control_header_cache_;
+
+ static double default_timeout_interval_;
+
+ double navigation_start_ = 0;
+
+ bool is_ad_resource_ = false;
+};
+
+// This class is needed to copy a ResourceRequest across threads, because it
+// has some members which cannot be transferred across threads (AtomicString
+// for example).
+// There are some rules / restrictions:
+// - This struct cannot contain an object that cannot be transferred across
+// threads (e.g., AtomicString)
+// - Non-simple members need explicit copying (e.g., String::IsolatedCopy,
+// KURL::Copy) rather than the copy constructor or the assignment operator.
+struct CrossThreadResourceRequestData {
+ WTF_MAKE_NONCOPYABLE(CrossThreadResourceRequestData);
+ USING_FAST_MALLOC(CrossThreadResourceRequestData);
+
+ public:
+ CrossThreadResourceRequestData() = default;
+ KURL url_;
+
+ mojom::FetchCacheMode cache_mode_;
+ double timeout_interval_;
+ KURL site_for_cookies_;
+ scoped_refptr<const SecurityOrigin> requestor_origin_;
+
+ String http_method_;
+ std::unique_ptr<CrossThreadHTTPHeaderMapData> http_headers_;
+ scoped_refptr<EncodedFormData> http_body_;
+ bool allow_stored_credentials_;
+ bool report_upload_progress_;
+ bool has_user_gesture_;
+ bool download_to_file_;
+ bool download_to_blob_;
+ bool skip_service_worker_;
+ bool use_stream_on_response_;
+ bool keepalive_;
+ bool should_reset_app_cache_;
+ ResourceLoadPriority priority_;
+ int intra_priority_value_;
+ int requestor_id_;
+ int plugin_child_id_;
+ int app_cache_host_id_;
+ WebURLRequest::RequestContext request_context_;
+ network::mojom::RequestContextFrameType frame_type_;
+ network::mojom::FetchRequestMode fetch_request_mode_;
+ network::mojom::FetchCredentialsMode fetch_credentials_mode_;
+ network::mojom::FetchRedirectMode fetch_redirect_mode_;
+ String fetch_integrity_;
+ WebURLRequest::PreviewsState previews_state_;
+ ReferrerPolicy referrer_policy_;
+ bool did_set_http_referrer_;
+ bool check_for_browser_side_navigation_;
+ double ui_start_time_;
+ bool is_external_request_;
+ network::mojom::CORSPreflightPolicy cors_preflight_policy_;
+ InputToLoadPerfMetricReportPolicy input_perf_metric_report_policy_;
+ ResourceRequest::RedirectStatus redirect_status_;
+ base::Optional<String> suggested_filename_;
+ bool is_ad_resource_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_REQUEST_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request_test.cc
new file mode 100644
index 00000000000..0c00adaf85c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request_test.cc
@@ -0,0 +1,161 @@
+// Copyright 2014 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/platform/loader/fetch/resource_request.h"
+
+#include <memory>
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/renderer/platform/network/encoded_form_data.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/referrer.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+
+namespace blink {
+
+TEST(ResourceRequestTest, CrossThreadResourceRequestData) {
+ ResourceRequest original;
+ original.SetURL(KURL("http://www.example.com/test.htm"));
+ original.SetCacheMode(mojom::FetchCacheMode::kDefault);
+ original.SetTimeoutInterval(10);
+ original.SetSiteForCookies(KURL("http://www.example.com/first_party.htm"));
+ original.SetRequestorOrigin(
+ SecurityOrigin::Create(KURL("http://www.example.com/first_party.htm")));
+ original.SetHTTPMethod(HTTPNames::GET);
+ original.SetHTTPHeaderField(AtomicString("Foo"), AtomicString("Bar"));
+ original.SetHTTPHeaderField(AtomicString("Piyo"), AtomicString("Fuga"));
+ original.SetPriority(ResourceLoadPriority::kLow, 20);
+
+ scoped_refptr<EncodedFormData> original_body(
+ EncodedFormData::Create("Test Body"));
+ original.SetHTTPBody(original_body);
+ original.SetAllowStoredCredentials(false);
+ original.SetReportUploadProgress(false);
+ original.SetHasUserGesture(false);
+ original.SetDownloadToFile(false);
+ original.SetSkipServiceWorker(false);
+ original.SetFetchRequestMode(network::mojom::FetchRequestMode::kCORS);
+ original.SetFetchCredentialsMode(
+ network::mojom::FetchCredentialsMode::kSameOrigin);
+ original.SetRequestorID(30);
+ original.SetPluginChildID(40);
+ original.SetAppCacheHostID(50);
+ original.SetRequestContext(WebURLRequest::kRequestContextAudio);
+ original.SetFrameType(network::mojom::RequestContextFrameType::kNested);
+ original.SetHTTPReferrer(
+ Referrer("http://www.example.com/referrer.htm", kReferrerPolicyDefault));
+
+ EXPECT_STREQ("http://www.example.com/test.htm",
+ original.Url().GetString().Utf8().data());
+ EXPECT_EQ(mojom::FetchCacheMode::kDefault, original.GetCacheMode());
+ EXPECT_EQ(10, original.TimeoutInterval());
+ EXPECT_STREQ("http://www.example.com/first_party.htm",
+ original.SiteForCookies().GetString().Utf8().data());
+ EXPECT_STREQ("www.example.com",
+ original.RequestorOrigin()->Host().Utf8().data());
+ EXPECT_STREQ("GET", original.HttpMethod().Utf8().data());
+ EXPECT_STREQ("Bar", original.HttpHeaderFields().Get("Foo").Utf8().data());
+ EXPECT_STREQ("Fuga", original.HttpHeaderFields().Get("Piyo").Utf8().data());
+ EXPECT_EQ(ResourceLoadPriority::kLow, original.Priority());
+ EXPECT_STREQ("Test Body",
+ original.HttpBody()->FlattenToString().Utf8().data());
+ EXPECT_FALSE(original.AllowStoredCredentials());
+ EXPECT_FALSE(original.ReportUploadProgress());
+ EXPECT_FALSE(original.HasUserGesture());
+ EXPECT_FALSE(original.DownloadToFile());
+ EXPECT_FALSE(original.GetSkipServiceWorker());
+ EXPECT_EQ(network::mojom::FetchRequestMode::kCORS,
+ original.GetFetchRequestMode());
+ EXPECT_EQ(network::mojom::FetchCredentialsMode::kSameOrigin,
+ original.GetFetchCredentialsMode());
+ EXPECT_EQ(30, original.RequestorID());
+ EXPECT_EQ(40, original.GetPluginChildID());
+ EXPECT_EQ(50, original.AppCacheHostID());
+ EXPECT_EQ(WebURLRequest::kRequestContextAudio, original.GetRequestContext());
+ EXPECT_EQ(network::mojom::RequestContextFrameType::kNested,
+ original.GetFrameType());
+ EXPECT_STREQ("http://www.example.com/referrer.htm",
+ original.HttpReferrer().Utf8().data());
+ EXPECT_EQ(kReferrerPolicyDefault, original.GetReferrerPolicy());
+
+ std::unique_ptr<CrossThreadResourceRequestData> data1(original.CopyData());
+ ResourceRequest copy1(data1.get());
+
+ EXPECT_STREQ("http://www.example.com/test.htm",
+ copy1.Url().GetString().Utf8().data());
+ EXPECT_EQ(mojom::FetchCacheMode::kDefault, copy1.GetCacheMode());
+ EXPECT_EQ(10, copy1.TimeoutInterval());
+ EXPECT_STREQ("http://www.example.com/first_party.htm",
+ copy1.SiteForCookies().GetString().Utf8().data());
+ EXPECT_STREQ("www.example.com",
+ copy1.RequestorOrigin()->Host().Utf8().data());
+ EXPECT_STREQ("GET", copy1.HttpMethod().Utf8().data());
+ EXPECT_STREQ("Bar", copy1.HttpHeaderFields().Get("Foo").Utf8().data());
+ EXPECT_EQ(ResourceLoadPriority::kLow, copy1.Priority());
+ EXPECT_STREQ("Test Body", copy1.HttpBody()->FlattenToString().Utf8().data());
+ EXPECT_FALSE(copy1.AllowStoredCredentials());
+ EXPECT_FALSE(copy1.ReportUploadProgress());
+ EXPECT_FALSE(copy1.HasUserGesture());
+ EXPECT_FALSE(copy1.DownloadToFile());
+ EXPECT_FALSE(copy1.GetSkipServiceWorker());
+ EXPECT_EQ(network::mojom::FetchRequestMode::kCORS,
+ copy1.GetFetchRequestMode());
+ EXPECT_EQ(network::mojom::FetchCredentialsMode::kSameOrigin,
+ copy1.GetFetchCredentialsMode());
+ EXPECT_EQ(30, copy1.RequestorID());
+ EXPECT_EQ(40, copy1.GetPluginChildID());
+ EXPECT_EQ(50, copy1.AppCacheHostID());
+ EXPECT_EQ(WebURLRequest::kRequestContextAudio, copy1.GetRequestContext());
+ EXPECT_EQ(network::mojom::RequestContextFrameType::kNested,
+ copy1.GetFrameType());
+ EXPECT_STREQ("http://www.example.com/referrer.htm",
+ copy1.HttpReferrer().Utf8().data());
+ EXPECT_EQ(kReferrerPolicyDefault, copy1.GetReferrerPolicy());
+
+ copy1.SetAllowStoredCredentials(true);
+ copy1.SetReportUploadProgress(true);
+ copy1.SetHasUserGesture(true);
+ copy1.SetDownloadToFile(true);
+ copy1.SetSkipServiceWorker(true);
+ copy1.SetFetchRequestMode(network::mojom::FetchRequestMode::kNoCORS);
+ copy1.SetFetchCredentialsMode(network::mojom::FetchCredentialsMode::kInclude);
+
+ std::unique_ptr<CrossThreadResourceRequestData> data2(copy1.CopyData());
+ ResourceRequest copy2(data2.get());
+ EXPECT_TRUE(copy2.AllowStoredCredentials());
+ EXPECT_TRUE(copy2.ReportUploadProgress());
+ EXPECT_TRUE(copy2.HasUserGesture());
+ EXPECT_TRUE(copy2.DownloadToFile());
+ EXPECT_TRUE(copy2.GetSkipServiceWorker());
+ EXPECT_EQ(network::mojom::FetchRequestMode::kNoCORS,
+ copy1.GetFetchRequestMode());
+ EXPECT_EQ(network::mojom::FetchCredentialsMode::kInclude,
+ copy1.GetFetchCredentialsMode());
+}
+
+TEST(ResourceRequestTest, SetHasUserGesture) {
+ ResourceRequest original;
+ EXPECT_FALSE(original.HasUserGesture());
+ original.SetHasUserGesture(true);
+ EXPECT_TRUE(original.HasUserGesture());
+ original.SetHasUserGesture(false);
+ EXPECT_TRUE(original.HasUserGesture());
+}
+
+TEST(ResourceRequestTest, SetIsAdResource) {
+ ResourceRequest original;
+ EXPECT_FALSE(original.IsAdResource());
+ original.SetIsAdResource();
+ EXPECT_TRUE(original.IsAdResource());
+
+ // Should persist across redirects.
+ std::unique_ptr<ResourceRequest> redirect_request =
+ original.CreateRedirectRequest(
+ KURL("https://example.test/redirect"), original.HttpMethod(),
+ original.SiteForCookies(), original.HttpReferrer(),
+ original.GetReferrerPolicy(), original.GetSkipServiceWorker());
+ EXPECT_TRUE(redirect_request->IsAdResource());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.cc
new file mode 100644
index 00000000000..6e6281a18b5
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.cc
@@ -0,0 +1,625 @@
+/*
+ * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+
+#include <algorithm>
+#include <limits>
+#include <memory>
+#include <string>
+
+#include "third_party/blink/public/platform/web_url_response.h"
+#include "third_party/blink/renderer/platform/network/http_names.h"
+#include "third_party/blink/renderer/platform/network/http_parsers.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+
+namespace blink {
+
+namespace {
+
+template <typename Interface>
+Vector<Interface> IsolatedCopy(const Vector<Interface>& src) {
+ Vector<Interface> result;
+ result.ReserveCapacity(src.size());
+ for (const auto& timestamp : src) {
+ result.push_back(timestamp.IsolatedCopy());
+ }
+ return result;
+}
+
+static const char kCacheControlHeader[] = "cache-control";
+static const char kPragmaHeader[] = "pragma";
+
+} // namespace
+
+ResourceResponse::SignedCertificateTimestamp::SignedCertificateTimestamp(
+ const blink::WebURLResponse::SignedCertificateTimestamp& sct)
+ : status_(sct.status),
+ origin_(sct.origin),
+ log_description_(sct.log_description),
+ log_id_(sct.log_id),
+ timestamp_(sct.timestamp),
+ hash_algorithm_(sct.hash_algorithm),
+ signature_algorithm_(sct.signature_algorithm),
+ signature_data_(sct.signature_data) {}
+
+ResourceResponse::SignedCertificateTimestamp
+ResourceResponse::SignedCertificateTimestamp::IsolatedCopy() const {
+ return SignedCertificateTimestamp(
+ status_.IsolatedCopy(), origin_.IsolatedCopy(),
+ log_description_.IsolatedCopy(), log_id_.IsolatedCopy(), timestamp_,
+ hash_algorithm_.IsolatedCopy(), signature_algorithm_.IsolatedCopy(),
+ signature_data_.IsolatedCopy());
+}
+
+ResourceResponse::ResourceResponse()
+ : expected_content_length_(0), is_null_(true) {}
+
+ResourceResponse::ResourceResponse(const KURL& url,
+ const AtomicString& mime_type,
+ long long expected_length,
+ const AtomicString& text_encoding_name)
+ : url_(url),
+ mime_type_(mime_type),
+ expected_content_length_(expected_length),
+ text_encoding_name_(text_encoding_name),
+ is_null_(false) {}
+
+ResourceResponse::ResourceResponse(CrossThreadResourceResponseData* data)
+ : ResourceResponse() {
+ SetURL(data->url_);
+ SetMimeType(AtomicString(data->mime_type_));
+ SetExpectedContentLength(data->expected_content_length_);
+ SetTextEncodingName(AtomicString(data->text_encoding_name_));
+
+ SetHTTPStatusCode(data->http_status_code_);
+ SetHTTPStatusText(AtomicString(data->http_status_text_));
+
+ http_header_fields_.Adopt(std::move(data->http_headers_));
+ SetResourceLoadTiming(std::move(data->resource_load_timing_));
+ remote_ip_address_ = AtomicString(data->remote_ip_address_);
+ remote_port_ = data->remote_port_;
+ has_major_certificate_errors_ = data->has_major_certificate_errors_;
+ ct_policy_compliance_ = data->ct_policy_compliance_;
+ is_legacy_symantec_cert_ = data->is_legacy_symantec_cert_;
+ cert_validity_start_ = data->cert_validity_start_;
+ was_fetched_via_spdy_ = data->was_fetched_via_spdy_;
+ was_fetched_via_proxy_ = data->was_fetched_via_proxy_;
+ was_fetched_via_service_worker_ = data->was_fetched_via_service_worker_;
+ was_fallback_required_by_service_worker_ =
+ data->was_fallback_required_by_service_worker_;
+ did_service_worker_navigation_preload_ =
+ data->did_service_worker_navigation_preload_;
+ response_type_via_service_worker_ = data->response_type_via_service_worker_;
+ security_style_ = data->security_style_;
+ security_details_.protocol = data->security_details_.protocol;
+ security_details_.cipher = data->security_details_.cipher;
+ security_details_.key_exchange = data->security_details_.key_exchange;
+ security_details_.key_exchange_group =
+ data->security_details_.key_exchange_group;
+ security_details_.mac = data->security_details_.mac;
+ security_details_.subject_name = data->security_details_.subject_name;
+ security_details_.san_list = data->security_details_.san_list;
+ security_details_.issuer = data->security_details_.issuer;
+ security_details_.valid_from = data->security_details_.valid_from;
+ security_details_.valid_to = data->security_details_.valid_to;
+ for (auto& cert : data->certificate_)
+ security_details_.certificate.push_back(AtomicString(cert));
+ security_details_.sct_list = data->security_details_.sct_list;
+ http_version_ = data->http_version_;
+ app_cache_id_ = data->app_cache_id_;
+ app_cache_manifest_url_ = data->app_cache_manifest_url_.Copy();
+ multipart_boundary_ = data->multipart_boundary_;
+ url_list_via_service_worker_ = data->url_list_via_service_worker_;
+ cache_storage_cache_name_ = data->cache_storage_cache_name_;
+ response_time_ = data->response_time_;
+ encoded_data_length_ = data->encoded_data_length_;
+ encoded_body_length_ = data->encoded_body_length_;
+ decoded_body_length_ = data->decoded_body_length_;
+ downloaded_file_path_ = data->downloaded_file_path_;
+ downloaded_file_handle_ = data->downloaded_file_handle_;
+
+ // Bug https://bugs.webkit.org/show_bug.cgi?id=60397 this doesn't support
+ // whatever values may be present in the opaque m_extraData structure.
+}
+
+ResourceResponse::ResourceResponse(const ResourceResponse&) = default;
+ResourceResponse& ResourceResponse::operator=(const ResourceResponse&) =
+ default;
+
+std::unique_ptr<CrossThreadResourceResponseData> ResourceResponse::CopyData()
+ const {
+ std::unique_ptr<CrossThreadResourceResponseData> data =
+ std::make_unique<CrossThreadResourceResponseData>();
+ data->url_ = Url().Copy();
+ data->mime_type_ = MimeType().GetString().IsolatedCopy();
+ data->expected_content_length_ = ExpectedContentLength();
+ data->text_encoding_name_ = TextEncodingName().GetString().IsolatedCopy();
+ data->http_status_code_ = HttpStatusCode();
+ data->http_status_text_ = HttpStatusText().GetString().IsolatedCopy();
+ data->http_headers_ = HttpHeaderFields().CopyData();
+ if (resource_load_timing_)
+ data->resource_load_timing_ = resource_load_timing_->DeepCopy();
+ data->remote_ip_address_ = remote_ip_address_.GetString().IsolatedCopy();
+ data->remote_port_ = remote_port_;
+ data->has_major_certificate_errors_ = has_major_certificate_errors_;
+ data->ct_policy_compliance_ = ct_policy_compliance_;
+ data->is_legacy_symantec_cert_ = is_legacy_symantec_cert_;
+ data->cert_validity_start_ = cert_validity_start_;
+ data->was_fetched_via_spdy_ = was_fetched_via_spdy_;
+ data->was_fetched_via_proxy_ = was_fetched_via_proxy_;
+ data->was_fetched_via_service_worker_ = was_fetched_via_service_worker_;
+ data->was_fallback_required_by_service_worker_ =
+ was_fallback_required_by_service_worker_;
+ data->did_service_worker_navigation_preload_ =
+ did_service_worker_navigation_preload_;
+ data->response_type_via_service_worker_ = response_type_via_service_worker_;
+ data->security_style_ = security_style_;
+ data->security_details_.protocol = security_details_.protocol.IsolatedCopy();
+ data->security_details_.cipher = security_details_.cipher.IsolatedCopy();
+ data->security_details_.key_exchange =
+ security_details_.key_exchange.IsolatedCopy();
+ data->security_details_.key_exchange_group =
+ security_details_.key_exchange_group.IsolatedCopy();
+ data->security_details_.mac = security_details_.mac.IsolatedCopy();
+ data->security_details_.subject_name =
+ security_details_.subject_name.IsolatedCopy();
+ data->security_details_.san_list = IsolatedCopy(security_details_.san_list);
+ data->security_details_.issuer = security_details_.issuer.IsolatedCopy();
+ data->security_details_.valid_from = security_details_.valid_from;
+ data->security_details_.valid_to = security_details_.valid_to;
+ for (auto& cert : security_details_.certificate)
+ data->certificate_.push_back(cert.GetString().IsolatedCopy());
+ data->security_details_.sct_list = IsolatedCopy(security_details_.sct_list);
+ data->http_version_ = http_version_;
+ data->app_cache_id_ = app_cache_id_;
+ data->app_cache_manifest_url_ = app_cache_manifest_url_.Copy();
+ data->multipart_boundary_ = multipart_boundary_;
+ data->url_list_via_service_worker_.resize(
+ url_list_via_service_worker_.size());
+ std::transform(url_list_via_service_worker_.begin(),
+ url_list_via_service_worker_.end(),
+ data->url_list_via_service_worker_.begin(),
+ [](const KURL& url) { return url.Copy(); });
+ data->cache_storage_cache_name_ = CacheStorageCacheName().IsolatedCopy();
+ data->response_time_ = response_time_;
+ data->encoded_data_length_ = encoded_data_length_;
+ data->encoded_body_length_ = encoded_body_length_;
+ data->decoded_body_length_ = decoded_body_length_;
+ data->downloaded_file_path_ = downloaded_file_path_.IsolatedCopy();
+ data->downloaded_file_handle_ = downloaded_file_handle_;
+
+ // Bug https://bugs.webkit.org/show_bug.cgi?id=60397 this doesn't support
+ // whatever values may be present in the opaque m_extraData structure.
+
+ return data;
+}
+
+bool ResourceResponse::IsHTTP() const {
+ return url_.ProtocolIsInHTTPFamily();
+}
+
+const KURL& ResourceResponse::Url() const {
+ return url_;
+}
+
+void ResourceResponse::SetURL(const KURL& url) {
+ is_null_ = false;
+
+ url_ = url;
+}
+
+const AtomicString& ResourceResponse::MimeType() const {
+ return mime_type_;
+}
+
+void ResourceResponse::SetMimeType(const AtomicString& mime_type) {
+ is_null_ = false;
+
+ // FIXME: MIME type is determined by HTTP Content-Type header. We should
+ // update the header, so that it doesn't disagree with m_mimeType.
+ mime_type_ = mime_type;
+}
+
+long long ResourceResponse::ExpectedContentLength() const {
+ return expected_content_length_;
+}
+
+void ResourceResponse::SetExpectedContentLength(
+ long long expected_content_length) {
+ is_null_ = false;
+
+ // FIXME: Content length is determined by HTTP Content-Length header. We
+ // should update the header, so that it doesn't disagree with
+ // m_expectedContentLength.
+ expected_content_length_ = expected_content_length;
+}
+
+const AtomicString& ResourceResponse::TextEncodingName() const {
+ return text_encoding_name_;
+}
+
+void ResourceResponse::SetTextEncodingName(const AtomicString& encoding_name) {
+ is_null_ = false;
+
+ // FIXME: Text encoding is determined by HTTP Content-Type header. We should
+ // update the header, so that it doesn't disagree with m_textEncodingName.
+ text_encoding_name_ = encoding_name;
+}
+
+int ResourceResponse::HttpStatusCode() const {
+ return http_status_code_;
+}
+
+void ResourceResponse::SetHTTPStatusCode(int status_code) {
+ http_status_code_ = status_code;
+}
+
+const AtomicString& ResourceResponse::HttpStatusText() const {
+ return http_status_text_;
+}
+
+void ResourceResponse::SetHTTPStatusText(const AtomicString& status_text) {
+ http_status_text_ = status_text;
+}
+
+const AtomicString& ResourceResponse::HttpHeaderField(
+ const AtomicString& name) const {
+ return http_header_fields_.Get(name);
+}
+
+void ResourceResponse::UpdateHeaderParsedState(const AtomicString& name) {
+ static const char kAgeHeader[] = "age";
+ static const char kDateHeader[] = "date";
+ static const char kExpiresHeader[] = "expires";
+ static const char kLastModifiedHeader[] = "last-modified";
+
+ if (DeprecatedEqualIgnoringCase(name, kAgeHeader))
+ have_parsed_age_header_ = false;
+ else if (DeprecatedEqualIgnoringCase(name, kCacheControlHeader) ||
+ DeprecatedEqualIgnoringCase(name, kPragmaHeader))
+ cache_control_header_ = CacheControlHeader();
+ else if (DeprecatedEqualIgnoringCase(name, kDateHeader))
+ have_parsed_date_header_ = false;
+ else if (DeprecatedEqualIgnoringCase(name, kExpiresHeader))
+ have_parsed_expires_header_ = false;
+ else if (DeprecatedEqualIgnoringCase(name, kLastModifiedHeader))
+ have_parsed_last_modified_header_ = false;
+}
+
+void ResourceResponse::SetSecurityDetails(
+ const String& protocol,
+ const String& key_exchange,
+ const String& key_exchange_group,
+ const String& cipher,
+ const String& mac,
+ const String& subject_name,
+ const Vector<String>& san_list,
+ const String& issuer,
+ time_t valid_from,
+ time_t valid_to,
+ const Vector<AtomicString>& certificate,
+ const SignedCertificateTimestampList& sct_list) {
+ security_details_.protocol = protocol;
+ security_details_.key_exchange = key_exchange;
+ security_details_.key_exchange_group = key_exchange_group;
+ security_details_.cipher = cipher;
+ security_details_.mac = mac;
+ security_details_.subject_name = subject_name;
+ security_details_.san_list = san_list;
+ security_details_.issuer = issuer;
+ security_details_.valid_from = valid_from;
+ security_details_.valid_to = valid_to;
+ security_details_.certificate = certificate;
+ security_details_.sct_list = sct_list;
+}
+
+void ResourceResponse::SetHTTPHeaderField(const AtomicString& name,
+ const AtomicString& value) {
+ UpdateHeaderParsedState(name);
+
+ http_header_fields_.Set(name, value);
+}
+
+void ResourceResponse::AddHTTPHeaderField(const AtomicString& name,
+ const AtomicString& value) {
+ UpdateHeaderParsedState(name);
+
+ HTTPHeaderMap::AddResult result = http_header_fields_.Add(name, value);
+ if (!result.is_new_entry)
+ result.stored_value->value = result.stored_value->value + ", " + value;
+}
+
+void ResourceResponse::ClearHTTPHeaderField(const AtomicString& name) {
+ http_header_fields_.Remove(name);
+}
+
+const HTTPHeaderMap& ResourceResponse::HttpHeaderFields() const {
+ return http_header_fields_;
+}
+
+bool ResourceResponse::CacheControlContainsNoCache() const {
+ if (!cache_control_header_.parsed) {
+ cache_control_header_ = ParseCacheControlDirectives(
+ http_header_fields_.Get(kCacheControlHeader),
+ http_header_fields_.Get(kPragmaHeader));
+ }
+ return cache_control_header_.contains_no_cache;
+}
+
+bool ResourceResponse::CacheControlContainsNoStore() const {
+ if (!cache_control_header_.parsed) {
+ cache_control_header_ = ParseCacheControlDirectives(
+ http_header_fields_.Get(kCacheControlHeader),
+ http_header_fields_.Get(kPragmaHeader));
+ }
+ return cache_control_header_.contains_no_store;
+}
+
+bool ResourceResponse::CacheControlContainsMustRevalidate() const {
+ if (!cache_control_header_.parsed) {
+ cache_control_header_ = ParseCacheControlDirectives(
+ http_header_fields_.Get(kCacheControlHeader),
+ http_header_fields_.Get(kPragmaHeader));
+ }
+ return cache_control_header_.contains_must_revalidate;
+}
+
+bool ResourceResponse::HasCacheValidatorFields() const {
+ static const char kLastModifiedHeader[] = "last-modified";
+ static const char kETagHeader[] = "etag";
+ return !http_header_fields_.Get(kLastModifiedHeader).IsEmpty() ||
+ !http_header_fields_.Get(kETagHeader).IsEmpty();
+}
+
+double ResourceResponse::CacheControlMaxAge() const {
+ if (!cache_control_header_.parsed) {
+ cache_control_header_ = ParseCacheControlDirectives(
+ http_header_fields_.Get(kCacheControlHeader),
+ http_header_fields_.Get(kPragmaHeader));
+ }
+ return cache_control_header_.max_age;
+}
+
+static double ParseDateValueInHeader(const HTTPHeaderMap& headers,
+ const AtomicString& header_name) {
+ const AtomicString& header_value = headers.Get(header_name);
+ if (header_value.IsEmpty())
+ return std::numeric_limits<double>::quiet_NaN();
+ // This handles all date formats required by RFC2616:
+ // Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
+ // Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
+ // Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
+ double date_in_milliseconds = ParseDate(header_value);
+ if (!std::isfinite(date_in_milliseconds))
+ return std::numeric_limits<double>::quiet_NaN();
+ return date_in_milliseconds / 1000;
+}
+
+double ResourceResponse::Date() const {
+ if (!have_parsed_date_header_) {
+ static const char kHeaderName[] = "date";
+ date_ = ParseDateValueInHeader(http_header_fields_, kHeaderName);
+ have_parsed_date_header_ = true;
+ }
+ return date_;
+}
+
+double ResourceResponse::Age() const {
+ if (!have_parsed_age_header_) {
+ static const char kHeaderName[] = "age";
+ const AtomicString& header_value = http_header_fields_.Get(kHeaderName);
+ bool ok;
+ age_ = header_value.ToDouble(&ok);
+ if (!ok)
+ age_ = std::numeric_limits<double>::quiet_NaN();
+ have_parsed_age_header_ = true;
+ }
+ return age_;
+}
+
+double ResourceResponse::Expires() const {
+ if (!have_parsed_expires_header_) {
+ static const char kHeaderName[] = "expires";
+ expires_ = ParseDateValueInHeader(http_header_fields_, kHeaderName);
+ have_parsed_expires_header_ = true;
+ }
+ return expires_;
+}
+
+double ResourceResponse::LastModified() const {
+ if (!have_parsed_last_modified_header_) {
+ static const char kHeaderName[] = "last-modified";
+ last_modified_ = ParseDateValueInHeader(http_header_fields_, kHeaderName);
+ have_parsed_last_modified_header_ = true;
+ }
+ return last_modified_;
+}
+
+bool ResourceResponse::IsAttachment() const {
+ static const char kAttachmentString[] = "attachment";
+ String value = http_header_fields_.Get(HTTPNames::Content_Disposition);
+ size_t loc = value.find(';');
+ if (loc != kNotFound)
+ value = value.Left(loc);
+ value = value.StripWhiteSpace();
+ return DeprecatedEqualIgnoringCase(value, kAttachmentString);
+}
+
+AtomicString ResourceResponse::HttpContentType() const {
+ return ExtractMIMETypeFromMediaType(
+ HttpHeaderField(HTTPNames::Content_Type).DeprecatedLower());
+}
+
+bool ResourceResponse::WasCached() const {
+ return was_cached_;
+}
+
+void ResourceResponse::SetWasCached(bool value) {
+ was_cached_ = value;
+}
+
+bool ResourceResponse::ConnectionReused() const {
+ return connection_reused_;
+}
+
+void ResourceResponse::SetConnectionReused(bool connection_reused) {
+ connection_reused_ = connection_reused;
+}
+
+unsigned ResourceResponse::ConnectionID() const {
+ return connection_id_;
+}
+
+void ResourceResponse::SetConnectionID(unsigned connection_id) {
+ connection_id_ = connection_id;
+}
+
+ResourceLoadTiming* ResourceResponse::GetResourceLoadTiming() const {
+ return resource_load_timing_.get();
+}
+
+void ResourceResponse::SetResourceLoadTiming(
+ scoped_refptr<ResourceLoadTiming> resource_load_timing) {
+ resource_load_timing_ = std::move(resource_load_timing);
+}
+
+scoped_refptr<ResourceLoadInfo> ResourceResponse::GetResourceLoadInfo() const {
+ return resource_load_info_.get();
+}
+
+void ResourceResponse::SetResourceLoadInfo(
+ scoped_refptr<ResourceLoadInfo> load_info) {
+ resource_load_info_ = std::move(load_info);
+}
+
+void ResourceResponse::SetCTPolicyCompliance(CTPolicyCompliance compliance) {
+ ct_policy_compliance_ = compliance;
+}
+
+bool ResourceResponse::IsOpaqueResponseFromServiceWorker() const {
+ switch (response_type_via_service_worker_) {
+ case network::mojom::FetchResponseType::kBasic:
+ case network::mojom::FetchResponseType::kCORS:
+ case network::mojom::FetchResponseType::kDefault:
+ case network::mojom::FetchResponseType::kError:
+ return false;
+ case network::mojom::FetchResponseType::kOpaque:
+ case network::mojom::FetchResponseType::kOpaqueRedirect:
+ return true;
+ }
+ NOTREACHED();
+ return false;
+}
+
+KURL ResourceResponse::OriginalURLViaServiceWorker() const {
+ if (url_list_via_service_worker_.IsEmpty())
+ return KURL();
+ return url_list_via_service_worker_.back();
+}
+
+AtomicString ResourceResponse::ConnectionInfoString() const {
+ std::string connection_info_string =
+ net::HttpResponseInfo::ConnectionInfoToString(connection_info_);
+ return AtomicString(
+ reinterpret_cast<const LChar*>(connection_info_string.data()),
+ connection_info_string.length());
+}
+
+void ResourceResponse::SetEncodedDataLength(long long value) {
+ encoded_data_length_ = value;
+}
+
+void ResourceResponse::SetEncodedBodyLength(long long value) {
+ encoded_body_length_ = value;
+}
+
+void ResourceResponse::SetDecodedBodyLength(long long value) {
+ decoded_body_length_ = value;
+}
+
+void ResourceResponse::SetDownloadedFilePath(
+ const String& downloaded_file_path) {
+ downloaded_file_path_ = downloaded_file_path;
+ if (downloaded_file_path_.IsEmpty()) {
+ downloaded_file_handle_ = nullptr;
+ return;
+ }
+ // TODO(dmurph): Investigate whether we need the mimeType on this blob.
+ std::unique_ptr<BlobData> blob_data =
+ BlobData::CreateForFileWithUnknownSize(downloaded_file_path_);
+ blob_data->DetachFromCurrentThread();
+ downloaded_file_handle_ = BlobDataHandle::Create(std::move(blob_data), -1);
+}
+
+void ResourceResponse::AppendRedirectResponse(
+ const ResourceResponse& response) {
+ redirect_responses_.push_back(response);
+}
+
+bool ResourceResponse::Compare(const ResourceResponse& a,
+ const ResourceResponse& b) {
+ if (a.IsNull() != b.IsNull())
+ return false;
+ if (a.Url() != b.Url())
+ return false;
+ if (a.MimeType() != b.MimeType())
+ return false;
+ if (a.ExpectedContentLength() != b.ExpectedContentLength())
+ return false;
+ if (a.TextEncodingName() != b.TextEncodingName())
+ return false;
+ if (a.HttpStatusCode() != b.HttpStatusCode())
+ return false;
+ if (a.HttpStatusText() != b.HttpStatusText())
+ return false;
+ if (a.HttpHeaderFields() != b.HttpHeaderFields())
+ return false;
+ if (a.GetResourceLoadTiming() && b.GetResourceLoadTiming() &&
+ *a.GetResourceLoadTiming() == *b.GetResourceLoadTiming())
+ return true;
+ if (a.GetResourceLoadTiming() != b.GetResourceLoadTiming())
+ return false;
+ if (a.EncodedBodyLength() != b.EncodedBodyLength())
+ return false;
+ if (a.DecodedBodyLength() != b.DecodedBodyLength())
+ return false;
+ return true;
+}
+
+STATIC_ASSERT_ENUM(WebURLResponse::kHTTPVersionUnknown,
+ ResourceResponse::kHTTPVersionUnknown);
+STATIC_ASSERT_ENUM(WebURLResponse::kHTTPVersion_0_9,
+ ResourceResponse::kHTTPVersion_0_9);
+STATIC_ASSERT_ENUM(WebURLResponse::kHTTPVersion_1_0,
+ ResourceResponse::kHTTPVersion_1_0);
+STATIC_ASSERT_ENUM(WebURLResponse::kHTTPVersion_1_1,
+ ResourceResponse::kHTTPVersion_1_1);
+STATIC_ASSERT_ENUM(WebURLResponse::kHTTPVersion_2_0,
+ ResourceResponse::kHTTPVersion_2_0);
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.h
new file mode 100644
index 00000000000..d25f83dfaba
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.h
@@ -0,0 +1,625 @@
+/*
+ * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_RESPONSE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_RESPONSE_H_
+
+#include <memory>
+#include <utility>
+
+#include "base/memory/scoped_refptr.h"
+#include "base/time/time.h"
+#include "services/network/public/mojom/fetch_api.mojom-blink.h"
+#include "third_party/blink/public/platform/web_url_response.h"
+#include "third_party/blink/renderer/platform/blob/blob_data.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_load_info.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h"
+#include "third_party/blink/renderer/platform/network/http_header_map.h"
+#include "third_party/blink/renderer/platform/network/http_parsers.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/text/cstring.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+struct CrossThreadResourceResponseData;
+
+// A ResourceResponse is a "response" object used in blink. Conceptually
+// it is https://fetch.spec.whatwg.org/#concept-response, but it contains
+// a lot of blink specific fields. WebURLResponse is the "public version"
+// of this class and public classes (i.e., classes in public/platform) use it.
+//
+// There are cases where we need to copy a response across threads, and
+// CrossThreadResourceResponseData is a struct for the purpose. When you add a
+// member variable to this class, do not forget to add the corresponding
+// one in CrossThreadResourceResponseData and write copying logic.
+class PLATFORM_EXPORT ResourceResponse final {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ enum HTTPVersion : uint8_t {
+ kHTTPVersionUnknown,
+ kHTTPVersion_0_9,
+ kHTTPVersion_1_0,
+ kHTTPVersion_1_1,
+ kHTTPVersion_2_0
+ };
+ enum SecurityStyle : uint8_t {
+ kSecurityStyleUnknown,
+ kSecurityStyleUnauthenticated,
+ kSecurityStyleAuthenticationBroken,
+ kSecurityStyleAuthenticated
+ };
+
+ enum CTPolicyCompliance {
+ kCTPolicyComplianceDetailsNotAvailable,
+ kCTPolicyComplies,
+ kCTPolicyDoesNotComply
+ };
+
+ class PLATFORM_EXPORT SignedCertificateTimestamp final {
+ public:
+ SignedCertificateTimestamp(String status,
+ String origin,
+ String log_description,
+ String log_id,
+ int64_t timestamp,
+ String hash_algorithm,
+ String signature_algorithm,
+ String signature_data)
+ : status_(status),
+ origin_(origin),
+ log_description_(log_description),
+ log_id_(log_id),
+ timestamp_(timestamp),
+ hash_algorithm_(hash_algorithm),
+ signature_algorithm_(signature_algorithm),
+ signature_data_(signature_data) {}
+ explicit SignedCertificateTimestamp(
+ const struct blink::WebURLResponse::SignedCertificateTimestamp&);
+ SignedCertificateTimestamp IsolatedCopy() const;
+
+ String status_;
+ String origin_;
+ String log_description_;
+ String log_id_;
+ int64_t timestamp_;
+ String hash_algorithm_;
+ String signature_algorithm_;
+ String signature_data_;
+ };
+
+ using SignedCertificateTimestampList =
+ WTF::Vector<SignedCertificateTimestamp>;
+
+ struct SecurityDetails {
+ DISALLOW_NEW();
+ SecurityDetails() : valid_from(0), valid_to(0) {}
+ // All strings are human-readable values.
+ String protocol;
+ // keyExchange is the empty string if not applicable for the connection's
+ // protocol.
+ String key_exchange;
+ // keyExchangeGroup is the empty string if not applicable for the
+ // connection's key exchange.
+ String key_exchange_group;
+ String cipher;
+ // mac is the empty string when the connection cipher suite does not
+ // have a separate MAC value (i.e. if the cipher suite is AEAD).
+ String mac;
+ String subject_name;
+ Vector<String> san_list;
+ String issuer;
+ time_t valid_from;
+ time_t valid_to;
+ // DER-encoded X509Certificate certificate chain.
+ Vector<AtomicString> certificate;
+ SignedCertificateTimestampList sct_list;
+ };
+
+ class ExtraData : public RefCounted<ExtraData> {
+ public:
+ virtual ~ExtraData() = default;
+ };
+
+ explicit ResourceResponse(CrossThreadResourceResponseData*);
+
+ // Gets a copy of the data suitable for passing to another thread.
+ std::unique_ptr<CrossThreadResourceResponseData> CopyData() const;
+
+ ResourceResponse();
+ explicit ResourceResponse(
+ const KURL&,
+ const AtomicString& mime_type = g_null_atom,
+ long long expected_length = 0,
+ const AtomicString& text_encoding_name = g_null_atom);
+ ResourceResponse(const ResourceResponse&);
+ ResourceResponse& operator=(const ResourceResponse&);
+
+ bool IsNull() const { return is_null_; }
+ bool IsHTTP() const;
+
+ // The URL of the resource. Note that if a service worker responded to the
+ // request for this resource, it may have fetched an entirely different URL
+ // and responded with that resource. wasFetchedViaServiceWorker() and
+ // originalURLViaServiceWorker() can be used to determine whether and how a
+ // service worker responded to the request. Example service worker code:
+ //
+ // onfetch = (event => {
+ // if (event.request.url == 'https://abc.com')
+ // event.respondWith(fetch('https://def.com'));
+ // });
+ //
+ // If this service worker responds to an "https://abc.com" request, then for
+ // the resulting ResourceResponse, url() is "https://abc.com",
+ // wasFetchedViaServiceWorker() is true, and originalURLViaServiceWorker() is
+ // "https://def.com".
+ const KURL& Url() const;
+ void SetURL(const KURL&);
+
+ const AtomicString& MimeType() const;
+ void SetMimeType(const AtomicString&);
+
+ long long ExpectedContentLength() const;
+ void SetExpectedContentLength(long long);
+
+ const AtomicString& TextEncodingName() const;
+ void SetTextEncodingName(const AtomicString&);
+
+ int HttpStatusCode() const;
+ void SetHTTPStatusCode(int);
+
+ const AtomicString& HttpStatusText() const;
+ void SetHTTPStatusText(const AtomicString&);
+
+ const AtomicString& HttpHeaderField(const AtomicString& name) const;
+ void SetHTTPHeaderField(const AtomicString& name, const AtomicString& value);
+ void AddHTTPHeaderField(const AtomicString& name, const AtomicString& value);
+ void ClearHTTPHeaderField(const AtomicString& name);
+ const HTTPHeaderMap& HttpHeaderFields() const;
+
+ bool IsMultipart() const { return MimeType() == "multipart/x-mixed-replace"; }
+
+ bool IsAttachment() const;
+
+ AtomicString HttpContentType() const;
+
+ // These functions return parsed values of the corresponding response headers.
+ // NaN means that the header was not present or had invalid value.
+ bool CacheControlContainsNoCache() const;
+ bool CacheControlContainsNoStore() const;
+ bool CacheControlContainsMustRevalidate() const;
+ bool HasCacheValidatorFields() const;
+ double CacheControlMaxAge() const;
+ double Date() const;
+ double Age() const;
+ double Expires() const;
+ double LastModified() const;
+
+ unsigned ConnectionID() const;
+ void SetConnectionID(unsigned);
+
+ bool ConnectionReused() const;
+ void SetConnectionReused(bool);
+
+ bool WasCached() const;
+ void SetWasCached(bool);
+
+ ResourceLoadTiming* GetResourceLoadTiming() const;
+ void SetResourceLoadTiming(scoped_refptr<ResourceLoadTiming>);
+
+ scoped_refptr<ResourceLoadInfo> GetResourceLoadInfo() const;
+ void SetResourceLoadInfo(scoped_refptr<ResourceLoadInfo>);
+
+ HTTPVersion HttpVersion() const { return http_version_; }
+ void SetHTTPVersion(HTTPVersion version) { http_version_ = version; }
+
+ bool HasMajorCertificateErrors() const {
+ return has_major_certificate_errors_;
+ }
+ void SetHasMajorCertificateErrors(bool has_major_certificate_errors) {
+ has_major_certificate_errors_ = has_major_certificate_errors;
+ }
+
+ CTPolicyCompliance GetCTPolicyCompliance() const {
+ return ct_policy_compliance_;
+ }
+ void SetCTPolicyCompliance(CTPolicyCompliance);
+
+ bool IsLegacySymantecCert() const { return is_legacy_symantec_cert_; }
+ void SetIsLegacySymantecCert(bool is_legacy_symantec_cert) {
+ is_legacy_symantec_cert_ = is_legacy_symantec_cert;
+ }
+
+ SecurityStyle GetSecurityStyle() const { return security_style_; }
+ void SetSecurityStyle(SecurityStyle security_style) {
+ security_style_ = security_style;
+ }
+
+ const SecurityDetails* GetSecurityDetails() const {
+ return &security_details_;
+ }
+ void SetSecurityDetails(const String& protocol,
+ const String& key_exchange,
+ const String& key_exchange_group,
+ const String& cipher,
+ const String& mac,
+ const String& subject_name,
+ const Vector<String>& san_list,
+ const String& issuer,
+ time_t valid_from,
+ time_t valid_to,
+ const Vector<AtomicString>& certificate,
+ const SignedCertificateTimestampList& sct_list);
+
+ long long AppCacheID() const { return app_cache_id_; }
+ void SetAppCacheID(long long id) { app_cache_id_ = id; }
+
+ const KURL& AppCacheManifestURL() const { return app_cache_manifest_url_; }
+ void SetAppCacheManifestURL(const KURL& url) {
+ app_cache_manifest_url_ = url;
+ }
+
+ bool WasFetchedViaSPDY() const { return was_fetched_via_spdy_; }
+ void SetWasFetchedViaSPDY(bool value) { was_fetched_via_spdy_ = value; }
+
+ // See ServiceWorkerResponseInfo::was_fetched_via_service_worker.
+ bool WasFetchedViaServiceWorker() const {
+ return was_fetched_via_service_worker_;
+ }
+ void SetWasFetchedViaServiceWorker(bool value) {
+ was_fetched_via_service_worker_ = value;
+ }
+
+ // See ServiceWorkerResponseInfo::was_fallback_required.
+ bool WasFallbackRequiredByServiceWorker() const {
+ return was_fallback_required_by_service_worker_;
+ }
+ void SetWasFallbackRequiredByServiceWorker(bool value) {
+ was_fallback_required_by_service_worker_ = value;
+ }
+
+ network::mojom::FetchResponseType ResponseTypeViaServiceWorker() const {
+ return response_type_via_service_worker_;
+ }
+ void SetResponseTypeViaServiceWorker(
+ network::mojom::FetchResponseType value) {
+ response_type_via_service_worker_ = value;
+ }
+ bool IsOpaqueResponseFromServiceWorker() const;
+
+ // See ServiceWorkerResponseInfo::url_list_via_service_worker.
+ const Vector<KURL>& UrlListViaServiceWorker() const {
+ return url_list_via_service_worker_;
+ }
+ void SetURLListViaServiceWorker(const Vector<KURL>& url_list) {
+ url_list_via_service_worker_ = url_list;
+ }
+
+ // Returns the last URL of urlListViaServiceWorker if exists. Otherwise
+ // returns an empty URL.
+ KURL OriginalURLViaServiceWorker() const;
+
+ const Vector<char>& MultipartBoundary() const { return multipart_boundary_; }
+ void SetMultipartBoundary(const char* bytes, size_t size) {
+ multipart_boundary_.clear();
+ multipart_boundary_.Append(bytes, size);
+ }
+
+ const String& CacheStorageCacheName() const {
+ return cache_storage_cache_name_;
+ }
+ void SetCacheStorageCacheName(const String& cache_storage_cache_name) {
+ cache_storage_cache_name_ = cache_storage_cache_name;
+ }
+
+ const Vector<String>& CorsExposedHeaderNames() const {
+ return cors_exposed_header_names_;
+ }
+ void SetCorsExposedHeaderNames(const Vector<String>& header_names) {
+ cors_exposed_header_names_ = header_names;
+ }
+
+ bool DidServiceWorkerNavigationPreload() const {
+ return did_service_worker_navigation_preload_;
+ }
+ void SetDidServiceWorkerNavigationPreload(bool value) {
+ did_service_worker_navigation_preload_ = value;
+ }
+
+ Time ResponseTime() const { return response_time_; }
+ void SetResponseTime(Time response_time) { response_time_ = response_time; }
+
+ const AtomicString& RemoteIPAddress() const { return remote_ip_address_; }
+ void SetRemoteIPAddress(const AtomicString& value) {
+ remote_ip_address_ = value;
+ }
+
+ unsigned short RemotePort() const { return remote_port_; }
+ void SetRemotePort(unsigned short value) { remote_port_ = value; }
+
+ const AtomicString& AlpnNegotiatedProtocol() const {
+ return alpn_negotiated_protocol_;
+ }
+ void SetAlpnNegotiatedProtocol(const AtomicString& value) {
+ alpn_negotiated_protocol_ = value;
+ }
+
+ net::HttpResponseInfo::ConnectionInfo ConnectionInfo() const {
+ return connection_info_;
+ }
+ void SetConnectionInfo(net::HttpResponseInfo::ConnectionInfo value) {
+ connection_info_ = value;
+ }
+
+ AtomicString ConnectionInfoString() const;
+
+ long long EncodedDataLength() const { return encoded_data_length_; }
+ void SetEncodedDataLength(long long value);
+
+ long long EncodedBodyLength() const { return encoded_body_length_; }
+ void SetEncodedBodyLength(long long value);
+
+ long long DecodedBodyLength() const { return decoded_body_length_; }
+ void SetDecodedBodyLength(long long value);
+
+ const String& DownloadedFilePath() const { return downloaded_file_path_; }
+ void SetDownloadedFilePath(const String&);
+
+ // Extra data associated with this response.
+ ExtraData* GetExtraData() const { return extra_data_.get(); }
+ void SetExtraData(scoped_refptr<ExtraData> extra_data) {
+ extra_data_ = std::move(extra_data);
+ }
+
+ unsigned MemoryUsage() const {
+ // average size, mostly due to URL and Header Map strings
+ return 1280;
+ }
+
+ // PlzNavigate: Even if there is redirections, only one
+ // ResourceResponse is built: the final response.
+ // The redirect response chain can be accessed by this function.
+ const Vector<ResourceResponse>& RedirectResponses() const {
+ return redirect_responses_;
+ }
+ void AppendRedirectResponse(const ResourceResponse&);
+
+ // This method doesn't compare the all members.
+ static bool Compare(const ResourceResponse&, const ResourceResponse&);
+
+ private:
+ void UpdateHeaderParsedState(const AtomicString& name);
+
+ KURL url_;
+ AtomicString mime_type_;
+ long long expected_content_length_;
+ AtomicString text_encoding_name_;
+
+ unsigned connection_id_ = 0;
+ int http_status_code_ = 0;
+ AtomicString http_status_text_;
+ HTTPHeaderMap http_header_fields_;
+
+ // Remote IP address of the socket which fetched this resource.
+ AtomicString remote_ip_address_;
+
+ // Remote port number of the socket which fetched this resource.
+ unsigned short remote_port_ = 0;
+
+ bool was_cached_ = false;
+ bool connection_reused_ = false;
+ bool is_null_;
+ mutable bool have_parsed_age_header_ = false;
+ mutable bool have_parsed_date_header_ = false;
+ mutable bool have_parsed_expires_header_ = false;
+ mutable bool have_parsed_last_modified_header_ = false;
+
+ // True if the resource was retrieved by the embedder in spite of
+ // certificate errors.
+ bool has_major_certificate_errors_ = false;
+
+ // The Certificate Transparency policy compliance status of the resource.
+ CTPolicyCompliance ct_policy_compliance_ =
+ kCTPolicyComplianceDetailsNotAvailable;
+
+ // True if the resource was retrieved with a legacy Symantec certificate which
+ // is slated for distrust in future.
+ bool is_legacy_symantec_cert_ = false;
+
+ // The time at which the resource's certificate expires. Null if there was no
+ // certificate.
+ base::Time cert_validity_start_;
+
+ // Was the resource fetched over SPDY. See http://dev.chromium.org/spdy
+ bool was_fetched_via_spdy_ = false;
+
+ // Was the resource fetched over an explicit proxy (HTTP, SOCKS, etc).
+ bool was_fetched_via_proxy_ = false;
+
+ // Was the resource fetched over a ServiceWorker.
+ bool was_fetched_via_service_worker_ = false;
+
+ // Was the fallback request with skip service worker flag required.
+ bool was_fallback_required_by_service_worker_ = false;
+
+ // True if service worker navigation preload was performed due to
+ // the request for this resource.
+ bool did_service_worker_navigation_preload_ = false;
+
+ // The type of the response which was returned by the ServiceWorker.
+ network::mojom::FetchResponseType response_type_via_service_worker_ =
+ network::mojom::FetchResponseType::kDefault;
+
+ // HTTP version used in the response, if known.
+ HTTPVersion http_version_ = kHTTPVersionUnknown;
+
+ // The security style of the resource.
+ // This only contains a valid value when the DevTools Network domain is
+ // enabled. (Otherwise, it contains a default value of Unknown.)
+ SecurityStyle security_style_ = kSecurityStyleUnknown;
+
+ // Security details of this request's connection.
+ // If m_securityStyle is Unknown or Unauthenticated, this does not contain
+ // valid data.
+ SecurityDetails security_details_;
+
+ scoped_refptr<ResourceLoadTiming> resource_load_timing_;
+ scoped_refptr<ResourceLoadInfo> resource_load_info_;
+
+ mutable CacheControlHeader cache_control_header_;
+
+ mutable double age_ = 0.0;
+ mutable double date_ = 0.0;
+ mutable double expires_ = 0.0;
+ mutable double last_modified_ = 0.0;
+
+ // The id of the appcache this response was retrieved from, or zero if
+ // the response was not retrieved from an appcache.
+ long long app_cache_id_ = 0;
+
+ // The manifest url of the appcache this response was retrieved from, if any.
+ // Note: only valid for main resource responses.
+ KURL app_cache_manifest_url_;
+
+ // The multipart boundary of this response.
+ Vector<char> multipart_boundary_;
+
+ // The URL list of the response which was fetched by the ServiceWorker.
+ // This is empty if the response was created inside the ServiceWorker.
+ Vector<KURL> url_list_via_service_worker_;
+
+ // The cache name of the CacheStorage from where the response is served via
+ // the ServiceWorker. Null if the response isn't from the CacheStorage.
+ String cache_storage_cache_name_;
+
+ // The headers that should be exposed according to CORS. Only guaranteed
+ // to be set if the response was fetched by a ServiceWorker.
+ Vector<String> cors_exposed_header_names_;
+
+ // The time at which the response headers were received. For cached
+ // responses, this time could be "far" in the past.
+ Time response_time_;
+
+ // ALPN negotiated protocol of the socket which fetched this resource.
+ AtomicString alpn_negotiated_protocol_;
+
+ // Information about the type of connection used to fetch this resource.
+ net::HttpResponseInfo::ConnectionInfo connection_info_ =
+ net::HttpResponseInfo::ConnectionInfo::CONNECTION_INFO_UNKNOWN;
+
+ // Size of the response in bytes prior to decompression.
+ long long encoded_data_length_ = 0;
+
+ // Size of the response body in bytes prior to decompression.
+ long long encoded_body_length_ = 0;
+
+ // Sizes of the response body in bytes after any content-encoding is
+ // removed.
+ long long decoded_body_length_ = 0;
+
+ // The downloaded file path if the load streamed to a file.
+ String downloaded_file_path_;
+
+ // The handle to the downloaded file to ensure the underlying file will not
+ // be deleted.
+ scoped_refptr<BlobDataHandle> downloaded_file_handle_;
+
+ // ExtraData associated with the response.
+ scoped_refptr<ExtraData> extra_data_;
+
+ // PlzNavigate: the redirect responses are transmitted
+ // inside the final response.
+ Vector<ResourceResponse> redirect_responses_;
+};
+
+inline bool operator==(const ResourceResponse& a, const ResourceResponse& b) {
+ return ResourceResponse::Compare(a, b);
+}
+inline bool operator!=(const ResourceResponse& a, const ResourceResponse& b) {
+ return !(a == b);
+}
+
+// This class is needed to copy a ResourceResponse across threads, because it
+// has some members which cannot be transferred across threads (AtomicString
+// for example).
+// There are some rules / restrictions:
+// - This struct cannot contain an object that cannot be transferred across
+// threads (e.g., AtomicString)
+// - Non-simple members need explicit copying (e.g., String::IsolatedCopy,
+// KURL::Copy) rather than the copy constructor or the assignment operator.
+struct CrossThreadResourceResponseData {
+ WTF_MAKE_NONCOPYABLE(CrossThreadResourceResponseData);
+ USING_FAST_MALLOC(CrossThreadResourceResponseData);
+
+ public:
+ CrossThreadResourceResponseData() = default;
+ KURL url_;
+ String mime_type_;
+ long long expected_content_length_;
+ String text_encoding_name_;
+ int http_status_code_;
+ String http_status_text_;
+ std::unique_ptr<CrossThreadHTTPHeaderMapData> http_headers_;
+ scoped_refptr<ResourceLoadTiming> resource_load_timing_;
+ bool has_major_certificate_errors_;
+ ResourceResponse::CTPolicyCompliance ct_policy_compliance_;
+ bool is_legacy_symantec_cert_;
+ base::Time cert_validity_start_;
+ ResourceResponse::SecurityStyle security_style_;
+ ResourceResponse::SecurityDetails security_details_;
+ // This is |certificate| from SecurityDetails since that structure should
+ // use an AtomicString but this temporary structure is sent across threads.
+ Vector<String> certificate_;
+ ResourceResponse::HTTPVersion http_version_;
+ long long app_cache_id_;
+ KURL app_cache_manifest_url_;
+ Vector<char> multipart_boundary_;
+ bool was_fetched_via_spdy_;
+ bool was_fetched_via_proxy_;
+ bool was_fetched_via_service_worker_;
+ bool was_fallback_required_by_service_worker_;
+ network::mojom::FetchResponseType response_type_via_service_worker_;
+ Vector<KURL> url_list_via_service_worker_;
+ String cache_storage_cache_name_;
+ bool did_service_worker_navigation_preload_;
+ Time response_time_;
+ String remote_ip_address_;
+ unsigned short remote_port_;
+ long long encoded_data_length_;
+ long long encoded_body_length_;
+ long long decoded_body_length_;
+ String downloaded_file_path_;
+ scoped_refptr<BlobDataHandle> downloaded_file_handle_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_RESPONSE_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response_test.cc
new file mode 100644
index 00000000000..c22bfd36a9e
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response_test.cc
@@ -0,0 +1,87 @@
+// Copyright 2016 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/platform/loader/fetch/resource_response.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_thread.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
+
+namespace blink {
+
+namespace {
+
+ResourceResponse CreateTestResponse() {
+ ResourceResponse response;
+ response.AddHTTPHeaderField("age", "0");
+ response.AddHTTPHeaderField("cache-control", "no-cache");
+ response.AddHTTPHeaderField("date", "Tue, 17 Jan 2017 04:01:00 GMT");
+ response.AddHTTPHeaderField("expires", "Tue, 17 Jan 2017 04:11:00 GMT");
+ response.AddHTTPHeaderField("last-modified", "Tue, 17 Jan 2017 04:00:00 GMT");
+ response.AddHTTPHeaderField("pragma", "public");
+ response.AddHTTPHeaderField("etag", "abc");
+ response.AddHTTPHeaderField("content-disposition",
+ "attachment; filename=a.txt");
+ return response;
+}
+
+void RunHeaderRelatedTest(const ResourceResponse& response) {
+ EXPECT_EQ(0, response.Age());
+ EXPECT_NE(0, response.Date());
+ EXPECT_NE(0, response.Expires());
+ EXPECT_NE(0, response.LastModified());
+ EXPECT_EQ(true, response.CacheControlContainsNoCache());
+}
+
+void RunInThread() {
+ ResourceResponse response(CreateTestResponse());
+ RunHeaderRelatedTest(response);
+}
+
+} // namespace
+
+TEST(ResourceResponseTest, SignedCertificateTimestampIsolatedCopy) {
+ ResourceResponse::SignedCertificateTimestamp src(
+ "status", "origin", "logDescription", "logId", 7, "hashAlgorithm",
+ "signatureAlgorithm", "signatureData");
+
+ ResourceResponse::SignedCertificateTimestamp dest = src.IsolatedCopy();
+
+ EXPECT_EQ(src.status_, dest.status_);
+ EXPECT_NE(src.status_.Impl(), dest.status_.Impl());
+ EXPECT_EQ(src.origin_, dest.origin_);
+ EXPECT_NE(src.origin_.Impl(), dest.origin_.Impl());
+ EXPECT_EQ(src.log_description_, dest.log_description_);
+ EXPECT_NE(src.log_description_.Impl(), dest.log_description_.Impl());
+ EXPECT_EQ(src.log_id_, dest.log_id_);
+ EXPECT_NE(src.log_id_.Impl(), dest.log_id_.Impl());
+ EXPECT_EQ(src.timestamp_, dest.timestamp_);
+ EXPECT_EQ(src.hash_algorithm_, dest.hash_algorithm_);
+ EXPECT_NE(src.hash_algorithm_.Impl(), dest.hash_algorithm_.Impl());
+ EXPECT_EQ(src.signature_algorithm_, dest.signature_algorithm_);
+ EXPECT_NE(src.signature_algorithm_.Impl(), dest.signature_algorithm_.Impl());
+ EXPECT_EQ(src.signature_data_, dest.signature_data_);
+ EXPECT_NE(src.signature_data_.Impl(), dest.signature_data_.Impl());
+}
+
+// This test checks that AtomicStrings in ResourceResponse doesn't cause the
+// failure of ThreadRestrictionVerifier check.
+TEST(ResourceResponseTest, CrossThreadAtomicStrings) {
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform;
+
+ ResourceResponse response(CreateTestResponse());
+ RunHeaderRelatedTest(response);
+ std::unique_ptr<WebThread> thread = Platform::Current()->CreateThread(
+ WebThreadCreationParams(WebThreadType::kTestThread)
+ .SetThreadNameForTest("WorkerThread"));
+ PostCrossThreadTask(*thread->GetTaskRunner(), FROM_HERE,
+ CrossThreadBind(&RunInThread));
+ thread.reset();
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_status.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_status.h
new file mode 100644
index 00000000000..28773d48bf2
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_status.h
@@ -0,0 +1,20 @@
+// Copyright 2016 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_PLATFORM_LOADER_FETCH_RESOURCE_STATUS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_STATUS_H_
+
+namespace blink {
+
+enum class ResourceStatus : uint8_t {
+ kNotStarted,
+ kPending, // load in progress
+ kCached, // load completed successfully
+ kLoadError,
+ kDecodeError
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_test.cc
new file mode 100644
index 00000000000..453c1c32137
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_test.cc
@@ -0,0 +1,424 @@
+// Copyright 2015 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/platform/loader/fetch/resource.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+#include "third_party/blink/renderer/platform/loader/testing/mock_resource.h"
+#include "third_party/blink/renderer/platform/loader/testing/mock_resource_client.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+#include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
+#include "third_party/blink/renderer/platform/wtf/time.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+namespace {
+
+class MockPlatform final : public TestingPlatformSupportWithMockScheduler {
+ public:
+ MockPlatform() = default;
+ ~MockPlatform() override = default;
+
+ // From blink::Platform:
+ void CacheMetadata(const WebURL& url, Time, const char*, size_t) override {
+ cached_urls_.push_back(url);
+ }
+
+ const Vector<WebURL>& CachedURLs() const { return cached_urls_; }
+
+ private:
+ Vector<WebURL> cached_urls_;
+};
+
+ResourceResponse CreateTestResourceResponse() {
+ ResourceResponse response(URLTestHelpers::ToKURL("https://example.com/"));
+ response.SetHTTPStatusCode(200);
+ return response;
+}
+
+void CreateTestResourceAndSetCachedMetadata(const ResourceResponse& response) {
+ const char kTestData[] = "test data";
+ MockResource* resource = MockResource::Create(response.Url());
+ resource->SetResponse(response);
+ resource->SendCachedMetadata(kTestData, sizeof(kTestData));
+ return;
+}
+
+} // anonymous namespace
+
+TEST(ResourceTest, SetCachedMetadata_SendsMetadataToPlatform) {
+ ScopedTestingPlatformSupport<MockPlatform> mock;
+ ResourceResponse response(CreateTestResourceResponse());
+ CreateTestResourceAndSetCachedMetadata(response);
+ EXPECT_EQ(1u, mock->CachedURLs().size());
+}
+
+TEST(
+ ResourceTest,
+ SetCachedMetadata_DoesNotSendMetadataToPlatformWhenFetchedViaServiceWorker) {
+ ScopedTestingPlatformSupport<MockPlatform> mock;
+ ResourceResponse response(CreateTestResourceResponse());
+ response.SetWasFetchedViaServiceWorker(true);
+ CreateTestResourceAndSetCachedMetadata(response);
+ EXPECT_EQ(0u, mock->CachedURLs().size());
+}
+
+TEST(ResourceTest, RevalidateWithFragment) {
+ ScopedTestingPlatformSupport<MockPlatform> mock;
+ KURL url("http://127.0.0.1:8000/foo.html");
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+ MockResource* resource = MockResource::Create(url);
+ resource->ResponseReceived(response, nullptr);
+ resource->FinishForTest();
+
+ // Revalidating with a url that differs by only the fragment
+ // shouldn't trigger a securiy check.
+ url.SetFragmentIdentifier("bar");
+ resource->SetRevalidatingRequest(ResourceRequest(url));
+ ResourceResponse revalidating_response(url);
+ revalidating_response.SetHTTPStatusCode(304);
+ resource->ResponseReceived(revalidating_response, nullptr);
+}
+
+TEST(ResourceTest, Vary) {
+ ScopedTestingPlatformSupport<MockPlatform> mock;
+ const KURL url("http://127.0.0.1:8000/foo.html");
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+
+ MockResource* resource = MockResource::Create(url);
+ resource->ResponseReceived(response, nullptr);
+ resource->FinishForTest();
+
+ ResourceRequest new_request(url);
+ EXPECT_FALSE(resource->MustReloadDueToVaryHeader(new_request));
+
+ response.SetHTTPHeaderField(HTTPNames::Vary, "*");
+ resource->SetResponse(response);
+ EXPECT_TRUE(resource->MustReloadDueToVaryHeader(new_request));
+
+ // Irrelevant header
+ response.SetHTTPHeaderField(HTTPNames::Vary, "definitelynotarealheader");
+ resource->SetResponse(response);
+ EXPECT_FALSE(resource->MustReloadDueToVaryHeader(new_request));
+
+ // Header present on new but not old
+ new_request.SetHTTPHeaderField(HTTPNames::User_Agent, "something");
+ response.SetHTTPHeaderField(HTTPNames::Vary, HTTPNames::User_Agent);
+ resource->SetResponse(response);
+ EXPECT_TRUE(resource->MustReloadDueToVaryHeader(new_request));
+ new_request.ClearHTTPHeaderField(HTTPNames::User_Agent);
+
+ ResourceRequest old_request(url);
+ old_request.SetHTTPHeaderField(HTTPNames::User_Agent, "something");
+ old_request.SetHTTPHeaderField(HTTPNames::Referer, "http://foo.com");
+ resource = MockResource::Create(old_request);
+ resource->ResponseReceived(response, nullptr);
+ resource->FinishForTest();
+
+ // Header present on old but not new
+ new_request.ClearHTTPHeaderField(HTTPNames::User_Agent);
+ response.SetHTTPHeaderField(HTTPNames::Vary, HTTPNames::User_Agent);
+ resource->SetResponse(response);
+ EXPECT_TRUE(resource->MustReloadDueToVaryHeader(new_request));
+
+ // Header present on both
+ new_request.SetHTTPHeaderField(HTTPNames::User_Agent, "something");
+ EXPECT_FALSE(resource->MustReloadDueToVaryHeader(new_request));
+
+ // One matching, one mismatching
+ response.SetHTTPHeaderField(HTTPNames::Vary, "User-Agent, Referer");
+ resource->SetResponse(response);
+ EXPECT_TRUE(resource->MustReloadDueToVaryHeader(new_request));
+
+ // Two matching
+ new_request.SetHTTPHeaderField(HTTPNames::Referer, "http://foo.com");
+ EXPECT_FALSE(resource->MustReloadDueToVaryHeader(new_request));
+}
+
+TEST(ResourceTest, RevalidationFailed) {
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform_;
+ const KURL url("http://test.example.com/");
+ MockResource* resource = MockResource::Create(ResourceRequest(url));
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+ resource->ResponseReceived(response, nullptr);
+ const char kData[5] = "abcd";
+ resource->AppendData(kData, 4);
+ resource->FinishForTest();
+ GetMemoryCache()->Add(resource);
+
+ MockCacheHandler* original_cache_handler = resource->CacheHandler();
+ EXPECT_TRUE(original_cache_handler);
+
+ // Simulate revalidation start.
+ resource->SetRevalidatingRequest(ResourceRequest(url));
+
+ EXPECT_EQ(original_cache_handler, resource->CacheHandler());
+
+ Persistent<MockResourceClient> client = new MockResourceClient;
+ resource->AddClient(client, nullptr);
+
+ ResourceResponse revalidating_response(url);
+ revalidating_response.SetHTTPStatusCode(200);
+ resource->ResponseReceived(revalidating_response, nullptr);
+
+ EXPECT_FALSE(resource->IsCacheValidator());
+ EXPECT_EQ(200, resource->GetResponse().HttpStatusCode());
+ EXPECT_FALSE(resource->ResourceBuffer());
+ EXPECT_TRUE(resource->CacheHandler());
+ EXPECT_NE(original_cache_handler, resource->CacheHandler());
+ EXPECT_EQ(resource, GetMemoryCache()->ResourceForURL(url));
+
+ resource->AppendData(kData, 4);
+
+ EXPECT_FALSE(client->NotifyFinishedCalled());
+
+ resource->FinishForTest();
+
+ EXPECT_TRUE(client->NotifyFinishedCalled());
+
+ resource->RemoveClient(client);
+ EXPECT_FALSE(resource->IsAlive());
+}
+
+TEST(ResourceTest, RevalidationSucceeded) {
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform_;
+ const KURL url("http://test.example.com/");
+ MockResource* resource = MockResource::Create(ResourceRequest(url));
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+ resource->ResponseReceived(response, nullptr);
+ const char kData[5] = "abcd";
+ resource->AppendData(kData, 4);
+ resource->FinishForTest();
+ GetMemoryCache()->Add(resource);
+
+ MockCacheHandler* original_cache_handler = resource->CacheHandler();
+ EXPECT_TRUE(original_cache_handler);
+
+ // Simulate a successful revalidation.
+ resource->SetRevalidatingRequest(ResourceRequest(url));
+
+ EXPECT_EQ(original_cache_handler, resource->CacheHandler());
+
+ Persistent<MockResourceClient> client = new MockResourceClient;
+ resource->AddClient(client, nullptr);
+
+ ResourceResponse revalidating_response(url);
+ revalidating_response.SetHTTPStatusCode(304);
+ resource->ResponseReceived(revalidating_response, nullptr);
+
+ EXPECT_FALSE(resource->IsCacheValidator());
+ EXPECT_EQ(200, resource->GetResponse().HttpStatusCode());
+ EXPECT_EQ(4u, resource->ResourceBuffer()->size());
+ EXPECT_EQ(original_cache_handler, resource->CacheHandler());
+ EXPECT_EQ(resource, GetMemoryCache()->ResourceForURL(url));
+
+ GetMemoryCache()->Remove(resource);
+
+ resource->RemoveClient(client);
+ EXPECT_FALSE(resource->IsAlive());
+ EXPECT_FALSE(client->NotifyFinishedCalled());
+}
+
+TEST(ResourceTest, RevalidationSucceededForResourceWithoutBody) {
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform_;
+ const KURL url("http://test.example.com/");
+ Resource* resource = MockResource::Create(ResourceRequest(url));
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+ resource->ResponseReceived(response, nullptr);
+ resource->FinishForTest();
+ GetMemoryCache()->Add(resource);
+
+ // Simulate a successful revalidation.
+ resource->SetRevalidatingRequest(ResourceRequest(url));
+
+ Persistent<MockResourceClient> client = new MockResourceClient;
+ resource->AddClient(client, nullptr);
+
+ ResourceResponse revalidating_response(url);
+ revalidating_response.SetHTTPStatusCode(304);
+ resource->ResponseReceived(revalidating_response, nullptr);
+ EXPECT_FALSE(resource->IsCacheValidator());
+ EXPECT_EQ(200, resource->GetResponse().HttpStatusCode());
+ EXPECT_FALSE(resource->ResourceBuffer());
+ EXPECT_EQ(resource, GetMemoryCache()->ResourceForURL(url));
+ GetMemoryCache()->Remove(resource);
+
+ resource->RemoveClient(client);
+ EXPECT_FALSE(resource->IsAlive());
+ EXPECT_FALSE(client->NotifyFinishedCalled());
+}
+
+TEST(ResourceTest, RevalidationSucceededUpdateHeaders) {
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform_;
+ const KURL url("http://test.example.com/");
+ Resource* resource = MockResource::Create(ResourceRequest(url));
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+ response.AddHTTPHeaderField("keep-alive", "keep-alive value");
+ response.AddHTTPHeaderField("expires", "expires value");
+ response.AddHTTPHeaderField("last-modified", "last-modified value");
+ response.AddHTTPHeaderField("proxy-authenticate", "proxy-authenticate value");
+ response.AddHTTPHeaderField("proxy-connection", "proxy-connection value");
+ response.AddHTTPHeaderField("x-custom", "custom value");
+ resource->ResponseReceived(response, nullptr);
+ resource->FinishForTest();
+ GetMemoryCache()->Add(resource);
+
+ // Simulate a successful revalidation.
+ resource->SetRevalidatingRequest(ResourceRequest(url));
+
+ // Validate that these headers pre-update.
+ EXPECT_EQ("keep-alive value",
+ resource->GetResponse().HttpHeaderField("keep-alive"));
+ EXPECT_EQ("expires value",
+ resource->GetResponse().HttpHeaderField("expires"));
+ EXPECT_EQ("last-modified value",
+ resource->GetResponse().HttpHeaderField("last-modified"));
+ EXPECT_EQ("proxy-authenticate value",
+ resource->GetResponse().HttpHeaderField("proxy-authenticate"));
+ EXPECT_EQ("proxy-authenticate value",
+ resource->GetResponse().HttpHeaderField("proxy-authenticate"));
+ EXPECT_EQ("proxy-connection value",
+ resource->GetResponse().HttpHeaderField("proxy-connection"));
+ EXPECT_EQ("custom value",
+ resource->GetResponse().HttpHeaderField("x-custom"));
+
+ Persistent<MockResourceClient> client = new MockResourceClient;
+ resource->AddClient(client, nullptr);
+
+ // Perform a revalidation step.
+ ResourceResponse revalidating_response(url);
+ revalidating_response.SetHTTPStatusCode(304);
+ // Headers that aren't copied with an 304 code.
+ revalidating_response.AddHTTPHeaderField("keep-alive", "garbage");
+ revalidating_response.AddHTTPHeaderField("expires", "garbage");
+ revalidating_response.AddHTTPHeaderField("last-modified", "garbage");
+ revalidating_response.AddHTTPHeaderField("proxy-authenticate", "garbage");
+ revalidating_response.AddHTTPHeaderField("proxy-connection", "garbage");
+ // Header that is updated with 304 code.
+ revalidating_response.AddHTTPHeaderField("x-custom", "updated");
+ resource->ResponseReceived(revalidating_response, nullptr);
+
+ // Validate the original response.
+ EXPECT_EQ(200, resource->GetResponse().HttpStatusCode());
+
+ // Validate that these headers are not updated.
+ EXPECT_EQ("keep-alive value",
+ resource->GetResponse().HttpHeaderField("keep-alive"));
+ EXPECT_EQ("expires value",
+ resource->GetResponse().HttpHeaderField("expires"));
+ EXPECT_EQ("last-modified value",
+ resource->GetResponse().HttpHeaderField("last-modified"));
+ EXPECT_EQ("proxy-authenticate value",
+ resource->GetResponse().HttpHeaderField("proxy-authenticate"));
+ EXPECT_EQ("proxy-authenticate value",
+ resource->GetResponse().HttpHeaderField("proxy-authenticate"));
+ EXPECT_EQ("proxy-connection value",
+ resource->GetResponse().HttpHeaderField("proxy-connection"));
+ EXPECT_EQ("updated", resource->GetResponse().HttpHeaderField("x-custom"));
+
+ resource->RemoveClient(client);
+ EXPECT_FALSE(resource->IsAlive());
+ EXPECT_FALSE(client->NotifyFinishedCalled());
+}
+
+TEST(ResourceTest, RedirectDuringRevalidation) {
+ ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+ platform_;
+ const KURL url("http://test.example.com/1");
+ const KURL redirect_target_url("http://test.example.com/2");
+
+ MockResource* resource = MockResource::Create(ResourceRequest(url));
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+ resource->ResponseReceived(response, nullptr);
+ const char kData[5] = "abcd";
+ resource->AppendData(kData, 4);
+ resource->FinishForTest();
+ GetMemoryCache()->Add(resource);
+
+ EXPECT_FALSE(resource->IsCacheValidator());
+ EXPECT_EQ(url, resource->GetResourceRequest().Url());
+ EXPECT_EQ(url, resource->LastResourceRequest().Url());
+
+ MockCacheHandler* original_cache_handler = resource->CacheHandler();
+ EXPECT_TRUE(original_cache_handler);
+
+ // Simulate a revalidation.
+ resource->SetRevalidatingRequest(ResourceRequest(url));
+ EXPECT_TRUE(resource->IsCacheValidator());
+ EXPECT_EQ(url, resource->GetResourceRequest().Url());
+ EXPECT_EQ(url, resource->LastResourceRequest().Url());
+ EXPECT_EQ(original_cache_handler, resource->CacheHandler());
+
+ Persistent<MockResourceClient> client = new MockResourceClient;
+ resource->AddClient(client, nullptr);
+
+ // The revalidating request is redirected.
+ ResourceResponse redirect_response(url);
+ redirect_response.SetHTTPHeaderField(
+ "location", AtomicString(redirect_target_url.GetString()));
+ redirect_response.SetHTTPStatusCode(308);
+ ResourceRequest redirected_revalidating_request(redirect_target_url);
+ resource->WillFollowRedirect(redirected_revalidating_request,
+ redirect_response);
+ EXPECT_FALSE(resource->IsCacheValidator());
+ EXPECT_EQ(url, resource->GetResourceRequest().Url());
+ EXPECT_EQ(redirect_target_url, resource->LastResourceRequest().Url());
+ EXPECT_FALSE(resource->CacheHandler());
+
+ // The final response is received.
+ ResourceResponse revalidating_response(redirect_target_url);
+ revalidating_response.SetHTTPStatusCode(200);
+ resource->ResponseReceived(revalidating_response, nullptr);
+
+ EXPECT_TRUE(resource->CacheHandler());
+
+ const char kData2[4] = "xyz";
+ resource->AppendData(kData2, 3);
+ resource->FinishForTest();
+ EXPECT_FALSE(resource->IsCacheValidator());
+ EXPECT_EQ(url, resource->GetResourceRequest().Url());
+ EXPECT_EQ(redirect_target_url, resource->LastResourceRequest().Url());
+ EXPECT_FALSE(resource->IsCacheValidator());
+ EXPECT_EQ(200, resource->GetResponse().HttpStatusCode());
+ EXPECT_EQ(3u, resource->ResourceBuffer()->size());
+ EXPECT_EQ(resource, GetMemoryCache()->ResourceForURL(url));
+
+ EXPECT_TRUE(client->NotifyFinishedCalled());
+
+ // Test the case where a client is added after revalidation is completed.
+ Persistent<MockResourceClient> client2 = new MockResourceClient;
+ resource->AddClient(
+ client2, Platform::Current()->CurrentThread()->GetTaskRunner().get());
+
+ // Because the client is added asynchronously,
+ // |runUntilIdle()| is called to make |client2| to be notified.
+ platform_->RunUntilIdle();
+
+ EXPECT_TRUE(client2->NotifyFinishedCalled());
+
+ GetMemoryCache()->Remove(resource);
+
+ resource->RemoveClient(client);
+ resource->RemoveClient(client2);
+ EXPECT_FALSE(resource->IsAlive());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_timing_info.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_timing_info.cc
new file mode 100644
index 00000000000..31574cfc049
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_timing_info.cc
@@ -0,0 +1,61 @@
+// Copyright 2015 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/platform/loader/fetch/resource_timing_info.h"
+
+#include <memory>
+#include "third_party/blink/renderer/platform/cross_thread_copier.h"
+
+namespace blink {
+
+scoped_refptr<ResourceTimingInfo> ResourceTimingInfo::Adopt(
+ std::unique_ptr<CrossThreadResourceTimingInfoData> data) {
+ scoped_refptr<ResourceTimingInfo> info = ResourceTimingInfo::Create(
+ AtomicString(data->type_), data->initial_time_, data->is_main_resource_);
+ info->original_timing_allow_origin_ =
+ AtomicString(data->original_timing_allow_origin_);
+ info->load_finish_time_ = data->load_finish_time_;
+ info->initial_url_ = data->initial_url_.Copy();
+ info->final_response_ = ResourceResponse(data->final_response_.get());
+ for (auto& response_data : data->redirect_chain_)
+ info->redirect_chain_.push_back(ResourceResponse(response_data.get()));
+ info->transfer_size_ = data->transfer_size_;
+ info->negative_allowed_ = data->negative_allowed_;
+ return info;
+}
+
+std::unique_ptr<CrossThreadResourceTimingInfoData>
+ResourceTimingInfo::CopyData() const {
+ std::unique_ptr<CrossThreadResourceTimingInfoData> data =
+ std::make_unique<CrossThreadResourceTimingInfoData>();
+ data->type_ = type_.GetString().IsolatedCopy();
+ data->original_timing_allow_origin_ =
+ original_timing_allow_origin_.GetString().IsolatedCopy();
+ data->initial_time_ = initial_time_;
+ data->load_finish_time_ = load_finish_time_;
+ data->initial_url_ = initial_url_.Copy();
+ data->final_response_ = final_response_.CopyData();
+ for (const auto& response : redirect_chain_)
+ data->redirect_chain_.push_back(response.CopyData());
+ data->transfer_size_ = transfer_size_;
+ data->is_main_resource_ = is_main_resource_;
+ data->negative_allowed_ = negative_allowed_;
+ return data;
+}
+
+void ResourceTimingInfo::AddRedirect(const ResourceResponse& redirect_response,
+ bool cross_origin) {
+ redirect_chain_.push_back(redirect_response);
+ if (has_cross_origin_redirect_)
+ return;
+ if (cross_origin) {
+ has_cross_origin_redirect_ = true;
+ transfer_size_ = 0;
+ } else {
+ DCHECK_GE(redirect_response.EncodedDataLength(), 0);
+ transfer_size_ += redirect_response.EncodedDataLength();
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h
new file mode 100644
index 00000000000..dd964d8be55
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2013 Intel Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_TIMING_INFO_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_TIMING_INFO_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/cross_thread_copier.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+
+namespace blink {
+
+struct CrossThreadResourceTimingInfoData;
+
+class PLATFORM_EXPORT ResourceTimingInfo
+ : public RefCounted<ResourceTimingInfo> {
+ USING_FAST_MALLOC(ResourceTimingInfo);
+ WTF_MAKE_NONCOPYABLE(ResourceTimingInfo);
+
+ public:
+ static scoped_refptr<ResourceTimingInfo> Create(const AtomicString& type,
+ const double time,
+ bool is_main_resource) {
+ return base::AdoptRef(new ResourceTimingInfo(type, time, is_main_resource));
+ }
+ static scoped_refptr<ResourceTimingInfo> Adopt(
+ std::unique_ptr<CrossThreadResourceTimingInfoData>);
+
+ // Gets a copy of the data suitable for passing to another thread.
+ std::unique_ptr<CrossThreadResourceTimingInfoData> CopyData() const;
+
+ double InitialTime() const { return initial_time_; }
+ bool IsMainResource() const { return is_main_resource_; }
+
+ const AtomicString& InitiatorType() const { return type_; }
+
+ void SetOriginalTimingAllowOrigin(
+ const AtomicString& original_timing_allow_origin) {
+ original_timing_allow_origin_ = original_timing_allow_origin;
+ }
+ const AtomicString& OriginalTimingAllowOrigin() const {
+ return original_timing_allow_origin_;
+ }
+
+ void SetLoadFinishTime(double time) { load_finish_time_ = time; }
+ double LoadFinishTime() const { return load_finish_time_; }
+
+ void SetInitialURL(const KURL& url) { initial_url_ = url; }
+ const KURL& InitialURL() const { return initial_url_; }
+
+ void SetFinalResponse(const ResourceResponse& response) {
+ final_response_ = response;
+ }
+ const ResourceResponse& FinalResponse() const { return final_response_; }
+
+ void AddRedirect(const ResourceResponse& redirect_response,
+ bool cross_origin);
+ const Vector<ResourceResponse>& RedirectChain() const {
+ return redirect_chain_;
+ }
+
+ void AddFinalTransferSize(long long encoded_data_length) {
+ transfer_size_ += encoded_data_length;
+ }
+ long long TransferSize() const { return transfer_size_; }
+
+ void ClearLoadTimings() {
+ final_response_.SetResourceLoadTiming(nullptr);
+ for (ResourceResponse& redirect : redirect_chain_)
+ redirect.SetResourceLoadTiming(nullptr);
+ }
+
+ // The timestamps in PerformanceResourceTiming are measured relative from the
+ // time origin. In most cases these timestamps must be positive value, so we
+ // use 0 for invalid negative values. But the timestamps for Service Worker
+ // navigation preload requests may be negative, because these requests may
+ // be started before the service worker started. We set this flag true, to
+ // support such case.
+ void SetNegativeAllowed(bool negative_allowed) {
+ negative_allowed_ = negative_allowed;
+ }
+ bool NegativeAllowed() const { return negative_allowed_; }
+
+ private:
+ ResourceTimingInfo(const AtomicString& type,
+ const double time,
+ bool is_main_resource)
+ : type_(type), initial_time_(time), is_main_resource_(is_main_resource) {}
+
+ AtomicString type_;
+ AtomicString original_timing_allow_origin_;
+ double initial_time_;
+ double load_finish_time_;
+ KURL initial_url_;
+ ResourceResponse final_response_;
+ Vector<ResourceResponse> redirect_chain_;
+ long long transfer_size_ = 0;
+ bool is_main_resource_;
+ bool has_cross_origin_redirect_ = false;
+ bool negative_allowed_ = false;
+};
+
+struct CrossThreadResourceTimingInfoData {
+ WTF_MAKE_NONCOPYABLE(CrossThreadResourceTimingInfoData);
+ USING_FAST_MALLOC(CrossThreadResourceTimingInfoData);
+
+ public:
+ CrossThreadResourceTimingInfoData() = default;
+
+ String type_;
+ String original_timing_allow_origin_;
+ double initial_time_;
+ double load_finish_time_;
+ KURL initial_url_;
+ std::unique_ptr<CrossThreadResourceResponseData> final_response_;
+ Vector<std::unique_ptr<CrossThreadResourceResponseData>> redirect_chain_;
+ long long transfer_size_;
+ bool is_main_resource_;
+ bool negative_allowed_;
+};
+
+template <>
+struct CrossThreadCopier<ResourceTimingInfo> {
+ typedef WTF::PassedWrapper<std::unique_ptr<CrossThreadResourceTimingInfoData>>
+ Type;
+ static Type Copy(const ResourceTimingInfo& info) {
+ return WTF::Passed(info.CopyData());
+ }
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.cc
new file mode 100644
index 00000000000..7fca9edf686
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.cc
@@ -0,0 +1,69 @@
+// Copyright 2017 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/platform/loader/fetch/script_fetch_options.h"
+
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+
+namespace blink {
+
+// https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-classic-script
+FetchParameters ScriptFetchOptions::CreateFetchParameters(
+ const KURL& url,
+ const SecurityOrigin* security_origin,
+ const WTF::TextEncoding& encoding,
+ FetchParameters::DeferOption defer) const {
+ // Step 1. Let request be the result of creating a potential-CORS request
+ // given url, ... [spec text]
+ ResourceRequest resource_request(url);
+
+ // Step 1. ... "script", ... [spec text]
+ ResourceLoaderOptions resource_loader_options;
+ resource_loader_options.initiator_info.name = "script";
+ FetchParameters params(resource_request, resource_loader_options);
+
+ // Step 1. ... and CORS setting. [spec text]
+ //
+ // Instead of using CrossOriginAttributeValue that corresponds to |CORS
+ // setting|, we use ScriptFetchOptions::CredentialsMode().
+ // We shouldn't call SetCrossOriginAccessControl() if CredentialsMode() is
+ // kFetchCredentialsModeOmit, because in that case the request should be
+ // no-cors, while SetCrossOriginAccessControl(kFetchCredentialsModeOmit)
+ // would result in a cors request.
+ if (CredentialsMode() != network::mojom::FetchCredentialsMode::kOmit) {
+ params.SetCrossOriginAccessControl(security_origin, CredentialsMode());
+ }
+
+ // Step 2. Set request's client to settings object. [spec text]
+ // Note: Implemented at ClassicPendingScript::Fetch().
+
+ // Step 3. Set up the classic script request given request and options. [spec
+ // text]
+ //
+ // https://html.spec.whatwg.org/multipage/webappapis.html#set-up-the-classic-script-request
+ // Set request's cryptographic nonce metadata to options's cryptographic
+ // nonce, [spec text]
+ params.SetContentSecurityPolicyNonce(Nonce());
+
+ // its integrity metadata to options's integrity metadata, [spec text]
+ params.SetIntegrityMetadata(GetIntegrityMetadata());
+ params.MutableResourceRequest().SetFetchIntegrity(
+ GetIntegrityAttributeValue());
+
+ // and its parser metadata to options's parser metadata. [spec text]
+ params.SetParserDisposition(ParserState());
+
+ params.SetCharset(encoding);
+
+ // This DeferOption logic is only for classic scripts, as we always set
+ // |kLazyLoad| for module scripts in ModuleScriptLoader.
+ params.SetDefer(defer);
+
+ // Steps 4- are Implemented at ClassicPendingScript::Fetch().
+
+ return params;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.h b/chromium/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.h
new file mode 100644
index 00000000000..57883285a4a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.h
@@ -0,0 +1,82 @@
+// Copyright 2017 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_PLATFORM_LOADER_FETCH_SCRIPT_FETCH_OPTIONS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_SCRIPT_FETCH_OPTIONS_H_
+
+#include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
+#include "third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class KURL;
+class SecurityOrigin;
+
+// ScriptFetchOptions corresponds to the spec concept "script fetch options".
+// https://html.spec.whatwg.org/multipage/webappapis.html#script-fetch-options
+class PLATFORM_EXPORT ScriptFetchOptions final {
+ public:
+ // https://html.spec.whatwg.org/multipage/webappapis.html#default-classic-script-fetch-options
+ // "The default classic script fetch options are a script fetch options whose
+ // cryptographic nonce is the empty string, integrity metadata is the empty
+ // string, parser metadata is "not-parser-inserted", and credentials mode
+ // is "omit"." [spec text]
+ ScriptFetchOptions()
+ : parser_state_(ParserDisposition::kNotParserInserted),
+ credentials_mode_(network::mojom::FetchCredentialsMode::kOmit) {}
+
+ ScriptFetchOptions(const String& nonce,
+ const IntegrityMetadataSet& integrity_metadata,
+ const String& integrity_attribute,
+ ParserDisposition parser_state,
+ network::mojom::FetchCredentialsMode credentials_mode)
+ : nonce_(nonce),
+ integrity_metadata_(integrity_metadata),
+ integrity_attribute_(integrity_attribute),
+ parser_state_(parser_state),
+ credentials_mode_(credentials_mode) {}
+ ~ScriptFetchOptions() = default;
+
+ const String& Nonce() const { return nonce_; }
+ const IntegrityMetadataSet& GetIntegrityMetadata() const {
+ return integrity_metadata_;
+ }
+ const String& GetIntegrityAttributeValue() const {
+ return integrity_attribute_;
+ }
+ const ParserDisposition& ParserState() const { return parser_state_; }
+ network::mojom::FetchCredentialsMode CredentialsMode() const {
+ return credentials_mode_;
+ }
+
+ // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-classic-script
+ // Steps 1 and 3.
+ FetchParameters CreateFetchParameters(const KURL&,
+ const SecurityOrigin*,
+ const WTF::TextEncoding&,
+ FetchParameters::DeferOption) const;
+
+ private:
+ // https://html.spec.whatwg.org/multipage/webappapis.html#concept-script-fetch-options-nonce
+ const String nonce_;
+
+ // https://html.spec.whatwg.org/multipage/webappapis.html#concept-script-fetch-options-integrity
+ const IntegrityMetadataSet integrity_metadata_;
+ const String integrity_attribute_;
+
+ // https://html.spec.whatwg.org/multipage/webappapis.html#concept-script-fetch-options-parser
+ const ParserDisposition parser_state_;
+
+ // https://html.spec.whatwg.org/multipage/webappapis.html#concept-script-fetch-options-credentials
+ const network::mojom::FetchCredentialsMode credentials_mode_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.cc
new file mode 100644
index 00000000000..4e4e33a3147
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.cc
@@ -0,0 +1,218 @@
+// Copyright 2018 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/platform/loader/fetch/source_keyed_cached_metadata_handler.h"
+
+#include "base/bit_cast.h"
+#include "third_party/blink/renderer/platform/crypto.h"
+#include "third_party/blink/renderer/platform/loader/fetch/cached_metadata.h"
+#include "third_party/blink/renderer/platform/wtf/string_hasher.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+// Defined here for storage/ODR reasons, but initialized in the header.
+const size_t SourceKeyedCachedMetadataHandler::kKeySize;
+
+class SourceKeyedCachedMetadataHandler::SingleKeyHandler final
+ : public SingleCachedMetadataHandler {
+ public:
+ void Trace(Visitor* visitor) override {
+ visitor->Trace(parent_);
+ SingleCachedMetadataHandler::Trace(visitor);
+ }
+
+ SingleKeyHandler(SourceKeyedCachedMetadataHandler* parent, Key key)
+ : parent_(parent), key_(key) {}
+
+ void SetCachedMetadata(uint32_t data_type_id,
+ const char* data,
+ size_t size,
+ CacheType cache_type) override {
+ DCHECK(!parent_->cached_metadata_map_.Contains(key_));
+ parent_->cached_metadata_map_.insert(
+ key_, CachedMetadata::Create(data_type_id, data, size));
+ if (cache_type == CachedMetadataHandler::kSendToPlatform)
+ parent_->SendToPlatform();
+ }
+
+ void ClearCachedMetadata(CacheType cache_type) override {
+ parent_->cached_metadata_map_.erase(key_);
+ if (cache_type == CachedMetadataHandler::kSendToPlatform)
+ parent_->SendToPlatform();
+ }
+
+ scoped_refptr<CachedMetadata> GetCachedMetadata(
+ uint32_t data_type_id) const override {
+ scoped_refptr<CachedMetadata> cached_metadata =
+ parent_->cached_metadata_map_.at(key_);
+ if (!cached_metadata || cached_metadata->DataTypeID() != data_type_id)
+ return nullptr;
+ return cached_metadata;
+ }
+
+ String Encoding() const override { return parent_->Encoding(); }
+
+ bool IsServedFromCacheStorage() const override {
+ return parent_->IsServedFromCacheStorage();
+ }
+
+ private:
+ Member<SourceKeyedCachedMetadataHandler> parent_;
+ Key key_;
+};
+
+class SourceKeyedCachedMetadataHandler::KeyHash {
+ public:
+ static unsigned GetHash(const Key& key) {
+ return StringHasher::ComputeHash(key.data(), key.size());
+ }
+
+ static bool Equal(const Key& a, const Key& b) { return a == b; }
+
+ static const bool safe_to_compare_to_empty_or_deleted = true;
+};
+
+SingleCachedMetadataHandler* SourceKeyedCachedMetadataHandler::HandlerForSource(
+ const String& source) {
+ DigestValue digest_value;
+
+ if (!ComputeDigest(kHashAlgorithmSha256,
+ static_cast<const char*>(source.Bytes()),
+ source.CharactersSizeInBytes(), digest_value))
+ return nullptr;
+
+ Key key;
+ DCHECK_EQ(digest_value.size(), kKeySize);
+ memcpy(key.data(), digest_value.data(), kKeySize);
+
+ return new SingleKeyHandler(this, key);
+}
+
+void SourceKeyedCachedMetadataHandler::ClearCachedMetadata(
+ CachedMetadataHandler::CacheType cache_type) {
+ cached_metadata_map_.clear();
+ if (cache_type == CachedMetadataHandler::kSendToPlatform)
+ SendToPlatform();
+};
+
+String SourceKeyedCachedMetadataHandler::Encoding() const {
+ return String(encoding_.GetName());
+}
+
+// Encoding of keyed map:
+// - marker: CachedMetadataHandler::kSourceKeyedMap (uint32_t)
+// - num_entries (int)
+// - key 1 (Key type)
+// - len data 1 (size_t)
+// - type data 1
+// - data for key 1
+// ...
+// - key N (Key type)
+// - len data N (size_t)
+// - type data N
+// - data for key N
+
+namespace {
+// Reading a value from a char buffer without using reinterpret cast. This
+// should inline and optimize to the same code as *reinterpret_cast<T>(data),
+// but without the risk of undefined behaviour.
+template <typename T>
+T ReadVal(const char* data) {
+ static_assert(base::is_trivially_copyable<T>::value,
+ "ReadVal requires the value type to be copyable");
+ T ret;
+ memcpy(&ret, data, sizeof(T));
+ return ret;
+}
+} // namespace
+
+void SourceKeyedCachedMetadataHandler::SetSerializedCachedMetadata(
+ const char* data,
+ size_t size) {
+ // We only expect to receive cached metadata from the platform once. If this
+ // triggers, it indicates an efficiency problem which is most likely
+ // unexpected in code designed to improve performance.
+ DCHECK(cached_metadata_map_.IsEmpty());
+
+ // Ensure we have a marker.
+ if (size < sizeof(uint32_t))
+ return;
+ uint32_t marker = ReadVal<uint32_t>(data);
+ // Check for our marker to avoid conflicts with other kinds of cached
+ // metadata.
+ if (marker != CachedMetadataHandler::kSourceKeyedMap) {
+ return;
+ }
+ data += sizeof(uint32_t);
+ size -= sizeof(uint32_t);
+
+ // Ensure we have a length.
+ if (size < sizeof(int))
+ return;
+ int num_entries = ReadVal<int>(data);
+ data += sizeof(int);
+ size -= sizeof(int);
+
+ for (int i = 0; i < num_entries; ++i) {
+ // Ensure we have an entry key and size.
+ if (size < kKeySize + sizeof(size_t)) {
+ cached_metadata_map_.clear();
+ return;
+ }
+
+ Key key;
+ std::copy(data, data + kKeySize, std::begin(key));
+ data += kKeySize;
+ size_t entry_size = ReadVal<size_t>(data);
+ data += sizeof(size_t);
+
+ size -= kKeySize + sizeof(size_t);
+
+ // Ensure we have enough data for this entry.
+ if (size < entry_size) {
+ cached_metadata_map_.clear();
+ return;
+ }
+
+ if (scoped_refptr<CachedMetadata> deserialized_entry =
+ CachedMetadata::CreateFromSerializedData(data, entry_size)) {
+ // Only insert the deserialized entry if it deserialized correctly.
+ cached_metadata_map_.insert(key, std::move(deserialized_entry));
+ }
+ data += entry_size;
+ size -= entry_size;
+ }
+
+ // Ensure we have no more data.
+ if (size > 0) {
+ cached_metadata_map_.clear();
+ }
+};
+
+void SourceKeyedCachedMetadataHandler::SendToPlatform() {
+ if (!sender_)
+ return;
+
+ if (cached_metadata_map_.IsEmpty()) {
+ sender_->Send(nullptr, 0);
+ } else {
+ Vector<char> serialized_data;
+ uint32_t marker = CachedMetadataHandler::kSourceKeyedMap;
+ serialized_data.Append(reinterpret_cast<char*>(&marker), sizeof(marker));
+ int num_entries = cached_metadata_map_.size();
+ serialized_data.Append(reinterpret_cast<char*>(&num_entries),
+ sizeof(num_entries));
+ for (const auto& metadata : cached_metadata_map_) {
+ serialized_data.Append(metadata.key.data(), kKeySize);
+ size_t entry_size = metadata.value->SerializedData().size();
+ serialized_data.Append(reinterpret_cast<const char*>(&entry_size),
+ sizeof(entry_size));
+ serialized_data.AppendVector(metadata.value->SerializedData());
+ }
+ sender_->Send(serialized_data.data(), serialized_data.size());
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.h b/chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.h
new file mode 100644
index 00000000000..b9b7b83a773
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.h
@@ -0,0 +1,81 @@
+// Copyright 2018 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_PLATFORM_LOADER_FETCH_SOURCE_KEYED_CACHED_METADATA_HANDLER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_SOURCE_KEYED_CACHED_METADATA_HANDLER_H_
+
+#include <stdint.h>
+#include <array>
+#include "third_party/blink/renderer/platform/loader/fetch/cached_metadata.h"
+#include "third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+
+namespace blink {
+
+// An implementation of CachedMetadataHandler which can hold multiple
+// CachedMetadata entries. These entries are keyed by a cryptograph hash of the
+// source code which produced them.
+//
+// This is used to store cached metadata for multiple inline scripts on a single
+// HTML document's resource.
+class PLATFORM_EXPORT SourceKeyedCachedMetadataHandler final
+ : public CachedMetadataHandler {
+ public:
+ SourceKeyedCachedMetadataHandler(
+ WTF::TextEncoding encoding,
+ std::unique_ptr<CachedMetadataSender> send_callback)
+ : sender_(std::move(send_callback)), encoding_(encoding) {}
+
+ // Produce a metadata handler for a single cached metadata associated with
+ // the given source code.
+ SingleCachedMetadataHandler* HandlerForSource(const String& source);
+
+ void ClearCachedMetadata(CachedMetadataHandler::CacheType) override;
+ String Encoding() const override;
+ bool IsServedFromCacheStorage() const override {
+ return sender_->IsServedFromCacheStorage();
+ }
+
+ void SetSerializedCachedMetadata(const char*, size_t);
+
+ private:
+ // Keys are SHA-256, which are 256/8 = 32 bytes.
+ static constexpr size_t kKeySize = 32;
+ typedef std::array<uint8_t, kKeySize> Key;
+
+ class SingleKeyHandler;
+ class KeyHash;
+ class KeyHashTraits : public WTF::GenericHashTraits<Key> {
+ public:
+ // Note: This class relies on hashes never being zero or 1 followed by all
+ // zeros. Practically, our hash space is large enough that the risk of such
+ // a collision is infinitesimal.
+
+ typedef Key EmptyValueType;
+ static const bool kEmptyValueIsZero = true;
+ static EmptyValueType EmptyValue() {
+ // Rely on integer value initialization to zero out the key array.
+ return Key{};
+ }
+
+ static void ConstructDeletedValue(Key& slot, bool) {
+ slot = {1}; // Remaining entries are value initialized to 0.
+ }
+ static bool IsDeletedValue(const Key& value) { return value == Key{1}; }
+ };
+
+ void SendToPlatform();
+
+ // TODO(leszeks): Maybe just store the SingleKeyHandlers directly in here?
+ WTF::HashMap<Key, scoped_refptr<CachedMetadata>, KeyHash, KeyHashTraits>
+ cached_metadata_map_;
+ std::unique_ptr<CachedMetadataSender> sender_;
+
+ const WTF::TextEncoding encoding_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_SOURCE_KEYED_CACHED_METADATA_HANDLER_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler_test.cc
new file mode 100644
index 00000000000..b30c1769416
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler_test.cc
@@ -0,0 +1,459 @@
+// Copyright 2018 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/platform/loader/fetch/source_keyed_cached_metadata_handler.h"
+
+#include <array>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/crypto.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+
+namespace blink {
+
+namespace {
+
+class MockSha256WebCryptoDigestor : public WebCryptoDigestor {
+ public:
+ virtual bool Consume(const unsigned char* data, unsigned data_size) {
+ String key(data, data_size);
+
+ auto it = kMapOfHashes.find(key);
+
+ if (it != kMapOfHashes.end()) {
+ hash_exists_ = true;
+ hash_ = it->value;
+ }
+
+ return hash_exists_;
+ }
+
+ virtual bool Finish(unsigned char*& result_data, unsigned& result_data_size) {
+ if (hash_exists_) {
+ result_data = hash_.data();
+ result_data_size = hash_.size();
+ }
+ return hash_exists_;
+ }
+
+ private:
+ Vector<unsigned char> hash_;
+ bool hash_exists_;
+
+ HashMap<String, Vector<unsigned char>> kMapOfHashes = {
+ {"source1",
+ Vector<unsigned char>{0xc4, 0xd5, 0xe4, 0x35, 0x74, 0x89, 0x3c, 0x3c,
+ 0xc3, 0xd4, 0xba, 0xba, 0x65, 0x58, 0x92, 0x48,
+ 0x47, 0x9a, 0x9f, 0xbf, 0xaf, 0x1f, 0x60, 0x8e,
+ 0xb1, 0x54, 0x1e, 0xc0, 0xc6, 0xfe, 0x63, 0x6f}},
+ {"source2",
+ Vector<unsigned char>{0x99, 0x2f, 0x4e, 0xb2, 0x41, 0xee, 0x6e, 0xef,
+ 0xe4, 0x92, 0x80, 0x25, 0xa2, 0x74, 0x7d, 0xb0,
+ 0x8b, 0x91, 0x98, 0x34, 0xc9, 0x3c, 0x5f, 0x57,
+ 0x41, 0x72, 0x5f, 0xa2, 0x6b, 0x63, 0x38, 0x41}}};
+};
+
+// Mock WebCrypto implementation for digest calculation.
+class MockDigestWebCrypto : public WebCrypto {
+ std::unique_ptr<WebCryptoDigestor> CreateDigestor(
+ WebCryptoAlgorithmId algorithm_id) override {
+ EXPECT_EQ(algorithm_id, WebCryptoAlgorithmId::kWebCryptoAlgorithmIdSha256);
+ return std::make_unique<MockSha256WebCryptoDigestor>();
+ }
+};
+
+// Structure holding cache metadata sent to the platform.
+struct CacheMetadataEntry {
+ CacheMetadataEntry(const WebURL& url,
+ base::Time response_time,
+ const char* data,
+ size_t data_size)
+ : url(url), response_time(response_time) {
+ this->data.Append(data, data_size);
+ }
+
+ WebURL url;
+ base::Time response_time;
+ Vector<char> data;
+};
+
+// Mock Platform implementation that provides basic crypto and caching.
+class SourceKeyedCachedMetadataHandlerMockPlatform final
+ : public TestingPlatformSupportWithMockScheduler {
+ public:
+ SourceKeyedCachedMetadataHandlerMockPlatform() {}
+ ~SourceKeyedCachedMetadataHandlerMockPlatform() override = default;
+
+ WebCrypto* Crypto() override { return &mock_web_crypto_; }
+
+ void CacheMetadata(const WebURL& url,
+ base::Time response_time,
+ const char* data,
+ size_t data_size) override {
+ cache_entries_.emplace_back(url, response_time, data, data_size);
+ }
+
+ bool HasCacheMetadataFor(const WebURL& url) {
+ for (const CacheMetadataEntry& entry : cache_entries_) {
+ if (entry.url == url) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ Vector<CacheMetadataEntry> GetCacheMetadatasFor(const WebURL& url) {
+ Vector<CacheMetadataEntry> url_entries;
+ for (const CacheMetadataEntry& entry : cache_entries_) {
+ if (entry.url == url) {
+ url_entries.push_back(entry);
+ }
+ }
+ return url_entries;
+ }
+
+ private:
+ MockDigestWebCrypto mock_web_crypto_;
+ Vector<CacheMetadataEntry> cache_entries_;
+};
+
+// Mock CachedMetadataSender implementation that forwards data to the platform.
+class MockCachedMetadataSender final : public CachedMetadataSender {
+ public:
+ MockCachedMetadataSender(KURL response_url) : response_url_(response_url) {}
+
+ void Send(const char* data, size_t size) {
+ Platform::Current()->CacheMetadata(response_url_, response_time_, data,
+ size);
+ }
+
+ bool IsServedFromCacheStorage() override { return false; }
+
+ private:
+ const KURL response_url_;
+ const Time response_time_;
+};
+
+template <size_t N>
+::testing::AssertionResult CachedMetadataFailure(
+ const char* failure_msg,
+ const char* actual_expression,
+ const std::array<char, N>& expected,
+ const scoped_refptr<CachedMetadata>& actual) {
+ ::testing::Message msg;
+ msg << failure_msg << " for " << actual_expression;
+ msg << "\n Expected: [" << N << "] { ";
+ for (size_t i = 0; i < N; ++i) {
+ if (i > 0)
+ msg << ", ";
+ msg << std::hex << static_cast<int>(expected[i]);
+ }
+ msg << " }";
+ if (actual) {
+ msg << "\n Actual: [" << actual->size() << "] { ";
+ for (size_t i = 0; i < actual->size(); ++i) {
+ if (i > 0)
+ msg << ", ";
+ msg << std::hex << static_cast<int>(actual->Data()[i]);
+ }
+ msg << " }";
+ } else {
+ msg << "\n Actual: (null)";
+ }
+
+ return testing::AssertionFailure() << msg;
+}
+
+template <size_t N>
+::testing::AssertionResult CachedMetadataEqual(
+ const char* expected_expression,
+ const char* actual_expression,
+ const std::array<char, N>& expected,
+ const scoped_refptr<CachedMetadata>& actual) {
+ if (!actual) {
+ return CachedMetadataFailure("Expected non-null data", actual_expression,
+ expected, actual);
+ }
+ if (actual->size() != N) {
+ return CachedMetadataFailure("Wrong size", actual_expression, expected,
+ actual);
+ }
+ const char* actual_data = actual->Data();
+ for (size_t i = 0; i < N; ++i) {
+ if (actual_data[i] != expected[i]) {
+ return CachedMetadataFailure("Wrong data", actual_expression, expected,
+ actual);
+ }
+ }
+
+ return testing::AssertionSuccess();
+}
+
+#define EXPECT_METADATA(data_array, cached_metadata) \
+ EXPECT_PRED_FORMAT2(CachedMetadataEqual, data_array, cached_metadata)
+
+} // namespace
+
+TEST(SourceKeyedCachedMetadataHandlerTest,
+ HandlerForSource_InitiallyNonNullHandlersWithNullData) {
+ ScopedTestingPlatformSupport<SourceKeyedCachedMetadataHandlerMockPlatform>
+ platform;
+
+ KURL url("http://SourceKeyedCachedMetadataHandlerTest.com");
+ SourceKeyedCachedMetadataHandler* handler =
+ new SourceKeyedCachedMetadataHandler(
+ WTF::TextEncoding(), std::make_unique<MockCachedMetadataSender>(url));
+
+ WTF::String source1("source1");
+ SingleCachedMetadataHandler* source1_handler =
+ handler->HandlerForSource(source1);
+
+ WTF::String source2("source2");
+ SingleCachedMetadataHandler* source2_handler =
+ handler->HandlerForSource(source2);
+
+ EXPECT_NE(nullptr, source1_handler);
+ EXPECT_EQ(nullptr, source1_handler->GetCachedMetadata(0xbeef));
+ EXPECT_NE(nullptr, source2_handler);
+ EXPECT_EQ(nullptr, source2_handler->GetCachedMetadata(0x5eed));
+}
+
+TEST(SourceKeyedCachedMetadataHandlerTest,
+ HandlerForSource_OneHandlerSetOtherNull) {
+ ScopedTestingPlatformSupport<SourceKeyedCachedMetadataHandlerMockPlatform>
+ platform;
+
+ KURL url("http://SourceKeyedCachedMetadataHandlerTest.com");
+ SourceKeyedCachedMetadataHandler* handler =
+ new SourceKeyedCachedMetadataHandler(
+ WTF::TextEncoding(), std::make_unique<MockCachedMetadataSender>(url));
+
+ WTF::String source1("source1");
+ SingleCachedMetadataHandler* source1_handler =
+ handler->HandlerForSource(source1);
+
+ WTF::String source2("source2");
+ SingleCachedMetadataHandler* source2_handler =
+ handler->HandlerForSource(source2);
+
+ std::array<char, 3> data1 = {1, 2, 3};
+ source1_handler->SetCachedMetadata(0xbeef, data1.data(), data1.size());
+
+ EXPECT_NE(nullptr, source1_handler);
+ EXPECT_METADATA(data1, source1_handler->GetCachedMetadata(0xbeef));
+
+ EXPECT_NE(nullptr, source2_handler);
+ EXPECT_EQ(nullptr, source2_handler->GetCachedMetadata(0x5eed));
+}
+
+TEST(SourceKeyedCachedMetadataHandlerTest, HandlerForSource_BothHandlersSet) {
+ ScopedTestingPlatformSupport<SourceKeyedCachedMetadataHandlerMockPlatform>
+ platform;
+
+ KURL url("http://SourceKeyedCachedMetadataHandlerTest.com");
+ SourceKeyedCachedMetadataHandler* handler =
+ new SourceKeyedCachedMetadataHandler(
+ WTF::TextEncoding(), std::make_unique<MockCachedMetadataSender>(url));
+
+ WTF::String source1("source1");
+ SingleCachedMetadataHandler* source1_handler =
+ handler->HandlerForSource(source1);
+
+ WTF::String source2("source2");
+ SingleCachedMetadataHandler* source2_handler =
+ handler->HandlerForSource(source2);
+
+ std::array<char, 3> data1 = {1, 2, 3};
+ source1_handler->SetCachedMetadata(0xbeef, data1.data(), data1.size());
+
+ std::array<char, 4> data2 = {3, 4, 5, 6};
+ source2_handler->SetCachedMetadata(0x5eed, data2.data(), data2.size());
+
+ EXPECT_NE(nullptr, source1_handler);
+ EXPECT_METADATA(data1, source1_handler->GetCachedMetadata(0xbeef));
+
+ EXPECT_NE(nullptr, source2_handler);
+ EXPECT_METADATA(data2, source2_handler->GetCachedMetadata(0x5eed));
+}
+
+TEST(SourceKeyedCachedMetadataHandlerTest, Serialize_EmptyClearDoesSend) {
+ ScopedTestingPlatformSupport<SourceKeyedCachedMetadataHandlerMockPlatform>
+ platform;
+
+ KURL url("http://SourceKeyedCachedMetadataHandlerTest.com");
+ SourceKeyedCachedMetadataHandler* handler =
+ new SourceKeyedCachedMetadataHandler(
+ WTF::TextEncoding(), std::make_unique<MockCachedMetadataSender>(url));
+
+ // Clear and send to the platform
+ handler->ClearCachedMetadata(CachedMetadataHandler::kSendToPlatform);
+
+ // Load from platform
+ Vector<CacheMetadataEntry> cache_metadatas =
+ platform->GetCacheMetadatasFor(url);
+
+ EXPECT_EQ(1u, cache_metadatas.size());
+}
+
+TEST(SourceKeyedCachedMetadataHandlerTest, Serialize_EachSetDoesSend) {
+ ScopedTestingPlatformSupport<SourceKeyedCachedMetadataHandlerMockPlatform>
+ platform;
+
+ KURL url("http://SourceKeyedCachedMetadataHandlerTest.com");
+ SourceKeyedCachedMetadataHandler* handler =
+ new SourceKeyedCachedMetadataHandler(
+ WTF::TextEncoding(), std::make_unique<MockCachedMetadataSender>(url));
+
+ WTF::String source1("source1");
+ SingleCachedMetadataHandler* source1_handler =
+ handler->HandlerForSource(source1);
+
+ WTF::String source2("source2");
+ SingleCachedMetadataHandler* source2_handler =
+ handler->HandlerForSource(source2);
+
+ std::array<char, 3> data1 = {1, 2, 3};
+ source1_handler->SetCachedMetadata(0xbeef, data1.data(), data1.size());
+
+ std::array<char, 4> data2 = {3, 4, 5, 6};
+ source2_handler->SetCachedMetadata(0x5eed, data2.data(), data2.size());
+
+ // Load from platform
+ Vector<CacheMetadataEntry> cache_metadatas =
+ platform->GetCacheMetadatasFor(url);
+
+ EXPECT_EQ(2u, cache_metadatas.size());
+}
+
+TEST(SourceKeyedCachedMetadataHandlerTest, Serialize_SetWithNoSendDoesNotSend) {
+ ScopedTestingPlatformSupport<SourceKeyedCachedMetadataHandlerMockPlatform>
+ platform;
+
+ KURL url("http://SourceKeyedCachedMetadataHandlerTest.com");
+ SourceKeyedCachedMetadataHandler* handler =
+ new SourceKeyedCachedMetadataHandler(
+ WTF::TextEncoding(), std::make_unique<MockCachedMetadataSender>(url));
+
+ WTF::String source1("source1");
+ SingleCachedMetadataHandler* source1_handler =
+ handler->HandlerForSource(source1);
+
+ WTF::String source2("source2");
+ SingleCachedMetadataHandler* source2_handler =
+ handler->HandlerForSource(source2);
+
+ std::array<char, 3> data1 = {1, 2, 3};
+ source1_handler->SetCachedMetadata(0xbeef, data1.data(), data1.size(),
+ CachedMetadataHandler::kCacheLocally);
+
+ std::array<char, 4> data2 = {3, 4, 5, 6};
+ source2_handler->SetCachedMetadata(0x5eed, data2.data(), data2.size());
+
+ // Load from platform
+ Vector<CacheMetadataEntry> cache_metadatas =
+ platform->GetCacheMetadatasFor(url);
+
+ EXPECT_EQ(1u, cache_metadatas.size());
+}
+
+TEST(SourceKeyedCachedMetadataHandlerTest,
+ SerializeAndDeserialize_NoHandlersSet) {
+ ScopedTestingPlatformSupport<SourceKeyedCachedMetadataHandlerMockPlatform>
+ platform;
+
+ KURL url("http://SourceKeyedCachedMetadataHandlerTest.com");
+ WTF::String source1("source1");
+ WTF::String source2("source2");
+ {
+ SourceKeyedCachedMetadataHandler* handler =
+ new SourceKeyedCachedMetadataHandler(
+ WTF::TextEncoding(),
+ std::make_unique<MockCachedMetadataSender>(url));
+
+ // Clear and send to the platform
+ handler->ClearCachedMetadata(CachedMetadataHandler::kSendToPlatform);
+ }
+
+ // Reload from platform
+ {
+ Vector<CacheMetadataEntry> cache_metadatas =
+ platform->GetCacheMetadatasFor(url);
+ // Use the last data received by the platform
+ EXPECT_EQ(1u, cache_metadatas.size());
+ CacheMetadataEntry& last_cache_metadata = cache_metadatas[0];
+
+ SourceKeyedCachedMetadataHandler* handler =
+ new SourceKeyedCachedMetadataHandler(
+ WTF::TextEncoding(),
+ std::make_unique<MockCachedMetadataSender>(url));
+ handler->SetSerializedCachedMetadata(last_cache_metadata.data.data(),
+ last_cache_metadata.data.size());
+
+ SingleCachedMetadataHandler* source1_handler =
+ handler->HandlerForSource(source1);
+ SingleCachedMetadataHandler* source2_handler =
+ handler->HandlerForSource(source2);
+
+ EXPECT_NE(nullptr, source1_handler);
+ EXPECT_EQ(nullptr, source1_handler->GetCachedMetadata(0xbeef));
+
+ EXPECT_NE(nullptr, source2_handler);
+ EXPECT_EQ(nullptr, source2_handler->GetCachedMetadata(0x5eed));
+ }
+}
+
+TEST(SourceKeyedCachedMetadataHandlerTest,
+ SerializeAndDeserialize_BothHandlersSet) {
+ ScopedTestingPlatformSupport<SourceKeyedCachedMetadataHandlerMockPlatform>
+ platform;
+
+ KURL url("http://SourceKeyedCachedMetadataHandlerTest.com");
+ WTF::String source1("source1");
+ WTF::String source2("source2");
+ std::array<char, 3> data1 = {1, 2, 3};
+ std::array<char, 4> data2 = {3, 4, 5, 6};
+ {
+ SourceKeyedCachedMetadataHandler* handler =
+ new SourceKeyedCachedMetadataHandler(
+ WTF::TextEncoding(),
+ std::make_unique<MockCachedMetadataSender>(url));
+
+ SingleCachedMetadataHandler* source1_handler =
+ handler->HandlerForSource(source1);
+ SingleCachedMetadataHandler* source2_handler =
+ handler->HandlerForSource(source2);
+
+ source1_handler->SetCachedMetadata(0xbeef, data1.data(), data1.size());
+ source2_handler->SetCachedMetadata(0x5eed, data2.data(), data2.size());
+ }
+
+ // Reload from platform
+ {
+ Vector<CacheMetadataEntry> cache_metadatas =
+ platform->GetCacheMetadatasFor(url);
+ // Use the last data received by the platform
+ EXPECT_EQ(2u, cache_metadatas.size());
+ CacheMetadataEntry& last_cache_metadata = cache_metadatas[1];
+
+ SourceKeyedCachedMetadataHandler* handler =
+ new SourceKeyedCachedMetadataHandler(
+ WTF::TextEncoding(),
+ std::make_unique<MockCachedMetadataSender>(url));
+ handler->SetSerializedCachedMetadata(last_cache_metadata.data.data(),
+ last_cache_metadata.data.size());
+
+ SingleCachedMetadataHandler* source1_handler =
+ handler->HandlerForSource(source1);
+ SingleCachedMetadataHandler* source2_handler =
+ handler->HandlerForSource(source2);
+
+ EXPECT_NE(nullptr, source1_handler);
+ EXPECT_METADATA(data1, source1_handler->GetCachedMetadata(0xbeef));
+
+ EXPECT_NE(nullptr, source2_handler);
+ EXPECT_METADATA(data2, source2_handler->GetCachedMetadata(0x5eed));
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/substitute_data.h b/chromium/third_party/blink/renderer/platform/loader/fetch/substitute_data.h
new file mode 100644
index 00000000000..2dd436b2711
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/substitute_data.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_SUBSTITUTE_DATA_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_SUBSTITUTE_DATA_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+class SubstituteData {
+ DISALLOW_NEW();
+
+ public:
+ SubstituteData() = default;
+
+ SubstituteData(scoped_refptr<SharedBuffer> content)
+ : SubstituteData(content, "text/html", "UTF-8", KURL()) {}
+
+ SubstituteData(scoped_refptr<SharedBuffer> content,
+ const AtomicString& mime_type,
+ const AtomicString& text_encoding,
+ const KURL& failing_url)
+ : content_(std::move(content)),
+ mime_type_(mime_type),
+ text_encoding_(text_encoding),
+ failing_url_(failing_url) {}
+
+ bool IsValid() const { return content_.get(); }
+
+ SharedBuffer* Content() const { return content_.get(); }
+ const AtomicString& MimeType() const { return mime_type_; }
+ const AtomicString& TextEncoding() const { return text_encoding_; }
+ const KURL& FailingURL() const { return failing_url_; }
+
+ private:
+ scoped_refptr<SharedBuffer> content_;
+ AtomicString mime_type_;
+ AtomicString text_encoding_;
+ KURL failing_url_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_SUBSTITUTE_DATA_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.cc
new file mode 100644
index 00000000000..b0b42a8bc22
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.cc
@@ -0,0 +1,65 @@
+// Copyright 2017 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/platform/language.h"
+#include "third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.h"
+
+namespace blink {
+
+TextResourceDecoderOptions::TextResourceDecoderOptions(
+ ContentType content_type,
+ const WTF::TextEncoding& default_encoding)
+ : TextResourceDecoderOptions(kUseContentAndBOMBasedDetection,
+ content_type,
+ default_encoding,
+ nullptr,
+ KURL()) {}
+
+TextResourceDecoderOptions
+TextResourceDecoderOptions::CreateAlwaysUseUTF8ForText() {
+ return TextResourceDecoderOptions(kAlwaysUseUTF8ForText, kPlainTextContent,
+ UTF8Encoding(), nullptr, NullURL());
+}
+
+TextResourceDecoderOptions TextResourceDecoderOptions::CreateWithAutoDetection(
+ ContentType content_type,
+ const WTF::TextEncoding& default_encoding,
+ const WTF::TextEncoding& hint_encoding,
+ const KURL& hint_url) {
+ return TextResourceDecoderOptions(kUseAllAutoDetection, content_type,
+ default_encoding, hint_encoding.GetName(),
+ hint_url);
+}
+
+TextResourceDecoderOptions::TextResourceDecoderOptions(
+ EncodingDetectionOption encoding_detection_option,
+ ContentType content_type,
+ const WTF::TextEncoding& default_encoding,
+ const char* hint_encoding,
+ const KURL& hint_url)
+ : encoding_detection_option_(encoding_detection_option),
+ content_type_(content_type),
+ default_encoding_(default_encoding),
+ use_lenient_xml_decoding_(false),
+ hint_encoding_(hint_encoding),
+ hint_url_(hint_url) {
+ hint_language_[0] = 0;
+ if (encoding_detection_option_ == kUseAllAutoDetection) {
+ // Checking empty URL helps unit testing. Providing DefaultLanguage() is
+ // sometimes difficult in tests.
+ if (!hint_url_.IsEmpty()) {
+ // This object is created in the main thread, but used in another thread.
+ // We should not share an AtomicString.
+ AtomicString locale = DefaultLanguage();
+ if (locale.length() >= 2) {
+ // DefaultLanguage() is always an ASCII string.
+ hint_language_[0] = static_cast<char>(locale[0]);
+ hint_language_[1] = static_cast<char>(locale[1]);
+ hint_language_[2] = 0;
+ }
+ }
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.h b/chromium/third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.h
new file mode 100644
index 00000000000..b38a63a6cf9
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.h
@@ -0,0 +1,99 @@
+// Copyright 2017 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_PLATFORM_LOADER_FETCH_TEXT_RESOURCE_DECODER_OPTIONS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_TEXT_RESOURCE_DECODER_OPTIONS_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT TextResourceDecoderOptions final {
+ public:
+ enum ContentType {
+ kPlainTextContent,
+ kHTMLContent,
+ kJSONContent,
+ kXMLContent,
+ kCSSContent,
+ kMaxContentType = kCSSContent
+ }; // PlainText only checks for BOM.
+
+ explicit TextResourceDecoderOptions(
+ ContentType,
+ const WTF::TextEncoding& default_encoding = WTF::TextEncoding());
+
+ // Corresponds to utf-8 decode in Encoding spec:
+ // https://encoding.spec.whatwg.org/#utf-8-decode.
+ static TextResourceDecoderOptions CreateAlwaysUseUTF8ForText();
+
+ static TextResourceDecoderOptions CreateWithAutoDetection(
+ ContentType,
+ const WTF::TextEncoding& default_encoding,
+ const WTF::TextEncoding& hint_encoding,
+ const KURL& hint_url);
+
+ void SetUseLenientXMLDecoding() { use_lenient_xml_decoding_ = true; }
+ void OverrideContentType(ContentType content_type) {
+ if (encoding_detection_option_ != kAlwaysUseUTF8ForText)
+ content_type_ = content_type;
+ }
+
+ static ContentType DetermineContentType(const String& mime_type);
+
+ // TextResourceDecoder does three kind of encoding detection:
+ // 1. By BOM,
+ // 2. By Content if |content_type_| is not |kPlainTextContext|
+ // (e.g. <meta> tag for HTML), and
+ // 3. By DetectTextEncoding().
+ enum EncodingDetectionOption {
+ // Use 1. + 2. + 3.
+ kUseAllAutoDetection,
+
+ // Use 1. + 2.
+ kUseContentAndBOMBasedDetection,
+
+ // Use None of them.
+ // |content_type_| must be |kPlainTextContent| and
+ // |default_encoding_| must be UTF8Encoding.
+ // This doesn't change encoding based on BOMs, but still processes
+ // utf-8 BOMs so that utf-8 BOMs don't appear in the decoded result.
+ kAlwaysUseUTF8ForText
+ };
+
+ EncodingDetectionOption GetEncodingDetectionOption() const {
+ return encoding_detection_option_;
+ }
+ ContentType GetContentType() const { return content_type_; }
+ const WTF::TextEncoding& DefaultEncoding() const { return default_encoding_; }
+ bool GetUseLenientXMLDecoding() const { return use_lenient_xml_decoding_; }
+
+ const char* HintEncoding() const { return hint_encoding_; }
+ const KURL& HintURL() const { return hint_url_; }
+ const char* HintLanguage() const { return hint_language_; }
+
+ private:
+ TextResourceDecoderOptions(EncodingDetectionOption,
+ ContentType,
+ const WTF::TextEncoding& default_encoding,
+ const char* hint_encoding,
+ const KURL& hint_url);
+
+ EncodingDetectionOption encoding_detection_option_;
+ ContentType content_type_;
+ WTF::TextEncoding default_encoding_;
+ bool use_lenient_xml_decoding_; // Don't stop on XML decoding errors.
+
+ // Hints for DetectTextEncoding().
+ // Only used when |encoding_detection_option_| == |kUseAllAutoDetection|.
+ const char* hint_encoding_;
+ KURL hint_url_;
+ char hint_language_[3];
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/unique_identifier.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/unique_identifier.cc
new file mode 100644
index 00000000000..67cb66ab375
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/unique_identifier.cc
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <atomic>
+
+#include "third_party/blink/renderer/platform/loader/fetch/unique_identifier.h"
+
+namespace blink {
+
+static std::atomic_ulong g_unique_identifier(1);
+
+unsigned long CreateUniqueIdentifier() {
+ return g_unique_identifier.fetch_add(1, std::memory_order_relaxed);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/unique_identifier.h b/chromium/third_party/blink/renderer/platform/loader/fetch/unique_identifier.h
new file mode 100644
index 00000000000..b408454b093
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/fetch/unique_identifier.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_UNIQUE_IDENTIFIER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_UNIQUE_IDENTIFIER_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+
+namespace blink {
+
+PLATFORM_EXPORT unsigned long CreateUniqueIdentifier();
+}
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_UNIQUE_IDENTIFIER_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/link_header.cc b/chromium/third_party/blink/renderer/platform/loader/link_header.cc
new file mode 100644
index 00000000000..d314eebcbca
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/link_header.cc
@@ -0,0 +1,102 @@
+// Copyright 2015 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/platform/loader/link_header.h"
+
+#include "base/strings/string_util.h"
+#include "components/link_header_util/link_header_util.h"
+#include "third_party/blink/renderer/platform/wtf/text/parsing_utilities.h"
+
+namespace blink {
+
+// Verify that the parameter is a link-extension which according to spec doesn't
+// have to have a value.
+static bool IsExtensionParameter(LinkHeader::LinkParameterName name) {
+ return name >= LinkHeader::kLinkParameterUnknown;
+}
+
+static LinkHeader::LinkParameterName ParameterNameFromString(
+ base::StringPiece name) {
+ if (base::EqualsCaseInsensitiveASCII(name, "rel"))
+ return LinkHeader::kLinkParameterRel;
+ if (base::EqualsCaseInsensitiveASCII(name, "anchor"))
+ return LinkHeader::kLinkParameterAnchor;
+ if (base::EqualsCaseInsensitiveASCII(name, "crossorigin"))
+ return LinkHeader::kLinkParameterCrossOrigin;
+ if (base::EqualsCaseInsensitiveASCII(name, "title"))
+ return LinkHeader::kLinkParameterTitle;
+ if (base::EqualsCaseInsensitiveASCII(name, "media"))
+ return LinkHeader::kLinkParameterMedia;
+ if (base::EqualsCaseInsensitiveASCII(name, "type"))
+ return LinkHeader::kLinkParameterType;
+ if (base::EqualsCaseInsensitiveASCII(name, "rev"))
+ return LinkHeader::kLinkParameterRev;
+ if (base::EqualsCaseInsensitiveASCII(name, "hreflang"))
+ return LinkHeader::kLinkParameterHreflang;
+ if (base::EqualsCaseInsensitiveASCII(name, "as"))
+ return LinkHeader::kLinkParameterAs;
+ if (base::EqualsCaseInsensitiveASCII(name, "nonce"))
+ return LinkHeader::kLinkParameterNonce;
+ if (base::EqualsCaseInsensitiveASCII(name, "integrity"))
+ return LinkHeader::kLinkParameterIntegrity;
+ if (base::EqualsCaseInsensitiveASCII(name, "srcset"))
+ return LinkHeader::kLinkParameterSrcset;
+ if (base::EqualsCaseInsensitiveASCII(name, "imgsizes"))
+ return LinkHeader::kLinkParameterImgsizes;
+ return LinkHeader::kLinkParameterUnknown;
+}
+
+void LinkHeader::SetValue(LinkParameterName name, const String& value) {
+ if (name == kLinkParameterRel && !rel_)
+ rel_ = value.DeprecatedLower();
+ else if (name == kLinkParameterAnchor)
+ is_valid_ = false;
+ else if (name == kLinkParameterCrossOrigin)
+ cross_origin_ = value;
+ else if (name == kLinkParameterAs)
+ as_ = value.DeprecatedLower();
+ else if (name == kLinkParameterType)
+ mime_type_ = value.DeprecatedLower();
+ else if (name == kLinkParameterMedia)
+ media_ = value.DeprecatedLower();
+ else if (name == kLinkParameterNonce)
+ nonce_ = value;
+ else if (name == kLinkParameterIntegrity)
+ integrity_ = value;
+ else if (name == kLinkParameterSrcset)
+ srcset_ = value;
+ else if (name == kLinkParameterImgsizes)
+ imgsizes_ = value;
+}
+
+template <typename Iterator>
+LinkHeader::LinkHeader(Iterator begin, Iterator end) : is_valid_(true) {
+ std::string url;
+ std::unordered_map<std::string, base::Optional<std::string>> params;
+ is_valid_ = link_header_util::ParseLinkHeaderValue(begin, end, &url, &params);
+ if (!is_valid_)
+ return;
+
+ url_ = String(&url[0], url.length());
+ for (const auto& param : params) {
+ LinkParameterName name = ParameterNameFromString(param.first);
+ if (!IsExtensionParameter(name) && !param.second)
+ is_valid_ = false;
+ std::string value = param.second.value_or("");
+ SetValue(name, String(&value[0], value.length()));
+ }
+}
+
+LinkHeaderSet::LinkHeaderSet(const String& header) {
+ if (header.IsNull())
+ return;
+
+ DCHECK(header.Is8Bit()) << "Headers should always be 8 bit";
+ std::string header_string(reinterpret_cast<const char*>(header.Characters8()),
+ header.length());
+ for (const auto& value : link_header_util::SplitLinkHeader(header_string))
+ header_set_.push_back(LinkHeader(value.first, value.second));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/link_header.h b/chromium/third_party/blink/renderer/platform/loader/link_header.h
new file mode 100644
index 00000000000..61d5e41310f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/link_header.h
@@ -0,0 +1,87 @@
+// Copyright 2015 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_PLATFORM_LOADER_LINK_HEADER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_LINK_HEADER_H_
+
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class LinkHeader {
+ DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
+
+ public:
+ const String& Url() const { return url_; }
+ const String& Rel() const { return rel_; }
+ const String& As() const { return as_; }
+ const String& MimeType() const { return mime_type_; }
+ const String& Media() const { return media_; }
+ const String& CrossOrigin() const { return cross_origin_; }
+ const String& Nonce() const { return nonce_; }
+ const String& Integrity() const { return integrity_; }
+ const String& Srcset() const { return srcset_; }
+ const String& Imgsizes() const { return imgsizes_; }
+ bool Valid() const { return is_valid_; }
+
+ enum LinkParameterName {
+ kLinkParameterRel,
+ kLinkParameterAnchor,
+ kLinkParameterTitle,
+ kLinkParameterMedia,
+ kLinkParameterType,
+ kLinkParameterRev,
+ kLinkParameterHreflang,
+ // Beyond this point, only link-extension parameters
+ kLinkParameterUnknown,
+ kLinkParameterCrossOrigin,
+ kLinkParameterAs,
+ kLinkParameterNonce,
+ kLinkParameterIntegrity,
+ kLinkParameterSrcset,
+ kLinkParameterImgsizes,
+ };
+
+ private:
+ friend class LinkHeaderSet;
+
+ template <typename Iterator>
+ LinkHeader(Iterator begin, Iterator end);
+ void SetValue(LinkParameterName, const String& value);
+
+ String url_;
+ String rel_;
+ String as_;
+ String mime_type_;
+ String media_;
+ String cross_origin_;
+ String nonce_;
+ String integrity_;
+ String srcset_;
+ String imgsizes_;
+ bool is_valid_;
+};
+
+class PLATFORM_EXPORT LinkHeaderSet {
+ STACK_ALLOCATED();
+
+ public:
+ LinkHeaderSet(const String& header);
+
+ Vector<LinkHeader>::const_iterator begin() const {
+ return header_set_.begin();
+ }
+ Vector<LinkHeader>::const_iterator end() const { return header_set_.end(); }
+ LinkHeader& operator[](size_t i) { return header_set_[i]; }
+ size_t size() { return header_set_.size(); }
+
+ private:
+ Vector<LinkHeader> header_set_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/link_header_test.cc b/chromium/third_party/blink/renderer/platform/loader/link_header_test.cc
new file mode 100644
index 00000000000..a82f7b897fa
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/link_header_test.cc
@@ -0,0 +1,287 @@
+// Copyright 2015 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/platform/loader/link_header.h"
+
+#include <base/macros.h>
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+namespace {
+
+TEST(LinkHeaderTest, Empty) {
+ String null_string;
+ LinkHeaderSet null_header_set(null_string);
+ ASSERT_EQ(null_header_set.size(), unsigned(0));
+ String empty_string("");
+ LinkHeaderSet empty_header_set(empty_string);
+ ASSERT_EQ(empty_header_set.size(), unsigned(0));
+}
+
+struct SingleTestCase {
+ const char* header_value;
+ bool valid;
+ const char* url;
+ const char* rel;
+ const char* as;
+ const char* media;
+} g_single_test_cases[] = {
+ {"</images/cat.jpg>; rel=prefetch", true, "/images/cat.jpg", "prefetch", "",
+ ""},
+ {"</images/cat.jpg>;rel=prefetch", true, "/images/cat.jpg", "prefetch", "",
+ ""},
+ {"</images/cat.jpg> ;rel=prefetch", true, "/images/cat.jpg", "prefetch",
+ "", ""},
+ {"</images/cat.jpg> ; rel=prefetch", true, "/images/cat.jpg",
+ "prefetch", "", ""},
+ {"< /images/cat.jpg> ; rel=prefetch", true, "/images/cat.jpg",
+ "prefetch", "", ""},
+ {"</images/cat.jpg > ; rel=prefetch", true, "/images/cat.jpg",
+ "prefetch", "", ""},
+ {"</images/cat.jpg wutwut> ; rel=prefetch", true,
+ "/images/cat.jpg wutwut", "prefetch", "", ""},
+ {"</images/cat.jpg wutwut \t > ; rel=prefetch", true,
+ "/images/cat.jpg wutwut", "prefetch", "", ""},
+ {"</images/cat.jpg>; rel=prefetch ", true, "/images/cat.jpg", "prefetch",
+ "", ""},
+ {"</images/cat.jpg>; Rel=prefetch ", true, "/images/cat.jpg", "prefetch",
+ "", ""},
+ {"</images/cat.jpg>; Rel=PReFetCh ", true, "/images/cat.jpg", "prefetch",
+ "", ""},
+ {"</images/cat.jpg>; rel=prefetch; rel=somethingelse", true,
+ "/images/cat.jpg", "prefetch", "", ""},
+ {" </images/cat.jpg>; rel=prefetch ", true, "/images/cat.jpg",
+ "prefetch", "", ""},
+ {"\t </images/cat.jpg>; rel=prefetch ", true, "/images/cat.jpg",
+ "prefetch", "", ""},
+ {"</images/cat.jpg>\t\t ; \trel=prefetch \t ", true, "/images/cat.jpg",
+ "prefetch", "", ""},
+ {"\f</images/cat.jpg>\t\t ; \trel=prefetch \t ", false},
+ {"</images/cat.jpg>; rel= prefetch", true, "/images/cat.jpg", "prefetch",
+ "", ""},
+ {"<../images/cat.jpg?dog>; rel= prefetch", true, "../images/cat.jpg?dog",
+ "prefetch", "", ""},
+ {"</images/cat.jpg>; rel =prefetch", true, "/images/cat.jpg", "prefetch",
+ "", ""},
+ {"</images/cat.jpg>; rel pel=prefetch", false},
+ {"< /images/cat.jpg>", true, "/images/cat.jpg", "", "", ""},
+ {"</images/cat.jpg>; rel =", false},
+ {"</images/cat.jpg>; wut=sup; rel =prefetch", true, "/images/cat.jpg",
+ "prefetch", "", ""},
+ {"</images/cat.jpg>; wut=sup ; rel =prefetch", true, "/images/cat.jpg",
+ "prefetch", "", ""},
+ {"</images/cat.jpg>; wut=sup ; rel =prefetch \t ;", true,
+ "/images/cat.jpg", "prefetch", "", ""},
+ {"</images/cat.jpg> wut=sup ; rel =prefetch \t ;", false},
+ {"< /images/cat.jpg", false},
+ {"< http://wut.com/ sdfsdf ?sd>; rel=dns-prefetch", true,
+ "http://wut.com/ sdfsdf ?sd", "dns-prefetch", "", ""},
+ {"< http://wut.com/%20%20%3dsdfsdf?sd>; rel=dns-prefetch", true,
+ "http://wut.com/%20%20%3dsdfsdf?sd", "dns-prefetch", "", ""},
+ {"< http://wut.com/dfsdf?sdf=ghj&wer=rty>; rel=prefetch", true,
+ "http://wut.com/dfsdf?sdf=ghj&wer=rty", "prefetch", "", ""},
+ {"< http://wut.com/dfsdf?sdf=ghj&wer=rty>;;;;; rel=prefetch", true,
+ "http://wut.com/dfsdf?sdf=ghj&wer=rty", "prefetch", "", ""},
+ {"< http://wut.com/%20%20%3dsdfsdf?sd>; rel=preload;as=image", true,
+ "http://wut.com/%20%20%3dsdfsdf?sd", "preload", "image", ""},
+ {"< http://wut.com/%20%20%3dsdfsdf?sd>; rel=preload;as=whatever", true,
+ "http://wut.com/%20%20%3dsdfsdf?sd", "preload", "whatever", ""},
+ {"</images/cat.jpg>; anchor=foo; rel=prefetch;", false},
+ {"</images/cat.jpg>; rel=prefetch;anchor=foo ", false},
+ {"</images/cat.jpg>; anchor='foo'; rel=prefetch;", false},
+ {"</images/cat.jpg>; rel=prefetch;anchor='foo' ", false},
+ {"</images/cat.jpg>; rel=prefetch;anchor='' ", false},
+ {"</images/cat.jpg>; rel=prefetch;", true, "/images/cat.jpg", "prefetch",
+ "", ""},
+ {"</images/cat.jpg>; rel=prefetch ;", true, "/images/cat.jpg",
+ "prefetch", "", ""},
+ {"</images/ca,t.jpg>; rel=prefetch ;", true, "/images/ca,t.jpg",
+ "prefetch", "", ""},
+ {"<simple.css>; rel=stylesheet; title=\"title with a DQUOTE and "
+ "backslash\"",
+ true, "simple.css", "stylesheet", "", ""},
+ {"<simple.css>; rel=stylesheet; title=\"title with a DQUOTE \\\" and "
+ "backslash: \\\"",
+ false},
+ {"<simple.css>; title=\"title with a DQUOTE \\\" and backslash: \"; "
+ "rel=stylesheet; ",
+ true, "simple.css", "stylesheet", "", ""},
+ {"<simple.css>; title=\'title with a DQUOTE \\\' and backslash: \'; "
+ "rel=stylesheet; ",
+ true, "simple.css", "stylesheet", "", ""},
+ {"<simple.css>; title=\"title with a DQUOTE \\\" and ;backslash,: \"; "
+ "rel=stylesheet; ",
+ true, "simple.css", "stylesheet", "", ""},
+ {"<simple.css>; title=\"title with a DQUOTE \' and ;backslash,: \"; "
+ "rel=stylesheet; ",
+ true, "simple.css", "stylesheet", "", ""},
+ {"<simple.css>; title=\"\"; rel=stylesheet; ", true, "simple.css",
+ "stylesheet", "", ""},
+ {"<simple.css>; title=\"\"; rel=\"stylesheet\"; ", true, "simple.css",
+ "stylesheet", "", ""},
+ {"<simple.css>; rel=stylesheet; title=\"", false},
+ {"<simple.css>; rel=stylesheet; title=\"\"", true, "simple.css",
+ "stylesheet", "", ""},
+ {"<simple.css>; rel=\"stylesheet\"; title=\"", false},
+ {"<simple.css>; rel=\";style,sheet\"; title=\"", false},
+ {"<simple.css>; rel=\"bla'sdf\"; title=\"", false},
+ {"<simple.css>; rel=\"\"; title=\"\"", true, "simple.css", "", "", ""},
+ {"<simple.css>; rel=''; title=\"\"", true, "simple.css", "''", "", ""},
+ {"<simple.css>; rel=''; title=", false},
+ {"<simple.css>; rel=''; title", false},
+ {"<simple.css>; rel=''; media", false},
+ {"<simple.css>; rel=''; hreflang", false},
+ {"<simple.css>; rel=''; type", false},
+ {"<simple.css>; rel=''; rev", false},
+ {"<simple.css>; rel=''; bla", true, "simple.css", "''", "", ""},
+ {"<simple.css>; rel='prefetch", true, "simple.css", "'prefetch", "", ""},
+ {"<simple.css>; rel=\"prefetch", false},
+ {"<simple.css>; rel=\"", false},
+ {"<http://whatever.com>; rel=preconnect; valid!", true,
+ "http://whatever.com", "preconnect", "", ""},
+ {"<http://whatever.com>; rel=preconnect; valid$", true,
+ "http://whatever.com", "preconnect", "", ""},
+ {"<http://whatever.com>; rel=preconnect; invalid@", false},
+ {"<http://whatever.com>; rel=preconnect; invalid*", false},
+ {"</images/cat.jpg>; rel=prefetch;media='(max-width: 5000px)'", true,
+ "/images/cat.jpg", "prefetch", "", "'(max-width: 5000px)'"},
+ {"</images/cat.jpg>; rel=prefetch;media=\"(max-width: 5000px)\"", true,
+ "/images/cat.jpg", "prefetch", "", "(max-width: 5000px)"},
+ {"</images/cat.jpg>; rel=prefetch;media=(max-width:5000px)", true,
+ "/images/cat.jpg", "prefetch", "", "(max-width:5000px)"},
+};
+
+void PrintTo(const SingleTestCase& test, std::ostream* os) {
+ *os << testing::PrintToString(test.header_value);
+}
+
+class SingleLinkHeaderTest : public testing::TestWithParam<SingleTestCase> {};
+
+// Test the cases with a single header
+TEST_P(SingleLinkHeaderTest, Single) {
+ const SingleTestCase test_case = GetParam();
+ LinkHeaderSet header_set(test_case.header_value);
+ ASSERT_EQ(1u, header_set.size());
+ LinkHeader& header = header_set[0];
+ EXPECT_EQ(test_case.valid, header.Valid());
+ if (test_case.valid) {
+ EXPECT_STREQ(test_case.url, header.Url().Ascii().data());
+ EXPECT_STREQ(test_case.rel, header.Rel().Ascii().data());
+ EXPECT_STREQ(test_case.as, header.As().Ascii().data());
+ EXPECT_STREQ(test_case.media, header.Media().Ascii().data());
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(LinkHeaderTest,
+ SingleLinkHeaderTest,
+ testing::ValuesIn(g_single_test_cases));
+
+struct DoubleTestCase {
+ const char* header_value;
+ const char* url;
+ const char* rel;
+ bool valid;
+ const char* url2;
+ const char* rel2;
+ bool valid2;
+} g_double_test_cases[] = {
+ {"<ybg.css>; rel=stylesheet, <simple.css>; rel=stylesheet", "ybg.css",
+ "stylesheet", true, "simple.css", "stylesheet", true},
+ {"<ybg.css>; rel=stylesheet,<simple.css>; rel=stylesheet", "ybg.css",
+ "stylesheet", true, "simple.css", "stylesheet", true},
+ {"<ybg.css>; rel=stylesheet;crossorigin,<simple.css>; rel=stylesheet",
+ "ybg.css", "stylesheet", true, "simple.css", "stylesheet", true},
+ {"<hel,lo.css>; rel=stylesheet; title=\"foo,bar\", <simple.css>; "
+ "rel=stylesheet; title=\"foo;bar\"",
+ "hel,lo.css", "stylesheet", true, "simple.css", "stylesheet", true},
+};
+
+void PrintTo(const DoubleTestCase& test, std::ostream* os) {
+ *os << testing::PrintToString(test.header_value);
+}
+
+class DoubleLinkHeaderTest : public testing::TestWithParam<DoubleTestCase> {};
+
+TEST_P(DoubleLinkHeaderTest, Double) {
+ const DoubleTestCase test_case = GetParam();
+ LinkHeaderSet header_set(test_case.header_value);
+ ASSERT_EQ(2u, header_set.size());
+ LinkHeader& header1 = header_set[0];
+ LinkHeader& header2 = header_set[1];
+ EXPECT_STREQ(test_case.url, header1.Url().Ascii().data());
+ EXPECT_STREQ(test_case.rel, header1.Rel().Ascii().data());
+ EXPECT_EQ(test_case.valid, header1.Valid());
+ EXPECT_STREQ(test_case.url2, header2.Url().Ascii().data());
+ EXPECT_STREQ(test_case.rel2, header2.Rel().Ascii().data());
+ EXPECT_EQ(test_case.valid2, header2.Valid());
+}
+
+INSTANTIATE_TEST_CASE_P(LinkHeaderTest,
+ DoubleLinkHeaderTest,
+ testing::ValuesIn(g_double_test_cases));
+
+struct CrossOriginTestCase {
+ const char* header_value;
+ const char* url;
+ const char* rel;
+ const char* crossorigin;
+ bool valid;
+} g_cross_origin_test_cases[] = {
+ {"<http://whatever.com>; rel=preconnect", "http://whatever.com",
+ "preconnect", nullptr, true},
+ {"<http://whatever.com>; rel=preconnect; crossorigin=", "", "", "", false},
+ {"<http://whatever.com>; rel=preconnect; crossorigin",
+ "http://whatever.com", "preconnect", "", true},
+ {"<http://whatever.com>; rel=preconnect; crossorigin ",
+ "http://whatever.com", "preconnect", "", true},
+ {"<http://whatever.com>; rel=preconnect; crossorigin;",
+ "http://whatever.com", "preconnect", "", true},
+ {"<http://whatever.com>; rel=preconnect; crossorigin, "
+ "<http://whatever2.com>; rel=preconnect",
+ "http://whatever.com", "preconnect", "", true},
+ {"<http://whatever.com>; rel=preconnect; crossorigin , "
+ "<http://whatever2.com>; rel=preconnect",
+ "http://whatever.com", "preconnect", "", true},
+ {"<http://whatever.com>; rel=preconnect; "
+ "crossorigin,<http://whatever2.com>; rel=preconnect",
+ "http://whatever.com", "preconnect", "", true},
+ {"<http://whatever.com>; rel=preconnect; crossorigin=anonymous",
+ "http://whatever.com", "preconnect", "anonymous", true},
+ {"<http://whatever.com>; rel=preconnect; crossorigin=use-credentials",
+ "http://whatever.com", "preconnect", "use-credentials", true},
+ {"<http://whatever.com>; rel=preconnect; crossorigin=whatever",
+ "http://whatever.com", "preconnect", "whatever", true},
+ {"<http://whatever.com>; rel=preconnect; crossorig|in=whatever",
+ "http://whatever.com", "preconnect", nullptr, true},
+ {"<http://whatever.com>; rel=preconnect; crossorigin|=whatever",
+ "http://whatever.com", "preconnect", nullptr, true},
+};
+
+void PrintTo(const CrossOriginTestCase& test, std::ostream* os) {
+ *os << testing::PrintToString(test.header_value);
+}
+
+class CrossOriginLinkHeaderTest
+ : public testing::TestWithParam<CrossOriginTestCase> {};
+
+TEST_P(CrossOriginLinkHeaderTest, CrossOrigin) {
+ const CrossOriginTestCase test_case = GetParam();
+ LinkHeaderSet header_set(test_case.header_value);
+ ASSERT_GE(header_set.size(), 1u);
+ LinkHeader& header = header_set[0];
+ EXPECT_STREQ(test_case.url, header.Url().Ascii().data());
+ EXPECT_STREQ(test_case.rel, header.Rel().Ascii().data());
+ EXPECT_EQ(test_case.valid, header.Valid());
+ if (!test_case.crossorigin)
+ EXPECT_TRUE(header.CrossOrigin().IsNull());
+ else
+ EXPECT_STREQ(test_case.crossorigin, header.CrossOrigin().Ascii().data());
+}
+
+INSTANTIATE_TEST_CASE_P(LinkHeaderTest,
+ CrossOriginLinkHeaderTest,
+ testing::ValuesIn(g_cross_origin_test_cases));
+
+} // namespace
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/subresource_integrity.cc b/chromium/third_party/blink/renderer/platform/loader/subresource_integrity.cc
new file mode 100644
index 00000000000..7d6141afa47
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/subresource_integrity.cc
@@ -0,0 +1,503 @@
+// Copyright 2014 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/platform/loader/subresource_integrity.h"
+
+#include "third_party/blink/public/platform/web_crypto.h"
+#include "third_party/blink/public/platform/web_crypto_algorithm.h"
+#include "third_party/blink/renderer/platform/crypto.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/wtf/ascii_ctype.h"
+#include "third_party/blink/renderer/platform/wtf/dtoa/utils.h"
+#include "third_party/blink/renderer/platform/wtf/text/base64.h"
+#include "third_party/blink/renderer/platform/wtf/text/parsing_utilities.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+#include "third_party/boringssl/src/include/openssl/curve25519.h"
+
+namespace blink {
+
+// FIXME: This should probably use common functions with ContentSecurityPolicy.
+static bool IsIntegrityCharacter(UChar c) {
+ // Check if it's a base64 encoded value. We're pretty loose here, as there's
+ // not much risk in it, and it'll make it simpler for developers.
+ return IsASCIIAlphanumeric(c) || c == '_' || c == '-' || c == '+' ||
+ c == '/' || c == '=';
+}
+
+static bool IsValueCharacter(UChar c) {
+ // VCHAR per https://tools.ietf.org/html/rfc5234#appendix-B.1
+ return c >= 0x21 && c <= 0x7e;
+}
+
+static bool DigestsEqual(const DigestValue& digest1,
+ const DigestValue& digest2) {
+ if (digest1.size() != digest2.size())
+ return false;
+
+ for (size_t i = 0; i < digest1.size(); i++) {
+ if (digest1[i] != digest2[i])
+ return false;
+ }
+
+ return true;
+}
+
+inline bool IsSpaceOrComma(UChar c) {
+ return IsASCIISpace(c) || c == ',';
+}
+
+static String DigestToString(const DigestValue& digest) {
+ return Base64Encode(reinterpret_cast<const char*>(digest.data()),
+ digest.size(), kBase64DoNotInsertLFs);
+}
+
+void SubresourceIntegrity::ReportInfo::AddUseCount(UseCounterFeature feature) {
+ use_counts_.push_back(feature);
+}
+
+void SubresourceIntegrity::ReportInfo::AddConsoleErrorMessage(
+ const String& message) {
+ console_error_messages_.push_back(message);
+}
+
+void SubresourceIntegrity::ReportInfo::Clear() {
+ use_counts_.clear();
+ console_error_messages_.clear();
+}
+
+bool SubresourceIntegrity::CheckSubresourceIntegrity(
+ const IntegrityMetadataSet& metadata_set,
+ const char* content,
+ size_t size,
+ const KURL& resource_url,
+ const Resource& resource,
+ ReportInfo& report_info) {
+ if (!resource.IsSameOriginOrCORSSuccessful()) {
+ report_info.AddConsoleErrorMessage(
+ "Subresource Integrity: The resource '" + resource_url.ElidedString() +
+ "' has an integrity attribute, but the resource "
+ "requires the request to be CORS enabled to check "
+ "the integrity, and it is not. The resource has been "
+ "blocked because the integrity cannot be enforced.");
+ report_info.AddUseCount(ReportInfo::UseCounterFeature::
+ kSRIElementIntegrityAttributeButIneligible);
+ return false;
+ }
+
+ return CheckSubresourceIntegrityImpl(
+ metadata_set, content, size, resource_url,
+ resource.GetResponse().HttpHeaderField("Integrity"), report_info);
+}
+
+bool SubresourceIntegrity::CheckSubresourceIntegrity(
+ const String& integrity_metadata,
+ IntegrityFeatures features,
+ const char* content,
+ size_t size,
+ const KURL& resource_url,
+ ReportInfo& report_info) {
+ if (integrity_metadata.IsEmpty())
+ return true;
+
+ IntegrityMetadataSet metadata_set;
+ IntegrityParseResult integrity_parse_result = ParseIntegrityAttribute(
+ integrity_metadata, features, metadata_set, &report_info);
+ if (integrity_parse_result != kIntegrityParseValidResult)
+ return true;
+ // TODO(vogelheim): crbug.com/753349, figure out how deal with Ed25519
+ // checking here.
+ String integrity_header;
+ return CheckSubresourceIntegrityImpl(
+ metadata_set, content, size, resource_url, integrity_header, report_info);
+}
+
+bool SubresourceIntegrity::CheckSubresourceIntegrityImpl(
+ const IntegrityMetadataSet& metadata_set,
+ const char* content,
+ size_t size,
+ const KURL& resource_url,
+ const String integrity_header,
+ ReportInfo& report_info) {
+ if (!metadata_set.size())
+ return true;
+
+ // Check any of the "strongest" integrity constraints.
+ IntegrityAlgorithm max_algorithm = FindBestAlgorithm(metadata_set);
+ CheckFunction checker = GetCheckFunctionForAlgorithm(max_algorithm);
+ bool report_ed25519 = max_algorithm == IntegrityAlgorithm::kEd25519;
+ if (report_ed25519) {
+ report_info.AddUseCount(ReportInfo::UseCounterFeature::kSRISignatureCheck);
+ }
+ for (const IntegrityMetadata& metadata : metadata_set) {
+ if (metadata.Algorithm() == max_algorithm &&
+ (*checker)(metadata, content, size, integrity_header)) {
+ report_info.AddUseCount(ReportInfo::UseCounterFeature::
+ kSRIElementWithMatchingIntegrityAttribute);
+ if (report_ed25519) {
+ report_info.AddUseCount(
+ ReportInfo::UseCounterFeature::kSRISignatureSuccess);
+ }
+ return true;
+ }
+ }
+
+ // If we arrive here, none of the "strongest" constaints have validated
+ // the data we received. Report this fact.
+ DigestValue digest;
+ if (ComputeDigest(kHashAlgorithmSha256, content, size, digest)) {
+ // This message exposes the digest of the resource to the console.
+ // Because this is only to the console, that's okay for now, but we
+ // need to be very careful not to expose this in exceptions or
+ // JavaScript, otherwise it risks exposing information about the
+ // resource cross-origin.
+ report_info.AddConsoleErrorMessage(
+ "Failed to find a valid digest in the 'integrity' attribute for "
+ "resource '" +
+ resource_url.ElidedString() + "' with computed SHA-256 integrity '" +
+ DigestToString(digest) + "'. The resource has been blocked.");
+ } else {
+ report_info.AddConsoleErrorMessage(
+ "There was an error computing an integrity value for resource '" +
+ resource_url.ElidedString() + "'. The resource has been blocked.");
+ }
+ report_info.AddUseCount(ReportInfo::UseCounterFeature::
+ kSRIElementWithNonMatchingIntegrityAttribute);
+ return false;
+}
+
+IntegrityAlgorithm SubresourceIntegrity::FindBestAlgorithm(
+ const IntegrityMetadataSet& metadata_set) {
+ // Find the "strongest" algorithm in the set. (This relies on
+ // IntegrityAlgorithm declaration order matching the "strongest" order, so
+ // make the compiler check this assumption first.)
+ static_assert(IntegrityAlgorithm::kSha256 < IntegrityAlgorithm::kSha384 &&
+ IntegrityAlgorithm::kSha384 < IntegrityAlgorithm::kSha512 &&
+ IntegrityAlgorithm::kSha512 < IntegrityAlgorithm::kEd25519,
+ "IntegrityAlgorithm enum order should match the priority "
+ "of the integrity algorithms.");
+
+ // metadata_set is non-empty, so we are guaranteed to always have a result.
+ // This is effectively an implemenation of std::max_element (C++17).
+ DCHECK(!metadata_set.IsEmpty());
+ auto iter = metadata_set.begin();
+ IntegrityAlgorithm max_algorithm = iter->second;
+ ++iter;
+ for (; iter != metadata_set.end(); ++iter) {
+ max_algorithm = std::max(iter->second, max_algorithm);
+ }
+ return max_algorithm;
+}
+
+SubresourceIntegrity::CheckFunction
+SubresourceIntegrity::GetCheckFunctionForAlgorithm(
+ IntegrityAlgorithm algorithm) {
+ switch (algorithm) {
+ case IntegrityAlgorithm::kSha256:
+ case IntegrityAlgorithm::kSha384:
+ case IntegrityAlgorithm::kSha512:
+ return SubresourceIntegrity::CheckSubresourceIntegrityDigest;
+ case IntegrityAlgorithm::kEd25519:
+ return SubresourceIntegrity::CheckSubresourceIntegritySignature;
+ }
+ NOTREACHED();
+ return nullptr;
+}
+
+bool SubresourceIntegrity::CheckSubresourceIntegrityDigest(
+ const IntegrityMetadata& metadata,
+ const char* content,
+ size_t size,
+ const String& integrity_header) {
+ blink::HashAlgorithm hash_algo = kHashAlgorithmSha256;
+ switch (metadata.Algorithm()) {
+ case IntegrityAlgorithm::kSha256:
+ hash_algo = kHashAlgorithmSha256;
+ break;
+ case IntegrityAlgorithm::kSha384:
+ hash_algo = kHashAlgorithmSha384;
+ break;
+ case IntegrityAlgorithm::kSha512:
+ hash_algo = kHashAlgorithmSha512;
+ break;
+ case IntegrityAlgorithm::kEd25519:
+ NOTREACHED();
+ break;
+ }
+
+ DigestValue digest;
+ if (!ComputeDigest(hash_algo, content, size, digest))
+ return false;
+
+ Vector<char> hash_vector;
+ Base64Decode(metadata.Digest(), hash_vector);
+ DigestValue converted_hash_vector;
+ converted_hash_vector.Append(reinterpret_cast<uint8_t*>(hash_vector.data()),
+ hash_vector.size());
+ return DigestsEqual(digest, converted_hash_vector);
+}
+
+bool SubresourceIntegrity::CheckSubresourceIntegritySignature(
+ const IntegrityMetadata& metadata,
+ const char* content,
+ size_t size,
+ const String& integrity_header) {
+ DCHECK_EQ(IntegrityAlgorithm::kEd25519, metadata.Algorithm());
+
+ Vector<char> pubkey;
+ if (!Base64Decode(metadata.Digest(), pubkey) ||
+ pubkey.size() != ED25519_PUBLIC_KEY_LEN)
+ return false;
+
+ // Parse the Integrity:-header containing the signature(s).
+ Vector<UChar> integrity_header_chars;
+ integrity_header.AppendTo(integrity_header_chars);
+ const UChar* position = integrity_header_chars.begin();
+
+ const UChar* const end_position = integrity_header_chars.end();
+ while (position < end_position) {
+ // We expect substrings of the form "ed25519-<BASE64>* ,".
+ // We'll move all of our UChar* pointers up front (before any early exits
+ // from the loop), since we should cleanly skip the next token in the
+ // header in all cases, even if the current token doesn't validate.
+ SkipWhile<UChar, IsSpaceOrComma>(position, end_position);
+ IntegrityAlgorithm algorithm;
+ bool found_ed25519 =
+ kAlgorithmValid ==
+ ParseIntegrityHeaderAlgorithm(position, end_position, algorithm) &&
+ IntegrityAlgorithm::kEd25519 == algorithm;
+ const UChar* digest_begin = position;
+ SkipUntil<UChar, IsSpaceOrComma>(position, end_position);
+ const UChar* const digest_end = position;
+
+ // Now, algorithm contains the parsed algorithm specifier, the digest is
+ // found at digest_begin..digest_end, and position sits before the next
+ // token.
+
+ if (!found_ed25519)
+ continue;
+
+ String signature_raw;
+ if (!ParseDigest(digest_begin, digest_end, signature_raw))
+ continue;
+
+ Vector<char> signature;
+ Base64Decode(signature_raw, signature);
+ if (signature.size() != ED25519_SIGNATURE_LEN)
+ continue;
+
+ // BoringSSL/OpenSSL functions return 1 for success.
+ if (1 ==
+ ED25519_verify(reinterpret_cast<const uint8_t*>(content), size,
+ reinterpret_cast<const uint8_t*>(&*signature.begin()),
+ reinterpret_cast<const uint8_t*>(&*pubkey.begin()))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+SubresourceIntegrity::AlgorithmParseResult
+SubresourceIntegrity::ParseAttributeAlgorithm(const UChar*& begin,
+ const UChar* end,
+ IntegrityFeatures features,
+ IntegrityAlgorithm& algorithm) {
+ static const AlgorithmPrefixPair kPrefixes[] = {
+ {"sha256", IntegrityAlgorithm::kSha256},
+ {"sha-256", IntegrityAlgorithm::kSha256},
+ {"sha384", IntegrityAlgorithm::kSha384},
+ {"sha-384", IntegrityAlgorithm::kSha384},
+ {"sha512", IntegrityAlgorithm::kSha512},
+ {"sha-512", IntegrityAlgorithm::kSha512},
+ {"ed25519", IntegrityAlgorithm::kEd25519}};
+
+ // The last algorithm prefix is the ed25519 signature algorithm, which should
+ // only be enabled if kSignatures is requested. We'll implement this by
+ // adjusting the last_prefix index into the array.
+ size_t last_prefix = WTF_ARRAY_LENGTH(kPrefixes);
+ if (features != IntegrityFeatures::kSignatures)
+ last_prefix--;
+
+ return ParseAlgorithmPrefix(begin, end, kPrefixes, last_prefix, algorithm);
+}
+
+SubresourceIntegrity::AlgorithmParseResult
+SubresourceIntegrity::ParseIntegrityHeaderAlgorithm(
+ const UChar*& begin,
+ const UChar* end,
+ IntegrityAlgorithm& algorithm) {
+ static const AlgorithmPrefixPair kPrefixes[] = {
+ {"ed25519", IntegrityAlgorithm::kEd25519}};
+ return ParseAlgorithmPrefix(begin, end, kPrefixes,
+ WTF_ARRAY_LENGTH(kPrefixes), algorithm);
+}
+
+SubresourceIntegrity::AlgorithmParseResult
+SubresourceIntegrity::ParseAlgorithmPrefix(
+ const UChar*& string_position,
+ const UChar* string_end,
+ const AlgorithmPrefixPair* prefix_table,
+ size_t prefix_table_size,
+ IntegrityAlgorithm& algorithm) {
+ for (size_t i = 0; i < prefix_table_size; i++) {
+ const UChar* pos = string_position;
+ if (SkipToken<UChar>(pos, string_end, prefix_table[i].first) &&
+ SkipExactly<UChar>(pos, string_end, '-')) {
+ string_position = pos;
+ algorithm = prefix_table[i].second;
+ return kAlgorithmValid;
+ }
+ }
+
+ const UChar* dash_position = string_position;
+ SkipUntil<UChar>(dash_position, string_end, '-');
+ return dash_position < string_end ? kAlgorithmUnknown : kAlgorithmUnparsable;
+}
+
+// Before:
+//
+// [algorithm]-[hash] OR [algorithm]-[hash]?[options]
+// ^ ^ ^ ^
+// position end position end
+//
+// After (if successful: if the method returns false, we make no promises and
+// the caller should exit early):
+//
+// [algorithm]-[hash] OR [algorithm]-[hash]?[options]
+// ^ ^ ^
+// position/end position end
+bool SubresourceIntegrity::ParseDigest(const UChar*& position,
+ const UChar* end,
+ String& digest) {
+ const UChar* begin = position;
+ SkipWhile<UChar, IsIntegrityCharacter>(position, end);
+ if (position == begin || (position != end && *position != '?')) {
+ digest = g_empty_string;
+ return false;
+ }
+
+ // We accept base64url encoding, but normalize to "normal" base64 internally:
+ digest = NormalizeToBase64(String(begin, position - begin));
+ return true;
+}
+
+SubresourceIntegrity::IntegrityParseResult
+SubresourceIntegrity::ParseIntegrityAttribute(
+ const WTF::String& attribute,
+ IntegrityFeatures features,
+ IntegrityMetadataSet& metadata_set) {
+ return ParseIntegrityAttribute(attribute, features, metadata_set, nullptr);
+}
+
+SubresourceIntegrity::IntegrityParseResult
+SubresourceIntegrity::ParseIntegrityAttribute(
+ const WTF::String& attribute,
+ IntegrityFeatures features,
+ IntegrityMetadataSet& metadata_set,
+ ReportInfo* report_info) {
+ // We expect a "clean" metadata_set, since metadata_set should only be filled
+ // once.
+ DCHECK(metadata_set.IsEmpty());
+
+ Vector<UChar> characters;
+ attribute.StripWhiteSpace().AppendTo(characters);
+ const UChar* position = characters.data();
+ const UChar* end = characters.end();
+ const UChar* current_integrity_end;
+
+ bool error = false;
+
+ // The integrity attribute takes the form:
+ // *WSP hash-with-options *( 1*WSP hash-with-options ) *WSP / *WSP
+ // To parse this, break on whitespace, parsing each algorithm/digest/option
+ // in order.
+ while (position < end) {
+ WTF::String digest;
+ IntegrityAlgorithm algorithm;
+
+ SkipWhile<UChar, IsASCIISpace>(position, end);
+ current_integrity_end = position;
+ SkipUntil<UChar, IsASCIISpace>(current_integrity_end, end);
+
+ // Algorithm parsing errors are non-fatal (the subresource should
+ // still be loaded) because strong hash algorithms should be used
+ // without fear of breaking older user agents that don't support
+ // them.
+ AlgorithmParseResult parse_result = ParseAttributeAlgorithm(
+ position, current_integrity_end, features, algorithm);
+ if (parse_result == kAlgorithmUnknown) {
+ // Unknown hash algorithms are treated as if they're not present,
+ // and thus are not marked as an error, they're just skipped.
+ SkipUntil<UChar, IsASCIISpace>(position, end);
+ if (report_info) {
+ report_info->AddConsoleErrorMessage(
+ "Error parsing 'integrity' attribute ('" + attribute +
+ "'). The specified hash algorithm must be one of "
+ "'sha256', 'sha384', or 'sha512'.");
+ report_info->AddUseCount(
+ ReportInfo::UseCounterFeature::
+ kSRIElementWithUnparsableIntegrityAttribute);
+ }
+ continue;
+ }
+
+ if (parse_result == kAlgorithmUnparsable) {
+ error = true;
+ SkipUntil<UChar, IsASCIISpace>(position, end);
+ if (report_info) {
+ report_info->AddConsoleErrorMessage(
+ "Error parsing 'integrity' attribute ('" + attribute +
+ "'). The hash algorithm must be one of 'sha256', "
+ "'sha384', or 'sha512', followed by a '-' "
+ "character.");
+ report_info->AddUseCount(
+ ReportInfo::UseCounterFeature::
+ kSRIElementWithUnparsableIntegrityAttribute);
+ }
+ continue;
+ }
+
+ DCHECK_EQ(parse_result, kAlgorithmValid);
+
+ if (!ParseDigest(position, current_integrity_end, digest)) {
+ error = true;
+ SkipUntil<UChar, IsASCIISpace>(position, end);
+ if (report_info) {
+ report_info->AddConsoleErrorMessage(
+ "Error parsing 'integrity' attribute ('" + attribute +
+ "'). The digest must be a valid, base64-encoded value.");
+ report_info->AddUseCount(
+ ReportInfo::UseCounterFeature::
+ kSRIElementWithUnparsableIntegrityAttribute);
+ }
+ continue;
+ }
+
+ // The spec defines a space in the syntax for options, separated by a
+ // '?' character followed by unbounded VCHARs, but no actual options
+ // have been defined yet. Thus, for forward compatibility, ignore any
+ // options specified.
+ if (SkipExactly<UChar>(position, end, '?')) {
+ const UChar* begin = position;
+ SkipWhile<UChar, IsValueCharacter>(position, end);
+ if (begin != position && report_info) {
+ report_info->AddConsoleErrorMessage(
+ "Ignoring unrecogized 'integrity' attribute option '" +
+ String(begin, position - begin) + "'.");
+ }
+ }
+
+ IntegrityMetadata integrity_metadata(digest, algorithm);
+ metadata_set.insert(integrity_metadata.ToPair());
+ }
+ if (metadata_set.size() == 0 && error)
+ return kIntegrityParseNoValidResult;
+
+ return kIntegrityParseValidResult;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/subresource_integrity.h b/chromium/third_party/blink/renderer/platform/loader/subresource_integrity.h
new file mode 100644
index 00000000000..a5fb3ca2c87
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/subresource_integrity.h
@@ -0,0 +1,152 @@
+// Copyright 2014 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_PLATFORM_LOADER_SUBRESOURCE_INTEGRITY_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_SUBRESOURCE_INTEGRITY_H_
+
+#include "base/gtest_prod_util.h"
+#include "third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class KURL;
+class Resource;
+
+class PLATFORM_EXPORT SubresourceIntegrity final {
+ STATIC_ONLY(SubresourceIntegrity);
+
+ public:
+ class PLATFORM_EXPORT ReportInfo final {
+ public:
+ enum class UseCounterFeature {
+ kSRIElementWithMatchingIntegrityAttribute,
+ kSRIElementWithNonMatchingIntegrityAttribute,
+ kSRIElementIntegrityAttributeButIneligible,
+ kSRIElementWithUnparsableIntegrityAttribute,
+ kSRISignatureCheck,
+ kSRISignatureSuccess,
+ };
+
+ void AddUseCount(UseCounterFeature);
+ void AddConsoleErrorMessage(const String&);
+ void Clear();
+
+ const Vector<UseCounterFeature>& UseCounts() const { return use_counts_; }
+ const Vector<String>& ConsoleErrorMessages() const {
+ return console_error_messages_;
+ }
+
+ private:
+ Vector<UseCounterFeature> use_counts_;
+ Vector<String> console_error_messages_;
+ };
+
+ enum IntegrityParseResult {
+ kIntegrityParseValidResult,
+ kIntegrityParseNoValidResult
+ };
+
+ // Determine which SRI features to support when parsing integrity attributes.
+ enum class IntegrityFeatures {
+ kDefault, // Default: All sha* hash codes.
+ kSignatures // Also support the ed25519 signature scheme.
+ };
+
+ // The version with the IntegrityMetadataSet passed as the first argument
+ // assumes that the integrity attribute has already been parsed, and the
+ // IntegrityMetadataSet represents the result of that parsing.
+ static bool CheckSubresourceIntegrity(const IntegrityMetadataSet&,
+ const char* content,
+ size_t content_size,
+ const KURL& resource_url,
+ const Resource&,
+ ReportInfo&);
+ static bool CheckSubresourceIntegrity(const String&,
+ IntegrityFeatures,
+ const char* content,
+ size_t content_size,
+ const KURL& resource_url,
+ ReportInfo&);
+
+ // The IntegrityMetadataSet arguments are out parameters which contain the
+ // set of all valid, parsed metadata from |attribute|.
+ static IntegrityParseResult ParseIntegrityAttribute(
+ const WTF::String& attribute,
+ IntegrityFeatures,
+ IntegrityMetadataSet&);
+ static IntegrityParseResult ParseIntegrityAttribute(
+ const WTF::String& attribute,
+ IntegrityFeatures,
+ IntegrityMetadataSet&,
+ ReportInfo*);
+
+ private:
+ friend class SubresourceIntegrityTest;
+ FRIEND_TEST_ALL_PREFIXES(SubresourceIntegrityTest, Parsing);
+ FRIEND_TEST_ALL_PREFIXES(SubresourceIntegrityTest, ParseAlgorithm);
+ FRIEND_TEST_ALL_PREFIXES(SubresourceIntegrityTest, ParseHeader);
+ FRIEND_TEST_ALL_PREFIXES(SubresourceIntegrityTest, Prioritization);
+ FRIEND_TEST_ALL_PREFIXES(SubresourceIntegrityTest, FindBestAlgorithm);
+ FRIEND_TEST_ALL_PREFIXES(SubresourceIntegrityTest,
+ GetCheckFunctionForAlgorithm);
+
+ // The core implementation for all CheckSubresoureIntegrity functions.
+ static bool CheckSubresourceIntegrityImpl(const IntegrityMetadataSet&,
+ const char*,
+ size_t,
+ const KURL& resource_url,
+ const String integrity_header,
+ ReportInfo&);
+
+ enum AlgorithmParseResult {
+ kAlgorithmValid,
+ kAlgorithmUnparsable,
+ kAlgorithmUnknown
+ };
+
+ static IntegrityAlgorithm FindBestAlgorithm(const IntegrityMetadataSet&);
+
+ typedef bool (*CheckFunction)(const IntegrityMetadata&,
+ const char*,
+ size_t,
+ const String&);
+ static CheckFunction GetCheckFunctionForAlgorithm(IntegrityAlgorithm);
+
+ static bool CheckSubresourceIntegrityDigest(const IntegrityMetadata&,
+ const char*,
+ size_t,
+ const String& integrity_header);
+ static bool CheckSubresourceIntegritySignature(
+ const IntegrityMetadata&,
+ const char*,
+ size_t,
+ const String& integrity_header);
+
+ static AlgorithmParseResult ParseAttributeAlgorithm(const UChar*& begin,
+ const UChar* end,
+ IntegrityFeatures,
+ IntegrityAlgorithm&);
+ static AlgorithmParseResult ParseIntegrityHeaderAlgorithm(
+ const UChar*& begin,
+ const UChar* end,
+ IntegrityAlgorithm&);
+ typedef std::pair<const char*, IntegrityAlgorithm> AlgorithmPrefixPair;
+ static AlgorithmParseResult ParseAlgorithmPrefix(
+ const UChar*& string_position,
+ const UChar* string_end,
+ const AlgorithmPrefixPair* prefix_table,
+ size_t prefix_table_size,
+ IntegrityAlgorithm&);
+ static bool ParseDigest(const UChar*& begin,
+ const UChar* end,
+ String& digest);
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/subresource_integrity_test.cc b/chromium/third_party/blink/renderer/platform/loader/subresource_integrity_test.cc
new file mode 100644
index 00000000000..e0c92673a0a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/subresource_integrity_test.cc
@@ -0,0 +1,713 @@
+// Copyright 2014 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/platform/loader/subresource_integrity.h"
+
+#include "base/memory/scoped_refptr.h"
+#include "services/network/public/mojom/fetch_api.mojom-blink.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/crypto.h"
+#include "third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h"
+#include "third_party/blink/renderer/platform/loader/fetch/raw_resource.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
+#include "third_party/blink/renderer/platform/loader/testing/crypto_testing_platform_support.h"
+#include "third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
+#include "third_party/blink/renderer/platform/wtf/dtoa/utils.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+#include <algorithm>
+
+namespace blink {
+
+static const char kBasicScript[] = "alert('test');";
+static unsigned char kSha256Hash[] = {
+ 0x18, 0x01, 0x78, 0xf1, 0x03, 0xa8, 0xc5, 0x1b, 0xee, 0xd2, 0x06,
+ 0x40, 0x99, 0x08, 0xaf, 0x51, 0xd2, 0x4f, 0xc8, 0x16, 0x9c, 0xab,
+ 0x39, 0xc1, 0x01, 0x7c, 0x27, 0x91, 0xfa, 0x66, 0x41, 0x7e};
+static unsigned char kSha384Hash[] = {
+ 0x9d, 0xea, 0x77, 0x5e, 0x9b, 0xe1, 0x53, 0x1a, 0x42, 0x30, 0xe5, 0x57,
+ 0x20, 0x53, 0xde, 0x71, 0x38, 0x40, 0xa9, 0xd6, 0x3f, 0xb9, 0x57, 0xa2,
+ 0x0f, 0x89, 0x17, 0x4a, 0xa5, 0xe9, 0xc7, 0x46, 0x09, 0x51, 0x65, 0x38,
+ 0x7d, 0x34, 0xda, 0x16, 0x07, 0x22, 0x4e, 0xe6, 0x64, 0xed, 0xf9, 0x84};
+static unsigned char kSha512Hash[] = {
+ 0x4d, 0x79, 0x09, 0xc3, 0x5f, 0x0f, 0xaa, 0x55, 0x65, 0x11, 0x45,
+ 0xd7, 0x8d, 0xe5, 0xdb, 0x19, 0xeb, 0x68, 0xa7, 0x54, 0xca, 0x07,
+ 0x7c, 0x18, 0x40, 0x8a, 0x75, 0xfe, 0x28, 0x71, 0x08, 0xe1, 0x46,
+ 0x51, 0xf1, 0xbd, 0x4d, 0x83, 0x9a, 0x03, 0x53, 0x25, 0x92, 0x94,
+ 0xc0, 0xa9, 0x25, 0x7a, 0xc9, 0xa7, 0xaf, 0x2c, 0xef, 0x13, 0x8f,
+ 0x9a, 0x60, 0x1f, 0x52, 0x66, 0x67, 0xef, 0x88, 0xb4};
+static const char kSha256Integrity[] =
+ "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4=";
+static const char kSha256IntegrityLenientSyntax[] =
+ "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4=";
+static const char kSha256IntegrityWithEmptyOption[] =
+ "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4=?";
+static const char kSha256IntegrityWithOption[] =
+ "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4=?foo=bar";
+static const char kSha256IntegrityWithOptions[] =
+ "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4=?foo=bar?baz=foz";
+static const char kSha256IntegrityWithMimeOption[] =
+ "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4=?ct=application/"
+ "javascript";
+static const char kSha384Integrity[] =
+ "sha384-nep3XpvhUxpCMOVXIFPecThAqdY_uVeiD4kXSqXpx0YJUWU4fTTaFgciTuZk7fmE";
+static const char kSha512Integrity[] =
+ "sha512-TXkJw18PqlVlEUXXjeXbGetop1TKB3wYQIp1_"
+ "ihxCOFGUfG9TYOaA1MlkpTAqSV6yaevLO8Tj5pgH1JmZ--ItA==";
+static const char kSha384IntegrityLabeledAs256[] =
+ "sha256-nep3XpvhUxpCMOVXIFPecThAqdY_uVeiD4kXSqXpx0YJUWU4fTTaFgciTuZk7fmE";
+static const char kSha256AndSha384Integrities[] =
+ "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4= "
+ "sha384-nep3XpvhUxpCMOVXIFPecThAqdY_uVeiD4kXSqXpx0YJUWU4fTTaFgciTuZk7fmE";
+static const char kBadSha256AndGoodSha384Integrities[] =
+ "sha256-deadbeef "
+ "sha384-nep3XpvhUxpCMOVXIFPecThAqdY_uVeiD4kXSqXpx0YJUWU4fTTaFgciTuZk7fmE";
+static const char kGoodSha256AndBadSha384Integrities[] =
+ "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4= sha384-deadbeef";
+static const char kBadSha256AndBadSha384Integrities[] =
+ "sha256-deadbeef sha384-deadbeef";
+static const char kUnsupportedHashFunctionIntegrity[] =
+ "sha1-JfLW308qMPKfb4DaHpUBEESwuPc=";
+
+class SubresourceIntegrityTest : public testing::Test {
+ public:
+ SubresourceIntegrityTest()
+ : sec_url("https://example.test:443"),
+ insec_url("http://example.test:80") {}
+
+ protected:
+ void SetUp() override {
+ context =
+ MockFetchContext::Create(MockFetchContext::kShouldLoadNewResource);
+ }
+
+ SubresourceIntegrity::IntegrityFeatures Features() const {
+ return RuntimeEnabledFeatures::SignatureBasedIntegrityEnabledByRuntimeFlag()
+ ? SubresourceIntegrity::IntegrityFeatures::kSignatures
+ : SubresourceIntegrity::IntegrityFeatures::kDefault;
+ }
+
+ void ExpectAlgorithm(const String& text,
+ IntegrityAlgorithm expected_algorithm) {
+ Vector<UChar> characters;
+ text.AppendTo(characters);
+ const UChar* position = characters.data();
+ const UChar* end = characters.end();
+ IntegrityAlgorithm algorithm;
+
+ EXPECT_EQ(SubresourceIntegrity::kAlgorithmValid,
+ SubresourceIntegrity::ParseAttributeAlgorithm(
+ position, end, Features(), algorithm));
+ EXPECT_EQ(expected_algorithm, algorithm);
+ EXPECT_EQ(end, position);
+ }
+
+ void ExpectAlgorithmFailure(
+ const String& text,
+ SubresourceIntegrity::AlgorithmParseResult expected_result) {
+ Vector<UChar> characters;
+ text.AppendTo(characters);
+ const UChar* position = characters.data();
+ const UChar* begin = characters.data();
+ const UChar* end = characters.end();
+ IntegrityAlgorithm algorithm;
+
+ EXPECT_EQ(expected_result, SubresourceIntegrity::ParseAttributeAlgorithm(
+ position, end, Features(), algorithm));
+ EXPECT_EQ(begin, position);
+ }
+
+ void ExpectDigest(const String& text, const char* expected_digest) {
+ Vector<UChar> characters;
+ text.AppendTo(characters);
+ const UChar* position = characters.data();
+ const UChar* end = characters.end();
+ String digest;
+
+ EXPECT_TRUE(SubresourceIntegrity::ParseDigest(position, end, digest));
+ EXPECT_EQ(expected_digest, digest);
+ }
+
+ void ExpectDigestFailure(const String& text) {
+ Vector<UChar> characters;
+ text.AppendTo(characters);
+ const UChar* position = characters.data();
+ const UChar* end = characters.end();
+ String digest;
+
+ EXPECT_FALSE(SubresourceIntegrity::ParseDigest(position, end, digest));
+ EXPECT_TRUE(digest.IsEmpty());
+ }
+
+ void ExpectParse(const char* integrity_attribute,
+ const char* expected_digest,
+ IntegrityAlgorithm expected_algorithm) {
+ IntegrityMetadataSet metadata_set;
+
+ EXPECT_EQ(SubresourceIntegrity::kIntegrityParseValidResult,
+ SubresourceIntegrity::ParseIntegrityAttribute(
+ integrity_attribute, Features(), metadata_set));
+ EXPECT_EQ(1u, metadata_set.size());
+ if (metadata_set.size() > 0) {
+ IntegrityMetadata metadata = *metadata_set.begin();
+ EXPECT_EQ(expected_digest, metadata.Digest());
+ EXPECT_EQ(expected_algorithm, metadata.Algorithm());
+ }
+ }
+
+ void ExpectParseMultipleHashes(
+ const char* integrity_attribute,
+ const IntegrityMetadata expected_metadata_array[],
+ size_t expected_metadata_array_size) {
+ IntegrityMetadataSet expected_metadata_set;
+ for (size_t i = 0; i < expected_metadata_array_size; i++) {
+ expected_metadata_set.insert(expected_metadata_array[i].ToPair());
+ }
+ IntegrityMetadataSet metadata_set;
+ EXPECT_EQ(SubresourceIntegrity::kIntegrityParseValidResult,
+ SubresourceIntegrity::ParseIntegrityAttribute(
+ integrity_attribute, Features(), metadata_set));
+ EXPECT_TRUE(
+ IntegrityMetadata::SetsEqual(expected_metadata_set, metadata_set));
+ }
+
+ void ExpectParseFailure(const char* integrity_attribute) {
+ IntegrityMetadataSet metadata_set;
+
+ EXPECT_EQ(SubresourceIntegrity::kIntegrityParseNoValidResult,
+ SubresourceIntegrity::ParseIntegrityAttribute(
+ integrity_attribute, Features(), metadata_set));
+ }
+
+ void ExpectEmptyParseResult(const char* integrity_attribute) {
+ IntegrityMetadataSet metadata_set;
+
+ EXPECT_EQ(SubresourceIntegrity::kIntegrityParseValidResult,
+ SubresourceIntegrity::ParseIntegrityAttribute(
+ integrity_attribute, Features(), metadata_set));
+ EXPECT_EQ(0u, metadata_set.size());
+ }
+
+ enum ServiceWorkerMode {
+ kNoServiceWorker,
+ kSWOpaqueResponse,
+ kSWClearResponse
+ };
+
+ enum Expectation { kIntegritySuccess, kIntegrityFailure };
+
+ struct TestCase {
+ const KURL& origin;
+ const KURL& target;
+ const KURL* allow_origin_url;
+ const ServiceWorkerMode service_worker;
+ const Expectation expectation;
+ };
+
+ void CheckExpectedIntegrity(const char* integrity, const TestCase test) {
+ CheckExpectedIntegrity(integrity, test, test.expectation);
+ }
+
+ // Allows to overwrite the test expectation for cases that are always expected
+ // to fail:
+ void CheckExpectedIntegrity(const char* integrity,
+ const TestCase test,
+ Expectation expectation) {
+ context->SetSecurityOrigin(SecurityOrigin::Create(test.origin));
+
+ IntegrityMetadataSet metadata_set;
+ EXPECT_EQ(SubresourceIntegrity::kIntegrityParseValidResult,
+ SubresourceIntegrity::ParseIntegrityAttribute(
+ String(integrity), Features(), metadata_set));
+
+ SubresourceIntegrity::ReportInfo report_info;
+ EXPECT_EQ(expectation == kIntegritySuccess,
+ SubresourceIntegrity::CheckSubresourceIntegrity(
+ metadata_set, kBasicScript, strlen(kBasicScript), test.target,
+ *CreateTestResource(test.target, test.allow_origin_url,
+ test.service_worker),
+ report_info));
+ }
+
+ Resource* CreateTestResource(const KURL& url,
+ const KURL* allow_origin_url,
+ ServiceWorkerMode service_worker_mode) {
+ ResourceFetcher* fetcher = ResourceFetcher::Create(context);
+ ResourceLoadScheduler* scheduler = ResourceLoadScheduler::Create();
+ Resource* resource = RawResource::CreateForTest(url, Resource::kRaw);
+ ResourceLoader* loader =
+ ResourceLoader::Create(fetcher, scheduler, resource);
+
+ ResourceRequest request;
+ request.SetURL(url);
+
+ ResourceResponse response(url);
+ response.SetHTTPStatusCode(200);
+
+ if (allow_origin_url) {
+ request.SetFetchRequestMode(network::mojom::FetchRequestMode::kCORS);
+ resource->MutableOptions().cors_handling_by_resource_fetcher =
+ kEnableCORSHandlingByResourceFetcher;
+ response.SetHTTPHeaderField(
+ "access-control-allow-origin",
+ SecurityOrigin::Create(*allow_origin_url)->ToAtomicString());
+ response.SetHTTPHeaderField("access-control-allow-credentials", "true");
+ }
+
+ resource->SetResourceRequest(request);
+
+ if (service_worker_mode != kNoServiceWorker) {
+ response.SetWasFetchedViaServiceWorker(true);
+
+ if (service_worker_mode == kSWOpaqueResponse) {
+ response.SetResponseTypeViaServiceWorker(
+ network::mojom::FetchResponseType::kOpaque);
+ } else {
+ response.SetResponseTypeViaServiceWorker(
+ network::mojom::FetchResponseType::kDefault);
+ }
+ }
+
+ StringBuilder cors_error_msg;
+ CORSStatus cors_status =
+ loader->DetermineCORSStatus(response, cors_error_msg);
+ resource->SetCORSStatus(cors_status);
+
+ return resource;
+ }
+
+ KURL sec_url;
+ KURL insec_url;
+
+ ScopedTestingPlatformSupport<CryptoTestingPlatformSupport> platform_;
+ Persistent<MockFetchContext> context;
+};
+
+// Test the prioritization (i.e. selecting the "strongest" algorithm.
+// This effectively tests the definition of IntegrityAlgorithm in
+// IntegrityMetadata. The test is here, because SubresourceIntegrity is the
+// class that relies on this working as expected.)
+TEST_F(SubresourceIntegrityTest, Prioritization) {
+ // Check that each algorithm is it's own "strongest".
+ EXPECT_EQ(
+ IntegrityAlgorithm::kSha256,
+ std::max({IntegrityAlgorithm::kSha256, IntegrityAlgorithm::kSha256}));
+ EXPECT_EQ(
+ IntegrityAlgorithm::kSha384,
+ std::max({IntegrityAlgorithm::kSha384, IntegrityAlgorithm::kSha384}));
+
+ EXPECT_EQ(
+ IntegrityAlgorithm::kSha512,
+ std::max({IntegrityAlgorithm::kSha512, IntegrityAlgorithm::kSha512}));
+ EXPECT_EQ(
+ IntegrityAlgorithm::kEd25519,
+ std::max({IntegrityAlgorithm::kEd25519, IntegrityAlgorithm::kEd25519}));
+
+ // Check a mix of algorithms.
+ EXPECT_EQ(IntegrityAlgorithm::kSha384,
+ std::max({IntegrityAlgorithm::kSha256, IntegrityAlgorithm::kSha384,
+ IntegrityAlgorithm::kSha256}));
+ EXPECT_EQ(IntegrityAlgorithm::kSha512,
+ std::max({IntegrityAlgorithm::kSha384, IntegrityAlgorithm::kSha512,
+ IntegrityAlgorithm::kSha256}));
+ EXPECT_EQ(
+ IntegrityAlgorithm::kEd25519,
+ std::max({IntegrityAlgorithm::kSha384, IntegrityAlgorithm::kSha512,
+ IntegrityAlgorithm::kEd25519, IntegrityAlgorithm::kSha512,
+ IntegrityAlgorithm::kSha256, IntegrityAlgorithm::kSha512}));
+}
+
+TEST_F(SubresourceIntegrityTest, ParseAlgorithm) {
+ ExpectAlgorithm("sha256-", IntegrityAlgorithm::kSha256);
+ ExpectAlgorithm("sha384-", IntegrityAlgorithm::kSha384);
+ ExpectAlgorithm("sha512-", IntegrityAlgorithm::kSha512);
+ ExpectAlgorithm("sha-256-", IntegrityAlgorithm::kSha256);
+ ExpectAlgorithm("sha-384-", IntegrityAlgorithm::kSha384);
+ ExpectAlgorithm("sha-512-", IntegrityAlgorithm::kSha512);
+
+ {
+ ScopedSignatureBasedIntegrityForTest signature_based_integrity(true);
+ ExpectAlgorithm("ed25519-", IntegrityAlgorithm::kEd25519);
+ }
+ ScopedSignatureBasedIntegrityForTest signature_based_integrity(false);
+ ExpectAlgorithmFailure("ed25519-", SubresourceIntegrity::kAlgorithmUnknown);
+
+ ExpectAlgorithmFailure("sha1-", SubresourceIntegrity::kAlgorithmUnknown);
+ ExpectAlgorithmFailure("sha-1-", SubresourceIntegrity::kAlgorithmUnknown);
+ ExpectAlgorithmFailure("foobarsha256-",
+ SubresourceIntegrity::kAlgorithmUnknown);
+ ExpectAlgorithmFailure("foobar-", SubresourceIntegrity::kAlgorithmUnknown);
+ ExpectAlgorithmFailure("-", SubresourceIntegrity::kAlgorithmUnknown);
+ ExpectAlgorithmFailure("ed-25519-", SubresourceIntegrity::kAlgorithmUnknown);
+ ExpectAlgorithmFailure("ed25518-", SubresourceIntegrity::kAlgorithmUnknown);
+
+ ExpectAlgorithmFailure("sha256", SubresourceIntegrity::kAlgorithmUnparsable);
+ ExpectAlgorithmFailure("", SubresourceIntegrity::kAlgorithmUnparsable);
+}
+
+TEST_F(SubresourceIntegrityTest, ParseDigest) {
+ ExpectDigest("abcdefg", "abcdefg");
+ ExpectDigest("abcdefg?", "abcdefg");
+ ExpectDigest("ab+de/g", "ab+de/g");
+ ExpectDigest("ab-de_g", "ab+de/g");
+
+ ExpectDigestFailure("?");
+ ExpectDigestFailure("&&&foobar&&&");
+ ExpectDigestFailure("\x01\x02\x03\x04");
+}
+
+//
+// End-to-end parsing tests.
+//
+
+TEST_F(SubresourceIntegrityTest, Parsing) {
+ ExpectParseFailure("not_really_a_valid_anything");
+ ExpectParseFailure("sha256-&&&foobar&&&");
+ ExpectParseFailure("sha256-\x01\x02\x03\x04");
+ ExpectParseFailure("sha256-!!! sha256-!!!");
+
+ ExpectEmptyParseResult("foobar:///sha256-abcdefg");
+ ExpectEmptyParseResult("ni://sha256-abcdefg");
+ ExpectEmptyParseResult("ni:///sha256-abcdefg");
+ ExpectEmptyParseResult("notsha256atall-abcdefg");
+
+ ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ IntegrityAlgorithm::kSha256);
+
+ ExpectParse("sha-256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ IntegrityAlgorithm::kSha256);
+
+ ExpectParse(" sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE= ",
+ "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ IntegrityAlgorithm::kSha256);
+
+ ExpectParse(
+ "sha384-XVVXBGoYw6AJOh9J-Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup_tA1v5GPr",
+ "XVVXBGoYw6AJOh9J+Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup/tA1v5GPr",
+ IntegrityAlgorithm::kSha384);
+
+ ExpectParse(
+ "sha-384-XVVXBGoYw6AJOh9J_Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup_"
+ "tA1v5GPr",
+ "XVVXBGoYw6AJOh9J/Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup/tA1v5GPr",
+ IntegrityAlgorithm::kSha384);
+
+ ExpectParse(
+ "sha512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
+ "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
+ IntegrityAlgorithm::kSha512);
+
+ ExpectParse(
+ "sha-512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
+ "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
+ IntegrityAlgorithm::kSha512);
+
+ ExpectParse(
+ "sha-512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==?ct=application/javascript",
+ "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
+ IntegrityAlgorithm::kSha512);
+
+ ExpectParse(
+ "sha-512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==?ct=application/xhtml+xml",
+ "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
+ IntegrityAlgorithm::kSha512);
+
+ ExpectParse(
+ "sha-512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==?foo=bar?ct=application/xhtml+xml",
+ "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
+ IntegrityAlgorithm::kSha512);
+
+ ExpectParse(
+ "sha-512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==?ct=application/xhtml+xml?foo=bar",
+ "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
+ IntegrityAlgorithm::kSha512);
+
+ ExpectParse(
+ "sha-512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==?baz=foz?ct=application/"
+ "xhtml+xml?foo=bar",
+ "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
+ IntegrityAlgorithm::kSha512);
+
+ ExpectParseMultipleHashes("", nullptr, 0);
+ ExpectParseMultipleHashes(" ", nullptr, 0);
+
+ const IntegrityMetadata valid_sha384_and_sha512[] = {
+ IntegrityMetadata(
+ "XVVXBGoYw6AJOh9J+Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup/tA1v5GPr",
+ IntegrityAlgorithm::kSha384),
+ IntegrityMetadata("tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
+ IntegrityAlgorithm::kSha512),
+ };
+ ExpectParseMultipleHashes(
+ "sha384-XVVXBGoYw6AJOh9J+Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup/tA1v5GPr "
+ "sha512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
+ "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
+ valid_sha384_and_sha512, WTF_ARRAY_LENGTH(valid_sha384_and_sha512));
+
+ const IntegrityMetadata valid_sha256_and_sha256[] = {
+ IntegrityMetadata("BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ IntegrityAlgorithm::kSha256),
+ IntegrityMetadata("deadbeef", IntegrityAlgorithm::kSha256),
+ };
+ ExpectParseMultipleHashes(
+ "sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE= sha256-deadbeef",
+ valid_sha256_and_sha256, WTF_ARRAY_LENGTH(valid_sha256_and_sha256));
+
+ const IntegrityMetadata valid_sha256_and_invalid_sha256[] = {
+ IntegrityMetadata("BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ IntegrityAlgorithm::kSha256),
+ };
+ ExpectParseMultipleHashes(
+ "sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE= sha256-!!!!",
+ valid_sha256_and_invalid_sha256,
+ WTF_ARRAY_LENGTH(valid_sha256_and_invalid_sha256));
+
+ const IntegrityMetadata invalid_sha256_and_valid_sha256[] = {
+ IntegrityMetadata("BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ IntegrityAlgorithm::kSha256),
+ };
+ ExpectParseMultipleHashes(
+ "sha256-!!! sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ invalid_sha256_and_valid_sha256,
+ WTF_ARRAY_LENGTH(invalid_sha256_and_valid_sha256));
+
+ ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo=bar",
+ "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ IntegrityAlgorithm::kSha256);
+
+ ExpectParse(
+ "sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo=bar?baz=foz",
+ "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ IntegrityAlgorithm::kSha256);
+
+ ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?",
+ "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ IntegrityAlgorithm::kSha256);
+ ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo=bar",
+ "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ IntegrityAlgorithm::kSha256);
+ ExpectParse(
+ "sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo=bar?baz=foz",
+ "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ IntegrityAlgorithm::kSha256);
+ ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo",
+ "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ IntegrityAlgorithm::kSha256);
+ ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo=bar?",
+ "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ IntegrityAlgorithm::kSha256);
+ ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo:bar",
+ "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
+ IntegrityAlgorithm::kSha256);
+
+ {
+ ScopedSignatureBasedIntegrityForTest signature_based_integrity(false);
+ ExpectEmptyParseResult("ed25519-xxxx");
+ ExpectEmptyParseResult(
+ "ed25519-qGFmwTxlocg707D1cX4w60iTwtfwbMLf8ITDyfko7s0=");
+ }
+
+ ScopedSignatureBasedIntegrityForTest signature_based_integrity(true);
+ ExpectParse("ed25519-xxxx", "xxxx", IntegrityAlgorithm::kEd25519);
+ ExpectParse("ed25519-qGFmwTxlocg707D1cX4w60iTwtfwbMLf8ITDyfko7s0=",
+ "qGFmwTxlocg707D1cX4w60iTwtfwbMLf8ITDyfko7s0=",
+ IntegrityAlgorithm::kEd25519);
+ ExpectParse("ed25519-qGFmwTxlocg707D1cX4w60iTwtfwbMLf8ITDyfko7s0=?foo=bar",
+ "qGFmwTxlocg707D1cX4w60iTwtfwbMLf8ITDyfko7s0=",
+ IntegrityAlgorithm::kEd25519);
+ ExpectEmptyParseResult("ed-25519-xxx");
+ ExpectEmptyParseResult(
+ "ed-25519-qGFmwTxlocg707D1cX4w60iTwtfwbMLf8ITDyfko7s0=");
+}
+
+TEST_F(SubresourceIntegrityTest, ParsingBase64) {
+ ExpectParse(
+ "sha384-XVVXBGoYw6AJOh9J+Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup/tA1v5GPr",
+ "XVVXBGoYw6AJOh9J+Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup/tA1v5GPr",
+ IntegrityAlgorithm::kSha384);
+}
+
+// Tests that SubresourceIntegrity::CheckSubresourceIntegrity behaves correctly
+// when faced with secure or insecure origins, same origin and cross origin
+// requests, successful and failing CORS checks as well as when the response was
+// handled by a service worker.
+TEST_F(SubresourceIntegrityTest, OriginIntegrity) {
+ TestCase cases[] = {
+ // Secure origin, same origin -> integrity expected:
+ {sec_url, sec_url, nullptr, kNoServiceWorker, kIntegritySuccess},
+ {sec_url, sec_url, nullptr, kSWClearResponse, kIntegritySuccess},
+
+ // Insecure origin, secure target, CORS ok -> integrity expected:
+ {insec_url, sec_url, &insec_url, kNoServiceWorker, kIntegritySuccess},
+ {insec_url, sec_url, &insec_url, kSWClearResponse, kIntegritySuccess},
+ {insec_url, sec_url, nullptr, kSWClearResponse, kIntegritySuccess},
+
+ // Secure origin, insecure target, CORS ok -> no failure expected:
+ {sec_url, insec_url, &sec_url, kNoServiceWorker, kIntegritySuccess},
+ {sec_url, insec_url, &sec_url, kSWClearResponse, kIntegritySuccess},
+ {sec_url, insec_url, nullptr, kSWClearResponse, kIntegritySuccess},
+
+ // Insecure origin, secure target, no CORS headers -> failure expected:
+ {insec_url, sec_url, nullptr, kNoServiceWorker, kIntegrityFailure},
+
+ // Insecure origin, secure target, CORS failure -> failure expected:
+ {insec_url, sec_url, &sec_url, kNoServiceWorker, kIntegrityFailure},
+ {insec_url, sec_url, &sec_url, kSWOpaqueResponse, kIntegrityFailure},
+ {insec_url, sec_url, nullptr, kSWOpaqueResponse, kIntegrityFailure},
+
+ // Secure origin, same origin, opaque response from service worker ->
+ // failure expected:
+ {sec_url, sec_url, &sec_url, kSWOpaqueResponse, kIntegrityFailure},
+
+ // Insecure origin, insecure target, same origin-> failure expected:
+ {sec_url, insec_url, nullptr, kNoServiceWorker, kIntegrityFailure},
+ };
+
+ MockWebCryptoDigestorFactory factory_sha256(
+ kBasicScript, strlen(kBasicScript), kSha256Hash, sizeof(kSha256Hash));
+ MockWebCryptoDigestorFactory factory_sha384(
+ kBasicScript, strlen(kBasicScript), kSha384Hash, sizeof(kSha384Hash));
+ MockWebCryptoDigestorFactory factory_sha512(
+ kBasicScript, strlen(kBasicScript), kSha512Hash, sizeof(kSha512Hash));
+
+ CryptoTestingPlatformSupport::SetMockCryptoScope mock_crypto_scope(
+ *platform_.GetTestingPlatformSupport());
+
+ EXPECT_CALL(mock_crypto_scope.MockCrypto(),
+ CreateDigestorProxy(kWebCryptoAlgorithmIdSha256))
+ .WillRepeatedly(testing::InvokeWithoutArgs(
+ &factory_sha256, &MockWebCryptoDigestorFactory::Create));
+ EXPECT_CALL(mock_crypto_scope.MockCrypto(),
+ CreateDigestorProxy(kWebCryptoAlgorithmIdSha384))
+ .WillRepeatedly(testing::InvokeWithoutArgs(
+ &factory_sha384, &MockWebCryptoDigestorFactory::Create));
+ EXPECT_CALL(mock_crypto_scope.MockCrypto(),
+ CreateDigestorProxy(kWebCryptoAlgorithmIdSha512))
+ .WillRepeatedly(testing::InvokeWithoutArgs(
+ &factory_sha512, &MockWebCryptoDigestorFactory::Create));
+
+ for (const auto& test : cases) {
+ SCOPED_TRACE(
+ testing::Message()
+ << "Origin: " << test.origin.BaseAsString()
+ << ", target: " << test.target.BaseAsString()
+ << ", CORS access-control-allow-origin header: "
+ << (test.allow_origin_url ? test.allow_origin_url->BaseAsString() : "-")
+ << ", service worker: "
+ << (test.service_worker == kNoServiceWorker
+ ? "no"
+ : (test.service_worker == kSWClearResponse ? "clear response"
+ : "opaque response"))
+ << ", expected result: "
+ << (test.expectation == kIntegritySuccess ? "integrity" : "failure"));
+
+ // Verify basic sha256, sha384, and sha512 integrity checks.
+ CheckExpectedIntegrity(kSha256Integrity, test);
+ CheckExpectedIntegrity(kSha256IntegrityLenientSyntax, test);
+ CheckExpectedIntegrity(kSha384Integrity, test);
+ CheckExpectedIntegrity(kSha512Integrity, test);
+
+ // Verify multiple hashes in an attribute.
+ CheckExpectedIntegrity(kSha256AndSha384Integrities, test);
+ CheckExpectedIntegrity(kBadSha256AndGoodSha384Integrities, test);
+
+ // Unsupported hash functions should succeed.
+ CheckExpectedIntegrity(kUnsupportedHashFunctionIntegrity, test);
+
+ // Options should be ignored
+ CheckExpectedIntegrity(kSha256IntegrityWithEmptyOption, test);
+ CheckExpectedIntegrity(kSha256IntegrityWithOption, test);
+ CheckExpectedIntegrity(kSha256IntegrityWithOptions, test);
+ CheckExpectedIntegrity(kSha256IntegrityWithMimeOption, test);
+
+ // The following tests are expected to fail in every scenario:
+
+ // The hash label must match the hash value.
+ CheckExpectedIntegrity(kSha384IntegrityLabeledAs256, test,
+ Expectation::kIntegrityFailure);
+
+ // With multiple values, at least one must match, and it must be the
+ // strongest hash algorithm.
+ CheckExpectedIntegrity(kGoodSha256AndBadSha384Integrities, test,
+ Expectation::kIntegrityFailure);
+ CheckExpectedIntegrity(kBadSha256AndBadSha384Integrities, test,
+ Expectation::kIntegrityFailure);
+ }
+}
+
+TEST_F(SubresourceIntegrityTest, FindBestAlgorithm) {
+ // Each algorithm is its own best.
+ EXPECT_EQ(IntegrityAlgorithm::kSha256,
+ SubresourceIntegrity::FindBestAlgorithm(
+ IntegrityMetadataSet({{"", IntegrityAlgorithm::kSha256}})));
+ EXPECT_EQ(IntegrityAlgorithm::kSha384,
+ SubresourceIntegrity::FindBestAlgorithm(
+ IntegrityMetadataSet({{"", IntegrityAlgorithm::kSha384}})));
+ EXPECT_EQ(IntegrityAlgorithm::kSha512,
+ SubresourceIntegrity::FindBestAlgorithm(
+ IntegrityMetadataSet({{"", IntegrityAlgorithm::kSha512}})));
+ EXPECT_EQ(IntegrityAlgorithm::kEd25519,
+ SubresourceIntegrity::FindBestAlgorithm(
+ IntegrityMetadataSet({{"", IntegrityAlgorithm::kEd25519}})));
+
+ // Test combinations of multiple algorithms.
+ EXPECT_EQ(IntegrityAlgorithm::kSha384,
+ SubresourceIntegrity::FindBestAlgorithm(
+ IntegrityMetadataSet({{"", IntegrityAlgorithm::kSha256},
+ {"", IntegrityAlgorithm::kSha384}})));
+ EXPECT_EQ(IntegrityAlgorithm::kSha512,
+ SubresourceIntegrity::FindBestAlgorithm(
+ IntegrityMetadataSet({{"", IntegrityAlgorithm::kSha256},
+ {"", IntegrityAlgorithm::kSha512},
+ {"", IntegrityAlgorithm::kSha384}})));
+ EXPECT_EQ(IntegrityAlgorithm::kEd25519,
+ SubresourceIntegrity::FindBestAlgorithm(
+ IntegrityMetadataSet({{"", IntegrityAlgorithm::kSha256},
+ {"", IntegrityAlgorithm::kSha512},
+ {"", IntegrityAlgorithm::kEd25519}})));
+}
+
+TEST_F(SubresourceIntegrityTest, GetCheckFunctionForAlgorithm) {
+ EXPECT_TRUE(SubresourceIntegrity::CheckSubresourceIntegrityDigest ==
+ SubresourceIntegrity::GetCheckFunctionForAlgorithm(
+ IntegrityAlgorithm::kSha256));
+ EXPECT_TRUE(SubresourceIntegrity::CheckSubresourceIntegrityDigest ==
+ SubresourceIntegrity::GetCheckFunctionForAlgorithm(
+ IntegrityAlgorithm::kSha384));
+ EXPECT_TRUE(SubresourceIntegrity::CheckSubresourceIntegrityDigest ==
+ SubresourceIntegrity::GetCheckFunctionForAlgorithm(
+ IntegrityAlgorithm::kSha512));
+ EXPECT_TRUE(SubresourceIntegrity::CheckSubresourceIntegritySignature ==
+ SubresourceIntegrity::GetCheckFunctionForAlgorithm(
+ IntegrityAlgorithm::kEd25519));
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/testing/crypto_testing_platform_support.h b/chromium/third_party/blink/renderer/platform/loader/testing/crypto_testing_platform_support.h
new file mode 100644
index 00000000000..19ba9b6dfe1
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/testing/crypto_testing_platform_support.h
@@ -0,0 +1,51 @@
+// Copyright 2017 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_PLATFORM_LOADER_TESTING_CRYPTO_TESTING_PLATFORM_SUPPORT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_CRYPTO_TESTING_PLATFORM_SUPPORT_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.h"
+#include "third_party/blink/renderer/platform/testing/mock_web_crypto.h"
+#include "third_party/blink/renderer/platform/wtf/allocator.h"
+
+namespace blink {
+
+class CryptoTestingPlatformSupport : public FetchTestingPlatformSupport {
+ public:
+ CryptoTestingPlatformSupport() = default;
+ ~CryptoTestingPlatformSupport() override = default;
+
+ // Platform:
+ WebCrypto* Crypto() override { return mock_web_crypto_.get(); }
+
+ class SetMockCryptoScope final {
+ STACK_ALLOCATED();
+
+ public:
+ explicit SetMockCryptoScope(CryptoTestingPlatformSupport& platform)
+ : platform_(platform) {
+ DCHECK(!platform_.Crypto());
+ platform_.SetMockCrypto(MockWebCrypto::Create());
+ }
+ ~SetMockCryptoScope() { platform_.SetMockCrypto(nullptr); }
+ MockWebCrypto& MockCrypto() { return *platform_.mock_web_crypto_; }
+
+ private:
+ CryptoTestingPlatformSupport& platform_;
+ };
+
+ private:
+ void SetMockCrypto(std::unique_ptr<MockWebCrypto> crypto) {
+ mock_web_crypto_ = std::move(crypto);
+ }
+
+ std::unique_ptr<MockWebCrypto> mock_web_crypto_;
+
+ DISALLOW_COPY_AND_ASSIGN(CryptoTestingPlatformSupport);
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.cc b/chromium/third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.cc
new file mode 100644
index 00000000000..ac86a8cca00
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.cc
@@ -0,0 +1,48 @@
+// Copyright 2017 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/platform/loader/testing/fetch_testing_platform_support.h"
+
+#include <memory>
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/public/platform/web_url_loader.h"
+#include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_error.h"
+#include "third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h"
+#include "third_party/blink/renderer/platform/loader/testing/web_url_loader_factory_with_mock.h"
+#include "third_party/blink/renderer/platform/testing/weburl_loader_mock_factory_impl.h"
+
+namespace blink {
+
+FetchTestingPlatformSupport::FetchTestingPlatformSupport()
+ : url_loader_mock_factory_(new WebURLLoaderMockFactoryImpl(this)) {}
+
+FetchTestingPlatformSupport::~FetchTestingPlatformSupport() {
+ // Shutdowns WebURLLoaderMockFactory gracefully, serving all pending requests
+ // first, then flushing all registered URLs.
+ url_loader_mock_factory_->ServeAsynchronousRequests();
+ url_loader_mock_factory_->UnregisterAllURLsAndClearMemoryCache();
+}
+
+MockFetchContext* FetchTestingPlatformSupport::Context() {
+ if (!context_) {
+ context_ =
+ MockFetchContext::Create(MockFetchContext::kShouldLoadNewResource);
+ }
+ return context_;
+}
+
+WebURLLoaderMockFactory*
+FetchTestingPlatformSupport::GetURLLoaderMockFactory() {
+ return url_loader_mock_factory_.get();
+}
+
+std::unique_ptr<WebURLLoaderFactory>
+FetchTestingPlatformSupport::CreateDefaultURLLoaderFactory() {
+ return std::make_unique<WebURLLoaderFactoryWithMock>(
+ url_loader_mock_factory_.get());
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.h b/chromium/third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.h
new file mode 100644
index 00000000000..10e4c61c532
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/testing/fetch_testing_platform_support.h
@@ -0,0 +1,39 @@
+// Copyright 2017 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_PLATFORM_LOADER_TESTING_FETCH_TESTING_PLATFORM_SUPPORT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_FETCH_TESTING_PLATFORM_SUPPORT_H_
+
+#include <memory>
+#include "third_party/blink/renderer/platform/heap/persistent.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+
+namespace blink {
+
+class MockFetchContext;
+
+class FetchTestingPlatformSupport
+ : public TestingPlatformSupportWithMockScheduler {
+ public:
+ FetchTestingPlatformSupport();
+ ~FetchTestingPlatformSupport() override;
+
+ MockFetchContext* Context();
+
+ // Platform:
+ WebURLLoaderMockFactory* GetURLLoaderMockFactory() override;
+ std::unique_ptr<WebURLLoaderFactory> CreateDefaultURLLoaderFactory() override;
+
+ private:
+ class FetchTestingWebURLLoaderMockFactory;
+
+ Persistent<MockFetchContext> context_;
+ std::unique_ptr<WebURLLoaderMockFactory> url_loader_mock_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(FetchTestingPlatformSupport);
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h b/chromium/third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h
new file mode 100644
index 00000000000..e5f3adb730f
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h
@@ -0,0 +1,166 @@
+// Copyright 2016 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_PLATFORM_LOADER_TESTING_MOCK_FETCH_CONTEXT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_MOCK_FETCH_CONTEXT_H_
+
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_url_loader_factory.h"
+#include "third_party/blink/renderer/platform/exported/wrapped_resource_request.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_context.h"
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h"
+#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/test/fake_frame_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/test/fake_task_runner.h"
+#include "third_party/blink/renderer/platform/wtf/optional.h"
+
+#include <memory>
+
+namespace blink {
+
+class KURL;
+class ResourceRequest;
+struct ResourceLoaderOptions;
+
+// Mocked FetchContext for testing.
+class MockFetchContext : public FetchContext {
+ public:
+ enum LoadPolicy {
+ kShouldLoadNewResource,
+ kShouldNotLoadNewResource,
+ };
+ static MockFetchContext* Create(LoadPolicy load_policy,
+ scoped_refptr<base::SingleThreadTaskRunner>
+ loading_task_runner = nullptr) {
+ return new MockFetchContext(load_policy, std::move(loading_task_runner));
+ }
+
+ ~MockFetchContext() override = default;
+
+ void SetLoadComplete(bool complete) { complete_ = complete; }
+ long long GetTransferSize() const { return transfer_size_; }
+
+ const SecurityOrigin* GetSecurityOrigin() const override {
+ return security_origin_.get();
+ }
+
+ void SetSecurityOrigin(scoped_refptr<const SecurityOrigin> security_origin) {
+ security_origin_ = security_origin;
+ }
+
+ // The last ResourceRequest passed to DispatchWillSendRequest.
+ WTF::Optional<ResourceRequest> RequestFromWillSendRequest() const {
+ return will_send_request_;
+ }
+
+ // FetchContext:
+ void DispatchWillSendRequest(
+ unsigned long identifier,
+ ResourceRequest& request,
+ const ResourceResponse& redirect_response,
+ Resource::Type,
+ const FetchInitiatorInfo& = FetchInitiatorInfo()) override {
+ will_send_request_ = request;
+ }
+ bool AllowImage(bool images_enabled, const KURL&) const override {
+ return true;
+ }
+ ResourceRequestBlockedReason CanRequest(
+ Resource::Type,
+ const ResourceRequest&,
+ const KURL&,
+ const ResourceLoaderOptions&,
+ SecurityViolationReportingPolicy,
+ FetchParameters::OriginRestriction,
+ ResourceRequest::RedirectStatus redirect_status) const override {
+ return ResourceRequestBlockedReason::kNone;
+ }
+ ResourceRequestBlockedReason CheckCSPForRequest(
+ WebURLRequest::RequestContext,
+ const KURL& url,
+ const ResourceLoaderOptions& options,
+ SecurityViolationReportingPolicy reporting_policy,
+ ResourceRequest::RedirectStatus redirect_status) const override {
+ return ResourceRequestBlockedReason::kNone;
+ }
+ virtual ResourceRequestBlockedReason CheckResponseNosniff(
+ WebURLRequest::RequestContext,
+ const ResourceResponse&) const {
+ return ResourceRequestBlockedReason::kNone;
+ }
+ bool ShouldLoadNewResource(Resource::Type) const override {
+ return load_policy_ == kShouldLoadNewResource;
+ }
+ bool IsLoadComplete() const override { return complete_; }
+ void AddResourceTiming(
+ const ResourceTimingInfo& resource_timing_info) override {
+ transfer_size_ = resource_timing_info.TransferSize();
+ }
+
+ std::unique_ptr<WebURLLoader> CreateURLLoader(
+ const ResourceRequest& request,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ const ResourceLoaderOptions&) override {
+ if (!url_loader_factory_) {
+ url_loader_factory_ =
+ Platform::Current()->CreateDefaultURLLoaderFactory();
+ }
+ WrappedResourceRequest wrapped(request);
+ return url_loader_factory_->CreateURLLoader(wrapped, task_runner);
+ }
+
+ ResourceLoadScheduler::ThrottlingPolicy InitialLoadThrottlingPolicy()
+ const override {
+ return ResourceLoadScheduler::ThrottlingPolicy::kTight;
+ }
+
+ FrameScheduler* GetFrameScheduler() const override {
+ return frame_scheduler_.get();
+ }
+
+ scoped_refptr<base::SingleThreadTaskRunner> GetLoadingTaskRunner() override {
+ return frame_scheduler_->GetTaskRunner(TaskType::kInternalTest);
+ }
+
+ private:
+ class MockFrameScheduler final : public scheduler::FakeFrameScheduler {
+ public:
+ explicit MockFrameScheduler(
+ scoped_refptr<base::SingleThreadTaskRunner> runner)
+ : runner_(std::move(runner)) {}
+ scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(
+ TaskType) override {
+ return runner_;
+ }
+
+ private:
+ scoped_refptr<base::SingleThreadTaskRunner> runner_;
+ };
+
+ MockFetchContext(
+ LoadPolicy load_policy,
+ scoped_refptr<base::SingleThreadTaskRunner> loading_task_runner)
+ : load_policy_(load_policy),
+ runner_(loading_task_runner
+ ? std::move(loading_task_runner)
+ : base::MakeRefCounted<scheduler::FakeTaskRunner>()),
+ security_origin_(SecurityOrigin::CreateUnique()),
+ frame_scheduler_(new MockFrameScheduler(runner_)),
+ complete_(false),
+ transfer_size_(-1) {}
+
+ enum LoadPolicy load_policy_;
+ scoped_refptr<base::SingleThreadTaskRunner> runner_;
+ scoped_refptr<const SecurityOrigin> security_origin_;
+ std::unique_ptr<FrameScheduler> frame_scheduler_;
+ std::unique_ptr<WebURLLoaderFactory> url_loader_factory_;
+ bool complete_;
+ long long transfer_size_;
+ WTF::Optional<ResourceRequest> will_send_request_;
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/testing/mock_resource.cc b/chromium/third_party/blink/renderer/platform/loader/testing/mock_resource.cc
new file mode 100644
index 00000000000..367473a7aa0
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/testing/mock_resource.cc
@@ -0,0 +1,103 @@
+// Copyright 2016 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/platform/loader/testing/mock_resource.h"
+
+#include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
+
+namespace blink {
+
+namespace {
+
+class MockResourceFactory final : public NonTextResourceFactory {
+ public:
+ MockResourceFactory() : NonTextResourceFactory(Resource::kMock) {}
+
+ Resource* Create(const ResourceRequest& request,
+ const ResourceLoaderOptions& options) const override {
+ return new MockResource(request, options);
+ }
+};
+
+} // namespace
+
+// static
+MockResource* MockResource::Fetch(FetchParameters& params,
+ ResourceFetcher* fetcher,
+ ResourceClient* client) {
+ params.SetRequestContext(WebURLRequest::kRequestContextSubresource);
+ return static_cast<MockResource*>(
+ fetcher->RequestResource(params, MockResourceFactory(), client));
+}
+
+// static
+MockResource* MockResource::Create(const ResourceRequest& request) {
+ ResourceLoaderOptions options;
+ return new MockResource(request, options);
+}
+
+MockResource* MockResource::Create(const KURL& url) {
+ ResourceRequest request(url);
+ return Create(request);
+}
+
+MockResource::MockResource(const ResourceRequest& request,
+ const ResourceLoaderOptions& options)
+ : Resource(request, Resource::kMock, options) {}
+
+CachedMetadataHandler* MockResource::CreateCachedMetadataHandler(
+ std::unique_ptr<CachedMetadataSender> send_callback) {
+ return new MockCacheHandler(std::move(send_callback));
+}
+
+void MockResource::SetSerializedCachedMetadata(const char* data, size_t size) {
+ Resource::SetSerializedCachedMetadata(data, size);
+ MockCacheHandler* cache_handler =
+ static_cast<MockCacheHandler*>(Resource::CacheHandler());
+ if (cache_handler) {
+ cache_handler->Set(data, size);
+ }
+}
+
+void MockResource::SendCachedMetadata(const char* data, size_t size) {
+ MockCacheHandler* cache_handler =
+ static_cast<MockCacheHandler*>(Resource::CacheHandler());
+ if (cache_handler) {
+ cache_handler->Set(data, size);
+ cache_handler->Send();
+ }
+}
+
+MockCacheHandler* MockResource::CacheHandler() {
+ return static_cast<MockCacheHandler*>(Resource::CacheHandler());
+}
+
+MockCacheHandler::MockCacheHandler(
+ std::unique_ptr<CachedMetadataSender> send_callback)
+ : send_callback_(std::move(send_callback)) {}
+
+void MockCacheHandler::Set(const char* data, size_t size) {
+ data_.emplace();
+ data_->Append(data, size);
+}
+
+void MockCacheHandler::ClearCachedMetadata(
+ CachedMetadataHandler::CacheType cache_type) {
+ if (cache_type == CachedMetadataHandler::kSendToPlatform) {
+ Send();
+ }
+ data_.reset();
+}
+
+void MockCacheHandler::Send() {
+ if (data_) {
+ send_callback_->Send(data_->data(), data_->size());
+ } else {
+ send_callback_->Send(nullptr, 0);
+ }
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/testing/mock_resource.h b/chromium/third_party/blink/renderer/platform/loader/testing/mock_resource.h
new file mode 100644
index 00000000000..69e307efbe4
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/testing/mock_resource.h
@@ -0,0 +1,60 @@
+// Copyright 2016 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_PLATFORM_LOADER_TESTING_MOCK_RESOURCE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_MOCK_RESOURCE_H_
+
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+
+namespace blink {
+
+class FetchParameters;
+class ResourceFetcher;
+struct ResourceLoaderOptions;
+
+// Mocked cache handler class used by MockResource to test the caching behaviour
+// of Resource.
+class MockCacheHandler : public CachedMetadataHandler {
+ public:
+ MockCacheHandler(std::unique_ptr<CachedMetadataSender> send_callback);
+
+ void Set(const char* data, size_t);
+ void ClearCachedMetadata(CachedMetadataHandler::CacheType) override;
+ void Send();
+
+ String Encoding() const override { return "mock encoding"; }
+ bool IsServedFromCacheStorage() const override { return false; }
+
+ private:
+ std::unique_ptr<CachedMetadataSender> send_callback_;
+ base::Optional<Vector<char>> data_;
+};
+
+// Mocked Resource sub-class for testing. MockResource class can pretend a type
+// of Resource sub-class in a simple way. You should not expect anything
+// complicated to emulate actual sub-resources, but you may be able to use this
+// class to verify classes that consume Resource sub-classes in a simple way.
+class MockResource final : public Resource {
+ public:
+ static MockResource* Fetch(FetchParameters&,
+ ResourceFetcher*,
+ ResourceClient*);
+ static MockResource* Create(const ResourceRequest&);
+ static MockResource* Create(const KURL&);
+ MockResource(const ResourceRequest&, const ResourceLoaderOptions&);
+
+ CachedMetadataHandler* CreateCachedMetadataHandler(
+ std::unique_ptr<CachedMetadataSender> send_callback) override;
+ void SetSerializedCachedMetadata(const char*, size_t) override;
+
+ MockCacheHandler* CacheHandler();
+
+ void SendCachedMetadata(const char*, size_t);
+};
+
+} // namespace blink
+
+#endif
diff --git a/chromium/third_party/blink/renderer/platform/loader/testing/mock_resource_client.h b/chromium/third_party/blink/renderer/platform/loader/testing/mock_resource_client.h
new file mode 100644
index 00000000000..a9fe34287e3
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/testing/mock_resource_client.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2013, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_MOCK_RESOURCE_CLIENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_MOCK_RESOURCE_CLIENT_H_
+
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_client.h"
+
+namespace blink {
+
+class MockResourceClient : public GarbageCollectedFinalized<MockResourceClient>,
+ public ResourceClient {
+ USING_GARBAGE_COLLECTED_MIXIN(MockResourceClient);
+
+ public:
+ MockResourceClient() = default;
+ ~MockResourceClient() override = default;
+
+ void NotifyFinished(Resource*) override {
+ CHECK(!notify_finished_called_);
+ notify_finished_called_ = true;
+ }
+ String DebugName() const override { return "MockResourceClient"; }
+ bool NotifyFinishedCalled() const { return notify_finished_called_; }
+ void RemoveAsClient() { ClearResource(); }
+
+ protected:
+ bool notify_finished_called_ = false;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_MOCK_RESOURCE_CLIENT_H_
diff --git a/chromium/third_party/blink/renderer/platform/loader/testing/web_url_loader_factory_with_mock.cc b/chromium/third_party/blink/renderer/platform/loader/testing/web_url_loader_factory_with_mock.cc
new file mode 100644
index 00000000000..3297570e76a
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/testing/web_url_loader_factory_with_mock.cc
@@ -0,0 +1,24 @@
+// Copyright 2017 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/platform/loader/testing/web_url_loader_factory_with_mock.h"
+
+#include "third_party/blink/public/platform/web_url_loader.h"
+#include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
+
+namespace blink {
+
+WebURLLoaderFactoryWithMock::WebURLLoaderFactoryWithMock(
+ WebURLLoaderMockFactory* mock_factory)
+ : mock_factory_(mock_factory) {}
+
+WebURLLoaderFactoryWithMock::~WebURLLoaderFactoryWithMock() = default;
+
+std::unique_ptr<WebURLLoader> WebURLLoaderFactoryWithMock::CreateURLLoader(
+ const WebURLRequest& request,
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ return mock_factory_->CreateURLLoader(nullptr);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/platform/loader/testing/web_url_loader_factory_with_mock.h b/chromium/third_party/blink/renderer/platform/loader/testing/web_url_loader_factory_with_mock.h
new file mode 100644
index 00000000000..c66ba9ff8fd
--- /dev/null
+++ b/chromium/third_party/blink/renderer/platform/loader/testing/web_url_loader_factory_with_mock.h
@@ -0,0 +1,32 @@
+// Copyright 2017 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_PLATFORM_LOADER_TESTING_WEB_URL_LOADER_FACTORY_WITH_MOCK_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_WEB_URL_LOADER_FACTORY_WITH_MOCK_H_
+
+#include <memory>
+
+#include "third_party/blink/public/platform/web_url_loader_factory.h"
+
+namespace blink {
+
+class WebURLLoaderMockFactory;
+
+class WebURLLoaderFactoryWithMock : public WebURLLoaderFactory {
+ public:
+ explicit WebURLLoaderFactoryWithMock(WebURLLoaderMockFactory*);
+ ~WebURLLoaderFactoryWithMock() override;
+
+ std::unique_ptr<WebURLLoader> CreateURLLoader(
+ const WebURLRequest&,
+ scoped_refptr<base::SingleThreadTaskRunner>) override;
+
+ private:
+ // Not owned. The mock factory should outlive |this|.
+ WebURLLoaderMockFactory* mock_factory_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_TESTING_WEB_URL_LOADER_FACTORY_WITH_MOCK_H_