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 "ChromeClient.h"
40 #include "ContentSecurityPolicy.h"
41 #include "DOMWindow.h"
43 #include "DocumentLoader.h"
45 #include "FrameLoader.h"
46 #include "FrameLoaderClient.h"
47 #include "HTMLElement.h"
48 #include "HTMLFrameOwnerElement.h"
49 #include "LoaderStrategy.h"
51 #include "MemoryCache.h"
53 #include "PingLoader.h"
54 #include "PlatformStrategies.h"
55 #include "RenderElement.h"
56 #include "ResourceLoadScheduler.h"
57 #include "ScriptController.h"
58 #include "SecurityOrigin.h"
59 #include "SessionID.h"
61 #include <wtf/text/CString.h>
62 #include <wtf/text/WTFString.h>
64 #if ENABLE(VIDEO_TRACK)
65 #include "CachedTextTrack.h"
68 #if ENABLE(RESOURCE_TIMING)
69 #include "Performance.h"
72 #define PRELOAD_DEBUG 0
76 static CachedResource* createResource(CachedResource::Type type, ResourceRequest& request, const String& charset, SessionID sessionID)
79 case CachedResource::ImageResource:
80 return new CachedImage(request, sessionID);
81 case CachedResource::CSSStyleSheet:
82 return new CachedCSSStyleSheet(request, charset, sessionID);
83 case CachedResource::Script:
84 return new CachedScript(request, charset, sessionID);
85 case CachedResource::SVGDocumentResource:
86 return new CachedSVGDocument(request, sessionID);
87 case CachedResource::FontResource:
88 return new CachedFont(request, sessionID);
89 case CachedResource::RawResource:
90 case CachedResource::MainResource:
91 return new CachedRawResource(request, type, sessionID);
93 case CachedResource::XSLStyleSheet:
94 return new CachedXSLStyleSheet(request, sessionID);
96 #if ENABLE(LINK_PREFETCH)
97 case CachedResource::LinkPrefetch:
98 return new CachedResource(request, CachedResource::LinkPrefetch, sessionID);
99 case CachedResource::LinkSubresource:
100 return new CachedResource(request, CachedResource::LinkSubresource, sessionID);
102 #if ENABLE(VIDEO_TRACK)
103 case CachedResource::TextTrackResource:
104 return new CachedTextTrack(request, sessionID);
107 ASSERT_NOT_REACHED();
111 CachedResourceLoader::CachedResourceLoader(DocumentLoader* documentLoader)
113 , m_documentLoader(documentLoader)
115 , m_garbageCollectDocumentResourcesTimer(this, &CachedResourceLoader::garbageCollectDocumentResourcesTimerFired)
116 , m_autoLoadImages(true)
117 , m_imagesEnabled(true)
118 , m_allowStaleResources(false)
122 CachedResourceLoader::~CachedResourceLoader()
124 m_documentLoader = 0;
128 DocumentResourceMap::iterator end = m_documentResources.end();
129 for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it)
130 it->value->setOwningCachedResourceLoader(0);
132 // Make sure no requests still point to this CachedResourceLoader
133 ASSERT(m_requestCount == 0);
136 CachedResource* CachedResourceLoader::cachedResource(const String& resourceURL) const
138 URL url = m_document->completeURL(resourceURL);
139 return cachedResource(url);
142 CachedResource* CachedResourceLoader::cachedResource(const URL& resourceURL) const
144 URL url = MemoryCache::removeFragmentIdentifierIfNeeded(resourceURL);
145 return m_documentResources.get(url).get();
148 Frame* CachedResourceLoader::frame() const
150 return m_documentLoader ? m_documentLoader->frame() : 0;
153 SessionID CachedResourceLoader::sessionID() const
155 SessionID sessionID = SessionID::defaultSessionID();
157 if (Frame* f = frame())
158 sessionID = f->page()->sessionID();
163 CachedResourceHandle<CachedImage> CachedResourceLoader::requestImage(CachedResourceRequest& request)
165 if (Frame* frame = this->frame()) {
166 if (frame->loader().pageDismissalEventBeingDispatched() != FrameLoader::NoDismissal) {
167 URL requestURL = request.resourceRequest().url();
168 if (requestURL.isValid() && canRequest(CachedResource::ImageResource, requestURL, request.options(), request.forPreload()))
169 PingLoader::loadImage(*frame, requestURL);
174 request.setDefer(clientDefersImage(request.resourceRequest().url()) ? CachedResourceRequest::DeferredByClient : CachedResourceRequest::NoDefer);
175 return downcast<CachedImage>(requestResource(CachedResource::ImageResource, request).get());
178 CachedResourceHandle<CachedFont> CachedResourceLoader::requestFont(CachedResourceRequest& request)
180 return downcast<CachedFont>(requestResource(CachedResource::FontResource, request).get());
183 #if ENABLE(VIDEO_TRACK)
184 CachedResourceHandle<CachedTextTrack> CachedResourceLoader::requestTextTrack(CachedResourceRequest& request)
186 return downcast<CachedTextTrack>(requestResource(CachedResource::TextTrackResource, request).get());
190 CachedResourceHandle<CachedCSSStyleSheet> CachedResourceLoader::requestCSSStyleSheet(CachedResourceRequest& request)
192 return downcast<CachedCSSStyleSheet>(requestResource(CachedResource::CSSStyleSheet, request).get());
195 CachedResourceHandle<CachedCSSStyleSheet> CachedResourceLoader::requestUserCSSStyleSheet(CachedResourceRequest& request)
197 URL url = MemoryCache::removeFragmentIdentifierIfNeeded(request.resourceRequest().url());
199 #if ENABLE(CACHE_PARTITIONING)
200 request.mutableResourceRequest().setDomainForCachePartition(document()->topOrigin()->domainForCachePartition());
203 if (CachedResource* existing = memoryCache()->resourceForRequest(request.resourceRequest(), sessionID())) {
204 if (is<CachedCSSStyleSheet>(*existing))
205 return downcast<CachedCSSStyleSheet>(existing);
206 memoryCache()->remove(existing);
208 if (url.string() != request.resourceRequest().url())
209 request.mutableResourceRequest().setURL(url);
211 CachedResourceHandle<CachedCSSStyleSheet> userSheet = new CachedCSSStyleSheet(request.resourceRequest(), request.charset(), sessionID());
213 memoryCache()->add(userSheet.get());
214 // FIXME: loadResource calls setOwningCachedResourceLoader() if the resource couldn't be added to cache. Does this function need to call it, too?
216 userSheet->load(this, ResourceLoaderOptions(DoNotSendCallbacks, SniffContent, BufferData, AllowStoredCredentials, AskClientForAllCredentials, SkipSecurityCheck, UseDefaultOriginRestrictionsForType, DoNotIncludeCertificateInfo));
221 CachedResourceHandle<CachedScript> CachedResourceLoader::requestScript(CachedResourceRequest& request)
223 return downcast<CachedScript>(requestResource(CachedResource::Script, request).get());
227 CachedResourceHandle<CachedXSLStyleSheet> CachedResourceLoader::requestXSLStyleSheet(CachedResourceRequest& request)
229 return downcast<CachedXSLStyleSheet>(requestResource(CachedResource::XSLStyleSheet, request).get());
233 CachedResourceHandle<CachedSVGDocument> CachedResourceLoader::requestSVGDocument(CachedResourceRequest& request)
235 return downcast<CachedSVGDocument>(requestResource(CachedResource::SVGDocumentResource, request).get());
238 #if ENABLE(LINK_PREFETCH)
239 CachedResourceHandle<CachedResource> CachedResourceLoader::requestLinkResource(CachedResource::Type type, CachedResourceRequest& request)
242 ASSERT(type == CachedResource::LinkPrefetch || type == CachedResource::LinkSubresource);
243 return requestResource(type, request);
247 CachedResourceHandle<CachedRawResource> CachedResourceLoader::requestRawResource(CachedResourceRequest& request)
249 return downcast<CachedRawResource>(requestResource(CachedResource::RawResource, request).get());
252 CachedResourceHandle<CachedRawResource> CachedResourceLoader::requestMainResource(CachedResourceRequest& request)
254 return downcast<CachedRawResource>(requestResource(CachedResource::MainResource, request).get());
257 bool CachedResourceLoader::checkInsecureContent(CachedResource::Type type, const URL& url) const
260 case CachedResource::Script:
262 case CachedResource::XSLStyleSheet:
264 case CachedResource::SVGDocumentResource:
265 case CachedResource::CSSStyleSheet:
266 // These resource can inject script into the current document (Script,
267 // XSL) or exfiltrate the content of the current document (CSS).
268 if (Frame* f = frame())
269 if (!f->loader().mixedContentChecker().canRunInsecureContent(m_document->securityOrigin(), url))
272 #if ENABLE(VIDEO_TRACK)
273 case CachedResource::TextTrackResource:
275 case CachedResource::RawResource:
276 case CachedResource::ImageResource:
277 case CachedResource::FontResource: {
278 // These resources can corrupt only the frame's pixels.
279 if (Frame* f = frame()) {
280 Frame& topFrame = f->tree().top();
281 if (!topFrame.loader().mixedContentChecker().canDisplayInsecureContent(topFrame.document()->securityOrigin(), url))
286 case CachedResource::MainResource:
287 #if ENABLE(LINK_PREFETCH)
288 case CachedResource::LinkPrefetch:
289 case CachedResource::LinkSubresource:
290 // Prefetch cannot affect the current document.
297 bool CachedResourceLoader::canRequest(CachedResource::Type type, const URL& url, const ResourceLoaderOptions& options, bool forPreload)
299 if (document() && !document()->securityOrigin()->canDisplay(url)) {
301 FrameLoader::reportLocalLoadFailed(frame(), url.stringCenterEllipsizedToLength());
302 LOG(ResourceLoading, "CachedResourceLoader::requestResource URL was not allowed by SecurityOrigin::canDisplay");
306 // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved.
307 bool shouldBypassMainWorldContentSecurityPolicy = (frame() && frame()->script().shouldBypassMainWorldContentSecurityPolicy());
309 // Some types of resources can be loaded only from the same origin. Other
310 // types of resources, like Images, Scripts, and CSS, can be loaded from
313 case CachedResource::MainResource:
314 case CachedResource::ImageResource:
315 case CachedResource::CSSStyleSheet:
316 case CachedResource::Script:
317 case CachedResource::FontResource:
318 case CachedResource::RawResource:
319 #if ENABLE(LINK_PREFETCH)
320 case CachedResource::LinkPrefetch:
321 case CachedResource::LinkSubresource:
323 #if ENABLE(VIDEO_TRACK)
324 case CachedResource::TextTrackResource:
326 if (options.requestOriginPolicy() == RestrictToSameOrigin && !m_document->securityOrigin()->canRequest(url)) {
327 printAccessDeniedMessage(url);
331 case CachedResource::SVGDocumentResource:
333 case CachedResource::XSLStyleSheet:
334 if (!m_document->securityOrigin()->canRequest(url)) {
335 printAccessDeniedMessage(url);
344 case CachedResource::XSLStyleSheet:
345 if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowScriptFromSource(url))
349 case CachedResource::Script:
350 if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowScriptFromSource(url))
352 if (frame() && !frame()->settings().isScriptEnabled())
355 case CachedResource::CSSStyleSheet:
356 if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowStyleFromSource(url))
359 case CachedResource::SVGDocumentResource:
360 case CachedResource::ImageResource:
361 if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowImageFromSource(url))
364 case CachedResource::FontResource: {
365 if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowFontFromSource(url))
369 case CachedResource::MainResource:
370 case CachedResource::RawResource:
371 #if ENABLE(LINK_PREFETCH)
372 case CachedResource::LinkPrefetch:
373 case CachedResource::LinkSubresource:
376 #if ENABLE(VIDEO_TRACK)
377 case CachedResource::TextTrackResource:
378 if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowMediaFromSource(url))
384 // SVG Images have unique security rules that prevent all subresource requests except for data urls.
385 if (type != CachedResource::MainResource && frame() && frame()->page()) {
386 if (frame()->page()->chrome().client().isSVGImageChromeClient() && !url.protocolIsData())
390 // Last of all, check for insecure content. We do this last so that when
391 // folks block insecure content with a CSP policy, they don't get a warning.
392 // They'll still get a warning in the console about CSP blocking the load.
394 // FIXME: Should we consider forPreload here?
395 if (!checkInsecureContent(type, url))
401 bool CachedResourceLoader::shouldContinueAfterNotifyingLoadedFromMemoryCache(const CachedResourceRequest& request, CachedResource* resource)
403 if (!resource || !frame() || resource->status() != CachedResource::Cached)
406 ResourceRequest newRequest = ResourceRequest(resource->url());
407 #if ENABLE(INSPECTOR)
408 if (request.resourceRequest().hiddenFromInspector())
409 newRequest.setHiddenFromInspector(true);
411 UNUSED_PARAM(request);
413 frame()->loader().loadedResourceFromMemoryCache(resource, newRequest);
415 // FIXME <http://webkit.org/b/113251>: If the delegate modifies the request's
416 // URL, it is no longer appropriate to use this CachedResource.
417 return !newRequest.isNull();
420 CachedResourceHandle<CachedResource> CachedResourceLoader::requestResource(CachedResource::Type type, CachedResourceRequest& request)
422 URL url = request.resourceRequest().url();
424 LOG(ResourceLoading, "CachedResourceLoader::requestResource '%s', charset '%s', priority=%d, forPreload=%u", url.stringCenterEllipsizedToLength().latin1().data(), request.charset().latin1().data(), request.priority(), request.forPreload());
426 // If only the fragment identifiers differ, it is the same resource.
427 url = MemoryCache::removeFragmentIdentifierIfNeeded(url);
432 if (!canRequest(type, url, request.options(), request.forPreload()))
435 if (Frame* f = frame())
436 f->loader().client().dispatchWillRequestResource(&request);
438 if (memoryCache()->disabled()) {
439 DocumentResourceMap::iterator it = m_documentResources.find(url.string());
440 if (it != m_documentResources.end()) {
441 it->value->setOwningCachedResourceLoader(0);
442 m_documentResources.remove(it);
446 // See if we can use an existing resource from the cache.
447 CachedResourceHandle<CachedResource> resource;
448 #if ENABLE(CACHE_PARTITIONING)
450 request.mutableResourceRequest().setDomainForCachePartition(document()->topOrigin()->domainForCachePartition());
453 resource = memoryCache()->resourceForRequest(request.resourceRequest(), sessionID());
455 const RevalidationPolicy policy = determineRevalidationPolicy(type, request.mutableResourceRequest(), request.forPreload(), resource.get(), request.defer());
458 memoryCache()->remove(resource.get());
461 resource = loadResource(type, request);
464 resource = revalidateResource(request, resource.get());
467 if (!shouldContinueAfterNotifyingLoadedFromMemoryCache(request, resource.get()))
469 memoryCache()->resourceAccessed(resource.get());
476 if (!request.forPreload() || policy != Use)
477 resource->setLoadPriority(request.priority());
479 if ((policy != Use || resource->stillNeedsLoad()) && CachedResourceRequest::NoDefer == request.defer()) {
480 resource->load(this, request.options());
482 // We don't support immediate loads, but we do support immediate failure.
483 if (resource->errorOccurred()) {
484 if (resource->inCache())
485 memoryCache()->remove(resource.get());
490 if (document() && !document()->loadEventFinished() && !request.resourceRequest().url().protocolIsData())
491 m_validatedURLs.add(request.resourceRequest().url());
493 ASSERT(resource->url() == url.string());
494 m_documentResources.set(resource->url(), resource);
498 void CachedResourceLoader::documentDidFinishLoadEvent()
500 m_validatedURLs.clear();
503 CachedResourceHandle<CachedResource> CachedResourceLoader::revalidateResource(const CachedResourceRequest& request, CachedResource* resource)
506 ASSERT(resource->inCache());
507 ASSERT(!memoryCache()->disabled());
508 ASSERT(resource->canUseCacheValidator());
509 ASSERT(!resource->resourceToRevalidate());
510 ASSERT(resource->sessionID() == sessionID());
512 // Copy the URL out of the resource to be revalidated in case it gets deleted by the remove() call below.
513 String url = resource->url();
514 CachedResourceHandle<CachedResource> newResource = createResource(resource->type(), resource->resourceRequest(), resource->encoding(), resource->sessionID());
516 LOG(ResourceLoading, "Resource %p created to revalidate %p", newResource.get(), resource);
517 newResource->setResourceToRevalidate(resource);
519 memoryCache()->remove(resource);
520 memoryCache()->add(newResource.get());
521 #if ENABLE(RESOURCE_TIMING)
522 storeResourceTimingInitiatorInformation(resource, request);
524 UNUSED_PARAM(request);
529 CachedResourceHandle<CachedResource> CachedResourceLoader::loadResource(CachedResource::Type type, CachedResourceRequest& request)
531 ASSERT(!memoryCache()->resourceForRequest(request.resourceRequest(), sessionID()));
533 LOG(ResourceLoading, "Loading CachedResource for '%s'.", request.resourceRequest().url().stringCenterEllipsizedToLength().latin1().data());
535 CachedResourceHandle<CachedResource> resource = createResource(type, request.mutableResourceRequest(), request.charset(), sessionID());
537 if (!memoryCache()->add(resource.get()))
538 resource->setOwningCachedResourceLoader(this);
539 #if ENABLE(RESOURCE_TIMING)
540 storeResourceTimingInitiatorInformation(resource, request);
545 #if ENABLE(RESOURCE_TIMING)
546 void CachedResourceLoader::storeResourceTimingInitiatorInformation(const CachedResourceHandle<CachedResource>& resource, const CachedResourceRequest& request)
548 if (resource->type() == CachedResource::MainResource) {
549 // <iframe>s should report the initial navigation requested by the parent document, but not subsequent navigations.
550 if (frame()->ownerElement() && m_documentLoader->frameLoader()->stateMachine().committingFirstRealLoad()) {
551 InitiatorInfo info = { frame()->ownerElement()->localName(), monotonicallyIncreasingTime() };
552 m_initiatorMap.add(resource.get(), info);
555 InitiatorInfo info = { request.initiatorName(), monotonicallyIncreasingTime() };
556 m_initiatorMap.add(resource.get(), info);
559 #endif // ENABLE(RESOURCE_TIMING)
561 CachedResourceLoader::RevalidationPolicy CachedResourceLoader::determineRevalidationPolicy(CachedResource::Type type, ResourceRequest& request, bool forPreload, CachedResource* existingResource, CachedResourceRequest::DeferOption defer) const
563 if (!existingResource)
566 // We already have a preload going for this URL.
567 if (forPreload && existingResource->isPreloaded())
570 // If the same URL has been loaded as a different type, we need to reload.
571 if (existingResource->type() != type) {
572 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to type mismatch.");
576 if (!existingResource->canReuse(request))
579 // Conditional requests should have failed canReuse check.
580 ASSERT(!request.isConditional());
582 // Do not load from cache if images are not enabled. The load for this image will be blocked
583 // in CachedImage::load.
584 if (CachedResourceRequest::DeferredByClient == defer)
587 // Don't reload resources while pasting.
588 if (m_allowStaleResources)
591 // Alwaus use preloads.
592 if (existingResource->isPreloaded())
595 // CachePolicyHistoryBuffer uses the cache no matter what.
596 if (cachePolicy(type) == CachePolicyHistoryBuffer)
599 // Don't reuse resources with Cache-control: no-store.
600 if (existingResource->response().cacheControlContainsNoStore()) {
601 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to Cache-control: no-store.");
605 // Validate the redirect chain
606 if (!existingResource->redirectChainAllowsReuse()) {
607 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to not cached or expired redirections.");
611 // If credentials were sent with the previous request and won't be
612 // with this one, or vice versa, re-fetch the resource.
614 // This helps with the case where the server sends back
615 // "Access-Control-Allow-Origin: *" all the time, but some of the
616 // client's requests are made without CORS and some with.
617 if (existingResource->resourceRequest().allowCookies() != request.allowCookies()) {
618 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to difference in credentials settings.");
622 // During the initial load, avoid loading the same resource multiple times for a single document, even if the cache policies would tell us to.
623 if (document() && !document()->loadEventFinished() && m_validatedURLs.contains(existingResource->url()))
626 // CachePolicyReload always reloads
627 if (cachePolicy(type) == CachePolicyReload) {
628 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to CachePolicyReload.");
632 // We'll try to reload the resource if it failed last time.
633 if (existingResource->errorOccurred()) {
634 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicye reloading due to resource being in the error state");
638 // For resources that are not yet loaded we ignore the cache policy.
639 if (existingResource->isLoading())
642 // Check if the cache headers requires us to revalidate (cache expiration for example).
643 if (existingResource->mustRevalidateDueToCacheHeaders(cachePolicy(type))) {
644 // See if the resource has usable ETag or Last-modified headers.
645 if (existingResource->canUseCacheValidator())
649 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to missing cache validators.");
656 void CachedResourceLoader::printAccessDeniedMessage(const URL& url) const
665 if (!m_document || m_document->url().isNull())
666 message = "Unsafe attempt to load URL " + url.stringCenterEllipsizedToLength() + '.';
668 message = "Unsafe attempt to load URL " + url.stringCenterEllipsizedToLength() + " from frame with URL " + m_document->url().stringCenterEllipsizedToLength() + ". Domains, protocols and ports must match.\n";
670 frame()->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message);
673 void CachedResourceLoader::setAutoLoadImages(bool enable)
675 if (enable == m_autoLoadImages)
678 m_autoLoadImages = enable;
680 if (!m_autoLoadImages)
683 reloadImagesIfNotDeferred();
686 void CachedResourceLoader::setImagesEnabled(bool enable)
688 if (enable == m_imagesEnabled)
691 m_imagesEnabled = enable;
693 if (!m_imagesEnabled)
696 reloadImagesIfNotDeferred();
699 bool CachedResourceLoader::clientDefersImage(const URL&) const
701 return !m_imagesEnabled;
704 bool CachedResourceLoader::shouldDeferImageLoad(const URL& url) const
706 return clientDefersImage(url) || !m_autoLoadImages;
709 void CachedResourceLoader::reloadImagesIfNotDeferred()
711 DocumentResourceMap::iterator end = m_documentResources.end();
712 for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it) {
713 CachedResource* resource = it->value.get();
714 if (is<CachedImage>(*resource) && resource->stillNeedsLoad() && !clientDefersImage(resource->url()))
715 downcast<CachedImage>(*resource).load(this, defaultCachedResourceOptions());
719 CachePolicy CachedResourceLoader::cachePolicy(CachedResource::Type type) const
722 return CachePolicyVerify;
724 if (type != CachedResource::MainResource)
725 return frame()->loader().subresourceCachePolicy();
727 if (frame()->loader().loadType() == FrameLoadType::ReloadFromOrigin || frame()->loader().loadType() == FrameLoadType::Reload)
728 return CachePolicyReload;
729 return CachePolicyVerify;
732 void CachedResourceLoader::removeCachedResource(CachedResource* resource) const
735 DocumentResourceMap::iterator it = m_documentResources.find(resource->url());
736 if (it != m_documentResources.end())
737 ASSERT(it->value.get() == resource);
739 m_documentResources.remove(resource->url());
742 void CachedResourceLoader::loadDone(CachedResource* resource, bool shouldPerformPostLoadActions)
744 RefPtr<DocumentLoader> protectDocumentLoader(m_documentLoader);
745 RefPtr<Document> protectDocument(m_document);
747 #if ENABLE(RESOURCE_TIMING)
748 if (resource && resource->response().isHTTP() && ((!resource->errorOccurred() && !resource->wasCanceled()) || resource->response().httpStatusCode() == 304)) {
749 HashMap<CachedResource*, InitiatorInfo>::iterator initiatorIt = m_initiatorMap.find(resource);
750 if (initiatorIt != m_initiatorMap.end()) {
752 Document* initiatorDocument = document();
753 if (resource->type() == CachedResource::MainResource)
754 initiatorDocument = document()->parentDocument();
755 ASSERT(initiatorDocument);
756 const InitiatorInfo& info = initiatorIt->value;
757 initiatorDocument->domWindow()->performance()->addResourceTiming(info.name, initiatorDocument, resource->resourceRequest(), resource->response(), info.startTime, resource->loadFinishTime());
758 m_initiatorMap.remove(initiatorIt);
762 UNUSED_PARAM(resource);
763 #endif // ENABLE(RESOURCE_TIMING)
766 frame()->loader().loadDone();
768 if (shouldPerformPostLoadActions)
769 performPostLoadActions();
771 if (!m_garbageCollectDocumentResourcesTimer.isActive())
772 m_garbageCollectDocumentResourcesTimer.startOneShot(0);
775 // Garbage collecting m_documentResources is a workaround for the
776 // CachedResourceHandles on the RHS being strong references. Ideally this
777 // would be a weak map, however CachedResourceHandles perform additional
778 // bookkeeping on CachedResources, so instead pseudo-GC them -- when the
779 // reference count reaches 1, m_documentResources is the only reference, so
780 // remove it from the map.
781 void CachedResourceLoader::garbageCollectDocumentResourcesTimerFired(Timer& timer)
783 ASSERT_UNUSED(timer, &timer == &m_garbageCollectDocumentResourcesTimer);
784 garbageCollectDocumentResources();
787 void CachedResourceLoader::garbageCollectDocumentResources()
789 typedef Vector<String, 10> StringVector;
790 StringVector resourcesToDelete;
792 for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != m_documentResources.end(); ++it) {
793 if (it->value->hasOneHandle()) {
794 resourcesToDelete.append(it->key);
795 it->value->setOwningCachedResourceLoader(0);
799 for (StringVector::const_iterator it = resourcesToDelete.begin(); it != resourcesToDelete.end(); ++it)
800 m_documentResources.remove(*it);
803 void CachedResourceLoader::performPostLoadActions()
805 checkForPendingPreloads();
807 platformStrategies()->loaderStrategy()->resourceLoadScheduler()->servePendingRequests();
810 void CachedResourceLoader::incrementRequestCount(const CachedResource* res)
812 if (res->ignoreForRequestCount())
818 void CachedResourceLoader::decrementRequestCount(const CachedResource* res)
820 if (res->ignoreForRequestCount())
824 ASSERT(m_requestCount > -1);
827 void CachedResourceLoader::preload(CachedResource::Type type, CachedResourceRequest& request, const String& charset)
829 // We always preload resources on iOS. See <https://bugs.webkit.org/show_bug.cgi?id=91276>.
830 // FIXME: We should consider adding a setting to toggle aggressive preloading behavior as opposed
831 // to making this behavior specific to iOS.
833 bool hasRendering = m_document->body() && m_document->renderView();
834 bool canBlockParser = type == CachedResource::Script || type == CachedResource::CSSStyleSheet;
835 if (!hasRendering && !canBlockParser) {
836 // Don't preload subresources that can't block the parser before we have something to draw.
837 // This helps prevent preloads from delaying first display when bandwidth is limited.
838 PendingPreload pendingPreload = { type, request, charset };
839 m_pendingPreloads.append(pendingPreload);
843 requestPreload(type, request, charset);
846 void CachedResourceLoader::checkForPendingPreloads()
848 if (m_pendingPreloads.isEmpty() || !m_document->body() || !m_document->body()->renderer())
851 // We always preload resources on iOS. See <https://bugs.webkit.org/show_bug.cgi?id=91276>.
852 // So, we should never have any pending preloads.
853 // FIXME: We should look to avoid compiling this code entirely when building for iOS.
854 ASSERT_NOT_REACHED();
856 while (!m_pendingPreloads.isEmpty()) {
857 PendingPreload preload = m_pendingPreloads.takeFirst();
858 // 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).
859 if (!cachedResource(preload.m_request.resourceRequest().url()))
860 requestPreload(preload.m_type, preload.m_request, preload.m_charset);
862 m_pendingPreloads.clear();
865 void CachedResourceLoader::requestPreload(CachedResource::Type type, CachedResourceRequest& request, const String& charset)
868 if (type == CachedResource::Script || type == CachedResource::CSSStyleSheet)
869 encoding = charset.isEmpty() ? m_document->charset() : charset;
871 request.setCharset(encoding);
872 request.setForPreload(true);
874 CachedResourceHandle<CachedResource> resource = requestResource(type, request);
875 if (!resource || (m_preloads && m_preloads->contains(resource.get())))
877 resource->increasePreloadCount();
880 m_preloads = std::make_unique<ListHashSet<CachedResource*>>();
881 m_preloads->add(resource.get());
884 printf("PRELOADING %s\n", resource->url().latin1().data());
888 bool CachedResourceLoader::isPreloaded(const String& urlString) const
890 const URL& url = m_document->completeURL(urlString);
893 ListHashSet<CachedResource*>::iterator end = m_preloads->end();
894 for (ListHashSet<CachedResource*>::iterator it = m_preloads->begin(); it != end; ++it) {
895 CachedResource* resource = *it;
896 if (resource->url() == url)
901 Deque<PendingPreload>::const_iterator dequeEnd = m_pendingPreloads.end();
902 for (Deque<PendingPreload>::const_iterator it = m_pendingPreloads.begin(); it != dequeEnd; ++it) {
903 PendingPreload pendingPreload = *it;
904 if (pendingPreload.m_request.resourceRequest().url() == url)
910 void CachedResourceLoader::clearPreloads()
918 ListHashSet<CachedResource*>::iterator end = m_preloads->end();
919 for (ListHashSet<CachedResource*>::iterator it = m_preloads->begin(); it != end; ++it) {
920 CachedResource* res = *it;
921 res->decreasePreloadCount();
922 bool deleted = res->deleteIfPossible();
923 if (!deleted && res->preloadResult() == CachedResource::PreloadNotReferenced)
924 memoryCache()->remove(res);
926 m_preloads = nullptr;
929 void CachedResourceLoader::clearPendingPreloads()
931 m_pendingPreloads.clear();
935 void CachedResourceLoader::printPreloadStats()
937 unsigned scripts = 0;
938 unsigned scriptMisses = 0;
939 unsigned stylesheets = 0;
940 unsigned stylesheetMisses = 0;
942 unsigned imageMisses = 0;
943 ListHashSet<CachedResource*>::iterator end = m_preloads.end();
944 for (ListHashSet<CachedResource*>::iterator it = m_preloads.begin(); it != end; ++it) {
945 CachedResource* res = *it;
946 if (res->preloadResult() == CachedResource::PreloadNotReferenced)
947 printf("!! UNREFERENCED PRELOAD %s\n", res->url().latin1().data());
948 else if (res->preloadResult() == CachedResource::PreloadReferencedWhileComplete)
949 printf("HIT COMPLETE PRELOAD %s\n", res->url().latin1().data());
950 else if (res->preloadResult() == CachedResource::PreloadReferencedWhileLoading)
951 printf("HIT LOADING PRELOAD %s\n", res->url().latin1().data());
953 if (res->type() == CachedResource::Script) {
955 if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
957 } else if (res->type() == CachedResource::CSSStyleSheet) {
959 if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
963 if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
967 if (res->errorOccurred())
968 memoryCache()->remove(res);
970 res->decreasePreloadCount();
972 m_preloads = nullptr;
975 printf("SCRIPTS: %d (%d hits, hit rate %d%%)\n", scripts, scripts - scriptMisses, (scripts - scriptMisses) * 100 / scripts);
977 printf("STYLESHEETS: %d (%d hits, hit rate %d%%)\n", stylesheets, stylesheets - stylesheetMisses, (stylesheets - stylesheetMisses) * 100 / stylesheets);
979 printf("IMAGES: %d (%d hits, hit rate %d%%)\n", images, images - imageMisses, (images - imageMisses) * 100 / images);
983 const ResourceLoaderOptions& CachedResourceLoader::defaultCachedResourceOptions()
985 static ResourceLoaderOptions options(SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, AskClientForAllCredentials, DoSecurityCheck, UseDefaultOriginRestrictionsForType, DoNotIncludeCertificateInfo);