2011-04-07 Adam Barth <abarth@webkit.org>
[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, 2008 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 "CachedFont.h"
32 #include "CachedImage.h"
33 #include "CachedResourceRequest.h"
34 #include "CachedScript.h"
35 #include "CachedXSLStyleSheet.h"
36 #include "Console.h"
37 #include "ContentSecurityPolicy.h"
38 #include "DOMWindow.h"
39 #include "Document.h"
40 #include "Frame.h"
41 #include "FrameLoader.h"
42 #include "FrameLoaderClient.h"
43 #include "HTMLElement.h"
44 #include "Logging.h"
45 #include "MemoryCache.h"
46 #include "PingLoader.h"
47 #include "ResourceLoadScheduler.h"
48 #include "SecurityOrigin.h"
49 #include "Settings.h"
50 #include <wtf/UnusedParam.h>
51 #include <wtf/text/CString.h>
52 #include <wtf/text/StringConcatenate.h>
53
54 #define PRELOAD_DEBUG 0
55
56 namespace WebCore {
57
58 static CachedResource* createResource(CachedResource::Type type, const KURL& url, const String& charset)
59 {
60     switch (type) {
61     case CachedResource::ImageResource:
62         return new CachedImage(url.string());
63     case CachedResource::CSSStyleSheet:
64         return new CachedCSSStyleSheet(url.string(), charset);
65     case CachedResource::Script:
66         return new CachedScript(url.string(), charset);
67     case CachedResource::FontResource:
68         return new CachedFont(url.string());
69 #if ENABLE(XSLT)
70     case CachedResource::XSLStyleSheet:
71         return new CachedXSLStyleSheet(url.string());
72 #endif
73 #if ENABLE(LINK_PREFETCH)
74     case CachedResource::LinkPrefetch:
75         return new CachedResource(url.string(), CachedResource::LinkPrefetch);
76 #endif
77     }
78     ASSERT_NOT_REACHED();
79     return 0;
80 }
81
82 CachedResourceLoader::CachedResourceLoader(Document* document)
83     : m_document(document)
84     , m_requestCount(0)
85     , m_loadDoneActionTimer(this, &CachedResourceLoader::loadDoneActionTimerFired)
86     , m_autoLoadImages(true)
87     , m_loadFinishing(false)
88     , m_allowStaleResources(false)
89 {
90 }
91
92 CachedResourceLoader::~CachedResourceLoader()
93 {
94     m_document = 0;
95
96     cancelRequests();
97     clearPreloads();
98     DocumentResourceMap::iterator end = m_documentResources.end();
99     for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it)
100         it->second->setOwningCachedResourceLoader(0);
101
102     // Make sure no requests still point to this CachedResourceLoader
103     ASSERT(m_requestCount == 0);
104 }
105
106 CachedResource* CachedResourceLoader::cachedResource(const String& resourceURL) const 
107 {
108     KURL url = m_document->completeURL(resourceURL);
109     return cachedResource(url); 
110 }
111
112 CachedResource* CachedResourceLoader::cachedResource(const KURL& resourceURL) const
113 {
114     KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(resourceURL);
115     return m_documentResources.get(url).get(); 
116 }
117
118 Frame* CachedResourceLoader::frame() const
119 {
120     return m_document ? m_document->frame() : 0;
121 }
122
123 CachedImage* CachedResourceLoader::requestImage(const String& url)
124 {
125     if (Frame* f = frame()) {
126         Settings* settings = f->settings();
127         if (!f->loader()->client()->allowImages(!settings || settings->areImagesEnabled()))
128             return 0;
129
130         if (f->loader()->pageDismissalEventBeingDispatched()) {
131             KURL completeURL = m_document->completeURL(url);
132             if (completeURL.isValid() && canRequest(CachedResource::ImageResource, completeURL))
133                 PingLoader::loadImage(f, completeURL);
134             return 0;
135         }
136     }
137     CachedImage* resource = static_cast<CachedImage*>(requestResource(CachedResource::ImageResource, url, String()));
138     if (autoLoadImages() && resource && resource->stillNeedsLoad()) {
139         resource->setLoading(true);
140         load(resource, true);
141     }
142     return resource;
143 }
144
145 CachedFont* CachedResourceLoader::requestFont(const String& url)
146 {
147     return static_cast<CachedFont*>(requestResource(CachedResource::FontResource, url, String()));
148 }
149
150 CachedCSSStyleSheet* CachedResourceLoader::requestCSSStyleSheet(const String& url, const String& charset, ResourceLoadPriority priority)
151 {
152     return static_cast<CachedCSSStyleSheet*>(requestResource(CachedResource::CSSStyleSheet, url, charset, priority));
153 }
154
155 CachedCSSStyleSheet* CachedResourceLoader::requestUserCSSStyleSheet(const String& requestURL, const String& charset)
156 {
157     KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(KURL(KURL(), requestURL));
158
159     if (CachedResource* existing = memoryCache()->resourceForURL(url)) {
160         if (existing->type() == CachedResource::CSSStyleSheet)
161             return static_cast<CachedCSSStyleSheet*>(existing);
162         memoryCache()->remove(existing);
163     }
164     CachedCSSStyleSheet* userSheet = new CachedCSSStyleSheet(url, charset);
165     
166     bool inCache = memoryCache()->add(userSheet);
167     if (!inCache)
168         userSheet->setInCache(true);
169     
170     userSheet->load(this, /*incremental*/ false, SkipSecurityCheck, /*sendResourceLoadCallbacks*/ false);
171
172     if (!inCache)
173         userSheet->setInCache(false);
174     
175     return userSheet;
176 }
177
178 CachedScript* CachedResourceLoader::requestScript(const String& url, const String& charset)
179 {
180     return static_cast<CachedScript*>(requestResource(CachedResource::Script, url, charset));
181 }
182
183 #if ENABLE(XSLT)
184 CachedXSLStyleSheet* CachedResourceLoader::requestXSLStyleSheet(const String& url)
185 {
186     return static_cast<CachedXSLStyleSheet*>(requestResource(CachedResource::XSLStyleSheet, url, String()));
187 }
188 #endif
189
190 #if ENABLE(LINK_PREFETCH)
191 CachedResource* CachedResourceLoader::requestLinkPrefetch(const String& url)
192 {
193     ASSERT(frame());
194     return requestResource(CachedResource::LinkPrefetch, url, String());
195 }
196 #endif
197
198 bool CachedResourceLoader::canRequest(CachedResource::Type type, const KURL& url)
199 {
200     // Some types of resources can be loaded only from the same origin.  Other
201     // types of resources, like Images, Scripts, and CSS, can be loaded from
202     // any URL.
203     switch (type) {
204     case CachedResource::ImageResource:
205     case CachedResource::CSSStyleSheet:
206     case CachedResource::Script:
207     case CachedResource::FontResource:
208 #if ENABLE(LINK_PREFETCH)
209     case CachedResource::LinkPrefetch:
210 #endif
211         // These types of resources can be loaded from any origin.
212         // FIXME: Are we sure about CachedResource::FontResource?
213         break;
214 #if ENABLE(XSLT)
215     case CachedResource::XSLStyleSheet:
216         if (!m_document->securityOrigin()->canRequest(url)) {
217             printAccessDeniedMessage(url);
218             return false;
219         }
220         break;
221 #endif
222     }
223
224     // Given that the load is allowed by the same-origin policy, we should
225     // check whether the load passes the mixed-content policy.
226     //
227     // Note: Currently, we always allow mixed content, but we generate a
228     //       callback to the FrameLoaderClient in case the embedder wants to
229     //       update any security indicators.
230     // 
231     switch (type) {
232     case CachedResource::Script:
233 #if ENABLE(XSLT)
234     case CachedResource::XSLStyleSheet:
235 #endif
236         // These resource can inject script into the current document.
237         if (Frame* f = frame())
238             f->loader()->checkIfRunInsecureContent(m_document->securityOrigin(), url);
239         break;
240     case CachedResource::ImageResource:
241     case CachedResource::CSSStyleSheet:
242     case CachedResource::FontResource: {
243         // These resources can corrupt only the frame's pixels.
244         if (Frame* f = frame()) {
245             Frame* top = f->tree()->top();
246             top->loader()->checkIfDisplayInsecureContent(top->document()->securityOrigin(), url);
247         }
248         break;
249     }
250 #if ENABLE(LINK_PREFETCH)
251     case CachedResource::LinkPrefetch:
252         // Prefetch cannot affect the current document.
253         break;
254 #endif
255     }
256     // FIXME: Consider letting the embedder block mixed content loads.
257
258     switch (type) {
259     case CachedResource::Script:
260         if (!m_document->contentSecurityPolicy()->allowScriptFromSource(url))
261             return false;
262         break;
263 #if ENABLE(XSLT)
264     case CachedResource::XSLStyleSheet:
265 #endif
266     case CachedResource::CSSStyleSheet:
267         if (!m_document->contentSecurityPolicy()->allowStyleFromSource(url))
268             return false;
269         break;
270     case CachedResource::ImageResource:
271         if (!m_document->contentSecurityPolicy()->allowImageFromSource(url))
272             return false;
273         break;
274     case CachedResource::FontResource: {
275         if (!m_document->contentSecurityPolicy()->allowFontFromSource(url))
276             return false;
277         break;
278     }
279 #if ENABLE(LINK_PREFETCH)
280     case CachedResource::LinkPrefetch:
281         break;
282 #endif
283     }
284
285     return true;
286 }
287
288 CachedResource* CachedResourceLoader::requestResource(CachedResource::Type type, const String& resourceURL, const String& charset, ResourceLoadPriority priority, bool forPreload)
289 {
290     KURL url = m_document->completeURL(resourceURL);
291     
292     LOG(ResourceLoading, "CachedResourceLoader::requestResource '%s', charset '%s', priority=%d, forPreload=%u", url.string().latin1().data(), charset.latin1().data(), priority, forPreload);
293     
294     // If only the fragment identifiers differ, it is the same resource.
295     url = MemoryCache::removeFragmentIdentifierIfNeeded(url);
296
297     if (!url.isValid())
298         return 0;
299     
300     if (!canRequest(type, url))
301         return 0;
302
303     // FIXME: Figure out what is the correct way to merge this security check with the one above.
304     if (!document()->securityOrigin()->canDisplay(url)) {
305         if (!forPreload)
306             FrameLoader::reportLocalLoadFailed(document()->frame(), url.string());
307         LOG(ResourceLoading, "CachedResourceLoader::requestResource URL was not allowed by SecurityOrigin::canDisplay");
308         return 0;
309     }
310
311     if (memoryCache()->disabled()) {
312         DocumentResourceMap::iterator it = m_documentResources.find(url.string());
313         if (it != m_documentResources.end()) {
314             it->second->setOwningCachedResourceLoader(0);
315             m_documentResources.remove(it);
316         }
317     }
318
319     // See if we can use an existing resource from the cache.
320     CachedResource* resource = memoryCache()->resourceForURL(url);
321
322     switch (determineRevalidationPolicy(type, forPreload, resource)) {
323     case Load:
324         resource = loadResource(type, url, charset, priority);
325         break;
326     case Reload:
327         memoryCache()->remove(resource);
328         resource = loadResource(type, url, charset, priority);
329         break;
330     case Revalidate:
331         resource = revalidateResource(resource, priority);
332         break;
333     case Use:
334         memoryCache()->resourceAccessed(resource);
335         notifyLoadedFromMemoryCache(resource);
336         break;
337     }
338
339     if (!resource)
340         return 0;
341
342     ASSERT(resource->url() == url.string());
343     m_documentResources.set(resource->url(), resource);
344     
345     return resource;
346 }
347     
348 CachedResource* CachedResourceLoader::revalidateResource(CachedResource* resource, ResourceLoadPriority priority)
349 {
350     ASSERT(resource);
351     ASSERT(resource->inCache());
352     ASSERT(!memoryCache()->disabled());
353     ASSERT(resource->canUseCacheValidator());
354     ASSERT(!resource->resourceToRevalidate());
355     
356     // Copy the URL out of the resource to be revalidated in case it gets deleted by the remove() call below.
357     String url = resource->url();
358     CachedResource* newResource = createResource(resource->type(), KURL(ParsedURLString, url), resource->encoding());
359     
360     LOG(ResourceLoading, "Resource %p created to revalidate %p", newResource, resource);
361     newResource->setResourceToRevalidate(resource);
362     
363     memoryCache()->remove(resource);
364     memoryCache()->add(newResource);
365     
366     newResource->setLoadPriority(priority);
367     newResource->load(this);
368     
369     m_validatedURLs.add(url);
370     return newResource;
371 }
372
373 CachedResource* CachedResourceLoader::loadResource(CachedResource::Type type, const KURL& url, const String& charset, ResourceLoadPriority priority)
374 {
375     ASSERT(!memoryCache()->resourceForURL(url));
376     
377     LOG(ResourceLoading, "Loading CachedResource for '%s'.", url.string().latin1().data());
378     
379     CachedResource* resource = createResource(type, url, charset);
380     
381     bool inCache = memoryCache()->add(resource);
382     
383     // Pretend the resource is in the cache, to prevent it from being deleted during the load() call.
384     // FIXME: CachedResource should just use normal refcounting instead.
385     if (!inCache)
386         resource->setInCache(true);
387     
388     resource->setLoadPriority(priority);
389     resource->load(this);
390     
391     if (!inCache) {
392         resource->setOwningCachedResourceLoader(this);
393         resource->setInCache(false);
394     }
395
396     // We don't support immediate loads, but we do support immediate failure.
397     if (resource->errorOccurred()) {
398         if (inCache)
399             memoryCache()->remove(resource);
400         else
401             delete resource;
402         return 0;
403     }
404
405     m_validatedURLs.add(url.string());
406     return resource;
407 }
408
409 CachedResourceLoader::RevalidationPolicy CachedResourceLoader::determineRevalidationPolicy(CachedResource::Type type, bool forPreload, CachedResource* existingResource) const
410 {
411     if (!existingResource)
412         return Load;
413     
414     // We already have a preload going for this URL.
415     if (forPreload && existingResource->isPreloaded())
416         return Use;
417     
418     // If the same URL has been loaded as a different type, we need to reload.
419     if (existingResource->type() != type) {
420         LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to type mismatch.");
421         return Reload;
422     }
423     
424     // Don't reload resources while pasting.
425     if (m_allowStaleResources)
426         return Use;
427     
428     // Alwaus use preloads.
429     if (existingResource->isPreloaded())
430         return Use;
431     
432     // CachePolicyHistoryBuffer uses the cache no matter what.
433     if (cachePolicy() == CachePolicyHistoryBuffer)
434         return Use;
435
436     // Don't reuse resources with Cache-control: no-store.
437     if (existingResource->response().cacheControlContainsNoStore()) {
438         LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to Cache-control: no-store.");
439         return Reload;
440     }
441
442     // Avoid loading the same resource multiple times for a single document, even if the cache policies would tell us to.
443     if (m_validatedURLs.contains(existingResource->url()))
444         return Use;
445     
446     // CachePolicyReload always reloads
447     if (cachePolicy() == CachePolicyReload) {
448         LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to CachePolicyReload.");
449         return Reload;
450     }
451     
452     // We'll try to reload the resource if it failed last time.
453     if (existingResource->errorOccurred()) {
454         LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicye reloading due to resource being in the error state");
455         return Reload;
456     }
457     
458     // For resources that are not yet loaded we ignore the cache policy.
459     if (existingResource->isLoading())
460         return Use;
461
462     // Check if the cache headers requires us to revalidate (cache expiration for example).
463     if (existingResource->mustRevalidateDueToCacheHeaders(cachePolicy())) {
464         // See if the resource has usable ETag or Last-modified headers.
465         if (existingResource->canUseCacheValidator())
466             return Revalidate;
467         
468         // No, must reload.
469         LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to missing cache validators.");            
470         return Reload;
471     }
472
473     return Use;
474 }
475
476 void CachedResourceLoader::printAccessDeniedMessage(const KURL& url) const
477 {
478     if (url.isNull())
479         return;
480
481     if (!frame())
482         return;
483
484     Settings* settings = frame()->settings();
485     if (!settings || settings->privateBrowsingEnabled())
486         return;
487
488     String message = m_document->url().isNull() ?
489         makeString("Unsafe attempt to load URL ", url.string(), '.') :
490         makeString("Unsafe attempt to load URL ", url.string(), " from frame with URL ", m_document->url().string(), ". Domains, protocols and ports must match.\n");
491
492     // FIXME: provide a real line number and source URL.
493     frame()->domWindow()->console()->addMessage(OtherMessageSource, LogMessageType, ErrorMessageLevel, message, 1, String());
494 }
495
496 void CachedResourceLoader::setAutoLoadImages(bool enable)
497 {
498     if (enable == m_autoLoadImages)
499         return;
500
501     m_autoLoadImages = enable;
502
503     if (!m_autoLoadImages)
504         return;
505
506     DocumentResourceMap::iterator end = m_documentResources.end();
507     for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it) {
508         CachedResource* resource = it->second.get();
509         if (resource->type() == CachedResource::ImageResource) {
510             CachedImage* image = const_cast<CachedImage*>(static_cast<const CachedImage*>(resource));
511
512             if (image->stillNeedsLoad())
513                 load(image, true);
514         }
515     }
516 }
517
518 CachePolicy CachedResourceLoader::cachePolicy() const
519 {
520     return frame() ? frame()->loader()->subresourceCachePolicy() : CachePolicyVerify;
521 }
522
523 void CachedResourceLoader::removeCachedResource(CachedResource* resource) const
524 {
525 #ifndef NDEBUG
526     DocumentResourceMap::iterator it = m_documentResources.find(resource->url());
527     if (it != m_documentResources.end())
528         ASSERT(it->second.get() == resource);
529 #endif
530     m_documentResources.remove(resource->url());
531 }
532
533 void CachedResourceLoader::load(CachedResource* resource, bool incremental, SecurityCheckPolicy securityCheck, bool sendResourceLoadCallbacks)
534 {
535     incrementRequestCount(resource);
536
537     RefPtr<CachedResourceRequest> request = CachedResourceRequest::load(this, resource, incremental, securityCheck, sendResourceLoadCallbacks);
538     if (request)
539         m_requests.add(request);
540 }
541
542 void CachedResourceLoader::loadDone(CachedResourceRequest* request)
543 {
544     m_loadFinishing = false;
545     RefPtr<CachedResourceRequest> protect(request);
546     if (request)
547         m_requests.remove(request);
548     if (frame())
549         frame()->loader()->loadDone();
550
551     if (!request) {
552         // If the request passed to this function is null, loadDone finished synchronously from when
553         // the load was started, so we want to kick off our next set of loads (via checkForPendingPreloads
554         // and servePendingRequests) asynchronously.
555         m_loadDoneActionTimer.startOneShot(0);
556         return;
557     }
558
559     performPostLoadActions();
560 }
561
562 void CachedResourceLoader::loadDoneActionTimerFired(Timer<CachedResourceLoader>*)
563 {
564     performPostLoadActions();
565 }
566
567 void CachedResourceLoader::performPostLoadActions()
568 {
569     checkForPendingPreloads();
570     resourceLoadScheduler()->servePendingRequests();
571 }
572
573 void CachedResourceLoader::cancelRequests()
574 {
575     clearPendingPreloads();
576     Vector<CachedResourceRequest*, 256> requestsToCancel;
577     RequestSet::iterator end = m_requests.end();
578     for (RequestSet::iterator i = m_requests.begin(); i != end; ++i)
579         requestsToCancel.append((*i).get());
580
581     for (unsigned i = 0; i < requestsToCancel.size(); ++i)
582         requestsToCancel[i]->didFail(true);
583 }
584
585 void CachedResourceLoader::notifyLoadedFromMemoryCache(CachedResource* resource)
586 {
587     if (!resource || !frame() || resource->status() != CachedResource::Cached)
588         return;
589
590     // FIXME: If the WebKit client changes or cancels the request, WebCore does not respect this and continues the load.
591     frame()->loader()->loadedResourceFromMemoryCache(resource);
592 }
593
594 void CachedResourceLoader::incrementRequestCount(const CachedResource* res)
595 {
596     if (res->isPrefetch())
597         return;
598
599     ++m_requestCount;
600 }
601
602 void CachedResourceLoader::decrementRequestCount(const CachedResource* res)
603 {
604     if (res->isPrefetch())
605         return;
606
607     --m_requestCount;
608     ASSERT(m_requestCount > -1);
609 }
610
611 int CachedResourceLoader::requestCount()
612 {
613     if (m_loadFinishing)
614          return m_requestCount + 1;
615     return m_requestCount;
616 }
617     
618 void CachedResourceLoader::preload(CachedResource::Type type, const String& url, const String& charset, bool referencedFromBody)
619 {
620     // FIXME: Rip this out when we are sure it is no longer necessary (even for mobile).
621     UNUSED_PARAM(referencedFromBody);
622
623     bool hasRendering = m_document->body() && m_document->body()->renderer();
624     bool canBlockParser = type == CachedResource::Script || type == CachedResource::CSSStyleSheet;
625     if (!hasRendering && !canBlockParser) {
626         // Don't preload subresources that can't block the parser before we have something to draw.
627         // This helps prevent preloads from delaying first display when bandwidth is limited.
628         PendingPreload pendingPreload = { type, url, charset };
629         m_pendingPreloads.append(pendingPreload);
630         return;
631     }
632     requestPreload(type, url, charset);
633 }
634
635 void CachedResourceLoader::checkForPendingPreloads() 
636 {
637     if (m_pendingPreloads.isEmpty() || !m_document->body() || !m_document->body()->renderer())
638         return;
639     while (!m_pendingPreloads.isEmpty()) {
640         PendingPreload preload = m_pendingPreloads.takeFirst();
641         // 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).
642         if (!cachedResource(m_document->completeURL(preload.m_url)))
643             requestPreload(preload.m_type, preload.m_url, preload.m_charset);
644     }
645     m_pendingPreloads.clear();
646 }
647
648 void CachedResourceLoader::requestPreload(CachedResource::Type type, const String& url, const String& charset)
649 {
650     String encoding;
651     if (type == CachedResource::Script || type == CachedResource::CSSStyleSheet)
652         encoding = charset.isEmpty() ? m_document->charset() : charset;
653
654     CachedResource* resource = requestResource(type, url, encoding, ResourceLoadPriorityUnresolved, true);
655     if (!resource || (m_preloads && m_preloads->contains(resource)))
656         return;
657     resource->increasePreloadCount();
658
659     if (!m_preloads)
660         m_preloads = adoptPtr(new ListHashSet<CachedResource*>);
661     m_preloads->add(resource);
662
663 #if PRELOAD_DEBUG
664     printf("PRELOADING %s\n",  resource->url().latin1().data());
665 #endif
666 }
667
668 void CachedResourceLoader::clearPreloads()
669 {
670 #if PRELOAD_DEBUG
671     printPreloadStats();
672 #endif
673     if (!m_preloads)
674         return;
675
676     ListHashSet<CachedResource*>::iterator end = m_preloads->end();
677     for (ListHashSet<CachedResource*>::iterator it = m_preloads->begin(); it != end; ++it) {
678         CachedResource* res = *it;
679         res->decreasePreloadCount();
680         if (res->canDelete() && !res->inCache())
681             delete res;
682         else if (res->preloadResult() == CachedResource::PreloadNotReferenced)
683             memoryCache()->remove(res);
684     }
685     m_preloads.clear();
686 }
687
688 void CachedResourceLoader::clearPendingPreloads()
689 {
690     m_pendingPreloads.clear();
691 }
692
693 #if PRELOAD_DEBUG
694 void CachedResourceLoader::printPreloadStats()
695 {
696     unsigned scripts = 0;
697     unsigned scriptMisses = 0;
698     unsigned stylesheets = 0;
699     unsigned stylesheetMisses = 0;
700     unsigned images = 0;
701     unsigned imageMisses = 0;
702     ListHashSet<CachedResource*>::iterator end = m_preloads.end();
703     for (ListHashSet<CachedResource*>::iterator it = m_preloads.begin(); it != end; ++it) {
704         CachedResource* res = *it;
705         if (res->preloadResult() == CachedResource::PreloadNotReferenced)
706             printf("!! UNREFERENCED PRELOAD %s\n", res->url().latin1().data());
707         else if (res->preloadResult() == CachedResource::PreloadReferencedWhileComplete)
708             printf("HIT COMPLETE PRELOAD %s\n", res->url().latin1().data());
709         else if (res->preloadResult() == CachedResource::PreloadReferencedWhileLoading)
710             printf("HIT LOADING PRELOAD %s\n", res->url().latin1().data());
711         
712         if (res->type() == CachedResource::Script) {
713             scripts++;
714             if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
715                 scriptMisses++;
716         } else if (res->type() == CachedResource::CSSStyleSheet) {
717             stylesheets++;
718             if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
719                 stylesheetMisses++;
720         } else {
721             images++;
722             if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
723                 imageMisses++;
724         }
725         
726         if (res->errorOccurred())
727             memoryCache()->remove(res);
728         
729         res->decreasePreloadCount();
730     }
731     m_preloads.clear();
732     
733     if (scripts)
734         printf("SCRIPTS: %d (%d hits, hit rate %d%%)\n", scripts, scripts - scriptMisses, (scripts - scriptMisses) * 100 / scripts);
735     if (stylesheets)
736         printf("STYLESHEETS: %d (%d hits, hit rate %d%%)\n", stylesheets, stylesheets - stylesheetMisses, (stylesheets - stylesheetMisses) * 100 / stylesheets);
737     if (images)
738         printf("IMAGES:  %d (%d hits, hit rate %d%%)\n", images, images - imageMisses, (images - imageMisses) * 100 / images);
739 }
740 #endif
741     
742 }