WebCore/loader/DocLoader.cpp
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     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 "DocLoader.h"
       
    29 
       
    30 #include "Cache.h"
       
    31 #include "CachedCSSStyleSheet.h"
       
    32 #include "CachedFont.h"
       
    33 #include "CachedImage.h"
       
    34 #include "CachedScript.h"
       
    35 #include "CachedXSLStyleSheet.h"
       
    36 #include "Console.h"
       
    37 #include "Document.h"
       
    38 #include "DOMWindow.h"
       
    39 #include "HTMLElement.h"
       
    40 #include "Frame.h"
       
    41 #include "FrameLoader.h"
       
    42 #include "FrameLoaderClient.h"
       
    43 #include "loader.h"
       
    44 #include "SecurityOrigin.h"
       
    45 #include "Settings.h"
       
    46 #include <wtf/text/CString.h>
       
    47 
       
    48 #define PRELOAD_DEBUG 0
       
    49 
       
    50 namespace WebCore {
       
    51 
       
    52 DocLoader::DocLoader(Document* doc)
       
    53     : m_cache(cache())
       
    54     , m_doc(doc)
       
    55     , m_requestCount(0)
       
    56     , m_autoLoadImages(true)
       
    57     , m_loadInProgress(false)
       
    58     , m_allowStaleResources(false)
       
    59 {
       
    60     m_cache->addDocLoader(this);
       
    61 }
       
    62 
       
    63 DocLoader::~DocLoader()
       
    64 {
       
    65     if (m_requestCount)
       
    66         m_cache->loader()->cancelRequests(this);
       
    67 
       
    68     clearPreloads();
       
    69     DocumentResourceMap::iterator end = m_documentResources.end();
       
    70     for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it)
       
    71         it->second->setDocLoader(0);
       
    72     m_cache->removeDocLoader(this);
       
    73 
       
    74     // Make sure no requests still point to this DocLoader
       
    75     ASSERT(m_requestCount == 0);
       
    76 }
       
    77 
       
    78 Frame* DocLoader::frame() const
       
    79 {
       
    80     return m_doc->frame();
       
    81 }
       
    82 
       
    83 void DocLoader::checkForReload(const KURL& fullURL)
       
    84 {
       
    85     if (m_allowStaleResources)
       
    86         return; // Don't reload resources while pasting
       
    87 
       
    88     if (fullURL.isEmpty())
       
    89         return;
       
    90     
       
    91     if (m_reloadedURLs.contains(fullURL.string()))
       
    92         return;
       
    93     
       
    94     CachedResource* existing = cache()->resourceForURL(fullURL.string());
       
    95     if (!existing || existing->isPreloaded())
       
    96         return;
       
    97 
       
    98     switch (cachePolicy()) {
       
    99     case CachePolicyVerify:
       
   100         if (!existing->mustRevalidate(CachePolicyVerify))
       
   101             return;
       
   102         cache()->revalidateResource(existing, this);
       
   103         break;
       
   104     case CachePolicyCache:
       
   105         if (!existing->mustRevalidate(CachePolicyCache))
       
   106             return;
       
   107         cache()->revalidateResource(existing, this);
       
   108         break;
       
   109     case CachePolicyReload:
       
   110         cache()->remove(existing);        
       
   111         break;
       
   112     case CachePolicyRevalidate:
       
   113         cache()->revalidateResource(existing, this);
       
   114         break;
       
   115     case CachePolicyAllowStale:
       
   116         return;
       
   117     }
       
   118 
       
   119     m_reloadedURLs.add(fullURL.string());
       
   120 }
       
   121 
       
   122 CachedImage* DocLoader::requestImage(const String& url)
       
   123 {
       
   124     if (Frame* f = frame()) {
       
   125         Settings* settings = f->settings();
       
   126         if (!f->loader()->client()->allowImages(!settings || settings->areImagesEnabled()))
       
   127             return 0;
       
   128     }
       
   129     CachedImage* resource = static_cast<CachedImage*>(requestResource(CachedResource::ImageResource, url, String()));
       
   130     if (autoLoadImages() && resource && resource->stillNeedsLoad()) {
       
   131         resource->setLoading(true);
       
   132         cache()->loader()->load(this, resource, true);
       
   133     }
       
   134     return resource;
       
   135 }
       
   136 
       
   137 CachedFont* DocLoader::requestFont(const String& url)
       
   138 {
       
   139     return static_cast<CachedFont*>(requestResource(CachedResource::FontResource, url, String()));
       
   140 }
       
   141 
       
   142 CachedCSSStyleSheet* DocLoader::requestCSSStyleSheet(const String& url, const String& charset)
       
   143 {
       
   144     return static_cast<CachedCSSStyleSheet*>(requestResource(CachedResource::CSSStyleSheet, url, charset));
       
   145 }
       
   146 
       
   147 CachedCSSStyleSheet* DocLoader::requestUserCSSStyleSheet(const String& url, const String& charset)
       
   148 {
       
   149     return cache()->requestUserCSSStyleSheet(this, url, charset);
       
   150 }
       
   151 
       
   152 CachedScript* DocLoader::requestScript(const String& url, const String& charset)
       
   153 {
       
   154     return static_cast<CachedScript*>(requestResource(CachedResource::Script, url, charset));
       
   155 }
       
   156 
       
   157 #if ENABLE(XSLT)
       
   158 CachedXSLStyleSheet* DocLoader::requestXSLStyleSheet(const String& url)
       
   159 {
       
   160     return static_cast<CachedXSLStyleSheet*>(requestResource(CachedResource::XSLStyleSheet, url, String()));
       
   161 }
       
   162 #endif
       
   163 
       
   164 #if ENABLE(XBL)
       
   165 CachedXBLDocument* DocLoader::requestXBLDocument(const String& url)
       
   166 {
       
   167     return static_cast<CachedXSLStyleSheet*>(requestResource(CachedResource::XBL, url, String()));
       
   168 }
       
   169 #endif
       
   170 
       
   171 #if ENABLE(LINK_PREFETCH)
       
   172 CachedResource* DocLoader::requestLinkPrefetch(const String& url)
       
   173 {
       
   174     return requestResource(CachedResource::LinkPrefetch, url, String());
       
   175 }
       
   176 #endif
       
   177 
       
   178 bool DocLoader::canRequest(CachedResource::Type type, const KURL& url)
       
   179 {
       
   180     // Some types of resources can be loaded only from the same origin.  Other
       
   181     // types of resources, like Images, Scripts, and CSS, can be loaded from
       
   182     // any URL.
       
   183     switch (type) {
       
   184     case CachedResource::ImageResource:
       
   185     case CachedResource::CSSStyleSheet:
       
   186     case CachedResource::Script:
       
   187     case CachedResource::FontResource:
       
   188 #if ENABLE(LINK_PREFETCH)
       
   189     case CachedResource::LinkPrefetch:
       
   190 #endif
       
   191         // These types of resources can be loaded from any origin.
       
   192         // FIXME: Are we sure about CachedResource::FontResource?
       
   193         break;
       
   194 #if ENABLE(XSLT)
       
   195     case CachedResource::XSLStyleSheet:
       
   196 #endif
       
   197 #if ENABLE(XBL)
       
   198     case CachedResource::XBL:
       
   199 #endif
       
   200 #if ENABLE(XSLT) || ENABLE(XBL)
       
   201         if (!m_doc->securityOrigin()->canRequest(url)) {
       
   202             printAccessDeniedMessage(url);
       
   203             return false;
       
   204         }
       
   205         break;
       
   206 #endif
       
   207     default:
       
   208         ASSERT_NOT_REACHED();
       
   209         break;
       
   210     }
       
   211 
       
   212     // Given that the load is allowed by the same-origin policy, we should
       
   213     // check whether the load passes the mixed-content policy.
       
   214     //
       
   215     // Note: Currently, we always allow mixed content, but we generate a
       
   216     //       callback to the FrameLoaderClient in case the embedder wants to
       
   217     //       update any security indicators.
       
   218     // 
       
   219     switch (type) {
       
   220     case CachedResource::Script:
       
   221 #if ENABLE(XSLT)
       
   222     case CachedResource::XSLStyleSheet:
       
   223 #endif
       
   224 #if ENABLE(XBL)
       
   225     case CachedResource::XBL:
       
   226 #endif
       
   227         // These resource can inject script into the current document.
       
   228         if (Frame* f = frame())
       
   229             f->loader()->checkIfRunInsecureContent(m_doc->securityOrigin(), url);
       
   230         break;
       
   231     case CachedResource::ImageResource:
       
   232     case CachedResource::CSSStyleSheet:
       
   233     case CachedResource::FontResource: {
       
   234         // These resources can corrupt only the frame's pixels.
       
   235         if (Frame* f = frame()) {
       
   236             Frame* top = f->tree()->top();
       
   237             top->loader()->checkIfDisplayInsecureContent(top->document()->securityOrigin(), url);
       
   238         }
       
   239         break;
       
   240     }
       
   241 #if ENABLE(LINK_PREFETCH)
       
   242     case CachedResource::LinkPrefetch:
       
   243         // Prefetch cannot affect the current document.
       
   244         break;
       
   245 #endif
       
   246     default:
       
   247         ASSERT_NOT_REACHED();
       
   248         break;
       
   249     }
       
   250     // FIXME: Consider letting the embedder block mixed content loads.
       
   251     return true;
       
   252 }
       
   253 
       
   254 CachedResource* DocLoader::requestResource(CachedResource::Type type, const String& url, const String& charset, bool isPreload)
       
   255 {
       
   256     KURL fullURL = m_doc->completeURL(url);
       
   257 
       
   258     if (!fullURL.isValid() || !canRequest(type, fullURL))
       
   259         return 0;
       
   260 
       
   261     if (cache()->disabled()) {
       
   262         DocumentResourceMap::iterator it = m_documentResources.find(fullURL.string());
       
   263         
       
   264         if (it != m_documentResources.end()) {
       
   265             it->second->setDocLoader(0);
       
   266             m_documentResources.remove(it);
       
   267         }
       
   268     }
       
   269 
       
   270     checkForReload(fullURL);
       
   271 
       
   272     CachedResource* resource = cache()->requestResource(this, type, fullURL, charset, isPreload);
       
   273     if (resource) {
       
   274         // Check final URL of resource to catch redirects.
       
   275         // See <https://bugs.webkit.org/show_bug.cgi?id=21963>.
       
   276         if (fullURL != resource->url() && !canRequest(type, KURL(ParsedURLString, resource->url())))
       
   277             return 0;
       
   278 
       
   279         m_documentResources.set(resource->url(), resource);
       
   280         checkCacheObjectStatus(resource);
       
   281     }
       
   282     return resource;
       
   283 }
       
   284 
       
   285 void DocLoader::printAccessDeniedMessage(const KURL& url) const
       
   286 {
       
   287     if (url.isNull())
       
   288         return;
       
   289 
       
   290     if (!frame())
       
   291         return;
       
   292 
       
   293     Settings* settings = frame()->settings();
       
   294     if (!settings || settings->privateBrowsingEnabled())
       
   295         return;
       
   296 
       
   297     String message = m_doc->url().isNull() ?
       
   298         String::format("Unsafe attempt to load URL %s.",
       
   299                        url.string().utf8().data()) :
       
   300         String::format("Unsafe attempt to load URL %s from frame with URL %s. "
       
   301                        "Domains, protocols and ports must match.\n",
       
   302                        url.string().utf8().data(),
       
   303                        m_doc->url().string().utf8().data());
       
   304 
       
   305     // FIXME: provide a real line number and source URL.
       
   306     frame()->domWindow()->console()->addMessage(OtherMessageSource, LogMessageType, ErrorMessageLevel, message, 1, String());
       
   307 }
       
   308 
       
   309 void DocLoader::setAutoLoadImages(bool enable)
       
   310 {
       
   311     if (enable == m_autoLoadImages)
       
   312         return;
       
   313 
       
   314     m_autoLoadImages = enable;
       
   315 
       
   316     if (!m_autoLoadImages)
       
   317         return;
       
   318 
       
   319     DocumentResourceMap::iterator end = m_documentResources.end();
       
   320     for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it) {
       
   321         CachedResource* resource = it->second.get();
       
   322         if (resource->type() == CachedResource::ImageResource) {
       
   323             CachedImage* image = const_cast<CachedImage*>(static_cast<const CachedImage*>(resource));
       
   324 
       
   325             if (image->stillNeedsLoad())
       
   326                 cache()->loader()->load(this, image, true);
       
   327         }
       
   328     }
       
   329 }
       
   330 
       
   331 CachePolicy DocLoader::cachePolicy() const
       
   332 {
       
   333     return frame() ? frame()->loader()->subresourceCachePolicy() : CachePolicyVerify;
       
   334 }
       
   335 
       
   336 void DocLoader::removeCachedResource(CachedResource* resource) const
       
   337 {
       
   338 #ifndef NDEBUG
       
   339     DocumentResourceMap::iterator it = m_documentResources.find(resource->url());
       
   340     if (it != m_documentResources.end())
       
   341         ASSERT(it->second.get() == resource);
       
   342 #endif
       
   343     m_documentResources.remove(resource->url());
       
   344 }
       
   345 
       
   346 void DocLoader::setLoadInProgress(bool load)
       
   347 {
       
   348     m_loadInProgress = load;
       
   349     if (!load && frame())
       
   350         frame()->loader()->loadDone();
       
   351 }
       
   352 
       
   353 void DocLoader::checkCacheObjectStatus(CachedResource* resource)
       
   354 {
       
   355     // Return from the function for objects that we didn't load from the cache or if we don't have a frame.
       
   356     if (!resource || !frame())
       
   357         return;
       
   358 
       
   359     switch (resource->status()) {
       
   360         case CachedResource::Cached:
       
   361             break;
       
   362         case CachedResource::NotCached:
       
   363         case CachedResource::Unknown:
       
   364         case CachedResource::New:
       
   365         case CachedResource::Pending:
       
   366             return;
       
   367     }
       
   368 
       
   369     // FIXME: If the WebKit client changes or cancels the request, WebCore does not respect this and continues the load.
       
   370     frame()->loader()->loadedResourceFromMemoryCache(resource);
       
   371 }
       
   372 
       
   373 void DocLoader::incrementRequestCount(const CachedResource* res)
       
   374 {
       
   375     if (res->isPrefetch())
       
   376         return;
       
   377 
       
   378     ++m_requestCount;
       
   379 }
       
   380 
       
   381 void DocLoader::decrementRequestCount(const CachedResource* res)
       
   382 {
       
   383     if (res->isPrefetch())
       
   384         return;
       
   385 
       
   386     --m_requestCount;
       
   387     ASSERT(m_requestCount > -1);
       
   388 }
       
   389 
       
   390 int DocLoader::requestCount()
       
   391 {
       
   392     if (loadInProgress())
       
   393          return m_requestCount + 1;
       
   394     return m_requestCount;
       
   395 }
       
   396     
       
   397 void DocLoader::preload(CachedResource::Type type, const String& url, const String& charset, bool referencedFromBody)
       
   398 {
       
   399     bool hasRendering = m_doc->body() && m_doc->body()->renderer();
       
   400     if (!hasRendering && (referencedFromBody || type == CachedResource::ImageResource)) {
       
   401         // Don't preload images or body resources before we have something to draw. This prevents
       
   402         // preloads from body delaying first display when bandwidth is limited.
       
   403         PendingPreload pendingPreload = { type, url, charset };
       
   404         m_pendingPreloads.append(pendingPreload);
       
   405         return;
       
   406     }
       
   407     requestPreload(type, url, charset);
       
   408 }
       
   409 
       
   410 void DocLoader::checkForPendingPreloads() 
       
   411 {
       
   412     unsigned count = m_pendingPreloads.size();
       
   413     if (!count || !m_doc->body() || !m_doc->body()->renderer())
       
   414         return;
       
   415     for (unsigned i = 0; i < count; ++i) {
       
   416         PendingPreload& preload = m_pendingPreloads[i];
       
   417         // 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).
       
   418         if (!cachedResource(m_doc->completeURL(preload.m_url)))
       
   419             requestPreload(preload.m_type, preload.m_url, preload.m_charset);
       
   420     }
       
   421     m_pendingPreloads.clear();
       
   422 }
       
   423 
       
   424 void DocLoader::requestPreload(CachedResource::Type type, const String& url, const String& charset)
       
   425 {
       
   426     String encoding;
       
   427     if (type == CachedResource::Script || type == CachedResource::CSSStyleSheet)
       
   428         encoding = charset.isEmpty() ? m_doc->frame()->loader()->writer()->encoding() : charset;
       
   429 
       
   430     CachedResource* resource = requestResource(type, url, encoding, true);
       
   431     if (!resource || (m_preloads && m_preloads->contains(resource)))
       
   432         return;
       
   433     resource->increasePreloadCount();
       
   434 
       
   435     if (!m_preloads)
       
   436         m_preloads.set(new ListHashSet<CachedResource*>);
       
   437     m_preloads->add(resource);
       
   438 
       
   439 #if PRELOAD_DEBUG
       
   440     printf("PRELOADING %s\n",  resource->url().latin1().data());
       
   441 #endif
       
   442 }
       
   443 
       
   444 void DocLoader::clearPreloads()
       
   445 {
       
   446 #if PRELOAD_DEBUG
       
   447     printPreloadStats();
       
   448 #endif
       
   449     if (!m_preloads)
       
   450         return;
       
   451 
       
   452     ListHashSet<CachedResource*>::iterator end = m_preloads->end();
       
   453     for (ListHashSet<CachedResource*>::iterator it = m_preloads->begin(); it != end; ++it) {
       
   454         CachedResource* res = *it;
       
   455         res->decreasePreloadCount();
       
   456         if (res->canDelete() && !res->inCache())
       
   457             delete res;
       
   458         else if (res->preloadResult() == CachedResource::PreloadNotReferenced)
       
   459             cache()->remove(res);
       
   460     }
       
   461     m_preloads.clear();
       
   462 }
       
   463 
       
   464 void DocLoader::clearPendingPreloads()
       
   465 {
       
   466     m_pendingPreloads.clear();
       
   467 }
       
   468 
       
   469 #if PRELOAD_DEBUG
       
   470 void DocLoader::printPreloadStats()
       
   471 {
       
   472     unsigned scripts = 0;
       
   473     unsigned scriptMisses = 0;
       
   474     unsigned stylesheets = 0;
       
   475     unsigned stylesheetMisses = 0;
       
   476     unsigned images = 0;
       
   477     unsigned imageMisses = 0;
       
   478     ListHashSet<CachedResource*>::iterator end = m_preloads.end();
       
   479     for (ListHashSet<CachedResource*>::iterator it = m_preloads.begin(); it != end; ++it) {
       
   480         CachedResource* res = *it;
       
   481         if (res->preloadResult() == CachedResource::PreloadNotReferenced)
       
   482             printf("!! UNREFERENCED PRELOAD %s\n", res->url().latin1().data());
       
   483         else if (res->preloadResult() == CachedResource::PreloadReferencedWhileComplete)
       
   484             printf("HIT COMPLETE PRELOAD %s\n", res->url().latin1().data());
       
   485         else if (res->preloadResult() == CachedResource::PreloadReferencedWhileLoading)
       
   486             printf("HIT LOADING PRELOAD %s\n", res->url().latin1().data());
       
   487         
       
   488         if (res->type() == CachedResource::Script) {
       
   489             scripts++;
       
   490             if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
       
   491                 scriptMisses++;
       
   492         } else if (res->type() == CachedResource::CSSStyleSheet) {
       
   493             stylesheets++;
       
   494             if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
       
   495                 stylesheetMisses++;
       
   496         } else {
       
   497             images++;
       
   498             if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
       
   499                 imageMisses++;
       
   500         }
       
   501         
       
   502         if (res->errorOccurred())
       
   503             cache()->remove(res);
       
   504         
       
   505         res->decreasePreloadCount();
       
   506     }
       
   507     m_preloads.clear();
       
   508     
       
   509     if (scripts)
       
   510         printf("SCRIPTS: %d (%d hits, hit rate %d%%)\n", scripts, scripts - scriptMisses, (scripts - scriptMisses) * 100 / scripts);
       
   511     if (stylesheets)
       
   512         printf("STYLESHEETS: %d (%d hits, hit rate %d%%)\n", stylesheets, stylesheets - stylesheetMisses, (stylesheets - stylesheetMisses) * 100 / stylesheets);
       
   513     if (images)
       
   514         printf("IMAGES:  %d (%d hits, hit rate %d%%)\n", images, images - imageMisses, (images - imageMisses) * 100 / images);
       
   515 }
       
   516 #endif
       
   517     
       
   518 }