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