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