• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2     Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
3     Copyright (C) 2001 Dirk Mueller (mueller@kde.org)
4     Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
5     Copyright (C) 2004, 2005, 2006, 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 
createResource(Resource::Type type,const ResourceRequest & request,const String & charset)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 
loadPriority(Resource::Type type,const FetchRequest & request)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 
resourceFromDataURIRequest(const ResourceRequest & request,const ResourceLoaderOptions & resourceOptions)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 
populateResourceTiming(ResourceTimingInfo * info,Resource * resource,bool clearLoadTimings)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 
reportResourceTiming(ResourceTimingInfo * info,Document * initiatorDocument,bool isMainResource)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 
requestTargetType(const ResourceFetcher * fetcher,const ResourceRequest & request,Resource::Type type)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 
ResourceFetcher(DocumentLoader * documentLoader)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 
~ResourceFetcher()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 
cachedResource(const String & resourceURL) const245 Resource* ResourceFetcher::cachedResource(const String& resourceURL) const
246 {
247     KURL url = m_document->completeURL(resourceURL);
248     return cachedResource(url);
249 }
250 
cachedResource(const KURL & resourceURL) const251 Resource* ResourceFetcher::cachedResource(const KURL& resourceURL) const
252 {
253     KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(resourceURL);
254     return m_documentResources.get(url).get();
255 }
256 
frame() const257 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 
context() const266 FetchContext& ResourceFetcher::context() const
267 {
268     if (Frame* frame = this->frame())
269         return frame->fetchContext();
270     return FetchContext::nullInstance();
271 }
272 
fetchSynchronously(FetchRequest & request)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 
fetchImage(FetchRequest & request)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 
preCacheDataURIImage(const FetchRequest & request)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 
fetchFont(FetchRequest & request)313 ResourcePtr<FontResource> ResourceFetcher::fetchFont(FetchRequest& request)
314 {
315     return toFontResource(requestResource(Resource::Font, request));
316 }
317 
fetchShader(FetchRequest & request)318 ResourcePtr<ShaderResource> ResourceFetcher::fetchShader(FetchRequest& request)
319 {
320     return toShaderResource(requestResource(Resource::Shader, request));
321 }
322 
fetchImport(FetchRequest & request)323 ResourcePtr<RawResource> ResourceFetcher::fetchImport(FetchRequest& request)
324 {
325     return toRawResource(requestResource(Resource::ImportResource, request));
326 }
327 
fetchCSSStyleSheet(FetchRequest & request)328 ResourcePtr<CSSStyleSheetResource> ResourceFetcher::fetchCSSStyleSheet(FetchRequest& request)
329 {
330     return toCSSStyleSheetResource(requestResource(Resource::CSSStyleSheet, request));
331 }
332 
fetchUserCSSStyleSheet(FetchRequest & request)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 
fetchScript(FetchRequest & request)347 ResourcePtr<ScriptResource> ResourceFetcher::fetchScript(FetchRequest& request)
348 {
349     return toScriptResource(requestResource(Resource::Script, request));
350 }
351 
fetchXSLStyleSheet(FetchRequest & request)352 ResourcePtr<XSLStyleSheetResource> ResourceFetcher::fetchXSLStyleSheet(FetchRequest& request)
353 {
354     ASSERT(RuntimeEnabledFeatures::xsltEnabled());
355     return toXSLStyleSheetResource(requestResource(Resource::XSLStyleSheet, request));
356 }
357 
fetchSVGDocument(FetchRequest & request)358 ResourcePtr<DocumentResource> ResourceFetcher::fetchSVGDocument(FetchRequest& request)
359 {
360     return toDocumentResource(requestResource(Resource::SVGDocument, request));
361 }
362 
fetchLinkResource(Resource::Type type,FetchRequest & request)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 
fetchRawResource(FetchRequest & request)370 ResourcePtr<RawResource> ResourceFetcher::fetchRawResource(FetchRequest& request)
371 {
372     return toRawResource(requestResource(Resource::Raw, request));
373 }
374 
fetchMainResource(FetchRequest & request)375 ResourcePtr<RawResource> ResourceFetcher::fetchMainResource(FetchRequest& request)
376 {
377     return toRawResource(requestResource(Resource::MainResource, request));
378 }
379 
checkInsecureContent(Resource::Type type,const KURL & url,MixedContentBlockingTreatment treatment) const380 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 
canRequest(Resource::Type type,const KURL & url,const ResourceLoaderOptions & options,bool forPreload,FetchRequest::OriginRestriction originRestriction)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 
canAccess(Resource * resource,CORSEnabled corsEnabled,FetchRequest::OriginRestriction originRestriction)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 
shouldLoadNewResource() const564 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 
resourceNeedsLoad(Resource * resource,const FetchRequest & request,RevalidationPolicy policy)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 
requestResource(Resource::Type type,FetchRequest & request)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 
resourceTimingReportTimerFired(Timer<ResourceFetcher> * timer)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 
determineTargetType(ResourceRequest & request,Resource::Type type)709 void ResourceFetcher::determineTargetType(ResourceRequest& request, Resource::Type type)
710 {
711     ResourceRequest::TargetType targetType = requestTargetType(this, request, type);
712     request.setTargetType(targetType);
713 }
714 
resourceRequestCachePolicy(const ResourceRequest & request,Resource::Type type)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 
addAdditionalRequestHeaders(ResourceRequest & request,Resource::Type type)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 
revalidateResource(const FetchRequest & request,Resource * resource)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 
loadResource(Resource::Type type,FetchRequest & request,const String & charset)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 
storeResourceTimingInitiatorInformation(const ResourcePtr<Resource> & resource,const FetchRequest & request)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 
determineRevalidationPolicy(Resource::Type type,ResourceRequest & request,bool forPreload,Resource * existingResource,FetchRequest::DeferOption defer) const829 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 
printAccessDeniedMessage(const KURL & url) const928 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 
setAutoLoadImages(bool enable)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 
setImagesEnabled(bool enable)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 
clientDefersImage(const KURL & url) const971 bool ResourceFetcher::clientDefersImage(const KURL& url) const
972 {
973     return frame() && !frame()->loader().client()->allowImage(m_imagesEnabled, url);
974 }
975 
shouldDeferImageLoad(const KURL & url) const976 bool ResourceFetcher::shouldDeferImageLoad(const KURL& url) const
977 {
978     return clientDefersImage(url) || !m_autoLoadImages;
979 }
980 
reloadImagesIfNotDeferred()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 
redirectReceived(Resource * resource,const ResourceResponse & redirectResponse)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 
didLoadResource(Resource * resource)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.
garbageCollectDocumentResourcesTimerFired(Timer<ResourceFetcher> * timer)1027 void ResourceFetcher::garbageCollectDocumentResourcesTimerFired(Timer<ResourceFetcher>* timer)
1028 {
1029     ASSERT_UNUSED(timer, timer == &m_garbageCollectDocumentResourcesTimer);
1030     garbageCollectDocumentResources();
1031 }
1032 
garbageCollectDocumentResources()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 
performPostLoadActions()1047 void ResourceFetcher::performPostLoadActions()
1048 {
1049     checkForPendingPreloads();
1050 }
1051 
notifyLoadedFromMemoryCache(Resource * resource)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 
incrementRequestCount(const Resource * res)1068 void ResourceFetcher::incrementRequestCount(const Resource* res)
1069 {
1070     if (res->ignoreForRequestCount())
1071         return;
1072 
1073     ++m_requestCount;
1074 }
1075 
decrementRequestCount(const Resource * res)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 
preload(Resource::Type type,FetchRequest & request,const String & charset)1085 void ResourceFetcher::preload(Resource::Type type, FetchRequest& request, const String& charset)
1086 {
1087     requestPreload(type, request, charset);
1088 }
1089 
checkForPendingPreloads()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 
requestPreload(Resource::Type type,FetchRequest & request,const String & charset)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 
isPreloaded(const String & urlString) const1128 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 
clearPreloads()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 
clearPendingPreloads()1169 void ResourceFetcher::clearPendingPreloads()
1170 {
1171     m_pendingPreloads.clear();
1172 }
1173 
didFinishLoading(const Resource * resource,double finishTime,const ResourceLoaderOptions & options)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 
didChangeLoadingPriority(const Resource * resource,ResourceLoadPriority loadPriority)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 
didFailLoading(const Resource * resource,const ResourceError & error,const ResourceLoaderOptions & options)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 
willSendRequest(unsigned long identifier,ResourceRequest & request,const ResourceResponse & redirectResponse,const ResourceLoaderOptions & options)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 
didReceiveResponse(const Resource * resource,const ResourceResponse & response,const ResourceLoaderOptions & options)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 
didReceiveData(const Resource * resource,const char * data,int dataLength,int encodedDataLength,const ResourceLoaderOptions & options)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 
didDownloadData(const Resource * resource,int dataLength,int encodedDataLength,const ResourceLoaderOptions & options)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 
subresourceLoaderFinishedLoadingOnePart(ResourceLoader * loader)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 
didInitializeResourceLoader(ResourceLoader * loader)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 
willTerminateResourceLoader(ResourceLoader * loader)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 
willStartLoadingResource(ResourceRequest & request)1259 void ResourceFetcher::willStartLoadingResource(ResourceRequest& request)
1260 {
1261     if (m_documentLoader)
1262         m_documentLoader->applicationCacheHost()->willStartLoadingResource(request);
1263 }
1264 
stopFetching()1265 void ResourceFetcher::stopFetching()
1266 {
1267     if (m_multipartLoaders)
1268         m_multipartLoaders->cancelAll();
1269     if (m_loaders)
1270         m_loaders->cancelAll();
1271 }
1272 
isFetching() const1273 bool ResourceFetcher::isFetching() const
1274 {
1275     return m_loaders && !m_loaders->isEmpty();
1276 }
1277 
setDefersLoading(bool defers)1278 void ResourceFetcher::setDefersLoading(bool defers)
1279 {
1280     if (m_loaders)
1281         m_loaders->setAllDefersLoading(defers);
1282 }
1283 
defersLoading() const1284 bool ResourceFetcher::defersLoading() const
1285 {
1286     if (Frame* frame = this->frame())
1287         return frame->page()->defersLoading();
1288     return false;
1289 }
1290 
isLoadedBy(ResourceLoaderHost * possibleOwner) const1291 bool ResourceFetcher::isLoadedBy(ResourceLoaderHost* possibleOwner) const
1292 {
1293     return this == possibleOwner;
1294 }
1295 
shouldRequest(Resource * resource,const ResourceRequest & request,const ResourceLoaderOptions & options)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 
refResourceLoaderHost()1305 void ResourceFetcher::refResourceLoaderHost()
1306 {
1307     ref();
1308 }
1309 
derefResourceLoaderHost()1310 void ResourceFetcher::derefResourceLoaderHost()
1311 {
1312     deref();
1313 }
1314 
1315 #if PRELOAD_DEBUG
printPreloadStats()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 
defaultResourceOptions()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