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/WorkerGlobalScope.h" |
| 11 #include "platform/RuntimeEnabledFeatures.h" |
| 12 #include "platform/WebTaskRunner.h" |
| 13 #include "platform/exported/WrappedResourceRequest.h" |
| 14 #include "platform/loader/fetch/ResourceFetcher.h" |
| 15 #include "platform/weborigin/SecurityPolicy.h" |
| 16 #include "public/platform/Platform.h" |
| 17 #include "public/platform/WebMixedContent.h" |
| 18 #include "public/platform/WebMixedContentContextType.h" |
| 19 #include "public/platform/WebScheduler.h" |
| 20 #include "public/platform/WebThread.h" |
| 21 #include "public/platform/WebWorkerFetchContext.h" |
| 22 |
| 23 namespace blink { |
| 24 |
| 25 namespace { |
| 26 |
| 27 class WorkerContextSupplement final |
| 28 : public GarbageCollectedFinalized<WorkerContextSupplement>, |
| 29 public Supplement<ExecutionContext> { |
| 30 USING_GARBAGE_COLLECTED_MIXIN(WorkerContextSupplement); |
| 31 |
| 32 public: |
| 33 static WorkerContextSupplement* from(ExecutionContext& executionContext) { |
| 34 if (!executionContext.isWorkerGlobalScope()) |
| 35 return nullptr; |
| 36 WorkerContextSupplement* supplement = static_cast<WorkerContextSupplement*>( |
| 37 Supplement<ExecutionContext>::from(executionContext, supplementName())); |
| 38 if (supplement) |
| 39 return supplement; |
| 40 WorkerClients* clients = toWorkerGlobalScope(executionContext).clients(); |
| 41 if (!clients) |
| 42 return nullptr; |
| 43 WorkerFetchContextInfo* contextInfo = |
| 44 WorkerFetchContextInfo::from(*clients); |
| 45 if (!contextInfo) |
| 46 return nullptr; |
| 47 WorkerFetchContext* workerFetchContext = WorkerFetchContext::create( |
| 48 toWorkerGlobalScope(executionContext), contextInfo); |
| 49 supplement = new WorkerContextSupplement(workerFetchContext); |
| 50 Supplement<ExecutionContext>::provideTo(executionContext, supplementName(), |
| 51 supplement); |
| 52 return supplement; |
| 53 } |
| 54 WorkerFetchContext* context() const { return m_workerFetchContext; } |
| 55 |
| 56 DEFINE_INLINE_VIRTUAL_TRACE() { |
| 57 visitor->trace(m_workerFetchContext); |
| 58 Supplement<ExecutionContext>::trace(visitor); |
| 59 } |
| 60 |
| 61 private: |
| 62 explicit WorkerContextSupplement(WorkerFetchContext* workerFetchContext) |
| 63 : m_workerFetchContext(workerFetchContext) {} |
| 64 static const char* supplementName() { return "WorkerContextSupplement"; } |
| 65 Member<WorkerFetchContext> m_workerFetchContext; |
| 66 }; |
| 67 } |
| 68 |
| 69 class WorkerFetchContext::WorkerResourceCallback final |
| 70 : public GarbageCollectedFinalized<WorkerResourceCallback>, |
| 71 public Resource::ResourceCallback { |
| 72 public: |
| 73 WorkerResourceCallback(WebTaskRunner* loadingTaskRunner) |
| 74 : m_loadingTaskRunner(loadingTaskRunner) {} |
| 75 ~WorkerResourceCallback() {} |
| 76 void schedule(Resource*) override; |
| 77 void cancel(Resource*) override; |
| 78 bool isScheduled(Resource*) const override; |
| 79 DECLARE_TRACE(); |
| 80 |
| 81 private: |
| 82 void runTask(); |
| 83 |
| 84 RefPtr<WebTaskRunner> m_loadingTaskRunner; |
| 85 TaskHandle m_taskHandle; |
| 86 HeapHashSet<Member<Resource>> m_resourcesWithPendingClients; |
| 87 }; |
| 88 |
| 89 DEFINE_TRACE(WorkerFetchContext::WorkerResourceCallback) { |
| 90 visitor->trace(m_resourcesWithPendingClients); |
| 91 } |
| 92 |
| 93 void WorkerFetchContext::WorkerResourceCallback::schedule(Resource* resource) { |
| 94 if (!m_taskHandle.isActive()) { |
| 95 m_taskHandle = m_loadingTaskRunner->postCancellableTask( |
| 96 BLINK_FROM_HERE, |
| 97 WTF::bind(&WorkerResourceCallback::runTask, wrapWeakPersistent(this))); |
| 98 } |
| 99 m_resourcesWithPendingClients.insert(resource); |
| 100 } |
| 101 |
| 102 void WorkerFetchContext::WorkerResourceCallback::cancel(Resource* resource) { |
| 103 m_resourcesWithPendingClients.erase(resource); |
| 104 if (m_taskHandle.isActive() && m_resourcesWithPendingClients.isEmpty()) |
| 105 m_taskHandle.cancel(); |
| 106 } |
| 107 |
| 108 bool WorkerFetchContext::WorkerResourceCallback::isScheduled( |
| 109 Resource* resource) const { |
| 110 return m_resourcesWithPendingClients.contains(resource); |
| 111 } |
| 112 |
| 113 void WorkerFetchContext::WorkerResourceCallback::runTask() { |
| 114 HeapVector<Member<Resource>> resources; |
| 115 for (const Member<Resource>& resource : m_resourcesWithPendingClients) |
| 116 resources.push_back(resource.get()); |
| 117 m_resourcesWithPendingClients.clear(); |
| 118 |
| 119 for (const auto& resource : resources) |
| 120 resource->finishPendingClients(); |
| 121 } |
| 122 |
| 123 WorkerFetchContext* WorkerFetchContext::create( |
| 124 WorkerGlobalScope& workerGlobalScope, |
| 125 WorkerFetchContextInfo* contextInfo) { |
| 126 return new WorkerFetchContext( |
| 127 workerGlobalScope, contextInfo->CreateContext(), |
| 128 contextInfo->isDataSaverEnabled(), |
| 129 contextInfo->isStrictMixedContentCheckingEnabled()); |
| 130 } |
| 131 |
| 132 WorkerFetchContext::~WorkerFetchContext() {} |
| 133 |
| 134 WorkerFetchContext* WorkerFetchContext::from( |
| 135 ExecutionContext& executionContext) { |
| 136 WorkerContextSupplement* supplement = |
| 137 WorkerContextSupplement::from(executionContext); |
| 138 if (!supplement) |
| 139 return nullptr; |
| 140 return supplement->context(); |
| 141 } |
| 142 |
| 143 WorkerFetchContext::WorkerFetchContext( |
| 144 WorkerGlobalScope& workerGlobalScope, |
| 145 std::unique_ptr<WebWorkerFetchContext> context, |
| 146 bool dataSaverEnabled, |
| 147 bool strictMixedContentCheckingEnabled) |
| 148 : m_workerGlobalScope(workerGlobalScope), |
| 149 m_context(std::move(context)), |
| 150 m_loadingTaskRunner(Platform::current() |
| 151 ->currentThread() |
| 152 ->scheduler() |
| 153 ->loadingTaskRunner()), |
| 154 m_timerTaskRunner( |
| 155 Platform::current()->currentThread()->scheduler()->timerTaskRunner()), |
| 156 m_dataSaverEnabled(dataSaverEnabled), |
| 157 m_strictMixedContentCheckingEnabled(strictMixedContentCheckingEnabled), |
| 158 m_resourceCallback( |
| 159 new WorkerResourceCallback(m_loadingTaskRunner.get())) {} |
| 160 |
| 161 bool WorkerFetchContext::isControlledByServiceWorker() const { |
| 162 return m_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 m_context->serviceWorkerID(); |
| 169 } |
| 170 |
| 171 ResourceRequestBlockedReason WorkerFetchContext::canRequest( |
| 172 Resource::Type type, |
| 173 const ResourceRequest& resourceRequest, |
| 174 const KURL& url, |
| 175 const ResourceLoaderOptions& options, |
| 176 SecurityViolationReportingPolicy reportingPolicy, |
| 177 FetchRequest::OriginRestriction originRestriction) const { |
| 178 ResourceRequestBlockedReason blockedReason = |
| 179 canRequestInternal(type, resourceRequest, url, options, reportingPolicy, |
| 180 originRestriction, resourceRequest.redirectStatus()); |
| 181 if (blockedReason != ResourceRequestBlockedReason::None) { |
| 182 probe::didBlockRequest(m_workerGlobalScope, resourceRequest, nullptr, |
| 183 options.initiatorInfo, blockedReason); |
| 184 } |
| 185 return blockedReason; |
| 186 } |
| 187 |
| 188 void WorkerFetchContext::addAdditionalRequestHeaders(ResourceRequest& request, |
| 189 FetchResourceType type) { |
| 190 bool isMainResource = type == FetchMainResource; |
| 191 if (!isMainResource) { |
| 192 if (!request.didSetHTTPReferrer()) { |
| 193 request.setHTTPReferrer(SecurityPolicy::generateReferrer( |
| 194 m_workerGlobalScope->getReferrerPolicy(), request.url(), |
| 195 m_workerGlobalScope->outgoingReferrer())); |
| 196 request.addHTTPOriginIfNeeded(m_workerGlobalScope->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 userAgent = m_workerGlobalScope->userAgent(); |
| 211 probe::applyUserAgentOverride(m_workerGlobalScope, &userAgent); |
| 212 DCHECK(!userAgent.isNull()); |
| 213 request.setHTTPUserAgent(AtomicString(userAgent)); |
| 214 |
| 215 if (m_dataSaverEnabled) |
| 216 request.setHTTPHeaderField("Save-Data", "on"); |
| 217 |
| 218 request.setIsMojoIPCForced(true); |
| 219 if (request.requestorOrigin()->isUnique() && |
| 220 !m_workerGlobalScope->getSecurityOrigin()->isUnique()) { |
| 221 request.setRequestorOrigin(m_workerGlobalScope->getSecurityOrigin()); |
| 222 } |
| 223 WrappedResourceRequest webreq(request); |
| 224 m_context->willSendRequest(webreq); |
| 225 } |
| 226 |
| 227 void WorkerFetchContext::dispatchWillSendRequest( |
| 228 unsigned long identifier, |
| 229 ResourceRequest& request, |
| 230 const ResourceResponse& redirectResponse, |
| 231 const FetchInitiatorInfo& initiatorInfo) { |
| 232 probe::willSendRequest(m_workerGlobalScope, identifier, nullptr, request, |
| 233 redirectResponse, initiatorInfo); |
| 234 } |
| 235 |
| 236 void WorkerFetchContext::dispatchDidReceiveResponse( |
| 237 unsigned long identifier, |
| 238 const ResourceResponse& response, |
| 239 WebURLRequest::FrameType frameType, |
| 240 WebURLRequest::RequestContext requestContext, |
| 241 Resource* resource, |
| 242 ResourceResponseType) { |
| 243 if (response.hasMajorCertificateErrors()) { |
| 244 WebMixedContentContextType contextType = |
| 245 WebMixedContent::contextTypeFromRequestContext( |
| 246 requestContext, false /* strictMixedContentCheckingForPlugin */); |
| 247 if (contextType == WebMixedContentContextType::Blockable) { |
| 248 m_context->didRunContentWithCertificateErrors(response.url()); |
| 249 } else { |
| 250 m_context->didDisplayContentWithCertificateErrors(response.url()); |
| 251 } |
| 252 } |
| 253 probe::didReceiveResourceResponse(m_workerGlobalScope, identifier, nullptr, |
| 254 response, resource); |
| 255 } |
| 256 |
| 257 void WorkerFetchContext::dispatchDidReceiveData(unsigned long identifier, |
| 258 const char* data, |
| 259 int dataLength) { |
| 260 probe::didReceiveData(m_workerGlobalScope, identifier, nullptr, data, |
| 261 dataLength); |
| 262 } |
| 263 |
| 264 void WorkerFetchContext::dispatchDidReceiveEncodedData(unsigned long identifier, |
| 265 int encodedDataLength) { |
| 266 probe::didReceiveEncodedDataLength(m_workerGlobalScope, identifier, |
| 267 encodedDataLength); |
| 268 } |
| 269 |
| 270 void WorkerFetchContext::dispatchDidFinishLoading(unsigned long identifier, |
| 271 double finishTime, |
| 272 int64_t encodedDataLength, |
| 273 int64_t decodedBodyLength) { |
| 274 probe::didFinishLoading(m_workerGlobalScope, identifier, nullptr, finishTime, |
| 275 encodedDataLength, decodedBodyLength); |
| 276 } |
| 277 |
| 278 void WorkerFetchContext::dispatchDidFail(unsigned long identifier, |
| 279 const ResourceError& error, |
| 280 int64_t encodedDataLength, |
| 281 bool isInternalRequest) { |
| 282 probe::didFailLoading(m_workerGlobalScope, identifier, error); |
| 283 } |
| 284 |
| 285 ResourceRequestBlockedReason WorkerFetchContext::allowResponse( |
| 286 Resource::Type type, |
| 287 const ResourceRequest& resourceRequest, |
| 288 const KURL& url, |
| 289 const ResourceLoaderOptions& options) const { |
| 290 ResourceRequestBlockedReason blockedReason = |
| 291 canRequestInternal(type, resourceRequest, url, options, |
| 292 SecurityViolationReportingPolicy::Report, |
| 293 FetchRequest::UseDefaultOriginRestrictionForType, |
| 294 RedirectStatus::FollowedRedirect); |
| 295 if (blockedReason != ResourceRequestBlockedReason::None) { |
| 296 probe::didBlockRequest(m_workerGlobalScope, resourceRequest, nullptr, |
| 297 options.initiatorInfo, blockedReason); |
| 298 } |
| 299 return blockedReason; |
| 300 } |
| 301 |
| 302 void WorkerFetchContext::addResourceTiming(const ResourceTimingInfo& info) { |
| 303 WorkerGlobalScopePerformance::performance(*m_workerGlobalScope) |
| 304 ->addResourceTiming(info); |
| 305 } |
| 306 |
| 307 ResourceRequestBlockedReason WorkerFetchContext::canRequestInternal( |
| 308 Resource::Type type, |
| 309 const ResourceRequest& resourceRequest, |
| 310 const KURL& url, |
| 311 const ResourceLoaderOptions& options, |
| 312 SecurityViolationReportingPolicy reportingPolicy, |
| 313 FetchRequest::OriginRestriction originRestriction, |
| 314 ResourceRequest::RedirectStatus redirectStatus) const { |
| 315 bool shouldBlockRequest = false; |
| 316 probe::shouldBlockRequest(m_workerGlobalScope, resourceRequest, |
| 317 &shouldBlockRequest); |
| 318 if (shouldBlockRequest) |
| 319 return ResourceRequestBlockedReason::Inspector; |
| 320 |
| 321 SecurityOrigin* securityOrigin = options.securityOrigin.get(); |
| 322 if (!securityOrigin) |
| 323 securityOrigin = m_workerGlobalScope->getSecurityOrigin(); |
| 324 if (!securityOrigin->canDisplay(url)) { |
| 325 if (originRestriction == FetchRequest::RestrictToSameOrigin) { |
| 326 return ResourceRequestBlockedReason::Origin; |
| 327 } |
| 328 } |
| 329 |
| 330 if (!url.user().isEmpty() || !url.pass().isEmpty()) { |
| 331 // See https://crbug.com/504300 |
| 332 if (RuntimeEnabledFeatures::blockCredentialedSubresourcesEnabled()) |
| 333 return ResourceRequestBlockedReason::Origin; |
| 334 } |
| 335 |
| 336 // TODO(horo): Check m_strictMixedContentCheckingEnabled |
| 337 // TODO(horo): Implement reporting. |
| 338 if (MixedContentChecker::isMixedContent(securityOrigin, url)) |
| 339 return ResourceRequestBlockedReason::MixedContent; |
| 340 |
| 341 // TODO(horo): Implement subresource filter. |
| 342 |
| 343 if (!m_workerGlobalScope->contentSecurityPolicy()->allowRequest( |
| 344 resourceRequest.requestContext(), url, |
| 345 options.contentSecurityPolicyNonce, options.integrityMetadata, |
| 346 options.parserDisposition, redirectStatus, reportingPolicy)) { |
| 347 return ResourceRequestBlockedReason::CSP; |
| 348 } |
| 349 |
| 350 return ResourceRequestBlockedReason::None; |
| 351 } |
| 352 |
| 353 WebURLLoader* WorkerFetchContext::createURLLoader() { |
| 354 return m_context->createURLLoader(); |
| 355 } |
| 356 |
| 357 RefPtr<WebTaskRunner> WorkerFetchContext::timerTaskRunner() const { |
| 358 return m_timerTaskRunner; |
| 359 } |
| 360 |
| 361 RefPtr<WebTaskRunner> WorkerFetchContext::loadingTaskRunner() const { |
| 362 return m_loadingTaskRunner; |
| 363 } |
| 364 |
| 365 Resource::ResourceCallback* WorkerFetchContext::resourceCallback() { |
| 366 return m_resourceCallback; |
| 367 } |
| 368 |
| 369 ResourceFetcher* WorkerFetchContext::getResourceFetcher() { |
| 370 if (m_resourceFetcher) |
| 371 return m_resourceFetcher; |
| 372 m_resourceFetcher = ResourceFetcher::create(this); |
| 373 return m_resourceFetcher; |
| 374 } |
| 375 |
| 376 DEFINE_TRACE(WorkerFetchContext) { |
| 377 visitor->trace(m_workerGlobalScope); |
| 378 visitor->trace(m_resourceFetcher); |
| 379 visitor->trace(m_resourceCallback); |
| 380 FetchContext::trace(visitor); |
| 381 } |
| 382 |
| 383 // static |
| 384 WorkerFetchContextInfo* WorkerFetchContextInfo::create( |
| 385 std::unique_ptr<WebWorkerFetchContextInfo> info) { |
| 386 return new WorkerFetchContextInfo(std::move(info)); |
| 387 } |
| 388 |
| 389 // static |
| 390 WorkerFetchContextInfo* WorkerFetchContextInfo::from(WorkerClients& clients) { |
| 391 return static_cast<WorkerFetchContextInfo*>( |
| 392 Supplement<WorkerClients>::from(clients, supplementName())); |
| 393 } |
| 394 |
| 395 WorkerFetchContextInfo::WorkerFetchContextInfo( |
| 396 std::unique_ptr<WebWorkerFetchContextInfo> info) |
| 397 : m_info(std::move(info)) {} |
| 398 |
| 399 WorkerFetchContextInfo::~WorkerFetchContextInfo() {} |
| 400 |
| 401 const char* WorkerFetchContextInfo::supplementName() { |
| 402 return "WorkerFetchContextInfo"; |
| 403 } |
| 404 |
| 405 std::unique_ptr<WebWorkerFetchContext> WorkerFetchContextInfo::CreateContext() { |
| 406 DCHECK(m_info); |
| 407 DCHECK(!isMainThread()); |
| 408 std::unique_ptr<WebWorkerFetchContext> webContext = |
| 409 m_info->CreateContext(Platform::current() |
| 410 ->currentThread() |
| 411 ->scheduler() |
| 412 ->loadingTaskRunner() |
| 413 ->toSingleThreadTaskRunner()); |
| 414 m_info.reset(); |
| 415 return webContext; |
| 416 } |
| 417 |
| 418 void provideWorkerFetchContextInfoToWorker( |
| 419 WorkerClients* clients, |
| 420 std::unique_ptr<WebWorkerFetchContextInfo> info) { |
| 421 DCHECK(clients); |
| 422 WorkerFetchContextInfo::provideTo( |
| 423 *clients, WorkerFetchContextInfo::supplementName(), |
| 424 WorkerFetchContextInfo::create(std::move(info))); |
| 425 } |
| 426 |
| 427 } // namespace blink |
OLD | NEW |