f82d3bdf28d6933f57d241eaf6c145e02a9cdb3d
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / fetch / ResourceFetcher.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 "core/fetch/ResourceFetcher.h"
29
30 #include "RuntimeEnabledFeatures.h"
31 #include "bindings/v8/ScriptController.h"
32 #include "core/dom/Document.h"
33 #include "core/fetch/CSSStyleSheetResource.h"
34 #include "core/fetch/CrossOriginAccessControl.h"
35 #include "core/fetch/DocumentResource.h"
36 #include "core/fetch/FetchContext.h"
37 #include "core/fetch/FontResource.h"
38 #include "core/fetch/ImageResource.h"
39 #include "core/fetch/MemoryCache.h"
40 #include "core/fetch/RawResource.h"
41 #include "core/fetch/ResourceLoader.h"
42 #include "core/fetch/ResourceLoaderSet.h"
43 #include "core/fetch/ScriptResource.h"
44 #include "core/fetch/ShaderResource.h"
45 #include "core/fetch/XSLStyleSheetResource.h"
46 #include "core/html/HTMLElement.h"
47 #include "core/html/HTMLFrameOwnerElement.h"
48 #include "core/html/imports/HTMLImport.h"
49 #include "core/inspector/InspectorInstrumentation.h"
50 #include "core/loader/DocumentLoader.h"
51 #include "core/loader/FrameLoader.h"
52 #include "core/loader/FrameLoaderClient.h"
53 #include "core/loader/PingLoader.h"
54 #include "core/loader/SubstituteData.h"
55 #include "core/loader/UniqueIdentifier.h"
56 #include "core/loader/appcache/ApplicationCacheHost.h"
57 #include "core/frame/DOMWindow.h"
58 #include "core/frame/LocalFrame.h"
59 #include "core/frame/csp/ContentSecurityPolicy.h"
60 #include "core/timing/Performance.h"
61 #include "core/timing/ResourceTimingInfo.h"
62 #include "core/frame/Settings.h"
63 #include "platform/Logging.h"
64 #include "platform/TraceEvent.h"
65 #include "platform/weborigin/SecurityOrigin.h"
66 #include "platform/weborigin/SecurityPolicy.h"
67 #include "public/platform/Platform.h"
68 #include "public/platform/WebURL.h"
69 #include "wtf/text/CString.h"
70 #include "wtf/text/WTFString.h"
71
72 #define PRELOAD_DEBUG 0
73
74 namespace WebCore {
75
76 static Resource* createResource(Resource::Type type, const ResourceRequest& request, const String& charset)
77 {
78     switch (type) {
79     case Resource::Image:
80         return new ImageResource(request);
81     case Resource::CSSStyleSheet:
82         return new CSSStyleSheetResource(request, charset);
83     case Resource::Script:
84         return new ScriptResource(request, charset);
85     case Resource::SVGDocument:
86         return new DocumentResource(request, Resource::SVGDocument);
87     case Resource::Font:
88         return new FontResource(request);
89     case Resource::MainResource:
90     case Resource::Raw:
91     case Resource::TextTrack:
92         return new RawResource(request, type);
93     case Resource::XSLStyleSheet:
94         return new XSLStyleSheetResource(request);
95     case Resource::LinkPrefetch:
96         return new Resource(request, Resource::LinkPrefetch);
97     case Resource::LinkSubresource:
98         return new Resource(request, Resource::LinkSubresource);
99     case Resource::Shader:
100         return new ShaderResource(request);
101     case Resource::ImportResource:
102         return new RawResource(request, type);
103     }
104
105     ASSERT_NOT_REACHED();
106     return 0;
107 }
108
109 static ResourceLoadPriority loadPriority(Resource::Type type, const FetchRequest& request)
110 {
111     if (request.priority() != ResourceLoadPriorityUnresolved)
112         return request.priority();
113
114     switch (type) {
115     case Resource::MainResource:
116         return ResourceLoadPriorityVeryHigh;
117     case Resource::CSSStyleSheet:
118         return ResourceLoadPriorityHigh;
119     case Resource::Raw:
120         return request.options().synchronousPolicy == RequestSynchronously ? ResourceLoadPriorityVeryHigh : ResourceLoadPriorityMedium;
121     case Resource::Script:
122     case Resource::Font:
123     case Resource::ImportResource:
124         return ResourceLoadPriorityMedium;
125     case Resource::Image:
126         // We'll default images to VeryLow, and promote whatever is visible. This improves
127         // speed-index by ~5% on average, ~14% at the 99th percentile.
128         return ResourceLoadPriorityVeryLow;
129     case Resource::XSLStyleSheet:
130         ASSERT(RuntimeEnabledFeatures::xsltEnabled());
131         return ResourceLoadPriorityHigh;
132     case Resource::SVGDocument:
133         return ResourceLoadPriorityLow;
134     case Resource::LinkPrefetch:
135         return ResourceLoadPriorityVeryLow;
136     case Resource::LinkSubresource:
137         return ResourceLoadPriorityLow;
138     case Resource::TextTrack:
139         return ResourceLoadPriorityLow;
140     case Resource::Shader:
141         return ResourceLoadPriorityMedium;
142     }
143     ASSERT_NOT_REACHED();
144     return ResourceLoadPriorityUnresolved;
145 }
146
147 static Resource* resourceFromDataURIRequest(const ResourceRequest& request, const ResourceLoaderOptions& resourceOptions)
148 {
149     const KURL& url = request.url();
150     ASSERT(url.protocolIsData());
151
152     blink::WebString mimetype;
153     blink::WebString charset;
154     RefPtr<SharedBuffer> data = PassRefPtr<SharedBuffer>(blink::Platform::current()->parseDataURL(url, mimetype, charset));
155     if (!data)
156         return 0;
157     ResourceResponse response(url, mimetype, data->size(), charset, String());
158
159     Resource* resource = createResource(Resource::Image, request, charset);
160     resource->setOptions(resourceOptions);
161     resource->responseReceived(response);
162     if (data->size())
163         resource->setResourceBuffer(data);
164     resource->finish();
165     return resource;
166 }
167
168 static void populateResourceTiming(ResourceTimingInfo* info, Resource* resource, bool clearLoadTimings)
169 {
170     info->setInitialRequest(resource->resourceRequest());
171     info->setFinalResponse(resource->response());
172     if (clearLoadTimings)
173         info->clearLoadTimings();
174     info->setLoadFinishTime(resource->loadFinishTime());
175 }
176
177 static void reportResourceTiming(ResourceTimingInfo* info, Document* initiatorDocument, bool isMainResource)
178 {
179     if (initiatorDocument && isMainResource)
180         initiatorDocument = initiatorDocument->parentDocument();
181     if (!initiatorDocument || !initiatorDocument->loader())
182         return;
183     if (DOMWindow* initiatorWindow = initiatorDocument->domWindow())
184         initiatorWindow->performance().addResourceTiming(*info, initiatorDocument);
185 }
186
187 static ResourceRequest::TargetType requestTargetType(const ResourceFetcher* fetcher, const ResourceRequest& request, Resource::Type type)
188 {
189     switch (type) {
190     case Resource::MainResource:
191         if (fetcher->frame()->tree().parent())
192             return ResourceRequest::TargetIsSubframe;
193         return ResourceRequest::TargetIsMainFrame;
194     case Resource::XSLStyleSheet:
195         ASSERT(RuntimeEnabledFeatures::xsltEnabled());
196     case Resource::CSSStyleSheet:
197         return ResourceRequest::TargetIsStyleSheet;
198     case Resource::Script:
199         return ResourceRequest::TargetIsScript;
200     case Resource::Font:
201         return ResourceRequest::TargetIsFont;
202     case Resource::Image:
203         return ResourceRequest::TargetIsImage;
204     case Resource::Shader:
205     case Resource::Raw:
206     case Resource::ImportResource:
207         return ResourceRequest::TargetIsSubresource;
208     case Resource::LinkPrefetch:
209         return ResourceRequest::TargetIsPrefetch;
210     case Resource::LinkSubresource:
211         return ResourceRequest::TargetIsSubresource;
212     case Resource::TextTrack:
213         return ResourceRequest::TargetIsTextTrack;
214     case Resource::SVGDocument:
215         return ResourceRequest::TargetIsImage;
216     }
217     ASSERT_NOT_REACHED();
218     return ResourceRequest::TargetIsSubresource;
219 }
220
221 ResourceFetcher::ResourceFetcher(DocumentLoader* documentLoader)
222     : m_document(0)
223     , m_documentLoader(documentLoader)
224     , m_requestCount(0)
225     , m_garbageCollectDocumentResourcesTimer(this, &ResourceFetcher::garbageCollectDocumentResourcesTimerFired)
226     , m_resourceTimingReportTimer(this, &ResourceFetcher::resourceTimingReportTimerFired)
227     , m_autoLoadImages(true)
228     , m_imagesEnabled(true)
229     , m_allowStaleResources(false)
230 {
231 }
232
233 ResourceFetcher::~ResourceFetcher()
234 {
235     m_documentLoader = 0;
236     m_document = 0;
237
238     clearPreloads();
239
240     // Make sure no requests still point to this ResourceFetcher
241     ASSERT(!m_requestCount);
242 }
243
244 Resource* ResourceFetcher::cachedResource(const KURL& resourceURL) const
245 {
246     KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(resourceURL);
247     return m_documentResources.get(url).get();
248 }
249
250 LocalFrame* ResourceFetcher::frame() const
251 {
252     if (m_documentLoader)
253         return m_documentLoader->frame();
254     if (m_document && m_document->import())
255         return m_document->import()->frame();
256     return 0;
257 }
258
259 FetchContext& ResourceFetcher::context() const
260 {
261     if (LocalFrame* frame = this->frame())
262         return frame->fetchContext();
263     return FetchContext::nullInstance();
264 }
265
266 ResourcePtr<Resource> ResourceFetcher::fetchSynchronously(FetchRequest& request)
267 {
268     ASSERT(document());
269     request.mutableResourceRequest().setTimeoutInterval(10);
270     ResourceLoaderOptions options(request.options());
271     options.synchronousPolicy = RequestSynchronously;
272     request.setOptions(options);
273     return requestResource(Resource::Raw, request);
274 }
275
276 ResourcePtr<ImageResource> ResourceFetcher::fetchImage(FetchRequest& request)
277 {
278     if (LocalFrame* f = frame()) {
279         if (f->document()->pageDismissalEventBeingDispatched() != Document::NoDismissal) {
280             KURL requestURL = request.resourceRequest().url();
281             if (requestURL.isValid() && canRequest(Resource::Image, requestURL, request.options(), request.forPreload(), request.originRestriction()))
282                 PingLoader::loadImage(f, requestURL);
283             return 0;
284         }
285     }
286
287     if (request.resourceRequest().url().protocolIsData())
288         preCacheDataURIImage(request);
289
290     request.setDefer(clientDefersImage(request.resourceRequest().url()) ? FetchRequest::DeferredByClient : FetchRequest::NoDefer);
291     return toImageResource(requestResource(Resource::Image, request));
292 }
293
294 void ResourceFetcher::preCacheDataURIImage(const FetchRequest& request)
295 {
296     const KURL& url = request.resourceRequest().url();
297     ASSERT(url.protocolIsData());
298
299     if (memoryCache()->resourceForURL(url))
300         return;
301
302     if (Resource* resource = resourceFromDataURIRequest(request.resourceRequest(), request.options())) {
303         memoryCache()->add(resource);
304         scheduleDocumentResourcesGC();
305     }
306 }
307
308 ResourcePtr<FontResource> ResourceFetcher::fetchFont(FetchRequest& request)
309 {
310     return toFontResource(requestResource(Resource::Font, request));
311 }
312
313 ResourcePtr<ShaderResource> ResourceFetcher::fetchShader(FetchRequest& request)
314 {
315     return toShaderResource(requestResource(Resource::Shader, request));
316 }
317
318 ResourcePtr<RawResource> ResourceFetcher::fetchImport(FetchRequest& request)
319 {
320     return toRawResource(requestResource(Resource::ImportResource, request));
321 }
322
323 ResourcePtr<CSSStyleSheetResource> ResourceFetcher::fetchCSSStyleSheet(FetchRequest& request)
324 {
325     return toCSSStyleSheetResource(requestResource(Resource::CSSStyleSheet, request));
326 }
327
328 ResourcePtr<CSSStyleSheetResource> ResourceFetcher::fetchUserCSSStyleSheet(FetchRequest& request)
329 {
330     KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(request.resourceRequest().url());
331
332     if (Resource* existing = memoryCache()->resourceForURL(url)) {
333         if (existing->type() == Resource::CSSStyleSheet)
334             return toCSSStyleSheetResource(existing);
335         memoryCache()->remove(existing);
336     }
337
338     request.setOptions(ResourceLoaderOptions(SniffContent, BufferData, AllowStoredCredentials, ClientRequestedCredentials, CheckContentSecurityPolicy, DocumentContext));
339     return toCSSStyleSheetResource(requestResource(Resource::CSSStyleSheet, request));
340 }
341
342 ResourcePtr<ScriptResource> ResourceFetcher::fetchScript(FetchRequest& request)
343 {
344     return toScriptResource(requestResource(Resource::Script, request));
345 }
346
347 ResourcePtr<XSLStyleSheetResource> ResourceFetcher::fetchXSLStyleSheet(FetchRequest& request)
348 {
349     ASSERT(RuntimeEnabledFeatures::xsltEnabled());
350     return toXSLStyleSheetResource(requestResource(Resource::XSLStyleSheet, request));
351 }
352
353 ResourcePtr<DocumentResource> ResourceFetcher::fetchSVGDocument(FetchRequest& request)
354 {
355     return toDocumentResource(requestResource(Resource::SVGDocument, request));
356 }
357
358 ResourcePtr<Resource> ResourceFetcher::fetchLinkResource(Resource::Type type, FetchRequest& request)
359 {
360     ASSERT(frame());
361     ASSERT(type == Resource::LinkPrefetch || type == Resource::LinkSubresource);
362     return requestResource(type, request);
363 }
364
365 ResourcePtr<RawResource> ResourceFetcher::fetchRawResource(FetchRequest& request)
366 {
367     return toRawResource(requestResource(Resource::Raw, request));
368 }
369
370 ResourcePtr<RawResource> ResourceFetcher::fetchMainResource(FetchRequest& request, const SubstituteData& substituteData)
371 {
372     if (substituteData.isValid())
373         preCacheSubstituteDataForMainResource(request, substituteData);
374     return toRawResource(requestResource(Resource::MainResource, request));
375 }
376
377 void ResourceFetcher::preCacheSubstituteDataForMainResource(const FetchRequest& request, const SubstituteData& substituteData)
378 {
379     const KURL& url = request.url();
380     if (Resource* oldResource = memoryCache()->resourceForURL(url))
381         memoryCache()->remove(oldResource);
382
383     ResourceResponse response(url, substituteData.mimeType(), substituteData.content()->size(), substituteData.textEncoding(), emptyString());
384     ResourcePtr<Resource> resource = createResource(Resource::MainResource, request.resourceRequest(), substituteData.textEncoding());
385     resource->setNeedsSynchronousCacheHit(substituteData.forceSynchronousLoad());
386     resource->setOptions(request.options());
387     resource->setDataBufferingPolicy(BufferData);
388     resource->responseReceived(response);
389     if (substituteData.content()->size())
390         resource->setResourceBuffer(substituteData.content());
391     resource->finish();
392     memoryCache()->add(resource.get());
393 }
394
395 bool ResourceFetcher::checkInsecureContent(Resource::Type type, const KURL& url, MixedContentBlockingTreatment treatment) const
396 {
397     if (treatment == TreatAsDefaultForType) {
398         switch (type) {
399         case Resource::XSLStyleSheet:
400             ASSERT(RuntimeEnabledFeatures::xsltEnabled());
401         case Resource::Script:
402         case Resource::SVGDocument:
403         case Resource::CSSStyleSheet:
404         case Resource::ImportResource:
405             // These resource can inject script into the current document (Script,
406             // XSL) or exfiltrate the content of the current document (CSS).
407             treatment = TreatAsActiveContent;
408             break;
409
410         case Resource::TextTrack:
411         case Resource::Shader:
412         case Resource::Raw:
413         case Resource::Image:
414         case Resource::Font:
415             // These resources can corrupt only the frame's pixels.
416             treatment = TreatAsPassiveContent;
417             break;
418
419         case Resource::MainResource:
420         case Resource::LinkPrefetch:
421         case Resource::LinkSubresource:
422             // These cannot affect the current document.
423             treatment = TreatAsAlwaysAllowedContent;
424             break;
425         }
426     }
427     if (treatment == TreatAsActiveContent) {
428         if (LocalFrame* f = frame()) {
429             if (!f->loader().mixedContentChecker()->canRunInsecureContent(m_document->securityOrigin(), url))
430                 return false;
431             LocalFrame* top = f->tree().top();
432             if (top != f && !top->loader().mixedContentChecker()->canRunInsecureContent(top->document()->securityOrigin(), url))
433                 return false;
434         }
435     } else if (treatment == TreatAsPassiveContent) {
436         if (LocalFrame* f = frame()) {
437             LocalFrame* top = f->tree().top();
438             if (!top->loader().mixedContentChecker()->canDisplayInsecureContent(top->document()->securityOrigin(), url))
439                 return false;
440         }
441     } else {
442         ASSERT(treatment == TreatAsAlwaysAllowedContent);
443     }
444     return true;
445 }
446
447 bool ResourceFetcher::canRequest(Resource::Type type, const KURL& url, const ResourceLoaderOptions& options, bool forPreload, FetchRequest::OriginRestriction originRestriction) const
448 {
449     SecurityOrigin* securityOrigin = options.securityOrigin.get();
450     if (!securityOrigin && document())
451         securityOrigin = document()->securityOrigin();
452
453     if (securityOrigin && !securityOrigin->canDisplay(url)) {
454         if (!forPreload)
455             context().reportLocalLoadFailed(url);
456         WTF_LOG(ResourceLoading, "ResourceFetcher::requestResource URL was not allowed by SecurityOrigin::canDisplay");
457         return 0;
458     }
459
460     // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved.
461     bool shouldBypassMainWorldContentSecurityPolicy = (frame() && frame()->script().shouldBypassMainWorldContentSecurityPolicy()) || (options.contentSecurityPolicyOption == DoNotCheckContentSecurityPolicy);
462
463     // Some types of resources can be loaded only from the same origin. Other
464     // types of resources, like Images, Scripts, and CSS, can be loaded from
465     // any URL.
466     switch (type) {
467     case Resource::MainResource:
468     case Resource::Image:
469     case Resource::CSSStyleSheet:
470     case Resource::Script:
471     case Resource::Font:
472     case Resource::Raw:
473     case Resource::LinkPrefetch:
474     case Resource::LinkSubresource:
475     case Resource::TextTrack:
476     case Resource::Shader:
477     case Resource::ImportResource:
478         // By default these types of resources can be loaded from any origin.
479         // FIXME: Are we sure about Resource::Font?
480         if (originRestriction == FetchRequest::RestrictToSameOrigin && !securityOrigin->canRequest(url)) {
481             printAccessDeniedMessage(url);
482             return false;
483         }
484         break;
485     case Resource::XSLStyleSheet:
486         ASSERT(RuntimeEnabledFeatures::xsltEnabled());
487     case Resource::SVGDocument:
488         if (!securityOrigin->canRequest(url)) {
489             printAccessDeniedMessage(url);
490             return false;
491         }
492         break;
493     }
494
495     switch (type) {
496     case Resource::XSLStyleSheet:
497         ASSERT(RuntimeEnabledFeatures::xsltEnabled());
498         if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowScriptFromSource(url))
499             return false;
500         break;
501     case Resource::Script:
502     case Resource::ImportResource:
503         if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowScriptFromSource(url))
504             return false;
505
506         if (frame()) {
507             Settings* settings = frame()->settings();
508             if (!frame()->loader().client()->allowScriptFromSource(!settings || settings->scriptEnabled(), url)) {
509                 frame()->loader().client()->didNotAllowScript();
510                 return false;
511             }
512         }
513         break;
514     case Resource::Shader:
515         // Since shaders are referenced from CSS Styles use the same rules here.
516     case Resource::CSSStyleSheet:
517         if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowStyleFromSource(url))
518             return false;
519         break;
520     case Resource::SVGDocument:
521     case Resource::Image:
522         if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowImageFromSource(url))
523             return false;
524         break;
525     case Resource::Font: {
526         if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowFontFromSource(url))
527             return false;
528         break;
529     }
530     case Resource::MainResource:
531     case Resource::Raw:
532     case Resource::LinkPrefetch:
533     case Resource::LinkSubresource:
534         break;
535     case Resource::TextTrack:
536         if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowMediaFromSource(url))
537             return false;
538         break;
539     }
540
541     // Last of all, check for insecure content. We do this last so that when
542     // folks block insecure content with a CSP policy, they don't get a warning.
543     // They'll still get a warning in the console about CSP blocking the load.
544
545     // FIXME: Should we consider forPreload here?
546     if (!checkInsecureContent(type, url, options.mixedContentBlockingTreatment))
547         return false;
548
549     return true;
550 }
551
552 bool ResourceFetcher::canAccessResource(Resource* resource, SecurityOrigin* sourceOrigin, const KURL& url) const
553 {
554     // Redirects can change the response URL different from one of request.
555     if (!canRequest(resource->type(), url, resource->options(), false, FetchRequest::UseDefaultOriginRestrictionForType))
556         return false;
557
558     if (!sourceOrigin && document())
559         sourceOrigin = document()->securityOrigin();
560
561     if (sourceOrigin->canRequest(url))
562         return true;
563
564     String errorDescription;
565     if (!resource->passesAccessControlCheck(sourceOrigin, errorDescription)) {
566         if (frame() && frame()->document()) {
567             String resourceType = Resource::resourceTypeToString(resource->type(), resource->options().initiatorInfo);
568             frame()->document()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, resourceType + " from origin '" + SecurityOrigin::create(url)->toString() + "' has been blocked from loading by Cross-Origin Resource Sharing policy: " + errorDescription);
569         }
570         return false;
571     }
572     return true;
573 }
574
575 bool ResourceFetcher::shouldLoadNewResource(Resource::Type type) const
576 {
577     if (!frame())
578         return false;
579     if (!m_documentLoader)
580         return true;
581     if (type == Resource::MainResource)
582         return m_documentLoader == frame()->loader().provisionalDocumentLoader();
583     return m_documentLoader == frame()->loader().documentLoader();
584 }
585
586 bool ResourceFetcher::resourceNeedsLoad(Resource* resource, const FetchRequest& request, RevalidationPolicy policy)
587 {
588     if (FetchRequest::DeferredByClient == request.defer())
589         return false;
590     if (policy != Use)
591         return true;
592     if (resource->stillNeedsLoad())
593         return true;
594     return request.options().synchronousPolicy == RequestSynchronously && resource->isLoading();
595 }
596
597 ResourcePtr<Resource> ResourceFetcher::requestResource(Resource::Type type, FetchRequest& request)
598 {
599     ASSERT(request.options().synchronousPolicy == RequestAsynchronously || type == Resource::Raw);
600
601     KURL url = request.resourceRequest().url();
602
603     WTF_LOG(ResourceLoading, "ResourceFetcher::requestResource '%s', charset '%s', priority=%d, forPreload=%u, type=%s", url.elidedString().latin1().data(), request.charset().latin1().data(), request.priority(), request.forPreload(), ResourceTypeName(type));
604
605     // If only the fragment identifiers differ, it is the same resource.
606     url = MemoryCache::removeFragmentIdentifierIfNeeded(url);
607
608     if (!url.isValid())
609         return 0;
610
611     if (!canRequest(type, url, request.options(), request.forPreload(), request.originRestriction()))
612         return 0;
613
614     if (LocalFrame* f = frame())
615         f->loader().client()->dispatchWillRequestResource(&request);
616
617     // See if we can use an existing resource from the cache.
618     ResourcePtr<Resource> resource = memoryCache()->resourceForURL(url);
619
620     const RevalidationPolicy policy = determineRevalidationPolicy(type, request.mutableResourceRequest(), request.forPreload(), resource.get(), request.defer(), request.options());
621     switch (policy) {
622     case Reload:
623         memoryCache()->remove(resource.get());
624         // Fall through
625     case Load:
626         resource = loadResource(type, request, request.charset());
627         break;
628     case Revalidate:
629         resource = revalidateResource(request, resource.get());
630         break;
631     case Use:
632         memoryCache()->updateForAccess(resource.get());
633         notifyLoadedFromMemoryCache(resource.get());
634         break;
635     }
636
637     if (!resource)
638         return 0;
639
640     if (!resource->hasClients())
641         m_deadStatsRecorder.update(policy);
642
643     if (policy != Use)
644         resource->setIdentifier(createUniqueIdentifier());
645
646     if (!request.forPreload() || policy != Use) {
647         ResourceLoadPriority priority = loadPriority(type, request);
648         if (priority != resource->resourceRequest().priority()) {
649             resource->resourceRequest().setPriority(priority);
650             resource->didChangePriority(priority, 0);
651         }
652     }
653
654     if (resourceNeedsLoad(resource.get(), request, policy)) {
655         if (!shouldLoadNewResource(type)) {
656             if (memoryCache()->contains(resource.get()))
657                 memoryCache()->remove(resource.get());
658             return 0;
659         }
660
661         if (!m_documentLoader || !m_documentLoader->scheduleArchiveLoad(resource.get(), request.resourceRequest()))
662             resource->load(this, request.options());
663
664         // For asynchronous loads that immediately fail, it's sufficient to return a
665         // null Resource, as it indicates that something prevented the load from starting.
666         // If there's a network error, that failure will happen asynchronously. However, if
667         // a sync load receives a network error, it will have already happened by this point.
668         // In that case, the requester should have access to the relevant ResourceError, so
669         // we need to return a non-null Resource.
670         if (resource->errorOccurred()) {
671             if (memoryCache()->contains(resource.get()))
672                 memoryCache()->remove(resource.get());
673             return request.options().synchronousPolicy == RequestSynchronously ? resource : 0;
674         }
675     }
676
677     // FIXME: Temporarily leave main resource caching disabled for chromium,
678     // see https://bugs.webkit.org/show_bug.cgi?id=107962. Before caching main
679     // resources, we should be sure to understand the implications for memory
680     // use.
681     //
682     // Ensure main resources aren't preloaded, and other main resource loads
683     // are removed from cache to prevent reuse.
684     if (type == Resource::MainResource) {
685         ASSERT(policy != Use || m_documentLoader->substituteData().isValid());
686         ASSERT(policy != Revalidate);
687         memoryCache()->remove(resource.get());
688         if (request.forPreload())
689             return 0;
690     }
691
692     if (!request.resourceRequest().url().protocolIsData() && (!m_documentLoader || !m_documentLoader->substituteData().isValid())) {
693         if (policy == Use && !m_validatedURLs.contains(request.resourceRequest().url())) {
694             // Resources loaded from memory cache should be reported the first time they're used.
695             RefPtr<ResourceTimingInfo> info = ResourceTimingInfo::create(request.options().initiatorInfo.name, monotonicallyIncreasingTime());
696             populateResourceTiming(info.get(), resource.get(), true);
697             m_scheduledResourceTimingReports.add(info, resource->type() == Resource::MainResource);
698             if (!m_resourceTimingReportTimer.isActive())
699                 m_resourceTimingReportTimer.startOneShot(0, FROM_HERE);
700         }
701
702         m_validatedURLs.add(request.resourceRequest().url());
703     }
704
705     ASSERT(resource->url() == url.string());
706     m_documentResources.set(resource->url(), resource);
707     return resource;
708 }
709
710 void ResourceFetcher::resourceTimingReportTimerFired(Timer<ResourceFetcher>* timer)
711 {
712     ASSERT_UNUSED(timer, timer == &m_resourceTimingReportTimer);
713     HashMap<RefPtr<ResourceTimingInfo>, bool> timingReports;
714     timingReports.swap(m_scheduledResourceTimingReports);
715     HashMap<RefPtr<ResourceTimingInfo>, bool>::iterator end = timingReports.end();
716     for (HashMap<RefPtr<ResourceTimingInfo>, bool>::iterator it = timingReports.begin(); it != end; ++it) {
717         RefPtr<ResourceTimingInfo> info = it->key;
718         bool isMainResource = it->value;
719         reportResourceTiming(info.get(), document(), isMainResource);
720     }
721 }
722
723 void ResourceFetcher::determineTargetType(ResourceRequest& request, Resource::Type type)
724 {
725     ResourceRequest::TargetType targetType = requestTargetType(this, request, type);
726     request.setTargetType(targetType);
727 }
728
729 ResourceRequestCachePolicy ResourceFetcher::resourceRequestCachePolicy(const ResourceRequest& request, Resource::Type type)
730 {
731     if (type == Resource::MainResource) {
732         FrameLoadType frameLoadType = frame()->loader().loadType();
733         bool isReload = frameLoadType == FrameLoadTypeReload || frameLoadType == FrameLoadTypeReloadFromOrigin;
734         if (request.httpMethod() == "POST" && frameLoadType == FrameLoadTypeBackForward)
735             return ReturnCacheDataDontLoad;
736         if (!m_documentLoader->overrideEncoding().isEmpty() || frameLoadType == FrameLoadTypeBackForward)
737             return ReturnCacheDataElseLoad;
738         if (isReload || frameLoadType == FrameLoadTypeSame || request.isConditional() || request.httpMethod() == "POST")
739             return ReloadIgnoringCacheData;
740         if (LocalFrame* parent = frame()->tree().parent())
741             return parent->document()->fetcher()->resourceRequestCachePolicy(request, type);
742         return UseProtocolCachePolicy;
743     }
744
745     if (request.isConditional())
746         return ReloadIgnoringCacheData;
747
748     if (m_documentLoader && m_document && !m_document->loadEventFinished()) {
749         // For POST requests, we mutate the main resource's cache policy to avoid form resubmission.
750         // This policy should not be inherited by subresources.
751         ResourceRequestCachePolicy mainResourceCachePolicy = m_documentLoader->request().cachePolicy();
752         if (mainResourceCachePolicy == ReturnCacheDataDontLoad)
753             return ReturnCacheDataElseLoad;
754         return mainResourceCachePolicy;
755     }
756     return UseProtocolCachePolicy;
757 }
758
759 void ResourceFetcher::addAdditionalRequestHeaders(ResourceRequest& request, Resource::Type type)
760 {
761     if (!frame())
762         return;
763
764     if (request.cachePolicy() == UseProtocolCachePolicy)
765         request.setCachePolicy(resourceRequestCachePolicy(request, type));
766     if (request.targetType() == ResourceRequest::TargetIsUnspecified)
767         determineTargetType(request, type);
768     if (type == Resource::LinkPrefetch || type == Resource::LinkSubresource)
769         request.setHTTPHeaderField("Purpose", "prefetch");
770
771     context().addAdditionalRequestHeaders(document(), request, (type == Resource::MainResource) ? FetchMainResource : FetchSubresource);
772 }
773
774 ResourcePtr<Resource> ResourceFetcher::revalidateResource(const FetchRequest& request, Resource* resource)
775 {
776     ASSERT(resource);
777     ASSERT(memoryCache()->contains(resource));
778     ASSERT(resource->isLoaded());
779     ASSERT(resource->canUseCacheValidator());
780     ASSERT(!resource->resourceToRevalidate());
781
782     ResourceRequest revalidatingRequest(resource->resourceRequest());
783     revalidatingRequest.clearHTTPReferrer();
784     addAdditionalRequestHeaders(revalidatingRequest, resource->type());
785
786     const AtomicString& lastModified = resource->response().httpHeaderField("Last-Modified");
787     const AtomicString& eTag = resource->response().httpHeaderField("ETag");
788     if (!lastModified.isEmpty() || !eTag.isEmpty()) {
789         ASSERT(context().cachePolicy(document()) != CachePolicyReload);
790         if (context().cachePolicy(document()) == CachePolicyRevalidate)
791             revalidatingRequest.setHTTPHeaderField("Cache-Control", "max-age=0");
792         if (!lastModified.isEmpty())
793             revalidatingRequest.setHTTPHeaderField("If-Modified-Since", lastModified);
794         if (!eTag.isEmpty())
795             revalidatingRequest.setHTTPHeaderField("If-None-Match", eTag);
796     }
797
798     ResourcePtr<Resource> newResource = createResource(resource->type(), revalidatingRequest, resource->encoding());
799
800     WTF_LOG(ResourceLoading, "Resource %p created to revalidate %p", newResource.get(), resource);
801     newResource->setResourceToRevalidate(resource);
802
803     memoryCache()->remove(resource);
804     memoryCache()->add(newResource.get());
805     storeResourceTimingInitiatorInformation(newResource, request);
806     TRACE_EVENT_ASYNC_BEGIN2("net", "Resource", newResource.get(), "url", newResource->url().string().ascii(), "priority", newResource->resourceRequest().priority());
807     return newResource;
808 }
809
810 ResourcePtr<Resource> ResourceFetcher::loadResource(Resource::Type type, FetchRequest& request, const String& charset)
811 {
812     ASSERT(!memoryCache()->resourceForURL(request.resourceRequest().url()));
813
814     WTF_LOG(ResourceLoading, "Loading Resource for '%s'.", request.resourceRequest().url().elidedString().latin1().data());
815
816     addAdditionalRequestHeaders(request.mutableResourceRequest(), type);
817     ResourcePtr<Resource> resource = createResource(type, request.mutableResourceRequest(), charset);
818
819     memoryCache()->add(resource.get());
820     storeResourceTimingInitiatorInformation(resource, request);
821     TRACE_EVENT_ASYNC_BEGIN2("net", "Resource", resource.get(), "url", resource->url().string().ascii(), "priority", resource->resourceRequest().priority());
822     return resource;
823 }
824
825 void ResourceFetcher::storeResourceTimingInitiatorInformation(const ResourcePtr<Resource>& resource, const FetchRequest& request)
826 {
827     if (request.options().requestInitiatorContext != DocumentContext)
828         return;
829
830     RefPtr<ResourceTimingInfo> info = ResourceTimingInfo::create(request.options().initiatorInfo.name, monotonicallyIncreasingTime());
831
832     if (resource->type() == Resource::MainResource) {
833         // <iframe>s should report the initial navigation requested by the parent document, but not subsequent navigations.
834         if (frame()->ownerElement() && !frame()->ownerElement()->loadedNonEmptyDocument()) {
835             info->setInitiatorType(frame()->ownerElement()->localName());
836             m_resourceTimingInfoMap.add(resource.get(), info);
837             frame()->ownerElement()->didLoadNonEmptyDocument();
838         }
839     } else {
840         m_resourceTimingInfoMap.add(resource.get(), info);
841     }
842 }
843
844 ResourceFetcher::RevalidationPolicy ResourceFetcher::determineRevalidationPolicy(Resource::Type type, ResourceRequest& request, bool forPreload, Resource* existingResource, FetchRequest::DeferOption defer, const ResourceLoaderOptions& options) const
845 {
846     if (!existingResource)
847         return Load;
848
849     // We already have a preload going for this URL.
850     if (forPreload && existingResource->isPreloaded())
851         return Use;
852
853     // If the same URL has been loaded as a different type, we need to reload.
854     if (existingResource->type() != type) {
855         WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to type mismatch.");
856         return Reload;
857     }
858
859     // Do not load from cache if images are not enabled. The load for this image will be blocked
860     // in ImageResource::load.
861     if (FetchRequest::DeferredByClient == defer)
862         return Reload;
863
864     // Always use data uris.
865     // FIXME: Extend this to non-images.
866     if (type == Resource::Image && request.url().protocolIsData())
867         return Use;
868
869     // If a main resource was populated from a SubstituteData load, use it.
870     if (type == Resource::MainResource && m_documentLoader->substituteData().isValid())
871         return Use;
872
873     if (!existingResource->canReuse(request))
874         return Reload;
875
876     // Never use cache entries for downloadToFile requests. The caller expects the resource in a file.
877     if (request.downloadToFile())
878         return Reload;
879
880     // Certain requests (e.g., XHRs) might have manually set headers that require revalidation.
881     // FIXME: In theory, this should be a Revalidate case. In practice, the MemoryCache revalidation path assumes a whole bunch
882     // of things about how revalidation works that manual headers violate, so punt to Reload instead.
883     if (request.isConditional())
884         return Reload;
885
886     // Don't reload resources while pasting.
887     if (m_allowStaleResources)
888         return Use;
889
890     // Always use preloads.
891     if (existingResource->isPreloaded())
892         return Use;
893
894     // CachePolicyHistoryBuffer uses the cache no matter what.
895     CachePolicy cachePolicy = context().cachePolicy(document());
896     if (cachePolicy == CachePolicyHistoryBuffer)
897         return Use;
898
899     // Don't reuse resources with Cache-control: no-store.
900     if (existingResource->response().cacheControlContainsNoStore()) {
901         WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to Cache-control: no-store.");
902         return Reload;
903     }
904
905     // If fetching a resource with a different 'CORS enabled' flag, reload.
906     if (type != Resource::MainResource && options.corsEnabled != existingResource->options().corsEnabled)
907         return Reload;
908
909     // If credentials were sent with the previous request and won't be
910     // with this one, or vice versa, re-fetch the resource.
911     //
912     // This helps with the case where the server sends back
913     // "Access-Control-Allow-Origin: *" all the time, but some of the
914     // client's requests are made without CORS and some with.
915     if (existingResource->resourceRequest().allowStoredCredentials() != request.allowStoredCredentials()) {
916         WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to difference in credentials settings.");
917         return Reload;
918     }
919
920     // During the initial load, avoid loading the same resource multiple times for a single document,
921     // even if the cache policies would tell us to. Raw resources are exempted.
922     if (type != Resource::Raw && document() && !document()->loadEventFinished() && m_validatedURLs.contains(existingResource->url()))
923         return Use;
924
925     // CachePolicyReload always reloads
926     if (cachePolicy == CachePolicyReload) {
927         WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to CachePolicyReload.");
928         return Reload;
929     }
930
931     // We'll try to reload the resource if it failed last time.
932     if (existingResource->errorOccurred()) {
933         WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicye reloading due to resource being in the error state");
934         return Reload;
935     }
936
937     // For resources that are not yet loaded we ignore the cache policy.
938     if (existingResource->isLoading())
939         return Use;
940
941     // If any of the redirects in the chain to loading the resource were not cacheable, we cannot reuse our cached resource.
942     if (!existingResource->canReuseRedirectChain()) {
943         WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to an uncacheable redirect");
944         return Reload;
945     }
946
947     // Check if the cache headers requires us to revalidate (cache expiration for example).
948     if (cachePolicy == CachePolicyRevalidate || existingResource->mustRevalidateDueToCacheHeaders()) {
949         // See if the resource has usable ETag or Last-modified headers.
950         if (existingResource->canUseCacheValidator())
951             return Revalidate;
952
953         // No, must reload.
954         WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to missing cache validators.");
955         return Reload;
956     }
957
958     return Use;
959 }
960
961 void ResourceFetcher::printAccessDeniedMessage(const KURL& url) const
962 {
963     if (url.isNull())
964         return;
965
966     if (!frame())
967         return;
968
969     String message;
970     if (!m_document || m_document->url().isNull())
971         message = "Unsafe attempt to load URL " + url.elidedString() + '.';
972     else
973         message = "Unsafe attempt to load URL " + url.elidedString() + " from frame with URL " + m_document->url().elidedString() + ". Domains, protocols and ports must match.\n";
974
975     frame()->document()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, message);
976 }
977
978 void ResourceFetcher::setAutoLoadImages(bool enable)
979 {
980     if (enable == m_autoLoadImages)
981         return;
982
983     m_autoLoadImages = enable;
984
985     if (!m_autoLoadImages)
986         return;
987
988     reloadImagesIfNotDeferred();
989 }
990
991 void ResourceFetcher::setImagesEnabled(bool enable)
992 {
993     if (enable == m_imagesEnabled)
994         return;
995
996     m_imagesEnabled = enable;
997
998     if (!m_imagesEnabled)
999         return;
1000
1001     reloadImagesIfNotDeferred();
1002 }
1003
1004 bool ResourceFetcher::clientDefersImage(const KURL& url) const
1005 {
1006     return frame() && !frame()->loader().client()->allowImage(m_imagesEnabled, url);
1007 }
1008
1009 bool ResourceFetcher::shouldDeferImageLoad(const KURL& url) const
1010 {
1011     return clientDefersImage(url) || !m_autoLoadImages;
1012 }
1013
1014 void ResourceFetcher::reloadImagesIfNotDeferred()
1015 {
1016     DocumentResourceMap::iterator end = m_documentResources.end();
1017     for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it) {
1018         Resource* resource = it->value.get();
1019         if (resource->type() == Resource::Image && resource->stillNeedsLoad() && !clientDefersImage(resource->url()))
1020             const_cast<Resource*>(resource)->load(this, defaultResourceOptions());
1021     }
1022 }
1023
1024 void ResourceFetcher::redirectReceived(Resource* resource, const ResourceResponse& redirectResponse)
1025 {
1026     ResourceTimingInfoMap::iterator it = m_resourceTimingInfoMap.find(resource);
1027     if (it != m_resourceTimingInfoMap.end())
1028         it->value->addRedirect(redirectResponse);
1029 }
1030
1031 void ResourceFetcher::didLoadResource(Resource* resource)
1032 {
1033     RefPtr<DocumentLoader> protectDocumentLoader(m_documentLoader);
1034     RefPtr<Document> protectDocument(m_document);
1035
1036     if (resource && resource->response().isHTTP() && ((!resource->errorOccurred() && !resource->wasCanceled()) || resource->response().httpStatusCode() == 304) && document()) {
1037         ResourceTimingInfoMap::iterator it = m_resourceTimingInfoMap.find(resource);
1038         if (it != m_resourceTimingInfoMap.end()) {
1039             RefPtr<ResourceTimingInfo> info = it->value;
1040             m_resourceTimingInfoMap.remove(it);
1041             populateResourceTiming(info.get(), resource, false);
1042             reportResourceTiming(info.get(), document(), resource->type() == Resource::MainResource);
1043         }
1044     }
1045
1046     if (frame())
1047         frame()->loader().loadDone();
1048     scheduleDocumentResourcesGC();
1049 }
1050
1051 void ResourceFetcher::scheduleDocumentResourcesGC()
1052 {
1053     if (!m_garbageCollectDocumentResourcesTimer.isActive())
1054         m_garbageCollectDocumentResourcesTimer.startOneShot(0, FROM_HERE);
1055 }
1056
1057 // Garbage collecting m_documentResources is a workaround for the
1058 // ResourcePtrs on the RHS being strong references. Ideally this
1059 // would be a weak map, however ResourcePtrs perform additional
1060 // bookkeeping on Resources, so instead pseudo-GC them -- when the
1061 // reference count reaches 1, m_documentResources is the only reference, so
1062 // remove it from the map.
1063 void ResourceFetcher::garbageCollectDocumentResourcesTimerFired(Timer<ResourceFetcher>* timer)
1064 {
1065     ASSERT_UNUSED(timer, timer == &m_garbageCollectDocumentResourcesTimer);
1066     garbageCollectDocumentResources();
1067 }
1068
1069 void ResourceFetcher::garbageCollectDocumentResources()
1070 {
1071     typedef Vector<String, 10> StringVector;
1072     StringVector resourcesToDelete;
1073
1074     for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != m_documentResources.end(); ++it) {
1075         if (it->value->hasOneHandle())
1076             resourcesToDelete.append(it->key);
1077     }
1078
1079     for (StringVector::const_iterator it = resourcesToDelete.begin(); it != resourcesToDelete.end(); ++it)
1080         m_documentResources.remove(*it);
1081 }
1082
1083 void ResourceFetcher::notifyLoadedFromMemoryCache(Resource* resource)
1084 {
1085     if (!frame() || !frame()->page() || resource->status() != Resource::Cached || m_validatedURLs.contains(resource->url()))
1086         return;
1087
1088     ResourceRequest request(resource->url());
1089     unsigned long identifier = createUniqueIdentifier();
1090     context().dispatchDidLoadResourceFromMemoryCache(request, resource->response());
1091     // FIXME: If willSendRequest changes the request, we don't respect it.
1092     willSendRequest(identifier, request, ResourceResponse(), resource->options().initiatorInfo);
1093     InspectorInstrumentation::markResourceAsCached(frame()->page(), identifier);
1094     context().sendRemainingDelegateMessages(m_documentLoader, identifier, resource->response(), resource->encodedSize());
1095 }
1096
1097 void ResourceFetcher::incrementRequestCount(const Resource* res)
1098 {
1099     if (res->ignoreForRequestCount())
1100         return;
1101
1102     ++m_requestCount;
1103 }
1104
1105 void ResourceFetcher::decrementRequestCount(const Resource* res)
1106 {
1107     if (res->ignoreForRequestCount())
1108         return;
1109
1110     --m_requestCount;
1111     ASSERT(m_requestCount > -1);
1112 }
1113
1114 void ResourceFetcher::preload(Resource::Type type, FetchRequest& request, const String& charset)
1115 {
1116     requestPreload(type, request, charset);
1117 }
1118
1119 void ResourceFetcher::requestPreload(Resource::Type type, FetchRequest& request, const String& charset)
1120 {
1121     String encoding;
1122     if (type == Resource::Script || type == Resource::CSSStyleSheet)
1123         encoding = charset.isEmpty() ? m_document->charset().string() : charset;
1124
1125     request.setCharset(encoding);
1126     request.setForPreload(true);
1127
1128     ResourcePtr<Resource> resource = requestResource(type, request);
1129     if (!resource || (m_preloads && m_preloads->contains(resource.get())))
1130         return;
1131     TRACE_EVENT_ASYNC_STEP_INTO0("net", "Resource", resource.get(), "Preload");
1132     resource->increasePreloadCount();
1133
1134     if (!m_preloads)
1135         m_preloads = adoptPtr(new ListHashSet<Resource*>);
1136     m_preloads->add(resource.get());
1137
1138 #if PRELOAD_DEBUG
1139     printf("PRELOADING %s\n",  resource->url().string().latin1().data());
1140 #endif
1141 }
1142
1143 bool ResourceFetcher::isPreloaded(const String& urlString) const
1144 {
1145     const KURL& url = m_document->completeURL(urlString);
1146
1147     if (m_preloads) {
1148         ListHashSet<Resource*>::iterator end = m_preloads->end();
1149         for (ListHashSet<Resource*>::iterator it = m_preloads->begin(); it != end; ++it) {
1150             Resource* resource = *it;
1151             if (resource->url() == url)
1152                 return true;
1153         }
1154     }
1155
1156     return false;
1157 }
1158
1159 void ResourceFetcher::clearPreloads()
1160 {
1161 #if PRELOAD_DEBUG
1162     printPreloadStats();
1163 #endif
1164     if (!m_preloads)
1165         return;
1166
1167     ListHashSet<Resource*>::iterator end = m_preloads->end();
1168     for (ListHashSet<Resource*>::iterator it = m_preloads->begin(); it != end; ++it) {
1169         Resource* res = *it;
1170         res->decreasePreloadCount();
1171         bool deleted = res->deleteIfPossible();
1172         if (!deleted && res->preloadResult() == Resource::PreloadNotReferenced)
1173             memoryCache()->remove(res);
1174     }
1175     m_preloads.clear();
1176 }
1177
1178 void ResourceFetcher::didFinishLoading(const Resource* resource, double finishTime, int64_t encodedDataLength)
1179 {
1180     TRACE_EVENT_ASYNC_END0("net", "Resource", resource);
1181     context().dispatchDidFinishLoading(m_documentLoader, resource->identifier(), finishTime, encodedDataLength);
1182 }
1183
1184 void ResourceFetcher::didChangeLoadingPriority(const Resource* resource, ResourceLoadPriority loadPriority, int intraPriorityValue)
1185 {
1186     TRACE_EVENT_ASYNC_STEP_INTO1("net", "Resource", resource, "ChangePriority", "priority", loadPriority);
1187     context().dispatchDidChangeResourcePriority(resource->identifier(), loadPriority, intraPriorityValue);
1188 }
1189
1190 void ResourceFetcher::didFailLoading(const Resource* resource, const ResourceError& error)
1191 {
1192     TRACE_EVENT_ASYNC_END0("net", "Resource", resource);
1193     context().dispatchDidFail(m_documentLoader, resource->identifier(), error);
1194 }
1195
1196 void ResourceFetcher::willSendRequest(unsigned long identifier, ResourceRequest& request, const ResourceResponse& redirectResponse, const FetchInitiatorInfo& initiatorInfo)
1197 {
1198     context().dispatchWillSendRequest(m_documentLoader, identifier, request, redirectResponse, initiatorInfo);
1199 }
1200
1201 void ResourceFetcher::didReceiveResponse(const Resource* resource, const ResourceResponse& response)
1202 {
1203     context().dispatchDidReceiveResponse(m_documentLoader, resource->identifier(), response, resource->loader());
1204 }
1205
1206 void ResourceFetcher::didReceiveData(const Resource* resource, const char* data, int dataLength, int encodedDataLength)
1207 {
1208     context().dispatchDidReceiveData(m_documentLoader, resource->identifier(), data, dataLength, encodedDataLength);
1209 }
1210
1211 void ResourceFetcher::didDownloadData(const Resource* resource, int dataLength, int encodedDataLength)
1212 {
1213     context().dispatchDidDownloadData(m_documentLoader, resource->identifier(), dataLength, encodedDataLength);
1214 }
1215
1216 void ResourceFetcher::subresourceLoaderFinishedLoadingOnePart(ResourceLoader* loader)
1217 {
1218     if (m_multipartLoaders)
1219         m_multipartLoaders->add(loader);
1220     if (m_loaders)
1221         m_loaders->remove(loader);
1222     if (LocalFrame* frame = this->frame())
1223         return frame->loader().checkLoadComplete(m_documentLoader);
1224 }
1225
1226 void ResourceFetcher::didInitializeResourceLoader(ResourceLoader* loader)
1227 {
1228     if (!m_document)
1229         return;
1230     if (!m_loaders)
1231         m_loaders = adoptPtr(new ResourceLoaderSet());
1232     ASSERT(!m_loaders->contains(loader));
1233     m_loaders->add(loader);
1234 }
1235
1236 void ResourceFetcher::willTerminateResourceLoader(ResourceLoader* loader)
1237 {
1238     if (!m_loaders || !m_loaders->contains(loader))
1239         return;
1240     m_loaders->remove(loader);
1241     if (LocalFrame* frame = this->frame())
1242         frame->loader().checkLoadComplete(m_documentLoader);
1243 }
1244
1245 void ResourceFetcher::willStartLoadingResource(ResourceRequest& request)
1246 {
1247     if (m_documentLoader)
1248         m_documentLoader->applicationCacheHost()->willStartLoadingResource(request);
1249 }
1250
1251 void ResourceFetcher::stopFetching()
1252 {
1253     if (m_multipartLoaders)
1254         m_multipartLoaders->cancelAll();
1255     if (m_loaders)
1256         m_loaders->cancelAll();
1257 }
1258
1259 bool ResourceFetcher::isFetching() const
1260 {
1261     return m_loaders && !m_loaders->isEmpty();
1262 }
1263
1264 void ResourceFetcher::setDefersLoading(bool defers)
1265 {
1266     if (m_loaders)
1267         m_loaders->setAllDefersLoading(defers);
1268 }
1269
1270 bool ResourceFetcher::defersLoading() const
1271 {
1272     if (LocalFrame* frame = this->frame())
1273         return frame->page()->defersLoading();
1274     return false;
1275 }
1276
1277 bool ResourceFetcher::isLoadedBy(ResourceLoaderHost* possibleOwner) const
1278 {
1279     return this == possibleOwner;
1280 }
1281
1282 bool ResourceFetcher::canAccessRedirect(Resource* resource, ResourceRequest& request, const ResourceResponse& redirectResponse, ResourceLoaderOptions& options)
1283 {
1284     if (!canRequest(resource->type(), request.url(), options, false, FetchRequest::UseDefaultOriginRestrictionForType))
1285         return false;
1286     if (options.corsEnabled == IsCORSEnabled) {
1287         SecurityOrigin* sourceOrigin = options.securityOrigin.get();
1288         if (!sourceOrigin && document())
1289             sourceOrigin = document()->securityOrigin();
1290
1291         String errorMessage;
1292         if (!CrossOriginAccessControl::handleRedirect(resource, sourceOrigin, request, redirectResponse, options, errorMessage)) {
1293             if (frame() && frame()->document())
1294                 frame()->document()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, errorMessage);
1295             return false;
1296         }
1297     }
1298     if (resource->type() == Resource::Image && shouldDeferImageLoad(request.url()))
1299         return false;
1300     return true;
1301 }
1302
1303 void ResourceFetcher::refResourceLoaderHost()
1304 {
1305     ref();
1306 }
1307
1308 void ResourceFetcher::derefResourceLoaderHost()
1309 {
1310     deref();
1311 }
1312
1313 #if PRELOAD_DEBUG
1314 void ResourceFetcher::printPreloadStats()
1315 {
1316     if (!m_preloads)
1317         return;
1318
1319     unsigned scripts = 0;
1320     unsigned scriptMisses = 0;
1321     unsigned stylesheets = 0;
1322     unsigned stylesheetMisses = 0;
1323     unsigned images = 0;
1324     unsigned imageMisses = 0;
1325     ListHashSet<Resource*>::iterator end = m_preloads->end();
1326     for (ListHashSet<Resource*>::iterator it = m_preloads->begin(); it != end; ++it) {
1327         Resource* res = *it;
1328         if (res->preloadResult() == Resource::PreloadNotReferenced)
1329             printf("!! UNREFERENCED PRELOAD %s\n", res->url().string().latin1().data());
1330         else if (res->preloadResult() == Resource::PreloadReferencedWhileComplete)
1331             printf("HIT COMPLETE PRELOAD %s\n", res->url().string().latin1().data());
1332         else if (res->preloadResult() == Resource::PreloadReferencedWhileLoading)
1333             printf("HIT LOADING PRELOAD %s\n", res->url().string().latin1().data());
1334
1335         if (res->type() == Resource::Script) {
1336             scripts++;
1337             if (res->preloadResult() < Resource::PreloadReferencedWhileLoading)
1338                 scriptMisses++;
1339         } else if (res->type() == Resource::CSSStyleSheet) {
1340             stylesheets++;
1341             if (res->preloadResult() < Resource::PreloadReferencedWhileLoading)
1342                 stylesheetMisses++;
1343         } else {
1344             images++;
1345             if (res->preloadResult() < Resource::PreloadReferencedWhileLoading)
1346                 imageMisses++;
1347         }
1348
1349         if (res->errorOccurred())
1350             memoryCache()->remove(res);
1351
1352         res->decreasePreloadCount();
1353     }
1354     m_preloads.clear();
1355
1356     if (scripts)
1357         printf("SCRIPTS: %d (%d hits, hit rate %d%%)\n", scripts, scripts - scriptMisses, (scripts - scriptMisses) * 100 / scripts);
1358     if (stylesheets)
1359         printf("STYLESHEETS: %d (%d hits, hit rate %d%%)\n", stylesheets, stylesheets - stylesheetMisses, (stylesheets - stylesheetMisses) * 100 / stylesheets);
1360     if (images)
1361         printf("IMAGES:  %d (%d hits, hit rate %d%%)\n", images, images - imageMisses, (images - imageMisses) * 100 / images);
1362 }
1363 #endif
1364
1365 const ResourceLoaderOptions& ResourceFetcher::defaultResourceOptions()
1366 {
1367     DEFINE_STATIC_LOCAL(ResourceLoaderOptions, options, (SniffContent, BufferData, AllowStoredCredentials, ClientRequestedCredentials, CheckContentSecurityPolicy, DocumentContext));
1368     return options;
1369 }
1370
1371 ResourceFetcher::DeadResourceStatsRecorder::DeadResourceStatsRecorder()
1372     : m_useCount(0)
1373     , m_revalidateCount(0)
1374     , m_loadCount(0)
1375 {
1376 }
1377
1378 ResourceFetcher::DeadResourceStatsRecorder::~DeadResourceStatsRecorder()
1379 {
1380     blink::Platform::current()->histogramCustomCounts(
1381         "WebCore.ResourceFetcher.HitCount", m_useCount, 0, 1000, 50);
1382     blink::Platform::current()->histogramCustomCounts(
1383         "WebCore.ResourceFetcher.RevalidateCount", m_revalidateCount, 0, 1000, 50);
1384     blink::Platform::current()->histogramCustomCounts(
1385         "WebCore.ResourceFetcher.LoadCount", m_loadCount, 0, 1000, 50);
1386 }
1387
1388 void ResourceFetcher::DeadResourceStatsRecorder::update(RevalidationPolicy policy)
1389 {
1390     switch (policy) {
1391     case Reload:
1392     case Load:
1393         ++m_loadCount;
1394         return;
1395     case Revalidate:
1396         ++m_revalidateCount;
1397         return;
1398     case Use:
1399         ++m_useCount;
1400         return;
1401     }
1402 }
1403
1404 }