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