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/
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.
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.
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.
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.
28 #include "CachedResourceLoader.h"
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"
39 #include "ContentSecurityPolicy.h"
40 #include "DOMWindow.h"
42 #include "DocumentLoader.h"
44 #include "FrameLoader.h"
45 #include "FrameLoaderClient.h"
46 #include "HTMLElement.h"
47 #include "HTMLFrameOwnerElement.h"
48 #include "LoaderStrategy.h"
50 #include "MemoryCache.h"
51 #include "PingLoader.h"
52 #include "PlatformStrategies.h"
53 #include "ResourceLoadScheduler.h"
54 #include "ScriptController.h"
55 #include "SecurityOrigin.h"
57 #include <wtf/UnusedParam.h>
58 #include <wtf/text/CString.h>
59 #include <wtf/text/WTFString.h>
61 #if ENABLE(VIDEO_TRACK)
62 #include "CachedTextTrack.h"
65 #if ENABLE(CSS_SHADERS)
66 #include "CachedShader.h"
69 #if ENABLE(RESOURCE_TIMING)
70 #include "Performance.h"
73 #define PRELOAD_DEBUG 0
77 static CachedResource* createResource(CachedResource::Type type, ResourceRequest& request, const String& charset)
80 case CachedResource::ImageResource:
81 return new CachedImage(request);
82 case CachedResource::CSSStyleSheet:
83 return new CachedCSSStyleSheet(request, charset);
84 case CachedResource::Script:
85 return new CachedScript(request, charset);
87 case CachedResource::SVGDocumentResource:
88 return new CachedSVGDocument(request);
90 case CachedResource::FontResource:
91 return new CachedFont(request);
92 case CachedResource::RawResource:
93 case CachedResource::MainResource:
94 return new CachedRawResource(request, type);
96 case CachedResource::XSLStyleSheet:
97 return new CachedXSLStyleSheet(request);
99 #if ENABLE(LINK_PREFETCH)
100 case CachedResource::LinkPrefetch:
101 return new CachedResource(request, CachedResource::LinkPrefetch);
102 case CachedResource::LinkSubresource:
103 return new CachedResource(request, CachedResource::LinkSubresource);
105 #if ENABLE(VIDEO_TRACK)
106 case CachedResource::TextTrackResource:
107 return new CachedTextTrack(request);
109 #if ENABLE(CSS_SHADERS)
110 case CachedResource::ShaderResource:
111 return new CachedShader(request);
114 ASSERT_NOT_REACHED();
118 CachedResourceLoader::CachedResourceLoader(DocumentLoader* documentLoader)
120 , m_documentLoader(documentLoader)
122 , m_garbageCollectDocumentResourcesTimer(this, &CachedResourceLoader::garbageCollectDocumentResourcesTimerFired)
123 , m_autoLoadImages(true)
124 , m_imagesEnabled(true)
125 , m_allowStaleResources(false)
129 CachedResourceLoader::~CachedResourceLoader()
131 m_documentLoader = 0;
135 DocumentResourceMap::iterator end = m_documentResources.end();
136 for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it)
137 it->value->setOwningCachedResourceLoader(0);
139 // Make sure no requests still point to this CachedResourceLoader
140 ASSERT(m_requestCount == 0);
143 CachedResource* CachedResourceLoader::cachedResource(const String& resourceURL) const
145 KURL url = m_document->completeURL(resourceURL);
146 return cachedResource(url);
149 CachedResource* CachedResourceLoader::cachedResource(const KURL& resourceURL) const
151 KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(resourceURL);
152 return m_documentResources.get(url).get();
155 Frame* CachedResourceLoader::frame() const
157 return m_documentLoader ? m_documentLoader->frame() : 0;
160 CachedResourceHandle<CachedImage> CachedResourceLoader::requestImage(CachedResourceRequest& request)
162 if (Frame* f = frame()) {
163 if (f->loader()->pageDismissalEventBeingDispatched() != FrameLoader::NoDismissal) {
164 KURL requestURL = request.resourceRequest().url();
165 if (requestURL.isValid() && canRequest(CachedResource::ImageResource, requestURL))
166 PingLoader::loadImage(f, requestURL);
170 request.setDefer(clientDefersImage(request.resourceRequest().url()) ? CachedResourceRequest::DeferredByClient : CachedResourceRequest::NoDefer);
171 return static_cast<CachedImage*>(requestResource(CachedResource::ImageResource, request).get());
174 CachedResourceHandle<CachedFont> CachedResourceLoader::requestFont(CachedResourceRequest& request)
176 return static_cast<CachedFont*>(requestResource(CachedResource::FontResource, request).get());
179 #if ENABLE(VIDEO_TRACK)
180 CachedResourceHandle<CachedTextTrack> CachedResourceLoader::requestTextTrack(CachedResourceRequest& request)
182 return static_cast<CachedTextTrack*>(requestResource(CachedResource::TextTrackResource, request).get());
186 #if ENABLE(CSS_SHADERS)
187 CachedResourceHandle<CachedShader> CachedResourceLoader::requestShader(CachedResourceRequest& request)
189 return static_cast<CachedShader*>(requestResource(CachedResource::ShaderResource, request).get());
193 CachedResourceHandle<CachedCSSStyleSheet> CachedResourceLoader::requestCSSStyleSheet(CachedResourceRequest& request)
195 return static_cast<CachedCSSStyleSheet*>(requestResource(CachedResource::CSSStyleSheet, request).get());
198 CachedResourceHandle<CachedCSSStyleSheet> CachedResourceLoader::requestUserCSSStyleSheet(CachedResourceRequest& request)
200 KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(request.resourceRequest().url());
202 #if ENABLE(CACHE_PARTITIONING)
203 request.mutableResourceRequest().setCachePartition(document()->topOrigin()->cachePartition());
206 if (CachedResource* existing = memoryCache()->resourceForRequest(request.resourceRequest())) {
207 if (existing->type() == CachedResource::CSSStyleSheet)
208 return static_cast<CachedCSSStyleSheet*>(existing);
209 memoryCache()->remove(existing);
211 if (url.string() != request.resourceRequest().url())
212 request.mutableResourceRequest().setURL(url);
214 CachedResourceHandle<CachedCSSStyleSheet> userSheet = new CachedCSSStyleSheet(request.resourceRequest(), request.charset());
216 memoryCache()->add(userSheet.get());
217 // FIXME: loadResource calls setOwningCachedResourceLoader() if the resource couldn't be added to cache. Does this function need to call it, too?
219 userSheet->load(this, ResourceLoaderOptions(DoNotSendCallbacks, SniffContent, BufferData, AllowStoredCredentials, AskClientForAllCredentials, SkipSecurityCheck));
224 CachedResourceHandle<CachedScript> CachedResourceLoader::requestScript(CachedResourceRequest& request)
226 return static_cast<CachedScript*>(requestResource(CachedResource::Script, request).get());
230 CachedResourceHandle<CachedXSLStyleSheet> CachedResourceLoader::requestXSLStyleSheet(CachedResourceRequest& request)
232 return static_cast<CachedXSLStyleSheet*>(requestResource(CachedResource::XSLStyleSheet, request).get());
237 CachedResourceHandle<CachedSVGDocument> CachedResourceLoader::requestSVGDocument(CachedResourceRequest& request)
239 return static_cast<CachedSVGDocument*>(requestResource(CachedResource::SVGDocumentResource, request).get());
243 #if ENABLE(LINK_PREFETCH)
244 CachedResourceHandle<CachedResource> CachedResourceLoader::requestLinkResource(CachedResource::Type type, CachedResourceRequest& request)
247 ASSERT(type == CachedResource::LinkPrefetch || type == CachedResource::LinkSubresource);
248 return requestResource(type, request);
252 CachedResourceHandle<CachedRawResource> CachedResourceLoader::requestRawResource(CachedResourceRequest& request)
254 return static_cast<CachedRawResource*>(requestResource(CachedResource::RawResource, request).get());
257 CachedResourceHandle<CachedRawResource> CachedResourceLoader::requestMainResource(CachedResourceRequest& request)
259 return static_cast<CachedRawResource*>(requestResource(CachedResource::MainResource, request).get());
262 bool CachedResourceLoader::checkInsecureContent(CachedResource::Type type, const KURL& url) const
265 case CachedResource::Script:
267 case CachedResource::XSLStyleSheet:
270 case CachedResource::SVGDocumentResource:
272 case CachedResource::CSSStyleSheet:
273 // These resource can inject script into the current document (Script,
274 // XSL) or exfiltrate the content of the current document (CSS).
275 if (Frame* f = frame())
276 if (!f->loader()->mixedContentChecker()->canRunInsecureContent(m_document->securityOrigin(), url))
279 #if ENABLE(VIDEO_TRACK)
280 case CachedResource::TextTrackResource:
282 #if ENABLE(CSS_SHADERS)
283 case CachedResource::ShaderResource:
285 case CachedResource::RawResource:
286 case CachedResource::ImageResource:
287 case CachedResource::FontResource: {
288 // These resources can corrupt only the frame's pixels.
289 if (Frame* f = frame()) {
290 Frame* top = f->tree()->top();
291 if (!top->loader()->mixedContentChecker()->canDisplayInsecureContent(top->document()->securityOrigin(), url))
296 case CachedResource::MainResource:
297 #if ENABLE(LINK_PREFETCH)
298 case CachedResource::LinkPrefetch:
299 case CachedResource::LinkSubresource:
300 // Prefetch cannot affect the current document.
307 bool CachedResourceLoader::canRequest(CachedResource::Type type, const KURL& url, bool forPreload)
309 if (document() && !document()->securityOrigin()->canDisplay(url)) {
311 FrameLoader::reportLocalLoadFailed(frame(), url.elidedString());
312 LOG(ResourceLoading, "CachedResourceLoader::requestResource URL was not allowed by SecurityOrigin::canDisplay");
316 // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved.
317 bool shouldBypassMainWorldContentSecurityPolicy = (frame() && frame()->script()->shouldBypassMainWorldContentSecurityPolicy());
319 // Some types of resources can be loaded only from the same origin. Other
320 // types of resources, like Images, Scripts, and CSS, can be loaded from
323 case CachedResource::MainResource:
324 case CachedResource::ImageResource:
325 case CachedResource::CSSStyleSheet:
326 case CachedResource::Script:
327 case CachedResource::FontResource:
328 case CachedResource::RawResource:
329 #if ENABLE(LINK_PREFETCH)
330 case CachedResource::LinkPrefetch:
331 case CachedResource::LinkSubresource:
333 #if ENABLE(VIDEO_TRACK)
334 case CachedResource::TextTrackResource:
336 #if ENABLE(CSS_SHADERS)
337 case CachedResource::ShaderResource:
339 // These types of resources can be loaded from any origin.
340 // FIXME: Are we sure about CachedResource::FontResource?
343 case CachedResource::SVGDocumentResource:
346 case CachedResource::XSLStyleSheet:
347 if (!m_document->securityOrigin()->canRequest(url)) {
348 printAccessDeniedMessage(url);
357 case CachedResource::XSLStyleSheet:
358 if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowScriptFromSource(url))
362 case CachedResource::Script:
363 if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowScriptFromSource(url))
367 Settings* settings = frame()->settings();
368 if (!frame()->loader()->client()->allowScriptFromSource(!settings || settings->isScriptEnabled(), url)) {
369 frame()->loader()->client()->didNotAllowScript();
374 #if ENABLE(CSS_SHADERS)
375 case CachedResource::ShaderResource:
376 // Since shaders are referenced from CSS Styles use the same rules here.
378 case CachedResource::CSSStyleSheet:
379 if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowStyleFromSource(url))
383 case CachedResource::SVGDocumentResource:
385 case CachedResource::ImageResource:
386 if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowImageFromSource(url))
389 case CachedResource::FontResource: {
390 if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowFontFromSource(url))
394 case CachedResource::MainResource:
395 case CachedResource::RawResource:
396 #if ENABLE(LINK_PREFETCH)
397 case CachedResource::LinkPrefetch:
398 case CachedResource::LinkSubresource:
401 #if ENABLE(VIDEO_TRACK)
402 case CachedResource::TextTrackResource:
403 // Cues aren't called out in the CPS spec yet, but they only work with a media element
404 // so use the media policy.
405 if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowMediaFromSource(url))
411 // Last of all, check for insecure content. We do this last so that when
412 // folks block insecure content with a CSP policy, they don't get a warning.
413 // They'll still get a warning in the console about CSP blocking the load.
415 // FIXME: Should we consider forPreload here?
416 if (!checkInsecureContent(type, url))
422 bool CachedResourceLoader::shouldContinueAfterNotifyingLoadedFromMemoryCache(CachedResource* resource)
424 if (!resource || !frame() || resource->status() != CachedResource::Cached)
427 ResourceRequest newRequest;
428 frame()->loader()->loadedResourceFromMemoryCache(resource, newRequest);
430 // FIXME <http://webkit.org/b/113251>: If the delegate modifies the request's
431 // URL, it is no longer appropriate to use this CachedResource.
432 return !newRequest.isNull();
435 CachedResourceHandle<CachedResource> CachedResourceLoader::requestResource(CachedResource::Type type, CachedResourceRequest& request)
437 KURL url = request.resourceRequest().url();
439 LOG(ResourceLoading, "CachedResourceLoader::requestResource '%s', charset '%s', priority=%d, forPreload=%u", url.elidedString().latin1().data(), request.charset().latin1().data(), request.priority(), request.forPreload());
441 // If only the fragment identifiers differ, it is the same resource.
442 url = MemoryCache::removeFragmentIdentifierIfNeeded(url);
447 if (!canRequest(type, url, request.forPreload()))
450 if (Frame* f = frame())
451 f->loader()->client()->dispatchWillRequestResource(&request);
453 if (memoryCache()->disabled()) {
454 DocumentResourceMap::iterator it = m_documentResources.find(url.string());
455 if (it != m_documentResources.end()) {
456 it->value->setOwningCachedResourceLoader(0);
457 m_documentResources.remove(it);
461 // See if we can use an existing resource from the cache.
462 CachedResourceHandle<CachedResource> resource;
463 #if ENABLE(CACHE_PARTITIONING)
465 request.mutableResourceRequest().setCachePartition(document()->topOrigin()->cachePartition());
468 resource = memoryCache()->resourceForRequest(request.resourceRequest());
470 const RevalidationPolicy policy = determineRevalidationPolicy(type, request.mutableResourceRequest(), request.forPreload(), resource.get(), request.defer());
473 memoryCache()->remove(resource.get());
476 resource = loadResource(type, request, request.charset());
479 resource = revalidateResource(request, resource.get());
482 if (!shouldContinueAfterNotifyingLoadedFromMemoryCache(resource.get()))
484 memoryCache()->resourceAccessed(resource.get());
491 if (!request.forPreload() || policy != Use)
492 resource->setLoadPriority(request.priority());
494 if ((policy != Use || resource->stillNeedsLoad()) && CachedResourceRequest::NoDefer == request.defer()) {
495 resource->load(this, request.options());
497 // We don't support immediate loads, but we do support immediate failure.
498 if (resource->errorOccurred()) {
499 if (resource->inCache())
500 memoryCache()->remove(resource.get());
505 if (!request.resourceRequest().url().protocolIsData())
506 m_validatedURLs.add(request.resourceRequest().url());
508 ASSERT(resource->url() == url.string());
509 m_documentResources.set(resource->url(), resource);
513 CachedResourceHandle<CachedResource> CachedResourceLoader::revalidateResource(const CachedResourceRequest& request, CachedResource* resource)
516 ASSERT(resource->inCache());
517 ASSERT(!memoryCache()->disabled());
518 ASSERT(resource->canUseCacheValidator());
519 ASSERT(!resource->resourceToRevalidate());
521 // Copy the URL out of the resource to be revalidated in case it gets deleted by the remove() call below.
522 String url = resource->url();
523 CachedResourceHandle<CachedResource> newResource = createResource(resource->type(), resource->resourceRequest(), resource->encoding());
525 LOG(ResourceLoading, "Resource %p created to revalidate %p", newResource.get(), resource);
526 newResource->setResourceToRevalidate(resource);
528 memoryCache()->remove(resource);
529 memoryCache()->add(newResource.get());
530 #if ENABLE(RESOURCE_TIMING)
531 storeResourceTimingInitiatorInformation(resource, request);
533 UNUSED_PARAM(request);
538 CachedResourceHandle<CachedResource> CachedResourceLoader::loadResource(CachedResource::Type type, CachedResourceRequest& request, const String& charset)
540 ASSERT(!memoryCache()->resourceForRequest(request.resourceRequest()));
542 LOG(ResourceLoading, "Loading CachedResource for '%s'.", request.resourceRequest().url().elidedString().latin1().data());
544 CachedResourceHandle<CachedResource> resource = createResource(type, request.mutableResourceRequest(), charset);
546 if (!memoryCache()->add(resource.get()))
547 resource->setOwningCachedResourceLoader(this);
548 #if ENABLE(RESOURCE_TIMING)
549 storeResourceTimingInitiatorInformation(resource, request);
554 #if ENABLE(RESOURCE_TIMING)
555 void CachedResourceLoader::storeResourceTimingInitiatorInformation(const CachedResourceHandle<CachedResource>& resource, const CachedResourceRequest& request)
557 if (resource->type() == CachedResource::MainResource) {
558 // <iframe>s should report the initial navigation requested by the parent document, but not subsequent navigations.
559 if (frame()->ownerElement() && m_documentLoader->frameLoader()->stateMachine()->committingFirstRealLoad()) {
560 InitiatorInfo info = { frame()->ownerElement()->localName(), monotonicallyIncreasingTime() };
561 m_initiatorMap.add(resource.get(), info);
564 InitiatorInfo info = { request.initiatorName(), monotonicallyIncreasingTime() };
565 m_initiatorMap.add(resource.get(), info);
568 #endif // ENABLE(RESOURCE_TIMING)
570 CachedResourceLoader::RevalidationPolicy CachedResourceLoader::determineRevalidationPolicy(CachedResource::Type type, ResourceRequest& request, bool forPreload, CachedResource* existingResource, CachedResourceRequest::DeferOption defer) const
572 if (!existingResource)
575 // We already have a preload going for this URL.
576 if (forPreload && existingResource->isPreloaded())
579 // If the same URL has been loaded as a different type, we need to reload.
580 if (existingResource->type() != type) {
581 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to type mismatch.");
585 if (!existingResource->canReuse(request))
588 // Certain requests (e.g., XHRs) might have manually set headers that require revalidation.
589 // FIXME: In theory, this should be a Revalidate case. In practice, the MemoryCache revalidation path assumes a whole bunch
590 // of things about how revalidation works that manual headers violate, so punt to Reload instead.
591 if (request.isConditional())
594 // Do not load from cache if images are not enabled. The load for this image will be blocked
595 // in CachedImage::load.
596 if (CachedResourceRequest::DeferredByClient == defer)
599 // Don't reload resources while pasting.
600 if (m_allowStaleResources)
603 // Alwaus use preloads.
604 if (existingResource->isPreloaded())
607 // CachePolicyHistoryBuffer uses the cache no matter what.
608 if (cachePolicy(type) == CachePolicyHistoryBuffer)
611 // Don't reuse resources with Cache-control: no-store.
612 if (existingResource->response().cacheControlContainsNoStore()) {
613 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to Cache-control: no-store.");
617 // If credentials were sent with the previous request and won't be
618 // with this one, or vice versa, re-fetch the resource.
620 // This helps with the case where the server sends back
621 // "Access-Control-Allow-Origin: *" all the time, but some of the
622 // client's requests are made without CORS and some with.
623 if (existingResource->resourceRequest().allowCookies() != request.allowCookies()) {
624 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to difference in credentials settings.");
628 // During the initial load, avoid loading the same resource multiple times for a single document, even if the cache policies would tell us to.
629 if (document() && !document()->loadEventFinished() && m_validatedURLs.contains(existingResource->url()))
632 // CachePolicyReload always reloads
633 if (cachePolicy(type) == CachePolicyReload) {
634 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to CachePolicyReload.");
638 // We'll try to reload the resource if it failed last time.
639 if (existingResource->errorOccurred()) {
640 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicye reloading due to resource being in the error state");
644 // For resources that are not yet loaded we ignore the cache policy.
645 if (existingResource->isLoading())
648 // Check if the cache headers requires us to revalidate (cache expiration for example).
649 if (existingResource->mustRevalidateDueToCacheHeaders(cachePolicy(type))) {
650 // See if the resource has usable ETag or Last-modified headers.
651 if (existingResource->canUseCacheValidator())
655 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to missing cache validators.");
662 void CachedResourceLoader::printAccessDeniedMessage(const KURL& url) const
671 if (!m_document || m_document->url().isNull())
672 message = "Unsafe attempt to load URL " + url.elidedString() + '.';
674 message = "Unsafe attempt to load URL " + url.elidedString() + " from frame with URL " + m_document->url().elidedString() + ". Domains, protocols and ports must match.\n";
676 frame()->document()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, message);
679 void CachedResourceLoader::setAutoLoadImages(bool enable)
681 if (enable == m_autoLoadImages)
684 m_autoLoadImages = enable;
686 if (!m_autoLoadImages)
689 reloadImagesIfNotDeferred();
692 void CachedResourceLoader::setImagesEnabled(bool enable)
694 if (enable == m_imagesEnabled)
697 m_imagesEnabled = enable;
699 if (!m_imagesEnabled)
702 reloadImagesIfNotDeferred();
705 bool CachedResourceLoader::clientDefersImage(const KURL& url) const
707 return frame() && !frame()->loader()->client()->allowImage(m_imagesEnabled, url);
710 bool CachedResourceLoader::shouldDeferImageLoad(const KURL& url) const
712 return clientDefersImage(url) || !m_autoLoadImages;
715 void CachedResourceLoader::reloadImagesIfNotDeferred()
717 DocumentResourceMap::iterator end = m_documentResources.end();
718 for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it) {
719 CachedResource* resource = it->value.get();
720 if (resource->type() == CachedResource::ImageResource && resource->stillNeedsLoad() && !clientDefersImage(resource->url()))
721 const_cast<CachedResource*>(resource)->load(this, defaultCachedResourceOptions());
725 CachePolicy CachedResourceLoader::cachePolicy(CachedResource::Type type) const
728 return CachePolicyVerify;
730 if (type != CachedResource::MainResource)
731 return frame()->loader()->subresourceCachePolicy();
733 if (frame()->loader()->loadType() == FrameLoadTypeReloadFromOrigin || frame()->loader()->loadType() == FrameLoadTypeReload)
734 return CachePolicyReload;
735 return CachePolicyVerify;
738 void CachedResourceLoader::removeCachedResource(CachedResource* resource) const
741 DocumentResourceMap::iterator it = m_documentResources.find(resource->url());
742 if (it != m_documentResources.end())
743 ASSERT(it->value.get() == resource);
745 m_documentResources.remove(resource->url());
748 void CachedResourceLoader::loadDone(CachedResource* resource)
750 RefPtr<DocumentLoader> protectDocumentLoader(m_documentLoader);
751 RefPtr<Document> protectDocument(m_document);
753 #if ENABLE(RESOURCE_TIMING)
754 if (resource && resource->response().isHTTP() && ((!resource->errorOccurred() && !resource->wasCanceled()) || resource->response().httpStatusCode() == 304)) {
755 HashMap<CachedResource*, InitiatorInfo>::iterator initiatorIt = m_initiatorMap.find(resource);
756 if (initiatorIt != m_initiatorMap.end()) {
758 Document* initiatorDocument = document();
759 if (resource->type() == CachedResource::MainResource)
760 initiatorDocument = document()->parentDocument();
761 ASSERT(initiatorDocument);
762 const InitiatorInfo& info = initiatorIt->value;
763 initiatorDocument->domWindow()->performance()->addResourceTiming(info.name, initiatorDocument, resource->resourceRequest(), resource->response(), info.startTime, resource->loadFinishTime());
764 m_initiatorMap.remove(initiatorIt);
768 UNUSED_PARAM(resource);
769 #endif // ENABLE(RESOURCE_TIMING)
772 frame()->loader()->loadDone();
773 performPostLoadActions();
775 if (!m_garbageCollectDocumentResourcesTimer.isActive())
776 m_garbageCollectDocumentResourcesTimer.startOneShot(0);
779 // Garbage collecting m_documentResources is a workaround for the
780 // CachedResourceHandles on the RHS being strong references. Ideally this
781 // would be a weak map, however CachedResourceHandles perform additional
782 // bookkeeping on CachedResources, so instead pseudo-GC them -- when the
783 // reference count reaches 1, m_documentResources is the only reference, so
784 // remove it from the map.
785 void CachedResourceLoader::garbageCollectDocumentResourcesTimerFired(Timer<CachedResourceLoader>* timer)
787 ASSERT_UNUSED(timer, timer == &m_garbageCollectDocumentResourcesTimer);
788 garbageCollectDocumentResources();
791 void CachedResourceLoader::garbageCollectDocumentResources()
793 typedef Vector<String, 10> StringVector;
794 StringVector resourcesToDelete;
796 for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != m_documentResources.end(); ++it) {
797 if (it->value->hasOneHandle()) {
798 resourcesToDelete.append(it->key);
799 it->value->setOwningCachedResourceLoader(0);
803 for (StringVector::const_iterator it = resourcesToDelete.begin(); it != resourcesToDelete.end(); ++it)
804 m_documentResources.remove(*it);
807 void CachedResourceLoader::performPostLoadActions()
809 checkForPendingPreloads();
811 #if USE(PLATFORM_STRATEGIES)
812 platformStrategies()->loaderStrategy()->resourceLoadScheduler()->servePendingRequests();
814 resourceLoadScheduler()->servePendingRequests();
818 void CachedResourceLoader::incrementRequestCount(const CachedResource* res)
820 if (res->ignoreForRequestCount())
826 void CachedResourceLoader::decrementRequestCount(const CachedResource* res)
828 if (res->ignoreForRequestCount())
832 ASSERT(m_requestCount > -1);
835 void CachedResourceLoader::preload(CachedResource::Type type, CachedResourceRequest& request, const String& charset)
837 bool delaySubresourceLoad = true;
839 delaySubresourceLoad = false;
841 if (delaySubresourceLoad) {
842 bool hasRendering = m_document->body() && m_document->body()->renderer();
843 bool canBlockParser = type == CachedResource::Script || type == CachedResource::CSSStyleSheet;
844 if (!hasRendering && !canBlockParser) {
845 // Don't preload subresources that can't block the parser before we have something to draw.
846 // This helps prevent preloads from delaying first display when bandwidth is limited.
847 PendingPreload pendingPreload = { type, request, charset };
848 m_pendingPreloads.append(pendingPreload);
852 requestPreload(type, request, charset);
855 void CachedResourceLoader::checkForPendingPreloads()
857 if (m_pendingPreloads.isEmpty() || !m_document->body() || !m_document->body()->renderer())
859 while (!m_pendingPreloads.isEmpty()) {
860 PendingPreload preload = m_pendingPreloads.takeFirst();
861 // 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).
862 if (!cachedResource(preload.m_request.resourceRequest().url()))
863 requestPreload(preload.m_type, preload.m_request, preload.m_charset);
865 m_pendingPreloads.clear();
868 void CachedResourceLoader::requestPreload(CachedResource::Type type, CachedResourceRequest& request, const String& charset)
871 if (type == CachedResource::Script || type == CachedResource::CSSStyleSheet)
872 encoding = charset.isEmpty() ? m_document->charset() : charset;
874 request.setCharset(encoding);
875 request.setForPreload(true);
877 CachedResourceHandle<CachedResource> resource = requestResource(type, request);
878 if (!resource || (m_preloads && m_preloads->contains(resource.get())))
880 resource->increasePreloadCount();
883 m_preloads = adoptPtr(new ListHashSet<CachedResource*>);
884 m_preloads->add(resource.get());
887 printf("PRELOADING %s\n", resource->url().latin1().data());
891 bool CachedResourceLoader::isPreloaded(const String& urlString) const
893 const KURL& url = m_document->completeURL(urlString);
896 ListHashSet<CachedResource*>::iterator end = m_preloads->end();
897 for (ListHashSet<CachedResource*>::iterator it = m_preloads->begin(); it != end; ++it) {
898 CachedResource* resource = *it;
899 if (resource->url() == url)
904 Deque<PendingPreload>::const_iterator dequeEnd = m_pendingPreloads.end();
905 for (Deque<PendingPreload>::const_iterator it = m_pendingPreloads.begin(); it != dequeEnd; ++it) {
906 PendingPreload pendingPreload = *it;
907 if (pendingPreload.m_request.resourceRequest().url() == url)
913 void CachedResourceLoader::clearPreloads()
921 ListHashSet<CachedResource*>::iterator end = m_preloads->end();
922 for (ListHashSet<CachedResource*>::iterator it = m_preloads->begin(); it != end; ++it) {
923 CachedResource* res = *it;
924 res->decreasePreloadCount();
925 bool deleted = res->deleteIfPossible();
926 if (!deleted && res->preloadResult() == CachedResource::PreloadNotReferenced)
927 memoryCache()->remove(res);
932 void CachedResourceLoader::clearPendingPreloads()
934 m_pendingPreloads.clear();
938 void CachedResourceLoader::printPreloadStats()
940 unsigned scripts = 0;
941 unsigned scriptMisses = 0;
942 unsigned stylesheets = 0;
943 unsigned stylesheetMisses = 0;
945 unsigned imageMisses = 0;
946 ListHashSet<CachedResource*>::iterator end = m_preloads.end();
947 for (ListHashSet<CachedResource*>::iterator it = m_preloads.begin(); it != end; ++it) {
948 CachedResource* res = *it;
949 if (res->preloadResult() == CachedResource::PreloadNotReferenced)
950 printf("!! UNREFERENCED PRELOAD %s\n", res->url().latin1().data());
951 else if (res->preloadResult() == CachedResource::PreloadReferencedWhileComplete)
952 printf("HIT COMPLETE PRELOAD %s\n", res->url().latin1().data());
953 else if (res->preloadResult() == CachedResource::PreloadReferencedWhileLoading)
954 printf("HIT LOADING PRELOAD %s\n", res->url().latin1().data());
956 if (res->type() == CachedResource::Script) {
958 if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
960 } else if (res->type() == CachedResource::CSSStyleSheet) {
962 if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
966 if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
970 if (res->errorOccurred())
971 memoryCache()->remove(res);
973 res->decreasePreloadCount();
978 printf("SCRIPTS: %d (%d hits, hit rate %d%%)\n", scripts, scripts - scriptMisses, (scripts - scriptMisses) * 100 / scripts);
980 printf("STYLESHEETS: %d (%d hits, hit rate %d%%)\n", stylesheets, stylesheets - stylesheetMisses, (stylesheets - stylesheetMisses) * 100 / stylesheets);
982 printf("IMAGES: %d (%d hits, hit rate %d%%)\n", images, images - imageMisses, (images - imageMisses) * 100 / images);
986 const ResourceLoaderOptions& CachedResourceLoader::defaultCachedResourceOptions()
988 static ResourceLoaderOptions options(SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, AskClientForAllCredentials, DoSecurityCheck);