/* Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) Copyright (C) 2001 Dirk Mueller (mueller@kde.org) Copyright (C) 2002 Waldo Bastian (bastian@kde.org) Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. This class provides all functionality needed for loading images, style sheets and html pages from the web. It has a memory cache for these objects. */ #include "config.h" #include "core/fetch/ResourceFetcher.h" #include "RuntimeEnabledFeatures.h" #include "bindings/v8/ScriptController.h" #include "core/dom/Document.h" #include "core/fetch/CSSStyleSheetResource.h" #include "core/fetch/DocumentResource.h" #include "core/fetch/FetchContext.h" #include "core/fetch/FontResource.h" #include "core/fetch/ImageResource.h" #include "core/fetch/MemoryCache.h" #include "core/fetch/RawResource.h" #include "core/fetch/ResourceLoader.h" #include "core/fetch/ResourceLoaderSet.h" #include "core/fetch/ScriptResource.h" #include "core/fetch/ShaderResource.h" #include "core/fetch/XSLStyleSheetResource.h" #include "core/html/HTMLElement.h" #include "core/html/HTMLFrameOwnerElement.h" #include "core/html/HTMLImport.h" #include "core/inspector/InspectorInstrumentation.h" #include "core/loader/DocumentLoader.h" #include "core/loader/FrameLoader.h" #include "core/loader/FrameLoaderClient.h" #include "core/loader/PingLoader.h" #include "core/loader/UniqueIdentifier.h" #include "core/loader/appcache/ApplicationCacheHost.h" #include "core/frame/ContentSecurityPolicy.h" #include "core/frame/DOMWindow.h" #include "core/frame/Frame.h" #include "core/timing/Performance.h" #include "core/timing/ResourceTimingInfo.h" #include "core/page/Settings.h" #include "platform/Logging.h" #include "platform/TraceEvent.h" #include "platform/weborigin/SecurityOrigin.h" #include "platform/weborigin/SecurityPolicy.h" #include "public/platform/Platform.h" #include "public/platform/WebURL.h" #include "wtf/text/CString.h" #include "wtf/text/WTFString.h" #define PRELOAD_DEBUG 0 namespace WebCore { static Resource* createResource(Resource::Type type, const ResourceRequest& request, const String& charset) { switch (type) { case Resource::Image: return new ImageResource(request); case Resource::CSSStyleSheet: return new CSSStyleSheetResource(request, charset); case Resource::Script: return new ScriptResource(request, charset); case Resource::SVGDocument: return new DocumentResource(request, Resource::SVGDocument); case Resource::Font: return new FontResource(request); case Resource::MainResource: case Resource::Raw: case Resource::TextTrack: return new RawResource(request, type); case Resource::XSLStyleSheet: return new XSLStyleSheetResource(request); case Resource::LinkPrefetch: return new Resource(request, Resource::LinkPrefetch); case Resource::LinkSubresource: return new Resource(request, Resource::LinkSubresource); case Resource::Shader: return new ShaderResource(request); case Resource::ImportResource: return new RawResource(request, type); } ASSERT_NOT_REACHED(); return 0; } static ResourceLoadPriority loadPriority(Resource::Type type, const FetchRequest& request) { if (request.priority() != ResourceLoadPriorityUnresolved) return request.priority(); switch (type) { case Resource::MainResource: return ResourceLoadPriorityVeryHigh; case Resource::CSSStyleSheet: return ResourceLoadPriorityHigh; case Resource::Raw: return request.options().synchronousPolicy == RequestSynchronously ? ResourceLoadPriorityVeryHigh : ResourceLoadPriorityMedium; case Resource::Script: case Resource::Font: case Resource::ImportResource: return ResourceLoadPriorityMedium; case Resource::Image: // We'll default images to VeryLow, and promote whatever is visible. This improves // speed-index by ~5% on average, ~14% at the 99th percentile. return ResourceLoadPriorityVeryLow; case Resource::XSLStyleSheet: ASSERT(RuntimeEnabledFeatures::xsltEnabled()); return ResourceLoadPriorityHigh; case Resource::SVGDocument: return ResourceLoadPriorityLow; case Resource::LinkPrefetch: return ResourceLoadPriorityVeryLow; case Resource::LinkSubresource: return ResourceLoadPriorityLow; case Resource::TextTrack: return ResourceLoadPriorityLow; case Resource::Shader: return ResourceLoadPriorityMedium; } ASSERT_NOT_REACHED(); return ResourceLoadPriorityUnresolved; } static Resource* resourceFromDataURIRequest(const ResourceRequest& request, const ResourceLoaderOptions& resourceOptions) { const KURL& url = request.url(); ASSERT(url.protocolIsData()); blink::WebString mimetype; blink::WebString charset; RefPtr data = PassRefPtr(blink::Platform::current()->parseDataURL(url, mimetype, charset)); if (!data) return 0; ResourceResponse response(url, mimetype, data->size(), charset, String()); Resource* resource = createResource(Resource::Image, request, charset); resource->setOptions(resourceOptions); resource->responseReceived(response); // FIXME: AppendData causes an unnecessary memcpy. if (data->size()) resource->appendData(data->data(), data->size()); resource->finish(); return resource; } static void reportResourceTiming(ResourceTimingInfo* info, Resource* resource, double finishTime, Document* initiatorDocument, bool clearLoadTimings) { if (resource->type() == Resource::MainResource) initiatorDocument = initiatorDocument->parentDocument(); ASSERT(initiatorDocument); info->setInitialRequest(resource->resourceRequest()); info->setFinalResponse(resource->response()); if (clearLoadTimings) info->clearLoadTimings(); info->setLoadFinishTime(finishTime); if (DOMWindow* initiatorWindow = initiatorDocument->domWindow()) { if (Performance* performance = initiatorWindow->performance()) performance->addResourceTiming(*info, initiatorDocument); } } static ResourceRequest::TargetType requestTargetType(const ResourceFetcher* fetcher, const ResourceRequest& request, Resource::Type type) { switch (type) { case Resource::MainResource: if (fetcher->frame()->tree().parent()) return ResourceRequest::TargetIsSubframe; return ResourceRequest::TargetIsMainFrame; case Resource::XSLStyleSheet: ASSERT(RuntimeEnabledFeatures::xsltEnabled()); case Resource::CSSStyleSheet: return ResourceRequest::TargetIsStyleSheet; case Resource::Script: return ResourceRequest::TargetIsScript; case Resource::Font: return ResourceRequest::TargetIsFont; case Resource::Image: return ResourceRequest::TargetIsImage; case Resource::Shader: case Resource::Raw: case Resource::ImportResource: return ResourceRequest::TargetIsSubresource; case Resource::LinkPrefetch: return ResourceRequest::TargetIsPrefetch; case Resource::LinkSubresource: return ResourceRequest::TargetIsSubresource; case Resource::TextTrack: return ResourceRequest::TargetIsTextTrack; case Resource::SVGDocument: return ResourceRequest::TargetIsImage; } ASSERT_NOT_REACHED(); return ResourceRequest::TargetIsSubresource; } ResourceFetcher::ResourceFetcher(DocumentLoader* documentLoader) : m_document(0) , m_documentLoader(documentLoader) , m_requestCount(0) , m_garbageCollectDocumentResourcesTimer(this, &ResourceFetcher::garbageCollectDocumentResourcesTimerFired) , m_autoLoadImages(true) , m_imagesEnabled(true) , m_allowStaleResources(false) { } ResourceFetcher::~ResourceFetcher() { m_documentLoader = 0; m_document = 0; clearPreloads(); // Make sure no requests still point to this ResourceFetcher ASSERT(!m_requestCount); } Resource* ResourceFetcher::cachedResource(const String& resourceURL) const { KURL url = m_document->completeURL(resourceURL); return cachedResource(url); } Resource* ResourceFetcher::cachedResource(const KURL& resourceURL) const { KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(resourceURL); return m_documentResources.get(url).get(); } Frame* ResourceFetcher::frame() const { if (m_documentLoader) return m_documentLoader->frame(); if (m_document && m_document->import()) return m_document->import()->frame(); return 0; } FetchContext& ResourceFetcher::context() const { if (Frame* frame = this->frame()) return frame->fetchContext(); return FetchContext::nullInstance(); } ResourcePtr ResourceFetcher::fetchSynchronously(FetchRequest& request) { ASSERT(document()); request.mutableResourceRequest().setTimeoutInterval(10); ResourceLoaderOptions options(request.options()); options.synchronousPolicy = RequestSynchronously; request.setOptions(options); return requestResource(Resource::Raw, request); } ResourcePtr ResourceFetcher::fetchImage(FetchRequest& request) { if (Frame* f = frame()) { if (f->document()->pageDismissalEventBeingDispatched() != Document::NoDismissal) { KURL requestURL = request.resourceRequest().url(); if (requestURL.isValid() && canRequest(Resource::Image, requestURL, request.options(), request.forPreload(), request.originRestriction())) PingLoader::loadImage(f, requestURL); return 0; } } if (request.resourceRequest().url().protocolIsData()) preCacheDataURIImage(request); request.setDefer(clientDefersImage(request.resourceRequest().url()) ? FetchRequest::DeferredByClient : FetchRequest::NoDefer); return toImageResource(requestResource(Resource::Image, request)); } void ResourceFetcher::preCacheDataURIImage(const FetchRequest& request) { const KURL& url = request.resourceRequest().url(); ASSERT(url.protocolIsData()); if (memoryCache()->resourceForURL(url)) return; if (Resource* resource = resourceFromDataURIRequest(request.resourceRequest(), request.options())) memoryCache()->add(resource); } ResourcePtr ResourceFetcher::fetchFont(FetchRequest& request) { return toFontResource(requestResource(Resource::Font, request)); } ResourcePtr ResourceFetcher::fetchShader(FetchRequest& request) { return toShaderResource(requestResource(Resource::Shader, request)); } ResourcePtr ResourceFetcher::fetchImport(FetchRequest& request) { return toRawResource(requestResource(Resource::ImportResource, request)); } ResourcePtr ResourceFetcher::fetchCSSStyleSheet(FetchRequest& request) { return toCSSStyleSheetResource(requestResource(Resource::CSSStyleSheet, request)); } ResourcePtr ResourceFetcher::fetchUserCSSStyleSheet(FetchRequest& request) { KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(request.resourceRequest().url()); if (Resource* existing = memoryCache()->resourceForURL(url)) { if (existing->type() == Resource::CSSStyleSheet) return toCSSStyleSheetResource(existing); memoryCache()->remove(existing); } request.setOptions(ResourceLoaderOptions(DoNotSendCallbacks, SniffContent, BufferData, AllowStoredCredentials, ClientRequestedCredentials, AskClientForCrossOriginCredentials, SkipSecurityCheck, CheckContentSecurityPolicy, DocumentContext)); return toCSSStyleSheetResource(requestResource(Resource::CSSStyleSheet, request)); } ResourcePtr ResourceFetcher::fetchScript(FetchRequest& request) { return toScriptResource(requestResource(Resource::Script, request)); } ResourcePtr ResourceFetcher::fetchXSLStyleSheet(FetchRequest& request) { ASSERT(RuntimeEnabledFeatures::xsltEnabled()); return toXSLStyleSheetResource(requestResource(Resource::XSLStyleSheet, request)); } ResourcePtr ResourceFetcher::fetchSVGDocument(FetchRequest& request) { return toDocumentResource(requestResource(Resource::SVGDocument, request)); } ResourcePtr ResourceFetcher::fetchLinkResource(Resource::Type type, FetchRequest& request) { ASSERT(frame()); ASSERT(type == Resource::LinkPrefetch || type == Resource::LinkSubresource); return requestResource(type, request); } ResourcePtr ResourceFetcher::fetchRawResource(FetchRequest& request) { return toRawResource(requestResource(Resource::Raw, request)); } ResourcePtr ResourceFetcher::fetchMainResource(FetchRequest& request) { return toRawResource(requestResource(Resource::MainResource, request)); } bool ResourceFetcher::checkInsecureContent(Resource::Type type, const KURL& url, MixedContentBlockingTreatment treatment) const { if (treatment == TreatAsDefaultForType) { switch (type) { case Resource::XSLStyleSheet: ASSERT(RuntimeEnabledFeatures::xsltEnabled()); case Resource::Script: case Resource::SVGDocument: case Resource::CSSStyleSheet: case Resource::ImportResource: // These resource can inject script into the current document (Script, // XSL) or exfiltrate the content of the current document (CSS). treatment = TreatAsActiveContent; break; case Resource::TextTrack: case Resource::Shader: case Resource::Raw: case Resource::Image: case Resource::Font: // These resources can corrupt only the frame's pixels. treatment = TreatAsPassiveContent; break; case Resource::MainResource: case Resource::LinkPrefetch: case Resource::LinkSubresource: // These cannot affect the current document. treatment = TreatAsAlwaysAllowedContent; break; } } if (treatment == TreatAsActiveContent) { if (Frame* f = frame()) { if (!f->loader().mixedContentChecker()->canRunInsecureContent(m_document->securityOrigin(), url)) return false; Frame* top = f->tree().top(); if (top != f && !top->loader().mixedContentChecker()->canRunInsecureContent(top->document()->securityOrigin(), url)) return false; } } else if (treatment == TreatAsPassiveContent) { if (Frame* f = frame()) { Frame* top = f->tree().top(); if (!top->loader().mixedContentChecker()->canDisplayInsecureContent(top->document()->securityOrigin(), url)) return false; } } else { ASSERT(treatment == TreatAsAlwaysAllowedContent); } return true; } bool ResourceFetcher::canRequest(Resource::Type type, const KURL& url, const ResourceLoaderOptions& options, bool forPreload, FetchRequest::OriginRestriction originRestriction) { SecurityOrigin* securityOrigin = options.securityOrigin.get(); if (!securityOrigin && document()) securityOrigin = document()->securityOrigin(); if (securityOrigin && !securityOrigin->canDisplay(url)) { if (!forPreload) context().reportLocalLoadFailed(url); WTF_LOG(ResourceLoading, "ResourceFetcher::requestResource URL was not allowed by SecurityOrigin::canDisplay"); return 0; } // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved. bool shouldBypassMainWorldContentSecurityPolicy = (frame() && frame()->script().shouldBypassMainWorldContentSecurityPolicy()) || (options.contentSecurityPolicyOption == DoNotCheckContentSecurityPolicy); // Some types of resources can be loaded only from the same origin. Other // types of resources, like Images, Scripts, and CSS, can be loaded from // any URL. switch (type) { case Resource::MainResource: case Resource::Image: case Resource::CSSStyleSheet: case Resource::Script: case Resource::Font: case Resource::Raw: case Resource::LinkPrefetch: case Resource::LinkSubresource: case Resource::TextTrack: case Resource::Shader: case Resource::ImportResource: // By default these types of resources can be loaded from any origin. // FIXME: Are we sure about Resource::Font? if (originRestriction == FetchRequest::RestrictToSameOrigin && !securityOrigin->canRequest(url)) { printAccessDeniedMessage(url); return false; } break; case Resource::XSLStyleSheet: ASSERT(RuntimeEnabledFeatures::xsltEnabled()); case Resource::SVGDocument: if (!securityOrigin->canRequest(url)) { printAccessDeniedMessage(url); return false; } break; } switch (type) { case Resource::XSLStyleSheet: ASSERT(RuntimeEnabledFeatures::xsltEnabled()); if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowScriptFromSource(url)) return false; break; case Resource::Script: case Resource::ImportResource: if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowScriptFromSource(url)) return false; if (frame()) { Settings* settings = frame()->settings(); if (!frame()->loader().client()->allowScriptFromSource(!settings || settings->isScriptEnabled(), url)) { frame()->loader().client()->didNotAllowScript(); return false; } } break; case Resource::Shader: // Since shaders are referenced from CSS Styles use the same rules here. case Resource::CSSStyleSheet: if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowStyleFromSource(url)) return false; break; case Resource::SVGDocument: case Resource::Image: if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowImageFromSource(url)) return false; break; case Resource::Font: { if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowFontFromSource(url)) return false; break; } case Resource::MainResource: case Resource::Raw: case Resource::LinkPrefetch: case Resource::LinkSubresource: break; case Resource::TextTrack: if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowMediaFromSource(url)) return false; break; } // Last of all, check for insecure content. We do this last so that when // folks block insecure content with a CSP policy, they don't get a warning. // They'll still get a warning in the console about CSP blocking the load. // FIXME: Should we consider forPreload here? if (!checkInsecureContent(type, url, options.mixedContentBlockingTreatment)) return false; return true; } bool ResourceFetcher::canAccess(Resource* resource, CORSEnabled corsEnabled, FetchRequest::OriginRestriction originRestriction) { // Redirects can change the response URL different from one of request. if (!canRequest(resource->type(), resource->response().url(), resource->options(), false, originRestriction)) return false; String error; switch (resource->type()) { case Resource::Script: case Resource::ImportResource: if (corsEnabled == PotentiallyCORSEnabled && !m_document->securityOrigin()->canRequest(resource->response().url()) && !resource->passesAccessControlCheck(m_document->securityOrigin(), error)) { if (frame() && frame()->document()) frame()->document()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Script from origin '" + SecurityOrigin::create(resource->response().url())->toString() + "' has been blocked from loading by Cross-Origin Resource Sharing policy: " + error); return false; } break; default: ASSERT_NOT_REACHED(); // FIXME: generalize to non-script resources return false; } return true; } bool ResourceFetcher::shouldLoadNewResource() const { if (!frame()) return false; if (!m_documentLoader) return true; if (m_documentLoader == frame()->loader().activeDocumentLoader()) return true; return document() && document()->pageDismissalEventBeingDispatched() != Document::NoDismissal; } bool ResourceFetcher::resourceNeedsLoad(Resource* resource, const FetchRequest& request, RevalidationPolicy policy) { if (FetchRequest::DeferredByClient == request.defer()) return false; if (policy != Use) return true; if (resource->stillNeedsLoad()) return true; return request.options().synchronousPolicy == RequestSynchronously && resource->isLoading(); } ResourcePtr ResourceFetcher::requestResource(Resource::Type type, FetchRequest& request) { ASSERT(request.options().synchronousPolicy == RequestAsynchronously || type == Resource::Raw); KURL url = request.resourceRequest().url(); 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)); // If only the fragment identifiers differ, it is the same resource. url = MemoryCache::removeFragmentIdentifierIfNeeded(url); if (!url.isValid()) return 0; if (!canRequest(type, url, request.options(), request.forPreload(), request.originRestriction())) return 0; if (Frame* f = frame()) f->loader().client()->dispatchWillRequestResource(&request); // See if we can use an existing resource from the cache. ResourcePtr resource = memoryCache()->resourceForURL(url); const RevalidationPolicy policy = determineRevalidationPolicy(type, request.mutableResourceRequest(), request.forPreload(), resource.get(), request.defer()); switch (policy) { case Reload: memoryCache()->remove(resource.get()); // Fall through case Load: resource = loadResource(type, request, request.charset()); break; case Revalidate: resource = revalidateResource(request, resource.get()); break; case Use: resource->updateForAccess(); notifyLoadedFromMemoryCache(resource.get()); break; } if (!resource) return 0; if (policy != Use) resource->setIdentifier(createUniqueIdentifier()); if (!request.forPreload() || policy != Use) { ResourceLoadPriority priority = loadPriority(type, request); if (priority != resource->resourceRequest().priority()) { resource->resourceRequest().setPriority(priority); resource->didChangePriority(priority); } } if (resourceNeedsLoad(resource.get(), request, policy)) { if (!shouldLoadNewResource()) { if (resource->inCache()) memoryCache()->remove(resource.get()); return 0; } if (!m_documentLoader || !m_documentLoader->scheduleArchiveLoad(resource.get(), request.resourceRequest())) resource->load(this, request.options()); // For asynchronous loads that immediately fail, it's sufficient to return a // null Resource, as it indicates that something prevented the load from starting. // If there's a network error, that failure will happen asynchronously. However, if // a sync load receives a network error, it will have already happened by this point. // In that case, the requester should have access to the relevant ResourceError, so // we need to return a non-null Resource. if (resource->errorOccurred()) { if (resource->inCache()) memoryCache()->remove(resource.get()); return request.options().synchronousPolicy == RequestSynchronously ? resource : 0; } } // FIXME: Temporarily leave main resource caching disabled for chromium, // see https://bugs.webkit.org/show_bug.cgi?id=107962. Before caching main // resources, we should be sure to understand the implications for memory // use. // // Ensure main resources aren't preloaded, and other main resource loads // are removed from cache to prevent reuse. if (type == Resource::MainResource) { ASSERT(policy != Use); ASSERT(policy != Revalidate); memoryCache()->remove(resource.get()); if (request.forPreload()) return 0; } if (!request.resourceRequest().url().protocolIsData()) { if (policy == Use && !m_validatedURLs.contains(request.resourceRequest().url())) { // Resources loaded from memory cache should be reported the first time they're used. RefPtr info = ResourceTimingInfo::create(request.options().initiatorInfo.name, monotonicallyIncreasingTime()); reportResourceTiming(info.get(), resource.get(), monotonicallyIncreasingTime(), document(), true); } m_validatedURLs.add(request.resourceRequest().url()); } ASSERT(resource->url() == url.string()); m_documentResources.set(resource->url(), resource); return resource; } void ResourceFetcher::determineTargetType(ResourceRequest& request, Resource::Type type) { ResourceRequest::TargetType targetType = requestTargetType(this, request, type); request.setTargetType(targetType); } ResourceRequestCachePolicy ResourceFetcher::resourceRequestCachePolicy(const ResourceRequest& request, Resource::Type type) { if (type == Resource::MainResource) { FrameLoadType frameLoadType = frame()->loader().loadType(); bool isReload = frameLoadType == FrameLoadTypeReload || frameLoadType == FrameLoadTypeReloadFromOrigin; if (request.httpMethod() == "POST" && frameLoadType == FrameLoadTypeBackForward) return ReturnCacheDataDontLoad; if (!m_documentLoader->overrideEncoding().isEmpty() || frameLoadType == FrameLoadTypeBackForward) return ReturnCacheDataElseLoad; if (isReload || frameLoadType == FrameLoadTypeSame || request.isConditional() || request.httpMethod() == "POST") return ReloadIgnoringCacheData; if (Frame* parent = frame()->tree().parent()) return parent->document()->fetcher()->resourceRequestCachePolicy(request, type); return UseProtocolCachePolicy; } if (request.isConditional()) return ReloadIgnoringCacheData; if (m_documentLoader && m_documentLoader->isLoadingInAPISense()) { // For POST requests, we mutate the main resource's cache policy to avoid form resubmission. // This policy should not be inherited by subresources. ResourceRequestCachePolicy mainResourceCachePolicy = m_documentLoader->request().cachePolicy(); if (mainResourceCachePolicy == ReturnCacheDataDontLoad) return ReturnCacheDataElseLoad; return mainResourceCachePolicy; } return UseProtocolCachePolicy; } void ResourceFetcher::addAdditionalRequestHeaders(ResourceRequest& request, Resource::Type type) { if (!frame()) return; if (request.cachePolicy() == UseProtocolCachePolicy) request.setCachePolicy(resourceRequestCachePolicy(request, type)); if (request.targetType() == ResourceRequest::TargetIsUnspecified) determineTargetType(request, type); if (type == Resource::LinkPrefetch || type == Resource::LinkSubresource) request.setHTTPHeaderField("Purpose", "prefetch"); context().addAdditionalRequestHeaders(*document(), request, type); } ResourcePtr ResourceFetcher::revalidateResource(const FetchRequest& request, Resource* resource) { ASSERT(resource); ASSERT(resource->inCache()); ASSERT(resource->isLoaded()); ASSERT(resource->canUseCacheValidator()); ASSERT(!resource->resourceToRevalidate()); ResourceRequest revalidatingRequest(resource->resourceRequest()); addAdditionalRequestHeaders(revalidatingRequest, resource->type()); const AtomicString& lastModified = resource->response().httpHeaderField("Last-Modified"); const AtomicString& eTag = resource->response().httpHeaderField("ETag"); if (!lastModified.isEmpty() || !eTag.isEmpty()) { ASSERT(context().cachePolicy(resource->type()) != CachePolicyReload); if (context().cachePolicy(resource->type()) == CachePolicyRevalidate) revalidatingRequest.setHTTPHeaderField("Cache-Control", "max-age=0"); if (!lastModified.isEmpty()) revalidatingRequest.setHTTPHeaderField("If-Modified-Since", lastModified); if (!eTag.isEmpty()) revalidatingRequest.setHTTPHeaderField("If-None-Match", eTag); } ResourcePtr newResource = createResource(resource->type(), revalidatingRequest, resource->encoding()); WTF_LOG(ResourceLoading, "Resource %p created to revalidate %p", newResource.get(), resource); newResource->setResourceToRevalidate(resource); memoryCache()->remove(resource); memoryCache()->add(newResource.get()); storeResourceTimingInitiatorInformation(newResource, request); TRACE_EVENT_ASYNC_BEGIN2("net", "Resource", newResource.get(), "url", newResource->url().string().ascii(), "priority", newResource->resourceRequest().priority()); return newResource; } ResourcePtr ResourceFetcher::loadResource(Resource::Type type, FetchRequest& request, const String& charset) { ASSERT(!memoryCache()->resourceForURL(request.resourceRequest().url())); WTF_LOG(ResourceLoading, "Loading Resource for '%s'.", request.resourceRequest().url().elidedString().latin1().data()); addAdditionalRequestHeaders(request.mutableResourceRequest(), type); ResourcePtr resource = createResource(type, request.mutableResourceRequest(), charset); memoryCache()->add(resource.get()); storeResourceTimingInitiatorInformation(resource, request); TRACE_EVENT_ASYNC_BEGIN2("net", "Resource", resource.get(), "url", resource->url().string().ascii(), "priority", resource->resourceRequest().priority()); return resource; } void ResourceFetcher::storeResourceTimingInitiatorInformation(const ResourcePtr& resource, const FetchRequest& request) { if (request.options().requestInitiatorContext != DocumentContext) return; RefPtr info = ResourceTimingInfo::create(request.options().initiatorInfo.name, monotonicallyIncreasingTime()); if (resource->type() == Resource::MainResource) { //