mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
This caused us to lose our gn check certification. :( Turns out gn check was just ignoring all the header paths it didn't understand and so gn check passing for sky wasn't meaning much. I tried to straighten out some of the mess in this CL, but its going to take several more rounds of massaging before gn check passes again. On the bright side (almost) all of our headers are absolute now. Turns out my script (attached to the bug) didn't notice ../ includes but I'll fix that in the next patch. R=abarth@chromium.org BUG=435361 Review URL: https://codereview.chromium.org/746023002
772 lines
28 KiB
C++
772 lines
28 KiB
C++
/*
|
|
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/bindings/core/v8/ScriptController.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/frame/UseCounter.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", 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;
|
|
}
|
|
}
|
|
|
|
}
|