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