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