| 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 | 
|  |