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