blob: ec4ab7aac8b4127f077c41a8b46221ae4be1b19b [file] [log] [blame]
/*
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 "sky/engine/config.h"
#include "sky/engine/core/fetch/ResourceFetcher.h"
#include "gen/sky/core/FetchInitiatorTypeNames.h"
#include "gen/sky/platform/RuntimeEnabledFeatures.h"
#include "sky/engine/core/dom/Document.h"
#include "sky/engine/core/fetch/FetchContext.h"
#include "sky/engine/core/fetch/FontResource.h"
#include "sky/engine/core/fetch/ImageResource.h"
#include "sky/engine/core/fetch/MemoryCache.h"
#include "sky/engine/core/fetch/RawResource.h"
#include "sky/engine/core/fetch/ResourceLoader.h"
#include "sky/engine/core/fetch/ResourceLoaderSet.h"
#include "sky/engine/core/frame/LocalDOMWindow.h"
#include "sky/engine/core/frame/LocalFrame.h"
#include "sky/engine/core/frame/Settings.h"
#include "sky/engine/core/html/HTMLElement.h"
#include "sky/engine/core/html/imports/HTMLImportsController.h"
#include "sky/engine/core/inspector/ConsoleMessage.h"
#include "sky/engine/core/loader/FrameLoaderClient.h"
#include "sky/engine/core/loader/UniqueIdentifier.h"
#include "sky/engine/core/page/Page.h"
#include "sky/engine/platform/Logging.h"
#include "sky/engine/platform/SharedBuffer.h"
#include "sky/engine/platform/TraceEvent.h"
#include "sky/engine/platform/weborigin/SecurityPolicy.h"
#include "sky/engine/public/platform/Platform.h"
#include "sky/engine/public/platform/WebURL.h"
#include "sky/engine/public/platform/WebURLRequest.h"
#include "sky/engine/wtf/text/CString.h"
#include "sky/engine/wtf/text/WTFString.h"
using blink::WebURLRequest;
namespace blink {
static Resource* createResource(Resource::Type type, const ResourceRequest& request, const String& charset)
{
switch (type) {
case Resource::Image:
return new ImageResource(request);
case Resource::Font:
return new FontResource(request);
case Resource::MainResource:
case Resource::Raw:
case Resource::Media:
return new RawResource(request, type);
case Resource::LinkPrefetch:
return new Resource(request, Resource::LinkPrefetch);
case Resource::LinkSubresource:
return new Resource(request, Resource::LinkSubresource);
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::Raw:
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::Media:
return ResourceLoadPriorityLow;
case Resource::LinkPrefetch:
return ResourceLoadPriorityVeryLow;
case Resource::LinkSubresource:
return ResourceLoadPriorityLow;
}
ASSERT_NOT_REACHED();
return ResourceLoadPriorityUnresolved;
}
static WebURLRequest::RequestContext requestContextFromType(const ResourceFetcher* fetcher, Resource::Type type)
{
switch (type) {
case Resource::MainResource:
// FIXME: Change this to a context frame type (once we introduce them): http://fetch.spec.whatwg.org/#concept-request-context-frame-type
return WebURLRequest::RequestContextHyperlink;
case Resource::Font:
return WebURLRequest::RequestContextFont;
case Resource::Image:
return WebURLRequest::RequestContextImage;
case Resource::Raw:
return WebURLRequest::RequestContextSubresource;
case Resource::ImportResource:
return WebURLRequest::RequestContextImport;
case Resource::LinkPrefetch:
return WebURLRequest::RequestContextPrefetch;
case Resource::LinkSubresource:
return WebURLRequest::RequestContextSubresource;
case Resource::Media: // TODO: Split this.
return WebURLRequest::RequestContextVideo;
}
ASSERT_NOT_REACHED();
return WebURLRequest::RequestContextSubresource;
}
ResourceFetcher::ResourceFetcher(Document* document)
: m_document(document)
, m_requestCount(0)
, m_garbageCollectDocumentResourcesTimer(this, &ResourceFetcher::garbageCollectDocumentResourcesTimerFired)
, m_autoLoadImages(true)
, m_imagesEnabled(true)
, m_allowStaleResources(false)
{
}
ResourceFetcher::~ResourceFetcher()
{
m_document = nullptr;
// Make sure no requests still point to this ResourceFetcher
ASSERT(!m_requestCount);
}
Resource* ResourceFetcher::cachedResource(const KURL& resourceURL) const
{
KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(resourceURL);
return m_documentResources.get(url).get();
}
LocalFrame* ResourceFetcher::frame() const
{
// FIXME(sky): This used to prefer DocumentLoader::frame
// over importsController->master()->frame(), but in our
// world we should always just have one frame.
if (!m_document)
return 0;
if (m_document->importsController())
return m_document->importsController()->master()->frame();
return m_document->frame();
}
FetchContext& ResourceFetcher::context() const
{
if (LocalFrame* frame = this->frame())
return frame->fetchContext();
return FetchContext::nullInstance();
}
ResourcePtr<ImageResource> ResourceFetcher::fetchImage(FetchRequest& request)
{
request.setDefer(clientDefersImage(request.resourceRequest().url()) ? FetchRequest::DeferredByClient : FetchRequest::NoDefer);
ResourcePtr<Resource> resource = requestResource(Resource::Image, request);
return resource && resource->type() == Resource::Image ? toImageResource(resource) : 0;
}
ResourcePtr<FontResource> ResourceFetcher::fetchFont(FetchRequest& request)
{
ASSERT(request.resourceRequest().frameType() == WebURLRequest::FrameTypeNone);
request.mutableResourceRequest().setRequestContext(WebURLRequest::RequestContextFont);
return toFontResource(requestResource(Resource::Font, request));
}
bool ResourceFetcher::canRequest(Resource::Type type, const KURL& url, const ResourceLoaderOptions& options, FetchRequest::OriginRestriction originRestriction) const
{
// FIXME(sky): Remove
return true;
}
bool ResourceFetcher::shouldLoadNewResource(Resource::Type type) const
{
if (!frame())
return false;
return true;
}
bool ResourceFetcher::resourceNeedsLoad(Resource* resource, const FetchRequest& request, RevalidationPolicy policy)
{
if (FetchRequest::DeferredByClient == request.defer())
return false;
if (policy != Use)
return true;
return resource->stillNeedsLoad();
}
void ResourceFetcher::requestLoadStarted(Resource* resource, const FetchRequest& request, ResourceLoadStartType type)
{
if (type == ResourceLoadingFromCache)
notifyLoadedFromMemoryCache(resource);
if (request.resourceRequest().url().protocolIsData())
return;
m_validatedURLs.add(request.resourceRequest().url());
}
ResourcePtr<Resource> ResourceFetcher::requestResource(Resource::Type type, FetchRequest& request)
{
ASSERT(request.options().synchronousPolicy == RequestAsynchronously || type == Resource::Raw);
TRACE_EVENT0("blink", "ResourceFetcher::requestResource");
KURL url = request.resourceRequest().url();
WTF_LOG(ResourceLoading, "ResourceFetcher::requestResource '%s', charset '%s', priority=%d, type=%s", url.elidedString().latin1().data(), request.charset().latin1().data(), request.priority(), 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.originRestriction()))
return 0;
if (LocalFrame* f = frame())
f->loaderClient()->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, resource.get());
switch (policy) {
case Reload:
memoryCache()->remove(resource.get());
// Fall through
case Load:
resource = createResourceForLoading(type, request, request.charset());
break;
case Revalidate:
resource = createResourceForRevalidation(request, resource.get());
break;
case Use:
memoryCache()->updateForAccess(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->mutableResourceRequest().setPriority(priority);
resource->didChangePriority(priority, 0);
}
if (resourceNeedsLoad(resource.get(), request, policy)) {
if (!shouldLoadNewResource(type)) {
if (memoryCache()->contains(resource.get()))
memoryCache()->remove(resource.get());
return 0;
}
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 (memoryCache()->contains(resource.get()))
memoryCache()->remove(resource.get());
return 0;
}
}
requestLoadStarted(resource.get(), request, policy == Use ? ResourceLoadingFromCache : ResourceLoadingFromNetwork);
ASSERT(resource->url() == url.string());
m_documentResources.set(resource->url(), resource);
return resource;
}
void ResourceFetcher::determineRequestContext(ResourceRequest& request, Resource::Type type)
{
WebURLRequest::RequestContext requestContext = requestContextFromType(this, type);
request.setRequestContext(requestContext);
}
ResourceRequestCachePolicy ResourceFetcher::resourceRequestCachePolicy(const ResourceRequest& request, Resource::Type type)
{
if (request.isConditional())
return ReloadIgnoringCacheData;
return UseProtocolCachePolicy;
}
void ResourceFetcher::addAdditionalRequestHeaders(ResourceRequest& request, Resource::Type type)
{
if (!frame())
return;
if (request.cachePolicy() == UseProtocolCachePolicy)
request.setCachePolicy(resourceRequestCachePolicy(request, type));
if (request.requestContext() == WebURLRequest::RequestContextUnspecified)
determineRequestContext(request, type);
if (type == Resource::LinkPrefetch || type == Resource::LinkSubresource)
request.setHTTPHeaderField("Purpose", "prefetch");
context().addAdditionalRequestHeaders(document(), request, (type == Resource::MainResource) ? FetchMainResource : FetchSubresource);
}
ResourcePtr<Resource> ResourceFetcher::createResourceForRevalidation(const FetchRequest& request, Resource* resource)
{
ASSERT(resource);
ASSERT(memoryCache()->contains(resource));
ASSERT(resource->isLoaded());
ASSERT(resource->canUseCacheValidator());
ASSERT(!resource->resourceToRevalidate());
ResourceRequest revalidatingRequest(resource->resourceRequest());
revalidatingRequest.clearHTTPReferrer();
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(document()) != CachePolicyReload);
if (context().cachePolicy(document()) == 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<Resource> 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());
return newResource;
}
ResourcePtr<Resource> ResourceFetcher::createResourceForLoading(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> resource = createResource(type, request.resourceRequest(), charset);
memoryCache()->add(resource.get());
return resource;
}
ResourceFetcher::RevalidationPolicy ResourceFetcher::determineRevalidationPolicy(Resource::Type type, const FetchRequest& fetchRequest, Resource* existingResource) const
{
const ResourceRequest& request = fetchRequest.resourceRequest();
if (!existingResource)
return Load;
// If the same URL has been loaded as a different type, we need to reload.
if (existingResource->type() != type) {
WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to type mismatch.");
return Reload;
}
// Do not load from cache if images are not enabled. The load for this image will be blocked
// in ImageResource::load.
if (FetchRequest::DeferredByClient == fetchRequest.defer())
return Reload;
// Always use data uris.
// FIXME: Extend this to non-images.
if (type == Resource::Image && request.url().protocolIsData())
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 (request.isConditional())
return Reload;
// Don't reload resources while pasting.
if (m_allowStaleResources)
return Use;
if (!fetchRequest.options().canReuseRequest(existingResource->options()))
return Reload;
// CachePolicyHistoryBuffer uses the cache no matter what.
CachePolicy cachePolicy = context().cachePolicy(document());
if (cachePolicy == CachePolicyHistoryBuffer)
return Use;
// Don't reuse resources with Cache-control: no-store.
if (existingResource->hasCacheControlNoStoreHeader()) {
WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to Cache-control: no-store.");
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().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.
// We also group loads of the same resource together.
// Raw resources are exempted, as XHRs fall into this category and may have user-set Cache-Control:
// headers or other factors that require separate requests.
if (type != Resource::Raw) {
if (document() && !document()->loadEventFinished() && m_validatedURLs.contains(existingResource->url()))
return Use;
if (existingResource->isLoading())
return Use;
}
// CachePolicyReload always reloads
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()) {
WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicye reloading due to resource being in the error state");
return Reload;
}
// List of available images logic allows images to be re-used without cache validation. We restrict this only to images
// from memory cache which are the same as the version in the current document.
if (type == Resource::Image && existingResource == cachedResource(request.url()))
return Use;
// Check if the cache headers requires us to revalidate (cache expiration for example).
if (cachePolicy == CachePolicyRevalidate || existingResource->mustRevalidateDueToCacheHeaders()
|| request.cacheControlContainsNoCache()) {
// See if the resource has usable ETag or Last-modified headers.
if (existingResource->canUseCacheValidator())
return Revalidate;
// No, must reload.
WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to missing cache validators.");
return Reload;
}
return Use;
}
void ResourceFetcher::printAccessDeniedMessage(const KURL& url) const
{
if (url.isNull())
return;
if (!frame())
return;
String message;
if (!m_document || m_document->url().isNull())
message = "Unsafe attempt to load URL " + url.elidedString() + '.';
else
message = "Unsafe attempt to load URL " + url.elidedString() + " from frame with URL " + m_document->url().elidedString() + ". Domains, protocols and ports must match.\n";
frame()->document()->addConsoleMessage(ConsoleMessage::create(SecurityMessageSource, ErrorMessageLevel, message));
}
void ResourceFetcher::setAutoLoadImages(bool enable)
{
if (enable == m_autoLoadImages)
return;
m_autoLoadImages = enable;
if (!m_autoLoadImages)
return;
reloadImagesIfNotDeferred();
}
void ResourceFetcher::setImagesEnabled(bool enable)
{
if (enable == m_imagesEnabled)
return;
m_imagesEnabled = enable;
if (!m_imagesEnabled)
return;
reloadImagesIfNotDeferred();
}
bool ResourceFetcher::clientDefersImage(const KURL& url) const
{
// FIXME(sky): remove
return false;
}
bool ResourceFetcher::shouldDeferImageLoad(const KURL& url) const
{
return clientDefersImage(url) || !m_autoLoadImages;
}
void ResourceFetcher::reloadImagesIfNotDeferred()
{
DocumentResourceMap::iterator end = m_documentResources.end();
for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it) {
Resource* resource = it->value.get();
if (resource->type() == Resource::Image && resource->stillNeedsLoad() && !clientDefersImage(resource->url()))
const_cast<Resource*>(resource)->load(this, defaultResourceOptions());
}
}
void ResourceFetcher::didLoadResource(Resource* resource)
{
RefPtr<Document> protectDocument(m_document);
if (m_document)
m_document->checkCompleted();
scheduleDocumentResourcesGC();
}
void ResourceFetcher::scheduleDocumentResourcesGC()
{
if (!m_garbageCollectDocumentResourcesTimer.isActive())
m_garbageCollectDocumentResourcesTimer.startOneShot(0, FROM_HERE);
}
// Garbage collecting m_documentResources is a workaround for the
// ResourcePtrs on the RHS being strong references. Ideally this
// would be a weak map, however ResourcePtrs perform additional
// bookkeeping on Resources, so instead pseudo-GC them -- when the
// reference count reaches 1, m_documentResources is the only reference, so
// remove it from the map.
void ResourceFetcher::garbageCollectDocumentResourcesTimerFired(Timer<ResourceFetcher>* timer)
{
ASSERT_UNUSED(timer, timer == &m_garbageCollectDocumentResourcesTimer);
garbageCollectDocumentResources();
}
void ResourceFetcher::garbageCollectDocumentResources()
{
typedef Vector<String, 10> StringVector;
StringVector resourcesToDelete;
for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != m_documentResources.end(); ++it) {
if (it->value->hasOneHandle())
resourcesToDelete.append(it->key);
}
m_documentResources.removeAll(resourcesToDelete);
}
void ResourceFetcher::notifyLoadedFromMemoryCache(Resource* resource)
{
if (!frame() || !frame()->page() || resource->status() != Resource::Cached || m_validatedURLs.contains(resource->url()))
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().initiatorInfo);
context().sendRemainingDelegateMessages(m_document, identifier, resource->response(), resource->encodedSize());
}
void ResourceFetcher::incrementRequestCount(const Resource* res)
{
if (res->ignoreForRequestCount())
return;
++m_requestCount;
}
void ResourceFetcher::decrementRequestCount(const Resource* res)
{
if (res->ignoreForRequestCount())
return;
--m_requestCount;
ASSERT(m_requestCount > -1);
}
void ResourceFetcher::didFinishLoading(const Resource* resource, double finishTime, int64_t encodedDataLength)
{
TRACE_EVENT_ASYNC_END0("net", "Resource", resource);
context().dispatchDidFinishLoading(m_document, resource->identifier(), finishTime, encodedDataLength);
}
void ResourceFetcher::didChangeLoadingPriority(const Resource* resource, ResourceLoadPriority loadPriority, int intraPriorityValue)
{
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)
{
TRACE_EVENT_ASYNC_END0("net", "Resource", resource);
context().dispatchDidFail(m_document, resource->identifier(), error);
}
void ResourceFetcher::willSendRequest(unsigned long identifier, ResourceRequest& request, const ResourceResponse& redirectResponse, const FetchInitiatorInfo& initiatorInfo)
{
context().dispatchWillSendRequest(m_document, identifier, request, redirectResponse, initiatorInfo);
}
void ResourceFetcher::didReceiveResponse(const Resource* resource, const ResourceResponse& response)
{
context().dispatchDidReceiveResponse(m_document, resource->identifier(), response, resource->loader());
}
void ResourceFetcher::didReceiveData(const Resource* resource, const char* data, int dataLength, int encodedDataLength)
{
context().dispatchDidReceiveData(m_document, resource->identifier(), data, dataLength, encodedDataLength);
}
void ResourceFetcher::didDownloadData(const Resource* resource, int dataLength, int encodedDataLength)
{
context().dispatchDidDownloadData(m_document, resource->identifier(), dataLength, encodedDataLength);
}
void ResourceFetcher::subresourceLoaderFinishedLoadingOnePart(ResourceLoader* loader)
{
if (!m_multipartLoaders)
m_multipartLoaders = ResourceLoaderSet::create();
m_multipartLoaders->add(loader);
m_loaders->remove(loader);
}
void ResourceFetcher::didInitializeResourceLoader(ResourceLoader* loader)
{
if (!m_document)
return;
if (!m_loaders)
m_loaders = ResourceLoaderSet::create();
ASSERT(!m_loaders->contains(loader));
m_loaders->add(loader);
}
void ResourceFetcher::willTerminateResourceLoader(ResourceLoader* loader)
{
if (m_loaders && m_loaders->contains(loader))
m_loaders->remove(loader);
if (m_multipartLoaders && m_multipartLoaders->contains(loader))
m_multipartLoaders->remove(loader);
}
void ResourceFetcher::willStartLoadingResource(Resource* resource, ResourceRequest& request)
{
TRACE_EVENT_ASYNC_BEGIN2(
"net", "Resource", resource, "url",
TRACE_STR_COPY(resource->url().string().ascii().data()), "priority",
resource->resourceRequest().priority());
}
void ResourceFetcher::stopFetching()
{
if (m_multipartLoaders)
m_multipartLoaders->cancelAll();
if (m_loaders)
m_loaders->cancelAll();
}
bool ResourceFetcher::isFetching() const
{
return m_loaders && !m_loaders->isEmpty();
}
bool ResourceFetcher::isLoadedBy(ResourceLoaderHost* possibleOwner) const
{
return this == possibleOwner;
}
#if !ENABLE(OILPAN)
void ResourceFetcher::refResourceLoaderHost()
{
ref();
}
void ResourceFetcher::derefResourceLoaderHost()
{
deref();
}
#endif
const ResourceLoaderOptions& ResourceFetcher::defaultResourceOptions()
{
DEFINE_STATIC_LOCAL(ResourceLoaderOptions, options, (BufferData, AllowStoredCredentials, ClientRequestedCredentials, 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;
}
}
}