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