| /* |
| * Copyright (C) 2013 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "third_party/blink/renderer/core/loader/frame_fetch_context.h" |
| |
| #include <algorithm> |
| #include <memory> |
| |
| #include "base/feature_list.h" |
| #include "base/optional.h" |
| #include "build/build_config.h" |
| #include "mojo/public/cpp/bindings/associated_remote.h" |
| #include "net/http/structured_headers.h" |
| #include "services/network/public/mojom/web_client_hints_types.mojom-blink.h" |
| #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" |
| #include "third_party/blink/public/common/client_hints/client_hints.h" |
| #include "third_party/blink/public/common/device_memory/approximated_device_memory.h" |
| #include "third_party/blink/public/common/features.h" |
| #include "third_party/blink/public/mojom/conversions/conversions.mojom-blink.h" |
| #include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom-blink.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/platform/modules/service_worker/web_service_worker_network_provider.h" |
| #include "third_party/blink/public/platform/scheduler/web_scoped_virtual_time_pauser.h" |
| #include "third_party/blink/public/platform/web_content_settings_client.h" |
| #include "third_party/blink/public/platform/web_effective_connection_type.h" |
| #include "third_party/blink/public/platform/websocket_handshake_throttle.h" |
| #include "third_party/blink/public/web/web_frame.h" |
| #include "third_party/blink/public/web/web_local_frame.h" |
| #include "third_party/blink/public/web/web_local_frame_client.h" |
| #include "third_party/blink/renderer/bindings/core/v8/script_controller.h" |
| #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" |
| #include "third_party/blink/renderer/core/dom/document.h" |
| #include "third_party/blink/renderer/core/fileapi/public_url_manager.h" |
| #include "third_party/blink/renderer/core/frame/ad_tracker.h" |
| #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h" |
| #include "third_party/blink/renderer/core/frame/deprecation.h" |
| #include "third_party/blink/renderer/core/frame/frame_console.h" |
| #include "third_party/blink/renderer/core/frame/local_dom_window.h" |
| #include "third_party/blink/renderer/core/frame/local_frame.h" |
| #include "third_party/blink/renderer/core/frame/local_frame_client.h" |
| #include "third_party/blink/renderer/core/frame/local_frame_view.h" |
| #include "third_party/blink/renderer/core/frame/navigator.h" |
| #include "third_party/blink/renderer/core/frame/settings.h" |
| #include "third_party/blink/renderer/core/html/html_frame_owner_element.h" |
| #include "third_party/blink/renderer/core/html/imports/html_imports_controller.h" |
| #include "third_party/blink/renderer/core/inspector/console_message.h" |
| #include "third_party/blink/renderer/core/inspector/identifiers_factory.h" |
| #include "third_party/blink/renderer/core/inspector/inspector_trace_events.h" |
| #include "third_party/blink/renderer/core/loader/appcache/application_cache_host.h" |
| #include "third_party/blink/renderer/core/loader/back_forward_cache_loader_helper_for_frame.h" |
| #include "third_party/blink/renderer/core/loader/document_loader.h" |
| #include "third_party/blink/renderer/core/loader/frame_loader.h" |
| #include "third_party/blink/renderer/core/loader/frame_resource_fetcher_properties.h" |
| #include "third_party/blink/renderer/core/loader/idleness_detector.h" |
| #include "third_party/blink/renderer/core/loader/interactive_detector.h" |
| #include "third_party/blink/renderer/core/loader/loader_factory_for_frame.h" |
| #include "third_party/blink/renderer/core/loader/mixed_content_checker.h" |
| #include "third_party/blink/renderer/core/loader/resource_load_observer_for_frame.h" |
| #include "third_party/blink/renderer/core/loader/subresource_filter.h" |
| #include "third_party/blink/renderer/core/page/page.h" |
| #include "third_party/blink/renderer/core/paint/first_meaningful_paint_detector.h" |
| #include "third_party/blink/renderer/core/probe/core_probes.h" |
| #include "third_party/blink/renderer/core/svg/graphics/svg_image_chrome_client.h" |
| #include "third_party/blink/renderer/core/timing/dom_window_performance.h" |
| #include "third_party/blink/renderer/core/timing/performance.h" |
| #include "third_party/blink/renderer/core/timing/window_performance.h" |
| #include "third_party/blink/renderer/core/url/url_search_params.h" |
| #include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h" |
| #include "third_party/blink/renderer/platform/exported/wrapped_resource_request.h" |
| #include "third_party/blink/renderer/platform/heap/heap.h" |
| #include "third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h" |
| #include "third_party/blink/renderer/platform/instrumentation/use_counter.h" |
| #include "third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.h" |
| #include "third_party/blink/renderer/platform/loader/fetch/detachable_use_counter.h" |
| #include "third_party/blink/renderer/platform/loader/fetch/resource.h" |
| #include "third_party/blink/renderer/platform/loader/fetch/resource_load_priority.h" |
| #include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h" |
| #include "third_party/blink/renderer/platform/loader/fetch/resource_loading_log.h" |
| #include "third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h" |
| #include "third_party/blink/renderer/platform/loader/fetch/unique_identifier.h" |
| #include "third_party/blink/renderer/platform/mhtml/mhtml_archive.h" |
| #include "third_party/blink/renderer/platform/network/http_names.h" |
| #include "third_party/blink/renderer/platform/network/network_state_notifier.h" |
| #include "third_party/blink/renderer/platform/network/network_utils.h" |
| #include "third_party/blink/renderer/platform/weborigin/scheme_registry.h" |
| #include "third_party/blink/renderer/platform/wtf/text/string_builder.h" |
| #include "third_party/blink/renderer/platform/wtf/vector.h" |
| |
| namespace blink { |
| |
| namespace { |
| |
| // Client hints sent to third parties are controlled through two mechanisms, |
| // based on the state of the experimental flag "FeaturePolicyForClientHints". |
| // |
| // If that flag is disabled (the default), then all hints are always sent for |
| // first-party subresources, and the kAllowClientHintsToThirdParty feature |
| // controls whether some specific hints are sent to third parties. (Only |
| // device-memory, resource-width, viewport-width and DPR are sent under this |
| // model). This feature is enabled by default on Android, and disabled by |
| // default on all other platforms. |
| // |
| // When the runtime flag is enabled, all client hints except UA are controlled |
| // entirely by feature policy on all platforms. In that case, hints will |
| // generally be sent for first-party resources, and not for third-party |
| // resources, unless specifically enabled by policy. |
| |
| // Determines FetchCacheMode for |frame|. This FetchCacheMode should be a base |
| // policy to consider one of each resource belonging to the frame, and should |
| // not count resource specific conditions in. |
| mojom::FetchCacheMode DetermineFrameCacheMode(Frame* frame) { |
| if (!frame) |
| return mojom::FetchCacheMode::kDefault; |
| auto* local_frame = DynamicTo<LocalFrame>(frame); |
| if (!local_frame) |
| return DetermineFrameCacheMode(frame->Tree().Parent()); |
| |
| // Does not propagate cache policy for subresources after the load event. |
| // TODO(toyoshim): We should be able to remove following parents' policy check |
| // if each frame has a relevant WebFrameLoadType for reload and history |
| // navigations. |
| if (local_frame->GetDocument()->LoadEventFinished()) |
| return mojom::FetchCacheMode::kDefault; |
| |
| // Respects BypassingCache rather than parent's policy. |
| WebFrameLoadType load_type = |
| local_frame->Loader().GetDocumentLoader()->LoadType(); |
| if (load_type == WebFrameLoadType::kReloadBypassingCache) |
| return mojom::FetchCacheMode::kBypassCache; |
| |
| // Respects parent's policy if it has a special one. |
| mojom::FetchCacheMode parent_cache_mode = |
| DetermineFrameCacheMode(frame->Tree().Parent()); |
| if (parent_cache_mode != mojom::FetchCacheMode::kDefault) |
| return parent_cache_mode; |
| |
| // Otherwise, follows WebFrameLoadType. |
| switch (load_type) { |
| case WebFrameLoadType::kStandard: |
| case WebFrameLoadType::kReplaceCurrentItem: |
| return mojom::FetchCacheMode::kDefault; |
| case WebFrameLoadType::kBackForward: |
| // Mutates the policy for POST requests to avoid form resubmission. |
| return mojom::FetchCacheMode::kForceCache; |
| case WebFrameLoadType::kReload: |
| return mojom::FetchCacheMode::kDefault; |
| case WebFrameLoadType::kReloadBypassingCache: |
| return mojom::FetchCacheMode::kBypassCache; |
| } |
| NOTREACHED(); |
| return mojom::FetchCacheMode::kDefault; |
| } |
| |
| } // namespace |
| |
| struct FrameFetchContext::FrozenState final : GarbageCollected<FrozenState> { |
| FrozenState(const KURL& url, |
| scoped_refptr<const SecurityOrigin> parent_security_origin, |
| ContentSecurityPolicy* content_security_policy, |
| net::SiteForCookies site_for_cookies, |
| scoped_refptr<const SecurityOrigin> top_frame_origin, |
| const ClientHintsPreferences& client_hints_preferences, |
| float device_pixel_ratio, |
| const String& user_agent, |
| const base::Optional<UserAgentMetadata>& user_agent_metadata, |
| bool is_svg_image_chrome_client, |
| bool is_prerendering) |
| : url(url), |
| parent_security_origin(std::move(parent_security_origin)), |
| content_security_policy(content_security_policy), |
| site_for_cookies(std::move(site_for_cookies)), |
| top_frame_origin(std::move(top_frame_origin)), |
| client_hints_preferences(client_hints_preferences), |
| device_pixel_ratio(device_pixel_ratio), |
| user_agent(user_agent), |
| user_agent_metadata(user_agent_metadata), |
| is_svg_image_chrome_client(is_svg_image_chrome_client), |
| is_prerendering(is_prerendering) {} |
| |
| const KURL url; |
| const scoped_refptr<const SecurityOrigin> parent_security_origin; |
| const Member<ContentSecurityPolicy> content_security_policy; |
| const net::SiteForCookies site_for_cookies; |
| const scoped_refptr<const SecurityOrigin> top_frame_origin; |
| const ClientHintsPreferences client_hints_preferences; |
| const float device_pixel_ratio; |
| const String user_agent; |
| const base::Optional<UserAgentMetadata> user_agent_metadata; |
| const bool is_svg_image_chrome_client; |
| const bool is_prerendering; |
| |
| void Trace(Visitor* visitor) const { |
| visitor->Trace(content_security_policy); |
| } |
| }; |
| |
| ResourceFetcher* FrameFetchContext::CreateFetcherForCommittedDocument( |
| DocumentLoader& loader, |
| Document& document) { |
| auto& properties = *MakeGarbageCollected<DetachableResourceFetcherProperties>( |
| *MakeGarbageCollected<FrameResourceFetcherProperties>(loader, document)); |
| LocalFrame* frame = document.GetFrame(); |
| DCHECK(frame); |
| auto* frame_fetch_context = |
| MakeGarbageCollected<FrameFetchContext>(loader, document, properties); |
| ResourceFetcherInit init( |
| properties, frame_fetch_context, |
| frame->GetTaskRunner(TaskType::kNetworking), |
| frame->GetTaskRunner(TaskType::kNetworkingUnfreezable), |
| MakeGarbageCollected<LoaderFactoryForFrame>(loader, *frame->DomWindow()), |
| frame->DomWindow(), |
| MakeGarbageCollected<BackForwardCacheLoaderHelperForFrame>(*frame)); |
| init.use_counter = |
| MakeGarbageCollected<DetachableUseCounter>(frame->DomWindow()); |
| init.console_logger = MakeGarbageCollected<DetachableConsoleLogger>( |
| document.GetExecutionContext()); |
| // Frame loading should normally start with |kTight| throttling, as the |
| // frame will be in layout-blocking state until the <body> tag is inserted |
| init.initial_throttling_policy = |
| ResourceLoadScheduler::ThrottlingPolicy::kTight; |
| init.frame_or_worker_scheduler = frame->GetFrameScheduler(); |
| init.archive = loader.Archive(); |
| init.loading_behavior_observer = frame_fetch_context; |
| ResourceFetcher* fetcher = MakeGarbageCollected<ResourceFetcher>(init); |
| fetcher->SetResourceLoadObserver( |
| MakeGarbageCollected<ResourceLoadObserverForFrame>( |
| loader, document, fetcher->GetProperties())); |
| fetcher->SetImagesEnabled(frame->GetSettings()->GetImagesEnabled()); |
| fetcher->SetAutoLoadImages( |
| frame->GetSettings()->GetLoadsImagesAutomatically()); |
| return fetcher; |
| } |
| |
| FrameFetchContext::FrameFetchContext( |
| DocumentLoader& document_loader, |
| Document& document, |
| const DetachableResourceFetcherProperties& properties) |
| : BaseFetchContext(properties), |
| document_loader_(document_loader), |
| document_(document), |
| save_data_enabled_( |
| GetNetworkStateNotifier().SaveDataEnabled() && |
| !GetFrame()->GetSettings()->GetDataSaverHoldbackWebApi()) {} |
| |
| net::SiteForCookies FrameFetchContext::GetSiteForCookies() const { |
| if (GetResourceFetcherProperties().IsDetached()) |
| return frozen_state_->site_for_cookies; |
| return document_->SiteForCookies(); |
| } |
| |
| scoped_refptr<const SecurityOrigin> FrameFetchContext::GetTopFrameOrigin() |
| const { |
| if (GetResourceFetcherProperties().IsDetached()) |
| return frozen_state_->top_frame_origin; |
| return document_->TopFrameOrigin(); |
| } |
| |
| SubresourceFilter* FrameFetchContext::GetSubresourceFilter() const { |
| if (GetResourceFetcherProperties().IsDetached()) |
| return nullptr; |
| return document_loader_->GetSubresourceFilter(); |
| } |
| |
| PreviewsState FrameFetchContext::previews_state() const { |
| if (GetResourceFetcherProperties().IsDetached()) |
| return PreviewsTypes::kPreviewsUnspecified; |
| return document_loader_->GetPreviewsState(); |
| } |
| |
| LocalFrame* FrameFetchContext::GetFrame() const { |
| return document_->GetFrame(); |
| } |
| |
| LocalFrameClient* FrameFetchContext::GetLocalFrameClient() const { |
| return GetFrame()->Client(); |
| } |
| |
| void FrameFetchContext::AddAdditionalRequestHeaders(ResourceRequest& request) { |
| // The remaining modifications are only necessary for HTTP and HTTPS. |
| if (!request.Url().IsEmpty() && !request.Url().ProtocolIsInHTTPFamily()) |
| return; |
| |
| if (GetResourceFetcherProperties().IsDetached()) |
| return; |
| |
| // Reload should reflect the current data saver setting. |
| if (IsReloadLoadType(document_loader_->LoadType())) |
| request.ClearHttpHeaderField(http_names::kSaveData); |
| |
| if (save_data_enabled_) |
| request.SetHttpHeaderField(http_names::kSaveData, "on"); |
| |
| AddBackForwardCacheExperimentHTTPHeaderIfNeeded( |
| document_->GetExecutionContext(), request); |
| } |
| |
| // TODO(toyoshim, arthursonzogni): PlzNavigate doesn't use this function to set |
| // the ResourceRequest's cache policy. The cache policy determination needs to |
| // be factored out from FrameFetchContext and moved to the FrameLoader for |
| // instance. |
| mojom::FetchCacheMode FrameFetchContext::ResourceRequestCachePolicy( |
| const ResourceRequest& request, |
| ResourceType type, |
| FetchParameters::DeferOption defer) const { |
| if (GetResourceFetcherProperties().IsDetached()) |
| return mojom::FetchCacheMode::kDefault; |
| |
| DCHECK(GetFrame()); |
| const auto cache_mode = DetermineFrameCacheMode(GetFrame()); |
| |
| // TODO(toyoshim): Revisit to consider if this clause can be merged to |
| // determineWebCachePolicy or determineFrameCacheMode. |
| if (cache_mode == mojom::FetchCacheMode::kDefault && |
| request.IsConditional()) { |
| return mojom::FetchCacheMode::kValidateCache; |
| } |
| return cache_mode; |
| } |
| |
| void FrameFetchContext::PrepareRequest( |
| ResourceRequest& request, |
| ResourceLoaderOptions& options, |
| WebScopedVirtualTimePauser& virtual_time_pauser, |
| ResourceType resource_type) { |
| // TODO(yhirano): Clarify which statements are actually needed when |
| // this is called during redirect. |
| const bool for_redirect = request.GetRedirectInfo().has_value(); |
| |
| SetFirstPartyCookie(request); |
| if (request.GetRequestContext() == |
| mojom::blink::RequestContextType::SERVICE_WORKER) { |
| // The top frame origin is defined to be null for service worker main |
| // resource requests. |
| DCHECK(!request.TopFrameOrigin()); |
| } else { |
| request.SetTopFrameOrigin(GetTopFrameOrigin()); |
| } |
| |
| String user_agent = GetUserAgent(); |
| request.SetHTTPUserAgent(AtomicString(user_agent)); |
| |
| if (GetResourceFetcherProperties().IsDetached()) |
| return; |
| |
| if (document_loader_->ForceFetchCacheMode()) |
| request.SetCacheMode(*document_loader_->ForceFetchCacheMode()); |
| |
| if (request.GetPreviewsState() == PreviewsTypes::kPreviewsUnspecified) { |
| PreviewsState request_previews_state = document_loader_->GetPreviewsState(); |
| if (request_previews_state == PreviewsTypes::kPreviewsUnspecified) |
| request_previews_state = PreviewsTypes::kPreviewsOff; |
| request.SetPreviewsState(request_previews_state); |
| } |
| |
| GetLocalFrameClient()->DispatchWillSendRequest(request); |
| FrameScheduler* frame_scheduler = GetFrame()->GetFrameScheduler(); |
| if (!for_redirect && frame_scheduler) { |
| virtual_time_pauser = frame_scheduler->CreateWebScopedVirtualTimePauser( |
| request.Url().GetString(), |
| WebScopedVirtualTimePauser::VirtualTaskDuration::kNonInstant); |
| } |
| |
| probe::PrepareRequest(Probe(), document_loader_, request, options, |
| resource_type); |
| |
| // ServiceWorker hook ups. |
| if (document_loader_->GetServiceWorkerNetworkProvider()) { |
| WrappedResourceRequest webreq(request); |
| document_loader_->GetServiceWorkerNetworkProvider()->WillSendRequest( |
| webreq); |
| } |
| } |
| |
| void FrameFetchContext::AddResourceTiming(const ResourceTimingInfo& info) { |
| // Normally, |document_| is cleared on Document shutdown. However, Documents |
| // for HTML imports will also not have a LocalFrame set: in that case, also |
| // early return, as there is nothing to report the resource timing to. |
| if (GetResourceFetcherProperties().IsDetached()) |
| return; |
| |
| // Timing for main resource is handled in DocumentLoader. |
| // All other resources are reported to the corresponding Document. |
| DOMWindowPerformance::performance(*document_->domWindow()) |
| ->GenerateAndAddResourceTiming(info); |
| } |
| |
| bool FrameFetchContext::AllowImage(bool images_enabled, const KURL& url) const { |
| if (GetResourceFetcherProperties().IsDetached()) |
| return true; |
| if (auto* settings_client = GetContentSettingsClient()) |
| images_enabled = settings_client->AllowImage(images_enabled, url); |
| return images_enabled; |
| } |
| |
| void FrameFetchContext::ModifyRequestForCSP(ResourceRequest& resource_request) { |
| if (GetResourceFetcherProperties().IsDetached()) |
| return; |
| |
| GetFrame()->Loader().ModifyRequestForCSP( |
| resource_request, |
| &GetResourceFetcherProperties().GetFetchClientSettingsObject(), |
| document_->domWindow(), mojom::blink::RequestContextFrameType::kNone); |
| } |
| |
| void FrameFetchContext::AddClientHintsIfNecessary( |
| const ClientHintsPreferences& hints_preferences, |
| const FetchParameters::ResourceWidth& resource_width, |
| ResourceRequest& request) { |
| // If the feature is enabled, then client hints are allowed only on secure |
| // URLs. |
| if (!ClientHintsPreferences::IsClientHintsAllowed(request.Url())) |
| return; |
| |
| // Check if |url| is allowed to run JavaScript. If not, client hints are not |
| // attached to the requests that initiate on the render side. |
| if (!AllowScriptFromSourceWithoutNotifying(request.Url())) |
| return; |
| |
| // When the runtime flag "FeaturePolicyForClientHints" is enabled, feature |
| // policy is used to enable hints for all subresources, based on the policy of |
| // the requesting document, and the origin of the resource. |
| const FeaturePolicy* policy = |
| document_ |
| ? document_->domWindow()->GetSecurityContext().GetFeaturePolicy() |
| : nullptr; |
| |
| url::Origin resource_origin = |
| SecurityOrigin::Create(request.Url())->ToUrlOrigin(); |
| bool is_1p_origin = IsFirstPartyOrigin(request.Url()); |
| |
| base::Optional<UserAgentMetadata> ua = GetUserAgentMetadata(); |
| |
| base::Optional<ClientHintImageInfo> image_info; |
| base::Optional<WTF::AtomicString> lang; |
| |
| if (document_) { // Only get frame info if the frame is not detached |
| image_info = ClientHintImageInfo(); |
| image_info->dpr = GetDevicePixelRatio(); |
| image_info->resource_width = resource_width; |
| if (!GetResourceFetcherProperties().IsDetached() && GetFrame()->View()) |
| image_info->viewport_width = GetFrame()->View()->ViewportWidth(); |
| |
| lang = GetFrame() |
| ->DomWindow() |
| ->navigator() |
| ->SerializeLanguagesForClientHintHeader(); |
| |
| // TODO(crbug.com/1151050): |SerializeLanguagesForClientHintHeader| getter |
| // affects later calls if there is a DevTools override. The following blink |
| // test fails unless set to "dirty" to manually reset languages: |
| // |
| // http/tests/inspector-protocol/emulation/emulation-user-agent-override.js |
| GetFrame()->DomWindow()->navigator()->SetLanguagesDirty(); |
| } |
| |
| // |hints_preferences| is used only in case of the preload scanner; |
| // GetClientHintsPreferences() has things parsed for this document |
| // by browser (from accept-ch header on this response or previously persisted) |
| // with renderer-parsed http-equiv merged in. |
| ClientHintsPreferences prefs; |
| prefs.CombineWith(hints_preferences); |
| prefs.CombineWith(GetClientHintsPreferences()); |
| |
| BaseFetchContext::AddClientHintsIfNecessary(prefs, resource_origin, |
| is_1p_origin, ua, policy, |
| image_info, lang, request); |
| } |
| |
| void FrameFetchContext::PopulateResourceRequest( |
| ResourceType type, |
| const ClientHintsPreferences& hints_preferences, |
| const FetchParameters::ResourceWidth& resource_width, |
| ResourceRequest& request, |
| const ResourceLoaderOptions& options) { |
| if (!GetResourceFetcherProperties().IsDetached()) |
| probe::SetDevToolsIds(Probe(), request, options.initiator_info); |
| |
| ModifyRequestForCSP(request); |
| AddClientHintsIfNecessary(hints_preferences, resource_width, request); |
| |
| const ContentSecurityPolicy* csp = |
| GetContentSecurityPolicyForWorld(options.world_for_csp.get()); |
| if (csp && csp->ShouldSendCSPHeader(type)) |
| // TODO(crbug.com/993769): Test if this header returns duplicated values |
| // (i.e. "CSP: active, active") on asynchronous "stale-while-revalidate" |
| // revalidation requests and if this is unexpected behavior. |
| request.AddHttpHeaderField("CSP", "active"); |
| } |
| |
| bool FrameFetchContext::IsPrerendering() const { |
| if (GetResourceFetcherProperties().IsDetached()) |
| return frozen_state_->is_prerendering; |
| return document_->IsPrerendering(); |
| } |
| |
| void FrameFetchContext::SetFirstPartyCookie(ResourceRequest& request) { |
| // Set the first party for cookies url if it has not been set yet (new |
| // requests). This value will be updated during redirects, consistent with |
| // https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00#section-2.1.1? |
| if (!request.SiteForCookiesSet()) |
| request.SetSiteForCookies(GetSiteForCookies()); |
| } |
| |
| bool FrameFetchContext::AllowScriptFromSource(const KURL& url) const { |
| if (AllowScriptFromSourceWithoutNotifying(url)) |
| return true; |
| WebContentSettingsClient* settings_client = GetContentSettingsClient(); |
| if (settings_client) |
| settings_client->DidNotAllowScript(); |
| return false; |
| } |
| |
| bool FrameFetchContext::AllowScriptFromSourceWithoutNotifying( |
| const KURL& url) const { |
| Settings* settings = GetSettings(); |
| bool allow_script = !settings || settings->GetScriptEnabled(); |
| if (auto* settings_client = GetContentSettingsClient()) |
| allow_script = settings_client->AllowScriptFromSource(allow_script, url); |
| return allow_script; |
| } |
| |
| bool FrameFetchContext::IsFirstPartyOrigin(const KURL& url) const { |
| if (GetResourceFetcherProperties().IsDetached()) |
| return false; |
| |
| return GetFrame() |
| ->Tree() |
| .Top() |
| .GetSecurityContext() |
| ->GetSecurityOrigin() |
| ->IsSameOriginWith(SecurityOrigin::Create(url).get()); |
| } |
| |
| bool FrameFetchContext::ShouldBlockRequestByInspector(const KURL& url) const { |
| if (GetResourceFetcherProperties().IsDetached()) |
| return false; |
| bool should_block_request = false; |
| probe::ShouldBlockRequest(Probe(), url, &should_block_request); |
| return should_block_request; |
| } |
| |
| void FrameFetchContext::DispatchDidBlockRequest( |
| const ResourceRequest& resource_request, |
| const FetchInitiatorInfo& fetch_initiator_info, |
| ResourceRequestBlockedReason blocked_reason, |
| ResourceType resource_type) const { |
| if (GetResourceFetcherProperties().IsDetached()) |
| return; |
| probe::DidBlockRequest(Probe(), resource_request, document_loader_, Url(), |
| fetch_initiator_info, blocked_reason, resource_type); |
| } |
| |
| ContentSecurityPolicy* FrameFetchContext::GetContentSecurityPolicyForWorld( |
| const DOMWrapperWorld* world) const { |
| if (GetResourceFetcherProperties().IsDetached()) |
| return frozen_state_->content_security_policy; |
| |
| return document_->GetExecutionContext()->GetContentSecurityPolicyForWorld( |
| world); |
| } |
| |
| bool FrameFetchContext::IsSVGImageChromeClient() const { |
| if (GetResourceFetcherProperties().IsDetached()) |
| return frozen_state_->is_svg_image_chrome_client; |
| |
| return GetFrame()->GetChromeClient().IsSVGImageChromeClient(); |
| } |
| |
| void FrameFetchContext::CountUsage(WebFeature feature) const { |
| if (GetResourceFetcherProperties().IsDetached()) |
| return; |
| document_loader_->GetUseCounterHelper().Count(feature, GetFrame()); |
| } |
| |
| void FrameFetchContext::CountDeprecation(WebFeature feature) const { |
| if (GetResourceFetcherProperties().IsDetached()) |
| return; |
| Deprecation::CountDeprecation(document_->domWindow(), feature); |
| } |
| |
| bool FrameFetchContext::ShouldBlockWebSocketByMixedContentCheck( |
| const KURL& url) const { |
| if (GetResourceFetcherProperties().IsDetached()) { |
| // TODO(yhirano): Implement the detached case. |
| return false; |
| } |
| return !MixedContentChecker::IsWebSocketAllowed(*this, GetFrame(), url); |
| } |
| |
| std::unique_ptr<WebSocketHandshakeThrottle> |
| FrameFetchContext::CreateWebSocketHandshakeThrottle() { |
| if (GetResourceFetcherProperties().IsDetached()) { |
| // TODO(yhirano): Implement the detached case. |
| return nullptr; |
| } |
| if (!GetFrame()) |
| return nullptr; |
| return WebFrame::FromCoreFrame(GetFrame()) |
| ->ToWebLocalFrame() |
| ->Client() |
| ->CreateWebSocketHandshakeThrottle(); |
| } |
| |
| bool FrameFetchContext::ShouldBlockFetchByMixedContentCheck( |
| mojom::blink::RequestContextType request_context, |
| const base::Optional<ResourceRequest::RedirectInfo>& redirect_info, |
| const KURL& url, |
| ReportingDisposition reporting_disposition, |
| const base::Optional<String>& devtools_id) const { |
| if (GetResourceFetcherProperties().IsDetached()) { |
| // TODO(yhirano): Implement the detached case. |
| return false; |
| } |
| const KURL& url_before_redirects = |
| redirect_info ? redirect_info->original_url : url; |
| ResourceRequest::RedirectStatus redirect_status = |
| redirect_info ? RedirectStatus::kFollowedRedirect |
| : RedirectStatus::kNoRedirect; |
| return MixedContentChecker::ShouldBlockFetch( |
| GetFrame(), request_context, url_before_redirects, redirect_status, url, |
| devtools_id, reporting_disposition, |
| document_loader_->GetContentSecurityNotifier()); |
| } |
| |
| bool FrameFetchContext::ShouldBlockFetchAsCredentialedSubresource( |
| const ResourceRequest& resource_request, |
| const KURL& url) const { |
| // URLs with no embedded credentials should load correctly. |
| if (url.User().IsEmpty() && url.Pass().IsEmpty()) |
| return false; |
| |
| if (resource_request.GetRequestContext() == |
| mojom::blink::RequestContextType::XML_HTTP_REQUEST) { |
| return false; |
| } |
| |
| // Relative URLs on top-level pages that were loaded with embedded credentials |
| // should load correctly. |
| // TODO(mkwst): This doesn't work when the subresource is an iframe. |
| // See https://crbug.com/756846. |
| if (Url().User() == url.User() && Url().Pass() == url.Pass() && |
| SecurityOrigin::Create(url)->IsSameOriginWith( |
| GetResourceFetcherProperties() |
| .GetFetchClientSettingsObject() |
| .GetSecurityOrigin())) { |
| return false; |
| } |
| |
| CountDeprecation(WebFeature::kRequestedSubresourceWithEmbeddedCredentials); |
| |
| // TODO(mkwst): Remove the runtime check one way or the other once we're |
| // sure it's going to stick (or that it's not). |
| return RuntimeEnabledFeatures::BlockCredentialedSubresourcesEnabled(); |
| } |
| |
| const KURL& FrameFetchContext::Url() const { |
| if (GetResourceFetcherProperties().IsDetached()) |
| return frozen_state_->url; |
| return document_->Url(); |
| } |
| |
| const SecurityOrigin* FrameFetchContext::GetParentSecurityOrigin() const { |
| if (GetResourceFetcherProperties().IsDetached()) |
| return frozen_state_->parent_security_origin.get(); |
| Frame* parent = GetFrame()->Tree().Parent(); |
| if (!parent) |
| return nullptr; |
| return parent->GetSecurityContext()->GetSecurityOrigin(); |
| } |
| |
| ContentSecurityPolicy* FrameFetchContext::GetContentSecurityPolicy() const { |
| if (GetResourceFetcherProperties().IsDetached()) |
| return frozen_state_->content_security_policy; |
| return document_->domWindow()->GetContentSecurityPolicy(); |
| } |
| |
| void FrameFetchContext::AddConsoleMessage(ConsoleMessage* message) const { |
| if (GetResourceFetcherProperties().IsDetached()) |
| return; |
| |
| document_->AddConsoleMessage(message); |
| } |
| |
| WebContentSettingsClient* FrameFetchContext::GetContentSettingsClient() const { |
| if (GetResourceFetcherProperties().IsDetached()) |
| return nullptr; |
| return GetFrame()->GetContentSettingsClient(); |
| } |
| |
| Settings* FrameFetchContext::GetSettings() const { |
| if (GetResourceFetcherProperties().IsDetached()) |
| return nullptr; |
| DCHECK(GetFrame()); |
| return GetFrame()->GetSettings(); |
| } |
| |
| String FrameFetchContext::GetUserAgent() const { |
| if (GetResourceFetcherProperties().IsDetached()) |
| return frozen_state_->user_agent; |
| return GetFrame()->Loader().UserAgent(); |
| } |
| |
| base::Optional<UserAgentMetadata> FrameFetchContext::GetUserAgentMetadata() |
| const { |
| if (GetResourceFetcherProperties().IsDetached()) |
| return frozen_state_->user_agent_metadata; |
| return GetLocalFrameClient()->UserAgentMetadata(); |
| } |
| |
| const FeaturePolicy* FrameFetchContext::GetFeaturePolicy() const { |
| return document_ |
| ? document_->domWindow()->GetSecurityContext().GetFeaturePolicy() |
| : nullptr; |
| } |
| |
| const ClientHintsPreferences FrameFetchContext::GetClientHintsPreferences() |
| const { |
| if (GetResourceFetcherProperties().IsDetached()) |
| return frozen_state_->client_hints_preferences; |
| LocalFrame* frame = document_->GetFrame(); |
| DCHECK(frame); |
| return frame->GetClientHintsPreferences(); |
| } |
| |
| float FrameFetchContext::GetDevicePixelRatio() const { |
| if (GetResourceFetcherProperties().IsDetached()) |
| return frozen_state_->device_pixel_ratio; |
| return document_->DevicePixelRatio(); |
| } |
| |
| FetchContext* FrameFetchContext::Detach() { |
| if (GetResourceFetcherProperties().IsDetached()) |
| return this; |
| |
| frozen_state_ = MakeGarbageCollected<FrozenState>( |
| Url(), GetParentSecurityOrigin(), GetContentSecurityPolicy(), |
| GetSiteForCookies(), GetTopFrameOrigin(), GetClientHintsPreferences(), |
| GetDevicePixelRatio(), GetUserAgent(), GetUserAgentMetadata(), |
| IsSVGImageChromeClient(), IsPrerendering()); |
| document_loader_ = nullptr; |
| document_ = nullptr; |
| return this; |
| } |
| |
| void FrameFetchContext::Trace(Visitor* visitor) const { |
| visitor->Trace(document_loader_); |
| visitor->Trace(document_); |
| visitor->Trace(frozen_state_); |
| BaseFetchContext::Trace(visitor); |
| } |
| |
| bool FrameFetchContext::CalculateIfAdSubresource( |
| const ResourceRequestHead& resource_request, |
| const base::Optional<KURL>& alias_url, |
| ResourceType type, |
| const FetchInitiatorInfo& initiator_info) { |
| // Mark the resource as an Ad if the BaseFetchContext thinks it's an ad. |
| bool known_ad = BaseFetchContext::CalculateIfAdSubresource( |
| resource_request, alias_url, type, initiator_info); |
| if (GetResourceFetcherProperties().IsDetached() || |
| !GetFrame()->GetAdTracker()) { |
| return known_ad; |
| } |
| |
| // The AdTracker needs to know about the request as well, and may also mark it |
| // as an ad. |
| const KURL& url = alias_url ? alias_url.value() : resource_request.Url(); |
| return GetFrame()->GetAdTracker()->CalculateIfAdSubresource( |
| document_->domWindow(), url, type, initiator_info, known_ad); |
| } |
| |
| bool FrameFetchContext::SendConversionRequestInsteadOfRedirecting( |
| const KURL& url, |
| const base::Optional<ResourceRequest::RedirectInfo>& redirect_info, |
| ReportingDisposition reporting_disposition) const { |
| if (GetResourceFetcherProperties().IsDetached()) |
| return false; |
| |
| if (!RuntimeEnabledFeatures::ConversionMeasurementEnabled( |
| document_->domWindow())) { |
| return false; |
| } |
| |
| LocalFrame* frame = document_->GetFrame(); |
| DCHECK(frame); |
| // Only register conversions pings that are redirects in the main frame. |
| if (!frame->IsMainFrame() || !redirect_info || |
| !SecurityOrigin::AreSameOrigin(url, redirect_info->previous_url)) { |
| return false; |
| } |
| |
| const char kWellKnownConversionRegsitrationPath[] = |
| "/.well-known/register-conversion"; |
| if (url.GetPath() != kWellKnownConversionRegsitrationPath) |
| return false; |
| |
| if (!document_->domWindow()->IsFeatureEnabled( |
| mojom::blink::FeaturePolicyFeature::kConversionMeasurement)) { |
| String message = |
| "The 'conversion-measurement' feature policy must be enabled to " |
| "register a conversion."; |
| document_->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>( |
| mojom::blink::ConsoleMessageSource::kOther, |
| mojom::blink::ConsoleMessageLevel::kError, message)); |
| return false; |
| } |
| |
| // Only allow conversion registration on secure pages with a secure conversion |
| // redirect. |
| scoped_refptr<const SecurityOrigin> redirect_origin = |
| SecurityOrigin::Create(url); |
| if (!GetFrame() |
| ->GetSecurityContext() |
| ->GetSecurityOrigin() |
| ->IsPotentiallyTrustworthy() || |
| !redirect_origin->IsPotentiallyTrustworthy()) { |
| return false; |
| } |
| |
| // Only report conversions for requests with reporting enabled (i.e. do not |
| // count preload requests). However, return true. |
| if (reporting_disposition == ReportingDisposition::kSuppressReporting) |
| return true; |
| |
| mojom::blink::ConversionPtr conversion = mojom::blink::Conversion::New(); |
| conversion->reporting_origin = SecurityOrigin::Create(url); |
| conversion->conversion_data = 0UL; |
| |
| const char kConversionDataParam[] = "conversion-data"; |
| URLSearchParams* search_params = URLSearchParams::Create(url.Query()); |
| if (search_params->has(kConversionDataParam)) { |
| bool is_valid_integer = false; |
| uint64_t data = search_params->get(kConversionDataParam) |
| .ToUInt64Strict(&is_valid_integer); |
| |
| // Default invalid params to 0. |
| conversion->conversion_data = is_valid_integer ? data : 0UL; |
| } |
| |
| mojo::AssociatedRemote<mojom::blink::ConversionHost> conversion_host; |
| GetFrame()->GetRemoteNavigationAssociatedInterfaces()->GetInterface( |
| &conversion_host); |
| conversion_host->RegisterConversion(std::move(conversion)); |
| |
| // Log use counters once we have a conversion. |
| UseCounter::Count(document_->domWindow(), |
| mojom::blink::WebFeature::kConversionAPIAll); |
| UseCounter::Count(document_->domWindow(), |
| mojom::blink::WebFeature::kConversionRegistration); |
| |
| return true; |
| } |
| |
| mojo::PendingReceiver<mojom::blink::WorkerTimingContainer> |
| FrameFetchContext::TakePendingWorkerTimingReceiver(int request_id) { |
| DCHECK(!GetResourceFetcherProperties().IsDetached()); |
| return document_loader_->TakePendingWorkerTimingReceiver(request_id); |
| } |
| |
| void FrameFetchContext::DidObserveLoadingBehavior( |
| LoadingBehaviorFlag behavior) { |
| if (GetResourceFetcherProperties().IsDetached()) |
| return; |
| GetFrame()->Loader().GetDocumentLoader()->DidObserveLoadingBehavior(behavior); |
| } |
| |
| std::unique_ptr<ResourceLoadInfoNotifierWrapper> |
| FrameFetchContext::CreateResourceLoadInfoNotifierWrapper() { |
| if (GetResourceFetcherProperties().IsDetached()) |
| return nullptr; |
| return GetLocalFrameClient()->CreateResourceLoadInfoNotifierWrapper(); |
| } |
| |
| mojom::blink::ContentSecurityNotifier& |
| FrameFetchContext::GetContentSecurityNotifier() const { |
| DCHECK(!GetResourceFetcherProperties().IsDetached()); |
| return document_loader_->GetContentSecurityNotifier(); |
| } |
| |
| base::Optional<ResourceRequestBlockedReason> FrameFetchContext::CanRequest( |
| ResourceType type, |
| const ResourceRequest& resource_request, |
| const KURL& url, |
| const ResourceLoaderOptions& options, |
| ReportingDisposition reporting_disposition, |
| const base::Optional<ResourceRequest::RedirectInfo>& redirect_info) const { |
| if (!GetResourceFetcherProperties().IsDetached() && |
| document_->IsFreezingInProgress() && !resource_request.GetKeepalive()) { |
| AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>( |
| mojom::ConsoleMessageSource::kJavaScript, |
| mojom::ConsoleMessageLevel::kError, |
| "Only fetch keepalive is allowed during onfreeze: " + url.GetString())); |
| return ResourceRequestBlockedReason::kOther; |
| } |
| return BaseFetchContext::CanRequest(type, resource_request, url, options, |
| reporting_disposition, redirect_info); |
| } |
| |
| CoreProbeSink* FrameFetchContext::Probe() const { |
| return probe::ToCoreProbeSink(GetFrame()->GetDocument()); |
| } |
| |
| } // namespace blink |