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/
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.
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.
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.
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.
28 #include "core/fetch/ResourceFetcher.h"
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/frame/FrameHost.h"
47 #include "core/frame/LocalDOMWindow.h"
48 #include "core/frame/LocalFrame.h"
49 #include "core/frame/Settings.h"
50 #include "core/frame/csp/ContentSecurityPolicy.h"
51 #include "core/html/HTMLElement.h"
52 #include "core/html/HTMLFrameOwnerElement.h"
53 #include "core/html/imports/HTMLImportsController.h"
54 #include "core/inspector/ConsoleMessage.h"
55 #include "core/inspector/InspectorInstrumentation.h"
56 #include "core/loader/DocumentLoader.h"
57 #include "core/loader/FrameLoader.h"
58 #include "core/loader/FrameLoaderClient.h"
59 #include "core/loader/PingLoader.h"
60 #include "core/loader/SubstituteData.h"
61 #include "core/loader/UniqueIdentifier.h"
62 #include "core/loader/appcache/ApplicationCacheHost.h"
63 #include "core/timing/Performance.h"
64 #include "core/timing/ResourceTimingInfo.h"
65 #include "core/svg/graphics/SVGImageChromeClient.h"
66 #include "platform/Logging.h"
67 #include "platform/RuntimeEnabledFeatures.h"
68 #include "platform/TraceEvent.h"
69 #include "platform/weborigin/SchemeRegistry.h"
70 #include "platform/weborigin/SecurityOrigin.h"
71 #include "platform/weborigin/SecurityPolicy.h"
72 #include "public/platform/Platform.h"
73 #include "public/platform/WebURL.h"
74 #include "public/platform/WebURLRequest.h"
75 #include "wtf/text/CString.h"
76 #include "wtf/text/WTFString.h"
78 #define PRELOAD_DEBUG 0
80 using blink::WebURLRequest;
84 static Resource* createResource(Resource::Type type, const ResourceRequest& request, const String& charset)
88 return new ImageResource(request);
89 case Resource::CSSStyleSheet:
90 return new CSSStyleSheetResource(request, charset);
91 case Resource::Script:
92 return new ScriptResource(request, charset);
93 case Resource::SVGDocument:
94 return new DocumentResource(request, Resource::SVGDocument);
96 return new FontResource(request);
97 case Resource::MainResource:
99 case Resource::TextTrack:
100 case Resource::Media:
101 return new RawResource(request, type);
102 case Resource::XSLStyleSheet:
103 return new XSLStyleSheetResource(request, charset);
104 case Resource::LinkPrefetch:
105 return new Resource(request, Resource::LinkPrefetch);
106 case Resource::LinkSubresource:
107 return new Resource(request, Resource::LinkSubresource);
108 case Resource::ImportResource:
109 return new RawResource(request, type);
112 ASSERT_NOT_REACHED();
116 static ResourceLoadPriority loadPriority(Resource::Type type, const FetchRequest& request)
118 if (request.priority() != ResourceLoadPriorityUnresolved)
119 return request.priority();
122 case Resource::MainResource:
123 return ResourceLoadPriorityVeryHigh;
124 case Resource::CSSStyleSheet:
125 return ResourceLoadPriorityHigh;
127 return request.options().synchronousPolicy == RequestSynchronously ? ResourceLoadPriorityVeryHigh : ResourceLoadPriorityMedium;
128 case Resource::Script:
129 // Async scripts do not block the parser so they get the lowest priority and can be
130 // loaded in parser order with images.
131 if (FetchRequest::LazyLoad == request.defer())
132 return ResourceLoadPriorityLow;
133 return ResourceLoadPriorityMedium;
135 case Resource::ImportResource:
136 return ResourceLoadPriorityMedium;
137 case Resource::Image:
138 // Default images to VeryLow, and promote whatever is visible. This improves
139 // speed-index by ~5% on average, ~14% at the 99th percentile.
140 return ResourceLoadPriorityVeryLow;
141 case Resource::Media:
142 return ResourceLoadPriorityLow;
143 case Resource::XSLStyleSheet:
144 ASSERT(RuntimeEnabledFeatures::xsltEnabled());
145 return ResourceLoadPriorityHigh;
146 case Resource::SVGDocument:
147 return ResourceLoadPriorityLow;
148 case Resource::LinkPrefetch:
149 return ResourceLoadPriorityVeryLow;
150 case Resource::LinkSubresource:
151 return ResourceLoadPriorityLow;
152 case Resource::TextTrack:
153 return ResourceLoadPriorityLow;
155 ASSERT_NOT_REACHED();
156 return ResourceLoadPriorityUnresolved;
159 static Resource* resourceFromDataURIRequest(const ResourceRequest& request, const ResourceLoaderOptions& resourceOptions, const String& cacheIdentifier)
161 const KURL& url = request.url();
162 ASSERT(url.protocolIsData());
164 blink::WebString mimetype;
165 blink::WebString charset;
166 RefPtr<SharedBuffer> data = PassRefPtr<SharedBuffer>(blink::Platform::current()->parseDataURL(url, mimetype, charset));
169 ResourceResponse response(url, mimetype, data->size(), charset, String());
171 Resource* resource = createResource(Resource::Image, request, charset);
172 resource->setOptions(resourceOptions);
173 // FIXME: We should provide a body stream here.
174 resource->responseReceived(response, nullptr);
176 resource->setResourceBuffer(data);
177 resource->setCacheIdentifier(cacheIdentifier);
182 static void populateResourceTiming(ResourceTimingInfo* info, Resource* resource, bool clearLoadTimings)
184 info->setInitialRequest(resource->resourceRequest());
185 info->setFinalResponse(resource->response());
186 if (clearLoadTimings) {
187 info->clearLoadTimings();
188 info->setLoadFinishTime(info->initialTime());
190 info->setLoadFinishTime(resource->loadFinishTime());
194 static void reportResourceTiming(ResourceTimingInfo* info, Document* initiatorDocument, bool isMainResource)
196 if (initiatorDocument && isMainResource)
197 initiatorDocument = initiatorDocument->parentDocument();
198 if (!initiatorDocument || !initiatorDocument->loader())
200 if (LocalDOMWindow* initiatorWindow = initiatorDocument->domWindow())
201 initiatorWindow->performance()->addResourceTiming(*info, initiatorDocument);
204 static WebURLRequest::RequestContext requestContextFromType(const ResourceFetcher* fetcher, Resource::Type type)
207 case Resource::MainResource:
208 if (fetcher->frame()->tree().parent())
209 return WebURLRequest::RequestContextIframe;
210 // FIXME: Change this to a context frame type (once we introduce them): http://fetch.spec.whatwg.org/#concept-request-context-frame-type
211 return WebURLRequest::RequestContextHyperlink;
212 case Resource::XSLStyleSheet:
213 ASSERT(RuntimeEnabledFeatures::xsltEnabled());
214 case Resource::CSSStyleSheet:
215 return WebURLRequest::RequestContextStyle;
216 case Resource::Script:
217 return WebURLRequest::RequestContextScript;
219 return WebURLRequest::RequestContextFont;
220 case Resource::Image:
221 return WebURLRequest::RequestContextImage;
223 return WebURLRequest::RequestContextSubresource;
224 case Resource::ImportResource:
225 return WebURLRequest::RequestContextImport;
226 case Resource::LinkPrefetch:
227 return WebURLRequest::RequestContextPrefetch;
228 case Resource::LinkSubresource:
229 return WebURLRequest::RequestContextSubresource;
230 case Resource::TextTrack:
231 return WebURLRequest::RequestContextTrack;
232 case Resource::SVGDocument:
233 return WebURLRequest::RequestContextImage;
234 case Resource::Media: // TODO: Split this.
235 return WebURLRequest::RequestContextVideo;
237 ASSERT_NOT_REACHED();
238 return WebURLRequest::RequestContextSubresource;
241 static ResourceRequestCachePolicy memoryCachePolicyToResourceRequestCachePolicy(
242 const CachePolicy policy) {
243 if (policy == CachePolicyVerify)
244 return UseProtocolCachePolicy;
245 if (policy == CachePolicyRevalidate)
246 return ReloadIgnoringCacheData;
247 if (policy == CachePolicyReload)
248 return ReloadBypassingCache;
249 if (policy == CachePolicyHistoryBuffer)
250 return ReturnCacheDataElseLoad;
251 return UseProtocolCachePolicy;
254 ResourceFetcher::ResourceFetcher(DocumentLoader* documentLoader)
255 : m_document(nullptr)
256 , m_documentLoader(documentLoader)
258 , m_garbageCollectDocumentResourcesTimer(this, &ResourceFetcher::garbageCollectDocumentResourcesTimerFired)
259 , m_resourceTimingReportTimer(this, &ResourceFetcher::resourceTimingReportTimerFired)
260 , m_autoLoadImages(true)
261 , m_imagesEnabled(true)
262 , m_allowStaleResources(false)
266 ResourceFetcher::~ResourceFetcher()
268 m_documentLoader = nullptr;
269 m_document = nullptr;
273 // Make sure no requests still point to this ResourceFetcher
274 ASSERT(!m_requestCount);
277 Resource* ResourceFetcher::cachedResource(const KURL& resourceURL) const
279 KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(resourceURL);
280 return m_documentResources.get(url).get();
283 LocalFrame* ResourceFetcher::frame() const
285 if (m_documentLoader)
286 return m_documentLoader->frame();
287 if (m_document && m_document->importsController())
288 return m_document->importsController()->master()->frame();
292 FetchContext& ResourceFetcher::context() const
294 if (LocalFrame* frame = this->frame())
295 return frame->fetchContext();
296 return FetchContext::nullInstance();
299 ResourcePtr<Resource> ResourceFetcher::fetchSynchronously(FetchRequest& request)
302 request.mutableResourceRequest().setTimeoutInterval(10);
303 ResourceLoaderOptions options(request.options());
304 options.synchronousPolicy = RequestSynchronously;
305 request.setOptions(options);
306 return requestResource(Resource::Raw, request);
309 ResourcePtr<ImageResource> ResourceFetcher::fetchImage(FetchRequest& request)
311 if (request.resourceRequest().requestContext() == WebURLRequest::RequestContextUnspecified)
312 request.mutableResourceRequest().setRequestContext(WebURLRequest::RequestContextImage);
313 if (LocalFrame* f = frame()) {
314 if (f->document()->pageDismissalEventBeingDispatched() != Document::NoDismissal) {
315 KURL requestURL = request.resourceRequest().url();
316 if (requestURL.isValid() && canRequest(Resource::Image, request.resourceRequest(), requestURL, request.options(), request.forPreload(), request.originRestriction()))
317 PingLoader::loadImage(f, requestURL);
322 if (request.resourceRequest().url().protocolIsData())
323 preCacheDataURIImage(request);
325 request.setDefer(clientDefersImage(request.resourceRequest().url()) ? FetchRequest::DeferredByClient : FetchRequest::NoDefer);
326 ResourcePtr<Resource> resource = requestResource(Resource::Image, request);
327 return resource && resource->type() == Resource::Image ? toImageResource(resource) : 0;
330 void ResourceFetcher::preCacheDataURIImage(const FetchRequest& request)
332 const KURL& url = request.resourceRequest().url();
333 ASSERT(url.protocolIsData());
335 const String cacheIdentifier = getCacheIdentifier();
336 if (memoryCache()->resourceForURL(url, cacheIdentifier))
339 if (Resource* resource = resourceFromDataURIRequest(request.resourceRequest(), request.options(), cacheIdentifier)) {
340 memoryCache()->add(resource);
341 scheduleDocumentResourcesGC();
345 ResourcePtr<FontResource> ResourceFetcher::fetchFont(FetchRequest& request)
347 ASSERT(request.resourceRequest().frameType() == WebURLRequest::FrameTypeNone);
348 request.mutableResourceRequest().setRequestContext(WebURLRequest::RequestContextFont);
349 return toFontResource(requestResource(Resource::Font, request));
352 ResourcePtr<RawResource> ResourceFetcher::fetchImport(FetchRequest& request)
354 ASSERT(request.resourceRequest().frameType() == WebURLRequest::FrameTypeNone);
355 request.mutableResourceRequest().setRequestContext(WebURLRequest::RequestContextImport);
356 return toRawResource(requestResource(Resource::ImportResource, request));
359 ResourcePtr<CSSStyleSheetResource> ResourceFetcher::fetchCSSStyleSheet(FetchRequest& request)
361 ASSERT(request.resourceRequest().frameType() == WebURLRequest::FrameTypeNone);
362 request.mutableResourceRequest().setRequestContext(WebURLRequest::RequestContextStyle);
363 return toCSSStyleSheetResource(requestResource(Resource::CSSStyleSheet, request));
366 ResourcePtr<ScriptResource> ResourceFetcher::fetchScript(FetchRequest& request)
368 ASSERT(request.resourceRequest().frameType() == WebURLRequest::FrameTypeNone);
369 request.mutableResourceRequest().setRequestContext(WebURLRequest::RequestContextScript);
370 return toScriptResource(requestResource(Resource::Script, request));
373 ResourcePtr<XSLStyleSheetResource> ResourceFetcher::fetchXSLStyleSheet(FetchRequest& request)
375 ASSERT(RuntimeEnabledFeatures::xsltEnabled());
376 request.mutableResourceRequest().setRequestContext(WebURLRequest::RequestContextXSLT);
377 return toXSLStyleSheetResource(requestResource(Resource::XSLStyleSheet, request));
380 ResourcePtr<DocumentResource> ResourceFetcher::fetchSVGDocument(FetchRequest& request)
382 ASSERT(request.resourceRequest().frameType() == WebURLRequest::FrameTypeNone);
383 request.mutableResourceRequest().setRequestContext(WebURLRequest::RequestContextImage);
384 return toDocumentResource(requestResource(Resource::SVGDocument, request));
387 ResourcePtr<Resource> ResourceFetcher::fetchLinkResource(Resource::Type type, FetchRequest& request)
390 ASSERT(type == Resource::LinkPrefetch || type == Resource::LinkSubresource);
391 ASSERT(request.resourceRequest().frameType() == WebURLRequest::FrameTypeNone);
392 request.mutableResourceRequest().setRequestContext(type == Resource::LinkPrefetch ? WebURLRequest::RequestContextPrefetch : WebURLRequest::RequestContextSubresource);
393 return requestResource(type, request);
396 ResourcePtr<RawResource> ResourceFetcher::fetchRawResource(FetchRequest& request)
398 ASSERT(request.resourceRequest().frameType() == WebURLRequest::FrameTypeNone);
399 ASSERT(request.resourceRequest().requestContext() != WebURLRequest::RequestContextUnspecified);
400 return toRawResource(requestResource(Resource::Raw, request));
403 ResourcePtr<RawResource> ResourceFetcher::fetchMainResource(FetchRequest& request, const SubstituteData& substituteData)
405 ASSERT(request.resourceRequest().frameType() != WebURLRequest::FrameTypeNone);
406 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);
408 if (substituteData.isValid())
409 preCacheSubstituteDataForMainResource(request, substituteData);
410 return toRawResource(requestResource(Resource::MainResource, request));
413 ResourcePtr<RawResource> ResourceFetcher::fetchMedia(FetchRequest& request)
415 ASSERT(request.resourceRequest().frameType() == WebURLRequest::FrameTypeNone);
416 // FIXME: Split this into audio and video.
417 request.mutableResourceRequest().setRequestContext(WebURLRequest::RequestContextVideo);
418 return toRawResource(requestResource(Resource::Media, request));
421 ResourcePtr<RawResource> ResourceFetcher::fetchTextTrack(FetchRequest& request)
423 ASSERT(request.resourceRequest().frameType() == WebURLRequest::FrameTypeNone);
424 request.mutableResourceRequest().setRequestContext(WebURLRequest::RequestContextTrack);
425 return toRawResource(requestResource(Resource::TextTrack, request));
428 void ResourceFetcher::preCacheSubstituteDataForMainResource(const FetchRequest& request, const SubstituteData& substituteData)
430 const String cacheIdentifier = getCacheIdentifier();
431 const KURL& url = request.url();
432 if (Resource* oldResource = memoryCache()->resourceForURL(url, cacheIdentifier))
433 memoryCache()->remove(oldResource);
435 ResourceResponse response(url, substituteData.mimeType(), substituteData.content()->size(), substituteData.textEncoding(), emptyString());
436 ResourcePtr<Resource> resource = createResource(Resource::MainResource, request.resourceRequest(), substituteData.textEncoding());
437 resource->setNeedsSynchronousCacheHit(substituteData.forceSynchronousLoad());
438 resource->setOptions(request.options());
439 resource->setDataBufferingPolicy(BufferData);
440 resource->responseReceived(response, nullptr);
441 if (substituteData.content()->size())
442 resource->setResourceBuffer(substituteData.content());
443 resource->setCacheIdentifier(cacheIdentifier);
445 memoryCache()->add(resource.get());
448 bool ResourceFetcher::canRequest(Resource::Type type, const ResourceRequest& resourceRequest, const KURL& url, const ResourceLoaderOptions& options, bool forPreload, FetchRequest::OriginRestriction originRestriction) const
450 SecurityOrigin* securityOrigin = options.securityOrigin.get();
451 if (!securityOrigin && document())
452 securityOrigin = document()->securityOrigin();
454 if (originRestriction != FetchRequest::NoOriginRestriction && securityOrigin && !securityOrigin->canDisplay(url)) {
456 context().reportLocalLoadFailed(url);
457 WTF_LOG(ResourceLoading, "ResourceFetcher::requestResource URL was not allowed by SecurityOrigin::canDisplay");
461 // Some types of resources can be loaded only from the same origin. Other
462 // types of resources, like Images, Scripts, and CSS, can be loaded from
465 case Resource::MainResource:
466 case Resource::Image:
467 case Resource::CSSStyleSheet:
468 case Resource::Script:
471 case Resource::LinkPrefetch:
472 case Resource::LinkSubresource:
473 case Resource::TextTrack:
474 case Resource::ImportResource:
475 case Resource::Media:
476 // By default these types of resources can be loaded from any origin.
477 // FIXME: Are we sure about Resource::Font?
478 if (originRestriction == FetchRequest::RestrictToSameOrigin && !securityOrigin->canRequest(url)) {
479 printAccessDeniedMessage(url);
483 case Resource::XSLStyleSheet:
484 ASSERT(RuntimeEnabledFeatures::xsltEnabled());
485 case Resource::SVGDocument:
486 if (!securityOrigin->canRequest(url)) {
487 printAccessDeniedMessage(url);
493 // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved.
494 bool shouldBypassMainWorldCSP = (frame() && frame()->script().shouldBypassMainWorldCSP()) || (options.contentSecurityPolicyOption == DoNotCheckContentSecurityPolicy);
496 // Don't send CSP messages for preloads, we might never actually display those items.
497 ContentSecurityPolicy::ReportingStatus cspReporting = forPreload ?
498 ContentSecurityPolicy::SuppressReport : ContentSecurityPolicy::SendReport;
500 // m_document can be null, but not in any of the cases where csp is actually used below.
501 // ImageResourceTest.MultipartImage crashes w/o the m_document null check.
502 // I believe it's the Resource::Raw case.
503 const ContentSecurityPolicy* csp = m_document ? m_document->contentSecurityPolicy() : nullptr;
505 // FIXME: This would be cleaner if moved this switch into an allowFromSource()
506 // helper on this object which took a Resource::Type, then this block would
507 // collapse to about 10 lines for handling Raw and Script special cases.
509 case Resource::XSLStyleSheet:
510 ASSERT(RuntimeEnabledFeatures::xsltEnabled());
511 if (!shouldBypassMainWorldCSP && !csp->allowScriptFromSource(url, cspReporting))
514 case Resource::Script:
515 case Resource::ImportResource:
516 if (!shouldBypassMainWorldCSP && !csp->allowScriptFromSource(url, cspReporting))
520 Settings* settings = frame()->settings();
521 if (!frame()->loader().client()->allowScriptFromSource(!settings || settings->scriptEnabled(), url)) {
522 frame()->loader().client()->didNotAllowScript();
527 case Resource::CSSStyleSheet:
528 if (!shouldBypassMainWorldCSP && !csp->allowStyleFromSource(url, cspReporting))
531 case Resource::SVGDocument:
532 case Resource::Image:
533 if (!shouldBypassMainWorldCSP && !csp->allowImageFromSource(url, cspReporting))
536 case Resource::Font: {
537 if (!shouldBypassMainWorldCSP && !csp->allowFontFromSource(url, cspReporting))
541 case Resource::MainResource:
543 case Resource::LinkPrefetch:
544 case Resource::LinkSubresource:
546 case Resource::Media:
547 case Resource::TextTrack:
548 if (!shouldBypassMainWorldCSP && !csp->allowMediaFromSource(url, cspReporting))
552 if (!frame()->loader().client()->allowMedia(url))
558 // SVG Images have unique security rules that prevent all subresource requests
559 // except for data urls.
560 if (type != Resource::MainResource) {
561 if (frame() && frame()->chromeClient().isSVGImageChromeClient() && !url.protocolIsData())
565 // FIXME: Once we use RequestContext for CSP (http://crbug.com/390497), remove this extra check.
566 if (resourceRequest.requestContext() == WebURLRequest::RequestContextManifest) {
567 if (!shouldBypassMainWorldCSP && !csp->allowManifestFromSource(url, cspReporting))
571 // Measure the number of legacy URL schemes ('ftp://') and the number of embedded-credential
572 // ('http://user:password@...') resources embedded as subresources. in the hopes that we can
573 // block them at some point in the future.
574 if (resourceRequest.frameType() != WebURLRequest::FrameTypeTopLevel) {
575 if (SchemeRegistry::shouldTreatURLSchemeAsLegacy(url.protocol()) && !SchemeRegistry::shouldTreatURLSchemeAsLegacy(frame()->document()->securityOrigin()->protocol()))
576 UseCounter::count(frame()->document(), UseCounter::LegacyProtocolEmbeddedAsSubresource);
577 if (!url.user().isEmpty() || !url.pass().isEmpty())
578 UseCounter::count(frame()->document(), UseCounter::RequestedSubresourceWithEmbeddedCredentials);
581 // Last of all, check for mixed content. We do this last so that when
582 // folks block mixed content with a CSP policy, they don't get a warning.
583 // They'll still get a warning in the console about CSP blocking the load.
585 // If we're loading the main resource of a subframe, ensure that we check
586 // against the parent of the active frame, rather than the frame itself.
587 LocalFrame* effectiveFrame = frame();
588 if (resourceRequest.frameType() == WebURLRequest::FrameTypeNested) {
589 // FIXME: Deal with RemoteFrames.
590 if (frame()->tree().parent()->isLocalFrame())
591 effectiveFrame = toLocalFrame(frame()->tree().parent());
594 return !MixedContentChecker::shouldBlockFetch(effectiveFrame, resourceRequest, url);
597 bool ResourceFetcher::canAccessResource(Resource* resource, SecurityOrigin* sourceOrigin, const KURL& url) const
599 // Redirects can change the response URL different from one of request.
600 if (!canRequest(resource->type(), resource->resourceRequest(), url, resource->options(), resource->isUnusedPreload(), FetchRequest::UseDefaultOriginRestrictionForType))
603 if (!sourceOrigin && document())
604 sourceOrigin = document()->securityOrigin();
606 if (sourceOrigin->canRequest(url))
609 String errorDescription;
610 if (!resource->passesAccessControlCheck(sourceOrigin, errorDescription)) {
611 if (resource->type() == Resource::Font)
612 toFontResource(resource)->setCORSFailed();
613 if (frame() && frame()->document()) {
614 String resourceType = Resource::resourceTypeToString(resource->type(), resource->options().initiatorInfo);
615 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));
622 bool ResourceFetcher::isControlledByServiceWorker() const
624 LocalFrame* localFrame = frame();
627 ASSERT(m_documentLoader || localFrame->loader().documentLoader());
628 if (m_documentLoader)
629 return localFrame->loader().client()->isControlledByServiceWorker(*m_documentLoader);
630 // m_documentLoader is null while loading resources from the imported HTML.
631 // In such cases whether the request is controlled by ServiceWorker or not
632 // is determined by the document loader of the frame.
633 return localFrame->loader().client()->isControlledByServiceWorker(*localFrame->loader().documentLoader());
636 int64_t ResourceFetcher::serviceWorkerID() const
638 LocalFrame* localFrame = frame();
641 ASSERT(m_documentLoader || localFrame->loader().documentLoader());
642 if (m_documentLoader)
643 return localFrame->loader().client()->serviceWorkerID(*m_documentLoader);
644 // m_documentLoader is null while loading resources from the imported HTML.
645 // In such cases a service worker ID could be retrieved from the document
646 // loader of the frame.
647 return localFrame->loader().client()->serviceWorkerID(*localFrame->loader().documentLoader());
650 bool ResourceFetcher::shouldLoadNewResource(Resource::Type type) const
654 if (!m_documentLoader)
656 if (type == Resource::MainResource)
657 return m_documentLoader == frame()->loader().provisionalDocumentLoader();
658 return m_documentLoader == frame()->loader().documentLoader();
661 bool ResourceFetcher::resourceNeedsLoad(Resource* resource, const FetchRequest& request, RevalidationPolicy policy)
663 if (FetchRequest::DeferredByClient == request.defer())
667 if (resource->stillNeedsLoad())
669 return request.options().synchronousPolicy == RequestSynchronously && resource->isLoading();
672 void ResourceFetcher::maybeNotifyInsecureContent(const Resource* resource) const
674 // As a side effect browser will be notified.
675 MixedContentChecker::shouldBlockFetch(frame(),
676 resource->lastResourceRequest(),
677 resource->lastResourceRequest().url());
680 void ResourceFetcher::requestLoadStarted(Resource* resource, const FetchRequest& request, ResourceLoadStartType type)
682 if (type == ResourceLoadingFromCache)
683 notifyLoadedFromMemoryCache(resource);
685 if (request.resourceRequest().url().protocolIsData() || (m_documentLoader && m_documentLoader->substituteData().isValid()))
688 if (type == ResourceLoadingFromCache && !resource->stillNeedsLoad() && !m_validatedURLs.contains(request.resourceRequest().url())) {
689 // Resources loaded from memory cache should be reported the first time they're used.
690 RefPtr<ResourceTimingInfo> info = ResourceTimingInfo::create(request.options().initiatorInfo.name, monotonicallyIncreasingTime());
691 populateResourceTiming(info.get(), resource, true);
692 m_scheduledResourceTimingReports.add(info, resource->type() == Resource::MainResource);
693 if (!m_resourceTimingReportTimer.isActive())
694 m_resourceTimingReportTimer.startOneShot(0, FROM_HERE);
697 m_validatedURLs.add(request.resourceRequest().url());
700 ResourcePtr<Resource> ResourceFetcher::requestResource(Resource::Type type, FetchRequest& request)
702 ASSERT(request.options().synchronousPolicy == RequestAsynchronously || type == Resource::Raw);
704 TRACE_EVENT0("blink", "ResourceFetcher::requestResource");
706 KURL url = request.resourceRequest().url();
708 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));
710 // If only the fragment identifiers differ, it is the same resource.
711 url = MemoryCache::removeFragmentIdentifierIfNeeded(url);
716 if (!canRequest(type, request.resourceRequest(), url, request.options(), request.forPreload(), request.originRestriction()))
719 if (LocalFrame* f = frame())
720 f->loader().client()->dispatchWillRequestResource(&request);
722 if (!request.forPreload()) {
723 V8DOMActivityLogger* activityLogger = nullptr;
724 if (request.options().initiatorInfo.name == FetchInitiatorTypeNames::xmlhttprequest)
725 activityLogger = V8DOMActivityLogger::currentActivityLogger();
727 activityLogger = V8DOMActivityLogger::currentActivityLoggerIfIsolatedWorld();
729 if (activityLogger) {
731 argv.append(Resource::resourceTypeToString(type, request.options().initiatorInfo));
733 activityLogger->logEvent("blinkRequestResource", argv.size(), argv.data());
737 // See if we can use an existing resource from the cache.
738 ResourcePtr<Resource> resource = memoryCache()->resourceForURL(url, getCacheIdentifier());
740 const RevalidationPolicy policy = determineRevalidationPolicy(type, request, resource.get());
743 memoryCache()->remove(resource.get());
746 resource = createResourceForLoading(type, request, request.charset());
749 resource = createResourceForRevalidation(request, resource.get());
752 memoryCache()->updateForAccess(resource.get());
759 if (!resource->hasClients())
760 m_deadStatsRecorder.update(policy);
763 resource->setIdentifier(createUniqueIdentifier());
765 if (!request.forPreload() || policy != Use) {
766 ResourceLoadPriority priority = loadPriority(type, request);
767 // When issuing another request for a resource that is already in-flight make
768 // sure to not demote the priority of the in-flight request. If the new request
769 // isn't at the same priority as the in-flight request, only allow promotions.
770 // This can happen when a visible image's priority is increased and then another
771 // reference to the image is parsed (which would be at a lower priority).
772 if (priority > resource->resourceRequest().priority()) {
773 resource->mutableResourceRequest().setPriority(priority);
774 resource->didChangePriority(priority, 0);
778 if (resourceNeedsLoad(resource.get(), request, policy)) {
779 if (!shouldLoadNewResource(type)) {
780 if (memoryCache()->contains(resource.get()))
781 memoryCache()->remove(resource.get());
785 if (!m_documentLoader || !m_documentLoader->scheduleArchiveLoad(resource.get(), request.resourceRequest()))
786 resource->load(this, request.options());
788 // For asynchronous loads that immediately fail, it's sufficient to return a
789 // null Resource, as it indicates that something prevented the load from starting.
790 // If there's a network error, that failure will happen asynchronously. However, if
791 // a sync load receives a network error, it will have already happened by this point.
792 // In that case, the requester should have access to the relevant ResourceError, so
793 // we need to return a non-null Resource.
794 if (resource->errorOccurred()) {
795 if (memoryCache()->contains(resource.get()))
796 memoryCache()->remove(resource.get());
797 return request.options().synchronousPolicy == RequestSynchronously ? resource : nullptr;
801 // FIXME: Temporarily leave main resource caching disabled for chromium,
802 // see https://bugs.webkit.org/show_bug.cgi?id=107962. Before caching main
803 // resources, we should be sure to understand the implications for memory
805 // Remove main resource from cache to prevent reuse.
806 if (type == Resource::MainResource) {
807 ASSERT(policy != Use || m_documentLoader->substituteData().isValid());
808 ASSERT(policy != Revalidate);
809 memoryCache()->remove(resource.get());
812 requestLoadStarted(resource.get(), request, policy == Use ? ResourceLoadingFromCache : ResourceLoadingFromNetwork);
814 ASSERT(resource->url() == url.string());
815 m_documentResources.set(resource->url(), resource);
819 void ResourceFetcher::resourceTimingReportTimerFired(Timer<ResourceFetcher>* timer)
821 ASSERT_UNUSED(timer, timer == &m_resourceTimingReportTimer);
822 HashMap<RefPtr<ResourceTimingInfo>, bool> timingReports;
823 timingReports.swap(m_scheduledResourceTimingReports);
824 for (const auto& timingInfo : timingReports)
825 reportResourceTiming(timingInfo.key.get(), document(), timingInfo.value);
828 void ResourceFetcher::determineRequestContext(ResourceRequest& request, Resource::Type type)
830 WebURLRequest::RequestContext requestContext = requestContextFromType(this, type);
831 request.setRequestContext(requestContext);
834 ResourceRequestCachePolicy ResourceFetcher::resourceRequestCachePolicy(const ResourceRequest& request, Resource::Type type)
836 if (type == Resource::MainResource) {
837 FrameLoadType frameLoadType = frame()->loader().loadType();
838 if (request.httpMethod() == "POST" && frameLoadType == FrameLoadTypeBackForward)
839 return ReturnCacheDataDontLoad;
840 if (!frame()->host()->overrideEncoding().isEmpty() || frameLoadType == FrameLoadTypeBackForward)
841 return ReturnCacheDataElseLoad;
842 if (frameLoadType == FrameLoadTypeReloadFromOrigin)
843 return ReloadBypassingCache;
844 if (frameLoadType == FrameLoadTypeReload || frameLoadType == FrameLoadTypeSame || request.isConditional() || request.httpMethod() == "POST")
845 return ReloadIgnoringCacheData;
846 Frame* parent = frame()->tree().parent();
847 if (parent && parent->isLocalFrame())
848 return toLocalFrame(parent)->document()->fetcher()->resourceRequestCachePolicy(request, type);
849 return UseProtocolCachePolicy;
852 if (request.isConditional())
853 return ReloadIgnoringCacheData;
855 if (m_documentLoader && m_document && !m_document->loadEventFinished()) {
856 // For POST requests, we mutate the main resource's cache policy to avoid form resubmission.
857 // This policy should not be inherited by subresources.
858 ResourceRequestCachePolicy mainResourceCachePolicy = m_documentLoader->request().cachePolicy();
859 if (m_documentLoader->request().httpMethod() == "POST") {
860 if (mainResourceCachePolicy == ReturnCacheDataDontLoad)
861 return ReturnCacheDataElseLoad;
862 return UseProtocolCachePolicy;
864 return memoryCachePolicyToResourceRequestCachePolicy(context().cachePolicy(m_document));
866 return UseProtocolCachePolicy;
869 void ResourceFetcher::addAdditionalRequestHeaders(ResourceRequest& request, Resource::Type type)
874 if (request.cachePolicy() == UseProtocolCachePolicy)
875 request.setCachePolicy(resourceRequestCachePolicy(request, type));
876 if (request.requestContext() == WebURLRequest::RequestContextUnspecified)
877 determineRequestContext(request, type);
878 if (type == Resource::LinkPrefetch || type == Resource::LinkSubresource)
879 request.setHTTPHeaderField("Purpose", "prefetch");
881 context().addAdditionalRequestHeaders(document(), request, (type == Resource::MainResource) ? FetchMainResource : FetchSubresource);
884 ResourcePtr<Resource> ResourceFetcher::createResourceForRevalidation(const FetchRequest& request, Resource* resource)
887 ASSERT(memoryCache()->contains(resource));
888 ASSERT(resource->isLoaded());
889 ASSERT(resource->canUseCacheValidator());
890 ASSERT(!resource->resourceToRevalidate());
891 ASSERT(!isControlledByServiceWorker());
893 ResourceRequest revalidatingRequest(resource->resourceRequest());
894 revalidatingRequest.clearHTTPReferrer();
895 addAdditionalRequestHeaders(revalidatingRequest, resource->type());
897 const AtomicString& lastModified = resource->response().httpHeaderField("Last-Modified");
898 const AtomicString& eTag = resource->response().httpHeaderField("ETag");
899 if (!lastModified.isEmpty() || !eTag.isEmpty()) {
900 ASSERT(context().cachePolicy(document()) != CachePolicyReload);
901 if (context().cachePolicy(document()) == CachePolicyRevalidate)
902 revalidatingRequest.setHTTPHeaderField("Cache-Control", "max-age=0");
904 if (!lastModified.isEmpty())
905 revalidatingRequest.setHTTPHeaderField("If-Modified-Since", lastModified);
907 revalidatingRequest.setHTTPHeaderField("If-None-Match", eTag);
909 ResourcePtr<Resource> newResource = createResource(resource->type(), revalidatingRequest, resource->encoding());
910 WTF_LOG(ResourceLoading, "Resource %p created to revalidate %p", newResource.get(), resource);
912 newResource->setResourceToRevalidate(resource);
913 newResource->setCacheIdentifier(resource->cacheIdentifier());
915 memoryCache()->remove(resource);
916 memoryCache()->add(newResource.get());
920 ResourcePtr<Resource> ResourceFetcher::createResourceForLoading(Resource::Type type, FetchRequest& request, const String& charset)
922 const String cacheIdentifier = getCacheIdentifier();
923 ASSERT(!memoryCache()->resourceForURL(request.resourceRequest().url(), cacheIdentifier));
925 WTF_LOG(ResourceLoading, "Loading Resource for '%s'.", request.resourceRequest().url().elidedString().latin1().data());
927 addAdditionalRequestHeaders(request.mutableResourceRequest(), type);
928 ResourcePtr<Resource> resource = createResource(type, request.resourceRequest(), charset);
929 resource->setCacheIdentifier(cacheIdentifier);
931 memoryCache()->add(resource.get());
935 void ResourceFetcher::storeResourceTimingInitiatorInformation(Resource* resource)
937 if (resource->options().requestInitiatorContext != DocumentContext)
940 RefPtr<ResourceTimingInfo> info = ResourceTimingInfo::create(resource->options().initiatorInfo.name, monotonicallyIncreasingTime());
942 if (resource->isCacheValidator()) {
943 const AtomicString& timingAllowOrigin = resource->resourceToRevalidate()->response().httpHeaderField("Timing-Allow-Origin");
944 if (!timingAllowOrigin.isEmpty())
945 info->setOriginalTimingAllowOrigin(timingAllowOrigin);
948 if (resource->type() == Resource::MainResource) {
949 // <iframe>s should report the initial navigation requested by the parent document, but not subsequent navigations.
950 // FIXME: Resource timing is broken when the parent is a remote frame.
951 if (frame()->deprecatedLocalOwner() && !frame()->deprecatedLocalOwner()->loadedNonEmptyDocument()) {
952 info->setInitiatorType(frame()->deprecatedLocalOwner()->localName());
953 m_resourceTimingInfoMap.add(resource, info);
954 frame()->deprecatedLocalOwner()->didLoadNonEmptyDocument();
957 m_resourceTimingInfoMap.add(resource, info);
961 ResourceFetcher::RevalidationPolicy ResourceFetcher::determineRevalidationPolicy(Resource::Type type, const FetchRequest& fetchRequest, Resource* existingResource) const
963 const ResourceRequest& request = fetchRequest.resourceRequest();
965 if (!existingResource)
968 // We already have a preload going for this URL.
969 if (fetchRequest.forPreload() && existingResource->isPreloaded())
972 // If the same URL has been loaded as a different type, we need to reload.
973 if (existingResource->type() != type) {
974 // FIXME: If existingResource is a Preload and the new type is LinkPrefetch
975 // We really should discard the new prefetch since the preload has more
976 // specific type information! crbug.com/379893
977 // fast/dom/HTMLLinkElement/link-and-subresource-test hits this case.
978 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to type mismatch.");
982 // Do not load from cache if images are not enabled. The load for this image will be blocked
983 // in ImageResource::load.
984 if (FetchRequest::DeferredByClient == fetchRequest.defer())
987 // Always use data uris.
988 // FIXME: Extend this to non-images.
989 if (type == Resource::Image && request.url().protocolIsData())
992 // If a main resource was populated from a SubstituteData load, use it.
993 if (type == Resource::MainResource && m_documentLoader->substituteData().isValid())
996 if (!existingResource->canReuse(request))
999 // Never use cache entries for downloadToFile requests. The caller expects the resource in a file.
1000 if (request.downloadToFile())
1003 // Certain requests (e.g., XHRs) might have manually set headers that require revalidation.
1004 // FIXME: In theory, this should be a Revalidate case. In practice, the MemoryCache revalidation path assumes a whole bunch
1005 // of things about how revalidation works that manual headers violate, so punt to Reload instead.
1006 if (request.isConditional())
1009 // Don't reload resources while pasting.
1010 if (m_allowStaleResources)
1013 if (!fetchRequest.options().canReuseRequest(existingResource->options()))
1016 // Always use preloads.
1017 if (existingResource->isPreloaded())
1020 // CachePolicyHistoryBuffer uses the cache no matter what.
1021 CachePolicy cachePolicy = context().cachePolicy(document());
1022 if (cachePolicy == CachePolicyHistoryBuffer)
1025 // Don't reuse resources with Cache-control: no-store.
1026 if (existingResource->hasCacheControlNoStoreHeader()) {
1027 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to Cache-control: no-store.");
1031 // If credentials were sent with the previous request and won't be
1032 // with this one, or vice versa, re-fetch the resource.
1034 // This helps with the case where the server sends back
1035 // "Access-Control-Allow-Origin: *" all the time, but some of the
1036 // client's requests are made without CORS and some with.
1037 if (existingResource->resourceRequest().allowStoredCredentials() != request.allowStoredCredentials()) {
1038 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to difference in credentials settings.");
1042 // During the initial load, avoid loading the same resource multiple times for a single document,
1043 // even if the cache policies would tell us to.
1044 // We also group loads of the same resource together.
1045 // Raw resources are exempted, as XHRs fall into this category and may have user-set Cache-Control:
1046 // headers or other factors that require separate requests.
1047 if (type != Resource::Raw) {
1048 if (document() && !document()->loadEventFinished() && m_validatedURLs.contains(existingResource->url()))
1050 if (existingResource->isLoading())
1054 // CachePolicyReload always reloads
1055 if (cachePolicy == CachePolicyReload) {
1056 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to CachePolicyReload.");
1060 // We'll try to reload the resource if it failed last time.
1061 if (existingResource->errorOccurred()) {
1062 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicye reloading due to resource being in the error state");
1066 // List of available images logic allows images to be re-used without cache validation. We restrict this only to images
1067 // from memory cache which are the same as the version in the current document.
1068 if (type == Resource::Image && existingResource == cachedResource(request.url()))
1071 // If any of the redirects in the chain to loading the resource were not cacheable, we cannot reuse our cached resource.
1072 if (!existingResource->canReuseRedirectChain()) {
1073 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to an uncacheable redirect");
1077 // Check if the cache headers requires us to revalidate (cache expiration for example).
1078 if (cachePolicy == CachePolicyRevalidate || existingResource->mustRevalidateDueToCacheHeaders()
1079 || request.cacheControlContainsNoCache()) {
1080 // See if the resource has usable ETag or Last-modified headers.
1081 // If the page is controlled by the ServiceWorker, we choose the Reload policy because the revalidation headers should not be exposed to the ServiceWorker.(crbug.com/429570)
1082 if (existingResource->canUseCacheValidator() && !isControlledByServiceWorker())
1086 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to missing cache validators.");
1093 void ResourceFetcher::printAccessDeniedMessage(const KURL& url) const
1102 if (!m_document || m_document->url().isNull())
1103 message = "Unsafe attempt to load URL " + url.elidedString() + '.';
1105 message = "Unsafe attempt to load URL " + url.elidedString() + " from frame with URL " + m_document->url().elidedString() + ". Domains, protocols and ports must match.\n";
1107 frame()->document()->addConsoleMessage(ConsoleMessage::create(SecurityMessageSource, ErrorMessageLevel, message));
1110 void ResourceFetcher::setAutoLoadImages(bool enable)
1112 if (enable == m_autoLoadImages)
1115 m_autoLoadImages = enable;
1117 if (!m_autoLoadImages)
1120 reloadImagesIfNotDeferred();
1123 void ResourceFetcher::setImagesEnabled(bool enable)
1125 if (enable == m_imagesEnabled)
1128 m_imagesEnabled = enable;
1130 if (!m_imagesEnabled)
1133 reloadImagesIfNotDeferred();
1136 bool ResourceFetcher::clientDefersImage(const KURL& url) const
1138 return frame() && !frame()->loader().client()->allowImage(m_imagesEnabled, url);
1141 bool ResourceFetcher::shouldDeferImageLoad(const KURL& url) const
1143 return clientDefersImage(url) || !m_autoLoadImages;
1146 void ResourceFetcher::reloadImagesIfNotDeferred()
1148 for (const auto& documentResource : m_documentResources) {
1149 Resource* resource = documentResource.value.get();
1150 if (resource->type() == Resource::Image && resource->stillNeedsLoad() && !clientDefersImage(resource->url()))
1151 const_cast<Resource*>(resource)->load(this, defaultResourceOptions());
1155 void ResourceFetcher::redirectReceived(Resource* resource, const ResourceResponse& redirectResponse)
1157 ResourceTimingInfoMap::iterator it = m_resourceTimingInfoMap.find(resource);
1158 if (it != m_resourceTimingInfoMap.end())
1159 it->value->addRedirect(redirectResponse);
1162 void ResourceFetcher::didLoadResource()
1164 RefPtr<DocumentLoader> protectDocumentLoader(m_documentLoader);
1165 RefPtrWillBeRawPtr<Document> protectDocument(m_document.get());
1168 frame()->loader().loadDone();
1169 scheduleDocumentResourcesGC();
1172 void ResourceFetcher::scheduleDocumentResourcesGC()
1174 if (!m_garbageCollectDocumentResourcesTimer.isActive())
1175 m_garbageCollectDocumentResourcesTimer.startOneShot(0, FROM_HERE);
1178 // Garbage collecting m_documentResources is a workaround for the
1179 // ResourcePtrs on the RHS being strong references. Ideally this
1180 // would be a weak map, however ResourcePtrs perform additional
1181 // bookkeeping on Resources, so instead pseudo-GC them -- when the
1182 // reference count reaches 1, m_documentResources is the only reference, so
1183 // remove it from the map.
1184 void ResourceFetcher::garbageCollectDocumentResourcesTimerFired(Timer<ResourceFetcher>* timer)
1186 ASSERT_UNUSED(timer, timer == &m_garbageCollectDocumentResourcesTimer);
1187 garbageCollectDocumentResources();
1190 void ResourceFetcher::garbageCollectDocumentResources()
1192 typedef Vector<String, 10> StringVector;
1193 StringVector resourcesToDelete;
1195 for (const auto& documentResource : m_documentResources) {
1196 if (documentResource.value->hasOneHandle())
1197 resourcesToDelete.append(documentResource.key);
1200 m_documentResources.removeAll(resourcesToDelete);
1203 void ResourceFetcher::notifyLoadedFromMemoryCache(Resource* resource)
1205 if (!frame() || !frame()->page() || resource->status() != Resource::Cached || m_validatedURLs.contains(resource->url()))
1208 ResourceRequest request(resource->url());
1209 unsigned long identifier = createUniqueIdentifier();
1210 context().dispatchDidLoadResourceFromMemoryCache(request, resource->response());
1211 // FIXME: If willSendRequest changes the request, we don't respect it.
1212 willSendRequest(identifier, request, ResourceResponse(), resource->options().initiatorInfo);
1213 InspectorInstrumentation::markResourceAsCached(frame()->page(), identifier);
1214 context().sendRemainingDelegateMessages(m_documentLoader, identifier, resource->response(), resource->encodedSize());
1217 void ResourceFetcher::incrementRequestCount(const Resource* res)
1219 if (res->ignoreForRequestCount())
1225 void ResourceFetcher::decrementRequestCount(const Resource* res)
1227 if (res->ignoreForRequestCount())
1231 ASSERT(m_requestCount > -1);
1234 void ResourceFetcher::preload(Resource::Type type, FetchRequest& request, const String& charset)
1236 requestPreload(type, request, charset);
1239 void ResourceFetcher::requestPreload(Resource::Type type, FetchRequest& request, const String& charset)
1241 // Ensure main resources aren't preloaded, since the cache can't actually reuse the preload.
1242 if (type == Resource::MainResource)
1246 if (type == Resource::Script || type == Resource::CSSStyleSheet)
1247 encoding = charset.isEmpty() ? m_document->charset().string() : charset;
1249 request.setCharset(encoding);
1250 request.setForPreload(true);
1252 ResourcePtr<Resource> resource;
1253 // Loading images involves several special cases, so use dedicated fetch method instead.
1254 if (type == Resource::Image)
1255 resource = fetchImage(request);
1257 resource = requestResource(type, request);
1258 if (!resource || (m_preloads && m_preloads->contains(resource.get())))
1260 TRACE_EVENT_ASYNC_STEP_INTO0("net", "Resource", resource.get(), "Preload");
1261 resource->increasePreloadCount();
1264 m_preloads = adoptPtr(new ListHashSet<Resource*>);
1265 m_preloads->add(resource.get());
1268 printf("PRELOADING %s\n", resource->url().string().latin1().data());
1272 bool ResourceFetcher::isPreloaded(const String& urlString) const
1274 const KURL& url = m_document->completeURL(urlString);
1277 for (Resource* resource : *m_preloads) {
1278 if (resource->url() == url)
1286 void ResourceFetcher::clearPreloads()
1289 printPreloadStats();
1294 for (Resource* resource : *m_preloads) {
1295 resource->decreasePreloadCount();
1296 bool deleted = resource->deleteIfPossible();
1297 if (!deleted && resource->preloadResult() == Resource::PreloadNotReferenced)
1298 memoryCache()->remove(resource);
1303 void ResourceFetcher::didFinishLoading(Resource* resource, double finishTime, int64_t encodedDataLength)
1305 TRACE_EVENT_ASYNC_END0("net", "Resource", resource);
1307 if (resource && resource->response().isHTTP() && resource->response().httpStatusCode() < 400 && document()) {
1308 ResourceTimingInfoMap::iterator it = m_resourceTimingInfoMap.find(resource);
1309 if (it != m_resourceTimingInfoMap.end()) {
1310 RefPtr<ResourceTimingInfo> info = it->value;
1311 m_resourceTimingInfoMap.remove(it);
1312 populateResourceTiming(info.get(), resource, false);
1313 reportResourceTiming(info.get(), document(), resource->type() == Resource::MainResource);
1316 context().dispatchDidFinishLoading(m_documentLoader, resource->identifier(), finishTime, encodedDataLength);
1319 void ResourceFetcher::didChangeLoadingPriority(const Resource* resource, ResourceLoadPriority loadPriority, int intraPriorityValue)
1321 TRACE_EVENT_ASYNC_STEP_INTO1("net", "Resource", resource, "ChangePriority", "priority", loadPriority);
1322 context().dispatchDidChangeResourcePriority(resource->identifier(), loadPriority, intraPriorityValue);
1325 void ResourceFetcher::didFailLoading(const Resource* resource, const ResourceError& error)
1327 TRACE_EVENT_ASYNC_END0("net", "Resource", resource);
1328 bool isInternalRequest = resource->options().initiatorInfo.name == FetchInitiatorTypeNames::internal;
1329 context().dispatchDidFail(m_documentLoader, resource->identifier(), error, isInternalRequest);
1332 void ResourceFetcher::willSendRequest(unsigned long identifier, ResourceRequest& request, const ResourceResponse& redirectResponse, const FetchInitiatorInfo& initiatorInfo)
1334 context().dispatchWillSendRequest(m_documentLoader, identifier, request, redirectResponse, initiatorInfo);
1337 void ResourceFetcher::didReceiveResponse(const Resource* resource, const ResourceResponse& response)
1339 MixedContentChecker::checkMixedPrivatePublic(frame(), response.remoteIPAddress());
1341 // If the response is fetched via ServiceWorker, the original URL of the response could be different from the URL of the request.
1342 if (response.wasFetchedViaServiceWorker()) {
1343 if (!canRequest(resource->type(), resource->resourceRequest(), response.url(), resource->options(), false, FetchRequest::UseDefaultOriginRestrictionForType)) {
1344 resource->loader()->cancel();
1345 bool isInternalRequest = resource->options().initiatorInfo.name == FetchInitiatorTypeNames::internal;
1346 context().dispatchDidFail(m_documentLoader, resource->identifier(), ResourceError(errorDomainBlinkInternal, 0, response.url().string(), "Unsafe attempt to load URL " + response.url().elidedString() + " fetched by a ServiceWorker."), isInternalRequest);
1350 context().dispatchDidReceiveResponse(m_documentLoader, resource->identifier(), response, resource->loader());
1353 void ResourceFetcher::didReceiveData(const Resource* resource, const char* data, int dataLength, int encodedDataLength)
1355 context().dispatchDidReceiveData(m_documentLoader, resource->identifier(), data, dataLength, encodedDataLength);
1358 void ResourceFetcher::didDownloadData(const Resource* resource, int dataLength, int encodedDataLength)
1360 context().dispatchDidDownloadData(m_documentLoader, resource->identifier(), dataLength, encodedDataLength);
1363 void ResourceFetcher::subresourceLoaderFinishedLoadingOnePart(ResourceLoader* loader)
1365 if (!m_multipartLoaders)
1366 m_multipartLoaders = ResourceLoaderSet::create();
1367 m_multipartLoaders->add(loader);
1368 m_loaders->remove(loader);
1369 if (LocalFrame* frame = this->frame())
1370 return frame->loader().checkLoadComplete();
1373 void ResourceFetcher::didInitializeResourceLoader(ResourceLoader* loader)
1378 m_loaders = ResourceLoaderSet::create();
1379 ASSERT(!m_loaders->contains(loader));
1380 m_loaders->add(loader);
1383 void ResourceFetcher::willTerminateResourceLoader(ResourceLoader* loader)
1385 if (m_loaders && m_loaders->contains(loader))
1386 m_loaders->remove(loader);
1387 if (m_multipartLoaders && m_multipartLoaders->contains(loader))
1388 m_multipartLoaders->remove(loader);
1389 if (LocalFrame* frame = this->frame())
1390 frame->loader().checkLoadComplete();
1393 void ResourceFetcher::willStartLoadingResource(Resource* resource, ResourceRequest& request)
1395 if (m_documentLoader)
1396 m_documentLoader->applicationCacheHost()->willStartLoadingResource(request);
1398 storeResourceTimingInitiatorInformation(resource);
1399 TRACE_EVENT_ASYNC_BEGIN2("net", "Resource", resource, "url", resource->url().string().ascii(), "priority", resource->resourceRequest().priority());
1402 void ResourceFetcher::stopFetching()
1404 if (m_multipartLoaders)
1405 m_multipartLoaders->cancelAll();
1407 m_loaders->cancelAll();
1410 bool ResourceFetcher::isFetching() const
1412 return m_loaders && !m_loaders->isEmpty();
1415 void ResourceFetcher::setDefersLoading(bool defers)
1418 m_loaders->setAllDefersLoading(defers);
1421 bool ResourceFetcher::defersLoading() const
1423 if (LocalFrame* frame = this->frame())
1424 return frame->page()->defersLoading();
1428 bool ResourceFetcher::isLoadedBy(ResourceLoaderHost* possibleOwner) const
1430 return this == possibleOwner;
1433 bool ResourceFetcher::canAccessRedirect(Resource* resource, ResourceRequest& request, const ResourceResponse& redirectResponse, ResourceLoaderOptions& options)
1435 if (!canRequest(resource->type(), request, request.url(), options, resource->isUnusedPreload(), FetchRequest::UseDefaultOriginRestrictionForType))
1437 if (options.corsEnabled == IsCORSEnabled) {
1438 SecurityOrigin* sourceOrigin = options.securityOrigin.get();
1439 if (!sourceOrigin && document())
1440 sourceOrigin = document()->securityOrigin();
1442 String errorMessage;
1443 if (!CrossOriginAccessControl::handleRedirect(resource, sourceOrigin, request, redirectResponse, options, errorMessage)) {
1444 if (resource->type() == Resource::Font)
1445 toFontResource(resource)->setCORSFailed();
1446 if (frame() && frame()->document())
1447 frame()->document()->addConsoleMessage(ConsoleMessage::create(JSMessageSource, ErrorMessageLevel, errorMessage));
1451 if (resource->type() == Resource::Image && shouldDeferImageLoad(request.url()))
1457 void ResourceFetcher::refResourceLoaderHost()
1462 void ResourceFetcher::derefResourceLoaderHost()
1469 void ResourceFetcher::printPreloadStats()
1474 unsigned scripts = 0;
1475 unsigned scriptMisses = 0;
1476 unsigned stylesheets = 0;
1477 unsigned stylesheetMisses = 0;
1478 unsigned images = 0;
1479 unsigned imageMisses = 0;
1480 for (Resource* resource : *m_preloads) {
1481 if (resource->preloadResult() == Resource::PreloadNotReferenced)
1482 printf("!! UNREFERENCED PRELOAD %s\n", resource->url().string().latin1().data());
1483 else if (resource->preloadResult() == Resource::PreloadReferencedWhileComplete)
1484 printf("HIT COMPLETE PRELOAD %s\n", resource->url().string().latin1().data());
1485 else if (resource->preloadResult() == Resource::PreloadReferencedWhileLoading)
1486 printf("HIT LOADING PRELOAD %s\n", resource->url().string().latin1().data());
1488 if (resource->type() == Resource::Script) {
1490 if (resource->preloadResult() < Resource::PreloadReferencedWhileLoading)
1492 } else if (resource->type() == Resource::CSSStyleSheet) {
1494 if (resource->preloadResult() < Resource::PreloadReferencedWhileLoading)
1498 if (resource->preloadResult() < Resource::PreloadReferencedWhileLoading)
1502 if (resource->errorOccurred())
1503 memoryCache()->remove(resource);
1505 resource->decreasePreloadCount();
1510 printf("SCRIPTS: %d (%d hits, hit rate %d%%)\n", scripts, scripts - scriptMisses, (scripts - scriptMisses) * 100 / scripts);
1512 printf("STYLESHEETS: %d (%d hits, hit rate %d%%)\n", stylesheets, stylesheets - stylesheetMisses, (stylesheets - stylesheetMisses) * 100 / stylesheets);
1514 printf("IMAGES: %d (%d hits, hit rate %d%%)\n", images, images - imageMisses, (images - imageMisses) * 100 / images);
1518 const ResourceLoaderOptions& ResourceFetcher::defaultResourceOptions()
1520 DEFINE_STATIC_LOCAL(ResourceLoaderOptions, options, (BufferData, AllowStoredCredentials, ClientRequestedCredentials, CheckContentSecurityPolicy, DocumentContext));
1524 String ResourceFetcher::getCacheIdentifier() const
1526 if (isControlledByServiceWorker())
1527 return String::number(serviceWorkerID());
1528 return MemoryCache::defaultCacheIdentifier();
1531 ResourceFetcher::DeadResourceStatsRecorder::DeadResourceStatsRecorder()
1533 , m_revalidateCount(0)
1538 ResourceFetcher::DeadResourceStatsRecorder::~DeadResourceStatsRecorder()
1540 blink::Platform::current()->histogramCustomCounts(
1541 "WebCore.ResourceFetcher.HitCount", m_useCount, 0, 1000, 50);
1542 blink::Platform::current()->histogramCustomCounts(
1543 "WebCore.ResourceFetcher.RevalidateCount", m_revalidateCount, 0, 1000, 50);
1544 blink::Platform::current()->histogramCustomCounts(
1545 "WebCore.ResourceFetcher.LoadCount", m_loadCount, 0, 1000, 50);
1548 void ResourceFetcher::DeadResourceStatsRecorder::update(RevalidationPolicy policy)
1556 ++m_revalidateCount;
1564 void ResourceFetcher::trace(Visitor* visitor)
1566 visitor->trace(m_document);
1567 visitor->trace(m_loaders);
1568 visitor->trace(m_multipartLoaders);
1569 ResourceLoaderHost::trace(visitor);