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 "RuntimeEnabledFeatures.h" 31 #include "bindings/v8/ScriptController.h" 32 #include "core/dom/Document.h" 33 #include "core/fetch/CSSStyleSheetResource.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/ShaderResource.h" 44 #include "core/fetch/XSLStyleSheetResource.h" 45 #include "core/html/HTMLElement.h" 46 #include "core/html/HTMLFrameOwnerElement.h" 47 #include "core/html/HTMLImport.h" 48 #include "core/inspector/InspectorInstrumentation.h" 49 #include "core/loader/DocumentLoader.h" 50 #include "core/loader/FrameLoader.h" 51 #include "core/loader/FrameLoaderClient.h" 52 #include "core/loader/PingLoader.h" 53 #include "core/loader/UniqueIdentifier.h" 54 #include "core/loader/appcache/ApplicationCacheHost.h" 55 #include "core/frame/ContentSecurityPolicy.h" 56 #include "core/frame/DOMWindow.h" 57 #include "core/frame/Frame.h" 58 #include "core/timing/Performance.h" 59 #include "core/timing/ResourceTimingInfo.h" 60 #include "core/frame/Settings.h" 61 #include "platform/Logging.h" 62 #include "platform/TraceEvent.h" 63 #include "platform/weborigin/SecurityOrigin.h" 64 #include "platform/weborigin/SecurityPolicy.h" 65 #include "public/platform/Platform.h" 66 #include "public/platform/WebURL.h" 67 #include "wtf/text/CString.h" 68 #include "wtf/text/WTFString.h" 69 70 #define PRELOAD_DEBUG 0 71 72 namespace WebCore { 73 74 static Resource* createResource(Resource::Type type, const ResourceRequest& request, const String& charset) 75 { 76 switch (type) { 77 case Resource::Image: 78 return new ImageResource(request); 79 case Resource::CSSStyleSheet: 80 return new CSSStyleSheetResource(request, charset); 81 case Resource::Script: 82 return new ScriptResource(request, charset); 83 case Resource::SVGDocument: 84 return new DocumentResource(request, Resource::SVGDocument); 85 case Resource::Font: 86 return new FontResource(request); 87 case Resource::MainResource: 88 case Resource::Raw: 89 case Resource::TextTrack: 90 return new RawResource(request, type); 91 case Resource::XSLStyleSheet: 92 return new XSLStyleSheetResource(request); 93 case Resource::LinkPrefetch: 94 return new Resource(request, Resource::LinkPrefetch); 95 case Resource::LinkSubresource: 96 return new Resource(request, Resource::LinkSubresource); 97 case Resource::Shader: 98 return new ShaderResource(request); 99 case Resource::ImportResource: 100 return new RawResource(request, type); 101 } 102 103 ASSERT_NOT_REACHED(); 104 return 0; 105 } 106 107 static ResourceLoadPriority loadPriority(Resource::Type type, const FetchRequest& request) 108 { 109 if (request.priority() != ResourceLoadPriorityUnresolved) 110 return request.priority(); 111 112 switch (type) { 113 case Resource::MainResource: 114 return ResourceLoadPriorityVeryHigh; 115 case Resource::CSSStyleSheet: 116 return ResourceLoadPriorityHigh; 117 case Resource::Raw: 118 return request.options().synchronousPolicy == RequestSynchronously ? ResourceLoadPriorityVeryHigh : ResourceLoadPriorityMedium; 119 case Resource::Script: 120 case Resource::Font: 121 case Resource::ImportResource: 122 return ResourceLoadPriorityMedium; 123 case Resource::Image: 124 // We'll default images to VeryLow, and promote whatever is visible. This improves 125 // speed-index by ~5% on average, ~14% at the 99th percentile. 126 return ResourceLoadPriorityVeryLow; 127 case Resource::XSLStyleSheet: 128 ASSERT(RuntimeEnabledFeatures::xsltEnabled()); 129 return ResourceLoadPriorityHigh; 130 case Resource::SVGDocument: 131 return ResourceLoadPriorityLow; 132 case Resource::LinkPrefetch: 133 return ResourceLoadPriorityVeryLow; 134 case Resource::LinkSubresource: 135 return ResourceLoadPriorityLow; 136 case Resource::TextTrack: 137 return ResourceLoadPriorityLow; 138 case Resource::Shader: 139 return ResourceLoadPriorityMedium; 140 } 141 ASSERT_NOT_REACHED(); 142 return ResourceLoadPriorityUnresolved; 143 } 144 145 static Resource* resourceFromDataURIRequest(const ResourceRequest& request, const ResourceLoaderOptions& resourceOptions) 146 { 147 const KURL& url = request.url(); 148 ASSERT(url.protocolIsData()); 149 150 blink::WebString mimetype; 151 blink::WebString charset; 152 RefPtr<SharedBuffer> data = PassRefPtr<SharedBuffer>(blink::Platform::current()->parseDataURL(url, mimetype, charset)); 153 if (!data) 154 return 0; 155 ResourceResponse response(url, mimetype, data->size(), charset, String()); 156 157 Resource* resource = createResource(Resource::Image, request, charset); 158 resource->setOptions(resourceOptions); 159 resource->responseReceived(response); 160 // FIXME: AppendData causes an unnecessary memcpy. 161 if (data->size()) 162 resource->appendData(data->data(), data->size()); 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(resource->loadFinishTime()); 174 } 175 176 static void reportResourceTiming(ResourceTimingInfo* info, Document* initiatorDocument, bool isMainResource) 177 { 178 if (initiatorDocument && isMainResource) 179 initiatorDocument = initiatorDocument->parentDocument(); 180 if (!initiatorDocument || !initiatorDocument->loader()) 181 return; 182 if (DOMWindow* initiatorWindow = initiatorDocument->domWindow()) { 183 if (Performance* performance = initiatorWindow->performance()) 184 performance->addResourceTiming(*info, initiatorDocument); 185 } 186 } 187 188 static ResourceRequest::TargetType requestTargetType(const ResourceFetcher* fetcher, const ResourceRequest& request, Resource::Type type) 189 { 190 switch (type) { 191 case Resource::MainResource: 192 if (fetcher->frame()->tree().parent()) 193 return ResourceRequest::TargetIsSubframe; 194 return ResourceRequest::TargetIsMainFrame; 195 case Resource::XSLStyleSheet: 196 ASSERT(RuntimeEnabledFeatures::xsltEnabled()); 197 case Resource::CSSStyleSheet: 198 return ResourceRequest::TargetIsStyleSheet; 199 case Resource::Script: 200 return ResourceRequest::TargetIsScript; 201 case Resource::Font: 202 return ResourceRequest::TargetIsFont; 203 case Resource::Image: 204 return ResourceRequest::TargetIsImage; 205 case Resource::Shader: 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 } 218 ASSERT_NOT_REACHED(); 219 return ResourceRequest::TargetIsSubresource; 220 } 221 222 ResourceFetcher::ResourceFetcher(DocumentLoader* documentLoader) 223 : m_document(0) 224 , m_documentLoader(documentLoader) 225 , m_requestCount(0) 226 , m_garbageCollectDocumentResourcesTimer(this, &ResourceFetcher::garbageCollectDocumentResourcesTimerFired) 227 , m_resourceTimingReportTimer(this, &ResourceFetcher::resourceTimingReportTimerFired) 228 , m_autoLoadImages(true) 229 , m_imagesEnabled(true) 230 , m_allowStaleResources(false) 231 { 232 } 233 234 ResourceFetcher::~ResourceFetcher() 235 { 236 m_documentLoader = 0; 237 m_document = 0; 238 239 clearPreloads(); 240 241 // Make sure no requests still point to this ResourceFetcher 242 ASSERT(!m_requestCount); 243 } 244 245 Resource* ResourceFetcher::cachedResource(const String& resourceURL) const 246 { 247 KURL url = m_document->completeURL(resourceURL); 248 return cachedResource(url); 249 } 250 251 Resource* ResourceFetcher::cachedResource(const KURL& resourceURL) const 252 { 253 KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(resourceURL); 254 return m_documentResources.get(url).get(); 255 } 256 257 Frame* ResourceFetcher::frame() const 258 { 259 if (m_documentLoader) 260 return m_documentLoader->frame(); 261 if (m_document && m_document->import()) 262 return m_document->import()->frame(); 263 return 0; 264 } 265 266 FetchContext& ResourceFetcher::context() const 267 { 268 if (Frame* frame = this->frame()) 269 return frame->fetchContext(); 270 return FetchContext::nullInstance(); 271 } 272 273 ResourcePtr<Resource> ResourceFetcher::fetchSynchronously(FetchRequest& request) 274 { 275 ASSERT(document()); 276 request.mutableResourceRequest().setTimeoutInterval(10); 277 ResourceLoaderOptions options(request.options()); 278 options.synchronousPolicy = RequestSynchronously; 279 request.setOptions(options); 280 return requestResource(Resource::Raw, request); 281 } 282 283 ResourcePtr<ImageResource> ResourceFetcher::fetchImage(FetchRequest& request) 284 { 285 if (Frame* f = frame()) { 286 if (f->document()->pageDismissalEventBeingDispatched() != Document::NoDismissal) { 287 KURL requestURL = request.resourceRequest().url(); 288 if (requestURL.isValid() && canRequest(Resource::Image, requestURL, request.options(), request.forPreload(), request.originRestriction())) 289 PingLoader::loadImage(f, requestURL); 290 return 0; 291 } 292 } 293 294 if (request.resourceRequest().url().protocolIsData()) 295 preCacheDataURIImage(request); 296 297 request.setDefer(clientDefersImage(request.resourceRequest().url()) ? FetchRequest::DeferredByClient : FetchRequest::NoDefer); 298 return toImageResource(requestResource(Resource::Image, request)); 299 } 300 301 void ResourceFetcher::preCacheDataURIImage(const FetchRequest& request) 302 { 303 const KURL& url = request.resourceRequest().url(); 304 ASSERT(url.protocolIsData()); 305 306 if (memoryCache()->resourceForURL(url)) 307 return; 308 309 if (Resource* resource = resourceFromDataURIRequest(request.resourceRequest(), request.options())) 310 memoryCache()->add(resource); 311 } 312 313 ResourcePtr<FontResource> ResourceFetcher::fetchFont(FetchRequest& request) 314 { 315 return toFontResource(requestResource(Resource::Font, request)); 316 } 317 318 ResourcePtr<ShaderResource> ResourceFetcher::fetchShader(FetchRequest& request) 319 { 320 return toShaderResource(requestResource(Resource::Shader, request)); 321 } 322 323 ResourcePtr<RawResource> ResourceFetcher::fetchImport(FetchRequest& request) 324 { 325 return toRawResource(requestResource(Resource::ImportResource, request)); 326 } 327 328 ResourcePtr<CSSStyleSheetResource> ResourceFetcher::fetchCSSStyleSheet(FetchRequest& request) 329 { 330 return toCSSStyleSheetResource(requestResource(Resource::CSSStyleSheet, request)); 331 } 332 333 ResourcePtr<CSSStyleSheetResource> ResourceFetcher::fetchUserCSSStyleSheet(FetchRequest& request) 334 { 335 KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(request.resourceRequest().url()); 336 337 if (Resource* existing = memoryCache()->resourceForURL(url)) { 338 if (existing->type() == Resource::CSSStyleSheet) 339 return toCSSStyleSheetResource(existing); 340 memoryCache()->remove(existing); 341 } 342 343 request.setOptions(ResourceLoaderOptions(DoNotSendCallbacks, SniffContent, BufferData, AllowStoredCredentials, ClientRequestedCredentials, AskClientForCrossOriginCredentials, SkipSecurityCheck, CheckContentSecurityPolicy, DocumentContext)); 344 return toCSSStyleSheetResource(requestResource(Resource::CSSStyleSheet, request)); 345 } 346 347 ResourcePtr<ScriptResource> ResourceFetcher::fetchScript(FetchRequest& request) 348 { 349 return toScriptResource(requestResource(Resource::Script, request)); 350 } 351 352 ResourcePtr<XSLStyleSheetResource> ResourceFetcher::fetchXSLStyleSheet(FetchRequest& request) 353 { 354 ASSERT(RuntimeEnabledFeatures::xsltEnabled()); 355 return toXSLStyleSheetResource(requestResource(Resource::XSLStyleSheet, request)); 356 } 357 358 ResourcePtr<DocumentResource> ResourceFetcher::fetchSVGDocument(FetchRequest& request) 359 { 360 return toDocumentResource(requestResource(Resource::SVGDocument, request)); 361 } 362 363 ResourcePtr<Resource> ResourceFetcher::fetchLinkResource(Resource::Type type, FetchRequest& request) 364 { 365 ASSERT(frame()); 366 ASSERT(type == Resource::LinkPrefetch || type == Resource::LinkSubresource); 367 return requestResource(type, request); 368 } 369 370 ResourcePtr<RawResource> ResourceFetcher::fetchRawResource(FetchRequest& request) 371 { 372 return toRawResource(requestResource(Resource::Raw, request)); 373 } 374 375 ResourcePtr<RawResource> ResourceFetcher::fetchMainResource(FetchRequest& request) 376 { 377 return toRawResource(requestResource(Resource::MainResource, request)); 378 } 379 380 bool ResourceFetcher::checkInsecureContent(Resource::Type type, const KURL& url, MixedContentBlockingTreatment treatment) const 381 { 382 if (treatment == TreatAsDefaultForType) { 383 switch (type) { 384 case Resource::XSLStyleSheet: 385 ASSERT(RuntimeEnabledFeatures::xsltEnabled()); 386 case Resource::Script: 387 case Resource::SVGDocument: 388 case Resource::CSSStyleSheet: 389 case Resource::ImportResource: 390 // These resource can inject script into the current document (Script, 391 // XSL) or exfiltrate the content of the current document (CSS). 392 treatment = TreatAsActiveContent; 393 break; 394 395 case Resource::TextTrack: 396 case Resource::Shader: 397 case Resource::Raw: 398 case Resource::Image: 399 case Resource::Font: 400 // These resources can corrupt only the frame's pixels. 401 treatment = TreatAsPassiveContent; 402 break; 403 404 case Resource::MainResource: 405 case Resource::LinkPrefetch: 406 case Resource::LinkSubresource: 407 // These cannot affect the current document. 408 treatment = TreatAsAlwaysAllowedContent; 409 break; 410 } 411 } 412 if (treatment == TreatAsActiveContent) { 413 if (Frame* f = frame()) { 414 if (!f->loader().mixedContentChecker()->canRunInsecureContent(m_document->securityOrigin(), url)) 415 return false; 416 Frame* top = f->tree().top(); 417 if (top != f && !top->loader().mixedContentChecker()->canRunInsecureContent(top->document()->securityOrigin(), url)) 418 return false; 419 } 420 } else if (treatment == TreatAsPassiveContent) { 421 if (Frame* f = frame()) { 422 Frame* top = f->tree().top(); 423 if (!top->loader().mixedContentChecker()->canDisplayInsecureContent(top->document()->securityOrigin(), url)) 424 return false; 425 } 426 } else { 427 ASSERT(treatment == TreatAsAlwaysAllowedContent); 428 } 429 return true; 430 } 431 432 bool ResourceFetcher::canRequest(Resource::Type type, const KURL& url, const ResourceLoaderOptions& options, bool forPreload, FetchRequest::OriginRestriction originRestriction) 433 { 434 SecurityOrigin* securityOrigin = options.securityOrigin.get(); 435 if (!securityOrigin && document()) 436 securityOrigin = document()->securityOrigin(); 437 438 if (securityOrigin && !securityOrigin->canDisplay(url)) { 439 if (!forPreload) 440 context().reportLocalLoadFailed(url); 441 WTF_LOG(ResourceLoading, "ResourceFetcher::requestResource URL was not allowed by SecurityOrigin::canDisplay"); 442 return 0; 443 } 444 445 // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved. 446 bool shouldBypassMainWorldContentSecurityPolicy = (frame() && frame()->script().shouldBypassMainWorldContentSecurityPolicy()) || (options.contentSecurityPolicyOption == DoNotCheckContentSecurityPolicy); 447 448 // Some types of resources can be loaded only from the same origin. Other 449 // types of resources, like Images, Scripts, and CSS, can be loaded from 450 // any URL. 451 switch (type) { 452 case Resource::MainResource: 453 case Resource::Image: 454 case Resource::CSSStyleSheet: 455 case Resource::Script: 456 case Resource::Font: 457 case Resource::Raw: 458 case Resource::LinkPrefetch: 459 case Resource::LinkSubresource: 460 case Resource::TextTrack: 461 case Resource::Shader: 462 case Resource::ImportResource: 463 // By default these types of resources can be loaded from any origin. 464 // FIXME: Are we sure about Resource::Font? 465 if (originRestriction == FetchRequest::RestrictToSameOrigin && !securityOrigin->canRequest(url)) { 466 printAccessDeniedMessage(url); 467 return false; 468 } 469 break; 470 case Resource::XSLStyleSheet: 471 ASSERT(RuntimeEnabledFeatures::xsltEnabled()); 472 case Resource::SVGDocument: 473 if (!securityOrigin->canRequest(url)) { 474 printAccessDeniedMessage(url); 475 return false; 476 } 477 break; 478 } 479 480 switch (type) { 481 case Resource::XSLStyleSheet: 482 ASSERT(RuntimeEnabledFeatures::xsltEnabled()); 483 if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowScriptFromSource(url)) 484 return false; 485 break; 486 case Resource::Script: 487 case Resource::ImportResource: 488 if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowScriptFromSource(url)) 489 return false; 490 491 if (frame()) { 492 Settings* settings = frame()->settings(); 493 if (!frame()->loader().client()->allowScriptFromSource(!settings || settings->isScriptEnabled(), url)) { 494 frame()->loader().client()->didNotAllowScript(); 495 return false; 496 } 497 } 498 break; 499 case Resource::Shader: 500 // Since shaders are referenced from CSS Styles use the same rules here. 501 case Resource::CSSStyleSheet: 502 if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowStyleFromSource(url)) 503 return false; 504 break; 505 case Resource::SVGDocument: 506 case Resource::Image: 507 if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowImageFromSource(url)) 508 return false; 509 break; 510 case Resource::Font: { 511 if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowFontFromSource(url)) 512 return false; 513 break; 514 } 515 case Resource::MainResource: 516 case Resource::Raw: 517 case Resource::LinkPrefetch: 518 case Resource::LinkSubresource: 519 break; 520 case Resource::TextTrack: 521 if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowMediaFromSource(url)) 522 return false; 523 break; 524 } 525 526 // Last of all, check for insecure content. We do this last so that when 527 // folks block insecure content with a CSP policy, they don't get a warning. 528 // They'll still get a warning in the console about CSP blocking the load. 529 530 // FIXME: Should we consider forPreload here? 531 if (!checkInsecureContent(type, url, options.mixedContentBlockingTreatment)) 532 return false; 533 534 return true; 535 } 536 537 bool ResourceFetcher::canAccess(Resource* resource, CORSEnabled corsEnabled, FetchRequest::OriginRestriction originRestriction) 538 { 539 // Redirects can change the response URL different from one of request. 540 if (!canRequest(resource->type(), resource->response().url(), resource->options(), false, originRestriction)) 541 return false; 542 543 String error; 544 switch (resource->type()) { 545 case Resource::Script: 546 case Resource::ImportResource: 547 if (corsEnabled == PotentiallyCORSEnabled 548 && !m_document->securityOrigin()->canRequest(resource->response().url()) 549 && !resource->passesAccessControlCheck(m_document->securityOrigin(), error)) { 550 if (frame() && frame()->document()) 551 frame()->document()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Script from origin '" + SecurityOrigin::create(resource->response().url())->toString() + "' has been blocked from loading by Cross-Origin Resource Sharing policy: " + error); 552 return false; 553 } 554 555 break; 556 default: 557 ASSERT_NOT_REACHED(); // FIXME: generalize to non-script resources 558 return false; 559 } 560 561 return true; 562 } 563 564 bool ResourceFetcher::shouldLoadNewResource() const 565 { 566 if (!frame()) 567 return false; 568 if (!m_documentLoader) 569 return true; 570 if (m_documentLoader == frame()->loader().activeDocumentLoader()) 571 return true; 572 return document() && document()->pageDismissalEventBeingDispatched() != Document::NoDismissal; 573 } 574 575 bool ResourceFetcher::resourceNeedsLoad(Resource* resource, const FetchRequest& request, RevalidationPolicy policy) 576 { 577 if (FetchRequest::DeferredByClient == request.defer()) 578 return false; 579 if (policy != Use) 580 return true; 581 if (resource->stillNeedsLoad()) 582 return true; 583 return request.options().synchronousPolicy == RequestSynchronously && resource->isLoading(); 584 } 585 586 ResourcePtr<Resource> ResourceFetcher::requestResource(Resource::Type type, FetchRequest& request) 587 { 588 ASSERT(request.options().synchronousPolicy == RequestAsynchronously || type == Resource::Raw); 589 590 KURL url = request.resourceRequest().url(); 591 592 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)); 593 594 // If only the fragment identifiers differ, it is the same resource. 595 url = MemoryCache::removeFragmentIdentifierIfNeeded(url); 596 597 if (!url.isValid()) 598 return 0; 599 600 if (!canRequest(type, url, request.options(), request.forPreload(), request.originRestriction())) 601 return 0; 602 603 if (Frame* f = frame()) 604 f->loader().client()->dispatchWillRequestResource(&request); 605 606 // See if we can use an existing resource from the cache. 607 ResourcePtr<Resource> resource = memoryCache()->resourceForURL(url); 608 609 const RevalidationPolicy policy = determineRevalidationPolicy(type, request.mutableResourceRequest(), request.forPreload(), resource.get(), request.defer()); 610 switch (policy) { 611 case Reload: 612 memoryCache()->remove(resource.get()); 613 // Fall through 614 case Load: 615 resource = loadResource(type, request, request.charset()); 616 break; 617 case Revalidate: 618 resource = revalidateResource(request, resource.get()); 619 break; 620 case Use: 621 resource->updateForAccess(); 622 notifyLoadedFromMemoryCache(resource.get()); 623 break; 624 } 625 626 if (!resource) 627 return 0; 628 629 if (policy != Use) 630 resource->setIdentifier(createUniqueIdentifier()); 631 632 if (!request.forPreload() || policy != Use) { 633 ResourceLoadPriority priority = loadPriority(type, request); 634 if (priority != resource->resourceRequest().priority()) { 635 resource->resourceRequest().setPriority(priority); 636 resource->didChangePriority(priority); 637 } 638 } 639 640 if (resourceNeedsLoad(resource.get(), request, policy)) { 641 if (!shouldLoadNewResource()) { 642 if (resource->inCache()) 643 memoryCache()->remove(resource.get()); 644 return 0; 645 } 646 647 if (!m_documentLoader || !m_documentLoader->scheduleArchiveLoad(resource.get(), request.resourceRequest())) 648 resource->load(this, request.options()); 649 650 // For asynchronous loads that immediately fail, it's sufficient to return a 651 // null Resource, as it indicates that something prevented the load from starting. 652 // If there's a network error, that failure will happen asynchronously. However, if 653 // a sync load receives a network error, it will have already happened by this point. 654 // In that case, the requester should have access to the relevant ResourceError, so 655 // we need to return a non-null Resource. 656 if (resource->errorOccurred()) { 657 if (resource->inCache()) 658 memoryCache()->remove(resource.get()); 659 return request.options().synchronousPolicy == RequestSynchronously ? resource : 0; 660 } 661 } 662 663 // FIXME: Temporarily leave main resource caching disabled for chromium, 664 // see https://bugs.webkit.org/show_bug.cgi?id=107962. Before caching main 665 // resources, we should be sure to understand the implications for memory 666 // use. 667 // 668 // Ensure main resources aren't preloaded, and other main resource loads 669 // are removed from cache to prevent reuse. 670 if (type == Resource::MainResource) { 671 ASSERT(policy != Use); 672 ASSERT(policy != Revalidate); 673 memoryCache()->remove(resource.get()); 674 if (request.forPreload()) 675 return 0; 676 } 677 678 if (!request.resourceRequest().url().protocolIsData()) { 679 if (policy == Use && !m_validatedURLs.contains(request.resourceRequest().url())) { 680 // Resources loaded from memory cache should be reported the first time they're used. 681 RefPtr<ResourceTimingInfo> info = ResourceTimingInfo::create(request.options().initiatorInfo.name, monotonicallyIncreasingTime()); 682 populateResourceTiming(info.get(), resource.get(), true); 683 m_scheduledResourceTimingReports.add(info, resource->type() == Resource::MainResource); 684 if (!m_resourceTimingReportTimer.isActive()) 685 m_resourceTimingReportTimer.startOneShot(0); 686 } 687 688 m_validatedURLs.add(request.resourceRequest().url()); 689 } 690 691 ASSERT(resource->url() == url.string()); 692 m_documentResources.set(resource->url(), resource); 693 return resource; 694 } 695 696 void ResourceFetcher::resourceTimingReportTimerFired(Timer<ResourceFetcher>* timer) 697 { 698 ASSERT_UNUSED(timer, timer == &m_resourceTimingReportTimer); 699 HashMap<RefPtr<ResourceTimingInfo>, bool> timingReports; 700 timingReports.swap(m_scheduledResourceTimingReports); 701 HashMap<RefPtr<ResourceTimingInfo>, bool>::iterator end = timingReports.end(); 702 for (HashMap<RefPtr<ResourceTimingInfo>, bool>::iterator it = timingReports.begin(); it != end; ++it) { 703 RefPtr<ResourceTimingInfo> info = it->key; 704 bool isMainResource = it->value; 705 reportResourceTiming(info.get(), document(), isMainResource); 706 } 707 } 708 709 void ResourceFetcher::determineTargetType(ResourceRequest& request, Resource::Type type) 710 { 711 ResourceRequest::TargetType targetType = requestTargetType(this, request, type); 712 request.setTargetType(targetType); 713 } 714 715 ResourceRequestCachePolicy ResourceFetcher::resourceRequestCachePolicy(const ResourceRequest& request, Resource::Type type) 716 { 717 if (type == Resource::MainResource) { 718 FrameLoadType frameLoadType = frame()->loader().loadType(); 719 bool isReload = frameLoadType == FrameLoadTypeReload || frameLoadType == FrameLoadTypeReloadFromOrigin; 720 if (request.httpMethod() == "POST" && frameLoadType == FrameLoadTypeBackForward) 721 return ReturnCacheDataDontLoad; 722 if (!m_documentLoader->overrideEncoding().isEmpty() || frameLoadType == FrameLoadTypeBackForward) 723 return ReturnCacheDataElseLoad; 724 if (isReload || frameLoadType == FrameLoadTypeSame || request.isConditional() || request.httpMethod() == "POST") 725 return ReloadIgnoringCacheData; 726 if (Frame* parent = frame()->tree().parent()) 727 return parent->document()->fetcher()->resourceRequestCachePolicy(request, type); 728 return UseProtocolCachePolicy; 729 } 730 731 if (request.isConditional()) 732 return ReloadIgnoringCacheData; 733 734 if (m_documentLoader && m_documentLoader->isLoadingInAPISense()) { 735 // For POST requests, we mutate the main resource's cache policy to avoid form resubmission. 736 // This policy should not be inherited by subresources. 737 ResourceRequestCachePolicy mainResourceCachePolicy = m_documentLoader->request().cachePolicy(); 738 if (mainResourceCachePolicy == ReturnCacheDataDontLoad) 739 return ReturnCacheDataElseLoad; 740 return mainResourceCachePolicy; 741 } 742 return UseProtocolCachePolicy; 743 } 744 745 void ResourceFetcher::addAdditionalRequestHeaders(ResourceRequest& request, Resource::Type type) 746 { 747 if (!frame()) 748 return; 749 750 if (request.cachePolicy() == UseProtocolCachePolicy) 751 request.setCachePolicy(resourceRequestCachePolicy(request, type)); 752 if (request.targetType() == ResourceRequest::TargetIsUnspecified) 753 determineTargetType(request, type); 754 if (type == Resource::LinkPrefetch || type == Resource::LinkSubresource) 755 request.setHTTPHeaderField("Purpose", "prefetch"); 756 757 context().addAdditionalRequestHeaders(*document(), request, type); 758 } 759 760 ResourcePtr<Resource> ResourceFetcher::revalidateResource(const FetchRequest& request, Resource* resource) 761 { 762 ASSERT(resource); 763 ASSERT(resource->inCache()); 764 ASSERT(resource->isLoaded()); 765 ASSERT(resource->canUseCacheValidator()); 766 ASSERT(!resource->resourceToRevalidate()); 767 768 ResourceRequest revalidatingRequest(resource->resourceRequest()); 769 addAdditionalRequestHeaders(revalidatingRequest, resource->type()); 770 771 const AtomicString& lastModified = resource->response().httpHeaderField("Last-Modified"); 772 const AtomicString& eTag = resource->response().httpHeaderField("ETag"); 773 if (!lastModified.isEmpty() || !eTag.isEmpty()) { 774 ASSERT(context().cachePolicy(document()) != CachePolicyReload); 775 if (context().cachePolicy(document()) == CachePolicyRevalidate) 776 revalidatingRequest.setHTTPHeaderField("Cache-Control", "max-age=0"); 777 if (!lastModified.isEmpty()) 778 revalidatingRequest.setHTTPHeaderField("If-Modified-Since", lastModified); 779 if (!eTag.isEmpty()) 780 revalidatingRequest.setHTTPHeaderField("If-None-Match", eTag); 781 } 782 783 ResourcePtr<Resource> newResource = createResource(resource->type(), revalidatingRequest, resource->encoding()); 784 785 WTF_LOG(ResourceLoading, "Resource %p created to revalidate %p", newResource.get(), resource); 786 newResource->setResourceToRevalidate(resource); 787 788 memoryCache()->remove(resource); 789 memoryCache()->add(newResource.get()); 790 storeResourceTimingInitiatorInformation(newResource, request); 791 TRACE_EVENT_ASYNC_BEGIN2("net", "Resource", newResource.get(), "url", newResource->url().string().ascii(), "priority", newResource->resourceRequest().priority()); 792 return newResource; 793 } 794 795 ResourcePtr<Resource> ResourceFetcher::loadResource(Resource::Type type, FetchRequest& request, const String& charset) 796 { 797 ASSERT(!memoryCache()->resourceForURL(request.resourceRequest().url())); 798 799 WTF_LOG(ResourceLoading, "Loading Resource for '%s'.", request.resourceRequest().url().elidedString().latin1().data()); 800 801 addAdditionalRequestHeaders(request.mutableResourceRequest(), type); 802 ResourcePtr<Resource> resource = createResource(type, request.mutableResourceRequest(), charset); 803 804 memoryCache()->add(resource.get()); 805 storeResourceTimingInitiatorInformation(resource, request); 806 TRACE_EVENT_ASYNC_BEGIN2("net", "Resource", resource.get(), "url", resource->url().string().ascii(), "priority", resource->resourceRequest().priority()); 807 return resource; 808 } 809 810 void ResourceFetcher::storeResourceTimingInitiatorInformation(const ResourcePtr<Resource>& resource, const FetchRequest& request) 811 { 812 if (request.options().requestInitiatorContext != DocumentContext) 813 return; 814 815 RefPtr<ResourceTimingInfo> info = ResourceTimingInfo::create(request.options().initiatorInfo.name, monotonicallyIncreasingTime()); 816 817 if (resource->type() == Resource::MainResource) { 818 // <iframe>s should report the initial navigation requested by the parent document, but not subsequent navigations. 819 if (frame()->ownerElement() && !frame()->ownerElement()->loadedNonEmptyDocument()) { 820 info->setInitiatorType(frame()->ownerElement()->localName()); 821 m_resourceTimingInfoMap.add(resource.get(), info); 822 frame()->ownerElement()->didLoadNonEmptyDocument(); 823 } 824 } else { 825 m_resourceTimingInfoMap.add(resource.get(), info); 826 } 827 } 828 829 ResourceFetcher::RevalidationPolicy ResourceFetcher::determineRevalidationPolicy(Resource::Type type, ResourceRequest& request, bool forPreload, Resource* existingResource, FetchRequest::DeferOption defer) const 830 { 831 if (!existingResource) 832 return Load; 833 834 // We already have a preload going for this URL. 835 if (forPreload && existingResource->isPreloaded()) 836 return Use; 837 838 // If the same URL has been loaded as a different type, we need to reload. 839 if (existingResource->type() != type) { 840 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to type mismatch."); 841 return Reload; 842 } 843 844 // Do not load from cache if images are not enabled. The load for this image will be blocked 845 // in ImageResource::load. 846 if (FetchRequest::DeferredByClient == defer) 847 return Reload; 848 849 // Always use data uris. 850 // FIXME: Extend this to non-images. 851 if (type == Resource::Image && request.url().protocolIsData()) 852 return Use; 853 854 if (!existingResource->canReuse(request)) 855 return Reload; 856 857 // Certain requests (e.g., XHRs) might have manually set headers that require revalidation. 858 // FIXME: In theory, this should be a Revalidate case. In practice, the MemoryCache revalidation path assumes a whole bunch 859 // of things about how revalidation works that manual headers violate, so punt to Reload instead. 860 if (request.isConditional()) 861 return Reload; 862 863 // Don't reload resources while pasting. 864 if (m_allowStaleResources) 865 return Use; 866 867 // Alwaus use preloads. 868 if (existingResource->isPreloaded()) 869 return Use; 870 871 // CachePolicyHistoryBuffer uses the cache no matter what. 872 CachePolicy cachePolicy = context().cachePolicy(document()); 873 if (cachePolicy == CachePolicyHistoryBuffer) 874 return Use; 875 876 // Don't reuse resources with Cache-control: no-store. 877 if (existingResource->response().cacheControlContainsNoStore()) { 878 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to Cache-control: no-store."); 879 return Reload; 880 } 881 882 // If credentials were sent with the previous request and won't be 883 // with this one, or vice versa, re-fetch the resource. 884 // 885 // This helps with the case where the server sends back 886 // "Access-Control-Allow-Origin: *" all the time, but some of the 887 // client's requests are made without CORS and some with. 888 if (existingResource->resourceRequest().allowCookies() != request.allowCookies()) { 889 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to difference in credentials settings."); 890 return Reload; 891 } 892 893 // During the initial load, avoid loading the same resource multiple times for a single document, 894 // even if the cache policies would tell us to. Raw resources are exempted. 895 if (type != Resource::Raw && document() && !document()->loadEventFinished() && m_validatedURLs.contains(existingResource->url())) 896 return Use; 897 898 // CachePolicyReload always reloads 899 if (cachePolicy == CachePolicyReload) { 900 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to CachePolicyReload."); 901 return Reload; 902 } 903 904 // We'll try to reload the resource if it failed last time. 905 if (existingResource->errorOccurred()) { 906 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicye reloading due to resource being in the error state"); 907 return Reload; 908 } 909 910 // For resources that are not yet loaded we ignore the cache policy. 911 if (existingResource->isLoading()) 912 return Use; 913 914 // Check if the cache headers requires us to revalidate (cache expiration for example). 915 if (cachePolicy == CachePolicyRevalidate || existingResource->mustRevalidateDueToCacheHeaders()) { 916 // See if the resource has usable ETag or Last-modified headers. 917 if (existingResource->canUseCacheValidator()) 918 return Revalidate; 919 920 // No, must reload. 921 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to missing cache validators."); 922 return Reload; 923 } 924 925 return Use; 926 } 927 928 void ResourceFetcher::printAccessDeniedMessage(const KURL& url) const 929 { 930 if (url.isNull()) 931 return; 932 933 if (!frame()) 934 return; 935 936 String message; 937 if (!m_document || m_document->url().isNull()) 938 message = "Unsafe attempt to load URL " + url.elidedString() + '.'; 939 else 940 message = "Unsafe attempt to load URL " + url.elidedString() + " from frame with URL " + m_document->url().elidedString() + ". Domains, protocols and ports must match.\n"; 941 942 frame()->document()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, message); 943 } 944 945 void ResourceFetcher::setAutoLoadImages(bool enable) 946 { 947 if (enable == m_autoLoadImages) 948 return; 949 950 m_autoLoadImages = enable; 951 952 if (!m_autoLoadImages) 953 return; 954 955 reloadImagesIfNotDeferred(); 956 } 957 958 void ResourceFetcher::setImagesEnabled(bool enable) 959 { 960 if (enable == m_imagesEnabled) 961 return; 962 963 m_imagesEnabled = enable; 964 965 if (!m_imagesEnabled) 966 return; 967 968 reloadImagesIfNotDeferred(); 969 } 970 971 bool ResourceFetcher::clientDefersImage(const KURL& url) const 972 { 973 return frame() && !frame()->loader().client()->allowImage(m_imagesEnabled, url); 974 } 975 976 bool ResourceFetcher::shouldDeferImageLoad(const KURL& url) const 977 { 978 return clientDefersImage(url) || !m_autoLoadImages; 979 } 980 981 void ResourceFetcher::reloadImagesIfNotDeferred() 982 { 983 DocumentResourceMap::iterator end = m_documentResources.end(); 984 for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it) { 985 Resource* resource = it->value.get(); 986 if (resource->type() == Resource::Image && resource->stillNeedsLoad() && !clientDefersImage(resource->url())) 987 const_cast<Resource*>(resource)->load(this, defaultResourceOptions()); 988 } 989 } 990 991 void ResourceFetcher::redirectReceived(Resource* resource, const ResourceResponse& redirectResponse) 992 { 993 ResourceTimingInfoMap::iterator it = m_resourceTimingInfoMap.find(resource); 994 if (it != m_resourceTimingInfoMap.end()) 995 it->value->addRedirect(redirectResponse); 996 } 997 998 void ResourceFetcher::didLoadResource(Resource* resource) 999 { 1000 RefPtr<DocumentLoader> protectDocumentLoader(m_documentLoader); 1001 RefPtr<Document> protectDocument(m_document); 1002 1003 if (resource && resource->response().isHTTP() && ((!resource->errorOccurred() && !resource->wasCanceled()) || resource->response().httpStatusCode() == 304) && document()) { 1004 ResourceTimingInfoMap::iterator it = m_resourceTimingInfoMap.find(resource); 1005 if (it != m_resourceTimingInfoMap.end()) { 1006 RefPtr<ResourceTimingInfo> info = it->value; 1007 m_resourceTimingInfoMap.remove(it); 1008 populateResourceTiming(info.get(), resource, false); 1009 reportResourceTiming(info.get(), document(), resource->type() == Resource::MainResource); 1010 } 1011 } 1012 1013 if (frame()) 1014 frame()->loader().loadDone(); 1015 performPostLoadActions(); 1016 1017 if (!m_garbageCollectDocumentResourcesTimer.isActive()) 1018 m_garbageCollectDocumentResourcesTimer.startOneShot(0); 1019 } 1020 1021 // Garbage collecting m_documentResources is a workaround for the 1022 // ResourcePtrs on the RHS being strong references. Ideally this 1023 // would be a weak map, however ResourcePtrs perform additional 1024 // bookkeeping on Resources, so instead pseudo-GC them -- when the 1025 // reference count reaches 1, m_documentResources is the only reference, so 1026 // remove it from the map. 1027 void ResourceFetcher::garbageCollectDocumentResourcesTimerFired(Timer<ResourceFetcher>* timer) 1028 { 1029 ASSERT_UNUSED(timer, timer == &m_garbageCollectDocumentResourcesTimer); 1030 garbageCollectDocumentResources(); 1031 } 1032 1033 void ResourceFetcher::garbageCollectDocumentResources() 1034 { 1035 typedef Vector<String, 10> StringVector; 1036 StringVector resourcesToDelete; 1037 1038 for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != m_documentResources.end(); ++it) { 1039 if (it->value->hasOneHandle()) 1040 resourcesToDelete.append(it->key); 1041 } 1042 1043 for (StringVector::const_iterator it = resourcesToDelete.begin(); it != resourcesToDelete.end(); ++it) 1044 m_documentResources.remove(*it); 1045 } 1046 1047 void ResourceFetcher::performPostLoadActions() 1048 { 1049 checkForPendingPreloads(); 1050 } 1051 1052 void ResourceFetcher::notifyLoadedFromMemoryCache(Resource* resource) 1053 { 1054 if (!frame() || !frame()->page() || resource->status() != Resource::Cached || m_validatedURLs.contains(resource->url())) 1055 return; 1056 if (!resource->shouldSendResourceLoadCallbacks()) 1057 return; 1058 1059 ResourceRequest request(resource->url()); 1060 unsigned long identifier = createUniqueIdentifier(); 1061 context().dispatchDidLoadResourceFromMemoryCache(request, resource->response()); 1062 // FIXME: If willSendRequest changes the request, we don't respect it. 1063 willSendRequest(identifier, request, ResourceResponse(), resource->options()); 1064 InspectorInstrumentation::markResourceAsCached(frame()->page(), identifier); 1065 context().sendRemainingDelegateMessages(m_documentLoader, identifier, resource->response(), resource->encodedSize()); 1066 } 1067 1068 void ResourceFetcher::incrementRequestCount(const Resource* res) 1069 { 1070 if (res->ignoreForRequestCount()) 1071 return; 1072 1073 ++m_requestCount; 1074 } 1075 1076 void ResourceFetcher::decrementRequestCount(const Resource* res) 1077 { 1078 if (res->ignoreForRequestCount()) 1079 return; 1080 1081 --m_requestCount; 1082 ASSERT(m_requestCount > -1); 1083 } 1084 1085 void ResourceFetcher::preload(Resource::Type type, FetchRequest& request, const String& charset) 1086 { 1087 requestPreload(type, request, charset); 1088 } 1089 1090 void ResourceFetcher::checkForPendingPreloads() 1091 { 1092 // FIXME: It seems wrong to poke body()->renderer() here. 1093 if (m_pendingPreloads.isEmpty() || !m_document->body() || !m_document->body()->renderer()) 1094 return; 1095 while (!m_pendingPreloads.isEmpty()) { 1096 PendingPreload preload = m_pendingPreloads.takeFirst(); 1097 // Don't request preload if the resource already loaded normally (this will result in double load if the page is being reloaded with cached results ignored). 1098 if (!cachedResource(preload.m_request.resourceRequest().url())) 1099 requestPreload(preload.m_type, preload.m_request, preload.m_charset); 1100 } 1101 m_pendingPreloads.clear(); 1102 } 1103 1104 void ResourceFetcher::requestPreload(Resource::Type type, FetchRequest& request, const String& charset) 1105 { 1106 String encoding; 1107 if (type == Resource::Script || type == Resource::CSSStyleSheet) 1108 encoding = charset.isEmpty() ? m_document->charset().string() : charset; 1109 1110 request.setCharset(encoding); 1111 request.setForPreload(true); 1112 1113 ResourcePtr<Resource> resource = requestResource(type, request); 1114 if (!resource || (m_preloads && m_preloads->contains(resource.get()))) 1115 return; 1116 TRACE_EVENT_ASYNC_STEP_INTO0("net", "Resource", resource.get(), "Preload"); 1117 resource->increasePreloadCount(); 1118 1119 if (!m_preloads) 1120 m_preloads = adoptPtr(new ListHashSet<Resource*>); 1121 m_preloads->add(resource.get()); 1122 1123 #if PRELOAD_DEBUG 1124 printf("PRELOADING %s\n", resource->url().latin1().data()); 1125 #endif 1126 } 1127 1128 bool ResourceFetcher::isPreloaded(const String& urlString) const 1129 { 1130 const KURL& url = m_document->completeURL(urlString); 1131 1132 if (m_preloads) { 1133 ListHashSet<Resource*>::iterator end = m_preloads->end(); 1134 for (ListHashSet<Resource*>::iterator it = m_preloads->begin(); it != end; ++it) { 1135 Resource* resource = *it; 1136 if (resource->url() == url) 1137 return true; 1138 } 1139 } 1140 1141 Deque<PendingPreload>::const_iterator dequeEnd = m_pendingPreloads.end(); 1142 for (Deque<PendingPreload>::const_iterator it = m_pendingPreloads.begin(); it != dequeEnd; ++it) { 1143 PendingPreload pendingPreload = *it; 1144 if (pendingPreload.m_request.resourceRequest().url() == url) 1145 return true; 1146 } 1147 return false; 1148 } 1149 1150 void ResourceFetcher::clearPreloads() 1151 { 1152 #if PRELOAD_DEBUG 1153 printPreloadStats(); 1154 #endif 1155 if (!m_preloads) 1156 return; 1157 1158 ListHashSet<Resource*>::iterator end = m_preloads->end(); 1159 for (ListHashSet<Resource*>::iterator it = m_preloads->begin(); it != end; ++it) { 1160 Resource* res = *it; 1161 res->decreasePreloadCount(); 1162 bool deleted = res->deleteIfPossible(); 1163 if (!deleted && res->preloadResult() == Resource::PreloadNotReferenced) 1164 memoryCache()->remove(res); 1165 } 1166 m_preloads.clear(); 1167 } 1168 1169 void ResourceFetcher::clearPendingPreloads() 1170 { 1171 m_pendingPreloads.clear(); 1172 } 1173 1174 void ResourceFetcher::didFinishLoading(const Resource* resource, double finishTime, const ResourceLoaderOptions& options) 1175 { 1176 TRACE_EVENT_ASYNC_END0("net", "Resource", resource); 1177 if (options.sendLoadCallbacks != SendCallbacks) 1178 return; 1179 context().dispatchDidFinishLoading(m_documentLoader, resource->identifier(), finishTime); 1180 } 1181 1182 void ResourceFetcher::didChangeLoadingPriority(const Resource* resource, ResourceLoadPriority loadPriority) 1183 { 1184 TRACE_EVENT_ASYNC_STEP_INTO1("net", "Resource", resource, "ChangePriority", "priority", loadPriority); 1185 context().dispatchDidChangeResourcePriority(resource->identifier(), loadPriority); 1186 } 1187 1188 void ResourceFetcher::didFailLoading(const Resource* resource, const ResourceError& error, const ResourceLoaderOptions& options) 1189 { 1190 TRACE_EVENT_ASYNC_END0("net", "Resource", resource); 1191 if (options.sendLoadCallbacks != SendCallbacks) 1192 return; 1193 context().dispatchDidFail(m_documentLoader, resource->identifier(), error); 1194 } 1195 1196 void ResourceFetcher::willSendRequest(unsigned long identifier, ResourceRequest& request, const ResourceResponse& redirectResponse, const ResourceLoaderOptions& options) 1197 { 1198 if (options.sendLoadCallbacks == SendCallbacks) 1199 context().dispatchWillSendRequest(m_documentLoader, identifier, request, redirectResponse, options.initiatorInfo); 1200 else 1201 InspectorInstrumentation::willSendRequest(frame(), identifier, m_documentLoader, request, redirectResponse, options.initiatorInfo); 1202 } 1203 1204 void ResourceFetcher::didReceiveResponse(const Resource* resource, const ResourceResponse& response, const ResourceLoaderOptions& options) 1205 { 1206 if (options.sendLoadCallbacks != SendCallbacks) 1207 return; 1208 context().dispatchDidReceiveResponse(m_documentLoader, resource->identifier(), response, resource->loader()); 1209 } 1210 1211 void ResourceFetcher::didReceiveData(const Resource* resource, const char* data, int dataLength, int encodedDataLength, const ResourceLoaderOptions& options) 1212 { 1213 // FIXME: use frame of master document for imported documents. 1214 InspectorInstrumentationCookie cookie = InspectorInstrumentation::willReceiveResourceData(frame(), resource->identifier(), encodedDataLength); 1215 if (options.sendLoadCallbacks != SendCallbacks) 1216 return; 1217 context().dispatchDidReceiveData(m_documentLoader, resource->identifier(), data, dataLength, encodedDataLength); 1218 InspectorInstrumentation::didReceiveResourceData(cookie); 1219 } 1220 1221 void ResourceFetcher::didDownloadData(const Resource* resource, int dataLength, int encodedDataLength, const ResourceLoaderOptions& options) 1222 { 1223 InspectorInstrumentationCookie cookie = InspectorInstrumentation::willReceiveResourceData(frame(), resource->identifier(), encodedDataLength); 1224 if (options.sendLoadCallbacks != SendCallbacks) 1225 return; 1226 context().dispatchDidDownloadData(m_documentLoader, resource->identifier(), dataLength, encodedDataLength); 1227 InspectorInstrumentation::didReceiveResourceData(cookie); 1228 } 1229 1230 void ResourceFetcher::subresourceLoaderFinishedLoadingOnePart(ResourceLoader* loader) 1231 { 1232 if (m_multipartLoaders) 1233 m_multipartLoaders->add(loader); 1234 if (m_loaders) 1235 m_loaders->remove(loader); 1236 if (Frame* frame = this->frame()) 1237 return frame->loader().checkLoadComplete(m_documentLoader); 1238 } 1239 1240 void ResourceFetcher::didInitializeResourceLoader(ResourceLoader* loader) 1241 { 1242 if (!m_document) 1243 return; 1244 if (!m_loaders) 1245 m_loaders = adoptPtr(new ResourceLoaderSet()); 1246 ASSERT(!m_loaders->contains(loader)); 1247 m_loaders->add(loader); 1248 } 1249 1250 void ResourceFetcher::willTerminateResourceLoader(ResourceLoader* loader) 1251 { 1252 if (!m_loaders || !m_loaders->contains(loader)) 1253 return; 1254 m_loaders->remove(loader); 1255 if (Frame* frame = this->frame()) 1256 frame->loader().checkLoadComplete(m_documentLoader); 1257 } 1258 1259 void ResourceFetcher::willStartLoadingResource(ResourceRequest& request) 1260 { 1261 if (m_documentLoader) 1262 m_documentLoader->applicationCacheHost()->willStartLoadingResource(request); 1263 } 1264 1265 void ResourceFetcher::stopFetching() 1266 { 1267 if (m_multipartLoaders) 1268 m_multipartLoaders->cancelAll(); 1269 if (m_loaders) 1270 m_loaders->cancelAll(); 1271 } 1272 1273 bool ResourceFetcher::isFetching() const 1274 { 1275 return m_loaders && !m_loaders->isEmpty(); 1276 } 1277 1278 void ResourceFetcher::setDefersLoading(bool defers) 1279 { 1280 if (m_loaders) 1281 m_loaders->setAllDefersLoading(defers); 1282 } 1283 1284 bool ResourceFetcher::defersLoading() const 1285 { 1286 if (Frame* frame = this->frame()) 1287 return frame->page()->defersLoading(); 1288 return false; 1289 } 1290 1291 bool ResourceFetcher::isLoadedBy(ResourceLoaderHost* possibleOwner) const 1292 { 1293 return this == possibleOwner; 1294 } 1295 1296 bool ResourceFetcher::shouldRequest(Resource* resource, const ResourceRequest& request, const ResourceLoaderOptions& options) 1297 { 1298 if (!canRequest(resource->type(), request.url(), options, false, FetchRequest::UseDefaultOriginRestrictionForType)) 1299 return false; 1300 if (resource->type() == Resource::Image && shouldDeferImageLoad(request.url())) 1301 return false; 1302 return true; 1303 } 1304 1305 void ResourceFetcher::refResourceLoaderHost() 1306 { 1307 ref(); 1308 } 1309 1310 void ResourceFetcher::derefResourceLoaderHost() 1311 { 1312 deref(); 1313 } 1314 1315 #if PRELOAD_DEBUG 1316 void ResourceFetcher::printPreloadStats() 1317 { 1318 unsigned scripts = 0; 1319 unsigned scriptMisses = 0; 1320 unsigned stylesheets = 0; 1321 unsigned stylesheetMisses = 0; 1322 unsigned images = 0; 1323 unsigned imageMisses = 0; 1324 ListHashSet<Resource*>::iterator end = m_preloads.end(); 1325 for (ListHashSet<Resource*>::iterator it = m_preloads.begin(); it != end; ++it) { 1326 Resource* res = *it; 1327 if (res->preloadResult() == Resource::PreloadNotReferenced) 1328 printf("!! UNREFERENCED PRELOAD %s\n", res->url().latin1().data()); 1329 else if (res->preloadResult() == Resource::PreloadReferencedWhileComplete) 1330 printf("HIT COMPLETE PRELOAD %s\n", res->url().latin1().data()); 1331 else if (res->preloadResult() == Resource::PreloadReferencedWhileLoading) 1332 printf("HIT LOADING PRELOAD %s\n", res->url().latin1().data()); 1333 1334 if (res->type() == Resource::Script) { 1335 scripts++; 1336 if (res->preloadResult() < Resource::PreloadReferencedWhileLoading) 1337 scriptMisses++; 1338 } else if (res->type() == Resource::CSSStyleSheet) { 1339 stylesheets++; 1340 if (res->preloadResult() < Resource::PreloadReferencedWhileLoading) 1341 stylesheetMisses++; 1342 } else { 1343 images++; 1344 if (res->preloadResult() < Resource::PreloadReferencedWhileLoading) 1345 imageMisses++; 1346 } 1347 1348 if (res->errorOccurred()) 1349 memoryCache()->remove(res); 1350 1351 res->decreasePreloadCount(); 1352 } 1353 m_preloads.clear(); 1354 1355 if (scripts) 1356 printf("SCRIPTS: %d (%d hits, hit rate %d%%)\n", scripts, scripts - scriptMisses, (scripts - scriptMisses) * 100 / scripts); 1357 if (stylesheets) 1358 printf("STYLESHEETS: %d (%d hits, hit rate %d%%)\n", stylesheets, stylesheets - stylesheetMisses, (stylesheets - stylesheetMisses) * 100 / stylesheets); 1359 if (images) 1360 printf("IMAGES: %d (%d hits, hit rate %d%%)\n", images, images - imageMisses, (images - imageMisses) * 100 / images); 1361 } 1362 #endif 1363 1364 const ResourceLoaderOptions& ResourceFetcher::defaultResourceOptions() 1365 { 1366 DEFINE_STATIC_LOCAL(ResourceLoaderOptions, options, (SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, ClientRequestedCredentials, AskClientForCrossOriginCredentials, DoSecurityCheck, CheckContentSecurityPolicy, DocumentContext)); 1367 return options; 1368 } 1369 1370 } 1371