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