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