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 "CachedScript.h"
36 #include "CachedXSLStyleSheet.h"
38 #include "ContentSecurityPolicy.h"
39 #include "DOMWindow.h"
42 #include "FrameLoader.h"
43 #include "FrameLoaderClient.h"
44 #include "HTMLElement.h"
46 #include "MemoryCache.h"
47 #include "PingLoader.h"
48 #include "ResourceLoadScheduler.h"
49 #include "SecurityOrigin.h"
51 #include <wtf/UnusedParam.h>
52 #include <wtf/text/CString.h>
53 #include <wtf/text/WTFString.h>
55 #if ENABLE(VIDEO_TRACK)
56 #include "CachedTextTrack.h"
59 #if ENABLE(CSS_SHADERS)
60 #include "CachedShader.h"
63 #define PRELOAD_DEBUG 0
67 static CachedResource* createResource(CachedResource::Type type, ResourceRequest& request, const String& charset)
70 case CachedResource::ImageResource:
71 return new CachedImage(request);
72 case CachedResource::CSSStyleSheet:
73 return new CachedCSSStyleSheet(request, charset);
74 case CachedResource::Script:
75 return new CachedScript(request, charset);
77 case CachedResource::SVGDocumentResource:
78 return new CachedSVGDocument(request);
80 case CachedResource::FontResource:
81 return new CachedFont(request);
82 case CachedResource::RawResource:
83 return new CachedRawResource(request);
85 case CachedResource::XSLStyleSheet:
86 return new CachedXSLStyleSheet(request);
88 #if ENABLE(LINK_PREFETCH)
89 case CachedResource::LinkPrefetch:
90 return new CachedResource(request, CachedResource::LinkPrefetch);
91 case CachedResource::LinkSubresource:
92 return new CachedResource(request, CachedResource::LinkSubresource);
94 #if ENABLE(VIDEO_TRACK)
95 case CachedResource::TextTrackResource:
96 return new CachedTextTrack(request);
98 #if ENABLE(CSS_SHADERS)
99 case CachedResource::ShaderResource:
100 return new CachedShader(request);
103 ASSERT_NOT_REACHED();
107 static const ResourceLoaderOptions& defaultCachedResourceOptions()
109 static ResourceLoaderOptions options(SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, AskClientForCrossOriginCredentials, DoSecurityCheck);
113 CachedResourceLoader::CachedResourceLoader(Document* document)
114 : m_document(document)
116 , m_garbageCollectDocumentResourcesTimer(this, &CachedResourceLoader::garbageCollectDocumentResourcesTimerFired)
117 , m_autoLoadImages(true)
118 , m_allowStaleResources(false)
122 CachedResourceLoader::~CachedResourceLoader()
127 DocumentResourceMap::iterator end = m_documentResources.end();
128 for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it)
129 it->second->setOwningCachedResourceLoader(0);
131 // Make sure no requests still point to this CachedResourceLoader
132 ASSERT(m_requestCount == 0);
135 CachedResource* CachedResourceLoader::cachedResource(const String& resourceURL) const
137 KURL url = m_document->completeURL(resourceURL);
138 return cachedResource(url);
141 CachedResource* CachedResourceLoader::cachedResource(const KURL& resourceURL) const
143 KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(resourceURL);
144 return m_documentResources.get(url).get();
147 Frame* CachedResourceLoader::frame() const
149 return m_document ? m_document->frame() : 0;
152 CachedResourceHandle<CachedImage> CachedResourceLoader::requestImage(ResourceRequest& request)
154 if (Frame* f = frame()) {
155 if (f->loader()->pageDismissalEventBeingDispatched() != FrameLoader::NoDismissal) {
156 KURL requestURL = request.url();
157 if (requestURL.isValid() && canRequest(CachedResource::ImageResource, requestURL))
158 PingLoader::loadImage(f, requestURL);
162 CachedResourceHandle<CachedImage> resource(static_cast<CachedImage*>(requestResource(CachedResource::ImageResource, request, String(), defaultCachedResourceOptions()).get()));
163 if (autoLoadImages() && resource && resource->stillNeedsLoad())
164 resource->load(this, defaultCachedResourceOptions());
168 CachedResourceHandle<CachedFont> CachedResourceLoader::requestFont(ResourceRequest& request)
170 return static_cast<CachedFont*>(requestResource(CachedResource::FontResource, request, String(), defaultCachedResourceOptions()).get());
173 #if ENABLE(VIDEO_TRACK)
174 CachedResourceHandle<CachedTextTrack> CachedResourceLoader::requestTextTrack(ResourceRequest& request)
176 return static_cast<CachedTextTrack*>(requestResource(CachedResource::TextTrackResource, request, String(), defaultCachedResourceOptions()).get());
180 #if ENABLE(CSS_SHADERS)
181 CachedResourceHandle<CachedShader> CachedResourceLoader::requestShader(ResourceRequest& request)
183 return static_cast<CachedShader*>(requestResource(CachedResource::ShaderResource, request, String(), defaultCachedResourceOptions()).get());
187 CachedResourceHandle<CachedCSSStyleSheet> CachedResourceLoader::requestCSSStyleSheet(ResourceRequest& request, const String& charset, ResourceLoadPriority priority)
189 return static_cast<CachedCSSStyleSheet*>(requestResource(CachedResource::CSSStyleSheet, request, charset, defaultCachedResourceOptions(), priority).get());
192 CachedResourceHandle<CachedCSSStyleSheet> CachedResourceLoader::requestUserCSSStyleSheet(ResourceRequest& request, const String& charset)
194 KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(request.url());
196 if (CachedResource* existing = memoryCache()->resourceForURL(url)) {
197 if (existing->type() == CachedResource::CSSStyleSheet)
198 return static_cast<CachedCSSStyleSheet*>(existing);
199 memoryCache()->remove(existing);
201 if (url.string() != request.url())
204 CachedResourceHandle<CachedCSSStyleSheet> userSheet = new CachedCSSStyleSheet(request, charset);
206 memoryCache()->add(userSheet.get());
207 // FIXME: loadResource calls setOwningCachedResourceLoader() if the resource couldn't be added to cache. Does this function need to call it, too?
209 userSheet->load(this, ResourceLoaderOptions(DoNotSendCallbacks, SniffContent, BufferData, AllowStoredCredentials, AskClientForCrossOriginCredentials, SkipSecurityCheck));
214 CachedResourceHandle<CachedScript> CachedResourceLoader::requestScript(ResourceRequest& request, const String& charset)
216 return static_cast<CachedScript*>(requestResource(CachedResource::Script, request, charset, defaultCachedResourceOptions()).get());
220 CachedResourceHandle<CachedXSLStyleSheet> CachedResourceLoader::requestXSLStyleSheet(ResourceRequest& request)
222 return static_cast<CachedXSLStyleSheet*>(requestResource(CachedResource::XSLStyleSheet, request, String(), defaultCachedResourceOptions()).get());
227 CachedResourceHandle<CachedSVGDocument> CachedResourceLoader::requestSVGDocument(ResourceRequest& request)
229 return static_cast<CachedSVGDocument*>(requestResource(CachedResource::SVGDocumentResource, request, request.url(), defaultCachedResourceOptions()).get());
233 #if ENABLE(LINK_PREFETCH)
234 CachedResourceHandle<CachedResource> CachedResourceLoader::requestLinkResource(CachedResource::Type type, ResourceRequest& request, ResourceLoadPriority priority)
237 ASSERT(type == CachedResource::LinkPrefetch || type == CachedResource::LinkSubresource);
238 return requestResource(type, request, String(), defaultCachedResourceOptions(), priority);
242 CachedResourceHandle<CachedRawResource> CachedResourceLoader::requestRawResource(ResourceRequest& request, const ResourceLoaderOptions& options)
244 return static_cast<CachedRawResource*>(requestResource(CachedResource::RawResource, request, String(), options, ResourceLoadPriorityUnresolved, false).get());
247 bool CachedResourceLoader::checkInsecureContent(CachedResource::Type type, const KURL& url) const
250 case CachedResource::Script:
252 case CachedResource::XSLStyleSheet:
255 case CachedResource::SVGDocumentResource:
257 case CachedResource::CSSStyleSheet:
258 // These resource can inject script into the current document (Script,
259 // XSL) or exfiltrate the content of the current document (CSS).
260 if (Frame* f = frame())
261 if (!f->loader()->checkIfRunInsecureContent(m_document->securityOrigin(), url))
264 #if ENABLE(VIDEO_TRACK)
265 case CachedResource::TextTrackResource:
267 #if ENABLE(CSS_SHADERS)
268 case CachedResource::ShaderResource:
270 case CachedResource::RawResource:
271 case CachedResource::ImageResource:
272 case CachedResource::FontResource: {
273 // These resources can corrupt only the frame's pixels.
274 if (Frame* f = frame()) {
275 Frame* top = f->tree()->top();
276 if (!top->loader()->checkIfDisplayInsecureContent(top->document()->securityOrigin(), url))
281 #if ENABLE(LINK_PREFETCH)
282 case CachedResource::LinkPrefetch:
283 case CachedResource::LinkSubresource:
284 // Prefetch cannot affect the current document.
291 bool CachedResourceLoader::canRequest(CachedResource::Type type, const KURL& url, bool forPreload)
293 if (!document()->securityOrigin()->canDisplay(url)) {
295 FrameLoader::reportLocalLoadFailed(document()->frame(), url.string());
296 LOG(ResourceLoading, "CachedResourceLoader::requestResource URL was not allowed by SecurityOrigin::canDisplay");
300 // Some types of resources can be loaded only from the same origin. Other
301 // types of resources, like Images, Scripts, and CSS, can be loaded from
304 case CachedResource::ImageResource:
305 case CachedResource::CSSStyleSheet:
306 case CachedResource::Script:
307 case CachedResource::FontResource:
308 case CachedResource::RawResource:
309 #if ENABLE(LINK_PREFETCH)
310 case CachedResource::LinkPrefetch:
311 case CachedResource::LinkSubresource:
313 #if ENABLE(VIDEO_TRACK)
314 case CachedResource::TextTrackResource:
316 #if ENABLE(CSS_SHADERS)
317 case CachedResource::ShaderResource:
319 // These types of resources can be loaded from any origin.
320 // FIXME: Are we sure about CachedResource::FontResource?
323 case CachedResource::SVGDocumentResource:
326 case CachedResource::XSLStyleSheet:
327 if (!m_document->securityOrigin()->canRequest(url)) {
328 printAccessDeniedMessage(url);
337 case CachedResource::XSLStyleSheet:
339 case CachedResource::Script:
340 if (!m_document->contentSecurityPolicy()->allowScriptFromSource(url))
344 Settings* settings = frame()->settings();
345 if (!frame()->loader()->client()->allowScriptFromSource(!settings || settings->isScriptEnabled(), url)) {
346 frame()->loader()->client()->didNotAllowScript();
351 #if ENABLE(CSS_SHADERS)
352 case CachedResource::ShaderResource:
353 // Since shaders are referenced from CSS Styles use the same rules here.
355 case CachedResource::CSSStyleSheet:
356 if (!m_document->contentSecurityPolicy()->allowStyleFromSource(url))
360 case CachedResource::SVGDocumentResource:
362 case CachedResource::ImageResource:
363 if (!m_document->contentSecurityPolicy()->allowImageFromSource(url))
367 Settings* settings = frame()->settings();
368 if (!frame()->loader()->client()->allowImage(!settings || settings->areImagesEnabled(), url))
372 case CachedResource::FontResource: {
373 if (!m_document->contentSecurityPolicy()->allowFontFromSource(url))
377 case CachedResource::RawResource:
378 #if ENABLE(LINK_PREFETCH)
379 case CachedResource::LinkPrefetch:
380 case CachedResource::LinkSubresource:
383 #if ENABLE(VIDEO_TRACK)
384 case CachedResource::TextTrackResource:
385 // Cues aren't called out in the CPS spec yet, but they only work with a media element
386 // so use the media policy.
387 if (!m_document->contentSecurityPolicy()->allowMediaFromSource(url))
393 // Last of all, check for insecure content. We do this last so that when
394 // folks block insecure content with a CSP policy, they don't get a warning.
395 // They'll still get a warning in the console about CSP blocking the load.
397 // FIXME: Should we consider forPreload here?
398 if (!checkInsecureContent(type, url))
404 CachedResourceHandle<CachedResource> CachedResourceLoader::requestResource(CachedResource::Type type, ResourceRequest& request, const String& charset, const ResourceLoaderOptions& options, ResourceLoadPriority priority, bool forPreload)
406 KURL url = request.url();
408 LOG(ResourceLoading, "CachedResourceLoader::requestResource '%s', charset '%s', priority=%d, forPreload=%u", url.string().latin1().data(), charset.latin1().data(), priority, forPreload);
410 // If only the fragment identifiers differ, it is the same resource.
411 url = MemoryCache::removeFragmentIdentifierIfNeeded(url);
416 if (!canRequest(type, url, forPreload))
419 if (memoryCache()->disabled()) {
420 DocumentResourceMap::iterator it = m_documentResources.find(url.string());
421 if (it != m_documentResources.end()) {
422 it->second->setOwningCachedResourceLoader(0);
423 m_documentResources.remove(it);
427 // See if we can use an existing resource from the cache.
428 CachedResourceHandle<CachedResource> resource = memoryCache()->resourceForURL(url);
430 if (request.url() != url)
433 switch (determineRevalidationPolicy(type, request, forPreload, resource.get())) {
435 resource = loadResource(type, request, charset, priority, options);
438 memoryCache()->remove(resource.get());
439 resource = loadResource(type, request, charset, priority, options);
442 resource = revalidateResource(resource.get(), priority, options);
445 memoryCache()->resourceAccessed(resource.get());
446 notifyLoadedFromMemoryCache(resource.get());
453 ASSERT(resource->url() == url.string());
454 m_documentResources.set(resource->url(), resource);
458 CachedResourceHandle<CachedResource> CachedResourceLoader::revalidateResource(CachedResource* resource, ResourceLoadPriority priority, const ResourceLoaderOptions& options)
461 ASSERT(resource->inCache());
462 ASSERT(!memoryCache()->disabled());
463 ASSERT(resource->canUseCacheValidator());
464 ASSERT(!resource->resourceToRevalidate());
466 // Copy the URL out of the resource to be revalidated in case it gets deleted by the remove() call below.
467 String url = resource->url();
468 bool urlProtocolIsData = resource->url().protocolIsData();
469 CachedResourceHandle<CachedResource> newResource = createResource(resource->type(), resource->resourceRequest(), resource->encoding());
471 LOG(ResourceLoading, "Resource %p created to revalidate %p", newResource.get(), resource);
472 newResource->setResourceToRevalidate(resource);
474 memoryCache()->remove(resource);
475 memoryCache()->add(newResource.get());
477 newResource->setLoadPriority(priority);
478 newResource->load(this, options);
480 if (!urlProtocolIsData)
481 m_validatedURLs.add(url);
485 CachedResourceHandle<CachedResource> CachedResourceLoader::loadResource(CachedResource::Type type, ResourceRequest& request, const String& charset, ResourceLoadPriority priority, const ResourceLoaderOptions& options)
487 ASSERT(!memoryCache()->resourceForURL(request.url()));
489 LOG(ResourceLoading, "Loading CachedResource for '%s'.", request.url().string().latin1().data());
491 CachedResourceHandle<CachedResource> resource = createResource(type, request, charset);
493 bool inCache = memoryCache()->add(resource.get());
495 resource->setLoadPriority(priority);
496 resource->load(this, options);
499 resource->setOwningCachedResourceLoader(this);
501 // We don't support immediate loads, but we do support immediate failure.
502 if (resource->errorOccurred()) {
504 memoryCache()->remove(resource.get());
508 if (!request.url().protocolIsData())
509 m_validatedURLs.add(request.url());
513 CachedResourceLoader::RevalidationPolicy CachedResourceLoader::determineRevalidationPolicy(CachedResource::Type type, ResourceRequest& request, bool forPreload, CachedResource* existingResource) const
515 if (!existingResource)
518 // We already have a preload going for this URL.
519 if (forPreload && existingResource->isPreloaded())
522 // If the same URL has been loaded as a different type, we need to reload.
523 if (existingResource->type() != type) {
524 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to type mismatch.");
528 if (existingResource->type() == CachedResource::RawResource && !static_cast<CachedRawResource*>(existingResource)->canReuse(request))
531 // Certain requests (e.g., XHRs) might have manually set headers that require revalidation.
532 // FIXME: In theory, this should be a Revalidate case. In practice, the MemoryCache revalidation path assumes a whole bunch
533 // of things about how revalidation works that manual headers violate, so punt to Reload instead.
534 if (request.isConditional())
537 // Don't reload resources while pasting.
538 if (m_allowStaleResources)
541 // Alwaus use preloads.
542 if (existingResource->isPreloaded())
545 // CachePolicyHistoryBuffer uses the cache no matter what.
546 if (cachePolicy() == CachePolicyHistoryBuffer)
549 // Don't reuse resources with Cache-control: no-store.
550 if (existingResource->response().cacheControlContainsNoStore()) {
551 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to Cache-control: no-store.");
555 // If credentials were sent with the previous request and won't be
556 // with this one, or vice versa, re-fetch the resource.
558 // This helps with the case where the server sends back
559 // "Access-Control-Allow-Origin: *" all the time, but some of the
560 // client's requests are made without CORS and some with.
561 if (existingResource->resourceRequest().allowCookies() != request.allowCookies()) {
562 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to difference in credentials settings.");
566 // During the initial load, avoid loading the same resource multiple times for a single document, even if the cache policies would tell us to.
567 if (!document()->loadEventFinished() && m_validatedURLs.contains(existingResource->url()))
570 // CachePolicyReload always reloads
571 if (cachePolicy() == CachePolicyReload) {
572 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to CachePolicyReload.");
576 // We'll try to reload the resource if it failed last time.
577 if (existingResource->errorOccurred()) {
578 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicye reloading due to resource being in the error state");
582 // For resources that are not yet loaded we ignore the cache policy.
583 if (existingResource->isLoading())
586 // Check if the cache headers requires us to revalidate (cache expiration for example).
587 if (existingResource->mustRevalidateDueToCacheHeaders(cachePolicy())) {
588 // See if the resource has usable ETag or Last-modified headers.
589 if (existingResource->canUseCacheValidator())
593 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to missing cache validators.");
600 void CachedResourceLoader::printAccessDeniedMessage(const KURL& url) const
608 Settings* settings = frame()->settings();
609 if (!settings || settings->privateBrowsingEnabled())
613 if (m_document->url().isNull())
614 message = "Unsafe attempt to load URL " + url.string() + '.';
616 message = "Unsafe attempt to load URL " + url.string() + " from frame with URL " + m_document->url().string() + ". Domains, protocols and ports must match.\n";
618 // FIXME: provide line number and source URL.
619 frame()->domWindow()->console()->addMessage(OtherMessageSource, LogMessageType, ErrorMessageLevel, message);
622 void CachedResourceLoader::setAutoLoadImages(bool enable)
624 if (enable == m_autoLoadImages)
627 m_autoLoadImages = enable;
629 if (!m_autoLoadImages)
632 DocumentResourceMap::iterator end = m_documentResources.end();
633 for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it) {
634 CachedResource* resource = it->second.get();
635 if (resource->type() == CachedResource::ImageResource) {
636 CachedImage* image = const_cast<CachedImage*>(static_cast<const CachedImage*>(resource));
638 if (image->stillNeedsLoad())
639 image->load(this, defaultCachedResourceOptions());
644 CachePolicy CachedResourceLoader::cachePolicy() const
646 return frame() ? frame()->loader()->subresourceCachePolicy() : CachePolicyVerify;
649 void CachedResourceLoader::removeCachedResource(CachedResource* resource) const
652 DocumentResourceMap::iterator it = m_documentResources.find(resource->url());
653 if (it != m_documentResources.end())
654 ASSERT(it->second.get() == resource);
656 m_documentResources.remove(resource->url());
659 void CachedResourceLoader::loadDone()
661 RefPtr<Document> protect(m_document);
663 frame()->loader()->loadDone();
664 performPostLoadActions();
666 if (!m_garbageCollectDocumentResourcesTimer.isActive())
667 m_garbageCollectDocumentResourcesTimer.startOneShot(0);
670 // Garbage collecting m_documentResources is a workaround for the
671 // CachedResourceHandles on the RHS being strong references. Ideally this
672 // would be a weak map, however CachedResourceHandles perform additional
673 // bookkeeping on CachedResources, so instead pseudo-GC them -- when the
674 // reference count reaches 1, m_documentResources is the only reference, so
675 // remove it from the map.
676 void CachedResourceLoader::garbageCollectDocumentResourcesTimerFired(Timer<CachedResourceLoader>* timer)
678 ASSERT_UNUSED(timer, timer == &m_garbageCollectDocumentResourcesTimer);
679 garbageCollectDocumentResources();
682 void CachedResourceLoader::garbageCollectDocumentResources()
684 typedef Vector<String, 10> StringVector;
685 StringVector resourcesToDelete;
687 for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != m_documentResources.end(); ++it) {
688 if (it->second->hasOneHandle()) {
689 resourcesToDelete.append(it->first);
690 it->second->setOwningCachedResourceLoader(0);
694 for (StringVector::const_iterator it = resourcesToDelete.begin(); it != resourcesToDelete.end(); ++it)
695 m_documentResources.remove(*it);
698 void CachedResourceLoader::performPostLoadActions()
700 checkForPendingPreloads();
701 resourceLoadScheduler()->servePendingRequests();
704 void CachedResourceLoader::notifyLoadedFromMemoryCache(CachedResource* resource)
706 if (!resource || !frame() || resource->status() != CachedResource::Cached)
709 // FIXME: If the WebKit client changes or cancels the request, WebCore does not respect this and continues the load.
710 frame()->loader()->loadedResourceFromMemoryCache(resource);
713 void CachedResourceLoader::incrementRequestCount(const CachedResource* res)
715 if (res->ignoreForRequestCount())
721 void CachedResourceLoader::decrementRequestCount(const CachedResource* res)
723 if (res->ignoreForRequestCount())
727 ASSERT(m_requestCount > -1);
730 void CachedResourceLoader::preload(CachedResource::Type type, ResourceRequest& request, const String& charset, bool referencedFromBody)
732 // FIXME: Rip this out when we are sure it is no longer necessary (even for mobile).
733 UNUSED_PARAM(referencedFromBody);
735 bool delaySubresourceLoad = true;
737 delaySubresourceLoad = false;
739 if (delaySubresourceLoad) {
740 bool hasRendering = m_document->body() && m_document->body()->renderer();
741 bool canBlockParser = type == CachedResource::Script || type == CachedResource::CSSStyleSheet;
742 if (!hasRendering && !canBlockParser) {
743 // Don't preload subresources that can't block the parser before we have something to draw.
744 // This helps prevent preloads from delaying first display when bandwidth is limited.
745 PendingPreload pendingPreload = { type, request, charset };
746 m_pendingPreloads.append(pendingPreload);
750 requestPreload(type, request, charset);
753 void CachedResourceLoader::checkForPendingPreloads()
755 if (m_pendingPreloads.isEmpty() || !m_document->body() || !m_document->body()->renderer())
757 while (!m_pendingPreloads.isEmpty()) {
758 PendingPreload preload = m_pendingPreloads.takeFirst();
759 // 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).
760 if (!cachedResource(preload.m_request.url()))
761 requestPreload(preload.m_type, preload.m_request, preload.m_charset);
763 m_pendingPreloads.clear();
766 void CachedResourceLoader::requestPreload(CachedResource::Type type, ResourceRequest& request, const String& charset)
769 if (type == CachedResource::Script || type == CachedResource::CSSStyleSheet)
770 encoding = charset.isEmpty() ? m_document->charset() : charset;
772 CachedResourceHandle<CachedResource> resource = requestResource(type, request, encoding, defaultCachedResourceOptions(), ResourceLoadPriorityUnresolved, true);
773 if (!resource || (m_preloads && m_preloads->contains(resource.get())))
775 resource->increasePreloadCount();
778 m_preloads = adoptPtr(new ListHashSet<CachedResource*>);
779 m_preloads->add(resource.get());
782 printf("PRELOADING %s\n", resource->url().latin1().data());
786 bool CachedResourceLoader::isPreloaded(const String& urlString) const
788 const KURL& url = m_document->completeURL(urlString);
791 ListHashSet<CachedResource*>::iterator end = m_preloads->end();
792 for (ListHashSet<CachedResource*>::iterator it = m_preloads->begin(); it != end; ++it) {
793 CachedResource* resource = *it;
794 if (resource->url() == url)
799 Deque<PendingPreload>::const_iterator dequeEnd = m_pendingPreloads.end();
800 for (Deque<PendingPreload>::const_iterator it = m_pendingPreloads.begin(); it != dequeEnd; ++it) {
801 PendingPreload pendingPreload = *it;
802 if (pendingPreload.m_request.url() == url)
808 void CachedResourceLoader::clearPreloads()
816 ListHashSet<CachedResource*>::iterator end = m_preloads->end();
817 for (ListHashSet<CachedResource*>::iterator it = m_preloads->begin(); it != end; ++it) {
818 CachedResource* res = *it;
819 res->decreasePreloadCount();
820 bool deleted = res->deleteIfPossible();
821 if (!deleted && res->preloadResult() == CachedResource::PreloadNotReferenced)
822 memoryCache()->remove(res);
827 void CachedResourceLoader::clearPendingPreloads()
829 m_pendingPreloads.clear();
833 void CachedResourceLoader::printPreloadStats()
835 unsigned scripts = 0;
836 unsigned scriptMisses = 0;
837 unsigned stylesheets = 0;
838 unsigned stylesheetMisses = 0;
840 unsigned imageMisses = 0;
841 ListHashSet<CachedResource*>::iterator end = m_preloads.end();
842 for (ListHashSet<CachedResource*>::iterator it = m_preloads.begin(); it != end; ++it) {
843 CachedResource* res = *it;
844 if (res->preloadResult() == CachedResource::PreloadNotReferenced)
845 printf("!! UNREFERENCED PRELOAD %s\n", res->url().latin1().data());
846 else if (res->preloadResult() == CachedResource::PreloadReferencedWhileComplete)
847 printf("HIT COMPLETE PRELOAD %s\n", res->url().latin1().data());
848 else if (res->preloadResult() == CachedResource::PreloadReferencedWhileLoading)
849 printf("HIT LOADING PRELOAD %s\n", res->url().latin1().data());
851 if (res->type() == CachedResource::Script) {
853 if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
855 } else if (res->type() == CachedResource::CSSStyleSheet) {
857 if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
861 if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
865 if (res->errorOccurred())
866 memoryCache()->remove(res);
868 res->decreasePreloadCount();
873 printf("SCRIPTS: %d (%d hits, hit rate %d%%)\n", scripts, scripts - scriptMisses, (scripts - scriptMisses) * 100 / scripts);
875 printf("STYLESHEETS: %d (%d hits, hit rate %d%%)\n", stylesheets, stylesheets - stylesheetMisses, (stylesheets - stylesheetMisses) * 100 / stylesheets);
877 printf("IMAGES: %d (%d hits, hit rate %d%%)\n", images, images - imageMisses, (images - imageMisses) * 100 / images);