#include "bindings/v8/ScriptController.h"
#include "core/dom/Document.h"
#include "core/fetch/CSSStyleSheetResource.h"
+#include "core/fetch/CrossOriginAccessControl.h"
#include "core/fetch/DocumentResource.h"
#include "core/fetch/FetchContext.h"
-#include "core/fetch/FetchRequest.h"
#include "core/fetch/FontResource.h"
#include "core/fetch/ImageResource.h"
#include "core/fetch/MemoryCache.h"
#include "core/fetch/XSLStyleSheetResource.h"
#include "core/html/HTMLElement.h"
#include "core/html/HTMLFrameOwnerElement.h"
-#include "core/html/HTMLImport.h"
+#include "core/html/imports/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/SubstituteData.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/frame/LocalFrame.h"
+#include "core/frame/csp/ContentSecurityPolicy.h"
#include "core/timing/Performance.h"
#include "core/timing/ResourceTimingInfo.h"
-#include "core/page/Settings.h"
+#include "core/frame/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 "weborigin/SecurityOrigin.h"
-#include "weborigin/SecurityPolicy.h"
#include "wtf/text/CString.h"
#include "wtf/text/WTFString.h"
case Resource::ImportResource:
return ResourceLoadPriorityMedium;
case Resource::Image:
- return request.forPreload() ? ResourceLoadPriorityVeryLow : ResourceLoadPriorityLow;
+ // 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;
const KURL& url = request.url();
ASSERT(url.protocolIsData());
- WebKit::WebString mimetype;
- WebKit::WebString charset;
- RefPtr<SharedBuffer> data = PassRefPtr<SharedBuffer>(WebKit::Platform::current()->parseDataURL(url, mimetype, charset));
+ blink::WebString mimetype;
+ blink::WebString charset;
+ RefPtr<SharedBuffer> data = PassRefPtr<SharedBuffer>(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->setResourceBuffer(data);
resource->finish();
return resource;
}
-static void reportResourceTiming(ResourceTimingInfo* info, Resource* resource, double finishTime, Document* initiatorDocument, bool clearLoadTimings)
+static void populateResourceTiming(ResourceTimingInfo* info, Resource* resource, bool clearLoadTimings)
{
- if (resource->type() == Resource::MainResource)
- initiatorDocument = initiatorDocument->parentDocument();
- ASSERT(initiatorDocument);
info->setInitialRequest(resource->resourceRequest());
info->setFinalResponse(resource->response());
- if (clearLoadTimings)
+ if (clearLoadTimings) {
info->clearLoadTimings();
- info->setLoadFinishTime(finishTime);
- if (DOMWindow* initiatorWindow = initiatorDocument->domWindow()) {
- if (Performance* performance = initiatorWindow->performance())
- performance->addResourceTiming(*info, initiatorDocument);
+ info->setLoadFinishTime(info->initialTime());
+ } else {
+ info->setLoadFinishTime(resource->loadFinishTime());
}
}
+static void reportResourceTiming(ResourceTimingInfo* info, Document* initiatorDocument, bool isMainResource)
+{
+ if (initiatorDocument && isMainResource)
+ initiatorDocument = initiatorDocument->parentDocument();
+ if (!initiatorDocument || !initiatorDocument->loader())
+ return;
+ if (DOMWindow* initiatorWindow = initiatorDocument->domWindow())
+ initiatorWindow->performance().addResourceTiming(*info, initiatorDocument);
+}
+
static ResourceRequest::TargetType requestTargetType(const ResourceFetcher* fetcher, const ResourceRequest& request, Resource::Type type)
{
switch (type) {
, m_documentLoader(documentLoader)
, m_requestCount(0)
, m_garbageCollectDocumentResourcesTimer(this, &ResourceFetcher::garbageCollectDocumentResourcesTimerFired)
+ , m_resourceTimingReportTimer(this, &ResourceFetcher::resourceTimingReportTimerFired)
, m_autoLoadImages(true)
, m_imagesEnabled(true)
, m_allowStaleResources(false)
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
+LocalFrame* ResourceFetcher::frame() const
{
if (m_documentLoader)
return m_documentLoader->frame();
FetchContext& ResourceFetcher::context() const
{
- if (Frame* frame = this->frame())
+ if (LocalFrame* frame = this->frame())
return frame->fetchContext();
return FetchContext::nullInstance();
}
ResourcePtr<ImageResource> ResourceFetcher::fetchImage(FetchRequest& request)
{
- if (Frame* f = frame()) {
+ if (LocalFrame* f = frame()) {
if (f->document()->pageDismissalEventBeingDispatched() != Document::NoDismissal) {
KURL requestURL = request.resourceRequest().url();
- if (requestURL.isValid() && canRequest(Resource::Image, requestURL, request.options(), request.forPreload()))
+ if (requestURL.isValid() && canRequest(Resource::Image, requestURL, request.options(), request.forPreload(), request.originRestriction()))
PingLoader::loadImage(f, requestURL);
return 0;
}
preCacheDataURIImage(request);
request.setDefer(clientDefersImage(request.resourceRequest().url()) ? FetchRequest::DeferredByClient : FetchRequest::NoDefer);
- return static_cast<ImageResource*>(requestResource(Resource::Image, request).get());
+ return toImageResource(requestResource(Resource::Image, request));
}
void ResourceFetcher::preCacheDataURIImage(const FetchRequest& request)
if (memoryCache()->resourceForURL(url))
return;
- if (Resource* resource = resourceFromDataURIRequest(request.resourceRequest(), request.options()))
+ if (Resource* resource = resourceFromDataURIRequest(request.resourceRequest(), request.options())) {
memoryCache()->add(resource);
+ scheduleDocumentResourcesGC();
+ }
}
ResourcePtr<FontResource> ResourceFetcher::fetchFont(FetchRequest& request)
{
- return static_cast<FontResource*>(requestResource(Resource::Font, request).get());
+ return toFontResource(requestResource(Resource::Font, request));
}
ResourcePtr<ShaderResource> ResourceFetcher::fetchShader(FetchRequest& request)
{
- return static_cast<ShaderResource*>(requestResource(Resource::Shader, request).get());
+ return toShaderResource(requestResource(Resource::Shader, request));
}
ResourcePtr<RawResource> ResourceFetcher::fetchImport(FetchRequest& request)
{
- return static_cast<RawResource*>(requestResource(Resource::ImportResource, request).get());
+ return toRawResource(requestResource(Resource::ImportResource, request));
}
ResourcePtr<CSSStyleSheetResource> ResourceFetcher::fetchCSSStyleSheet(FetchRequest& request)
{
- return static_cast<CSSStyleSheetResource*>(requestResource(Resource::CSSStyleSheet, request).get());
+ return toCSSStyleSheetResource(requestResource(Resource::CSSStyleSheet, request));
}
ResourcePtr<CSSStyleSheetResource> ResourceFetcher::fetchUserCSSStyleSheet(FetchRequest& request)
if (Resource* existing = memoryCache()->resourceForURL(url)) {
if (existing->type() == Resource::CSSStyleSheet)
- return static_cast<CSSStyleSheetResource*>(existing);
+ return toCSSStyleSheetResource(existing);
memoryCache()->remove(existing);
}
- request.setOptions(ResourceLoaderOptions(DoNotSendCallbacks, SniffContent, BufferData, AllowStoredCredentials, ClientRequestedCredentials, AskClientForCrossOriginCredentials, SkipSecurityCheck, CheckContentSecurityPolicy, UseDefaultOriginRestrictionsForType, DocumentContext));
- return static_cast<CSSStyleSheetResource*>(requestResource(Resource::CSSStyleSheet, request).get());
+ request.setOptions(ResourceLoaderOptions(SniffContent, BufferData, AllowStoredCredentials, ClientRequestedCredentials, CheckContentSecurityPolicy, DocumentContext));
+ return toCSSStyleSheetResource(requestResource(Resource::CSSStyleSheet, request));
}
ResourcePtr<ScriptResource> ResourceFetcher::fetchScript(FetchRequest& request)
{
- return static_cast<ScriptResource*>(requestResource(Resource::Script, request).get());
+ return toScriptResource(requestResource(Resource::Script, request));
}
ResourcePtr<XSLStyleSheetResource> ResourceFetcher::fetchXSLStyleSheet(FetchRequest& request)
{
ASSERT(RuntimeEnabledFeatures::xsltEnabled());
- return static_cast<XSLStyleSheetResource*>(requestResource(Resource::XSLStyleSheet, request).get());
+ return toXSLStyleSheetResource(requestResource(Resource::XSLStyleSheet, request));
}
ResourcePtr<DocumentResource> ResourceFetcher::fetchSVGDocument(FetchRequest& request)
{
- return static_cast<DocumentResource*>(requestResource(Resource::SVGDocument, request).get());
+ return toDocumentResource(requestResource(Resource::SVGDocument, request));
}
ResourcePtr<Resource> ResourceFetcher::fetchLinkResource(Resource::Type type, FetchRequest& request)
ResourcePtr<RawResource> ResourceFetcher::fetchRawResource(FetchRequest& request)
{
- return static_cast<RawResource*>(requestResource(Resource::Raw, request).get());
+ return toRawResource(requestResource(Resource::Raw, request));
}
-ResourcePtr<RawResource> ResourceFetcher::fetchMainResource(FetchRequest& request)
+ResourcePtr<RawResource> ResourceFetcher::fetchMainResource(FetchRequest& request, const SubstituteData& substituteData)
{
- return static_cast<RawResource*>(requestResource(Resource::MainResource, request).get());
+ if (substituteData.isValid())
+ preCacheSubstituteDataForMainResource(request, substituteData);
+ return toRawResource(requestResource(Resource::MainResource, request));
+}
+
+void ResourceFetcher::preCacheSubstituteDataForMainResource(const FetchRequest& request, const SubstituteData& substituteData)
+{
+ const KURL& url = request.url();
+ if (Resource* oldResource = memoryCache()->resourceForURL(url))
+ memoryCache()->remove(oldResource);
+
+ ResourceResponse response(url, substituteData.mimeType(), substituteData.content()->size(), substituteData.textEncoding(), emptyString());
+ ResourcePtr<Resource> resource = createResource(Resource::MainResource, request.resourceRequest(), substituteData.textEncoding());
+ resource->setNeedsSynchronousCacheHit(substituteData.forceSynchronousLoad());
+ resource->setOptions(request.options());
+ resource->setDataBufferingPolicy(BufferData);
+ resource->responseReceived(response);
+ if (substituteData.content()->size())
+ resource->setResourceBuffer(substituteData.content());
+ resource->finish();
+ memoryCache()->add(resource.get());
}
bool ResourceFetcher::checkInsecureContent(Resource::Type type, const KURL& url, MixedContentBlockingTreatment treatment) const
}
}
if (treatment == TreatAsActiveContent) {
- if (Frame* f = frame()) {
+ if (LocalFrame* f = frame()) {
if (!f->loader().mixedContentChecker()->canRunInsecureContent(m_document->securityOrigin(), url))
return false;
- Frame* top = f->tree().top();
+ LocalFrame* 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 (LocalFrame* f = frame()) {
+ LocalFrame* top = f->tree().top();
if (!top->loader().mixedContentChecker()->canDisplayInsecureContent(top->document()->securityOrigin(), url))
return false;
}
return true;
}
-bool ResourceFetcher::canRequest(Resource::Type type, const KURL& url, const ResourceLoaderOptions& options, bool forPreload)
+bool ResourceFetcher::canRequest(Resource::Type type, const KURL& url, const ResourceLoaderOptions& options, bool forPreload, FetchRequest::OriginRestriction originRestriction) const
{
- if (document() && !document()->securityOrigin()->canDisplay(url)) {
+ SecurityOrigin* securityOrigin = options.securityOrigin.get();
+ if (!securityOrigin && document())
+ securityOrigin = document()->securityOrigin();
+
+ if (securityOrigin && !securityOrigin->canDisplay(url)) {
if (!forPreload)
context().reportLocalLoadFailed(url);
- LOG(ResourceLoading, "ResourceFetcher::requestResource URL was not allowed by SecurityOrigin::canDisplay");
+ WTF_LOG(ResourceLoading, "ResourceFetcher::requestResource URL was not allowed by SecurityOrigin::canDisplay");
return 0;
}
case Resource::ImportResource:
// By default these types of resources can be loaded from any origin.
// FIXME: Are we sure about Resource::Font?
- if (options.requestOriginPolicy == RestrictToSameOrigin && !m_document->securityOrigin()->canRequest(url)) {
+ if (originRestriction == FetchRequest::RestrictToSameOrigin && !securityOrigin->canRequest(url)) {
printAccessDeniedMessage(url);
return false;
}
case Resource::XSLStyleSheet:
ASSERT(RuntimeEnabledFeatures::xsltEnabled());
case Resource::SVGDocument:
- if (!m_document->securityOrigin()->canRequest(url)) {
+ if (!securityOrigin->canRequest(url)) {
printAccessDeniedMessage(url);
return false;
}
if (frame()) {
Settings* settings = frame()->settings();
- if (!frame()->loader().client()->allowScriptFromSource(!settings || settings->isScriptEnabled(), url)) {
+ if (!frame()->loader().client()->allowScriptFromSource(!settings || settings->scriptEnabled(), url)) {
frame()->loader().client()->didNotAllowScript();
return false;
}
return true;
}
-bool ResourceFetcher::canAccess(Resource* resource)
+bool ResourceFetcher::canAccessResource(Resource* resource, SecurityOrigin* sourceOrigin, const KURL& url) const
{
// Redirects can change the response URL different from one of request.
- if (!canRequest(resource->type(), resource->response().url(), resource->options(), false))
+ if (!canRequest(resource->type(), url, resource->options(), false, FetchRequest::UseDefaultOriginRestrictionForType))
return false;
- String error;
- switch (resource->type()) {
- case Resource::Script:
- case Resource::ImportResource:
- if (resource->options().requestOriginPolicy == PotentiallyCrossOriginEnabled
- && !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;
- }
+ if (!sourceOrigin && document())
+ sourceOrigin = document()->securityOrigin();
- break;
- default:
- ASSERT_NOT_REACHED(); // FIXME: generalize to non-script resources
+ if (sourceOrigin->canRequest(url))
+ return true;
+
+ String errorDescription;
+ if (!resource->passesAccessControlCheck(sourceOrigin, errorDescription)) {
+ if (frame() && frame()->document()) {
+ String resourceType = Resource::resourceTypeToString(resource->type(), resource->options().initiatorInfo);
+ frame()->document()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, resourceType + " from origin '" + SecurityOrigin::create(url)->toString() + "' has been blocked from loading by Cross-Origin Resource Sharing policy: " + errorDescription);
+ }
return false;
}
-
return true;
}
-bool ResourceFetcher::shouldLoadNewResource() const
+bool ResourceFetcher::shouldLoadNewResource(Resource::Type type) const
{
if (!frame())
return false;
if (!m_documentLoader)
return true;
- return m_documentLoader == frame()->loader().activeDocumentLoader();
+ if (type == Resource::MainResource)
+ return m_documentLoader == frame()->loader().provisionalDocumentLoader();
+ return m_documentLoader == frame()->loader().documentLoader();
}
bool ResourceFetcher::resourceNeedsLoad(Resource* resource, const FetchRequest& request, RevalidationPolicy policy)
KURL url = request.resourceRequest().url();
- 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));
+ 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()))
+ if (!canRequest(type, url, request.options(), request.forPreload(), request.originRestriction()))
return 0;
- if (Frame* f = frame())
+ if (LocalFrame* f = frame())
f->loader().client()->dispatchWillRequestResource(&request);
// See if we can use an existing resource from the cache.
ResourcePtr<Resource> resource = memoryCache()->resourceForURL(url);
- const RevalidationPolicy policy = determineRevalidationPolicy(type, request.mutableResourceRequest(), request.forPreload(), resource.get(), request.defer());
+ const RevalidationPolicy policy = determineRevalidationPolicy(type, request.mutableResourceRequest(), request.forPreload(), resource.get(), request.defer(), request.options());
switch (policy) {
case Reload:
memoryCache()->remove(resource.get());
resource = revalidateResource(request, resource.get());
break;
case Use:
- resource->updateForAccess();
+ memoryCache()->updateForAccess(resource.get());
notifyLoadedFromMemoryCache(resource.get());
break;
}
if (!resource)
return 0;
+ if (!resource->hasClients())
+ m_deadStatsRecorder.update(policy);
+
if (policy != Use)
resource->setIdentifier(createUniqueIdentifier());
ResourceLoadPriority priority = loadPriority(type, request);
if (priority != resource->resourceRequest().priority()) {
resource->resourceRequest().setPriority(priority);
- resource->didChangePriority(priority);
+ resource->didChangePriority(priority, 0);
}
}
if (resourceNeedsLoad(resource.get(), request, policy)) {
- if (!shouldLoadNewResource()) {
- if (resource->inCache())
+ if (!shouldLoadNewResource(type)) {
+ if (memoryCache()->contains(resource.get()))
memoryCache()->remove(resource.get());
return 0;
}
// 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())
+ if (memoryCache()->contains(resource.get()))
memoryCache()->remove(resource.get());
return request.options().synchronousPolicy == RequestSynchronously ? resource : 0;
}
// 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 != Use || m_documentLoader->substituteData().isValid());
ASSERT(policy != Revalidate);
memoryCache()->remove(resource.get());
if (request.forPreload())
return 0;
}
- if (!request.resourceRequest().url().protocolIsData()) {
+ if (!request.resourceRequest().url().protocolIsData() && (!m_documentLoader || !m_documentLoader->substituteData().isValid())) {
if (policy == Use && !m_validatedURLs.contains(request.resourceRequest().url())) {
// Resources loaded from memory cache should be reported the first time they're used.
RefPtr<ResourceTimingInfo> info = ResourceTimingInfo::create(request.options().initiatorInfo.name, monotonicallyIncreasingTime());
- reportResourceTiming(info.get(), resource.get(), monotonicallyIncreasingTime(), document(), true);
+ populateResourceTiming(info.get(), resource.get(), true);
+ m_scheduledResourceTimingReports.add(info, resource->type() == Resource::MainResource);
+ if (!m_resourceTimingReportTimer.isActive())
+ m_resourceTimingReportTimer.startOneShot(0, FROM_HERE);
}
m_validatedURLs.add(request.resourceRequest().url());
return resource;
}
+void ResourceFetcher::resourceTimingReportTimerFired(Timer<ResourceFetcher>* timer)
+{
+ ASSERT_UNUSED(timer, timer == &m_resourceTimingReportTimer);
+ HashMap<RefPtr<ResourceTimingInfo>, bool> timingReports;
+ timingReports.swap(m_scheduledResourceTimingReports);
+ HashMap<RefPtr<ResourceTimingInfo>, bool>::iterator end = timingReports.end();
+ for (HashMap<RefPtr<ResourceTimingInfo>, bool>::iterator it = timingReports.begin(); it != end; ++it) {
+ RefPtr<ResourceTimingInfo> info = it->key;
+ bool isMainResource = it->value;
+ reportResourceTiming(info.get(), document(), isMainResource);
+ }
+}
+
void ResourceFetcher::determineTargetType(ResourceRequest& request, Resource::Type type)
{
ResourceRequest::TargetType targetType = requestTargetType(this, request, type);
return ReturnCacheDataElseLoad;
if (isReload || frameLoadType == FrameLoadTypeSame || request.isConditional() || request.httpMethod() == "POST")
return ReloadIgnoringCacheData;
- if (Frame* parent = frame()->tree().parent())
+ if (LocalFrame* parent = frame()->tree().parent())
return parent->document()->fetcher()->resourceRequestCachePolicy(request, type);
return UseProtocolCachePolicy;
}
if (request.isConditional())
return ReloadIgnoringCacheData;
- if (m_documentLoader && m_documentLoader->isLoadingInAPISense()) {
+ if (m_documentLoader && m_document && !m_document->loadEventFinished()) {
// 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 (type == Resource::LinkPrefetch || type == Resource::LinkSubresource)
request.setHTTPHeaderField("Purpose", "prefetch");
- context().addAdditionalRequestHeaders(*document(), request, type);
+ context().addAdditionalRequestHeaders(document(), request, (type == Resource::MainResource) ? FetchMainResource : FetchSubresource);
}
ResourcePtr<Resource> ResourceFetcher::revalidateResource(const FetchRequest& request, Resource* resource)
{
ASSERT(resource);
- ASSERT(resource->inCache());
+ ASSERT(memoryCache()->contains(resource));
ASSERT(resource->isLoaded());
ASSERT(resource->canUseCacheValidator());
ASSERT(!resource->resourceToRevalidate());
ResourceRequest revalidatingRequest(resource->resourceRequest());
+ revalidatingRequest.clearHTTPReferrer();
addAdditionalRequestHeaders(revalidatingRequest, resource->type());
- const String& lastModified = resource->response().httpHeaderField("Last-Modified");
- const String& eTag = resource->response().httpHeaderField("ETag");
+ 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)
+ ASSERT(context().cachePolicy(document()) != CachePolicyReload);
+ if (context().cachePolicy(document()) == CachePolicyRevalidate)
revalidatingRequest.setHTTPHeaderField("Cache-Control", "max-age=0");
if (!lastModified.isEmpty())
revalidatingRequest.setHTTPHeaderField("If-Modified-Since", lastModified);
ResourcePtr<Resource> newResource = createResource(resource->type(), revalidatingRequest, resource->encoding());
- LOG(ResourceLoading, "Resource %p created to revalidate %p", newResource.get(), resource);
+ WTF_LOG(ResourceLoading, "Resource %p created to revalidate %p", newResource.get(), resource);
newResource->setResourceToRevalidate(resource);
memoryCache()->remove(resource);
{
ASSERT(!memoryCache()->resourceForURL(request.resourceRequest().url()));
- LOG(ResourceLoading, "Loading Resource for '%s'.", request.resourceRequest().url().elidedString().latin1().data());
+ WTF_LOG(ResourceLoading, "Loading Resource for '%s'.", request.resourceRequest().url().elidedString().latin1().data());
addAdditionalRequestHeaders(request.mutableResourceRequest(), type);
ResourcePtr<Resource> resource = createResource(type, request.mutableResourceRequest(), charset);
}
}
-ResourceFetcher::RevalidationPolicy ResourceFetcher::determineRevalidationPolicy(Resource::Type type, ResourceRequest& request, bool forPreload, Resource* existingResource, FetchRequest::DeferOption defer) const
+ResourceFetcher::RevalidationPolicy ResourceFetcher::determineRevalidationPolicy(Resource::Type type, ResourceRequest& request, bool forPreload, Resource* existingResource, FetchRequest::DeferOption defer, const ResourceLoaderOptions& options) const
{
if (!existingResource)
return Load;
// If the same URL has been loaded as a different type, we need to reload.
if (existingResource->type() != type) {
- LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to type mismatch.");
+ WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to type mismatch.");
return Reload;
}
if (type == Resource::Image && request.url().protocolIsData())
return Use;
+ // If a main resource was populated from a SubstituteData load, use it.
+ if (type == Resource::MainResource && m_documentLoader->substituteData().isValid())
+ return Use;
+
if (!existingResource->canReuse(request))
return Reload;
+ // Never use cache entries for downloadToFile requests. The caller expects the resource in a file.
+ if (request.downloadToFile())
+ return Reload;
+
// Certain requests (e.g., XHRs) might have manually set headers that require revalidation.
// FIXME: In theory, this should be a Revalidate case. In practice, the MemoryCache revalidation path assumes a whole bunch
// of things about how revalidation works that manual headers violate, so punt to Reload instead.
if (m_allowStaleResources)
return Use;
- // Alwaus use preloads.
+ // Always use preloads.
if (existingResource->isPreloaded())
return Use;
// CachePolicyHistoryBuffer uses the cache no matter what.
- if (context().cachePolicy(type) == CachePolicyHistoryBuffer)
+ CachePolicy cachePolicy = context().cachePolicy(document());
+ if (cachePolicy == CachePolicyHistoryBuffer)
return Use;
// Don't reuse resources with Cache-control: no-store.
if (existingResource->response().cacheControlContainsNoStore()) {
- LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to Cache-control: no-store.");
+ WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to Cache-control: no-store.");
return Reload;
}
+ // If fetching a resource with a different 'CORS enabled' flag, reload.
+ if (type != Resource::MainResource && options.corsEnabled != existingResource->options().corsEnabled)
+ return Reload;
+
// If credentials were sent with the previous request and won't be
// with this one, or vice versa, re-fetch the resource.
//
// This helps with the case where the server sends back
// "Access-Control-Allow-Origin: *" all the time, but some of the
// client's requests are made without CORS and some with.
- if (existingResource->resourceRequest().allowCookies() != request.allowCookies()) {
- LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to difference in credentials settings.");
+ if (existingResource->resourceRequest().allowStoredCredentials() != request.allowStoredCredentials()) {
+ WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to difference in credentials settings.");
return Reload;
}
- // During the initial load, avoid loading the same resource multiple times for a single document, even if the cache policies would tell us to.
- if (document() && !document()->loadEventFinished() && m_validatedURLs.contains(existingResource->url()))
+ // During the initial load, avoid loading the same resource multiple times for a single document,
+ // even if the cache policies would tell us to. Raw resources are exempted.
+ if (type != Resource::Raw && document() && !document()->loadEventFinished() && m_validatedURLs.contains(existingResource->url()))
return Use;
// CachePolicyReload always reloads
- if (context().cachePolicy(type) == CachePolicyReload) {
- LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to CachePolicyReload.");
+ if (cachePolicy == CachePolicyReload) {
+ WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to CachePolicyReload.");
return Reload;
}
// We'll try to reload the resource if it failed last time.
if (existingResource->errorOccurred()) {
- LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicye reloading due to resource being in the error state");
+ WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicye reloading due to resource being in the error state");
return Reload;
}
if (existingResource->isLoading())
return Use;
+ // If any of the redirects in the chain to loading the resource were not cacheable, we cannot reuse our cached resource.
+ if (!existingResource->canReuseRedirectChain()) {
+ WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to an uncacheable redirect");
+ return Reload;
+ }
+
// Check if the cache headers requires us to revalidate (cache expiration for example).
- if (existingResource->mustRevalidateDueToCacheHeaders(context().cachePolicy(type))) {
+ if (cachePolicy == CachePolicyRevalidate || existingResource->mustRevalidateDueToCacheHeaders()) {
// See if the resource has usable ETag or Last-modified headers.
if (existingResource->canUseCacheValidator())
return Revalidate;
// No, must reload.
- LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to missing cache validators.");
+ WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to missing cache validators.");
return Reload;
}
if (it != m_resourceTimingInfoMap.end()) {
RefPtr<ResourceTimingInfo> info = it->value;
m_resourceTimingInfoMap.remove(it);
- reportResourceTiming(info.get(), resource, resource->loadFinishTime(), document(), false);
+ populateResourceTiming(info.get(), resource, false);
+ reportResourceTiming(info.get(), document(), resource->type() == Resource::MainResource);
}
}
if (frame())
frame()->loader().loadDone();
- performPostLoadActions();
+ scheduleDocumentResourcesGC();
+}
+void ResourceFetcher::scheduleDocumentResourcesGC()
+{
if (!m_garbageCollectDocumentResourcesTimer.isActive())
- m_garbageCollectDocumentResourcesTimer.startOneShot(0);
+ m_garbageCollectDocumentResourcesTimer.startOneShot(0, FROM_HERE);
}
// Garbage collecting m_documentResources is a workaround for the
m_documentResources.remove(*it);
}
-void ResourceFetcher::performPostLoadActions()
-{
- checkForPendingPreloads();
-}
-
void ResourceFetcher::notifyLoadedFromMemoryCache(Resource* resource)
{
if (!frame() || !frame()->page() || resource->status() != Resource::Cached || m_validatedURLs.contains(resource->url()))
return;
- if (!resource->shouldSendResourceLoadCallbacks())
- return;
ResourceRequest request(resource->url());
unsigned long identifier = createUniqueIdentifier();
context().dispatchDidLoadResourceFromMemoryCache(request, resource->response());
// FIXME: If willSendRequest changes the request, we don't respect it.
- willSendRequest(identifier, request, ResourceResponse(), resource->options());
+ willSendRequest(identifier, request, ResourceResponse(), resource->options().initiatorInfo);
InspectorInstrumentation::markResourceAsCached(frame()->page(), identifier);
- context().sendRemainingDelegateMessages(m_documentLoader, identifier, resource->response(), 0, resource->encodedSize(), 0, ResourceError());
+ context().sendRemainingDelegateMessages(m_documentLoader, identifier, resource->response(), resource->encodedSize());
}
void ResourceFetcher::incrementRequestCount(const Resource* res)
requestPreload(type, request, charset);
}
-void ResourceFetcher::checkForPendingPreloads()
-{
- // FIXME: It seems wrong to poke body()->renderer() here.
- if (m_pendingPreloads.isEmpty() || !m_document->body() || !m_document->body()->renderer())
- return;
- while (!m_pendingPreloads.isEmpty()) {
- PendingPreload preload = m_pendingPreloads.takeFirst();
- // Don't request preload if the resource already loaded normally (this will result in double load if the page is being reloaded with cached results ignored).
- if (!cachedResource(preload.m_request.resourceRequest().url()))
- requestPreload(preload.m_type, preload.m_request, preload.m_charset);
- }
- m_pendingPreloads.clear();
-}
-
void ResourceFetcher::requestPreload(Resource::Type type, FetchRequest& request, const String& charset)
{
String encoding;
if (type == Resource::Script || type == Resource::CSSStyleSheet)
- encoding = charset.isEmpty() ? m_document->charset() : charset;
+ encoding = charset.isEmpty() ? m_document->charset().string() : charset;
request.setCharset(encoding);
request.setForPreload(true);
ResourcePtr<Resource> resource = requestResource(type, request);
if (!resource || (m_preloads && m_preloads->contains(resource.get())))
return;
- TRACE_EVENT_ASYNC_STEP0("net", "Resource", resource.get(), "Preload");
+ TRACE_EVENT_ASYNC_STEP_INTO0("net", "Resource", resource.get(), "Preload");
resource->increasePreloadCount();
if (!m_preloads)
m_preloads->add(resource.get());
#if PRELOAD_DEBUG
- printf("PRELOADING %s\n", resource->url().latin1().data());
+ printf("PRELOADING %s\n", resource->url().string().latin1().data());
#endif
}
}
}
- Deque<PendingPreload>::const_iterator dequeEnd = m_pendingPreloads.end();
- for (Deque<PendingPreload>::const_iterator it = m_pendingPreloads.begin(); it != dequeEnd; ++it) {
- PendingPreload pendingPreload = *it;
- if (pendingPreload.m_request.resourceRequest().url() == url)
- return true;
- }
return false;
}
m_preloads.clear();
}
-void ResourceFetcher::clearPendingPreloads()
-{
- m_pendingPreloads.clear();
-}
-
-void ResourceFetcher::didFinishLoading(const Resource* resource, double finishTime, const ResourceLoaderOptions& options)
+void ResourceFetcher::didFinishLoading(const Resource* resource, double finishTime, int64_t encodedDataLength)
{
TRACE_EVENT_ASYNC_END0("net", "Resource", resource);
- if (options.sendLoadCallbacks != SendCallbacks)
- return;
- context().dispatchDidFinishLoading(m_documentLoader, resource->identifier(), finishTime);
+ context().dispatchDidFinishLoading(m_documentLoader, resource->identifier(), finishTime, encodedDataLength);
}
-void ResourceFetcher::didChangeLoadingPriority(const Resource* resource, ResourceLoadPriority loadPriority)
+void ResourceFetcher::didChangeLoadingPriority(const Resource* resource, ResourceLoadPriority loadPriority, int intraPriorityValue)
{
- TRACE_EVENT_ASYNC_STEP1("net", "Resource", resource, "ChangePriority", "priority", loadPriority);
- context().dispatchDidChangeResourcePriority(resource->identifier(), loadPriority);
+ TRACE_EVENT_ASYNC_STEP_INTO1("net", "Resource", resource, "ChangePriority", "priority", loadPriority);
+ context().dispatchDidChangeResourcePriority(resource->identifier(), loadPriority, intraPriorityValue);
}
-void ResourceFetcher::didFailLoading(const Resource* resource, const ResourceError& error, const ResourceLoaderOptions& options)
+void ResourceFetcher::didFailLoading(const Resource* resource, const ResourceError& error)
{
TRACE_EVENT_ASYNC_END0("net", "Resource", resource);
- if (options.sendLoadCallbacks != SendCallbacks)
- return;
context().dispatchDidFail(m_documentLoader, resource->identifier(), error);
}
-void ResourceFetcher::willSendRequest(unsigned long identifier, ResourceRequest& request, const ResourceResponse& redirectResponse, const ResourceLoaderOptions& options)
+void ResourceFetcher::willSendRequest(unsigned long identifier, ResourceRequest& request, const ResourceResponse& redirectResponse, const FetchInitiatorInfo& initiatorInfo)
{
- if (options.sendLoadCallbacks == SendCallbacks)
- context().dispatchWillSendRequest(m_documentLoader, identifier, request, redirectResponse, options.initiatorInfo);
- else
- InspectorInstrumentation::willSendRequest(frame(), identifier, m_documentLoader, request, redirectResponse, options.initiatorInfo);
+ context().dispatchWillSendRequest(m_documentLoader, identifier, request, redirectResponse, initiatorInfo);
}
-void ResourceFetcher::didReceiveResponse(const Resource* resource, const ResourceResponse& response, const ResourceLoaderOptions& options)
+void ResourceFetcher::didReceiveResponse(const Resource* resource, const ResourceResponse& response)
{
- if (options.sendLoadCallbacks != SendCallbacks)
- return;
context().dispatchDidReceiveResponse(m_documentLoader, resource->identifier(), response, resource->loader());
}
-void ResourceFetcher::didReceiveData(const Resource* resource, const char* data, int dataLength, int encodedDataLength, const ResourceLoaderOptions& options)
+void ResourceFetcher::didReceiveData(const Resource* resource, const char* data, int dataLength, int encodedDataLength)
{
- // FIXME: use frame of master document for imported documents.
- InspectorInstrumentationCookie cookie = InspectorInstrumentation::willReceiveResourceData(frame(), resource->identifier(), encodedDataLength);
- if (options.sendLoadCallbacks != SendCallbacks)
- return;
context().dispatchDidReceiveData(m_documentLoader, resource->identifier(), data, dataLength, encodedDataLength);
- InspectorInstrumentation::didReceiveResourceData(cookie);
+}
+
+void ResourceFetcher::didDownloadData(const Resource* resource, int dataLength, int encodedDataLength)
+{
+ context().dispatchDidDownloadData(m_documentLoader, resource->identifier(), dataLength, encodedDataLength);
}
void ResourceFetcher::subresourceLoaderFinishedLoadingOnePart(ResourceLoader* loader)
m_multipartLoaders->add(loader);
if (m_loaders)
m_loaders->remove(loader);
- if (Frame* frame = this->frame())
+ if (LocalFrame* frame = this->frame())
return frame->loader().checkLoadComplete(m_documentLoader);
}
if (!m_loaders || !m_loaders->contains(loader))
return;
m_loaders->remove(loader);
- if (Frame* frame = this->frame())
+ if (LocalFrame* frame = this->frame())
frame->loader().checkLoadComplete(m_documentLoader);
}
bool ResourceFetcher::defersLoading() const
{
- if (Frame* frame = this->frame())
+ if (LocalFrame* frame = this->frame())
return frame->page()->defersLoading();
return false;
}
return this == possibleOwner;
}
-bool ResourceFetcher::shouldRequest(Resource* resource, const ResourceRequest& request, const ResourceLoaderOptions& options)
+bool ResourceFetcher::canAccessRedirect(Resource* resource, ResourceRequest& request, const ResourceResponse& redirectResponse, ResourceLoaderOptions& options)
{
- if (!canRequest(resource->type(), request.url(), options))
+ if (!canRequest(resource->type(), request.url(), options, false, FetchRequest::UseDefaultOriginRestrictionForType))
return false;
+ if (options.corsEnabled == IsCORSEnabled) {
+ SecurityOrigin* sourceOrigin = options.securityOrigin.get();
+ if (!sourceOrigin && document())
+ sourceOrigin = document()->securityOrigin();
+
+ String errorMessage;
+ if (!CrossOriginAccessControl::handleRedirect(resource, sourceOrigin, request, redirectResponse, options, errorMessage)) {
+ if (frame() && frame()->document())
+ frame()->document()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, errorMessage);
+ return false;
+ }
+ }
if (resource->type() == Resource::Image && shouldDeferImageLoad(request.url()))
return false;
return true;
#if PRELOAD_DEBUG
void ResourceFetcher::printPreloadStats()
{
+ if (!m_preloads)
+ return;
+
unsigned scripts = 0;
unsigned scriptMisses = 0;
unsigned stylesheets = 0;
unsigned stylesheetMisses = 0;
unsigned images = 0;
unsigned imageMisses = 0;
- ListHashSet<Resource*>::iterator end = m_preloads.end();
- for (ListHashSet<Resource*>::iterator it = m_preloads.begin(); it != end; ++it) {
+ ListHashSet<Resource*>::iterator end = m_preloads->end();
+ for (ListHashSet<Resource*>::iterator it = m_preloads->begin(); it != end; ++it) {
Resource* res = *it;
if (res->preloadResult() == Resource::PreloadNotReferenced)
- printf("!! UNREFERENCED PRELOAD %s\n", res->url().latin1().data());
+ printf("!! UNREFERENCED PRELOAD %s\n", res->url().string().latin1().data());
else if (res->preloadResult() == Resource::PreloadReferencedWhileComplete)
- printf("HIT COMPLETE PRELOAD %s\n", res->url().latin1().data());
+ printf("HIT COMPLETE PRELOAD %s\n", res->url().string().latin1().data());
else if (res->preloadResult() == Resource::PreloadReferencedWhileLoading)
- printf("HIT LOADING PRELOAD %s\n", res->url().latin1().data());
+ printf("HIT LOADING PRELOAD %s\n", res->url().string().latin1().data());
if (res->type() == Resource::Script) {
scripts++;
const ResourceLoaderOptions& ResourceFetcher::defaultResourceOptions()
{
- DEFINE_STATIC_LOCAL(ResourceLoaderOptions, options, (SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, ClientRequestedCredentials, AskClientForCrossOriginCredentials, DoSecurityCheck, CheckContentSecurityPolicy, UseDefaultOriginRestrictionsForType, DocumentContext));
+ DEFINE_STATIC_LOCAL(ResourceLoaderOptions, options, (SniffContent, BufferData, AllowStoredCredentials, ClientRequestedCredentials, CheckContentSecurityPolicy, DocumentContext));
return options;
}
+ResourceFetcher::DeadResourceStatsRecorder::DeadResourceStatsRecorder()
+ : m_useCount(0)
+ , m_revalidateCount(0)
+ , m_loadCount(0)
+{
+}
+
+ResourceFetcher::DeadResourceStatsRecorder::~DeadResourceStatsRecorder()
+{
+ blink::Platform::current()->histogramCustomCounts(
+ "WebCore.ResourceFetcher.HitCount", m_useCount, 0, 1000, 50);
+ blink::Platform::current()->histogramCustomCounts(
+ "WebCore.ResourceFetcher.RevalidateCount", m_revalidateCount, 0, 1000, 50);
+ blink::Platform::current()->histogramCustomCounts(
+ "WebCore.ResourceFetcher.LoadCount", m_loadCount, 0, 1000, 50);
+}
+
+void ResourceFetcher::DeadResourceStatsRecorder::update(RevalidationPolicy policy)
+{
+ switch (policy) {
+ case Reload:
+ case Load:
+ ++m_loadCount;
+ return;
+ case Revalidate:
+ ++m_revalidateCount;
+ return;
+ case Use:
+ ++m_useCount;
+ return;
+ }
+}
+
}