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