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