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