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