Index: third_party/WebKit/Source/core/loader/WorkerFetchContext.cpp |
diff --git a/third_party/WebKit/Source/core/loader/WorkerFetchContext.cpp b/third_party/WebKit/Source/core/loader/WorkerFetchContext.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..436a61b4edc7fbdf6ed98a5bc3b19054ba9fdffe |
--- /dev/null |
+++ b/third_party/WebKit/Source/core/loader/WorkerFetchContext.cpp |
@@ -0,0 +1,380 @@ |
+// 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 "core/loader/WorkerFetchContext.h" |
+ |
+#include "core/loader/MixedContentChecker.h" |
+#include "core/probe/CoreProbes.h" |
+#include "core/timing/WorkerGlobalScopePerformance.h" |
+#include "core/workers/WorkerClients.h" |
+#include "core/workers/WorkerGlobalScope.h" |
+#include "platform/RuntimeEnabledFeatures.h" |
+#include "platform/Supplementable.h" |
+#include "platform/WebTaskRunner.h" |
+#include "platform/exported/WrappedResourceRequest.h" |
+#include "platform/loader/fetch/ResourceFetcher.h" |
+#include "platform/weborigin/SecurityPolicy.h" |
+#include "public/platform/Platform.h" |
+#include "public/platform/WebMixedContent.h" |
+#include "public/platform/WebMixedContentContextType.h" |
+#include "public/platform/WebScheduler.h" |
+#include "public/platform/WebThread.h" |
+#include "public/platform/WebWorkerFetchContext.h" |
+ |
+namespace blink { |
+ |
+namespace { |
+ |
+// WorkerFetchContextInfo is used to pass the WebWorkerFetchContext from the |
+// main thread to the worker thread by attaching to the WorkerClients as a |
+// Supplement. |
+class WorkerFetchContextInfo final |
+ : public GarbageCollectedFinalized<WorkerFetchContextInfo>, |
+ public Supplement<WorkerClients> { |
+ USING_GARBAGE_COLLECTED_MIXIN(WorkerFetchContextInfo); |
+ |
+ public: |
+ static WorkerFetchContextInfo* From(WorkerClients& clients) { |
+ return static_cast<WorkerFetchContextInfo*>( |
+ Supplement<WorkerClients>::From(clients, SupplementName())); |
+ } |
+ static const char* SupplementName() { return "WorkerFetchContextInfo"; } |
+ |
+ explicit WorkerFetchContextInfo( |
+ std::unique_ptr<WebWorkerFetchContext> web_context) |
+ : web_context_(std::move(web_context)) {} |
+ virtual ~WorkerFetchContextInfo() {} |
+ |
+ std::unique_ptr<WebWorkerFetchContext> TakeContext() { |
+ return std::move(web_context_); |
+ } |
+ |
+ DEFINE_INLINE_VIRTUAL_TRACE() { Supplement<WorkerClients>::Trace(visitor); } |
+ |
+ private: |
+ std::unique_ptr<WebWorkerFetchContext> web_context_; |
+}; |
+ |
+} // namespace |
+ |
+class WorkerFetchContext::WorkerResourceCallback final |
+ : public GarbageCollectedFinalized<WorkerResourceCallback>, |
+ public Resource::ResourceCallback { |
+ public: |
+ WorkerResourceCallback(WebTaskRunner* loading_task_runner) |
+ : loading_task_runner_(loading_task_runner) {} |
+ ~WorkerResourceCallback() {} |
+ void Schedule(Resource*) override; |
+ void Cancel(Resource*) override; |
+ bool IsScheduled(Resource*) const override; |
+ DECLARE_TRACE(); |
+ |
+ private: |
+ void RunTask(); |
+ |
+ RefPtr<WebTaskRunner> loading_task_runner_; |
+ TaskHandle task_handle_; |
+ HeapHashSet<Member<Resource>> resources_with_pending_clients_; |
+}; |
+ |
+DEFINE_TRACE(WorkerFetchContext::WorkerResourceCallback) { |
+ visitor->Trace(resources_with_pending_clients_); |
+} |
+ |
+void WorkerFetchContext::WorkerResourceCallback::Schedule(Resource* resource) { |
+ if (!task_handle_.IsActive()) { |
+ task_handle_ = loading_task_runner_->PostCancellableTask( |
+ BLINK_FROM_HERE, |
+ WTF::Bind(&WorkerResourceCallback::RunTask, WrapWeakPersistent(this))); |
+ } |
+ resources_with_pending_clients_.insert(resource); |
+} |
+ |
+void WorkerFetchContext::WorkerResourceCallback::Cancel(Resource* resource) { |
+ resources_with_pending_clients_.erase(resource); |
+ if (task_handle_.IsActive() && resources_with_pending_clients_.IsEmpty()) |
+ task_handle_.Cancel(); |
+} |
+ |
+bool WorkerFetchContext::WorkerResourceCallback::IsScheduled( |
+ Resource* resource) const { |
+ return resources_with_pending_clients_.Contains(resource); |
+} |
+ |
+void WorkerFetchContext::WorkerResourceCallback::RunTask() { |
+ HeapVector<Member<Resource>> resources; |
+ for (const Member<Resource>& resource : resources_with_pending_clients_) |
+ resources.push_back(resource); |
+ resources_with_pending_clients_.Clear(); |
+ |
+ for (const auto& resource : resources) |
+ resource->FinishPendingClients(); |
+} |
+ |
+WorkerFetchContext::~WorkerFetchContext() {} |
+ |
+WorkerFetchContext* WorkerFetchContext::Create( |
+ WorkerGlobalScope& worker_global_scope) { |
+ WorkerClients* worker_clients = worker_global_scope.Clients(); |
+ if (!worker_clients) |
+ return nullptr; |
+ WorkerFetchContextInfo* context_info = |
+ static_cast<WorkerFetchContextInfo*>(Supplement<WorkerClients>::From( |
+ *worker_clients, WorkerFetchContextInfo::SupplementName())); |
+ if (!context_info) |
+ return nullptr; |
+ std::unique_ptr<WebWorkerFetchContext> web_context = |
+ context_info->TakeContext(); |
+ DCHECK(web_context); |
+ return new WorkerFetchContext(worker_global_scope, std::move(web_context)); |
+} |
+ |
+WorkerFetchContext::WorkerFetchContext( |
+ WorkerGlobalScope& worker_global_scope, |
+ std::unique_ptr<WebWorkerFetchContext> web_context) |
+ : worker_global_scope_(worker_global_scope), |
+ web_context_(std::move(web_context)), |
+ loading_task_runner_(Platform::Current() |
+ ->CurrentThread() |
+ ->Scheduler() |
+ ->LoadingTaskRunner()), |
+ timer_task_runner_( |
+ Platform::Current()->CurrentThread()->Scheduler()->TimerTaskRunner()), |
+ resource_callback_( |
+ new WorkerResourceCallback(loading_task_runner_.Get())) { |
+ web_context_->InitializeOnWorkerThread( |
+ loading_task_runner_->ToSingleThreadTaskRunner()); |
+} |
+ |
+ResourceFetcher* WorkerFetchContext::GetResourceFetcher() { |
+ if (resource_fetcher_) |
+ return resource_fetcher_; |
+ resource_fetcher_ = ResourceFetcher::Create(this); |
+ return resource_fetcher_; |
+} |
+ |
+WebURLLoader* WorkerFetchContext::CreateURLLoader() { |
+ return web_context_->CreateURLLoader(); |
+} |
+ |
+bool WorkerFetchContext::IsControlledByServiceWorker() const { |
+ return web_context_->IsControlledByServiceWorker(); |
+} |
+ |
+int64_t WorkerFetchContext::ServiceWorkerID() const { |
+ // TODO(horo): ServiceWorkerID() is used only for memory cache which is |
+ // disabled in worker thread. So we should remove this method. |
+ return web_context_->ServiceWorkerID(); |
+} |
+ |
+ResourceRequestBlockedReason WorkerFetchContext::CanRequest( |
+ Resource::Type type, |
+ const ResourceRequest& resource_request, |
+ const KURL& url, |
+ const ResourceLoaderOptions& options, |
+ SecurityViolationReportingPolicy reporting_policy, |
+ FetchParameters::OriginRestriction origin_restriction) const { |
+ ResourceRequestBlockedReason blocked_reason = CanRequestInternal( |
+ type, resource_request, url, options, reporting_policy, |
+ origin_restriction, resource_request.GetRedirectStatus()); |
+ if (blocked_reason != ResourceRequestBlockedReason::kNone) { |
+ probe::didBlockRequest(worker_global_scope_, resource_request, nullptr, |
+ options.initiator_info, blocked_reason); |
+ } |
+ return blocked_reason; |
+} |
+ |
+void WorkerFetchContext::AddAdditionalRequestHeaders(ResourceRequest& request, |
+ FetchResourceType type) { |
+ bool isMainResource = type == kFetchMainResource; |
+ if (!isMainResource) { |
+ if (!request.DidSetHTTPReferrer()) { |
+ request.SetHTTPReferrer(SecurityPolicy::GenerateReferrer( |
+ worker_global_scope_->GetReferrerPolicy(), request.Url(), |
+ worker_global_scope_->OutgoingReferrer())); |
+ request.AddHTTPOriginIfNeeded(worker_global_scope_->GetSecurityOrigin()); |
+ } else { |
+ DCHECK_EQ(SecurityPolicy::GenerateReferrer(request.GetReferrerPolicy(), |
+ request.Url(), |
+ request.HttpReferrer()) |
+ .referrer, |
+ request.HttpReferrer()); |
+ request.AddHTTPOriginIfNeeded(request.HttpReferrer()); |
+ } |
+ } |
+} |
+ |
+void WorkerFetchContext::PrepareRequest(ResourceRequest& request, |
+ RedirectType) { |
+ String user_agent = worker_global_scope_->UserAgent(); |
+ probe::applyUserAgentOverride(worker_global_scope_, &user_agent); |
+ DCHECK(!user_agent.IsNull()); |
+ request.SetHTTPUserAgent(AtomicString(user_agent)); |
+ |
+ request.SetIsMojoIPCForced(true); |
+ if (request.RequestorOrigin()->IsUnique() && |
+ !worker_global_scope_->GetSecurityOrigin()->IsUnique()) { |
+ request.SetRequestorOrigin(worker_global_scope_->GetSecurityOrigin()); |
+ } |
+ WrappedResourceRequest webreq(request); |
+ web_context_->WillSendRequest(webreq); |
+} |
+ |
+void WorkerFetchContext::DispatchWillSendRequest( |
+ unsigned long identifier, |
+ ResourceRequest& request, |
+ const ResourceResponse& redirect_response, |
+ const FetchInitiatorInfo& initiator_info) { |
+ probe::willSendRequest(worker_global_scope_, identifier, nullptr, request, |
+ redirect_response, initiator_info); |
+} |
+ |
+void WorkerFetchContext::DispatchDidReceiveResponse( |
+ unsigned long identifier, |
+ const ResourceResponse& response, |
+ WebURLRequest::FrameType frame_type, |
+ WebURLRequest::RequestContext request_context, |
+ Resource* resource, |
+ ResourceResponseType) { |
+ if (response.HasMajorCertificateErrors()) { |
+ WebMixedContentContextType context_type = |
+ WebMixedContent::ContextTypeFromRequestContext( |
+ request_context, false /* strictMixedContentCheckingForPlugin */); |
+ if (context_type == WebMixedContentContextType::kBlockable) { |
+ web_context_->DidRunContentWithCertificateErrors(response.Url()); |
+ } else { |
+ web_context_->DidDisplayContentWithCertificateErrors(response.Url()); |
+ } |
+ } |
+ probe::didReceiveResourceResponse(worker_global_scope_, identifier, nullptr, |
+ response, resource); |
+} |
+ |
+void WorkerFetchContext::DispatchDidReceiveData(unsigned long identifier, |
+ const char* data, |
+ int data_length) { |
+ probe::didReceiveData(worker_global_scope_, identifier, nullptr, data, |
+ data_length); |
+} |
+ |
+void WorkerFetchContext::DispatchDidReceiveEncodedData( |
+ unsigned long identifier, |
+ int encoded_data_length) { |
+ probe::didReceiveEncodedDataLength(worker_global_scope_, identifier, |
+ encoded_data_length); |
+} |
+ |
+void WorkerFetchContext::DispatchDidFinishLoading(unsigned long identifier, |
+ double finish_time, |
+ int64_t encoded_data_length, |
+ int64_t decoded_body_length) { |
+ probe::didFinishLoading(worker_global_scope_, identifier, nullptr, |
+ finish_time, encoded_data_length, |
+ decoded_body_length); |
+} |
+ |
+void WorkerFetchContext::DispatchDidFail(unsigned long identifier, |
+ const ResourceError& error, |
+ int64_t encoded_data_length, |
+ bool is_internal_request) { |
+ probe::didFailLoading(worker_global_scope_, identifier, error); |
+} |
+ |
+ResourceRequestBlockedReason WorkerFetchContext::AllowResponse( |
+ Resource::Type type, |
+ const ResourceRequest& resource_request, |
+ const KURL& url, |
+ const ResourceLoaderOptions& options) const { |
+ ResourceRequestBlockedReason blocked_reason = |
+ CanRequestInternal(type, resource_request, url, options, |
+ SecurityViolationReportingPolicy::kReport, |
+ FetchParameters::kUseDefaultOriginRestrictionForType, |
+ RedirectStatus::kFollowedRedirect); |
+ if (blocked_reason != ResourceRequestBlockedReason::kNone) { |
+ probe::didBlockRequest(worker_global_scope_, resource_request, nullptr, |
+ options.initiator_info, blocked_reason); |
+ } |
+ return blocked_reason; |
+} |
+ |
+void WorkerFetchContext::AddResourceTiming(const ResourceTimingInfo& info) { |
+ WorkerGlobalScopePerformance::performance(*worker_global_scope_) |
+ ->AddResourceTiming(info); |
+} |
+ |
+ResourceRequestBlockedReason WorkerFetchContext::CanRequestInternal( |
+ Resource::Type type, |
+ const ResourceRequest& resource_request, |
+ const KURL& url, |
+ const ResourceLoaderOptions& options, |
+ SecurityViolationReportingPolicy reporting_policy, |
+ FetchParameters::OriginRestriction origin_restriction, |
+ ResourceRequest::RedirectStatus redirect_status) const { |
+ bool should_block_request = false; |
+ probe::shouldBlockRequest(worker_global_scope_, resource_request, |
+ &should_block_request); |
+ if (should_block_request) |
+ return ResourceRequestBlockedReason::kInspector; |
+ |
+ SecurityOrigin* security_origin = options.security_origin.Get(); |
+ if (!security_origin) |
+ security_origin = worker_global_scope_->GetSecurityOrigin(); |
+ if (!security_origin->CanDisplay(url)) { |
+ if (origin_restriction == FetchParameters::kRestrictToSameOrigin) { |
+ return ResourceRequestBlockedReason::kOrigin; |
+ } |
+ } |
+ |
+ if (!url.User().IsEmpty() || !url.Pass().IsEmpty()) { |
+ // See https://crbug.com/504300 |
+ if (RuntimeEnabledFeatures::blockCredentialedSubresourcesEnabled()) |
+ return ResourceRequestBlockedReason::kOrigin; |
+ } |
+ |
+ // TODO(horo): Check GetStrictMixedContentChecking() |
+ // TODO(horo): Implement reporting. |
+ if (MixedContentChecker::IsMixedContent(security_origin, url)) |
+ return ResourceRequestBlockedReason::kMixedContent; |
+ |
+ // TODO(horo): Implement subresource filter. |
+ |
+ if (!worker_global_scope_->GetContentSecurityPolicy()->AllowRequest( |
+ resource_request.GetRequestContext(), url, |
+ options.content_security_policy_nonce, options.integrity_metadata, |
+ options.parser_disposition, redirect_status, reporting_policy)) { |
+ return ResourceRequestBlockedReason::CSP; |
+ } |
+ |
+ return ResourceRequestBlockedReason::kNone; |
+} |
+ |
+RefPtr<WebTaskRunner> WorkerFetchContext::TimerTaskRunner() const { |
+ return timer_task_runner_; |
+} |
+ |
+RefPtr<WebTaskRunner> WorkerFetchContext::LoadingTaskRunner() const { |
+ return loading_task_runner_; |
+} |
+ |
+Resource::ResourceCallback* WorkerFetchContext::GetResourceCallback() { |
+ return resource_callback_; |
+} |
+ |
+DEFINE_TRACE(WorkerFetchContext) { |
+ visitor->Trace(worker_global_scope_); |
+ visitor->Trace(resource_fetcher_); |
+ visitor->Trace(resource_callback_); |
+ FetchContext::Trace(visitor); |
+} |
+ |
+void ProvideWorkerFetchContextToWorker( |
+ WorkerClients* clients, |
+ std::unique_ptr<WebWorkerFetchContext> web_context) { |
+ DCHECK(clients); |
+ WorkerFetchContextInfo::ProvideTo( |
+ *clients, WorkerFetchContextInfo::SupplementName(), |
+ new WorkerFetchContextInfo(std::move(web_context))); |
+} |
+ |
+} // namespace blink |