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