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