8ca1508b1851af951d45d2a7903b07dc2d04477a
[WebKit-https.git] / Source / WebCore / loader / cache / CachedResourceLoader.cpp
1 /*
2     Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
3     Copyright (C) 2001 Dirk Mueller (mueller@kde.org)
4     Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
5     Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
6     Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/
7
8     This library is free software; you can redistribute it and/or
9     modify it under the terms of the GNU Library General Public
10     License as published by the Free Software Foundation; either
11     version 2 of the License, or (at your option) any later version.
12
13     This library is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16     Library General Public License for more details.
17
18     You should have received a copy of the GNU Library General Public License
19     along with this library; see the file COPYING.LIB.  If not, write to
20     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21     Boston, MA 02110-1301, USA.
22
23     This class provides all functionality needed for loading images, style sheets and html
24     pages from the web. It has a memory cache for these objects.
25 */
26
27 #include "config.h"
28 #include "CachedResourceLoader.h"
29
30 #include "CachedCSSStyleSheet.h"
31 #include "CachedSVGDocument.h"
32 #include "CachedFont.h"
33 #include "CachedImage.h"
34 #include "CachedRawResource.h"
35 #include "CachedResourceRequest.h"
36 #include "CachedScript.h"
37 #include "CachedXSLStyleSheet.h"
38 #include "Console.h"
39 #include "ContentSecurityPolicy.h"
40 #include "DOMWindow.h"
41 #include "Document.h"
42 #include "DocumentLoader.h"
43 #include "Frame.h"
44 #include "FrameLoader.h"
45 #include "FrameLoaderClient.h"
46 #include "HTMLElement.h"
47 #include "HTMLFrameOwnerElement.h"
48 #include "LoaderStrategy.h"
49 #include "Logging.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"
56 #include "Settings.h"
57 #include <wtf/text/CString.h>
58 #include <wtf/text/WTFString.h>
59
60 #if ENABLE(VIDEO_TRACK)
61 #include "CachedTextTrack.h"
62 #endif
63
64 #if ENABLE(CSS_SHADERS)
65 #include "CachedShader.h"
66 #endif
67
68 #if ENABLE(RESOURCE_TIMING)
69 #include "Performance.h"
70 #endif
71
72 #define PRELOAD_DEBUG 0
73
74 namespace WebCore {
75
76 static CachedResource* createResource(CachedResource::Type type, ResourceRequest& request, const String& charset)
77 {
78     switch (type) {
79     case CachedResource::ImageResource:
80         return new CachedImage(request);
81     case CachedResource::CSSStyleSheet:
82         return new CachedCSSStyleSheet(request, charset);
83     case CachedResource::Script:
84         return new CachedScript(request, charset);
85 #if ENABLE(SVG)
86     case CachedResource::SVGDocumentResource:
87         return new CachedSVGDocument(request);
88 #endif
89     case CachedResource::FontResource:
90         return new CachedFont(request);
91     case CachedResource::RawResource:
92     case CachedResource::MainResource:
93         return new CachedRawResource(request, type);
94 #if ENABLE(XSLT)
95     case CachedResource::XSLStyleSheet:
96         return new CachedXSLStyleSheet(request);
97 #endif
98 #if ENABLE(LINK_PREFETCH)
99     case CachedResource::LinkPrefetch:
100         return new CachedResource(request, CachedResource::LinkPrefetch);
101     case CachedResource::LinkSubresource:
102         return new CachedResource(request, CachedResource::LinkSubresource);
103 #endif
104 #if ENABLE(VIDEO_TRACK)
105     case CachedResource::TextTrackResource:
106         return new CachedTextTrack(request);
107 #endif
108 #if ENABLE(CSS_SHADERS)
109     case CachedResource::ShaderResource:
110         return new CachedShader(request);
111 #endif
112     }
113     ASSERT_NOT_REACHED();
114     return 0;
115 }
116
117 CachedResourceLoader::CachedResourceLoader(DocumentLoader* documentLoader)
118     : m_document(0)
119     , m_documentLoader(documentLoader)
120     , m_requestCount(0)
121     , m_garbageCollectDocumentResourcesTimer(this, &CachedResourceLoader::garbageCollectDocumentResourcesTimerFired)
122     , m_autoLoadImages(true)
123     , m_imagesEnabled(true)
124     , m_allowStaleResources(false)
125 {
126 }
127
128 CachedResourceLoader::~CachedResourceLoader()
129 {
130     m_documentLoader = 0;
131     m_document = 0;
132
133     clearPreloads();
134     DocumentResourceMap::iterator end = m_documentResources.end();
135     for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it)
136         it->value->setOwningCachedResourceLoader(0);
137
138     // Make sure no requests still point to this CachedResourceLoader
139     ASSERT(m_requestCount == 0);
140 }
141
142 CachedResource* CachedResourceLoader::cachedResource(const String& resourceURL) const 
143 {
144     KURL url = m_document->completeURL(resourceURL);
145     return cachedResource(url); 
146 }
147
148 CachedResource* CachedResourceLoader::cachedResource(const KURL& resourceURL) const
149 {
150     KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(resourceURL);
151     return m_documentResources.get(url).get(); 
152 }
153
154 Frame* CachedResourceLoader::frame() const
155 {
156     return m_documentLoader ? m_documentLoader->frame() : 0;
157 }
158
159 CachedResourceHandle<CachedImage> CachedResourceLoader::requestImage(CachedResourceRequest& request)
160 {
161     if (Frame* f = frame()) {
162         if (f->loader().pageDismissalEventBeingDispatched() != FrameLoader::NoDismissal) {
163             KURL requestURL = request.resourceRequest().url();
164             if (requestURL.isValid() && canRequest(CachedResource::ImageResource, requestURL, request.options(), request.forPreload()))
165                 PingLoader::loadImage(f, requestURL);
166             return 0;
167         }
168     }
169     request.setDefer(clientDefersImage(request.resourceRequest().url()) ? CachedResourceRequest::DeferredByClient : CachedResourceRequest::NoDefer);
170     return static_cast<CachedImage*>(requestResource(CachedResource::ImageResource, request).get());
171 }
172
173 CachedResourceHandle<CachedFont> CachedResourceLoader::requestFont(CachedResourceRequest& request)
174 {
175     return static_cast<CachedFont*>(requestResource(CachedResource::FontResource, request).get());
176 }
177
178 #if ENABLE(VIDEO_TRACK)
179 CachedResourceHandle<CachedTextTrack> CachedResourceLoader::requestTextTrack(CachedResourceRequest& request)
180 {
181     return static_cast<CachedTextTrack*>(requestResource(CachedResource::TextTrackResource, request).get());
182 }
183 #endif
184
185 #if ENABLE(CSS_SHADERS)
186 CachedResourceHandle<CachedShader> CachedResourceLoader::requestShader(CachedResourceRequest& request)
187 {
188     return static_cast<CachedShader*>(requestResource(CachedResource::ShaderResource, request).get());
189 }
190 #endif
191
192 CachedResourceHandle<CachedCSSStyleSheet> CachedResourceLoader::requestCSSStyleSheet(CachedResourceRequest& request)
193 {
194     return static_cast<CachedCSSStyleSheet*>(requestResource(CachedResource::CSSStyleSheet, request).get());
195 }
196
197 CachedResourceHandle<CachedCSSStyleSheet> CachedResourceLoader::requestUserCSSStyleSheet(CachedResourceRequest& request)
198 {
199     KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(request.resourceRequest().url());
200
201 #if ENABLE(CACHE_PARTITIONING)
202     request.mutableResourceRequest().setCachePartition(document()->topOrigin()->cachePartition());
203 #endif
204
205     if (CachedResource* existing = memoryCache()->resourceForRequest(request.resourceRequest())) {
206         if (existing->type() == CachedResource::CSSStyleSheet)
207             return static_cast<CachedCSSStyleSheet*>(existing);
208         memoryCache()->remove(existing);
209     }
210     if (url.string() != request.resourceRequest().url())
211         request.mutableResourceRequest().setURL(url);
212
213     CachedResourceHandle<CachedCSSStyleSheet> userSheet = new CachedCSSStyleSheet(request.resourceRequest(), request.charset());
214
215     memoryCache()->add(userSheet.get());
216     // FIXME: loadResource calls setOwningCachedResourceLoader() if the resource couldn't be added to cache. Does this function need to call it, too?
217
218     userSheet->load(this, ResourceLoaderOptions(DoNotSendCallbacks, SniffContent, BufferData, AllowStoredCredentials, AskClientForAllCredentials, SkipSecurityCheck, UseDefaultOriginRestrictionsForType));
219     
220     return userSheet;
221 }
222
223 CachedResourceHandle<CachedScript> CachedResourceLoader::requestScript(CachedResourceRequest& request)
224 {
225     return static_cast<CachedScript*>(requestResource(CachedResource::Script, request).get());
226 }
227
228 #if ENABLE(XSLT)
229 CachedResourceHandle<CachedXSLStyleSheet> CachedResourceLoader::requestXSLStyleSheet(CachedResourceRequest& request)
230 {
231     return static_cast<CachedXSLStyleSheet*>(requestResource(CachedResource::XSLStyleSheet, request).get());
232 }
233 #endif
234
235 #if ENABLE(SVG)
236 CachedResourceHandle<CachedSVGDocument> CachedResourceLoader::requestSVGDocument(CachedResourceRequest& request)
237 {
238     return static_cast<CachedSVGDocument*>(requestResource(CachedResource::SVGDocumentResource, request).get());
239 }
240 #endif
241
242 #if ENABLE(LINK_PREFETCH)
243 CachedResourceHandle<CachedResource> CachedResourceLoader::requestLinkResource(CachedResource::Type type, CachedResourceRequest& request)
244 {
245     ASSERT(frame());
246     ASSERT(type == CachedResource::LinkPrefetch || type == CachedResource::LinkSubresource);
247     return requestResource(type, request);
248 }
249 #endif
250
251 CachedResourceHandle<CachedRawResource> CachedResourceLoader::requestRawResource(CachedResourceRequest& request)
252 {
253     return static_cast<CachedRawResource*>(requestResource(CachedResource::RawResource, request).get());
254 }
255
256 CachedResourceHandle<CachedRawResource> CachedResourceLoader::requestMainResource(CachedResourceRequest& request)
257 {
258     return static_cast<CachedRawResource*>(requestResource(CachedResource::MainResource, request).get());
259 }
260
261 bool CachedResourceLoader::checkInsecureContent(CachedResource::Type type, const KURL& url) const
262 {
263     switch (type) {
264     case CachedResource::Script:
265 #if ENABLE(XSLT)
266     case CachedResource::XSLStyleSheet:
267 #endif
268 #if ENABLE(SVG)
269     case CachedResource::SVGDocumentResource:
270 #endif
271     case CachedResource::CSSStyleSheet:
272         // These resource can inject script into the current document (Script,
273         // XSL) or exfiltrate the content of the current document (CSS).
274         if (Frame* f = frame())
275             if (!f->loader().mixedContentChecker().canRunInsecureContent(m_document->securityOrigin(), url))
276                 return false;
277         break;
278 #if ENABLE(VIDEO_TRACK)
279     case CachedResource::TextTrackResource:
280 #endif
281 #if ENABLE(CSS_SHADERS)
282     case CachedResource::ShaderResource:
283 #endif
284     case CachedResource::RawResource:
285     case CachedResource::ImageResource:
286     case CachedResource::FontResource: {
287         // These resources can corrupt only the frame's pixels.
288         if (Frame* f = frame()) {
289             Frame* top = f->tree().top();
290             if (!top->loader().mixedContentChecker().canDisplayInsecureContent(top->document()->securityOrigin(), url))
291                 return false;
292         }
293         break;
294     }
295     case CachedResource::MainResource:
296 #if ENABLE(LINK_PREFETCH)
297     case CachedResource::LinkPrefetch:
298     case CachedResource::LinkSubresource:
299         // Prefetch cannot affect the current document.
300 #endif
301         break;
302     }
303     return true;
304 }
305
306 bool CachedResourceLoader::canRequest(CachedResource::Type type, const KURL& url, const ResourceLoaderOptions& options, bool forPreload)
307 {
308     if (document() && !document()->securityOrigin()->canDisplay(url)) {
309         if (!forPreload)
310             FrameLoader::reportLocalLoadFailed(frame(), url.stringCenterEllipsizedToLength());
311         LOG(ResourceLoading, "CachedResourceLoader::requestResource URL was not allowed by SecurityOrigin::canDisplay");
312         return 0;
313     }
314
315     // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved.
316     bool shouldBypassMainWorldContentSecurityPolicy = (frame() && frame()->script().shouldBypassMainWorldContentSecurityPolicy());
317
318     // Some types of resources can be loaded only from the same origin.  Other
319     // types of resources, like Images, Scripts, and CSS, can be loaded from
320     // any URL.
321     switch (type) {
322     case CachedResource::MainResource:
323     case CachedResource::ImageResource:
324     case CachedResource::CSSStyleSheet:
325     case CachedResource::Script:
326     case CachedResource::FontResource:
327     case CachedResource::RawResource:
328 #if ENABLE(LINK_PREFETCH)
329     case CachedResource::LinkPrefetch:
330     case CachedResource::LinkSubresource:
331 #endif
332 #if ENABLE(VIDEO_TRACK)
333     case CachedResource::TextTrackResource:
334 #endif
335 #if ENABLE(CSS_SHADERS)
336     case CachedResource::ShaderResource:
337 #endif
338         if (options.requestOriginPolicy == RestrictToSameOrigin && !m_document->securityOrigin()->canRequest(url)) {
339             printAccessDeniedMessage(url);
340             return false;
341         }
342         break;
343 #if ENABLE(SVG)
344     case CachedResource::SVGDocumentResource:
345 #endif
346 #if ENABLE(XSLT)
347     case CachedResource::XSLStyleSheet:
348         if (!m_document->securityOrigin()->canRequest(url)) {
349             printAccessDeniedMessage(url);
350             return false;
351         }
352 #endif
353         break;
354     }
355
356     switch (type) {
357 #if ENABLE(XSLT)
358     case CachedResource::XSLStyleSheet:
359         if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowScriptFromSource(url))
360             return false;
361         break;
362 #endif
363     case CachedResource::Script:
364         if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowScriptFromSource(url))
365             return false;
366
367         if (frame()) {
368             if (!frame()->loader().client().allowScriptFromSource(frame()->settings().isScriptEnabled(), url)) {
369                 frame()->loader().client().didNotAllowScript();
370                 return false;
371             }
372         }
373         break;
374 #if ENABLE(CSS_SHADERS)
375     case CachedResource::ShaderResource:
376         // Since shaders are referenced from CSS Styles use the same rules here.
377 #endif
378     case CachedResource::CSSStyleSheet:
379         if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowStyleFromSource(url))
380             return false;
381         break;
382 #if ENABLE(SVG)
383     case CachedResource::SVGDocumentResource:
384 #endif
385     case CachedResource::ImageResource:
386         if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowImageFromSource(url))
387             return false;
388         break;
389     case CachedResource::FontResource: {
390         if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowFontFromSource(url))
391             return false;
392         break;
393     }
394     case CachedResource::MainResource:
395     case CachedResource::RawResource:
396 #if ENABLE(LINK_PREFETCH)
397     case CachedResource::LinkPrefetch:
398     case CachedResource::LinkSubresource:
399 #endif
400         break;
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))
406             return false;
407         break;
408 #endif
409     }
410
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.
414
415     // FIXME: Should we consider forPreload here?
416     if (!checkInsecureContent(type, url))
417         return false;
418
419     return true;
420 }
421
422 bool CachedResourceLoader::shouldContinueAfterNotifyingLoadedFromMemoryCache(CachedResource* resource)
423 {
424     if (!resource || !frame() || resource->status() != CachedResource::Cached)
425         return true;
426
427     ResourceRequest newRequest;
428     frame()->loader().loadedResourceFromMemoryCache(resource, newRequest);
429     
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();
433 }
434
435 CachedResourceHandle<CachedResource> CachedResourceLoader::requestResource(CachedResource::Type type, CachedResourceRequest& request)
436 {
437     KURL url = request.resourceRequest().url();
438     
439     LOG(ResourceLoading, "CachedResourceLoader::requestResource '%s', charset '%s', priority=%d, forPreload=%u", url.stringCenterEllipsizedToLength().latin1().data(), request.charset().latin1().data(), request.priority(), request.forPreload());
440     
441     // If only the fragment identifiers differ, it is the same resource.
442     url = MemoryCache::removeFragmentIdentifierIfNeeded(url);
443
444     if (!url.isValid())
445         return 0;
446
447     if (!canRequest(type, url, request.options(), request.forPreload()))
448         return 0;
449
450     if (Frame* f = frame())
451         f->loader().client().dispatchWillRequestResource(&request);
452
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);
458         }
459     }
460
461     // See if we can use an existing resource from the cache.
462     CachedResourceHandle<CachedResource> resource;
463 #if ENABLE(CACHE_PARTITIONING)
464     if (document())
465         request.mutableResourceRequest().setCachePartition(document()->topOrigin()->cachePartition());
466 #endif
467
468     resource = memoryCache()->resourceForRequest(request.resourceRequest());
469
470     const RevalidationPolicy policy = determineRevalidationPolicy(type, request.mutableResourceRequest(), request.forPreload(), resource.get(), request.defer());
471     switch (policy) {
472     case Reload:
473         memoryCache()->remove(resource.get());
474         // Fall through
475     case Load:
476         resource = loadResource(type, request, request.charset());
477         break;
478     case Revalidate:
479         resource = revalidateResource(request, resource.get());
480         break;
481     case Use:
482         if (!shouldContinueAfterNotifyingLoadedFromMemoryCache(resource.get()))
483             return 0;
484         memoryCache()->resourceAccessed(resource.get());
485         break;
486     }
487
488     if (!resource)
489         return 0;
490
491     if (!request.forPreload() || policy != Use)
492         resource->setLoadPriority(request.priority());
493
494     if ((policy != Use || resource->stillNeedsLoad()) && CachedResourceRequest::NoDefer == request.defer()) {
495         resource->load(this, request.options());
496
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());
501             return 0;
502         }
503     }
504
505     if (!request.resourceRequest().url().protocolIsData())
506         m_validatedURLs.add(request.resourceRequest().url());
507
508     ASSERT(resource->url() == url.string());
509     m_documentResources.set(resource->url(), resource);
510     return resource;
511 }
512
513 CachedResourceHandle<CachedResource> CachedResourceLoader::revalidateResource(const CachedResourceRequest& request, CachedResource* resource)
514 {
515     ASSERT(resource);
516     ASSERT(resource->inCache());
517     ASSERT(!memoryCache()->disabled());
518     ASSERT(resource->canUseCacheValidator());
519     ASSERT(!resource->resourceToRevalidate());
520     
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());
524     
525     LOG(ResourceLoading, "Resource %p created to revalidate %p", newResource.get(), resource);
526     newResource->setResourceToRevalidate(resource);
527     
528     memoryCache()->remove(resource);
529     memoryCache()->add(newResource.get());
530 #if ENABLE(RESOURCE_TIMING)
531     storeResourceTimingInitiatorInformation(resource, request);
532 #else
533     UNUSED_PARAM(request);
534 #endif
535     return newResource;
536 }
537
538 CachedResourceHandle<CachedResource> CachedResourceLoader::loadResource(CachedResource::Type type, CachedResourceRequest& request, const String& charset)
539 {
540     ASSERT(!memoryCache()->resourceForRequest(request.resourceRequest()));
541
542     LOG(ResourceLoading, "Loading CachedResource for '%s'.", request.resourceRequest().url().stringCenterEllipsizedToLength().latin1().data());
543
544     CachedResourceHandle<CachedResource> resource = createResource(type, request.mutableResourceRequest(), charset);
545
546     if (!memoryCache()->add(resource.get()))
547         resource->setOwningCachedResourceLoader(this);
548 #if ENABLE(RESOURCE_TIMING)
549     storeResourceTimingInitiatorInformation(resource, request);
550 #endif
551     return resource;
552 }
553
554 #if ENABLE(RESOURCE_TIMING)
555 void CachedResourceLoader::storeResourceTimingInitiatorInformation(const CachedResourceHandle<CachedResource>& resource, const CachedResourceRequest& request)
556 {
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);
562         }
563     } else {
564         InitiatorInfo info = { request.initiatorName(), monotonicallyIncreasingTime() };
565         m_initiatorMap.add(resource.get(), info);
566     }
567 }
568 #endif // ENABLE(RESOURCE_TIMING)
569
570 CachedResourceLoader::RevalidationPolicy CachedResourceLoader::determineRevalidationPolicy(CachedResource::Type type, ResourceRequest& request, bool forPreload, CachedResource* existingResource, CachedResourceRequest::DeferOption defer) const
571 {
572     if (!existingResource)
573         return Load;
574
575     // We already have a preload going for this URL.
576     if (forPreload && existingResource->isPreloaded())
577         return Use;
578
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.");
582         return Reload;
583     }
584
585     if (!existingResource->canReuse(request))
586         return Reload;
587
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())
592         return Reload;
593
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)
597         return Reload;
598     
599     // Don't reload resources while pasting.
600     if (m_allowStaleResources)
601         return Use;
602     
603     // Alwaus use preloads.
604     if (existingResource->isPreloaded())
605         return Use;
606     
607     // CachePolicyHistoryBuffer uses the cache no matter what.
608     if (cachePolicy(type) == CachePolicyHistoryBuffer)
609         return Use;
610
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.");
614         return Reload;
615     }
616
617     // If credentials were sent with the previous request and won't be
618     // with this one, or vice versa, re-fetch the resource.
619     //
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.");
625         return Reload;
626     }
627
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()))
630         return Use;
631
632     // CachePolicyReload always reloads
633     if (cachePolicy(type) == CachePolicyReload) {
634         LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to CachePolicyReload.");
635         return Reload;
636     }
637     
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");
641         return Reload;
642     }
643     
644     // For resources that are not yet loaded we ignore the cache policy.
645     if (existingResource->isLoading())
646         return Use;
647
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())
652             return Revalidate;
653         
654         // No, must reload.
655         LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to missing cache validators.");            
656         return Reload;
657     }
658
659     return Use;
660 }
661
662 void CachedResourceLoader::printAccessDeniedMessage(const KURL& url) const
663 {
664     if (url.isNull())
665         return;
666
667     if (!frame())
668         return;
669
670     String message;
671     if (!m_document || m_document->url().isNull())
672         message = "Unsafe attempt to load URL " + url.stringCenterEllipsizedToLength() + '.';
673     else
674         message = "Unsafe attempt to load URL " + url.stringCenterEllipsizedToLength() + " from frame with URL " + m_document->url().stringCenterEllipsizedToLength() + ". Domains, protocols and ports must match.\n";
675
676     frame()->document()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, message);
677 }
678
679 void CachedResourceLoader::setAutoLoadImages(bool enable)
680 {
681     if (enable == m_autoLoadImages)
682         return;
683
684     m_autoLoadImages = enable;
685
686     if (!m_autoLoadImages)
687         return;
688
689     reloadImagesIfNotDeferred();
690 }
691
692 void CachedResourceLoader::setImagesEnabled(bool enable)
693 {
694     if (enable == m_imagesEnabled)
695         return;
696
697     m_imagesEnabled = enable;
698
699     if (!m_imagesEnabled)
700         return;
701
702     reloadImagesIfNotDeferred();
703 }
704
705 bool CachedResourceLoader::clientDefersImage(const KURL& url) const
706 {
707     return frame() && !frame()->loader().client().allowImage(m_imagesEnabled, url);
708 }
709
710 bool CachedResourceLoader::shouldDeferImageLoad(const KURL& url) const
711 {
712     return clientDefersImage(url) || !m_autoLoadImages;
713 }
714
715 void CachedResourceLoader::reloadImagesIfNotDeferred()
716 {
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());
722     }
723 }
724
725 CachePolicy CachedResourceLoader::cachePolicy(CachedResource::Type type) const
726 {
727     if (!frame())
728         return CachePolicyVerify;
729
730     if (type != CachedResource::MainResource)
731         return frame()->loader().subresourceCachePolicy();
732     
733     if (frame()->loader().loadType() == FrameLoadTypeReloadFromOrigin || frame()->loader().loadType() == FrameLoadTypeReload)
734         return CachePolicyReload;
735     return CachePolicyVerify;
736 }
737
738 void CachedResourceLoader::removeCachedResource(CachedResource* resource) const
739 {
740 #ifndef NDEBUG
741     DocumentResourceMap::iterator it = m_documentResources.find(resource->url());
742     if (it != m_documentResources.end())
743         ASSERT(it->value.get() == resource);
744 #endif
745     m_documentResources.remove(resource->url());
746 }
747
748 void CachedResourceLoader::loadDone(CachedResource* resource)
749 {
750     RefPtr<DocumentLoader> protectDocumentLoader(m_documentLoader);
751     RefPtr<Document> protectDocument(m_document);
752
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()) {
757             ASSERT(document());
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);
765         }
766     }
767 #else
768     UNUSED_PARAM(resource);
769 #endif // ENABLE(RESOURCE_TIMING)
770
771     if (frame())
772         frame()->loader().loadDone();
773     performPostLoadActions();
774
775     if (!m_garbageCollectDocumentResourcesTimer.isActive())
776         m_garbageCollectDocumentResourcesTimer.startOneShot(0);
777 }
778
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)
786 {
787     ASSERT_UNUSED(timer, timer == &m_garbageCollectDocumentResourcesTimer);
788     garbageCollectDocumentResources();
789 }
790
791 void CachedResourceLoader::garbageCollectDocumentResources()
792 {
793     typedef Vector<String, 10> StringVector;
794     StringVector resourcesToDelete;
795
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);
800         }
801     }
802
803     for (StringVector::const_iterator it = resourcesToDelete.begin(); it != resourcesToDelete.end(); ++it)
804         m_documentResources.remove(*it);
805 }
806
807 void CachedResourceLoader::performPostLoadActions()
808 {
809     checkForPendingPreloads();
810
811     platformStrategies()->loaderStrategy()->resourceLoadScheduler()->servePendingRequests();
812 }
813
814 void CachedResourceLoader::incrementRequestCount(const CachedResource* res)
815 {
816     if (res->ignoreForRequestCount())
817         return;
818
819     ++m_requestCount;
820 }
821
822 void CachedResourceLoader::decrementRequestCount(const CachedResource* res)
823 {
824     if (res->ignoreForRequestCount())
825         return;
826
827     --m_requestCount;
828     ASSERT(m_requestCount > -1);
829 }
830
831 void CachedResourceLoader::preload(CachedResource::Type type, CachedResourceRequest& request, const String& charset)
832 {
833     bool delaySubresourceLoad = true;
834 #if PLATFORM(IOS)
835     delaySubresourceLoad = false;
836 #endif
837     if (delaySubresourceLoad) {
838         bool hasRendering = m_document->body() && m_document->body()->renderer();
839         bool canBlockParser = type == CachedResource::Script || type == CachedResource::CSSStyleSheet;
840         if (!hasRendering && !canBlockParser) {
841             // Don't preload subresources that can't block the parser before we have something to draw.
842             // This helps prevent preloads from delaying first display when bandwidth is limited.
843             PendingPreload pendingPreload = { type, request, charset };
844             m_pendingPreloads.append(pendingPreload);
845             return;
846         }
847     }
848     requestPreload(type, request, charset);
849 }
850
851 void CachedResourceLoader::checkForPendingPreloads() 
852 {
853     if (m_pendingPreloads.isEmpty() || !m_document->body() || !m_document->body()->renderer())
854         return;
855     while (!m_pendingPreloads.isEmpty()) {
856         PendingPreload preload = m_pendingPreloads.takeFirst();
857         // 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).
858         if (!cachedResource(preload.m_request.resourceRequest().url()))
859             requestPreload(preload.m_type, preload.m_request, preload.m_charset);
860     }
861     m_pendingPreloads.clear();
862 }
863
864 void CachedResourceLoader::requestPreload(CachedResource::Type type, CachedResourceRequest& request, const String& charset)
865 {
866     String encoding;
867     if (type == CachedResource::Script || type == CachedResource::CSSStyleSheet)
868         encoding = charset.isEmpty() ? m_document->charset() : charset;
869
870     request.setCharset(encoding);
871     request.setForPreload(true);
872
873     CachedResourceHandle<CachedResource> resource = requestResource(type, request);
874     if (!resource || (m_preloads && m_preloads->contains(resource.get())))
875         return;
876     resource->increasePreloadCount();
877
878     if (!m_preloads)
879         m_preloads = adoptPtr(new ListHashSet<CachedResource*>);
880     m_preloads->add(resource.get());
881
882 #if PRELOAD_DEBUG
883     printf("PRELOADING %s\n",  resource->url().latin1().data());
884 #endif
885 }
886
887 bool CachedResourceLoader::isPreloaded(const String& urlString) const
888 {
889     const KURL& url = m_document->completeURL(urlString);
890
891     if (m_preloads) {
892         ListHashSet<CachedResource*>::iterator end = m_preloads->end();
893         for (ListHashSet<CachedResource*>::iterator it = m_preloads->begin(); it != end; ++it) {
894             CachedResource* resource = *it;
895             if (resource->url() == url)
896                 return true;
897         }
898     }
899
900     Deque<PendingPreload>::const_iterator dequeEnd = m_pendingPreloads.end();
901     for (Deque<PendingPreload>::const_iterator it = m_pendingPreloads.begin(); it != dequeEnd; ++it) {
902         PendingPreload pendingPreload = *it;
903         if (pendingPreload.m_request.resourceRequest().url() == url)
904             return true;
905     }
906     return false;
907 }
908
909 void CachedResourceLoader::clearPreloads()
910 {
911 #if PRELOAD_DEBUG
912     printPreloadStats();
913 #endif
914     if (!m_preloads)
915         return;
916
917     ListHashSet<CachedResource*>::iterator end = m_preloads->end();
918     for (ListHashSet<CachedResource*>::iterator it = m_preloads->begin(); it != end; ++it) {
919         CachedResource* res = *it;
920         res->decreasePreloadCount();
921         bool deleted = res->deleteIfPossible();
922         if (!deleted && res->preloadResult() == CachedResource::PreloadNotReferenced)
923             memoryCache()->remove(res);
924     }
925     m_preloads.clear();
926 }
927
928 void CachedResourceLoader::clearPendingPreloads()
929 {
930     m_pendingPreloads.clear();
931 }
932
933 #if PRELOAD_DEBUG
934 void CachedResourceLoader::printPreloadStats()
935 {
936     unsigned scripts = 0;
937     unsigned scriptMisses = 0;
938     unsigned stylesheets = 0;
939     unsigned stylesheetMisses = 0;
940     unsigned images = 0;
941     unsigned imageMisses = 0;
942     ListHashSet<CachedResource*>::iterator end = m_preloads.end();
943     for (ListHashSet<CachedResource*>::iterator it = m_preloads.begin(); it != end; ++it) {
944         CachedResource* res = *it;
945         if (res->preloadResult() == CachedResource::PreloadNotReferenced)
946             printf("!! UNREFERENCED PRELOAD %s\n", res->url().latin1().data());
947         else if (res->preloadResult() == CachedResource::PreloadReferencedWhileComplete)
948             printf("HIT COMPLETE PRELOAD %s\n", res->url().latin1().data());
949         else if (res->preloadResult() == CachedResource::PreloadReferencedWhileLoading)
950             printf("HIT LOADING PRELOAD %s\n", res->url().latin1().data());
951         
952         if (res->type() == CachedResource::Script) {
953             scripts++;
954             if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
955                 scriptMisses++;
956         } else if (res->type() == CachedResource::CSSStyleSheet) {
957             stylesheets++;
958             if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
959                 stylesheetMisses++;
960         } else {
961             images++;
962             if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
963                 imageMisses++;
964         }
965         
966         if (res->errorOccurred())
967             memoryCache()->remove(res);
968         
969         res->decreasePreloadCount();
970     }
971     m_preloads.clear();
972     
973     if (scripts)
974         printf("SCRIPTS: %d (%d hits, hit rate %d%%)\n", scripts, scripts - scriptMisses, (scripts - scriptMisses) * 100 / scripts);
975     if (stylesheets)
976         printf("STYLESHEETS: %d (%d hits, hit rate %d%%)\n", stylesheets, stylesheets - stylesheetMisses, (stylesheets - stylesheetMisses) * 100 / stylesheets);
977     if (images)
978         printf("IMAGES:  %d (%d hits, hit rate %d%%)\n", images, images - imageMisses, (images - imageMisses) * 100 / images);
979 }
980 #endif
981
982 const ResourceLoaderOptions& CachedResourceLoader::defaultCachedResourceOptions()
983 {
984     static ResourceLoaderOptions options(SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, AskClientForAllCredentials, DoSecurityCheck, UseDefaultOriginRestrictionsForType);
985     return options;
986 }
987
988 }