Home | History | Annotate | Download | only in fetch
      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, 2007, 2008, 2009, 2010, 2011 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 "core/fetch/ResourceFetcher.h"
     29 
     30 #include "RuntimeEnabledFeatures.h"
     31 #include "bindings/v8/ScriptController.h"
     32 #include "core/dom/Document.h"
     33 #include "core/fetch/CSSStyleSheetResource.h"
     34 #include "core/fetch/DocumentResource.h"
     35 #include "core/fetch/FetchContext.h"
     36 #include "core/fetch/FontResource.h"
     37 #include "core/fetch/ImageResource.h"
     38 #include "core/fetch/MemoryCache.h"
     39 #include "core/fetch/RawResource.h"
     40 #include "core/fetch/ResourceLoader.h"
     41 #include "core/fetch/ResourceLoaderSet.h"
     42 #include "core/fetch/ScriptResource.h"
     43 #include "core/fetch/ShaderResource.h"
     44 #include "core/fetch/XSLStyleSheetResource.h"
     45 #include "core/html/HTMLElement.h"
     46 #include "core/html/HTMLFrameOwnerElement.h"
     47 #include "core/html/HTMLImport.h"
     48 #include "core/inspector/InspectorInstrumentation.h"
     49 #include "core/loader/DocumentLoader.h"
     50 #include "core/loader/FrameLoader.h"
     51 #include "core/loader/FrameLoaderClient.h"
     52 #include "core/loader/PingLoader.h"
     53 #include "core/loader/UniqueIdentifier.h"
     54 #include "core/loader/appcache/ApplicationCacheHost.h"
     55 #include "core/frame/ContentSecurityPolicy.h"
     56 #include "core/frame/DOMWindow.h"
     57 #include "core/frame/Frame.h"
     58 #include "core/timing/Performance.h"
     59 #include "core/timing/ResourceTimingInfo.h"
     60 #include "core/frame/Settings.h"
     61 #include "platform/Logging.h"
     62 #include "platform/TraceEvent.h"
     63 #include "platform/weborigin/SecurityOrigin.h"
     64 #include "platform/weborigin/SecurityPolicy.h"
     65 #include "public/platform/Platform.h"
     66 #include "public/platform/WebURL.h"
     67 #include "wtf/text/CString.h"
     68 #include "wtf/text/WTFString.h"
     69 
     70 #define PRELOAD_DEBUG 0
     71 
     72 namespace WebCore {
     73 
     74 static Resource* createResource(Resource::Type type, const ResourceRequest& request, const String& charset)
     75 {
     76     switch (type) {
     77     case Resource::Image:
     78         return new ImageResource(request);
     79     case Resource::CSSStyleSheet:
     80         return new CSSStyleSheetResource(request, charset);
     81     case Resource::Script:
     82         return new ScriptResource(request, charset);
     83     case Resource::SVGDocument:
     84         return new DocumentResource(request, Resource::SVGDocument);
     85     case Resource::Font:
     86         return new FontResource(request);
     87     case Resource::MainResource:
     88     case Resource::Raw:
     89     case Resource::TextTrack:
     90         return new RawResource(request, type);
     91     case Resource::XSLStyleSheet:
     92         return new XSLStyleSheetResource(request);
     93     case Resource::LinkPrefetch:
     94         return new Resource(request, Resource::LinkPrefetch);
     95     case Resource::LinkSubresource:
     96         return new Resource(request, Resource::LinkSubresource);
     97     case Resource::Shader:
     98         return new ShaderResource(request);
     99     case Resource::ImportResource:
    100         return new RawResource(request, type);
    101     }
    102 
    103     ASSERT_NOT_REACHED();
    104     return 0;
    105 }
    106 
    107 static ResourceLoadPriority loadPriority(Resource::Type type, const FetchRequest& request)
    108 {
    109     if (request.priority() != ResourceLoadPriorityUnresolved)
    110         return request.priority();
    111 
    112     switch (type) {
    113     case Resource::MainResource:
    114         return ResourceLoadPriorityVeryHigh;
    115     case Resource::CSSStyleSheet:
    116         return ResourceLoadPriorityHigh;
    117     case Resource::Raw:
    118         return request.options().synchronousPolicy == RequestSynchronously ? ResourceLoadPriorityVeryHigh : ResourceLoadPriorityMedium;
    119     case Resource::Script:
    120     case Resource::Font:
    121     case Resource::ImportResource:
    122         return ResourceLoadPriorityMedium;
    123     case Resource::Image:
    124         // We'll default images to VeryLow, and promote whatever is visible. This improves
    125         // speed-index by ~5% on average, ~14% at the 99th percentile.
    126         return ResourceLoadPriorityVeryLow;
    127     case Resource::XSLStyleSheet:
    128         ASSERT(RuntimeEnabledFeatures::xsltEnabled());
    129         return ResourceLoadPriorityHigh;
    130     case Resource::SVGDocument:
    131         return ResourceLoadPriorityLow;
    132     case Resource::LinkPrefetch:
    133         return ResourceLoadPriorityVeryLow;
    134     case Resource::LinkSubresource:
    135         return ResourceLoadPriorityLow;
    136     case Resource::TextTrack:
    137         return ResourceLoadPriorityLow;
    138     case Resource::Shader:
    139         return ResourceLoadPriorityMedium;
    140     }
    141     ASSERT_NOT_REACHED();
    142     return ResourceLoadPriorityUnresolved;
    143 }
    144 
    145 static Resource* resourceFromDataURIRequest(const ResourceRequest& request, const ResourceLoaderOptions& resourceOptions)
    146 {
    147     const KURL& url = request.url();
    148     ASSERT(url.protocolIsData());
    149 
    150     blink::WebString mimetype;
    151     blink::WebString charset;
    152     RefPtr<SharedBuffer> data = PassRefPtr<SharedBuffer>(blink::Platform::current()->parseDataURL(url, mimetype, charset));
    153     if (!data)
    154         return 0;
    155     ResourceResponse response(url, mimetype, data->size(), charset, String());
    156 
    157     Resource* resource = createResource(Resource::Image, request, charset);
    158     resource->setOptions(resourceOptions);
    159     resource->responseReceived(response);
    160     // FIXME: AppendData causes an unnecessary memcpy.
    161     if (data->size())
    162         resource->appendData(data->data(), data->size());
    163     resource->finish();
    164     return resource;
    165 }
    166 
    167 static void populateResourceTiming(ResourceTimingInfo* info, Resource* resource, bool clearLoadTimings)
    168 {
    169     info->setInitialRequest(resource->resourceRequest());
    170     info->setFinalResponse(resource->response());
    171     if (clearLoadTimings)
    172         info->clearLoadTimings();
    173     info->setLoadFinishTime(resource->loadFinishTime());
    174 }
    175 
    176 static void reportResourceTiming(ResourceTimingInfo* info, Document* initiatorDocument, bool isMainResource)
    177 {
    178     if (initiatorDocument && isMainResource)
    179         initiatorDocument = initiatorDocument->parentDocument();
    180     if (!initiatorDocument || !initiatorDocument->loader())
    181         return;
    182     if (DOMWindow* initiatorWindow = initiatorDocument->domWindow()) {
    183         if (Performance* performance = initiatorWindow->performance())
    184             performance->addResourceTiming(*info, initiatorDocument);
    185     }
    186 }
    187 
    188 static ResourceRequest::TargetType requestTargetType(const ResourceFetcher* fetcher, const ResourceRequest& request, Resource::Type type)
    189 {
    190     switch (type) {
    191     case Resource::MainResource:
    192         if (fetcher->frame()->tree().parent())
    193             return ResourceRequest::TargetIsSubframe;
    194         return ResourceRequest::TargetIsMainFrame;
    195     case Resource::XSLStyleSheet:
    196         ASSERT(RuntimeEnabledFeatures::xsltEnabled());
    197     case Resource::CSSStyleSheet:
    198         return ResourceRequest::TargetIsStyleSheet;
    199     case Resource::Script:
    200         return ResourceRequest::TargetIsScript;
    201     case Resource::Font:
    202         return ResourceRequest::TargetIsFont;
    203     case Resource::Image:
    204         return ResourceRequest::TargetIsImage;
    205     case Resource::Shader:
    206     case Resource::Raw:
    207     case Resource::ImportResource:
    208         return ResourceRequest::TargetIsSubresource;
    209     case Resource::LinkPrefetch:
    210         return ResourceRequest::TargetIsPrefetch;
    211     case Resource::LinkSubresource:
    212         return ResourceRequest::TargetIsSubresource;
    213     case Resource::TextTrack:
    214         return ResourceRequest::TargetIsTextTrack;
    215     case Resource::SVGDocument:
    216         return ResourceRequest::TargetIsImage;
    217     }
    218     ASSERT_NOT_REACHED();
    219     return ResourceRequest::TargetIsSubresource;
    220 }
    221 
    222 ResourceFetcher::ResourceFetcher(DocumentLoader* documentLoader)
    223     : m_document(0)
    224     , m_documentLoader(documentLoader)
    225     , m_requestCount(0)
    226     , m_garbageCollectDocumentResourcesTimer(this, &ResourceFetcher::garbageCollectDocumentResourcesTimerFired)
    227     , m_resourceTimingReportTimer(this, &ResourceFetcher::resourceTimingReportTimerFired)
    228     , m_autoLoadImages(true)
    229     , m_imagesEnabled(true)
    230     , m_allowStaleResources(false)
    231 {
    232 }
    233 
    234 ResourceFetcher::~ResourceFetcher()
    235 {
    236     m_documentLoader = 0;
    237     m_document = 0;
    238 
    239     clearPreloads();
    240 
    241     // Make sure no requests still point to this ResourceFetcher
    242     ASSERT(!m_requestCount);
    243 }
    244 
    245 Resource* ResourceFetcher::cachedResource(const String& resourceURL) const
    246 {
    247     KURL url = m_document->completeURL(resourceURL);
    248     return cachedResource(url);
    249 }
    250 
    251 Resource* ResourceFetcher::cachedResource(const KURL& resourceURL) const
    252 {
    253     KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(resourceURL);
    254     return m_documentResources.get(url).get();
    255 }
    256 
    257 Frame* ResourceFetcher::frame() const
    258 {
    259     if (m_documentLoader)
    260         return m_documentLoader->frame();
    261     if (m_document && m_document->import())
    262         return m_document->import()->frame();
    263     return 0;
    264 }
    265 
    266 FetchContext& ResourceFetcher::context() const
    267 {
    268     if (Frame* frame = this->frame())
    269         return frame->fetchContext();
    270     return FetchContext::nullInstance();
    271 }
    272 
    273 ResourcePtr<Resource> ResourceFetcher::fetchSynchronously(FetchRequest& request)
    274 {
    275     ASSERT(document());
    276     request.mutableResourceRequest().setTimeoutInterval(10);
    277     ResourceLoaderOptions options(request.options());
    278     options.synchronousPolicy = RequestSynchronously;
    279     request.setOptions(options);
    280     return requestResource(Resource::Raw, request);
    281 }
    282 
    283 ResourcePtr<ImageResource> ResourceFetcher::fetchImage(FetchRequest& request)
    284 {
    285     if (Frame* f = frame()) {
    286         if (f->document()->pageDismissalEventBeingDispatched() != Document::NoDismissal) {
    287             KURL requestURL = request.resourceRequest().url();
    288             if (requestURL.isValid() && canRequest(Resource::Image, requestURL, request.options(), request.forPreload(), request.originRestriction()))
    289                 PingLoader::loadImage(f, requestURL);
    290             return 0;
    291         }
    292     }
    293 
    294     if (request.resourceRequest().url().protocolIsData())
    295         preCacheDataURIImage(request);
    296 
    297     request.setDefer(clientDefersImage(request.resourceRequest().url()) ? FetchRequest::DeferredByClient : FetchRequest::NoDefer);
    298     return toImageResource(requestResource(Resource::Image, request));
    299 }
    300 
    301 void ResourceFetcher::preCacheDataURIImage(const FetchRequest& request)
    302 {
    303     const KURL& url = request.resourceRequest().url();
    304     ASSERT(url.protocolIsData());
    305 
    306     if (memoryCache()->resourceForURL(url))
    307         return;
    308 
    309     if (Resource* resource = resourceFromDataURIRequest(request.resourceRequest(), request.options()))
    310         memoryCache()->add(resource);
    311 }
    312 
    313 ResourcePtr<FontResource> ResourceFetcher::fetchFont(FetchRequest& request)
    314 {
    315     return toFontResource(requestResource(Resource::Font, request));
    316 }
    317 
    318 ResourcePtr<ShaderResource> ResourceFetcher::fetchShader(FetchRequest& request)
    319 {
    320     return toShaderResource(requestResource(Resource::Shader, request));
    321 }
    322 
    323 ResourcePtr<RawResource> ResourceFetcher::fetchImport(FetchRequest& request)
    324 {
    325     return toRawResource(requestResource(Resource::ImportResource, request));
    326 }
    327 
    328 ResourcePtr<CSSStyleSheetResource> ResourceFetcher::fetchCSSStyleSheet(FetchRequest& request)
    329 {
    330     return toCSSStyleSheetResource(requestResource(Resource::CSSStyleSheet, request));
    331 }
    332 
    333 ResourcePtr<CSSStyleSheetResource> ResourceFetcher::fetchUserCSSStyleSheet(FetchRequest& request)
    334 {
    335     KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(request.resourceRequest().url());
    336 
    337     if (Resource* existing = memoryCache()->resourceForURL(url)) {
    338         if (existing->type() == Resource::CSSStyleSheet)
    339             return toCSSStyleSheetResource(existing);
    340         memoryCache()->remove(existing);
    341     }
    342 
    343     request.setOptions(ResourceLoaderOptions(DoNotSendCallbacks, SniffContent, BufferData, AllowStoredCredentials, ClientRequestedCredentials, AskClientForCrossOriginCredentials, SkipSecurityCheck, CheckContentSecurityPolicy, DocumentContext));
    344     return toCSSStyleSheetResource(requestResource(Resource::CSSStyleSheet, request));
    345 }
    346 
    347 ResourcePtr<ScriptResource> ResourceFetcher::fetchScript(FetchRequest& request)
    348 {
    349     return toScriptResource(requestResource(Resource::Script, request));
    350 }
    351 
    352 ResourcePtr<XSLStyleSheetResource> ResourceFetcher::fetchXSLStyleSheet(FetchRequest& request)
    353 {
    354     ASSERT(RuntimeEnabledFeatures::xsltEnabled());
    355     return toXSLStyleSheetResource(requestResource(Resource::XSLStyleSheet, request));
    356 }
    357 
    358 ResourcePtr<DocumentResource> ResourceFetcher::fetchSVGDocument(FetchRequest& request)
    359 {
    360     return toDocumentResource(requestResource(Resource::SVGDocument, request));
    361 }
    362 
    363 ResourcePtr<Resource> ResourceFetcher::fetchLinkResource(Resource::Type type, FetchRequest& request)
    364 {
    365     ASSERT(frame());
    366     ASSERT(type == Resource::LinkPrefetch || type == Resource::LinkSubresource);
    367     return requestResource(type, request);
    368 }
    369 
    370 ResourcePtr<RawResource> ResourceFetcher::fetchRawResource(FetchRequest& request)
    371 {
    372     return toRawResource(requestResource(Resource::Raw, request));
    373 }
    374 
    375 ResourcePtr<RawResource> ResourceFetcher::fetchMainResource(FetchRequest& request)
    376 {
    377     return toRawResource(requestResource(Resource::MainResource, request));
    378 }
    379 
    380 bool ResourceFetcher::checkInsecureContent(Resource::Type type, const KURL& url, MixedContentBlockingTreatment treatment) const
    381 {
    382     if (treatment == TreatAsDefaultForType) {
    383         switch (type) {
    384         case Resource::XSLStyleSheet:
    385             ASSERT(RuntimeEnabledFeatures::xsltEnabled());
    386         case Resource::Script:
    387         case Resource::SVGDocument:
    388         case Resource::CSSStyleSheet:
    389         case Resource::ImportResource:
    390             // These resource can inject script into the current document (Script,
    391             // XSL) or exfiltrate the content of the current document (CSS).
    392             treatment = TreatAsActiveContent;
    393             break;
    394 
    395         case Resource::TextTrack:
    396         case Resource::Shader:
    397         case Resource::Raw:
    398         case Resource::Image:
    399         case Resource::Font:
    400             // These resources can corrupt only the frame's pixels.
    401             treatment = TreatAsPassiveContent;
    402             break;
    403 
    404         case Resource::MainResource:
    405         case Resource::LinkPrefetch:
    406         case Resource::LinkSubresource:
    407             // These cannot affect the current document.
    408             treatment = TreatAsAlwaysAllowedContent;
    409             break;
    410         }
    411     }
    412     if (treatment == TreatAsActiveContent) {
    413         if (Frame* f = frame()) {
    414             if (!f->loader().mixedContentChecker()->canRunInsecureContent(m_document->securityOrigin(), url))
    415                 return false;
    416             Frame* top = f->tree().top();
    417             if (top != f && !top->loader().mixedContentChecker()->canRunInsecureContent(top->document()->securityOrigin(), url))
    418                 return false;
    419         }
    420     } else if (treatment == TreatAsPassiveContent) {
    421         if (Frame* f = frame()) {
    422             Frame* top = f->tree().top();
    423             if (!top->loader().mixedContentChecker()->canDisplayInsecureContent(top->document()->securityOrigin(), url))
    424                 return false;
    425         }
    426     } else {
    427         ASSERT(treatment == TreatAsAlwaysAllowedContent);
    428     }
    429     return true;
    430 }
    431 
    432 bool ResourceFetcher::canRequest(Resource::Type type, const KURL& url, const ResourceLoaderOptions& options, bool forPreload, FetchRequest::OriginRestriction originRestriction)
    433 {
    434     SecurityOrigin* securityOrigin = options.securityOrigin.get();
    435     if (!securityOrigin && document())
    436         securityOrigin = document()->securityOrigin();
    437 
    438     if (securityOrigin && !securityOrigin->canDisplay(url)) {
    439         if (!forPreload)
    440             context().reportLocalLoadFailed(url);
    441         WTF_LOG(ResourceLoading, "ResourceFetcher::requestResource URL was not allowed by SecurityOrigin::canDisplay");
    442         return 0;
    443     }
    444 
    445     // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved.
    446     bool shouldBypassMainWorldContentSecurityPolicy = (frame() && frame()->script().shouldBypassMainWorldContentSecurityPolicy()) || (options.contentSecurityPolicyOption == DoNotCheckContentSecurityPolicy);
    447 
    448     // Some types of resources can be loaded only from the same origin. Other
    449     // types of resources, like Images, Scripts, and CSS, can be loaded from
    450     // any URL.
    451     switch (type) {
    452     case Resource::MainResource:
    453     case Resource::Image:
    454     case Resource::CSSStyleSheet:
    455     case Resource::Script:
    456     case Resource::Font:
    457     case Resource::Raw:
    458     case Resource::LinkPrefetch:
    459     case Resource::LinkSubresource:
    460     case Resource::TextTrack:
    461     case Resource::Shader:
    462     case Resource::ImportResource:
    463         // By default these types of resources can be loaded from any origin.
    464         // FIXME: Are we sure about Resource::Font?
    465         if (originRestriction == FetchRequest::RestrictToSameOrigin && !securityOrigin->canRequest(url)) {
    466             printAccessDeniedMessage(url);
    467             return false;
    468         }
    469         break;
    470     case Resource::XSLStyleSheet:
    471         ASSERT(RuntimeEnabledFeatures::xsltEnabled());
    472     case Resource::SVGDocument:
    473         if (!securityOrigin->canRequest(url)) {
    474             printAccessDeniedMessage(url);
    475             return false;
    476         }
    477         break;
    478     }
    479 
    480     switch (type) {
    481     case Resource::XSLStyleSheet:
    482         ASSERT(RuntimeEnabledFeatures::xsltEnabled());
    483         if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowScriptFromSource(url))
    484             return false;
    485         break;
    486     case Resource::Script:
    487     case Resource::ImportResource:
    488         if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowScriptFromSource(url))
    489             return false;
    490 
    491         if (frame()) {
    492             Settings* settings = frame()->settings();
    493             if (!frame()->loader().client()->allowScriptFromSource(!settings || settings->isScriptEnabled(), url)) {
    494                 frame()->loader().client()->didNotAllowScript();
    495                 return false;
    496             }
    497         }
    498         break;
    499     case Resource::Shader:
    500         // Since shaders are referenced from CSS Styles use the same rules here.
    501     case Resource::CSSStyleSheet:
    502         if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowStyleFromSource(url))
    503             return false;
    504         break;
    505     case Resource::SVGDocument:
    506     case Resource::Image:
    507         if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowImageFromSource(url))
    508             return false;
    509         break;
    510     case Resource::Font: {
    511         if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowFontFromSource(url))
    512             return false;
    513         break;
    514     }
    515     case Resource::MainResource:
    516     case Resource::Raw:
    517     case Resource::LinkPrefetch:
    518     case Resource::LinkSubresource:
    519         break;
    520     case Resource::TextTrack:
    521         if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowMediaFromSource(url))
    522             return false;
    523         break;
    524     }
    525 
    526     // Last of all, check for insecure content. We do this last so that when
    527     // folks block insecure content with a CSP policy, they don't get a warning.
    528     // They'll still get a warning in the console about CSP blocking the load.
    529 
    530     // FIXME: Should we consider forPreload here?
    531     if (!checkInsecureContent(type, url, options.mixedContentBlockingTreatment))
    532         return false;
    533 
    534     return true;
    535 }
    536 
    537 bool ResourceFetcher::canAccess(Resource* resource, CORSEnabled corsEnabled, FetchRequest::OriginRestriction originRestriction)
    538 {
    539     // Redirects can change the response URL different from one of request.
    540     if (!canRequest(resource->type(), resource->response().url(), resource->options(), false, originRestriction))
    541         return false;
    542 
    543     String error;
    544     switch (resource->type()) {
    545     case Resource::Script:
    546     case Resource::ImportResource:
    547         if (corsEnabled == PotentiallyCORSEnabled
    548             && !m_document->securityOrigin()->canRequest(resource->response().url())
    549             && !resource->passesAccessControlCheck(m_document->securityOrigin(), error)) {
    550             if (frame() && frame()->document())
    551                 frame()->document()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Script from origin '" + SecurityOrigin::create(resource->response().url())->toString() + "' has been blocked from loading by Cross-Origin Resource Sharing policy: " + error);
    552             return false;
    553         }
    554 
    555         break;
    556     default:
    557         ASSERT_NOT_REACHED(); // FIXME: generalize to non-script resources
    558         return false;
    559     }
    560 
    561     return true;
    562 }
    563 
    564 bool ResourceFetcher::shouldLoadNewResource() const
    565 {
    566     if (!frame())
    567         return false;
    568     if (!m_documentLoader)
    569         return true;
    570     if (m_documentLoader == frame()->loader().activeDocumentLoader())
    571         return true;
    572     return document() && document()->pageDismissalEventBeingDispatched() != Document::NoDismissal;
    573 }
    574 
    575 bool ResourceFetcher::resourceNeedsLoad(Resource* resource, const FetchRequest& request, RevalidationPolicy policy)
    576 {
    577     if (FetchRequest::DeferredByClient == request.defer())
    578         return false;
    579     if (policy != Use)
    580         return true;
    581     if (resource->stillNeedsLoad())
    582         return true;
    583     return request.options().synchronousPolicy == RequestSynchronously && resource->isLoading();
    584 }
    585 
    586 ResourcePtr<Resource> ResourceFetcher::requestResource(Resource::Type type, FetchRequest& request)
    587 {
    588     ASSERT(request.options().synchronousPolicy == RequestAsynchronously || type == Resource::Raw);
    589 
    590     KURL url = request.resourceRequest().url();
    591 
    592     WTF_LOG(ResourceLoading, "ResourceFetcher::requestResource '%s', charset '%s', priority=%d, forPreload=%u, type=%s", url.elidedString().latin1().data(), request.charset().latin1().data(), request.priority(), request.forPreload(), ResourceTypeName(type));
    593 
    594     // If only the fragment identifiers differ, it is the same resource.
    595     url = MemoryCache::removeFragmentIdentifierIfNeeded(url);
    596 
    597     if (!url.isValid())
    598         return 0;
    599 
    600     if (!canRequest(type, url, request.options(), request.forPreload(), request.originRestriction()))
    601         return 0;
    602 
    603     if (Frame* f = frame())
    604         f->loader().client()->dispatchWillRequestResource(&request);
    605 
    606     // See if we can use an existing resource from the cache.
    607     ResourcePtr<Resource> resource = memoryCache()->resourceForURL(url);
    608 
    609     const RevalidationPolicy policy = determineRevalidationPolicy(type, request.mutableResourceRequest(), request.forPreload(), resource.get(), request.defer());
    610     switch (policy) {
    611     case Reload:
    612         memoryCache()->remove(resource.get());
    613         // Fall through
    614     case Load:
    615         resource = loadResource(type, request, request.charset());
    616         break;
    617     case Revalidate:
    618         resource = revalidateResource(request, resource.get());
    619         break;
    620     case Use:
    621         resource->updateForAccess();
    622         notifyLoadedFromMemoryCache(resource.get());
    623         break;
    624     }
    625 
    626     if (!resource)
    627         return 0;
    628 
    629     if (policy != Use)
    630         resource->setIdentifier(createUniqueIdentifier());
    631 
    632     if (!request.forPreload() || policy != Use) {
    633         ResourceLoadPriority priority = loadPriority(type, request);
    634         if (priority != resource->resourceRequest().priority()) {
    635             resource->resourceRequest().setPriority(priority);
    636             resource->didChangePriority(priority);
    637         }
    638     }
    639 
    640     if (resourceNeedsLoad(resource.get(), request, policy)) {
    641         if (!shouldLoadNewResource()) {
    642             if (resource->inCache())
    643                 memoryCache()->remove(resource.get());
    644             return 0;
    645         }
    646 
    647         if (!m_documentLoader || !m_documentLoader->scheduleArchiveLoad(resource.get(), request.resourceRequest()))
    648             resource->load(this, request.options());
    649 
    650         // For asynchronous loads that immediately fail, it's sufficient to return a
    651         // null Resource, as it indicates that something prevented the load from starting.
    652         // If there's a network error, that failure will happen asynchronously. However, if
    653         // a sync load receives a network error, it will have already happened by this point.
    654         // In that case, the requester should have access to the relevant ResourceError, so
    655         // we need to return a non-null Resource.
    656         if (resource->errorOccurred()) {
    657             if (resource->inCache())
    658                 memoryCache()->remove(resource.get());
    659             return request.options().synchronousPolicy == RequestSynchronously ? resource : 0;
    660         }
    661     }
    662 
    663     // FIXME: Temporarily leave main resource caching disabled for chromium,
    664     // see https://bugs.webkit.org/show_bug.cgi?id=107962. Before caching main
    665     // resources, we should be sure to understand the implications for memory
    666     // use.
    667     //
    668     // Ensure main resources aren't preloaded, and other main resource loads
    669     // are removed from cache to prevent reuse.
    670     if (type == Resource::MainResource) {
    671         ASSERT(policy != Use);
    672         ASSERT(policy != Revalidate);
    673         memoryCache()->remove(resource.get());
    674         if (request.forPreload())
    675             return 0;
    676     }
    677 
    678     if (!request.resourceRequest().url().protocolIsData()) {
    679         if (policy == Use && !m_validatedURLs.contains(request.resourceRequest().url())) {
    680             // Resources loaded from memory cache should be reported the first time they're used.
    681             RefPtr<ResourceTimingInfo> info = ResourceTimingInfo::create(request.options().initiatorInfo.name, monotonicallyIncreasingTime());
    682             populateResourceTiming(info.get(), resource.get(), true);
    683             m_scheduledResourceTimingReports.add(info, resource->type() == Resource::MainResource);
    684             if (!m_resourceTimingReportTimer.isActive())
    685                 m_resourceTimingReportTimer.startOneShot(0);
    686         }
    687 
    688         m_validatedURLs.add(request.resourceRequest().url());
    689     }
    690 
    691     ASSERT(resource->url() == url.string());
    692     m_documentResources.set(resource->url(), resource);
    693     return resource;
    694 }
    695 
    696 void ResourceFetcher::resourceTimingReportTimerFired(Timer<ResourceFetcher>* timer)
    697 {
    698     ASSERT_UNUSED(timer, timer == &m_resourceTimingReportTimer);
    699     HashMap<RefPtr<ResourceTimingInfo>, bool> timingReports;
    700     timingReports.swap(m_scheduledResourceTimingReports);
    701     HashMap<RefPtr<ResourceTimingInfo>, bool>::iterator end = timingReports.end();
    702     for (HashMap<RefPtr<ResourceTimingInfo>, bool>::iterator it = timingReports.begin(); it != end; ++it) {
    703         RefPtr<ResourceTimingInfo> info = it->key;
    704         bool isMainResource = it->value;
    705         reportResourceTiming(info.get(), document(), isMainResource);
    706     }
    707 }
    708 
    709 void ResourceFetcher::determineTargetType(ResourceRequest& request, Resource::Type type)
    710 {
    711     ResourceRequest::TargetType targetType = requestTargetType(this, request, type);
    712     request.setTargetType(targetType);
    713 }
    714 
    715 ResourceRequestCachePolicy ResourceFetcher::resourceRequestCachePolicy(const ResourceRequest& request, Resource::Type type)
    716 {
    717     if (type == Resource::MainResource) {
    718         FrameLoadType frameLoadType = frame()->loader().loadType();
    719         bool isReload = frameLoadType == FrameLoadTypeReload || frameLoadType == FrameLoadTypeReloadFromOrigin;
    720         if (request.httpMethod() == "POST" && frameLoadType == FrameLoadTypeBackForward)
    721             return ReturnCacheDataDontLoad;
    722         if (!m_documentLoader->overrideEncoding().isEmpty() || frameLoadType == FrameLoadTypeBackForward)
    723             return ReturnCacheDataElseLoad;
    724         if (isReload || frameLoadType == FrameLoadTypeSame || request.isConditional() || request.httpMethod() == "POST")
    725             return ReloadIgnoringCacheData;
    726         if (Frame* parent = frame()->tree().parent())
    727             return parent->document()->fetcher()->resourceRequestCachePolicy(request, type);
    728         return UseProtocolCachePolicy;
    729     }
    730 
    731     if (request.isConditional())
    732         return ReloadIgnoringCacheData;
    733 
    734     if (m_documentLoader && m_documentLoader->isLoadingInAPISense()) {
    735         // For POST requests, we mutate the main resource's cache policy to avoid form resubmission.
    736         // This policy should not be inherited by subresources.
    737         ResourceRequestCachePolicy mainResourceCachePolicy = m_documentLoader->request().cachePolicy();
    738         if (mainResourceCachePolicy == ReturnCacheDataDontLoad)
    739             return ReturnCacheDataElseLoad;
    740         return mainResourceCachePolicy;
    741     }
    742     return UseProtocolCachePolicy;
    743 }
    744 
    745 void ResourceFetcher::addAdditionalRequestHeaders(ResourceRequest& request, Resource::Type type)
    746 {
    747     if (!frame())
    748         return;
    749 
    750     if (request.cachePolicy() == UseProtocolCachePolicy)
    751         request.setCachePolicy(resourceRequestCachePolicy(request, type));
    752     if (request.targetType() == ResourceRequest::TargetIsUnspecified)
    753         determineTargetType(request, type);
    754     if (type == Resource::LinkPrefetch || type == Resource::LinkSubresource)
    755         request.setHTTPHeaderField("Purpose", "prefetch");
    756 
    757     context().addAdditionalRequestHeaders(*document(), request, type);
    758 }
    759 
    760 ResourcePtr<Resource> ResourceFetcher::revalidateResource(const FetchRequest& request, Resource* resource)
    761 {
    762     ASSERT(resource);
    763     ASSERT(resource->inCache());
    764     ASSERT(resource->isLoaded());
    765     ASSERT(resource->canUseCacheValidator());
    766     ASSERT(!resource->resourceToRevalidate());
    767 
    768     ResourceRequest revalidatingRequest(resource->resourceRequest());
    769     addAdditionalRequestHeaders(revalidatingRequest, resource->type());
    770 
    771     const AtomicString& lastModified = resource->response().httpHeaderField("Last-Modified");
    772     const AtomicString& eTag = resource->response().httpHeaderField("ETag");
    773     if (!lastModified.isEmpty() || !eTag.isEmpty()) {
    774         ASSERT(context().cachePolicy(document()) != CachePolicyReload);
    775         if (context().cachePolicy(document()) == CachePolicyRevalidate)
    776             revalidatingRequest.setHTTPHeaderField("Cache-Control", "max-age=0");
    777         if (!lastModified.isEmpty())
    778             revalidatingRequest.setHTTPHeaderField("If-Modified-Since", lastModified);
    779         if (!eTag.isEmpty())
    780             revalidatingRequest.setHTTPHeaderField("If-None-Match", eTag);
    781     }
    782 
    783     ResourcePtr<Resource> newResource = createResource(resource->type(), revalidatingRequest, resource->encoding());
    784 
    785     WTF_LOG(ResourceLoading, "Resource %p created to revalidate %p", newResource.get(), resource);
    786     newResource->setResourceToRevalidate(resource);
    787 
    788     memoryCache()->remove(resource);
    789     memoryCache()->add(newResource.get());
    790     storeResourceTimingInitiatorInformation(newResource, request);
    791     TRACE_EVENT_ASYNC_BEGIN2("net", "Resource", newResource.get(), "url", newResource->url().string().ascii(), "priority", newResource->resourceRequest().priority());
    792     return newResource;
    793 }
    794 
    795 ResourcePtr<Resource> ResourceFetcher::loadResource(Resource::Type type, FetchRequest& request, const String& charset)
    796 {
    797     ASSERT(!memoryCache()->resourceForURL(request.resourceRequest().url()));
    798 
    799     WTF_LOG(ResourceLoading, "Loading Resource for '%s'.", request.resourceRequest().url().elidedString().latin1().data());
    800 
    801     addAdditionalRequestHeaders(request.mutableResourceRequest(), type);
    802     ResourcePtr<Resource> resource = createResource(type, request.mutableResourceRequest(), charset);
    803 
    804     memoryCache()->add(resource.get());
    805     storeResourceTimingInitiatorInformation(resource, request);
    806     TRACE_EVENT_ASYNC_BEGIN2("net", "Resource", resource.get(), "url", resource->url().string().ascii(), "priority", resource->resourceRequest().priority());
    807     return resource;
    808 }
    809 
    810 void ResourceFetcher::storeResourceTimingInitiatorInformation(const ResourcePtr<Resource>& resource, const FetchRequest& request)
    811 {
    812     if (request.options().requestInitiatorContext != DocumentContext)
    813         return;
    814 
    815     RefPtr<ResourceTimingInfo> info = ResourceTimingInfo::create(request.options().initiatorInfo.name, monotonicallyIncreasingTime());
    816 
    817     if (resource->type() == Resource::MainResource) {
    818         // <iframe>s should report the initial navigation requested by the parent document, but not subsequent navigations.
    819         if (frame()->ownerElement() && !frame()->ownerElement()->loadedNonEmptyDocument()) {
    820             info->setInitiatorType(frame()->ownerElement()->localName());
    821             m_resourceTimingInfoMap.add(resource.get(), info);
    822             frame()->ownerElement()->didLoadNonEmptyDocument();
    823         }
    824     } else {
    825         m_resourceTimingInfoMap.add(resource.get(), info);
    826     }
    827 }
    828 
    829 ResourceFetcher::RevalidationPolicy ResourceFetcher::determineRevalidationPolicy(Resource::Type type, ResourceRequest& request, bool forPreload, Resource* existingResource, FetchRequest::DeferOption defer) const
    830 {
    831     if (!existingResource)
    832         return Load;
    833 
    834     // We already have a preload going for this URL.
    835     if (forPreload && existingResource->isPreloaded())
    836         return Use;
    837 
    838     // If the same URL has been loaded as a different type, we need to reload.
    839     if (existingResource->type() != type) {
    840         WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to type mismatch.");
    841         return Reload;
    842     }
    843 
    844     // Do not load from cache if images are not enabled. The load for this image will be blocked
    845     // in ImageResource::load.
    846     if (FetchRequest::DeferredByClient == defer)
    847         return Reload;
    848 
    849     // Always use data uris.
    850     // FIXME: Extend this to non-images.
    851     if (type == Resource::Image && request.url().protocolIsData())
    852         return Use;
    853 
    854     if (!existingResource->canReuse(request))
    855         return Reload;
    856 
    857     // Certain requests (e.g., XHRs) might have manually set headers that require revalidation.
    858     // FIXME: In theory, this should be a Revalidate case. In practice, the MemoryCache revalidation path assumes a whole bunch
    859     // of things about how revalidation works that manual headers violate, so punt to Reload instead.
    860     if (request.isConditional())
    861         return Reload;
    862 
    863     // Don't reload resources while pasting.
    864     if (m_allowStaleResources)
    865         return Use;
    866 
    867     // Alwaus use preloads.
    868     if (existingResource->isPreloaded())
    869         return Use;
    870 
    871     // CachePolicyHistoryBuffer uses the cache no matter what.
    872     CachePolicy cachePolicy = context().cachePolicy(document());
    873     if (cachePolicy == CachePolicyHistoryBuffer)
    874         return Use;
    875 
    876     // Don't reuse resources with Cache-control: no-store.
    877     if (existingResource->response().cacheControlContainsNoStore()) {
    878         WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to Cache-control: no-store.");
    879         return Reload;
    880     }
    881 
    882     // If credentials were sent with the previous request and won't be
    883     // with this one, or vice versa, re-fetch the resource.
    884     //
    885     // This helps with the case where the server sends back
    886     // "Access-Control-Allow-Origin: *" all the time, but some of the
    887     // client's requests are made without CORS and some with.
    888     if (existingResource->resourceRequest().allowCookies() != request.allowCookies()) {
    889         WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to difference in credentials settings.");
    890         return Reload;
    891     }
    892 
    893     // During the initial load, avoid loading the same resource multiple times for a single document,
    894     // even if the cache policies would tell us to. Raw resources are exempted.
    895     if (type != Resource::Raw && document() && !document()->loadEventFinished() && m_validatedURLs.contains(existingResource->url()))
    896         return Use;
    897 
    898     // CachePolicyReload always reloads
    899     if (cachePolicy == CachePolicyReload) {
    900         WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to CachePolicyReload.");
    901         return Reload;
    902     }
    903 
    904     // We'll try to reload the resource if it failed last time.
    905     if (existingResource->errorOccurred()) {
    906         WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicye reloading due to resource being in the error state");
    907         return Reload;
    908     }
    909 
    910     // For resources that are not yet loaded we ignore the cache policy.
    911     if (existingResource->isLoading())
    912         return Use;
    913 
    914     // Check if the cache headers requires us to revalidate (cache expiration for example).
    915     if (cachePolicy == CachePolicyRevalidate || existingResource->mustRevalidateDueToCacheHeaders()) {
    916         // See if the resource has usable ETag or Last-modified headers.
    917         if (existingResource->canUseCacheValidator())
    918             return Revalidate;
    919 
    920         // No, must reload.
    921         WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to missing cache validators.");
    922         return Reload;
    923     }
    924 
    925     return Use;
    926 }
    927 
    928 void ResourceFetcher::printAccessDeniedMessage(const KURL& url) const
    929 {
    930     if (url.isNull())
    931         return;
    932 
    933     if (!frame())
    934         return;
    935 
    936     String message;
    937     if (!m_document || m_document->url().isNull())
    938         message = "Unsafe attempt to load URL " + url.elidedString() + '.';
    939     else
    940         message = "Unsafe attempt to load URL " + url.elidedString() + " from frame with URL " + m_document->url().elidedString() + ". Domains, protocols and ports must match.\n";
    941 
    942     frame()->document()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, message);
    943 }
    944 
    945 void ResourceFetcher::setAutoLoadImages(bool enable)
    946 {
    947     if (enable == m_autoLoadImages)
    948         return;
    949 
    950     m_autoLoadImages = enable;
    951 
    952     if (!m_autoLoadImages)
    953         return;
    954 
    955     reloadImagesIfNotDeferred();
    956 }
    957 
    958 void ResourceFetcher::setImagesEnabled(bool enable)
    959 {
    960     if (enable == m_imagesEnabled)
    961         return;
    962 
    963     m_imagesEnabled = enable;
    964 
    965     if (!m_imagesEnabled)
    966         return;
    967 
    968     reloadImagesIfNotDeferred();
    969 }
    970 
    971 bool ResourceFetcher::clientDefersImage(const KURL& url) const
    972 {
    973     return frame() && !frame()->loader().client()->allowImage(m_imagesEnabled, url);
    974 }
    975 
    976 bool ResourceFetcher::shouldDeferImageLoad(const KURL& url) const
    977 {
    978     return clientDefersImage(url) || !m_autoLoadImages;
    979 }
    980 
    981 void ResourceFetcher::reloadImagesIfNotDeferred()
    982 {
    983     DocumentResourceMap::iterator end = m_documentResources.end();
    984     for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it) {
    985         Resource* resource = it->value.get();
    986         if (resource->type() == Resource::Image && resource->stillNeedsLoad() && !clientDefersImage(resource->url()))
    987             const_cast<Resource*>(resource)->load(this, defaultResourceOptions());
    988     }
    989 }
    990 
    991 void ResourceFetcher::redirectReceived(Resource* resource, const ResourceResponse& redirectResponse)
    992 {
    993     ResourceTimingInfoMap::iterator it = m_resourceTimingInfoMap.find(resource);
    994     if (it != m_resourceTimingInfoMap.end())
    995         it->value->addRedirect(redirectResponse);
    996 }
    997 
    998 void ResourceFetcher::didLoadResource(Resource* resource)
    999 {
   1000     RefPtr<DocumentLoader> protectDocumentLoader(m_documentLoader);
   1001     RefPtr<Document> protectDocument(m_document);
   1002 
   1003     if (resource && resource->response().isHTTP() && ((!resource->errorOccurred() && !resource->wasCanceled()) || resource->response().httpStatusCode() == 304) && document()) {
   1004         ResourceTimingInfoMap::iterator it = m_resourceTimingInfoMap.find(resource);
   1005         if (it != m_resourceTimingInfoMap.end()) {
   1006             RefPtr<ResourceTimingInfo> info = it->value;
   1007             m_resourceTimingInfoMap.remove(it);
   1008             populateResourceTiming(info.get(), resource, false);
   1009             reportResourceTiming(info.get(), document(), resource->type() == Resource::MainResource);
   1010         }
   1011     }
   1012 
   1013     if (frame())
   1014         frame()->loader().loadDone();
   1015     performPostLoadActions();
   1016 
   1017     if (!m_garbageCollectDocumentResourcesTimer.isActive())
   1018         m_garbageCollectDocumentResourcesTimer.startOneShot(0);
   1019 }
   1020 
   1021 // Garbage collecting m_documentResources is a workaround for the
   1022 // ResourcePtrs on the RHS being strong references. Ideally this
   1023 // would be a weak map, however ResourcePtrs perform additional
   1024 // bookkeeping on Resources, so instead pseudo-GC them -- when the
   1025 // reference count reaches 1, m_documentResources is the only reference, so
   1026 // remove it from the map.
   1027 void ResourceFetcher::garbageCollectDocumentResourcesTimerFired(Timer<ResourceFetcher>* timer)
   1028 {
   1029     ASSERT_UNUSED(timer, timer == &m_garbageCollectDocumentResourcesTimer);
   1030     garbageCollectDocumentResources();
   1031 }
   1032 
   1033 void ResourceFetcher::garbageCollectDocumentResources()
   1034 {
   1035     typedef Vector<String, 10> StringVector;
   1036     StringVector resourcesToDelete;
   1037 
   1038     for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != m_documentResources.end(); ++it) {
   1039         if (it->value->hasOneHandle())
   1040             resourcesToDelete.append(it->key);
   1041     }
   1042 
   1043     for (StringVector::const_iterator it = resourcesToDelete.begin(); it != resourcesToDelete.end(); ++it)
   1044         m_documentResources.remove(*it);
   1045 }
   1046 
   1047 void ResourceFetcher::performPostLoadActions()
   1048 {
   1049     checkForPendingPreloads();
   1050 }
   1051 
   1052 void ResourceFetcher::notifyLoadedFromMemoryCache(Resource* resource)
   1053 {
   1054     if (!frame() || !frame()->page() || resource->status() != Resource::Cached || m_validatedURLs.contains(resource->url()))
   1055         return;
   1056     if (!resource->shouldSendResourceLoadCallbacks())
   1057         return;
   1058 
   1059     ResourceRequest request(resource->url());
   1060     unsigned long identifier = createUniqueIdentifier();
   1061     context().dispatchDidLoadResourceFromMemoryCache(request, resource->response());
   1062     // FIXME: If willSendRequest changes the request, we don't respect it.
   1063     willSendRequest(identifier, request, ResourceResponse(), resource->options());
   1064     InspectorInstrumentation::markResourceAsCached(frame()->page(), identifier);
   1065     context().sendRemainingDelegateMessages(m_documentLoader, identifier, resource->response(), resource->encodedSize());
   1066 }
   1067 
   1068 void ResourceFetcher::incrementRequestCount(const Resource* res)
   1069 {
   1070     if (res->ignoreForRequestCount())
   1071         return;
   1072 
   1073     ++m_requestCount;
   1074 }
   1075 
   1076 void ResourceFetcher::decrementRequestCount(const Resource* res)
   1077 {
   1078     if (res->ignoreForRequestCount())
   1079         return;
   1080 
   1081     --m_requestCount;
   1082     ASSERT(m_requestCount > -1);
   1083 }
   1084 
   1085 void ResourceFetcher::preload(Resource::Type type, FetchRequest& request, const String& charset)
   1086 {
   1087     requestPreload(type, request, charset);
   1088 }
   1089 
   1090 void ResourceFetcher::checkForPendingPreloads()
   1091 {
   1092     // FIXME: It seems wrong to poke body()->renderer() here.
   1093     if (m_pendingPreloads.isEmpty() || !m_document->body() || !m_document->body()->renderer())
   1094         return;
   1095     while (!m_pendingPreloads.isEmpty()) {
   1096         PendingPreload preload = m_pendingPreloads.takeFirst();
   1097         // 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).
   1098         if (!cachedResource(preload.m_request.resourceRequest().url()))
   1099             requestPreload(preload.m_type, preload.m_request, preload.m_charset);
   1100     }
   1101     m_pendingPreloads.clear();
   1102 }
   1103 
   1104 void ResourceFetcher::requestPreload(Resource::Type type, FetchRequest& request, const String& charset)
   1105 {
   1106     String encoding;
   1107     if (type == Resource::Script || type == Resource::CSSStyleSheet)
   1108         encoding = charset.isEmpty() ? m_document->charset().string() : charset;
   1109 
   1110     request.setCharset(encoding);
   1111     request.setForPreload(true);
   1112 
   1113     ResourcePtr<Resource> resource = requestResource(type, request);
   1114     if (!resource || (m_preloads && m_preloads->contains(resource.get())))
   1115         return;
   1116     TRACE_EVENT_ASYNC_STEP_INTO0("net", "Resource", resource.get(), "Preload");
   1117     resource->increasePreloadCount();
   1118 
   1119     if (!m_preloads)
   1120         m_preloads = adoptPtr(new ListHashSet<Resource*>);
   1121     m_preloads->add(resource.get());
   1122 
   1123 #if PRELOAD_DEBUG
   1124     printf("PRELOADING %s\n",  resource->url().latin1().data());
   1125 #endif
   1126 }
   1127 
   1128 bool ResourceFetcher::isPreloaded(const String& urlString) const
   1129 {
   1130     const KURL& url = m_document->completeURL(urlString);
   1131 
   1132     if (m_preloads) {
   1133         ListHashSet<Resource*>::iterator end = m_preloads->end();
   1134         for (ListHashSet<Resource*>::iterator it = m_preloads->begin(); it != end; ++it) {
   1135             Resource* resource = *it;
   1136             if (resource->url() == url)
   1137                 return true;
   1138         }
   1139     }
   1140 
   1141     Deque<PendingPreload>::const_iterator dequeEnd = m_pendingPreloads.end();
   1142     for (Deque<PendingPreload>::const_iterator it = m_pendingPreloads.begin(); it != dequeEnd; ++it) {
   1143         PendingPreload pendingPreload = *it;
   1144         if (pendingPreload.m_request.resourceRequest().url() == url)
   1145             return true;
   1146     }
   1147     return false;
   1148 }
   1149 
   1150 void ResourceFetcher::clearPreloads()
   1151 {
   1152 #if PRELOAD_DEBUG
   1153     printPreloadStats();
   1154 #endif
   1155     if (!m_preloads)
   1156         return;
   1157 
   1158     ListHashSet<Resource*>::iterator end = m_preloads->end();
   1159     for (ListHashSet<Resource*>::iterator it = m_preloads->begin(); it != end; ++it) {
   1160         Resource* res = *it;
   1161         res->decreasePreloadCount();
   1162         bool deleted = res->deleteIfPossible();
   1163         if (!deleted && res->preloadResult() == Resource::PreloadNotReferenced)
   1164             memoryCache()->remove(res);
   1165     }
   1166     m_preloads.clear();
   1167 }
   1168 
   1169 void ResourceFetcher::clearPendingPreloads()
   1170 {
   1171     m_pendingPreloads.clear();
   1172 }
   1173 
   1174 void ResourceFetcher::didFinishLoading(const Resource* resource, double finishTime, const ResourceLoaderOptions& options)
   1175 {
   1176     TRACE_EVENT_ASYNC_END0("net", "Resource", resource);
   1177     if (options.sendLoadCallbacks != SendCallbacks)
   1178         return;
   1179     context().dispatchDidFinishLoading(m_documentLoader, resource->identifier(), finishTime);
   1180 }
   1181 
   1182 void ResourceFetcher::didChangeLoadingPriority(const Resource* resource, ResourceLoadPriority loadPriority)
   1183 {
   1184     TRACE_EVENT_ASYNC_STEP_INTO1("net", "Resource", resource, "ChangePriority", "priority", loadPriority);
   1185     context().dispatchDidChangeResourcePriority(resource->identifier(), loadPriority);
   1186 }
   1187 
   1188 void ResourceFetcher::didFailLoading(const Resource* resource, const ResourceError& error, const ResourceLoaderOptions& options)
   1189 {
   1190     TRACE_EVENT_ASYNC_END0("net", "Resource", resource);
   1191     if (options.sendLoadCallbacks != SendCallbacks)
   1192         return;
   1193     context().dispatchDidFail(m_documentLoader, resource->identifier(), error);
   1194 }
   1195 
   1196 void ResourceFetcher::willSendRequest(unsigned long identifier, ResourceRequest& request, const ResourceResponse& redirectResponse, const ResourceLoaderOptions& options)
   1197 {
   1198     if (options.sendLoadCallbacks == SendCallbacks)
   1199         context().dispatchWillSendRequest(m_documentLoader, identifier, request, redirectResponse, options.initiatorInfo);
   1200     else
   1201         InspectorInstrumentation::willSendRequest(frame(), identifier, m_documentLoader, request, redirectResponse, options.initiatorInfo);
   1202 }
   1203 
   1204 void ResourceFetcher::didReceiveResponse(const Resource* resource, const ResourceResponse& response, const ResourceLoaderOptions& options)
   1205 {
   1206     if (options.sendLoadCallbacks != SendCallbacks)
   1207         return;
   1208     context().dispatchDidReceiveResponse(m_documentLoader, resource->identifier(), response, resource->loader());
   1209 }
   1210 
   1211 void ResourceFetcher::didReceiveData(const Resource* resource, const char* data, int dataLength, int encodedDataLength, const ResourceLoaderOptions& options)
   1212 {
   1213     // FIXME: use frame of master document for imported documents.
   1214     InspectorInstrumentationCookie cookie = InspectorInstrumentation::willReceiveResourceData(frame(), resource->identifier(), encodedDataLength);
   1215     if (options.sendLoadCallbacks != SendCallbacks)
   1216         return;
   1217     context().dispatchDidReceiveData(m_documentLoader, resource->identifier(), data, dataLength, encodedDataLength);
   1218     InspectorInstrumentation::didReceiveResourceData(cookie);
   1219 }
   1220 
   1221 void ResourceFetcher::didDownloadData(const Resource* resource, int dataLength, int encodedDataLength, const ResourceLoaderOptions& options)
   1222 {
   1223     InspectorInstrumentationCookie cookie = InspectorInstrumentation::willReceiveResourceData(frame(), resource->identifier(), encodedDataLength);
   1224     if (options.sendLoadCallbacks != SendCallbacks)
   1225         return;
   1226     context().dispatchDidDownloadData(m_documentLoader, resource->identifier(), dataLength, encodedDataLength);
   1227     InspectorInstrumentation::didReceiveResourceData(cookie);
   1228 }
   1229 
   1230 void ResourceFetcher::subresourceLoaderFinishedLoadingOnePart(ResourceLoader* loader)
   1231 {
   1232     if (m_multipartLoaders)
   1233         m_multipartLoaders->add(loader);
   1234     if (m_loaders)
   1235         m_loaders->remove(loader);
   1236     if (Frame* frame = this->frame())
   1237         return frame->loader().checkLoadComplete(m_documentLoader);
   1238 }
   1239 
   1240 void ResourceFetcher::didInitializeResourceLoader(ResourceLoader* loader)
   1241 {
   1242     if (!m_document)
   1243         return;
   1244     if (!m_loaders)
   1245         m_loaders = adoptPtr(new ResourceLoaderSet());
   1246     ASSERT(!m_loaders->contains(loader));
   1247     m_loaders->add(loader);
   1248 }
   1249 
   1250 void ResourceFetcher::willTerminateResourceLoader(ResourceLoader* loader)
   1251 {
   1252     if (!m_loaders || !m_loaders->contains(loader))
   1253         return;
   1254     m_loaders->remove(loader);
   1255     if (Frame* frame = this->frame())
   1256         frame->loader().checkLoadComplete(m_documentLoader);
   1257 }
   1258 
   1259 void ResourceFetcher::willStartLoadingResource(ResourceRequest& request)
   1260 {
   1261     if (m_documentLoader)
   1262         m_documentLoader->applicationCacheHost()->willStartLoadingResource(request);
   1263 }
   1264 
   1265 void ResourceFetcher::stopFetching()
   1266 {
   1267     if (m_multipartLoaders)
   1268         m_multipartLoaders->cancelAll();
   1269     if (m_loaders)
   1270         m_loaders->cancelAll();
   1271 }
   1272 
   1273 bool ResourceFetcher::isFetching() const
   1274 {
   1275     return m_loaders && !m_loaders->isEmpty();
   1276 }
   1277 
   1278 void ResourceFetcher::setDefersLoading(bool defers)
   1279 {
   1280     if (m_loaders)
   1281         m_loaders->setAllDefersLoading(defers);
   1282 }
   1283 
   1284 bool ResourceFetcher::defersLoading() const
   1285 {
   1286     if (Frame* frame = this->frame())
   1287         return frame->page()->defersLoading();
   1288     return false;
   1289 }
   1290 
   1291 bool ResourceFetcher::isLoadedBy(ResourceLoaderHost* possibleOwner) const
   1292 {
   1293     return this == possibleOwner;
   1294 }
   1295 
   1296 bool ResourceFetcher::shouldRequest(Resource* resource, const ResourceRequest& request, const ResourceLoaderOptions& options)
   1297 {
   1298     if (!canRequest(resource->type(), request.url(), options, false, FetchRequest::UseDefaultOriginRestrictionForType))
   1299         return false;
   1300     if (resource->type() == Resource::Image && shouldDeferImageLoad(request.url()))
   1301         return false;
   1302     return true;
   1303 }
   1304 
   1305 void ResourceFetcher::refResourceLoaderHost()
   1306 {
   1307     ref();
   1308 }
   1309 
   1310 void ResourceFetcher::derefResourceLoaderHost()
   1311 {
   1312     deref();
   1313 }
   1314 
   1315 #if PRELOAD_DEBUG
   1316 void ResourceFetcher::printPreloadStats()
   1317 {
   1318     unsigned scripts = 0;
   1319     unsigned scriptMisses = 0;
   1320     unsigned stylesheets = 0;
   1321     unsigned stylesheetMisses = 0;
   1322     unsigned images = 0;
   1323     unsigned imageMisses = 0;
   1324     ListHashSet<Resource*>::iterator end = m_preloads.end();
   1325     for (ListHashSet<Resource*>::iterator it = m_preloads.begin(); it != end; ++it) {
   1326         Resource* res = *it;
   1327         if (res->preloadResult() == Resource::PreloadNotReferenced)
   1328             printf("!! UNREFERENCED PRELOAD %s\n", res->url().latin1().data());
   1329         else if (res->preloadResult() == Resource::PreloadReferencedWhileComplete)
   1330             printf("HIT COMPLETE PRELOAD %s\n", res->url().latin1().data());
   1331         else if (res->preloadResult() == Resource::PreloadReferencedWhileLoading)
   1332             printf("HIT LOADING PRELOAD %s\n", res->url().latin1().data());
   1333 
   1334         if (res->type() == Resource::Script) {
   1335             scripts++;
   1336             if (res->preloadResult() < Resource::PreloadReferencedWhileLoading)
   1337                 scriptMisses++;
   1338         } else if (res->type() == Resource::CSSStyleSheet) {
   1339             stylesheets++;
   1340             if (res->preloadResult() < Resource::PreloadReferencedWhileLoading)
   1341                 stylesheetMisses++;
   1342         } else {
   1343             images++;
   1344             if (res->preloadResult() < Resource::PreloadReferencedWhileLoading)
   1345                 imageMisses++;
   1346         }
   1347 
   1348         if (res->errorOccurred())
   1349             memoryCache()->remove(res);
   1350 
   1351         res->decreasePreloadCount();
   1352     }
   1353     m_preloads.clear();
   1354 
   1355     if (scripts)
   1356         printf("SCRIPTS: %d (%d hits, hit rate %d%%)\n", scripts, scripts - scriptMisses, (scripts - scriptMisses) * 100 / scripts);
   1357     if (stylesheets)
   1358         printf("STYLESHEETS: %d (%d hits, hit rate %d%%)\n", stylesheets, stylesheets - stylesheetMisses, (stylesheets - stylesheetMisses) * 100 / stylesheets);
   1359     if (images)
   1360         printf("IMAGES:  %d (%d hits, hit rate %d%%)\n", images, images - imageMisses, (images - imageMisses) * 100 / images);
   1361 }
   1362 #endif
   1363 
   1364 const ResourceLoaderOptions& ResourceFetcher::defaultResourceOptions()
   1365 {
   1366     DEFINE_STATIC_LOCAL(ResourceLoaderOptions, options, (SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, ClientRequestedCredentials, AskClientForCrossOriginCredentials, DoSecurityCheck, CheckContentSecurityPolicy, DocumentContext));
   1367     return options;
   1368 }
   1369 
   1370 }
   1371