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