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