Unreviewed, rolling out r124000.
[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
680     typedef Vector<String, 10> StringVector;
681     StringVector resourcesToDelete;
682
683     for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != m_documentResources.end(); ++it) {
684         if (it->second->hasOneHandle()) {
685             resourcesToDelete.append(it->first);
686             it->second->setOwningCachedResourceLoader(0);
687         }
688     }
689
690     for (StringVector::const_iterator it = resourcesToDelete.begin(); it != resourcesToDelete.end(); ++it)
691         m_documentResources.remove(*it);
692 }
693
694 void CachedResourceLoader::performPostLoadActions()
695 {
696     checkForPendingPreloads();
697     resourceLoadScheduler()->servePendingRequests();
698 }
699
700 void CachedResourceLoader::notifyLoadedFromMemoryCache(CachedResource* resource)
701 {
702     if (!resource || !frame() || resource->status() != CachedResource::Cached)
703         return;
704
705     // FIXME: If the WebKit client changes or cancels the request, WebCore does not respect this and continues the load.
706     frame()->loader()->loadedResourceFromMemoryCache(resource);
707 }
708
709 void CachedResourceLoader::incrementRequestCount(const CachedResource* res)
710 {
711     if (res->ignoreForRequestCount())
712         return;
713
714     ++m_requestCount;
715 }
716
717 void CachedResourceLoader::decrementRequestCount(const CachedResource* res)
718 {
719     if (res->ignoreForRequestCount())
720         return;
721
722     --m_requestCount;
723     ASSERT(m_requestCount > -1);
724 }
725     
726 void CachedResourceLoader::preload(CachedResource::Type type, ResourceRequest& request, const String& charset, bool referencedFromBody)
727 {
728     // FIXME: Rip this out when we are sure it is no longer necessary (even for mobile).
729     UNUSED_PARAM(referencedFromBody);
730
731     bool delaySubresourceLoad = true;
732 #if PLATFORM(IOS)
733     delaySubresourceLoad = false;
734 #endif
735     if (delaySubresourceLoad) {
736         bool hasRendering = m_document->body() && m_document->body()->renderer();
737         bool canBlockParser = type == CachedResource::Script || type == CachedResource::CSSStyleSheet;
738         if (!hasRendering && !canBlockParser) {
739             // Don't preload subresources that can't block the parser before we have something to draw.
740             // This helps prevent preloads from delaying first display when bandwidth is limited.
741             PendingPreload pendingPreload = { type, request, charset };
742             m_pendingPreloads.append(pendingPreload);
743             return;
744         }
745     }
746     requestPreload(type, request, charset);
747 }
748
749 void CachedResourceLoader::checkForPendingPreloads() 
750 {
751     if (m_pendingPreloads.isEmpty() || !m_document->body() || !m_document->body()->renderer())
752         return;
753     while (!m_pendingPreloads.isEmpty()) {
754         PendingPreload preload = m_pendingPreloads.takeFirst();
755         // 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).
756         if (!cachedResource(preload.m_request.url()))
757             requestPreload(preload.m_type, preload.m_request, preload.m_charset);
758     }
759     m_pendingPreloads.clear();
760 }
761
762 void CachedResourceLoader::requestPreload(CachedResource::Type type, ResourceRequest& request, const String& charset)
763 {
764     String encoding;
765     if (type == CachedResource::Script || type == CachedResource::CSSStyleSheet)
766         encoding = charset.isEmpty() ? m_document->charset() : charset;
767
768     CachedResourceHandle<CachedResource> resource = requestResource(type, request, encoding, defaultCachedResourceOptions(), ResourceLoadPriorityUnresolved, true);
769     if (!resource || (m_preloads && m_preloads->contains(resource.get())))
770         return;
771     resource->increasePreloadCount();
772
773     if (!m_preloads)
774         m_preloads = adoptPtr(new ListHashSet<CachedResource*>);
775     m_preloads->add(resource.get());
776
777 #if PRELOAD_DEBUG
778     printf("PRELOADING %s\n",  resource->url().latin1().data());
779 #endif
780 }
781
782 bool CachedResourceLoader::isPreloaded(const String& urlString) const
783 {
784     const KURL& url = m_document->completeURL(urlString);
785
786     if (m_preloads) {
787         ListHashSet<CachedResource*>::iterator end = m_preloads->end();
788         for (ListHashSet<CachedResource*>::iterator it = m_preloads->begin(); it != end; ++it) {
789             CachedResource* resource = *it;
790             if (resource->url() == url)
791                 return true;
792         }
793     }
794
795     Deque<PendingPreload>::const_iterator dequeEnd = m_pendingPreloads.end();
796     for (Deque<PendingPreload>::const_iterator it = m_pendingPreloads.begin(); it != dequeEnd; ++it) {
797         PendingPreload pendingPreload = *it;
798         if (pendingPreload.m_request.url() == url)
799             return true;
800     }
801     return false;
802 }
803
804 void CachedResourceLoader::clearPreloads()
805 {
806 #if PRELOAD_DEBUG
807     printPreloadStats();
808 #endif
809     if (!m_preloads)
810         return;
811
812     ListHashSet<CachedResource*>::iterator end = m_preloads->end();
813     for (ListHashSet<CachedResource*>::iterator it = m_preloads->begin(); it != end; ++it) {
814         CachedResource* res = *it;
815         res->decreasePreloadCount();
816         if (res->canDelete() && !res->inCache())
817             delete res;
818         else if (res->preloadResult() == CachedResource::PreloadNotReferenced)
819             memoryCache()->remove(res);
820     }
821     m_preloads.clear();
822 }
823
824 void CachedResourceLoader::clearPendingPreloads()
825 {
826     m_pendingPreloads.clear();
827 }
828
829 #if PRELOAD_DEBUG
830 void CachedResourceLoader::printPreloadStats()
831 {
832     unsigned scripts = 0;
833     unsigned scriptMisses = 0;
834     unsigned stylesheets = 0;
835     unsigned stylesheetMisses = 0;
836     unsigned images = 0;
837     unsigned imageMisses = 0;
838     ListHashSet<CachedResource*>::iterator end = m_preloads.end();
839     for (ListHashSet<CachedResource*>::iterator it = m_preloads.begin(); it != end; ++it) {
840         CachedResource* res = *it;
841         if (res->preloadResult() == CachedResource::PreloadNotReferenced)
842             printf("!! UNREFERENCED PRELOAD %s\n", res->url().latin1().data());
843         else if (res->preloadResult() == CachedResource::PreloadReferencedWhileComplete)
844             printf("HIT COMPLETE PRELOAD %s\n", res->url().latin1().data());
845         else if (res->preloadResult() == CachedResource::PreloadReferencedWhileLoading)
846             printf("HIT LOADING PRELOAD %s\n", res->url().latin1().data());
847         
848         if (res->type() == CachedResource::Script) {
849             scripts++;
850             if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
851                 scriptMisses++;
852         } else if (res->type() == CachedResource::CSSStyleSheet) {
853             stylesheets++;
854             if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
855                 stylesheetMisses++;
856         } else {
857             images++;
858             if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
859                 imageMisses++;
860         }
861         
862         if (res->errorOccurred())
863             memoryCache()->remove(res);
864         
865         res->decreasePreloadCount();
866     }
867     m_preloads.clear();
868     
869     if (scripts)
870         printf("SCRIPTS: %d (%d hits, hit rate %d%%)\n", scripts, scripts - scriptMisses, (scripts - scriptMisses) * 100 / scripts);
871     if (stylesheets)
872         printf("STYLESHEETS: %d (%d hits, hit rate %d%%)\n", stylesheets, stylesheets - stylesheetMisses, (stylesheets - stylesheetMisses) * 100 / stylesheets);
873     if (images)
874         printf("IMAGES:  %d (%d hits, hit rate %d%%)\n", images, images - imageMisses, (images - imageMisses) * 100 / images);
875 }
876 #endif
877     
878 }