diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/platform/loader')
97 files changed, 7697 insertions, 2325 deletions
diff --git a/chromium/third_party/blink/renderer/platform/loader/BUILD.gn b/chromium/third_party/blink/renderer/platform/loader/BUILD.gn index 4f8c6195b48..fb533c27816 100644 --- a/chromium/third_party/blink/renderer/platform/loader/BUILD.gn +++ b/chromium/third_party/blink/renderer/platform/loader/BUILD.gn @@ -24,6 +24,7 @@ blink_platform_sources("loader") { "cors/cors.h", "cors/cors_error_string.cc", "cors/cors_error_string.h", + "fetch/back_forward_cache_loader_helper.h", "fetch/buffering_bytes_consumer.cc", "fetch/buffering_bytes_consumer.h", "fetch/bytes_consumer.cc", @@ -64,6 +65,7 @@ blink_platform_sources("loader") { "fetch/preload_key.h", "fetch/raw_resource.cc", "fetch/raw_resource.h", + "fetch/render_blocking_behavior.h", "fetch/resource.cc", "fetch/resource.h", "fetch/resource_client.cc", @@ -116,10 +118,17 @@ blink_platform_sources("loader") { "fetch/trust_token_params_conversion.h", "fetch/unique_identifier.cc", "fetch/unique_identifier.h", + "fetch/url_loader/mojo_url_loader_client.cc", + "fetch/url_loader/mojo_url_loader_client.h", "fetch/url_loader/request_conversion.cc", "fetch/url_loader/request_conversion.h", - "fetch/url_loader/web_bundle_subresource_loader.cc", - "fetch/url_loader/web_bundle_subresource_loader.h", + "fetch/url_loader/sync_load_context.cc", + "fetch/url_loader/sync_load_context.h", + "fetch/url_loader/sync_load_response.cc", + "fetch/url_loader/sync_load_response.h", + "fetch/url_loader/web_resource_request_sender.cc", + "fetch/url_loader/web_url_loader.cc", + "fetch/url_loader/web_url_loader_factory.cc", "fetch/url_loader/worker_main_script_loader.cc", "fetch/url_loader/worker_main_script_loader.h", "fetch/url_loader/worker_main_script_loader_client.h", @@ -131,6 +140,8 @@ blink_platform_sources("loader") { "internet_disconnected_web_url_loader.cc", "link_header.cc", "link_header.h", + "mixed_content.cc", + "mixed_content.h", "mixed_content_autoupgrade_status.h", "static_data_navigation_body_loader.cc", "static_data_navigation_body_loader.h", @@ -147,17 +158,21 @@ blink_platform_sources("loader") { deps = [ ":make_platform_loader_generated_fetch_initiator_type_names", "//components/link_header_util", - "//components/web_package", + "//components/variations/net:net", "//net", "//services/metrics/public/cpp:ukm_builders", "//services/network/public/cpp", "//services/network/public/mojom:mojom_blink", + "//third_party/blink/public/mojom:mojom_platform_blink", "//third_party/blink/renderer/platform/blob:blob", "//third_party/blink/renderer/platform/instrumentation:instrumentation", "//third_party/blink/renderer/platform/network:network", "//third_party/blink/renderer/platform/scheduler:scheduler", ] - public_deps = [ "//third_party/blink/renderer/platform/heap:heap" ] + public_deps = [ + "//third_party/blink/public/mojom:mojom_platform_headers", + "//third_party/blink/renderer/platform/heap:heap", + ] allow_circular_includes_from = [ "//third_party/blink/renderer/platform/network:network" ] } @@ -174,6 +189,7 @@ source_set("unit_tests") { "cors/cors_test.cc", "fetch/buffering_bytes_consumer_test.cc", "fetch/bytes_consumer_test.cc", + "fetch/cached_metadata_handler_test.cc", "fetch/client_hints_preferences_test.cc", "fetch/data_pipe_bytes_consumer_test.cc", "fetch/fetch_api_request_body_mojom_traits_test.cc", @@ -192,7 +208,10 @@ source_set("unit_tests") { "fetch/response_body_loader_test.cc", "fetch/shared_buffer_bytes_consumer_test.cc", "fetch/source_keyed_cached_metadata_handler_test.cc", - "fetch/url_loader/web_bundle_subresource_loader_test.cc", + "fetch/url_loader/mojo_url_loader_client_unittest.cc", + "fetch/url_loader/sync_load_context_unittest.cc", + "fetch/url_loader/web_resource_request_sender_unittest.cc", + "fetch/url_loader/web_url_loader_unittest.cc", "fetch/url_loader/worker_main_script_loader_unittest.cc", "ftp_directory_listing_test.cc", "link_header_test.cc", @@ -204,8 +223,8 @@ source_set("unit_tests") { deps = [ "//base/test:test_support", - "//components/web_package:test_support", "//mojo/public/cpp/test_support:test_utils", + "//net:test_support", "//net/traffic_annotation:test_support", "//services/network:test_support", "//testing/gmock", diff --git a/chromium/third_party/blink/renderer/platform/loader/DEPS b/chromium/third_party/blink/renderer/platform/loader/DEPS index 52019c0ffc9..4d149b6e493 100644 --- a/chromium/third_party/blink/renderer/platform/loader/DEPS +++ b/chromium/third_party/blink/renderer/platform/loader/DEPS @@ -11,8 +11,11 @@ include_rules = [ "+components/link_header_util", # for LinkHeader.cpp "+net/base/load_flags.h", "+net/base/net_errors.h", + "+net/traffic_annotation/network_traffic_annotation_test_helper.h", + "+net/url_request/redirect_info.h", "+services/metrics/public", # for UKM API "+services/network/public", # for Fetch API and CORS + "+third_party/blink/renderer/platform/back_forward_cache_utils.h", "+third_party/blink/renderer/platform/bindings/dom_wrapper_world.h", "+third_party/blink/renderer/platform/bindings/parkable_string.h", "+third_party/blink/renderer/platform/bindings/script_forbidden_scope.h", @@ -28,6 +31,7 @@ include_rules = [ "+third_party/blink/renderer/platform/loader/fetch/cross_origin_attribute_value.h", "+third_party/blink/renderer/platform/instrumentation/memory_pressure_listener.h", "+third_party/blink/renderer/platform/mhtml", + "+third_party/blink/renderer/platform/mojo/mojo_binding_context.h", "+third_party/blink/renderer/platform/network", "+third_party/blink/renderer/platform/platform_export.h", "+third_party/blink/renderer/platform/platform_probe_sink.h", diff --git a/chromium/third_party/blink/renderer/platform/loader/DIR_METADATA b/chromium/third_party/blink/renderer/platform/loader/DIR_METADATA new file mode 100644 index 00000000000..a080a748373 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/loader/DIR_METADATA @@ -0,0 +1,4 @@ +monorail { + component: "Blink>Loader" +} +team_email: "loading-dev@chromium.org"
\ No newline at end of file diff --git a/chromium/third_party/blink/renderer/platform/loader/OWNERS b/chromium/third_party/blink/renderer/platform/loader/OWNERS index 1c34e1edc6a..b46fa621550 100644 --- a/chromium/third_party/blink/renderer/platform/loader/OWNERS +++ b/chromium/third_party/blink/renderer/platform/loader/OWNERS @@ -4,6 +4,3 @@ mkwst@chromium.org toyoshim@chromium.org yhirano@chromium.org yoavweiss@chromium.org - -# TEAM: loading-dev@chromium.org -# COMPONENT: Blink>Loader diff --git a/chromium/third_party/blink/renderer/platform/loader/allowed_by_nosniff.cc b/chromium/third_party/blink/renderer/platform/loader/allowed_by_nosniff.cc index 180d4f32735..1d45872564e 100644 --- a/chromium/third_party/blink/renderer/platform/loader/allowed_by_nosniff.cc +++ b/chromium/third_party/blink/renderer/platform/loader/allowed_by_nosniff.cc @@ -56,6 +56,16 @@ const WebFeature kTextXmlFeatures[2] = { WebFeature::kSameOriginTextXml, }; +const WebFeature kJsonFeatures[2] = { + WebFeature::kCrossOriginJsonTypeForScript, + WebFeature::kSameOriginJsonTypeForScript, +}; + +const WebFeature kUnknownFeatures[2] = { + WebFeature::kCrossOriginStrictNosniffWouldBlock, + WebFeature::kSameOriginStrictNosniffWouldBlock, +}; + // Helper function to decide what to do with with a given mime type. This takes // - a mime type // - inputs that affect the decision (is_same_origin, mime_type_check_mode). @@ -121,6 +131,11 @@ bool AllowMimeTypeAsScript(const String& mime_type, counter = kTextPlainFeatures[same_origin]; } else if (mime_type.StartsWithIgnoringCase("text/xml")) { counter = kTextXmlFeatures[same_origin]; + } else if (mime_type.StartsWithIgnoringCase("text/json") || + mime_type.StartsWithIgnoringCase("application/json")) { + counter = kJsonFeatures[same_origin]; + } else { + counter = kUnknownFeatures[same_origin]; } return true; diff --git a/chromium/third_party/blink/renderer/platform/loader/allowed_by_nosniff_test.cc b/chromium/third_party/blink/renderer/platform/loader/allowed_by_nosniff_test.cc index 4f47d21a355..f4db8179fc9 100644 --- a/chromium/third_party/blink/renderer/platform/loader/allowed_by_nosniff_test.cc +++ b/chromium/third_party/blink/renderer/platform/loader/allowed_by_nosniff_test.cc @@ -152,6 +152,16 @@ TEST_F(AllowedByNosniffTest, Counters) { {bla, blubb, "text/plain", kOpaque, WebFeature::kCrossOriginTextPlain}, {bla, bla, "text/plain", kBasic, WebFeature::kSameOriginTextScript}, {bla, bla, "text/plain", kBasic, WebFeature::kSameOriginTextPlain}, + {bla, bla, "text/json", kBasic, WebFeature::kSameOriginTextScript}, + + // JSON + {bla, bla, "text/json", kBasic, WebFeature::kSameOriginJsonTypeForScript}, + {bla, bla, "application/json", kBasic, + WebFeature::kSameOriginJsonTypeForScript}, + {bla, blubb, "text/json", kOpaque, + WebFeature::kCrossOriginJsonTypeForScript}, + {bla, blubb, "application/json", kOpaque, + WebFeature::kCrossOriginJsonTypeForScript}, // Test mime type and subtype handling. {bla, bla, "text/xml", kBasic, WebFeature::kSameOriginTextScript}, @@ -166,6 +176,12 @@ TEST_F(AllowedByNosniffTest, Counters) { {blubb, blubb, "application/xml", kCors, WebFeature::kCrossOriginApplicationXml}, {bla, bla, "text/html", kBasic, WebFeature::kSameOriginTextHtml}, + + // Unknown + {bla, bla, "not/script", kBasic, + WebFeature::kSameOriginStrictNosniffWouldBlock}, + {bla, blubb, "not/script", kOpaque, + WebFeature::kCrossOriginStrictNosniffWouldBlock}, }; for (auto& testcase : data) { diff --git a/chromium/third_party/blink/renderer/platform/loader/child_url_loader_factory_bundle.cc b/chromium/third_party/blink/renderer/platform/loader/child_url_loader_factory_bundle.cc index c2d4cc78b9b..44ba2e94ed1 100644 --- a/chromium/third_party/blink/renderer/platform/loader/child_url_loader_factory_bundle.cc +++ b/chromium/third_party/blink/renderer/platform/loader/child_url_loader_factory_bundle.cc @@ -120,10 +120,29 @@ BoundRemoteMapToPendingRemoteMap( return output; } +// TODO(https://crbug.com/1114822): Remove ScopedRequestCrashKeys (it duplicates +// a similar class in //services/network/crash_keys.h) once it is no longer used +// below. class ScopedRequestCrashKeys { public: - explicit ScopedRequestCrashKeys(const network::ResourceRequest& request); - ~ScopedRequestCrashKeys(); + static base::debug::CrashKeyString* GetRequestUrlCrashKey() { + static auto* crash_key = base::debug::AllocateCrashKeyString( + "request_url", base::debug::CrashKeySize::Size256); + return crash_key; + } + + static base::debug::CrashKeyString* GetRequestInitiatorCrashKey() { + static auto* crash_key = base::debug::AllocateCrashKeyString( + "request_initiator", base::debug::CrashKeySize::Size64); + return crash_key; + } + + explicit ScopedRequestCrashKeys(const network::ResourceRequest& request) + : url_(GetRequestUrlCrashKey(), request.url.possibly_invalid_spec()), + request_initiator_(GetRequestInitiatorCrashKey(), + base::OptionalOrNullptr(request.request_initiator)) { + } + ~ScopedRequestCrashKeys() = default; ScopedRequestCrashKeys(const ScopedRequestCrashKeys&) = delete; ScopedRequestCrashKeys& operator=(const ScopedRequestCrashKeys&) = delete; @@ -133,26 +152,6 @@ class ScopedRequestCrashKeys { url::debug::ScopedOriginCrashKey request_initiator_; }; -base::debug::CrashKeyString* GetRequestUrlCrashKey() { - static auto* crash_key = base::debug::AllocateCrashKeyString( - "request_url", base::debug::CrashKeySize::Size256); - return crash_key; -} - -base::debug::CrashKeyString* GetRequestInitiatorCrashKey() { - static auto* crash_key = base::debug::AllocateCrashKeyString( - "request_initiator", base::debug::CrashKeySize::Size64); - return crash_key; -} - -ScopedRequestCrashKeys::ScopedRequestCrashKeys( - const network::ResourceRequest& request) - : url_(GetRequestUrlCrashKey(), request.url.possibly_invalid_spec()), - request_initiator_(GetRequestInitiatorCrashKey(), - base::OptionalOrNullptr(request.request_initiator)) {} - -ScopedRequestCrashKeys::~ScopedRequestCrashKeys() = default; - } // namespace ChildPendingURLLoaderFactoryBundle::ChildPendingURLLoaderFactoryBundle() = @@ -238,24 +237,6 @@ network::mojom::URLLoaderFactory* ChildURLLoaderFactoryBundle::GetFactory( if (base_result) return base_result; - // All renderer-initiated requests need to provide a value for - // |request_initiator| - this is enforced by - // CorsURLLoaderFactory::IsValidRequest (see the - // InitiatorLockCompatibility::kNoInitiator case). - DCHECK(request.request_initiator.has_value()); - if (is_deprecated_process_wide_factory_) { - // The CHECK condition below (in a Renderer process) is also enforced later - // (in the NetworkService process) by CorsURLLoaderFactory::IsValidRequest - // (see the InitiatorLockCompatibility::kNoLock case) - this enforcement may - // result in a renderer kill when the NetworkService is hosted in a separate - // process from the Browser process. Despite the redundancy, we want to - // also have the CHECK below, so that the Renderer process terminates - // earlier, with a callstack that (unlike the NetworkService - // mojo::ReportBadMessage) is hopefully useful for tracking down the source - // of the problem. - CHECK(request.request_initiator->opaque()); - } - InitDirectNetworkFactoryIfNecessary(); DCHECK(direct_network_factory_); return direct_network_factory_.get(); @@ -293,14 +274,6 @@ void ChildURLLoaderFactoryBundle::CreateLoaderAndStart( // special prefetch handling. // TODO(horo): Move this routing logic to network service, when we will have // the special prefetch handling in network service. - if ((request.resource_type == - static_cast<int>(blink::mojom::ResourceType::kPrefetch)) && - prefetch_loader_factory_) { - prefetch_loader_factory_->CreateLoaderAndStart( - std::move(loader), routing_id, request_id, options, request, - std::move(client), traffic_annotation); - return; - } if ((request.load_flags & net::LOAD_PREFETCH) && prefetch_loader_factory_) { // This is no-state prefetch (see // WebURLRequest::GetLoadFlagsForWebUrlRequest). diff --git a/chromium/third_party/blink/renderer/platform/loader/cors/cors.cc b/chromium/third_party/blink/renderer/platform/loader/cors/cors.cc index f759b9a5d3f..919102b97d3 100644 --- a/chromium/third_party/blink/renderer/platform/loader/cors/cors.cc +++ b/chromium/third_party/blink/renderer/platform/loader/cors/cors.cc @@ -4,79 +4,20 @@ #include "third_party/blink/renderer/platform/loader/cors/cors.h" -#include <memory> #include <string> -#include <utility> #include "net/http/http_util.h" #include "services/network/public/cpp/cors/cors.h" -#include "services/network/public/cpp/cors/preflight_cache.h" -#include "services/network/public/cpp/request_mode.h" #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h" #include "third_party/blink/public/platform/web_string.h" -#include "third_party/blink/renderer/platform/loader/cors/cors_error_string.h" #include "third_party/blink/renderer/platform/loader/fetch/resource_response.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" -#include "url/origin.h" namespace blink { namespace { -base::Optional<std::string> GetHeaderValue(const HTTPHeaderMap& header_map, - const AtomicString& header_name) { - if (header_map.Contains(header_name)) { - return header_map.Get(header_name).Latin1(); - } - 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 result.Ascii(); -} - -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(i->key.Ascii(), i->value.Ascii()); - } - return request_headers; -} - -url::Origin AsUrlOrigin(const SecurityOrigin& origin) { - // "file:" origin is treated like an opaque unique origin when - // allow-file-access-from-files is not specified. Such origin is not - // opaque (i.e., IsOpaque() returns false) but still serializes to - // "null". - return origin.ToString() == "null" ? url::Origin() : origin.ToUrlOrigin(); -} - // A parser for the value of the Access-Control-Expose-Headers header. class HTTPHeaderNameListParser { STACK_ALLOCATED(); @@ -159,165 +100,10 @@ class HTTPHeaderNameListParser { namespace cors { -base::Optional<network::CorsErrorStatus> CheckAccess( - const KURL& response_url, - const HTTPHeaderMap& response_header, - network::mojom::CredentialsMode credentials_mode, - const SecurityOrigin& origin) { - return network::cors::CheckAccess( - response_url, - GetHeaderValue(response_header, http_names::kAccessControlAllowOrigin), - GetHeaderValue(response_header, - http_names::kAccessControlAllowCredentials), - credentials_mode, AsUrlOrigin(origin)); -} - -base::Optional<network::CorsErrorStatus> CheckPreflightAccess( - const KURL& response_url, - const int response_status_code, - const HTTPHeaderMap& response_header, - network::mojom::CredentialsMode actual_credentials_mode, - const SecurityOrigin& origin) { - return network::cors::CheckPreflightAccess( - response_url, response_status_code, - GetHeaderValue(response_header, http_names::kAccessControlAllowOrigin), - GetHeaderValue(response_header, - http_names::kAccessControlAllowCredentials), - actual_credentials_mode, AsUrlOrigin(origin)); -} - -base::Optional<network::CorsErrorStatus> CheckRedirectLocation( - const KURL& url, - network::mojom::RequestMode request_mode, - const SecurityOrigin* origin, - CorsFlag cors_flag) { - base::Optional<url::Origin> origin_to_pass; - if (origin) - origin_to_pass = AsUrlOrigin(*origin); - - // Blink-side implementations rewrite the origin instead of setting the - // tainted flag. - return network::cors::CheckRedirectLocation( - url, request_mode, origin_to_pass, cors_flag == CorsFlag::Set, false); -} - -base::Optional<network::CorsErrorStatus> CheckExternalPreflight( - const HTTPHeaderMap& response_header) { - return network::cors::CheckExternalPreflight( - GetHeaderValue(response_header, http_names::kAccessControlAllowExternal)); -} - bool IsCorsEnabledRequestMode(network::mojom::RequestMode request_mode) { return network::cors::IsCorsEnabledRequestMode(request_mode); } -base::Optional<network::CorsErrorStatus> EnsurePreflightResultAndCacheOnSuccess( - const HTTPHeaderMap& response_header_map, - const String& origin, - const KURL& request_url, - const String& request_method, - const HTTPHeaderMap& request_header_map, - network::mojom::CredentialsMode request_credentials_mode) { - DCHECK(!origin.IsNull()); - DCHECK(!request_method.IsNull()); - - base::Optional<network::mojom::CorsError> error; - - std::unique_ptr<network::cors::PreflightResult> result = - network::cors::PreflightResult::Create( - request_credentials_mode, - GetOptionalHeaderValue(response_header_map, - http_names::kAccessControlAllowMethods), - GetOptionalHeaderValue(response_header_map, - http_names::kAccessControlAllowHeaders), - GetOptionalHeaderValue(response_header_map, - http_names::kAccessControlMaxAge), - &error); - if (error) - return network::CorsErrorStatus(*error); - - base::Optional<network::CorsErrorStatus> status; - status = result->EnsureAllowedCrossOriginMethod(request_method.Ascii()); - if (status) - return status; - - // |is_revalidating| is not needed for blink-side CORS. - constexpr bool is_revalidating = false; - status = result->EnsureAllowedCrossOriginHeaders( - *CreateNetHttpRequestHeaders(request_header_map), is_revalidating); - if (status) - return status; - - GetPerThreadPreflightCache().AppendEntry( - url::Origin::Create(GURL(origin.Ascii())), request_url, - net::NetworkIsolationKey(), std::move(result)); - return base::nullopt; -} - -bool CheckIfRequestCanSkipPreflight( - const String& origin, - const KURL& url, - network::mojom::CredentialsMode credentials_mode, - const String& method, - const HTTPHeaderMap& request_header_map) { - DCHECK(!origin.IsNull()); - DCHECK(!method.IsNull()); - - // |is_revalidating| is not needed for blink-side CORS. - constexpr bool is_revalidating = false; - return GetPerThreadPreflightCache().CheckIfRequestCanSkipPreflight( - url::Origin::Create(GURL(origin.Ascii())), url, - net::NetworkIsolationKey(), credentials_mode, method.Ascii(), - *CreateNetHttpRequestHeaders(request_header_map), is_revalidating); -} - -// Keep this in sync with the identical function -// network::cors::CorsURLLoader::CalculateResponseTainting. -// -// This is the same as that function except using KURL and SecurityOrigin -// instead of GURL and url::Origin. We can't combine them because converting -// SecurityOrigin to url::Origin loses information about origins that are -// allowed by SecurityPolicy. -// -// This function also doesn't use a |tainted_origin| flag because Blink loaders -// mutate the origin instead of using such a flag. -network::mojom::FetchResponseType CalculateResponseTainting( - const KURL& url, - network::mojom::RequestMode request_mode, - const SecurityOrigin* origin, - const SecurityOrigin* isolated_world_origin, - CorsFlag cors_flag) { - if (url.ProtocolIsData()) - return network::mojom::FetchResponseType::kBasic; - - if (cors_flag == CorsFlag::Set) { - DCHECK(IsCorsEnabledRequestMode(request_mode)); - return network::mojom::FetchResponseType::kCors; - } - - if (!origin) { - // This is actually not defined in the fetch spec, but in this case CORS - // is disabled so no one should care this value. - return network::mojom::FetchResponseType::kBasic; - } - - if (request_mode == network::mojom::RequestMode::kNoCors) { - bool can_request = origin->CanRequest(url); - if (!can_request && isolated_world_origin) - can_request = isolated_world_origin->CanRequest(url); - if (!can_request) - return network::mojom::FetchResponseType::kOpaque; - } - return network::mojom::FetchResponseType::kBasic; -} - -bool CalculateCredentialsFlag( - network::mojom::CredentialsMode credentials_mode, - network::mojom::FetchResponseType response_tainting) { - return network::cors::CalculateCredentialsFlag(credentials_mode, - response_tainting); -} - bool IsCorsSafelistedMethod(const String& method) { DCHECK(!method.IsNull()); return network::cors::IsCorsSafelistedMethod(method.Latin1()); @@ -327,9 +113,10 @@ bool IsCorsSafelistedContentType(const String& media_type) { return network::cors::IsCorsSafelistedContentType(media_type.Latin1()); } -bool IsNoCorsSafelistedHeaderName(const String& name) { +bool IsNoCorsSafelistedHeader(const String& name, const String& value) { DCHECK(!name.IsNull()); - return network::cors::IsNoCorsSafelistedHeaderName(name.Latin1()); + DCHECK(!value.IsNull()); + return network::cors::IsNoCorsSafelistedHeader(name.Latin1(), value.Latin1()); } bool IsPrivilegedNoCorsHeaderName(const String& name) { @@ -337,23 +124,9 @@ bool IsPrivilegedNoCorsHeaderName(const String& name) { return network::cors::IsPrivilegedNoCorsHeaderName(name.Latin1()); } -bool IsNoCorsSafelistedHeader(const String& name, const String& value) { +bool IsNoCorsSafelistedHeaderName(const String& name) { DCHECK(!name.IsNull()); - DCHECK(!value.IsNull()); - return network::cors::IsNoCorsSafelistedHeader(name.Latin1(), value.Latin1()); -} - -Vector<String> CorsUnsafeRequestHeaderNames(const HTTPHeaderMap& headers) { - net::HttpRequestHeaders::HeaderVector in; - for (const auto& entry : headers) { - in.push_back(net::HttpRequestHeaders::HeaderKeyValuePair( - entry.key.Latin1(), entry.value.Latin1())); - } - - Vector<String> header_names; - for (const auto& name : network::cors::CorsUnsafeRequestHeaderNames(in)) - header_names.push_back(WebString::FromLatin1(name)); - return header_names; + return network::cors::IsNoCorsSafelistedHeaderName(name.Latin1()); } PLATFORM_EXPORT Vector<String> PrivilegedNoCorsHeaderNames() { @@ -368,8 +141,13 @@ bool IsForbiddenHeaderName(const String& name) { } bool ContainsOnlyCorsSafelistedHeaders(const HTTPHeaderMap& header_map) { - Vector<String> header_names = CorsUnsafeRequestHeaderNames(header_map); - return header_names.IsEmpty(); + net::HttpRequestHeaders::HeaderVector in; + for (const auto& entry : header_map) { + in.push_back(net::HttpRequestHeaders::HeaderKeyValuePair( + entry.key.Latin1(), entry.value.Latin1())); + } + + return network::cors::CorsUnsafeRequestHeaderNames(in).empty(); } bool ContainsOnlyCorsSafelistedOrForbiddenHeaders( @@ -476,6 +254,7 @@ bool IsNoCorsAllowedContext(mojom::blink::RequestContextType context) { case mojom::blink::RequestContextType::SHARED_WORKER: case mojom::blink::RequestContextType::VIDEO: case mojom::blink::RequestContextType::WORKER: + case mojom::blink::RequestContextType::SUBRESOURCE_WEBBUNDLE: return true; default: return false; diff --git a/chromium/third_party/blink/renderer/platform/loader/cors/cors.h b/chromium/third_party/blink/renderer/platform/loader/cors/cors.h index e76d68adaa2..f3886e799e6 100644 --- a/chromium/third_party/blink/renderer/platform/loader/cors/cors.h +++ b/chromium/third_party/blink/renderer/platform/loader/cors/cors.h @@ -31,78 +31,17 @@ enum class CorsFlag : uint8_t { 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 base::Optional<network::CorsErrorStatus> CheckAccess( - const KURL&, - const HTTPHeaderMap&, - network::mojom::CredentialsMode, - const SecurityOrigin&); - -PLATFORM_EXPORT base::Optional<network::CorsErrorStatus> CheckPreflightAccess( - const KURL&, - const int response_status_code, - const HTTPHeaderMap&, - network::mojom::CredentialsMode, - const SecurityOrigin&); - -PLATFORM_EXPORT base::Optional<network::CorsErrorStatus> CheckRedirectLocation( - const KURL&, - network::mojom::RequestMode, - const SecurityOrigin*, - CorsFlag); - -PLATFORM_EXPORT base::Optional<network::CorsErrorStatus> CheckExternalPreflight( - const HTTPHeaderMap&); - +// Blink core. PLATFORM_EXPORT bool IsCorsEnabledRequestMode(network::mojom::RequestMode); - -PLATFORM_EXPORT base::Optional<network::CorsErrorStatus> -EnsurePreflightResultAndCacheOnSuccess( - const HTTPHeaderMap& response_header_map, - const String& origin, - const KURL& request_url, - const String& request_method, - const HTTPHeaderMap& request_header_map, - network::mojom::CredentialsMode request_credentials_mode); - -PLATFORM_EXPORT bool CheckIfRequestCanSkipPreflight( - const String& origin, - const KURL&, - network::mojom::CredentialsMode, - const String& method, - const HTTPHeaderMap& request_header_map); - -// Returns the response tainting value -// (https://fetch.spec.whatwg.org/#concept-request-response-tainting) for a -// request and the CORS flag, as specified in -// https://fetch.spec.whatwg.org/#main-fetch. -PLATFORM_EXPORT network::mojom::FetchResponseType CalculateResponseTainting( - const KURL& url, - network::mojom::RequestMode request_mode, - const SecurityOrigin* origin, - const SecurityOrigin* isolated_world_origin, - CorsFlag cors_flag); - -PLATFORM_EXPORT bool CalculateCredentialsFlag( - network::mojom::CredentialsMode credentials_mode, - network::mojom::FetchResponseType response_tainting); - -// 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 IsNoCorsSafelistedHeader(const String& name, const String& value); PLATFORM_EXPORT bool IsPrivilegedNoCorsHeaderName(const String& name); PLATFORM_EXPORT bool IsNoCorsSafelistedHeaderName(const String& name); -PLATFORM_EXPORT Vector<String> CorsUnsafeRequestHeaderNames( - const HTTPHeaderMap& headers); PLATFORM_EXPORT Vector<String> PrivilegedNoCorsHeaderNames(); PLATFORM_EXPORT bool IsForbiddenHeaderName(const String& name); PLATFORM_EXPORT bool ContainsOnlyCorsSafelistedHeaders(const HTTPHeaderMap&); -PLATFORM_EXPORT bool ContainsOnlyCorsSafelistedOrForbiddenHeaders( - const HTTPHeaderMap&); PLATFORM_EXPORT bool IsOkStatus(int status); @@ -114,7 +53,6 @@ PLATFORM_EXPORT bool IsOkStatus(int status); // |kNavigate|. // This should be identical to CalculateCorsFlag defined in // //services/network/cors/cors_url_loader.cc. -// This function will be removed when out-of-renderer CORS is enabled. PLATFORM_EXPORT bool CalculateCorsFlag( const KURL& url, const SecurityOrigin* initiator_origin, diff --git a/chromium/third_party/blink/renderer/platform/loader/cors/cors_test.cc b/chromium/third_party/blink/renderer/platform/loader/cors/cors_test.cc index 173f852a577..33a82641852 100644 --- a/chromium/third_party/blink/renderer/platform/loader/cors/cors_test.cc +++ b/chromium/third_party/blink/renderer/platform/loader/cors/cors_test.cc @@ -7,7 +7,6 @@ #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h" #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h" -#include "third_party/blink/renderer/platform/weborigin/security_origin.h" namespace blink { @@ -101,87 +100,6 @@ TEST_F(CorsExposedHeadersTest, Asterisk) { HTTPHeaderSet({"a", "b", "*"})); } -// Keep this in sync with the CalculateResponseTainting test in -// services/network/cors/cors_url_loader_unittest.cc. -TEST(CorsTest, CalculateResponseTainting) { - using network::mojom::FetchResponseType; - using network::mojom::RequestMode; - - const KURL same_origin_url("https://example.com/"); - const KURL cross_origin_url("https://example2.com/"); - scoped_refptr<SecurityOrigin> origin_refptr = - SecurityOrigin::Create(same_origin_url); - const SecurityOrigin* origin = origin_refptr.get(); - const SecurityOrigin* no_origin = nullptr; - - // CORS flag is false, same-origin request - EXPECT_EQ( - FetchResponseType::kBasic, - cors::CalculateResponseTainting(same_origin_url, RequestMode::kSameOrigin, - origin, nullptr, CorsFlag::Unset)); - EXPECT_EQ( - FetchResponseType::kBasic, - cors::CalculateResponseTainting(same_origin_url, RequestMode::kNoCors, - origin, nullptr, CorsFlag::Unset)); - EXPECT_EQ(FetchResponseType::kBasic, - cors::CalculateResponseTainting(same_origin_url, RequestMode::kCors, - origin, nullptr, CorsFlag::Unset)); - EXPECT_EQ(FetchResponseType::kBasic, - cors::CalculateResponseTainting( - same_origin_url, RequestMode::kCorsWithForcedPreflight, origin, - nullptr, CorsFlag::Unset)); - EXPECT_EQ( - FetchResponseType::kBasic, - cors::CalculateResponseTainting(same_origin_url, RequestMode::kNavigate, - origin, nullptr, CorsFlag::Unset)); - - // CORS flag is false, cross-origin request - EXPECT_EQ( - FetchResponseType::kOpaque, - cors::CalculateResponseTainting(cross_origin_url, RequestMode::kNoCors, - origin, nullptr, CorsFlag::Unset)); - EXPECT_EQ( - FetchResponseType::kBasic, - cors::CalculateResponseTainting(cross_origin_url, RequestMode::kNavigate, - origin, nullptr, CorsFlag::Unset)); - - // CORS flag is true, same-origin request - EXPECT_EQ(FetchResponseType::kCors, - cors::CalculateResponseTainting(same_origin_url, RequestMode::kCors, - origin, nullptr, CorsFlag::Set)); - EXPECT_EQ(FetchResponseType::kCors, - cors::CalculateResponseTainting( - same_origin_url, RequestMode::kCorsWithForcedPreflight, origin, - nullptr, CorsFlag::Set)); - - // CORS flag is true, cross-origin request - EXPECT_EQ(FetchResponseType::kCors, cors::CalculateResponseTainting( - cross_origin_url, RequestMode::kCors, - origin, nullptr, CorsFlag::Set)); - EXPECT_EQ(FetchResponseType::kCors, - cors::CalculateResponseTainting( - cross_origin_url, RequestMode::kCorsWithForcedPreflight, origin, - nullptr, CorsFlag::Set)); - - // Origin is not provided. - EXPECT_EQ( - FetchResponseType::kBasic, - cors::CalculateResponseTainting(same_origin_url, RequestMode::kNoCors, - no_origin, nullptr, CorsFlag::Unset)); - EXPECT_EQ( - FetchResponseType::kBasic, - cors::CalculateResponseTainting(same_origin_url, RequestMode::kNavigate, - no_origin, nullptr, CorsFlag::Unset)); - EXPECT_EQ( - FetchResponseType::kBasic, - cors::CalculateResponseTainting(cross_origin_url, RequestMode::kNoCors, - no_origin, nullptr, CorsFlag::Unset)); - EXPECT_EQ( - FetchResponseType::kBasic, - cors::CalculateResponseTainting(cross_origin_url, RequestMode::kNavigate, - no_origin, nullptr, CorsFlag::Unset)); -} - } // namespace } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/DEPS b/chromium/third_party/blink/renderer/platform/loader/fetch/DEPS index 6b62d911e62..2c306ca7b4d 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/DEPS +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/DEPS @@ -1,5 +1,6 @@ include_rules = [ "+net/base/ip_endpoint.h", + "+net/base/schemeful_site.h", "+net/dns/public", "+services/network/public/cpp/fetch_api_utils.h", "+services/network/public/cpp/optional_trust_token_params.h", diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/back_forward_cache_loader_helper.h b/chromium/third_party/blink/renderer/platform/loader/fetch/back_forward_cache_loader_helper.h new file mode 100644 index 00000000000..ea87bb15a53 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/back_forward_cache_loader_helper.h @@ -0,0 +1,43 @@ +// Copyright 2021 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_BACK_FORWARD_CACHE_LOADER_HELPER_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_BACK_FORWARD_CACHE_LOADER_HELPER_H_ + +#include "third_party/blink/public/mojom/frame/back_forward_cache_controller.mojom-forward.h" +#include "third_party/blink/renderer/platform/heap/garbage_collected.h" +#include "third_party/blink/renderer/platform/heap/heap.h" +#include "third_party/blink/renderer/platform/heap/member.h" +#include "third_party/blink/renderer/platform/platform_export.h" + +namespace blink { + +// Helper class for in-flight network request support for back-forward cache. +class PLATFORM_EXPORT BackForwardCacheLoaderHelper + : public GarbageCollected<BackForwardCacheLoaderHelper> { + public: + // Evict the page from BackForwardCache. Should be called when handling an + // event which can't proceed if the page is in BackForwardCache and can't be + // easily deferred to handle later, for example network redirect handling. + virtual void EvictFromBackForwardCache(mojom::RendererEvictionReason reason) { + } + + // Called when a network request buffered an additional `num_bytes` while the + // in back-forward cache. May be called multiple times. + virtual void DidBufferLoadWhileInBackForwardCache(size_t num_bytes) {} + + // Returns true if we can still continue buffering data from in-flight network + // requests while in back-forward cache. + virtual bool CanContinueBufferingWhileInBackForwardCache() const { + return false; + } + + virtual void Detach() {} + + virtual void Trace(Visitor*) const {} +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_BACK_FORWARD_CACHE_LOADER_HELPER_H_ diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.cc index b33a8bc2cf0..60743ac1d79 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.cc @@ -19,7 +19,7 @@ BufferingBytesConsumer* BufferingBytesConsumer::CreateWithDelay( BytesConsumer* bytes_consumer, scoped_refptr<base::SingleThreadTaskRunner> timer_task_runner) { return MakeGarbageCollected<BufferingBytesConsumer>( - util::PassKey<BufferingBytesConsumer>(), bytes_consumer, + base::PassKey<BufferingBytesConsumer>(), bytes_consumer, std::move(timer_task_runner), base::TimeDelta::FromMilliseconds(kDelayMilliseconds)); } @@ -28,12 +28,12 @@ BufferingBytesConsumer* BufferingBytesConsumer::CreateWithDelay( BufferingBytesConsumer* BufferingBytesConsumer::Create( BytesConsumer* bytes_consumer) { return MakeGarbageCollected<BufferingBytesConsumer>( - util::PassKey<BufferingBytesConsumer>(), bytes_consumer, nullptr, + base::PassKey<BufferingBytesConsumer>(), bytes_consumer, nullptr, base::TimeDelta()); } BufferingBytesConsumer::BufferingBytesConsumer( - util::PassKey<BufferingBytesConsumer> key, + base::PassKey<BufferingBytesConsumer> key, BytesConsumer* bytes_consumer, scoped_refptr<base::SingleThreadTaskRunner> timer_task_runner, base::TimeDelta buffering_start_delay) @@ -165,6 +165,7 @@ BytesConsumer::Error BufferingBytesConsumer::GetError() const { void BufferingBytesConsumer::Trace(Visitor* visitor) const { visitor->Trace(bytes_consumer_); visitor->Trace(client_); + visitor->Trace(timer_); BytesConsumer::Trace(visitor); BytesConsumer::Client::Trace(visitor); } diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.h b/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.h index 8ca2f349ca3..44b8a663a76 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.h @@ -8,7 +8,7 @@ #include <memory> #include "base/memory/scoped_refptr.h" -#include "base/util/type_safety/pass_key.h" +#include "base/types/pass_key.h" #include "third_party/blink/renderer/platform/heap/handle.h" #include "third_party/blink/renderer/platform/loader/fetch/bytes_consumer.h" #include "third_party/blink/renderer/platform/platform_export.h" @@ -46,7 +46,7 @@ class PLATFORM_EXPORT BufferingBytesConsumer final // Use the Create*() factory methods instead of direct instantiation. BufferingBytesConsumer( - util::PassKey<BufferingBytesConsumer> key, + base::PassKey<BufferingBytesConsumer> key, BytesConsumer* bytes_consumer, scoped_refptr<base::SingleThreadTaskRunner> timer_task_runner, base::TimeDelta buffering_start_delay); @@ -87,7 +87,7 @@ class PLATFORM_EXPORT BufferingBytesConsumer final void BufferData(); const Member<BytesConsumer> bytes_consumer_; - TaskRunnerTimer<BufferingBytesConsumer> timer_; + HeapTaskRunnerTimer<BufferingBytesConsumer> timer_; Deque<Vector<char>> buffer_; size_t offset_for_first_chunk_ = 0; diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer_test.cc index 037092cf783..d77b39794df 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer_test.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer_test.cc @@ -32,8 +32,8 @@ class BufferingBytesConsumerTest : public testing::Test { mojo::ScopedDataPipeConsumerHandle consumer_handle; mojo::ScopedDataPipeProducerHandle producer_handle; CHECK_EQ(MOJO_RESULT_OK, - mojo::CreateDataPipe(&data_pipe_options, &producer_handle, - &consumer_handle)); + mojo::CreateDataPipe(&data_pipe_options, producer_handle, + consumer_handle)); return consumer_handle; } diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler_test.cc new file mode 100644 index 00000000000..d9f2f162d1b --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler_test.cc @@ -0,0 +1,135 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this sink 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_handler.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/mojom/loader/code_cache.mojom-blink.h" +#include "third_party/blink/public/platform/web_url.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/testing/testing_platform_support_with_mock_scheduler.h" +#include "third_party/blink/renderer/platform/weborigin/kurl.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(mojom::blink::CodeCacheType cache_type, + const WebURL& url, + base::Time, + const uint8_t*, + size_t) override { + cached_urls_.push_back(url); + } + + void CacheMetadataInCacheStorage(const WebURL& url, + base::Time, + const uint8_t*, + size_t, + const WebSecurityOrigin&, + const WebString&) override { + cache_storage_cached_urls_.push_back(url); + } + + const Vector<WebURL>& CachedURLs() const { return cached_urls_; } + const Vector<WebURL>& CacheStorageCachedURLs() const { + return cache_storage_cached_urls_; + } + + private: + Vector<WebURL> cached_urls_; + Vector<WebURL> cache_storage_cached_urls_; +}; + +ResourceResponse CreateTestResourceResponse() { + ResourceResponse response(KURL("https://example.com/")); + response.SetHttpStatusCode(200); + return response; +} + +void SendDataFor(const ResourceResponse& response) { + constexpr uint8_t kTestData[] = {1, 2, 3, 4, 5}; + std::unique_ptr<CachedMetadataSender> sender = CachedMetadataSender::Create( + response, mojom::blink::CodeCacheType::kJavascript, + SecurityOrigin::Create(response.CurrentRequestUrl())); + sender->Send(kTestData, sizeof(kTestData)); +} + +TEST(CachedMetadataHandlerTest, SendsMetadataToPlatform) { + ScopedTestingPlatformSupport<MockPlatform> mock; + ResourceResponse response(CreateTestResourceResponse()); + + SendDataFor(response); + EXPECT_EQ(1u, mock->CachedURLs().size()); + EXPECT_EQ(0u, mock->CacheStorageCachedURLs().size()); +} + +TEST( + CachedMetadataHandlerTest, + DoesNotSendMetadataToPlatformWhenFetchedViaServiceWorkerWithSyntheticResponse) { + ScopedTestingPlatformSupport<MockPlatform> mock; + + // Equivalent to service worker calling respondWith(new Response(...)) + ResourceResponse response(CreateTestResourceResponse()); + response.SetWasFetchedViaServiceWorker(true); + + SendDataFor(response); + EXPECT_EQ(0u, mock->CachedURLs().size()); + EXPECT_EQ(0u, mock->CacheStorageCachedURLs().size()); +} + +TEST( + CachedMetadataHandlerTest, + SendsMetadataToPlatformWhenFetchedViaServiceWorkerWithPassThroughResponse) { + ScopedTestingPlatformSupport<MockPlatform> mock; + + // Equivalent to service worker calling respondWith(fetch(evt.request.url)); + ResourceResponse response(CreateTestResourceResponse()); + response.SetWasFetchedViaServiceWorker(true); + response.SetUrlListViaServiceWorker({response.CurrentRequestUrl()}); + + SendDataFor(response); + EXPECT_EQ(1u, mock->CachedURLs().size()); + EXPECT_EQ(0u, mock->CacheStorageCachedURLs().size()); +} + +TEST( + CachedMetadataHandlerTest, + DoesNotSendMetadataToPlatformWhenFetchedViaServiceWorkerWithDifferentURLResponse) { + ScopedTestingPlatformSupport<MockPlatform> mock; + + // Equivalent to service worker calling respondWith(fetch(some_different_url)) + ResourceResponse response(CreateTestResourceResponse()); + response.SetWasFetchedViaServiceWorker(true); + response.SetUrlListViaServiceWorker( + {KURL("https://example.com/different/url")}); + + SendDataFor(response); + EXPECT_EQ(0u, mock->CachedURLs().size()); + EXPECT_EQ(0u, mock->CacheStorageCachedURLs().size()); +} + +TEST(CachedMetadataHandlerTest, + SendsMetadataToPlatformWhenFetchedViaServiceWorkerWithCacheResponse) { + ScopedTestingPlatformSupport<MockPlatform> mock; + + // Equivalent to service worker calling respondWith(cache.match(some_url)); + ResourceResponse response(CreateTestResourceResponse()); + response.SetWasFetchedViaServiceWorker(true); + response.SetCacheStorageCacheName("dummy"); + + SendDataFor(response); + EXPECT_EQ(0u, mock->CachedURLs().size()); + EXPECT_EQ(1u, mock->CacheStorageCachedURLs().size()); +} + +} // namespace +} // namespace blink 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 index 638a7f6b0b6..1ff152ca7af 100644 --- 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 @@ -7,13 +7,14 @@ #include "base/command_line.h" #include "base/macros.h" #include "services/network/public/cpp/client_hints.h" +#include "services/network/public/cpp/is_potentially_trustworthy.h" #include "third_party/blink/public/common/client_hints/client_hints.h" #include "third_party/blink/public/common/switches.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" -#include "third_party/blink/renderer/platform/weborigin/security_origin.h" +#include "url/origin.h" namespace blink { @@ -34,6 +35,18 @@ void ClientHintsPreferences::UpdateFrom( } } +void ClientHintsPreferences::CombineWith( + const ClientHintsPreferences& preferences) { + for (size_t i = 0; + i < static_cast<int>(network::mojom::WebClientHintsType::kMaxValue) + 1; + ++i) { + network::mojom::WebClientHintsType type = + static_cast<network::mojom::WebClientHintsType>(i); + if (preferences.ShouldSend(type)) + SetShouldSend(type); + } +} + bool ClientHintsPreferences::UserAgentClientHintEnabled() { return RuntimeEnabledFeatures::UserAgentClientHintEnabled() && !base::CommandLine::ForCurrentProcess()->HasSwitch( @@ -83,8 +96,7 @@ void ClientHintsPreferences::UpdateFromHttpEquivAcceptCH( // static bool ClientHintsPreferences::IsClientHintsAllowed(const KURL& url) { return (url.ProtocolIs("http") || url.ProtocolIs("https")) && - (SecurityOrigin::IsSecure(url) || - SecurityOrigin::Create(url)->IsLocalhost()); + network::IsOriginPotentiallyTrustworthy(url::Origin::Create(url)); } WebEnabledClientHints ClientHintsPreferences::GetWebEnabledClientHints() const { 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 index 0d1a88e2322..7083040b28e 100644 --- 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 @@ -31,6 +31,7 @@ class PLATFORM_EXPORT ClientHintsPreferences { ClientHintsPreferences(); void UpdateFrom(const ClientHintsPreferences&); + void CombineWith(const ClientHintsPreferences&); // Parses <meta http-equiv="accept-ch"> value |header_value|, and updates // |this| to enable the requested client hints. |url| is the URL of the page. 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 index b1c50f139e5..a0db4ee39df 100644 --- 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 @@ -45,16 +45,18 @@ TEST_F(ClientHintsPreferencesTest, BasicSecure) { false, false, false}, {"DPRW", false, false, false, false, false, false, false, false, false, false, false, false}, - {"ua", false, false, false, false, false, false, false, true, false, - false, false, false}, - {"ua-arch", false, false, false, false, false, false, false, false, true, - false, false, false}, - {"ua-platform", false, false, false, false, false, false, false, false, - false, true, false, false}, - {"ua-model", false, false, false, false, false, false, false, false, - false, false, true, false}, - {"ua, ua-arch, ua-platform, ua-model, ua-full-version", false, false, - false, false, false, false, false, true, true, true, true, true}, + {"sec-ch-ua", false, false, false, false, false, false, false, true, + false, false, false, false}, + {"sec-ch-ua-arch", false, false, false, false, false, false, false, false, + true, false, false, false}, + {"sec-ch-ua-platform", false, false, false, false, false, false, false, + false, false, true, false, false}, + {"sec-ch-ua-model", false, false, false, false, false, false, false, + false, false, false, true, false}, + {"sec-ch-ua, sec-ch-ua-arch, sec-ch-ua-platform, sec-ch-ua-model, " + "sec-ch-ua-full-version", + false, false, false, false, false, false, false, true, true, true, true, + true}, }; for (const auto& test_case : cases) { @@ -254,8 +256,10 @@ TEST_F(ClientHintsPreferencesTest, ParseHeaders) { false, false, false, false, false}, {"dpr rtt", false, false, false, false, false, false, false, false, false, false, false, false, false}, - {"ua, ua-arch, ua-platform, ua-model, ua-full-version", false, false, - false, false, false, false, false, false, true, true, true, true, true}, + {"sec-ch-ua, sec-ch-ua-arch, sec-ch-ua-platform, sec-ch-ua-model, " + "sec-ch-ua-full-version", + false, false, false, false, false, false, false, false, true, true, true, + true, true}, }; for (const auto& test : test_cases) { diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/cross_origin_attribute_value.h b/chromium/third_party/blink/renderer/platform/loader/fetch/cross_origin_attribute_value.h index 2da56da0848..4093593334a 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/cross_origin_attribute_value.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/cross_origin_attribute_value.h @@ -7,6 +7,8 @@ namespace blink { +// This corresponds to the CORS settings attributes defined in the HTML spec: +// https://html.spec.whatwg.org/C/#cors-settings-attributes enum CrossOriginAttributeValue { kCrossOriginAttributeNotSet, kCrossOriginAttributeAnonymous, diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer_test.cc index ec03ab4e89b..9ae4783e87a 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer_test.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer_test.cc @@ -22,24 +22,26 @@ class DataPipeBytesConsumerTest : public testing::Test { }; TEST_F(DataPipeBytesConsumerTest, TwoPhaseRead) { - mojo::DataPipe pipe; - ASSERT_TRUE(pipe.producer_handle.is_valid()); + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle), + MOJO_RESULT_OK); const std::string kData = "Such hospitality. I'm underwhelmed."; uint32_t write_size = kData.size(); - MojoResult rv = pipe.producer_handle->WriteData(kData.c_str(), &write_size, - MOJO_WRITE_DATA_FLAG_NONE); + MojoResult rv = producer_handle->WriteData(kData.c_str(), &write_size, + MOJO_WRITE_DATA_FLAG_NONE); ASSERT_EQ(MOJO_RESULT_OK, rv); ASSERT_EQ(kData.size(), write_size); // Close the producer so the consumer will reach the kDone state after // completion is signaled below. - pipe.producer_handle.reset(); + producer_handle.reset(); DataPipeBytesConsumer::CompletionNotifier* notifier = nullptr; DataPipeBytesConsumer* consumer = MakeGarbageCollected<DataPipeBytesConsumer>( - task_runner_, std::move(pipe.consumer_handle), ¬ifier); + task_runner_, std::move(consumer_handle), ¬ifier); notifier->SignalComplete(); auto result = MakeGarbageCollected<BytesConsumerTestReader>(consumer)->Run( task_runner_.get()); @@ -48,22 +50,24 @@ TEST_F(DataPipeBytesConsumerTest, TwoPhaseRead) { } TEST_F(DataPipeBytesConsumerTest, TwoPhaseRead_SignalError) { - mojo::DataPipe pipe; - ASSERT_TRUE(pipe.producer_handle.is_valid()); + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle), + MOJO_RESULT_OK); const std::string kData = "Such hospitality. I'm underwhelmed."; uint32_t write_size = kData.size(); - MojoResult rv = pipe.producer_handle->WriteData(kData.c_str(), &write_size, - MOJO_WRITE_DATA_FLAG_NONE); + MojoResult rv = producer_handle->WriteData(kData.c_str(), &write_size, + MOJO_WRITE_DATA_FLAG_NONE); ASSERT_EQ(MOJO_RESULT_OK, rv); ASSERT_EQ(kData.size(), write_size); - pipe.producer_handle.reset(); + producer_handle.reset(); DataPipeBytesConsumer::CompletionNotifier* notifier = nullptr; DataPipeBytesConsumer* consumer = MakeGarbageCollected<DataPipeBytesConsumer>( - task_runner_, std::move(pipe.consumer_handle), ¬ifier); + task_runner_, std::move(consumer_handle), ¬ifier); // Then explicitly signal an error. This should override the pipe completion // and result in kError. @@ -79,12 +83,14 @@ TEST_F(DataPipeBytesConsumerTest, TwoPhaseRead_SignalError) { // must be called for the DataPipeBytesConsumer to reach the closed // state. TEST_F(DataPipeBytesConsumerTest, EndOfPipeBeforeComplete) { - mojo::DataPipe pipe; - ASSERT_TRUE(pipe.producer_handle.is_valid()); + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle), + MOJO_RESULT_OK); DataPipeBytesConsumer::CompletionNotifier* notifier = nullptr; DataPipeBytesConsumer* consumer = MakeGarbageCollected<DataPipeBytesConsumer>( - task_runner_, std::move(pipe.consumer_handle), ¬ifier); + task_runner_, std::move(consumer_handle), ¬ifier); EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState()); @@ -94,7 +100,7 @@ TEST_F(DataPipeBytesConsumerTest, EndOfPipeBeforeComplete) { Result rv = consumer->BeginRead(&buffer, &available); EXPECT_EQ(Result::kShouldWait, rv); - pipe.producer_handle.reset(); + producer_handle.reset(); rv = consumer->BeginRead(&buffer, &available); EXPECT_EQ(Result::kShouldWait, rv); EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState()); @@ -107,12 +113,14 @@ TEST_F(DataPipeBytesConsumerTest, EndOfPipeBeforeComplete) { } TEST_F(DataPipeBytesConsumerTest, CompleteBeforeEndOfPipe) { - mojo::DataPipe pipe; - ASSERT_TRUE(pipe.producer_handle.is_valid()); + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle), + MOJO_RESULT_OK); DataPipeBytesConsumer::CompletionNotifier* notifier = nullptr; DataPipeBytesConsumer* consumer = MakeGarbageCollected<DataPipeBytesConsumer>( - task_runner_, std::move(pipe.consumer_handle), ¬ifier); + task_runner_, std::move(consumer_handle), ¬ifier); EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState()); @@ -128,7 +136,7 @@ TEST_F(DataPipeBytesConsumerTest, CompleteBeforeEndOfPipe) { rv = consumer->BeginRead(&buffer, &available); EXPECT_EQ(Result::kShouldWait, rv); - pipe.producer_handle.reset(); + producer_handle.reset(); rv = consumer->BeginRead(&buffer, &available); EXPECT_EQ(Result::kDone, rv); EXPECT_EQ(PublicState::kClosed, consumer->GetPublicState()); @@ -138,12 +146,14 @@ TEST_F(DataPipeBytesConsumerTest, CompleteBeforeEndOfPipe) { // errored state immediately without waiting for the end of the // DataPipe. TEST_F(DataPipeBytesConsumerTest, EndOfPipeBeforeError) { - mojo::DataPipe pipe; - ASSERT_TRUE(pipe.producer_handle.is_valid()); + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle), + MOJO_RESULT_OK); DataPipeBytesConsumer::CompletionNotifier* notifier = nullptr; DataPipeBytesConsumer* consumer = MakeGarbageCollected<DataPipeBytesConsumer>( - task_runner_, std::move(pipe.consumer_handle), ¬ifier); + task_runner_, std::move(consumer_handle), ¬ifier); EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState()); @@ -153,7 +163,7 @@ TEST_F(DataPipeBytesConsumerTest, EndOfPipeBeforeError) { Result rv = consumer->BeginRead(&buffer, &available); EXPECT_EQ(Result::kShouldWait, rv); - pipe.producer_handle.reset(); + producer_handle.reset(); rv = consumer->BeginRead(&buffer, &available); EXPECT_EQ(Result::kShouldWait, rv); EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState()); @@ -170,8 +180,7 @@ TEST_F(DataPipeBytesConsumerTest, SignalSizeBeforeRead) { mojo::ScopedDataPipeProducerHandle writable; const MojoCreateDataPipeOptions options{ sizeof(MojoCreateDataPipeOptions), MOJO_CREATE_DATA_PIPE_FLAG_NONE, 1, 0}; - ASSERT_EQ(MOJO_RESULT_OK, - mojo::CreateDataPipe(&options, &writable, &readable)); + ASSERT_EQ(MOJO_RESULT_OK, mojo::CreateDataPipe(&options, writable, readable)); DataPipeBytesConsumer::CompletionNotifier* notifier = nullptr; DataPipeBytesConsumer* consumer = MakeGarbageCollected<DataPipeBytesConsumer>( task_runner_, std::move(readable), ¬ifier); @@ -212,8 +221,7 @@ TEST_F(DataPipeBytesConsumerTest, SignalExcessSizeBeforeEndOfData) { mojo::ScopedDataPipeProducerHandle writable; const MojoCreateDataPipeOptions options{ sizeof(MojoCreateDataPipeOptions), MOJO_CREATE_DATA_PIPE_FLAG_NONE, 1, 0}; - ASSERT_EQ(MOJO_RESULT_OK, - mojo::CreateDataPipe(&options, &writable, &readable)); + ASSERT_EQ(MOJO_RESULT_OK, mojo::CreateDataPipe(&options, writable, readable)); DataPipeBytesConsumer::CompletionNotifier* notifier = nullptr; DataPipeBytesConsumer* consumer = MakeGarbageCollected<DataPipeBytesConsumer>( task_runner_, std::move(readable), ¬ifier); @@ -240,8 +248,7 @@ TEST_F(DataPipeBytesConsumerTest, SignalExcessSizeAfterEndOfData) { mojo::ScopedDataPipeProducerHandle writable; const MojoCreateDataPipeOptions options{ sizeof(MojoCreateDataPipeOptions), MOJO_CREATE_DATA_PIPE_FLAG_NONE, 1, 0}; - ASSERT_EQ(MOJO_RESULT_OK, - mojo::CreateDataPipe(&options, &writable, &readable)); + ASSERT_EQ(MOJO_RESULT_OK, mojo::CreateDataPipe(&options, writable, readable)); DataPipeBytesConsumer::CompletionNotifier* notifier = nullptr; DataPipeBytesConsumer* consumer = MakeGarbageCollected<DataPipeBytesConsumer>( task_runner_, std::move(readable), ¬ifier); @@ -268,8 +275,7 @@ TEST_F(DataPipeBytesConsumerTest, SignalSizeAfterRead) { mojo::ScopedDataPipeProducerHandle writable; const MojoCreateDataPipeOptions options{ sizeof(MojoCreateDataPipeOptions), MOJO_CREATE_DATA_PIPE_FLAG_NONE, 1, 0}; - ASSERT_EQ(MOJO_RESULT_OK, - mojo::CreateDataPipe(&options, &writable, &readable)); + ASSERT_EQ(MOJO_RESULT_OK, mojo::CreateDataPipe(&options, writable, readable)); DataPipeBytesConsumer::CompletionNotifier* notifier = nullptr; DataPipeBytesConsumer* consumer = MakeGarbageCollected<DataPipeBytesConsumer>( @@ -300,12 +306,14 @@ TEST_F(DataPipeBytesConsumerTest, SignalSizeAfterRead) { } TEST_F(DataPipeBytesConsumerTest, ErrorBeforeEndOfPipe) { - mojo::DataPipe pipe; - ASSERT_TRUE(pipe.producer_handle.is_valid()); + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle), + MOJO_RESULT_OK); DataPipeBytesConsumer::CompletionNotifier* notifier = nullptr; DataPipeBytesConsumer* consumer = MakeGarbageCollected<DataPipeBytesConsumer>( - task_runner_, std::move(pipe.consumer_handle), ¬ifier); + task_runner_, std::move(consumer_handle), ¬ifier); EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState()); @@ -321,7 +329,7 @@ TEST_F(DataPipeBytesConsumerTest, ErrorBeforeEndOfPipe) { rv = consumer->BeginRead(&buffer, &available); EXPECT_EQ(Result::kError, rv); - pipe.producer_handle.reset(); + producer_handle.reset(); rv = consumer->BeginRead(&buffer, &available); EXPECT_EQ(Result::kError, rv); EXPECT_EQ(PublicState::kErrored, consumer->GetPublicState()); @@ -330,12 +338,14 @@ TEST_F(DataPipeBytesConsumerTest, ErrorBeforeEndOfPipe) { // Verify that draining the DataPipe and SignalComplete() will // close the DataPipeBytesConsumer. TEST_F(DataPipeBytesConsumerTest, DrainPipeBeforeComplete) { - mojo::DataPipe pipe; - ASSERT_TRUE(pipe.producer_handle.is_valid()); + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle), + MOJO_RESULT_OK); DataPipeBytesConsumer::CompletionNotifier* notifier = nullptr; DataPipeBytesConsumer* consumer = MakeGarbageCollected<DataPipeBytesConsumer>( - task_runner_, std::move(pipe.consumer_handle), ¬ifier); + task_runner_, std::move(consumer_handle), ¬ifier); EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState()); @@ -360,12 +370,14 @@ TEST_F(DataPipeBytesConsumerTest, DrainPipeBeforeComplete) { } TEST_F(DataPipeBytesConsumerTest, CompleteBeforeDrainPipe) { - mojo::DataPipe pipe; - ASSERT_TRUE(pipe.producer_handle.is_valid()); + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle), + MOJO_RESULT_OK); DataPipeBytesConsumer::CompletionNotifier* notifier = nullptr; DataPipeBytesConsumer* consumer = MakeGarbageCollected<DataPipeBytesConsumer>( - task_runner_, std::move(pipe.consumer_handle), ¬ifier); + task_runner_, std::move(consumer_handle), ¬ifier); EXPECT_EQ(PublicState::kReadableOrWaiting, consumer->GetPublicState()); diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_api_request_body_mojom_traits.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_api_request_body_mojom_traits.cc index 4558e08606d..a12b2b814d8 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_api_request_body_mojom_traits.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_api_request_body_mojom_traits.cc @@ -8,73 +8,40 @@ #include "mojo/public/cpp/base/file_path_mojom_traits.h" #include "mojo/public/cpp/bindings/array_traits_wtf_vector.h" #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h" +#include "third_party/blink/public/platform/cross_variant_mojo_util.h" #include "third_party/blink/public/platform/file_path_conversion.h" #include "third_party/blink/renderer/platform/blob/blob_data.h" +#include "third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.h" #include "third_party/blink/renderer/platform/network/form_data_encoder.h" #include "third_party/blink/renderer/platform/network/wrapped_data_pipe_getter.h" namespace mojo { // static -WTF::Vector<blink::mojom::blink::FetchAPIDataElementPtr> +WTF::Vector<network::DataElement> StructTraits<blink::mojom::FetchAPIRequestBodyDataView, blink::ResourceRequestBody>::elements(blink::ResourceRequestBody& mutable_body) { - WTF::Vector<blink::mojom::blink::FetchAPIDataElementPtr> out_elements; - const auto& body = mutable_body; - if (body.IsEmpty()) { - return out_elements; + scoped_refptr<network::ResourceRequestBody> network_body; + if (auto form_body = mutable_body.FormBody()) { + // Here we need to keep the original body, because other members such as + // `identifier` are on the form body. + network_body = NetworkResourceRequestBodyFor( + blink::ResourceRequestBody(form_body), + /*allow_http1_for_streaming_upload=*/false); + } else if (mutable_body.StreamBody()) { + // Here we don't need to keep the original body (and it's impossible to do + // so, because the streaming body is not copyable). + network_body = NetworkResourceRequestBodyFor( + std::move(mutable_body), /*allow_http1_for_streaming_upload=*/false); } - - if (mutable_body.StreamBody()) { - auto out = blink::mojom::blink::FetchAPIDataElement::New(); - out->type = network::mojom::DataElementType::kReadOnceStream; - out->chunked_data_pipe_getter = mutable_body.TakeStreamBody(); - out_elements.push_back(std::move(out)); - return out_elements; + if (!network_body) { + return WTF::Vector<network::DataElement>(); } - - DCHECK(body.FormBody()); - for (const auto& element : body.FormBody()->elements_) { - auto out = blink::mojom::blink::FetchAPIDataElement::New(); - switch (element.type_) { - case blink::FormDataElement::kData: - out->type = network::mojom::DataElementType::kBytes; - out->buf.ReserveCapacity(element.data_.size()); - for (const char c : element.data_) { - out->buf.push_back(static_cast<uint8_t>(c)); - } - break; - case blink::FormDataElement::kEncodedFile: - out->type = network::mojom::DataElementType::kFile; - out->path = base::FilePath::FromUTF8Unsafe(element.filename_.Utf8()); - out->offset = element.file_start_; - out->length = element.file_length_; - out->expected_modification_time = - element.expected_file_modification_time_.value_or(base::Time()); - break; - case blink::FormDataElement::kEncodedBlob: { - out->type = network::mojom::DataElementType::kDataPipe; - out->length = element.optional_blob_data_handle_->size(); - - mojo::Remote<blink::mojom::blink::Blob> blob_remote( - mojo::PendingRemote<blink::mojom::blink::Blob>( - element.optional_blob_data_handle_->CloneBlobRemote() - .PassPipe(), - blink::mojom::blink::Blob::Version_)); - blob_remote->AsDataPipeGetter( - out->data_pipe_getter.InitWithNewPipeAndPassReceiver()); - break; - } - case blink::FormDataElement::kDataPipe: - out->type = network::mojom::DataElementType::kDataPipe; - if (element.data_pipe_getter_) { - element.data_pipe_getter_->GetDataPipeGetter()->Clone( - out->data_pipe_getter.InitWithNewPipeAndPassReceiver()); - } - break; - } - out_elements.push_back(std::move(out)); + WTF::Vector<network::DataElement> out_elements; + DCHECK(network_body->elements_mutable()); + for (auto& element : *network_body->elements_mutable()) { + out_elements.emplace_back(std::move(element)); } return out_elements; } @@ -89,73 +56,58 @@ bool StructTraits<blink::mojom::FetchAPIRequestBodyDataView, return true; } - mojo::ArrayDataView<blink::mojom::FetchAPIDataElementDataView> elements_view; + mojo::ArrayDataView<network::mojom::DataElementDataView> elements_view; in.GetElementsDataView(&elements_view); if (elements_view.size() == 1) { - blink::mojom::FetchAPIDataElementDataView view; + network::mojom::DataElementDataView view; elements_view.GetDataView(0, &view); - network::mojom::DataElementType type; - if (!view.ReadType(&type)) { - return false; - } - if (type == network::mojom::DataElementType::kReadOnceStream) { - auto chunked_data_pipe_getter = view.TakeChunkedDataPipeGetter< - mojo::PendingRemote<network::mojom::blink::ChunkedDataPipeGetter>>(); - *out = blink::ResourceRequestBody(std::move(chunked_data_pipe_getter)); + DCHECK(!view.is_null()); + if (view.tag() == network::DataElement::Tag::kChunkedDataPipe) { + network::DataElement element; + if (!elements_view.Read(0, &element)) { + return false; + } + auto& chunked_data_pipe = + element.As<network::DataElementChunkedDataPipe>(); + *out = blink::ResourceRequestBody(blink::ToCrossVariantMojoType( + chunked_data_pipe.ReleaseChunkedDataPipeGetter())); return true; } } auto form_data = blink::EncodedFormData::Create(); for (size_t i = 0; i < elements_view.size(); ++i) { - blink::mojom::FetchAPIDataElementDataView view; - elements_view.GetDataView(i, &view); - - network::mojom::DataElementType type; - if (!view.ReadType(&type)) { + network::DataElement element; + if (!elements_view.Read(i, &element)) { return false; } - switch (type) { - case network::mojom::DataElementType::kBytes: { - // TODO(richard.li): Delete this workaround when type of - // blink::FormDataElement::data_ is changed to WTF::Vector<uint8_t> - WTF::Vector<uint8_t> buf; - if (!view.ReadBuf(&buf)) { - return false; - } - form_data->AppendData(buf.data(), buf.size()); + + switch (element.type()) { + case network::DataElement::Tag::kBytes: { + const auto& bytes = element.As<network::DataElementBytes>(); + form_data->AppendData(bytes.bytes().data(), bytes.bytes().size()); break; } - case network::mojom::DataElementType::kFile: { - base::FilePath file_path; - base::Time expected_time; - if (!view.ReadPath(&file_path) || - !view.ReadExpectedModificationTime(&expected_time)) { - return false; - } - base::Optional<base::Time> expected_file_modification_time; - if (!expected_time.is_null()) { - expected_file_modification_time = expected_time; + case network::DataElement::Tag::kFile: { + const auto& file = element.As<network::DataElementFile>(); + base::Optional<base::Time> expected_modification_time; + if (!file.expected_modification_time().is_null()) { + expected_modification_time = file.expected_modification_time(); } - form_data->AppendFileRange(blink::FilePathToString(file_path), - view.offset(), view.length(), - expected_file_modification_time); + form_data->AppendFileRange(blink::FilePathToString(file.path()), + file.offset(), file.length(), + expected_modification_time); break; } - case network::mojom::DataElementType::kDataPipe: { - auto data_pipe_ptr_remote = view.TakeDataPipeGetter< - mojo::PendingRemote<network::mojom::blink::DataPipeGetter>>(); - DCHECK(data_pipe_ptr_remote.is_valid()); - + case network::DataElement::Tag::kDataPipe: { + auto& datapipe = element.As<network::DataElementDataPipe>(); form_data->AppendDataPipe( base::MakeRefCounted<blink::WrappedDataPipeGetter>( - std::move(data_pipe_ptr_remote))); - + blink::ToCrossVariantMojoType( + datapipe.ReleaseDataPipeGetter()))); break; } - case network::mojom::DataElementType::kUnknown: - case network::mojom::DataElementType::kChunkedDataPipe: - case network::mojom::DataElementType::kReadOnceStream: + case network::DataElement::Tag::kChunkedDataPipe: NOTREACHED(); return false; } diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_api_request_body_mojom_traits.h b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_api_request_body_mojom_traits.h index f85aee270a8..4576e3abe9d 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_api_request_body_mojom_traits.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_api_request_body_mojom_traits.h @@ -21,7 +21,7 @@ struct PLATFORM_EXPORT StructTraits<blink::mojom::FetchAPIRequestBodyDataView, static void SetToNull(blink::ResourceRequestBody* out) { *out = blink::ResourceRequestBody(); } - static WTF::Vector<blink::mojom::blink::FetchAPIDataElementPtr> elements( + static WTF::Vector<network::DataElement> elements( blink::ResourceRequestBody& mutable_body); static int64_t identifier(const blink::ResourceRequestBody& body) { return body.FormBody() ? body.FormBody()->Identifier() : 0; diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_api_request_body_mojom_traits_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_api_request_body_mojom_traits_test.cc index 7f96b51c38f..c6505181f2b 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_api_request_body_mojom_traits_test.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_api_request_body_mojom_traits_test.cc @@ -30,7 +30,7 @@ TEST_F(FetchApiRequestBodyMojomTraitsTest, RoundTripEmpty) { ResourceRequestBody dest; EXPECT_TRUE(mojo::test::SerializeAndDeserialize< - blink::mojom::blink::FetchAPIRequestBody>(&src, &dest)); + blink::mojom::blink::FetchAPIRequestBody>(src, dest)); EXPECT_TRUE(dest.IsEmpty()); } @@ -43,7 +43,7 @@ TEST_F(FetchApiRequestBodyMojomTraitsTest, RoundTripBytes) { ResourceRequestBody dest; EXPECT_TRUE(mojo::test::SerializeAndDeserialize< - blink::mojom::blink::FetchAPIRequestBody>(&src, &dest)); + blink::mojom::blink::FetchAPIRequestBody>(src, dest)); ASSERT_TRUE(dest.FormBody()); EXPECT_EQ(dest.FormBody()->Identifier(), 29); @@ -61,7 +61,7 @@ TEST_F(FetchApiRequestBodyMojomTraitsTest, RoundTripFile) { ResourceRequestBody dest; EXPECT_TRUE(mojo::test::SerializeAndDeserialize< - blink::mojom::blink::FetchAPIRequestBody>(&src, &dest)); + blink::mojom::blink::FetchAPIRequestBody>(src, dest)); ASSERT_TRUE(dest.FormBody()); ASSERT_EQ(1u, dest.FormBody()->Elements().size()); @@ -79,7 +79,7 @@ TEST_F(FetchApiRequestBodyMojomTraitsTest, RoundTripFileRange) { ResourceRequestBody dest; EXPECT_TRUE(mojo::test::SerializeAndDeserialize< - blink::mojom::blink::FetchAPIRequestBody>(&src, &dest)); + blink::mojom::blink::FetchAPIRequestBody>(src, dest)); ASSERT_TRUE(dest.FormBody()); ASSERT_EQ(1u, dest.FormBody()->Elements().size()); @@ -102,7 +102,7 @@ TEST_F(FetchApiRequestBodyMojomTraitsTest, RoundTripBlobWithOpionalHandle) { ResourceRequestBody dest; EXPECT_TRUE(mojo::test::SerializeAndDeserialize< - blink::mojom::blink::FetchAPIRequestBody>(&src, &dest)); + blink::mojom::blink::FetchAPIRequestBody>(src, dest)); ASSERT_TRUE(dest.FormBody()); ASSERT_EQ(1u, dest.FormBody()->Elements().size()); @@ -122,7 +122,7 @@ TEST_F(FetchApiRequestBodyMojomTraitsTest, RoundTripDataPipeGetter) { ResourceRequestBody dest; EXPECT_TRUE(mojo::test::SerializeAndDeserialize< - blink::mojom::blink::FetchAPIRequestBody>(&src, &dest)); + blink::mojom::blink::FetchAPIRequestBody>(src, dest)); ASSERT_TRUE(dest.FormBody()); ASSERT_EQ(1u, dest.FormBody()->Elements().size()); @@ -139,7 +139,7 @@ TEST_F(FetchApiRequestBodyMojomTraitsTest, RoundTripStreamBody) { ResourceRequestBody dest; EXPECT_TRUE(mojo::test::SerializeAndDeserialize< - blink::mojom::blink::FetchAPIRequestBody>(&src, &dest)); + blink::mojom::blink::FetchAPIRequestBody>(src, dest)); EXPECT_FALSE(dest.FormBody()); ASSERT_TRUE(dest.StreamBody()); diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h index c2405674ddf..f3af4183d5c 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h @@ -65,18 +65,6 @@ class PLATFORM_EXPORT FetchClientSettingsObject // https://html.spec.whatwg.org/C/#concept-settings-object-referrer-policy virtual network::mojom::ReferrerPolicy GetReferrerPolicy() const = 0; - // |GetReferrerPolicyDisregardingMetaTagsContainingLists| - // returns the policy that would have been set had we been ignoring all <meta - // name=referrer> tags with values comma-separated lists of policies. This - // allows histogramming the proportion of requests that would end up with - // different referrers were these tags ignored, helping interpret the impact - // of removing support for them (which is inconsistent with the spec and other - // engines). - virtual base::Optional<network::mojom::ReferrerPolicy> - GetReferrerPolicyDisregardingMetaTagsContainingLists() const { - return base::nullopt; - } - // "referrerURL" used in the "Determine request's Referrer" algorithm: // https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer virtual const String GetOutgoingReferrer() const = 0; 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 index f4d380bda6e..6eef1738995 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_context.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_context.h @@ -120,6 +120,19 @@ class PLATFORM_EXPORT FetchContext : public GarbageCollected<FetchContext> { const { return ResourceRequestBlockedReason::kOther; } + // In derived classes, performs *only* a SubresourceFilter check for whether + // the request can go through or should be blocked. + virtual base::Optional<ResourceRequestBlockedReason> + CanRequestBasedOnSubresourceFilterOnly( + ResourceType, + const ResourceRequest&, + const KURL&, + const ResourceLoaderOptions&, + ReportingDisposition, + const base::Optional<ResourceRequest::RedirectInfo>& redirect_info) + const { + return ResourceRequestBlockedReason::kOther; + } virtual base::Optional<ResourceRequestBlockedReason> CheckCSPForRequest( mojom::blink::RequestContextType, network::mojom::RequestDestination request_destination, @@ -151,9 +164,11 @@ class PLATFORM_EXPORT FetchContext : public GarbageCollected<FetchContext> { virtual const FeaturePolicy* GetFeaturePolicy() const { return nullptr; } // Determine if the request is on behalf of an advertisement. If so, return - // true. + // true. Checks `resource_request.Url()` unless `alias_url` is non-null, in + // which case it checks the latter. virtual bool CalculateIfAdSubresource( - const ResourceRequest& resource_request, + const ResourceRequestHead& resource_request, + const base::Optional<KURL>& alias_url, ResourceType type, const FetchInitiatorInfo& initiator_info) { return false; @@ -174,6 +189,9 @@ class PLATFORM_EXPORT FetchContext : public GarbageCollected<FetchContext> { return nullptr; } + // Returns if the request context is for prerendering or not. + virtual bool IsPrerendering() const { return false; } + private: DISALLOW_COPY_AND_ASSIGN(FetchContext); }; 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 index 0a351b55b34..c8995c1284e 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.cc @@ -130,4 +130,9 @@ void FetchParameters::SetLazyImageNonBlocking() { image_request_behavior_ = kNonBlockingImage; } +void FetchParameters::SetModuleScript() { + DCHECK_EQ(mojom::blink::ScriptType::kClassic, script_type_); + script_type_ = mojom::blink::ScriptType::kModule; +} + } // 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 index c38f4571771..cb95a274740 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h @@ -27,10 +27,13 @@ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_FETCH_PARAMETERS_H_ #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink-forward.h" +#include "third_party/blink/public/mojom/script/script_type.mojom-blink-forward.h" +#include "third_party/blink/public/mojom/script/script_type.mojom-shared.h" #include "third_party/blink/public/platform/web_url_request.h" #include "third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.h" #include "third_party/blink/renderer/platform/loader/fetch/cross_origin_attribute_value.h" #include "third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h" +#include "third_party/blink/renderer/platform/loader/fetch/render_blocking_behavior.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" @@ -66,6 +69,7 @@ class PLATFORM_EXPORT FetchParameters { kNonBlockingImage // The image load may continue, but must be placed in // ResourceFetcher::non_blocking_loaders_. }; + struct ResourceWidth { DISALLOW_NEW(); float width; @@ -192,6 +196,10 @@ class PLATFORM_EXPORT FetchParameters { void SetLazyImageDeferred(); void SetLazyImageNonBlocking(); + mojom::blink::ScriptType GetScriptType() const { return script_type_; } + + void SetModuleScript(); + // See documentation in blink::ResourceRequest. bool IsFromOriginDirtyStyleSheet() const { return is_from_origin_dirty_style_sheet_; @@ -204,6 +212,15 @@ class PLATFORM_EXPORT FetchParameters { resource_request_.SetSignedExchangePrefetchCacheEnabled(enabled); } + RenderBlockingBehavior GetRenderBlockingBehavior() const { + return render_blocking_behavior_; + } + + void SetRenderBlockingBehavior( + RenderBlockingBehavior render_blocking_behavior) { + render_blocking_behavior_ = render_blocking_behavior; + } + private: ResourceRequest resource_request_; // |decoder_options_|'s ContentType is set to |kPlainTextContent| in @@ -216,8 +233,11 @@ class PLATFORM_EXPORT FetchParameters { ResourceWidth resource_width_; ClientHintsPreferences client_hint_preferences_; ImageRequestBehavior image_request_behavior_; + mojom::blink::ScriptType script_type_ = mojom::blink::ScriptType::kClassic; bool is_stale_revalidation_ = false; bool is_from_origin_dirty_style_sheet_ = false; + RenderBlockingBehavior render_blocking_behavior_ = + RenderBlockingBehavior::kUnset; }; } // namespace blink 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 index 5b5a5363894..c5ca2727e80 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache.cc @@ -447,7 +447,8 @@ bool MemoryCache::OnMemoryDump(WebMemoryDumpLevelOfDetail level_of_detail, return true; } -void MemoryCache::OnMemoryPressure(WebMemoryPressureLevel level) { +void MemoryCache::OnMemoryPressure( + base::MemoryPressureListener::MemoryPressureLevel level) { PruneAll(); } 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 index 16184a56356..74b17f2e573 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/memory_cache.h @@ -151,7 +151,8 @@ class PLATFORM_EXPORT MemoryCache final : public GarbageCollected<MemoryCache>, // Take memory usage snapshot for tracing. bool OnMemoryDump(WebMemoryDumpLevelOfDetail, WebProcessMemoryDump*) override; - void OnMemoryPressure(WebMemoryPressureLevel) override; + void OnMemoryPressure( + base::MemoryPressureListener::MemoryPressureLevel) override; private: enum PruneStrategy { 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 index f3e230d965f..e647437fdf6 100644 --- 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 @@ -132,7 +132,8 @@ class MemoryCacheCorrectnessTest : public testing::Test { base::MakeRefCounted<scheduler::FakeTaskRunner>(), base::MakeRefCounted<scheduler::FakeTaskRunner>(), MakeGarbageCollected<TestLoaderFactory>(), - MakeGarbageCollected<MockContextLifecycleNotifier>())); + MakeGarbageCollected<MockContextLifecycleNotifier>(), + nullptr /* back_forward_cache_loader_helper */)); Resource::SetClockForTesting(platform_->test_task_runner()->GetMockClock()); } void TearDown() override { 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 index 3c928d3a779..a1217b9eb1f 100644 --- 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 @@ -104,12 +104,13 @@ class MemoryCacheTest : public testing::Test { global_memory_cache_ = ReplaceMemoryCacheForTesting( MakeGarbageCollected<MemoryCache>(platform_->test_task_runner())); auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>(); + lifecycle_notifier_ = MakeGarbageCollected<MockContextLifecycleNotifier>(); fetcher_ = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit( properties->MakeDetachable(), MakeGarbageCollected<MockFetchContext>(), base::MakeRefCounted<scheduler::FakeTaskRunner>(), base::MakeRefCounted<scheduler::FakeTaskRunner>(), - MakeGarbageCollected<TestLoaderFactory>(), - MakeGarbageCollected<MockContextLifecycleNotifier>())); + MakeGarbageCollected<TestLoaderFactory>(), lifecycle_notifier_, + nullptr /* back_forward_cache_loader_helper */)); } void TearDown() override { @@ -118,6 +119,7 @@ class MemoryCacheTest : public testing::Test { Persistent<MemoryCache> global_memory_cache_; Persistent<ResourceFetcher> fetcher_; + Persistent<MockContextLifecycleNotifier> lifecycle_notifier_; ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler> platform_; }; 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 index 965b76b4a08..c4f1754af79 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource.cc @@ -36,8 +36,6 @@ #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/response_body_loader.h" -#include "third_party/blink/renderer/platform/loader/fetch/script_cached_metadata_handler.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/public/thread.h" #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h" @@ -233,11 +231,6 @@ void RawResource::WillNotFollowRedirect() { c->RedirectBlocked(); } -SingleCachedMetadataHandler* RawResource::ScriptCacheHandler() { - DCHECK_EQ(ResourceType::kRaw, GetType()); - return static_cast<SingleCachedMetadataHandler*>(Resource::CacheHandler()); -} - scoped_refptr<BlobDataHandle> RawResource::DownloadedBlob() const { return downloaded_blob_; } @@ -300,33 +293,15 @@ void RawResource::ResponseBodyReceived( client->ResponseBodyReceived(this, body_loader.DrainAsBytesConsumer()); } -CachedMetadataHandler* RawResource::CreateCachedMetadataHandler( - std::unique_ptr<CachedMetadataSender> send_callback) { - if (GetType() == ResourceType::kRaw) { - // This is a resource of indeterminate type, e.g. a fetched WebAssembly - // module; create a cache handler that can store a single metadata entry. - return MakeGarbageCollected<ScriptCachedMetadataHandler>( - Encoding(), std::move(send_callback)); - } - return Resource::CreateCachedMetadataHandler(std::move(send_callback)); -} - void RawResource::SetSerializedCachedMetadata(mojo_base::BigBuffer data) { // Resource ignores the cached metadata. Resource::SetSerializedCachedMetadata(mojo_base::BigBuffer()); - // Notify clients before potentially transferring ownership of the buffer. ResourceClientWalker<RawResourceClient> w(Clients()); - while (RawResourceClient* c = w.Next()) { - c->SetSerializedCachedMetadata(this, data.data(), data.size()); - } - - if (GetType() == ResourceType::kRaw) { - ScriptCachedMetadataHandler* cache_handler = - static_cast<ScriptCachedMetadataHandler*>(Resource::CacheHandler()); - if (cache_handler) { - cache_handler->SetSerializedCachedMetadata(std::move(data)); - } + // We rely on the fact that RawResource cannot have multiple clients. + CHECK_LE(Clients().size(), 1u); + if (RawResourceClient* c = w.Next()) { + c->CachedMetadataReceived(this, std::move(data)); } } 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 index 12dd376670b..6846361c04a 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/raw_resource.h @@ -26,6 +26,7 @@ #include <memory> #include "base/optional.h" +#include "third_party/blink/renderer/platform/blob/blob_data.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" @@ -39,7 +40,6 @@ class BufferingBytesConsumer; class FetchParameters; class RawResourceClient; class ResourceFetcher; -class SingleCachedMetadataHandler; class PLATFORM_EXPORT RawResource final : public Resource { public: @@ -87,19 +87,10 @@ class PLATFORM_EXPORT RawResource final : public Resource { void SetSerializedCachedMetadata(mojo_base::BigBuffer data) override; - // Used for code caching of fetched code resources. Returns a cache handler - // which can only store a single cache metadata entry. This is valid only if - // type is kRaw. - SingleCachedMetadataHandler* ScriptCacheHandler(); - scoped_refptr<BlobDataHandle> DownloadedBlob() const; void Trace(Visitor* visitor) const override; - protected: - CachedMetadataHandler* CreateCachedMetadataHandler( - std::unique_ptr<CachedMetadataSender> send_callback) override; - private: class RawResourceFactory : public NonTextResourceFactory { public: @@ -184,7 +175,7 @@ class PLATFORM_EXPORT RawResourceClient : public ResourceClient { uint64_t /* totalBytesToBeSent */) {} virtual void ResponseBodyReceived(Resource*, BytesConsumer&) {} virtual void ResponseReceived(Resource*, const ResourceResponse&) {} - virtual void SetSerializedCachedMetadata(Resource*, const uint8_t*, size_t) {} + virtual void CachedMetadataReceived(Resource*, mojo_base::BigBuffer) {} virtual bool RedirectReceived(Resource*, const ResourceRequest&, const ResourceResponse&) { 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 index 9ed72a6c757..48f308f59eb 100644 --- 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 @@ -67,7 +67,6 @@ class RawResourceTest : public testing::Test { void DidFinishLoadingBody() override {} void DidFailLoadingBody() override {} void DidCancelLoadingBody() override {} - void EvictFromBackForwardCache(mojom::RendererEvictionReason) override {} }; ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler> @@ -243,7 +242,7 @@ TEST_F(RawResourceTest, PreloadWithAsynchronousAddClient) { ReplayingBytesConsumer::Command(ReplayingBytesConsumer::Command::kDone)); ResponseBodyLoader* body_loader = MakeGarbageCollected<ResponseBodyLoader>( *bytes_consumer, *MakeGarbageCollected<NoopResponseBodyLoaderClient>(), - platform_->test_task_runner().get()); + platform_->test_task_runner().get(), nullptr); Persistent<DummyClient> dummy_client = MakeGarbageCollected<DummyClient>(); // Set the response first to make ResourceClient addition asynchronous. diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/render_blocking_behavior.h b/chromium/third_party/blink/renderer/platform/loader/fetch/render_blocking_behavior.h new file mode 100644 index 00000000000..7a7761fa226 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/render_blocking_behavior.h @@ -0,0 +1,19 @@ +// Copyright 2021 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_RENDER_BLOCKING_BEHAVIOR_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RENDER_BLOCKING_BEHAVIOR_H_ + +namespace blink { +enum class RenderBlockingBehavior : uint8_t { + kUnset, // Render blocking value was not set. + kBlocking, // Render Blocking resource. + kNonBlocking, // Non-blocking resource. + kNonBlockingDynamic, // Dynamically injected non-blocking resource. + kPotentiallyBlocking, // Dynamically injected non-blocking resource. + kInBodyParserBlocking, // Blocks parser below element declaration. +}; +} // namespace blink + +#endif diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource.cc index 3265d12e68e..b59317fa3e7 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource.cc @@ -41,8 +41,6 @@ #include "third_party/blink/renderer/platform/instrumentation/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/cached_metadata_handler.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" @@ -71,12 +69,6 @@ void NotifyFinishObservers( observer->NotifyFinished(); } -blink::mojom::CodeCacheType ToCodeCacheType(ResourceType resource_type) { - return resource_type == ResourceType::kRaw - ? blink::mojom::CodeCacheType::kWebAssembly - : blink::mojom::CodeCacheType::kJavascript; -} - void GetSharedBufferMemoryDump(SharedBuffer* buffer, const String& dump_prefix, WebProcessMemoryDump* memory_dump) { @@ -136,7 +128,8 @@ static inline bool ShouldUpdateHeaderAfterRevalidation( namespace { const base::Clock* g_clock_for_testing = nullptr; -} + +} // namespace static inline base::Time Now() { const base::Clock* clock = g_clock_for_testing @@ -163,6 +156,13 @@ Resource::Resource(const ResourceRequestHead& request, response_timestamp_(Now()), resource_request_(request), overhead_size_(CalculateOverheadSize()) { + scoped_refptr<const SecurityOrigin> top_frame_origin = + resource_request_.TopFrameOrigin(); + if (top_frame_origin) { + net::SchemefulSite site(top_frame_origin->ToUrlOrigin()); + existing_top_frame_sites_in_cache_.insert(site); + } + InstanceCounters::IncrementCounter(InstanceCounters::kResourceCounter); if (IsMainThread()) @@ -175,7 +175,6 @@ Resource::~Resource() { void Resource::Trace(Visitor* visitor) const { visitor->Trace(loader_); - visitor->Trace(cache_handler_); visitor->Trace(clients_); visitor->Trace(clients_awaiting_callback_); visitor->Trace(finished_clients_); @@ -507,23 +506,12 @@ bool Resource::WillFollowRedirect(const ResourceRequest& new_request, void Resource::SetResponse(const ResourceResponse& response) { response_ = response; - - // Currently we support the metadata caching only for HTTP family. - if (!GetResourceRequest().Url().ProtocolIsInHTTPFamily() || - !GetResponse().CurrentRequestUrl().ProtocolIsInHTTPFamily()) { - cache_handler_.Clear(); - return; - } - - cache_handler_ = CreateCachedMetadataHandler( - CachedMetadataSender::Create(GetResponse(), ToCodeCacheType(GetType()), - GetResourceRequest().RequestorOrigin())); } void Resource::ResponseReceived(const ResourceResponse& response) { response_timestamp_ = Now(); if (is_revalidating_) { - if (response.HttpStatusCode() == 304) { + if (IsSuccessfulRevalidationResponse(response)) { RevalidationSucceeded(response); return; } @@ -538,8 +526,6 @@ void Resource::ResponseReceived(const ResourceResponse& response) { void Resource::SetSerializedCachedMetadata(mojo_base::BigBuffer data) { DCHECK(!is_revalidating_); DCHECK(!GetResponse().IsNull()); - // Actual metadata transferred here will be lost. - DCHECK(!data.size()); } String Resource::ReasonNotDeletable() const { @@ -675,11 +661,9 @@ void Resource::DidRemoveClientOrObserver() { // 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()) + if (HasCacheControlNoStoreHeader() && IsMainThread()) { GetMemoryCache()->Remove(this); + } } } @@ -765,13 +749,16 @@ Resource::MatchStatus Resource::CanReuse(const FetchParameters& params) const { return MatchStatus::kUnknownFailure; } + // Use GetResourceRequest to get the const resource_request_. + const ResourceRequestHead& current_request = GetResourceRequest(); + // 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() != + if (current_request.AllowStoredCredentials() != new_request.AllowStoredCredentials()) { return MatchStatus::kRequestCredentialsModeDoesNotMatch; } @@ -818,10 +805,10 @@ Resource::MatchStatus Resource::CanReuse(const FetchParameters& params) const { return MatchStatus::kSynchronousFlagDoesNotMatch; } - if (resource_request_.GetKeepalive() || new_request.GetKeepalive()) + if (current_request.GetKeepalive() || new_request.GetKeepalive()) return MatchStatus::kKeepaliveSet; - if (GetResourceRequest().HttpMethod() != http_names::kGET || + if (current_request.HttpMethod() != http_names::kGET || new_request.HttpMethod() != http_names::kGET) { return MatchStatus::kRequestMethodDoesNotMatch; } @@ -833,16 +820,13 @@ Resource::MatchStatus Resource::CanReuse(const FetchParameters& params) const { if (!existing_origin->IsSameOriginWith(new_origin.get())) return MatchStatus::kUnknownFailure; - // securityOrigin has more complicated checks which callers are responsible - // for. - if (new_request.GetCredentialsMode() != - resource_request_.GetCredentialsMode()) { + current_request.GetCredentialsMode()) { return MatchStatus::kRequestCredentialsModeDoesNotMatch; } const auto new_mode = new_request.GetMode(); - const auto existing_mode = resource_request_.GetMode(); + const auto existing_mode = current_request.GetMode(); if (new_mode != existing_mode) return MatchStatus::kRequestModeDoesNotMatch; @@ -856,9 +840,6 @@ void Resource::Prune() { void Resource::OnPurgeMemory() { Prune(); - if (!cache_handler_) - return; - cache_handler_->ClearCachedMetadata(CachedMetadataHandler::kClearLocally); } void Resource::OnMemoryDump(WebMemoryDumpLevelOfDetail level_of_detail, @@ -924,10 +905,6 @@ void Resource::OnMemoryDump(WebMemoryDumpLevelOfDetail level_of_detail, overhead_dump->AddScalar("size", "bytes", OverheadSize()); memory_dump->AddSuballocation( overhead_dump->Guid(), String(WTF::Partitions::kAllocatedObjectPoolName)); - - const String cache_name = dump_name + "/code_cache"; - if (cache_handler_) - cache_handler_->OnMemoryDump(memory_dump, cache_name); } String Resource::GetMemoryDumpName() const { @@ -977,7 +954,6 @@ void Resource::RevalidationSucceeded( void Resource::RevalidationFailed() { SECURITY_CHECK(redirect_chain_.IsEmpty()); ClearData(); - cache_handler_.Clear(); integrity_disposition_ = ResourceIntegrityDisposition::kNotChecked; integrity_report_info_.Clear(); DestroyDecodedDataForFailedRevalidation(); @@ -1188,19 +1164,6 @@ const char* Resource::ResourceTypeToString( return InitiatorTypeNameToString(fetch_initiator_name); } -// static -blink::mojom::CodeCacheType Resource::ResourceTypeToCodeCacheType( - ResourceType resource_type) { - DCHECK( - // Cacheable WebAssembly modules are fetched, so raw resource type. - resource_type == ResourceType::kRaw || - // Cacheable Javascript is a script resource. - resource_type == ResourceType::kScript || - // Also accept mock resources for testing. - resource_type == ResourceType::kMock); - return ToCodeCacheType(resource_type); -} - bool Resource::IsLoadEventBlockingResourceType() const { switch (type_) { case ResourceType::kImage: @@ -1229,13 +1192,14 @@ void Resource::SetClockForTesting(const base::Clock* clock) { g_clock_for_testing = clock; } -size_t Resource::CodeCacheSize() const { - return cache_handler_ ? cache_handler_->GetCodeCacheSize() : 0; +bool Resource::AppendTopFrameSiteForMetrics(const SecurityOrigin& origin) { + net::SchemefulSite site(origin.ToUrlOrigin()); + auto result = existing_top_frame_sites_in_cache_.insert(site); + return !result.second; } -CachedMetadataHandler* Resource::CreateCachedMetadataHandler( - std::unique_ptr<CachedMetadataSender> send_callback) { - return nullptr; +void Resource::SetIsAdResource() { + resource_request_.SetIsAdResource(); } } // 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 index 5ef043e5352..ffbfd4696ca 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource.h @@ -31,6 +31,7 @@ #include "base/single_thread_task_runner.h" #include "base/time/time.h" #include "mojo/public/cpp/base/big_buffer.h" +#include "net/base/schemeful_site.h" #include "third_party/blink/public/mojom/loader/code_cache.mojom-blink-forward.h" #include "third_party/blink/public/platform/scheduler/web_scoped_virtual_time_pauser.h" #include "third_party/blink/renderer/platform/instrumentation/memory_pressure_listener.h" @@ -63,8 +64,6 @@ class Clock; namespace blink { class BlobDataHandle; -class CachedMetadataHandler; -class CachedMetadataSender; class FetchParameters; class ResourceClient; class ResourceFinishObserver; @@ -142,6 +141,9 @@ class PLATFORM_EXPORT Resource : public GarbageCollected<Resource>, // Match fails due to different request headers. kRequestHeadersDoNotMatch, + + // Match fails due to different script types. + kScriptTypeDoesNotMatch, }; ~Resource() override; @@ -228,7 +230,7 @@ class PLATFORM_EXPORT Resource : public GarbageCollected<Resource>, size_t DecodedSize() const { return decoded_size_; } size_t OverheadSize() const { return overhead_size_; } - size_t CodeCacheSize() const; + virtual size_t CodeCacheSize() const { return 0; } bool IsLoaded() const { return status_ > ResourceStatus::kPending; } @@ -266,8 +268,8 @@ class PLATFORM_EXPORT Resource : public GarbageCollected<Resource>, const ResourceResponse& GetResponse() const { return response_; } // 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. + // The default implementation does nothing. Subclasses interested in the data + // should implement the resource-specific behavior. virtual void SetSerializedCachedMetadata(mojo_base::BigBuffer data); AtomicString HttpContentType() const; @@ -382,8 +384,6 @@ class PLATFORM_EXPORT Resource : public GarbageCollected<Resource>, ResourceType, const AtomicString& fetch_initiator_name); - static blink::mojom::CodeCacheType ResourceTypeToCodeCacheType(ResourceType); - class ProhibitAddRemoveClientInScope : public base::AutoReset<bool> { public: ProhibitAddRemoveClientInScope(Resource* resource) @@ -407,6 +407,14 @@ class PLATFORM_EXPORT Resource : public GarbageCollected<Resource>, return CalculateOverheadSize(); } + // Appends the top-frame site derived from |origin| to + // |existing_top_frame_sites_in_cache_| and returns true if the same site + // already exists. + bool AppendTopFrameSiteForMetrics(const SecurityOrigin& origin); + + // Sets the ResourceRequest to be tagged as an ad. + void SetIsAdResource(); + protected: Resource(const ResourceRequestHead&, ResourceType, @@ -440,6 +448,11 @@ class PLATFORM_EXPORT Resource : public GarbageCollected<Resource>, finished_clients_.Contains(client); } + bool IsSuccessfulRevalidationResponse( + const ResourceResponse& response) const { + return IsCacheValidator() && response.HttpStatusCode() == 304; + } + struct RedirectPair { DISALLOW_NEW(); @@ -471,16 +484,6 @@ class PLATFORM_EXPORT Resource : public GarbageCollected<Resource>, 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); - - CachedMetadataHandler* CacheHandler() { return cache_handler_.Get(); } - private: friend class ResourceLoader; @@ -500,8 +503,6 @@ class PLATFORM_EXPORT Resource : public GarbageCollected<Resource>, ResourceType type_; ResourceStatus status_; - Member<CachedMetadataHandler> cache_handler_; - base::Optional<ResourceError> error_; base::TimeTicks load_response_end_; @@ -552,6 +553,13 @@ class PLATFORM_EXPORT Resource : public GarbageCollected<Resource>, WebScopedVirtualTimePauser virtual_time_pauser_; + // To compute metrics for measuring the efficacy of the + // memory cache if it was partitioned by top-frame site (in addition to the + // current origin which it is already partitioned by). + // TODO(crbug.com/1127971): Remove this once the decision is made to partition + // the cache using either Network Isolation Key or scoped to per-document. + std::set<net::SchemefulSite> existing_top_frame_sites_in_cache_; + DISALLOW_COPY_AND_ASSIGN(Resource); }; @@ -592,11 +600,6 @@ class NonTextResourceFactory : public ResourceFactory { } }; -#define DEFINE_RESOURCE_TYPE_CASTS(typeName) \ - DEFINE_TYPE_CASTS(typeName##Resource, Resource, resource, \ - resource->GetType() == ResourceType::k##typeName, \ - resource.GetType() == ResourceType::k##typeName) - } // 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 index 784a98d7377..adb87702500 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_error.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_error.cc @@ -180,7 +180,8 @@ bool ResourceError::IsCancellation() const { } bool ResourceError::IsTrustTokenCacheHit() const { - return error_code_ == net::ERR_TRUST_TOKEN_OPERATION_CACHE_HIT; + return error_code_ == + net::ERR_TRUST_TOKEN_OPERATION_SUCCESS_WITHOUT_SENDING_REQUEST; } bool ResourceError::IsUnactionableTrustTokensStatus() const { @@ -300,9 +301,8 @@ String DescriptionForBlockedByClientOrResponse(int error, int extended_error) { case ResourceRequestBlockedReason::kCorpNotSameSite: detail = "NotSameSite"; break; - case ResourceRequestBlockedReason:: - kBlockedByExtensionCrbug1128174Investigation: - detail = "BlockedByExtensionCrbug1128174Investigation"; + case ResourceRequestBlockedReason::kConversionRequest: + detail = "ConversionRequest"; break; } return WebString::FromASCII(net::ErrorToString(error) + "." + detail); 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 index 7c99713e600..3c445ddaa2b 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc @@ -47,6 +47,7 @@ #include "third_party/blink/public/mojom/web_feature/web_feature.mojom-blink.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/scheduler/web_scoped_virtual_time_pauser.h" +#include "third_party/blink/public/platform/web_back_forward_cache_loader_helper.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" @@ -56,6 +57,7 @@ #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/cors/cors.h" +#include "third_party/blink/renderer/platform/loader/fetch/back_forward_cache_loader_helper.h" #include "third_party/blink/renderer/platform/loader/fetch/console_logger.h" #include "third_party/blink/renderer/platform/loader/fetch/detachable_use_counter.h" #include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h" @@ -75,6 +77,7 @@ #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/mojo/mojo_binding_context.h" #include "third_party/blink/renderer/platform/network/encoded_form_data.h" #include "third_party/blink/renderer/platform/network/network_utils.h" #include "third_party/blink/renderer/platform/runtime_enabled_features.h" @@ -242,50 +245,6 @@ ResourceLoadPriority AdjustPriorityWithPriorityHint( return new_priority; } -ResourceLoadPriority AdjustPriorityWithDeferScriptIntervention( - const FetchContext& fetch_context, - ResourceLoadPriority priority_so_far, - ResourceType type, - const ResourceRequestHead& resource_request, - FetchParameters::DeferOption defer_option, - bool is_link_preload) { - if (!base::FeatureList::IsEnabled( - blink::features::kLowerJavaScriptPriorityWhenForceDeferred)) { - return priority_so_far; - } - - PreviewsState context_previews_state = fetch_context.previews_state(); - - if (type != ResourceType::kScript) - return priority_so_far; - - // If none of the JavaScript resources are render blocking (due to the - // DeferAllScript intervention), then lower their priority so they do not - // contend for network resources with higher priority resources that may be - // render blocking (e.g., html, css). ResourceLoadPriority::kMedium - // corresponds to a network priority of - // network::mojom::blink::RequestPriority::kLow which is considered delayable - // by the resource scheduler on the browser side. - if (RuntimeEnabledFeatures::ForceDeferScriptInterventionEnabled() || - (context_previews_state & PreviewsTypes::kDeferAllScriptOn)) { - return std::min(priority_so_far, ResourceLoadPriority::kMedium); - } - return priority_so_far; -} - -std::unique_ptr<TracedValue> BeginResourceLoadData( - const blink::ResourceRequest& request) { - auto value = std::make_unique<TracedValue>(); - value->SetString("url", request.Url().GetString()); - return value; -} - -std::unique_ptr<TracedValue> EndResourceLoadFailData() { - auto value = std::make_unique<TracedValue>(); - value->SetString("outcome", "Fail"); - return value; -} - std::unique_ptr<TracedValue> ResourcePrioritySetData( blink::ResourceLoadPriority priority) { auto value = std::make_unique<TracedValue>(); @@ -295,9 +254,9 @@ std::unique_ptr<TracedValue> ResourcePrioritySetData( // This function corresponds with step 2 substep 7 of // https://fetch.spec.whatwg.org/#main-fetch. -void SetReferrer(ResourceRequest& request, - const FetchClientSettingsObject& fetch_client_settings_object, - UseCounter& use_counter) { +void SetReferrer( + ResourceRequest& request, + const FetchClientSettingsObject& fetch_client_settings_object) { String referrer_to_use = request.ReferrerString(); network::mojom::ReferrerPolicy referrer_policy_to_use = request.GetReferrerPolicy(); @@ -305,42 +264,12 @@ void SetReferrer(ResourceRequest& request, if (referrer_to_use == Referrer::ClientReferrerString()) referrer_to_use = fetch_client_settings_object.GetOutgoingReferrer(); - bool used_referrer_policy_from_context = false; - if (referrer_policy_to_use == network::mojom::ReferrerPolicy::kDefault) { - used_referrer_policy_from_context = true; + if (referrer_policy_to_use == network::mojom::ReferrerPolicy::kDefault) referrer_policy_to_use = fetch_client_settings_object.GetReferrerPolicy(); - } Referrer generated_referrer = SecurityPolicy::GenerateReferrer( referrer_policy_to_use, request.Url(), referrer_to_use); - // The request's referrer would be different in the absence of <meta - // name=referrer> tags with comma-separated-list values exactly when both - // 1. the request falls back to its client settings object's policy; and - // 2. if we recompute the referrer using the value of the client settings - // object's policy, disregarding any policy that came from a meta tag with a - // comma-separated value, we obtain a different referrer. - if (used_referrer_policy_from_context) { - base::Optional<network::mojom::blink::ReferrerPolicy> - policy_but_for_meta_tags_with_policy_lists = - fetch_client_settings_object - .GetReferrerPolicyDisregardingMetaTagsContainingLists(); - - bool referrer_would_be_different_absent_meta_tags_with_policy_lists = - policy_but_for_meta_tags_with_policy_lists.has_value() && - (generated_referrer.referrer != - SecurityPolicy::GenerateReferrer( - *policy_but_for_meta_tags_with_policy_lists, request.Url(), - referrer_to_use) - .referrer); - - if (referrer_would_be_different_absent_meta_tags_with_policy_lists) { - use_counter.CountUse( - mojom::WebFeature:: - kHTMLMetaElementReferrerPolicyMultipleTokensAffectingRequest); - } - } - request.SetReferrerString(generated_referrer.referrer); request.SetReferrerPolicy(generated_referrer.referrer_policy); } @@ -371,13 +300,15 @@ ResourceFetcherInit::ResourceFetcherInit( scoped_refptr<base::SingleThreadTaskRunner> freezable_task_runner, scoped_refptr<base::SingleThreadTaskRunner> unfreezable_task_runner, ResourceFetcher::LoaderFactory* loader_factory, - ContextLifecycleNotifier* context_lifecycle_notifier) + ContextLifecycleNotifier* context_lifecycle_notifier, + BackForwardCacheLoaderHelper* back_forward_cache_loader_helper) : properties(&properties), context(context), freezable_task_runner(std::move(freezable_task_runner)), unfreezable_task_runner(std::move(unfreezable_task_runner)), loader_factory(loader_factory), - context_lifecycle_notifier(context_lifecycle_notifier) { + context_lifecycle_notifier(context_lifecycle_notifier), + back_forward_cache_loader_helper(back_forward_cache_loader_helper) { DCHECK(context); DCHECK(this->freezable_task_runner); DCHECK(this->unfreezable_task_runner); @@ -553,10 +484,6 @@ ResourceLoadPriority ResourceFetcher::ComputeLoadPriority( priority = AdjustPriorityWithPriorityHint(priority, type, resource_request, defer_option, is_link_preload); - priority = AdjustPriorityWithDeferScriptIntervention( - Context(), priority, type, resource_request, defer_option, - is_link_preload); - if (properties_->IsSubframeDeprioritizationEnabled()) { if (properties_->IsMainFrame()) { UMA_HISTOGRAM_ENUMERATION( @@ -601,6 +528,7 @@ ResourceFetcher::ResourceFetcher(const ResourceFetcherInit& init) init.frame_or_worker_scheduler, *console_logger_, init.loading_behavior_observer)), + back_forward_cache_loader_helper_(init.back_forward_cache_loader_helper), archive_(init.archive), resource_timing_report_timer_( freezable_task_runner_, @@ -645,6 +573,11 @@ ResourceFetcher::IsControlledByServiceWorker() const { bool ResourceFetcher::ResourceNeedsLoad(Resource* resource, const FetchParameters& params, RevalidationPolicy policy) { + // MHTML documents should not trigger actual loads (i.e. all resource requests + // should be fulfilled by the MHTML archive). + if (archive_) + return false; + // Defer a font load until it is actually needed unless this is a link // preload. if (resource->GetType() == ResourceType::kFont && !params.IsLinkPreload()) @@ -664,13 +597,15 @@ bool ResourceFetcher::ResourceNeedsLoad(Resource* resource, void ResourceFetcher::DidLoadResourceFromMemoryCache( Resource* resource, const ResourceRequest& request, - bool is_static_data) { + bool is_static_data, + RenderBlockingBehavior render_blocking_behavior) { if (IsDetached() || !resource_load_observer_) return; resource_load_observer_->WillSendRequest( request.InspectorId(), request, ResourceResponse() /* redirects */, - resource->GetType(), resource->Options().initiator_info); + resource->GetType(), resource->Options().initiator_info, + render_blocking_behavior); resource_load_observer_->DidReceiveResponse( request.InspectorId(), request, resource->GetResponse(), resource, ResourceLoadObserver::ResponseSource::kFromMemoryCache); @@ -707,7 +642,7 @@ void ResourceFetcher::DidLoadResourceFromMemoryCache( } } -Resource* ResourceFetcher::ResourceForStaticData( +Resource* ResourceFetcher::CreateResourceForStaticData( const FetchParameters& params, const ResourceFactory& factory) { const KURL& url = params.GetResourceRequest().Url(); @@ -801,11 +736,13 @@ void ResourceFetcher::MakePreloadedResourceBlockOnloadIfNeeded( } } -void ResourceFetcher::UpdateMemoryCacheStats(Resource* resource, - RevalidationPolicy policy, - const FetchParameters& params, - const ResourceFactory& factory, - bool is_static_data) const { +void ResourceFetcher::UpdateMemoryCacheStats( + Resource* resource, + RevalidationPolicy policy, + const FetchParameters& params, + const ResourceFactory& factory, + bool is_static_data, + bool same_top_frame_site_resource_cached) const { if (is_static_data) return; @@ -813,6 +750,11 @@ void ResourceFetcher::UpdateMemoryCacheStats(Resource* resource, DEFINE_RESOURCE_HISTOGRAM("Preload."); } else { DEFINE_RESOURCE_HISTOGRAM(""); + + // Log metrics to evaluate effectiveness of the memory cache if it was + // partitioned by the top-frame site. + if (same_top_frame_site_resource_cached) + DEFINE_RESOURCE_HISTOGRAM("PerTopFrameSite."); } // Aims to count Resource only referenced from MemoryCache (i.e. what would be @@ -924,6 +866,8 @@ base::Optional<ResourceRequestBlockedReason> ResourceFetcher::PrepareRequest( DCHECK_NE(computed_load_priority, ResourceLoadPriority::kUnresolved); resource_request.SetPriority(computed_load_priority); + resource_request.SetRenderBlockingBehavior( + params.GetRenderBlockingBehavior()); if (resource_request.GetCacheMode() == mojom::FetchCacheMode::kDefault) { resource_request.SetCacheMode(Context().ResourceRequestCachePolicy( @@ -947,8 +891,7 @@ base::Optional<ResourceRequestBlockedReason> ResourceFetcher::PrepareRequest( http_names::kGET && !params.IsStaleRevalidation()); - SetReferrer(resource_request, properties_->GetFetchClientSettingsObject(), - GetUseCounter()); + SetReferrer(resource_request, properties_->GetFetchClientSettingsObject()); resource_request.SetExternalRequestStateFromRequestorAddressSpace( properties_->GetFetchClientSettingsObject().GetAddressSpace()); @@ -959,7 +902,7 @@ base::Optional<ResourceRequestBlockedReason> ResourceFetcher::PrepareRequest( TRACE_DISABLED_BY_DEFAULT("network"), "ResourcePrioritySet", TRACE_ID_WITH_SCOPE("BlinkResourceID", TRACE_ID_LOCAL(resource_request.InspectorId())), - "data", ResourcePrioritySetData(resource_request.Priority())); + "priority", resource_request.Priority()); KURL url = MemoryCache::RemoveFragmentIdentifierIfNeeded(params.Url()); base::Optional<ResourceRequestBlockedReason> blocked_reason = @@ -967,8 +910,9 @@ base::Optional<ResourceRequestBlockedReason> ResourceFetcher::PrepareRequest( reporting_disposition, resource_request.GetRedirectInfo()); - if (Context().CalculateIfAdSubresource(resource_request, resource_type, - options.initiator_info)) + if (Context().CalculateIfAdSubresource(resource_request, + base::nullopt /* alias_url */, + resource_type, options.initiator_info)) resource_request.SetIsAdResource(); if (blocked_reason) @@ -988,9 +932,37 @@ base::Optional<ResourceRequestBlockedReason> ResourceFetcher::PrepareRequest( resource_request.SetAllowStoredCredentials(false); } + if (resource_request.GetWebBundleTokenParams()) { + DCHECK_EQ(resource_request.GetRequestDestination(), + network::mojom::RequestDestination::kWebBundle); + } else { + AttachWebBundleTokenIfNeeded(resource_request); + } + return base::nullopt; } +bool ResourceFetcher::ShouldBeLoadedFromWebBundle(const KURL& url) const { + for (auto& bundle : subresource_web_bundles_) { + if (bundle->CanHandleRequest(url)) + return true; + } + return false; +} + +void ResourceFetcher::AttachWebBundleTokenIfNeeded( + ResourceRequest& resource_request) const { + for (auto& bundle : subresource_web_bundles_) { + if (!bundle->CanHandleRequest(resource_request.Url())) + continue; + resource_request.SetWebBundleTokenParams( + ResourceRequestHead::WebBundleTokenParams(bundle->GetBundleUrl(), + bundle->WebBundleToken(), + mojo::NullRemote())); + return; + } +} + Resource* ResourceFetcher::RequestResource(FetchParameters& params, const ResourceFactory& factory, ResourceClient* client) { @@ -1022,8 +994,8 @@ Resource* ResourceFetcher::RequestResource(FetchParameters& params, params.IsFromOriginDirtyStyleSheet()); TRACE_EVENT_NESTABLE_ASYNC_BEGIN1( TRACE_DISABLED_BY_DEFAULT("network"), "ResourceLoad", - TRACE_ID_WITH_SCOPE("BlinkResourceID", TRACE_ID_LOCAL(identifier)), - "beginData", BeginResourceLoadData(resource_request)); + TRACE_ID_WITH_SCOPE("BlinkResourceID", TRACE_ID_LOCAL(identifier)), "url", + resource_request.Url()); SCOPED_BLINK_UMA_HISTOGRAM_TIMER_THREAD_SAFE( "Blink.Fetch.RequestResourceTime"); TRACE_EVENT1("blink", "ResourceFetcher::requestResource", "url", @@ -1054,7 +1026,7 @@ Resource* ResourceFetcher::RequestResource(FetchParameters& params, bool is_static_data = is_data_url || archive_; bool is_stale_revalidation = params.IsStaleRevalidation(); if (!is_stale_revalidation && is_static_data) { - resource = ResourceForStaticData(params, factory); + resource = CreateResourceForStaticData(params, factory); if (resource) { policy = DetermineRevalidationPolicy(resource_type, params, *resource, true); @@ -1068,6 +1040,10 @@ Resource* ResourceFetcher::RequestResource(FetchParameters& params, } } + bool same_top_frame_site_resource_cached = false; + bool in_cached_resources_map = cached_resources_map_.Contains( + MemoryCache::RemoveFragmentIdentifierIfNeeded(params.Url())); + if (!is_stale_revalidation && !resource) { resource = MatchPreload(params, resource_type); if (resource) { @@ -1076,16 +1052,28 @@ Resource* ResourceFetcher::RequestResource(FetchParameters& params, // found, we may need to make it block the onload event. MakePreloadedResourceBlockOnloadIfNeeded(resource, params); } else if (IsMainThread()) { - resource = GetMemoryCache()->ResourceForURL( - params.Url(), GetCacheIdentifier(params.Url())); + if (base::FeatureList::IsEnabled(features::kScopeMemoryCachePerContext) && + !in_cached_resources_map) { + resource = nullptr; + } else { + resource = GetMemoryCache()->ResourceForURL( + params.Url(), GetCacheIdentifier(params.Url())); + } if (resource) { policy = DetermineRevalidationPolicy(resource_type, params, *resource, is_static_data); + scoped_refptr<const SecurityOrigin> top_frame_origin = + resource_request.TopFrameOrigin(); + if (top_frame_origin) { + same_top_frame_site_resource_cached = + resource->AppendTopFrameSiteForMetrics(*top_frame_origin); + } } } } - UpdateMemoryCacheStats(resource, policy, params, factory, is_static_data); + UpdateMemoryCacheStats(resource, policy, params, factory, is_static_data, + same_top_frame_site_resource_cached); switch (policy) { case RevalidationPolicy::kReload: @@ -1133,10 +1121,10 @@ Resource* ResourceFetcher::RequestResource(FetchParameters& params, DCHECK(EqualIgnoringFragmentIdentifier(resource->Url(), params.Url())); if (policy == RevalidationPolicy::kUse && resource->GetStatus() == ResourceStatus::kCached && - !cached_resources_map_.Contains( - MemoryCache::RemoveFragmentIdentifierIfNeeded(params.Url()))) { + !in_cached_resources_map) { // Loaded from MemoryCache. - DidLoadResourceFromMemoryCache(resource, resource_request, is_static_data); + DidLoadResourceFromMemoryCache(resource, resource_request, is_static_data, + params.GetRenderBlockingBehavior()); } if (!is_stale_revalidation) { String resource_url = @@ -1154,13 +1142,14 @@ Resource* ResourceFetcher::RequestResource(FetchParameters& params, // load-blocking. Lazy loaded images that are eventually fetched, however, // should always be added to |non_blocking_loaders_|, as they are never // load-blocking. - LoadBlockingPolicy load_blocking_policy = LoadBlockingPolicy::kDefault; + ImageLoadBlockingPolicy load_blocking_policy = + ImageLoadBlockingPolicy::kDefault; if (resource->GetType() == ResourceType::kImage) { image_resources_.insert(resource); not_loaded_image_resources_.insert(resource); if (params.GetImageRequestBehavior() == FetchParameters::kNonBlockingImage) { - load_blocking_policy = LoadBlockingPolicy::kForceNonBlockingLoad; + load_blocking_policy = ImageLoadBlockingPolicy::kForceNonBlockingLoad; } } @@ -1171,7 +1160,7 @@ Resource* ResourceFetcher::RequestResource(FetchParameters& params, if (ResourceNeedsLoad(resource, params, policy)) { if (!StartLoad(resource, std::move(params.MutableResourceRequest().MutableBody()), - load_blocking_policy)) { + load_blocking_policy, params.GetRenderBlockingBehavior())) { resource->FinishAsError(ResourceError::CancelledError(params.Url()), freezable_task_runner_.get()); } @@ -1185,7 +1174,7 @@ Resource* ResourceFetcher::RequestResource(FetchParameters& params, TRACE_EVENT_NESTABLE_ASYNC_END1( TRACE_DISABLED_BY_DEFAULT("network"), "ResourceLoad", TRACE_ID_WITH_SCOPE("BlinkResourceID", TRACE_ID_LOCAL(identifier)), - "endData", EndResourceLoadFailData()); + "outcome", "Fail"); } return resource; @@ -1250,22 +1239,10 @@ std::unique_ptr<WebURLLoader> ResourceFetcher::CreateURLLoader( const ResourceLoaderOptions& options) { DCHECK(!GetProperties().IsDetached()); DCHECK(loader_factory_); - for (auto& bundle : subresource_web_bundles_) { - if (!bundle->CanHandleRequest(request.Url())) - continue; - ResourceLoaderOptions new_options(options); - new_options.url_loader_factory = base::MakeRefCounted<base::RefCountedData< - mojo::PendingRemote<network::mojom::blink::URLLoaderFactory>>>( - bundle->GetURLLoaderFactory()); - // TODO(yoichio): CreateURLLoader take a ResourceRequestHead instead of - // ResourceRequest. - return loader_factory_->CreateURLLoader(ResourceRequest(request), - new_options, freezable_task_runner_, - unfreezable_task_runner_); - } - return loader_factory_->CreateURLLoader(ResourceRequest(request), options, - freezable_task_runner_, - unfreezable_task_runner_); + return loader_factory_->CreateURLLoader( + ResourceRequest(request), options, freezable_task_runner_, + unfreezable_task_runner_, + WebBackForwardCacheLoaderHelper(back_forward_cache_loader_helper_)); } std::unique_ptr<WebCodeCacheLoader> ResourceFetcher::CreateCodeCacheLoader() { @@ -1287,9 +1264,12 @@ Resource* ResourceFetcher::CreateResourceForLoading( const ResourceFactory& factory) { const String cache_identifier = GetCacheIdentifier(params.GetResourceRequest().Url()); - DCHECK(!IsMainThread() || params.IsStaleRevalidation() || - !GetMemoryCache()->ResourceForURL(params.GetResourceRequest().Url(), - cache_identifier)); + if (!base::FeatureList::IsEnabled( + blink::features::kScopeMemoryCachePerContext)) { + DCHECK(!IsMainThread() || params.IsStaleRevalidation() || + !GetMemoryCache()->ResourceForURL(params.GetResourceRequest().Url(), + cache_identifier)); + } RESOURCE_LOADING_DVLOG(1) << "Loading Resource for " << params.GetResourceRequest().Url().ElidedString(); @@ -1426,6 +1406,9 @@ void ResourceFetcher::PrintPreloadWarning(Resource* resource, case Resource::MatchStatus::kRequestHeadersDoNotMatch: builder.Append("because the request headers do not match."); break; + case Resource::MatchStatus::kScriptTypeDoesNotMatch: + builder.Append("because the script type does not match."); + break; } console_logger_->AddConsoleMessage(mojom::ConsoleMessageSource::kOther, mojom::ConsoleMessageLevel::kWarning, @@ -1519,6 +1502,8 @@ ResourceFetcher::DetermineRevalidationPolicyInternal( bool is_static_data) const { const ResourceRequest& request = fetch_params.GetResourceRequest(); + Resource* cached_resource_in_fetcher = CachedResource(request.Url()); + if (IsDownloadOrStreamRequest(request)) { return {RevalidationPolicy::kReload, "It is for download or for streaming."}; @@ -1578,7 +1563,7 @@ ResourceFetcher::DetermineRevalidationPolicyInternal( // If resource was populated from archive or data: url, use it. // This doesn't necessarily mean that |resource| was just created by using - // ResourceForStaticData(). + // CreateResourceForStaticData(). if (is_static_data) { return {RevalidationPolicy::kUse, "Use the existing static resource."}; } @@ -1640,7 +1625,7 @@ ResourceFetcher::DetermineRevalidationPolicyInternal( // validation. We restrict this only to images from memory cache which are the // same as the version in the current document. if (type == ResourceType::kImage && - &existing_resource == CachedResource(request.Url())) { + &existing_resource == cached_resource_in_fetcher) { return {RevalidationPolicy::kUse, "Images can be reused without cache validation."}; } @@ -1758,6 +1743,8 @@ void ResourceFetcher::ClearContext() { resource_load_observer_ = nullptr; use_counter_->Detach(); console_logger_->Detach(); + if (back_forward_cache_loader_helper_) + back_forward_cache_loader_helper_->Detach(); loader_factory_ = nullptr; unused_preloads_timer_.Cancel(); @@ -1962,18 +1949,32 @@ void ResourceFetcher::MoveResourceLoaderToNonBlocking(ResourceLoader* loader) { } bool ResourceFetcher::StartLoad(Resource* resource) { + DCHECK(resource->GetType() == ResourceType::kFont || + resource->GetType() == ResourceType::kImage); return StartLoad(resource, ResourceRequestBody(), - LoadBlockingPolicy::kDefault); + ImageLoadBlockingPolicy::kDefault, + RenderBlockingBehavior::kNonBlocking); } -bool ResourceFetcher::StartLoad(Resource* resource, - ResourceRequestBody request_body, - LoadBlockingPolicy policy) { +bool ResourceFetcher::StartLoad( + Resource* resource, + ResourceRequestBody request_body, + ImageLoadBlockingPolicy policy, + RenderBlockingBehavior render_blocking_behavior) { DCHECK(resource); DCHECK(resource->StillNeedsLoad()); ResourceLoader* loader = nullptr; + if (archive_ && resource->Url().ProtocolIsInHTTPFamily()) { + // MHTML documents should not trigger HTTP requests. + // + // TODO(lukasza): https://crbug.com/1151438: Remove the ad-hoc DwoC below, + // once the bug is fixed and verified. + NOTREACHED(); + base::debug::DumpWithoutCrashing(); + } + { // Forbids JavaScript/revalidation until start() // to prevent unintended state transitions. @@ -1995,7 +1996,7 @@ bool ResourceFetcher::StartLoad(Resource* resource, ResourceResponse response; resource_load_observer_->WillSendRequest( resource->InspectorId(), request, response, resource->GetType(), - resource->Options().initiator_info); + resource->Options().initiator_info, render_blocking_behavior); } using QuotaType = decltype(inflight_keepalive_bytes_); @@ -2021,7 +2022,7 @@ bool ResourceFetcher::StartLoad(Resource* resource, // is handled by MakePreloadedResourceBlockOnloadIfNeeded(). if (!resource->IsLinkPreload() && resource->IsLoadEventBlockingResourceType() && - policy != LoadBlockingPolicy::kForceNonBlockingLoad) { + policy != ImageLoadBlockingPolicy::kForceNonBlockingLoad) { loaders_.insert(loader); } else { non_blocking_loaders_.insert(loader); @@ -2126,6 +2127,11 @@ String ResourceFetcher::GetCacheIdentifier(const KURL& url) const { if (properties_->WebBundlePhysicalUrl().IsValid()) return properties_->WebBundlePhysicalUrl().GetString(); + // Requests that can be satisfied via `archive_` (i.e. MHTML) or + // `subresource_web_bundles_` should not participate in the global caching, + // but should use a bundle/mhtml-specific cache. + if (archive_) + return archive_->GetCacheIdentifier(); for (auto& bundle : subresource_web_bundles_) { if (bundle->CanHandleRequest(url)) return bundle->GetCacheIdentifier(); @@ -2181,7 +2187,8 @@ void ResourceFetcher::EmulateLoadStartedForInspector( } DCHECK_EQ(resource->GetStatus(), ResourceStatus::kCached); DidLoadResourceFromMemoryCache(resource, params.GetResourceRequest(), - false /* is_static_data */); + false /* is_static_data */, + params.GetRenderBlockingBehavior()); } void ResourceFetcher::PrepareForLeakDetection() { @@ -2270,14 +2277,6 @@ void ResourceFetcher::RemoveSubresourceWebBundle( subresource_web_bundles_.erase(&subresource_web_bundle); } -void ResourceFetcher::EvictFromBackForwardCache( - mojom::RendererEvictionReason reason) { - if (!resource_load_observer_) - return; - - resource_load_observer_->EvictFromBackForwardCache(reason); -} - void ResourceFetcher::Trace(Visitor* visitor) const { visitor->Trace(context_); visitor->Trace(properties_); @@ -2286,7 +2285,9 @@ void ResourceFetcher::Trace(Visitor* visitor) const { visitor->Trace(console_logger_); visitor->Trace(loader_factory_); visitor->Trace(scheduler_); + visitor->Trace(back_forward_cache_loader_helper_); visitor->Trace(archive_); + visitor->Trace(resource_timing_report_timer_); visitor->Trace(loaders_); visitor->Trace(non_blocking_loaders_); visitor->Trace(cached_resources_map_); 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 index 327e6812aba..7bfb1e03b7a 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h @@ -43,6 +43,7 @@ #include "third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h" #include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h" #include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h" +#include "third_party/blink/renderer/platform/mojo/mojo_binding_context.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" @@ -52,6 +53,7 @@ namespace blink { enum class ResourceType : uint8_t; +class BackForwardCacheLoaderHelper; class DetachableConsoleLogger; class DetachableUseCounter; class DetachableResourceFetcherProperties; @@ -64,6 +66,7 @@ class ResourceError; class ResourceLoadObserver; class ResourceTimingInfo; class SubresourceWebBundle; +class WebBackForwardCacheLoaderHelper; class WebCodeCacheLoader; struct ResourceFetcherInit; struct ResourceLoaderOptions; @@ -99,8 +102,8 @@ class PLATFORM_EXPORT ResourceFetcher const ResourceRequest&, const ResourceLoaderOptions&, scoped_refptr<base::SingleThreadTaskRunner> freezable_task_runner, - scoped_refptr<base::SingleThreadTaskRunner> - unfreezable_task_runner) = 0; + scoped_refptr<base::SingleThreadTaskRunner> unfreezable_task_runner, + WebBackForwardCacheLoaderHelper) = 0; // Create a code cache loader to fetch data from code caches. virtual std::unique_ptr<WebCodeCacheLoader> CreateCodeCacheLoader() = 0; @@ -174,11 +177,13 @@ class PLATFORM_EXPORT ResourceFetcher base::OnceCallback<void(int)>); using DocumentResourceMap = HeapHashMap<String, WeakMember<Resource>>; + // Note: This function is defined for devtools. Do not use this function in + // non-inspector/non-tent-only contexts. const DocumentResourceMap& AllResources() const { return cached_resources_map_; } - enum class LoadBlockingPolicy { + enum class ImageLoadBlockingPolicy { kDefault, kForceNonBlockingLoad, }; @@ -189,7 +194,10 @@ class PLATFORM_EXPORT ResourceFetcher // call this method explicitly on cases such as ResourceNeedsLoad() returning // false. bool StartLoad(Resource*); - bool StartLoad(Resource*, ResourceRequestBody, LoadBlockingPolicy); + bool StartLoad(Resource*, + ResourceRequestBody, + ImageLoadBlockingPolicy, + RenderBlockingBehavior); void SetAutoLoadImages(bool); void SetImagesEnabled(bool); @@ -305,8 +313,12 @@ class PLATFORM_EXPORT ResourceFetcher void AddSubresourceWebBundle(SubresourceWebBundle& subresource_web_bundle); void RemoveSubresourceWebBundle(SubresourceWebBundle& subresource_web_bundle); + void AttachWebBundleTokenIfNeeded(ResourceRequest&) const; + bool ShouldBeLoadedFromWebBundle(const KURL&) const; - void EvictFromBackForwardCache(mojom::RendererEvictionReason reason); + BackForwardCacheLoaderHelper* GetBackForwardCacheLoaderHelper() { + return back_forward_cache_loader_helper_; + } private: friend class ResourceCacheValidationSuppressor; @@ -341,8 +353,8 @@ class PLATFORM_EXPORT ResourceFetcher const ResourceFactory&, WebScopedVirtualTimePauser& virtual_time_pauser); - Resource* ResourceForStaticData(const FetchParameters&, - const ResourceFactory&); + Resource* CreateResourceForStaticData(const FetchParameters&, + const ResourceFactory&); Resource* ResourceForBlockedRequest(const FetchParameters&, const ResourceFactory&, ResourceRequestBlockedReason, @@ -394,7 +406,8 @@ class PLATFORM_EXPORT ResourceFetcher void DidLoadResourceFromMemoryCache(Resource*, const ResourceRequest&, - bool is_static_data); + bool is_static_data, + RenderBlockingBehavior); bool ResourceNeedsLoad(Resource*, const FetchParameters&, RevalidationPolicy); @@ -406,7 +419,8 @@ class PLATFORM_EXPORT ResourceFetcher RevalidationPolicy, const FetchParameters&, const ResourceFactory&, - bool is_static_data) const; + bool is_static_data, + bool same_top_frame_site_resource_cached) const; void ScheduleStaleRevalidate(Resource* stale_resource); void RevalidateStaleResource(Resource* stale_resource); @@ -422,6 +436,7 @@ class PLATFORM_EXPORT ResourceFetcher const Member<DetachableConsoleLogger> console_logger_; Member<LoaderFactory> loader_factory_; const Member<ResourceLoadScheduler> scheduler_; + Member<BackForwardCacheLoaderHelper> back_forward_cache_loader_helper_; DocumentResourceMap cached_resources_map_; @@ -437,7 +452,7 @@ class PLATFORM_EXPORT ResourceFetcher HeapVector<Member<Resource>> matched_preloads_; Member<MHTMLArchive> archive_; - TaskRunnerTimer<ResourceFetcher> resource_timing_report_timer_; + HeapTaskRunnerTimer<ResourceFetcher> resource_timing_report_timer_; TaskHandle unused_preloads_timer_; @@ -529,7 +544,8 @@ struct PLATFORM_EXPORT ResourceFetcherInit final { scoped_refptr<base::SingleThreadTaskRunner> freezable_task_runner, scoped_refptr<base::SingleThreadTaskRunner> unfreezable_task_runner, ResourceFetcher::LoaderFactory* loader_factory, - ContextLifecycleNotifier* context_lifecycle_notifier); + ContextLifecycleNotifier* context_lifecycle_notifier, + BackForwardCacheLoaderHelper* back_forward_cache_loader_helper = nullptr); DetachableResourceFetcherProperties* const properties; FetchContext* const context; @@ -546,6 +562,7 @@ struct PLATFORM_EXPORT ResourceFetcherInit final { ResourceLoadScheduler::ThrottleOptionOverride throttle_option_override = ResourceLoadScheduler::ThrottleOptionOverride::kNone; LoadingBehaviorObserver* loading_behavior_observer = nullptr; + BackForwardCacheLoaderHelper* back_forward_cache_loader_helper = nullptr; DISALLOW_COPY_AND_ASSIGN(ResourceFetcherInit); }; 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 index 1bb89327220..ae66f3e7952 100644 --- 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 @@ -32,9 +32,11 @@ #include <memory> #include "base/optional.h" +#include "base/test/scoped_feature_list.h" #include "build/build_config.h" #include "services/network/public/mojom/ip_address_space.mojom-blink.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h" #include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom-blink.h" #include "third_party/blink/public/mojom/security_context/insecure_request_policy.mojom-blink.h" @@ -65,6 +67,7 @@ #include "third_party/blink/renderer/platform/loader/testing/mock_resource_client.h" #include "third_party/blink/renderer/platform/loader/testing/test_loader_factory.h" #include "third_party/blink/renderer/platform/loader/testing/test_resource_fetcher_properties.h" +#include "third_party/blink/renderer/platform/testing/histogram_tester.h" #include "third_party/blink/renderer/platform/testing/mock_context_lifecycle_notifier.h" #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" #include "third_party/blink/renderer/platform/testing/scoped_mocked_url.h" @@ -127,7 +130,8 @@ class ResourceFetcherTest : public testing::Test { const ResourceRequest& request, const ResourceResponse& redirect_response, ResourceType, - const FetchInitiatorInfo&) override { + const FetchInitiatorInfo&, + RenderBlockingBehavior) override { request_ = PartialResourceRequest(request); } void DidChangePriority(uint64_t identifier, @@ -153,7 +157,6 @@ class ResourceFetcherTest : public testing::Test { const ResourceError&, int64_t encoded_data_length, IsInternalRequest is_internal_request) override {} - void EvictFromBackForwardCache(mojom::RendererEvictionReason) override {} const base::Optional<PartialResourceRequest>& GetLastRequest() const { return request_; } @@ -175,7 +178,8 @@ class ResourceFetcherTest : public testing::Test { CreateTaskRunner(), MakeGarbageCollected<TestLoaderFactory>( platform_->GetURLLoaderMockFactory()), - MakeGarbageCollected<MockContextLifecycleNotifier>())); + MakeGarbageCollected<MockContextLifecycleNotifier>(), + nullptr /* back_forward_cache_loader_helper */)); } ResourceFetcher* CreateFetcher( @@ -222,12 +226,14 @@ TEST_F(ResourceFetcherTest, StartLoadAfterFrameDetach) { EXPECT_FALSE(GetMemoryCache()->ResourceForURL(secure_url)); // Start by calling StartLoad() directly, rather than via RequestResource(). - // This shouldn't crash. + // This shouldn't crash. Setting the resource type to image, as StartLoad with + // a single argument is only called on images or fonts. fetcher->StartLoad(RawResource::CreateForTest( - secure_url, SecurityOrigin::CreateUniqueOpaque(), ResourceType::kRaw)); + secure_url, SecurityOrigin::CreateUniqueOpaque(), ResourceType::kImage)); } TEST_F(ResourceFetcherTest, UseExistingResource) { + blink::HistogramTester histogram_tester; auto* fetcher = CreateFetcher(); KURL url("http://127.0.0.1:8000/foo.html"); @@ -248,6 +254,253 @@ TEST_F(ResourceFetcherTest, UseExistingResource) { Resource* new_resource = MockResource::Fetch(fetch_params, fetcher, nullptr); EXPECT_EQ(resource, new_resource); + + // Test histograms. + histogram_tester.ExpectTotalCount("Blink.MemoryCache.RevalidationPolicy.Mock", + 2); + histogram_tester.ExpectBucketCount( + "Blink.MemoryCache.RevalidationPolicy.Mock", + 3 /* RevalidationPolicy::kLoad */, 1); + histogram_tester.ExpectBucketCount( + "Blink.MemoryCache.RevalidationPolicy.Mock", + 0 /* RevalidationPolicy::kUse */, 1); + + // Create a new fetcher and load the same resource. + auto* new_fetcher = CreateFetcher(); + Resource* new_fetcher_resource = + MockResource::Fetch(fetch_params, new_fetcher, nullptr); + EXPECT_EQ(resource, new_fetcher_resource); + histogram_tester.ExpectTotalCount("Blink.MemoryCache.RevalidationPolicy.Mock", + 3); + histogram_tester.ExpectBucketCount( + "Blink.MemoryCache.RevalidationPolicy.Mock", + 3 /* RevalidationPolicy::kLoad */, 1); + histogram_tester.ExpectBucketCount( + "Blink.MemoryCache.RevalidationPolicy.Mock", + 0 /* RevalidationPolicy::kUse */, 2); +} + +TEST_F(ResourceFetcherTest, MemoryCachePerContextUseExistingResource) { + blink::HistogramTester histogram_tester; + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature( + features::kScopeMemoryCachePerContext); + + KURL url("http://127.0.0.1:8000/foo.html"); + ResourceResponse response(url); + response.SetHttpStatusCode(200); + response.SetHttpHeaderField(http_names::kCacheControl, "max-age=3600"); + platform_->GetURLLoaderMockFactory()->RegisterURL( + url, WrappedResourceResponse(response), + test::PlatformTestDataPath(kTestResourceFilename)); + + FetchParameters fetch_params = + FetchParameters::CreateForTest(ResourceRequest(url)); + + auto* fetcher_a = CreateFetcher(); + Resource* resource_a = MockResource::Fetch(fetch_params, fetcher_a, nullptr); + ASSERT_TRUE(resource_a); + platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests(); + EXPECT_TRUE(resource_a->IsLoaded()); + EXPECT_TRUE(GetMemoryCache()->Contains(resource_a)); + + Resource* resource_a1 = MockResource::Fetch(fetch_params, fetcher_a, nullptr); + ASSERT_TRUE(resource_a1); + EXPECT_EQ(resource_a, resource_a1); + + // Test histograms. + histogram_tester.ExpectTotalCount("Blink.MemoryCache.RevalidationPolicy.Mock", + 2); + histogram_tester.ExpectBucketCount( + "Blink.MemoryCache.RevalidationPolicy.Mock", + 3 /* RevalidationPolicy::kLoad */, 1); + histogram_tester.ExpectBucketCount( + "Blink.MemoryCache.RevalidationPolicy.Mock", + 0 /* RevalidationPolicy::kUse */, 1); + + // Create a new fetcher and load the same resource. It should be loaded again. + auto* fetcher_b = CreateFetcher(); + Resource* resource_b = MockResource::Fetch(fetch_params, fetcher_b, nullptr); + EXPECT_NE(resource_a1, resource_b); + ASSERT_TRUE(resource_b); + platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests(); + EXPECT_TRUE(resource_b->IsLoaded()); + EXPECT_TRUE(GetMemoryCache()->Contains(resource_b)); + histogram_tester.ExpectTotalCount("Blink.MemoryCache.RevalidationPolicy.Mock", + 3); + histogram_tester.ExpectBucketCount( + "Blink.MemoryCache.RevalidationPolicy.Mock", + 3 /* RevalidationPolicy::kLoad */, 2); + + // (TODO: crbug.com/1127971) Using the first fetcher now should reuse the same + // resource as was earlier loaded by the same fetcher. + // EXPECT_EQ(resource_a1, resource_a2); + Resource* resource_a2 = MockResource::Fetch(fetch_params, fetcher_a, nullptr); + EXPECT_EQ(resource_b, resource_a2); + histogram_tester.ExpectBucketCount( + "Blink.MemoryCache.RevalidationPolicy.Mock", + 0 /* RevalidationPolicy::kUse */, 2); + + // Using the second fetcher now should reuse the same resource as was earlier + // loaded by the same fetcher. + Resource* resource_b1 = MockResource::Fetch(fetch_params, fetcher_b, nullptr); + EXPECT_EQ(resource_b, resource_b1); + histogram_tester.ExpectBucketCount( + "Blink.MemoryCache.RevalidationPolicy.Mock", + 0 /* RevalidationPolicy::kUse */, 3); +} + +TEST_F(ResourceFetcherTest, MetricsPerTopFrameSite) { + blink::HistogramTester histogram_tester; + + KURL url("http://127.0.0.1:8000/foo.html"); + ResourceResponse response(url); + response.SetHttpStatusCode(200); + response.SetHttpHeaderField(http_names::kCacheControl, "max-age=3600"); + platform_->GetURLLoaderMockFactory()->RegisterURL( + url, WrappedResourceResponse(response), + test::PlatformTestDataPath(kTestResourceFilename)); + + ResourceRequestHead request_head(url); + scoped_refptr<const SecurityOrigin> origin_a = + SecurityOrigin::Create(KURL("https://a.test")); + request_head.SetTopFrameOrigin(origin_a); + request_head.SetRequestorOrigin(origin_a); + FetchParameters fetch_params = + FetchParameters::CreateForTest(ResourceRequest(request_head)); + auto* fetcher_1 = CreateFetcher(); + Resource* resource_1 = MockResource::Fetch(fetch_params, fetcher_1, nullptr); + ASSERT_TRUE(resource_1); + platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests(); + EXPECT_TRUE(resource_1->IsLoaded()); + EXPECT_TRUE(GetMemoryCache()->Contains(resource_1)); + + auto* fetcher_2 = CreateFetcher(); + ResourceRequestHead request_head_2(url); + scoped_refptr<const SecurityOrigin> origin_b = + SecurityOrigin::Create(KURL("https://b.test")); + request_head_2.SetTopFrameOrigin(origin_b); + request_head_2.SetRequestorOrigin(origin_a); + FetchParameters fetch_params_2 = + FetchParameters::CreateForTest(ResourceRequest(request_head_2)); + Resource* resource_2 = + MockResource::Fetch(fetch_params_2, fetcher_2, nullptr); + EXPECT_EQ(resource_1, resource_2); + + // Test histograms. + histogram_tester.ExpectTotalCount( + "Blink.MemoryCache.RevalidationPolicy.PerTopFrameSite.Mock", 0); + + histogram_tester.ExpectTotalCount("Blink.MemoryCache.RevalidationPolicy.Mock", + 2); + + histogram_tester.ExpectBucketCount( + "Blink.MemoryCache.RevalidationPolicy.Mock", + 3 /* RevalidationPolicy::kLoad */, 1); + histogram_tester.ExpectBucketCount( + "Blink.MemoryCache.RevalidationPolicy.Mock", + 0 /* RevalidationPolicy::kUse */, 1); + + // Now load the same resource with origin_b as top-frame site. The + // PerTopFrameSite histogram should be incremented. + auto* fetcher_3 = CreateFetcher(); + ResourceRequestHead request_head_3(url); + scoped_refptr<const SecurityOrigin> foo_origin_b = + SecurityOrigin::Create(KURL("https://foo.b.test")); + request_head_3.SetTopFrameOrigin(foo_origin_b); + request_head_3.SetRequestorOrigin(origin_a); + FetchParameters fetch_params_3 = + FetchParameters::CreateForTest(ResourceRequest(request_head_3)); + Resource* resource_3 = + MockResource::Fetch(fetch_params_2, fetcher_3, nullptr); + EXPECT_EQ(resource_1, resource_3); + histogram_tester.ExpectTotalCount( + "Blink.MemoryCache.RevalidationPolicy.PerTopFrameSite.Mock", 1); + histogram_tester.ExpectBucketCount( + "Blink.MemoryCache.RevalidationPolicy.PerTopFrameSite.Mock", + 0 /* RevalidationPolicy::kUse */, 1); + histogram_tester.ExpectTotalCount("Blink.MemoryCache.RevalidationPolicy.Mock", + 3); + histogram_tester.ExpectBucketCount( + "Blink.MemoryCache.RevalidationPolicy.Mock", + 0 /* RevalidationPolicy::kUse */, 2); +} + +TEST_F(ResourceFetcherTest, MetricsPerTopFrameSiteOpaqueOrigins) { + blink::HistogramTester histogram_tester; + + KURL url("http://127.0.0.1:8000/foo.html"); + ResourceResponse response(url); + response.SetHttpStatusCode(200); + response.SetHttpHeaderField(http_names::kCacheControl, "max-age=3600"); + platform_->GetURLLoaderMockFactory()->RegisterURL( + url, WrappedResourceResponse(response), + test::PlatformTestDataPath(kTestResourceFilename)); + + ResourceRequestHead request_head(url); + scoped_refptr<const SecurityOrigin> origin_a = + SecurityOrigin::Create(KURL("https://a.test")); + scoped_refptr<const SecurityOrigin> opaque_origin1 = + SecurityOrigin::CreateUniqueOpaque(); + request_head.SetTopFrameOrigin(opaque_origin1); + request_head.SetRequestorOrigin(origin_a); + FetchParameters fetch_params = + FetchParameters::CreateForTest(ResourceRequest(request_head)); + auto* fetcher_1 = CreateFetcher(); + Resource* resource_1 = MockResource::Fetch(fetch_params, fetcher_1, nullptr); + ASSERT_TRUE(resource_1); + platform_->GetURLLoaderMockFactory()->ServeAsynchronousRequests(); + EXPECT_TRUE(resource_1->IsLoaded()); + EXPECT_TRUE(GetMemoryCache()->Contains(resource_1)); + + // Create a 2nd opaque top-level origin. + auto* fetcher_2 = CreateFetcher(); + ResourceRequestHead request_head_2(url); + scoped_refptr<const SecurityOrigin> opaque_origin2 = + SecurityOrigin::CreateUniqueOpaque(); + request_head_2.SetTopFrameOrigin(opaque_origin2); + request_head_2.SetRequestorOrigin(origin_a); + FetchParameters fetch_params_2 = + FetchParameters::CreateForTest(ResourceRequest(request_head_2)); + Resource* resource_2 = + MockResource::Fetch(fetch_params_2, fetcher_2, nullptr); + EXPECT_EQ(resource_1, resource_2); + + // Test histograms. + histogram_tester.ExpectTotalCount( + "Blink.MemoryCache.RevalidationPolicy.PerTopFrameSite.Mock", 0); + + histogram_tester.ExpectTotalCount("Blink.MemoryCache.RevalidationPolicy.Mock", + 2); + + histogram_tester.ExpectBucketCount( + "Blink.MemoryCache.RevalidationPolicy.Mock", + 3 /* RevalidationPolicy::kLoad */, 1); + histogram_tester.ExpectBucketCount( + "Blink.MemoryCache.RevalidationPolicy.Mock", + 0 /* RevalidationPolicy::kUse */, 1); + + // Now load the same resource with opaque_origin1 as top-frame site. The + // PerTopFrameSite histogram should be incremented. + auto* fetcher_3 = CreateFetcher(); + ResourceRequestHead request_head_3(url); + request_head_3.SetTopFrameOrigin(opaque_origin2); + request_head_3.SetRequestorOrigin(origin_a); + FetchParameters fetch_params_3 = + FetchParameters::CreateForTest(ResourceRequest(request_head_3)); + Resource* resource_3 = + MockResource::Fetch(fetch_params_2, fetcher_3, nullptr); + EXPECT_EQ(resource_1, resource_3); + histogram_tester.ExpectTotalCount( + "Blink.MemoryCache.RevalidationPolicy.PerTopFrameSite.Mock", 1); + histogram_tester.ExpectBucketCount( + "Blink.MemoryCache.RevalidationPolicy.PerTopFrameSite.Mock", + 0 /* RevalidationPolicy::kUse */, 1); + histogram_tester.ExpectTotalCount("Blink.MemoryCache.RevalidationPolicy.Mock", + 3); + histogram_tester.ExpectBucketCount( + "Blink.MemoryCache.RevalidationPolicy.Mock", + 0 /* RevalidationPolicy::kUse */, 2); } // Verify that the ad bit is copied to WillSendRequest's request when the @@ -413,7 +666,8 @@ class RequestSameResourceOnComplete base::MakeRefCounted<scheduler::FakeTaskRunner>(), base::MakeRefCounted<scheduler::FakeTaskRunner>(), MakeGarbageCollected<TestLoaderFactory>(mock_factory_), - MakeGarbageCollected<MockContextLifecycleNotifier>())); + MakeGarbageCollected<MockContextLifecycleNotifier>(), + nullptr /* back_forward_cache_loader_helper */)); ResourceRequest resource_request2(GetResource()->Url()); resource_request2.SetCacheMode(mojom::FetchCacheMode::kValidateCache); FetchParameters fetch_params2 = @@ -500,7 +754,7 @@ class ServeRequestsOnCompleteClient final void ResponseReceived(Resource*, const ResourceResponse&) override { ASSERT_TRUE(false); } - void SetSerializedCachedMetadata(Resource*, const uint8_t*, size_t) override { + void CachedMetadataReceived(Resource*, mojo_base::BigBuffer) override { ASSERT_TRUE(false); } void DataReceived(Resource*, const char*, size_t) override { @@ -581,7 +835,8 @@ class ScopedMockRedirectRequester { properties->MakeDetachable(), context_, task_runner_, base::MakeRefCounted<scheduler::FakeTaskRunner>(), MakeGarbageCollected<TestLoaderFactory>(mock_factory_), - MakeGarbageCollected<MockContextLifecycleNotifier>())); + MakeGarbageCollected<MockContextLifecycleNotifier>(), + nullptr /* back_forward_cache_loader_helper */)); ResourceRequest resource_request(url); resource_request.SetRequestContext( mojom::blink::RequestContextType::INTERNAL); @@ -1166,13 +1421,13 @@ TEST_F(ResourceFetcherTest, DeprioritizeSubframe) { TEST_F(ResourceFetcherTest, Detach) { DetachableResourceFetcherProperties& properties = MakeGarbageCollected<TestResourceFetcherProperties>()->MakeDetachable(); - auto* const fetcher = - MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit( - properties, MakeGarbageCollected<MockFetchContext>(), - CreateTaskRunner(), CreateTaskRunner(), - MakeGarbageCollected<TestLoaderFactory>( - platform_->GetURLLoaderMockFactory()), - MakeGarbageCollected<MockContextLifecycleNotifier>())); + auto* const fetcher = MakeGarbageCollected<ResourceFetcher>( + ResourceFetcherInit(properties, MakeGarbageCollected<MockFetchContext>(), + CreateTaskRunner(), CreateTaskRunner(), + MakeGarbageCollected<TestLoaderFactory>( + platform_->GetURLLoaderMockFactory()), + MakeGarbageCollected<MockContextLifecycleNotifier>(), + nullptr /* back_forward_cache_loader_helper */)); EXPECT_EQ(&properties, &fetcher->GetProperties()); EXPECT_FALSE(properties.IsDetached()); diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_observer.h b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_observer.h index be55d02976a..3e87f24c44d 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_observer.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_load_observer.h @@ -8,7 +8,7 @@ #include <inttypes.h> #include "base/containers/span.h" -#include "base/util/type_safety/strong_alias.h" +#include "base/types/strong_alias.h" #include "third_party/blink/public/mojom/frame/back_forward_cache_controller.mojom-blink-forward.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" #include "third_party/blink/renderer/platform/loader/fetch/resource.h" @@ -52,7 +52,8 @@ class PLATFORM_EXPORT ResourceLoadObserver const ResourceRequest&, const ResourceResponse& redirect_response, ResourceType, - const FetchInitiatorInfo&) = 0; + const FetchInitiatorInfo&, + RenderBlockingBehavior) = 0; // Called when the priority of the request changes. virtual void DidChangePriority(uint64_t identifier, @@ -89,7 +90,7 @@ class PLATFORM_EXPORT ResourceLoadObserver int64_t decoded_body_length, bool should_report_corb_blocking) = 0; - using IsInternalRequest = util::StrongAlias<class IsInternalRequestTag, bool>; + using IsInternalRequest = base::StrongAlias<class IsInternalRequestTag, bool>; // Called when a request fails. virtual void DidFailLoading(const KURL&, uint64_t identifier, @@ -97,11 +98,6 @@ class PLATFORM_EXPORT ResourceLoadObserver int64_t encoded_data_length, IsInternalRequest) = 0; - // Evict the page from BackForwardCache. Should be called when handling an - // event which can't proceed if the page is in BackForwardCache and can't be - // easily deferred to handle later, for example network redirect handling. - virtual void EvictFromBackForwardCache(mojom::RendererEvictionReason) {} - virtual void Trace(Visitor*) const {} }; 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 index e285beac7c2..94af06e1f6e 100644 --- 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 @@ -83,6 +83,7 @@ network::mojom::blink::LoadTimingInfoPtr ResourceLoadTiming::ToMojo() const { dns_start_, dns_end_, connect_start_, connect_end_, ssl_start_, ssl_end_), send_start_, send_end_, receive_headers_start_, receive_headers_end_, + /*receive_non_informational_headers_start=*/base::TimeTicks::Now(), /*first_early_hints_time=*/base::TimeTicks::Now(), push_start_, push_end_, worker_start_, worker_ready_, worker_fetch_start_, worker_respond_with_settled_); 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 index 984f2281a2b..5cf198700d2 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc @@ -55,12 +55,14 @@ #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/back_forward_cache_utils.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/instrumentation/tracing/trace_event.h" #include "third_party/blink/renderer/platform/instrumentation/tracing/traced_value.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/back_forward_cache_loader_helper.h" #include "third_party/blink/renderer/platform/loader/fetch/cached_metadata_handler.h" #include "third_party/blink/renderer/platform/loader/fetch/console_logger.h" #include "third_party/blink/renderer/platform/loader/fetch/fetch_context.h" @@ -91,20 +93,15 @@ namespace blink { namespace { -enum RequestOutcome { kSuccess, kFail }; +enum class RequestOutcome { kSuccess, kFail }; -std::unique_ptr<TracedValue> EndResourceLoadData(RequestOutcome outcome) { - auto value = std::make_unique<TracedValue>(); +const char* RequestOutcomeToString(RequestOutcome outcome) { switch (outcome) { case RequestOutcome::kSuccess: - value->SetString("outcome", "Success"); - break; + return "Success"; case RequestOutcome::kFail: - value->SetString("outcome", "Fail"); - break; + return "Fail"; } - - return value; } bool IsThrottlableRequestContext(mojom::blink::RequestContextType context) { @@ -190,6 +187,28 @@ SchedulingPolicy::Feature GetFeatureFromRequestContextType( } } +void LogCnameAliasMetrics(const CnameAliasMetricInfo& info) { + UMA_HISTOGRAM_BOOLEAN("SubresourceFilter.CnameAlias.Renderer.HadAliases", + info.has_aliases); + + if (info.has_aliases) { + UMA_HISTOGRAM_BOOLEAN( + "SubresourceFilter.CnameAlias.Renderer.WasAdTaggedBasedOnAlias", + info.was_ad_tagged_based_on_alias); + UMA_HISTOGRAM_BOOLEAN( + "SubresourceFilter.CnameAlias.Renderer.WasBlockedBasedOnAlias", + info.was_blocked_based_on_alias); + UMA_HISTOGRAM_COUNTS_1000( + "SubresourceFilter.CnameAlias.Renderer.ListLength", info.list_length); + UMA_HISTOGRAM_COUNTS_1000( + "SubresourceFilter.CnameAlias.Renderer.InvalidCount", + info.invalid_count); + UMA_HISTOGRAM_COUNTS_1000( + "SubresourceFilter.CnameAlias.Renderer.RedundantCount", + info.redundant_count); + } +} + } // namespace // CodeCacheRequest handles the requests to fetch data from code cache. @@ -419,11 +438,11 @@ ResourceLoader::ResourceLoader(ResourceFetcher* fetcher, // Only when this feature is turned on and the loading tasks keep being // processed and the data is queued up on the renderer, a page can stay in // BackForwardCache with network requests. - if (!base::FeatureList::IsEnabled(features::kLoadingTasksUnfreezable)) { + if (!IsInflightNetworkRequestBackForwardCacheSupportEnabled()) { feature_handle_for_scheduler_ = frame_or_worker_scheduler->RegisterFeature( GetFeatureFromRequestContextType(request_context), - {SchedulingPolicy::RecordMetricsForBackForwardCache()}); + {SchedulingPolicy::DisableBackForwardCache()}); } } } @@ -439,6 +458,7 @@ void ResourceLoader::Trace(Visitor* visitor) const { visitor->Trace(resource_); visitor->Trace(response_body_loader_); visitor->Trace(data_pipe_completion_notifier_); + visitor->Trace(cancel_timer_); ResourceLoadSchedulerClient::Trace(visitor); } @@ -525,8 +545,8 @@ void ResourceLoader::DidStartLoadingResponseBodyInternal( DCHECK(!response_body_loader_); ResponseBodyLoaderClient& response_body_loader_client = *this; response_body_loader_ = MakeGarbageCollected<ResponseBodyLoader>( - bytes_consumer, response_body_loader_client, - task_runner_for_body_loader_); + bytes_consumer, response_body_loader_client, task_runner_for_body_loader_, + fetcher_->GetBackForwardCacheLoaderHelper()); resource_->ResponseBodyReceived(*response_body_loader_, task_runner_for_body_loader_); if (response_body_loader_->IsDrained()) { @@ -539,6 +559,8 @@ void ResourceLoader::DidStartLoadingResponseBodyInternal( } void ResourceLoader::Run() { + // TODO(crbug.com/1169032): Manage cookies' capability control here for the + // Prerender2. StartWith(resource_->GetResourceRequest()); } @@ -787,8 +809,9 @@ bool ResourceLoader::WillFollowRedirect( reporting_disposition, new_request->GetRedirectInfo()); - if (Context().CalculateIfAdSubresource(*new_request, resource_type, - options.initiator_info)) + if (Context().CalculateIfAdSubresource( + *new_request, base::nullopt /* alias_url */, resource_type, + options.initiator_info)) new_request->SetIsAdResource(); if (blocked_reason) { @@ -823,7 +846,8 @@ bool ResourceLoader::WillFollowRedirect( if (auto* observer = fetcher_->GetResourceLoadObserver()) { observer->WillSendRequest(resource_->InspectorId(), *new_request, redirect_response, resource_->GetType(), - options.initiator_info); + options.initiator_info, + initial_request.GetRenderBlockingBehavior()); } // First-party cookie logic moved from DocumentLoader in Blink to @@ -858,7 +882,16 @@ void ResourceLoader::DidReceiveCachedMetadata(mojo_base::BigBuffer data) { } blink::mojom::CodeCacheType ResourceLoader::GetCodeCacheType() const { - return Resource::ResourceTypeToCodeCacheType(resource_->GetType()); + const auto& request = resource_->GetResourceRequest(); + if (request.GetRequestDestination() == + network::mojom::RequestDestination::kEmpty) { + // For requests initiated by the fetch function, we use code cache for + // WASM compiled code. + return mojom::blink::CodeCacheType::kWebAssembly; + } else { + // Otherwise, we use code cache for scripting. + return mojom::blink::CodeCacheType::kJavascript; + } } void ResourceLoader::SendCachedCodeToResource(mojo_base::BigBuffer data) { @@ -949,6 +982,15 @@ void ResourceLoader::DidReceiveResponseInternal( return; } + // Redirect information for possible post-request checks below. + const base::Optional<ResourceRequest::RedirectInfo>& previous_redirect_info = + request.GetRedirectInfo(); + const KURL& original_url = previous_redirect_info + ? previous_redirect_info->original_url + : request.Url(); + const ResourceRequest::RedirectInfo redirect_info(original_url, + request.Url()); + if (response.WasFetchedViaServiceWorker()) { // Run post-request CSP checks. This is the "Should response to request be // blocked by Content Security Policy?" algorithm in the CSP specification: @@ -968,16 +1010,10 @@ void ResourceLoader::DidReceiveResponseInternal( // checks as a first-class concept instead of just reusing the functions for // pre-request checks, and consider running the checks regardless of service // worker interception. - const KURL& response_url = response.ResponseUrl(); - const base::Optional<ResourceRequest::RedirectInfo>& - previous_redirect_info = request.GetRedirectInfo(); - const KURL& original_url = previous_redirect_info - ? previous_redirect_info->original_url - : request.Url(); - const ResourceRequest::RedirectInfo redirect_info(original_url, - request.Url()); + // // CanRequest() below only checks enforced policies: check report-only // here to ensure violations are sent. + const KURL& response_url = response.ResponseUrl(); Context().CheckCSPForRequest( request_context, request_destination, response_url, options, ReportingDisposition::kReport, original_url, @@ -994,6 +1030,18 @@ void ResourceLoader::DidReceiveResponseInternal( } } + if (base::FeatureList::IsEnabled( + features::kSendCnameAliasesToSubresourceFilterFromRenderer)) { + CnameAliasMetricInfo info; + bool should_block = ShouldBlockRequestBasedOnSubresourceFilterDnsAliasCheck( + response.DnsAliases(), request.Url(), original_url, resource_type, + initial_request, options, redirect_info, &info); + LogCnameAliasMetrics(info); + + if (should_block) + return; + } + // A response should not serve partial content if it was not requested via a // Range header: https://fetch.spec.whatwg.org/#main-fetch if (response.GetType() == network::mojom::FetchResponseType::kOpaque && @@ -1029,12 +1077,12 @@ void ResourceLoader::DidReceiveResponseInternal( if (response.CacheControlContainsNoCache()) { frame_or_worker_scheduler->RegisterStickyFeature( SchedulingPolicy::Feature::kSubresourceHasCacheControlNoCache, - {SchedulingPolicy::RecordMetricsForBackForwardCache()}); + {SchedulingPolicy::DisableBackForwardCache()}); } if (response.CacheControlContainsNoStore()) { frame_or_worker_scheduler->RegisterStickyFeature( SchedulingPolicy::Feature::kSubresourceHasCacheControlNoStore, - {SchedulingPolicy::RecordMetricsForBackForwardCache()}); + {SchedulingPolicy::DisableBackForwardCache()}); } } @@ -1098,7 +1146,7 @@ void ResourceLoader::DidFinishLoadingFirstPartInMultipart() { TRACE_DISABLED_BY_DEFAULT("network"), "ResourceLoad", TRACE_ID_WITH_SCOPE("BlinkResourceID", TRACE_ID_LOCAL(resource_->InspectorId())), - "endData", EndResourceLoadData(RequestOutcome::kSuccess)); + "outcome", RequestOutcomeToString(RequestOutcome::kSuccess)); fetcher_->HandleLoaderFinish(resource_.Get(), base::TimeTicks(), ResourceFetcher::kDidFinishFirstPartInMultipart, @@ -1142,7 +1190,7 @@ void ResourceLoader::DidFinishLoading(base::TimeTicks response_end_time, TRACE_DISABLED_BY_DEFAULT("network"), "ResourceLoad", TRACE_ID_WITH_SCOPE("BlinkResourceID", TRACE_ID_LOCAL(resource_->InspectorId())), - "endData", EndResourceLoadData(RequestOutcome::kSuccess)); + "outcome", RequestOutcomeToString(RequestOutcome::kSuccess)); fetcher_->HandleLoaderFinish( resource_.Get(), response_end_time, ResourceFetcher::kDidFinishLoading, @@ -1209,7 +1257,7 @@ void ResourceLoader::HandleError(const ResourceError& error) { TRACE_DISABLED_BY_DEFAULT("network"), "ResourceLoad", TRACE_ID_WITH_SCOPE("BlinkResourceID", TRACE_ID_LOCAL(resource_->InspectorId())), - "endData", EndResourceLoadData(RequestOutcome::kFail)); + "outcome", RequestOutcomeToString(RequestOutcome::kFail)); // Set Now() as the response time, in case a more accurate one wasn't set in // DidFinishLoading or DidFail. This is important for error cases that don't @@ -1222,11 +1270,6 @@ void ResourceLoader::HandleError(const ResourceError& error) { inflight_keepalive_bytes_); } -void ResourceLoader::EvictFromBackForwardCache( - mojom::RendererEvictionReason reason) { - fetcher_->EvictFromBackForwardCache(reason); -} - void ResourceLoader::RequestSynchronously(const ResourceRequestHead& request) { DCHECK(loader_); DCHECK_EQ(request.Priority(), ResourceLoadPriority::kHighest); @@ -1507,4 +1550,73 @@ void ResourceLoader::HandleDataUrl() { false /* should_report_corb_blocking */); } +bool ResourceLoader::ShouldBlockRequestBasedOnSubresourceFilterDnsAliasCheck( + const Vector<String>& dns_aliases, + const KURL& request_url, + const KURL& original_url, + ResourceType resource_type, + const ResourceRequestHead& initial_request, + const ResourceLoaderOptions& options, + const ResourceRequest::RedirectInfo redirect_info, + CnameAliasMetricInfo* out_metric_info) { + DCHECK(out_metric_info); + + // Look for CNAME aliases, and if any are found, run SubresourceFilter + // checks on them to perform resource-blocking and ad-tagging based on the + // aliases: if any one of the aliases is on the denylist, then the + // request will be deemed on the denylist and treated accordingly (blocked + // and/or tagged). + out_metric_info->has_aliases = !dns_aliases.IsEmpty(); + out_metric_info->list_length = dns_aliases.size(); + + // If there are no aliases, we have no reason to block based on them. + if (!out_metric_info->has_aliases) + return false; + + // CNAME aliases were found, and so the SubresourceFilter must be + // consulted for each one. + // Create a copy of the request URL. We will swap out the host below. + KURL alias_url = request_url; + + for (const String& alias : dns_aliases) { + alias_url.SetHost(alias); + + // The SubresourceFilter only performs nontrivial matches for + // valid URLs. Skip sending this alias if it's invalid. + if (!alias_url.IsValid()) { + out_metric_info->invalid_count++; + continue; + } + + // Do not perform a SubresourceFilter check on an `alias_url` that matches + // the requested URL (or, inclusively, the original URL in the case of + // redirects). + if (alias_url == original_url || alias_url == request_url) { + out_metric_info->redundant_count++; + continue; + } + + base::Optional<ResourceRequestBlockedReason> blocked_reason = + Context().CanRequestBasedOnSubresourceFilterOnly( + resource_type, ResourceRequest(initial_request), alias_url, options, + ReportingDisposition::kReport, redirect_info); + if (blocked_reason) { + HandleError(ResourceError::CancelledDueToAccessCheckError( + alias_url, blocked_reason.value())); + out_metric_info->was_blocked_based_on_alias = true; + return true; + } + + if (!resource_->GetResourceRequest().IsAdResource() && + Context().CalculateIfAdSubresource(resource_->GetResourceRequest(), + alias_url, resource_type, + options.initiator_info)) { + resource_->SetIsAdResource(); + out_metric_info->was_ad_tagged_based_on_alias = true; + } + } + + return false; +} + } // 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 index d53b0facf20..4498155f5be 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader.h @@ -31,6 +31,7 @@ #include <memory> #include "base/containers/span.h" +#include "base/feature_list.h" #include "base/gtest_prod_util.h" #include "base/single_thread_task_runner.h" #include "mojo/public/cpp/base/big_buffer.h" @@ -58,6 +59,17 @@ class ResourceError; class ResourceFetcher; class ResponseBodyLoader; +// Struct for keeping variables used in recording CNAME alias metrics bundled +// together. +struct CnameAliasMetricInfo { + bool has_aliases = false; + bool was_ad_tagged_based_on_alias = false; + bool was_blocked_based_on_alias = false; + int list_length = 0; + int invalid_count = 0; + int redundant_count = 0; +}; + // 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 @@ -140,7 +152,6 @@ class PLATFORM_EXPORT ResourceLoader final int64_t encoded_data_length, int64_t encoded_body_length, int64_t decoded_body_length) override; - void EvictFromBackForwardCache(mojom::RendererEvictionReason) override; blink::mojom::CodeCacheType GetCodeCacheType() const; void SendCachedCodeToResource(mojo_base::BigBuffer data); @@ -205,6 +216,19 @@ class PLATFORM_EXPORT ResourceLoader final // Processes Data URL in ResourceLoader instead of using |loader_|. void HandleDataUrl(); + // If enabled, performs SubresourceFilter checks for any DNS aliases found for + // the requested URL, which may result in ad-tagging the ResourceRequest. + // Returns true if the request should be blocked based on these checks. + bool ShouldBlockRequestBasedOnSubresourceFilterDnsAliasCheck( + const Vector<String>& dns_aliases, + const KURL& request_url, + const KURL& original_url, + ResourceType resource_type, + const ResourceRequestHead& initial_request, + const ResourceLoaderOptions& options, + const ResourceRequest::RedirectInfo redirect_info, + CnameAliasMetricInfo* out_metric_info); + std::unique_ptr<WebURLLoader> loader_; ResourceLoadScheduler::ClientId scheduler_client_id_; Member<ResourceFetcher> fetcher_; @@ -247,7 +271,7 @@ class PLATFORM_EXPORT ResourceLoader final // HandleDataURL(). bool defers_handling_data_url_ = false; - TaskRunnerTimer<ResourceLoader> cancel_timer_; + HeapTaskRunnerTimer<ResourceLoader> cancel_timer_; FrameScheduler::SchedulingAffectingFeatureHandle feature_handle_for_scheduler_; diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_defer_loading_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_defer_loading_test.cc index b3755010517..a4f285a969d 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_defer_loading_test.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_loader_defer_loading_test.cc @@ -118,7 +118,8 @@ class DeferTestLoaderFactory final : public ResourceFetcher::LoaderFactory { const ResourceRequest& request, const ResourceLoaderOptions& options, scoped_refptr<base::SingleThreadTaskRunner> freezable_task_runner, - scoped_refptr<base::SingleThreadTaskRunner> unfreezable_task_runner) + scoped_refptr<base::SingleThreadTaskRunner> unfreezable_task_runner, + WebBackForwardCacheLoaderHelper back_forward_cache_loader_helper) override { return std::make_unique<TestWebURLLoader>(defers_flag_); } @@ -159,7 +160,8 @@ class ResourceLoaderDefersLoadingTest : public testing::Test { base::MakeRefCounted<scheduler::FakeTaskRunner>(), MakeGarbageCollected<DeferTestLoaderFactory>( &web_url_loader_defers_, process_code_cache_request_callback_), - MakeGarbageCollected<MockContextLifecycleNotifier>())); + MakeGarbageCollected<MockContextLifecycleNotifier>(), + nullptr /* back_forward_cache_loader_helper */)); } void SetCodeCacheProcessFunction(ProcessCodeCacheRequestCallback callback) { 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 index e404cafa3ba..4b6952bad01 100644 --- 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 @@ -32,7 +32,7 @@ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_RESOURCE_LOADER_OPTIONS_H_ #include "base/memory/scoped_refptr.h" -#include "base/util/type_safety/strong_alias.h" +#include "base/types/strong_alias.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "services/network/public/mojom/content_security_policy.mojom-blink-forward.h" #include "services/network/public/mojom/url_loader_factory.mojom-blink-forward.h" @@ -70,7 +70,7 @@ enum CacheAwareLoadingEnabled : uint8_t { // When true, a response is blocked unless it has // cross-origin-embedder-policy: require-corp. using RejectCoepUnsafeNone = - util::StrongAlias<class RejectCoepUnsafeNoneTag, bool>; + base::StrongAlias<class RejectCoepUnsafeNoneTag, bool>; // This class is thread-bound. Do not copy/pass an instance across threads. struct PLATFORM_EXPORT ResourceLoaderOptions { 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 index 615e434eff9..5458967f87e 100644 --- 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 @@ -7,11 +7,15 @@ #include <string> #include <utility> +#include "base/test/metrics/histogram_tester.h" +#include "base/test/scoped_feature_list.h" #include "mojo/public/c/system/data_pipe.h" #include "services/network/public/mojom/fetch_api.mojom-blink.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h" #include "third_party/blink/public/platform/resource_load_info_notifier_wrapper.h" +#include "third_party/blink/public/platform/web_back_forward_cache_loader_helper.h" #include "third_party/blink/public/platform/web_url_loader.h" #include "third_party/blink/public/platform/web_url_loader_factory.h" #include "third_party/blink/public/platform/web_url_request_extra_data.h" @@ -29,6 +33,19 @@ namespace blink { +const char kCnameAliasHadAliasesHistogram[] = + "SubresourceFilter.CnameAlias.Renderer.HadAliases"; +const char kCnameAliasIsInvalidCountHistogram[] = + "SubresourceFilter.CnameAlias.Renderer.InvalidCount"; +const char kCnameAliasIsRedundantCountHistogram[] = + "SubresourceFilter.CnameAlias.Renderer.RedundantCount"; +const char kCnameAliasListLengthHistogram[] = + "SubresourceFilter.CnameAlias.Renderer.ListLength"; +const char kCnameAliasWasAdTaggedHistogram[] = + "SubresourceFilter.CnameAlias.Renderer.WasAdTaggedBasedOnAlias"; +const char kCnameAliasWasBlockedHistogram[] = + "SubresourceFilter.CnameAlias.Renderer.WasBlockedBasedOnAlias"; + class ResourceLoaderTest : public testing::Test { DISALLOW_COPY_AND_ASSIGN(ResourceLoaderTest); @@ -64,7 +81,8 @@ class ResourceLoaderTest : public testing::Test { const ResourceRequest& request, const ResourceLoaderOptions& options, scoped_refptr<base::SingleThreadTaskRunner> freezable_task_runner, - scoped_refptr<base::SingleThreadTaskRunner> unfreezable_task_runner) + scoped_refptr<base::SingleThreadTaskRunner> unfreezable_task_runner, + WebBackForwardCacheLoaderHelper back_forward_cache_loader_helper) override { return std::make_unique<NoopWebURLLoader>( std::move(freezable_task_runner)); @@ -78,6 +96,16 @@ class ResourceLoaderTest : public testing::Test { return base::MakeRefCounted<scheduler::FakeTaskRunner>(); } + ResourceFetcher* MakeResourceFetcher( + TestResourceFetcherProperties* properties, + FetchContext* context) { + return MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit( + properties->MakeDetachable(), context, CreateTaskRunner(), + CreateTaskRunner(), MakeGarbageCollected<NoopLoaderFactory>(), + MakeGarbageCollected<MockContextLifecycleNotifier>(), + nullptr /* back_forward_cache_loader_helper */)); + } + private: class NoopWebURLLoader final : public WebURLLoader { public: @@ -141,10 +169,7 @@ std::ostream& operator<<(std::ostream& o, const ResourceLoaderTest::From& f) { TEST_F(ResourceLoaderTest, LoadResponseBody) { auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>(); FetchContext* context = MakeGarbageCollected<MockFetchContext>(); - auto* fetcher = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit( - properties->MakeDetachable(), context, CreateTaskRunner(), - CreateTaskRunner(), MakeGarbageCollected<NoopLoaderFactory>(), - MakeGarbageCollected<MockContextLifecycleNotifier>())); + auto* fetcher = MakeResourceFetcher(properties, context); KURL url("https://www.example.com/"); ResourceRequest request(url); @@ -165,7 +190,7 @@ TEST_F(ResourceLoaderTest, LoadResponseBody) { options.element_num_bytes = 1; options.capacity_num_bytes = 3; - MojoResult result = CreateDataPipe(&options, &producer, &consumer); + MojoResult result = CreateDataPipe(&options, producer, consumer); ASSERT_EQ(result, MOJO_RESULT_OK); loader->DidReceiveResponse(WrappedResourceResponse(response)); @@ -206,10 +231,7 @@ TEST_F(ResourceLoaderTest, LoadResponseBody) { TEST_F(ResourceLoaderTest, LoadDataURL_AsyncAndNonStream) { auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>(); FetchContext* context = MakeGarbageCollected<MockFetchContext>(); - auto* fetcher = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit( - properties->MakeDetachable(), context, CreateTaskRunner(), - CreateTaskRunner(), MakeGarbageCollected<NoopLoaderFactory>(), - MakeGarbageCollected<MockContextLifecycleNotifier>())); + auto* fetcher = MakeResourceFetcher(properties, context); // Fetch a data url. KURL url("data:text/plain,Hello%20World!"); @@ -260,10 +282,7 @@ class TestRawResourceClient final TEST_F(ResourceLoaderTest, LoadDataURL_AsyncAndStream) { auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>(); FetchContext* context = MakeGarbageCollected<MockFetchContext>(); - auto* fetcher = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit( - properties->MakeDetachable(), context, CreateTaskRunner(), - CreateTaskRunner(), MakeGarbageCollected<NoopLoaderFactory>(), - MakeGarbageCollected<MockContextLifecycleNotifier>())); + auto* fetcher = MakeResourceFetcher(properties, context); scheduler::FakeTaskRunner* task_runner = static_cast<scheduler::FakeTaskRunner*>(fetcher->GetTaskRunner().get()); @@ -300,10 +319,7 @@ TEST_F(ResourceLoaderTest, LoadDataURL_AsyncAndStream) { TEST_F(ResourceLoaderTest, LoadDataURL_AsyncEmptyData) { auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>(); FetchContext* context = MakeGarbageCollected<MockFetchContext>(); - auto* fetcher = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit( - properties->MakeDetachable(), context, CreateTaskRunner(), - CreateTaskRunner(), MakeGarbageCollected<NoopLoaderFactory>(), - MakeGarbageCollected<MockContextLifecycleNotifier>())); + auto* fetcher = MakeResourceFetcher(properties, context); // Fetch an empty data url. KURL url("data:text/html,"); @@ -324,10 +340,7 @@ TEST_F(ResourceLoaderTest, LoadDataURL_AsyncEmptyData) { TEST_F(ResourceLoaderTest, LoadDataURL_Sync) { auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>(); FetchContext* context = MakeGarbageCollected<MockFetchContext>(); - auto* fetcher = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit( - properties->MakeDetachable(), context, CreateTaskRunner(), - CreateTaskRunner(), MakeGarbageCollected<NoopLoaderFactory>(), - MakeGarbageCollected<MockContextLifecycleNotifier>())); + auto* fetcher = MakeResourceFetcher(properties, context); // Fetch a data url synchronously. KURL url("data:text/plain,Hello%20World!"); @@ -350,10 +363,7 @@ TEST_F(ResourceLoaderTest, LoadDataURL_Sync) { TEST_F(ResourceLoaderTest, LoadDataURL_SyncEmptyData) { auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>(); FetchContext* context = MakeGarbageCollected<MockFetchContext>(); - auto* fetcher = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit( - properties->MakeDetachable(), context, CreateTaskRunner(), - CreateTaskRunner(), MakeGarbageCollected<NoopLoaderFactory>(), - MakeGarbageCollected<MockContextLifecycleNotifier>())); + auto* fetcher = MakeResourceFetcher(properties, context); // Fetch an empty data url synchronously. KURL url("data:text/html,"); @@ -372,10 +382,7 @@ TEST_F(ResourceLoaderTest, LoadDataURL_SyncEmptyData) { TEST_F(ResourceLoaderTest, LoadDataURL_DefersAsyncAndNonStream) { auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>(); FetchContext* context = MakeGarbageCollected<MockFetchContext>(); - auto* fetcher = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit( - properties->MakeDetachable(), context, CreateTaskRunner(), - CreateTaskRunner(), MakeGarbageCollected<NoopLoaderFactory>(), - MakeGarbageCollected<MockContextLifecycleNotifier>())); + auto* fetcher = MakeResourceFetcher(properties, context); scheduler::FakeTaskRunner* task_runner = static_cast<scheduler::FakeTaskRunner*>(fetcher->GetTaskRunner().get()); @@ -419,10 +426,7 @@ TEST_F(ResourceLoaderTest, LoadDataURL_DefersAsyncAndNonStream) { TEST_F(ResourceLoaderTest, LoadDataURL_DefersAsyncAndStream) { auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>(); FetchContext* context = MakeGarbageCollected<MockFetchContext>(); - auto* fetcher = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit( - properties->MakeDetachable(), context, CreateTaskRunner(), - CreateTaskRunner(), MakeGarbageCollected<NoopLoaderFactory>(), - MakeGarbageCollected<MockContextLifecycleNotifier>())); + auto* fetcher = MakeResourceFetcher(properties, context); scheduler::FakeTaskRunner* task_runner = static_cast<scheduler::FakeTaskRunner*>(fetcher->GetTaskRunner().get()); @@ -493,10 +497,7 @@ class ResourceLoaderIsolatedCodeCacheTest : public ResourceLoaderTest { auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>(origin); FetchContext* context = MakeGarbageCollected<MockFetchContext>(); - auto* fetcher = MakeGarbageCollected<ResourceFetcher>(ResourceFetcherInit( - properties->MakeDetachable(), context, CreateTaskRunner(), - CreateTaskRunner(), MakeGarbageCollected<NoopLoaderFactory>(), - MakeGarbageCollected<MockContextLifecycleNotifier>())); + auto* fetcher = MakeResourceFetcher(properties, context); ResourceRequest request; request.SetUrl(foo_url_); request.SetRequestContext(mojom::blink::RequestContextType::FETCH); @@ -554,4 +555,286 @@ TEST_F(ResourceLoaderIsolatedCodeCacheTest, CacheResponseFromServiceWorker) { EXPECT_EQ(false, LoadAndCheckIsolatedCodeCache(response)); } +class ResourceLoaderSubresourceFilterCnameAliasTest + : public ResourceLoaderTest { + public: + ResourceLoaderSubresourceFilterCnameAliasTest() = default; + ~ResourceLoaderSubresourceFilterCnameAliasTest() override = default; + + void SetUp() override { + feature_list_.InitAndEnableFeature( + features::kSendCnameAliasesToSubresourceFilterFromRenderer); + ResourceLoaderTest::SetUp(); + } + + base::HistogramTester* histogram_tester() { return &histogram_tester_; } + + void SetMockSubresourceFilterBlockLists(Vector<String> blocked_urls, + Vector<String> tagged_urls) { + blocked_urls_ = blocked_urls; + tagged_urls_ = tagged_urls; + } + + Resource* CreateResource(ResourceRequest request) { + FetchParameters params = FetchParameters::CreateForTest(std::move(request)); + auto* fetcher = MakeResourceFetcherWithMockSubresourceFilter(); + return RawResource::Fetch(params, fetcher, nullptr); + } + + void GiveResponseToLoader(ResourceResponse response, ResourceLoader* loader) { + CreateMojoDataPipe(); + loader->DidReceiveResponse(WrappedResourceResponse(response)); + } + + protected: + FetchContext* MakeFetchContextWithMockSubresourceFilter( + Vector<String> blocked_urls, + Vector<String> tagged_urls) { + auto* context = MakeGarbageCollected<MockFetchContext>(); + context->set_blocked_urls(blocked_urls); + context->set_tagged_urls(tagged_urls); + return context; + } + + ResourceFetcher* MakeResourceFetcherWithMockSubresourceFilter() { + auto* properties = MakeGarbageCollected<TestResourceFetcherProperties>(); + FetchContext* context = + MakeFetchContextWithMockSubresourceFilter(blocked_urls_, tagged_urls_); + return MakeResourceFetcher(properties, context); + } + + void CreateMojoDataPipe() { + mojo::ScopedDataPipeProducerHandle producer; + mojo::ScopedDataPipeConsumerHandle consumer; + MojoCreateDataPipeOptions options; + options.struct_size = sizeof(MojoCreateDataPipeOptions); + options.flags = MOJO_CREATE_DATA_PIPE_FLAG_NONE; + options.element_num_bytes = 1; + options.capacity_num_bytes = 3; + + MojoResult result = CreateDataPipe(&options, producer, consumer); + ASSERT_EQ(result, MOJO_RESULT_OK); + } + + void ExpectHistogramsMatching(CnameAliasMetricInfo info) { + histogram_tester()->ExpectUniqueSample(kCnameAliasHadAliasesHistogram, + info.has_aliases, 1); + + if (info.has_aliases) { + histogram_tester()->ExpectUniqueSample(kCnameAliasWasAdTaggedHistogram, + info.was_ad_tagged_based_on_alias, + 1); + histogram_tester()->ExpectUniqueSample( + kCnameAliasWasBlockedHistogram, info.was_blocked_based_on_alias, 1); + histogram_tester()->ExpectUniqueSample(kCnameAliasListLengthHistogram, + info.list_length, 1); + histogram_tester()->ExpectUniqueSample(kCnameAliasIsInvalidCountHistogram, + info.invalid_count, 1); + histogram_tester()->ExpectUniqueSample( + kCnameAliasIsRedundantCountHistogram, info.redundant_count, 1); + } + } + + private: + base::test::ScopedFeatureList feature_list_; + base::HistogramTester histogram_tester_; + Vector<String> blocked_urls_; + Vector<String> tagged_urls_; +}; + +TEST_F(ResourceLoaderSubresourceFilterCnameAliasTest, + DnsAliasesCheckedBySubresourceFilterDisallowed_TaggedAndBlocked) { + // Set the blocklists: the first for blocking, the second for ad-tagging. + Vector<String> blocked_urls = {"https://bad-ad.com/some_path.html"}; + Vector<String> tagged_urls = {"https://ad.com/some_path.html"}; + SetMockSubresourceFilterBlockLists(blocked_urls, tagged_urls); + + // Create the request. + KURL url("https://www.example.com/some_path.html"); + ResourceRequest request(url); + request.SetRequestContext(mojom::blink::RequestContextType::FETCH); + + // Create the resource and loader. + Resource* resource = CreateResource(std::move(request)); + ResourceLoader* loader = resource->Loader(); + + // Create the response. + ResourceResponse response(url); + response.SetHttpStatusCode(200); + + // Set the CNAME aliases. + Vector<String> aliases({"ad.com", "bad-ad.com", "alias3.com"}); + response.SetDnsAliases(aliases); + + // Give the response to the loader. + GiveResponseToLoader(response, loader); + + // Test the histograms to verify that the CNAME aliases were detected. + // Expect that the resource was tagged as a ad, due to first alias. + // Expect that the resource was blocked, due to second alias. + CnameAliasMetricInfo info = {.has_aliases = true, + .was_ad_tagged_based_on_alias = true, + .was_blocked_based_on_alias = true, + .list_length = 3, + .invalid_count = 0, + .redundant_count = 0}; + + ExpectHistogramsMatching(info); +} + +TEST_F(ResourceLoaderSubresourceFilterCnameAliasTest, + DnsAliasesCheckedBySubresourceFilterDisallowed_BlockedOnly) { + // Set the blocklists: the first for blocking, the second for ad-tagging. + Vector<String> blocked_urls = {"https://bad-ad.com/some_path.html"}; + Vector<String> tagged_urls = {}; + SetMockSubresourceFilterBlockLists(blocked_urls, tagged_urls); + + // Create the request. + KURL url("https://www.example.com/some_path.html"); + ResourceRequest request(url); + request.SetRequestContext(mojom::blink::RequestContextType::FETCH); + + // Create the resource and loader. + Resource* resource = CreateResource(std::move(request)); + ResourceLoader* loader = resource->Loader(); + + // Create the response. + ResourceResponse response(url); + response.SetHttpStatusCode(200); + + // Set the CNAME aliases. + Vector<String> aliases({"ad.com", "bad-ad.com", "alias3.com"}); + response.SetDnsAliases(aliases); + + // Give the response to the loader. + GiveResponseToLoader(response, loader); + + // Test the histograms to verify that the CNAME aliases were detected. + // Expect that the resource was blocked, due to second alias. + CnameAliasMetricInfo info = {.has_aliases = true, + .was_ad_tagged_based_on_alias = false, + .was_blocked_based_on_alias = true, + .list_length = 3, + .invalid_count = 0, + .redundant_count = 0}; + + ExpectHistogramsMatching(info); +} + +TEST_F(ResourceLoaderSubresourceFilterCnameAliasTest, + DnsAliasesCheckedBySubresourceFilterDisallowed_TaggedOnly) { + // Set the blocklists: the first for blocking, the second for ad-tagging. + Vector<String> blocked_urls = {}; + Vector<String> tagged_urls = {"https://bad-ad.com/some_path.html"}; + SetMockSubresourceFilterBlockLists(blocked_urls, tagged_urls); + + // Create the request. + KURL url("https://www.example.com/some_path.html"); + ResourceRequest request(url); + request.SetRequestContext(mojom::blink::RequestContextType::FETCH); + + // Create the resource and loader. + Resource* resource = CreateResource(std::move(request)); + ResourceLoader* loader = resource->Loader(); + + // Create the response. + ResourceResponse response(url); + response.SetHttpStatusCode(200); + + // Set the CNAME aliases. + Vector<String> aliases({"ad.com", "", "alias3.com", "bad-ad.com"}); + response.SetDnsAliases(aliases); + + // Give the response to the loader. + GiveResponseToLoader(response, loader); + + // Test the histograms to verify that the CNAME aliases were detected. + // Expect that the resource was tagged, due to fourth alias. + // Expect that the invalid empty alias is counted as such. + CnameAliasMetricInfo info = {.has_aliases = true, + .was_ad_tagged_based_on_alias = true, + .was_blocked_based_on_alias = false, + .list_length = 4, + .invalid_count = 1, + .redundant_count = 0}; + + ExpectHistogramsMatching(info); +} + +TEST_F(ResourceLoaderSubresourceFilterCnameAliasTest, + DnsAliasesCheckedBySubresourceFilterAllowed_NotBlockedOrTagged) { + // Set the blocklists: the first for blocking, the second for ad-tagging. + Vector<String> blocked_urls = {}; + Vector<String> tagged_urls = {}; + SetMockSubresourceFilterBlockLists(blocked_urls, tagged_urls); + + // Create the request. + KURL url("https://www.example.com/some_path.html"); + ResourceRequest request(url); + request.SetRequestContext(mojom::blink::RequestContextType::FETCH); + + // Create the resource and loader. + Resource* resource = CreateResource(std::move(request)); + ResourceLoader* loader = resource->Loader(); + + // Create the response. + ResourceResponse response(url); + response.SetHttpStatusCode(200); + + // Set the CNAME aliases. + Vector<String> aliases( + {"non-ad.com", "?", "alias3.com", "not-an-ad.com", "www.example.com"}); + response.SetDnsAliases(aliases); + + // Give the response to the loader. + GiveResponseToLoader(response, loader); + + // Test the histograms to verify that the CNAME aliases were detected. + // Expect that the resource was neither tagged nor blocked. + // Expect that the invalid alias is counted as such. + // Expect that the redundant (i.e. matching the request URL) fifth alias to be + // counted as such. + CnameAliasMetricInfo info = {.has_aliases = true, + .was_ad_tagged_based_on_alias = false, + .was_blocked_based_on_alias = false, + .list_length = 5, + .invalid_count = 1, + .redundant_count = 1}; + + ExpectHistogramsMatching(info); +} + +TEST_F(ResourceLoaderSubresourceFilterCnameAliasTest, + DnsAliasesCheckedBySubresourceFilterNoAliases_NoneDetected) { + // Set the blocklists: the first for blocking, the second for ad-tagging. + Vector<String> blocked_urls = {}; + Vector<String> tagged_urls = {}; + SetMockSubresourceFilterBlockLists(blocked_urls, tagged_urls); + + // Create the request. + KURL url("https://www.example.com/some_path.html"); + ResourceRequest request(url); + request.SetRequestContext(mojom::blink::RequestContextType::FETCH); + + // Create the resource and loader. + Resource* resource = CreateResource(std::move(request)); + ResourceLoader* loader = resource->Loader(); + + // Create the response. + ResourceResponse response(url); + response.SetHttpStatusCode(200); + + // Set the CNAME aliases. + Vector<String> aliases; + response.SetDnsAliases(aliases); + + // Give the response to the loader. + GiveResponseToLoader(response, loader); + + // Test the histogram to verify that no aliases were detected. + CnameAliasMetricInfo info = {.has_aliases = false}; + + ExpectHistogramsMatching(info); +} + } // 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 index 62435966a4b..1be57a1ab0f 100644 --- 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 @@ -9,7 +9,7 @@ #if DCHECK_IS_ON() // We can see logs with |--v=N| or |--vmodule=ResourceLoadingLog=N| where N is a -// verbose level. +// verbose level, as well as the |--enable-logging=stderr| CLI argument. #define RESOURCE_LOADING_DVLOG(verbose_level) \ LAZY_STREAM( \ VLOG_STREAM(verbose_level), \ 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 index 792cbebbfd1..7a460830b6b 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request.cc @@ -30,6 +30,7 @@ #include "base/unguessable_token.h" #include "services/network/public/mojom/ip_address_space.mojom-blink.h" +#include "services/network/public/mojom/web_bundle_handle.mojom-blink.h" #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h" #include "third_party/blink/public/platform/web_url_request.h" #include "third_party/blink/renderer/platform/network/encoded_form_data.h" @@ -40,6 +41,42 @@ namespace blink { +ResourceRequestHead::WebBundleTokenParams& +ResourceRequestHead::WebBundleTokenParams::operator=( + const WebBundleTokenParams& other) { + bundle_url = other.bundle_url; + token = other.token; + handle = other.CloneHandle(); + return *this; +} + +ResourceRequestHead::WebBundleTokenParams::WebBundleTokenParams( + const WebBundleTokenParams& other) { + *this = other; +} + +ResourceRequestHead::WebBundleTokenParams::WebBundleTokenParams( + const KURL& bundle_url, + const base::UnguessableToken& web_bundle_token, + mojo::PendingRemote<network::mojom::WebBundleHandle> web_bundle_handle) + : bundle_url(bundle_url), + token(web_bundle_token), + handle(std::move(web_bundle_handle)) {} + +mojo::PendingRemote<network::mojom::WebBundleHandle> +ResourceRequestHead::WebBundleTokenParams::CloneHandle() const { + if (!handle) + return mojo::NullRemote(); + mojo::Remote<network::mojom::WebBundleHandle> remote(std::move( + const_cast<mojo::PendingRemote<network::mojom::WebBundleHandle>&>( + handle))); + mojo::PendingRemote<network::mojom::WebBundleHandle> new_remote; + remote->Clone(new_remote.InitWithNewPipeAndPassReceiver()); + const_cast<mojo::PendingRemote<network::mojom::WebBundleHandle>&>(handle) = + remote.Unbind(); + return new_remote; +} + const base::TimeDelta ResourceRequestHead::default_timeout_interval_ = base::TimeDelta::Max(); @@ -127,26 +164,12 @@ ResourceRequest::ResourceRequest(const KURL& url) : ResourceRequestHead(url) {} ResourceRequest::ResourceRequest(const ResourceRequestHead& head) : ResourceRequestHead(head) {} -ResourceRequest& ResourceRequest::operator=(const ResourceRequest& src) { - DCHECK(!body_.StreamBody().is_valid()); - DCHECK(!src.body_.StreamBody().is_valid()); - this->ResourceRequestHead::operator=(src); - body_.SetFormBody(src.body_.FormBody()); - return *this; -} - ResourceRequest::ResourceRequest(ResourceRequest&&) = default; ResourceRequest& ResourceRequest::operator=(ResourceRequest&&) = default; ResourceRequest::~ResourceRequest() = default; -void ResourceRequest::CopyFrom(const ResourceRequest& src) { - DCHECK(!body_.StreamBody().is_valid()); - DCHECK(!src.body_.StreamBody().is_valid()); - *this = src; -} - void ResourceRequest::CopyHeadFrom(const ResourceRequestHead& src) { this->ResourceRequestHead::operator=(src); } @@ -197,6 +220,7 @@ std::unique_ptr<ResourceRequest> ResourceRequestHead::CreateRedirectRequest( IsSignedExchangePrefetchCacheEnabled()); request->SetRecursivePrefetchToken(RecursivePrefetchToken()); request->SetFetchLikeAPI(IsFetchLikeAPI()); + request->SetFavicon(IsFavicon()); return request; } 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 index d55e1fae866..3290fa3a99e 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_request.h @@ -41,9 +41,12 @@ #include "services/network/public/mojom/fetch_api.mojom-blink-forward.h" #include "services/network/public/mojom/ip_address_space.mojom-blink-forward.h" #include "services/network/public/mojom/trust_tokens.mojom-blink.h" +#include "services/network/public/mojom/url_loader.mojom-blink.h" +#include "services/network/public/mojom/web_bundle_handle.mojom-blink.h" #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink-forward.h" #include "third_party/blink/public/platform/resource_request_blocked_reason.h" #include "third_party/blink/public/platform/web_url_request_extra_data.h" +#include "third_party/blink/renderer/platform/loader/fetch/render_blocking_behavior.h" #include "third_party/blink/renderer/platform/loader/fetch/resource_load_priority.h" #include "third_party/blink/renderer/platform/network/http_header_map.h" #include "third_party/blink/renderer/platform/network/http_names.h" @@ -80,6 +83,23 @@ class PLATFORM_EXPORT ResourceRequestHead { : original_url(original_url), previous_url(previous_url) {} }; + struct PLATFORM_EXPORT WebBundleTokenParams { + WebBundleTokenParams() = delete; + WebBundleTokenParams(const WebBundleTokenParams& other); + WebBundleTokenParams& operator=(const WebBundleTokenParams& other); + + WebBundleTokenParams( + const KURL& bundle_url, + const base::UnguessableToken& token, + mojo::PendingRemote<network::mojom::WebBundleHandle> handle); + + mojo::PendingRemote<network::mojom::WebBundleHandle> CloneHandle() const; + + KURL bundle_url; + base::UnguessableToken token; + mojo::PendingRemote<network::mojom::WebBundleHandle> handle; + }; + ResourceRequestHead(); explicit ResourceRequestHead(const KURL&); @@ -457,6 +477,10 @@ class PLATFORM_EXPORT ResourceRequestHead { void SetFetchLikeAPI(bool enabled) { is_fetch_like_api_ = enabled; } + bool IsFavicon() const { return is_favicon_; } + + void SetFavicon(bool enabled) { is_favicon_ = enabled; } + bool PrefetchMaybeForTopLeveNavigation() const { return prefetch_maybe_for_top_level_navigation_; } @@ -486,6 +510,23 @@ class PLATFORM_EXPORT ResourceRequestHead { return allowHTTP1ForStreamingUpload_; } + const base::Optional<ResourceRequestHead::WebBundleTokenParams>& + GetWebBundleTokenParams() const { + return web_bundle_token_params_; + } + + void SetWebBundleTokenParams( + ResourceRequestHead::WebBundleTokenParams params) { + web_bundle_token_params_ = params; + } + + void SetRenderBlockingBehavior(RenderBlockingBehavior behavior) { + render_blocking_behavior_ = behavior; + } + RenderBlockingBehavior GetRenderBlockingBehavior() const { + return render_blocking_behavior_; + } + private: const CacheControlHeader& GetCacheControlHeader() const; @@ -569,6 +610,8 @@ class PLATFORM_EXPORT ResourceRequestHead { bool is_fetch_like_api_ = false; + bool is_favicon_ = false; + // Currently this is only used when a prefetch request has `as=document` // specified. If true, and the request is cross-origin, the browser will cache // the request under the cross-origin's partition. Furthermore, its reuse from @@ -581,6 +624,16 @@ class PLATFORM_EXPORT ResourceRequestHead { // prefetch responses. The browser process uses this token to ensure the // request is cached correctly. base::Optional<base::UnguessableToken> recursive_prefetch_token_; + + // This is used when fetching either a WebBundle or a subresrouce in the + // WebBundle. The network process uses this token to associate the request to + // the bundle. + base::Optional<WebBundleTokenParams> web_bundle_token_params_; + + // Render blocking behavior of the resource. Used in maintaining correct + // reporting for redirects. + RenderBlockingBehavior render_blocking_behavior_ = + RenderBlockingBehavior::kUnset; }; class PLATFORM_EXPORT ResourceRequestBody { @@ -644,13 +697,11 @@ class PLATFORM_EXPORT ResourceRequest final : public ResourceRequestHead { ResourceRequest(const ResourceRequest&) = delete; ResourceRequest(ResourceRequest&&); + ResourceRequest& operator=(const ResourceRequest&) = delete; ResourceRequest& operator=(ResourceRequest&&); ~ResourceRequest(); - // TODO(yoichio): Use move semantics as much as possible. - // See crbug.com/787704. - void CopyFrom(const ResourceRequest&); void CopyHeadFrom(const ResourceRequestHead&); const scoped_refptr<EncodedFormData>& HttpBody() const; @@ -659,8 +710,6 @@ class PLATFORM_EXPORT ResourceRequest final : public ResourceRequestHead { ResourceRequestBody& MutableBody() { return body_; } private: - ResourceRequest& operator=(const ResourceRequest&); - ResourceRequestBody body_; }; 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 index 5fadb1c34d9..cbec9d25317 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_response.h @@ -321,6 +321,9 @@ class PLATFORM_EXPORT ResourceResponse final { app_cache_manifest_url_ = url; } + const KURL& WebBundleURL() const { return web_bundle_url_; } + void SetWebBundleURL(const KURL& url) { web_bundle_url_ = url; } + bool WasFetchedViaSPDY() const { return was_fetched_via_spdy_; } void SetWasFetchedViaSPDY(bool value) { was_fetched_via_spdy_ = value; } @@ -358,6 +361,9 @@ class PLATFORM_EXPORT ResourceResponse final { // https://html.spec.whatwg.org/C/#cors-cross-origin bool IsCorsCrossOrigin() const; + int64_t GetPadding() const { return padding_; } + void SetPadding(int64_t padding) { padding_ = padding; } + // See network::ResourceResponseInfo::url_list_via_service_worker. const Vector<KURL>& UrlListViaServiceWorker() const { return url_list_via_service_worker_; @@ -494,9 +500,23 @@ class PLATFORM_EXPORT ResourceResponse final { was_cookie_in_request_ = was_cookie_in_request; } + const Vector<String>& DnsAliases() const { return dns_aliases_; } + + void SetDnsAliases(Vector<String> aliases) { + dns_aliases_ = std::move(aliases); + } + network::mojom::CrossOriginEmbedderPolicyValue GetCrossOriginEmbedderPolicy() const; + const base::Optional<net::AuthChallengeInfo>& AuthChallengeInfo() const { + return auth_challenge_info_; + } + void SetAuthChallengeInfo( + const base::Optional<net::AuthChallengeInfo>& value) { + auth_challenge_info_ = value; + } + private: void UpdateHeaderParsedState(const AtomicString& name); @@ -597,6 +617,11 @@ class PLATFORM_EXPORT ResourceResponse final { network::mojom::FetchResponseType response_type_ = network::mojom::FetchResponseType::kDefault; + // Pre-computed padding. This should only be non-zero if |response_type| is + // set to kOpaque. In addition, it is only set if the response was provided + // by a service worker FetchEvent handler. + int64_t padding_ = 0; + // HTTP version used in the response, if known. HTTPVersion http_version_ = kHTTPVersionUnknown; @@ -666,6 +691,17 @@ class PLATFORM_EXPORT ResourceResponse final { // cross-origin prefetch responses. It is used to pass the token along to // preload header requests from these responses. base::Optional<base::UnguessableToken> recursive_prefetch_token_; + + // Any DNS aliases for the requested URL, as read from CNAME records. + // The alias chain order is preserved in reverse, from canonical name (i.e. + // address record name) through to query name. + Vector<String> dns_aliases_; + + // The URL of WebBundle this response was loaded from. This value is only + // populated for resources loaded from a WebBundle. + KURL web_bundle_url_; + + base::Optional<net::AuthChallengeInfo> auth_challenge_info_; }; } // namespace blink 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 index 1e2d9198ec8..ce3b6c5e949 100644 --- 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 @@ -4,6 +4,7 @@ #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h" @@ -102,4 +103,15 @@ TEST(ResourceResponseTest, AddHttpHeaderFieldWithMultipleValues) { EXPECT_EQ("a=1, b=2, c=3", response.HttpHeaderField("set-cookie")); } +TEST(ResourceResponseTest, DnsAliasesCanBeSetAndAccessed) { + ResourceResponse response(CreateTestResponse()); + + EXPECT_TRUE(response.DnsAliases().IsEmpty()); + + Vector<String> aliases({"alias1", "alias2"}); + response.SetDnsAliases(aliases); + + EXPECT_THAT(response.DnsAliases(), testing::ElementsAre("alias1", "alias2")); +} + } // namespace blink 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 index 4353c202b8a..7bbd1ee946e 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/resource_test.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/resource_test.cc @@ -12,137 +12,13 @@ #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_with_mock_scheduler.h" -#include "third_party/blink/renderer/platform/testing/url_test_helpers.h" #include "third_party/blink/renderer/platform/wtf/shared_buffer.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(blink::mojom::CodeCacheType cache_type, - const WebURL& url, - base::Time, - const uint8_t*, - size_t) override { - cached_urls_.push_back(url); - } - - void CacheMetadataInCacheStorage(const blink::WebURL& url, - base::Time, - const uint8_t*, - size_t, - const blink::WebSecurityOrigin&, - const blink::WebString&) override { - cache_storage_cached_urls_.push_back(url); - } - - const Vector<WebURL>& CachedURLs() const { return cached_urls_; } - const Vector<WebURL>& CacheStorageCachedURLs() const { - return cache_storage_cached_urls_; - } - - private: - Vector<WebURL> cached_urls_; - Vector<WebURL> cache_storage_cached_urls_; -}; - -ResourceResponse CreateTestResourceResponse() { - ResourceResponse response(url_test_helpers::ToKURL("https://example.com/")); - response.SetHttpStatusCode(200); - return response; -} - -void CreateTestResourceAndSetCachedMetadata(const ResourceResponse& response) { - const uint8_t kTestData[] = {1, 2, 3, 4, 5}; - ResourceRequest request(response.CurrentRequestUrl()); - request.SetRequestorOrigin( - SecurityOrigin::Create(response.CurrentRequestUrl())); - auto* resource = MakeGarbageCollected<MockResource>(request); - 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()); - EXPECT_EQ(0u, mock->CacheStorageCachedURLs().size()); -} - -TEST( - ResourceTest, - SetCachedMetadata_DoesNotSendMetadataToPlatformWhenFetchedViaServiceWorkerWithSyntheticResponse) { - ScopedTestingPlatformSupport<MockPlatform> mock; - - // Equivalent to service worker calling respondWith(new Response(...)) - ResourceResponse response(CreateTestResourceResponse()); - response.SetWasFetchedViaServiceWorker(true); - - CreateTestResourceAndSetCachedMetadata(response); - EXPECT_EQ(0u, mock->CachedURLs().size()); - EXPECT_EQ(0u, mock->CacheStorageCachedURLs().size()); -} - -TEST( - ResourceTest, - SetCachedMetadata_SendsMetadataToPlatformWhenFetchedViaServiceWorkerWithPassThroughResponse) { - ScopedTestingPlatformSupport<MockPlatform> mock; - - // Equivalent to service worker calling respondWith(fetch(evt.request.url)); - ResourceResponse response(CreateTestResourceResponse()); - response.SetWasFetchedViaServiceWorker(true); - response.SetUrlListViaServiceWorker( - Vector<KURL>(1, response.CurrentRequestUrl())); - - CreateTestResourceAndSetCachedMetadata(response); - EXPECT_EQ(1u, mock->CachedURLs().size()); - EXPECT_EQ(0u, mock->CacheStorageCachedURLs().size()); -} - -TEST( - ResourceTest, - SetCachedMetadata_DoesNotSendMetadataToPlatformWhenFetchedViaServiceWorkerWithDifferentURLResponse) { - ScopedTestingPlatformSupport<MockPlatform> mock; - - // Equivalent to service worker calling respondWith(fetch(some_different_url)) - ResourceResponse response(CreateTestResourceResponse()); - response.SetWasFetchedViaServiceWorker(true); - response.SetUrlListViaServiceWorker(Vector<KURL>( - 1, url_test_helpers::ToKURL("https://example.com/different/url"))); - - CreateTestResourceAndSetCachedMetadata(response); - EXPECT_EQ(0u, mock->CachedURLs().size()); - EXPECT_EQ(0u, mock->CacheStorageCachedURLs().size()); -} - -TEST( - ResourceTest, - SetCachedMetadata_SendsMetadataToPlatformWhenFetchedViaServiceWorkerWithCacheResponse) { - ScopedTestingPlatformSupport<MockPlatform> mock; - - // Equivalent to service worker calling respondWith(cache.match(some_url)); - ResourceResponse response(CreateTestResourceResponse()); - response.SetWasFetchedViaServiceWorker(true); - response.SetCacheStorageCacheName("dummy"); - - CreateTestResourceAndSetCachedMetadata(response); - EXPECT_EQ(0u, mock->CachedURLs().size()); - EXPECT_EQ(1u, mock->CacheStorageCachedURLs().size()); -} - TEST(ResourceTest, RevalidateWithFragment) { - ScopedTestingPlatformSupport<MockPlatform> mock; KURL url("http://127.0.0.1:8000/foo.html"); ResourceResponse response(url); response.SetHttpStatusCode(200); @@ -160,7 +36,6 @@ TEST(ResourceTest, RevalidateWithFragment) { } TEST(ResourceTest, Vary) { - ScopedTestingPlatformSupport<MockPlatform> mock; const KURL url("http://127.0.0.1:8000/foo.html"); ResourceResponse response(url); response.SetHttpStatusCode(200); @@ -228,14 +103,9 @@ TEST(ResourceTest, RevalidationFailed) { 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 = MakeGarbageCollected<MockResourceClient>(); resource->AddClient(client, nullptr); @@ -247,8 +117,6 @@ TEST(ResourceTest, RevalidationFailed) { 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); @@ -265,7 +133,7 @@ TEST(ResourceTest, RevalidationFailed) { TEST(ResourceTest, RevalidationSucceeded) { ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler> - platform_; + platform; const KURL url("http://test.example.com/"); auto* resource = MakeGarbageCollected<MockResource>(url); ResourceResponse response(url); @@ -276,14 +144,9 @@ TEST(ResourceTest, RevalidationSucceeded) { 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 = MakeGarbageCollected<MockResourceClient>(); resource->AddClient(client, nullptr); @@ -295,7 +158,6 @@ TEST(ResourceTest, RevalidationSucceeded) { 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); @@ -307,7 +169,7 @@ TEST(ResourceTest, RevalidationSucceeded) { TEST(ResourceTest, RevalidationSucceededForResourceWithoutBody) { ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler> - platform_; + platform; const KURL url("http://test.example.com/"); auto* resource = MakeGarbageCollected<MockResource>(url); ResourceResponse response(url); @@ -339,7 +201,7 @@ TEST(ResourceTest, RevalidationSucceededForResourceWithoutBody) { TEST(ResourceTest, RevalidationSucceededUpdateHeaders) { ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler> - platform_; + platform; const KURL url("http://test.example.com/"); auto* resource = MakeGarbageCollected<MockResource>(url); ResourceResponse response(url); @@ -415,7 +277,7 @@ TEST(ResourceTest, RevalidationSucceededUpdateHeaders) { TEST(ResourceTest, RedirectDuringRevalidation) { ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler> - platform_; + platform; const KURL url("http://test.example.com/1"); const KURL redirect_target_url("http://test.example.com/2"); @@ -432,15 +294,11 @@ TEST(ResourceTest, RedirectDuringRevalidation) { 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 = MakeGarbageCollected<MockResourceClient>(); @@ -457,15 +315,12 @@ TEST(ResourceTest, RedirectDuringRevalidation) { 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); - EXPECT_TRUE(resource->CacheHandler()); - const char kData2[4] = "xyz"; resource->AppendData(kData2, 3); resource->FinishForTest(); @@ -482,13 +337,11 @@ TEST(ResourceTest, RedirectDuringRevalidation) { // Test the case where a client is added after revalidation is completed. Persistent<MockResourceClient> client2 = MakeGarbageCollected<MockResourceClient>(); - auto* platform = static_cast<TestingPlatformSupportWithMockScheduler*>( - Platform::Current()); resource->AddClient(client2, platform->test_task_runner().get()); // Because the client is added asynchronously, // |runUntilIdle()| is called to make |client2| to be notified. - platform_->RunUntilIdle(); + platform->RunUntilIdle(); EXPECT_TRUE(client2->NotifyFinishedCalled()); @@ -508,7 +361,7 @@ class ScopedResourceMockClock { }; TEST(ResourceTest, StaleWhileRevalidateCacheControl) { - ScopedTestingPlatformSupport<MockPlatform> mock; + ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler> mock; ScopedResourceMockClock clock(mock->test_task_runner()->GetMockClock()); const KURL url("http://127.0.0.1:8000/foo.html"); ResourceResponse response(url); @@ -536,7 +389,7 @@ TEST(ResourceTest, StaleWhileRevalidateCacheControl) { } TEST(ResourceTest, StaleWhileRevalidateCacheControlWithRedirect) { - ScopedTestingPlatformSupport<MockPlatform> mock; + ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler> mock; ScopedResourceMockClock clock(mock->test_task_runner()->GetMockClock()); const KURL url("http://127.0.0.1:8000/foo.html"); const KURL redirect_target_url("http://127.0.0.1:8000/food.html"); @@ -582,4 +435,13 @@ TEST(ResourceTest, DefaultOverheadSize) { EXPECT_EQ(resource->CalculateOverheadSizeForTest(), resource->OverheadSize()); } +TEST(ResourceTest, SetIsAdResource) { + const KURL url("http://127.0.0.1:8000/foo.html"); + auto* resource = MakeGarbageCollected<MockResource>(url); + EXPECT_FALSE(resource->GetResourceRequest().IsAdResource()); + resource->SetIsAdResource(); + EXPECT_TRUE(resource->GetResourceRequest().IsAdResource()); +} + +} // namespace } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader.cc index 611a1701ad6..1bd85bd0f18 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader.cc @@ -9,6 +9,8 @@ #include "base/auto_reset.h" #include "third_party/blink/public/common/features.h" #include "third_party/blink/public/mojom/frame/back_forward_cache_controller.mojom-blink.h" +#include "third_party/blink/renderer/platform/back_forward_cache_utils.h" +#include "third_party/blink/renderer/platform/loader/fetch/back_forward_cache_loader_helper.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_loader.h" @@ -18,7 +20,7 @@ namespace blink { constexpr size_t ResponseBodyLoader::kMaxNumConsumedBytesInTask; -constexpr size_t kDefaultMaxBufferedBodyBytes = 100 * 1000; +constexpr size_t kDefaultMaxBufferedBodyBytesPerRequest = 100 * 1000; class ResponseBodyLoader::DelegatingBytesConsumer final : public BytesConsumer, @@ -286,10 +288,9 @@ class ResponseBodyLoader::Buffer final public: explicit Buffer(ResponseBodyLoader* owner) : owner_(owner), - max_bytes_to_read_(base::GetFieldTrialParamByFeatureAsInt( - blink::features::kLoadingTasksUnfreezable, + max_bytes_to_read_(GetLoadingTasksUnfreezableParamAsInt( "max_buffered_bytes", - kDefaultMaxBufferedBodyBytes)) {} + kDefaultMaxBufferedBodyBytesPerRequest)) {} bool IsEmpty() const { return buffered_data_.IsEmpty(); } @@ -297,6 +298,9 @@ class ResponseBodyLoader::Buffer final // exceeds |max_bytes_to_read_| bytes. bool AddChunk(const char* buffer, size_t available) { total_bytes_read_ += available; + TRACE_EVENT2("loading", "ResponseBodyLoader::Buffer::AddChunk", + "total_bytes_read", static_cast<int>(total_bytes_read_), + "added_bytes", static_cast<int>(available)); if (total_bytes_read_ > max_bytes_to_read_) return false; Vector<char> new_chunk; @@ -344,9 +348,11 @@ class ResponseBodyLoader::Buffer final ResponseBodyLoader::ResponseBodyLoader( BytesConsumer& bytes_consumer, ResponseBodyLoaderClient& client, - scoped_refptr<base::SingleThreadTaskRunner> task_runner) + scoped_refptr<base::SingleThreadTaskRunner> task_runner, + BackForwardCacheLoaderHelper* back_forward_cache_loader_helper) : bytes_consumer_(bytes_consumer), client_(client), + back_forward_cache_loader_helper_(back_forward_cache_loader_helper), task_runner_(std::move(task_runner)) { bytes_consumer_->SetClient(this); body_buffer_ = MakeGarbageCollected<Buffer>(this); @@ -433,10 +439,26 @@ void ResponseBodyLoader::DidCancelLoadingBody() { client_->DidCancelLoadingBody(); } -// TODO(yuzus): Remove this and provide the capability to the loader. void ResponseBodyLoader::EvictFromBackForwardCache( mojom::blink::RendererEvictionReason reason) { - client_->EvictFromBackForwardCache(reason); + if (!back_forward_cache_loader_helper_) + return; + back_forward_cache_loader_helper_->EvictFromBackForwardCache(reason); +} + +void ResponseBodyLoader::DidBufferLoadWhileInBackForwardCache( + size_t num_bytes) { + if (!back_forward_cache_loader_helper_) + return; + back_forward_cache_loader_helper_->DidBufferLoadWhileInBackForwardCache( + num_bytes); +} + +bool ResponseBodyLoader::CanContinueBufferingWhileInBackForwardCache() { + if (!back_forward_cache_loader_helper_) + return false; + return back_forward_cache_loader_helper_ + ->CanContinueBufferingWhileInBackForwardCache(); } void ResponseBodyLoader::Start() { @@ -470,7 +492,7 @@ void ResponseBodyLoader::Suspend(WebURLLoader::DeferType suspended_state) { suspended_state_ = suspended_state; if (IsSuspendedForBackForwardCache()) { - DCHECK(base::FeatureList::IsEnabled(features::kLoadingTasksUnfreezable)); + DCHECK(IsInflightNetworkRequestBackForwardCacheSupportEnabled()); // If we're already suspended (but not for back-forward cache), we might've // ignored some OnStateChange calls. if (was_suspended) { @@ -483,7 +505,7 @@ void ResponseBodyLoader::Suspend(WebURLLoader::DeferType suspended_state) { void ResponseBodyLoader::EvictFromBackForwardCacheIfDrained() { if (IsDrained()) { - client_->EvictFromBackForwardCache( + EvictFromBackForwardCache( mojom::blink::RendererEvictionReason::kNetworkRequestDatapipeDrained); } } @@ -554,7 +576,9 @@ void ResponseBodyLoader::OnStateChange() { std::min(available, kMaxNumConsumedBytesInTask - num_bytes_consumed); if (IsSuspendedForBackForwardCache()) { // Save the read data into |body_buffer_| instead. - if (!body_buffer_->AddChunk(buffer, available)) { + DidBufferLoadWhileInBackForwardCache(available); + if (!body_buffer_->AddChunk(buffer, available) || + !CanContinueBufferingWhileInBackForwardCache()) { // We've read too much data while suspended for back-forward cache. // Evict the page from the back-forward cache. result = bytes_consumer_->EndRead(available); @@ -598,6 +622,7 @@ void ResponseBodyLoader::Trace(Visitor* visitor) const { visitor->Trace(delegating_bytes_consumer_); visitor->Trace(client_); visitor->Trace(body_buffer_); + visitor->Trace(back_forward_cache_loader_helper_); ResponseBodyLoaderDrainableInterface::Trace(visitor); ResponseBodyLoaderClient::Trace(visitor); BytesConsumer::Client::Trace(visitor); diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader.h b/chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader.h index 1589dfddbcd..4ca1a94aa88 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader.h @@ -9,6 +9,7 @@ #include "base/memory/scoped_refptr.h" #include "mojo/public/cpp/system/data_pipe.h" #include "third_party/blink/public/mojom/frame/back_forward_cache_controller.mojom-blink-forward.h" +#include "third_party/blink/public/platform/web_back_forward_cache_loader_helper.h" #include "third_party/blink/public/platform/web_url_loader.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" #include "third_party/blink/renderer/platform/heap/member.h" @@ -24,6 +25,7 @@ class SingleThreadTaskRunner; namespace blink { +class BackForwardCacheLoaderHelper; class ResponseBodyLoader; // See ResponseBodyLoader for details. This is a virtual interface to expose @@ -74,9 +76,11 @@ class PLATFORM_EXPORT ResponseBodyLoader final private ResponseBodyLoaderClient, private BytesConsumer::Client { public: - ResponseBodyLoader(BytesConsumer&, - ResponseBodyLoaderClient&, - scoped_refptr<base::SingleThreadTaskRunner>); + ResponseBodyLoader( + BytesConsumer&, + ResponseBodyLoaderClient&, + scoped_refptr<base::SingleThreadTaskRunner>, + BackForwardCacheLoaderHelper* back_forward_cache_loader_helper); // ResponseBodyLoaderDrainableInterface implementation. mojo::ScopedDataPipeConsumerHandle DrainAsDataPipe( @@ -127,17 +131,19 @@ class PLATFORM_EXPORT ResponseBodyLoader final void DidFinishLoadingBody() override; void DidFailLoadingBody() override; void DidCancelLoadingBody() override; - void EvictFromBackForwardCache(mojom::blink::RendererEvictionReason) override; + void EvictFromBackForwardCache(mojom::blink::RendererEvictionReason); + void DidBufferLoadWhileInBackForwardCache(size_t num_bytes); + bool CanContinueBufferingWhileInBackForwardCache(); // BytesConsumer::Client implementation. void OnStateChange() override; String DebugName() const override { return "ResponseBodyLoader"; } - // When |buffer_data_while_suspended_for_bfcache_| is true, we'll save the - // response body read when suspended. + Member<Buffer> body_buffer_; Member<BytesConsumer> bytes_consumer_; Member<DelegatingBytesConsumer> delegating_bytes_consumer_; const Member<ResponseBodyLoaderClient> client_; + WeakMember<BackForwardCacheLoaderHelper> back_forward_cache_loader_helper_; const scoped_refptr<base::SingleThreadTaskRunner> task_runner_; WebURLLoader::DeferType suspended_state_ = WebURLLoader::DeferType::kNotDeferred; diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader_client.h b/chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader_client.h index ef9d2d87ea9..993a92f26e3 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader_client.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader_client.h @@ -27,10 +27,6 @@ class ResponseBodyLoaderClient : public GarbageCollectedMixin { // Called when the loader cancelled loading the body. virtual void DidCancelLoadingBody() = 0; - - // Called when the body loader is suspended and the data pipe is drained. - virtual void EvictFromBackForwardCache( - mojom::blink::RendererEvictionReason) = 0; }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader_test.cc index 4c22d831f78..0b0d928c738 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader_test.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/response_body_loader_test.cc @@ -10,7 +10,9 @@ #include "base/test/scoped_feature_list.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/common/features.h" +#include "third_party/blink/public/platform/web_runtime_features.h" #include "third_party/blink/renderer/platform/heap/persistent.h" +#include "third_party/blink/renderer/platform/loader/fetch/back_forward_cache_loader_helper.h" #include "third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer.h" #include "third_party/blink/renderer/platform/loader/testing/bytes_consumer_test_reader.h" #include "third_party/blink/renderer/platform/loader/testing/replaying_bytes_consumer.h" @@ -21,6 +23,15 @@ namespace blink { namespace { +class TestBackForwardCacheLoaderHelper : public BackForwardCacheLoaderHelper { + public: + TestBackForwardCacheLoaderHelper() = default; + + bool CanContinueBufferingWhileInBackForwardCache() const override { + return true; + } +}; + class ResponseBodyLoaderTest : public testing::Test { protected: using Command = ReplayingBytesConsumer::Command; @@ -74,7 +85,6 @@ class ResponseBodyLoaderTest : public testing::Test { DCHECK(!failed_); cancelled_ = true; } - void EvictFromBackForwardCache(mojom::RendererEvictionReason) override {} void SetLoader(ResponseBodyLoader& loader) { loader_ = loader; } void Trace(Visitor* visitor) const override { visitor->Trace(loader_); } @@ -134,6 +144,15 @@ class ResponseBodyLoaderTest : public testing::Test { const Member<BytesConsumer> bytes_consumer_; const Member<TestClient> test_response_body_loader_client_; }; + + ResponseBodyLoader* MakeResponseBodyLoader( + BytesConsumer& bytes_consumer, + ResponseBodyLoaderClient& client, + scoped_refptr<base::SingleThreadTaskRunner> task_runner) { + return MakeGarbageCollected<ResponseBodyLoader>( + bytes_consumer, client, task_runner, + MakeGarbageCollected<TestBackForwardCacheLoaderHelper>()); + } }; class ResponseBodyLoaderDrainedBytesConsumerNotificationOutOfOnStateChangeTest @@ -151,8 +170,7 @@ TEST_F(ResponseBodyLoaderTest, Load) { consumer->Add(Command(Command::kDone)); auto* client = MakeGarbageCollected<TestClient>(); - auto* body_loader = - MakeGarbageCollected<ResponseBodyLoader>(*consumer, *client, task_runner); + auto* body_loader = MakeResponseBodyLoader(*consumer, *client, task_runner); EXPECT_FALSE(client->LoadingIsFinished()); EXPECT_FALSE(client->LoadingIsFailed()); @@ -180,8 +198,7 @@ TEST_F(ResponseBodyLoaderTest, LoadFailure) { consumer->Add(Command(Command::kError)); auto* client = MakeGarbageCollected<TestClient>(); - auto* body_loader = - MakeGarbageCollected<ResponseBodyLoader>(*consumer, *client, task_runner); + auto* body_loader = MakeResponseBodyLoader(*consumer, *client, task_runner); EXPECT_FALSE(client->LoadingIsFinished()); EXPECT_FALSE(client->LoadingIsFailed()); @@ -208,8 +225,7 @@ TEST_F(ResponseBodyLoaderTest, LoadWithDataAndDone) { consumer->Add(Command(Command::kDataAndDone, "llo")); auto* client = MakeGarbageCollected<TestClient>(); - auto* body_loader = - MakeGarbageCollected<ResponseBodyLoader>(*consumer, *client, task_runner); + auto* body_loader = MakeResponseBodyLoader(*consumer, *client, task_runner); EXPECT_FALSE(client->LoadingIsFinished()); EXPECT_FALSE(client->LoadingIsFailed()); @@ -238,8 +254,7 @@ TEST_F(ResponseBodyLoaderTest, Abort) { auto* client = MakeGarbageCollected<TestClient>( TestClient::Option::kAbortOnDidReceiveData); - auto* body_loader = - MakeGarbageCollected<ResponseBodyLoader>(*consumer, *client, task_runner); + auto* body_loader = MakeResponseBodyLoader(*consumer, *client, task_runner); client->SetLoader(*body_loader); EXPECT_FALSE(client->LoadingIsFinished()); @@ -270,8 +285,7 @@ TEST_F(ResponseBodyLoaderTest, Suspend) { auto* client = MakeGarbageCollected<TestClient>( TestClient::Option::kSuspendOnDidReceiveData); - auto* body_loader = - MakeGarbageCollected<ResponseBodyLoader>(*consumer, *client, task_runner); + auto* body_loader = MakeResponseBodyLoader(*consumer, *client, task_runner); client->SetLoader(*body_loader); EXPECT_FALSE(client->LoadingIsFinished()); @@ -335,8 +349,7 @@ TEST_F(ResponseBodyLoaderTest, ReadTooBigBuffer) { consumer->Add(Command(Command::kDone)); auto* client = MakeGarbageCollected<TestClient>(); - auto* body_loader = - MakeGarbageCollected<ResponseBodyLoader>(*consumer, *client, task_runner); + auto* body_loader = MakeResponseBodyLoader(*consumer, *client, task_runner); EXPECT_FALSE(client->LoadingIsFinished()); EXPECT_FALSE(client->LoadingIsFailed()); @@ -367,8 +380,7 @@ TEST_F(ResponseBodyLoaderTest, NotDrainable) { consumer->Add(Command(Command::kDone)); auto* client = MakeGarbageCollected<TestClient>(); - auto* body_loader = - MakeGarbageCollected<ResponseBodyLoader>(*consumer, *client, task_runner); + auto* body_loader = MakeResponseBodyLoader(*consumer, *client, task_runner); ResponseBodyLoaderClient* intermediate_client = nullptr; auto data_pipe = body_loader->DrainAsDataPipe(&intermediate_client); @@ -399,7 +411,7 @@ TEST_F(ResponseBodyLoaderTest, NotDrainable) { TEST_F(ResponseBodyLoaderTest, DrainAsDataPipe) { mojo::ScopedDataPipeConsumerHandle consumer_end; mojo::ScopedDataPipeProducerHandle producer_end; - auto result = mojo::CreateDataPipe(nullptr, &producer_end, &consumer_end); + auto result = mojo::CreateDataPipe(nullptr, producer_end, consumer_end); ASSERT_EQ(result, MOJO_RESULT_OK); @@ -410,8 +422,7 @@ TEST_F(ResponseBodyLoaderTest, DrainAsDataPipe) { task_runner, std::move(consumer_end), &completion_notifier); auto* client = MakeGarbageCollected<TestClient>(); - auto* body_loader = - MakeGarbageCollected<ResponseBodyLoader>(*consumer, *client, task_runner); + auto* body_loader = MakeResponseBodyLoader(*consumer, *client, task_runner); ResponseBodyLoaderClient* client_for_draining = nullptr; auto data_pipe = body_loader->DrainAsDataPipe(&client_for_draining); @@ -443,6 +454,8 @@ class ResponseBodyLoaderLoadingTasksUnfreezableTest scoped_feature_list_.InitAndEnableFeature( features::kLoadingTasksUnfreezable); } + WebRuntimeFeatures::EnableBackForwardCache( + DeferWithBackForwardCacheEnabled()); } bool DeferWithBackForwardCacheEnabled() { return GetParam(); } @@ -458,8 +471,7 @@ TEST_P(ResponseBodyLoaderLoadingTasksUnfreezableTest, auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>(); auto* consumer = MakeGarbageCollected<ReplayingBytesConsumer>(task_runner); auto* client = MakeGarbageCollected<TestClient>(); - auto* body_loader = - MakeGarbageCollected<ResponseBodyLoader>(*consumer, *client, task_runner); + auto* body_loader = MakeResponseBodyLoader(*consumer, *client, task_runner); consumer->Add(Command(Command::kData, "he")); body_loader->Start(); task_runner->RunUntilIdle(); @@ -517,8 +529,7 @@ TEST_P(ResponseBodyLoaderLoadingTasksUnfreezableTest, auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>(); auto* consumer = MakeGarbageCollected<ReplayingBytesConsumer>(task_runner); auto* client = MakeGarbageCollected<TestClient>(); - auto* body_loader = - MakeGarbageCollected<ResponseBodyLoader>(*consumer, *client, task_runner); + auto* body_loader = MakeResponseBodyLoader(*consumer, *client, task_runner); consumer->Add(Command(Command::kData, "he")); body_loader->Start(); task_runner->RunUntilIdle(); @@ -571,8 +582,7 @@ TEST_P(ResponseBodyLoaderLoadingTasksUnfreezableTest, auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>(); auto* consumer = MakeGarbageCollected<ReplayingBytesConsumer>(task_runner); auto* client = MakeGarbageCollected<TestClient>(); - auto* body_loader = - MakeGarbageCollected<ResponseBodyLoader>(*consumer, *client, task_runner); + auto* body_loader = MakeResponseBodyLoader(*consumer, *client, task_runner); consumer->Add(Command(Command::kData, "he")); body_loader->Start(); task_runner->RunUntilIdle(); @@ -634,8 +644,7 @@ TEST_P(ResponseBodyLoaderLoadingTasksUnfreezableTest, auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>(); auto* consumer = MakeGarbageCollected<ReplayingBytesConsumer>(task_runner); auto* client = MakeGarbageCollected<TestClient>(); - auto* body_loader = - MakeGarbageCollected<ResponseBodyLoader>(*consumer, *client, task_runner); + auto* body_loader = MakeResponseBodyLoader(*consumer, *client, task_runner); body_loader->Start(); task_runner->RunUntilIdle(); EXPECT_EQ("", client->GetData()); @@ -675,7 +684,7 @@ INSTANTIATE_TEST_SUITE_P(All, TEST_F(ResponseBodyLoaderTest, DrainAsDataPipeAndReportError) { mojo::ScopedDataPipeConsumerHandle consumer_end; mojo::ScopedDataPipeProducerHandle producer_end; - auto result = mojo::CreateDataPipe(nullptr, &producer_end, &consumer_end); + auto result = mojo::CreateDataPipe(nullptr, producer_end, consumer_end); ASSERT_EQ(result, MOJO_RESULT_OK); @@ -686,8 +695,7 @@ TEST_F(ResponseBodyLoaderTest, DrainAsDataPipeAndReportError) { task_runner, std::move(consumer_end), &completion_notifier); auto* client = MakeGarbageCollected<TestClient>(); - auto* body_loader = - MakeGarbageCollected<ResponseBodyLoader>(*consumer, *client, task_runner); + auto* body_loader = MakeResponseBodyLoader(*consumer, *client, task_runner); ResponseBodyLoaderClient* client_for_draining = nullptr; auto data_pipe = body_loader->DrainAsDataPipe(&client_for_draining); @@ -721,8 +729,9 @@ TEST_F(ResponseBodyLoaderTest, DrainAsBytesConsumer) { original_consumer->Add(Command(Command::kDone)); auto* client = MakeGarbageCollected<TestClient>(); - auto* body_loader = MakeGarbageCollected<ResponseBodyLoader>( - *original_consumer, *client, task_runner); + + auto* body_loader = + MakeResponseBodyLoader(*original_consumer, *client, task_runner); BytesConsumer& consumer = body_loader->DrainAsBytesConsumer(); @@ -750,8 +759,9 @@ TEST_F(ResponseBodyLoaderTest, CancelDrainedBytesConsumer) { original_consumer->Add(Command(Command::kDone)); auto* client = MakeGarbageCollected<TestClient>(); - auto* body_loader = MakeGarbageCollected<ResponseBodyLoader>( - *original_consumer, *client, task_runner); + + auto* body_loader = + MakeResponseBodyLoader(*original_consumer, *client, task_runner); BytesConsumer& consumer = body_loader->DrainAsBytesConsumer(); @@ -786,8 +796,9 @@ TEST_F(ResponseBodyLoaderTest, DrainAsBytesConsumerWithError) { original_consumer->Add(Command(Command::kError)); auto* client = MakeGarbageCollected<TestClient>(); - auto* body_loader = MakeGarbageCollected<ResponseBodyLoader>( - *original_consumer, *client, task_runner); + + auto* body_loader = + MakeResponseBodyLoader(*original_consumer, *client, task_runner); BytesConsumer& consumer = body_loader->DrainAsBytesConsumer(); @@ -814,8 +825,9 @@ TEST_F(ResponseBodyLoaderTest, AbortAfterBytesConsumerIsDrained) { original_consumer->Add(Command(Command::kDone)); auto* client = MakeGarbageCollected<TestClient>(); - auto* body_loader = MakeGarbageCollected<ResponseBodyLoader>( - *original_consumer, *client, task_runner); + + auto* body_loader = + MakeResponseBodyLoader(*original_consumer, *client, task_runner); BytesConsumer& consumer = body_loader->DrainAsBytesConsumer(); auto* bytes_consumer_client = @@ -844,8 +856,9 @@ TEST_F(ResponseBodyLoaderTest, AbortAfterBytesConsumerIsDrainedIsNotified) { MakeGarbageCollected<ReplayingBytesConsumer>(task_runner); auto* client = MakeGarbageCollected<TestClient>(); - auto* body_loader = MakeGarbageCollected<ResponseBodyLoader>( - *original_consumer, *client, task_runner); + + auto* body_loader = + MakeResponseBodyLoader(*original_consumer, *client, task_runner); BytesConsumer& consumer = body_loader->DrainAsBytesConsumer(); @@ -873,8 +886,9 @@ TEST_F(ResponseBodyLoaderDrainedBytesConsumerNotificationOutOfOnStateChangeTest, original_consumer->Add(Command(Command::kDone)); auto* client = MakeGarbageCollected<TestClient>(); - auto* body_loader = MakeGarbageCollected<ResponseBodyLoader>( - *original_consumer, *client, task_runner); + + auto* body_loader = + MakeResponseBodyLoader(*original_consumer, *client, task_runner); BytesConsumer& consumer = body_loader->DrainAsBytesConsumer(); const char* buffer = nullptr; @@ -920,8 +934,9 @@ TEST_F(ResponseBodyLoaderDrainedBytesConsumerNotificationOutOfOnStateChangeTest, original_consumer->Add(Command(Command::kError)); auto* client = MakeGarbageCollected<TestClient>(); - auto* body_loader = MakeGarbageCollected<ResponseBodyLoader>( - *original_consumer, *client, task_runner); + + auto* body_loader = + MakeResponseBodyLoader(*original_consumer, *client, task_runner); BytesConsumer& consumer = body_loader->DrainAsBytesConsumer(); const char* buffer = nullptr; @@ -972,8 +987,9 @@ TEST_F(ResponseBodyLoaderDrainedBytesConsumerNotificationOutOfOnStateChangeTest, original_consumer->Add(Command(Command::kDataAndDone, "hello")); auto* client = MakeGarbageCollected<TestClient>(); - auto* body_loader = MakeGarbageCollected<ResponseBodyLoader>( - *original_consumer, *client, task_runner); + + auto* body_loader = + MakeResponseBodyLoader(*original_consumer, *client, task_runner); BytesConsumer& consumer = body_loader->DrainAsBytesConsumer(); const char* buffer = nullptr; @@ -1019,7 +1035,7 @@ TEST_F(ResponseBodyLoaderDrainedBytesConsumerNotificationOutOfOnStateChangeTest, DrainAsDataPipe) { mojo::ScopedDataPipeConsumerHandle consumer_end; mojo::ScopedDataPipeProducerHandle producer_end; - auto result = mojo::CreateDataPipe(nullptr, &producer_end, &consumer_end); + auto result = mojo::CreateDataPipe(nullptr, producer_end, consumer_end); ASSERT_EQ(result, MOJO_RESULT_OK); @@ -1030,8 +1046,8 @@ TEST_F(ResponseBodyLoaderDrainedBytesConsumerNotificationOutOfOnStateChangeTest, task_runner, std::move(consumer_end), &completion_notifier); auto* client = MakeGarbageCollected<TestClient>(); - auto* body_loader = MakeGarbageCollected<ResponseBodyLoader>( - *original_consumer, *client, task_runner); + auto* body_loader = + MakeResponseBodyLoader(*original_consumer, *client, task_runner); BytesConsumer& consumer = body_loader->DrainAsBytesConsumer(); @@ -1058,8 +1074,9 @@ TEST_F(ResponseBodyLoaderDrainedBytesConsumerNotificationOutOfOnStateChangeTest, original_consumer->Add(Command(Command::kWait)); auto* client = MakeGarbageCollected<TestClient>(); - auto* body_loader = MakeGarbageCollected<ResponseBodyLoader>( - *original_consumer, *client, task_runner); + + auto* body_loader = + MakeResponseBodyLoader(*original_consumer, *client, task_runner); BytesConsumer& consumer = body_loader->DrainAsBytesConsumer(); task_runner->RunUntilIdle(); @@ -1087,8 +1104,9 @@ TEST_F(ResponseBodyLoaderDrainedBytesConsumerNotificationInOnStateChangeTest, original_consumer->Add(Command(Command::kDone)); auto* client = MakeGarbageCollected<TestClient>(); - auto* body_loader = MakeGarbageCollected<ResponseBodyLoader>( - *original_consumer, *client, task_runner); + + auto* body_loader = + MakeResponseBodyLoader(*original_consumer, *client, task_runner); BytesConsumer& consumer = body_loader->DrainAsBytesConsumer(); auto* reading_client = MakeGarbageCollected<ReadingClient>(consumer, *client); @@ -1127,8 +1145,9 @@ TEST_F(ResponseBodyLoaderDrainedBytesConsumerNotificationInOnStateChangeTest, original_consumer->Add(Command(Command::kError)); auto* client = MakeGarbageCollected<TestClient>(); - auto* body_loader = MakeGarbageCollected<ResponseBodyLoader>( - *original_consumer, *client, task_runner); + + auto* body_loader = + MakeResponseBodyLoader(*original_consumer, *client, task_runner); BytesConsumer& consumer = body_loader->DrainAsBytesConsumer(); auto* reading_client = MakeGarbageCollected<ReadingClient>(consumer, *client); @@ -1167,8 +1186,9 @@ TEST_F(ResponseBodyLoaderDrainedBytesConsumerNotificationInOnStateChangeTest, original_consumer->Add(Command(Command::kDataAndDone, "hahaha")); auto* client = MakeGarbageCollected<TestClient>(); - auto* body_loader = MakeGarbageCollected<ResponseBodyLoader>( - *original_consumer, *client, task_runner); + + auto* body_loader = + MakeResponseBodyLoader(*original_consumer, *client, task_runner); BytesConsumer& consumer = body_loader->DrainAsBytesConsumer(); auto* reading_client = MakeGarbageCollected<ReadingClient>(consumer, *client); 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 index 6f58aa7b447..399d8302dd7 100644 --- 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 @@ -30,6 +30,7 @@ FetchParameters ScriptFetchOptions::CreateFetchParameters( FetchParameters params(std::move(resource_request), resource_loader_options); params.SetRequestContext(mojom::blink::RequestContextType::SCRIPT); params.SetRequestDestination(network::mojom::RequestDestination::kScript); + params.SetRenderBlockingBehavior(render_blocking_behavior_); // Step 1. ... and CORS setting. [spec text] if (cross_origin != kCrossOriginAttributeNotSet) 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 index 13a4cbd2c6f..74cff0cd404 100644 --- 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 @@ -11,6 +11,7 @@ #include "third_party/blink/renderer/platform/loader/fetch/cross_origin_attribute_value.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/render_blocking_behavior.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/allocator/allocator.h" @@ -47,6 +48,7 @@ class PLATFORM_EXPORT ScriptFetchOptions final { network::mojom::CredentialsMode credentials_mode, network::mojom::ReferrerPolicy referrer_policy, mojom::FetchImportanceMode importance, + RenderBlockingBehavior render_blocking_behavior, RejectCoepUnsafeNone reject_coep_unsafe_none = RejectCoepUnsafeNone(false)) : nonce_(nonce), @@ -56,6 +58,7 @@ class PLATFORM_EXPORT ScriptFetchOptions final { credentials_mode_(credentials_mode), referrer_policy_(referrer_policy), importance_(importance), + render_blocking_behavior_(render_blocking_behavior), reject_coep_unsafe_none_(reject_coep_unsafe_none) {} ~ScriptFetchOptions() = default; @@ -111,6 +114,8 @@ class PLATFORM_EXPORT ScriptFetchOptions final { // "importance" member to the script fetch options struct. const mojom::FetchImportanceMode importance_; + const RenderBlockingBehavior render_blocking_behavior_ = + RenderBlockingBehavior::kUnset; // True when we should reject a response with COEP: none. // https://wicg.github.io/cross-origin-embedder-policy/#integration-html // This is for dedicated workers. diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/stale_revalidation_resource_client.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/stale_revalidation_resource_client.cc index 2d1bb0e5f9b..2a3447539ff 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/stale_revalidation_resource_client.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/stale_revalidation_resource_client.cc @@ -11,7 +11,7 @@ namespace blink { StaleRevalidationResourceClient::StaleRevalidationResourceClient( Resource* stale_resource) - : start_time_(base::TimeTicks::Now()), stale_resource_(stale_resource) {} + : stale_resource_(stale_resource) {} StaleRevalidationResourceClient::~StaleRevalidationResourceClient() = default; @@ -20,13 +20,6 @@ void StaleRevalidationResourceClient::NotifyFinished(Resource* resource) { if (stale_resource_ && IsMainThread()) GetMemoryCache()->Remove(stale_resource_); ClearResource(); - - base::TimeTicks response_end = resource->LoadResponseEnd(); - if (!response_end.is_null()) { - UMA_HISTOGRAM_LONG_TIMES( - "Blink.ResourceFetcher.StaleWhileRevalidateDuration", - response_end - start_time_); - } } void StaleRevalidationResourceClient::Trace(Visitor* visitor) const { diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/stale_revalidation_resource_client.h b/chromium/third_party/blink/renderer/platform/loader/fetch/stale_revalidation_resource_client.h index 889f68ba8c1..036b13a7bab 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/stale_revalidation_resource_client.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/stale_revalidation_resource_client.h @@ -26,7 +26,6 @@ class StaleRevalidationResourceClient String DebugName() const override; private: - base::TimeTicks start_time_; // |stale_resource_| is the original resource that will be removed from the // MemoryCache when this revalidation request is completed. Note that it is // different than the active resource for this resource client which accessed diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/subresource_web_bundle.h b/chromium/third_party/blink/renderer/platform/loader/fetch/subresource_web_bundle.h index 2971478b1b4..e44eaffab65 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/subresource_web_bundle.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/subresource_web_bundle.h @@ -10,20 +10,24 @@ #include "third_party/blink/renderer/platform/heap/garbage_collected.h" #include "third_party/blink/renderer/platform/platform_export.h" +namespace base { +class UnguessableToken; +} + namespace blink { class KURL; -// SubresourceWebBundle is attached to ResourceFetcher and used to intercept -// subresource requests for a certain set of URLs and serve responses from a +// SubresourceWebBundle is attached to ResourceFetcher and used to set +// WebBundleToken to subresource requests which should be served from a // WebBundle. This is used for Subresource loading with Web Bundles // (https://github.com/WICG/webpackage/blob/master/explainers/subresource-loading.md). class PLATFORM_EXPORT SubresourceWebBundle : public GarbageCollectedMixin { public: void Trace(Visitor* visitor) const override {} virtual bool CanHandleRequest(const KURL& url) const = 0; - virtual mojo::PendingRemote<network::mojom::blink::URLLoaderFactory> - GetURLLoaderFactory() = 0; + virtual const KURL& GetBundleUrl() const = 0; + virtual const base::UnguessableToken& WebBundleToken() const = 0; virtual String GetCacheIdentifier() const = 0; }; diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/DEPS b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/DEPS index cb1e4f6caaa..d1edc9d3dec 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/DEPS +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/DEPS @@ -1,14 +1,31 @@ +include_rules = [ + "+base/atomic_sequence_num.h", + "+net/base/registry_controlled_domains/registry_controlled_domain.h", + "+net/base/request_priority.h", + "+net/base/host_port_pair.h", + "+net/cert/cert_status_flags.h", + "+net/cert/ct_sct_to_string.h", + "+net/cert/x509_certificate.h", + "+net/cert/x509_util.h", + "+net/ssl/ssl_cipher_suite_names.h", + "+net/ssl/ssl_connection_status_flags.h", + "+net/ssl/ssl_info.h", + "+net/traffic_annotation/network_traffic_annotation.h", + "+net/url_request/referrer_policy.h", + "+services/network/public/mojom/url_loader.mojom.h", + "+services/network/public/mojom/url_response_head.mojom.h", + "+third_party/blink/public/mojom/loader/code_cache.mojom-shared.h" +] + specific_include_rules = { "request_conversion.cc" : [ "+media/media_buildflags.h" ], - "web_bundle_subresource_loader.cc" : [ - "+components/web_package", - "+net/http/http_status_code.h", + "sync_load_context_unittest.cc": [ + "+base/threading/thread.h", + "+services/network/test/test_url_loader_factory.h" ], - "web_bundle_subresource_loader_test.cc" : [ - "+components/web_package", - "+net/traffic_annotation/network_traffic_annotation_test_helper.h", - "+services/network/test/test_url_loader_client.h", + "web_url_loader_unittest.cc": [ + "+net/test/cert_test_util.h" ], } diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/mojo_url_loader_client.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/mojo_url_loader_client.cc new file mode 100644 index 00000000000..18ae7873f82 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/mojo_url_loader_client.cc @@ -0,0 +1,594 @@ +// 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/url_loader/mojo_url_loader_client.h" + +#include <iterator> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/feature_list.h" +#include "base/metrics/histogram_macros.h" +#include "base/single_thread_task_runner.h" +#include "mojo/public/cpp/system/data_pipe_drainer.h" +#include "net/url_request/redirect_info.h" +#include "services/network/public/cpp/features.h" +#include "services/network/public/mojom/url_response_head.mojom.h" +#include "third_party/blink/public/common/features.h" +#include "third_party/blink/public/mojom/frame/back_forward_cache_controller.mojom.h" +#include "third_party/blink/public/platform/platform.h" +#include "third_party/blink/public/platform/web_back_forward_cache_loader_helper.h" +#include "third_party/blink/public/platform/web_resource_request_sender.h" +#include "third_party/blink/renderer/platform/back_forward_cache_utils.h" +#include "third_party/blink/renderer/platform/loader/fetch/back_forward_cache_loader_helper.h" +#include "third_party/blink/renderer/platform/wtf/functional.h" + +namespace blink { +namespace { + +constexpr size_t kDefaultMaxBufferedBodyBytesPerRequest = 100 * 1000; +constexpr base::TimeDelta kGracePeriodToFinishLoadingWhileInBackForwardCache = + base::TimeDelta::FromSeconds(15); + +} // namespace + +class MojoURLLoaderClient::DeferredMessage { + public: + DeferredMessage() = default; + virtual void HandleMessage( + WebResourceRequestSender* resource_request_sender) = 0; + virtual bool IsCompletionMessage() const = 0; + virtual ~DeferredMessage() = default; + + private: + DISALLOW_COPY_AND_ASSIGN(DeferredMessage); +}; + +class MojoURLLoaderClient::DeferredOnReceiveResponse final + : public DeferredMessage { + public: + explicit DeferredOnReceiveResponse( + network::mojom::URLResponseHeadPtr response_head) + : response_head_(std::move(response_head)) {} + + void HandleMessage( + WebResourceRequestSender* resource_request_sender) override { + resource_request_sender->OnReceivedResponse(std::move(response_head_)); + } + bool IsCompletionMessage() const override { return false; } + + private: + network::mojom::URLResponseHeadPtr response_head_; +}; + +class MojoURLLoaderClient::DeferredOnReceiveRedirect final + : public DeferredMessage { + public: + DeferredOnReceiveRedirect( + const net::RedirectInfo& redirect_info, + network::mojom::URLResponseHeadPtr response_head, + scoped_refptr<base::SingleThreadTaskRunner> task_runner) + : redirect_info_(redirect_info), + response_head_(std::move(response_head)), + task_runner_(std::move(task_runner)) {} + + void HandleMessage( + WebResourceRequestSender* resource_request_sender) override { + resource_request_sender->OnReceivedRedirect( + redirect_info_, std::move(response_head_), task_runner_); + } + bool IsCompletionMessage() const override { return false; } + + private: + const net::RedirectInfo redirect_info_; + network::mojom::URLResponseHeadPtr response_head_; + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; +}; + +class MojoURLLoaderClient::DeferredOnUploadProgress final + : public DeferredMessage { + public: + DeferredOnUploadProgress(int64_t current, int64_t total) + : current_(current), total_(total) {} + + void HandleMessage( + WebResourceRequestSender* resource_request_sender) override { + resource_request_sender->OnUploadProgress(current_, total_); + } + bool IsCompletionMessage() const override { return false; } + + private: + const int64_t current_; + const int64_t total_; +}; + +class MojoURLLoaderClient::DeferredOnReceiveCachedMetadata final + : public DeferredMessage { + public: + explicit DeferredOnReceiveCachedMetadata(mojo_base::BigBuffer data) + : data_(std::move(data)) {} + + void HandleMessage( + WebResourceRequestSender* resource_request_sender) override { + resource_request_sender->OnReceivedCachedMetadata(std::move(data_)); + } + bool IsCompletionMessage() const override { return false; } + + private: + mojo_base::BigBuffer data_; +}; + +class MojoURLLoaderClient::DeferredOnStartLoadingResponseBody final + : public DeferredMessage { + public: + explicit DeferredOnStartLoadingResponseBody( + mojo::ScopedDataPipeConsumerHandle body) + : body_(std::move(body)) {} + + void HandleMessage( + WebResourceRequestSender* resource_request_sender) override { + resource_request_sender->OnStartLoadingResponseBody(std::move(body_)); + } + bool IsCompletionMessage() const override { return false; } + + private: + mojo::ScopedDataPipeConsumerHandle body_; +}; + +class MojoURLLoaderClient::DeferredOnComplete final : public DeferredMessage { + public: + explicit DeferredOnComplete(const network::URLLoaderCompletionStatus& status) + : status_(status) {} + + void HandleMessage( + WebResourceRequestSender* resource_request_sender) override { + resource_request_sender->OnRequestComplete(status_); + } + bool IsCompletionMessage() const override { return true; } + + private: + const network::URLLoaderCompletionStatus status_; +}; + +class MojoURLLoaderClient::BodyBuffer final + : public mojo::DataPipeDrainer::Client { + public: + BodyBuffer(MojoURLLoaderClient* owner, + mojo::ScopedDataPipeConsumerHandle readable, + mojo::ScopedDataPipeProducerHandle writable, + scoped_refptr<base::SingleThreadTaskRunner> task_runner) + : owner_(owner), + writable_(std::move(writable)), + writable_watcher_(FROM_HERE, + mojo::SimpleWatcher::ArmingPolicy::MANUAL, + std::move(task_runner)), + max_bytes_drained_(GetLoadingTasksUnfreezableParamAsInt( + "max_buffered_bytes", + kDefaultMaxBufferedBodyBytesPerRequest)) { + pipe_drainer_ = + std::make_unique<mojo::DataPipeDrainer>(this, std::move(readable)); + writable_watcher_.Watch( + writable_.get(), + MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + base::BindRepeating(&BodyBuffer::WriteBufferedBody, + base::Unretained(this))); + } + + bool active() const { return writable_watcher_.IsWatching(); } + + // mojo::DataPipeDrainer::Client + void OnDataAvailable(const void* data, size_t num_bytes) override { + DCHECK(draining_); + SCOPED_CRASH_KEY_NUMBER("OnDataAvailable", "buffered_body_size", + buffered_body_.size()); + SCOPED_CRASH_KEY_NUMBER("OnDataAvailable", "data_bytes", num_bytes); + SCOPED_CRASH_KEY_STRING256("OnDataAvailable", "last_loaded_url", + owner_->last_loaded_url().GetString().Utf8()); + + total_bytes_drained_ += num_bytes; + TRACE_EVENT2("loading", "MojoURLLoaderClient::BodyBuffer::OnDataAvailable", + "total_bytes_drained", static_cast<int>(total_bytes_drained_), + "added_bytes", static_cast<int>(num_bytes)); + + if (owner_->IsDeferredWithBackForwardCache()) { + owner_->DidBufferLoadWhileInBackForwardCache(num_bytes); + if (total_bytes_drained_ > max_bytes_drained_ || + !owner_->CanContinueBufferingWhileInBackForwardCache()) { + owner_->EvictFromBackForwardCache( + blink::mojom::RendererEvictionReason::kNetworkExceedsBufferLimit); + return; + } + } + buffered_body_.emplace(static_cast<const char*>(data), + static_cast<const char*>(data) + num_bytes); + WriteBufferedBody(MOJO_RESULT_OK); + } + + void OnDataComplete() override { + DCHECK(draining_); + draining_ = false; + WriteBufferedBody(MOJO_RESULT_OK); + } + + private: + void WriteBufferedBody(MojoResult) { + // Try to write all the remaining chunks in |buffered_body_|. + while (!buffered_body_.empty()) { + // Write the chunk at the front of |buffered_body_|. + const std::vector<char>& current_chunk = buffered_body_.front(); + DCHECK_LE(offset_in_current_chunk_, current_chunk.size()); + uint32_t bytes_sent = base::saturated_cast<uint32_t>( + current_chunk.size() - offset_in_current_chunk_); + MojoResult result = + writable_->WriteData(current_chunk.data() + offset_in_current_chunk_, + &bytes_sent, MOJO_WRITE_DATA_FLAG_NONE); + switch (result) { + case MOJO_RESULT_OK: + break; + case MOJO_RESULT_FAILED_PRECONDITION: + // The pipe is closed unexpectedly, finish writing now. + draining_ = false; + Finish(); + return; + case MOJO_RESULT_SHOULD_WAIT: + writable_watcher_.ArmOrNotify(); + return; + default: + NOTREACHED(); + return; + } + // We've sent |bytes_sent| bytes, update the current offset in the + // frontmost chunk. + offset_in_current_chunk_ += bytes_sent; + DCHECK_LE(offset_in_current_chunk_, current_chunk.size()); + if (offset_in_current_chunk_ == current_chunk.size()) { + // We've finished writing the chunk at the front of the queue, pop it so + // that we'll write the next chunk next time. + buffered_body_.pop(); + offset_in_current_chunk_ = 0; + } + } + // We're finished if we've drained the original pipe and sent all the + // buffered body. + if (!draining_) + Finish(); + } + + void Finish() { + DCHECK(!draining_); + // We've read and written all the data from the original pipe. + writable_watcher_.Cancel(); + writable_.reset(); + // There might be a deferred OnComplete message waiting for us to finish + // draining the response body, so flush the deferred messages in + // the owner MojoURLLoaderClient. + owner_->FlushDeferredMessages(); + } + + MojoURLLoaderClient* const owner_; + mojo::ScopedDataPipeProducerHandle writable_; + mojo::SimpleWatcher writable_watcher_; + std::unique_ptr<mojo::DataPipeDrainer> pipe_drainer_; + // We save the received response body as a queue of chunks so that we can free + // memory as soon as we finish sending a chunk completely. + base::queue<std::vector<char>> buffered_body_; + uint32_t offset_in_current_chunk_ = 0; + size_t total_bytes_drained_ = 0; + const size_t max_bytes_drained_; + bool draining_ = true; +}; + +MojoURLLoaderClient::MojoURLLoaderClient( + WebResourceRequestSender* resource_request_sender, + scoped_refptr<base::SingleThreadTaskRunner> task_runner, + bool bypass_redirect_checks, + const GURL& request_url, + WebBackForwardCacheLoaderHelper back_forward_cache_loader_helper) + : back_forward_cache_timeout_( + base::TimeDelta::FromSeconds(GetLoadingTasksUnfreezableParamAsInt( + "grace_period_to_finish_loading_in_seconds", + static_cast<int>( + kGracePeriodToFinishLoadingWhileInBackForwardCache + .InSeconds())))), + resource_request_sender_(resource_request_sender), + task_runner_(std::move(task_runner)), + bypass_redirect_checks_(bypass_redirect_checks), + last_loaded_url_(request_url), + back_forward_cache_loader_helper_(back_forward_cache_loader_helper) {} + +MojoURLLoaderClient::~MojoURLLoaderClient() = default; + +void MojoURLLoaderClient::SetDefersLoading(WebURLLoader::DeferType value) { + deferred_state_ = value; + if (value == WebURLLoader::DeferType::kNotDeferred) { + StopBackForwardCacheEvictionTimer(); + task_runner_->PostTask( + FROM_HERE, WTF::Bind(&MojoURLLoaderClient::FlushDeferredMessages, + weak_factory_.GetWeakPtr())); + } else if (IsDeferredWithBackForwardCache() && !has_received_complete_ && + !back_forward_cache_eviction_timer_.IsRunning()) { + // We should evict the page associated with this load if the connection + // takes too long until it either finished or failed. + back_forward_cache_eviction_timer_.SetTaskRunner(task_runner_); + back_forward_cache_eviction_timer_.Start( + FROM_HERE, back_forward_cache_timeout_, + WTF::Bind(&MojoURLLoaderClient::EvictFromBackForwardCacheDueToTimeout, + weak_factory_.GetWeakPtr())); + } +} + +void MojoURLLoaderClient::OnReceiveResponse( + network::mojom::URLResponseHeadPtr response_head) { + TRACE_EVENT1("loading", "MojoURLLoaderClient::OnReceiveResponse", "url", + last_loaded_url_.GetString().Utf8()); + + has_received_response_head_ = true; + on_receive_response_time_ = base::TimeTicks::Now(); + + if (NeedsStoringMessage()) { + StoreAndDispatch( + std::make_unique<DeferredOnReceiveResponse>(std::move(response_head))); + } else { + resource_request_sender_->OnReceivedResponse(std::move(response_head)); + } +} + +BackForwardCacheLoaderHelper* +MojoURLLoaderClient::GetBackForwardCacheLoaderHelper() { + return back_forward_cache_loader_helper_.GetBackForwardCacheLoaderHelper(); +} + +void MojoURLLoaderClient::EvictFromBackForwardCache( + blink::mojom::RendererEvictionReason reason) { + StopBackForwardCacheEvictionTimer(); + auto* back_forward_cache_loader_helper = GetBackForwardCacheLoaderHelper(); + if (!back_forward_cache_loader_helper) + return; + back_forward_cache_loader_helper->EvictFromBackForwardCache(reason); +} + +void MojoURLLoaderClient::DidBufferLoadWhileInBackForwardCache( + size_t num_bytes) { + auto* back_forward_cache_loader_helper = GetBackForwardCacheLoaderHelper(); + if (!back_forward_cache_loader_helper) + return; + back_forward_cache_loader_helper->DidBufferLoadWhileInBackForwardCache( + num_bytes); +} + +bool MojoURLLoaderClient::CanContinueBufferingWhileInBackForwardCache() { + auto* back_forward_cache_loader_helper = GetBackForwardCacheLoaderHelper(); + if (!back_forward_cache_loader_helper) + return false; + return back_forward_cache_loader_helper + ->CanContinueBufferingWhileInBackForwardCache(); +} + +void MojoURLLoaderClient::EvictFromBackForwardCacheDueToTimeout() { + EvictFromBackForwardCache( + blink::mojom::RendererEvictionReason::kNetworkRequestTimeout); +} + +void MojoURLLoaderClient::StopBackForwardCacheEvictionTimer() { + back_forward_cache_eviction_timer_.Stop(); +} + +void MojoURLLoaderClient::OnReceiveRedirect( + const net::RedirectInfo& redirect_info, + network::mojom::URLResponseHeadPtr response_head) { + DCHECK(!has_received_response_head_); + if (deferred_state_ == + blink::WebURLLoader::DeferType::kDeferredWithBackForwardCache) { + EvictFromBackForwardCache( + blink::mojom::RendererEvictionReason::kNetworkRequestRedirected); + + OnComplete(network::URLLoaderCompletionStatus(net::ERR_ABORTED)); + return; + } + if (!bypass_redirect_checks_ && + !Platform::Current()->IsRedirectSafe(last_loaded_url_, + redirect_info.new_url)) { + OnComplete(network::URLLoaderCompletionStatus(net::ERR_UNSAFE_REDIRECT)); + return; + } + + last_loaded_url_ = KURL(redirect_info.new_url); + if (NeedsStoringMessage()) { + StoreAndDispatch(std::make_unique<DeferredOnReceiveRedirect>( + redirect_info, std::move(response_head), task_runner_)); + } else { + resource_request_sender_->OnReceivedRedirect( + redirect_info, std::move(response_head), task_runner_); + } +} + +void MojoURLLoaderClient::OnUploadProgress( + int64_t current_position, + int64_t total_size, + OnUploadProgressCallback ack_callback) { + if (NeedsStoringMessage()) { + StoreAndDispatch(std::make_unique<DeferredOnUploadProgress>( + current_position, total_size)); + } else { + resource_request_sender_->OnUploadProgress(current_position, total_size); + } + std::move(ack_callback).Run(); +} + +void MojoURLLoaderClient::OnReceiveCachedMetadata(mojo_base::BigBuffer data) { + if (NeedsStoringMessage()) { + StoreAndDispatch( + std::make_unique<DeferredOnReceiveCachedMetadata>(std::move(data))); + } else { + resource_request_sender_->OnReceivedCachedMetadata(std::move(data)); + } +} + +void MojoURLLoaderClient::OnTransferSizeUpdated(int32_t transfer_size_diff) { + if (NeedsStoringMessage()) { + accumulated_transfer_size_diff_during_deferred_ += transfer_size_diff; + } else { + resource_request_sender_->OnTransferSizeUpdated(transfer_size_diff); + } +} + +void MojoURLLoaderClient::OnStartLoadingResponseBody( + mojo::ScopedDataPipeConsumerHandle body) { + TRACE_EVENT1("loading", "MojoURLLoaderClient::OnStartLoadingResponseBody", + "url", last_loaded_url_.GetString().Utf8()); + + DCHECK(has_received_response_head_); + DCHECK(!has_received_response_body_); + has_received_response_body_ = true; + + if (!on_receive_response_time_.is_null()) { + UMA_HISTOGRAM_TIMES( + "Renderer.OnReceiveResponseToOnStartLoadingResponseBody", + base::TimeTicks::Now() - on_receive_response_time_); + } + + if (!NeedsStoringMessage()) { + // Send the message immediately. + resource_request_sender_->OnStartLoadingResponseBody(std::move(body)); + return; + } + + if (deferred_state_ != + blink::WebURLLoader::DeferType::kDeferredWithBackForwardCache) { + // Defer the message, storing the original body pipe. + StoreAndDispatch( + std::make_unique<DeferredOnStartLoadingResponseBody>(std::move(body))); + return; + } + + DCHECK(IsInflightNetworkRequestBackForwardCacheSupportEnabled()); + // We want to run loading tasks while deferred (but without dispatching the + // messages). Drain the original pipe containing the response body into a + // new pipe so that we won't block the network service if we're deferred for + // a long time. + mojo::ScopedDataPipeProducerHandle new_body_producer; + mojo::ScopedDataPipeConsumerHandle new_body_consumer; + MojoResult result = + mojo::CreateDataPipe(nullptr, new_body_producer, new_body_consumer); + if (result != MOJO_RESULT_OK) { + OnComplete( + network::URLLoaderCompletionStatus(net::ERR_INSUFFICIENT_RESOURCES)); + return; + } + body_buffer_ = std::make_unique<BodyBuffer>( + this, std::move(body), std::move(new_body_producer), task_runner_); + + StoreAndDispatch(std::make_unique<DeferredOnStartLoadingResponseBody>( + std::move(new_body_consumer))); +} + +void MojoURLLoaderClient::OnComplete( + const network::URLLoaderCompletionStatus& status) { + has_received_complete_ = true; + StopBackForwardCacheEvictionTimer(); + + // Dispatch completion status to the WebResourceRequestSender. + // Except for errors, there must always be a response's body. + DCHECK(has_received_response_body_ || status.error_code != net::OK); + if (NeedsStoringMessage()) { + StoreAndDispatch(std::make_unique<DeferredOnComplete>(status)); + } else { + resource_request_sender_->OnRequestComplete(status); + } +} + +bool MojoURLLoaderClient::NeedsStoringMessage() const { + return deferred_state_ != WebURLLoader::DeferType::kNotDeferred || + deferred_messages_.size() > 0 || + accumulated_transfer_size_diff_during_deferred_ > 0; +} + +void MojoURLLoaderClient::StoreAndDispatch( + std::unique_ptr<DeferredMessage> message) { + DCHECK(NeedsStoringMessage()); + if (deferred_state_ != WebURLLoader::DeferType::kNotDeferred) { + deferred_messages_.emplace_back(std::move(message)); + } else if (deferred_messages_.size() > 0 || + accumulated_transfer_size_diff_during_deferred_ > 0) { + deferred_messages_.emplace_back(std::move(message)); + FlushDeferredMessages(); + } else { + NOTREACHED(); + } +} + +void MojoURLLoaderClient::OnConnectionClosed() { + // If the connection aborts before the load completes, mark it as aborted. + if (!has_received_complete_) { + OnComplete(network::URLLoaderCompletionStatus(net::ERR_ABORTED)); + return; + } +} + +void MojoURLLoaderClient::FlushDeferredMessages() { + if (deferred_state_ != WebURLLoader::DeferType::kNotDeferred) + return; + WebVector<std::unique_ptr<DeferredMessage>> messages; + messages.Swap(deferred_messages_); + bool has_completion_message = false; + base::WeakPtr<MojoURLLoaderClient> weak_this = weak_factory_.GetWeakPtr(); + // First, dispatch all messages excluding the followings: + // - transfer size change + // - completion + // These two types of messages are dispatched later. + for (size_t index = 0; index < messages.size(); ++index) { + if (messages[index]->IsCompletionMessage()) { + // The completion message arrives at the end of the message queue. + DCHECK(!has_completion_message); + DCHECK_EQ(index, messages.size() - 1); + has_completion_message = true; + break; + } + + messages[index]->HandleMessage(resource_request_sender_); + if (!weak_this) + return; + if (deferred_state_ != WebURLLoader::DeferType::kNotDeferred) { + deferred_messages_.reserve(messages.size() - index - 1); + for (size_t i = index + 1; i < messages.size(); ++i) + deferred_messages_.emplace_back(std::move(messages[i])); + return; + } + } + + // Dispatch the transfer size update. + if (accumulated_transfer_size_diff_during_deferred_ > 0) { + auto transfer_size_diff = accumulated_transfer_size_diff_during_deferred_; + accumulated_transfer_size_diff_during_deferred_ = 0; + resource_request_sender_->OnTransferSizeUpdated(transfer_size_diff); + if (!weak_this) + return; + if (deferred_state_ != WebURLLoader::DeferType::kNotDeferred) { + if (has_completion_message) { + DCHECK_GT(messages.size(), 0u); + DCHECK(messages.back()->IsCompletionMessage()); + deferred_messages_.emplace_back(std::move(messages.back())); + } + return; + } + } + + // Dispatch the completion message. + if (has_completion_message) { + DCHECK_GT(messages.size(), 0u); + DCHECK(messages.back()->IsCompletionMessage()); + if (body_buffer_ && body_buffer_->active()) { + // If we still have an active body buffer, it means we haven't drained all + // of the contents of the response body yet. We shouldn't dispatch the + // completion message now, so + // put the message back into |deferred_messages_| to be sent later after + // the body buffer is no longer active. + deferred_messages_.emplace_back(std::move(messages.back())); + return; + } + messages.back()->HandleMessage(resource_request_sender_); + } +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/mojo_url_loader_client.h b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/mojo_url_loader_client.h new file mode 100644 index 00000000000..d2386cda8c2 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/mojo_url_loader_client.h @@ -0,0 +1,128 @@ +// 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_URL_LOADER_MOJO_URL_LOADER_CLIENT_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_URL_LOADER_MOJO_URL_LOADER_CLIENT_H_ + +#include <stdint.h> +#include <vector> + +#include "base/callback_forward.h" +#include "base/memory/scoped_refptr.h" +#include "base/memory/weak_ptr.h" +#include "base/time/time.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "mojo/public/cpp/system/data_pipe.h" +#include "services/network/public/mojom/url_loader.mojom.h" +#include "third_party/blink/public/mojom/frame/back_forward_cache_controller.mojom-forward.h" +#include "third_party/blink/public/platform/web_back_forward_cache_loader_helper.h" +#include "third_party/blink/public/platform/web_common.h" +#include "third_party/blink/public/platform/web_url_loader.h" +#include "third_party/blink/public/platform/web_vector.h" +#include "third_party/blink/renderer/platform/weborigin/kurl.h" + +namespace base { +class SingleThreadTaskRunner; +} // namespace base + +namespace net { +struct RedirectInfo; +} // namespace net + +namespace network { +struct URLLoaderCompletionStatus; +} // namespace network + +namespace blink { +class BackForwardCacheLoaderHelper; +class WebResourceRequestSender; + +// MojoURLLoaderClient is an implementation of +// network::mojom::URLLoaderClient to receive messages from a single URLLoader. +class BLINK_PLATFORM_EXPORT MojoURLLoaderClient final + : public network::mojom::URLLoaderClient { + public: + MojoURLLoaderClient( + WebResourceRequestSender* resource_request_sender, + scoped_refptr<base::SingleThreadTaskRunner> task_runner, + bool bypass_redirect_checks, + const GURL& request_url, + WebBackForwardCacheLoaderHelper back_forward_cache_loader_helper); + ~MojoURLLoaderClient() override; + + // Set the defer status. If loading is deferred, received messages are not + // dispatched to clients until it is set not deferred. + void SetDefersLoading(WebURLLoader::DeferType value); + + // network::mojom::URLLoaderClient implementation + void OnReceiveResponse( + network::mojom::URLResponseHeadPtr response_head) override; + void OnReceiveRedirect( + const net::RedirectInfo& redirect_info, + network::mojom::URLResponseHeadPtr response_head) override; + void OnUploadProgress(int64_t current_position, + int64_t total_size, + OnUploadProgressCallback ack_callback) override; + void OnReceiveCachedMetadata(mojo_base::BigBuffer data) override; + void OnTransferSizeUpdated(int32_t transfer_size_diff) override; + void OnStartLoadingResponseBody( + mojo::ScopedDataPipeConsumerHandle body) override; + void OnComplete(const network::URLLoaderCompletionStatus& status) override; + + void EvictFromBackForwardCache(blink::mojom::RendererEvictionReason reason); + void DidBufferLoadWhileInBackForwardCache(size_t num_bytes); + bool CanContinueBufferingWhileInBackForwardCache(); + bool IsDeferredWithBackForwardCache() { + return deferred_state_ == + blink::WebURLLoader::DeferType::kDeferredWithBackForwardCache; + } + + private: + class BodyBuffer; + class DeferredMessage; + class DeferredOnReceiveResponse; + class DeferredOnReceiveRedirect; + class DeferredOnUploadProgress; + class DeferredOnReceiveCachedMetadata; + class DeferredOnStartLoadingResponseBody; + class DeferredOnComplete; + + bool NeedsStoringMessage() const; + void StoreAndDispatch(std::unique_ptr<DeferredMessage> message); + void OnConnectionClosed(); + const KURL& last_loaded_url() const { return last_loaded_url_; } + + // Dispatches the messages received after SetDefersLoading is called. + void FlushDeferredMessages(); + + void EvictFromBackForwardCacheDueToTimeout(); + void StopBackForwardCacheEvictionTimer(); + BackForwardCacheLoaderHelper* GetBackForwardCacheLoaderHelper(); + + WebVector<std::unique_ptr<DeferredMessage>> deferred_messages_; + std::unique_ptr<BodyBuffer> body_buffer_; + base::OneShotTimer back_forward_cache_eviction_timer_; + base::TimeDelta back_forward_cache_timeout_; + bool has_received_response_head_ = false; + bool has_received_response_body_ = false; + bool has_received_complete_ = false; + WebURLLoader::DeferType deferred_state_ = + WebURLLoader::DeferType::kNotDeferred; + int32_t accumulated_transfer_size_diff_during_deferred_ = 0; + WebResourceRequestSender* const resource_request_sender_; + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; + bool bypass_redirect_checks_ = false; + KURL last_loaded_url_; + WebBackForwardCacheLoaderHelper back_forward_cache_loader_helper_; + + // For UMA. + base::TimeTicks on_receive_response_time_; + + base::WeakPtrFactory<MojoURLLoaderClient> weak_factory_{this}; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_URL_LOADER_MOJO_URL_LOADER_CLIENT_H_ diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/mojo_url_loader_client_unittest.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/mojo_url_loader_client_unittest.cc new file mode 100644 index 00000000000..caad3c87d67 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/mojo_url_loader_client_unittest.cc @@ -0,0 +1,898 @@ +// 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/url_loader/mojo_url_loader_client.h" + +#include <vector> +#include "base/run_loop.h" +#include "base/test/scoped_feature_list.h" +#include "base/test/task_environment.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "net/traffic_annotation/network_traffic_annotation_test_helper.h" +#include "net/url_request/redirect_info.h" +#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" +#include "services/network/public/mojom/url_loader_factory.mojom.h" +#include "services/network/public/mojom/url_response_head.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/common/features.h" +#include "third_party/blink/public/common/loader/throttling_url_loader.h" +#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h" +#include "third_party/blink/public/platform/resource_load_info_notifier_wrapper.h" +#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h" +#include "third_party/blink/public/platform/web_back_forward_cache_loader_helper.h" +#include "third_party/blink/public/platform/web_resource_request_sender.h" +#include "third_party/blink/public/platform/web_runtime_features.h" +#include "third_party/blink/renderer/platform/loader/fetch/back_forward_cache_loader_helper.h" +#include "third_party/blink/renderer/platform/testing/testing_platform_support.h" + +namespace blink { + +namespace { + +constexpr size_t kDataPipeCapacity = 4096; + +class MockWebResourceRequestSender : public WebResourceRequestSender { + public: + struct Context; + MockWebResourceRequestSender() : context_(new Context()) {} + ~MockWebResourceRequestSender() override = default; + + void OnUploadProgress(int64_t position, int64_t size) override { + EXPECT_FALSE(context_->complete); + } + + void OnReceivedRedirect( + const net::RedirectInfo& redirect_info, + network::mojom::URLResponseHeadPtr head, + scoped_refptr<base::SingleThreadTaskRunner> task_runner) override { + EXPECT_FALSE(context_->cancelled); + EXPECT_FALSE(context_->complete); + ++context_->seen_redirects; + context_->last_load_timing = head->load_timing; + if (context_->defer_on_redirect) { + context_->url_laoder_client->SetDefersLoading( + blink::WebURLLoader::DeferType::kDeferred); + } + } + + void OnReceivedResponse(network::mojom::URLResponseHeadPtr head) override { + EXPECT_FALSE(context_->cancelled); + EXPECT_FALSE(context_->received_response); + EXPECT_FALSE(context_->complete); + context_->received_response = true; + context_->last_load_timing = head->load_timing; + if (context_->cancel_on_receive_response) + context_->cancelled = true; + } + + void OnStartLoadingResponseBody( + mojo::ScopedDataPipeConsumerHandle body) override { + if (context_->cancelled) + return; + EXPECT_TRUE(context_->received_response); + EXPECT_FALSE(context_->complete); + context_->body_handle = std::move(body); + } + + void OnTransferSizeUpdated(int transfer_size_diff) override { + EXPECT_TRUE(context_->received_response); + EXPECT_FALSE(context_->complete); + if (context_->cancelled) + return; + context_->total_encoded_data_length += transfer_size_diff; + if (context_->defer_on_transfer_size_updated) { + context_->url_laoder_client->SetDefersLoading( + blink::WebURLLoader::DeferType::kDeferred); + } + } + + void OnReceivedCachedMetadata(mojo_base::BigBuffer data) override { + EXPECT_TRUE(context_->received_response); + EXPECT_FALSE(context_->complete); + if (context_->cancelled) + return; + context_->cached_metadata = std::move(data); + } + + void OnRequestComplete( + const network::URLLoaderCompletionStatus& status) override { + if (context_->cancelled) + return; + EXPECT_TRUE(context_->received_response); + EXPECT_FALSE(context_->complete); + context_->complete = true; + context_->completion_status = status; + } + + Context* context() { return context_.get(); } + + struct Context final { + Context() = default; + ~Context() = default; + Context(const Context&) = delete; + Context& operator=(const Context&) = delete; + + // True if should follow redirects, false if should cancel them. + bool follow_redirects = true; + // True if the request should be deferred on redirects. + bool defer_on_redirect = false; + + // Number of total redirects seen. + int seen_redirects = 0; + + bool cancel_on_receive_response = false; + bool cancel_on_receive_data = false; + bool received_response = false; + + mojo_base::BigBuffer cached_metadata; + // Data received. If downloading to file, remains empty. + std::string data; + + // Mojo's data pipe passed on OnStartLoadingResponseBody. + mojo::ScopedDataPipeConsumerHandle body_handle; + + // Total encoded data length, regardless of whether downloading to a file or + // not. + int total_encoded_data_length = 0; + bool defer_on_transfer_size_updated = false; + + bool complete = false; + bool cancelled = false; + int request_id = -1; + + net::LoadTimingInfo last_load_timing; + network::URLLoaderCompletionStatus completion_status; + MojoURLLoaderClient* url_laoder_client; + }; + + private: + std::unique_ptr<Context> context_; +}; + +std::string ReadOneChunk(mojo::ScopedDataPipeConsumerHandle* handle) { + char buffer[kDataPipeCapacity]; + uint32_t read_bytes = kDataPipeCapacity; + MojoResult result = + (*handle)->ReadData(buffer, &read_bytes, MOJO_READ_DATA_FLAG_NONE); + if (result != MOJO_RESULT_OK) + return ""; + return std::string(buffer, read_bytes); +} + +std::string GetRequestPeerContextBody( + MockWebResourceRequestSender::Context* context) { + if (context->body_handle) { + context->data += ReadOneChunk(&context->body_handle); + } + return context->data; +} + +class TestBackForwardCacheLoaderHelper : public BackForwardCacheLoaderHelper { + public: + TestBackForwardCacheLoaderHelper() = default; + + bool CanContinueBufferingWhileInBackForwardCache() const override { + return true; + } +}; + +} // namespace + +class WebMojoURLLoaderClientTest : public ::testing::Test, + public network::mojom::URLLoaderFactory, + public ::testing::WithParamInterface<bool> { + protected: + WebMojoURLLoaderClientTest() + : resource_request_sender_(new MockWebResourceRequestSender()) { + if (DeferWithBackForwardCacheEnabled()) { + scoped_feature_list_.InitAndEnableFeature( + blink::features::kLoadingTasksUnfreezable); + } + + WebRuntimeFeatures::EnableBackForwardCache( + DeferWithBackForwardCacheEnabled()); + + auto url_loader_factory = + base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(this); + auto request = std::make_unique<network::ResourceRequest>(); + auto loading_task_runner = + blink::scheduler::GetSingleThreadTaskRunnerForTesting(); + + client_ = std::make_unique<MojoURLLoaderClient>( + resource_request_sender_.get(), loading_task_runner, + url_loader_factory->BypassRedirectChecks(), request->url, + WebBackForwardCacheLoaderHelper( + MakeGarbageCollected<TestBackForwardCacheLoaderHelper>())); + context_ = resource_request_sender_->context(); + context_->url_laoder_client = client_.get(); + url_loader_ = ThrottlingURLLoader::CreateLoaderAndStart( + std::move(url_loader_factory), + std::vector<std::unique_ptr<blink::URLLoaderThrottle>>(), + /*routing_id=*/0, request_id_, /*loader_options=0*/ 0, request.get(), + client_.get(), TRAFFIC_ANNOTATION_FOR_TESTS, + std::move(loading_task_runner), + base::make_optional(std::vector<std::string>())); + + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(url_loader_client_); + } + + bool DeferWithBackForwardCacheEnabled() { return GetParam(); } + + void TearDown() override { url_loader_client_.reset(); } + + void CreateLoaderAndStart( + mojo::PendingReceiver<network::mojom::URLLoader> receiver, + int32_t routing_id, + int32_t request_id, + uint32_t options, + const network::ResourceRequest& url_request, + mojo::PendingRemote<network::mojom::URLLoaderClient> client, + const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) + override { + url_loader_client_.Bind(std::move(client)); + } + + void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) + override { + NOTREACHED(); + } + + static MojoCreateDataPipeOptions DataPipeOptions() { + MojoCreateDataPipeOptions options; + options.struct_size = sizeof(MojoCreateDataPipeOptions); + options.flags = MOJO_CREATE_DATA_PIPE_FLAG_NONE; + options.element_num_bytes = 1; + options.capacity_num_bytes = kDataPipeCapacity; + return options; + } + + class TestPlatform final : public TestingPlatformSupport { + public: + bool IsRedirectSafe(const GURL& from_url, const GURL& to_url) override { + return true; + } + }; + + base::test::SingleThreadTaskEnvironment task_environment_; + ScopedTestingPlatformSupport<TestPlatform> platform_; + base::test::ScopedFeatureList scoped_feature_list_; + std::unique_ptr<ThrottlingURLLoader> url_loader_; + std::unique_ptr<MojoURLLoaderClient> client_; + std::unique_ptr<MockWebResourceRequestSender> resource_request_sender_; + MockWebResourceRequestSender::Context* context_; + int request_id_ = 0; + mojo::Remote<network::mojom::URLLoaderClient> url_loader_client_; +}; + +TEST_P(WebMojoURLLoaderClientTest, OnReceiveResponse) { + url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New()); + + EXPECT_FALSE(context_->received_response); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(context_->received_response); +} + +TEST_P(WebMojoURLLoaderClientTest, ResponseBody) { + url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New()); + + EXPECT_FALSE(context_->received_response); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(context_->received_response); + + MojoCreateDataPipeOptions options = DataPipeOptions(); + mojo::ScopedDataPipeProducerHandle data_pipe_producer; + mojo::ScopedDataPipeConsumerHandle data_pipe_consumer; + EXPECT_EQ(MOJO_RESULT_OK, mojo::CreateDataPipe(&options, data_pipe_producer, + data_pipe_consumer)); + url_loader_client_->OnStartLoadingResponseBody(std::move(data_pipe_consumer)); + uint32_t size = 5; + MojoResult result = + data_pipe_producer->WriteData("hello", &size, MOJO_WRITE_DATA_FLAG_NONE); + ASSERT_EQ(MOJO_RESULT_OK, result); + EXPECT_EQ(5u, size); + + base::RunLoop().RunUntilIdle(); + EXPECT_EQ("hello", GetRequestPeerContextBody(context_)); +} + +TEST_P(WebMojoURLLoaderClientTest, OnReceiveRedirect) { + net::RedirectInfo redirect_info; + + url_loader_client_->OnReceiveRedirect(redirect_info, + network::mojom::URLResponseHead::New()); + + EXPECT_EQ(0, context_->seen_redirects); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(1, context_->seen_redirects); +} + +TEST_P(WebMojoURLLoaderClientTest, OnReceiveCachedMetadata) { + std::vector<uint8_t> data; + data.push_back('a'); + mojo_base::BigBuffer metadata(data); + + url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New()); + url_loader_client_->OnReceiveCachedMetadata(std::move(metadata)); + + EXPECT_FALSE(context_->received_response); + EXPECT_EQ(0u, context_->cached_metadata.size()); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(context_->received_response); + ASSERT_EQ(1u, context_->cached_metadata.size()); + EXPECT_EQ('a', context_->cached_metadata.data()[0]); +} + +TEST_P(WebMojoURLLoaderClientTest, OnTransferSizeUpdated) { + url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New()); + url_loader_client_->OnTransferSizeUpdated(4); + url_loader_client_->OnTransferSizeUpdated(4); + + EXPECT_FALSE(context_->received_response); + EXPECT_EQ(0, context_->total_encoded_data_length); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(context_->received_response); + EXPECT_EQ(8, context_->total_encoded_data_length); +} + +TEST_P(WebMojoURLLoaderClientTest, OnCompleteWithResponseBody) { + network::URLLoaderCompletionStatus status; + + url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New()); + MojoCreateDataPipeOptions options = DataPipeOptions(); + mojo::ScopedDataPipeProducerHandle data_pipe_producer; + mojo::ScopedDataPipeConsumerHandle data_pipe_consumer; + EXPECT_EQ(MOJO_RESULT_OK, mojo::CreateDataPipe(&options, data_pipe_producer, + data_pipe_consumer)); + url_loader_client_->OnStartLoadingResponseBody(std::move(data_pipe_consumer)); + uint32_t size = 5; + MojoResult result = + data_pipe_producer->WriteData("hello", &size, MOJO_WRITE_DATA_FLAG_NONE); + ASSERT_EQ(MOJO_RESULT_OK, result); + EXPECT_EQ(5u, size); + data_pipe_producer.reset(); + + EXPECT_FALSE(context_->received_response); + EXPECT_EQ("", GetRequestPeerContextBody(context_)); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(context_->received_response); + EXPECT_EQ("hello", GetRequestPeerContextBody(context_)); + + url_loader_client_->OnComplete(status); + + EXPECT_FALSE(context_->complete); + base::RunLoop().RunUntilIdle(); + + EXPECT_TRUE(context_->received_response); + EXPECT_EQ("hello", GetRequestPeerContextBody(context_)); + EXPECT_TRUE(context_->complete); +} + +// Due to the lack of ordering guarantee, it is possible that the response body +// bytes arrives after the completion message. URLLoaderClientImpl should +// restore the order. +TEST_P(WebMojoURLLoaderClientTest, OnCompleteShouldBeTheLastMessage) { + network::URLLoaderCompletionStatus status; + + url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New()); + MojoCreateDataPipeOptions options = DataPipeOptions(); + mojo::ScopedDataPipeProducerHandle data_pipe_producer; + mojo::ScopedDataPipeConsumerHandle data_pipe_consumer; + EXPECT_EQ(MOJO_RESULT_OK, mojo::CreateDataPipe(&options, data_pipe_producer, + data_pipe_consumer)); + url_loader_client_->OnStartLoadingResponseBody(std::move(data_pipe_consumer)); + url_loader_client_->OnComplete(status); + + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(context_->received_response); + EXPECT_TRUE(context_->complete); + + uint32_t size = 5; + MojoResult result = + data_pipe_producer->WriteData("hello", &size, MOJO_WRITE_DATA_FLAG_NONE); + ASSERT_EQ(MOJO_RESULT_OK, result); + EXPECT_EQ(5u, size); + + base::RunLoop().RunUntilIdle(); + EXPECT_EQ("hello", GetRequestPeerContextBody(context_)); +} + +TEST_P(WebMojoURLLoaderClientTest, CancelOnReceiveResponse) { + context_->cancel_on_receive_response = true; + + network::URLLoaderCompletionStatus status; + + url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New()); + MojoCreateDataPipeOptions options = DataPipeOptions(); + mojo::ScopedDataPipeProducerHandle data_pipe_producer; + mojo::ScopedDataPipeConsumerHandle data_pipe_consumer; + EXPECT_EQ(MOJO_RESULT_OK, mojo::CreateDataPipe(&options, data_pipe_producer, + data_pipe_consumer)); + url_loader_client_->OnStartLoadingResponseBody(std::move(data_pipe_consumer)); + url_loader_client_->OnComplete(status); + + EXPECT_FALSE(context_->received_response); + EXPECT_FALSE(context_->complete); + EXPECT_FALSE(context_->cancelled); + + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(context_->received_response); + EXPECT_FALSE(context_->complete); + EXPECT_TRUE(context_->cancelled); +} + +TEST_P(WebMojoURLLoaderClientTest, Defer) { + network::URLLoaderCompletionStatus status; + + url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New()); + MojoCreateDataPipeOptions options = DataPipeOptions(); + mojo::ScopedDataPipeProducerHandle data_pipe_producer; + mojo::ScopedDataPipeConsumerHandle data_pipe_consumer; + EXPECT_EQ(MOJO_RESULT_OK, mojo::CreateDataPipe(&options, data_pipe_producer, + data_pipe_consumer)); + data_pipe_producer.reset(); // Empty body. + url_loader_client_->OnStartLoadingResponseBody(std::move(data_pipe_consumer)); + url_loader_client_->OnComplete(status); + + EXPECT_FALSE(context_->received_response); + EXPECT_FALSE(context_->complete); + + client_->SetDefersLoading(blink::WebURLLoader::DeferType::kDeferred); + + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(context_->received_response); + EXPECT_FALSE(context_->complete); + + client_->SetDefersLoading(blink::WebURLLoader::DeferType::kNotDeferred); + EXPECT_FALSE(context_->received_response); + EXPECT_FALSE(context_->complete); + + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(context_->received_response); + EXPECT_TRUE(context_->complete); +} + +TEST_P(WebMojoURLLoaderClientTest, DeferWithResponseBody) { + network::URLLoaderCompletionStatus status; + + url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New()); + MojoCreateDataPipeOptions options = DataPipeOptions(); + mojo::ScopedDataPipeProducerHandle data_pipe_producer; + mojo::ScopedDataPipeConsumerHandle data_pipe_consumer; + EXPECT_EQ(MOJO_RESULT_OK, mojo::CreateDataPipe(&options, data_pipe_producer, + data_pipe_consumer)); + std::string msg1 = "hello"; + uint32_t size = msg1.size(); + ASSERT_EQ(MOJO_RESULT_OK, data_pipe_producer->WriteData( + msg1.data(), &size, MOJO_WRITE_DATA_FLAG_NONE)); + EXPECT_EQ(msg1.size(), size); + data_pipe_producer.reset(); + + url_loader_client_->OnStartLoadingResponseBody(std::move(data_pipe_consumer)); + url_loader_client_->OnComplete(status); + + EXPECT_FALSE(context_->received_response); + EXPECT_FALSE(context_->complete); + EXPECT_EQ("", GetRequestPeerContextBody(context_)); + + client_->SetDefersLoading(blink::WebURLLoader::DeferType::kDeferred); + + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(context_->received_response); + EXPECT_FALSE(context_->complete); + EXPECT_EQ("", GetRequestPeerContextBody(context_)); + + client_->SetDefersLoading(blink::WebURLLoader::DeferType::kNotDeferred); + EXPECT_FALSE(context_->received_response); + EXPECT_FALSE(context_->complete); + EXPECT_EQ("", GetRequestPeerContextBody(context_)); + + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(context_->received_response); + EXPECT_TRUE(context_->complete); + EXPECT_EQ("hello", GetRequestPeerContextBody(context_)); +} + +TEST_P(WebMojoURLLoaderClientTest, + DeferredAndDeferredWithBackForwardCacheTransitions) { + if (!DeferWithBackForwardCacheEnabled()) + return; + // Call OnReceiveResponse and OnStartLoadingResponseBody while + // deferred (not for back-forward cache). + client_->SetDefersLoading(blink::WebURLLoader::DeferType::kDeferred); + url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New()); + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + ASSERT_EQ(MOJO_RESULT_OK, + mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle)); + url_loader_client_->OnStartLoadingResponseBody(std::move(consumer_handle)); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(context_->received_response); + EXPECT_FALSE(context_->complete); + EXPECT_EQ("", GetRequestPeerContextBody(context_)); + + // Write data to the response body pipe. + std::string msg1 = "he"; + uint32_t size = msg1.size(); + ASSERT_EQ(MOJO_RESULT_OK, producer_handle->WriteData( + msg1.data(), &size, MOJO_WRITE_DATA_FLAG_NONE)); + EXPECT_EQ(msg1.size(), size); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ("", GetRequestPeerContextBody(context_)); + + // Defer for back-forward cache. + client_->SetDefersLoading( + blink::WebURLLoader::DeferType::kDeferredWithBackForwardCache); + std::string msg2 = "ll"; + size = msg2.size(); + ASSERT_EQ(MOJO_RESULT_OK, producer_handle->WriteData( + msg2.data(), &size, MOJO_WRITE_DATA_FLAG_NONE)); + EXPECT_EQ(msg2.size(), size); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ("", GetRequestPeerContextBody(context_)); + + // Defer not for back-forward cache again. + client_->SetDefersLoading( + blink::WebURLLoader::DeferType::kDeferredWithBackForwardCache); + std::string msg3 = "o"; + size = msg3.size(); + ASSERT_EQ(MOJO_RESULT_OK, producer_handle->WriteData( + msg3.data(), &size, MOJO_WRITE_DATA_FLAG_NONE)); + EXPECT_EQ(msg3.size(), size); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ("", GetRequestPeerContextBody(context_)); + + // Stop deferring. + client_->SetDefersLoading(blink::WebURLLoader::DeferType::kNotDeferred); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(context_->received_response); + EXPECT_FALSE(context_->complete); + EXPECT_EQ("hello", GetRequestPeerContextBody(context_)); + + // Write more data to the pipe while not deferred. + std::string msg4 = "world"; + size = msg4.size(); + ASSERT_EQ(MOJO_RESULT_OK, producer_handle->WriteData( + msg4.data(), &size, MOJO_WRITE_DATA_FLAG_NONE)); + EXPECT_EQ(msg4.size(), size); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(context_->received_response); + EXPECT_FALSE(context_->complete); + EXPECT_EQ("helloworld", GetRequestPeerContextBody(context_)); +} + +TEST_P(WebMojoURLLoaderClientTest, + DeferredWithBackForwardCacheStoppedDeferringBeforeClosing) { + if (!DeferWithBackForwardCacheEnabled()) + return; + // Call OnReceiveResponse, OnStartLoadingResponseBody, OnComplete while + // deferred. + client_->SetDefersLoading( + blink::WebURLLoader::DeferType::kDeferredWithBackForwardCache); + url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New()); + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + ASSERT_EQ(MOJO_RESULT_OK, + mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle)); + url_loader_client_->OnStartLoadingResponseBody(std::move(consumer_handle)); + network::URLLoaderCompletionStatus status; + url_loader_client_->OnComplete(status); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(context_->received_response); + EXPECT_FALSE(context_->complete); + EXPECT_EQ("", GetRequestPeerContextBody(context_)); + + // Write data to the response body pipe, but don't close the connection yet. + std::string msg1 = "hello"; + uint32_t size = msg1.size(); + // We expect that the other end of the pipe to be ready to read the data + // immediately. + ASSERT_EQ(MOJO_RESULT_OK, producer_handle->WriteData( + msg1.data(), &size, MOJO_WRITE_DATA_FLAG_NONE)); + EXPECT_EQ(msg1.size(), size); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ("", GetRequestPeerContextBody(context_)); + + // Stop deferring. OnComplete message shouldn't be dispatched yet because + // we're still waiting for the response body pipe to be closed. + client_->SetDefersLoading(blink::WebURLLoader::DeferType::kNotDeferred); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(context_->received_response); + // When the body is buffered, we'll wait until the pipe is closed before + // sending the OnComplete message. + EXPECT_FALSE(context_->complete); + EXPECT_EQ("hello", GetRequestPeerContextBody(context_)); + + // Write more data to the pipe while not deferred. + std::string msg2 = "world"; + size = msg2.size(); + ASSERT_EQ(MOJO_RESULT_OK, producer_handle->WriteData( + msg2.data(), &size, MOJO_WRITE_DATA_FLAG_NONE)); + EXPECT_EQ(msg2.size(), size); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(context_->received_response); + EXPECT_FALSE(context_->complete); + EXPECT_EQ("helloworld", GetRequestPeerContextBody(context_)); + + // Close the response body pipe. + producer_handle.reset(); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(context_->received_response); + EXPECT_TRUE(context_->complete); + EXPECT_EQ("helloworld", GetRequestPeerContextBody(context_)); +} + +TEST_P(WebMojoURLLoaderClientTest, DeferBodyWithoutOnComplete) { + url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New()); + // Call OnStartLoadingResponseBody while deferred. + client_->SetDefersLoading(blink::WebURLLoader::DeferType::kDeferred); + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + ASSERT_EQ(MOJO_RESULT_OK, + mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle)); + url_loader_client_->OnStartLoadingResponseBody(std::move(consumer_handle)); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(context_->received_response); + EXPECT_FALSE(context_->complete); + EXPECT_EQ("", GetRequestPeerContextBody(context_)); + + // Write data to the response body pipe, but don't close the connection yet. + std::string msg1 = "hello"; + uint32_t size = msg1.size(); + // We expect that the other end of the pipe to be ready to read the data + // immediately. + ASSERT_EQ(MOJO_RESULT_OK, producer_handle->WriteData( + msg1.data(), &size, MOJO_WRITE_DATA_FLAG_NONE)); + EXPECT_EQ(msg1.size(), size); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ("", GetRequestPeerContextBody(context_)); + + // Stop deferring. + client_->SetDefersLoading(blink::WebURLLoader::DeferType::kNotDeferred); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(context_->received_response); + EXPECT_FALSE(context_->complete); + EXPECT_EQ("hello", GetRequestPeerContextBody(context_)); + + // Close the response body pipe. + producer_handle.reset(); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(context_->received_response); + EXPECT_FALSE(context_->complete); + EXPECT_EQ("hello", GetRequestPeerContextBody(context_)); +} + +TEST_P(WebMojoURLLoaderClientTest, + DeferredWithBackForwardCacheLongResponseBody) { + if (!DeferWithBackForwardCacheEnabled()) + return; + // Call OnReceiveResponse, OnStartLoadingResponseBody, OnComplete while + // deferred. + client_->SetDefersLoading( + blink::WebURLLoader::DeferType::kDeferredWithBackForwardCache); + url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New()); + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + ASSERT_EQ(MOJO_RESULT_OK, + mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle)); + url_loader_client_->OnStartLoadingResponseBody(std::move(consumer_handle)); + network::URLLoaderCompletionStatus status; + url_loader_client_->OnComplete(status); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(context_->received_response); + EXPECT_FALSE(context_->complete); + EXPECT_EQ("", GetRequestPeerContextBody(context_)); + + // Write to the response body pipe. It will take several writes. + const uint32_t body_size = 70000; + uint32_t bytes_remaining = body_size; + std::string body(body_size, '*'); + while (bytes_remaining > 0) { + uint32_t start_position = body_size - bytes_remaining; + uint32_t bytes_sent = bytes_remaining; + MojoResult result = producer_handle->WriteData( + body.c_str() + start_position, &bytes_sent, MOJO_WRITE_DATA_FLAG_NONE); + if (result == MOJO_RESULT_SHOULD_WAIT) { + // When we buffer the body the pipe gets drained asynchronously, so it's + // possible to keep writing to the pipe if we wait. + base::RunLoop().RunUntilIdle(); + continue; + } + EXPECT_EQ(MOJO_RESULT_OK, result); + EXPECT_GE(bytes_remaining, bytes_sent); + bytes_remaining -= bytes_sent; + } + // Ensure we've written all that we can write. When buffering is disabled, we + // can only write |body_size| - |bytes_remaining| bytes. + const uint32_t bytes_written = body_size - bytes_remaining; + EXPECT_EQ(body_size, bytes_written); + producer_handle.reset(); + + // Stop deferring. + client_->SetDefersLoading(blink::WebURLLoader::DeferType::kNotDeferred); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(context_->received_response); + // When the body is buffered, BodyBuffer shouldn't be finished writing to the + // new response body pipe at this point (because nobody is reading it). + EXPECT_FALSE(context_->complete); + + // Calling GetRequestPeerContextBody to read data from the new response body + // pipe will make BodyBuffer write the rest of the body to the pipe. + uint32_t bytes_read = 0; + while (bytes_read < bytes_written) { + bytes_read = GetRequestPeerContextBody(context_).size(); + base::RunLoop().RunUntilIdle(); + } + // Ensure that we've read everything we've written. + EXPECT_EQ(bytes_written, bytes_read); + EXPECT_EQ(body, GetRequestPeerContextBody(context_)); + EXPECT_TRUE(context_->complete); +} + +// As "transfer size update" message is handled specially in the implementation, +// we have a separate test. +TEST_P(WebMojoURLLoaderClientTest, DeferWithTransferSizeUpdated) { + network::URLLoaderCompletionStatus status; + + url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New()); + MojoCreateDataPipeOptions options = DataPipeOptions(); + mojo::ScopedDataPipeProducerHandle data_pipe_producer; + mojo::ScopedDataPipeConsumerHandle data_pipe_consumer; + EXPECT_EQ(MOJO_RESULT_OK, mojo::CreateDataPipe(&options, data_pipe_producer, + data_pipe_consumer)); + uint32_t size = 5; + MojoResult result = + data_pipe_producer->WriteData("hello", &size, MOJO_WRITE_DATA_FLAG_NONE); + ASSERT_EQ(MOJO_RESULT_OK, result); + EXPECT_EQ(5u, size); + data_pipe_producer.reset(); + + url_loader_client_->OnStartLoadingResponseBody(std::move(data_pipe_consumer)); + url_loader_client_->OnTransferSizeUpdated(4); + url_loader_client_->OnComplete(status); + + EXPECT_FALSE(context_->received_response); + EXPECT_FALSE(context_->complete); + EXPECT_EQ("", GetRequestPeerContextBody(context_)); + EXPECT_EQ(0, context_->total_encoded_data_length); + + client_->SetDefersLoading(blink::WebURLLoader::DeferType::kDeferred); + + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(context_->received_response); + EXPECT_FALSE(context_->complete); + EXPECT_EQ("", GetRequestPeerContextBody(context_)); + EXPECT_EQ(0, context_->total_encoded_data_length); + + client_->SetDefersLoading(blink::WebURLLoader::DeferType::kNotDeferred); + EXPECT_FALSE(context_->received_response); + EXPECT_FALSE(context_->complete); + EXPECT_EQ("", GetRequestPeerContextBody(context_)); + EXPECT_EQ(0, context_->total_encoded_data_length); + + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(context_->received_response); + EXPECT_TRUE(context_->complete); + EXPECT_EQ("hello", GetRequestPeerContextBody(context_)); + EXPECT_EQ(4, context_->total_encoded_data_length); +} + +TEST_P(WebMojoURLLoaderClientTest, SetDeferredDuringFlushingDeferredMessage) { + context_->defer_on_redirect = true; + + net::RedirectInfo redirect_info; + network::URLLoaderCompletionStatus status; + + url_loader_client_->OnReceiveRedirect(redirect_info, + network::mojom::URLResponseHead::New()); + url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New()); + MojoCreateDataPipeOptions options = DataPipeOptions(); + mojo::ScopedDataPipeProducerHandle data_pipe_producer; + mojo::ScopedDataPipeConsumerHandle data_pipe_consumer; + EXPECT_EQ(MOJO_RESULT_OK, mojo::CreateDataPipe(&options, data_pipe_producer, + data_pipe_consumer)); + uint32_t size = 5; + MojoResult result = + data_pipe_producer->WriteData("hello", &size, MOJO_WRITE_DATA_FLAG_NONE); + ASSERT_EQ(MOJO_RESULT_OK, result); + EXPECT_EQ(5u, size); + data_pipe_producer.reset(); + + url_loader_client_->OnStartLoadingResponseBody(std::move(data_pipe_consumer)); + url_loader_client_->OnTransferSizeUpdated(4); + url_loader_client_->OnComplete(status); + + EXPECT_EQ(0, context_->seen_redirects); + EXPECT_FALSE(context_->received_response); + EXPECT_FALSE(context_->complete); + EXPECT_EQ("", GetRequestPeerContextBody(context_)); + EXPECT_EQ(0, context_->total_encoded_data_length); + + client_->SetDefersLoading(blink::WebURLLoader::DeferType::kDeferred); + + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(0, context_->seen_redirects); + EXPECT_FALSE(context_->received_response); + EXPECT_FALSE(context_->complete); + EXPECT_EQ("", GetRequestPeerContextBody(context_)); + EXPECT_EQ(0, context_->total_encoded_data_length); + + client_->SetDefersLoading(blink::WebURLLoader::DeferType::kNotDeferred); + EXPECT_EQ(0, context_->seen_redirects); + EXPECT_FALSE(context_->received_response); + EXPECT_FALSE(context_->complete); + EXPECT_EQ("", GetRequestPeerContextBody(context_)); + EXPECT_EQ(0, context_->total_encoded_data_length); + + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(1, context_->seen_redirects); + EXPECT_FALSE(context_->received_response); + EXPECT_FALSE(context_->complete); + EXPECT_EQ("", GetRequestPeerContextBody(context_)); + EXPECT_EQ(0, context_->total_encoded_data_length); + EXPECT_FALSE(context_->cancelled); + + client_->SetDefersLoading(blink::WebURLLoader::DeferType::kNotDeferred); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(1, context_->seen_redirects); + EXPECT_TRUE(context_->received_response); + EXPECT_TRUE(context_->complete); + EXPECT_EQ("hello", GetRequestPeerContextBody(context_)); + EXPECT_EQ(4, context_->total_encoded_data_length); + EXPECT_FALSE(context_->cancelled); +} + +TEST_P(WebMojoURLLoaderClientTest, + SetDeferredDuringFlushingDeferredMessageOnTransferSizeUpdated) { + context_->defer_on_transfer_size_updated = true; + + network::URLLoaderCompletionStatus status; + + url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New()); + MojoCreateDataPipeOptions options = DataPipeOptions(); + mojo::ScopedDataPipeProducerHandle data_pipe_producer; + mojo::ScopedDataPipeConsumerHandle data_pipe_consumer; + EXPECT_EQ(MOJO_RESULT_OK, mojo::CreateDataPipe(&options, data_pipe_producer, + data_pipe_consumer)); + data_pipe_producer.reset(); // Empty body. + url_loader_client_->OnStartLoadingResponseBody(std::move(data_pipe_consumer)); + + url_loader_client_->OnTransferSizeUpdated(4); + url_loader_client_->OnComplete(status); + + EXPECT_FALSE(context_->received_response); + EXPECT_FALSE(context_->complete); + EXPECT_EQ(0, context_->total_encoded_data_length); + + client_->SetDefersLoading(blink::WebURLLoader::DeferType::kDeferred); + + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(context_->received_response); + EXPECT_FALSE(context_->complete); + EXPECT_EQ(0, context_->total_encoded_data_length); + + client_->SetDefersLoading(blink::WebURLLoader::DeferType::kNotDeferred); + EXPECT_FALSE(context_->received_response); + EXPECT_FALSE(context_->complete); + EXPECT_EQ(0, context_->total_encoded_data_length); + + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(context_->received_response); + EXPECT_FALSE(context_->complete); + EXPECT_EQ(4, context_->total_encoded_data_length); + EXPECT_FALSE(context_->cancelled); + + client_->SetDefersLoading(blink::WebURLLoader::DeferType::kNotDeferred); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(context_->received_response); + EXPECT_TRUE(context_->complete); + EXPECT_EQ(4, context_->total_encoded_data_length); + EXPECT_FALSE(context_->cancelled); +} + +INSTANTIATE_TEST_SUITE_P(All, WebMojoURLLoaderClientTest, ::testing::Bool()); + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.cc index fb258384b40..47dae6851d3 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.cc @@ -8,6 +8,7 @@ #include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/remote.h" #include "mojo/public/cpp/system/data_pipe.h" +#include "net/base/load_flags.h" #include "net/base/request_priority.h" #include "net/http/http_request_headers.h" #include "net/http/http_util.h" @@ -19,10 +20,10 @@ #include "services/network/public/mojom/data_pipe_getter.mojom.h" #include "services/network/public/mojom/trust_tokens.mojom-blink.h" #include "services/network/public/mojom/trust_tokens.mojom.h" -#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/mojom/blob/blob.mojom.h" #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h" #include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h" +#include "third_party/blink/public/platform/cross_variant_mojo_util.h" #include "third_party/blink/public/platform/file_path_conversion.h" #include "third_party/blink/public/platform/url_conversion.h" #include "third_party/blink/public/platform/web_string.h" @@ -35,19 +36,13 @@ namespace blink { -const char* ImageAcceptHeader() { - static constexpr char kImageAcceptHeaderWithAvif[] = - "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8"; - static constexpr size_t kOffset = sizeof("image/avif,") - 1; #if BUILDFLAG(ENABLE_AV1_DECODER) - static const char* header = base::FeatureList::IsEnabled(features::kAVIF) - ? kImageAcceptHeaderWithAvif - : kImageAcceptHeaderWithAvif + kOffset; +constexpr char kImageAcceptHeader[] = + "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8"; #else - static const char* header = kImageAcceptHeaderWithAvif + kOffset; +constexpr char kImageAcceptHeader[] = + "image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8"; #endif - return header; -} namespace { @@ -153,6 +148,7 @@ mojom::ResourceType RequestContextToResourceType( case mojom::blink::RequestContextType::DOWNLOAD: case mojom::blink::RequestContextType::MANIFEST: case mojom::blink::RequestContextType::SUBRESOURCE: + case mojom::blink::RequestContextType::SUBRESOURCE_WEBBUNDLE: return mojom::ResourceType::kSubResource; // TextTrack @@ -216,26 +212,23 @@ void PopulateResourceRequestBody(const EncodedFormData& src, break; case FormDataElement::kEncodedBlob: { DCHECK(element.optional_blob_data_handle_); - mojo::Remote<mojom::Blob> blob_remote(mojo::PendingRemote<mojom::Blob>( - element.optional_blob_data_handle_->CloneBlobRemote().PassPipe(), - mojom::Blob::Version_)); - mojo::PendingRemote<network::mojom::DataPipeGetter> + mojo::Remote<mojom::blink::Blob> blob_remote( + element.optional_blob_data_handle_->CloneBlobRemote()); + mojo::PendingRemote<network::mojom::blink::DataPipeGetter> data_pipe_getter_remote; blob_remote->AsDataPipeGetter( data_pipe_getter_remote.InitWithNewPipeAndPassReceiver()); - dest->AppendDataPipe(std::move(data_pipe_getter_remote)); + dest->AppendDataPipe( + ToCrossVariantMojoType(std::move(data_pipe_getter_remote))); break; } case FormDataElement::kDataPipe: { - // Convert network::mojom::blink::DataPipeGetter to - // network::mojom::DataPipeGetter through a raw message pipe. mojo::PendingRemote<network::mojom::blink::DataPipeGetter> pending_data_pipe_getter; element.data_pipe_getter_->GetDataPipeGetter()->Clone( pending_data_pipe_getter.InitWithNewPipeAndPassReceiver()); dest->AppendDataPipe( - mojo::PendingRemote<network::mojom::DataPipeGetter>( - pending_data_pipe_getter.PassPipe(), 0u)); + ToCrossVariantMojoType(std::move(pending_data_pipe_getter))); break; } } @@ -244,6 +237,27 @@ void PopulateResourceRequestBody(const EncodedFormData& src, } // namespace +scoped_refptr<network::ResourceRequestBody> NetworkResourceRequestBodyFor( + ResourceRequestBody src_body, + bool allow_http1_for_streaming_upload) { + scoped_refptr<network::ResourceRequestBody> dest_body; + if (const EncodedFormData* form_body = src_body.FormBody().get()) { + dest_body = base::MakeRefCounted<network::ResourceRequestBody>(); + + PopulateResourceRequestBody(*form_body, dest_body.get()); + } else if (src_body.StreamBody().is_valid()) { + mojo::PendingRemote<network::mojom::blink::ChunkedDataPipeGetter> + stream_body = src_body.TakeStreamBody(); + dest_body = base::MakeRefCounted<network::ResourceRequestBody>(); + dest_body->SetToChunkedDataPipe( + ToCrossVariantMojoType(std::move(stream_body)), + network::ResourceRequestBody::ReadOnlyOnce(true)); + dest_body->SetAllowHTTP1ForStreamingUpload( + allow_http1_for_streaming_upload); + } + return dest_body; +} + void PopulateResourceRequest(const ResourceRequestHead& src, ResourceRequestBody src_body, network::ResourceRequest* dest) { @@ -305,19 +319,24 @@ void PopulateResourceRequest(const ResourceRequestHead& src, dest->credentials_mode = src.GetCredentialsMode(); dest->redirect_mode = src.GetRedirectMode(); dest->fetch_integrity = src.GetFetchIntegrity().Utf8(); - - mojom::ResourceType resource_type = - RequestContextToResourceType(src.GetRequestContext()); + if (src.GetWebBundleTokenParams().has_value()) { + dest->web_bundle_token_params = + base::make_optional(network::ResourceRequest::WebBundleTokenParams( + src.GetWebBundleTokenParams()->bundle_url, + src.GetWebBundleTokenParams()->token, + src.GetWebBundleTokenParams()->CloneHandle())); + } // TODO(kinuko): Deprecate this. - dest->resource_type = static_cast<int>(resource_type); + dest->resource_type = + static_cast<int>(RequestContextToResourceType(src.GetRequestContext())); - if (resource_type == mojom::ResourceType::kXhr && + if (src.IsFetchLikeAPI() && (dest->url.has_username() || dest->url.has_password())) { dest->do_not_prompt_for_login = true; } - if (resource_type == mojom::ResourceType::kPrefetch || - resource_type == mojom::ResourceType::kFavicon) { + if (src.GetRequestContext() == mojom::blink::RequestContextType::PREFETCH || + src.IsFavicon()) { dest->do_not_prompt_for_login = true; } @@ -351,32 +370,25 @@ void PopulateResourceRequest(const ResourceRequestHead& src, dest->is_fetch_like_api = src.IsFetchLikeAPI(); - if (const EncodedFormData* body = src_body.FormBody().get()) { - DCHECK_NE(dest->method, net::HttpRequestHeaders::kGetMethod); - DCHECK_NE(dest->method, net::HttpRequestHeaders::kHeadMethod); - dest->request_body = base::MakeRefCounted<network::ResourceRequestBody>(); + dest->is_favicon = src.IsFavicon(); - PopulateResourceRequestBody(*body, dest->request_body.get()); - } else if (src_body.StreamBody().is_valid()) { + dest->request_body = NetworkResourceRequestBodyFor( + std::move(src_body), src.AllowHTTP1ForStreamingUpload()); + if (dest->request_body) { DCHECK_NE(dest->method, net::HttpRequestHeaders::kGetMethod); DCHECK_NE(dest->method, net::HttpRequestHeaders::kHeadMethod); - mojo::PendingRemote<network::mojom::blink::ChunkedDataPipeGetter> - stream_body = src_body.TakeStreamBody(); - dest->request_body = base::MakeRefCounted<network::ResourceRequestBody>(); - mojo::PendingRemote<network::mojom::ChunkedDataPipeGetter> - network_stream_body(stream_body.PassPipe(), 0u); - dest->request_body->SetToReadOnceStream(std::move(network_stream_body)); - dest->request_body->SetAllowHTTP1ForStreamingUpload( - src.AllowHTTP1ForStreamingUpload()); } - if (resource_type == mojom::ResourceType::kStylesheet) { + network::mojom::RequestDestination request_destination = + src.GetRequestDestination(); + if (request_destination == network::mojom::RequestDestination::kStyle || + request_destination == network::mojom::RequestDestination::kXslt) { dest->headers.SetHeader(net::HttpRequestHeaders::kAccept, kStylesheetAcceptHeader); - } else if (resource_type == mojom::ResourceType::kImage || - resource_type == mojom::ResourceType::kFavicon) { + } else if (request_destination == + network::mojom::RequestDestination::kImage) { dest->headers.SetHeaderIfMissing(net::HttpRequestHeaders::kAccept, - ImageAcceptHeader()); + kImageAcceptHeader); } else { // Calling SetHeaderIfMissing() instead of SetHeader() because JS can // manually set an accept header on an XHR. diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.h b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.h index f80cd23c0f2..329b583e5be 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/request_conversion.h @@ -7,19 +7,25 @@ // This file consists of request conversion functions between blink and network. +#include "base/memory/ref_counted.h" #include "third_party/blink/renderer/platform/platform_export.h" namespace network { +class ResourceRequestBody; struct ResourceRequest; } // namespace network namespace blink { -PLATFORM_EXPORT const char* ImageAcceptHeader(); +PLATFORM_EXPORT extern const char kImageAcceptHeader[]; class ResourceRequestHead; class ResourceRequestBody; +scoped_refptr<network::ResourceRequestBody> NetworkResourceRequestBodyFor( + const ResourceRequestBody src_body, + bool allow_http1_for_streaming_upload); + void PopulateResourceRequest(const ResourceRequestHead& src, ResourceRequestBody src_body, network::ResourceRequest* dest); diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/sync_load_context.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/sync_load_context.cc new file mode 100644 index 00000000000..0cf43dcc510 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/sync_load_context.cc @@ -0,0 +1,335 @@ +// 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/url_loader/sync_load_context.h" + +#include <string> + +#include "base/bind.h" +#include "base/check_op.h" +#include "base/memory/ptr_util.h" +#include "base/optional.h" +#include "base/synchronization/waitable_event.h" +#include "base/time/time.h" +#include "mojo/public/cpp/bindings/associated_remote.h" +#include "net/url_request/redirect_info.h" +#include "services/network/public/cpp/resource_request.h" +#include "services/network/public/mojom/url_response_head.mojom.h" +#include "third_party/blink/public/common/client_hints/client_hints.h" +#include "third_party/blink/public/common/loader/url_loader_throttle.h" +#include "third_party/blink/public/platform/resource_load_info_notifier_wrapper.h" +#include "third_party/blink/public/platform/web_back_forward_cache_loader_helper.h" +#include "third_party/blink/renderer/platform/loader/fetch/url_loader/sync_load_response.h" +#include "third_party/blink/renderer/platform/weborigin/kurl.h" + +namespace blink { + +// An inner helper class to manage the SyncLoadContext's events and timeouts, +// so that we can stop or resumse all of them at once. +class SyncLoadContext::SignalHelper final { + public: + SignalHelper(SyncLoadContext* context, + base::WaitableEvent* redirect_or_response_event, + base::WaitableEvent* abort_event, + base::TimeDelta timeout) + : context_(context), + redirect_or_response_event_(redirect_or_response_event), + abort_event_(abort_event) { + // base::TimeDelta::Max() means no timeout. + if (timeout != base::TimeDelta::Max()) { + // Instantiate a base::OneShotTimer instance. + timeout_timer_.emplace(); + } + Start(timeout); + } + + void SignalRedirectOrResponseComplete() { + abort_watcher_.StopWatching(); + if (timeout_timer_) + timeout_timer_->AbandonAndStop(); + redirect_or_response_event_->Signal(); + } + + bool RestartAfterRedirect() { + if (abort_event_ && abort_event_->IsSignaled()) + return false; + + base::TimeDelta timeout_remainder = base::TimeDelta::Max(); + if (timeout_timer_) { + timeout_remainder = + timeout_timer_->desired_run_time() - base::TimeTicks::Now(); + if (timeout_remainder <= base::TimeDelta()) + return false; + } + Start(timeout_remainder); + return true; + } + + private: + void Start(base::TimeDelta timeout) { + DCHECK(!redirect_or_response_event_->IsSignaled()); + if (abort_event_) { + abort_watcher_.StartWatching( + abort_event_, + base::BindOnce(&SyncLoadContext::OnAbort, base::Unretained(context_)), + context_->task_runner_); + } + if (timeout_timer_) { + DCHECK_NE(base::TimeDelta::Max(), timeout); + timeout_timer_->Start(FROM_HERE, timeout, context_, + &SyncLoadContext::OnTimeout); + } + } + + SyncLoadContext* context_; + base::WaitableEvent* redirect_or_response_event_; + base::WaitableEvent* abort_event_; + base::WaitableEventWatcher abort_watcher_; + base::Optional<base::OneShotTimer> timeout_timer_; +}; + +// static +void SyncLoadContext::StartAsyncWithWaitableEvent( + std::unique_ptr<network::ResourceRequest> request, + int routing_id, + scoped_refptr<base::SingleThreadTaskRunner> loading_task_runner, + const net::NetworkTrafficAnnotationTag& traffic_annotation, + uint32_t loader_options, + std::unique_ptr<network::PendingSharedURLLoaderFactory> + pending_url_loader_factory, + WebVector<std::unique_ptr<URLLoaderThrottle>> throttles, + SyncLoadResponse* response, + SyncLoadContext** context_for_redirect, + base::WaitableEvent* redirect_or_response_event, + base::WaitableEvent* abort_event, + base::TimeDelta timeout, + mojo::PendingRemote<mojom::BlobRegistry> download_to_blob_registry, + const WebVector<WebString>& cors_exempt_header_list, + std::unique_ptr<ResourceLoadInfoNotifierWrapper> + resource_load_info_notifier_wrapper) { + scoped_refptr<SyncLoadContext> context(new SyncLoadContext( + request.get(), std::move(pending_url_loader_factory), response, + context_for_redirect, redirect_or_response_event, abort_event, timeout, + std::move(download_to_blob_registry), loading_task_runner)); + context->resource_request_sender_->SendAsync( + std::move(request), routing_id, std::move(loading_task_runner), + traffic_annotation, loader_options, cors_exempt_header_list, + context, context->url_loader_factory_, std::move(throttles), + std::move(resource_load_info_notifier_wrapper), + WebBackForwardCacheLoaderHelper()); +} + +SyncLoadContext::SyncLoadContext( + network::ResourceRequest* request, + std::unique_ptr<network::PendingSharedURLLoaderFactory> url_loader_factory, + SyncLoadResponse* response, + SyncLoadContext** context_for_redirect, + base::WaitableEvent* redirect_or_response_event, + base::WaitableEvent* abort_event, + base::TimeDelta timeout, + mojo::PendingRemote<mojom::BlobRegistry> download_to_blob_registry, + scoped_refptr<base::SingleThreadTaskRunner> task_runner) + : response_(response), + context_for_redirect_(context_for_redirect), + body_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL), + download_to_blob_registry_(std::move(download_to_blob_registry)), + task_runner_(std::move(task_runner)), + signals_(std::make_unique<SignalHelper>(this, + redirect_or_response_event, + abort_event, + timeout)) { + if (download_to_blob_registry_) + mode_ = Mode::kBlob; + + url_loader_factory_ = + network::SharedURLLoaderFactory::Create(std::move(url_loader_factory)); + + // Constructs a new WebResourceRequestSender specifically for this request. + resource_request_sender_ = std::make_unique<WebResourceRequestSender>(); + + // Initialize the final URL with the original request URL. It will be + // overwritten on redirects. + response_->url = request->url; +} + +SyncLoadContext::~SyncLoadContext() {} + +void SyncLoadContext::OnUploadProgress(uint64_t position, uint64_t size) {} + +bool SyncLoadContext::OnReceivedRedirect( + const net::RedirectInfo& redirect_info, + network::mojom::URLResponseHeadPtr head, + std::vector<std::string>* removed_headers) { + DCHECK(!Completed()); + if (removed_headers) { + // TODO(yoav): Get the actual FeaturePolicy here to support selective + // removal for sync XHR. + FindClientHintsToRemove(nullptr /* feature_policy */, redirect_info.new_url, + removed_headers); + } + + response_->url = redirect_info.new_url; + response_->head = std::move(head); + response_->redirect_info = redirect_info; + *context_for_redirect_ = this; + resource_request_sender_->SetDefersLoading( + WebURLLoader::DeferType::kDeferred); + signals_->SignalRedirectOrResponseComplete(); + return true; +} + +void SyncLoadContext::FollowRedirect() { + if (!signals_->RestartAfterRedirect()) { + CancelRedirect(); + return; + } + + response_->redirect_info = net::RedirectInfo(); + *context_for_redirect_ = nullptr; + + resource_request_sender_->SetDefersLoading( + WebURLLoader::DeferType::kNotDeferred); +} + +void SyncLoadContext::CancelRedirect() { + response_->redirect_info = net::RedirectInfo(); + *context_for_redirect_ = nullptr; + + response_->error_code = net::ERR_ABORTED; + CompleteRequest(); +} + +void SyncLoadContext::OnReceivedResponse( + network::mojom::URLResponseHeadPtr head) { + DCHECK(!Completed()); + response_->head = std::move(head); +} + +void SyncLoadContext::OnStartLoadingResponseBody( + mojo::ScopedDataPipeConsumerHandle body) { + if (mode_ == Mode::kBlob) { + DCHECK(download_to_blob_registry_); + DCHECK(!blob_response_started_); + + blob_response_started_ = true; + + download_to_blob_registry_->RegisterFromStream( + response_->head->mime_type, "", + std::max<int64_t>(0, response_->head->content_length), std::move(body), + mojo::NullAssociatedRemote(), + base::BindOnce(&SyncLoadContext::OnFinishCreatingBlob, + base::Unretained(this))); + return; + } + DCHECK_EQ(Mode::kInitial, mode_); + mode_ = Mode::kDataPipe; + // setup datapipe to read. + body_handle_ = std::move(body); + body_watcher_.Watch( + body_handle_.get(), + MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + base::BindRepeating(&SyncLoadContext::OnBodyReadable, + base::Unretained(this))); + body_watcher_.ArmOrNotify(); +} + +void SyncLoadContext::OnTransferSizeUpdated(int transfer_size_diff) {} + +void SyncLoadContext::OnCompletedRequest( + const network::URLLoaderCompletionStatus& status) { + if (Completed()) { + // It means the response has been aborted due to an error before finishing + // the response. + return; + } + request_completed_ = true; + response_->error_code = status.error_code; + response_->extended_error_code = status.extended_error_code; + response_->resolve_error_info = status.resolve_error_info; + response_->cors_error = status.cors_error_status; + response_->head->encoded_data_length = status.encoded_data_length; + response_->head->encoded_body_length = status.encoded_body_length; + if ((blob_response_started_ && !blob_finished_) || body_handle_.is_valid()) { + // The body is still begin downloaded as a Blob, or being read through the + // handle. Wait until it's completed. + return; + } + CompleteRequest(); +} + +void SyncLoadContext::OnFinishCreatingBlob(mojom::SerializedBlobPtr blob) { + DCHECK(!Completed()); + blob_finished_ = true; + response_->downloaded_blob = std::move(blob); + if (request_completed_) + CompleteRequest(); +} + +void SyncLoadContext::OnBodyReadable(MojoResult, + const mojo::HandleSignalsState&) { + DCHECK_EQ(Mode::kDataPipe, mode_); + DCHECK(body_handle_.is_valid()); + const void* buffer = nullptr; + uint32_t read_bytes = 0; + MojoResult result = body_handle_->BeginReadData(&buffer, &read_bytes, + MOJO_READ_DATA_FLAG_NONE); + if (result == MOJO_RESULT_SHOULD_WAIT) { + body_watcher_.ArmOrNotify(); + return; + } + if (result == MOJO_RESULT_FAILED_PRECONDITION) { + // Whole body has been read. + body_handle_.reset(); + body_watcher_.Cancel(); + if (request_completed_) + CompleteRequest(); + return; + } + if (result != MOJO_RESULT_OK) { + // Something went wrong. + body_handle_.reset(); + body_watcher_.Cancel(); + response_->error_code = net::ERR_FAILED; + CompleteRequest(); + return; + } + + response_->data.Append(static_cast<const char*>(buffer), read_bytes); + body_handle_->EndReadData(read_bytes); + body_watcher_.ArmOrNotify(); +} + +void SyncLoadContext::OnAbort(base::WaitableEvent* event) { + DCHECK(!Completed()); + response_->error_code = net::ERR_ABORTED; + CompleteRequest(); +} + +void SyncLoadContext::OnTimeout() { + // OnTimeout() must not be called after CompleteRequest() was called, because + // the OneShotTimer must have been stopped. + DCHECK(!Completed()); + response_->error_code = net::ERR_TIMED_OUT; + CompleteRequest(); +} + +void SyncLoadContext::CompleteRequest() { + DCHECK(blob_finished_ || (mode_ != Mode::kBlob)); + DCHECK(!body_handle_.is_valid()); + body_watcher_.Cancel(); + signals_->SignalRedirectOrResponseComplete(); + signals_ = nullptr; + response_ = nullptr; + + // This will indirectly cause this object to be deleted. + resource_request_sender_->DeletePendingRequest(task_runner_); +} + +bool SyncLoadContext::Completed() const { + DCHECK_EQ(!signals_, !response_); + return !response_; +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/sync_load_context.h b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/sync_load_context.h new file mode 100644 index 00000000000..7f30c10962e --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/sync_load_context.h @@ -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. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_URL_LOADER_SYNC_LOAD_CONTEXT_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_URL_LOADER_SYNC_LOAD_CONTEXT_H_ + +#include "base/macros.h" +#include "base/optional.h" +#include "base/single_thread_task_runner.h" +#include "base/synchronization/waitable_event_watcher.h" +#include "base/timer/timer.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "mojo/public/cpp/system/data_pipe.h" +#include "mojo/public/cpp/system/simple_watcher.h" +#include "net/traffic_annotation/network_traffic_annotation.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" +#include "third_party/blink/public/mojom/blob/blob_registry.mojom.h" +#include "third_party/blink/public/platform/web_common.h" +#include "third_party/blink/public/platform/web_request_peer.h" +#include "third_party/blink/public/platform/web_resource_request_sender.h" + +namespace base { +class WaitableEvent; +} + +namespace network { +struct ResourceRequest; +} + +namespace blink { +class ResourceLoadInfoNotifierWrapper; +class URLLoaderThrottle; +struct SyncLoadResponse; + +// This class owns the context necessary to perform an asynchronous request +// while the main thread is blocked so that it appears to be synchronous. +// There are a couple of modes to load a request: +// 1) kDataPipe; body is received on a data pipe passed on +// OnStartLoadingResponseBody(), and the body is set to response_.data. +// 2) kBlob: body is received on a data pipe passed on +// OnStartLoadingResponseBody(), and wraps the data pipe with a +// SerializedBlobPtr. +class BLINK_PLATFORM_EXPORT SyncLoadContext : public WebRequestPeer { + public: + // Begins a new asynchronous request on whatever sequence this method is + // called on. |completed_event| will be signalled when the request is complete + // and |response| will be populated with the response data. |abort_event| + // will be signalled from the main thread to abort the sync request on a + // worker thread when the worker thread is being terminated. + // The pointer whose address is `context_for_redirect` is held by the caller + // that is blocked on this method, so it will remain valid until the operation + // completes. If there are redirects, `context_for_redirect` will point to the + // callee context. + // If |download_to_blob_registry| is not null, it is used to + // redirect the download to a blob, with the resulting blob populated in + // |response|. + static void StartAsyncWithWaitableEvent( + std::unique_ptr<network::ResourceRequest> request, + int routing_id, + scoped_refptr<base::SingleThreadTaskRunner> loading_task_runner, + const net::NetworkTrafficAnnotationTag& traffic_annotation, + uint32_t loader_options, + std::unique_ptr<network::PendingSharedURLLoaderFactory> + pending_url_loader_factory, + WebVector<std::unique_ptr<URLLoaderThrottle>> throttles, + SyncLoadResponse* response, + SyncLoadContext** context_for_redirect, + base::WaitableEvent* completed_event, + base::WaitableEvent* abort_event, + base::TimeDelta timeout, + mojo::PendingRemote<mojom::BlobRegistry> download_to_blob_registry, + const WebVector<WebString>& cors_exempt_header_list, + std::unique_ptr<ResourceLoadInfoNotifierWrapper> + resource_load_info_notifier_wrapper); + + ~SyncLoadContext() override; + + void FollowRedirect(); + void CancelRedirect(); + + private: + friend class SyncLoadContextTest; + + SyncLoadContext( + network::ResourceRequest* request, + std::unique_ptr<network::PendingSharedURLLoaderFactory> + url_loader_factory, + SyncLoadResponse* response, + SyncLoadContext** context_for_redirect, + base::WaitableEvent* completed_event, + base::WaitableEvent* abort_event, + base::TimeDelta timeout, + mojo::PendingRemote<mojom::BlobRegistry> download_to_blob_registry, + scoped_refptr<base::SingleThreadTaskRunner> task_runner); + // WebRequestPeer implementation: + void OnUploadProgress(uint64_t position, uint64_t size) override; + bool OnReceivedRedirect(const net::RedirectInfo& redirect_info, + network::mojom::URLResponseHeadPtr head, + std::vector<std::string>*) override; + void OnReceivedResponse(network::mojom::URLResponseHeadPtr head) override; + void OnStartLoadingResponseBody( + mojo::ScopedDataPipeConsumerHandle body) override; + void OnTransferSizeUpdated(int transfer_size_diff) override; + void OnCompletedRequest( + const network::URLLoaderCompletionStatus& status) override; + void OnFinishCreatingBlob(mojom::SerializedBlobPtr blob); + + void OnBodyReadable(MojoResult, const mojo::HandleSignalsState&); + + void OnAbort(base::WaitableEvent* event); + void OnTimeout(); + + void CompleteRequest(); + bool Completed() const; + + // This raw pointer will remain valid for the lifetime of this object because + // it remains on the stack until |event_| is signaled. + // Set to null after CompleteRequest() is called. + SyncLoadResponse* response_; + + // This raw pointer will be set to `this` when receiving redirects on + // independent thread and set to nullptr in `FollowRedirect()` or + // `CancelRedirect()` on the same thread after `redirect_or_response_event_` + // is signaled, which protects it against race condition. + SyncLoadContext** context_for_redirect_; + + enum class Mode { kInitial, kDataPipe, kBlob }; + Mode mode_ = Mode::kInitial; + + // Used when Mode::kDataPipe. + mojo::ScopedDataPipeConsumerHandle body_handle_; + mojo::SimpleWatcher body_watcher_; + + // State necessary to run a request on an independent thread. + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_; + std::unique_ptr<WebResourceRequestSender> resource_request_sender_; + + // State for downloading to a blob. + mojo::Remote<mojom::BlobRegistry> download_to_blob_registry_; + bool blob_response_started_ = false; + bool blob_finished_ = false; + bool request_completed_ = false; + + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; + + class SignalHelper; + std::unique_ptr<SignalHelper> signals_; + + DISALLOW_COPY_AND_ASSIGN(SyncLoadContext); +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_URL_LOADER_SYNC_LOAD_CONTEXT_H_ diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/sync_load_context_unittest.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/sync_load_context_unittest.cc new file mode 100644 index 00000000000..836fde66b6d --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/sync_load_context_unittest.cc @@ -0,0 +1,219 @@ +// 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/url_loader/sync_load_context.h" +#include "base/bind.h" +#include "base/memory/ptr_util.h" +#include "base/test/task_environment.h" +#include "base/threading/thread.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/system/data_pipe_utils.h" +#include "net/traffic_annotation/network_traffic_annotation_test_helper.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" +#include "services/network/test/test_url_loader_factory.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/platform/resource_load_info_notifier_wrapper.h" +#include "third_party/blink/public/platform/web_back_forward_cache_loader_helper.h" +#include "third_party/blink/public/platform/web_resource_request_sender.h" +#include "third_party/blink/renderer/platform/loader/fetch/url_loader/sync_load_response.h" + +namespace blink { + +namespace { + +class TestSharedURLLoaderFactory : public network::TestURLLoaderFactory, + public network::SharedURLLoaderFactory { + public: + // mojom::URLLoaderFactory implementation. + void CreateLoaderAndStart( + mojo::PendingReceiver<network::mojom::URLLoader> receiver, + int32_t routing_id, + int32_t request_id, + uint32_t options, + const network::ResourceRequest& url_request, + mojo::PendingRemote<network::mojom::URLLoaderClient> client, + const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) + override { + network::TestURLLoaderFactory::CreateLoaderAndStart( + std::move(receiver), routing_id, request_id, options, url_request, + std::move(client), traffic_annotation); + } + + void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory>) override { + NOTREACHED(); + } + + std::unique_ptr<network::PendingSharedURLLoaderFactory> Clone() override { + NOTREACHED(); + return nullptr; + } + + private: + friend class base::RefCounted<TestSharedURLLoaderFactory>; + ~TestSharedURLLoaderFactory() override = default; +}; + +class MockPendingSharedURLLoaderFactory + : public network::PendingSharedURLLoaderFactory { + public: + explicit MockPendingSharedURLLoaderFactory() + : factory_(base::MakeRefCounted<TestSharedURLLoaderFactory>()) {} + + scoped_refptr<TestSharedURLLoaderFactory> factory() const { return factory_; } + + protected: + scoped_refptr<network::SharedURLLoaderFactory> CreateFactory() override { + return factory_; + } + + scoped_refptr<TestSharedURLLoaderFactory> factory_; +}; + +class MockResourceRequestSender : public WebResourceRequestSender { + public: + void CreatePendingRequest(scoped_refptr<WebRequestPeer> peer) { + peer_ = std::move(peer); + } + + void DeletePendingRequest( + scoped_refptr<base::SingleThreadTaskRunner> task_runner) override { + peer_.reset(); + } + + private: + scoped_refptr<WebRequestPeer> peer_; +}; + +} // namespace + +class SyncLoadContextTest : public testing::Test { + public: + SyncLoadContextTest() : loading_thread_("loading thread") {} + + void SetUp() override { + ASSERT_TRUE(loading_thread_.StartAndWaitForTesting()); + } + + void StartAsyncWithWaitableEventOnLoadingThread( + std::unique_ptr<network::ResourceRequest> request, + std::unique_ptr<network::PendingSharedURLLoaderFactory> pending_factory, + SyncLoadResponse* out_response, + SyncLoadContext** context_for_redirect, + base::WaitableEvent* redirect_or_response_event) { + loading_thread_.task_runner()->PostTask( + FROM_HERE, + base::BindOnce( + &SyncLoadContext::StartAsyncWithWaitableEvent, std::move(request), + MSG_ROUTING_NONE, loading_thread_.task_runner(), + TRAFFIC_ANNOTATION_FOR_TESTS, 0 /* loader_options */, + std::move(pending_factory), + WebVector<std::unique_ptr<URLLoaderThrottle>>(), out_response, + context_for_redirect, redirect_or_response_event, + nullptr /* terminate_sync_load_event */, + base::TimeDelta::FromSeconds(60) /* timeout */, + mojo::NullRemote() /* download_to_blob_registry */, + WebVector<WebString>() /* cors_exempt_header_list */, + std::make_unique<ResourceLoadInfoNotifierWrapper>( + /*resource_load_info_notifier=*/nullptr, + task_environment_.GetMainThreadTaskRunner()))); + } + + static void RunSyncLoadContextViaDataPipe( + network::ResourceRequest* request, + SyncLoadResponse* response, + SyncLoadContext** context_for_redirect, + std::string expected_data, + base::WaitableEvent* redirect_or_response_event, + scoped_refptr<base::SingleThreadTaskRunner> task_runner) { + DCHECK(task_runner->BelongsToCurrentThread()); + auto* context = new SyncLoadContext( + request, std::make_unique<MockPendingSharedURLLoaderFactory>(), + response, context_for_redirect, redirect_or_response_event, + nullptr /* terminate_sync_load_event */, + base::TimeDelta::FromSeconds(60) /* timeout */, + mojo::NullRemote() /* download_to_blob_registry */, task_runner); + + auto mock_resource_request_sender = + std::make_unique<MockResourceRequestSender>(); + mock_resource_request_sender->CreatePendingRequest( + base::WrapRefCounted(context)); + context->resource_request_sender_ = std::move(mock_resource_request_sender); + + // Simulate the response. + context->OnReceivedResponse(network::mojom::URLResponseHead::New()); + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + EXPECT_EQ(MOJO_RESULT_OK, + mojo::CreateDataPipe(nullptr /* options */, producer_handle, + consumer_handle)); + context->OnStartLoadingResponseBody(std::move(consumer_handle)); + context->OnCompletedRequest(network::URLLoaderCompletionStatus(net::OK)); + + mojo::BlockingCopyFromString(expected_data, producer_handle); + } + + protected: + base::test::TaskEnvironment task_environment_; + base::Thread loading_thread_; +}; + +TEST_F(SyncLoadContextTest, StartAsyncWithWaitableEvent) { + GURL expected_url = GURL("https://example.com"); + std::string expected_data = "foobarbaz"; + + // Create and exercise SyncLoadContext on the |loading_thread_|. + auto request = std::make_unique<network::ResourceRequest>(); + request->url = expected_url; + auto pending_factory = std::make_unique<MockPendingSharedURLLoaderFactory>(); + pending_factory->factory()->AddResponse(expected_url.spec(), expected_data); + SyncLoadResponse response; + SyncLoadContext* context_for_redirect = nullptr; + base::WaitableEvent redirect_or_response_event( + base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + StartAsyncWithWaitableEventOnLoadingThread( + std::move(request), std::move(pending_factory), &response, + &context_for_redirect, &redirect_or_response_event); + + // Wait until the response is received. + redirect_or_response_event.Wait(); + + // Check if |response| is set properly after the WaitableEvent fires. + EXPECT_EQ(net::OK, response.error_code); + const char* response_data = nullptr; + size_t size = response.data.GetSomeData(response_data, 0); + EXPECT_EQ(expected_data, std::string(response_data, size)); +} + +TEST_F(SyncLoadContextTest, ResponseBodyViaDataPipe) { + GURL expected_url = GURL("https://example.com"); + std::string expected_data = "foobarbaz"; + + // Create and exercise SyncLoadContext on the |loading_thread_|. + auto request = std::make_unique<network::ResourceRequest>(); + request->url = expected_url; + SyncLoadResponse response; + base::WaitableEvent redirect_or_response_event( + base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + SyncLoadContext* context_for_redirect = nullptr; + loading_thread_.task_runner()->PostTask( + FROM_HERE, + base::BindOnce(&SyncLoadContextTest::RunSyncLoadContextViaDataPipe, + request.get(), &response, &context_for_redirect, + expected_data, &redirect_or_response_event, + loading_thread_.task_runner())); + + // Wait until the response is received. + redirect_or_response_event.Wait(); + + // Check if |response| is set properly after the WaitableEvent fires. + EXPECT_EQ(net::OK, response.error_code); + const char* response_data = nullptr; + size_t size = response.data.GetSomeData(response_data, 0); + EXPECT_EQ(expected_data, std::string(response_data, size)); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/sync_load_response.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/sync_load_response.cc new file mode 100644 index 00000000000..d57646a79e7 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/sync_load_response.cc @@ -0,0 +1,18 @@ +// 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/url_loader/sync_load_response.h" + +namespace blink { + +SyncLoadResponse::SyncLoadResponse() = default; + +SyncLoadResponse::SyncLoadResponse(SyncLoadResponse&& other) = default; + +SyncLoadResponse::~SyncLoadResponse() = default; + +SyncLoadResponse& SyncLoadResponse::operator=(SyncLoadResponse&& other) = + default; + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/sync_load_response.h b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/sync_load_response.h new file mode 100644 index 00000000000..01203eb37f2 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/sync_load_response.h @@ -0,0 +1,60 @@ +// 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_URL_LOADER_SYNC_LOAD_RESPONSE_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_URL_LOADER_SYNC_LOAD_RESPONSE_H_ + +#include "base/optional.h" +#include "net/dns/public/resolve_error_info.h" +#include "services/network/public/cpp/cors/cors_error_status.h" +#include "services/network/public/mojom/url_response_head.mojom.h" +#include "third_party/blink/public/mojom/blob/serialized_blob.mojom.h" +#include "third_party/blink/public/platform/web_common.h" +#include "third_party/blink/public/platform/web_data.h" +#include "url/gurl.h" + +namespace blink { + +// See the SyncLoad method. (The name of this struct is not +// suffixed with "Info" because it also contains the response data.) +struct BLINK_PLATFORM_EXPORT SyncLoadResponse { + SyncLoadResponse(); + SyncLoadResponse(SyncLoadResponse&& other); + ~SyncLoadResponse(); + + SyncLoadResponse& operator=(SyncLoadResponse&& other); + + base::Optional<net::RedirectInfo> redirect_info; + + network::mojom::URLResponseHeadPtr head = + network::mojom::URLResponseHead::New(); + + // The response error code. + int error_code; + + // The response extended error code. + int extended_error_code = 0; + + // Detailed host resolution error information. + net::ResolveErrorInfo resolve_error_info; + + // Optional CORS error details. + base::Optional<network::CorsErrorStatus> cors_error; + + // The final URL of the response. This may differ from the request URL in + // the case of a server redirect. + // Use GURL to avoid extra conversion between KURL and GURL because non-blink + // types are allowed for loader here. + GURL url; + + // The response data. + WebData data; + + // Used for blob response type XMLHttpRequest. + mojom::SerializedBlobPtr downloaded_blob; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_URL_LOADER_SYNC_LOAD_RESPONSE_H_ diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/web_bundle_subresource_loader.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/web_bundle_subresource_loader.cc deleted file mode 100644 index 1c96e428bca..00000000000 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/web_bundle_subresource_loader.cc +++ /dev/null @@ -1,459 +0,0 @@ -// Copyright 2020 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/url_loader/web_bundle_subresource_loader.h" - -#include "base/numerics/safe_conversions.h" -#include "base/strings/string_number_conversions.h" -#include "base/task/post_task.h" -#include "components/web_package/web_bundle_parser.h" -#include "components/web_package/web_bundle_utils.h" -#include "mojo/public/cpp/bindings/receiver_set.h" -#include "mojo/public/cpp/system/data_pipe.h" -#include "mojo/public/cpp/system/data_pipe_drainer.h" -#include "mojo/public/cpp/system/data_pipe_producer.h" -#include "net/http/http_status_code.h" -#include "services/network/public/mojom/url_loader.mojom.h" -#include "services/network/public/mojom/url_loader_factory.mojom.h" -#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h" -#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" -#include "third_party/blink/renderer/platform/wtf/functional.h" -#include "third_party/blink/renderer/platform/wtf/shared_buffer.h" - -namespace blink { - -namespace { - -class WebBundleSubresourceLoader : public network::mojom::URLLoader { - public: - WebBundleSubresourceLoader( - mojo::PendingReceiver<network::mojom::URLLoader> loader, - const network::ResourceRequest& request, - mojo::PendingRemote<network::mojom::URLLoaderClient> client) - : url_(request.url), - receiver_(this, std::move(loader)), - client_(std::move(client)) { - receiver_.set_disconnect_handler(base::BindOnce( - &WebBundleSubresourceLoader::OnMojoDisconnect, GetWeakPtr())); - } - WebBundleSubresourceLoader(const WebBundleSubresourceLoader&) = delete; - WebBundleSubresourceLoader& operator=(const WebBundleSubresourceLoader&) = - delete; - - const GURL& Url() const { return url_; } - - base::WeakPtr<WebBundleSubresourceLoader> GetWeakPtr() { - return weak_ptr_factory_.GetWeakPtr(); - } - - void OnResponse(network::mojom::URLResponseHeadPtr response) { - client_->OnReceiveResponse(std::move(response)); - } - - void OnData(mojo::ScopedDataPipeConsumerHandle consumer) { - client_->OnStartLoadingResponseBody(std::move(consumer)); - } - - void OnFail(net::Error error) { - client_->OnComplete(network::URLLoaderCompletionStatus(error)); - delete this; - } - - void OnWriteCompleted(MojoResult result) { - network::URLLoaderCompletionStatus status( - result == MOJO_RESULT_OK ? net::OK : net::ERR_INVALID_WEB_BUNDLE); - client_->OnComplete(status); - delete this; - } - - private: - // network::mojom::URLLoader - void FollowRedirect( - const std::vector<std::string>& removed_headers, - const net::HttpRequestHeaders& modified_headers, - const net::HttpRequestHeaders& modified_cors_exempt_headers, - const base::Optional<GURL>& new_url) override { - NOTREACHED(); - } - - void SetPriority(net::RequestPriority priority, - int32_t intra_priority_value) override { - // Not supported (do nothing). - } - - void PauseReadingBodyFromNet() override {} - void ResumeReadingBodyFromNet() override {} - - void OnMojoDisconnect() { delete this; } - - const GURL url_; - mojo::Receiver<network::mojom::URLLoader> receiver_; - mojo::Remote<network::mojom::URLLoaderClient> client_; - base::WeakPtrFactory<WebBundleSubresourceLoader> weak_ptr_factory_{this}; -}; - -class PipeDataSource : public mojo::DataPipeProducer::DataSource { - public: - explicit PipeDataSource(std::vector<uint8_t> data) : data_(std::move(data)) {} - uint64_t GetLength() const override { return data_.size(); } - - ReadResult Read(uint64_t uint64_offset, base::span<char> buffer) override { - ReadResult result; - if (uint64_offset > data_.size()) { - result.result = MOJO_RESULT_OUT_OF_RANGE; - return result; - } - size_t offset = base::checked_cast<size_t>(uint64_offset); - size_t len = std::min(data_.size() - offset, buffer.size()); - if (len > 0) { - DCHECK_LT(offset, data_.size()); - memcpy(buffer.data(), &data_[offset], len); - } - result.bytes_read = len; - return result; - } - - private: - // Since mojo::DataPipeProducer runs in its own sequence, we can't just have - // a reference to the SharedBuffer in LinkWebBundleDataSource. - std::vector<uint8_t> data_; -}; - -void DeleteProducerAndRunCallback( - std::unique_ptr<mojo::DataPipeProducer> producer, - base::OnceCallback<void(MojoResult result)> callback, - MojoResult result) { - std::move(callback).Run(result); -} - -class LinkWebBundleDataSource : public web_package::mojom::BundleDataSource, - public mojo::DataPipeDrainer::Client { - public: - using ReadToDataPipeCallback = base::OnceCallback<void(MojoResult result)>; - - LinkWebBundleDataSource( - mojo::PendingReceiver<web_package::mojom::BundleDataSource> - data_source_receiver, - mojo::ScopedDataPipeConsumerHandle bundle_body) - : data_source_receiver_(this, std::move(data_source_receiver)), - data_(SharedBuffer::Create()), - pipe_drainer_( - std::make_unique<mojo::DataPipeDrainer>(this, - std::move(bundle_body))) {} - - ~LinkWebBundleDataSource() override { - // The receiver must be closed before destructing pending callbacks in - // |pending_reads_| / |pending_reads_to_data_pipe_|. - data_source_receiver_.reset(); - } - - LinkWebBundleDataSource(const LinkWebBundleDataSource&) = delete; - LinkWebBundleDataSource& operator=(const LinkWebBundleDataSource&) = delete; - - void ReadToDataPipe(mojo::ScopedDataPipeProducerHandle producer, - uint64_t offset, - uint64_t length, - ReadToDataPipeCallback callback) { - TRACE_EVENT0("loading", "LinkWebBundleDataSource::ReadToDataPipe"); - if (!finished_loading_ && offset + length > data_->size()) { - // Current implementation does not support progressive loading of inner - // response body. - PendingReadToDataPipe pending; - pending.producer = std::move(producer); - pending.offset = offset; - pending.length = length; - pending.callback = std::move(callback); - pending_reads_to_data_pipe_.push_back(std::move(pending)); - return; - } - - auto writer = std::make_unique<mojo::DataPipeProducer>(std::move(producer)); - mojo::DataPipeProducer* raw_writer = writer.get(); - raw_writer->Write(std::make_unique<PipeDataSource>(GetData(offset, length)), - base::BindOnce(&DeleteProducerAndRunCallback, - std::move(writer), std::move(callback))); - } - - // mojom::BundleDataSource - void Read(uint64_t offset, uint64_t length, ReadCallback callback) override { - TRACE_EVENT0("loading", "LinkWebBundleDataSource::Read"); - if (!finished_loading_ && offset + length > data_->size()) { - PendingRead pending; - pending.offset = offset; - pending.length = length; - pending.callback = std::move(callback); - pending_reads_.push_back(std::move(pending)); - return; - } - std::move(callback).Run(GetData(offset, length)); - } - - // mojo::DataPipeDrainer::Client - void OnDataAvailable(const void* data, size_t num_bytes) override { - DCHECK(!finished_loading_); - data_->Append(reinterpret_cast<const char*>(data), num_bytes); - ProcessPendingReads(); - } - - void OnDataComplete() override { - DCHECK(!finished_loading_); - finished_loading_ = true; - ProcessPendingReads(); - } - - private: - void ProcessPendingReads() { - std::vector<PendingRead> pendings; - pendings.swap(pending_reads_); - for (auto& pending : pendings) - Read(pending.offset, pending.length, std::move(pending.callback)); - - std::vector<PendingReadToDataPipe> pipe_pendings; - pipe_pendings.swap(pending_reads_to_data_pipe_); - for (auto& pending : pipe_pendings) { - ReadToDataPipe(std::move(pending.producer), pending.offset, - pending.length, std::move(pending.callback)); - } - } - - std::vector<uint8_t> GetData(uint64_t uint64_offset, uint64_t uint64_length) { - size_t offset = base::checked_cast<size_t>(uint64_offset); - size_t length = base::checked_cast<size_t>(uint64_length); - std::vector<uint8_t> output(length); - size_t bytes_copied = 0; - for (auto it = data_->GetIteratorAt(offset); - bytes_copied < length && it != data_->cend(); it++) { - size_t n = std::min(it->size(), length - bytes_copied); - memcpy(output.data() + bytes_copied, it->data(), n); - bytes_copied += n; - } - output.resize(bytes_copied); - return output; - } - - struct PendingRead { - uint64_t offset; - uint64_t length; - ReadCallback callback; - }; - struct PendingReadToDataPipe { - mojo::ScopedDataPipeProducerHandle producer; - uint64_t offset; - uint64_t length; - ReadToDataPipeCallback callback; - }; - - mojo::Receiver<web_package::mojom::BundleDataSource> data_source_receiver_; - scoped_refptr<SharedBuffer> data_; - std::vector<PendingRead> pending_reads_; - std::vector<PendingReadToDataPipe> pending_reads_to_data_pipe_; - bool finished_loading_ = false; - std::unique_ptr<mojo::DataPipeDrainer> pipe_drainer_; -}; - -// Self destroys when no more bindings exist. -class WebBundleSubresourceLoaderFactory - : public network::mojom::URLLoaderFactory { - public: - WebBundleSubresourceLoaderFactory( - mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver, - mojo::ScopedDataPipeConsumerHandle bundle_body, - scoped_refptr<base::SingleThreadTaskRunner> callback_task_runner, - WebBundleErrorCallback error_callback) - : callback_task_runner_(callback_task_runner), - error_callback_(error_callback) { - receivers_.Add(this, std::move(receiver)); - receivers_.set_disconnect_handler(base::BindRepeating( - &WebBundleSubresourceLoaderFactory::OnMojoDisconnect, - base::Unretained(this))); - mojo::PendingRemote<web_package::mojom::BundleDataSource> data_source; - source_ = std::make_unique<LinkWebBundleDataSource>( - data_source.InitWithNewPipeAndPassReceiver(), std::move(bundle_body)); - // WebBundleParser will self-destruct on remote mojo ends' disconnection. - new web_package::WebBundleParser(parser_.BindNewPipeAndPassReceiver(), - std::move(data_source)); - - parser_->ParseMetadata( - WTF::Bind(&WebBundleSubresourceLoaderFactory::OnMetadataParsed, - weak_ptr_factory_.GetWeakPtr())); - } - - ~WebBundleSubresourceLoaderFactory() override { - for (auto loader : pending_loaders_) { - if (loader) - loader->OnFail(net::ERR_FAILED); - } - } - - WebBundleSubresourceLoaderFactory(const WebBundleSubresourceLoaderFactory&) = - delete; - WebBundleSubresourceLoaderFactory& operator=( - const WebBundleSubresourceLoaderFactory&) = delete; - - void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> factory) - override { - receivers_.Add(this, std::move(factory)); - } - - void CreateLoaderAndStart( - mojo::PendingReceiver<network::mojom::URLLoader> pending_receiver, - int32_t routing_id, - int32_t request_id, - uint32_t options, - const network::ResourceRequest& request, - mojo::PendingRemote<network::mojom::URLLoaderClient> client, - const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) - override { - TRACE_EVENT0("loading", - "WebBundleSubresourceLoaderFactory::CreateLoaderAndStart"); - WebBundleSubresourceLoader* loader = new WebBundleSubresourceLoader( - std::move(pending_receiver), request, std::move(client)); - if (metadata_error_) { - loader->OnFail(net::ERR_INVALID_WEB_BUNDLE); - return; - } - if (!metadata_) { - pending_loaders_.push_back(loader->GetWeakPtr()); - return; - } - StartLoad(loader); - } - - private: - void OnMojoDisconnect() { - if (!receivers_.empty()) - return; - delete this; - } - - void StartLoad(WebBundleSubresourceLoader* loader) { - DCHECK(metadata_); - if (!loader) - return; - auto it = metadata_->requests.find(loader->Url()); - if (it == metadata_->requests.end()) { - RunErrorCallback(WebBundleErrorType::kResourceNotFound, - loader->Url().possibly_invalid_spec() + - " is not found in the WebBundle."); - loader->OnFail(net::ERR_INVALID_WEB_BUNDLE); - return; - } - // Currently, we just return the first response for the URL. - // TODO(crbug.com/1082020): Support variant matching. - auto& location = it->second->response_locations[0]; - parser_->ParseResponse( - location->offset, location->length, - WTF::Bind(&WebBundleSubresourceLoaderFactory::OnResponseParsed, - weak_ptr_factory_.GetWeakPtr(), loader->GetWeakPtr())); - } - - void OnMetadataParsed(web_package::mojom::BundleMetadataPtr metadata, - web_package::mojom::BundleMetadataParseErrorPtr error) { - TRACE_EVENT0("loading", - "WebBundleSubresourceLoaderFactory::OnMetadataParsed"); - if (error) { - metadata_error_ = std::move(error); - RunErrorCallback(WebBundleErrorType::kMetadataParseError, - metadata_error_->message); - for (auto loader : pending_loaders_) { - if (loader) - loader->OnFail(net::ERR_INVALID_WEB_BUNDLE); - } - pending_loaders_.clear(); - return; - } - - metadata_ = std::move(metadata); - for (auto loader : pending_loaders_) - StartLoad(loader.get()); - pending_loaders_.clear(); - } - - void OnResponseParsed(base::WeakPtr<WebBundleSubresourceLoader> loader, - web_package::mojom::BundleResponsePtr response, - web_package::mojom::BundleResponseParseErrorPtr error) { - TRACE_EVENT0("loading", - "WebBundleSubresourceLoaderFactory::OnResponseParsed"); - if (!loader) - return; - if (error) { - RunErrorCallback(WebBundleErrorType::kResponseParseError, error->message); - loader->OnFail(net::ERR_INVALID_WEB_BUNDLE); - return; - } - // Currently we allow only net::HTTP_OK responses in bundles. - // TODO(crbug.com/990733): Revisit this once - // https://github.com/WICG/webpackage/issues/478 is resolved. - if (response->response_code != net::HTTP_OK) { - RunErrorCallback(WebBundleErrorType::kResponseParseError, - "Invalid response code " + - base::NumberToString(response->response_code)); - loader->OnFail(net::ERR_INVALID_WEB_BUNDLE); - return; - } - - loader->OnResponse(web_package::CreateResourceResponse(response)); - - mojo::ScopedDataPipeProducerHandle producer; - mojo::ScopedDataPipeConsumerHandle consumer; - if (CreateDataPipe(nullptr, &producer, &consumer) != MOJO_RESULT_OK) { - loader->OnFail(net::ERR_INSUFFICIENT_RESOURCES); - return; - } - loader->OnData(std::move(consumer)); - source_->ReadToDataPipe( - std::move(producer), response->payload_offset, response->payload_length, - base::BindOnce(&WebBundleSubresourceLoader::OnWriteCompleted, - loader->GetWeakPtr())); - } - - void RunErrorCallback(WebBundleErrorType type, const std::string& message) { - PostCrossThreadTask(*callback_task_runner_, FROM_HERE, - WTF::CrossThreadBindOnce(error_callback_, type, - String(message.c_str()))); - } - - mojo::ReceiverSet<network::mojom::URLLoaderFactory> receivers_; - std::unique_ptr<LinkWebBundleDataSource> source_; - mojo::Remote<web_package::mojom::WebBundleParser> parser_; - web_package::mojom::BundleMetadataPtr metadata_; - web_package::mojom::BundleMetadataParseErrorPtr metadata_error_; - std::vector<base::WeakPtr<WebBundleSubresourceLoader>> pending_loaders_; - scoped_refptr<base::SingleThreadTaskRunner> callback_task_runner_; - WebBundleErrorCallback error_callback_; - - base::WeakPtrFactory<WebBundleSubresourceLoaderFactory> weak_ptr_factory_{ - this}; -}; - -// Runs on a background thread. -void CreateFactoryOnBackground( - mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver, - mojo::ScopedDataPipeConsumerHandle bundle_body, - scoped_refptr<base::SingleThreadTaskRunner> callback_task_runner, - WebBundleErrorCallback error_callback) { - new WebBundleSubresourceLoaderFactory( - std::move(receiver), std::move(bundle_body), callback_task_runner, - std::move(error_callback)); -} - -} // namespace - -void CreateWebBundleSubresourceLoaderFactory( - CrossVariantMojoReceiver<network::mojom::URLLoaderFactoryInterfaceBase> - factory_receiver, - mojo::ScopedDataPipeConsumerHandle bundle_body, - WebBundleErrorCallback error_callback) { - auto task_runner = base::ThreadPool::CreateSequencedTaskRunner( - {base::TaskPriority::USER_VISIBLE, - base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}); - task_runner->PostTask( - FROM_HERE, - base::BindOnce(&CreateFactoryOnBackground, std::move(factory_receiver), - std::move(bundle_body), - base::ThreadTaskRunnerHandle::Get(), - std::move(error_callback))); -} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/web_bundle_subresource_loader.h b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/web_bundle_subresource_loader.h deleted file mode 100644 index 82866299c59..00000000000 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/web_bundle_subresource_loader.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2020 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_URL_LOADER_WEB_BUNDLE_SUBRESOURCE_LOADER_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_URL_LOADER_WEB_BUNDLE_SUBRESOURCE_LOADER_H_ - -#include "mojo/public/cpp/bindings/pending_receiver.h" -#include "services/network/public/mojom/url_loader_factory.mojom-shared.h" -#include "third_party/blink/public/platform/cross_variant_mojo_util.h" -#include "third_party/blink/renderer/platform/platform_export.h" - -namespace WTF { -class String; -} // namespace WTF - -namespace blink { - -enum class WebBundleErrorType { - kMetadataParseError, - kResponseParseError, - kResourceNotFound, -}; - -using WebBundleErrorCallback = - base::RepeatingCallback<void(WebBundleErrorType, - const WTF::String& message)>; - -// Creates a network::mojom::URLLoaderFactory that can load resources from a -// WebBundle, and binds it to |factory_receiver|. -PLATFORM_EXPORT void CreateWebBundleSubresourceLoaderFactory( - CrossVariantMojoReceiver<network::mojom::URLLoaderFactoryInterfaceBase> - factory_receiver, - mojo::ScopedDataPipeConsumerHandle bundle_body, - WebBundleErrorCallback error_callback); - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_URL_LOADER_WEB_BUNDLE_SUBRESOURCE_LOADER_H_ diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/web_bundle_subresource_loader_test.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/web_bundle_subresource_loader_test.cc deleted file mode 100644 index 545ffebb7ac..00000000000 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/web_bundle_subresource_loader_test.cc +++ /dev/null @@ -1,336 +0,0 @@ -// Copyright 2020 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/url_loader/web_bundle_subresource_loader.h" - -#include "base/test/task_environment.h" -#include "components/web_package/test_support/web_bundle_builder.h" -#include "mojo/public/cpp/bindings/remote.h" -#include "mojo/public/cpp/system/data_pipe_utils.h" -#include "net/traffic_annotation/network_traffic_annotation_test_helper.h" -#include "services/network/public/mojom/url_loader.mojom.h" -#include "services/network/public/mojom/url_loader_factory.mojom.h" -#include "services/network/test/test_url_loader_client.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" - -namespace blink { - -namespace { - -const char kResourceUrl[] = "https://example.com/"; -const char kResourceUrl2[] = "https://example.com/another"; -const char kResourceUrl3[] = "https://example.com/yetanother"; - -std::vector<uint8_t> CreateSmallBundle() { - web_package::test::WebBundleBuilder builder(kResourceUrl, - "" /* manifest_url */); - builder.AddExchange(kResourceUrl, - {{":status", "200"}, {"content-type", "text/plain"}}, - "body"); - return builder.CreateBundle(); -} - -std::vector<uint8_t> CreateLargeBundle() { - web_package::test::WebBundleBuilder builder(kResourceUrl, - "" /* manifest_url */); - builder.AddExchange(kResourceUrl, - {{":status", "200"}, {"content-type", "text/plain"}}, - "body"); - builder.AddExchange(kResourceUrl2, - {{":status", "200"}, {"content-type", "text/plain"}}, - std::string(10000, 'a')); - builder.AddExchange(kResourceUrl3, - {{":status", "200"}, {"content-type", "text/plain"}}, - "body"); - return builder.CreateBundle(); -} - -} // namespace - -class WebBundleSubresourceLoaderFactoryTest : public ::testing::Test { - public: - void SetUp() override { - mojo::ScopedDataPipeConsumerHandle consumer; - ASSERT_EQ(CreateDataPipe(nullptr, &bundle_data_destination_, &consumer), - MOJO_RESULT_OK); - CreateWebBundleSubresourceLoaderFactory( - loader_factory_.BindNewPipeAndPassReceiver(), std::move(consumer), - base::BindRepeating( - &WebBundleSubresourceLoaderFactoryTest::OnWebBundleError, - base::Unretained(this))); - } - - void WriteBundle(base::span<const uint8_t> data) { - mojo::BlockingCopyFromString( - std::string(reinterpret_cast<const char*>(data.data()), data.size()), - bundle_data_destination_); - } - - void FinishWritingBundle() { bundle_data_destination_.reset(); } - - struct StartRequestResult { - mojo::Remote<network::mojom::URLLoader> loader; - std::unique_ptr<network::TestURLLoaderClient> client; - }; - - StartRequestResult StartRequest(const GURL& url) { - return StartRequestWithLoaderFactory(loader_factory_, url); - } - - void RunUntilBundleError() { - if (last_bundle_error_.has_value()) - return; - base::RunLoop run_loop; - quit_closure_for_bundle_error_ = run_loop.QuitClosure(); - run_loop.Run(); - } - - const base::Optional<std::pair<WebBundleErrorType, WTF::String>>& - last_bundle_error() const { - return last_bundle_error_; - } - - protected: - StartRequestResult StartRequestWithLoaderFactory( - mojo::Remote<network::mojom::URLLoaderFactory>& factory, - const GURL& url) { - network::ResourceRequest request; - request.url = url; - request.method = "GET"; - StartRequestResult result; - result.client = std::make_unique<network::TestURLLoaderClient>(); - factory->CreateLoaderAndStart( - result.loader.BindNewPipeAndPassReceiver(), 0 /* routing_id */, - 0 /* request_id */, 0 /* options */, request, - result.client->CreateRemote(), - net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS)); - return result; - } - - mojo::Remote<network::mojom::URLLoaderFactory> loader_factory_; - - private: - void OnWebBundleError(WebBundleErrorType type, const WTF::String& message) { - last_bundle_error_ = std::make_pair(type, message); - if (quit_closure_for_bundle_error_) - std::move(quit_closure_for_bundle_error_).Run(); - } - - mojo::ScopedDataPipeProducerHandle bundle_data_destination_; - base::test::TaskEnvironment task_environment; - base::Optional<std::pair<WebBundleErrorType, WTF::String>> last_bundle_error_; - base::OnceClosure quit_closure_for_bundle_error_; -}; - -TEST_F(WebBundleSubresourceLoaderFactoryTest, Basic) { - WriteBundle(CreateSmallBundle()); - FinishWritingBundle(); - - auto request = StartRequest(GURL(kResourceUrl)); - request.client->RunUntilComplete(); - - EXPECT_EQ(net::OK, request.client->completion_status().error_code); - EXPECT_FALSE(last_bundle_error().has_value()); - std::string body; - EXPECT_TRUE(mojo::BlockingCopyToString( - request.client->response_body_release(), &body)); - EXPECT_EQ("body", body); -} - -TEST_F(WebBundleSubresourceLoaderFactoryTest, Clone) { - mojo::Remote<network::mojom::URLLoaderFactory> cloned_factory_; - loader_factory_->Clone(cloned_factory_.BindNewPipeAndPassReceiver()); - - WriteBundle(CreateSmallBundle()); - FinishWritingBundle(); - - auto request = - StartRequestWithLoaderFactory(cloned_factory_, GURL(kResourceUrl)); - request.client->RunUntilComplete(); - - EXPECT_EQ(net::OK, request.client->completion_status().error_code); - EXPECT_FALSE(last_bundle_error().has_value()); -} - -TEST_F(WebBundleSubresourceLoaderFactoryTest, MetadataParseError) { - auto request = StartRequest(GURL(kResourceUrl)); - - std::vector<uint8_t> bundle = CreateSmallBundle(); - bundle[4] ^= 1; // Mutate magic bytes. - WriteBundle(bundle); - FinishWritingBundle(); - - request.client->RunUntilComplete(); - RunUntilBundleError(); - - EXPECT_EQ(net::ERR_INVALID_WEB_BUNDLE, - request.client->completion_status().error_code); - EXPECT_EQ(last_bundle_error()->first, - WebBundleErrorType::kMetadataParseError); - EXPECT_EQ(last_bundle_error()->second, "Wrong magic bytes."); - - // Requests made after metadata parse error should also fail. - auto request2 = StartRequest(GURL(kResourceUrl)); - request2.client->RunUntilComplete(); - - EXPECT_EQ(net::ERR_INVALID_WEB_BUNDLE, - request2.client->completion_status().error_code); -} - -TEST_F(WebBundleSubresourceLoaderFactoryTest, ResponseParseError) { - web_package::test::WebBundleBuilder builder(kResourceUrl, - "" /* manifest_url */); - // An invalid response. - builder.AddExchange(kResourceUrl, {{":status", "0"}}, "body"); - WriteBundle(builder.CreateBundle()); - FinishWritingBundle(); - - auto request = StartRequest(GURL(kResourceUrl)); - request.client->RunUntilComplete(); - RunUntilBundleError(); - - EXPECT_EQ(net::ERR_INVALID_WEB_BUNDLE, - request.client->completion_status().error_code); - EXPECT_EQ(last_bundle_error()->first, - WebBundleErrorType::kResponseParseError); - EXPECT_EQ(last_bundle_error()->second, - ":status must be 3 ASCII decimal digits."); -} - -TEST_F(WebBundleSubresourceLoaderFactoryTest, ResourceNotFoundInBundle) { - WriteBundle(CreateSmallBundle()); - FinishWritingBundle(); - - auto request = StartRequest(GURL("https://example.com/no-such-resource")); - request.client->RunUntilComplete(); - RunUntilBundleError(); - - EXPECT_EQ(net::ERR_INVALID_WEB_BUNDLE, - request.client->completion_status().error_code); - EXPECT_EQ(last_bundle_error()->first, WebBundleErrorType::kResourceNotFound); - EXPECT_EQ( - last_bundle_error()->second, - "https://example.com/no-such-resource is not found in the WebBundle."); -} - -TEST_F(WebBundleSubresourceLoaderFactoryTest, RedirectResponseIsNotAllowed) { - web_package::test::WebBundleBuilder builder(kResourceUrl, - "" /* manifest_url */); - builder.AddExchange(kResourceUrl, - {{":status", "301"}, {"location", kResourceUrl2}}, ""); - builder.AddExchange(kResourceUrl2, - {{":status", "200"}, {"content-type", "text/plain"}}, - "body"); - WriteBundle(builder.CreateBundle()); - FinishWritingBundle(); - - auto request = StartRequest(GURL(kResourceUrl)); - request.client->RunUntilComplete(); - RunUntilBundleError(); - - EXPECT_EQ(net::ERR_INVALID_WEB_BUNDLE, - request.client->completion_status().error_code); - EXPECT_EQ(last_bundle_error()->first, - WebBundleErrorType::kResponseParseError); - EXPECT_EQ(last_bundle_error()->second, "Invalid response code 301"); -} - -TEST_F(WebBundleSubresourceLoaderFactoryTest, StartRequestBeforeReadingBundle) { - auto request = StartRequest(GURL(kResourceUrl)); - - WriteBundle(CreateSmallBundle()); - FinishWritingBundle(); - request.client->RunUntilComplete(); - - EXPECT_EQ(net::OK, request.client->completion_status().error_code); -} - -TEST_F(WebBundleSubresourceLoaderFactoryTest, MultipleRequests) { - auto request1 = StartRequest(GURL(kResourceUrl)); - auto request2 = StartRequest(GURL(kResourceUrl2)); - - std::vector<uint8_t> bundle = CreateLargeBundle(); - // Write the first 10kB of the bundle in which the bundle's metadata and the - // response for kResourceUrl are included. - ASSERT_GT(bundle.size(), 10000U); - WriteBundle(base::make_span(bundle).subspan(0, 10000)); - request1.client->RunUntilComplete(); - - EXPECT_EQ(net::OK, request1.client->completion_status().error_code); - EXPECT_FALSE(request2.client->has_received_completion()); - - // Write the rest of the data. - WriteBundle(base::make_span(bundle).subspan(10000)); - FinishWritingBundle(); - request2.client->RunUntilComplete(); - - EXPECT_EQ(net::OK, request2.client->completion_status().error_code); -} - -TEST_F(WebBundleSubresourceLoaderFactoryTest, CancelRequest) { - auto request_to_complete1 = StartRequest(GURL(kResourceUrl)); - auto request_to_complete2 = StartRequest(GURL(kResourceUrl2)); - auto request_to_cancel1 = StartRequest(GURL(kResourceUrl)); - auto request_to_cancel2 = StartRequest(GURL(kResourceUrl2)); - auto request_to_cancel3 = StartRequest(GURL(kResourceUrl3)); - - // Cancel request before getting metadata. - request_to_cancel1.loader.reset(); - - std::vector<uint8_t> bundle = CreateLargeBundle(); - // Write the first 10kB of the bundle in which the bundle's metadata, response - // for kResourceUrl, and response header for kResourceUrl2 are included. - ASSERT_GT(bundle.size(), 10000U); - WriteBundle(base::make_span(bundle).subspan(0, 10000)); - - // This makes sure the bytes written above are consumed by WebBundle parser. - request_to_complete1.client->RunUntilComplete(); - - // Cancel request after reading response header, but before reading body. - request_to_cancel2.loader.reset(); - - // Cancel request after getting metadata, but before reading response header. - request_to_cancel3.loader.reset(); - - // Write the rest of the data. - WriteBundle(base::make_span(bundle).subspan(10000)); - FinishWritingBundle(); - request_to_complete2.client->RunUntilComplete(); - EXPECT_EQ(net::OK, - request_to_complete2.client->completion_status().error_code); -} - -TEST_F(WebBundleSubresourceLoaderFactoryTest, - FactoryDestructionCancelsInflightRequests) { - auto request = StartRequest(GURL(kResourceUrl)); - - loader_factory_.reset(); - - WriteBundle(CreateSmallBundle()); - FinishWritingBundle(); - request.client->RunUntilComplete(); - - EXPECT_EQ(net::ERR_FAILED, request.client->completion_status().error_code); -} - -TEST_F(WebBundleSubresourceLoaderFactoryTest, TruncatedBundle) { - std::vector<uint8_t> bundle = CreateSmallBundle(); - // Truncate in the middle of responses section. - bundle.resize(bundle.size() - 10); - WriteBundle(std::move(bundle)); - FinishWritingBundle(); - - auto request = StartRequest(GURL(kResourceUrl)); - request.client->RunUntilComplete(); - RunUntilBundleError(); - - EXPECT_EQ(net::ERR_INVALID_WEB_BUNDLE, - request.client->completion_status().error_code); - EXPECT_EQ(last_bundle_error()->first, - WebBundleErrorType::kResponseParseError); - EXPECT_EQ(last_bundle_error()->second, "Error reading response header."); -} - -} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/web_resource_request_sender.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/web_resource_request_sender.cc new file mode 100644 index 00000000000..dee858aaca9 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/web_resource_request_sender.cc @@ -0,0 +1,630 @@ +// Copyright 2020 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/public/platform/web_resource_request_sender.h" + +#include <utility> + +#include "base/atomic_sequence_num.h" +#include "base/bind.h" +#include "base/compiler_specific.h" +#include "base/debug/alias.h" +#include "base/files/file_path.h" +#include "base/metrics/histogram_macros.h" +#include "base/rand_util.h" +#include "base/strings/string_util.h" +#include "base/synchronization/waitable_event.h" +#include "base/task/post_task.h" +#include "base/task/thread_pool.h" +#include "base/time/time.h" +#include "build/build_config.h" +#include "net/base/load_flags.h" +#include "net/base/net_errors.h" +#include "net/base/request_priority.h" +#include "net/http/http_response_headers.h" +#include "net/url_request/referrer_policy.h" +#include "services/network/public/cpp/features.h" +#include "services/network/public/cpp/is_potentially_trustworthy.h" +#include "services/network/public/cpp/resource_request.h" +#include "services/network/public/cpp/url_loader_completion_status.h" +#include "services/network/public/mojom/fetch_api.mojom.h" +#include "services/network/public/mojom/url_response_head.mojom.h" +#include "third_party/blink/public/common/client_hints/client_hints.h" +#include "third_party/blink/public/common/loader/inter_process_time_ticks_converter.h" +#include "third_party/blink/public/common/loader/network_utils.h" +#include "third_party/blink/public/common/loader/referrer_utils.h" +#include "third_party/blink/public/common/loader/resource_type_util.h" +#include "third_party/blink/public/common/loader/throttling_url_loader.h" +#include "third_party/blink/public/mojom/frame/back_forward_cache_controller.mojom-blink.h" +#include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h" +#include "third_party/blink/public/platform/platform.h" +#include "third_party/blink/public/platform/resource_load_info_notifier_wrapper.h" +#include "third_party/blink/public/platform/web_back_forward_cache_loader_helper.h" +#include "third_party/blink/public/platform/web_request_peer.h" +#include "third_party/blink/public/platform/web_resource_request_sender_delegate.h" +#include "third_party/blink/public/platform/web_string.h" +#include "third_party/blink/renderer/platform/back_forward_cache_utils.h" +#include "third_party/blink/renderer/platform/loader/fetch/back_forward_cache_loader_helper.h" +#include "third_party/blink/renderer/platform/loader/fetch/url_loader/mojo_url_loader_client.h" +#include "third_party/blink/renderer/platform/loader/fetch/url_loader/sync_load_context.h" +#include "third_party/blink/renderer/platform/loader/fetch/url_loader/sync_load_response.h" +#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h" +#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" + +namespace WTF { + +template <> +struct CrossThreadCopier< + blink::WebVector<std::unique_ptr<blink::URLLoaderThrottle>>> { + STATIC_ONLY(CrossThreadCopier); + using Type = blink::WebVector<std::unique_ptr<blink::URLLoaderThrottle>>; + static Type Copy(Type&& value) { return std::move(value); } +}; + +template <> +struct CrossThreadCopier<net::NetworkTrafficAnnotationTag> + : public CrossThreadCopierPassThrough<net::NetworkTrafficAnnotationTag> { + STATIC_ONLY(CrossThreadCopier); + using Type = net::NetworkTrafficAnnotationTag; + static const Type& Copy(const Type& traffic_annotation) { + return traffic_annotation; + } +}; + +template <> +struct CrossThreadCopier<blink::WebVector<blink::WebString>> { + STATIC_ONLY(CrossThreadCopier); + using Type = blink::WebVector<blink::WebString>; + static Type Copy(const Type& value) { + Type result; + result.reserve(value.size()); + for (const auto& element : value) + result.emplace_back(element.IsolatedCopy()); + return result; + } +}; + +} // namespace WTF + +namespace blink { + +namespace { + +// Converts |time| from a remote to local TimeTicks, overwriting the original +// value. +void RemoteToLocalTimeTicks(const InterProcessTimeTicksConverter& converter, + base::TimeTicks* time) { + RemoteTimeTicks remote_time = RemoteTimeTicks::FromTimeTicks(*time); + *time = converter.ToLocalTimeTicks(remote_time).ToTimeTicks(); +} + +void CheckSchemeForReferrerPolicy(const network::ResourceRequest& request) { + if ((request.referrer_policy == + ReferrerUtils::GetDefaultNetReferrerPolicy() || + request.referrer_policy == + net::ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE) && + request.referrer.SchemeIsCryptographic() && + !url::Origin::Create(request.url).opaque() && + !network::IsUrlPotentiallyTrustworthy(request.url)) { + LOG(FATAL) << "Trying to send secure referrer for insecure request " + << "without an appropriate referrer policy.\n" + << "URL = " << request.url << "\n" + << "URL's Origin = " + << url::Origin::Create(request.url).Serialize() << "\n" + << "Referrer = " << request.referrer; + } +} + +int GetInitialRequestID() { + // Starting with a random number speculatively avoids RDH_INVALID_REQUEST_ID + // which are assumed to have been caused by restarting RequestID at 0 when + // restarting a renderer after a crash - this would cause collisions if + // requests from the previously crashed renderer are still active. See + // https://crbug.com/614281#c61 for more details about this hypothesis. + // + // To avoid increasing the likelihood of overflowing the range of available + // RequestIDs, kMax is set to a relatively low value of 2^20 (rather than + // to something higher like 2^31). + const int kMin = 0; + const int kMax = 1 << 20; + return base::RandInt(kMin, kMax); +} + +// Determines if the loader should be restarted on a redirect using +// ThrottlingURLLoader::FollowRedirectForcingRestart. +bool RedirectRequiresLoaderRestart(const GURL& original_url, + const GURL& redirect_url) { + // Restart is needed if the URL is no longer handled by network service. + if (network_utils::IsURLHandledByNetworkService(original_url)) + return !network_utils::IsURLHandledByNetworkService(redirect_url); + + // If URL wasn't originally handled by network service, restart is needed if + // schemes are different. + return original_url.scheme_piece() != redirect_url.scheme_piece(); +} + +} // namespace + +// static +int WebResourceRequestSender::MakeRequestID() { + static const int kInitialRequestID = GetInitialRequestID(); + static base::AtomicSequenceNumber sequence; + return kInitialRequestID + sequence.GetNext(); +} + +WebResourceRequestSender::WebResourceRequestSender() + : delegate_(Platform::Current()->GetResourceRequestSenderDelegate()) {} + +WebResourceRequestSender::~WebResourceRequestSender() = default; + +void WebResourceRequestSender::SendSync( + std::unique_ptr<network::ResourceRequest> request, + int routing_id, + const net::NetworkTrafficAnnotationTag& traffic_annotation, + uint32_t loader_options, + SyncLoadResponse* response, + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, + WebVector<std::unique_ptr<URLLoaderThrottle>> throttles, + base::TimeDelta timeout, + const WebVector<WebString>& cors_exempt_header_list, + base::WaitableEvent* terminate_sync_load_event, + mojo::PendingRemote<mojom::BlobRegistry> download_to_blob_registry, + scoped_refptr<WebRequestPeer> peer, + std::unique_ptr<ResourceLoadInfoNotifierWrapper> + resource_load_info_notifier_wrapper, + WebBackForwardCacheLoaderHelper back_forward_cache_loader_helper) { + if (IsInflightNetworkRequestBackForwardCacheSupportEnabled()) { + // Sync fetches are triggered by script, which should not run when a + // document is in back-forward cache. If we somehow made it here, we should + // trigger a back-forward cache eviction. + auto* helper = + back_forward_cache_loader_helper.GetBackForwardCacheLoaderHelper(); + if (helper) { + helper->EvictFromBackForwardCache( + mojom::RendererEvictionReason::kJavaScriptExecution); + } + } + + CheckSchemeForReferrerPolicy(*request); + + DCHECK(loader_options & network::mojom::kURLLoadOptionSynchronous); + DCHECK(request->load_flags & net::LOAD_IGNORE_LIMITS); + + std::unique_ptr<network::PendingSharedURLLoaderFactory> pending_factory = + url_loader_factory->Clone(); + base::WaitableEvent redirect_or_response_event( + base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + + // Prepare the configured throttles for use on a separate thread. + for (const auto& throttle : throttles) + throttle->DetachFromCurrentSequence(); + + // A task is posted to a separate thread to execute the request so that + // this thread may block on a waitable event. It is safe to pass raw + // pointers to on-stack objects as this stack frame will + // survive until the request is complete. + scoped_refptr<base::SingleThreadTaskRunner> task_runner = + base::ThreadPool::CreateSingleThreadTaskRunner({}); + SyncLoadContext* context_for_redirect = nullptr; + PostCrossThreadTask( + *task_runner, FROM_HERE, + WTF::CrossThreadBindOnce( + &SyncLoadContext::StartAsyncWithWaitableEvent, std::move(request), + routing_id, task_runner, traffic_annotation, loader_options, + std::move(pending_factory), std::move(throttles), + CrossThreadUnretained(response), + CrossThreadUnretained(&context_for_redirect), + CrossThreadUnretained(&redirect_or_response_event), + CrossThreadUnretained(terminate_sync_load_event), timeout, + std::move(download_to_blob_registry), cors_exempt_header_list, + std::move(resource_load_info_notifier_wrapper))); + + // redirect_or_response_event will signal when each redirect completes, and + // when the final response is complete. + redirect_or_response_event.Wait(); + + while (context_for_redirect) { + DCHECK(response->redirect_info); + bool follow_redirect = peer->OnReceivedRedirect( + *response->redirect_info, response->head.Clone(), + nullptr /* removed_headers */); + redirect_or_response_event.Reset(); + if (follow_redirect) { + task_runner->PostTask( + FROM_HERE, base::BindOnce(&SyncLoadContext::FollowRedirect, + base::Unretained(context_for_redirect))); + } else { + task_runner->PostTask( + FROM_HERE, base::BindOnce(&SyncLoadContext::CancelRedirect, + base::Unretained(context_for_redirect))); + } + redirect_or_response_event.Wait(); + } +} + +int WebResourceRequestSender::SendAsync( + std::unique_ptr<network::ResourceRequest> request, + int routing_id, + scoped_refptr<base::SingleThreadTaskRunner> loading_task_runner, + const net::NetworkTrafficAnnotationTag& traffic_annotation, + uint32_t loader_options, + const WebVector<WebString>& cors_exempt_header_list, + scoped_refptr<WebRequestPeer> peer, + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, + WebVector<std::unique_ptr<URLLoaderThrottle>> throttles, + std::unique_ptr<ResourceLoadInfoNotifierWrapper> + resource_load_info_notifier_wrapper, + WebBackForwardCacheLoaderHelper back_forward_cache_loader_helper) { + auto weak_this = weak_factory_.GetWeakPtr(); + + CheckSchemeForReferrerPolicy(*request); + +#if defined(OS_ANDROID) + // Main frame shouldn't come here. + DCHECK(!(request->is_main_frame && + IsRequestDestinationFrame(request->destination))); + if (request->has_user_gesture) { + resource_load_info_notifier_wrapper->NotifyUpdateUserGestureCarryoverInfo(); + } +#endif + + // Compute a unique request_id for this renderer process. + int request_id = MakeRequestID(); + request_info_ = std::make_unique<PendingRequestInfo>( + std::move(peer), request->destination, request->render_frame_id, + request->url, std::move(resource_load_info_notifier_wrapper)); + + request_info_->resource_load_info_notifier_wrapper + ->NotifyResourceLoadInitiated( + request_id, request->url, request->method, request->referrer, + request_info_->request_destination, request->priority); + + request_info_->previews_state = request->previews_state; + + auto client = std::make_unique<MojoURLLoaderClient>( + this, loading_task_runner, url_loader_factory->BypassRedirectChecks(), + request->url, back_forward_cache_loader_helper); + + std::vector<std::string> std_cors_exempt_header_list( + cors_exempt_header_list.size()); + std::transform(cors_exempt_header_list.begin(), cors_exempt_header_list.end(), + std_cors_exempt_header_list.begin(), + [](const WebString& h) { return h.Latin1(); }); + std::unique_ptr<ThrottlingURLLoader> url_loader = + ThrottlingURLLoader::CreateLoaderAndStart( + std::move(url_loader_factory), throttles.ReleaseVector(), routing_id, + request_id, loader_options, request.get(), client.get(), + traffic_annotation, std::move(loading_task_runner), + base::make_optional(std_cors_exempt_header_list)); + // TODO(https://crbug.com/1175286): Remove this mitigation when we understand + // why `this` or `request_info_` is being destroyed. + if (!weak_this || !request_info_) + return request_id; + + request_info_->url_loader = std::move(url_loader); + request_info_->url_loader_client = std::move(client); + + return request_id; +} + +void WebResourceRequestSender::Cancel( + scoped_refptr<base::SingleThreadTaskRunner> task_runner) { + // Cancel the request if it didn't complete, and clean it up so the bridge + // will receive no more messages. + DeletePendingRequest(std::move(task_runner)); +} + +void WebResourceRequestSender::SetDefersLoading(WebURLLoader::DeferType value) { + if (!request_info_) { + DLOG(ERROR) << "unknown request"; + return; + } + if (value != WebURLLoader::DeferType::kNotDeferred) { + request_info_->is_deferred = value; + request_info_->url_loader_client->SetDefersLoading(value); + } else if (request_info_->is_deferred != + WebURLLoader::DeferType::kNotDeferred) { + request_info_->is_deferred = WebURLLoader::DeferType::kNotDeferred; + request_info_->url_loader_client->SetDefersLoading( + WebURLLoader::DeferType::kNotDeferred); + + FollowPendingRedirect(request_info_.get()); + } +} + +void WebResourceRequestSender::DidChangePriority( + net::RequestPriority new_priority, + int intra_priority_value) { + if (!request_info_) { + DLOG(ERROR) << "unknown request"; + return; + } + + request_info_->url_loader->SetPriority(new_priority, intra_priority_value); +} + +void WebResourceRequestSender::DeletePendingRequest( + scoped_refptr<base::SingleThreadTaskRunner> task_runner) { + if (!request_info_) + return; + + if (request_info_->net_error == net::ERR_IO_PENDING) { + request_info_->net_error = net::ERR_ABORTED; + request_info_->resource_load_info_notifier_wrapper + ->NotifyResourceLoadCanceled(request_info_->net_error); + } + + // Cancel loading. + request_info_->url_loader.reset(); + + // Clear URLLoaderClient to stop receiving further Mojo IPC from the browser + // process. + request_info_->url_loader_client.reset(); + + // Always delete the `request_info_` asyncly so that cancelling the request + // doesn't delete the request context which the `request_info_->peer` points + // to while its response is still being handled. + task_runner->DeleteSoon(FROM_HERE, request_info_.release()); +} + +WebResourceRequestSender::PendingRequestInfo::PendingRequestInfo( + scoped_refptr<WebRequestPeer> peer, + network::mojom::RequestDestination request_destination, + int render_frame_id, + const GURL& request_url, + std::unique_ptr<ResourceLoadInfoNotifierWrapper> + resource_load_info_notifier_wrapper) + : peer(std::move(peer)), + request_destination(request_destination), + render_frame_id(render_frame_id), + url(request_url), + response_url(request_url), + local_request_start(base::TimeTicks::Now()), + resource_load_info_notifier_wrapper( + std::move(resource_load_info_notifier_wrapper)) {} + +WebResourceRequestSender::PendingRequestInfo::~PendingRequestInfo() = default; + +void WebResourceRequestSender::FollowPendingRedirect( + PendingRequestInfo* request_info) { + if (request_info->has_pending_redirect && + request_info->should_follow_redirect) { + request_info->has_pending_redirect = false; + // net::URLRequest clears its request_start on redirect, so should we. + request_info->local_request_start = base::TimeTicks::Now(); + // Redirect URL may not be handled by the network service, so force a + // restart in case another URLLoaderFactory should handle the URL. + if (request_info->redirect_requires_loader_restart) { + request_info->url_loader->FollowRedirectForcingRestart(); + } else { + std::vector<std::string> removed_headers( + request_info_->removed_headers.size()); + std::transform(request_info_->removed_headers.begin(), + request_info_->removed_headers.end(), + removed_headers.begin(), + [](const WebString& h) { return h.Ascii(); }); + request_info->url_loader->FollowRedirect( + removed_headers, {} /* modified_headers */, + {} /* modified_cors_exempt_headers */); + } + } +} + +void WebResourceRequestSender::OnTransferSizeUpdated( + int32_t transfer_size_diff) { + DCHECK_GT(transfer_size_diff, 0); + if (!request_info_) + return; + + // TODO(yhirano): Consider using int64_t in + // WebRequestPeer::OnTransferSizeUpdated. + request_info_->peer->OnTransferSizeUpdated(transfer_size_diff); + if (!request_info_) + return; + request_info_->resource_load_info_notifier_wrapper + ->NotifyResourceTransferSizeUpdated(transfer_size_diff); +} + +void WebResourceRequestSender::OnUploadProgress(int64_t position, + int64_t size) { + if (!request_info_) + return; + + request_info_->peer->OnUploadProgress(position, size); +} + +void WebResourceRequestSender::OnReceivedResponse( + network::mojom::URLResponseHeadPtr response_head) { + TRACE_EVENT0("loading", "WebResourceRequestSender::OnReceivedResponse"); + if (!request_info_) + return; + request_info_->local_response_start = base::TimeTicks::Now(); + request_info_->remote_request_start = + response_head->load_timing.request_start; + // Now that response_start has been set, we can properly set the TimeTicks in + // the URLResponseHead. + ToLocalURLResponseHead(*request_info_, *response_head); + request_info_->load_timing_info = response_head->load_timing; + if (delegate_) { + scoped_refptr<WebRequestPeer> new_peer = delegate_->OnReceivedResponse( + std::move(request_info_->peer), + WebString::FromUTF8(response_head->mime_type), + KURL(request_info_->url)); + DCHECK(new_peer); + request_info_->peer = std::move(new_peer); + } + + request_info_->peer->OnReceivedResponse(response_head.Clone()); + if (!request_info_) + return; + + request_info_->resource_load_info_notifier_wrapper + ->NotifyResourceResponseReceived(std::move(response_head), + request_info_->previews_state); +} + +void WebResourceRequestSender::OnReceivedCachedMetadata( + mojo_base::BigBuffer data) { + if (!request_info_) + return; + + if (data.size()) { + request_info_->peer->OnReceivedCachedMetadata(std::move(data)); + } +} + +void WebResourceRequestSender::OnReceivedRedirect( + const net::RedirectInfo& redirect_info, + network::mojom::URLResponseHeadPtr response_head, + scoped_refptr<base::SingleThreadTaskRunner> task_runner) { + TRACE_EVENT0("loading", "WebResourceRequestSender::OnReceivedRedirect"); + if (!request_info_) + return; + if (!request_info_->url_loader && request_info_->should_follow_redirect) { + // This is a redirect that synchronously came as the loader is being + // constructed, due to a URLLoaderThrottle that changed the starting + // URL. Handle this in a posted task, as we don't have the loader + // pointer yet. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&WebResourceRequestSender::OnReceivedRedirect, + weak_factory_.GetWeakPtr(), redirect_info, + std::move(response_head), task_runner)); + return; + } + + request_info_->local_response_start = base::TimeTicks::Now(); + request_info_->remote_request_start = + response_head->load_timing.request_start; + request_info_->redirect_requires_loader_restart = + RedirectRequiresLoaderRestart(request_info_->response_url, + redirect_info.new_url); + + ToLocalURLResponseHead(*request_info_, *response_head); + std::vector<std::string> removed_headers; + if (request_info_->peer->OnReceivedRedirect( + redirect_info, response_head.Clone(), &removed_headers)) { + // Double-check if the request is still around. The call above could + // potentially remove it. + if (!request_info_) + return; + // TODO(yoav): If request_info doesn't change above, we could avoid this + // copy. + WebVector<WebString> vector(removed_headers.size()); + std::transform( + removed_headers.begin(), removed_headers.end(), vector.begin(), + [](const std::string& h) { return WebString::FromASCII(h); }); + request_info_->removed_headers = vector; + request_info_->response_url = redirect_info.new_url; + request_info_->has_pending_redirect = true; + request_info_->resource_load_info_notifier_wrapper + ->NotifyResourceRedirectReceived(redirect_info, + std::move(response_head)); + + if (request_info_->is_deferred == WebURLLoader::DeferType::kNotDeferred) + FollowPendingRedirect(request_info_.get()); + } else { + Cancel(std::move(task_runner)); + } +} + +void WebResourceRequestSender::OnStartLoadingResponseBody( + mojo::ScopedDataPipeConsumerHandle body) { + TRACE_EVENT0("loading", + "WebResourceRequestSender::OnStartLoadingResponseBody"); + + if (!request_info_) + return; + request_info_->peer->OnStartLoadingResponseBody(std::move(body)); +} + +void WebResourceRequestSender::OnRequestComplete( + const network::URLLoaderCompletionStatus& status) { + TRACE_EVENT0("loading", "WebResourceRequestSender::OnRequestComplete"); + + if (!request_info_) + return; + request_info_->net_error = status.error_code; + + request_info_->resource_load_info_notifier_wrapper + ->NotifyResourceLoadCompleted(status); + + WebRequestPeer* peer = request_info_->peer.get(); + + if (delegate_) { + delegate_->OnRequestComplete(); + } + + network::URLLoaderCompletionStatus renderer_status(status); + if (status.completion_time.is_null()) { + // No completion timestamp is provided, leave it as is. + } else if (request_info_->remote_request_start.is_null() || + request_info_->load_timing_info.request_start.is_null()) { + // We cannot convert the remote time to a local time, let's use the current + // timestamp. This happens when + // - We get an error before OnReceivedRedirect or OnReceivedResponse is + // called, or + // - Somehow such a timestamp was missing in the LoadTimingInfo. + renderer_status.completion_time = base::TimeTicks::Now(); + } else { + // We have already converted the request start timestamp, let's use that + // conversion information. + // Note: We cannot create a InterProcessTimeTicksConverter with + // (local_request_start, now, remote_request_start, remote_completion_time) + // as that may result in inconsistent timestamps. + renderer_status.completion_time = + std::min(status.completion_time - request_info_->remote_request_start + + request_info_->load_timing_info.request_start, + base::TimeTicks::Now()); + } + // The request ID will be removed from our pending list in the destructor. + // Normally, dispatching this message causes the reference-counted request to + // die immediately. + // TODO(kinuko): Revisit here. This probably needs to call request_info_->peer + // but the past attempt to change it seems to have caused crashes. + // (crbug.com/547047) + peer->OnCompletedRequest(renderer_status); +} + +void WebResourceRequestSender::ToLocalURLResponseHead( + const PendingRequestInfo& request_info, + network::mojom::URLResponseHead& response_head) const { + if (base::TimeTicks::IsConsistentAcrossProcesses() || + request_info.local_request_start.is_null() || + request_info.local_response_start.is_null() || + response_head.request_start.is_null() || + response_head.response_start.is_null() || + response_head.load_timing.request_start.is_null()) { + return; + } + InterProcessTimeTicksConverter converter( + LocalTimeTicks::FromTimeTicks(request_info.local_request_start), + LocalTimeTicks::FromTimeTicks(request_info.local_response_start), + RemoteTimeTicks::FromTimeTicks(response_head.request_start), + RemoteTimeTicks::FromTimeTicks(response_head.response_start)); + + net::LoadTimingInfo* load_timing = &response_head.load_timing; + RemoteToLocalTimeTicks(converter, &load_timing->request_start); + RemoteToLocalTimeTicks(converter, &load_timing->proxy_resolve_start); + RemoteToLocalTimeTicks(converter, &load_timing->proxy_resolve_end); + RemoteToLocalTimeTicks(converter, &load_timing->connect_timing.dns_start); + RemoteToLocalTimeTicks(converter, &load_timing->connect_timing.dns_end); + RemoteToLocalTimeTicks(converter, &load_timing->connect_timing.connect_start); + RemoteToLocalTimeTicks(converter, &load_timing->connect_timing.connect_end); + RemoteToLocalTimeTicks(converter, &load_timing->connect_timing.ssl_start); + RemoteToLocalTimeTicks(converter, &load_timing->connect_timing.ssl_end); + RemoteToLocalTimeTicks(converter, &load_timing->send_start); + RemoteToLocalTimeTicks(converter, &load_timing->send_end); + RemoteToLocalTimeTicks(converter, &load_timing->receive_headers_start); + RemoteToLocalTimeTicks(converter, &load_timing->receive_headers_end); + RemoteToLocalTimeTicks(converter, &load_timing->push_start); + RemoteToLocalTimeTicks(converter, &load_timing->push_end); + RemoteToLocalTimeTicks(converter, &load_timing->service_worker_start_time); + RemoteToLocalTimeTicks(converter, &load_timing->service_worker_ready_time); + RemoteToLocalTimeTicks(converter, &load_timing->service_worker_fetch_start); + RemoteToLocalTimeTicks(converter, + &load_timing->service_worker_respond_with_settled); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/web_resource_request_sender_unittest.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/web_resource_request_sender_unittest.cc new file mode 100644 index 00000000000..48e373a8be6 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/web_resource_request_sender_unittest.cc @@ -0,0 +1,561 @@ +// Copyright (c) 2012 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/public/platform/web_resource_request_sender.h" + +#include <stddef.h> +#include <stdint.h> + +#include <memory> +#include <string> +#include <tuple> +#include <utility> +#include <vector> + +#include "base/feature_list.h" +#include "base/macros.h" +#include "base/run_loop.h" +#include "base/test/task_environment.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "net/base/net_errors.h" +#include "net/base/request_priority.h" +#include "net/http/http_response_headers.h" +#include "net/traffic_annotation/network_traffic_annotation_test_helper.h" +#include "services/network/public/cpp/resource_request.h" +#include "services/network/public/cpp/url_loader_completion_status.h" +#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" +#include "services/network/public/mojom/url_response_head.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/common/features.h" +#include "third_party/blink/public/common/loader/referrer_utils.h" +#include "third_party/blink/public/platform/resource_load_info_notifier_wrapper.h" +#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h" +#include "third_party/blink/public/platform/web_back_forward_cache_loader_helper.h" +#include "third_party/blink/public/platform/web_request_peer.h" +#include "third_party/blink/public/platform/web_resource_request_sender_delegate.h" +#include "third_party/blink/public/platform/web_url_request_extra_data.h" +#include "third_party/blink/renderer/platform/testing/testing_platform_support.h" +#include "url/gurl.h" + +namespace blink { + +namespace { + +static constexpr char kTestPageUrl[] = "http://www.google.com/"; +static constexpr char kTestPageHeaders[] = + "HTTP/1.1 200 OK\nContent-Type:text/html\n\n"; +static constexpr char kTestPageMimeType[] = "text/html"; +static constexpr char kTestPageCharset[] = ""; +static constexpr char kTestPageContents[] = + "<html><head><title>Google</title></head><body><h1>Google</h1></body></" + "html>"; + +constexpr size_t kDataPipeCapacity = 4096; + +std::string ReadOneChunk(mojo::ScopedDataPipeConsumerHandle* handle) { + char buffer[kDataPipeCapacity]; + uint32_t read_bytes = kDataPipeCapacity; + MojoResult result = + (*handle)->ReadData(buffer, &read_bytes, MOJO_READ_DATA_FLAG_NONE); + if (result != MOJO_RESULT_OK) + return ""; + return std::string(buffer, read_bytes); +} + +// Returns a fake TimeTicks based on the given microsecond offset. +base::TimeTicks TicksFromMicroseconds(int64_t micros) { + return base::TimeTicks() + base::TimeDelta::FromMicroseconds(micros); +} + +} // namespace + +class TestResourceRequestSenderDelegate + : public WebResourceRequestSenderDelegate { + public: + TestResourceRequestSenderDelegate() = default; + ~TestResourceRequestSenderDelegate() override = default; + + void OnRequestComplete() override {} + + scoped_refptr<WebRequestPeer> OnReceivedResponse( + scoped_refptr<WebRequestPeer> current_peer, + const WebString& mime_type, + const WebURL& url) override { + return base::MakeRefCounted<WrapperPeer>(std::move(current_peer)); + } + + class WrapperPeer : public WebRequestPeer { + public: + explicit WrapperPeer(scoped_refptr<WebRequestPeer> original_peer) + : original_peer_(std::move(original_peer)) {} + + // WebRequestPeer overrides: + void OnUploadProgress(uint64_t position, uint64_t size) override {} + bool OnReceivedRedirect(const net::RedirectInfo& redirect_info, + network::mojom::URLResponseHeadPtr head, + std::vector<std::string>*) override { + return false; + } + void OnReceivedResponse(network::mojom::URLResponseHeadPtr head) override { + response_head_ = std::move(head); + } + void OnStartLoadingResponseBody( + mojo::ScopedDataPipeConsumerHandle body) override { + body_handle_ = std::move(body); + } + void OnTransferSizeUpdated(int transfer_size_diff) override {} + void OnCompletedRequest( + const network::URLLoaderCompletionStatus& status) override { + original_peer_->OnReceivedResponse(std::move(response_head_)); + original_peer_->OnStartLoadingResponseBody(std::move(body_handle_)); + original_peer_->OnCompletedRequest(status); + } + + private: + scoped_refptr<WebRequestPeer> original_peer_; + network::mojom::URLResponseHeadPtr response_head_; + mojo::ScopedDataPipeConsumerHandle body_handle_; + + DISALLOW_COPY_AND_ASSIGN(WrapperPeer); + }; + + private: + DISALLOW_COPY_AND_ASSIGN(TestResourceRequestSenderDelegate); +}; + +// A mock WebRequestPeer to receive messages from the WebResourceRequestSender. +class MockRequestPeer : public WebRequestPeer { + public: + explicit MockRequestPeer(WebResourceRequestSender* resource_request_sender) + : resource_request_sender_(resource_request_sender) {} + + // WebRequestPeer overrides: + void OnUploadProgress(uint64_t position, uint64_t size) override {} + bool OnReceivedRedirect(const net::RedirectInfo& redirect_info, + network::mojom::URLResponseHeadPtr head, + std::vector<std::string>* removed_headers) override { + last_load_timing_ = head->load_timing; + return true; + } + void OnReceivedResponse(network::mojom::URLResponseHeadPtr head) override { + last_load_timing_ = head->load_timing; + received_response_ = true; + if (cancel_on_receive_response_) { + resource_request_sender_->Cancel( + scheduler::GetSingleThreadTaskRunnerForTesting()); + } + } + void OnStartLoadingResponseBody( + mojo::ScopedDataPipeConsumerHandle body) override { + if (cancel_on_receive_response_) + return; + if (body) { + data_ += ReadOneChunk(&body); + } + } + void OnTransferSizeUpdated(int transfer_size_diff) override {} + void OnReceivedCachedMetadata(mojo_base::BigBuffer data) override {} + void OnCompletedRequest( + const network::URLLoaderCompletionStatus& status) override { + if (cancel_on_receive_response_) + return; + completion_status_ = status; + complete_ = true; + } + + std::string data() { return data_; } + bool received_response() { return received_response_; } + bool complete() { return complete_; } + net::LoadTimingInfo last_load_timing() { return last_load_timing_; } + network::URLLoaderCompletionStatus completion_status() { + return completion_status_; + } + + void SetCancelOnReceiveResponse(bool cancel_on_receive_response) { + cancel_on_receive_response_ = cancel_on_receive_response; + } + + private: + // Data received. If downloading to file, remains empty. + std::string data_; + + bool received_response_ = false; + bool complete_ = false; + bool cancel_on_receive_response_ = false; + net::LoadTimingInfo last_load_timing_; + network::URLLoaderCompletionStatus completion_status_; + WebResourceRequestSender* resource_request_sender_ = nullptr; +}; // namespace blink + +// Sets up the message sender override for the unit test. +class WebResourceRequestSenderTest : public testing::Test, + public network::mojom::URLLoaderFactory { + public: + explicit WebResourceRequestSenderTest() + : platform_(&delegate_), + resource_request_sender_(new WebResourceRequestSender()) {} + + ~WebResourceRequestSenderTest() override { + resource_request_sender_.reset(); + base::RunLoop().RunUntilIdle(); + } + + void CreateLoaderAndStart( + mojo::PendingReceiver<network::mojom::URLLoader> receiver, + int32_t routing_id, + int32_t request_id, + uint32_t options, + const network::ResourceRequest& url_request, + mojo::PendingRemote<network::mojom::URLLoaderClient> client, + const net::MutableNetworkTrafficAnnotationTag& annotation) override { + loader_and_clients_.emplace_back(std::move(receiver), std::move(client)); + } + + void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) + override { + NOTREACHED(); + } + + void CallOnReceiveResponse(network::mojom::URLLoaderClient* client) { + auto head = network::mojom::URLResponseHead::New(); + std::string raw_headers(kTestPageHeaders); + std::replace(raw_headers.begin(), raw_headers.end(), '\n', '\0'); + head->headers = new net::HttpResponseHeaders(raw_headers); + head->mime_type = kTestPageMimeType; + head->charset = kTestPageCharset; + client->OnReceiveResponse(std::move(head)); + } + + std::unique_ptr<network::ResourceRequest> CreateResourceRequest() { + std::unique_ptr<network::ResourceRequest> request( + new network::ResourceRequest()); + + request->method = "GET"; + request->url = GURL(kTestPageUrl); + request->site_for_cookies = + net::SiteForCookies::FromUrl(GURL(kTestPageUrl)); + request->referrer_policy = ReferrerUtils::GetDefaultNetReferrerPolicy(); + request->resource_type = + static_cast<int>(mojom::ResourceType::kSubResource); + request->priority = net::LOW; + request->mode = network::mojom::RequestMode::kNoCors; + + auto url_request_extra_data = + base::MakeRefCounted<WebURLRequestExtraData>(); + url_request_extra_data->CopyToResourceRequest(request.get()); + + return request; + } + + WebResourceRequestSender* sender() { return resource_request_sender_.get(); } + + void StartAsync(std::unique_ptr<network::ResourceRequest> request, + scoped_refptr<WebRequestPeer> peer) { + sender()->SendAsync( + std::move(request), 0, scheduler::GetSingleThreadTaskRunnerForTesting(), + TRAFFIC_ANNOTATION_FOR_TESTS, false, WebVector<WebString>(), + std::move(peer), + base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(this), + std::vector<std::unique_ptr<URLLoaderThrottle>>(), + std::make_unique<ResourceLoadInfoNotifierWrapper>( + /*resource_load_info_notifier=*/nullptr), + WebBackForwardCacheLoaderHelper()); + } + + static MojoCreateDataPipeOptions DataPipeOptions() { + MojoCreateDataPipeOptions options; + options.struct_size = sizeof(MojoCreateDataPipeOptions); + options.flags = MOJO_CREATE_DATA_PIPE_FLAG_NONE; + options.element_num_bytes = 1; + options.capacity_num_bytes = kDataPipeCapacity; + return options; + } + + class TestPlatform final : public TestingPlatformSupport { + public: + explicit TestPlatform(WebResourceRequestSenderDelegate* delegate) + : delegate_(delegate) {} + WebResourceRequestSenderDelegate* GetResourceRequestSenderDelegate() + override { + return delegate_; + } + + private: + WebResourceRequestSenderDelegate* delegate_; + }; + + protected: + std::vector<std::pair<mojo::PendingReceiver<network::mojom::URLLoader>, + mojo::PendingRemote<network::mojom::URLLoaderClient>>> + loader_and_clients_; + TestResourceRequestSenderDelegate delegate_; + base::test::SingleThreadTaskEnvironment task_environment_; + ScopedTestingPlatformSupport<TestPlatform, WebResourceRequestSenderDelegate*> + platform_; + std::unique_ptr<WebResourceRequestSender> resource_request_sender_; + + scoped_refptr<MockRequestPeer> mock_peer_; +}; + +// Tests the generation of unique request ids. +TEST_F(WebResourceRequestSenderTest, MakeRequestID) { + int first_id = WebResourceRequestSender::MakeRequestID(); + int second_id = WebResourceRequestSender::MakeRequestID(); + + // Child process ids are unique (per process) and counting from 0 upwards: + EXPECT_GT(second_id, first_id); + EXPECT_GE(first_id, 0); +} + +TEST_F(WebResourceRequestSenderTest, DelegateTest) { + std::unique_ptr<network::ResourceRequest> request(CreateResourceRequest()); + mock_peer_ = + base::MakeRefCounted<MockRequestPeer>(resource_request_sender_.get()); + StartAsync(std::move(request), mock_peer_); + + ASSERT_EQ(1u, loader_and_clients_.size()); + mojo::Remote<network::mojom::URLLoaderClient> client( + std::move(loader_and_clients_[0].second)); + loader_and_clients_.clear(); + + // The wrapper eats all messages until RequestComplete message is sent. + CallOnReceiveResponse(client.get()); + + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + auto options = DataPipeOptions(); + ASSERT_EQ(mojo::CreateDataPipe(&options, producer_handle, consumer_handle), + MOJO_RESULT_OK); + client->OnStartLoadingResponseBody(std::move(consumer_handle)); + + uint32_t size = strlen(kTestPageContents); + auto result = producer_handle->WriteData(kTestPageContents, &size, + MOJO_WRITE_DATA_FLAG_NONE); + ASSERT_EQ(result, MOJO_RESULT_OK); + ASSERT_EQ(size, strlen(kTestPageContents)); + + producer_handle.reset(); + + base::RunLoop().RunUntilIdle(); + + EXPECT_FALSE(mock_peer_->received_response()); + + // This lets the wrapper peer pass all the messages to the original + // peer at once. + network::URLLoaderCompletionStatus status; + status.error_code = net::OK; + status.exists_in_cache = false; + status.encoded_data_length = strlen(kTestPageContents); + client->OnComplete(status); + + base::RunLoop().RunUntilIdle(); + + EXPECT_TRUE(mock_peer_->received_response()); + EXPECT_EQ(kTestPageContents, mock_peer_->data()); + EXPECT_TRUE(mock_peer_->complete()); +} + +TEST_F(WebResourceRequestSenderTest, CancelDuringCallbackWithWrapperPeer) { + std::unique_ptr<network::ResourceRequest> request(CreateResourceRequest()); + mock_peer_ = + base::MakeRefCounted<MockRequestPeer>(resource_request_sender_.get()); + mock_peer_->SetCancelOnReceiveResponse(true); + StartAsync(std::move(request), mock_peer_); + + ASSERT_EQ(1u, loader_and_clients_.size()); + mojo::Remote<network::mojom::URLLoaderClient> client( + std::move(loader_and_clients_[0].second)); + loader_and_clients_.clear(); + + CallOnReceiveResponse(client.get()); + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + auto options = DataPipeOptions(); + ASSERT_EQ(mojo::CreateDataPipe(&options, producer_handle, consumer_handle), + MOJO_RESULT_OK); + client->OnStartLoadingResponseBody(std::move(consumer_handle)); + uint32_t size = strlen(kTestPageContents); + auto result = producer_handle->WriteData(kTestPageContents, &size, + MOJO_WRITE_DATA_FLAG_NONE); + ASSERT_EQ(result, MOJO_RESULT_OK); + ASSERT_EQ(size, strlen(kTestPageContents)); + producer_handle.reset(); + + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(mock_peer_->received_response()); + + // This lets the wrapper peer pass all the messages to the original + // peer at once, but the original peer cancels right after it receives + // the response. (This will remove pending request info from + // WebResourceRequestSender while the wrapper peer is still running + // OnCompletedRequest, but it should not lead to crashes.) + network::URLLoaderCompletionStatus status; + status.error_code = net::OK; + status.exists_in_cache = false; + status.encoded_data_length = strlen(kTestPageContents); + client->OnComplete(status); + + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(mock_peer_->received_response()); + // Request should have been cancelled with no additional messages. + // EXPECT_TRUE(peer_context.cancelled); + EXPECT_EQ("", mock_peer_->data()); + EXPECT_FALSE(mock_peer_->complete()); +} + +class TimeConversionTest : public WebResourceRequestSenderTest { + public: + void PerformTest(network::mojom::URLResponseHeadPtr response_head) { + std::unique_ptr<network::ResourceRequest> request(CreateResourceRequest()); + StartAsync(std::move(request), mock_peer_); + + ASSERT_EQ(1u, loader_and_clients_.size()); + mojo::Remote<network::mojom::URLLoaderClient> client( + std::move(loader_and_clients_[0].second)); + loader_and_clients_.clear(); + client->OnReceiveResponse(std::move(response_head)); + } + + const network::mojom::URLResponseHead& response_info() const { + return *response_info_; + } + + private: + network::mojom::URLResponseHeadPtr response_info_ = + network::mojom::URLResponseHead::New(); +}; + +// TODO(simonjam): Enable this when 10829031 lands. +TEST_F(TimeConversionTest, DISABLED_ProperlyInitialized) { + auto response_head = network::mojom::URLResponseHead::New(); + response_head->request_start = TicksFromMicroseconds(5); + response_head->response_start = TicksFromMicroseconds(15); + response_head->load_timing.request_start_time = base::Time::Now(); + response_head->load_timing.request_start = TicksFromMicroseconds(10); + response_head->load_timing.connect_timing.connect_start = + TicksFromMicroseconds(13); + + auto request_start = response_head->load_timing.request_start; + PerformTest(std::move(response_head)); + + EXPECT_LT(base::TimeTicks(), response_info().load_timing.request_start); + EXPECT_EQ(base::TimeTicks(), + response_info().load_timing.connect_timing.dns_start); + EXPECT_LE(request_start, + response_info().load_timing.connect_timing.connect_start); +} + +TEST_F(TimeConversionTest, PartiallyInitialized) { + auto response_head = network::mojom::URLResponseHead::New(); + response_head->request_start = TicksFromMicroseconds(5); + response_head->response_start = TicksFromMicroseconds(15); + + PerformTest(std::move(response_head)); + + EXPECT_EQ(base::TimeTicks(), response_info().load_timing.request_start); + EXPECT_EQ(base::TimeTicks(), + response_info().load_timing.connect_timing.dns_start); +} + +TEST_F(TimeConversionTest, NotInitialized) { + auto response_head = network::mojom::URLResponseHead::New(); + + PerformTest(std::move(response_head)); + + EXPECT_EQ(base::TimeTicks(), response_info().load_timing.request_start); + EXPECT_EQ(base::TimeTicks(), + response_info().load_timing.connect_timing.dns_start); +} + +class CompletionTimeConversionTest : public WebResourceRequestSenderTest { + public: + void PerformTest(base::TimeTicks remote_request_start, + base::TimeTicks completion_time, + base::TimeDelta delay) { + std::unique_ptr<network::ResourceRequest> request(CreateResourceRequest()); + mock_peer_ = + base::MakeRefCounted<MockRequestPeer>(resource_request_sender_.get()); + StartAsync(std::move(request), mock_peer_); + + ASSERT_EQ(1u, loader_and_clients_.size()); + mojo::Remote<network::mojom::URLLoaderClient> client( + std::move(loader_and_clients_[0].second)); + auto response_head = network::mojom::URLResponseHead::New(); + response_head->request_start = remote_request_start; + response_head->load_timing.request_start = remote_request_start; + response_head->load_timing.receive_headers_end = remote_request_start; + // We need to put something non-null time, otherwise no values will be + // copied. + response_head->load_timing.request_start_time = + base::Time() + base::TimeDelta::FromSeconds(99); + client->OnReceiveResponse(std::move(response_head)); + + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle), + MOJO_RESULT_OK); + client->OnStartLoadingResponseBody(std::move(consumer_handle)); + producer_handle.reset(); // The response is empty. + + network::URLLoaderCompletionStatus status; + status.completion_time = completion_time; + + client->OnComplete(status); + + const base::TimeTicks until = base::TimeTicks::Now() + delay; + while (base::TimeTicks::Now() < until) + base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1)); + base::RunLoop().RunUntilIdle(); + loader_and_clients_.clear(); + } + + base::TimeTicks request_start() const { + EXPECT_TRUE(mock_peer_->received_response()); + return mock_peer_->last_load_timing().request_start; + } + base::TimeTicks completion_time() const { + EXPECT_TRUE(mock_peer_->complete()); + return mock_peer_->completion_status().completion_time; + } +}; + +TEST_F(CompletionTimeConversionTest, NullCompletionTimestamp) { + const auto remote_request_start = + base::TimeTicks() + base::TimeDelta::FromMilliseconds(4); + + PerformTest(remote_request_start, base::TimeTicks(), base::TimeDelta()); + + EXPECT_EQ(base::TimeTicks(), completion_time()); +} + +TEST_F(CompletionTimeConversionTest, RemoteRequestStartIsUnavailable) { + base::TimeTicks begin = base::TimeTicks::Now(); + + const auto remote_completion_time = + base::TimeTicks() + base::TimeDelta::FromMilliseconds(8); + + PerformTest(base::TimeTicks(), remote_completion_time, base::TimeDelta()); + + base::TimeTicks end = base::TimeTicks::Now(); + EXPECT_LE(begin, completion_time()); + EXPECT_LE(completion_time(), end); +} + +TEST_F(CompletionTimeConversionTest, Convert) { + const auto remote_request_start = + base::TimeTicks() + base::TimeDelta::FromMilliseconds(4); + + const auto remote_completion_time = + remote_request_start + base::TimeDelta::FromMilliseconds(3); + + PerformTest(remote_request_start, remote_completion_time, + base::TimeDelta::FromMilliseconds(15)); + + EXPECT_EQ(completion_time(), + request_start() + base::TimeDelta::FromMilliseconds(3)); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/web_url_loader.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/web_url_loader.cc new file mode 100644 index 00000000000..bfd4e078fef --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/web_url_loader.cc @@ -0,0 +1,1172 @@ +// 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/public/platform/web_url_loader.h" + +#include <stdint.h> + +#include <algorithm> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "base/auto_reset.h" +#include "base/bind.h" +#include "base/callback.h" +#include "base/check_op.h" +#include "base/command_line.h" +#include "base/feature_list.h" +#include "base/files/file_path.h" +#include "base/metrics/histogram_macros.h" +#include "base/notreached.h" +#include "base/optional.h" +#include "base/sequence_checker.h" +#include "base/single_thread_task_runner.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "base/threading/thread_task_runner_handle.h" +#include "base/time/time.h" +#include "build/build_config.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "net/base/filename_util.h" +#include "net/base/host_port_pair.h" +#include "net/base/ip_endpoint.h" +#include "net/base/load_flags.h" +#include "net/base/net_errors.h" +#include "net/base/registry_controlled_domains/registry_controlled_domain.h" +#include "net/cert/cert_status_flags.h" +#include "net/cert/ct_sct_to_string.h" +#include "net/cert/x509_certificate.h" +#include "net/cert/x509_util.h" +#include "net/http/http_request_headers.h" +#include "net/http/http_response_headers.h" +#include "net/ssl/ssl_cipher_suite_names.h" +#include "net/ssl/ssl_connection_status_flags.h" +#include "net/ssl/ssl_info.h" +#include "services/network/public/cpp/http_raw_request_response_info.h" +#include "services/network/public/cpp/is_potentially_trustworthy.h" +#include "services/network/public/cpp/resource_request.h" +#include "services/network/public/mojom/fetch_api.mojom-shared.h" +#include "services/network/public/mojom/ip_address_space.mojom-shared.h" +#include "services/network/public/mojom/trust_tokens.mojom-shared.h" +#include "services/network/public/mojom/url_loader.mojom.h" +#include "services/network/public/mojom/url_response_head.mojom.h" +#include "third_party/blink/public/common/features.h" +#include "third_party/blink/public/common/loader/mime_sniffing_throttle.h" +#include "third_party/blink/public/common/loader/previews_state.h" +#include "third_party/blink/public/common/loader/referrer_utils.h" +#include "third_party/blink/public/common/loader/resource_type_util.h" +#include "third_party/blink/public/common/mime_util/mime_util.h" +#include "third_party/blink/public/common/net/ip_address_space_util.h" +#include "third_party/blink/public/common/security/security_style.h" +#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h" +#include "third_party/blink/public/mojom/blob/blob_registry.mojom.h" +#include "third_party/blink/public/mojom/frame/frame.mojom.h" +#include "third_party/blink/public/platform/file_path_conversion.h" +#include "third_party/blink/public/platform/platform.h" +#include "third_party/blink/public/platform/resource_load_info_notifier_wrapper.h" +#include "third_party/blink/public/platform/resource_request_blocked_reason.h" +#include "third_party/blink/public/platform/url_conversion.h" +#include "third_party/blink/public/platform/web_back_forward_cache_loader_helper.h" +#include "third_party/blink/public/platform/web_http_load_info.h" +#include "third_party/blink/public/platform/web_request_peer.h" +#include "third_party/blink/public/platform/web_resource_request_sender.h" +#include "third_party/blink/public/platform/web_security_origin.h" +#include "third_party/blink/public/platform/web_url.h" +#include "third_party/blink/public/platform/web_url_error.h" +#include "third_party/blink/public/platform/web_url_loader_client.h" +#include "third_party/blink/public/platform/web_url_request.h" +#include "third_party/blink/public/platform/web_url_request_extra_data.h" +#include "third_party/blink/public/platform/web_url_response.h" +#include "third_party/blink/public/web/web_local_frame.h" +#include "third_party/blink/public/web/web_security_policy.h" +#include "third_party/blink/renderer/platform/loader/fetch/url_loader/sync_load_response.h" +#include "third_party/boringssl/src/include/openssl/ssl.h" +#include "url/origin.h" + +using base::Time; +using base::TimeTicks; +using blink::scheduler::WebResourceLoadingTaskRunnerHandle; + +namespace blink { + +// Utilities ------------------------------------------------------------------- + +namespace { + +// Converts timing data from |load_timing| to the mojo type. +network::mojom::LoadTimingInfo ToMojoLoadTiming( + const net::LoadTimingInfo& load_timing) { + DCHECK(!load_timing.request_start.is_null()); + + return network::mojom::LoadTimingInfo( + load_timing.socket_reused, load_timing.socket_log_id, + load_timing.request_start_time, load_timing.request_start, + load_timing.proxy_resolve_start, load_timing.proxy_resolve_end, + load_timing.connect_timing, load_timing.send_start, load_timing.send_end, + load_timing.receive_headers_start, load_timing.receive_headers_end, + load_timing.receive_non_informational_headers_start, + load_timing.first_early_hints_time, load_timing.push_start, + load_timing.push_end, load_timing.service_worker_start_time, + load_timing.service_worker_ready_time, + load_timing.service_worker_fetch_start, + load_timing.service_worker_respond_with_settled); +} + +// This is complementary to ConvertNetPriorityToWebKitPriority, defined in +// service_worker_context_client.cc. +// TODO(yhirano): Move this to blink/platform/loader. +net::RequestPriority ConvertWebKitPriorityToNetPriority( + const WebURLRequest::Priority& priority) { + switch (priority) { + case WebURLRequest::Priority::kVeryHigh: + return net::HIGHEST; + + case WebURLRequest::Priority::kHigh: + return net::MEDIUM; + + case WebURLRequest::Priority::kMedium: + return net::LOW; + + case WebURLRequest::Priority::kLow: + return net::LOWEST; + + case WebURLRequest::Priority::kVeryLow: + return net::IDLE; + + case WebURLRequest::Priority::kUnresolved: + default: + NOTREACHED(); + return net::LOW; + } +} + +// Convert a net::SignedCertificateTimestampAndStatus object to a +// WebURLResponse::SignedCertificateTimestamp object. +WebURLResponse::SignedCertificateTimestamp NetSCTToBlinkSCT( + const net::SignedCertificateTimestampAndStatus& sct_and_status) { + return WebURLResponse::SignedCertificateTimestamp( + WebString::FromASCII(net::ct::StatusToString(sct_and_status.status)), + WebString::FromASCII(net::ct::OriginToString(sct_and_status.sct->origin)), + WebString::FromUTF8(sct_and_status.sct->log_description), + WebString::FromASCII( + base::HexEncode(sct_and_status.sct->log_id.c_str(), + sct_and_status.sct->log_id.length())), + sct_and_status.sct->timestamp.ToJavaTime(), + WebString::FromASCII(net::ct::HashAlgorithmToString( + sct_and_status.sct->signature.hash_algorithm)), + WebString::FromASCII(net::ct::SignatureAlgorithmToString( + sct_and_status.sct->signature.signature_algorithm)), + WebString::FromASCII(base::HexEncode( + sct_and_status.sct->signature.signature_data.c_str(), + sct_and_status.sct->signature.signature_data.length()))); +} + +WebString CryptoBufferAsWebString(const CRYPTO_BUFFER* buffer) { + base::StringPiece sp = net::x509_util::CryptoBufferAsStringPiece(buffer); + return WebString::FromLatin1(reinterpret_cast<const WebLChar*>(sp.begin()), + sp.size()); +} + +void SetSecurityStyleAndDetails(const GURL& url, + const network::mojom::URLResponseHead& head, + WebURLResponse* response, + bool report_security_info) { + if (!report_security_info) { + response->SetSecurityStyle(SecurityStyle::kUnknown); + return; + } + if (!url.SchemeIsCryptographic()) { + // Some origins are considered secure even though they're not cryptographic, + // so treat them as secure in the UI. + if (network::IsUrlPotentiallyTrustworthy(url)) + response->SetSecurityStyle(SecurityStyle::kSecure); + else + response->SetSecurityStyle(SecurityStyle::kInsecure); + return; + } + + // The resource loader does not provide a guarantee that requests always have + // security info (such as a certificate) attached. Use SecurityStyleUnknown + // in this case where there isn't enough information to be useful. + if (!head.ssl_info.has_value()) { + response->SetSecurityStyle(SecurityStyle::kUnknown); + return; + } + + const net::SSLInfo& ssl_info = *head.ssl_info; + + const char* protocol = ""; + const char* key_exchange = ""; + const char* cipher = ""; + const char* mac = ""; + const char* key_exchange_group = ""; + + if (ssl_info.connection_status) { + int ssl_version = + net::SSLConnectionStatusToVersion(ssl_info.connection_status); + net::SSLVersionToString(&protocol, ssl_version); + + bool is_aead; + bool is_tls13; + uint16_t cipher_suite = + net::SSLConnectionStatusToCipherSuite(ssl_info.connection_status); + net::SSLCipherSuiteToStrings(&key_exchange, &cipher, &mac, &is_aead, + &is_tls13, cipher_suite); + if (!key_exchange) { + DCHECK(is_tls13); + key_exchange = ""; + } + + if (!mac) { + DCHECK(is_aead); + mac = ""; + } + + if (ssl_info.key_exchange_group != 0) { + // Historically the field was named 'curve' rather than 'group'. + key_exchange_group = SSL_get_curve_name(ssl_info.key_exchange_group); + if (!key_exchange_group) { + NOTREACHED(); + key_exchange_group = ""; + } + } + } + + if (net::IsCertStatusError(head.cert_status)) { + response->SetSecurityStyle(SecurityStyle::kInsecure); + } else { + response->SetSecurityStyle(SecurityStyle::kSecure); + } + + WebURLResponse::SignedCertificateTimestampList sct_list( + ssl_info.signed_certificate_timestamps.size()); + + for (size_t i = 0; i < sct_list.size(); ++i) + sct_list[i] = NetSCTToBlinkSCT(ssl_info.signed_certificate_timestamps[i]); + + if (!ssl_info.cert) { + NOTREACHED(); + response->SetSecurityStyle(SecurityStyle::kUnknown); + return; + } + + std::vector<std::string> san_dns; + std::vector<std::string> san_ip; + ssl_info.cert->GetSubjectAltName(&san_dns, &san_ip); + WebVector<WebString> web_san(san_dns.size() + san_ip.size()); + std::transform(san_dns.begin(), san_dns.end(), web_san.begin(), + [](const std::string& h) { return WebString::FromLatin1(h); }); + std::transform(san_ip.begin(), san_ip.end(), web_san.begin() + san_dns.size(), + [](const std::string& h) { + net::IPAddress ip(reinterpret_cast<const uint8_t*>(h.data()), + h.size()); + return WebString::FromLatin1(ip.ToString()); + }); + + WebVector<WebString> web_cert; + web_cert.reserve(ssl_info.cert->intermediate_buffers().size() + 1); + web_cert.emplace_back(CryptoBufferAsWebString(ssl_info.cert->cert_buffer())); + for (const auto& cert : ssl_info.cert->intermediate_buffers()) + web_cert.emplace_back(CryptoBufferAsWebString(cert.get())); + + WebURLResponse::WebSecurityDetails webSecurityDetails( + WebString::FromASCII(protocol), WebString::FromASCII(key_exchange), + WebString::FromASCII(key_exchange_group), WebString::FromASCII(cipher), + WebString::FromASCII(mac), + WebString::FromUTF8(ssl_info.cert->subject().common_name), web_san, + WebString::FromUTF8(ssl_info.cert->issuer().common_name), + ssl_info.cert->valid_start().ToDoubleT(), + ssl_info.cert->valid_expiry().ToDoubleT(), web_cert, sct_list); + + response->SetSecurityDetails(webSecurityDetails); +} + +bool IsBannedCrossSiteAuth( + network::ResourceRequest* resource_request, + WebURLRequestExtraData* passed_url_request_extra_data) { + auto& request_url = resource_request->url; + auto& first_party = resource_request->site_for_cookies; + + bool allow_cross_origin_auth_prompt = false; + if (passed_url_request_extra_data) { + WebURLRequestExtraData* url_request_extra_data = + static_cast<WebURLRequestExtraData*>(passed_url_request_extra_data); + allow_cross_origin_auth_prompt = + url_request_extra_data->allow_cross_origin_auth_prompt(); + } + + if (first_party.IsFirstPartyWithSchemefulMode( + request_url, /*compute_schemefully=*/false)) { + // If the first party is secure but the subresource is not, this is + // mixed-content. Do not allow the image. + if (!allow_cross_origin_auth_prompt && + network::IsUrlPotentiallyTrustworthy(first_party.RepresentativeUrl()) && + !network::IsUrlPotentiallyTrustworthy(request_url)) { + return true; + } + return false; + } + + return !allow_cross_origin_auth_prompt; +} + +} // namespace + +// This inner class exists since the WebURLLoader may be deleted while inside a +// call to WebURLLoaderClient. Refcounting is to keep the context from +// being deleted if it may have work to do after calling into the client. +class WebURLLoader::Context : public WebRequestPeer { + public: + Context(WebURLLoader* loader, + const WebVector<WebString>& cors_exempt_header_list, + base::WaitableEvent* terminate_sync_load_event, + std::unique_ptr<WebResourceLoadingTaskRunnerHandle> + freezable_task_runner_handle, + std::unique_ptr<WebResourceLoadingTaskRunnerHandle> + unfreezable_task_runner_handle, + scoped_refptr<network::SharedURLLoaderFactory> factory, + mojo::PendingRemote<mojom::KeepAliveHandle> keep_alive_handle, + WebBackForwardCacheLoaderHelper back_forward_cache_loader_helper); + + int request_id() const { return request_id_; } + WebURLLoaderClient* client() const { return client_; } + void set_client(WebURLLoaderClient* client) { client_ = client; } + scoped_refptr<base::SingleThreadTaskRunner> freezable_task_runner() { + return freezable_task_runner_; + } + scoped_refptr<base::SingleThreadTaskRunner> unfreezable_task_runner() { + return unfreezable_task_runner_; + } + + void Cancel(); + void SetDefersLoading(WebURLLoader::DeferType value); + void DidChangePriority(WebURLRequest::Priority new_priority, + int intra_priority_value); + void Start(std::unique_ptr<network::ResourceRequest> request, + scoped_refptr<WebURLRequestExtraData> url_request_extra_data, + int requestor_id, + bool pass_response_pipe_to_client, + bool no_mime_sniffing, + base::TimeDelta timeout_interval, + SyncLoadResponse* sync_load_response, + std::unique_ptr<ResourceLoadInfoNotifierWrapper> + resource_load_info_notifier_wrapper); + + // WebRequestPeer overrides: + void OnUploadProgress(uint64_t position, uint64_t size) override; + bool OnReceivedRedirect(const net::RedirectInfo& redirect_info, + network::mojom::URLResponseHeadPtr head, + std::vector<std::string>* removed_headers) override; + void OnReceivedResponse(network::mojom::URLResponseHeadPtr head) override; + void OnStartLoadingResponseBody( + mojo::ScopedDataPipeConsumerHandle body) override; + void OnTransferSizeUpdated(int transfer_size_diff) override; + void OnReceivedCachedMetadata(mojo_base::BigBuffer data) override; + void OnCompletedRequest( + const network::URLLoaderCompletionStatus& status) override; + + void SetResourceRequestSenderForTesting( // IN-TEST + std::unique_ptr<WebResourceRequestSender> resource_request_sender); + + private: + // The maximal number of bytes consumed in a task. When there are more bytes + // in the data pipe, they will be consumed in following tasks. Setting a too + // small number will generate ton of tasks but setting a too large number will + // lead to thread janks. Also, some clients cannot handle too large chunks + // (512k for example). + static constexpr uint32_t kMaxNumConsumedBytesInTask = 64 * 1024; + + ~Context() override; + + // Called when the body data stream is detached from the reader side. + void CancelBodyStreaming(); + + void OnBodyAvailable(MojoResult, const mojo::HandleSignalsState&); + void OnBodyHasBeenRead(uint32_t read_bytes); + + static net::NetworkTrafficAnnotationTag GetTrafficAnnotationTag( + network::ResourceRequest* request); + + WebURLLoader* loader_; + + KURL url_; + // Controls SetSecurityStyleAndDetails() in PopulateURLResponse(). Initially + // set to WebURLRequest::ReportRawHeaders() in Start() and gets updated in + // WillFollowRedirect() (by the InspectorNetworkAgent) while the new + // ReportRawHeaders() value won't be propagated to the browser process. + // + // TODO(tyoshino): Investigate whether it's worth propagating the new value. + bool report_raw_headers_; + + WebURLLoaderClient* client_; + std::unique_ptr<WebResourceLoadingTaskRunnerHandle> + freezable_task_runner_handle_; + std::unique_ptr<WebResourceLoadingTaskRunnerHandle> + unfreezable_task_runner_handle_; + scoped_refptr<base::SingleThreadTaskRunner> freezable_task_runner_; + scoped_refptr<base::SingleThreadTaskRunner> unfreezable_task_runner_; + mojo::PendingRemote<mojom::KeepAliveHandle> keep_alive_handle_; + WebURLLoader::DeferType defers_loading_; + const WebVector<WebString> cors_exempt_header_list_; + base::WaitableEvent* terminate_sync_load_event_; + + int request_id_; + bool in_two_phase_read_ = false; + bool is_in_on_body_available_ = false; + + base::Optional<network::URLLoaderCompletionStatus> completion_status_; + + std::unique_ptr<WebResourceRequestSender> resource_request_sender_; + + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_; + + WebBackForwardCacheLoaderHelper back_forward_cache_loader_helper_; +}; + +// WebURLLoader::Context ------------------------------------------------------- + +// static +constexpr uint32_t WebURLLoader::Context::kMaxNumConsumedBytesInTask; + +WebURLLoader::Context::Context( + WebURLLoader* loader, + const WebVector<WebString>& cors_exempt_header_list, + base::WaitableEvent* terminate_sync_load_event, + std::unique_ptr<WebResourceLoadingTaskRunnerHandle> + freezable_task_runner_handle, + std::unique_ptr<WebResourceLoadingTaskRunnerHandle> + unfreezable_task_runner_handle, + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, + mojo::PendingRemote<mojom::KeepAliveHandle> keep_alive_handle, + WebBackForwardCacheLoaderHelper back_forward_cache_loader_helper) + : loader_(loader), + report_raw_headers_(false), + client_(nullptr), + freezable_task_runner_handle_(std::move(freezable_task_runner_handle)), + unfreezable_task_runner_handle_( + std::move(unfreezable_task_runner_handle)), + freezable_task_runner_(freezable_task_runner_handle_->GetTaskRunner()), + unfreezable_task_runner_( + unfreezable_task_runner_handle_->GetTaskRunner()), + keep_alive_handle_(std::move(keep_alive_handle)), + defers_loading_(WebURLLoader::DeferType::kNotDeferred), + cors_exempt_header_list_(cors_exempt_header_list), + terminate_sync_load_event_(terminate_sync_load_event), + request_id_(-1), + resource_request_sender_(std::make_unique<WebResourceRequestSender>()), + url_loader_factory_(std::move(url_loader_factory)), + back_forward_cache_loader_helper_(back_forward_cache_loader_helper) { + DCHECK(url_loader_factory_); +} + +void WebURLLoader::Context::Cancel() { + TRACE_EVENT_WITH_FLOW0("loading", "WebURLLoader::Context::Cancel", this, + TRACE_EVENT_FLAG_FLOW_IN); + if (request_id_ != -1) { + resource_request_sender_->Cancel(freezable_task_runner_); + request_id_ = -1; + } + + // Do not make any further calls to the client. + client_ = nullptr; + loader_ = nullptr; +} + +void WebURLLoader::Context::SetDefersLoading(WebURLLoader::DeferType value) { + if (request_id_ != -1) + resource_request_sender_->SetDefersLoading(value); + defers_loading_ = value; +} + +void WebURLLoader::Context::DidChangePriority( + WebURLRequest::Priority new_priority, + int intra_priority_value) { + if (request_id_ != -1) { + net::RequestPriority net_priority = + ConvertWebKitPriorityToNetPriority(new_priority); + resource_request_sender_->DidChangePriority(net_priority, + intra_priority_value); + freezable_task_runner_handle_->DidChangeRequestPriority(net_priority); + } +} + +void WebURLLoader::Context::Start( + std::unique_ptr<network::ResourceRequest> request, + scoped_refptr<WebURLRequestExtraData> passed_url_request_extra_data, + int requestor_id, + bool pass_response_pipe_to_client, + bool no_mime_sniffing, + base::TimeDelta timeout_interval, + SyncLoadResponse* sync_load_response, + std::unique_ptr<ResourceLoadInfoNotifierWrapper> + resource_load_info_notifier_wrapper) { + DCHECK_EQ(request_id_, -1); + + // Notify Blink's scheduler with the initial resource fetch priority. + freezable_task_runner_handle_->DidChangeRequestPriority(request->priority); + + url_ = KURL(request->url); + report_raw_headers_ = request->report_raw_headers; + + // TODO(horo): Check credentials flag is unset when credentials mode is omit. + // Check credentials flag is set when credentials mode is include. + + const network::mojom::RequestDestination request_destination = + request->destination; + + // TODO(yhirano): Move the logic below to blink/platform/loader. + if (!request->is_favicon && + request_destination == network::mojom::RequestDestination::kImage && + IsBannedCrossSiteAuth(request.get(), + passed_url_request_extra_data.get())) { + // Prevent third-party image content from prompting for login, as this + // is often a scam to extract credentials for another domain from the + // user. Only block image loads, as the attack applies largely to the + // "src" property of the <img> tag. It is common for web properties to + // allow untrusted values for <img src>; this is considered a fair thing + // for an HTML sanitizer to do. Conversely, any HTML sanitizer that didn't + // filter sources for <script>, <link>, <embed>, <object>, <iframe> tags + // would be considered vulnerable in and of itself. + request->do_not_prompt_for_login = true; + request->load_flags |= net::LOAD_DO_NOT_USE_EMBEDDED_IDENTITY; + } + + scoped_refptr<WebURLRequestExtraData> empty_url_request_extra_data; + WebURLRequestExtraData* url_request_extra_data; + if (passed_url_request_extra_data) { + url_request_extra_data = static_cast<WebURLRequestExtraData*>( + passed_url_request_extra_data.get()); + } else { + empty_url_request_extra_data = + base::MakeRefCounted<WebURLRequestExtraData>(); + url_request_extra_data = empty_url_request_extra_data.get(); + } + url_request_extra_data->CopyToResourceRequest(request.get()); + + if (request->load_flags & net::LOAD_PREFETCH) + request->corb_detachable = true; + + auto throttles = + url_request_extra_data->TakeURLLoaderThrottles().ReleaseVector(); + // The frame request blocker is only for a frame's subresources. + if (url_request_extra_data->frame_request_blocker() && + !IsRequestDestinationFrame(request_destination)) { + auto throttle = url_request_extra_data->frame_request_blocker() + ->GetThrottleIfRequestsBlocked(); + if (throttle) + throttles.push_back(std::move(throttle)); + } + + // TODO(minggang): Remove the useage of render frame id. + Platform::Current()->AppendVariationsThrottles(request->render_frame_id, + &throttles); + + uint32_t loader_options = network::mojom::kURLLoadOptionNone; + if (!no_mime_sniffing) { + loader_options |= network::mojom::kURLLoadOptionSniffMimeType; + throttles.push_back( + std::make_unique<MimeSniffingThrottle>(unfreezable_task_runner_)); + } + + if (sync_load_response) { + DCHECK(defers_loading_ == WebURLLoader::DeferType::kNotDeferred); + + loader_options |= network::mojom::kURLLoadOptionSynchronous; + request->load_flags |= net::LOAD_IGNORE_LIMITS; + + mojo::PendingRemote<mojom::BlobRegistry> download_to_blob_registry; + if (pass_response_pipe_to_client) { + Platform::Current()->GetBrowserInterfaceBroker()->GetInterface( + download_to_blob_registry.InitWithNewPipeAndPassReceiver()); + } + net::NetworkTrafficAnnotationTag tag = + GetTrafficAnnotationTag(request.get()); + resource_request_sender_->SendSync( + std::move(request), requestor_id, tag, loader_options, + sync_load_response, url_loader_factory_, std::move(throttles), + timeout_interval, cors_exempt_header_list_, terminate_sync_load_event_, + std::move(download_to_blob_registry), base::WrapRefCounted(this), + std::move(resource_load_info_notifier_wrapper), + back_forward_cache_loader_helper_); + return; + } + + TRACE_EVENT_WITH_FLOW0("loading", "WebURLLoader::Context::Start", this, + TRACE_EVENT_FLAG_FLOW_OUT); + net::NetworkTrafficAnnotationTag tag = GetTrafficAnnotationTag(request.get()); + request_id_ = resource_request_sender_->SendAsync( + std::move(request), requestor_id, unfreezable_task_runner_, tag, + loader_options, cors_exempt_header_list_, base::WrapRefCounted(this), + url_loader_factory_, std::move(throttles), + std::move(resource_load_info_notifier_wrapper), + back_forward_cache_loader_helper_); + + if (defers_loading_ != WebURLLoader::DeferType::kNotDeferred) { + resource_request_sender_->SetDefersLoading( + WebURLLoader::DeferType::kDeferred); + } +} + +void WebURLLoader::Context::OnUploadProgress(uint64_t position, uint64_t size) { + if (client_) + client_->DidSendData(position, size); +} + +bool WebURLLoader::Context::OnReceivedRedirect( + const net::RedirectInfo& redirect_info, + network::mojom::URLResponseHeadPtr head, + std::vector<std::string>* removed_headers) { + if (!client_) + return false; + + TRACE_EVENT_WITH_FLOW0("loading", "WebURLLoader::Context::OnReceivedRedirect", + this, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); + + WebURLResponse response; + PopulateURLResponse(url_, *head, &response, report_raw_headers_, request_id_); + + url_ = KURL(redirect_info.new_url); + return client_->WillFollowRedirect( + url_, redirect_info.new_site_for_cookies, + WebString::FromUTF8(redirect_info.new_referrer), + ReferrerUtils::NetToMojoReferrerPolicy(redirect_info.new_referrer_policy), + WebString::FromUTF8(redirect_info.new_method), response, + report_raw_headers_, removed_headers); +} + +void WebURLLoader::Context::OnReceivedResponse( + network::mojom::URLResponseHeadPtr head) { + if (!client_) + return; + + TRACE_EVENT_WITH_FLOW0("loading", "WebURLLoader::Context::OnReceivedResponse", + this, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); + + // These headers must be stripped off before entering into the renderer + // (see also https://crbug.com/1019732). + DCHECK(!head->headers || !head->headers->HasHeader("set-cookie")); + DCHECK(!head->headers || !head->headers->HasHeader("set-cookie2")); + DCHECK(!head->headers || !head->headers->HasHeader("clear-site-data")); + + WebURLResponse response; + PopulateURLResponse(url_, *head, &response, report_raw_headers_, request_id_); + + client_->DidReceiveResponse(response); + + // DidReceiveResponse() may have triggered a cancel, causing the |client_| to + // go away. + if (!client_) + return; +} + +void WebURLLoader::Context::OnStartLoadingResponseBody( + mojo::ScopedDataPipeConsumerHandle body) { + if (client_) + client_->DidStartLoadingResponseBody(std::move(body)); + + TRACE_EVENT_WITH_FLOW0( + "loading", "WebURLLoader::Context::OnStartLoadingResponseBody", this, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); +} + +void WebURLLoader::Context::OnTransferSizeUpdated(int transfer_size_diff) { + client_->DidReceiveTransferSizeUpdate(transfer_size_diff); +} + +void WebURLLoader::Context::OnReceivedCachedMetadata( + mojo_base::BigBuffer data) { + if (!client_) + return; + TRACE_EVENT_WITH_FLOW1( + "loading", "WebURLLoader::Context::OnReceivedCachedMetadata", this, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "length", + data.size()); + client_->DidReceiveCachedMetadata(std::move(data)); +} + +void WebURLLoader::Context::OnCompletedRequest( + const network::URLLoaderCompletionStatus& status) { + int64_t total_transfer_size = status.encoded_data_length; + int64_t encoded_body_size = status.encoded_body_length; + + if (client_) { + TRACE_EVENT_WITH_FLOW0("loading", + "WebURLLoader::Context::OnCompletedRequest", this, + TRACE_EVENT_FLAG_FLOW_IN); + + if (status.error_code != net::OK) { + client_->DidFail(PopulateURLError(status, url_), status.completion_time, + total_transfer_size, encoded_body_size, + status.decoded_body_length); + } else { + client_->DidFinishLoading(status.completion_time, total_transfer_size, + encoded_body_size, status.decoded_body_length, + status.should_report_corb_blocking); + } + } +} + +WebURLLoader::Context::~Context() { + // We must be already cancelled at this point. + DCHECK_LT(request_id_, 0); +} + +void WebURLLoader::Context::CancelBodyStreaming() { + scoped_refptr<Context> protect(this); + + if (client_) { + // TODO(yhirano): Set |stale_copy_in_cache| appropriately if possible. + client_->DidFail(WebURLError(net::ERR_ABORTED, url_), + base::TimeTicks::Now(), + WebURLLoaderClient::kUnknownEncodedDataLength, 0, 0); + } + + // Notify the browser process that the request is canceled. + Cancel(); +} + +// WebURLLoader ---------------------------------------------------------------- + +WebURLLoader::WebURLLoader( + const WebVector<WebString>& cors_exempt_header_list, + base::WaitableEvent* terminate_sync_load_event, + std::unique_ptr<WebResourceLoadingTaskRunnerHandle> + freezable_task_runner_handle, + std::unique_ptr<WebResourceLoadingTaskRunnerHandle> + unfreezable_task_runner_handle, + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, + mojo::PendingRemote<mojom::KeepAliveHandle> keep_alive_handle, + WebBackForwardCacheLoaderHelper back_forward_cache_loader_helper) + : context_(new Context(this, + cors_exempt_header_list, + terminate_sync_load_event, + std::move(freezable_task_runner_handle), + std::move(unfreezable_task_runner_handle), + std::move(url_loader_factory), + std::move(keep_alive_handle), + back_forward_cache_loader_helper)) {} + +WebURLLoader::WebURLLoader() = default; + +WebURLLoader::~WebURLLoader() { + Cancel(); +} + +void WebURLLoader::PopulateURLResponse( + const WebURL& url, + const network::mojom::URLResponseHead& head, + WebURLResponse* response, + bool report_security_info, + int request_id) { + response->SetCurrentRequestUrl(url); + response->SetResponseTime(head.response_time); + response->SetMimeType(WebString::FromUTF8(head.mime_type)); + response->SetTextEncodingName(WebString::FromUTF8(head.charset)); + response->SetExpectedContentLength(head.content_length); + response->SetHasMajorCertificateErrors( + net::IsCertStatusError(head.cert_status)); + response->SetCTPolicyCompliance(head.ct_policy_compliance); + response->SetIsLegacyTLSVersion(head.is_legacy_tls_version); + response->SetHasRangeRequested(head.has_range_requested); + response->SetTimingAllowPassed(head.timing_allow_passed); + response->SetAppCacheID(head.appcache_id); + response->SetAppCacheManifestURL(KURL(head.appcache_manifest_url)); + response->SetWasCached(!head.load_timing.request_start_time.is_null() && + head.response_time < + head.load_timing.request_start_time); + response->SetConnectionID(head.load_timing.socket_log_id); + response->SetConnectionReused(head.load_timing.socket_reused); + response->SetWasFetchedViaSPDY(head.was_fetched_via_spdy); + response->SetWasFetchedViaServiceWorker(head.was_fetched_via_service_worker); + response->SetServiceWorkerResponseSource(head.service_worker_response_source); + response->SetWasFallbackRequiredByServiceWorker( + head.was_fallback_required_by_service_worker); + response->SetType(head.response_type); + response->SetPadding(head.padding); + WebVector<KURL> url_list_via_service_worker( + head.url_list_via_service_worker.size()); + std::transform(head.url_list_via_service_worker.begin(), + head.url_list_via_service_worker.end(), + url_list_via_service_worker.begin(), + [](const GURL& h) { return KURL(h); }); + response->SetUrlListViaServiceWorker(url_list_via_service_worker); + response->SetCacheStorageCacheName( + head.service_worker_response_source == + network::mojom::FetchResponseSource::kCacheStorage + ? WebString::FromUTF8(head.cache_storage_cache_name) + : WebString()); + + WebVector<WebString> dns_aliases(head.dns_aliases.size()); + std::transform(head.dns_aliases.begin(), head.dns_aliases.end(), + dns_aliases.begin(), + [](const std::string& h) { return WebString::FromASCII(h); }); + response->SetDnsAliases(dns_aliases); + response->SetRemoteIPEndpoint(head.remote_endpoint); + // This computation can only be done once SetUrlListViaServiceWorker() has + // been called on |response|, so that ResponseUrl() returns the correct + // answer. + // + // Implements: https://wicg.github.io/cors-rfc1918/#integration-html + response->SetAddressSpace(CalculateResourceAddressSpace( + KURL(response->ResponseUrl()), head.remote_endpoint.address())); + + WebVector<WebString> cors_exposed_header_names( + head.cors_exposed_header_names.size()); + std::transform(head.cors_exposed_header_names.begin(), + head.cors_exposed_header_names.end(), + cors_exposed_header_names.begin(), + [](const std::string& h) { return WebString::FromLatin1(h); }); + response->SetCorsExposedHeaderNames(cors_exposed_header_names); + response->SetDidServiceWorkerNavigationPreload( + head.did_service_worker_navigation_preload); + response->SetEncodedDataLength(head.encoded_data_length); + response->SetEncodedBodyLength(head.encoded_body_length); + response->SetWasAlpnNegotiated(head.was_alpn_negotiated); + response->SetAlpnNegotiatedProtocol( + WebString::FromUTF8(head.alpn_negotiated_protocol)); + response->SetWasAlternateProtocolAvailable( + head.was_alternate_protocol_available); + response->SetConnectionInfo(head.connection_info); + response->SetAsyncRevalidationRequested(head.async_revalidation_requested); + response->SetNetworkAccessed(head.network_accessed); + response->SetRequestId(request_id); + response->SetIsSignedExchangeInnerResponse( + head.is_signed_exchange_inner_response); + response->SetWasInPrefetchCache(head.was_in_prefetch_cache); + response->SetWasCookieInRequest(head.was_cookie_in_request); + response->SetRecursivePrefetchToken(head.recursive_prefetch_token); + response->SetWebBundleURL(KURL(head.web_bundle_url)); + + SetSecurityStyleAndDetails(KURL(url), head, response, report_security_info); + + // If there's no received headers end time, don't set load timing. This is + // the case for non-HTTP requests, requests that don't go over the wire, and + // certain error cases. + if (!head.load_timing.receive_headers_end.is_null()) { + response->SetLoadTiming(ToMojoLoadTiming(head.load_timing)); + } + + if (head.raw_request_response_info.get()) { + WebHTTPLoadInfo load_info; + + load_info.SetHTTPStatusCode( + head.raw_request_response_info->http_status_code); + load_info.SetHTTPStatusText(WebString::FromLatin1( + head.raw_request_response_info->http_status_text)); + + load_info.SetRequestHeadersText(WebString::FromLatin1( + head.raw_request_response_info->request_headers_text)); + load_info.SetResponseHeadersText(WebString::FromLatin1( + head.raw_request_response_info->response_headers_text)); + for (auto& header : head.raw_request_response_info->request_headers) { + load_info.AddRequestHeader(WebString::FromLatin1(header->key), + WebString::FromLatin1(header->value)); + } + for (auto& header : head.raw_request_response_info->response_headers) { + load_info.AddResponseHeader(WebString::FromLatin1(header->key), + WebString::FromLatin1(header->value)); + } + response->SetHTTPLoadInfo(load_info); + } + + response->SetAuthChallengeInfo(head.auth_challenge_info); + + const net::HttpResponseHeaders* headers = head.headers.get(); + if (!headers) + return; + + WebURLResponse::HTTPVersion version = WebURLResponse::kHTTPVersionUnknown; + if (headers->GetHttpVersion() == net::HttpVersion(0, 9)) + version = WebURLResponse::kHTTPVersion_0_9; + else if (headers->GetHttpVersion() == net::HttpVersion(1, 0)) + version = WebURLResponse::kHTTPVersion_1_0; + else if (headers->GetHttpVersion() == net::HttpVersion(1, 1)) + version = WebURLResponse::kHTTPVersion_1_1; + else if (headers->GetHttpVersion() == net::HttpVersion(2, 0)) + version = WebURLResponse::kHTTPVersion_2_0; + response->SetHttpVersion(version); + response->SetHttpStatusCode(headers->response_code()); + response->SetHttpStatusText(WebString::FromLatin1(headers->GetStatusText())); + + // Build up the header map. + size_t iter = 0; + std::string name; + std::string value; + while (headers->EnumerateHeaderLines(&iter, &name, &value)) { + response->AddHttpHeaderField(WebString::FromLatin1(name), + WebString::FromLatin1(value)); + } +} + +// static +WebURLError WebURLLoader::PopulateURLError( + const network::URLLoaderCompletionStatus& status, + const WebURL& url) { + DCHECK_NE(net::OK, status.error_code); + const WebURLError::HasCopyInCache has_copy_in_cache = + status.exists_in_cache ? WebURLError::HasCopyInCache::kTrue + : WebURLError::HasCopyInCache::kFalse; + if (status.cors_error_status) + return WebURLError(*status.cors_error_status, has_copy_in_cache, url); + if (status.blocked_by_response_reason) { + DCHECK_EQ(net::ERR_BLOCKED_BY_RESPONSE, status.error_code); + return WebURLError(*status.blocked_by_response_reason, + status.resolve_error_info, has_copy_in_cache, url); + } + + if (status.trust_token_operation_status != + network::mojom::TrustTokenOperationStatus::kOk) { + DCHECK(status.error_code == + net::ERR_TRUST_TOKEN_OPERATION_SUCCESS_WITHOUT_SENDING_REQUEST || + status.error_code == net::ERR_TRUST_TOKEN_OPERATION_FAILED) + << "Unexpected error code on Trust Token operation failure (or cache " + "hit): " + << status.error_code; + + return WebURLError(status.error_code, status.trust_token_operation_status, + url); + } + + return WebURLError(status.error_code, status.extended_error_code, + status.resolve_error_info, has_copy_in_cache, + WebURLError::IsWebSecurityViolation::kFalse, url); +} + +void WebURLLoader::LoadSynchronously( + std::unique_ptr<network::ResourceRequest> request, + scoped_refptr<WebURLRequestExtraData> url_request_extra_data, + int requestor_id, + bool pass_response_pipe_to_client, + bool no_mime_sniffing, + base::TimeDelta timeout_interval, + WebURLLoaderClient* client, + WebURLResponse& response, + base::Optional<WebURLError>& error, + WebData& data, + int64_t& encoded_data_length, + int64_t& encoded_body_length, + WebBlobInfo& downloaded_blob, + std::unique_ptr<ResourceLoadInfoNotifierWrapper> + resource_load_info_notifier_wrapper) { + if (!context_) + return; + + TRACE_EVENT0("loading", "WebURLLoader::loadSynchronously"); + SyncLoadResponse sync_load_response; + + DCHECK(!context_->client()); + context_->set_client(client); + + const bool report_raw_headers = request->report_raw_headers; + context_->Start(std::move(request), std::move(url_request_extra_data), + requestor_id, pass_response_pipe_to_client, no_mime_sniffing, + timeout_interval, &sync_load_response, + std::move(resource_load_info_notifier_wrapper)); + + const KURL final_url(sync_load_response.url); + + // TODO(tc): For file loads, we may want to include a more descriptive + // status code or status text. + const int error_code = sync_load_response.error_code; + if (error_code != net::OK) { + if (sync_load_response.cors_error) { + error = WebURLError(*sync_load_response.cors_error, + WebURLError::HasCopyInCache::kFalse, final_url); + } else { + // SyncResourceHandler returns ERR_ABORTED for CORS redirect errors, + // so we treat the error as a web security violation. + const WebURLError::IsWebSecurityViolation is_web_security_violation = + error_code == net::ERR_ABORTED + ? WebURLError::IsWebSecurityViolation::kTrue + : WebURLError::IsWebSecurityViolation::kFalse; + error = WebURLError(error_code, sync_load_response.extended_error_code, + sync_load_response.resolve_error_info, + WebURLError::HasCopyInCache::kFalse, + is_web_security_violation, final_url); + } + return; + } + + PopulateURLResponse(final_url, *sync_load_response.head, &response, + report_raw_headers, context_->request_id()); + encoded_data_length = sync_load_response.head->encoded_data_length; + encoded_body_length = sync_load_response.head->encoded_body_length; + if (sync_load_response.downloaded_blob) { + downloaded_blob = WebBlobInfo( + WebString::FromLatin1(sync_load_response.downloaded_blob->uuid), + WebString::FromLatin1(sync_load_response.downloaded_blob->content_type), + sync_load_response.downloaded_blob->size, + std::move(sync_load_response.downloaded_blob->blob)); + } + + data.Assign(sync_load_response.data); +} + +void WebURLLoader::LoadAsynchronously( + std::unique_ptr<network::ResourceRequest> request, + scoped_refptr<WebURLRequestExtraData> url_request_extra_data, + int requestor_id, + bool no_mime_sniffing, + std::unique_ptr<ResourceLoadInfoNotifierWrapper> + resource_load_info_notifier_wrapper, + WebURLLoaderClient* client) { + if (!context_) + return; + + TRACE_EVENT_WITH_FLOW0("loading", "WebURLLoader::loadAsynchronously", this, + TRACE_EVENT_FLAG_FLOW_OUT); + DCHECK(!context_->client()); + + context_->set_client(client); + context_->Start(std::move(request), std::move(url_request_extra_data), + requestor_id, + /*pass_response_pipe_to_client=*/false, no_mime_sniffing, + base::TimeDelta(), nullptr, + std::move(resource_load_info_notifier_wrapper)); +} + +void WebURLLoader::Cancel() { + if (context_) + context_->Cancel(); +} + +void WebURLLoader::SetDefersLoading(DeferType value) { + if (context_) + context_->SetDefersLoading(value); +} + +void WebURLLoader::DidChangePriority(WebURLRequest::Priority new_priority, + int intra_priority_value) { + if (context_) + context_->DidChangePriority(new_priority, intra_priority_value); +} + +scoped_refptr<base::SingleThreadTaskRunner> +WebURLLoader::GetTaskRunnerForBodyLoader() { + if (!context_) + return nullptr; + return context_->unfreezable_task_runner(); +} + +void WebURLLoader::SetResourceRequestSenderForTesting( + std::unique_ptr<WebResourceRequestSender> resource_request_sender) { + context_->SetResourceRequestSenderForTesting( // IN-TEST + std::move(resource_request_sender)); +} + +// static +// We have this function at the bottom of this file because it confuses +// syntax highliting. +// TODO(kinuko): Deprecate this, we basically need to know the destination +// and if it's for favicon or not. +net::NetworkTrafficAnnotationTag WebURLLoader::Context::GetTrafficAnnotationTag( + network::ResourceRequest* request) { + if (request->is_favicon) { + return net::DefineNetworkTrafficAnnotation("favicon_loader", R"( + semantics { + sender: "Blink Resource Loader" + description: + "Chrome sends a request to download favicon for a URL." + trigger: + "Navigating to a URL." + data: "None." + destination: WEBSITE + } + policy { + cookies_allowed: YES + cookies_store: "user" + setting: "These requests cannot be disabled in settings." + policy_exception_justification: + "Not implemented." + })"); + } + switch (request->destination) { + case network::mojom::RequestDestination::kDocument: + case network::mojom::RequestDestination::kIframe: + case network::mojom::RequestDestination::kFrame: + NOTREACHED(); + FALLTHROUGH; + + case network::mojom::RequestDestination::kEmpty: + case network::mojom::RequestDestination::kAudio: + case network::mojom::RequestDestination::kAudioWorklet: + case network::mojom::RequestDestination::kFont: + case network::mojom::RequestDestination::kImage: + case network::mojom::RequestDestination::kManifest: + case network::mojom::RequestDestination::kPaintWorklet: + case network::mojom::RequestDestination::kReport: + case network::mojom::RequestDestination::kScript: + case network::mojom::RequestDestination::kServiceWorker: + case network::mojom::RequestDestination::kSharedWorker: + case network::mojom::RequestDestination::kStyle: + case network::mojom::RequestDestination::kTrack: + case network::mojom::RequestDestination::kVideo: + case network::mojom::RequestDestination::kWebBundle: + case network::mojom::RequestDestination::kWorker: + case network::mojom::RequestDestination::kXslt: + return net::DefineNetworkTrafficAnnotation("blink_resource_loader", R"( + semantics { + sender: "Blink Resource Loader" + description: + "Blink-initiated request, which includes all resources for " + "normal page loads, chrome URLs, and downloads." + trigger: + "The user navigates to a URL or downloads a file. Also when a " + "webpage, ServiceWorker, or chrome:// uses any network communication." + data: "Anything the initiator wants to send." + destination: OTHER + } + policy { + cookies_allowed: YES + cookies_store: "user" + setting: "These requests cannot be disabled in settings." + policy_exception_justification: + "Not implemented. Without these requests, Chrome will be unable " + "to load any webpage." + })"); + + case network::mojom::RequestDestination::kEmbed: + case network::mojom::RequestDestination::kObject: + return net::DefineNetworkTrafficAnnotation( + "blink_extension_resource_loader", R"( + semantics { + sender: "Blink Resource Loader" + description: + "Blink-initiated request for resources required for NaCl instances " + "tagged with <embed> or <object>, or installed extensions." + trigger: + "An extension or NaCl instance may initiate a request at any time, " + "even in the background." + data: "Anything the initiator wants to send." + destination: OTHER + } + policy { + cookies_allowed: YES + cookies_store: "user" + setting: + "These requests cannot be disabled in settings, but they are " + "sent only if user installs extensions." + chrome_policy { + ExtensionInstallBlocklist { + ExtensionInstallBlocklist: { + entries: '*' + } + } + } + })"); + } + + return net::NetworkTrafficAnnotationTag::NotReached(); +} + +void WebURLLoader::Context::SetResourceRequestSenderForTesting( + std::unique_ptr<blink::WebResourceRequestSender> resource_request_sender) { + resource_request_sender_ = std::move(resource_request_sender); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/web_url_loader_factory.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/web_url_loader_factory.cc new file mode 100644 index 00000000000..204f81362f7 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/web_url_loader_factory.cc @@ -0,0 +1,46 @@ +// Copyright 2021 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/public/platform/web_url_loader_factory.h" + +#include "third_party/blink/public/platform/web_back_forward_cache_loader_helper.h" +#include "third_party/blink/public/platform/web_url_loader.h" + +using blink::scheduler::WebResourceLoadingTaskRunnerHandle; + +namespace blink { + +WebURLLoaderFactory::WebURLLoaderFactory( + scoped_refptr<network::SharedURLLoaderFactory> loader_factory, + const WebVector<WebString>& cors_exempt_header_list, + base::WaitableEvent* terminate_sync_load_event) + : loader_factory_(std::move(loader_factory)), + cors_exempt_header_list_(cors_exempt_header_list), + terminate_sync_load_event_(terminate_sync_load_event) { + DCHECK(loader_factory_); +} + +WebURLLoaderFactory::WebURLLoaderFactory() = default; + +WebURLLoaderFactory::~WebURLLoaderFactory() = default; + +std::unique_ptr<WebURLLoader> WebURLLoaderFactory::CreateURLLoader( + const WebURLRequest& request, + std::unique_ptr<WebResourceLoadingTaskRunnerHandle> + freezable_task_runner_handle, + std::unique_ptr<WebResourceLoadingTaskRunnerHandle> + unfreezable_task_runner_handle, + CrossVariantMojoRemote<mojom::KeepAliveHandleInterfaceBase> + keep_alive_handle, + WebBackForwardCacheLoaderHelper back_forward_cache_loader_helper) { + DCHECK(freezable_task_runner_handle); + DCHECK(unfreezable_task_runner_handle); + return std::make_unique<WebURLLoader>( + cors_exempt_header_list_, terminate_sync_load_event_, + std::move(freezable_task_runner_handle), + std::move(unfreezable_task_runner_handle), loader_factory_, + std::move(keep_alive_handle), back_forward_cache_loader_helper); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/web_url_loader_unittest.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/web_url_loader_unittest.cc new file mode 100644 index 00000000000..6f5e2e2e1d6 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/web_url_loader_unittest.cc @@ -0,0 +1,715 @@ +// 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/public/platform/web_url_loader.h" + +#include <stdint.h> +#include <string.h> + +#include <utility> +#include <vector> + +#include "base/command_line.h" +#include "base/memory/ptr_util.h" +#include "base/memory/weak_ptr.h" +#include "base/run_loop.h" +#include "base/single_thread_task_runner.h" +#include "base/stl_util.h" +#include "base/test/task_environment.h" +#include "base/time/default_tick_clock.h" +#include "base/time/time.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/system/data_pipe.h" +#include "net/base/host_port_pair.h" +#include "net/base/ip_endpoint.h" +#include "net/base/net_errors.h" +#include "net/cert/x509_util.h" +#include "net/http/http_response_headers.h" +#include "net/http/http_util.h" +#include "net/ssl/ssl_connection_status_flags.h" +#include "net/test/cert_test_util.h" +#include "net/traffic_annotation/network_traffic_annotation_test_helper.h" +#include "net/url_request/redirect_info.h" +#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" +#include "services/network/public/mojom/fetch_api.mojom-shared.h" +#include "services/network/public/mojom/url_response_head.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/platform/resource_load_info_notifier_wrapper.h" +#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h" +#include "third_party/blink/public/platform/web_back_forward_cache_loader_helper.h" +#include "third_party/blink/public/platform/web_data.h" +#include "third_party/blink/public/platform/web_request_peer.h" +#include "third_party/blink/public/platform/web_resource_request_sender.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/public/platform/web_url_loader.h" +#include "third_party/blink/public/platform/web_url_loader_client.h" +#include "third_party/blink/public/platform/web_url_request.h" +#include "third_party/blink/public/platform/web_url_request_extra_data.h" +#include "third_party/blink/public/platform/web_url_response.h" +#include "third_party/blink/public/platform/web_vector.h" +#include "third_party/blink/renderer/platform/loader/fetch/url_loader/sync_load_response.h" +#include "third_party/blink/renderer/platform/weborigin/kurl.h" +#include "url/gurl.h" +#include "url/origin.h" + +namespace blink { +namespace { + +const char kTestURL[] = "http://foo"; +const char kTestHTTPSURL[] = "https://foo"; +const char kTestData[] = "blah!"; + +class MockResourceRequestSender : public WebResourceRequestSender { + public: + MockResourceRequestSender() = default; + ~MockResourceRequestSender() override = default; + + // WebResourceRequestSender implementation: + void SendSync( + std::unique_ptr<network::ResourceRequest> request, + int routing_id, + const net::NetworkTrafficAnnotationTag& traffic_annotation, + uint32_t loader_options, + SyncLoadResponse* response, + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, + WebVector<std::unique_ptr<URLLoaderThrottle>> throttles, + base::TimeDelta timeout, + const WebVector<WebString>& cors_exempt_header_list, + base::WaitableEvent* terminate_sync_load_event, + mojo::PendingRemote<mojom::BlobRegistry> download_to_blob_registry, + scoped_refptr<WebRequestPeer> peer, + std::unique_ptr<ResourceLoadInfoNotifierWrapper> + resource_load_info_notifier_wrapper, + WebBackForwardCacheLoaderHelper back_forward_cache_loader_helper) + override { + *response = std::move(sync_load_response_); + } + + int SendAsync( + std::unique_ptr<network::ResourceRequest> request, + int routing_id, + scoped_refptr<base::SingleThreadTaskRunner> loading_task_runner, + const net::NetworkTrafficAnnotationTag& traffic_annotation, + uint32_t loader_options, + const WebVector<WebString>& cors_exempt_header_list, + scoped_refptr<WebRequestPeer> peer, + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, + WebVector<std::unique_ptr<URLLoaderThrottle>> throttles, + std::unique_ptr<ResourceLoadInfoNotifierWrapper> + resource_load_info_notifier_wrapper, + WebBackForwardCacheLoaderHelper back_forward_cache_loader_helper) + override { + EXPECT_FALSE(peer_); + if (sync_load_response_.head->encoded_body_length != -1) + EXPECT_TRUE(loader_options & network::mojom::kURLLoadOptionSynchronous); + peer_ = std::move(peer); + return 1; + } + + void Cancel( + scoped_refptr<base::SingleThreadTaskRunner> task_runner) override { + EXPECT_FALSE(canceled_); + canceled_ = true; + + task_runner->ReleaseSoon(FROM_HERE, std::move(peer_)); + } + + WebRequestPeer* peer() { return peer_.get(); } + + bool canceled() { return canceled_; } + + void SetDefersLoading(WebURLLoader::DeferType value) override { + defers_loading_ = (value != WebURLLoader::DeferType::kNotDeferred); + } + bool defers_loading() const { return defers_loading_; } + + void set_sync_load_response(SyncLoadResponse&& sync_load_response) { + sync_load_response_ = std::move(sync_load_response); + } + + private: + scoped_refptr<WebRequestPeer> peer_; + bool canceled_ = false; + bool defers_loading_ = false; + SyncLoadResponse sync_load_response_; + + DISALLOW_COPY_AND_ASSIGN(MockResourceRequestSender); +}; + +class FakeURLLoaderFactory final : public network::mojom::URLLoaderFactory { + public: + FakeURLLoaderFactory() = default; + ~FakeURLLoaderFactory() override = default; + void CreateLoaderAndStart( + mojo::PendingReceiver<network::mojom::URLLoader> receiver, + int32_t routing_id, + int32_t request_id, + uint32_t options, + const network::ResourceRequest& url_request, + mojo::PendingRemote<network::mojom::URLLoaderClient> client, + const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) + override { + NOTREACHED(); + } + + void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) + override { + NOTREACHED(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(FakeURLLoaderFactory); +}; + +class TestWebURLLoaderClient : public WebURLLoaderClient { + public: + TestWebURLLoaderClient() + : loader_(new WebURLLoader( + /*cors_exempt_header_list=*/WebVector<WebString>(), + /*terminate_sync_load_event=*/nullptr, + scheduler::WebResourceLoadingTaskRunnerHandle::CreateUnprioritized( + scheduler::GetSingleThreadTaskRunnerForTesting()), + scheduler::WebResourceLoadingTaskRunnerHandle::CreateUnprioritized( + scheduler::GetSingleThreadTaskRunnerForTesting()), + base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( + &fake_url_loader_factory_), + /*keep_alive_handle=*/mojo::NullRemote(), + WebBackForwardCacheLoaderHelper())), + delete_on_receive_redirect_(false), + delete_on_receive_response_(false), + delete_on_receive_data_(false), + delete_on_finish_(false), + delete_on_fail_(false), + did_receive_redirect_(false), + did_receive_response_(false), + did_finish_(false) {} + + ~TestWebURLLoaderClient() override { + // During the deconstruction of the `loader_`, the request context will be + // released asynchronously and we must ensure that the request context has + // been deleted practically before the test quits, thus, memory leak will + // not be reported on the ASAN build. So, we call 'reset()' to trigger the + // deconstruction, and then execute `RunUntilIdle()` to empty the task queue + // to achieve that. + if (loader_) + loader_.reset(); + base::RunLoop().RunUntilIdle(); + } + + // WebURLLoaderClient implementation: + bool WillFollowRedirect(const WebURL& new_url, + const net::SiteForCookies& new_site_for_cookies, + const WebString& new_referrer, + network::mojom::ReferrerPolicy new_referrer_policy, + const WebString& new_method, + const WebURLResponse& passed_redirect_response, + bool& report_raw_headers, + std::vector<std::string>*) override { + EXPECT_TRUE(loader_); + + // No test currently simulates mutiple redirects. + EXPECT_FALSE(did_receive_redirect_); + did_receive_redirect_ = true; + + if (delete_on_receive_redirect_) + loader_.reset(); + + return true; + } + + void DidSendData(uint64_t bytesSent, uint64_t totalBytesToBeSent) override { + EXPECT_TRUE(loader_); + } + + void DidReceiveResponse(const WebURLResponse& response) override { + EXPECT_TRUE(loader_); + EXPECT_FALSE(did_receive_response_); + + did_receive_response_ = true; + response_ = response; + if (delete_on_receive_response_) + loader_.reset(); + } + + void DidStartLoadingResponseBody( + mojo::ScopedDataPipeConsumerHandle response_body) override { + DCHECK(!response_body_); + DCHECK(response_body); + response_body_ = std::move(response_body); + } + + void DidReceiveData(const char* data, int dataLength) override { + NOTREACHED(); + } + + void DidFinishLoading(base::TimeTicks finishTime, + int64_t totalEncodedDataLength, + int64_t totalEncodedBodyLength, + int64_t totalDecodedBodyLength, + bool should_report_corb_blocking) override { + EXPECT_TRUE(loader_); + EXPECT_TRUE(did_receive_response_); + EXPECT_FALSE(did_finish_); + did_finish_ = true; + + if (delete_on_finish_) + loader_.reset(); + } + + void DidFail(const WebURLError& error, + base::TimeTicks finishTime, + int64_t totalEncodedDataLength, + int64_t totalEncodedBodyLength, + int64_t totalDecodedBodyLength) override { + EXPECT_TRUE(loader_); + EXPECT_FALSE(did_finish_); + error_ = error; + + if (delete_on_fail_) + loader_.reset(); + } + + WebURLLoader* loader() { return loader_.get(); } + void DeleteLoader() { loader_.reset(); } + + void set_delete_on_receive_redirect() { delete_on_receive_redirect_ = true; } + void set_delete_on_receive_response() { delete_on_receive_response_ = true; } + void set_delete_on_receive_data() { delete_on_receive_data_ = true; } + void set_delete_on_finish() { delete_on_finish_ = true; } + void set_delete_on_fail() { delete_on_fail_ = true; } + + bool did_receive_redirect() const { return did_receive_redirect_; } + bool did_receive_response() const { return did_receive_response_; } + bool did_receive_response_body() const { return !!response_body_; } + bool did_finish() const { return did_finish_; } + const base::Optional<WebURLError>& error() const { return error_; } + const WebURLResponse& response() const { return response_; } + + private: + FakeURLLoaderFactory fake_url_loader_factory_; + std::unique_ptr<WebURLLoader> loader_; + + bool delete_on_receive_redirect_; + bool delete_on_receive_response_; + bool delete_on_receive_data_; + bool delete_on_finish_; + bool delete_on_fail_; + + bool did_receive_redirect_; + bool did_receive_response_; + mojo::ScopedDataPipeConsumerHandle response_body_; + bool did_finish_; + base::Optional<WebURLError> error_; + WebURLResponse response_; + + DISALLOW_COPY_AND_ASSIGN(TestWebURLLoaderClient); +}; + +class WebURLLoaderTest : public testing::Test { + public: + WebURLLoaderTest() : client_(std::make_unique<TestWebURLLoaderClient>()) { + auto sender = std::make_unique<MockResourceRequestSender>(); + sender_ = sender.get(); + client_->loader()->SetResourceRequestSenderForTesting(std::move(sender)); + } + + ~WebURLLoaderTest() override = default; + + void DoStartAsyncRequest() { + auto request = std::make_unique<network::ResourceRequest>(); + request->url = GURL(kTestURL); + request->destination = network::mojom::RequestDestination::kEmpty; + request->priority = net::IDLE; + client()->loader()->LoadAsynchronously( + std::move(request), /*url_request_extra_data=*/nullptr, + /*requestor_id=*/0, + /*no_mime_sniffing=*/false, + std::make_unique<ResourceLoadInfoNotifierWrapper>( + /*resource_load_info_notifier=*/nullptr), + client()); + ASSERT_TRUE(peer()); + } + + void DoReceiveRedirect() { + EXPECT_FALSE(client()->did_receive_redirect()); + net::RedirectInfo redirect_info; + redirect_info.status_code = 302; + redirect_info.new_method = "GET"; + redirect_info.new_url = GURL(kTestURL); + redirect_info.new_site_for_cookies = + net::SiteForCookies::FromUrl(GURL(kTestURL)); + std::vector<std::string> removed_headers; + peer()->OnReceivedRedirect(redirect_info, + network::mojom::URLResponseHead::New(), + &removed_headers); + EXPECT_TRUE(client()->did_receive_redirect()); + } + + void DoReceiveHTTPSRedirect() { + EXPECT_FALSE(client()->did_receive_redirect()); + net::RedirectInfo redirect_info; + redirect_info.status_code = 302; + redirect_info.new_method = "GET"; + redirect_info.new_url = GURL(kTestHTTPSURL); + redirect_info.new_site_for_cookies = + net::SiteForCookies::FromUrl(GURL(kTestHTTPSURL)); + peer()->OnReceivedRedirect(redirect_info, + network::mojom::URLResponseHead::New(), nullptr); + EXPECT_TRUE(client()->did_receive_redirect()); + } + + void DoReceiveResponse() { + EXPECT_FALSE(client()->did_receive_response()); + peer()->OnReceivedResponse(network::mojom::URLResponseHead::New()); + EXPECT_TRUE(client()->did_receive_response()); + } + + void DoStartLoadingResponseBody() { + mojo::ScopedDataPipeConsumerHandle handle_to_pass; + MojoResult rv = mojo::CreateDataPipe(nullptr, body_handle_, handle_to_pass); + ASSERT_EQ(MOJO_RESULT_OK, rv); + peer()->OnStartLoadingResponseBody(std::move(handle_to_pass)); + } + + void DoCompleteRequest() { + EXPECT_FALSE(client()->did_finish()); + DCHECK(body_handle_); + body_handle_.reset(); + base::RunLoop().RunUntilIdle(); + network::URLLoaderCompletionStatus status(net::OK); + status.encoded_data_length = base::size(kTestData); + status.encoded_body_length = base::size(kTestData); + status.decoded_body_length = base::size(kTestData); + peer()->OnCompletedRequest(status); + EXPECT_TRUE(client()->did_finish()); + // There should be no error. + EXPECT_FALSE(client()->error()); + } + + void DoFailRequest() { + EXPECT_FALSE(client()->did_finish()); + DCHECK(body_handle_); + body_handle_.reset(); + base::RunLoop().RunUntilIdle(); + network::URLLoaderCompletionStatus status(net::ERR_FAILED); + status.encoded_data_length = base::size(kTestData); + status.encoded_body_length = base::size(kTestData); + status.decoded_body_length = base::size(kTestData); + peer()->OnCompletedRequest(status); + EXPECT_FALSE(client()->did_finish()); + ASSERT_TRUE(client()->error()); + EXPECT_EQ(net::ERR_FAILED, client()->error()->reason()); + } + + TestWebURLLoaderClient* client() { return client_.get(); } + MockResourceRequestSender* sender() { return sender_; } + WebRequestPeer* peer() { return sender_->peer(); } + + private: + base::test::SingleThreadTaskEnvironment task_environment_; + mojo::ScopedDataPipeProducerHandle body_handle_; + std::unique_ptr<TestWebURLLoaderClient> client_; + MockResourceRequestSender* sender_ = nullptr; +}; + +TEST_F(WebURLLoaderTest, Success) { + DoStartAsyncRequest(); + DoReceiveResponse(); + DoStartLoadingResponseBody(); + DoCompleteRequest(); + EXPECT_FALSE(sender()->canceled()); + EXPECT_TRUE(client()->did_receive_response_body()); +} + +TEST_F(WebURLLoaderTest, Redirect) { + DoStartAsyncRequest(); + DoReceiveRedirect(); + DoReceiveResponse(); + DoStartLoadingResponseBody(); + DoCompleteRequest(); + EXPECT_FALSE(sender()->canceled()); + EXPECT_TRUE(client()->did_receive_response_body()); +} + +TEST_F(WebURLLoaderTest, Failure) { + DoStartAsyncRequest(); + DoReceiveResponse(); + DoStartLoadingResponseBody(); + DoFailRequest(); + EXPECT_FALSE(sender()->canceled()); +} + +// The client may delete the WebURLLoader during any callback from the loader. +// These tests make sure that doesn't result in a crash. +TEST_F(WebURLLoaderTest, DeleteOnReceiveRedirect) { + client()->set_delete_on_receive_redirect(); + DoStartAsyncRequest(); + DoReceiveRedirect(); +} + +TEST_F(WebURLLoaderTest, DeleteOnReceiveResponse) { + client()->set_delete_on_receive_response(); + DoStartAsyncRequest(); + DoReceiveResponse(); +} + +TEST_F(WebURLLoaderTest, DeleteOnFinish) { + client()->set_delete_on_finish(); + DoStartAsyncRequest(); + DoReceiveResponse(); + DoStartLoadingResponseBody(); + DoCompleteRequest(); +} + +TEST_F(WebURLLoaderTest, DeleteOnFail) { + client()->set_delete_on_fail(); + DoStartAsyncRequest(); + DoReceiveResponse(); + DoStartLoadingResponseBody(); + DoFailRequest(); +} + +TEST_F(WebURLLoaderTest, DefersLoadingBeforeStart) { + client()->loader()->SetDefersLoading(WebURLLoader::DeferType::kDeferred); + EXPECT_FALSE(sender()->defers_loading()); + DoStartAsyncRequest(); + EXPECT_TRUE(sender()->defers_loading()); +} + +TEST_F(WebURLLoaderTest, ResponseIPEndpoint) { + KURL url("http://example.test/"); + + struct TestCase { + const char* ip; + uint16_t port; + } cases[] = { + {"127.0.0.1", 443}, + {"123.123.123.123", 80}, + {"::1", 22}, + {"2001:0db8:85a3:0000:0000:8a2e:0370:7334", 1337}, + {"2001:db8:85a3:0:0:8a2e:370:7334", 12345}, + {"2001:db8:85a3::8a2e:370:7334", 8080}, + {"::ffff:192.0.2.128", 8443}, + }; + + for (const auto& test : cases) { + SCOPED_TRACE(test.ip); + + net::IPAddress address; + ASSERT_TRUE(address.AssignFromIPLiteral(test.ip)); + + network::mojom::URLResponseHead head; + head.remote_endpoint = net::IPEndPoint(address, test.port); + + WebURLResponse response; + WebURLLoader::PopulateURLResponse(url, head, &response, true, -1); + EXPECT_EQ(head.remote_endpoint, response.RemoteIPEndpoint()); + }; +} + +TEST_F(WebURLLoaderTest, ResponseAddressSpace) { + using AddressSpace = network::mojom::IPAddressSpace; + + struct TestCase { + std::string url; + std::string ip; + AddressSpace expected; + } cases[] = { + {"http://localhost", "127.0.0.1", AddressSpace::kLocal}, + {"http://localhost", "::1", AddressSpace::kLocal}, + {"file:///a/path", "", AddressSpace::kLocal}, + {"file:///a/path", "8.8.8.8", AddressSpace::kLocal}, + {"http://router.local", "10.1.0.1", AddressSpace::kPrivate}, + {"http://router.local", "::ffff:192.0.2.128", AddressSpace::kPrivate}, + {"https://bleep.test", "8.8.8.8", AddressSpace::kPublic}, + {"http://a.test", "2001:db8:85a3::8a2e:370:7334", AddressSpace::kPublic}, + {"http://invalid", "", AddressSpace::kUnknown}, + }; + + for (const auto& test : cases) { + SCOPED_TRACE(test.url + ", " + test.ip); + + KURL url(test.url.c_str()); + + // We are forced to use the result of AssignFromIPLiteral(), and we cannot + // just assign it to an unused variable. Check that all non-empty literals + // are correctly parsed. + net::IPAddress address; + EXPECT_EQ(!test.ip.empty(), address.AssignFromIPLiteral(test.ip)); + + network::mojom::URLResponseHead head; + head.remote_endpoint = net::IPEndPoint(address, 443); + + WebURLResponse response; + WebURLLoader::PopulateURLResponse(url, head, &response, true, -1); + + EXPECT_EQ(test.expected, response.AddressSpace()); + } +} + +// This test verifies that the IPAddressSpace set on WebURLResponse takes into +// account WebURLResponse::ResponseUrl() instead of +// WebURLResponse::CurrentRequestUrl(). +TEST_F(WebURLLoaderTest, ResponseAddressSpaceConsidersResponseUrl) { + KURL request_url("http://request.test"); + + // The remote endpoint contains a public IP address, but the response was + // ultimately fetched by a service worker from a file URL. + network::mojom::URLResponseHead head; + head.remote_endpoint = net::IPEndPoint(net::IPAddress(8, 8, 8, 8), 80); + head.was_fetched_via_service_worker = true; + head.url_list_via_service_worker = { + GURL("http://redirect.test"), + GURL("file:///a/path"), + }; + + WebURLResponse response; + WebURLLoader::PopulateURLResponse(request_url, head, &response, true, -1); + + // The address space of the response reflects the fact the it was fetched + // from a file, even though the request was initially to a public website. + EXPECT_EQ(KURL("http://request.test"), KURL(response.CurrentRequestUrl())); + EXPECT_EQ(KURL("file:///a/path"), KURL(response.ResponseUrl())); + EXPECT_EQ(network::mojom::IPAddressSpace::kLocal, response.AddressSpace()); +} + +TEST_F(WebURLLoaderTest, ResponseCert) { + KURL url("https://test.example/"); + + net::CertificateList certs; + ASSERT_TRUE(net::LoadCertificateFiles( + {"subjectAltName_sanity_check.pem", "root_ca_cert.pem"}, &certs)); + ASSERT_EQ(2U, certs.size()); + + base::StringPiece cert0_der = + net::x509_util::CryptoBufferAsStringPiece(certs[0]->cert_buffer()); + base::StringPiece cert1_der = + net::x509_util::CryptoBufferAsStringPiece(certs[1]->cert_buffer()); + + net::SSLInfo ssl_info; + ssl_info.cert = + net::X509Certificate::CreateFromDERCertChain({cert0_der, cert1_der}); + net::SSLConnectionStatusSetVersion(net::SSL_CONNECTION_VERSION_TLS1_2, + &ssl_info.connection_status); + + network::mojom::URLResponseHead head; + head.ssl_info = ssl_info; + WebURLResponse web_url_response; + WebURLLoader::PopulateURLResponse(url, head, &web_url_response, true, -1); + + base::Optional<WebURLResponse::WebSecurityDetails> security_details = + web_url_response.SecurityDetailsForTesting(); + ASSERT_TRUE(security_details.has_value()); + EXPECT_EQ("TLS 1.2", security_details->protocol); + EXPECT_EQ("127.0.0.1", security_details->subject_name); + EXPECT_EQ("127.0.0.1", security_details->issuer); + ASSERT_EQ(3U, security_details->san_list.size()); + EXPECT_EQ("test.example", security_details->san_list[0]); + EXPECT_EQ("127.0.0.2", security_details->san_list[1]); + EXPECT_EQ("fe80::1", security_details->san_list[2]); + EXPECT_EQ(certs[0]->valid_start().ToTimeT(), security_details->valid_from); + EXPECT_EQ(certs[0]->valid_expiry().ToTimeT(), security_details->valid_to); + ASSERT_EQ(2U, security_details->certificate.size()); + EXPECT_EQ(WebString::FromLatin1(std::string(cert0_der)), + security_details->certificate[0]); + EXPECT_EQ(WebString::FromLatin1(std::string(cert1_der)), + security_details->certificate[1]); +} + +TEST_F(WebURLLoaderTest, ResponseCertWithNoSANs) { + KURL url("https://test.example/"); + + net::CertificateList certs; + ASSERT_TRUE(net::LoadCertificateFiles({"multi-root-B-by-C.pem"}, &certs)); + ASSERT_EQ(1U, certs.size()); + + base::StringPiece cert0_der = + net::x509_util::CryptoBufferAsStringPiece(certs[0]->cert_buffer()); + + net::SSLInfo ssl_info; + net::SSLConnectionStatusSetVersion(net::SSL_CONNECTION_VERSION_TLS1_2, + &ssl_info.connection_status); + ssl_info.cert = certs[0]; + network::mojom::URLResponseHead head; + head.ssl_info = ssl_info; + WebURLResponse web_url_response; + WebURLLoader::PopulateURLResponse(url, head, &web_url_response, true, -1); + + base::Optional<WebURLResponse::WebSecurityDetails> security_details = + web_url_response.SecurityDetailsForTesting(); + ASSERT_TRUE(security_details.has_value()); + EXPECT_EQ("TLS 1.2", security_details->protocol); + EXPECT_EQ("B CA - Multi-root", security_details->subject_name); + EXPECT_EQ("C CA - Multi-root", security_details->issuer); + EXPECT_EQ(0U, security_details->san_list.size()); + EXPECT_EQ(certs[0]->valid_start().ToTimeT(), security_details->valid_from); + EXPECT_EQ(certs[0]->valid_expiry().ToTimeT(), security_details->valid_to); + ASSERT_EQ(1U, security_details->certificate.size()); + EXPECT_EQ(WebString::FromLatin1(std::string(cert0_der)), + security_details->certificate[0]); +} + +// Verifies that the lengths used by the PerformanceResourceTiming API are +// correctly assigned for sync XHR. +TEST_F(WebURLLoaderTest, SyncLengths) { + static const char kBodyData[] = "Today is Thursday"; + const int kEncodedBodyLength = 30; + const int kEncodedDataLength = 130; + const KURL url(kTestURL); + + auto request = std::make_unique<network::ResourceRequest>(); + request->url = url; + request->destination = network::mojom::RequestDestination::kEmpty; + request->priority = net::HIGHEST; + + // Prepare a mock response + SyncLoadResponse sync_load_response; + sync_load_response.error_code = net::OK; + sync_load_response.url = url; + sync_load_response.data.Assign(WebData(kBodyData)); + ASSERT_EQ(17u, sync_load_response.data.size()); + sync_load_response.head->encoded_body_length = kEncodedBodyLength; + sync_load_response.head->encoded_data_length = kEncodedDataLength; + sender()->set_sync_load_response(std::move(sync_load_response)); + + WebURLResponse response; + base::Optional<WebURLError> error; + WebData data; + int64_t encoded_data_length = 0; + int64_t encoded_body_length = 0; + WebBlobInfo downloaded_blob; + + client()->loader()->LoadSynchronously( + std::move(request), /*url_request_extra_data=*/nullptr, + /*requestor_id=*/0, + /*pass_response_pipe_to_client=*/false, /*no_mime_sniffing=*/false, + base::TimeDelta(), nullptr, response, error, data, encoded_data_length, + encoded_body_length, downloaded_blob, + std::make_unique<ResourceLoadInfoNotifierWrapper>( + /*resource_load_info_notifier=*/nullptr)); + + EXPECT_EQ(kEncodedBodyLength, encoded_body_length); + EXPECT_EQ(kEncodedDataLength, encoded_data_length); + EXPECT_TRUE(downloaded_blob.Uuid().IsNull()); +} + +// Verifies that PopulateURLResponse() copies AuthChallengeInfo to the response. +TEST_F(WebURLLoaderTest, AuthChallengeInfo) { + network::mojom::URLResponseHead head; + net::AuthChallengeInfo auth_challenge_info; + auth_challenge_info.is_proxy = true; + auth_challenge_info.challenge = "foobar"; + head.auth_challenge_info = auth_challenge_info; + + blink::WebURLResponse response; + WebURLLoader::PopulateURLResponse(KURL(), head, &response, true, -1); + ASSERT_TRUE(response.AuthChallengeInfo().has_value()); + EXPECT_TRUE(response.AuthChallengeInfo()->is_proxy); + EXPECT_EQ("foobar", response.AuthChallengeInfo()->challenge); +} + +} // namespace +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader.cc index 3735b99b52a..40ab612c1f8 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader.cc @@ -7,12 +7,14 @@ #include "services/network/public/mojom/url_response_head.mojom.h" #include "third_party/blink/public/common/features.h" #include "third_party/blink/public/common/loader/referrer_utils.h" -#include "third_party/blink/public/platform/platform.h" +#include "third_party/blink/public/mojom/loader/code_cache.mojom-shared.h" #include "third_party/blink/public/platform/resource_load_info_notifier_wrapper.h" #include "third_party/blink/public/platform/url_conversion.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_response.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/cached_metadata_handler.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" @@ -31,7 +33,7 @@ WorkerMainScriptLoader::WorkerMainScriptLoader() = default; WorkerMainScriptLoader::~WorkerMainScriptLoader() = default; void WorkerMainScriptLoader::Start( - FetchParameters& fetch_params, + const FetchParameters& fetch_params, std::unique_ptr<WorkerMainScriptLoadParameters> worker_main_script_load_params, FetchContext* fetch_context, @@ -39,7 +41,7 @@ void WorkerMainScriptLoader::Start( WorkerMainScriptLoaderClient* client) { DCHECK(resource_load_observer); DCHECK(client); - initial_request_.CopyFrom(fetch_params.GetResourceRequest()); + initial_request_ = fetch_params.GetResourceRequest(); resource_loader_options_ = fetch_params.Options(); initial_request_url_ = fetch_params.GetResourceRequest().Url(); last_request_url_ = initial_request_url_; @@ -53,10 +55,12 @@ void WorkerMainScriptLoader::Start( // TODO(crbug.com/929370): Support CSP check to post violation reports for // worker top-level scripts, if off-the-main-thread fetch is enabled. + ResourceRequest resource_request(initial_request_); resource_load_observer_->WillSendRequest( - initial_request_.InspectorId(), initial_request_, + initial_request_.InspectorId(), resource_request, /*redirect_response=*/ResourceResponse(), ResourceType::kScript, - resource_loader_options_.initiator_info); + resource_loader_options_.initiator_info, + RenderBlockingBehavior::kNonBlocking); resource_load_info_notifier_wrapper_->NotifyResourceLoadInitiated( /*request_id=*/-1, initial_request_url_, @@ -71,7 +75,7 @@ void WorkerMainScriptLoader::Start( WebURLResponse response; auto response_head = std::move(worker_main_script_load_params->response_head); - Platform::Current()->PopulateURLResponse( + WebURLLoader::PopulateURLResponse( WebURL(last_request_url_), *response_head, &response, response_head->ssl_info.has_value(), /*request_id=*/-1); resource_response_ = response.ToResourceResponse(); @@ -79,7 +83,7 @@ void WorkerMainScriptLoader::Start( std::move(response_head), PreviewsTypes::kPreviewsUnspecified); resource_load_observer_->DidReceiveResponse( - initial_request_.InspectorId(), initial_request_, resource_response_, + initial_request_.InspectorId(), resource_request, resource_response_, /*resource=*/nullptr, ResourceLoadObserver::ResponseSource::kNotFromMemoryCache); @@ -318,12 +322,13 @@ void WorkerMainScriptLoader::HandleRedirections( redirect_info.new_referrer_policy), /*skip_service_worker=*/false); WebURLResponse response; - Platform::Current()->PopulateURLResponse( + WebURLLoader::PopulateURLResponse( WebURL(last_request_url_), *redirect_response, &response, redirect_response->ssl_info.has_value(), /*request_id=*/-1); resource_load_observer_->WillSendRequest( new_request->InspectorId(), *new_request, response.ToResourceResponse(), - ResourceType::kScript, resource_loader_options_.initiator_info); + ResourceType::kScript, resource_loader_options_.initiator_info, + RenderBlockingBehavior::kNonBlocking); resource_load_info_notifier_wrapper_->NotifyResourceRedirectReceived( redirect_info, std::move(redirect_response)); } diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader.h b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader.h index e6f82c490c1..a9426432965 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader.h +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader.h @@ -36,9 +36,10 @@ class SingleCachedMetadataHandler; class WorkerMainScriptLoaderClient; struct ResourceLoaderOptions; -// For dedicated workers (PlzDedicatedWorker) and shared workers, the main -// script is pre-requested by the browser process. This class is used for -// receiving the response in the renderer process. +// For dedicated workers (PlzDedicatedWorker), service workers +// (PlzServiceWorker), and shared workers, the main script is pre-requested by +// the browser process. This class is used for receiving the response in the +// renderer process. class PLATFORM_EXPORT WorkerMainScriptLoader final : public GarbageCollected<WorkerMainScriptLoader>, public network::mojom::URLLoaderClient { @@ -47,7 +48,7 @@ class PLATFORM_EXPORT WorkerMainScriptLoader final ~WorkerMainScriptLoader() override; // Starts to load the main script. - void Start(FetchParameters& fetch_params, + void Start(const FetchParameters& fetch_params, std::unique_ptr<WorkerMainScriptLoadParameters> worker_main_script_load_params, FetchContext* fetch_context, @@ -98,7 +99,7 @@ class PLATFORM_EXPORT WorkerMainScriptLoader final Member<WorkerMainScriptLoaderClient> client_; Member<ResourceLoadObserver> resource_load_observer_; - ResourceRequest initial_request_; + ResourceRequestHead initial_request_; ResourceLoaderOptions resource_loader_options_{nullptr /* world */}; KURL initial_request_url_; KURL last_request_url_; diff --git a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader_unittest.cc b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader_unittest.cc index 7a8e62cd882..e5df67c95aa 100644 --- a/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader_unittest.cc +++ b/chromium/third_party/blink/renderer/platform/loader/fetch/url_loader/worker_main_script_loader_unittest.cc @@ -49,20 +49,6 @@ class WorkerMainScriptLoaderTest : public testing::Test { } protected: - class TestPlatform final : public TestingPlatformSupport { - public: - void PopulateURLResponse(const WebURL& url, - const network::mojom::URLResponseHead& head, - WebURLResponse* response, - bool report_security_info, - int request_id) override { - response->SetCurrentRequestUrl(url); - response->SetHttpStatusCode(head.headers.get()->response_code()); - response->SetMimeType(WebString::FromUTF8(head.mime_type)); - response->SetTextEncodingName(WebString::FromUTF8(head.charset)); - } - }; - class TestClient final : public GarbageCollected<TestClient>, public WorkerMainScriptLoaderClient { @@ -160,12 +146,13 @@ class WorkerMainScriptLoaderTest : public testing::Test { class MockResourceLoadObserver : public ResourceLoadObserver { public: MOCK_METHOD2(DidStartRequest, void(const FetchParameters&, ResourceType)); - MOCK_METHOD5(WillSendRequest, + MOCK_METHOD6(WillSendRequest, void(uint64_t identifier, const ResourceRequest&, const ResourceResponse& redirect_response, ResourceType, - const FetchInitiatorInfo&)); + const FetchInitiatorInfo&, + RenderBlockingBehavior)); MOCK_METHOD3(DidChangePriority, void(uint64_t identifier, ResourceLoadPriority, @@ -227,7 +214,7 @@ class WorkerMainScriptLoaderTest : public testing::Test { mojo::ScopedDataPipeConsumerHandle body_consumer; MojoCreateDataPipeOptions options = CreateDataPipeOptions(); EXPECT_EQ(MOJO_RESULT_OK, - mojo::CreateDataPipe(&options, body_producer, &body_consumer)); + mojo::CreateDataPipe(&options, *body_producer, body_consumer)); worker_main_script_load_params->response_body = std::move(body_consumer); return worker_main_script_load_params; @@ -261,7 +248,6 @@ class WorkerMainScriptLoaderTest : public testing::Test { protected: base::test::TaskEnvironment task_environment_; - ScopedTestingPlatformSupport<TestPlatform> platform_; mojo::PendingRemote<network::mojom::URLLoader> pending_remote_loader_; mojo::Remote<network::mojom::URLLoaderClient> loader_client_; @@ -279,7 +265,7 @@ TEST_F(WorkerMainScriptLoaderTest, ResponseWithSucessThenOnComplete) { MockResourceLoadObserver* mock_observer = MakeGarbageCollected<MockResourceLoadObserver>(); FakeResourceLoadInfoNotifier fake_resource_load_info_notifier; - EXPECT_CALL(*mock_observer, WillSendRequest(_, _, _, _, _)); + EXPECT_CALL(*mock_observer, WillSendRequest(_, _, _, _, _, _)); EXPECT_CALL(*mock_observer, DidReceiveResponse(_, _, _, _, _)); EXPECT_CALL(*mock_observer, DidReceiveData(_, _)); EXPECT_CALL(*mock_observer, DidFinishLoading(_, _, _, _, _)); @@ -310,7 +296,7 @@ TEST_F(WorkerMainScriptLoaderTest, ResponseWithFailureThenOnComplete) { MockResourceLoadObserver* mock_observer = MakeGarbageCollected<MockResourceLoadObserver>(); FakeResourceLoadInfoNotifier fake_resource_load_info_notifier; - EXPECT_CALL(*mock_observer, WillSendRequest(_, _, _, _, _)); + EXPECT_CALL(*mock_observer, WillSendRequest(_, _, _, _, _, _)); EXPECT_CALL(*mock_observer, DidReceiveResponse(_, _, _, _, _)); EXPECT_CALL(*mock_observer, DidFinishLoading(_, _, _, _, _)).Times(0); EXPECT_CALL(*mock_observer, DidFailLoading(_, _, _, _, _)); @@ -334,7 +320,7 @@ TEST_F(WorkerMainScriptLoaderTest, DisconnectBeforeOnComplete) { MockResourceLoadObserver* mock_observer = MakeGarbageCollected<MockResourceLoadObserver>(); FakeResourceLoadInfoNotifier fake_resource_load_info_notifier; - EXPECT_CALL(*mock_observer, WillSendRequest(_, _, _, _, _)); + EXPECT_CALL(*mock_observer, WillSendRequest(_, _, _, _, _, _)); EXPECT_CALL(*mock_observer, DidReceiveResponse(_, _, _, _, _)); EXPECT_CALL(*mock_observer, DidFinishLoading(_, _, _, _, _)).Times(0); EXPECT_CALL(*mock_observer, DidFailLoading(_, _, _, _, _)); @@ -358,7 +344,7 @@ TEST_F(WorkerMainScriptLoaderTest, OnCompleteWithError) { MockResourceLoadObserver* mock_observer = MakeGarbageCollected<MockResourceLoadObserver>(); FakeResourceLoadInfoNotifier fake_resource_load_info_notifier; - EXPECT_CALL(*mock_observer, WillSendRequest(_, _, _, _, _)); + EXPECT_CALL(*mock_observer, WillSendRequest(_, _, _, _, _, _)); EXPECT_CALL(*mock_observer, DidReceiveResponse(_, _, _, _, _)); EXPECT_CALL(*mock_observer, DidReceiveData(_, _)); EXPECT_CALL(*mock_observer, DidFinishLoading(_, _, _, _, _)).Times(0); diff --git a/chromium/third_party/blink/renderer/platform/loader/internet_disconnected_web_url_loader.cc b/chromium/third_party/blink/renderer/platform/loader/internet_disconnected_web_url_loader.cc index 496c0178ab4..635edb5b2c3 100644 --- a/chromium/third_party/blink/renderer/platform/loader/internet_disconnected_web_url_loader.cc +++ b/chromium/third_party/blink/renderer/platform/loader/internet_disconnected_web_url_loader.cc @@ -8,6 +8,7 @@ #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/resource_load_info_notifier_wrapper.h" #include "third_party/blink/public/platform/scheduler/web_resource_loading_task_runner_handle.h" +#include "third_party/blink/public/platform/web_back_forward_cache_loader_helper.h" #include "third_party/blink/public/platform/web_url.h" #include "third_party/blink/public/platform/web_url_loader_client.h" #include "third_party/blink/public/platform/web_url_request.h" @@ -21,7 +22,10 @@ InternetDisconnectedWebURLLoaderFactory::CreateURLLoader( std::unique_ptr<scheduler::WebResourceLoadingTaskRunnerHandle> freezable_task_runner_handle, std::unique_ptr<scheduler::WebResourceLoadingTaskRunnerHandle> - unfreezable_task_runner_handle) { + unfreezable_task_runner_handle, + CrossVariantMojoRemote<blink::mojom::KeepAliveHandleInterfaceBase> + keep_alive_handle, + WebBackForwardCacheLoaderHelper back_forward_cache_loader_helper) { DCHECK(freezable_task_runner_handle); return std::make_unique<InternetDisconnectedWebURLLoader>( std::move(freezable_task_runner_handle)); diff --git a/chromium/third_party/blink/renderer/platform/loader/mixed_content.cc b/chromium/third_party/blink/renderer/platform/loader/mixed_content.cc new file mode 100644 index 00000000000..5ab3cfd6032 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/loader/mixed_content.cc @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2016 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/mixed_content.h" + +#include "base/notreached.h" +#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h" +#include "third_party/blink/public/mojom/loader/mixed_content.mojom-blink.h" + +namespace blink { + +// static +mojom::blink::MixedContentContextType +MixedContent::ContextTypeFromRequestContext( + mojom::blink::RequestContextType context, + MixedContent::CheckModeForPlugin check_mode_for_plugin) { + switch (context) { + // "Optionally-blockable" mixed content + case mojom::RequestContextType::AUDIO: + case mojom::RequestContextType::IMAGE: + case mojom::RequestContextType::VIDEO: + return mojom::blink::MixedContentContextType::kOptionallyBlockable; + + // Plugins! Oh how dearly we love plugin-loaded content! + case mojom::RequestContextType::PLUGIN: { + return check_mode_for_plugin == MixedContent::CheckModeForPlugin::kStrict + ? mojom::blink::MixedContentContextType::kBlockable + : mojom::blink::MixedContentContextType::kOptionallyBlockable; + } + + // "Blockable" mixed content + case mojom::RequestContextType::BEACON: + case mojom::RequestContextType::CSP_REPORT: + case mojom::RequestContextType::EMBED: + case mojom::RequestContextType::EVENT_SOURCE: + case mojom::RequestContextType::FAVICON: + case mojom::RequestContextType::FETCH: + case mojom::RequestContextType::FONT: + case mojom::RequestContextType::FORM: + case mojom::RequestContextType::FRAME: + case mojom::RequestContextType::HYPERLINK: + case mojom::RequestContextType::IFRAME: + case mojom::RequestContextType::IMAGE_SET: + case mojom::RequestContextType::IMPORT: + case mojom::RequestContextType::INTERNAL: + case mojom::RequestContextType::LOCATION: + case mojom::RequestContextType::MANIFEST: + case mojom::RequestContextType::OBJECT: + case mojom::RequestContextType::PING: + case mojom::RequestContextType::PREFETCH: + case mojom::RequestContextType::SCRIPT: + case mojom::RequestContextType::SERVICE_WORKER: + case mojom::RequestContextType::SHARED_WORKER: + case mojom::RequestContextType::STYLE: + case mojom::RequestContextType::SUBRESOURCE: + case mojom::RequestContextType::SUBRESOURCE_WEBBUNDLE: + case mojom::RequestContextType::TRACK: + case mojom::RequestContextType::WORKER: + case mojom::RequestContextType::XML_HTTP_REQUEST: + case mojom::RequestContextType::XSLT: + return mojom::blink::MixedContentContextType::kBlockable; + + // FIXME: Contexts that we should block, but don't currently. + // https://crbug.com/388650 + case mojom::RequestContextType::DOWNLOAD: + return mojom::blink::MixedContentContextType::kShouldBeBlockable; + + case mojom::RequestContextType::UNSPECIFIED: + NOTREACHED(); + } + NOTREACHED(); + return mojom::blink::MixedContentContextType::kBlockable; +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/loader/mixed_content.h b/chromium/third_party/blink/renderer/platform/loader/mixed_content.h new file mode 100644 index 00000000000..c27703e154a --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/loader/mixed_content.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2016 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_MIXED_CONTENT_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_MIXED_CONTENT_H_ + +#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink-forward.h" +#include "third_party/blink/public/mojom/loader/mixed_content.mojom-blink-forward.h" +#include "third_party/blink/renderer/platform/platform_export.h" + +namespace blink { + +// Helper functions related to mixed content checks. +class MixedContent { + public: + enum class CheckModeForPlugin { kStrict, kLax }; + + PLATFORM_EXPORT static mojom::blink::MixedContentContextType + ContextTypeFromRequestContext(mojom::RequestContextType, + CheckModeForPlugin); +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_MIXED_CONTENT_H_ 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 index 217da2c4bd9..4d5e3fd4841 100644 --- 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 @@ -38,6 +38,13 @@ class MockFetchContext : public FetchContext { uint64_t GetTransferSize() const { return transfer_size_; } + void set_blocked_urls(Vector<String> blocked_urls) { + blocked_urls_ = std::move(blocked_urls); + } + void set_tagged_urls(Vector<String> tagged_urls) { + tagged_urls_ = std::move(tagged_urls); + } + bool AllowImage(bool images_enabled, const KURL&) const override { return true; } @@ -51,6 +58,21 @@ class MockFetchContext : public FetchContext { const override { return base::nullopt; } + base::Optional<ResourceRequestBlockedReason> + CanRequestBasedOnSubresourceFilterOnly( + ResourceType type, + const ResourceRequest& resource_request, + const KURL& url, + const ResourceLoaderOptions& options, + ReportingDisposition reporting_disposition, + const base::Optional<ResourceRequest::RedirectInfo>& redirect_info) + const override { + if (blocked_urls_.Contains(url.GetString())) { + return ResourceRequestBlockedReason::kSubresourceFilter; + } + + return base::nullopt; + } base::Optional<ResourceRequestBlockedReason> CheckCSPForRequest( mojom::blink::RequestContextType, network::mojom::RequestDestination request_destination, @@ -79,6 +101,15 @@ class MockFetchContext : public FetchContext { weak_wrapper_resource_load_info_notifier_->AsWeakPtr()); } + bool CalculateIfAdSubresource( + const ResourceRequestHead& resource_request, + const base::Optional<KURL>& alias_url, + ResourceType type, + const FetchInitiatorInfo& initiator_info) override { + const KURL url = alias_url ? alias_url.value() : resource_request.Url(); + return tagged_urls_.Contains(url.GetString()); + } + void SetResourceLoadInfoNotifier( mojom::ResourceLoadInfoNotifier* resource_load_info_notifier) { resource_load_info_notifier_ = resource_load_info_notifier; @@ -89,6 +120,8 @@ class MockFetchContext : public FetchContext { mojom::ResourceLoadInfoNotifier* resource_load_info_notifier_ = nullptr; std::unique_ptr<WeakWrapperResourceLoadInfoNotifier> weak_wrapper_resource_load_info_notifier_; + Vector<String> blocked_urls_; + Vector<String> tagged_urls_; }; } // namespace blink 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 index 0931e9c4fe7..3a20dfa3286 100644 --- a/chromium/third_party/blink/renderer/platform/loader/testing/mock_resource.cc +++ b/chromium/third_party/blink/renderer/platform/loader/testing/mock_resource.cc @@ -43,57 +43,4 @@ MockResource::MockResource(const ResourceRequest& request, const ResourceLoaderOptions& options) : Resource(request, ResourceType::kMock, options) {} -CachedMetadataHandler* MockResource::CreateCachedMetadataHandler( - std::unique_ptr<CachedMetadataSender> send_callback) { - return MakeGarbageCollected<MockCacheHandler>(std::move(send_callback)); -} - -void MockResource::SetSerializedCachedMetadata(mojo_base::BigBuffer data) { - // Resource ignores the cached metadata. - Resource::SetSerializedCachedMetadata(mojo_base::BigBuffer()); - MockCacheHandler* cache_handler = - static_cast<MockCacheHandler*>(Resource::CacheHandler()); - if (cache_handler) { - cache_handler->Set(data.data(), data.size()); - } -} - -void MockResource::SendCachedMetadata(const uint8_t* 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 uint8_t* data, size_t size) { - data_.emplace(); - data_->Append(data, SafeCast<wtf_size_t>(size)); -} - -void MockCacheHandler::ClearCachedMetadata( - CachedMetadataHandler::ClearCacheType cache_type) { - if (cache_type == CachedMetadataHandler::kClearPersistentStorage) { - 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 index 991860b1079..2e522f8c0dc 100644 --- a/chromium/third_party/blink/renderer/platform/loader/testing/mock_resource.h +++ b/chromium/third_party/blink/renderer/platform/loader/testing/mock_resource.h @@ -6,9 +6,7 @@ #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/cached_metadata_handler.h" #include "third_party/blink/renderer/platform/loader/fetch/resource.h" -#include "third_party/blink/renderer/platform/wtf/ref_counted.h" namespace blink { @@ -16,27 +14,6 @@ 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 uint8_t* data, size_t); - void ClearCachedMetadata(CachedMetadataHandler::ClearCacheType) override; - void Send(); - - String Encoding() const override { return "mock encoding"; } - bool IsServedFromCacheStorage() const override { return false; } - void OnMemoryDump(WebProcessMemoryDump* pmd, - const String& dump_prefix) const override {} - size_t GetCodeCacheSize() const override { return 0; } - - private: - std::unique_ptr<CachedMetadataSender> send_callback_; - base::Optional<Vector<uint8_t>> 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 @@ -49,14 +26,6 @@ class MockResource final : public Resource { explicit MockResource(const KURL&); explicit MockResource(const ResourceRequest&); MockResource(const ResourceRequest&, const ResourceLoaderOptions&); - - CachedMetadataHandler* CreateCachedMetadataHandler( - std::unique_ptr<CachedMetadataSender> send_callback) override; - void SetSerializedCachedMetadata(mojo_base::BigBuffer data) override; - - MockCacheHandler* CacheHandler(); - - void SendCachedMetadata(const uint8_t*, size_t); }; } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/loader/testing/test_loader_factory.h b/chromium/third_party/blink/renderer/platform/loader/testing/test_loader_factory.h index e361dceaf2c..806f9847b9e 100644 --- a/chromium/third_party/blink/renderer/platform/loader/testing/test_loader_factory.h +++ b/chromium/third_party/blink/renderer/platform/loader/testing/test_loader_factory.h @@ -9,6 +9,7 @@ #include <utility> #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/scheduler/web_resource_loading_task_runner_handle.h" +#include "third_party/blink/public/platform/web_back_forward_cache_loader_helper.h" #include "third_party/blink/public/platform/web_url_loader_factory.h" #include "third_party/blink/public/platform/web_url_loader_mock_factory.h" #include "third_party/blink/renderer/platform/exported/wrapped_resource_request.h" @@ -32,7 +33,8 @@ class TestLoaderFactory : public ResourceFetcher::LoaderFactory { const ResourceRequest& request, const ResourceLoaderOptions& options, scoped_refptr<base::SingleThreadTaskRunner> freezable_task_runner, - scoped_refptr<base::SingleThreadTaskRunner> unfreezable_task_runner) + scoped_refptr<base::SingleThreadTaskRunner> unfreezable_task_runner, + WebBackForwardCacheLoaderHelper back_forward_cache_loader_helper) override { WrappedResourceRequest wrapped(request); return url_loader_factory_->CreateURLLoader( @@ -40,7 +42,9 @@ class TestLoaderFactory : public ResourceFetcher::LoaderFactory { scheduler::WebResourceLoadingTaskRunnerHandle::CreateUnprioritized( std::move(freezable_task_runner)), scheduler::WebResourceLoadingTaskRunnerHandle::CreateUnprioritized( - std::move(unfreezable_task_runner))); + std::move(unfreezable_task_runner)), + /*keep_alive_handle=*/mojo::NullRemote(), + back_forward_cache_loader_helper); } std::unique_ptr<WebCodeCacheLoader> CreateCodeCacheLoader() override { 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 index f4b9ec2c790..2b3c3aa0411 100644 --- 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 @@ -4,6 +4,7 @@ #include "third_party/blink/renderer/platform/loader/testing/web_url_loader_factory_with_mock.h" +#include "third_party/blink/public/platform/web_back_forward_cache_loader_helper.h" #include "third_party/blink/public/platform/web_url_loader.h" #include "third_party/blink/public/platform/web_url_loader_mock_factory.h" @@ -18,7 +19,9 @@ WebURLLoaderFactoryWithMock::~WebURLLoaderFactoryWithMock() = default; std::unique_ptr<WebURLLoader> WebURLLoaderFactoryWithMock::CreateURLLoader( const WebURLRequest& request, std::unique_ptr<blink::scheduler::WebResourceLoadingTaskRunnerHandle>, - std::unique_ptr<blink::scheduler::WebResourceLoadingTaskRunnerHandle>) { + std::unique_ptr<blink::scheduler::WebResourceLoadingTaskRunnerHandle>, + CrossVariantMojoRemote<blink::mojom::KeepAliveHandleInterfaceBase>, + WebBackForwardCacheLoaderHelper) { return mock_factory_->CreateURLLoader(); } 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 index 2a7ad5f5c3e..769ae0dd84a 100644 --- 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 @@ -22,8 +22,9 @@ class WebURLLoaderFactoryWithMock : public WebURLLoaderFactory { std::unique_ptr<WebURLLoader> CreateURLLoader( const WebURLRequest&, std::unique_ptr<blink::scheduler::WebResourceLoadingTaskRunnerHandle>, - std::unique_ptr<blink::scheduler::WebResourceLoadingTaskRunnerHandle>) - override; + std::unique_ptr<blink::scheduler::WebResourceLoadingTaskRunnerHandle>, + CrossVariantMojoRemote<blink::mojom::KeepAliveHandleInterfaceBase>, + WebBackForwardCacheLoaderHelper) override; private: // Not owned. The mock factory should outlive |this|. diff --git a/chromium/third_party/blink/renderer/platform/loader/web_url_request_extra_data.cc b/chromium/third_party/blink/renderer/platform/loader/web_url_request_extra_data.cc index dffa242e974..9a341b2a439 100644 --- a/chromium/third_party/blink/renderer/platform/loader/web_url_request_extra_data.cc +++ b/chromium/third_party/blink/renderer/platform/loader/web_url_request_extra_data.cc @@ -19,7 +19,6 @@ void WebURLRequestExtraData::CopyToResourceRequest( request->is_main_frame = is_main_frame_; request->transition_type = transition_type_; request->originated_from_service_worker = originated_from_service_worker_; - request->force_ignore_site_for_cookies = force_ignore_site_for_cookies_; } } // namespace blink diff --git a/chromium/third_party/blink/renderer/platform/loader/web_url_request_util.cc b/chromium/third_party/blink/renderer/platform/loader/web_url_request_util.cc index d89f39a78f8..f6f6459b6a8 100644 --- a/chromium/third_party/blink/renderer/platform/loader/web_url_request_util.cc +++ b/chromium/third_party/blink/renderer/platform/loader/web_url_request_util.cc @@ -15,15 +15,17 @@ #include "services/network/public/mojom/data_pipe_getter.mojom-blink.h" #include "third_party/blink/public/mojom/blob/blob_registry.mojom-blink.h" #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h" +#include "third_party/blink/public/mojom/loader/mixed_content.mojom-blink.h" #include "third_party/blink/public/mojom/loader/resource_load_info.mojom-blink.h" +#include "third_party/blink/public/platform/cross_variant_mojo_util.h" #include "third_party/blink/public/platform/file_path_conversion.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/web_data.h" #include "third_party/blink/public/platform/web_http_body.h" #include "third_party/blink/public/platform/web_http_header_visitor.h" -#include "third_party/blink/public/platform/web_mixed_content.h" #include "third_party/blink/public/platform/web_string.h" #include "third_party/blink/public/platform/web_url_request.h" +#include "third_party/blink/renderer/platform/loader/mixed_content.h" #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" #include "third_party/blink/renderer/platform/wtf/text/string_builder.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" @@ -75,29 +77,31 @@ WebHTTPBody GetWebHTTPBodyForRequestBody( http_body.SetContainsPasswordData(input.contains_sensitive_info()); for (auto& element : *input.elements()) { switch (element.type()) { - case network::mojom::blink::DataElementType::kBytes: + case network::DataElement::Tag::kBytes: { + const auto& bytes = element.As<network::DataElementBytes>().bytes(); http_body.AppendData( - WebData(element.bytes(), SafeCast<size_t>(element.length()))); + WebData(reinterpret_cast<const char*>(bytes.data()), bytes.size())); break; - case network::mojom::blink::DataElementType::kFile: { + } + case network::DataElement::Tag::kFile: { + const auto& file = element.As<network::DataElementFile>(); base::Optional<base::Time> modification_time; - if (!element.expected_modification_time().is_null()) - modification_time = element.expected_modification_time(); + if (!file.expected_modification_time().is_null()) + modification_time = file.expected_modification_time(); http_body.AppendFileRange( - FilePathToWebString(element.path()), element.offset(), - (element.length() != std::numeric_limits<uint64_t>::max()) - ? element.length() + FilePathToWebString(file.path()), file.offset(), + (file.length() != std::numeric_limits<uint64_t>::max()) + ? file.length() : -1, modification_time); break; } - case network::mojom::blink::DataElementType::kDataPipe: { - http_body.AppendDataPipe(element.CloneDataPipeGetter()); + case network::DataElement::Tag::kDataPipe: { + http_body.AppendDataPipe( + element.As<network::DataElementDataPipe>().CloneDataPipeGetter()); break; } - case network::mojom::blink::DataElementType::kUnknown: - case network::mojom::blink::DataElementType::kChunkedDataPipe: - case network::mojom::blink::DataElementType::kReadOnceStream: + case network::DataElement::Tag::kChunkedDataPipe: NOTREACHED(); break; } @@ -148,24 +152,19 @@ scoped_refptr<network::ResourceRequestBody> GetRequestBodyForWebHTTPBody( case HTTPBodyElementType::kTypeBlob: { DCHECK(element.optional_blob); mojo::Remote<mojom::blink::Blob> blob_remote( - mojo::PendingRemote<mojom::blink::Blob>( - std::move(element.optional_blob))); + std::move(element.optional_blob)); mojo::PendingRemote<network::mojom::blink::DataPipeGetter> data_pipe_getter_remote; blob_remote->AsDataPipeGetter( data_pipe_getter_remote.InitWithNewPipeAndPassReceiver()); request_body->AppendDataPipe( - mojo::PendingRemote<network::mojom::DataPipeGetter>( - data_pipe_getter_remote.PassPipe(), 0u)); + ToCrossVariantMojoType(std::move(data_pipe_getter_remote))); break; } case HTTPBodyElementType::kTypeDataPipe: { - // Convert the raw message pipe to - // mojo::Remote<network::mojom::DataPipeGetter> data_pipe_getter. mojo::Remote<network::mojom::blink::DataPipeGetter> data_pipe_getter( - mojo::PendingRemote<network::mojom::blink::DataPipeGetter>( - std::move(element.data_pipe_getter))); + std::move(element.data_pipe_getter)); // Set the cloned DataPipeGetter to the output |request_body|, while // keeping the original message pipe back in the input |httpBody|. This @@ -175,8 +174,7 @@ scoped_refptr<network::ResourceRequestBody> GetRequestBodyForWebHTTPBody( cloned_getter; data_pipe_getter->Clone(cloned_getter.InitWithNewPipeAndPassReceiver()); request_body->AppendDataPipe( - mojo::PendingRemote<network::mojom::DataPipeGetter>( - cloned_getter.PassPipe(), 0u)); + ToCrossVariantMojoType(std::move(cloned_getter))); element.data_pipe_getter = data_pipe_getter.Unbind(); break; } @@ -199,10 +197,10 @@ network::mojom::blink::RequestDestination GetRequestDestinationForWebURLRequest( request.GetRequestDestination()); } -WebMixedContentContextType GetMixedContentContextTypeForWebURLRequest( - const WebURLRequest& request) { - return WebMixedContent::ContextTypeFromRequestContext( - request.GetRequestContext(), WebMixedContent::CheckModeForPlugin::kLax); +mojom::blink::MixedContentContextType +GetMixedContentContextTypeForWebURLRequest(const WebURLRequest& request) { + return MixedContent::ContextTypeFromRequestContext( + request.GetRequestContext(), MixedContent::CheckModeForPlugin::kLax); } } // namespace blink |