Home | History | Annotate | Download | only in cache
      1 /*
      2     Copyright (C) 1998 Lars Knoll (knoll (at) mpi-hd.mpg.de)
      3     Copyright (C) 2001 Dirk Mueller (mueller (at) kde.org)
      4     Copyright (C) 2002 Waldo Bastian (bastian (at) 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::LinkResource:
     75         return new CachedResource(url.string(), CachedResource::LinkResource);
     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 #ifdef ANDROID_BLOCK_NETWORK_IMAGE
     90     , m_blockNetworkImage(false)
     91 #endif
     92 {
     93 }
     94 
     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 
    109 CachedResource* CachedResourceLoader::cachedResource(const String& resourceURL) const
    110 {
    111     KURL url = m_document->completeURL(resourceURL);
    112     return cachedResource(url);
    113 }
    114 
    115 CachedResource* CachedResourceLoader::cachedResource(const KURL& resourceURL) const
    116 {
    117     KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(resourceURL);
    118     return m_documentResources.get(url).get();
    119 }
    120 
    121 Frame* CachedResourceLoader::frame() const
    122 {
    123     return m_document ? m_document->frame() : 0;
    124 }
    125 
    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 
    160 CachedFont* CachedResourceLoader::requestFont(const String& url)
    161 {
    162     return static_cast<CachedFont*>(requestResource(CachedResource::FontResource, url, String()));
    163 }
    164 
    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 
    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 
    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)
    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)
    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 
    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 
    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 
    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 
    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 
    426 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 
    493 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 
    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
    543 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 
    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 
    579 CachePolicy CachedResourceLoader::cachePolicy() const
    580 {
    581     return frame() ? frame()->loader()->subresourceCachePolicy() : CachePolicyVerify;
    582 }
    583 
    584 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 
    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 
    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 
    623 void CachedResourceLoader::loadDoneActionTimerFired(Timer<CachedResourceLoader>*)
    624 {
    625     performPostLoadActions();
    626 }
    627 
    628 void CachedResourceLoader::performPostLoadActions()
    629 {
    630     checkForPendingPreloads();
    631     resourceLoadScheduler()->servePendingRequests();
    632 }
    633 
    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 
    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 
    655 void CachedResourceLoader::incrementRequestCount(const CachedResource* res)
    656 {
    657     if (res->isLinkResource())
    658         return;
    659 
    660     ++m_requestCount;
    661 }
    662 
    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 
    672 int CachedResourceLoader::requestCount()
    673 {
    674     if (m_loadFinishing)
    675          return m_requestCount + 1;
    676     return m_requestCount;
    677 }
    678 
    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 
    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 
    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 
    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 
    749 void CachedResourceLoader::clearPendingPreloads()
    750 {
    751     m_pendingPreloads.clear();
    752 }
    753 
    754 #if PRELOAD_DEBUG
    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