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