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