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