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..49edee064960b9da9c44c63367d293802ef418f5 |
--- /dev/null |
+++ b/third_party/WebKit/Source/core/loader/WorkerFetchContext.cpp |
@@ -0,0 +1,427 @@ |
+// 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/WorkerGlobalScope.h" |
+#include "platform/RuntimeEnabledFeatures.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 { |
+ |
+class WorkerContextSupplement final |
+ : public GarbageCollectedFinalized<WorkerContextSupplement>, |
+ public Supplement<ExecutionContext> { |
+ USING_GARBAGE_COLLECTED_MIXIN(WorkerContextSupplement); |
+ |
+ public: |
+ static WorkerContextSupplement* from(ExecutionContext& executionContext) { |
+ if (!executionContext.isWorkerGlobalScope()) |
+ return nullptr; |
+ WorkerContextSupplement* supplement = static_cast<WorkerContextSupplement*>( |
+ Supplement<ExecutionContext>::from(executionContext, supplementName())); |
+ if (supplement) |
+ return supplement; |
+ WorkerClients* clients = toWorkerGlobalScope(executionContext).clients(); |
+ if (!clients) |
+ return nullptr; |
+ WorkerFetchContextInfo* contextInfo = |
+ WorkerFetchContextInfo::from(*clients); |
+ if (!contextInfo) |
+ return nullptr; |
+ WorkerFetchContext* workerFetchContext = WorkerFetchContext::create( |
+ toWorkerGlobalScope(executionContext), contextInfo); |
+ supplement = new WorkerContextSupplement(workerFetchContext); |
+ Supplement<ExecutionContext>::provideTo(executionContext, supplementName(), |
+ supplement); |
+ return supplement; |
+ } |
+ WorkerFetchContext* context() const { return m_workerFetchContext; } |
+ |
+ DEFINE_INLINE_VIRTUAL_TRACE() { |
+ visitor->trace(m_workerFetchContext); |
+ Supplement<ExecutionContext>::trace(visitor); |
+ } |
+ |
+ private: |
+ explicit WorkerContextSupplement(WorkerFetchContext* workerFetchContext) |
+ : m_workerFetchContext(workerFetchContext) {} |
+ static const char* supplementName() { return "WorkerContextSupplement"; } |
+ Member<WorkerFetchContext> m_workerFetchContext; |
+}; |
+} |
+ |
+class WorkerFetchContext::WorkerResourceCallback final |
+ : public GarbageCollectedFinalized<WorkerResourceCallback>, |
+ public Resource::ResourceCallback { |
+ public: |
+ WorkerResourceCallback(WebTaskRunner* loadingTaskRunner) |
+ : m_loadingTaskRunner(loadingTaskRunner) {} |
+ ~WorkerResourceCallback() {} |
+ void schedule(Resource*) override; |
+ void cancel(Resource*) override; |
+ bool isScheduled(Resource*) const override; |
+ DECLARE_TRACE(); |
+ |
+ private: |
+ void runTask(); |
+ |
+ RefPtr<WebTaskRunner> m_loadingTaskRunner; |
+ TaskHandle m_taskHandle; |
+ HeapHashSet<Member<Resource>> m_resourcesWithPendingClients; |
+}; |
+ |
+DEFINE_TRACE(WorkerFetchContext::WorkerResourceCallback) { |
+ visitor->trace(m_resourcesWithPendingClients); |
+} |
+ |
+void WorkerFetchContext::WorkerResourceCallback::schedule(Resource* resource) { |
+ if (!m_taskHandle.isActive()) { |
+ m_taskHandle = m_loadingTaskRunner->postCancellableTask( |
+ BLINK_FROM_HERE, |
+ WTF::bind(&WorkerResourceCallback::runTask, wrapWeakPersistent(this))); |
+ } |
+ m_resourcesWithPendingClients.insert(resource); |
+} |
+ |
+void WorkerFetchContext::WorkerResourceCallback::cancel(Resource* resource) { |
+ m_resourcesWithPendingClients.erase(resource); |
+ if (m_taskHandle.isActive() && m_resourcesWithPendingClients.isEmpty()) |
+ m_taskHandle.cancel(); |
+} |
+ |
+bool WorkerFetchContext::WorkerResourceCallback::isScheduled( |
+ Resource* resource) const { |
+ return m_resourcesWithPendingClients.contains(resource); |
+} |
+ |
+void WorkerFetchContext::WorkerResourceCallback::runTask() { |
+ HeapVector<Member<Resource>> resources; |
+ for (const Member<Resource>& resource : m_resourcesWithPendingClients) |
+ resources.push_back(resource.get()); |
+ m_resourcesWithPendingClients.clear(); |
+ |
+ for (const auto& resource : resources) |
+ resource->finishPendingClients(); |
+} |
+ |
+WorkerFetchContext* WorkerFetchContext::create( |
+ WorkerGlobalScope& workerGlobalScope, |
+ WorkerFetchContextInfo* contextInfo) { |
+ return new WorkerFetchContext( |
+ workerGlobalScope, contextInfo->CreateContext(), |
+ contextInfo->isDataSaverEnabled(), |
+ contextInfo->isStrictMixedContentCheckingEnabled()); |
+} |
+ |
+WorkerFetchContext::~WorkerFetchContext() {} |
+ |
+WorkerFetchContext* WorkerFetchContext::from( |
+ ExecutionContext& executionContext) { |
+ WorkerContextSupplement* supplement = |
+ WorkerContextSupplement::from(executionContext); |
+ if (!supplement) |
+ return nullptr; |
+ return supplement->context(); |
+} |
+ |
+WorkerFetchContext::WorkerFetchContext( |
+ WorkerGlobalScope& workerGlobalScope, |
+ std::unique_ptr<WebWorkerFetchContext> context, |
+ bool dataSaverEnabled, |
+ bool strictMixedContentCheckingEnabled) |
+ : m_workerGlobalScope(workerGlobalScope), |
+ m_context(std::move(context)), |
+ m_loadingTaskRunner(Platform::current() |
+ ->currentThread() |
+ ->scheduler() |
+ ->loadingTaskRunner()), |
+ m_timerTaskRunner( |
+ Platform::current()->currentThread()->scheduler()->timerTaskRunner()), |
+ m_dataSaverEnabled(dataSaverEnabled), |
+ m_strictMixedContentCheckingEnabled(strictMixedContentCheckingEnabled), |
+ m_resourceCallback( |
+ new WorkerResourceCallback(m_loadingTaskRunner.get())) {} |
+ |
+bool WorkerFetchContext::isControlledByServiceWorker() const { |
+ return m_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 m_context->serviceWorkerID(); |
+} |
+ |
+ResourceRequestBlockedReason WorkerFetchContext::canRequest( |
+ Resource::Type type, |
+ const ResourceRequest& resourceRequest, |
+ const KURL& url, |
+ const ResourceLoaderOptions& options, |
+ SecurityViolationReportingPolicy reportingPolicy, |
+ FetchRequest::OriginRestriction originRestriction) const { |
+ ResourceRequestBlockedReason blockedReason = |
+ canRequestInternal(type, resourceRequest, url, options, reportingPolicy, |
+ originRestriction, resourceRequest.redirectStatus()); |
+ if (blockedReason != ResourceRequestBlockedReason::None) { |
+ probe::didBlockRequest(m_workerGlobalScope, resourceRequest, nullptr, |
+ options.initiatorInfo, blockedReason); |
+ } |
+ return blockedReason; |
+} |
+ |
+void WorkerFetchContext::addAdditionalRequestHeaders(ResourceRequest& request, |
+ FetchResourceType type) { |
+ bool isMainResource = type == FetchMainResource; |
+ if (!isMainResource) { |
+ if (!request.didSetHTTPReferrer()) { |
+ request.setHTTPReferrer(SecurityPolicy::generateReferrer( |
+ m_workerGlobalScope->getReferrerPolicy(), request.url(), |
+ m_workerGlobalScope->outgoingReferrer())); |
+ request.addHTTPOriginIfNeeded(m_workerGlobalScope->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 userAgent = m_workerGlobalScope->userAgent(); |
+ probe::applyUserAgentOverride(m_workerGlobalScope, &userAgent); |
+ DCHECK(!userAgent.isNull()); |
+ request.setHTTPUserAgent(AtomicString(userAgent)); |
+ |
+ if (m_dataSaverEnabled) |
+ request.setHTTPHeaderField("Save-Data", "on"); |
+ |
+ request.setIsMojoIPCForced(true); |
+ if (request.requestorOrigin()->isUnique() && |
+ !m_workerGlobalScope->getSecurityOrigin()->isUnique()) { |
+ request.setRequestorOrigin(m_workerGlobalScope->getSecurityOrigin()); |
+ } |
+ WrappedResourceRequest webreq(request); |
+ m_context->willSendRequest(webreq); |
+} |
+ |
+void WorkerFetchContext::dispatchWillSendRequest( |
+ unsigned long identifier, |
+ ResourceRequest& request, |
+ const ResourceResponse& redirectResponse, |
+ const FetchInitiatorInfo& initiatorInfo) { |
+ probe::willSendRequest(m_workerGlobalScope, identifier, nullptr, request, |
+ redirectResponse, initiatorInfo); |
+} |
+ |
+void WorkerFetchContext::dispatchDidReceiveResponse( |
+ unsigned long identifier, |
+ const ResourceResponse& response, |
+ WebURLRequest::FrameType frameType, |
+ WebURLRequest::RequestContext requestContext, |
+ Resource* resource, |
+ ResourceResponseType) { |
+ if (response.hasMajorCertificateErrors()) { |
+ WebMixedContentContextType contextType = |
+ WebMixedContent::contextTypeFromRequestContext( |
+ requestContext, false /* strictMixedContentCheckingForPlugin */); |
+ if (contextType == WebMixedContentContextType::Blockable) { |
+ m_context->didRunContentWithCertificateErrors(response.url()); |
+ } else { |
+ m_context->didDisplayContentWithCertificateErrors(response.url()); |
+ } |
+ } |
+ probe::didReceiveResourceResponse(m_workerGlobalScope, identifier, nullptr, |
+ response, resource); |
+} |
+ |
+void WorkerFetchContext::dispatchDidReceiveData(unsigned long identifier, |
+ const char* data, |
+ int dataLength) { |
+ probe::didReceiveData(m_workerGlobalScope, identifier, nullptr, data, |
+ dataLength); |
+} |
+ |
+void WorkerFetchContext::dispatchDidReceiveEncodedData(unsigned long identifier, |
+ int encodedDataLength) { |
+ probe::didReceiveEncodedDataLength(m_workerGlobalScope, identifier, |
+ encodedDataLength); |
+} |
+ |
+void WorkerFetchContext::dispatchDidFinishLoading(unsigned long identifier, |
+ double finishTime, |
+ int64_t encodedDataLength, |
+ int64_t decodedBodyLength) { |
+ probe::didFinishLoading(m_workerGlobalScope, identifier, nullptr, finishTime, |
+ encodedDataLength, decodedBodyLength); |
+} |
+ |
+void WorkerFetchContext::dispatchDidFail(unsigned long identifier, |
+ const ResourceError& error, |
+ int64_t encodedDataLength, |
+ bool isInternalRequest) { |
+ probe::didFailLoading(m_workerGlobalScope, identifier, error); |
+} |
+ |
+ResourceRequestBlockedReason WorkerFetchContext::allowResponse( |
+ Resource::Type type, |
+ const ResourceRequest& resourceRequest, |
+ const KURL& url, |
+ const ResourceLoaderOptions& options) const { |
+ ResourceRequestBlockedReason blockedReason = |
+ canRequestInternal(type, resourceRequest, url, options, |
+ SecurityViolationReportingPolicy::Report, |
+ FetchRequest::UseDefaultOriginRestrictionForType, |
+ RedirectStatus::FollowedRedirect); |
+ if (blockedReason != ResourceRequestBlockedReason::None) { |
+ probe::didBlockRequest(m_workerGlobalScope, resourceRequest, nullptr, |
+ options.initiatorInfo, blockedReason); |
+ } |
+ return blockedReason; |
+} |
+ |
+void WorkerFetchContext::addResourceTiming(const ResourceTimingInfo& info) { |
+ WorkerGlobalScopePerformance::performance(*m_workerGlobalScope) |
+ ->addResourceTiming(info); |
+} |
+ |
+ResourceRequestBlockedReason WorkerFetchContext::canRequestInternal( |
+ Resource::Type type, |
+ const ResourceRequest& resourceRequest, |
+ const KURL& url, |
+ const ResourceLoaderOptions& options, |
+ SecurityViolationReportingPolicy reportingPolicy, |
+ FetchRequest::OriginRestriction originRestriction, |
+ ResourceRequest::RedirectStatus redirectStatus) const { |
+ bool shouldBlockRequest = false; |
+ probe::shouldBlockRequest(m_workerGlobalScope, resourceRequest, |
+ &shouldBlockRequest); |
+ if (shouldBlockRequest) |
+ return ResourceRequestBlockedReason::Inspector; |
+ |
+ SecurityOrigin* securityOrigin = options.securityOrigin.get(); |
+ if (!securityOrigin) |
+ securityOrigin = m_workerGlobalScope->getSecurityOrigin(); |
+ if (!securityOrigin->canDisplay(url)) { |
+ if (originRestriction == FetchRequest::RestrictToSameOrigin) { |
+ return ResourceRequestBlockedReason::Origin; |
+ } |
+ } |
+ |
+ if (!url.user().isEmpty() || !url.pass().isEmpty()) { |
+ // See https://crbug.com/504300 |
+ if (RuntimeEnabledFeatures::blockCredentialedSubresourcesEnabled()) |
+ return ResourceRequestBlockedReason::Origin; |
+ } |
+ |
+ // TODO(horo): Check m_strictMixedContentCheckingEnabled |
+ // TODO(horo): Implement reporting. |
+ if (MixedContentChecker::isMixedContent(securityOrigin, url)) |
+ return ResourceRequestBlockedReason::MixedContent; |
+ |
+ // TODO(horo): Implement subresource filter. |
+ |
+ if (!m_workerGlobalScope->contentSecurityPolicy()->allowRequest( |
+ resourceRequest.requestContext(), url, |
+ options.contentSecurityPolicyNonce, options.integrityMetadata, |
+ options.parserDisposition, redirectStatus, reportingPolicy)) { |
+ return ResourceRequestBlockedReason::CSP; |
+ } |
+ |
+ return ResourceRequestBlockedReason::None; |
+} |
+ |
+WebURLLoader* WorkerFetchContext::createURLLoader() { |
+ return m_context->createURLLoader(); |
+} |
+ |
+RefPtr<WebTaskRunner> WorkerFetchContext::timerTaskRunner() const { |
+ return m_timerTaskRunner; |
+} |
+ |
+RefPtr<WebTaskRunner> WorkerFetchContext::loadingTaskRunner() const { |
+ return m_loadingTaskRunner; |
+} |
+ |
+Resource::ResourceCallback* WorkerFetchContext::resourceCallback() { |
+ return m_resourceCallback; |
+} |
+ |
+ResourceFetcher* WorkerFetchContext::getResourceFetcher() { |
+ if (m_resourceFetcher) |
+ return m_resourceFetcher; |
+ m_resourceFetcher = ResourceFetcher::create(this); |
+ return m_resourceFetcher; |
+} |
+ |
+DEFINE_TRACE(WorkerFetchContext) { |
+ visitor->trace(m_workerGlobalScope); |
+ visitor->trace(m_resourceFetcher); |
+ visitor->trace(m_resourceCallback); |
+ FetchContext::trace(visitor); |
+} |
+ |
+// static |
+WorkerFetchContextInfo* WorkerFetchContextInfo::create( |
+ std::unique_ptr<WebWorkerFetchContextInfo> info) { |
+ return new WorkerFetchContextInfo(std::move(info)); |
+} |
+ |
+// static |
+WorkerFetchContextInfo* WorkerFetchContextInfo::from(WorkerClients& clients) { |
+ return static_cast<WorkerFetchContextInfo*>( |
+ Supplement<WorkerClients>::from(clients, supplementName())); |
+} |
+ |
+WorkerFetchContextInfo::WorkerFetchContextInfo( |
+ std::unique_ptr<WebWorkerFetchContextInfo> info) |
+ : m_info(std::move(info)) {} |
+ |
+WorkerFetchContextInfo::~WorkerFetchContextInfo() {} |
+ |
+const char* WorkerFetchContextInfo::supplementName() { |
+ return "WorkerFetchContextInfo"; |
+} |
+ |
+std::unique_ptr<WebWorkerFetchContext> WorkerFetchContextInfo::CreateContext() { |
+ DCHECK(m_info); |
+ DCHECK(!isMainThread()); |
+ std::unique_ptr<WebWorkerFetchContext> webContext = |
+ m_info->CreateContext(Platform::current() |
+ ->currentThread() |
+ ->scheduler() |
+ ->loadingTaskRunner() |
+ ->toSingleThreadTaskRunner()); |
+ m_info.reset(); |
+ return webContext; |
+} |
+ |
+void provideWorkerFetchContextInfoToWorker( |
+ WorkerClients* clients, |
+ std::unique_ptr<WebWorkerFetchContextInfo> info) { |
+ DCHECK(clients); |
+ WorkerFetchContextInfo::provideTo( |
+ *clients, WorkerFetchContextInfo::supplementName(), |
+ WorkerFetchContextInfo::create(std::move(info))); |
+} |
+ |
+} // namespace blink |