1/* 2 Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) 3 Copyright (C) 2001 Dirk Mueller (mueller@kde.org) 4 Copyright (C) 2002 Waldo Bastian (bastian@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 "CachedResourceLoader.h" 29 30#include "CachedCSSStyleSheet.h" 31#include "CachedSVGDocument.h" 32#include "CachedFont.h" 33#include "CachedImage.h" 34#include "CachedRawResource.h" 35#include "CachedResourceRequest.h" 36#include "CachedScript.h" 37#include "CachedXSLStyleSheet.h" 38#include "ContentSecurityPolicy.h" 39#include "DOMWindow.h" 40#include "Document.h" 41#include "DocumentLoader.h" 42#include "Frame.h" 43#include "FrameLoader.h" 44#include "FrameLoaderClient.h" 45#include "HTMLElement.h" 46#include "HTMLFrameOwnerElement.h" 47#include "LoaderStrategy.h" 48#include "Logging.h" 49#include "MemoryCache.h" 50#include "Page.h" 51#include "PingLoader.h" 52#include "PlatformStrategies.h" 53#include "RenderElement.h" 54#include "ResourceLoadScheduler.h" 55#include "ScriptController.h" 56#include "SecurityOrigin.h" 57#include "SessionID.h" 58#include "Settings.h" 59#include <wtf/text/CString.h> 60#include <wtf/text/WTFString.h> 61 62#if ENABLE(VIDEO_TRACK) 63#include "CachedTextTrack.h" 64#endif 65 66#if ENABLE(RESOURCE_TIMING) 67#include "Performance.h" 68#endif 69 70#define PRELOAD_DEBUG 0 71 72namespace WebCore { 73 74static CachedResource* createResource(CachedResource::Type type, ResourceRequest& request, const String& charset, SessionID sessionID) 75{ 76 switch (type) { 77 case CachedResource::ImageResource: 78 return new CachedImage(request, sessionID); 79 case CachedResource::CSSStyleSheet: 80 return new CachedCSSStyleSheet(request, charset, sessionID); 81 case CachedResource::Script: 82 return new CachedScript(request, charset, sessionID); 83 case CachedResource::SVGDocumentResource: 84 return new CachedSVGDocument(request, sessionID); 85 case CachedResource::FontResource: 86 return new CachedFont(request, sessionID); 87 case CachedResource::RawResource: 88 case CachedResource::MainResource: 89 return new CachedRawResource(request, type, sessionID); 90#if ENABLE(XSLT) 91 case CachedResource::XSLStyleSheet: 92 return new CachedXSLStyleSheet(request, sessionID); 93#endif 94#if ENABLE(LINK_PREFETCH) 95 case CachedResource::LinkPrefetch: 96 return new CachedResource(request, CachedResource::LinkPrefetch, sessionID); 97 case CachedResource::LinkSubresource: 98 return new CachedResource(request, CachedResource::LinkSubresource, sessionID); 99#endif 100#if ENABLE(VIDEO_TRACK) 101 case CachedResource::TextTrackResource: 102 return new CachedTextTrack(request, sessionID); 103#endif 104 } 105 ASSERT_NOT_REACHED(); 106 return 0; 107} 108 109CachedResourceLoader::CachedResourceLoader(DocumentLoader* documentLoader) 110 : m_document(0) 111 , m_documentLoader(documentLoader) 112 , m_requestCount(0) 113 , m_garbageCollectDocumentResourcesTimer(this, &CachedResourceLoader::garbageCollectDocumentResourcesTimerFired) 114 , m_autoLoadImages(true) 115 , m_imagesEnabled(true) 116 , m_allowStaleResources(false) 117{ 118} 119 120CachedResourceLoader::~CachedResourceLoader() 121{ 122 m_documentLoader = 0; 123 m_document = 0; 124 125 clearPreloads(); 126 DocumentResourceMap::iterator end = m_documentResources.end(); 127 for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it) 128 it->value->setOwningCachedResourceLoader(0); 129 130 // Make sure no requests still point to this CachedResourceLoader 131 ASSERT(m_requestCount == 0); 132} 133 134CachedResource* CachedResourceLoader::cachedResource(const String& resourceURL) const 135{ 136 URL url = m_document->completeURL(resourceURL); 137 return cachedResource(url); 138} 139 140CachedResource* CachedResourceLoader::cachedResource(const URL& resourceURL) const 141{ 142 URL url = MemoryCache::removeFragmentIdentifierIfNeeded(resourceURL); 143 return m_documentResources.get(url).get(); 144} 145 146Frame* CachedResourceLoader::frame() const 147{ 148 return m_documentLoader ? m_documentLoader->frame() : 0; 149} 150 151SessionID CachedResourceLoader::sessionID() const 152{ 153 SessionID sessionID = SessionID::defaultSessionID(); 154 155 if (Frame* f = frame()) 156 sessionID = f->page()->sessionID(); 157 158 return sessionID; 159} 160 161CachedResourceHandle<CachedImage> CachedResourceLoader::requestImage(CachedResourceRequest& request) 162{ 163 if (Frame* frame = this->frame()) { 164 if (frame->loader().pageDismissalEventBeingDispatched() != FrameLoader::NoDismissal) { 165 URL requestURL = request.resourceRequest().url(); 166 if (requestURL.isValid() && canRequest(CachedResource::ImageResource, requestURL, request.options(), request.forPreload())) 167 PingLoader::loadImage(*frame, requestURL); 168 return nullptr; 169 } 170 } 171 172 request.setDefer(clientDefersImage(request.resourceRequest().url()) ? CachedResourceRequest::DeferredByClient : CachedResourceRequest::NoDefer); 173 return toCachedImage(requestResource(CachedResource::ImageResource, request).get()); 174} 175 176CachedResourceHandle<CachedFont> CachedResourceLoader::requestFont(CachedResourceRequest& request) 177{ 178 return toCachedFont(requestResource(CachedResource::FontResource, request).get()); 179} 180 181#if ENABLE(VIDEO_TRACK) 182CachedResourceHandle<CachedTextTrack> CachedResourceLoader::requestTextTrack(CachedResourceRequest& request) 183{ 184 return toCachedTextTrack(requestResource(CachedResource::TextTrackResource, request).get()); 185} 186#endif 187 188CachedResourceHandle<CachedCSSStyleSheet> CachedResourceLoader::requestCSSStyleSheet(CachedResourceRequest& request) 189{ 190 return toCachedCSSStyleSheet(requestResource(CachedResource::CSSStyleSheet, request).get()); 191} 192 193CachedResourceHandle<CachedCSSStyleSheet> CachedResourceLoader::requestUserCSSStyleSheet(CachedResourceRequest& request) 194{ 195 URL url = MemoryCache::removeFragmentIdentifierIfNeeded(request.resourceRequest().url()); 196 197#if ENABLE(CACHE_PARTITIONING) 198 request.mutableResourceRequest().setCachePartition(document()->topOrigin()->cachePartition()); 199#endif 200 201 if (CachedResource* existing = memoryCache()->resourceForRequest(request.resourceRequest(), sessionID())) { 202 if (existing->type() == CachedResource::CSSStyleSheet) 203 return toCachedCSSStyleSheet(existing); 204 memoryCache()->remove(existing); 205 } 206 if (url.string() != request.resourceRequest().url()) 207 request.mutableResourceRequest().setURL(url); 208 209 CachedResourceHandle<CachedCSSStyleSheet> userSheet = new CachedCSSStyleSheet(request.resourceRequest(), request.charset(), sessionID()); 210 211 memoryCache()->add(userSheet.get()); 212 // FIXME: loadResource calls setOwningCachedResourceLoader() if the resource couldn't be added to cache. Does this function need to call it, too? 213 214 userSheet->load(this, ResourceLoaderOptions(DoNotSendCallbacks, SniffContent, BufferData, AllowStoredCredentials, AskClientForAllCredentials, SkipSecurityCheck, UseDefaultOriginRestrictionsForType)); 215 216 return userSheet; 217} 218 219CachedResourceHandle<CachedScript> CachedResourceLoader::requestScript(CachedResourceRequest& request) 220{ 221 return toCachedScript(requestResource(CachedResource::Script, request).get()); 222} 223 224#if ENABLE(XSLT) 225CachedResourceHandle<CachedXSLStyleSheet> CachedResourceLoader::requestXSLStyleSheet(CachedResourceRequest& request) 226{ 227 return toCachedXSLStyleSheet(requestResource(CachedResource::XSLStyleSheet, request).get()); 228} 229#endif 230 231CachedResourceHandle<CachedSVGDocument> CachedResourceLoader::requestSVGDocument(CachedResourceRequest& request) 232{ 233 return toCachedSVGDocument(requestResource(CachedResource::SVGDocumentResource, request).get()); 234} 235 236#if ENABLE(LINK_PREFETCH) 237CachedResourceHandle<CachedResource> CachedResourceLoader::requestLinkResource(CachedResource::Type type, CachedResourceRequest& request) 238{ 239 ASSERT(frame()); 240 ASSERT(type == CachedResource::LinkPrefetch || type == CachedResource::LinkSubresource); 241 return requestResource(type, request); 242} 243#endif 244 245CachedResourceHandle<CachedRawResource> CachedResourceLoader::requestRawResource(CachedResourceRequest& request) 246{ 247 return toCachedRawResource(requestResource(CachedResource::RawResource, request).get()); 248} 249 250CachedResourceHandle<CachedRawResource> CachedResourceLoader::requestMainResource(CachedResourceRequest& request) 251{ 252 return toCachedRawResource(requestResource(CachedResource::MainResource, request).get()); 253} 254 255bool CachedResourceLoader::checkInsecureContent(CachedResource::Type type, const URL& url) const 256{ 257 switch (type) { 258 case CachedResource::Script: 259#if ENABLE(XSLT) 260 case CachedResource::XSLStyleSheet: 261#endif 262 case CachedResource::SVGDocumentResource: 263 case CachedResource::CSSStyleSheet: 264 // These resource can inject script into the current document (Script, 265 // XSL) or exfiltrate the content of the current document (CSS). 266 if (Frame* f = frame()) 267 if (!f->loader().mixedContentChecker().canRunInsecureContent(m_document->securityOrigin(), url)) 268 return false; 269 break; 270#if ENABLE(VIDEO_TRACK) 271 case CachedResource::TextTrackResource: 272#endif 273 case CachedResource::RawResource: 274 case CachedResource::ImageResource: 275 case CachedResource::FontResource: { 276 // These resources can corrupt only the frame's pixels. 277 if (Frame* f = frame()) { 278 Frame& topFrame = f->tree().top(); 279 if (!topFrame.loader().mixedContentChecker().canDisplayInsecureContent(topFrame.document()->securityOrigin(), url)) 280 return false; 281 } 282 break; 283 } 284 case CachedResource::MainResource: 285#if ENABLE(LINK_PREFETCH) 286 case CachedResource::LinkPrefetch: 287 case CachedResource::LinkSubresource: 288 // Prefetch cannot affect the current document. 289#endif 290 break; 291 } 292 return true; 293} 294 295bool CachedResourceLoader::canRequest(CachedResource::Type type, const URL& url, const ResourceLoaderOptions& options, bool forPreload) 296{ 297 if (document() && !document()->securityOrigin()->canDisplay(url)) { 298 if (!forPreload) 299 FrameLoader::reportLocalLoadFailed(frame(), url.stringCenterEllipsizedToLength()); 300 LOG(ResourceLoading, "CachedResourceLoader::requestResource URL was not allowed by SecurityOrigin::canDisplay"); 301 return 0; 302 } 303 304 // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved. 305 bool shouldBypassMainWorldContentSecurityPolicy = (frame() && frame()->script().shouldBypassMainWorldContentSecurityPolicy()); 306 307 // Some types of resources can be loaded only from the same origin. Other 308 // types of resources, like Images, Scripts, and CSS, can be loaded from 309 // any URL. 310 switch (type) { 311 case CachedResource::MainResource: 312 case CachedResource::ImageResource: 313 case CachedResource::CSSStyleSheet: 314 case CachedResource::Script: 315 case CachedResource::FontResource: 316 case CachedResource::RawResource: 317#if ENABLE(LINK_PREFETCH) 318 case CachedResource::LinkPrefetch: 319 case CachedResource::LinkSubresource: 320#endif 321#if ENABLE(VIDEO_TRACK) 322 case CachedResource::TextTrackResource: 323#endif 324 if (options.requestOriginPolicy() == RestrictToSameOrigin && !m_document->securityOrigin()->canRequest(url)) { 325 printAccessDeniedMessage(url); 326 return false; 327 } 328 break; 329 case CachedResource::SVGDocumentResource: 330#if ENABLE(XSLT) 331 case CachedResource::XSLStyleSheet: 332 if (!m_document->securityOrigin()->canRequest(url)) { 333 printAccessDeniedMessage(url); 334 return false; 335 } 336#endif 337 break; 338 } 339 340 switch (type) { 341#if ENABLE(XSLT) 342 case CachedResource::XSLStyleSheet: 343 if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowScriptFromSource(url)) 344 return false; 345 break; 346#endif 347 case CachedResource::Script: 348 if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowScriptFromSource(url)) 349 return false; 350 if (frame() && !frame()->settings().isScriptEnabled()) 351 return false; 352 break; 353 case CachedResource::CSSStyleSheet: 354 if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowStyleFromSource(url)) 355 return false; 356 break; 357 case CachedResource::SVGDocumentResource: 358 case CachedResource::ImageResource: 359 if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowImageFromSource(url)) 360 return false; 361 break; 362 case CachedResource::FontResource: { 363 if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowFontFromSource(url)) 364 return false; 365 break; 366 } 367 case CachedResource::MainResource: 368 case CachedResource::RawResource: 369#if ENABLE(LINK_PREFETCH) 370 case CachedResource::LinkPrefetch: 371 case CachedResource::LinkSubresource: 372#endif 373 break; 374#if ENABLE(VIDEO_TRACK) 375 case CachedResource::TextTrackResource: 376 if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowMediaFromSource(url)) 377 return false; 378 break; 379#endif 380 } 381 382 // Last of all, check for insecure content. We do this last so that when 383 // folks block insecure content with a CSP policy, they don't get a warning. 384 // They'll still get a warning in the console about CSP blocking the load. 385 386 // FIXME: Should we consider forPreload here? 387 if (!checkInsecureContent(type, url)) 388 return false; 389 390 return true; 391} 392 393bool CachedResourceLoader::shouldContinueAfterNotifyingLoadedFromMemoryCache(const CachedResourceRequest& request, CachedResource* resource) 394{ 395 if (!resource || !frame() || resource->status() != CachedResource::Cached) 396 return true; 397 398 ResourceRequest newRequest = ResourceRequest(resource->url()); 399#if ENABLE(INSPECTOR) 400 if (request.resourceRequest().hiddenFromInspector()) 401 newRequest.setHiddenFromInspector(true); 402#else 403 UNUSED_PARAM(request); 404#endif 405 frame()->loader().loadedResourceFromMemoryCache(resource, newRequest); 406 407 // FIXME <http://webkit.org/b/113251>: If the delegate modifies the request's 408 // URL, it is no longer appropriate to use this CachedResource. 409 return !newRequest.isNull(); 410} 411 412CachedResourceHandle<CachedResource> CachedResourceLoader::requestResource(CachedResource::Type type, CachedResourceRequest& request) 413{ 414 URL url = request.resourceRequest().url(); 415 416 LOG(ResourceLoading, "CachedResourceLoader::requestResource '%s', charset '%s', priority=%d, forPreload=%u", url.stringCenterEllipsizedToLength().latin1().data(), request.charset().latin1().data(), request.priority(), request.forPreload()); 417 418 // If only the fragment identifiers differ, it is the same resource. 419 url = MemoryCache::removeFragmentIdentifierIfNeeded(url); 420 421 if (!url.isValid()) 422 return 0; 423 424 if (!canRequest(type, url, request.options(), request.forPreload())) 425 return 0; 426 427 if (Frame* f = frame()) 428 f->loader().client().dispatchWillRequestResource(&request); 429 430 if (memoryCache()->disabled()) { 431 DocumentResourceMap::iterator it = m_documentResources.find(url.string()); 432 if (it != m_documentResources.end()) { 433 it->value->setOwningCachedResourceLoader(0); 434 m_documentResources.remove(it); 435 } 436 } 437 438 // See if we can use an existing resource from the cache. 439 CachedResourceHandle<CachedResource> resource; 440#if ENABLE(CACHE_PARTITIONING) 441 if (document()) 442 request.mutableResourceRequest().setCachePartition(document()->topOrigin()->cachePartition()); 443#endif 444 445 resource = memoryCache()->resourceForRequest(request.resourceRequest(), sessionID()); 446 447 const RevalidationPolicy policy = determineRevalidationPolicy(type, request.mutableResourceRequest(), request.forPreload(), resource.get(), request.defer()); 448 switch (policy) { 449 case Reload: 450 memoryCache()->remove(resource.get()); 451 FALLTHROUGH; 452 case Load: 453 resource = loadResource(type, request); 454 break; 455 case Revalidate: 456 resource = revalidateResource(request, resource.get()); 457 break; 458 case Use: 459 if (!shouldContinueAfterNotifyingLoadedFromMemoryCache(request, resource.get())) 460 return 0; 461 memoryCache()->resourceAccessed(resource.get()); 462 break; 463 } 464 465 if (!resource) 466 return 0; 467 468 if (!request.forPreload() || policy != Use) 469 resource->setLoadPriority(request.priority()); 470 471 if ((policy != Use || resource->stillNeedsLoad()) && CachedResourceRequest::NoDefer == request.defer()) { 472 resource->load(this, request.options()); 473 474 // We don't support immediate loads, but we do support immediate failure. 475 if (resource->errorOccurred()) { 476 if (resource->inCache()) 477 memoryCache()->remove(resource.get()); 478 return 0; 479 } 480 } 481 482 if (document() && !document()->loadEventFinished() && !request.resourceRequest().url().protocolIsData()) 483 m_validatedURLs.add(request.resourceRequest().url()); 484 485 ASSERT(resource->url() == url.string()); 486 m_documentResources.set(resource->url(), resource); 487 return resource; 488} 489 490void CachedResourceLoader::documentDidFinishLoadEvent() 491{ 492 m_validatedURLs.clear(); 493} 494 495CachedResourceHandle<CachedResource> CachedResourceLoader::revalidateResource(const CachedResourceRequest& request, CachedResource* resource) 496{ 497 ASSERT(resource); 498 ASSERT(resource->inCache()); 499 ASSERT(!memoryCache()->disabled()); 500 ASSERT(resource->canUseCacheValidator()); 501 ASSERT(!resource->resourceToRevalidate()); 502 ASSERT(resource->sessionID() == sessionID()); 503 504 // Copy the URL out of the resource to be revalidated in case it gets deleted by the remove() call below. 505 String url = resource->url(); 506 CachedResourceHandle<CachedResource> newResource = createResource(resource->type(), resource->resourceRequest(), resource->encoding(), resource->sessionID()); 507 508 LOG(ResourceLoading, "Resource %p created to revalidate %p", newResource.get(), resource); 509 newResource->setResourceToRevalidate(resource); 510 511 memoryCache()->remove(resource); 512 memoryCache()->add(newResource.get()); 513#if ENABLE(RESOURCE_TIMING) 514 storeResourceTimingInitiatorInformation(resource, request); 515#else 516 UNUSED_PARAM(request); 517#endif 518 return newResource; 519} 520 521CachedResourceHandle<CachedResource> CachedResourceLoader::loadResource(CachedResource::Type type, CachedResourceRequest& request) 522{ 523 ASSERT(!memoryCache()->resourceForRequest(request.resourceRequest(), sessionID())); 524 525 LOG(ResourceLoading, "Loading CachedResource for '%s'.", request.resourceRequest().url().stringCenterEllipsizedToLength().latin1().data()); 526 527 CachedResourceHandle<CachedResource> resource = createResource(type, request.mutableResourceRequest(), request.charset(), sessionID()); 528 529 if (!memoryCache()->add(resource.get())) 530 resource->setOwningCachedResourceLoader(this); 531#if ENABLE(RESOURCE_TIMING) 532 storeResourceTimingInitiatorInformation(resource, request); 533#endif 534 return resource; 535} 536 537#if ENABLE(RESOURCE_TIMING) 538void CachedResourceLoader::storeResourceTimingInitiatorInformation(const CachedResourceHandle<CachedResource>& resource, const CachedResourceRequest& request) 539{ 540 if (resource->type() == CachedResource::MainResource) { 541 // <iframe>s should report the initial navigation requested by the parent document, but not subsequent navigations. 542 if (frame()->ownerElement() && m_documentLoader->frameLoader()->stateMachine().committingFirstRealLoad()) { 543 InitiatorInfo info = { frame()->ownerElement()->localName(), monotonicallyIncreasingTime() }; 544 m_initiatorMap.add(resource.get(), info); 545 } 546 } else { 547 InitiatorInfo info = { request.initiatorName(), monotonicallyIncreasingTime() }; 548 m_initiatorMap.add(resource.get(), info); 549 } 550} 551#endif // ENABLE(RESOURCE_TIMING) 552 553CachedResourceLoader::RevalidationPolicy CachedResourceLoader::determineRevalidationPolicy(CachedResource::Type type, ResourceRequest& request, bool forPreload, CachedResource* existingResource, CachedResourceRequest::DeferOption defer) const 554{ 555 if (!existingResource) 556 return Load; 557 558 // We already have a preload going for this URL. 559 if (forPreload && existingResource->isPreloaded()) 560 return Use; 561 562 // If the same URL has been loaded as a different type, we need to reload. 563 if (existingResource->type() != type) { 564 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to type mismatch."); 565 return Reload; 566 } 567 568 if (!existingResource->canReuse(request)) 569 return Reload; 570 571 // Conditional requests should have failed canReuse check. 572 ASSERT(!request.isConditional()); 573 574 // Do not load from cache if images are not enabled. The load for this image will be blocked 575 // in CachedImage::load. 576 if (CachedResourceRequest::DeferredByClient == defer) 577 return Reload; 578 579 // Don't reload resources while pasting. 580 if (m_allowStaleResources) 581 return Use; 582 583 // Alwaus use preloads. 584 if (existingResource->isPreloaded()) 585 return Use; 586 587 // CachePolicyHistoryBuffer uses the cache no matter what. 588 if (cachePolicy(type) == CachePolicyHistoryBuffer) 589 return Use; 590 591 // Don't reuse resources with Cache-control: no-store. 592 if (existingResource->response().cacheControlContainsNoStore()) { 593 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to Cache-control: no-store."); 594 return Reload; 595 } 596 597 // If credentials were sent with the previous request and won't be 598 // with this one, or vice versa, re-fetch the resource. 599 // 600 // This helps with the case where the server sends back 601 // "Access-Control-Allow-Origin: *" all the time, but some of the 602 // client's requests are made without CORS and some with. 603 if (existingResource->resourceRequest().allowCookies() != request.allowCookies()) { 604 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to difference in credentials settings."); 605 return Reload; 606 } 607 608 // During the initial load, avoid loading the same resource multiple times for a single document, even if the cache policies would tell us to. 609 if (document() && !document()->loadEventFinished() && m_validatedURLs.contains(existingResource->url())) 610 return Use; 611 612 // CachePolicyReload always reloads 613 if (cachePolicy(type) == CachePolicyReload) { 614 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to CachePolicyReload."); 615 return Reload; 616 } 617 618 // We'll try to reload the resource if it failed last time. 619 if (existingResource->errorOccurred()) { 620 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicye reloading due to resource being in the error state"); 621 return Reload; 622 } 623 624 // For resources that are not yet loaded we ignore the cache policy. 625 if (existingResource->isLoading()) 626 return Use; 627 628 // Check if the cache headers requires us to revalidate (cache expiration for example). 629 if (existingResource->mustRevalidateDueToCacheHeaders(cachePolicy(type))) { 630 // See if the resource has usable ETag or Last-modified headers. 631 if (existingResource->canUseCacheValidator()) 632 return Revalidate; 633 634 // No, must reload. 635 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to missing cache validators."); 636 return Reload; 637 } 638 639 return Use; 640} 641 642void CachedResourceLoader::printAccessDeniedMessage(const URL& url) const 643{ 644 if (url.isNull()) 645 return; 646 647 if (!frame()) 648 return; 649 650 String message; 651 if (!m_document || m_document->url().isNull()) 652 message = "Unsafe attempt to load URL " + url.stringCenterEllipsizedToLength() + '.'; 653 else 654 message = "Unsafe attempt to load URL " + url.stringCenterEllipsizedToLength() + " from frame with URL " + m_document->url().stringCenterEllipsizedToLength() + ". Domains, protocols and ports must match.\n"; 655 656 frame()->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message); 657} 658 659void CachedResourceLoader::setAutoLoadImages(bool enable) 660{ 661 if (enable == m_autoLoadImages) 662 return; 663 664 m_autoLoadImages = enable; 665 666 if (!m_autoLoadImages) 667 return; 668 669 reloadImagesIfNotDeferred(); 670} 671 672void CachedResourceLoader::setImagesEnabled(bool enable) 673{ 674 if (enable == m_imagesEnabled) 675 return; 676 677 m_imagesEnabled = enable; 678 679 if (!m_imagesEnabled) 680 return; 681 682 reloadImagesIfNotDeferred(); 683} 684 685bool CachedResourceLoader::clientDefersImage(const URL&) const 686{ 687 return !m_imagesEnabled; 688} 689 690bool CachedResourceLoader::shouldDeferImageLoad(const URL& url) const 691{ 692 return clientDefersImage(url) || !m_autoLoadImages; 693} 694 695void CachedResourceLoader::reloadImagesIfNotDeferred() 696{ 697 DocumentResourceMap::iterator end = m_documentResources.end(); 698 for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it) { 699 CachedResource* resource = it->value.get(); 700 if (resource->isImage() && resource->stillNeedsLoad() && !clientDefersImage(resource->url())) 701 const_cast<CachedResource*>(resource)->load(this, defaultCachedResourceOptions()); 702 } 703} 704 705CachePolicy CachedResourceLoader::cachePolicy(CachedResource::Type type) const 706{ 707 if (!frame()) 708 return CachePolicyVerify; 709 710 if (type != CachedResource::MainResource) 711 return frame()->loader().subresourceCachePolicy(); 712 713 if (frame()->loader().loadType() == FrameLoadType::ReloadFromOrigin || frame()->loader().loadType() == FrameLoadType::Reload) 714 return CachePolicyReload; 715 return CachePolicyVerify; 716} 717 718void CachedResourceLoader::removeCachedResource(CachedResource* resource) const 719{ 720#ifndef NDEBUG 721 DocumentResourceMap::iterator it = m_documentResources.find(resource->url()); 722 if (it != m_documentResources.end()) 723 ASSERT(it->value.get() == resource); 724#endif 725 m_documentResources.remove(resource->url()); 726} 727 728void CachedResourceLoader::loadDone(CachedResource* resource, bool shouldPerformPostLoadActions) 729{ 730 RefPtr<DocumentLoader> protectDocumentLoader(m_documentLoader); 731 RefPtr<Document> protectDocument(m_document); 732 733#if ENABLE(RESOURCE_TIMING) 734 if (resource && resource->response().isHTTP() && ((!resource->errorOccurred() && !resource->wasCanceled()) || resource->response().httpStatusCode() == 304)) { 735 HashMap<CachedResource*, InitiatorInfo>::iterator initiatorIt = m_initiatorMap.find(resource); 736 if (initiatorIt != m_initiatorMap.end()) { 737 ASSERT(document()); 738 Document* initiatorDocument = document(); 739 if (resource->type() == CachedResource::MainResource) 740 initiatorDocument = document()->parentDocument(); 741 ASSERT(initiatorDocument); 742 const InitiatorInfo& info = initiatorIt->value; 743 initiatorDocument->domWindow()->performance()->addResourceTiming(info.name, initiatorDocument, resource->resourceRequest(), resource->response(), info.startTime, resource->loadFinishTime()); 744 m_initiatorMap.remove(initiatorIt); 745 } 746 } 747#else 748 UNUSED_PARAM(resource); 749#endif // ENABLE(RESOURCE_TIMING) 750 751 if (frame()) 752 frame()->loader().loadDone(); 753 754 if (shouldPerformPostLoadActions) 755 performPostLoadActions(); 756 757 if (!m_garbageCollectDocumentResourcesTimer.isActive()) 758 m_garbageCollectDocumentResourcesTimer.startOneShot(0); 759} 760 761// Garbage collecting m_documentResources is a workaround for the 762// CachedResourceHandles on the RHS being strong references. Ideally this 763// would be a weak map, however CachedResourceHandles perform additional 764// bookkeeping on CachedResources, so instead pseudo-GC them -- when the 765// reference count reaches 1, m_documentResources is the only reference, so 766// remove it from the map. 767void CachedResourceLoader::garbageCollectDocumentResourcesTimerFired(Timer<CachedResourceLoader>& timer) 768{ 769 ASSERT_UNUSED(timer, &timer == &m_garbageCollectDocumentResourcesTimer); 770 garbageCollectDocumentResources(); 771} 772 773void CachedResourceLoader::garbageCollectDocumentResources() 774{ 775 typedef Vector<String, 10> StringVector; 776 StringVector resourcesToDelete; 777 778 for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != m_documentResources.end(); ++it) { 779 if (it->value->hasOneHandle()) { 780 resourcesToDelete.append(it->key); 781 it->value->setOwningCachedResourceLoader(0); 782 } 783 } 784 785 for (StringVector::const_iterator it = resourcesToDelete.begin(); it != resourcesToDelete.end(); ++it) 786 m_documentResources.remove(*it); 787} 788 789void CachedResourceLoader::performPostLoadActions() 790{ 791 checkForPendingPreloads(); 792 793 platformStrategies()->loaderStrategy()->resourceLoadScheduler()->servePendingRequests(); 794} 795 796void CachedResourceLoader::incrementRequestCount(const CachedResource* res) 797{ 798 if (res->ignoreForRequestCount()) 799 return; 800 801 ++m_requestCount; 802} 803 804void CachedResourceLoader::decrementRequestCount(const CachedResource* res) 805{ 806 if (res->ignoreForRequestCount()) 807 return; 808 809 --m_requestCount; 810 ASSERT(m_requestCount > -1); 811} 812 813void CachedResourceLoader::preload(CachedResource::Type type, CachedResourceRequest& request, const String& charset) 814{ 815 // We always preload resources on iOS. See <https://bugs.webkit.org/show_bug.cgi?id=91276>. 816 // FIXME: We should consider adding a setting to toggle aggressive preloading behavior as opposed 817 // to making this behavior specific to iOS. 818#if !PLATFORM(IOS) 819 bool hasRendering = m_document->body() && m_document->renderView(); 820 bool canBlockParser = type == CachedResource::Script || type == CachedResource::CSSStyleSheet; 821 if (!hasRendering && !canBlockParser) { 822 // Don't preload subresources that can't block the parser before we have something to draw. 823 // This helps prevent preloads from delaying first display when bandwidth is limited. 824 PendingPreload pendingPreload = { type, request, charset }; 825 m_pendingPreloads.append(pendingPreload); 826 return; 827 } 828#endif 829 requestPreload(type, request, charset); 830} 831 832void CachedResourceLoader::checkForPendingPreloads() 833{ 834 if (m_pendingPreloads.isEmpty() || !m_document->body() || !m_document->body()->renderer()) 835 return; 836#if PLATFORM(IOS) 837 // We always preload resources on iOS. See <https://bugs.webkit.org/show_bug.cgi?id=91276>. 838 // So, we should never have any pending preloads. 839 // FIXME: We should look to avoid compiling this code entirely when building for iOS. 840 ASSERT_NOT_REACHED(); 841#endif 842 while (!m_pendingPreloads.isEmpty()) { 843 PendingPreload preload = m_pendingPreloads.takeFirst(); 844 // 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). 845 if (!cachedResource(preload.m_request.resourceRequest().url())) 846 requestPreload(preload.m_type, preload.m_request, preload.m_charset); 847 } 848 m_pendingPreloads.clear(); 849} 850 851void CachedResourceLoader::requestPreload(CachedResource::Type type, CachedResourceRequest& request, const String& charset) 852{ 853 String encoding; 854 if (type == CachedResource::Script || type == CachedResource::CSSStyleSheet) 855 encoding = charset.isEmpty() ? m_document->charset() : charset; 856 857 request.setCharset(encoding); 858 request.setForPreload(true); 859 860 CachedResourceHandle<CachedResource> resource = requestResource(type, request); 861 if (!resource || (m_preloads && m_preloads->contains(resource.get()))) 862 return; 863 resource->increasePreloadCount(); 864 865 if (!m_preloads) 866 m_preloads = adoptPtr(new ListHashSet<CachedResource*>); 867 m_preloads->add(resource.get()); 868 869#if PRELOAD_DEBUG 870 printf("PRELOADING %s\n", resource->url().latin1().data()); 871#endif 872} 873 874bool CachedResourceLoader::isPreloaded(const String& urlString) const 875{ 876 const URL& url = m_document->completeURL(urlString); 877 878 if (m_preloads) { 879 ListHashSet<CachedResource*>::iterator end = m_preloads->end(); 880 for (ListHashSet<CachedResource*>::iterator it = m_preloads->begin(); it != end; ++it) { 881 CachedResource* resource = *it; 882 if (resource->url() == url) 883 return true; 884 } 885 } 886 887 Deque<PendingPreload>::const_iterator dequeEnd = m_pendingPreloads.end(); 888 for (Deque<PendingPreload>::const_iterator it = m_pendingPreloads.begin(); it != dequeEnd; ++it) { 889 PendingPreload pendingPreload = *it; 890 if (pendingPreload.m_request.resourceRequest().url() == url) 891 return true; 892 } 893 return false; 894} 895 896void CachedResourceLoader::clearPreloads() 897{ 898#if PRELOAD_DEBUG 899 printPreloadStats(); 900#endif 901 if (!m_preloads) 902 return; 903 904 ListHashSet<CachedResource*>::iterator end = m_preloads->end(); 905 for (ListHashSet<CachedResource*>::iterator it = m_preloads->begin(); it != end; ++it) { 906 CachedResource* res = *it; 907 res->decreasePreloadCount(); 908 bool deleted = res->deleteIfPossible(); 909 if (!deleted && res->preloadResult() == CachedResource::PreloadNotReferenced) 910 memoryCache()->remove(res); 911 } 912 m_preloads.clear(); 913} 914 915void CachedResourceLoader::clearPendingPreloads() 916{ 917 m_pendingPreloads.clear(); 918} 919 920#if PRELOAD_DEBUG 921void CachedResourceLoader::printPreloadStats() 922{ 923 unsigned scripts = 0; 924 unsigned scriptMisses = 0; 925 unsigned stylesheets = 0; 926 unsigned stylesheetMisses = 0; 927 unsigned images = 0; 928 unsigned imageMisses = 0; 929 ListHashSet<CachedResource*>::iterator end = m_preloads.end(); 930 for (ListHashSet<CachedResource*>::iterator it = m_preloads.begin(); it != end; ++it) { 931 CachedResource* res = *it; 932 if (res->preloadResult() == CachedResource::PreloadNotReferenced) 933 printf("!! UNREFERENCED PRELOAD %s\n", res->url().latin1().data()); 934 else if (res->preloadResult() == CachedResource::PreloadReferencedWhileComplete) 935 printf("HIT COMPLETE PRELOAD %s\n", res->url().latin1().data()); 936 else if (res->preloadResult() == CachedResource::PreloadReferencedWhileLoading) 937 printf("HIT LOADING PRELOAD %s\n", res->url().latin1().data()); 938 939 if (res->type() == CachedResource::Script) { 940 scripts++; 941 if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading) 942 scriptMisses++; 943 } else if (res->type() == CachedResource::CSSStyleSheet) { 944 stylesheets++; 945 if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading) 946 stylesheetMisses++; 947 } else { 948 images++; 949 if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading) 950 imageMisses++; 951 } 952 953 if (res->errorOccurred()) 954 memoryCache()->remove(res); 955 956 res->decreasePreloadCount(); 957 } 958 m_preloads.clear(); 959 960 if (scripts) 961 printf("SCRIPTS: %d (%d hits, hit rate %d%%)\n", scripts, scripts - scriptMisses, (scripts - scriptMisses) * 100 / scripts); 962 if (stylesheets) 963 printf("STYLESHEETS: %d (%d hits, hit rate %d%%)\n", stylesheets, stylesheets - stylesheetMisses, (stylesheets - stylesheetMisses) * 100 / stylesheets); 964 if (images) 965 printf("IMAGES: %d (%d hits, hit rate %d%%)\n", images, images - imageMisses, (images - imageMisses) * 100 / images); 966} 967#endif 968 969const ResourceLoaderOptions& CachedResourceLoader::defaultCachedResourceOptions() 970{ 971 static ResourceLoaderOptions options(SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, AskClientForAllCredentials, DoSecurityCheck, UseDefaultOriginRestrictionsForType); 972 return options; 973} 974 975} 976