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