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