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