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