CachedResourceLoader should set headers of the HTTP request prior checking for the...
[WebKit-https.git] / Source / WebCore / loader / cache / CachedResourceLoader.cpp
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-2016 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 "CachedResourceLoader.h"
29
30 #include "CachedCSSStyleSheet.h"
31 #include "CachedSVGDocument.h"
32 #include "CachedFont.h"
33 #include "CachedImage.h"
34 #include "CachedRawResource.h"
35 #include "CachedResourceRequest.h"
36 #include "CachedSVGFont.h"
37 #include "CachedScript.h"
38 #include "CachedXSLStyleSheet.h"
39 #include "Chrome.h"
40 #include "ChromeClient.h"
41 #include "ContentExtensionError.h"
42 #include "ContentExtensionRule.h"
43 #include "ContentSecurityPolicy.h"
44 #include "DOMWindow.h"
45 #include "DiagnosticLoggingClient.h"
46 #include "DiagnosticLoggingKeys.h"
47 #include "Document.h"
48 #include "DocumentLoader.h"
49 #include "Frame.h"
50 #include "FrameLoader.h"
51 #include "FrameLoaderClient.h"
52 #include "HTMLElement.h"
53 #include "HTMLFrameOwnerElement.h"
54 #include "LoaderStrategy.h"
55 #include "LocalizedStrings.h"
56 #include "Logging.h"
57 #include "MainFrame.h"
58 #include "MemoryCache.h"
59 #include "Page.h"
60 #include "Performance.h"
61 #include "PingLoader.h"
62 #include "PlatformStrategies.h"
63 #include "RenderElement.h"
64 #include "ResourceLoadInfo.h"
65 #include "RuntimeEnabledFeatures.h"
66 #include "ScriptController.h"
67 #include "SecurityOrigin.h"
68 #include "SecurityPolicy.h"
69 #include "SessionID.h"
70 #include "Settings.h"
71 #include "StyleSheetContents.h"
72 #include "SubresourceLoader.h"
73 #include "UserContentController.h"
74 #include "UserStyleSheet.h"
75 #include <wtf/text/CString.h>
76 #include <wtf/text/WTFString.h>
77
78 #if ENABLE(VIDEO_TRACK)
79 #include "CachedTextTrack.h"
80 #endif
81
82 #define PRELOAD_DEBUG 0
83
84 #define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), Network, "%p - CachedResourceLoader::" fmt, this, ##__VA_ARGS__)
85
86 namespace WebCore {
87
88 static CachedResource* createResource(CachedResource::Type type, CachedResourceRequest&& request, SessionID sessionID)
89 {
90     switch (type) {
91     case CachedResource::ImageResource:
92         return new CachedImage(WTFMove(request), sessionID);
93     case CachedResource::CSSStyleSheet:
94         return new CachedCSSStyleSheet(WTFMove(request), sessionID);
95     case CachedResource::Script:
96         return new CachedScript(WTFMove(request), sessionID);
97     case CachedResource::SVGDocumentResource:
98         return new CachedSVGDocument(WTFMove(request), sessionID);
99 #if ENABLE(SVG_FONTS)
100     case CachedResource::SVGFontResource:
101         return new CachedSVGFont(WTFMove(request), sessionID);
102 #endif
103     case CachedResource::FontResource:
104         return new CachedFont(WTFMove(request), sessionID);
105     case CachedResource::MediaResource:
106     case CachedResource::RawResource:
107     case CachedResource::MainResource:
108         return new CachedRawResource(WTFMove(request), type, sessionID);
109 #if ENABLE(XSLT)
110     case CachedResource::XSLStyleSheet:
111         return new CachedXSLStyleSheet(WTFMove(request), sessionID);
112 #endif
113 #if ENABLE(LINK_PREFETCH)
114     case CachedResource::LinkPrefetch:
115         return new CachedResource(WTFMove(request), CachedResource::LinkPrefetch, sessionID);
116     case CachedResource::LinkSubresource:
117         return new CachedResource(WTFMove(request), CachedResource::LinkSubresource, sessionID);
118 #endif
119 #if ENABLE(VIDEO_TRACK)
120     case CachedResource::TextTrackResource:
121         return new CachedTextTrack(WTFMove(request), sessionID);
122 #endif
123     }
124     ASSERT_NOT_REACHED();
125     return nullptr;
126 }
127
128 CachedResourceLoader::CachedResourceLoader(DocumentLoader* documentLoader)
129     : m_document(nullptr)
130     , m_documentLoader(documentLoader)
131     , m_requestCount(0)
132     , m_garbageCollectDocumentResourcesTimer(*this, &CachedResourceLoader::garbageCollectDocumentResources)
133     , m_autoLoadImages(true)
134     , m_imagesEnabled(true)
135     , m_allowStaleResources(false)
136 {
137 }
138
139 CachedResourceLoader::~CachedResourceLoader()
140 {
141     m_documentLoader = nullptr;
142     m_document = nullptr;
143
144     clearPreloads();
145     for (auto& resource : m_documentResources.values())
146         resource->setOwningCachedResourceLoader(nullptr);
147
148     // Make sure no requests still point to this CachedResourceLoader
149     ASSERT(m_requestCount == 0);
150 }
151
152 CachedResource* CachedResourceLoader::cachedResource(const String& resourceURL) const
153 {
154     ASSERT(!resourceURL.isNull());
155     return cachedResource(MemoryCache::removeFragmentIdentifierIfNeeded(m_document->completeURL(resourceURL)));
156 }
157
158 CachedResource* CachedResourceLoader::cachedResource(const URL& url) const
159 {
160     ASSERT(!MemoryCache::shouldRemoveFragmentIdentifier(url));
161     return m_documentResources.get(url).get();
162 }
163
164 Frame* CachedResourceLoader::frame() const
165 {
166     return m_documentLoader ? m_documentLoader->frame() : nullptr;
167 }
168
169 SessionID CachedResourceLoader::sessionID() const
170 {
171     SessionID sessionID = SessionID::defaultSessionID();
172
173     if (Frame* f = frame())
174         sessionID = f->page()->sessionID();
175
176     return sessionID;
177 }
178
179 CachedResourceHandle<CachedImage> CachedResourceLoader::requestImage(CachedResourceRequest&& request)
180 {
181     if (Frame* frame = this->frame()) {
182         if (frame->loader().pageDismissalEventBeingDispatched() != FrameLoader::PageDismissalType::None) {
183             if (Document* document = frame->document())
184                 request.upgradeInsecureRequestIfNeeded(*document);
185             URL requestURL = request.resourceRequest().url();
186             if (requestURL.isValid() && canRequest(CachedResource::ImageResource, requestURL, request, ForPreload::No))
187                 PingLoader::loadImage(*frame, requestURL);
188             return nullptr;
189         }
190     }
191
192     auto defer = clientDefersImage(request.resourceRequest().url()) ? DeferOption::DeferredByClient : DeferOption::NoDefer;
193     return downcast<CachedImage>(requestResource(CachedResource::ImageResource, WTFMove(request), ForPreload::No, defer).get());
194 }
195
196 CachedResourceHandle<CachedFont> CachedResourceLoader::requestFont(CachedResourceRequest&& request, bool isSVG)
197 {
198 #if ENABLE(SVG_FONTS)
199     if (isSVG)
200         return downcast<CachedSVGFont>(requestResource(CachedResource::SVGFontResource, WTFMove(request)).get());
201 #else
202     UNUSED_PARAM(isSVG);
203 #endif
204     return downcast<CachedFont>(requestResource(CachedResource::FontResource, WTFMove(request)).get());
205 }
206
207 #if ENABLE(VIDEO_TRACK)
208 CachedResourceHandle<CachedTextTrack> CachedResourceLoader::requestTextTrack(CachedResourceRequest&& request)
209 {
210     return downcast<CachedTextTrack>(requestResource(CachedResource::TextTrackResource, WTFMove(request)).get());
211 }
212 #endif
213
214 CachedResourceHandle<CachedCSSStyleSheet> CachedResourceLoader::requestCSSStyleSheet(CachedResourceRequest&& request)
215 {
216     return downcast<CachedCSSStyleSheet>(requestResource(CachedResource::CSSStyleSheet, WTFMove(request)).get());
217 }
218
219 CachedResourceHandle<CachedCSSStyleSheet> CachedResourceLoader::requestUserCSSStyleSheet(CachedResourceRequest&& request)
220 {
221 #if ENABLE(CACHE_PARTITIONING)
222     ASSERT(document());
223     request.setDomainForCachePartition(*document());
224 #endif
225
226     auto& memoryCache = MemoryCache::singleton();
227     if (request.allowsCaching()) {
228         if (CachedResource* existing = memoryCache.resourceForRequest(request.resourceRequest(), sessionID())) {
229             if (is<CachedCSSStyleSheet>(*existing))
230                 return downcast<CachedCSSStyleSheet>(existing);
231             memoryCache.remove(*existing);
232         }
233     }
234
235     request.removeFragmentIdentifierIfNeeded();
236
237     CachedResourceHandle<CachedCSSStyleSheet> userSheet = new CachedCSSStyleSheet(WTFMove(request), sessionID());
238
239     if (userSheet->allowsCaching())
240         memoryCache.add(*userSheet);
241     // FIXME: loadResource calls setOwningCachedResourceLoader() if the resource couldn't be added to cache. Does this function need to call it, too?
242
243     userSheet->load(*this);
244     return userSheet;
245 }
246
247 CachedResourceHandle<CachedScript> CachedResourceLoader::requestScript(CachedResourceRequest&& request)
248 {
249     return downcast<CachedScript>(requestResource(CachedResource::Script, WTFMove(request)).get());
250 }
251
252 #if ENABLE(XSLT)
253 CachedResourceHandle<CachedXSLStyleSheet> CachedResourceLoader::requestXSLStyleSheet(CachedResourceRequest&& request)
254 {
255     return downcast<CachedXSLStyleSheet>(requestResource(CachedResource::XSLStyleSheet, WTFMove(request)).get());
256 }
257 #endif
258
259 CachedResourceHandle<CachedSVGDocument> CachedResourceLoader::requestSVGDocument(CachedResourceRequest&& request)
260 {
261     return downcast<CachedSVGDocument>(requestResource(CachedResource::SVGDocumentResource, WTFMove(request)).get());
262 }
263
264 #if ENABLE(LINK_PREFETCH)
265 CachedResourceHandle<CachedResource> CachedResourceLoader::requestLinkResource(CachedResource::Type type, CachedResourceRequest&& request)
266 {
267     ASSERT(frame());
268     ASSERT(type == CachedResource::LinkPrefetch || type == CachedResource::LinkSubresource);
269     return requestResource(type, WTFMove(request));
270 }
271 #endif
272
273 CachedResourceHandle<CachedRawResource> CachedResourceLoader::requestMedia(CachedResourceRequest&& request)
274 {
275     return downcast<CachedRawResource>(requestResource(CachedResource::MediaResource, WTFMove(request)).get());
276 }
277
278 CachedResourceHandle<CachedRawResource> CachedResourceLoader::requestRawResource(CachedResourceRequest&& request)
279 {
280     return downcast<CachedRawResource>(requestResource(CachedResource::RawResource, WTFMove(request)).get());
281 }
282
283 CachedResourceHandle<CachedRawResource> CachedResourceLoader::requestMainResource(CachedResourceRequest&& request)
284 {
285     return downcast<CachedRawResource>(requestResource(CachedResource::MainResource, WTFMove(request)).get());
286 }
287
288 static MixedContentChecker::ContentType contentTypeFromResourceType(CachedResource::Type type)
289 {
290     switch (type) {
291     // https://w3c.github.io/webappsec-mixed-content/#category-optionally-blockable
292     // Editor's Draft, 11 February 2016
293     // 3.1. Optionally-blockable Content
294     case CachedResource::ImageResource:
295     case CachedResource::MediaResource:
296             return MixedContentChecker::ContentType::ActiveCanWarn;
297
298     case CachedResource::CSSStyleSheet:
299     case CachedResource::Script:
300     case CachedResource::FontResource:
301         return MixedContentChecker::ContentType::Active;
302
303 #if ENABLE(SVG_FONTS)
304     case CachedResource::SVGFontResource:
305         return MixedContentChecker::ContentType::Active;
306 #endif
307
308     case CachedResource::RawResource:
309     case CachedResource::SVGDocumentResource:
310         return MixedContentChecker::ContentType::Active;
311 #if ENABLE(XSLT)
312     case CachedResource::XSLStyleSheet:
313         return MixedContentChecker::ContentType::Active;
314 #endif
315
316 #if ENABLE(LINK_PREFETCH)
317     case CachedResource::LinkPrefetch:
318     case CachedResource::LinkSubresource:
319         return MixedContentChecker::ContentType::Active;
320 #endif
321
322 #if ENABLE(VIDEO_TRACK)
323     case CachedResource::TextTrackResource:
324         return MixedContentChecker::ContentType::Active;
325 #endif
326     default:
327         ASSERT_NOT_REACHED();
328         return MixedContentChecker::ContentType::Active;
329     }
330 }
331
332 bool CachedResourceLoader::checkInsecureContent(CachedResource::Type type, const URL& url) const
333 {
334
335     if (!canRequestInContentDispositionAttachmentSandbox(type, url))
336         return false;
337
338     switch (type) {
339     case CachedResource::Script:
340 #if ENABLE(XSLT)
341     case CachedResource::XSLStyleSheet:
342 #endif
343     case CachedResource::SVGDocumentResource:
344     case CachedResource::CSSStyleSheet:
345         // These resource can inject script into the current document (Script,
346         // XSL) or exfiltrate the content of the current document (CSS).
347         if (Frame* f = frame()) {
348             if (!f->loader().mixedContentChecker().canRunInsecureContent(m_document->securityOrigin(), url))
349                 return false;
350             Frame& top = f->tree().top();
351             if (&top != f && !top.loader().mixedContentChecker().canRunInsecureContent(top.document()->securityOrigin(), url))
352                 return false;
353         }
354         break;
355 #if ENABLE(VIDEO_TRACK)
356     case CachedResource::TextTrackResource:
357 #endif
358     case CachedResource::MediaResource:
359     case CachedResource::RawResource:
360     case CachedResource::ImageResource:
361 #if ENABLE(SVG_FONTS)
362     case CachedResource::SVGFontResource:
363 #endif
364     case CachedResource::FontResource: {
365         // These resources can corrupt only the frame's pixels.
366         if (Frame* f = frame()) {
367             Frame& topFrame = f->tree().top();
368             if (!topFrame.loader().mixedContentChecker().canDisplayInsecureContent(topFrame.document()->securityOrigin(), contentTypeFromResourceType(type), url))
369                 return false;
370         }
371         break;
372     }
373     case CachedResource::MainResource:
374 #if ENABLE(LINK_PREFETCH)
375     case CachedResource::LinkPrefetch:
376     case CachedResource::LinkSubresource:
377         // Prefetch cannot affect the current document.
378 #endif
379         break;
380     }
381     return true;
382 }
383
384 bool CachedResourceLoader::allowedByContentSecurityPolicy(CachedResource::Type type, const URL& url, const ResourceLoaderOptions& options, ContentSecurityPolicy::RedirectResponseReceived redirectResponseReceived) const
385 {
386     if (options.contentSecurityPolicyImposition == ContentSecurityPolicyImposition::SkipPolicyCheck)
387         return true;
388
389     ASSERT(m_document);
390     ASSERT(m_document->contentSecurityPolicy());
391
392     switch (type) {
393 #if ENABLE(XSLT)
394     case CachedResource::XSLStyleSheet:
395 #endif
396     case CachedResource::Script:
397         if (!m_document->contentSecurityPolicy()->allowScriptFromSource(url, redirectResponseReceived))
398             return false;
399         break;
400     case CachedResource::CSSStyleSheet:
401         if (!m_document->contentSecurityPolicy()->allowStyleFromSource(url, redirectResponseReceived))
402             return false;
403         break;
404     case CachedResource::SVGDocumentResource:
405     case CachedResource::ImageResource:
406         if (!m_document->contentSecurityPolicy()->allowImageFromSource(url, redirectResponseReceived))
407             return false;
408         break;
409 #if ENABLE(SVG_FONTS)
410     case CachedResource::SVGFontResource:
411 #endif
412     case CachedResource::FontResource:
413         if (!m_document->contentSecurityPolicy()->allowFontFromSource(url, redirectResponseReceived))
414             return false;
415         break;
416     case CachedResource::MediaResource:
417 #if ENABLE(VIDEO_TRACK)
418     case CachedResource::TextTrackResource:
419 #endif
420         if (!m_document->contentSecurityPolicy()->allowMediaFromSource(url, redirectResponseReceived))
421             return false;
422         break;
423     case CachedResource::RawResource:
424         return true;
425     default:
426         ASSERT_NOT_REACHED();
427     }
428
429     return true;
430 }
431
432 static inline bool isSameOriginDataURL(const URL& url, const ResourceLoaderOptions& options)
433 {
434     // FIXME: Remove same-origin data URL flag since it was removed from fetch spec (https://github.com/whatwg/fetch/issues/381).
435     return url.protocolIsData() && options.sameOriginDataURLFlag == SameOriginDataURLFlag::Set;
436 }
437
438 bool CachedResourceLoader::canRequest(CachedResource::Type type, const URL& url, const CachedResourceRequest& request, ForPreload forPreload)
439 {
440     auto& options = request.options();
441
442     if (document() && !document()->securityOrigin()->canDisplay(url)) {
443         if (forPreload == ForPreload::No)
444             FrameLoader::reportLocalLoadFailed(frame(), url.stringCenterEllipsizedToLength());
445         LOG(ResourceLoading, "CachedResourceLoader::requestResource URL was not allowed by SecurityOrigin::canDisplay");
446         return false;
447     }
448
449     if (options.mode == FetchOptions::Mode::SameOrigin && !m_document->securityOrigin()->canRequest(url) && !isSameOriginDataURL(url, options)) {
450         printAccessDeniedMessage(url);
451         return false;
452     }
453
454     if (!allowedByContentSecurityPolicy(type, url, options, ContentSecurityPolicy::RedirectResponseReceived::No))
455         return false;
456
457     // SVG Images have unique security rules that prevent all subresource requests except for data urls.
458     if (type != CachedResource::MainResource && frame() && frame()->page()) {
459         if (frame()->page()->chrome().client().isSVGImageChromeClient() && !url.protocolIsData())
460             return false;
461     }
462
463     // Last of all, check for insecure content. We do this last so that when folks block insecure content with a CSP policy, they don't get a warning.
464     // They'll still get a warning in the console about CSP blocking the load.
465
466     // FIXME: Should we consider whether the request is for preload here?
467     if (!checkInsecureContent(type, url))
468         return false;
469
470     return true;
471 }
472
473 // FIXME: Should we find a way to know whether the redirection is for a preload request like we do for CachedResourceLoader::canRequest?
474 bool CachedResourceLoader::canRequestAfterRedirection(CachedResource::Type type, const URL& url, const ResourceLoaderOptions& options) const
475 {
476     if (document() && !document()->securityOrigin()->canDisplay(url)) {
477         FrameLoader::reportLocalLoadFailed(frame(), url.stringCenterEllipsizedToLength());
478         LOG(ResourceLoading, "CachedResourceLoader::requestResource URL was not allowed by SecurityOrigin::canDisplay");
479         return false;
480     }
481
482     // FIXME: According to https://fetch.spec.whatwg.org/#http-redirect-fetch, we should check that the URL is HTTP(s) except if in navigation mode.
483     // But we currently allow at least data URLs to be loaded.
484
485     if (options.mode == FetchOptions::Mode::SameOrigin && !m_document->securityOrigin()->canRequest(url)) {
486         printAccessDeniedMessage(url);
487         return false;
488     }
489
490     if (!allowedByContentSecurityPolicy(type, url, options, ContentSecurityPolicy::RedirectResponseReceived::Yes))
491         return false;
492
493     // Last of all, check for insecure content. We do this last so that when folks block insecure content with a CSP policy, they don't get a warning.
494     // They'll still get a warning in the console about CSP blocking the load.
495     if (!checkInsecureContent(type, url))
496         return false;
497
498     return true;
499 }
500
501 bool CachedResourceLoader::updateRequestAfterRedirection(CachedResource::Type type, ResourceRequest& request, const ResourceLoaderOptions& options)
502 {
503     ASSERT(m_documentLoader);
504     if (auto* document = m_documentLoader->cachedResourceLoader().document())
505         upgradeInsecureResourceRequestIfNeeded(request, *document);
506
507     // FIXME: We might want to align the checks done here with the ones done in CachedResourceLoader::requestResource, content extensions blocking in particular.
508
509     return canRequestAfterRedirection(type, request.url(), options);
510 }
511
512 bool CachedResourceLoader::canRequestInContentDispositionAttachmentSandbox(CachedResource::Type type, const URL& url) const
513 {
514     Document* document;
515
516     // FIXME: Do we want to expand this to all resource types that the mixed content checker would consider active content?
517     switch (type) {
518     case CachedResource::MainResource:
519         if (auto ownerElement = frame() ? frame()->ownerElement() : nullptr) {
520             document = &ownerElement->document();
521             break;
522         }
523         return true;
524     case CachedResource::CSSStyleSheet:
525         document = m_document;
526         break;
527     default:
528         return true;
529     }
530
531     if (!document->shouldEnforceContentDispositionAttachmentSandbox() || document->securityOrigin()->canRequest(url))
532         return true;
533
534     String message = "Unsafe attempt to load URL " + url.stringCenterEllipsizedToLength() + " from document with Content-Disposition: attachment at URL " + document->url().stringCenterEllipsizedToLength() + ".";
535     document->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message);
536     return false;
537 }
538
539 bool CachedResourceLoader::shouldContinueAfterNotifyingLoadedFromMemoryCache(const CachedResourceRequest& request, CachedResource* resource)
540 {
541     if (!resource || !frame() || resource->status() != CachedResource::Cached)
542         return true;
543
544     ResourceRequest newRequest = ResourceRequest(resource->url());
545     if (request.resourceRequest().hiddenFromInspector())
546         newRequest.setHiddenFromInspector(true);
547     frame()->loader().loadedResourceFromMemoryCache(resource, newRequest);
548
549     // FIXME <http://webkit.org/b/113251>: If the delegate modifies the request's
550     // URL, it is no longer appropriate to use this CachedResource.
551     return !newRequest.isNull();
552 }
553
554 static inline bool originsMatch(const CachedResourceRequest& request, const CachedResource& resource)
555 {
556     if (request.origin() == resource.origin())
557         return true;
558     if (!request.origin() || !resource.origin())
559         return false;
560     // We use string comparison as this is how they are serialized as HTTP Origin header value.
561     // This is in particular useful for unique origins that are serialized as "null"
562     return request.origin()->toString() == resource.origin()->toString();
563 }
564
565 bool CachedResourceLoader::shouldUpdateCachedResourceWithCurrentRequest(const CachedResource& resource, const CachedResourceRequest& request)
566 {
567     // WebKit is not supporting CORS for fonts (https://bugs.webkit.org/show_bug.cgi?id=86817), no need to update the resource before reusing it.
568     if (resource.type() == CachedResource::Type::FontResource)
569         return false;
570
571 #if ENABLE(SVG_FONTS)
572     if (resource.type() == CachedResource::Type::SVGFontResource)
573         return false;
574 #endif
575
576 #if ENABLE(XSLT)
577     // Load is same-origin, we do not check for CORS.
578     if (resource.type() == CachedResource::XSLStyleSheet)
579         return false;
580 #endif
581
582     // FIXME: We should enable resource reuse for these resource types
583     switch (resource.type()) {
584     case CachedResource::SVGDocumentResource:
585         return false;
586     case CachedResource::MainResource:
587         return false;
588 #if ENABLE(LINK_PREFETCH)
589     case CachedResource::LinkPrefetch:
590         return false;
591     case CachedResource::LinkSubresource:
592         return false;
593 #endif
594     default:
595         break;
596     }
597
598     if (resource.options().mode != request.options().mode || !originsMatch(request, resource))
599         return true;
600
601     if (resource.options().redirect != request.options().redirect && resource.hasRedirections())
602         return true;
603
604     return false;
605 }
606
607 static inline bool isResourceSuitableForDirectReuse(const CachedResource& resource, const CachedResourceRequest& request)
608 {
609     // FIXME: For being loaded requests, the response tainting may not be correctly computed if the fetch mode is not the same.
610     // Even if the fetch mode is the same, we are not sure that the resource can be reused (Vary: Origin header for instance).
611     // We should find a way to improve this.
612     if (resource.status() != CachedResource::Cached)
613         return false;
614
615     // If the cached resource has not followed redirections, it is incomplete and we should not use it.
616     // Let's make sure the memory cache has no such resource.
617     ASSERT(resource.response().type() != ResourceResponse::Type::Opaqueredirect);
618
619     // We could support redirect modes other than Follow in case of a redirected resource.
620     // This case is rare and is not worth optimizing currently.
621     if (request.options().redirect != FetchOptions::Redirect::Follow && resource.hasRedirections())
622         return false;
623
624     // FIXME: Implement reuse of cached raw resources.
625     if (resource.type() == CachedResource::Type::RawResource || resource.type() == CachedResource::Type::MediaResource)
626         return false;
627
628     return true;
629 }
630
631 CachedResourceHandle<CachedResource> CachedResourceLoader::updateCachedResourceWithCurrentRequest(const CachedResource& resource, CachedResourceRequest&& request)
632 {
633     if (!isResourceSuitableForDirectReuse(resource, request)) {
634         request.setCachingPolicy(CachingPolicy::DisallowCaching);
635         return loadResource(resource.type(), WTFMove(request));
636     }
637
638     auto resourceHandle = createResource(resource.type(), WTFMove(request), sessionID());
639     resourceHandle->loadFrom(resource);
640     return resourceHandle;
641 }
642
643 static inline void logMemoryCacheResourceRequest(Frame* frame, const String& description, const String& value = String())
644 {
645     if (!frame || !frame->page())
646         return;
647     if (value.isNull())
648         frame->page()->diagnosticLoggingClient().logDiagnosticMessage(DiagnosticLoggingKeys::resourceRequestKey(), description, ShouldSample::Yes);
649     else
650         frame->page()->diagnosticLoggingClient().logDiagnosticMessageWithValue(DiagnosticLoggingKeys::resourceRequestKey(), description, value, ShouldSample::Yes);
651 }
652
653 void CachedResourceLoader::prepareFetch(CachedResource::Type type, CachedResourceRequest& request)
654 {
655     // Implementing step 1 to 7 of https://fetch.spec.whatwg.org/#fetching
656
657     if (!request.origin() && document())
658         request.setOrigin(document()->securityOrigin());
659
660     request.setAcceptHeaderIfNone(type);
661
662     // Accept-Language value is handled in underlying port-specific code.
663     // FIXME: Decide whether to support client hints
664 }
665
666 void CachedResourceLoader::updateHTTPRequestHeaders(CachedResource::Type type, CachedResourceRequest& request)
667 {
668     // Implementing steps 7 to 12 of https://fetch.spec.whatwg.org/#http-network-or-cache-fetch
669
670     // FIXME: We should reconcile handling of MainResource with other resources.
671     if (type != CachedResource::Type::MainResource) {
672         // In some cases we may try to load resources in frameless documents. Such loads always fail.
673         // FIXME: We shouldn't need to do the check on frame.
674         if (auto* frame = this->frame())
675             request.updateReferrerOriginAndUserAgentHeaders(frame->loader(), document() ? document()->referrerPolicy() : ReferrerPolicy::Default);
676     }
677
678     request.updateAccordingCacheMode();
679 }
680
681 CachedResourceHandle<CachedResource> CachedResourceLoader::requestResource(CachedResource::Type type, CachedResourceRequest&& request, ForPreload forPreload, DeferOption defer)
682 {
683     if (Document* document = this->document())
684         request.upgradeInsecureRequestIfNeeded(*document);
685
686     URL url = request.resourceRequest().url();
687
688     LOG(ResourceLoading, "CachedResourceLoader::requestResource '%s', charset '%s', priority=%d, forPreload=%u", url.stringCenterEllipsizedToLength().latin1().data(), request.charset().latin1().data(), request.priority() ? static_cast<int>(request.priority().value()) : -1, forPreload == ForPreload::Yes);
689
690     if (!url.isValid()) {
691         RELEASE_LOG_IF_ALLOWED("requestResource: URL is invalid (frame = %p)", frame());
692         return nullptr;
693     }
694
695     prepareFetch(type, request);
696
697     // We are passing url as well as request, as request url may contain a fragment identifier.
698     if (!canRequest(type, url, request, forPreload)) {
699         RELEASE_LOG_IF_ALLOWED("requestResource: Not allowed to request resource (frame = %p)", frame());
700         return nullptr;
701     }
702
703 #if ENABLE(CONTENT_EXTENSIONS)
704     if (frame() && frame()->mainFrame().page() && m_documentLoader) {
705         const auto& resourceRequest = request.resourceRequest();
706         auto blockedStatus = frame()->mainFrame().page()->userContentProvider().processContentExtensionRulesForLoad(resourceRequest.url(), toResourceType(type), *m_documentLoader);
707         request.applyBlockedStatus(blockedStatus);
708         if (blockedStatus.blockedLoad) {
709             RELEASE_LOG_IF_ALLOWED("requestResource: Resource blocked by content blocker (frame = %p)", frame());
710             if (type == CachedResource::Type::MainResource) {
711                 auto resource = createResource(type, WTFMove(request), sessionID());
712                 ASSERT(resource);
713                 resource->error(CachedResource::Status::LoadError);
714                 resource->setResourceError(ResourceError(ContentExtensions::WebKitContentBlockerDomain, 0, resourceRequest.url(), WEB_UI_STRING("The URL was blocked by a content blocker", "WebKitErrorBlockedByContentBlocker description")));
715                 return resource;
716             }
717             return nullptr;
718         }
719         if (blockedStatus.madeHTTPS
720             && type == CachedResource::Type::MainResource
721             && m_documentLoader->isLoadingMainResource()) {
722             // This is to make sure the correct 'new' URL shows in the location bar.
723             m_documentLoader->frameLoader()->client().dispatchDidChangeProvisionalURL();
724         }
725         url = request.resourceRequest().url(); // The content extension could have changed it from http to https.
726         url = MemoryCache::removeFragmentIdentifierIfNeeded(url); // Might need to remove fragment identifier again.
727     }
728 #endif
729
730 #if ENABLE(WEB_TIMING)
731     LoadTiming loadTiming;
732     loadTiming.markStartTimeAndFetchStart();
733 #endif
734
735     if (request.resourceRequest().url().protocolIsInHTTPFamily())
736         updateHTTPRequestHeaders(type, request);
737
738     auto& memoryCache = MemoryCache::singleton();
739     if (request.allowsCaching() && memoryCache.disabled()) {
740         DocumentResourceMap::iterator it = m_documentResources.find(url.string());
741         if (it != m_documentResources.end()) {
742             it->value->setOwningCachedResourceLoader(nullptr);
743             m_documentResources.remove(it);
744         }
745     }
746
747     // See if we can use an existing resource from the cache.
748     CachedResourceHandle<CachedResource> resource;
749 #if ENABLE(CACHE_PARTITIONING)
750     if (document())
751         request.setDomainForCachePartition(*document());
752 #endif
753
754     if (request.allowsCaching())
755         resource = memoryCache.resourceForRequest(request.resourceRequest(), sessionID());
756
757     logMemoryCacheResourceRequest(frame(), resource ? DiagnosticLoggingKeys::inMemoryCacheKey() : DiagnosticLoggingKeys::notInMemoryCacheKey());
758
759     RevalidationPolicy policy = determineRevalidationPolicy(type, request, resource.get(), forPreload, defer);
760     switch (policy) {
761     case Reload:
762         memoryCache.remove(*resource);
763         FALLTHROUGH;
764     case Load:
765         if (resource)
766             logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedKey());
767         resource = loadResource(type, WTFMove(request));
768         break;
769     case Revalidate:
770         if (resource)
771             logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::revalidatingKey());
772         resource = revalidateResource(WTFMove(request), *resource);
773         break;
774     case Use:
775         ASSERT(resource);
776         if (shouldUpdateCachedResourceWithCurrentRequest(*resource, request)) {
777             resource = updateCachedResourceWithCurrentRequest(*resource, WTFMove(request));
778             if (resource->status() != CachedResource::Status::Cached)
779                 policy = Load;
780         } else {
781             if (!shouldContinueAfterNotifyingLoadedFromMemoryCache(request, resource.get()))
782                 return nullptr;
783             logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::usedKey());
784             memoryCache.resourceAccessed(*resource);
785 #if ENABLE(WEB_TIMING)
786             if (document() && RuntimeEnabledFeatures::sharedFeatures().resourceTimingEnabled()) {
787                 // FIXME (161170): The networkLoadTiming shouldn't be stored on the ResourceResponse.
788                 resource->response().networkLoadTiming().reset();
789                 loadTiming.setResponseEnd(monotonicallyIncreasingTime());
790                 m_resourceTimingInfo.storeResourceTimingInitiatorInformation(resource, request.initiatorName(), frame());
791                 m_resourceTimingInfo.addResourceTiming(resource.get(), *document(), loadTiming);
792             }
793 #endif
794             if (forPreload == ForPreload::No)
795                 resource->setLoadPriority(request.priority());
796         }
797         break;
798     }
799
800     if (!resource)
801         return nullptr;
802
803     if (forPreload == ForPreload::No && resource->loader() && resource->resourceRequest().ignoreForRequestCount()) {
804         resource->resourceRequest().setIgnoreForRequestCount(false);
805         incrementRequestCount(*resource);
806     }
807
808     if ((policy != Use || resource->stillNeedsLoad()) && defer == DeferOption::NoDefer) {
809         resource->load(*this);
810
811         // We don't support immediate loads, but we do support immediate failure.
812         if (resource->errorOccurred()) {
813             if (resource->allowsCaching() && resource->inCache())
814                 memoryCache.remove(*resource);
815             return nullptr;
816         }
817     }
818
819     if (document() && !document()->loadEventFinished() && !resource->resourceRequest().url().protocolIsData())
820         m_validatedURLs.add(resource->resourceRequest().url());
821
822     ASSERT(resource->url() == url.string());
823     m_documentResources.set(resource->url(), resource);
824     return resource;
825 }
826
827 void CachedResourceLoader::documentDidFinishLoadEvent()
828 {
829     m_validatedURLs.clear();
830 }
831
832 CachedResourceHandle<CachedResource> CachedResourceLoader::revalidateResource(CachedResourceRequest&& request, CachedResource& resource)
833 {
834     ASSERT(resource.inCache());
835     auto& memoryCache = MemoryCache::singleton();
836     ASSERT(!memoryCache.disabled());
837     ASSERT(resource.canUseCacheValidator());
838     ASSERT(!resource.resourceToRevalidate());
839     ASSERT(resource.sessionID() == sessionID());
840     ASSERT(resource.allowsCaching());
841
842 #if ENABLE(WEB_TIMING)
843     AtomicString initiatorName = request.initiatorName();
844 #endif
845     CachedResourceHandle<CachedResource> newResource = createResource(resource.type(), WTFMove(request), resource.sessionID());
846
847     LOG(ResourceLoading, "Resource %p created to revalidate %p", newResource.get(), &resource);
848     newResource->setResourceToRevalidate(&resource);
849
850     memoryCache.remove(resource);
851     memoryCache.add(*newResource);
852 #if ENABLE(WEB_TIMING)
853     if (RuntimeEnabledFeatures::sharedFeatures().resourceTimingEnabled())
854         m_resourceTimingInfo.storeResourceTimingInitiatorInformation(newResource, initiatorName, frame());
855 #endif
856     return newResource;
857 }
858
859 CachedResourceHandle<CachedResource> CachedResourceLoader::loadResource(CachedResource::Type type, CachedResourceRequest&& request)
860 {
861     auto& memoryCache = MemoryCache::singleton();
862     ASSERT(!request.allowsCaching() || !memoryCache.resourceForRequest(request.resourceRequest(), sessionID())
863         || request.resourceRequest().cachePolicy() == DoNotUseAnyCache || request.resourceRequest().cachePolicy() == ReloadIgnoringCacheData || request.resourceRequest().cachePolicy() == RefreshAnyCacheData);
864
865     LOG(ResourceLoading, "Loading CachedResource for '%s'.", request.resourceRequest().url().stringCenterEllipsizedToLength().latin1().data());
866
867 #if ENABLE(WEB_TIMING)
868     AtomicString initiatorName = request.initiatorName();
869 #endif
870     CachedResourceHandle<CachedResource> resource = createResource(type, WTFMove(request), sessionID());
871
872     if (resource->allowsCaching() && !memoryCache.add(*resource))
873         resource->setOwningCachedResourceLoader(this);
874 #if ENABLE(WEB_TIMING)
875     if (RuntimeEnabledFeatures::sharedFeatures().resourceTimingEnabled())
876         m_resourceTimingInfo.storeResourceTimingInitiatorInformation(resource, initiatorName, frame());
877 #endif
878     return resource;
879 }
880
881 static void logRevalidation(const String& reason, DiagnosticLoggingClient& logClient)
882 {
883     logClient.logDiagnosticMessageWithValue(DiagnosticLoggingKeys::cachedResourceRevalidationKey(), DiagnosticLoggingKeys::reasonKey(), reason, ShouldSample::Yes);
884 }
885
886 static void logResourceRevalidationDecision(CachedResource::RevalidationDecision reason, const Frame* frame)
887 {
888     if (!frame || !frame->page())
889         return;
890     auto& logClient = frame->page()->diagnosticLoggingClient();
891     switch (reason) {
892     case CachedResource::RevalidationDecision::No:
893         break;
894     case CachedResource::RevalidationDecision::YesDueToExpired:
895         logRevalidation(DiagnosticLoggingKeys::isExpiredKey(), logClient);
896         break;
897     case CachedResource::RevalidationDecision::YesDueToNoStore:
898         logRevalidation(DiagnosticLoggingKeys::noStoreKey(), logClient);
899         break;
900     case CachedResource::RevalidationDecision::YesDueToNoCache:
901         logRevalidation(DiagnosticLoggingKeys::noCacheKey(), logClient);
902         break;
903     case CachedResource::RevalidationDecision::YesDueToCachePolicy:
904         logRevalidation(DiagnosticLoggingKeys::reloadKey(), logClient);
905         break;
906     }
907 }
908
909 CachedResourceLoader::RevalidationPolicy CachedResourceLoader::determineRevalidationPolicy(CachedResource::Type type, CachedResourceRequest& cachedResourceRequest, CachedResource* existingResource, ForPreload forPreload, DeferOption defer) const
910 {
911     auto& request = cachedResourceRequest.resourceRequest();
912
913     if (!existingResource)
914         return Load;
915
916     if (request.cachePolicy() == DoNotUseAnyCache || request.cachePolicy() == ReloadIgnoringCacheData)
917         return Load;
918
919     if (request.cachePolicy() == RefreshAnyCacheData)
920         return Reload;
921
922     // We already have a preload going for this URL.
923     if (forPreload == ForPreload::Yes && existingResource->isPreloaded())
924         return Use;
925
926     // If the same URL has been loaded as a different type, we need to reload.
927     if (existingResource->type() != type) {
928         LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to type mismatch.");
929         logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonTypeMismatchKey());
930         return Reload;
931     }
932
933     if (!existingResource->varyHeaderValuesMatch(request))
934         return Reload;
935
936     auto* textDecoder = existingResource->textResourceDecoder();
937     if (textDecoder && !textDecoder->hasEqualEncodingForCharset(cachedResourceRequest.charset()))
938         return Reload;
939
940     // FIXME: We should use the same cache policy for all resource types. The raw resource policy is overly strict
941     //        while the normal subresource policy is too loose.
942     if (existingResource->isMainOrMediaOrRawResource() && frame()) {
943         bool strictPolicyDisabled = frame()->loader().isStrictRawResourceValidationPolicyDisabledForTesting();
944         bool canReuseRawResource = strictPolicyDisabled || downcast<CachedRawResource>(*existingResource).canReuse(request);
945         if (!canReuseRawResource)
946             return Reload;
947     }
948
949     // Conditional requests should have failed canReuse check.
950     ASSERT(!request.isConditional());
951
952     // Do not load from cache if images are not enabled. The load for this image will be blocked in CachedImage::load.
953     if (defer == DeferOption::DeferredByClient)
954         return Reload;
955
956     // Don't reload resources while pasting or if cache mode allows stale resources.
957     if (m_allowStaleResources || cachedResourceRequest.options().cache == FetchOptions::Cache::ForceCache || cachedResourceRequest.options().cache == FetchOptions::Cache::OnlyIfCached)
958         return Use;
959
960     ASSERT(cachedResourceRequest.options().cache == FetchOptions::Cache::Default || cachedResourceRequest.options().cache == FetchOptions::Cache::NoCache);
961
962     // Always use preloads.
963     if (existingResource->isPreloaded())
964         return Use;
965
966     // We can find resources that are being validated from cache only when validation is just successfully completing.
967     if (existingResource->validationCompleting())
968         return Use;
969     ASSERT(!existingResource->validationInProgress());
970
971     // Validate the redirect chain.
972     bool cachePolicyIsHistoryBuffer = cachePolicy(type) == CachePolicyHistoryBuffer;
973     if (!existingResource->redirectChainAllowsReuse(cachePolicyIsHistoryBuffer ? ReuseExpiredRedirection : DoNotReuseExpiredRedirection)) {
974         LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to not cached or expired redirections.");
975         logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonRedirectChainKey());
976         return Reload;
977     }
978
979     // CachePolicyHistoryBuffer uses the cache except if this is a main resource with "cache-control: no-store".
980     if (cachePolicyIsHistoryBuffer) {
981         // FIXME: Ignoring "cache-control: no-cache" for sub-resources on history navigation but not the main
982         // resource is inconsistent. We should probably harmonize this.
983         if (!existingResource->response().cacheControlContainsNoStore() || type != CachedResource::MainResource)
984             return Use;
985     }
986
987     // Don't reuse resources with Cache-control: no-store.
988     if (existingResource->response().cacheControlContainsNoStore()) {
989         LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to Cache-control: no-store.");
990         logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonNoStoreKey());
991         return Reload;
992     }
993
994     // If credentials were sent with the previous request and won't be
995     // with this one, or vice versa, re-fetch the resource.
996     //
997     // This helps with the case where the server sends back
998     // "Access-Control-Allow-Origin: *" all the time, but some of the
999     // client's requests are made without CORS and some with.
1000     if (existingResource->resourceRequest().allowCookies() != request.allowCookies()) {
1001         LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to difference in credentials settings.");
1002         logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonCredentialSettingsKey());
1003         return Reload;
1004     }
1005
1006     // During the initial load, avoid loading the same resource multiple times for a single document, even if the cache policies would tell us to.
1007     if (document() && !document()->loadEventFinished() && m_validatedURLs.contains(existingResource->url()))
1008         return Use;
1009
1010     // CachePolicyReload always reloads
1011     if (cachePolicy(type) == CachePolicyReload) {
1012         LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to CachePolicyReload.");
1013         logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonReloadKey());
1014         return Reload;
1015     }
1016     
1017     // We'll try to reload the resource if it failed last time.
1018     if (existingResource->errorOccurred()) {
1019         LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicye reloading due to resource being in the error state");
1020         logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonErrorKey());
1021         return Reload;
1022     }
1023
1024     if (existingResource->isLoading()) {
1025         // Do not use cached main resources that are still loading because sharing
1026         // loading CachedResources in this case causes issues with regards to cancellation.
1027         // If one of the DocumentLoader clients decides to cancel the load, then the load
1028         // would be cancelled for all other DocumentLoaders as well.
1029         if (type == CachedResource::Type::MainResource)
1030             return Reload;
1031         // For cached subresources that are still loading we ignore the cache policy.
1032         return Use;
1033     }
1034
1035     auto revalidationDecision = existingResource->makeRevalidationDecision(cachePolicy(type));
1036     logResourceRevalidationDecision(revalidationDecision, frame());
1037
1038     // Check if the cache headers requires us to revalidate (cache expiration for example).
1039     if (revalidationDecision != CachedResource::RevalidationDecision::No) {
1040         // See if the resource has usable ETag or Last-modified headers.
1041         if (existingResource->canUseCacheValidator())
1042             return Revalidate;
1043         
1044         // No, must reload.
1045         LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to missing cache validators.");
1046         logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonMustRevalidateNoValidatorKey());
1047         return Reload;
1048     }
1049
1050     return Use;
1051 }
1052
1053 void CachedResourceLoader::printAccessDeniedMessage(const URL& url) const
1054 {
1055     if (url.isNull())
1056         return;
1057
1058     if (!frame())
1059         return;
1060
1061     String message;
1062     if (!m_document || m_document->url().isNull())
1063         message = "Unsafe attempt to load URL " + url.stringCenterEllipsizedToLength() + '.';
1064     else
1065         message = "Unsafe attempt to load URL " + url.stringCenterEllipsizedToLength() + " from frame with URL " + m_document->url().stringCenterEllipsizedToLength() + ". Domains, protocols and ports must match.\n";
1066
1067     frame()->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message);
1068 }
1069
1070 void CachedResourceLoader::setAutoLoadImages(bool enable)
1071 {
1072     if (enable == m_autoLoadImages)
1073         return;
1074
1075     m_autoLoadImages = enable;
1076
1077     if (!m_autoLoadImages)
1078         return;
1079
1080     reloadImagesIfNotDeferred();
1081 }
1082
1083 void CachedResourceLoader::setImagesEnabled(bool enable)
1084 {
1085     if (enable == m_imagesEnabled)
1086         return;
1087
1088     m_imagesEnabled = enable;
1089
1090     if (!m_imagesEnabled)
1091         return;
1092
1093     reloadImagesIfNotDeferred();
1094 }
1095
1096 bool CachedResourceLoader::clientDefersImage(const URL&) const
1097 {
1098     return !m_imagesEnabled;
1099 }
1100
1101 bool CachedResourceLoader::shouldPerformImageLoad(const URL& url) const
1102 {
1103     return m_autoLoadImages || url.protocolIsData();
1104 }
1105
1106 bool CachedResourceLoader::shouldDeferImageLoad(const URL& url) const
1107 {
1108     return clientDefersImage(url) || !shouldPerformImageLoad(url);
1109 }
1110
1111 void CachedResourceLoader::reloadImagesIfNotDeferred()
1112 {
1113     for (auto& resource : m_documentResources.values()) {
1114         if (is<CachedImage>(*resource) && resource->stillNeedsLoad() && !clientDefersImage(resource->url()))
1115             downcast<CachedImage>(*resource).load(*this);
1116     }
1117 }
1118
1119 CachePolicy CachedResourceLoader::cachePolicy(CachedResource::Type type) const
1120 {
1121     Frame* frame = this->frame();
1122     if (!frame)
1123         return CachePolicyVerify;
1124
1125     if (type != CachedResource::MainResource)
1126         return frame->loader().subresourceCachePolicy();
1127
1128     if (Page* page = frame->page()) {
1129         if (page->isResourceCachingDisabled())
1130             return CachePolicyReload;
1131     }
1132
1133     switch (frame->loader().loadType()) {
1134     case FrameLoadType::ReloadFromOrigin:
1135     case FrameLoadType::Reload:
1136         return CachePolicyReload;
1137     case FrameLoadType::Back:
1138     case FrameLoadType::Forward:
1139     case FrameLoadType::IndexedBackForward:
1140         // Do not revalidate cached main resource on back/forward navigation.
1141         return CachePolicyHistoryBuffer;
1142     default:
1143         return CachePolicyVerify;
1144     }
1145 }
1146
1147 void CachedResourceLoader::removeCachedResource(CachedResource& resource)
1148 {
1149 #ifndef NDEBUG
1150     DocumentResourceMap::iterator it = m_documentResources.find(resource.url());
1151     if (it != m_documentResources.end())
1152         ASSERT(it->value.get() == &resource);
1153 #endif
1154     m_documentResources.remove(resource.url());
1155 }
1156
1157 void CachedResourceLoader::loadDone(CachedResource* resource, bool shouldPerformPostLoadActions)
1158 {
1159     RefPtr<DocumentLoader> protectDocumentLoader(m_documentLoader);
1160     RefPtr<Document> protectDocument(m_document);
1161
1162 #if ENABLE(WEB_TIMING)
1163     if (resource && document() && RuntimeEnabledFeatures::sharedFeatures().resourceTimingEnabled())
1164         m_resourceTimingInfo.addResourceTiming(resource, *document(), resource->loader()->loadTiming());
1165 #else
1166     UNUSED_PARAM(resource);
1167 #endif
1168
1169     if (frame())
1170         frame()->loader().loadDone();
1171
1172     if (shouldPerformPostLoadActions)
1173         performPostLoadActions();
1174
1175     if (!m_garbageCollectDocumentResourcesTimer.isActive())
1176         m_garbageCollectDocumentResourcesTimer.startOneShot(0);
1177 }
1178
1179 // Garbage collecting m_documentResources is a workaround for the
1180 // CachedResourceHandles on the RHS being strong references. Ideally this
1181 // would be a weak map, however CachedResourceHandles perform additional
1182 // bookkeeping on CachedResources, so instead pseudo-GC them -- when the
1183 // reference count reaches 1, m_documentResources is the only reference, so
1184 // remove it from the map.
1185 void CachedResourceLoader::garbageCollectDocumentResources()
1186 {
1187     typedef Vector<String, 10> StringVector;
1188     StringVector resourcesToDelete;
1189
1190     for (auto& resource : m_documentResources) {
1191         if (resource.value->hasOneHandle()) {
1192             resourcesToDelete.append(resource.key);
1193             resource.value->setOwningCachedResourceLoader(nullptr);
1194         }
1195     }
1196
1197     for (auto& resource : resourcesToDelete)
1198         m_documentResources.remove(resource);
1199 }
1200
1201 void CachedResourceLoader::performPostLoadActions()
1202 {
1203     checkForPendingPreloads();
1204
1205     platformStrategies()->loaderStrategy()->servePendingRequests();
1206 }
1207
1208 void CachedResourceLoader::incrementRequestCount(const CachedResource& resource)
1209 {
1210     if (resource.ignoreForRequestCount())
1211         return;
1212
1213     ++m_requestCount;
1214 }
1215
1216 void CachedResourceLoader::decrementRequestCount(const CachedResource& resource)
1217 {
1218     if (resource.ignoreForRequestCount())
1219         return;
1220
1221     --m_requestCount;
1222     ASSERT(m_requestCount > -1);
1223 }
1224
1225 CachedResourceHandle<CachedResource> CachedResourceLoader::preload(CachedResource::Type type, CachedResourceRequest&& request, PreloadType preloadType)
1226 {
1227     // We always preload resources on iOS. See <https://bugs.webkit.org/show_bug.cgi?id=91276>.
1228     // FIXME: We should consider adding a setting to toggle aggressive preloading behavior as opposed
1229     // to making this behavior specific to iOS.
1230 #if !PLATFORM(IOS)
1231     bool hasRendering = m_document->bodyOrFrameset() && m_document->renderView();
1232     bool canBlockParser = type == CachedResource::Script || type == CachedResource::CSSStyleSheet;
1233     if (!hasRendering && !canBlockParser && preloadType == ImplicitPreload) {
1234         // Don't preload subresources that can't block the parser before we have something to draw.
1235         // This helps prevent preloads from delaying first display when bandwidth is limited.
1236         PendingPreload pendingPreload = { type, WTFMove(request) };
1237         m_pendingPreloads.append(pendingPreload);
1238         return nullptr;
1239     }
1240 #else
1241     UNUSED_PARAM(preloadType);
1242 #endif
1243     return requestPreload(type, WTFMove(request));
1244 }
1245
1246 void CachedResourceLoader::checkForPendingPreloads()
1247 {
1248     if (m_pendingPreloads.isEmpty())
1249         return;
1250     auto* body = m_document->bodyOrFrameset();
1251     if (!body || !body->renderer())
1252         return;
1253 #if PLATFORM(IOS)
1254     // We always preload resources on iOS. See <https://bugs.webkit.org/show_bug.cgi?id=91276>.
1255     // So, we should never have any pending preloads.
1256     // FIXME: We should look to avoid compiling this code entirely when building for iOS.
1257     ASSERT_NOT_REACHED();
1258 #endif
1259     while (!m_pendingPreloads.isEmpty()) {
1260         PendingPreload preload = m_pendingPreloads.takeFirst();
1261         // Don't request preload if the resource already loaded normally (this will result in double load if the page is being reloaded with cached results ignored).
1262         if (!cachedResource(preload.m_request.resourceRequest().url()))
1263             requestPreload(preload.m_type, WTFMove(preload.m_request));
1264     }
1265     m_pendingPreloads.clear();
1266 }
1267
1268 CachedResourceHandle<CachedResource> CachedResourceLoader::requestPreload(CachedResource::Type type, CachedResourceRequest&& request)
1269 {
1270     if (request.charset().isEmpty() && (type == CachedResource::Script || type == CachedResource::CSSStyleSheet))
1271         request.setCharset(m_document->charset());
1272
1273     CachedResourceHandle<CachedResource> resource = requestResource(type, WTFMove(request), ForPreload::Yes);
1274     if (!resource || (m_preloads && m_preloads->contains(resource.get())))
1275         return nullptr;
1276     // Fonts need special treatment since just creating the resource doesn't trigger a load.
1277     if (type == CachedResource::FontResource)
1278         downcast<CachedFont>(resource.get())->beginLoadIfNeeded(*this);
1279     resource->increasePreloadCount();
1280
1281     if (!m_preloads)
1282         m_preloads = std::make_unique<ListHashSet<CachedResource*>>();
1283     m_preloads->add(resource.get());
1284
1285 #if PRELOAD_DEBUG
1286     printf("PRELOADING %s\n",  resource->url().latin1().data());
1287 #endif
1288     return resource;
1289 }
1290
1291 bool CachedResourceLoader::isPreloaded(const String& urlString) const
1292 {
1293     const URL& url = m_document->completeURL(urlString);
1294
1295     if (m_preloads) {
1296         for (auto& resource : *m_preloads) {
1297             if (resource->url() == url)
1298                 return true;
1299         }
1300     }
1301
1302     for (auto& pendingPreload : m_pendingPreloads) {
1303         if (pendingPreload.m_request.resourceRequest().url() == url)
1304             return true;
1305     }
1306     return false;
1307 }
1308
1309 void CachedResourceLoader::clearPreloads()
1310 {
1311 #if PRELOAD_DEBUG
1312     printPreloadStats();
1313 #endif
1314     if (!m_preloads)
1315         return;
1316
1317     for (auto* resource : *m_preloads) {
1318         resource->decreasePreloadCount();
1319         bool deleted = resource->deleteIfPossible();
1320         if (!deleted && resource->preloadResult() == CachedResource::PreloadNotReferenced)
1321             MemoryCache::singleton().remove(*resource);
1322     }
1323     m_preloads = nullptr;
1324 }
1325
1326 void CachedResourceLoader::clearPendingPreloads()
1327 {
1328     m_pendingPreloads.clear();
1329 }
1330
1331 #if PRELOAD_DEBUG
1332 void CachedResourceLoader::printPreloadStats()
1333 {
1334     unsigned scripts = 0;
1335     unsigned scriptMisses = 0;
1336     unsigned stylesheets = 0;
1337     unsigned stylesheetMisses = 0;
1338     unsigned images = 0;
1339     unsigned imageMisses = 0;
1340     for (auto& resource : m_preloads) {
1341         if (resource->preloadResult() == CachedResource::PreloadNotReferenced)
1342             printf("!! UNREFERENCED PRELOAD %s\n", resource->url().latin1().data());
1343         else if (resource->preloadResult() == CachedResource::PreloadReferencedWhileComplete)
1344             printf("HIT COMPLETE PRELOAD %s\n", resource->url().latin1().data());
1345         else if (resource->preloadResult() == CachedResource::PreloadReferencedWhileLoading)
1346             printf("HIT LOADING PRELOAD %s\n", resource->url().latin1().data());
1347
1348         if (resource->type() == CachedResource::Script) {
1349             scripts++;
1350             if (resource->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
1351                 scriptMisses++;
1352         } else if (resource->type() == CachedResource::CSSStyleSheet) {
1353             stylesheets++;
1354             if (resource->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
1355                 stylesheetMisses++;
1356         } else {
1357             images++;
1358             if (resource->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
1359                 imageMisses++;
1360         }
1361
1362         if (resource->errorOccurred() && resource->preloadResult() == CachedResource::PreloadNotReferenced)
1363             MemoryCache::singleton().remove(resource);
1364
1365         resource->decreasePreloadCount();
1366     }
1367     m_preloads = nullptr;
1368
1369     if (scripts)
1370         printf("SCRIPTS: %d (%d hits, hit rate %d%%)\n", scripts, scripts - scriptMisses, (scripts - scriptMisses) * 100 / scripts);
1371     if (stylesheets)
1372         printf("STYLESHEETS: %d (%d hits, hit rate %d%%)\n", stylesheets, stylesheets - stylesheetMisses, (stylesheets - stylesheetMisses) * 100 / stylesheets);
1373     if (images)
1374         printf("IMAGES:  %d (%d hits, hit rate %d%%)\n", images, images - imageMisses, (images - imageMisses) * 100 / images);
1375 }
1376 #endif
1377
1378 const ResourceLoaderOptions& CachedResourceLoader::defaultCachedResourceOptions()
1379 {
1380     static ResourceLoaderOptions options(SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, ClientCredentialPolicy::MayAskClientForCredentials, FetchOptions::Credentials::Include, DoSecurityCheck, FetchOptions::Mode::NoCors, DoNotIncludeCertificateInfo, ContentSecurityPolicyImposition::DoPolicyCheck, DefersLoadingPolicy::AllowDefersLoading, CachingPolicy::AllowCaching);
1381     return options;
1382 }
1383
1384 bool CachedResourceLoader::isAlwaysOnLoggingAllowed() const
1385 {
1386     return m_documentLoader ? m_documentLoader->isAlwaysOnLoggingAllowed() : true;
1387 }
1388
1389 }