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