OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "core/loader/WorkerFetchContext.h" |
| 6 |
| 7 #include "core/loader/MixedContentChecker.h" |
| 8 #include "core/probe/CoreProbes.h" |
| 9 #include "core/timing/WorkerGlobalScopePerformance.h" |
| 10 #include "core/workers/WorkerClients.h" |
| 11 #include "core/workers/WorkerGlobalScope.h" |
| 12 #include "platform/RuntimeEnabledFeatures.h" |
| 13 #include "platform/Supplementable.h" |
| 14 #include "platform/WebTaskRunner.h" |
| 15 #include "platform/exported/WrappedResourceRequest.h" |
| 16 #include "platform/loader/fetch/ResourceFetcher.h" |
| 17 #include "platform/weborigin/SecurityPolicy.h" |
| 18 #include "public/platform/Platform.h" |
| 19 #include "public/platform/WebMixedContent.h" |
| 20 #include "public/platform/WebMixedContentContextType.h" |
| 21 #include "public/platform/WebScheduler.h" |
| 22 #include "public/platform/WebThread.h" |
| 23 #include "public/platform/WebWorkerFetchContext.h" |
| 24 |
| 25 namespace blink { |
| 26 |
| 27 namespace { |
| 28 |
| 29 // WorkerFetchContextInfo is used to pass the WebWorkerFetchContext from the |
| 30 // main thread to the worker thread by attaching to the WorkerClients as a |
| 31 // Supplement. |
| 32 class WorkerFetchContextInfo final |
| 33 : public GarbageCollectedFinalized<WorkerFetchContextInfo>, |
| 34 public Supplement<WorkerClients> { |
| 35 USING_GARBAGE_COLLECTED_MIXIN(WorkerFetchContextInfo); |
| 36 |
| 37 public: |
| 38 static WorkerFetchContextInfo* From(WorkerClients& clients) { |
| 39 return static_cast<WorkerFetchContextInfo*>( |
| 40 Supplement<WorkerClients>::From(clients, SupplementName())); |
| 41 } |
| 42 static const char* SupplementName() { return "WorkerFetchContextInfo"; } |
| 43 |
| 44 explicit WorkerFetchContextInfo( |
| 45 std::unique_ptr<WebWorkerFetchContext> web_context) |
| 46 : web_context_(std::move(web_context)) {} |
| 47 virtual ~WorkerFetchContextInfo() {} |
| 48 |
| 49 std::unique_ptr<WebWorkerFetchContext> TakeContext() { |
| 50 return std::move(web_context_); |
| 51 } |
| 52 |
| 53 DEFINE_INLINE_VIRTUAL_TRACE() { Supplement<WorkerClients>::Trace(visitor); } |
| 54 |
| 55 private: |
| 56 std::unique_ptr<WebWorkerFetchContext> web_context_; |
| 57 }; |
| 58 |
| 59 } // namespace |
| 60 |
| 61 class WorkerFetchContext::WorkerResourceCallback final |
| 62 : public GarbageCollectedFinalized<WorkerResourceCallback>, |
| 63 public Resource::ResourceCallback { |
| 64 public: |
| 65 WorkerResourceCallback(WebTaskRunner* loading_task_runner) |
| 66 : loading_task_runner_(loading_task_runner) {} |
| 67 ~WorkerResourceCallback() {} |
| 68 void Schedule(Resource*) override; |
| 69 void Cancel(Resource*) override; |
| 70 bool IsScheduled(Resource*) const override; |
| 71 DECLARE_TRACE(); |
| 72 |
| 73 private: |
| 74 void RunTask(); |
| 75 |
| 76 RefPtr<WebTaskRunner> loading_task_runner_; |
| 77 TaskHandle task_handle_; |
| 78 HeapHashSet<Member<Resource>> resources_with_pending_clients_; |
| 79 }; |
| 80 |
| 81 DEFINE_TRACE(WorkerFetchContext::WorkerResourceCallback) { |
| 82 visitor->Trace(resources_with_pending_clients_); |
| 83 } |
| 84 |
| 85 void WorkerFetchContext::WorkerResourceCallback::Schedule(Resource* resource) { |
| 86 if (!task_handle_.IsActive()) { |
| 87 task_handle_ = loading_task_runner_->PostCancellableTask( |
| 88 BLINK_FROM_HERE, |
| 89 WTF::Bind(&WorkerResourceCallback::RunTask, WrapWeakPersistent(this))); |
| 90 } |
| 91 resources_with_pending_clients_.insert(resource); |
| 92 } |
| 93 |
| 94 void WorkerFetchContext::WorkerResourceCallback::Cancel(Resource* resource) { |
| 95 resources_with_pending_clients_.erase(resource); |
| 96 if (task_handle_.IsActive() && resources_with_pending_clients_.IsEmpty()) |
| 97 task_handle_.Cancel(); |
| 98 } |
| 99 |
| 100 bool WorkerFetchContext::WorkerResourceCallback::IsScheduled( |
| 101 Resource* resource) const { |
| 102 return resources_with_pending_clients_.Contains(resource); |
| 103 } |
| 104 |
| 105 void WorkerFetchContext::WorkerResourceCallback::RunTask() { |
| 106 HeapVector<Member<Resource>> resources; |
| 107 for (const Member<Resource>& resource : resources_with_pending_clients_) |
| 108 resources.push_back(resource); |
| 109 resources_with_pending_clients_.Clear(); |
| 110 |
| 111 for (const auto& resource : resources) |
| 112 resource->FinishPendingClients(); |
| 113 } |
| 114 |
| 115 WorkerFetchContext::~WorkerFetchContext() {} |
| 116 |
| 117 WorkerFetchContext* WorkerFetchContext::Create( |
| 118 WorkerGlobalScope& worker_global_scope) { |
| 119 WorkerClients* worker_clients = worker_global_scope.Clients(); |
| 120 if (!worker_clients) |
| 121 return nullptr; |
| 122 WorkerFetchContextInfo* context_info = |
| 123 static_cast<WorkerFetchContextInfo*>(Supplement<WorkerClients>::From( |
| 124 *worker_clients, WorkerFetchContextInfo::SupplementName())); |
| 125 if (!context_info) |
| 126 return nullptr; |
| 127 std::unique_ptr<WebWorkerFetchContext> web_context = |
| 128 context_info->TakeContext(); |
| 129 DCHECK(web_context); |
| 130 return new WorkerFetchContext(worker_global_scope, std::move(web_context)); |
| 131 } |
| 132 |
| 133 WorkerFetchContext::WorkerFetchContext( |
| 134 WorkerGlobalScope& worker_global_scope, |
| 135 std::unique_ptr<WebWorkerFetchContext> web_context) |
| 136 : worker_global_scope_(worker_global_scope), |
| 137 web_context_(std::move(web_context)), |
| 138 loading_task_runner_(Platform::Current() |
| 139 ->CurrentThread() |
| 140 ->Scheduler() |
| 141 ->LoadingTaskRunner()), |
| 142 timer_task_runner_( |
| 143 Platform::Current()->CurrentThread()->Scheduler()->TimerTaskRunner()), |
| 144 resource_callback_( |
| 145 new WorkerResourceCallback(loading_task_runner_.Get())) { |
| 146 web_context_->InitializeOnWorkerThread( |
| 147 loading_task_runner_->ToSingleThreadTaskRunner()); |
| 148 } |
| 149 |
| 150 ResourceFetcher* WorkerFetchContext::GetResourceFetcher() { |
| 151 if (resource_fetcher_) |
| 152 return resource_fetcher_; |
| 153 resource_fetcher_ = ResourceFetcher::Create(this); |
| 154 return resource_fetcher_; |
| 155 } |
| 156 |
| 157 WebURLLoader* WorkerFetchContext::CreateURLLoader() { |
| 158 return web_context_->CreateURLLoader(); |
| 159 } |
| 160 |
| 161 bool WorkerFetchContext::IsControlledByServiceWorker() const { |
| 162 return web_context_->IsControlledByServiceWorker(); |
| 163 } |
| 164 |
| 165 int64_t WorkerFetchContext::ServiceWorkerID() const { |
| 166 // TODO(horo): ServiceWorkerID() is used only for memory cache which is |
| 167 // disabled in worker thread. So we should remove this method. |
| 168 return web_context_->ServiceWorkerID(); |
| 169 } |
| 170 |
| 171 ResourceRequestBlockedReason WorkerFetchContext::CanRequest( |
| 172 Resource::Type type, |
| 173 const ResourceRequest& resource_request, |
| 174 const KURL& url, |
| 175 const ResourceLoaderOptions& options, |
| 176 SecurityViolationReportingPolicy reporting_policy, |
| 177 FetchParameters::OriginRestriction origin_restriction) const { |
| 178 ResourceRequestBlockedReason blocked_reason = CanRequestInternal( |
| 179 type, resource_request, url, options, reporting_policy, |
| 180 origin_restriction, resource_request.GetRedirectStatus()); |
| 181 if (blocked_reason != ResourceRequestBlockedReason::kNone) { |
| 182 probe::didBlockRequest(worker_global_scope_, resource_request, nullptr, |
| 183 options.initiator_info, blocked_reason); |
| 184 } |
| 185 return blocked_reason; |
| 186 } |
| 187 |
| 188 void WorkerFetchContext::AddAdditionalRequestHeaders(ResourceRequest& request, |
| 189 FetchResourceType type) { |
| 190 bool isMainResource = type == kFetchMainResource; |
| 191 if (!isMainResource) { |
| 192 if (!request.DidSetHTTPReferrer()) { |
| 193 request.SetHTTPReferrer(SecurityPolicy::GenerateReferrer( |
| 194 worker_global_scope_->GetReferrerPolicy(), request.Url(), |
| 195 worker_global_scope_->OutgoingReferrer())); |
| 196 request.AddHTTPOriginIfNeeded(worker_global_scope_->GetSecurityOrigin()); |
| 197 } else { |
| 198 DCHECK_EQ(SecurityPolicy::GenerateReferrer(request.GetReferrerPolicy(), |
| 199 request.Url(), |
| 200 request.HttpReferrer()) |
| 201 .referrer, |
| 202 request.HttpReferrer()); |
| 203 request.AddHTTPOriginIfNeeded(request.HttpReferrer()); |
| 204 } |
| 205 } |
| 206 } |
| 207 |
| 208 void WorkerFetchContext::PrepareRequest(ResourceRequest& request, |
| 209 RedirectType) { |
| 210 String user_agent = worker_global_scope_->UserAgent(); |
| 211 probe::applyUserAgentOverride(worker_global_scope_, &user_agent); |
| 212 DCHECK(!user_agent.IsNull()); |
| 213 request.SetHTTPUserAgent(AtomicString(user_agent)); |
| 214 |
| 215 request.SetIsMojoIPCForced(true); |
| 216 if (request.RequestorOrigin()->IsUnique() && |
| 217 !worker_global_scope_->GetSecurityOrigin()->IsUnique()) { |
| 218 request.SetRequestorOrigin(worker_global_scope_->GetSecurityOrigin()); |
| 219 } |
| 220 WrappedResourceRequest webreq(request); |
| 221 web_context_->WillSendRequest(webreq); |
| 222 } |
| 223 |
| 224 void WorkerFetchContext::DispatchWillSendRequest( |
| 225 unsigned long identifier, |
| 226 ResourceRequest& request, |
| 227 const ResourceResponse& redirect_response, |
| 228 const FetchInitiatorInfo& initiator_info) { |
| 229 probe::willSendRequest(worker_global_scope_, identifier, nullptr, request, |
| 230 redirect_response, initiator_info); |
| 231 } |
| 232 |
| 233 void WorkerFetchContext::DispatchDidReceiveResponse( |
| 234 unsigned long identifier, |
| 235 const ResourceResponse& response, |
| 236 WebURLRequest::FrameType frame_type, |
| 237 WebURLRequest::RequestContext request_context, |
| 238 Resource* resource, |
| 239 ResourceResponseType) { |
| 240 if (response.HasMajorCertificateErrors()) { |
| 241 WebMixedContentContextType context_type = |
| 242 WebMixedContent::ContextTypeFromRequestContext( |
| 243 request_context, false /* strictMixedContentCheckingForPlugin */); |
| 244 if (context_type == WebMixedContentContextType::kBlockable) { |
| 245 web_context_->DidRunContentWithCertificateErrors(response.Url()); |
| 246 } else { |
| 247 web_context_->DidDisplayContentWithCertificateErrors(response.Url()); |
| 248 } |
| 249 } |
| 250 probe::didReceiveResourceResponse(worker_global_scope_, identifier, nullptr, |
| 251 response, resource); |
| 252 } |
| 253 |
| 254 void WorkerFetchContext::DispatchDidReceiveData(unsigned long identifier, |
| 255 const char* data, |
| 256 int data_length) { |
| 257 probe::didReceiveData(worker_global_scope_, identifier, nullptr, data, |
| 258 data_length); |
| 259 } |
| 260 |
| 261 void WorkerFetchContext::DispatchDidReceiveEncodedData( |
| 262 unsigned long identifier, |
| 263 int encoded_data_length) { |
| 264 probe::didReceiveEncodedDataLength(worker_global_scope_, identifier, |
| 265 encoded_data_length); |
| 266 } |
| 267 |
| 268 void WorkerFetchContext::DispatchDidFinishLoading(unsigned long identifier, |
| 269 double finish_time, |
| 270 int64_t encoded_data_length, |
| 271 int64_t decoded_body_length) { |
| 272 probe::didFinishLoading(worker_global_scope_, identifier, nullptr, |
| 273 finish_time, encoded_data_length, |
| 274 decoded_body_length); |
| 275 } |
| 276 |
| 277 void WorkerFetchContext::DispatchDidFail(unsigned long identifier, |
| 278 const ResourceError& error, |
| 279 int64_t encoded_data_length, |
| 280 bool is_internal_request) { |
| 281 probe::didFailLoading(worker_global_scope_, identifier, error); |
| 282 } |
| 283 |
| 284 ResourceRequestBlockedReason WorkerFetchContext::AllowResponse( |
| 285 Resource::Type type, |
| 286 const ResourceRequest& resource_request, |
| 287 const KURL& url, |
| 288 const ResourceLoaderOptions& options) const { |
| 289 ResourceRequestBlockedReason blocked_reason = |
| 290 CanRequestInternal(type, resource_request, url, options, |
| 291 SecurityViolationReportingPolicy::kReport, |
| 292 FetchParameters::kUseDefaultOriginRestrictionForType, |
| 293 RedirectStatus::kFollowedRedirect); |
| 294 if (blocked_reason != ResourceRequestBlockedReason::kNone) { |
| 295 probe::didBlockRequest(worker_global_scope_, resource_request, nullptr, |
| 296 options.initiator_info, blocked_reason); |
| 297 } |
| 298 return blocked_reason; |
| 299 } |
| 300 |
| 301 void WorkerFetchContext::AddResourceTiming(const ResourceTimingInfo& info) { |
| 302 WorkerGlobalScopePerformance::performance(*worker_global_scope_) |
| 303 ->AddResourceTiming(info); |
| 304 } |
| 305 |
| 306 ResourceRequestBlockedReason WorkerFetchContext::CanRequestInternal( |
| 307 Resource::Type type, |
| 308 const ResourceRequest& resource_request, |
| 309 const KURL& url, |
| 310 const ResourceLoaderOptions& options, |
| 311 SecurityViolationReportingPolicy reporting_policy, |
| 312 FetchParameters::OriginRestriction origin_restriction, |
| 313 ResourceRequest::RedirectStatus redirect_status) const { |
| 314 bool should_block_request = false; |
| 315 probe::shouldBlockRequest(worker_global_scope_, resource_request, |
| 316 &should_block_request); |
| 317 if (should_block_request) |
| 318 return ResourceRequestBlockedReason::kInspector; |
| 319 |
| 320 SecurityOrigin* security_origin = options.security_origin.Get(); |
| 321 if (!security_origin) |
| 322 security_origin = worker_global_scope_->GetSecurityOrigin(); |
| 323 if (!security_origin->CanDisplay(url)) { |
| 324 if (origin_restriction == FetchParameters::kRestrictToSameOrigin) { |
| 325 return ResourceRequestBlockedReason::kOrigin; |
| 326 } |
| 327 } |
| 328 |
| 329 if (!url.User().IsEmpty() || !url.Pass().IsEmpty()) { |
| 330 // See https://crbug.com/504300 |
| 331 if (RuntimeEnabledFeatures::blockCredentialedSubresourcesEnabled()) |
| 332 return ResourceRequestBlockedReason::kOrigin; |
| 333 } |
| 334 |
| 335 // TODO(horo): Check GetStrictMixedContentChecking() |
| 336 // TODO(horo): Implement reporting. |
| 337 if (MixedContentChecker::IsMixedContent(security_origin, url)) |
| 338 return ResourceRequestBlockedReason::kMixedContent; |
| 339 |
| 340 // TODO(horo): Implement subresource filter. |
| 341 |
| 342 if (!worker_global_scope_->GetContentSecurityPolicy()->AllowRequest( |
| 343 resource_request.GetRequestContext(), url, |
| 344 options.content_security_policy_nonce, options.integrity_metadata, |
| 345 options.parser_disposition, redirect_status, reporting_policy)) { |
| 346 return ResourceRequestBlockedReason::CSP; |
| 347 } |
| 348 |
| 349 return ResourceRequestBlockedReason::kNone; |
| 350 } |
| 351 |
| 352 RefPtr<WebTaskRunner> WorkerFetchContext::TimerTaskRunner() const { |
| 353 return timer_task_runner_; |
| 354 } |
| 355 |
| 356 RefPtr<WebTaskRunner> WorkerFetchContext::LoadingTaskRunner() const { |
| 357 return loading_task_runner_; |
| 358 } |
| 359 |
| 360 Resource::ResourceCallback* WorkerFetchContext::GetResourceCallback() { |
| 361 return resource_callback_; |
| 362 } |
| 363 |
| 364 DEFINE_TRACE(WorkerFetchContext) { |
| 365 visitor->Trace(worker_global_scope_); |
| 366 visitor->Trace(resource_fetcher_); |
| 367 visitor->Trace(resource_callback_); |
| 368 FetchContext::Trace(visitor); |
| 369 } |
| 370 |
| 371 void ProvideWorkerFetchContextToWorker( |
| 372 WorkerClients* clients, |
| 373 std::unique_ptr<WebWorkerFetchContext> web_context) { |
| 374 DCHECK(clients); |
| 375 WorkerFetchContextInfo::ProvideTo( |
| 376 *clients, WorkerFetchContextInfo::SupplementName(), |
| 377 new WorkerFetchContextInfo(std::move(web_context))); |
| 378 } |
| 379 |
| 380 } // namespace blink |
OLD | NEW |