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