94f380a0bd997444a9cb2c53a9034b08bf140457
[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, 2005, 2006, 2007, 2008, 2009, 2010, 2011 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 "ContentExtensionRule.h"
42 #include "ContentSecurityPolicy.h"
43 #include "DOMWindow.h"
44 #include "DiagnosticLoggingClient.h"
45 #include "DiagnosticLoggingKeys.h"
46 #include "Document.h"
47 #include "DocumentLoader.h"
48 #include "Frame.h"
49 #include "FrameLoader.h"
50 #include "FrameLoaderClient.h"
51 #include "HTMLElement.h"
52 #include "HTMLFrameOwnerElement.h"
53 #include "LoaderStrategy.h"
54 #include "Logging.h"
55 #include "MainFrame.h"
56 #include "MemoryCache.h"
57 #include "Page.h"
58 #include "PingLoader.h"
59 #include "PlatformStrategies.h"
60 #include "RenderElement.h"
61 #include "ResourceLoadInfo.h"
62 #include "ResourceLoadScheduler.h"
63 #include "ScriptController.h"
64 #include "SecurityOrigin.h"
65 #include "SessionID.h"
66 #include "Settings.h"
67 #include "StyleSheetContents.h"
68 #include "UserContentController.h"
69 #include "UserStyleSheet.h"
70 #include <wtf/text/CString.h>
71 #include <wtf/text/WTFString.h>
72
73 #if ENABLE(VIDEO_TRACK)
74 #include "CachedTextTrack.h"
75 #endif
76
77 #if ENABLE(RESOURCE_TIMING)
78 #include "Performance.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::RawResource:
103     case CachedResource::MainResource:
104         return new CachedRawResource(request, type, sessionID);
105 #if ENABLE(XSLT)
106     case CachedResource::XSLStyleSheet:
107         return new CachedXSLStyleSheet(request, sessionID);
108 #endif
109 #if ENABLE(LINK_PREFETCH)
110     case CachedResource::LinkPrefetch:
111         return new CachedResource(request, CachedResource::LinkPrefetch, sessionID);
112     case CachedResource::LinkSubresource:
113         return new CachedResource(request, CachedResource::LinkSubresource, sessionID);
114 #endif
115 #if ENABLE(VIDEO_TRACK)
116     case CachedResource::TextTrackResource:
117         return new CachedTextTrack(request, sessionID);
118 #endif
119     }
120     ASSERT_NOT_REACHED();
121     return 0;
122 }
123
124 CachedResourceLoader::CachedResourceLoader(DocumentLoader* documentLoader)
125     : m_document(0)
126     , m_documentLoader(documentLoader)
127     , m_requestCount(0)
128     , m_garbageCollectDocumentResourcesTimer(*this, &CachedResourceLoader::garbageCollectDocumentResourcesTimerFired)
129     , m_autoLoadImages(true)
130     , m_imagesEnabled(true)
131     , m_allowStaleResources(false)
132 {
133 }
134
135 CachedResourceLoader::~CachedResourceLoader()
136 {
137     m_documentLoader = 0;
138     m_document = 0;
139
140     clearPreloads();
141     DocumentResourceMap::iterator end = m_documentResources.end();
142     for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it)
143         it->value->setOwningCachedResourceLoader(0);
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     URL url = m_document->completeURL(resourceURL);
152     return cachedResource(url); 
153 }
154
155 CachedResource* CachedResourceLoader::cachedResource(const URL& resourceURL) const
156 {
157     URL url = MemoryCache::removeFragmentIdentifierIfNeeded(resourceURL);
158     return m_documentResources.get(url).get(); 
159 }
160
161 Frame* CachedResourceLoader::frame() const
162 {
163     return m_documentLoader ? m_documentLoader->frame() : 0;
164 }
165
166 SessionID CachedResourceLoader::sessionID() const
167 {
168     SessionID sessionID = SessionID::defaultSessionID();
169
170     if (Frame* f = frame())
171         sessionID = f->page()->sessionID();
172
173     return sessionID;
174 }
175
176 CachedResourceHandle<CachedImage> CachedResourceLoader::requestImage(CachedResourceRequest& request, ShouldBypassMainWorldContentSecurityPolicy shouldBypassMainWorldContentSecurityPolicy)
177 {
178     if (Frame* frame = this->frame()) {
179         if (frame->loader().pageDismissalEventBeingDispatched() != FrameLoader::NoDismissal) {
180             URL requestURL = request.resourceRequest().url();
181             if (requestURL.isValid() && canRequest(CachedResource::ImageResource, requestURL, request.options(), request.forPreload()))
182                 PingLoader::loadImage(*frame, requestURL);
183             return nullptr;
184         }
185     }
186     
187     request.setDefer(clientDefersImage(request.resourceRequest().url()) ? CachedResourceRequest::DeferredByClient : CachedResourceRequest::NoDefer);
188     return downcast<CachedImage>(requestResource(CachedResource::ImageResource, request, shouldBypassMainWorldContentSecurityPolicy).get());
189 }
190
191 CachedResourceHandle<CachedFont> CachedResourceLoader::requestFont(CachedResourceRequest& request, bool isSVG)
192 {
193 #if ENABLE(SVG_FONTS)
194     if (isSVG)
195         return downcast<CachedSVGFont>(requestResource(CachedResource::SVGFontResource, request).get());
196 #else
197     UNUSED_PARAM(isSVG);
198 #endif
199     return downcast<CachedFont>(requestResource(CachedResource::FontResource, request).get());
200 }
201
202 #if ENABLE(VIDEO_TRACK)
203 CachedResourceHandle<CachedTextTrack> CachedResourceLoader::requestTextTrack(CachedResourceRequest& request)
204 {
205     return downcast<CachedTextTrack>(requestResource(CachedResource::TextTrackResource, request).get());
206 }
207 #endif
208
209 CachedResourceHandle<CachedCSSStyleSheet> CachedResourceLoader::requestCSSStyleSheet(CachedResourceRequest& request)
210 {
211     return downcast<CachedCSSStyleSheet>(requestResource(CachedResource::CSSStyleSheet, request).get());
212 }
213
214 CachedResourceHandle<CachedCSSStyleSheet> CachedResourceLoader::requestUserCSSStyleSheet(CachedResourceRequest& request)
215 {
216     URL url = MemoryCache::removeFragmentIdentifierIfNeeded(request.resourceRequest().url());
217
218 #if ENABLE(CACHE_PARTITIONING)
219     request.mutableResourceRequest().setDomainForCachePartition(document()->topOrigin()->domainForCachePartition());
220 #endif
221
222     auto& memoryCache = MemoryCache::singleton();
223     if (CachedResource* existing = memoryCache.resourceForRequest(request.resourceRequest(), sessionID())) {
224         if (is<CachedCSSStyleSheet>(*existing))
225             return downcast<CachedCSSStyleSheet>(existing);
226         memoryCache.remove(*existing);
227     }
228     if (url.string() != request.resourceRequest().url())
229         request.mutableResourceRequest().setURL(url);
230
231     CachedResourceHandle<CachedCSSStyleSheet> userSheet = new CachedCSSStyleSheet(request.resourceRequest(), request.charset(), sessionID());
232
233     memoryCache.add(*userSheet);
234     // FIXME: loadResource calls setOwningCachedResourceLoader() if the resource couldn't be added to cache. Does this function need to call it, too?
235
236     userSheet->load(*this, ResourceLoaderOptions(DoNotSendCallbacks, SniffContent, BufferData, AllowStoredCredentials, AskClientForAllCredentials, SkipSecurityCheck, UseDefaultOriginRestrictionsForType, DoNotIncludeCertificateInfo));
237     
238     return userSheet;
239 }
240
241 CachedResourceHandle<CachedScript> CachedResourceLoader::requestScript(CachedResourceRequest& request)
242 {
243     return downcast<CachedScript>(requestResource(CachedResource::Script, request).get());
244 }
245
246 #if ENABLE(XSLT)
247 CachedResourceHandle<CachedXSLStyleSheet> CachedResourceLoader::requestXSLStyleSheet(CachedResourceRequest& request)
248 {
249     return downcast<CachedXSLStyleSheet>(requestResource(CachedResource::XSLStyleSheet, request).get());
250 }
251 #endif
252
253 CachedResourceHandle<CachedSVGDocument> CachedResourceLoader::requestSVGDocument(CachedResourceRequest& request)
254 {
255     return downcast<CachedSVGDocument>(requestResource(CachedResource::SVGDocumentResource, request).get());
256 }
257
258 #if ENABLE(LINK_PREFETCH)
259 CachedResourceHandle<CachedResource> CachedResourceLoader::requestLinkResource(CachedResource::Type type, CachedResourceRequest& request)
260 {
261     ASSERT(frame());
262     ASSERT(type == CachedResource::LinkPrefetch || type == CachedResource::LinkSubresource);
263     return requestResource(type, request);
264 }
265 #endif
266
267 CachedResourceHandle<CachedRawResource> CachedResourceLoader::requestRawResource(CachedResourceRequest& request)
268 {
269     return downcast<CachedRawResource>(requestResource(CachedResource::RawResource, request).get());
270 }
271
272 CachedResourceHandle<CachedRawResource> CachedResourceLoader::requestMainResource(CachedResourceRequest& request)
273 {
274     return downcast<CachedRawResource>(requestResource(CachedResource::MainResource, request).get());
275 }
276
277 static MixedContentChecker::ContentType contentTypeFromResourceType(CachedResource::Type type)
278 {
279     switch (type) {
280     case CachedResource::ImageResource:
281             return MixedContentChecker::ContentType::ActiveCanWarn;
282
283     case CachedResource::CSSStyleSheet:
284     case CachedResource::Script:
285     case CachedResource::FontResource:
286         return MixedContentChecker::ContentType::Active;
287
288 #if ENABLE(SVG_FONTS)
289     case CachedResource::SVGFontResource:
290         return MixedContentChecker::ContentType::Active;
291 #endif
292
293     case CachedResource::RawResource:
294     case CachedResource::SVGDocumentResource:
295         return MixedContentChecker::ContentType::Active;
296 #if ENABLE(XSLT)
297     case CachedResource::XSLStyleSheet:
298         return MixedContentChecker::ContentType::Active;
299 #endif
300
301 #if ENABLE(LINK_PREFETCH)
302     case CachedResource::LinkPrefetch:
303     case CachedResource::LinkSubresource:
304         return MixedContentChecker::ContentType::Active;
305 #endif
306
307 #if ENABLE(VIDEO_TRACK)
308     case CachedResource::TextTrackResource:
309         return MixedContentChecker::ContentType::Active;
310 #endif
311     default:
312         ASSERT_NOT_REACHED();
313         return MixedContentChecker::ContentType::Active;
314     }
315 }
316
317 bool CachedResourceLoader::checkInsecureContent(CachedResource::Type type, const URL& url) const
318 {
319     switch (type) {
320     case CachedResource::Script:
321 #if ENABLE(XSLT)
322     case CachedResource::XSLStyleSheet:
323 #endif
324     case CachedResource::SVGDocumentResource:
325     case CachedResource::CSSStyleSheet:
326         // These resource can inject script into the current document (Script,
327         // XSL) or exfiltrate the content of the current document (CSS).
328         if (Frame* f = frame())
329             if (!f->loader().mixedContentChecker().canRunInsecureContent(m_document->securityOrigin(), url))
330                 return false;
331         break;
332 #if ENABLE(VIDEO_TRACK)
333     case CachedResource::TextTrackResource:
334 #endif
335     case CachedResource::RawResource:
336     case CachedResource::ImageResource:
337 #if ENABLE(SVG_FONTS)
338     case CachedResource::SVGFontResource:
339 #endif
340     case CachedResource::FontResource: {
341         // These resources can corrupt only the frame's pixels.
342         if (Frame* f = frame()) {
343             Frame& topFrame = f->tree().top();
344             if (!topFrame.loader().mixedContentChecker().canDisplayInsecureContent(topFrame.document()->securityOrigin(), contentTypeFromResourceType(type), url))
345                 return false;
346         }
347         break;
348     }
349     case CachedResource::MainResource:
350 #if ENABLE(LINK_PREFETCH)
351     case CachedResource::LinkPrefetch:
352     case CachedResource::LinkSubresource:
353         // Prefetch cannot affect the current document.
354 #endif
355         break;
356     }
357     return true;
358 }
359
360 bool CachedResourceLoader::canRequest(CachedResource::Type type, const URL& url, const ResourceLoaderOptions& options, bool forPreload, ShouldBypassMainWorldContentSecurityPolicy bypassMainWorldContentSecurityPolicy)
361 {
362     if (document() && !document()->securityOrigin()->canDisplay(url)) {
363         if (!forPreload)
364             FrameLoader::reportLocalLoadFailed(frame(), url.stringCenterEllipsizedToLength());
365         LOG(ResourceLoading, "CachedResourceLoader::requestResource URL was not allowed by SecurityOrigin::canDisplay");
366         return 0;
367     }
368
369
370     // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved.
371     bool shouldBypassMainWorldContentSecurityPolicy = (bypassMainWorldContentSecurityPolicy == ShouldBypassMainWorldContentSecurityPolicy::Yes)
372         || (frame() && frame()->script().shouldBypassMainWorldContentSecurityPolicy());
373
374     // Some types of resources can be loaded only from the same origin.  Other
375     // types of resources, like Images, Scripts, and CSS, can be loaded from
376     // any URL.
377     switch (type) {
378     case CachedResource::MainResource:
379     case CachedResource::ImageResource:
380     case CachedResource::CSSStyleSheet:
381     case CachedResource::Script:
382 #if ENABLE(SVG_FONTS)
383     case CachedResource::SVGFontResource:
384 #endif
385     case CachedResource::FontResource:
386     case CachedResource::RawResource:
387 #if ENABLE(LINK_PREFETCH)
388     case CachedResource::LinkPrefetch:
389     case CachedResource::LinkSubresource:
390 #endif
391 #if ENABLE(VIDEO_TRACK)
392     case CachedResource::TextTrackResource:
393 #endif
394         if (options.requestOriginPolicy() == RestrictToSameOrigin && !m_document->securityOrigin()->canRequest(url)) {
395             printAccessDeniedMessage(url);
396             return false;
397         }
398         break;
399     case CachedResource::SVGDocumentResource:
400 #if ENABLE(XSLT)
401     case CachedResource::XSLStyleSheet:
402         if (!m_document->securityOrigin()->canRequest(url)) {
403             printAccessDeniedMessage(url);
404             return false;
405         }
406 #endif
407         break;
408     }
409
410     switch (type) {
411 #if ENABLE(XSLT)
412     case CachedResource::XSLStyleSheet:
413         if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowScriptFromSource(url))
414             return false;
415         break;
416 #endif
417     case CachedResource::Script:
418         if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowScriptFromSource(url))
419             return false;
420         if (frame() && !frame()->settings().isScriptEnabled())
421             return false;
422         break;
423     case CachedResource::CSSStyleSheet:
424         if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowStyleFromSource(url))
425             return false;
426         break;
427     case CachedResource::SVGDocumentResource:
428     case CachedResource::ImageResource:
429         if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowImageFromSource(url))
430             return false;
431         break;
432 #if ENABLE(SVG_FONTS)
433     case CachedResource::SVGFontResource:
434 #endif
435     case CachedResource::FontResource: {
436         if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowFontFromSource(url))
437             return false;
438         break;
439     }
440     case CachedResource::MainResource:
441     case CachedResource::RawResource:
442 #if ENABLE(LINK_PREFETCH)
443     case CachedResource::LinkPrefetch:
444     case CachedResource::LinkSubresource:
445 #endif
446         break;
447 #if ENABLE(VIDEO_TRACK)
448     case CachedResource::TextTrackResource:
449         if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowMediaFromSource(url))
450             return false;
451         break;
452 #endif
453     }
454
455     // SVG Images have unique security rules that prevent all subresource requests except for data urls.
456     if (type != CachedResource::MainResource && frame() && frame()->page()) {
457         if (frame()->page()->chrome().client().isSVGImageChromeClient() && !url.protocolIsData())
458             return false;
459     }
460
461     // Last of all, check for insecure content. We do this last so that when
462     // folks block insecure content with a CSP policy, they don't get a warning.
463     // They'll still get a warning in the console about CSP blocking the load.
464
465     // FIXME: Should we consider forPreload here?
466     if (!checkInsecureContent(type, url))
467         return false;
468
469     return true;
470 }
471
472 bool CachedResourceLoader::shouldContinueAfterNotifyingLoadedFromMemoryCache(const CachedResourceRequest& request, CachedResource* resource)
473 {
474     if (!resource || !frame() || resource->status() != CachedResource::Cached)
475         return true;
476
477     ResourceRequest newRequest = ResourceRequest(resource->url());
478     if (request.resourceRequest().hiddenFromInspector())
479         newRequest.setHiddenFromInspector(true);
480     frame()->loader().loadedResourceFromMemoryCache(resource, newRequest);
481
482     // FIXME <http://webkit.org/b/113251>: If the delegate modifies the request's
483     // URL, it is no longer appropriate to use this CachedResource.
484     return !newRequest.isNull();
485 }
486
487 static inline void logMemoryCacheResourceRequest(Frame* frame, const String& description, const String& value = String())
488 {
489     if (!frame)
490         return;
491     if (value.isNull())
492         frame->mainFrame().diagnosticLoggingClient().logDiagnosticMessage(DiagnosticLoggingKeys::resourceRequestKey(), description, ShouldSample::Yes);
493     else
494         frame->mainFrame().diagnosticLoggingClient().logDiagnosticMessageWithValue(DiagnosticLoggingKeys::resourceRequestKey(), description, value, ShouldSample::Yes);
495 }
496
497 CachedResourceHandle<CachedResource> CachedResourceLoader::requestResource(CachedResource::Type type, CachedResourceRequest& request, ShouldBypassMainWorldContentSecurityPolicy shouldBypassMainWorldContentSecurityPolicy)
498 {
499     URL url = request.resourceRequest().url();
500     
501     LOG(ResourceLoading, "CachedResourceLoader::requestResource '%s', charset '%s', priority=%d, forPreload=%u", url.stringCenterEllipsizedToLength().latin1().data(), request.charset().latin1().data(), request.priority() ? request.priority().value() : -1, request.forPreload());
502     
503     // If only the fragment identifiers differ, it is the same resource.
504     url = MemoryCache::removeFragmentIdentifierIfNeeded(url);
505
506     if (!url.isValid())
507         return nullptr;
508
509     if (!canRequest(type, url, request.options(), request.forPreload(), shouldBypassMainWorldContentSecurityPolicy))
510         return nullptr;
511
512 #if ENABLE(CONTENT_EXTENSIONS)
513     if (frame() && frame()->mainFrame().page() && frame()->mainFrame().page()->userContentController() && m_documentLoader)
514         frame()->mainFrame().page()->userContentController()->processContentExtensionRulesForLoad(request.mutableResourceRequest(), toResourceType(type), *m_documentLoader);
515
516     if (request.mutableResourceRequest().isNull())
517         return nullptr;
518 #endif
519
520     auto& memoryCache = MemoryCache::singleton();
521     if (memoryCache.disabled()) {
522         DocumentResourceMap::iterator it = m_documentResources.find(url.string());
523         if (it != m_documentResources.end()) {
524             it->value->setOwningCachedResourceLoader(0);
525             m_documentResources.remove(it);
526         }
527     }
528
529     // See if we can use an existing resource from the cache.
530     CachedResourceHandle<CachedResource> resource;
531 #if ENABLE(CACHE_PARTITIONING)
532     if (document())
533         request.mutableResourceRequest().setDomainForCachePartition(document()->topOrigin()->domainForCachePartition());
534 #endif
535
536     resource = memoryCache.resourceForRequest(request.resourceRequest(), sessionID());
537
538     logMemoryCacheResourceRequest(frame(), resource ? DiagnosticLoggingKeys::inMemoryCacheKey() : DiagnosticLoggingKeys::notInMemoryCacheKey());
539
540     const RevalidationPolicy policy = determineRevalidationPolicy(type, request.mutableResourceRequest(), request.forPreload(), resource.get(), request.defer());
541     switch (policy) {
542     case Reload:
543         memoryCache.remove(*resource);
544         FALLTHROUGH;
545     case Load:
546         if (resource)
547             logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedKey());
548         resource = loadResource(type, request);
549         break;
550     case Revalidate:
551         if (resource)
552             logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::revalidatingKey());
553         resource = revalidateResource(request, resource.get());
554         break;
555     case Use:
556         if (!shouldContinueAfterNotifyingLoadedFromMemoryCache(request, resource.get()))
557             return nullptr;
558         logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::usedKey());
559         memoryCache.resourceAccessed(*resource);
560         break;
561     }
562
563     if (!resource)
564         return nullptr;
565
566     if (!request.forPreload() || policy != Use)
567         resource->setLoadPriority(request.priority());
568
569     if ((policy != Use || resource->stillNeedsLoad()) && CachedResourceRequest::NoDefer == request.defer()) {
570         resource->load(*this, request.options());
571
572         // We don't support immediate loads, but we do support immediate failure.
573         if (resource->errorOccurred()) {
574             if (resource->inCache())
575                 memoryCache.remove(*resource);
576             return nullptr;
577         }
578     }
579
580     if (document() && !document()->loadEventFinished() && !request.resourceRequest().url().protocolIsData())
581         m_validatedURLs.add(request.resourceRequest().url());
582
583     ASSERT(resource->url() == url.string());
584     m_documentResources.set(resource->url(), resource);
585     return resource;
586 }
587
588 void CachedResourceLoader::documentDidFinishLoadEvent()
589 {
590     m_validatedURLs.clear();
591 }
592
593 CachedResourceHandle<CachedResource> CachedResourceLoader::revalidateResource(const CachedResourceRequest& request, CachedResource* resource)
594 {
595     ASSERT(resource);
596     ASSERT(resource->inCache());
597     auto& memoryCache = MemoryCache::singleton();
598     ASSERT(!memoryCache.disabled());
599     ASSERT(resource->canUseCacheValidator());
600     ASSERT(!resource->resourceToRevalidate());
601     ASSERT(resource->sessionID() == sessionID());
602
603     CachedResourceHandle<CachedResource> newResource = createResource(resource->type(), resource->resourceRequest(), resource->encoding(), resource->sessionID());
604     
605     LOG(ResourceLoading, "Resource %p created to revalidate %p", newResource.get(), resource);
606     newResource->setResourceToRevalidate(resource);
607     
608     memoryCache.remove(*resource);
609     memoryCache.add(*newResource);
610 #if ENABLE(RESOURCE_TIMING)
611     storeResourceTimingInitiatorInformation(resource, request);
612 #else
613     UNUSED_PARAM(request);
614 #endif
615     return newResource;
616 }
617
618 CachedResourceHandle<CachedResource> CachedResourceLoader::loadResource(CachedResource::Type type, CachedResourceRequest& request)
619 {
620     auto& memoryCache = MemoryCache::singleton();
621     ASSERT(!memoryCache.resourceForRequest(request.resourceRequest(), sessionID()));
622
623     LOG(ResourceLoading, "Loading CachedResource for '%s'.", request.resourceRequest().url().stringCenterEllipsizedToLength().latin1().data());
624
625     CachedResourceHandle<CachedResource> resource = createResource(type, request.mutableResourceRequest(), request.charset(), sessionID());
626
627     if (!memoryCache.add(*resource))
628         resource->setOwningCachedResourceLoader(this);
629 #if ENABLE(RESOURCE_TIMING)
630     storeResourceTimingInitiatorInformation(resource, request);
631 #endif
632     return resource;
633 }
634
635 #if ENABLE(RESOURCE_TIMING)
636 void CachedResourceLoader::storeResourceTimingInitiatorInformation(const CachedResourceHandle<CachedResource>& resource, const CachedResourceRequest& request)
637 {
638     if (resource->type() == CachedResource::MainResource) {
639         // <iframe>s should report the initial navigation requested by the parent document, but not subsequent navigations.
640         if (frame()->ownerElement() && m_documentLoader->frameLoader()->stateMachine().committingFirstRealLoad()) {
641             InitiatorInfo info = { frame()->ownerElement()->localName(), monotonicallyIncreasingTime() };
642             m_initiatorMap.add(resource.get(), info);
643         }
644     } else {
645         InitiatorInfo info = { request.initiatorName(), monotonicallyIncreasingTime() };
646         m_initiatorMap.add(resource.get(), info);
647     }
648 }
649 #endif // ENABLE(RESOURCE_TIMING)
650
651 CachedResourceLoader::RevalidationPolicy CachedResourceLoader::determineRevalidationPolicy(CachedResource::Type type, ResourceRequest& request, bool forPreload, CachedResource* existingResource, CachedResourceRequest::DeferOption defer) const
652 {
653     if (!existingResource)
654         return Load;
655
656     // We already have a preload going for this URL.
657     if (forPreload && existingResource->isPreloaded())
658         return Use;
659
660     // If the same URL has been loaded as a different type, we need to reload.
661     if (existingResource->type() != type) {
662         LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to type mismatch.");
663         logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonTypeMismatchKey());
664         return Reload;
665     }
666
667     if (!existingResource->canReuse(request))
668         return Reload;
669
670     // Conditional requests should have failed canReuse check.
671     ASSERT(!request.isConditional());
672
673     // Do not load from cache if images are not enabled. The load for this image will be blocked
674     // in CachedImage::load.
675     if (CachedResourceRequest::DeferredByClient == defer)
676         return Reload;
677     
678     // Don't reload resources while pasting.
679     if (m_allowStaleResources)
680         return Use;
681     
682     // Alwaus use preloads.
683     if (existingResource->isPreloaded())
684         return Use;
685
686     // Validate the redirect chain.
687     bool cachePolicyIsHistoryBuffer = cachePolicy(type) == CachePolicyHistoryBuffer;
688     if (!existingResource->redirectChainAllowsReuse(cachePolicyIsHistoryBuffer ? ReuseExpiredRedirection : DoNotReuseExpiredRedirection)) {
689         LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to not cached or expired redirections.");
690         logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonRedirectChainKey());
691         return Reload;
692     }
693
694     // CachePolicyHistoryBuffer uses the cache except if this is a main resource with "cache-control: no-store".
695     if (cachePolicyIsHistoryBuffer) {
696         // FIXME: Ignoring "cache-control: no-cache" for sub-resources on history navigation but not the main
697         // resource is inconsistent. We should probably harmonize this.
698         if (!existingResource->response().cacheControlContainsNoStore() || type != CachedResource::MainResource)
699             return Use;
700     }
701
702     // Don't reuse resources with Cache-control: no-store.
703     if (existingResource->response().cacheControlContainsNoStore()) {
704         LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to Cache-control: no-store.");
705         logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonNoStoreKey());
706         return Reload;
707     }
708
709     // If credentials were sent with the previous request and won't be
710     // with this one, or vice versa, re-fetch the resource.
711     //
712     // This helps with the case where the server sends back
713     // "Access-Control-Allow-Origin: *" all the time, but some of the
714     // client's requests are made without CORS and some with.
715     if (existingResource->resourceRequest().allowCookies() != request.allowCookies()) {
716         LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to difference in credentials settings.");
717         logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonCredentialSettingsKey());
718         return Reload;
719     }
720
721     // During the initial load, avoid loading the same resource multiple times for a single document, even if the cache policies would tell us to.
722     if (document() && !document()->loadEventFinished() && m_validatedURLs.contains(existingResource->url()))
723         return Use;
724
725     // CachePolicyReload always reloads
726     if (cachePolicy(type) == CachePolicyReload) {
727         LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to CachePolicyReload.");
728         logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonReloadKey());
729         return Reload;
730     }
731     
732     // We'll try to reload the resource if it failed last time.
733     if (existingResource->errorOccurred()) {
734         LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicye reloading due to resource being in the error state");
735         logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonErrorKey());
736         return Reload;
737     }
738     
739     // For resources that are not yet loaded we ignore the cache policy.
740     if (existingResource->isLoading())
741         return Use;
742
743     // Check if the cache headers requires us to revalidate (cache expiration for example).
744     if (existingResource->mustRevalidateDueToCacheHeaders(*this, cachePolicy(type))) {
745         // See if the resource has usable ETag or Last-modified headers.
746         if (existingResource->canUseCacheValidator())
747             return Revalidate;
748         
749         // No, must reload.
750         LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to missing cache validators.");
751         logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonMustRevalidateNoValidatorKey());
752         return Reload;
753     }
754
755     return Use;
756 }
757
758 void CachedResourceLoader::printAccessDeniedMessage(const URL& url) const
759 {
760     if (url.isNull())
761         return;
762
763     if (!frame())
764         return;
765
766     String message;
767     if (!m_document || m_document->url().isNull())
768         message = "Unsafe attempt to load URL " + url.stringCenterEllipsizedToLength() + '.';
769     else
770         message = "Unsafe attempt to load URL " + url.stringCenterEllipsizedToLength() + " from frame with URL " + m_document->url().stringCenterEllipsizedToLength() + ". Domains, protocols and ports must match.\n";
771
772     frame()->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message);
773 }
774
775 void CachedResourceLoader::setAutoLoadImages(bool enable)
776 {
777     if (enable == m_autoLoadImages)
778         return;
779
780     m_autoLoadImages = enable;
781
782     if (!m_autoLoadImages)
783         return;
784
785     reloadImagesIfNotDeferred();
786 }
787
788 void CachedResourceLoader::setImagesEnabled(bool enable)
789 {
790     if (enable == m_imagesEnabled)
791         return;
792
793     m_imagesEnabled = enable;
794
795     if (!m_imagesEnabled)
796         return;
797
798     reloadImagesIfNotDeferred();
799 }
800
801 bool CachedResourceLoader::clientDefersImage(const URL&) const
802 {
803     return !m_imagesEnabled;
804 }
805
806 bool CachedResourceLoader::shouldPerformImageLoad(const URL& url) const
807 {
808     return m_autoLoadImages || url.protocolIsData();
809 }
810
811 bool CachedResourceLoader::shouldDeferImageLoad(const URL& url) const
812 {
813     return clientDefersImage(url) || !shouldPerformImageLoad(url);
814 }
815
816 void CachedResourceLoader::reloadImagesIfNotDeferred()
817 {
818     DocumentResourceMap::iterator end = m_documentResources.end();
819     for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it) {
820         CachedResource* resource = it->value.get();
821         if (is<CachedImage>(*resource) && resource->stillNeedsLoad() && !clientDefersImage(resource->url()))
822             downcast<CachedImage>(*resource).load(*this, defaultCachedResourceOptions());
823     }
824 }
825
826 CachePolicy CachedResourceLoader::cachePolicy(CachedResource::Type type) const
827 {
828     if (!frame())
829         return CachePolicyVerify;
830
831     if (type != CachedResource::MainResource)
832         return frame()->loader().subresourceCachePolicy();
833     
834     switch (frame()->loader().loadType()) {
835     case FrameLoadType::ReloadFromOrigin:
836     case FrameLoadType::Reload:
837         return CachePolicyReload;
838     case FrameLoadType::Back:
839     case FrameLoadType::Forward:
840     case FrameLoadType::IndexedBackForward:
841         // Do not revalidate cached main resource on back/forward navigation.
842         return CachePolicyHistoryBuffer;
843     default:
844         return CachePolicyVerify;
845     }
846 }
847
848 void CachedResourceLoader::removeCachedResource(CachedResource& resource)
849 {
850 #ifndef NDEBUG
851     DocumentResourceMap::iterator it = m_documentResources.find(resource.url());
852     if (it != m_documentResources.end())
853         ASSERT(it->value.get() == &resource);
854 #endif
855     m_documentResources.remove(resource.url());
856 }
857
858 void CachedResourceLoader::addCachedResource(CachedResource& resource)
859 {
860     m_documentResources.set(resource.url(), &resource);
861
862     if (!MemoryCache::singleton().add(resource))
863         resource.setOwningCachedResourceLoader(this);
864 }
865
866 void CachedResourceLoader::loadDone(CachedResource* resource, bool shouldPerformPostLoadActions)
867 {
868     RefPtr<DocumentLoader> protectDocumentLoader(m_documentLoader);
869     RefPtr<Document> protectDocument(m_document);
870
871 #if ENABLE(RESOURCE_TIMING)
872     if (resource && resource->response().isHTTP() && ((!resource->errorOccurred() && !resource->wasCanceled()) || resource->response().httpStatusCode() == 304)) {
873         HashMap<CachedResource*, InitiatorInfo>::iterator initiatorIt = m_initiatorMap.find(resource);
874         if (initiatorIt != m_initiatorMap.end()) {
875             ASSERT(document());
876             Document* initiatorDocument = document();
877             if (resource->type() == CachedResource::MainResource)
878                 initiatorDocument = document()->parentDocument();
879             ASSERT(initiatorDocument);
880             const InitiatorInfo& info = initiatorIt->value;
881             initiatorDocument->domWindow()->performance()->addResourceTiming(info.name, initiatorDocument, resource->resourceRequest(), resource->response(), info.startTime, resource->loadFinishTime());
882             m_initiatorMap.remove(initiatorIt);
883         }
884     }
885 #else
886     UNUSED_PARAM(resource);
887 #endif // ENABLE(RESOURCE_TIMING)
888
889     if (frame())
890         frame()->loader().loadDone();
891
892     if (shouldPerformPostLoadActions)
893         performPostLoadActions();
894
895     if (!m_garbageCollectDocumentResourcesTimer.isActive())
896         m_garbageCollectDocumentResourcesTimer.startOneShot(0);
897 }
898
899 // Garbage collecting m_documentResources is a workaround for the
900 // CachedResourceHandles on the RHS being strong references. Ideally this
901 // would be a weak map, however CachedResourceHandles perform additional
902 // bookkeeping on CachedResources, so instead pseudo-GC them -- when the
903 // reference count reaches 1, m_documentResources is the only reference, so
904 // remove it from the map.
905 void CachedResourceLoader::garbageCollectDocumentResourcesTimerFired()
906 {
907     garbageCollectDocumentResources();
908 }
909
910 void CachedResourceLoader::garbageCollectDocumentResources()
911 {
912     typedef Vector<String, 10> StringVector;
913     StringVector resourcesToDelete;
914
915     for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != m_documentResources.end(); ++it) {
916         if (it->value->hasOneHandle()) {
917             resourcesToDelete.append(it->key);
918             it->value->setOwningCachedResourceLoader(0);
919         }
920     }
921
922     for (StringVector::const_iterator it = resourcesToDelete.begin(); it != resourcesToDelete.end(); ++it)
923         m_documentResources.remove(*it);
924 }
925
926 void CachedResourceLoader::performPostLoadActions()
927 {
928     checkForPendingPreloads();
929
930     platformStrategies()->loaderStrategy()->resourceLoadScheduler()->servePendingRequests();
931 }
932
933 void CachedResourceLoader::incrementRequestCount(const CachedResource* res)
934 {
935     if (res->ignoreForRequestCount())
936         return;
937
938     ++m_requestCount;
939 }
940
941 void CachedResourceLoader::decrementRequestCount(const CachedResource* res)
942 {
943     if (res->ignoreForRequestCount())
944         return;
945
946     --m_requestCount;
947     ASSERT(m_requestCount > -1);
948 }
949
950 void CachedResourceLoader::preload(CachedResource::Type type, CachedResourceRequest& request, const String& charset)
951 {
952     // We always preload resources on iOS. See <https://bugs.webkit.org/show_bug.cgi?id=91276>.
953     // FIXME: We should consider adding a setting to toggle aggressive preloading behavior as opposed
954     // to making this behavior specific to iOS.
955 #if !PLATFORM(IOS)
956     bool hasRendering = m_document->bodyOrFrameset() && m_document->renderView();
957     bool canBlockParser = type == CachedResource::Script || type == CachedResource::CSSStyleSheet;
958     if (!hasRendering && !canBlockParser) {
959         // Don't preload subresources that can't block the parser before we have something to draw.
960         // This helps prevent preloads from delaying first display when bandwidth is limited.
961         PendingPreload pendingPreload = { type, request, charset };
962         m_pendingPreloads.append(pendingPreload);
963         return;
964     }
965 #endif
966     requestPreload(type, request, charset);
967 }
968
969 void CachedResourceLoader::checkForPendingPreloads() 
970 {
971     if (m_pendingPreloads.isEmpty())
972         return;
973     auto* body = m_document->bodyOrFrameset();
974     if (!body || !body->renderer())
975         return;
976 #if PLATFORM(IOS)
977     // We always preload resources on iOS. See <https://bugs.webkit.org/show_bug.cgi?id=91276>.
978     // So, we should never have any pending preloads.
979     // FIXME: We should look to avoid compiling this code entirely when building for iOS.
980     ASSERT_NOT_REACHED();
981 #endif
982     while (!m_pendingPreloads.isEmpty()) {
983         PendingPreload preload = m_pendingPreloads.takeFirst();
984         // 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).
985         if (!cachedResource(preload.m_request.resourceRequest().url()))
986             requestPreload(preload.m_type, preload.m_request, preload.m_charset);
987     }
988     m_pendingPreloads.clear();
989 }
990
991 void CachedResourceLoader::requestPreload(CachedResource::Type type, CachedResourceRequest& request, const String& charset)
992 {
993     String encoding;
994     if (type == CachedResource::Script || type == CachedResource::CSSStyleSheet)
995         encoding = charset.isEmpty() ? m_document->charset() : charset;
996
997     request.setCharset(encoding);
998     request.setForPreload(true);
999
1000     CachedResourceHandle<CachedResource> resource = requestResource(type, request);
1001     if (!resource || (m_preloads && m_preloads->contains(resource.get())))
1002         return;
1003     resource->increasePreloadCount();
1004
1005     if (!m_preloads)
1006         m_preloads = std::make_unique<ListHashSet<CachedResource*>>();
1007     m_preloads->add(resource.get());
1008
1009 #if PRELOAD_DEBUG
1010     printf("PRELOADING %s\n",  resource->url().latin1().data());
1011 #endif
1012 }
1013
1014 bool CachedResourceLoader::isPreloaded(const String& urlString) const
1015 {
1016     const URL& url = m_document->completeURL(urlString);
1017
1018     if (m_preloads) {
1019         ListHashSet<CachedResource*>::iterator end = m_preloads->end();
1020         for (ListHashSet<CachedResource*>::iterator it = m_preloads->begin(); it != end; ++it) {
1021             CachedResource* resource = *it;
1022             if (resource->url() == url)
1023                 return true;
1024         }
1025     }
1026
1027     Deque<PendingPreload>::const_iterator dequeEnd = m_pendingPreloads.end();
1028     for (Deque<PendingPreload>::const_iterator it = m_pendingPreloads.begin(); it != dequeEnd; ++it) {
1029         PendingPreload pendingPreload = *it;
1030         if (pendingPreload.m_request.resourceRequest().url() == url)
1031             return true;
1032     }
1033     return false;
1034 }
1035
1036 void CachedResourceLoader::clearPreloads()
1037 {
1038 #if PRELOAD_DEBUG
1039     printPreloadStats();
1040 #endif
1041     if (!m_preloads)
1042         return;
1043
1044     for (auto* resource : *m_preloads) {
1045         resource->decreasePreloadCount();
1046         bool deleted = resource->deleteIfPossible();
1047         if (!deleted && resource->preloadResult() == CachedResource::PreloadNotReferenced)
1048             MemoryCache::singleton().remove(*resource);
1049     }
1050     m_preloads = nullptr;
1051 }
1052
1053 void CachedResourceLoader::clearPendingPreloads()
1054 {
1055     m_pendingPreloads.clear();
1056 }
1057
1058 #if PRELOAD_DEBUG
1059 void CachedResourceLoader::printPreloadStats()
1060 {
1061     unsigned scripts = 0;
1062     unsigned scriptMisses = 0;
1063     unsigned stylesheets = 0;
1064     unsigned stylesheetMisses = 0;
1065     unsigned images = 0;
1066     unsigned imageMisses = 0;
1067     ListHashSet<CachedResource*>::iterator end = m_preloads.end();
1068     for (ListHashSet<CachedResource*>::iterator it = m_preloads.begin(); it != end; ++it) {
1069         CachedResource* res = *it;
1070         if (res->preloadResult() == CachedResource::PreloadNotReferenced)
1071             printf("!! UNREFERENCED PRELOAD %s\n", res->url().latin1().data());
1072         else if (res->preloadResult() == CachedResource::PreloadReferencedWhileComplete)
1073             printf("HIT COMPLETE PRELOAD %s\n", res->url().latin1().data());
1074         else if (res->preloadResult() == CachedResource::PreloadReferencedWhileLoading)
1075             printf("HIT LOADING PRELOAD %s\n", res->url().latin1().data());
1076         
1077         if (res->type() == CachedResource::Script) {
1078             scripts++;
1079             if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
1080                 scriptMisses++;
1081         } else if (res->type() == CachedResource::CSSStyleSheet) {
1082             stylesheets++;
1083             if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
1084                 stylesheetMisses++;
1085         } else {
1086             images++;
1087             if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
1088                 imageMisses++;
1089         }
1090         
1091         if (res->errorOccurred())
1092             MemoryCache::singleton().remove(res);
1093         
1094         res->decreasePreloadCount();
1095     }
1096     m_preloads = nullptr;
1097     
1098     if (scripts)
1099         printf("SCRIPTS: %d (%d hits, hit rate %d%%)\n", scripts, scripts - scriptMisses, (scripts - scriptMisses) * 100 / scripts);
1100     if (stylesheets)
1101         printf("STYLESHEETS: %d (%d hits, hit rate %d%%)\n", stylesheets, stylesheets - stylesheetMisses, (stylesheets - stylesheetMisses) * 100 / stylesheets);
1102     if (images)
1103         printf("IMAGES:  %d (%d hits, hit rate %d%%)\n", images, images - imageMisses, (images - imageMisses) * 100 / images);
1104 }
1105 #endif
1106
1107 const ResourceLoaderOptions& CachedResourceLoader::defaultCachedResourceOptions()
1108 {
1109     static ResourceLoaderOptions options(SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, AskClientForAllCredentials, DoSecurityCheck, UseDefaultOriginRestrictionsForType, DoNotIncludeCertificateInfo);
1110     return options;
1111 }
1112
1113 }