1 /* 2 Copyright (C) 1998 Lars Knoll (knoll (at) mpi-hd.mpg.de) 3 Copyright (C) 2001 Dirk Mueller (mueller (at) kde.org) 4 Copyright (C) 2002 Waldo Bastian (bastian (at) kde.org) 5 Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. 6 Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/ 7 8 This library is free software; you can redistribute it and/or 9 modify it under the terms of the GNU Library General Public 10 License as published by the Free Software Foundation; either 11 version 2 of the License, or (at your option) any later version. 12 13 This library is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 Library General Public License for more details. 17 18 You should have received a copy of the GNU Library General Public License 19 along with this library; see the file COPYING.LIB. If not, write to 20 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 Boston, MA 02110-1301, USA. 22 23 This class provides all functionality needed for loading images, style sheets and html 24 pages from the web. It has a memory cache for these objects. 25 */ 26 27 #include "config.h" 28 #include "core/fetch/ResourceFetcher.h" 29 30 #include "bindings/core/v8/ScriptController.h" 31 #include "bindings/core/v8/V8DOMActivityLogger.h" 32 #include "core/FetchInitiatorTypeNames.h" 33 #include "core/dom/Document.h" 34 #include "core/fetch/CSSStyleSheetResource.h" 35 #include "core/fetch/CrossOriginAccessControl.h" 36 #include "core/fetch/DocumentResource.h" 37 #include "core/fetch/FetchContext.h" 38 #include "core/fetch/FontResource.h" 39 #include "core/fetch/ImageResource.h" 40 #include "core/fetch/MemoryCache.h" 41 #include "core/fetch/RawResource.h" 42 #include "core/fetch/ResourceLoader.h" 43 #include "core/fetch/ResourceLoaderSet.h" 44 #include "core/fetch/ScriptResource.h" 45 #include "core/fetch/XSLStyleSheetResource.h" 46 #include "core/html/HTMLElement.h" 47 #include "core/html/HTMLFrameOwnerElement.h" 48 #include "core/html/imports/HTMLImportsController.h" 49 #include "core/inspector/ConsoleMessage.h" 50 #include "core/inspector/InspectorInstrumentation.h" 51 #include "core/loader/DocumentLoader.h" 52 #include "core/loader/FrameLoader.h" 53 #include "core/loader/FrameLoaderClient.h" 54 #include "core/loader/PingLoader.h" 55 #include "core/loader/SubstituteData.h" 56 #include "core/loader/UniqueIdentifier.h" 57 #include "core/loader/appcache/ApplicationCacheHost.h" 58 #include "core/frame/LocalDOMWindow.h" 59 #include "core/frame/LocalFrame.h" 60 #include "core/frame/csp/ContentSecurityPolicy.h" 61 #include "core/timing/Performance.h" 62 #include "core/timing/ResourceTimingInfo.h" 63 #include "core/frame/Settings.h" 64 #include "core/svg/graphics/SVGImageChromeClient.h" 65 #include "platform/Logging.h" 66 #include "platform/RuntimeEnabledFeatures.h" 67 #include "platform/TraceEvent.h" 68 #include "platform/weborigin/SchemeRegistry.h" 69 #include "platform/weborigin/SecurityOrigin.h" 70 #include "platform/weborigin/SecurityPolicy.h" 71 #include "public/platform/Platform.h" 72 #include "public/platform/WebURL.h" 73 #include "public/platform/WebURLRequest.h" 74 #include "wtf/text/CString.h" 75 #include "wtf/text/WTFString.h" 76 77 #define PRELOAD_DEBUG 0 78 79 using blink::WebURLRequest; 80 81 namespace blink { 82 83 static Resource* createResource(Resource::Type type, const ResourceRequest& request, const String& charset) 84 { 85 switch (type) { 86 case Resource::Image: 87 return new ImageResource(request); 88 case Resource::CSSStyleSheet: 89 return new CSSStyleSheetResource(request, charset); 90 case Resource::Script: 91 return new ScriptResource(request, charset); 92 case Resource::SVGDocument: 93 return new DocumentResource(request, Resource::SVGDocument); 94 case Resource::Font: 95 return new FontResource(request); 96 case Resource::MainResource: 97 case Resource::Raw: 98 case Resource::TextTrack: 99 case Resource::Media: 100 return new RawResource(request, type); 101 case Resource::XSLStyleSheet: 102 return new XSLStyleSheetResource(request, charset); 103 case Resource::LinkPrefetch: 104 return new Resource(request, Resource::LinkPrefetch); 105 case Resource::LinkSubresource: 106 return new Resource(request, Resource::LinkSubresource); 107 case Resource::ImportResource: 108 return new RawResource(request, type); 109 } 110 111 ASSERT_NOT_REACHED(); 112 return 0; 113 } 114 115 static ResourceLoadPriority loadPriority(Resource::Type type, const FetchRequest& request) 116 { 117 if (request.priority() != ResourceLoadPriorityUnresolved) 118 return request.priority(); 119 120 switch (type) { 121 case Resource::MainResource: 122 return ResourceLoadPriorityVeryHigh; 123 case Resource::CSSStyleSheet: 124 return ResourceLoadPriorityHigh; 125 case Resource::Raw: 126 return request.options().synchronousPolicy == RequestSynchronously ? ResourceLoadPriorityVeryHigh : ResourceLoadPriorityMedium; 127 case Resource::Script: 128 // Async scripts do not block the parser so they get the lowest priority and can be 129 // loaded in parser order with images. 130 if (FetchRequest::LazyLoad == request.defer()) 131 return ResourceLoadPriorityLow; 132 return ResourceLoadPriorityMedium; 133 case Resource::Font: 134 case Resource::ImportResource: 135 return ResourceLoadPriorityMedium; 136 case Resource::Image: 137 // Default images to VeryLow, and promote whatever is visible. This improves 138 // speed-index by ~5% on average, ~14% at the 99th percentile. 139 return ResourceLoadPriorityVeryLow; 140 case Resource::Media: 141 return ResourceLoadPriorityLow; 142 case Resource::XSLStyleSheet: 143 ASSERT(RuntimeEnabledFeatures::xsltEnabled()); 144 return ResourceLoadPriorityHigh; 145 case Resource::SVGDocument: 146 return ResourceLoadPriorityLow; 147 case Resource::LinkPrefetch: 148 return ResourceLoadPriorityVeryLow; 149 case Resource::LinkSubresource: 150 return ResourceLoadPriorityLow; 151 case Resource::TextTrack: 152 return ResourceLoadPriorityLow; 153 } 154 ASSERT_NOT_REACHED(); 155 return ResourceLoadPriorityUnresolved; 156 } 157 158 static Resource* resourceFromDataURIRequest(const ResourceRequest& request, const ResourceLoaderOptions& resourceOptions) 159 { 160 const KURL& url = request.url(); 161 ASSERT(url.protocolIsData()); 162 163 blink::WebString mimetype; 164 blink::WebString charset; 165 RefPtr<SharedBuffer> data = PassRefPtr<SharedBuffer>(blink::Platform::current()->parseDataURL(url, mimetype, charset)); 166 if (!data) 167 return 0; 168 ResourceResponse response(url, mimetype, data->size(), charset, String()); 169 170 Resource* resource = createResource(Resource::Image, request, charset); 171 resource->setOptions(resourceOptions); 172 resource->responseReceived(response); 173 if (data->size()) 174 resource->setResourceBuffer(data); 175 resource->finish(); 176 return resource; 177 } 178 179 static void populateResourceTiming(ResourceTimingInfo* info, Resource* resource, bool clearLoadTimings) 180 { 181 info->setInitialRequest(resource->resourceRequest()); 182 info->setFinalResponse(resource->response()); 183 if (clearLoadTimings) { 184 info->clearLoadTimings(); 185 info->setLoadFinishTime(info->initialTime()); 186 } else { 187 info->setLoadFinishTime(resource->loadFinishTime()); 188 } 189 } 190 191 static void reportResourceTiming(ResourceTimingInfo* info, Document* initiatorDocument, bool isMainResource) 192 { 193 if (initiatorDocument && isMainResource) 194 initiatorDocument = initiatorDocument->parentDocument(); 195 if (!initiatorDocument || !initiatorDocument->loader()) 196 return; 197 if (LocalDOMWindow* initiatorWindow = initiatorDocument->domWindow()) 198 initiatorWindow->performance().addResourceTiming(*info, initiatorDocument); 199 } 200 201 static WebURLRequest::RequestContext requestContextFromType(const ResourceFetcher* fetcher, Resource::Type type) 202 { 203 switch (type) { 204 case Resource::MainResource: 205 if (fetcher->frame()->tree().parent()) 206 return WebURLRequest::RequestContextIframe; 207 // FIXME: Change this to a context frame type (once we introduce them): http://fetch.spec.whatwg.org/#concept-request-context-frame-type 208 return WebURLRequest::RequestContextHyperlink; 209 case Resource::XSLStyleSheet: 210 ASSERT(RuntimeEnabledFeatures::xsltEnabled()); 211 case Resource::CSSStyleSheet: 212 return WebURLRequest::RequestContextStyle; 213 case Resource::Script: 214 return WebURLRequest::RequestContextScript; 215 case Resource::Font: 216 return WebURLRequest::RequestContextFont; 217 case Resource::Image: 218 return WebURLRequest::RequestContextImage; 219 case Resource::Raw: 220 return WebURLRequest::RequestContextSubresource; 221 case Resource::ImportResource: 222 return WebURLRequest::RequestContextImport; 223 case Resource::LinkPrefetch: 224 return WebURLRequest::RequestContextPrefetch; 225 case Resource::LinkSubresource: 226 return WebURLRequest::RequestContextSubresource; 227 case Resource::TextTrack: 228 return WebURLRequest::RequestContextTrack; 229 case Resource::SVGDocument: 230 return WebURLRequest::RequestContextImage; 231 case Resource::Media: // TODO: Split this. 232 return WebURLRequest::RequestContextVideo; 233 } 234 ASSERT_NOT_REACHED(); 235 return WebURLRequest::RequestContextSubresource; 236 } 237 238 static ResourceRequestCachePolicy memoryCachePolicyToResourceRequestCachePolicy( 239 const CachePolicy policy) { 240 if (policy == CachePolicyVerify) 241 return UseProtocolCachePolicy; 242 if (policy == CachePolicyRevalidate) 243 return ReloadIgnoringCacheData; 244 if (policy == CachePolicyReload) 245 return ReloadBypassingCache; 246 if (policy == CachePolicyHistoryBuffer) 247 return ReturnCacheDataElseLoad; 248 return UseProtocolCachePolicy; 249 } 250 251 ResourceFetcher::ResourceFetcher(DocumentLoader* documentLoader) 252 : m_document(nullptr) 253 , m_documentLoader(documentLoader) 254 , m_requestCount(0) 255 , m_garbageCollectDocumentResourcesTimer(this, &ResourceFetcher::garbageCollectDocumentResourcesTimerFired) 256 , m_resourceTimingReportTimer(this, &ResourceFetcher::resourceTimingReportTimerFired) 257 , m_autoLoadImages(true) 258 , m_imagesEnabled(true) 259 , m_allowStaleResources(false) 260 { 261 } 262 263 ResourceFetcher::~ResourceFetcher() 264 { 265 m_documentLoader = 0; 266 m_document = nullptr; 267 268 clearPreloads(); 269 270 // Make sure no requests still point to this ResourceFetcher 271 ASSERT(!m_requestCount); 272 } 273 274 Resource* ResourceFetcher::cachedResource(const KURL& resourceURL) const 275 { 276 KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(resourceURL); 277 return m_documentResources.get(url).get(); 278 } 279 280 LocalFrame* ResourceFetcher::frame() const 281 { 282 if (m_documentLoader) 283 return m_documentLoader->frame(); 284 if (m_document && m_document->importsController()) 285 return m_document->importsController()->master()->frame(); 286 return 0; 287 } 288 289 FetchContext& ResourceFetcher::context() const 290 { 291 if (LocalFrame* frame = this->frame()) 292 return frame->fetchContext(); 293 return FetchContext::nullInstance(); 294 } 295 296 ResourcePtr<Resource> ResourceFetcher::fetchSynchronously(FetchRequest& request) 297 { 298 ASSERT(document()); 299 request.mutableResourceRequest().setTimeoutInterval(10); 300 ResourceLoaderOptions options(request.options()); 301 options.synchronousPolicy = RequestSynchronously; 302 request.setOptions(options); 303 return requestResource(Resource::Raw, request); 304 } 305 306 ResourcePtr<ImageResource> ResourceFetcher::fetchImage(FetchRequest& request) 307 { 308 if (request.resourceRequest().requestContext() == WebURLRequest::RequestContextUnspecified) 309 request.mutableResourceRequest().setRequestContext(WebURLRequest::RequestContextImage); 310 if (LocalFrame* f = frame()) { 311 if (f->document()->pageDismissalEventBeingDispatched() != Document::NoDismissal) { 312 KURL requestURL = request.resourceRequest().url(); 313 if (requestURL.isValid() && canRequest(Resource::Image, request.resourceRequest(), requestURL, request.options(), request.forPreload(), request.originRestriction())) 314 PingLoader::loadImage(f, requestURL); 315 return 0; 316 } 317 } 318 319 if (request.resourceRequest().url().protocolIsData()) 320 preCacheDataURIImage(request); 321 322 request.setDefer(clientDefersImage(request.resourceRequest().url()) ? FetchRequest::DeferredByClient : FetchRequest::NoDefer); 323 ResourcePtr<Resource> resource = requestResource(Resource::Image, request); 324 return resource && resource->type() == Resource::Image ? toImageResource(resource) : 0; 325 } 326 327 void ResourceFetcher::preCacheDataURIImage(const FetchRequest& request) 328 { 329 const KURL& url = request.resourceRequest().url(); 330 ASSERT(url.protocolIsData()); 331 332 if (memoryCache()->resourceForURL(url)) 333 return; 334 335 if (Resource* resource = resourceFromDataURIRequest(request.resourceRequest(), request.options())) { 336 memoryCache()->add(resource); 337 scheduleDocumentResourcesGC(); 338 } 339 } 340 341 ResourcePtr<FontResource> ResourceFetcher::fetchFont(FetchRequest& request) 342 { 343 ASSERT(request.resourceRequest().frameType() == WebURLRequest::FrameTypeNone); 344 request.mutableResourceRequest().setRequestContext(WebURLRequest::RequestContextFont); 345 return toFontResource(requestResource(Resource::Font, request)); 346 } 347 348 ResourcePtr<RawResource> ResourceFetcher::fetchImport(FetchRequest& request) 349 { 350 ASSERT(request.resourceRequest().frameType() == WebURLRequest::FrameTypeNone); 351 request.mutableResourceRequest().setRequestContext(WebURLRequest::RequestContextImport); 352 return toRawResource(requestResource(Resource::ImportResource, request)); 353 } 354 355 ResourcePtr<CSSStyleSheetResource> ResourceFetcher::fetchCSSStyleSheet(FetchRequest& request) 356 { 357 ASSERT(request.resourceRequest().frameType() == WebURLRequest::FrameTypeNone); 358 request.mutableResourceRequest().setRequestContext(WebURLRequest::RequestContextStyle); 359 return toCSSStyleSheetResource(requestResource(Resource::CSSStyleSheet, request)); 360 } 361 362 ResourcePtr<ScriptResource> ResourceFetcher::fetchScript(FetchRequest& request) 363 { 364 ASSERT(request.resourceRequest().frameType() == WebURLRequest::FrameTypeNone); 365 request.mutableResourceRequest().setRequestContext(WebURLRequest::RequestContextScript); 366 return toScriptResource(requestResource(Resource::Script, request)); 367 } 368 369 ResourcePtr<XSLStyleSheetResource> ResourceFetcher::fetchXSLStyleSheet(FetchRequest& request) 370 { 371 ASSERT(RuntimeEnabledFeatures::xsltEnabled()); 372 request.mutableResourceRequest().setRequestContext(WebURLRequest::RequestContextXSLT); 373 return toXSLStyleSheetResource(requestResource(Resource::XSLStyleSheet, request)); 374 } 375 376 ResourcePtr<DocumentResource> ResourceFetcher::fetchSVGDocument(FetchRequest& request) 377 { 378 ASSERT(request.resourceRequest().frameType() == WebURLRequest::FrameTypeNone); 379 request.mutableResourceRequest().setRequestContext(WebURLRequest::RequestContextImage); 380 return toDocumentResource(requestResource(Resource::SVGDocument, request)); 381 } 382 383 ResourcePtr<Resource> ResourceFetcher::fetchLinkResource(Resource::Type type, FetchRequest& request) 384 { 385 ASSERT(frame()); 386 ASSERT(type == Resource::LinkPrefetch || type == Resource::LinkSubresource); 387 ASSERT(request.resourceRequest().frameType() == WebURLRequest::FrameTypeNone); 388 request.mutableResourceRequest().setRequestContext(type == Resource::LinkPrefetch ? WebURLRequest::RequestContextPrefetch : WebURLRequest::RequestContextSubresource); 389 return requestResource(type, request); 390 } 391 392 ResourcePtr<RawResource> ResourceFetcher::fetchRawResource(FetchRequest& request) 393 { 394 ASSERT(request.resourceRequest().frameType() == WebURLRequest::FrameTypeNone); 395 ASSERT(request.resourceRequest().requestContext() != WebURLRequest::RequestContextUnspecified); 396 return toRawResource(requestResource(Resource::Raw, request)); 397 } 398 399 ResourcePtr<RawResource> ResourceFetcher::fetchMainResource(FetchRequest& request, const SubstituteData& substituteData) 400 { 401 ASSERT(request.resourceRequest().frameType() != WebURLRequest::FrameTypeNone); 402 ASSERT(request.resourceRequest().requestContext() == WebURLRequest::RequestContextForm || request.resourceRequest().requestContext() == WebURLRequest::RequestContextFrame || request.resourceRequest().requestContext() == WebURLRequest::RequestContextHyperlink || request.resourceRequest().requestContext() == WebURLRequest::RequestContextIframe || request.resourceRequest().requestContext() == WebURLRequest::RequestContextInternal || request.resourceRequest().requestContext() == WebURLRequest::RequestContextLocation); 403 404 if (substituteData.isValid()) 405 preCacheSubstituteDataForMainResource(request, substituteData); 406 return toRawResource(requestResource(Resource::MainResource, request)); 407 } 408 409 ResourcePtr<RawResource> ResourceFetcher::fetchMedia(FetchRequest& request) 410 { 411 ASSERT(request.resourceRequest().frameType() == WebURLRequest::FrameTypeNone); 412 // FIXME: Split this into audio and video. 413 request.mutableResourceRequest().setRequestContext(WebURLRequest::RequestContextVideo); 414 return toRawResource(requestResource(Resource::Media, request)); 415 } 416 417 ResourcePtr<RawResource> ResourceFetcher::fetchTextTrack(FetchRequest& request) 418 { 419 ASSERT(request.resourceRequest().frameType() == WebURLRequest::FrameTypeNone); 420 request.mutableResourceRequest().setRequestContext(WebURLRequest::RequestContextTrack); 421 return toRawResource(requestResource(Resource::TextTrack, request)); 422 } 423 424 void ResourceFetcher::preCacheSubstituteDataForMainResource(const FetchRequest& request, const SubstituteData& substituteData) 425 { 426 const KURL& url = request.url(); 427 if (Resource* oldResource = memoryCache()->resourceForURL(url)) 428 memoryCache()->remove(oldResource); 429 430 ResourceResponse response(url, substituteData.mimeType(), substituteData.content()->size(), substituteData.textEncoding(), emptyString()); 431 ResourcePtr<Resource> resource = createResource(Resource::MainResource, request.resourceRequest(), substituteData.textEncoding()); 432 resource->setNeedsSynchronousCacheHit(substituteData.forceSynchronousLoad()); 433 resource->setOptions(request.options()); 434 resource->setDataBufferingPolicy(BufferData); 435 resource->responseReceived(response); 436 if (substituteData.content()->size()) 437 resource->setResourceBuffer(substituteData.content()); 438 resource->finish(); 439 memoryCache()->add(resource.get()); 440 } 441 442 bool ResourceFetcher::canRequest(Resource::Type type, const ResourceRequest& resourceRequest, const KURL& url, const ResourceLoaderOptions& options, bool forPreload, FetchRequest::OriginRestriction originRestriction) const 443 { 444 SecurityOrigin* securityOrigin = options.securityOrigin.get(); 445 if (!securityOrigin && document()) 446 securityOrigin = document()->securityOrigin(); 447 448 if (originRestriction != FetchRequest::NoOriginRestriction && securityOrigin && !securityOrigin->canDisplay(url)) { 449 if (!forPreload) 450 context().reportLocalLoadFailed(url); 451 WTF_LOG(ResourceLoading, "ResourceFetcher::requestResource URL was not allowed by SecurityOrigin::canDisplay"); 452 return 0; 453 } 454 455 // Some types of resources can be loaded only from the same origin. Other 456 // types of resources, like Images, Scripts, and CSS, can be loaded from 457 // any URL. 458 switch (type) { 459 case Resource::MainResource: 460 case Resource::Image: 461 case Resource::CSSStyleSheet: 462 case Resource::Script: 463 case Resource::Font: 464 case Resource::Raw: 465 case Resource::LinkPrefetch: 466 case Resource::LinkSubresource: 467 case Resource::TextTrack: 468 case Resource::ImportResource: 469 case Resource::Media: 470 // By default these types of resources can be loaded from any origin. 471 // FIXME: Are we sure about Resource::Font? 472 if (originRestriction == FetchRequest::RestrictToSameOrigin && !securityOrigin->canRequest(url)) { 473 printAccessDeniedMessage(url); 474 return false; 475 } 476 break; 477 case Resource::XSLStyleSheet: 478 ASSERT(RuntimeEnabledFeatures::xsltEnabled()); 479 case Resource::SVGDocument: 480 if (!securityOrigin->canRequest(url)) { 481 printAccessDeniedMessage(url); 482 return false; 483 } 484 break; 485 } 486 487 // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved. 488 bool shouldBypassMainWorldCSP = (frame() && frame()->script().shouldBypassMainWorldCSP()) || (options.contentSecurityPolicyOption == DoNotCheckContentSecurityPolicy); 489 490 // Don't send CSP messages for preloads, we might never actually display those items. 491 ContentSecurityPolicy::ReportingStatus cspReporting = forPreload ? 492 ContentSecurityPolicy::SuppressReport : ContentSecurityPolicy::SendReport; 493 494 // m_document can be null, but not in any of the cases where csp is actually used below. 495 // ImageResourceTest.MultipartImage crashes w/o the m_document null check. 496 // I believe it's the Resource::Raw case. 497 const ContentSecurityPolicy* csp = m_document ? m_document->contentSecurityPolicy() : nullptr; 498 499 // FIXME: This would be cleaner if moved this switch into an allowFromSource() 500 // helper on this object which took a Resource::Type, then this block would 501 // collapse to about 10 lines for handling Raw and Script special cases. 502 switch (type) { 503 case Resource::XSLStyleSheet: 504 ASSERT(RuntimeEnabledFeatures::xsltEnabled()); 505 if (!shouldBypassMainWorldCSP && !csp->allowScriptFromSource(url, cspReporting)) 506 return false; 507 break; 508 case Resource::Script: 509 case Resource::ImportResource: 510 if (!shouldBypassMainWorldCSP && !csp->allowScriptFromSource(url, cspReporting)) 511 return false; 512 513 if (frame()) { 514 Settings* settings = frame()->settings(); 515 if (!frame()->loader().client()->allowScriptFromSource(!settings || settings->scriptEnabled(), url)) { 516 frame()->loader().client()->didNotAllowScript(); 517 return false; 518 } 519 } 520 break; 521 case Resource::CSSStyleSheet: 522 if (!shouldBypassMainWorldCSP && !csp->allowStyleFromSource(url, cspReporting)) 523 return false; 524 break; 525 case Resource::SVGDocument: 526 case Resource::Image: 527 if (!shouldBypassMainWorldCSP && !csp->allowImageFromSource(url, cspReporting)) 528 return false; 529 break; 530 case Resource::Font: { 531 if (!shouldBypassMainWorldCSP && !csp->allowFontFromSource(url, cspReporting)) 532 return false; 533 break; 534 } 535 case Resource::MainResource: 536 case Resource::Raw: 537 case Resource::LinkPrefetch: 538 case Resource::LinkSubresource: 539 break; 540 case Resource::Media: 541 case Resource::TextTrack: 542 if (!shouldBypassMainWorldCSP && !csp->allowMediaFromSource(url, cspReporting)) 543 return false; 544 545 if (frame()) { 546 if (!frame()->loader().client()->allowMedia(url)) 547 return false; 548 } 549 break; 550 } 551 552 // SVG Images have unique security rules that prevent all subresource requests 553 // except for data urls. 554 if (type != Resource::MainResource) { 555 if (frame() && frame()->chromeClient().isSVGImageChromeClient() && !url.protocolIsData()) 556 return false; 557 } 558 559 // Measure the number of legacy URL schemes ('ftp://') and the number of embedded-credential 560 // ('http://user:password@...') resources embedded as subresources. in the hopes that we can 561 // block them at some point in the future. 562 if (resourceRequest.frameType() != WebURLRequest::FrameTypeTopLevel) { 563 if (SchemeRegistry::shouldTreatURLSchemeAsLegacy(url.protocol()) && !SchemeRegistry::shouldTreatURLSchemeAsLegacy(frame()->document()->securityOrigin()->protocol())) 564 UseCounter::count(frame()->document(), UseCounter::LegacyProtocolEmbeddedAsSubresource); 565 if (!url.user().isEmpty() || !url.pass().isEmpty()) 566 UseCounter::count(frame()->document(), UseCounter::RequestedSubresourceWithEmbeddedCredentials); 567 } 568 569 // Last of all, check for mixed content. We do this last so that when 570 // folks block mixed content with a CSP policy, they don't get a warning. 571 // They'll still get a warning in the console about CSP blocking the load. 572 573 // If we're loading the main resource of a subframe, ensure that we check 574 // against the parent of the active frame, rather than the frame itself. 575 LocalFrame* effectiveFrame = frame(); 576 if (resourceRequest.frameType() == WebURLRequest::FrameTypeNested) { 577 // FIXME: Deal with RemoteFrames. 578 if (frame()->tree().parent()->isLocalFrame()) 579 effectiveFrame = toLocalFrame(frame()->tree().parent()); 580 } 581 582 return !MixedContentChecker::shouldBlockFetch(effectiveFrame, resourceRequest, url); 583 } 584 585 bool ResourceFetcher::canAccessResource(Resource* resource, SecurityOrigin* sourceOrigin, const KURL& url) const 586 { 587 // Redirects can change the response URL different from one of request. 588 if (!canRequest(resource->type(), resource->resourceRequest(), url, resource->options(), resource->isUnusedPreload(), FetchRequest::UseDefaultOriginRestrictionForType)) 589 return false; 590 591 if (!sourceOrigin && document()) 592 sourceOrigin = document()->securityOrigin(); 593 594 if (sourceOrigin->canRequest(url)) 595 return true; 596 597 String errorDescription; 598 if (!resource->passesAccessControlCheck(sourceOrigin, errorDescription)) { 599 if (resource->type() == Resource::Font) 600 toFontResource(resource)->setCORSFailed(); 601 if (frame() && frame()->document()) { 602 String resourceType = Resource::resourceTypeToString(resource->type(), resource->options().initiatorInfo); 603 frame()->document()->addConsoleMessage(ConsoleMessage::create(JSMessageSource, ErrorMessageLevel, resourceType + " from origin '" + SecurityOrigin::create(url)->toString() + "' has been blocked from loading by Cross-Origin Resource Sharing policy: " + errorDescription)); 604 } 605 return false; 606 } 607 return true; 608 } 609 610 bool ResourceFetcher::shouldLoadNewResource(Resource::Type type) const 611 { 612 if (!frame()) 613 return false; 614 if (!m_documentLoader) 615 return true; 616 if (type == Resource::MainResource) 617 return m_documentLoader == frame()->loader().provisionalDocumentLoader(); 618 return m_documentLoader == frame()->loader().documentLoader(); 619 } 620 621 bool ResourceFetcher::resourceNeedsLoad(Resource* resource, const FetchRequest& request, RevalidationPolicy policy) 622 { 623 if (FetchRequest::DeferredByClient == request.defer()) 624 return false; 625 if (policy != Use) 626 return true; 627 if (resource->stillNeedsLoad()) 628 return true; 629 return request.options().synchronousPolicy == RequestSynchronously && resource->isLoading(); 630 } 631 632 void ResourceFetcher::requestLoadStarted(Resource* resource, const FetchRequest& request, ResourceLoadStartType type) 633 { 634 if (type == ResourceLoadingFromCache) 635 notifyLoadedFromMemoryCache(resource); 636 637 if (request.resourceRequest().url().protocolIsData() || (m_documentLoader && m_documentLoader->substituteData().isValid())) 638 return; 639 640 if (type == ResourceLoadingFromCache && !resource->stillNeedsLoad() && !m_validatedURLs.contains(request.resourceRequest().url())) { 641 // Resources loaded from memory cache should be reported the first time they're used. 642 RefPtr<ResourceTimingInfo> info = ResourceTimingInfo::create(request.options().initiatorInfo.name, monotonicallyIncreasingTime()); 643 populateResourceTiming(info.get(), resource, true); 644 m_scheduledResourceTimingReports.add(info, resource->type() == Resource::MainResource); 645 if (!m_resourceTimingReportTimer.isActive()) 646 m_resourceTimingReportTimer.startOneShot(0, FROM_HERE); 647 } 648 649 m_validatedURLs.add(request.resourceRequest().url()); 650 } 651 652 ResourcePtr<Resource> ResourceFetcher::requestResource(Resource::Type type, FetchRequest& request) 653 { 654 ASSERT(request.options().synchronousPolicy == RequestAsynchronously || type == Resource::Raw); 655 656 TRACE_EVENT0("blink", "ResourceFetcher::requestResource"); 657 658 KURL url = request.resourceRequest().url(); 659 660 WTF_LOG(ResourceLoading, "ResourceFetcher::requestResource '%s', charset '%s', priority=%d, forPreload=%u, type=%s", url.elidedString().latin1().data(), request.charset().latin1().data(), request.priority(), request.forPreload(), ResourceTypeName(type)); 661 662 // If only the fragment identifiers differ, it is the same resource. 663 url = MemoryCache::removeFragmentIdentifierIfNeeded(url); 664 665 if (!url.isValid()) 666 return 0; 667 668 if (!canRequest(type, request.resourceRequest(), url, request.options(), request.forPreload(), request.originRestriction())) 669 return 0; 670 671 if (LocalFrame* f = frame()) 672 f->loader().client()->dispatchWillRequestResource(&request); 673 674 if (!request.forPreload()) { 675 V8DOMActivityLogger* activityLogger = 0; 676 if (request.options().initiatorInfo.name == FetchInitiatorTypeNames::xmlhttprequest) 677 activityLogger = V8DOMActivityLogger::currentActivityLogger(); 678 else 679 activityLogger = V8DOMActivityLogger::currentActivityLoggerIfIsolatedWorld(); 680 681 if (activityLogger) { 682 Vector<String> argv; 683 argv.append(Resource::resourceTypeToString(type, request.options().initiatorInfo)); 684 argv.append(url); 685 activityLogger->logEvent("blinkRequestResource", argv.size(), argv.data()); 686 } 687 } 688 689 // See if we can use an existing resource from the cache. 690 ResourcePtr<Resource> resource = memoryCache()->resourceForURL(url); 691 692 const RevalidationPolicy policy = determineRevalidationPolicy(type, request, resource.get()); 693 switch (policy) { 694 case Reload: 695 memoryCache()->remove(resource.get()); 696 // Fall through 697 case Load: 698 resource = createResourceForLoading(type, request, request.charset()); 699 break; 700 case Revalidate: 701 resource = createResourceForRevalidation(request, resource.get()); 702 break; 703 case Use: 704 memoryCache()->updateForAccess(resource.get()); 705 break; 706 } 707 708 if (!resource) 709 return 0; 710 711 if (!resource->hasClients()) 712 m_deadStatsRecorder.update(policy); 713 714 if (policy != Use) 715 resource->setIdentifier(createUniqueIdentifier()); 716 717 if (!request.forPreload() || policy != Use) { 718 ResourceLoadPriority priority = loadPriority(type, request); 719 if (priority != resource->resourceRequest().priority()) { 720 resource->mutableResourceRequest().setPriority(priority); 721 resource->didChangePriority(priority, 0); 722 } 723 } 724 725 if (resourceNeedsLoad(resource.get(), request, policy)) { 726 if (!shouldLoadNewResource(type)) { 727 if (memoryCache()->contains(resource.get())) 728 memoryCache()->remove(resource.get()); 729 return 0; 730 } 731 732 if (!m_documentLoader || !m_documentLoader->scheduleArchiveLoad(resource.get(), request.resourceRequest())) 733 resource->load(this, request.options()); 734 735 // For asynchronous loads that immediately fail, it's sufficient to return a 736 // null Resource, as it indicates that something prevented the load from starting. 737 // If there's a network error, that failure will happen asynchronously. However, if 738 // a sync load receives a network error, it will have already happened by this point. 739 // In that case, the requester should have access to the relevant ResourceError, so 740 // we need to return a non-null Resource. 741 if (resource->errorOccurred()) { 742 if (memoryCache()->contains(resource.get())) 743 memoryCache()->remove(resource.get()); 744 return request.options().synchronousPolicy == RequestSynchronously ? resource : 0; 745 } 746 } 747 748 // FIXME: Temporarily leave main resource caching disabled for chromium, 749 // see https://bugs.webkit.org/show_bug.cgi?id=107962. Before caching main 750 // resources, we should be sure to understand the implications for memory 751 // use. 752 // Remove main resource from cache to prevent reuse. 753 if (type == Resource::MainResource) { 754 ASSERT(policy != Use || m_documentLoader->substituteData().isValid()); 755 ASSERT(policy != Revalidate); 756 memoryCache()->remove(resource.get()); 757 } 758 759 requestLoadStarted(resource.get(), request, policy == Use ? ResourceLoadingFromCache : ResourceLoadingFromNetwork); 760 761 ASSERT(resource->url() == url.string()); 762 m_documentResources.set(resource->url(), resource); 763 return resource; 764 } 765 766 void ResourceFetcher::resourceTimingReportTimerFired(Timer<ResourceFetcher>* timer) 767 { 768 ASSERT_UNUSED(timer, timer == &m_resourceTimingReportTimer); 769 HashMap<RefPtr<ResourceTimingInfo>, bool> timingReports; 770 timingReports.swap(m_scheduledResourceTimingReports); 771 HashMap<RefPtr<ResourceTimingInfo>, bool>::iterator end = timingReports.end(); 772 for (HashMap<RefPtr<ResourceTimingInfo>, bool>::iterator it = timingReports.begin(); it != end; ++it) { 773 RefPtr<ResourceTimingInfo> info = it->key; 774 bool isMainResource = it->value; 775 reportResourceTiming(info.get(), document(), isMainResource); 776 } 777 } 778 779 void ResourceFetcher::determineRequestContext(ResourceRequest& request, Resource::Type type) 780 { 781 WebURLRequest::RequestContext requestContext = requestContextFromType(this, type); 782 request.setRequestContext(requestContext); 783 } 784 785 ResourceRequestCachePolicy ResourceFetcher::resourceRequestCachePolicy(const ResourceRequest& request, Resource::Type type) 786 { 787 if (type == Resource::MainResource) { 788 FrameLoadType frameLoadType = frame()->loader().loadType(); 789 if (request.httpMethod() == "POST" && frameLoadType == FrameLoadTypeBackForward) 790 return ReturnCacheDataDontLoad; 791 if (!m_documentLoader->overrideEncoding().isEmpty() || frameLoadType == FrameLoadTypeBackForward) 792 return ReturnCacheDataElseLoad; 793 if (frameLoadType == FrameLoadTypeReloadFromOrigin) 794 return ReloadBypassingCache; 795 if (frameLoadType == FrameLoadTypeReload || frameLoadType == FrameLoadTypeSame || request.isConditional() || request.httpMethod() == "POST") 796 return ReloadIgnoringCacheData; 797 Frame* parent = frame()->tree().parent(); 798 if (parent && parent->isLocalFrame()) 799 return toLocalFrame(parent)->document()->fetcher()->resourceRequestCachePolicy(request, type); 800 return UseProtocolCachePolicy; 801 } 802 803 if (request.isConditional()) 804 return ReloadIgnoringCacheData; 805 806 if (m_documentLoader && m_document && !m_document->loadEventFinished()) { 807 // For POST requests, we mutate the main resource's cache policy to avoid form resubmission. 808 // This policy should not be inherited by subresources. 809 ResourceRequestCachePolicy mainResourceCachePolicy = m_documentLoader->request().cachePolicy(); 810 if (m_documentLoader->request().httpMethod() == "POST") { 811 if (mainResourceCachePolicy == ReturnCacheDataDontLoad) 812 return ReturnCacheDataElseLoad; 813 return UseProtocolCachePolicy; 814 } 815 return memoryCachePolicyToResourceRequestCachePolicy(context().cachePolicy(m_document)); 816 } 817 return UseProtocolCachePolicy; 818 } 819 820 void ResourceFetcher::addAdditionalRequestHeaders(ResourceRequest& request, Resource::Type type) 821 { 822 if (!frame()) 823 return; 824 825 if (request.cachePolicy() == UseProtocolCachePolicy) 826 request.setCachePolicy(resourceRequestCachePolicy(request, type)); 827 if (request.requestContext() == WebURLRequest::RequestContextUnspecified) 828 determineRequestContext(request, type); 829 if (type == Resource::LinkPrefetch || type == Resource::LinkSubresource) 830 request.setHTTPHeaderField("Purpose", "prefetch"); 831 832 context().addAdditionalRequestHeaders(document(), request, (type == Resource::MainResource) ? FetchMainResource : FetchSubresource); 833 } 834 835 ResourcePtr<Resource> ResourceFetcher::createResourceForRevalidation(const FetchRequest& request, Resource* resource) 836 { 837 ASSERT(resource); 838 ASSERT(memoryCache()->contains(resource)); 839 ASSERT(resource->isLoaded()); 840 ASSERT(resource->canUseCacheValidator()); 841 ASSERT(!resource->resourceToRevalidate()); 842 843 ResourceRequest revalidatingRequest(resource->resourceRequest()); 844 revalidatingRequest.clearHTTPReferrer(); 845 addAdditionalRequestHeaders(revalidatingRequest, resource->type()); 846 847 const AtomicString& lastModified = resource->response().httpHeaderField("Last-Modified"); 848 const AtomicString& eTag = resource->response().httpHeaderField("ETag"); 849 if (!lastModified.isEmpty() || !eTag.isEmpty()) { 850 ASSERT(context().cachePolicy(document()) != CachePolicyReload); 851 if (context().cachePolicy(document()) == CachePolicyRevalidate) 852 revalidatingRequest.setHTTPHeaderField("Cache-Control", "max-age=0"); 853 } 854 if (!lastModified.isEmpty()) 855 revalidatingRequest.setHTTPHeaderField("If-Modified-Since", lastModified); 856 if (!eTag.isEmpty()) 857 revalidatingRequest.setHTTPHeaderField("If-None-Match", eTag); 858 859 ResourcePtr<Resource> newResource = createResource(resource->type(), revalidatingRequest, resource->encoding()); 860 WTF_LOG(ResourceLoading, "Resource %p created to revalidate %p", newResource.get(), resource); 861 862 newResource->setResourceToRevalidate(resource); 863 864 memoryCache()->remove(resource); 865 memoryCache()->add(newResource.get()); 866 return newResource; 867 } 868 869 ResourcePtr<Resource> ResourceFetcher::createResourceForLoading(Resource::Type type, FetchRequest& request, const String& charset) 870 { 871 ASSERT(!memoryCache()->resourceForURL(request.resourceRequest().url())); 872 873 WTF_LOG(ResourceLoading, "Loading Resource for '%s'.", request.resourceRequest().url().elidedString().latin1().data()); 874 875 addAdditionalRequestHeaders(request.mutableResourceRequest(), type); 876 ResourcePtr<Resource> resource = createResource(type, request.resourceRequest(), charset); 877 878 memoryCache()->add(resource.get()); 879 return resource; 880 } 881 882 void ResourceFetcher::storeResourceTimingInitiatorInformation(Resource* resource) 883 { 884 if (resource->options().requestInitiatorContext != DocumentContext) 885 return; 886 887 RefPtr<ResourceTimingInfo> info = ResourceTimingInfo::create(resource->options().initiatorInfo.name, monotonicallyIncreasingTime()); 888 889 if (resource->isCacheValidator()) { 890 const AtomicString& timingAllowOrigin = resource->resourceToRevalidate()->response().httpHeaderField("Timing-Allow-Origin"); 891 if (!timingAllowOrigin.isEmpty()) 892 info->setOriginalTimingAllowOrigin(timingAllowOrigin); 893 } 894 895 if (resource->type() == Resource::MainResource) { 896 // <iframe>s should report the initial navigation requested by the parent document, but not subsequent navigations. 897 // FIXME: Resource timing is broken when the parent is a remote frame. 898 if (frame()->deprecatedLocalOwner() && !frame()->deprecatedLocalOwner()->loadedNonEmptyDocument()) { 899 info->setInitiatorType(frame()->deprecatedLocalOwner()->localName()); 900 m_resourceTimingInfoMap.add(resource, info); 901 frame()->deprecatedLocalOwner()->didLoadNonEmptyDocument(); 902 } 903 } else { 904 m_resourceTimingInfoMap.add(resource, info); 905 } 906 } 907 908 ResourceFetcher::RevalidationPolicy ResourceFetcher::determineRevalidationPolicy(Resource::Type type, const FetchRequest& fetchRequest, Resource* existingResource) const 909 { 910 const ResourceRequest& request = fetchRequest.resourceRequest(); 911 912 if (!existingResource) 913 return Load; 914 915 // We already have a preload going for this URL. 916 if (fetchRequest.forPreload() && existingResource->isPreloaded()) 917 return Use; 918 919 // If the same URL has been loaded as a different type, we need to reload. 920 if (existingResource->type() != type) { 921 // FIXME: If existingResource is a Preload and the new type is LinkPrefetch 922 // We really should discard the new prefetch since the preload has more 923 // specific type information! crbug.com/379893 924 // fast/dom/HTMLLinkElement/link-and-subresource-test hits this case. 925 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to type mismatch."); 926 return Reload; 927 } 928 929 // Do not load from cache if images are not enabled. The load for this image will be blocked 930 // in ImageResource::load. 931 if (FetchRequest::DeferredByClient == fetchRequest.defer()) 932 return Reload; 933 934 // Always use data uris. 935 // FIXME: Extend this to non-images. 936 if (type == Resource::Image && request.url().protocolIsData()) 937 return Use; 938 939 // If a main resource was populated from a SubstituteData load, use it. 940 if (type == Resource::MainResource && m_documentLoader->substituteData().isValid()) 941 return Use; 942 943 if (!existingResource->canReuse(request)) 944 return Reload; 945 946 // Never use cache entries for downloadToFile requests. The caller expects the resource in a file. 947 if (request.downloadToFile()) 948 return Reload; 949 950 // Certain requests (e.g., XHRs) might have manually set headers that require revalidation. 951 // FIXME: In theory, this should be a Revalidate case. In practice, the MemoryCache revalidation path assumes a whole bunch 952 // of things about how revalidation works that manual headers violate, so punt to Reload instead. 953 if (request.isConditional()) 954 return Reload; 955 956 // Don't reload resources while pasting. 957 if (m_allowStaleResources) 958 return Use; 959 960 if (!fetchRequest.options().canReuseRequest(existingResource->options())) 961 return Reload; 962 963 // Always use preloads. 964 if (existingResource->isPreloaded()) 965 return Use; 966 967 // CachePolicyHistoryBuffer uses the cache no matter what. 968 CachePolicy cachePolicy = context().cachePolicy(document()); 969 if (cachePolicy == CachePolicyHistoryBuffer) 970 return Use; 971 972 // Don't reuse resources with Cache-control: no-store. 973 if (existingResource->hasCacheControlNoStoreHeader()) { 974 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to Cache-control: no-store."); 975 return Reload; 976 } 977 978 // If credentials were sent with the previous request and won't be 979 // with this one, or vice versa, re-fetch the resource. 980 // 981 // This helps with the case where the server sends back 982 // "Access-Control-Allow-Origin: *" all the time, but some of the 983 // client's requests are made without CORS and some with. 984 if (existingResource->resourceRequest().allowStoredCredentials() != request.allowStoredCredentials()) { 985 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to difference in credentials settings."); 986 return Reload; 987 } 988 989 // During the initial load, avoid loading the same resource multiple times for a single document, 990 // even if the cache policies would tell us to. 991 // We also group loads of the same resource together. 992 // Raw resources are exempted, as XHRs fall into this category and may have user-set Cache-Control: 993 // headers or other factors that require separate requests. 994 if (type != Resource::Raw) { 995 if (document() && !document()->loadEventFinished() && m_validatedURLs.contains(existingResource->url())) 996 return Use; 997 if (existingResource->isLoading()) 998 return Use; 999 } 1000 1001 // CachePolicyReload always reloads 1002 if (cachePolicy == CachePolicyReload) { 1003 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to CachePolicyReload."); 1004 return Reload; 1005 } 1006 1007 // We'll try to reload the resource if it failed last time. 1008 if (existingResource->errorOccurred()) { 1009 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicye reloading due to resource being in the error state"); 1010 return Reload; 1011 } 1012 1013 // List of available images logic allows images to be re-used without cache validation. We restrict this only to images 1014 // from memory cache which are the same as the version in the current document. 1015 if (type == Resource::Image && existingResource == cachedResource(request.url())) 1016 return Use; 1017 1018 // If any of the redirects in the chain to loading the resource were not cacheable, we cannot reuse our cached resource. 1019 if (!existingResource->canReuseRedirectChain()) { 1020 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to an uncacheable redirect"); 1021 return Reload; 1022 } 1023 1024 // Check if the cache headers requires us to revalidate (cache expiration for example). 1025 if (cachePolicy == CachePolicyRevalidate || existingResource->mustRevalidateDueToCacheHeaders() 1026 || request.cacheControlContainsNoCache()) { 1027 // See if the resource has usable ETag or Last-modified headers. 1028 if (existingResource->canUseCacheValidator()) 1029 return Revalidate; 1030 1031 // No, must reload. 1032 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to missing cache validators."); 1033 return Reload; 1034 } 1035 1036 return Use; 1037 } 1038 1039 void ResourceFetcher::printAccessDeniedMessage(const KURL& url) const 1040 { 1041 if (url.isNull()) 1042 return; 1043 1044 if (!frame()) 1045 return; 1046 1047 String message; 1048 if (!m_document || m_document->url().isNull()) 1049 message = "Unsafe attempt to load URL " + url.elidedString() + '.'; 1050 else 1051 message = "Unsafe attempt to load URL " + url.elidedString() + " from frame with URL " + m_document->url().elidedString() + ". Domains, protocols and ports must match.\n"; 1052 1053 frame()->document()->addConsoleMessage(ConsoleMessage::create(SecurityMessageSource, ErrorMessageLevel, message)); 1054 } 1055 1056 void ResourceFetcher::setAutoLoadImages(bool enable) 1057 { 1058 if (enable == m_autoLoadImages) 1059 return; 1060 1061 m_autoLoadImages = enable; 1062 1063 if (!m_autoLoadImages) 1064 return; 1065 1066 reloadImagesIfNotDeferred(); 1067 } 1068 1069 void ResourceFetcher::setImagesEnabled(bool enable) 1070 { 1071 if (enable == m_imagesEnabled) 1072 return; 1073 1074 m_imagesEnabled = enable; 1075 1076 if (!m_imagesEnabled) 1077 return; 1078 1079 reloadImagesIfNotDeferred(); 1080 } 1081 1082 bool ResourceFetcher::clientDefersImage(const KURL& url) const 1083 { 1084 return frame() && !frame()->loader().client()->allowImage(m_imagesEnabled, url); 1085 } 1086 1087 bool ResourceFetcher::shouldDeferImageLoad(const KURL& url) const 1088 { 1089 return clientDefersImage(url) || !m_autoLoadImages; 1090 } 1091 1092 void ResourceFetcher::reloadImagesIfNotDeferred() 1093 { 1094 DocumentResourceMap::iterator end = m_documentResources.end(); 1095 for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it) { 1096 Resource* resource = it->value.get(); 1097 if (resource->type() == Resource::Image && resource->stillNeedsLoad() && !clientDefersImage(resource->url())) 1098 const_cast<Resource*>(resource)->load(this, defaultResourceOptions()); 1099 } 1100 } 1101 1102 void ResourceFetcher::redirectReceived(Resource* resource, const ResourceResponse& redirectResponse) 1103 { 1104 ResourceTimingInfoMap::iterator it = m_resourceTimingInfoMap.find(resource); 1105 if (it != m_resourceTimingInfoMap.end()) 1106 it->value->addRedirect(redirectResponse); 1107 } 1108 1109 void ResourceFetcher::didLoadResource(Resource* resource) 1110 { 1111 RefPtr<DocumentLoader> protectDocumentLoader(m_documentLoader); 1112 RefPtrWillBeRawPtr<Document> protectDocument(m_document.get()); 1113 1114 if (resource && resource->response().isHTTP() && ((!resource->errorOccurred() && !resource->wasCanceled()) || resource->response().httpStatusCode() == 304) && document()) { 1115 ResourceTimingInfoMap::iterator it = m_resourceTimingInfoMap.find(resource); 1116 if (it != m_resourceTimingInfoMap.end()) { 1117 RefPtr<ResourceTimingInfo> info = it->value; 1118 m_resourceTimingInfoMap.remove(it); 1119 populateResourceTiming(info.get(), resource, false); 1120 reportResourceTiming(info.get(), document(), resource->type() == Resource::MainResource); 1121 } 1122 } 1123 1124 if (frame()) 1125 frame()->loader().loadDone(); 1126 scheduleDocumentResourcesGC(); 1127 } 1128 1129 void ResourceFetcher::scheduleDocumentResourcesGC() 1130 { 1131 if (!m_garbageCollectDocumentResourcesTimer.isActive()) 1132 m_garbageCollectDocumentResourcesTimer.startOneShot(0, FROM_HERE); 1133 } 1134 1135 // Garbage collecting m_documentResources is a workaround for the 1136 // ResourcePtrs on the RHS being strong references. Ideally this 1137 // would be a weak map, however ResourcePtrs perform additional 1138 // bookkeeping on Resources, so instead pseudo-GC them -- when the 1139 // reference count reaches 1, m_documentResources is the only reference, so 1140 // remove it from the map. 1141 void ResourceFetcher::garbageCollectDocumentResourcesTimerFired(Timer<ResourceFetcher>* timer) 1142 { 1143 ASSERT_UNUSED(timer, timer == &m_garbageCollectDocumentResourcesTimer); 1144 garbageCollectDocumentResources(); 1145 } 1146 1147 void ResourceFetcher::garbageCollectDocumentResources() 1148 { 1149 typedef Vector<String, 10> StringVector; 1150 StringVector resourcesToDelete; 1151 1152 for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != m_documentResources.end(); ++it) { 1153 if (it->value->hasOneHandle()) 1154 resourcesToDelete.append(it->key); 1155 } 1156 1157 m_documentResources.removeAll(resourcesToDelete); 1158 } 1159 1160 void ResourceFetcher::notifyLoadedFromMemoryCache(Resource* resource) 1161 { 1162 if (!frame() || !frame()->page() || resource->status() != Resource::Cached || m_validatedURLs.contains(resource->url())) 1163 return; 1164 1165 ResourceRequest request(resource->url()); 1166 unsigned long identifier = createUniqueIdentifier(); 1167 context().dispatchDidLoadResourceFromMemoryCache(request, resource->response()); 1168 // FIXME: If willSendRequest changes the request, we don't respect it. 1169 willSendRequest(identifier, request, ResourceResponse(), resource->options().initiatorInfo); 1170 InspectorInstrumentation::markResourceAsCached(frame()->page(), identifier); 1171 context().sendRemainingDelegateMessages(m_documentLoader, identifier, resource->response(), resource->encodedSize()); 1172 } 1173 1174 void ResourceFetcher::incrementRequestCount(const Resource* res) 1175 { 1176 if (res->ignoreForRequestCount()) 1177 return; 1178 1179 ++m_requestCount; 1180 } 1181 1182 void ResourceFetcher::decrementRequestCount(const Resource* res) 1183 { 1184 if (res->ignoreForRequestCount()) 1185 return; 1186 1187 --m_requestCount; 1188 ASSERT(m_requestCount > -1); 1189 } 1190 1191 void ResourceFetcher::preload(Resource::Type type, FetchRequest& request, const String& charset) 1192 { 1193 requestPreload(type, request, charset); 1194 } 1195 1196 void ResourceFetcher::requestPreload(Resource::Type type, FetchRequest& request, const String& charset) 1197 { 1198 // Ensure main resources aren't preloaded, since the cache can't actually reuse the preload. 1199 if (type == Resource::MainResource) 1200 return; 1201 1202 String encoding; 1203 if (type == Resource::Script || type == Resource::CSSStyleSheet) 1204 encoding = charset.isEmpty() ? m_document->charset().string() : charset; 1205 1206 request.setCharset(encoding); 1207 request.setForPreload(true); 1208 1209 ResourcePtr<Resource> resource; 1210 // Loading images involves several special cases, so use dedicated fetch method instead. 1211 if (type == Resource::Image) 1212 resource = fetchImage(request); 1213 if (!resource) 1214 resource = requestResource(type, request); 1215 if (!resource || (m_preloads && m_preloads->contains(resource.get()))) 1216 return; 1217 TRACE_EVENT_ASYNC_STEP_INTO0("net", "Resource", resource.get(), "Preload"); 1218 resource->increasePreloadCount(); 1219 1220 if (!m_preloads) 1221 m_preloads = adoptPtr(new ListHashSet<Resource*>); 1222 m_preloads->add(resource.get()); 1223 1224 #if PRELOAD_DEBUG 1225 printf("PRELOADING %s\n", resource->url().string().latin1().data()); 1226 #endif 1227 } 1228 1229 bool ResourceFetcher::isPreloaded(const String& urlString) const 1230 { 1231 const KURL& url = m_document->completeURL(urlString); 1232 1233 if (m_preloads) { 1234 ListHashSet<Resource*>::iterator end = m_preloads->end(); 1235 for (ListHashSet<Resource*>::iterator it = m_preloads->begin(); it != end; ++it) { 1236 Resource* resource = *it; 1237 if (resource->url() == url) 1238 return true; 1239 } 1240 } 1241 1242 return false; 1243 } 1244 1245 void ResourceFetcher::clearPreloads() 1246 { 1247 #if PRELOAD_DEBUG 1248 printPreloadStats(); 1249 #endif 1250 if (!m_preloads) 1251 return; 1252 1253 ListHashSet<Resource*>::iterator end = m_preloads->end(); 1254 for (ListHashSet<Resource*>::iterator it = m_preloads->begin(); it != end; ++it) { 1255 Resource* res = *it; 1256 res->decreasePreloadCount(); 1257 bool deleted = res->deleteIfPossible(); 1258 if (!deleted && res->preloadResult() == Resource::PreloadNotReferenced) 1259 memoryCache()->remove(res); 1260 } 1261 m_preloads.clear(); 1262 } 1263 1264 void ResourceFetcher::didFinishLoading(const Resource* resource, double finishTime, int64_t encodedDataLength) 1265 { 1266 TRACE_EVENT_ASYNC_END0("net", "Resource", resource); 1267 context().dispatchDidFinishLoading(m_documentLoader, resource->identifier(), finishTime, encodedDataLength); 1268 } 1269 1270 void ResourceFetcher::didChangeLoadingPriority(const Resource* resource, ResourceLoadPriority loadPriority, int intraPriorityValue) 1271 { 1272 TRACE_EVENT_ASYNC_STEP_INTO1("net", "Resource", resource, "ChangePriority", "priority", loadPriority); 1273 context().dispatchDidChangeResourcePriority(resource->identifier(), loadPriority, intraPriorityValue); 1274 } 1275 1276 void ResourceFetcher::didFailLoading(const Resource* resource, const ResourceError& error) 1277 { 1278 TRACE_EVENT_ASYNC_END0("net", "Resource", resource); 1279 context().dispatchDidFail(m_documentLoader, resource->identifier(), error); 1280 } 1281 1282 void ResourceFetcher::willSendRequest(unsigned long identifier, ResourceRequest& request, const ResourceResponse& redirectResponse, const FetchInitiatorInfo& initiatorInfo) 1283 { 1284 context().dispatchWillSendRequest(m_documentLoader, identifier, request, redirectResponse, initiatorInfo); 1285 } 1286 1287 void ResourceFetcher::didReceiveResponse(const Resource* resource, const ResourceResponse& response) 1288 { 1289 MixedContentChecker::checkMixedPrivatePublic(frame(), response.remoteIPAddress()); 1290 1291 // If the response is fetched via ServiceWorker, the original URL of the response could be different from the URL of the request. 1292 if (response.wasFetchedViaServiceWorker()) { 1293 if (!canRequest(resource->type(), resource->resourceRequest(), response.url(), resource->options(), false, FetchRequest::UseDefaultOriginRestrictionForType)) { 1294 resource->loader()->cancel(); 1295 context().dispatchDidFail(m_documentLoader, resource->identifier(), ResourceError(errorDomainBlinkInternal, 0, response.url().string(), "Unsafe attempt to load URL " + response.url().elidedString() + " fetched by a ServiceWorker.")); 1296 return; 1297 } 1298 } 1299 context().dispatchDidReceiveResponse(m_documentLoader, resource->identifier(), response, resource->loader()); 1300 } 1301 1302 void ResourceFetcher::didReceiveData(const Resource* resource, const char* data, int dataLength, int encodedDataLength) 1303 { 1304 context().dispatchDidReceiveData(m_documentLoader, resource->identifier(), data, dataLength, encodedDataLength); 1305 } 1306 1307 void ResourceFetcher::didDownloadData(const Resource* resource, int dataLength, int encodedDataLength) 1308 { 1309 context().dispatchDidDownloadData(m_documentLoader, resource->identifier(), dataLength, encodedDataLength); 1310 } 1311 1312 void ResourceFetcher::subresourceLoaderFinishedLoadingOnePart(ResourceLoader* loader) 1313 { 1314 if (!m_multipartLoaders) 1315 m_multipartLoaders = ResourceLoaderSet::create(); 1316 m_multipartLoaders->add(loader); 1317 m_loaders->remove(loader); 1318 if (LocalFrame* frame = this->frame()) 1319 return frame->loader().checkLoadComplete(); 1320 } 1321 1322 void ResourceFetcher::didInitializeResourceLoader(ResourceLoader* loader) 1323 { 1324 if (!m_document) 1325 return; 1326 if (!m_loaders) 1327 m_loaders = ResourceLoaderSet::create(); 1328 ASSERT(!m_loaders->contains(loader)); 1329 m_loaders->add(loader); 1330 } 1331 1332 void ResourceFetcher::willTerminateResourceLoader(ResourceLoader* loader) 1333 { 1334 if (m_loaders && m_loaders->contains(loader)) 1335 m_loaders->remove(loader); 1336 if (m_multipartLoaders && m_multipartLoaders->contains(loader)) 1337 m_multipartLoaders->remove(loader); 1338 if (LocalFrame* frame = this->frame()) 1339 frame->loader().checkLoadComplete(); 1340 } 1341 1342 void ResourceFetcher::willStartLoadingResource(Resource* resource, ResourceRequest& request) 1343 { 1344 if (m_documentLoader) 1345 m_documentLoader->applicationCacheHost()->willStartLoadingResource(request); 1346 1347 storeResourceTimingInitiatorInformation(resource); 1348 TRACE_EVENT_ASYNC_BEGIN2("net", "Resource", resource, "url", resource->url().string().ascii(), "priority", resource->resourceRequest().priority()); 1349 } 1350 1351 void ResourceFetcher::stopFetching() 1352 { 1353 if (m_multipartLoaders) 1354 m_multipartLoaders->cancelAll(); 1355 if (m_loaders) 1356 m_loaders->cancelAll(); 1357 } 1358 1359 bool ResourceFetcher::isFetching() const 1360 { 1361 return m_loaders && !m_loaders->isEmpty(); 1362 } 1363 1364 void ResourceFetcher::setDefersLoading(bool defers) 1365 { 1366 if (m_loaders) 1367 m_loaders->setAllDefersLoading(defers); 1368 } 1369 1370 bool ResourceFetcher::defersLoading() const 1371 { 1372 if (LocalFrame* frame = this->frame()) 1373 return frame->page()->defersLoading(); 1374 return false; 1375 } 1376 1377 bool ResourceFetcher::isLoadedBy(ResourceLoaderHost* possibleOwner) const 1378 { 1379 return this == possibleOwner; 1380 } 1381 1382 bool ResourceFetcher::canAccessRedirect(Resource* resource, ResourceRequest& request, const ResourceResponse& redirectResponse, ResourceLoaderOptions& options) 1383 { 1384 if (!canRequest(resource->type(), request, request.url(), options, resource->isUnusedPreload(), FetchRequest::UseDefaultOriginRestrictionForType)) 1385 return false; 1386 if (options.corsEnabled == IsCORSEnabled) { 1387 SecurityOrigin* sourceOrigin = options.securityOrigin.get(); 1388 if (!sourceOrigin && document()) 1389 sourceOrigin = document()->securityOrigin(); 1390 1391 String errorMessage; 1392 if (!CrossOriginAccessControl::handleRedirect(resource, sourceOrigin, request, redirectResponse, options, errorMessage)) { 1393 if (resource->type() == Resource::Font) 1394 toFontResource(resource)->setCORSFailed(); 1395 if (frame() && frame()->document()) 1396 frame()->document()->addConsoleMessage(ConsoleMessage::create(JSMessageSource, ErrorMessageLevel, errorMessage)); 1397 return false; 1398 } 1399 } 1400 if (resource->type() == Resource::Image && shouldDeferImageLoad(request.url())) 1401 return false; 1402 return true; 1403 } 1404 1405 #if !ENABLE(OILPAN) 1406 void ResourceFetcher::refResourceLoaderHost() 1407 { 1408 ref(); 1409 } 1410 1411 void ResourceFetcher::derefResourceLoaderHost() 1412 { 1413 deref(); 1414 } 1415 #endif 1416 1417 #if PRELOAD_DEBUG 1418 void ResourceFetcher::printPreloadStats() 1419 { 1420 if (!m_preloads) 1421 return; 1422 1423 unsigned scripts = 0; 1424 unsigned scriptMisses = 0; 1425 unsigned stylesheets = 0; 1426 unsigned stylesheetMisses = 0; 1427 unsigned images = 0; 1428 unsigned imageMisses = 0; 1429 ListHashSet<Resource*>::iterator end = m_preloads->end(); 1430 for (ListHashSet<Resource*>::iterator it = m_preloads->begin(); it != end; ++it) { 1431 Resource* res = *it; 1432 if (res->preloadResult() == Resource::PreloadNotReferenced) 1433 printf("!! UNREFERENCED PRELOAD %s\n", res->url().string().latin1().data()); 1434 else if (res->preloadResult() == Resource::PreloadReferencedWhileComplete) 1435 printf("HIT COMPLETE PRELOAD %s\n", res->url().string().latin1().data()); 1436 else if (res->preloadResult() == Resource::PreloadReferencedWhileLoading) 1437 printf("HIT LOADING PRELOAD %s\n", res->url().string().latin1().data()); 1438 1439 if (res->type() == Resource::Script) { 1440 scripts++; 1441 if (res->preloadResult() < Resource::PreloadReferencedWhileLoading) 1442 scriptMisses++; 1443 } else if (res->type() == Resource::CSSStyleSheet) { 1444 stylesheets++; 1445 if (res->preloadResult() < Resource::PreloadReferencedWhileLoading) 1446 stylesheetMisses++; 1447 } else { 1448 images++; 1449 if (res->preloadResult() < Resource::PreloadReferencedWhileLoading) 1450 imageMisses++; 1451 } 1452 1453 if (res->errorOccurred()) 1454 memoryCache()->remove(res); 1455 1456 res->decreasePreloadCount(); 1457 } 1458 m_preloads.clear(); 1459 1460 if (scripts) 1461 printf("SCRIPTS: %d (%d hits, hit rate %d%%)\n", scripts, scripts - scriptMisses, (scripts - scriptMisses) * 100 / scripts); 1462 if (stylesheets) 1463 printf("STYLESHEETS: %d (%d hits, hit rate %d%%)\n", stylesheets, stylesheets - stylesheetMisses, (stylesheets - stylesheetMisses) * 100 / stylesheets); 1464 if (images) 1465 printf("IMAGES: %d (%d hits, hit rate %d%%)\n", images, images - imageMisses, (images - imageMisses) * 100 / images); 1466 } 1467 #endif 1468 1469 const ResourceLoaderOptions& ResourceFetcher::defaultResourceOptions() 1470 { 1471 DEFINE_STATIC_LOCAL(ResourceLoaderOptions, options, (BufferData, AllowStoredCredentials, ClientRequestedCredentials, CheckContentSecurityPolicy, DocumentContext)); 1472 return options; 1473 } 1474 1475 ResourceFetcher::DeadResourceStatsRecorder::DeadResourceStatsRecorder() 1476 : m_useCount(0) 1477 , m_revalidateCount(0) 1478 , m_loadCount(0) 1479 { 1480 } 1481 1482 ResourceFetcher::DeadResourceStatsRecorder::~DeadResourceStatsRecorder() 1483 { 1484 blink::Platform::current()->histogramCustomCounts( 1485 "WebCore.ResourceFetcher.HitCount", m_useCount, 0, 1000, 50); 1486 blink::Platform::current()->histogramCustomCounts( 1487 "WebCore.ResourceFetcher.RevalidateCount", m_revalidateCount, 0, 1000, 50); 1488 blink::Platform::current()->histogramCustomCounts( 1489 "WebCore.ResourceFetcher.LoadCount", m_loadCount, 0, 1000, 50); 1490 } 1491 1492 void ResourceFetcher::DeadResourceStatsRecorder::update(RevalidationPolicy policy) 1493 { 1494 switch (policy) { 1495 case Reload: 1496 case Load: 1497 ++m_loadCount; 1498 return; 1499 case Revalidate: 1500 ++m_revalidateCount; 1501 return; 1502 case Use: 1503 ++m_useCount; 1504 return; 1505 } 1506 } 1507 1508 void ResourceFetcher::trace(Visitor* visitor) 1509 { 1510 visitor->trace(m_document); 1511 visitor->trace(m_loaders); 1512 visitor->trace(m_multipartLoaders); 1513 ResourceLoaderHost::trace(visitor); 1514 } 1515 1516 } 1517