| Index: third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp
|
| diff --git a/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp b/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp
|
| deleted file mode 100644
|
| index 5f25871b730cf19284137b85d30a6eb761c716f8..0000000000000000000000000000000000000000
|
| --- a/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp
|
| +++ /dev/null
|
| @@ -1,1514 +0,0 @@
|
| -/*
|
| - 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 "core/fetch/ResourceFetcher.h"
|
| -
|
| -#include "core/fetch/FetchContext.h"
|
| -#include "core/fetch/FetchInitiatorTypeNames.h"
|
| -#include "core/fetch/MemoryCache.h"
|
| -#include "core/fetch/ResourceLoader.h"
|
| -#include "core/fetch/ResourceLoadingLog.h"
|
| -#include "core/fetch/UniqueIdentifier.h"
|
| -#include "platform/Histogram.h"
|
| -#include "platform/RuntimeEnabledFeatures.h"
|
| -#include "platform/instrumentation/tracing/TraceEvent.h"
|
| -#include "platform/instrumentation/tracing/TracedValue.h"
|
| -#include "platform/mhtml/ArchiveResource.h"
|
| -#include "platform/mhtml/MHTMLArchive.h"
|
| -#include "platform/network/NetworkInstrumentation.h"
|
| -#include "platform/network/NetworkUtils.h"
|
| -#include "platform/network/ResourceTimingInfo.h"
|
| -#include "platform/weborigin/KnownPorts.h"
|
| -#include "platform/weborigin/SecurityOrigin.h"
|
| -#include "platform/weborigin/SecurityPolicy.h"
|
| -#include "public/platform/Platform.h"
|
| -#include "public/platform/WebCachePolicy.h"
|
| -#include "public/platform/WebURL.h"
|
| -#include "public/platform/WebURLRequest.h"
|
| -#include "wtf/text/CString.h"
|
| -#include "wtf/text/WTFString.h"
|
| -#include <memory>
|
| -
|
| -using blink::WebURLRequest;
|
| -
|
| -namespace blink {
|
| -
|
| -namespace {
|
| -
|
| -// Events for UMA. Do not reorder or delete. Add new events at the end, but
|
| -// before SriResourceIntegrityMismatchEventCount.
|
| -enum SriResourceIntegrityMismatchEvent {
|
| - CheckingForIntegrityMismatch = 0,
|
| - RefetchDueToIntegrityMismatch = 1,
|
| - SriResourceIntegrityMismatchEventCount
|
| -};
|
| -
|
| -#define DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, name) \
|
| - case Resource::name: { \
|
| - DEFINE_THREAD_SAFE_STATIC_LOCAL( \
|
| - EnumerationHistogram, resourceHistogram, \
|
| - new EnumerationHistogram( \
|
| - "Blink.MemoryCache.RevalidationPolicy." prefix #name, Load + 1)); \
|
| - resourceHistogram.count(policy); \
|
| - break; \
|
| - }
|
| -
|
| -#define DEFINE_RESOURCE_HISTOGRAM(prefix) \
|
| - switch (factory.type()) { \
|
| - DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, CSSStyleSheet) \
|
| - DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, Font) \
|
| - DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, Image) \
|
| - DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, ImportResource) \
|
| - DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, LinkPrefetch) \
|
| - DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, MainResource) \
|
| - DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, Manifest) \
|
| - DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, Media) \
|
| - DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, Mock) \
|
| - DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, Raw) \
|
| - DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, Script) \
|
| - DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, SVGDocument) \
|
| - DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, TextTrack) \
|
| - DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, XSLStyleSheet) \
|
| - }
|
| -
|
| -void addRedirectsToTimingInfo(Resource* resource, ResourceTimingInfo* info) {
|
| - // Store redirect responses that were packed inside the final response.
|
| - const auto& responses = resource->response().redirectResponses();
|
| - for (size_t i = 0; i < responses.size(); ++i) {
|
| - const KURL& newURL = i + 1 < responses.size()
|
| - ? KURL(responses[i + 1].url())
|
| - : resource->resourceRequest().url();
|
| - bool crossOrigin =
|
| - !SecurityOrigin::areSameSchemeHostPort(responses[i].url(), newURL);
|
| - info->addRedirect(responses[i], crossOrigin);
|
| - }
|
| -}
|
| -
|
| -void RecordSriResourceIntegrityMismatchEvent(
|
| - SriResourceIntegrityMismatchEvent event) {
|
| - DEFINE_THREAD_SAFE_STATIC_LOCAL(
|
| - EnumerationHistogram, integrityHistogram,
|
| - new EnumerationHistogram("sri.resource_integrity_mismatch_event",
|
| - SriResourceIntegrityMismatchEventCount));
|
| - integrityHistogram.count(event);
|
| -}
|
| -
|
| -ResourceLoadPriority typeToPriority(Resource::Type type) {
|
| - switch (type) {
|
| - case Resource::MainResource:
|
| - case Resource::CSSStyleSheet:
|
| - case Resource::Font:
|
| - // Also parser-blocking scripts (set explicitly in loadPriority)
|
| - return ResourceLoadPriorityVeryHigh;
|
| - case Resource::XSLStyleSheet:
|
| - DCHECK(RuntimeEnabledFeatures::xsltEnabled());
|
| - case Resource::Raw:
|
| - case Resource::ImportResource:
|
| - case Resource::Script:
|
| - // Also visible resources/images (set explicitly in loadPriority)
|
| - return ResourceLoadPriorityHigh;
|
| - case Resource::Manifest:
|
| - case Resource::Mock:
|
| - // Also late-body scripts discovered by the preload scanner (set
|
| - // explicitly in loadPriority)
|
| - return ResourceLoadPriorityMedium;
|
| - case Resource::Image:
|
| - case Resource::TextTrack:
|
| - case Resource::Media:
|
| - case Resource::SVGDocument:
|
| - // Also async scripts (set explicitly in loadPriority)
|
| - return ResourceLoadPriorityLow;
|
| - case Resource::LinkPrefetch:
|
| - return ResourceLoadPriorityVeryLow;
|
| - }
|
| -
|
| - NOTREACHED();
|
| - return ResourceLoadPriorityUnresolved;
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -ResourceLoadPriority ResourceFetcher::computeLoadPriority(
|
| - Resource::Type type,
|
| - const FetchRequest& request,
|
| - ResourcePriority::VisibilityStatus visibility) {
|
| - ResourceLoadPriority priority = typeToPriority(type);
|
| -
|
| - // Visible resources (images in practice) get a boost to High priority.
|
| - if (visibility == ResourcePriority::Visible)
|
| - priority = ResourceLoadPriorityHigh;
|
| -
|
| - // Resources before the first image are considered "early" in the document and
|
| - // resources after the first image are "late" in the document. Important to
|
| - // note that this is based on when the preload scanner discovers a resource
|
| - // for the most part so the main parser may not have reached the image element
|
| - // yet.
|
| - if (type == Resource::Image)
|
| - m_imageFetched = true;
|
| -
|
| - if (FetchRequest::IdleLoad == request.defer()) {
|
| - priority = ResourceLoadPriorityVeryLow;
|
| - } else if (type == Resource::Script) {
|
| - // Special handling for scripts.
|
| - // Default/Parser-Blocking/Preload early in document: High (set in
|
| - // typeToPriority)
|
| - // Async/Defer: Low Priority (applies to both preload and parser-inserted)
|
| - // Preload late in document: Medium
|
| - if (FetchRequest::LazyLoad == request.defer())
|
| - priority = ResourceLoadPriorityLow;
|
| - else if (request.forPreload() && m_imageFetched)
|
| - priority = ResourceLoadPriorityMedium;
|
| - } else if (FetchRequest::LazyLoad == request.defer()) {
|
| - priority = ResourceLoadPriorityVeryLow;
|
| - }
|
| -
|
| - // A manually set priority acts as a floor. This is used to ensure that
|
| - // synchronous requests are always given the highest possible priority, as
|
| - // well as to ensure that there isn't priority churn if images move in and out
|
| - // of the viewport, or is displayed more than once, both in and out of the
|
| - // viewport.
|
| - return std::max(context().modifyPriorityForExperiments(priority),
|
| - request.resourceRequest().priority());
|
| -}
|
| -
|
| -static void populateTimingInfo(ResourceTimingInfo* info,
|
| - Resource* resource) {
|
| - KURL initialURL = resource->response().redirectResponses().isEmpty()
|
| - ? resource->resourceRequest().url()
|
| - : resource->response().redirectResponses()[0].url();
|
| - info->setInitialURL(initialURL);
|
| - info->setFinalResponse(resource->response());
|
| -}
|
| -
|
| -static WebURLRequest::RequestContext requestContextFromType(
|
| - bool isMainFrame,
|
| - Resource::Type type) {
|
| - switch (type) {
|
| - case Resource::MainResource:
|
| - if (!isMainFrame)
|
| - return WebURLRequest::RequestContextIframe;
|
| - // 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::XSLStyleSheet:
|
| - DCHECK(RuntimeEnabledFeatures::xsltEnabled());
|
| - case Resource::CSSStyleSheet:
|
| - return WebURLRequest::RequestContextStyle;
|
| - case Resource::Script:
|
| - return WebURLRequest::RequestContextScript;
|
| - 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::TextTrack:
|
| - return WebURLRequest::RequestContextTrack;
|
| - case Resource::SVGDocument:
|
| - return WebURLRequest::RequestContextImage;
|
| - case Resource::Media: // TODO: Split this.
|
| - return WebURLRequest::RequestContextVideo;
|
| - case Resource::Manifest:
|
| - return WebURLRequest::RequestContextManifest;
|
| - case Resource::Mock:
|
| - return WebURLRequest::RequestContextSubresource;
|
| - }
|
| - NOTREACHED();
|
| - return WebURLRequest::RequestContextSubresource;
|
| -}
|
| -
|
| -ResourceFetcher::ResourceFetcher(FetchContext* newContext)
|
| - : m_context(newContext),
|
| - m_archive(context().isMainFrame() ? nullptr : context().archive()),
|
| - // loadingTaskRunner() is null in tests that use the null fetch context.
|
| - m_resourceTimingReportTimer(
|
| - context().loadingTaskRunner()
|
| - ? context().loadingTaskRunner()
|
| - : Platform::current()->currentThread()->getWebTaskRunner(),
|
| - this,
|
| - &ResourceFetcher::resourceTimingReportTimerFired),
|
| - m_autoLoadImages(true),
|
| - m_imagesEnabled(true),
|
| - m_allowStaleResources(false),
|
| - m_imageFetched(false) {}
|
| -
|
| -ResourceFetcher::~ResourceFetcher() {}
|
| -
|
| -Resource* ResourceFetcher::cachedResource(const KURL& resourceURL) const {
|
| - KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(resourceURL);
|
| - const WeakMember<Resource>& resource = m_documentResources.get(url);
|
| - return resource.get();
|
| -}
|
| -
|
| -bool ResourceFetcher::isControlledByServiceWorker() const {
|
| - return context().isControlledByServiceWorker();
|
| -}
|
| -
|
| -bool ResourceFetcher::resourceNeedsLoad(Resource* resource,
|
| - const FetchRequest& request,
|
| - RevalidationPolicy policy) {
|
| - // Defer a font load until it is actually needed unless this is a preload.
|
| - if (resource->getType() == Resource::Font && !request.forPreload())
|
| - return false;
|
| - if (resource->isImage() && shouldDeferImageLoad(resource->url()))
|
| - return false;
|
| - return policy != Use || resource->stillNeedsLoad();
|
| -}
|
| -
|
| -// Limit the number of URLs in m_validatedURLs to avoid memory bloat.
|
| -// http://crbug.com/52411
|
| -static const int kMaxValidatedURLsSize = 10000;
|
| -
|
| -void ResourceFetcher::requestLoadStarted(unsigned long identifier,
|
| - Resource* resource,
|
| - const FetchRequest& request,
|
| - ResourceLoadStartType type,
|
| - bool isStaticData) {
|
| - if (type == ResourceLoadingFromCache &&
|
| - resource->getStatus() == Resource::Cached &&
|
| - !m_validatedURLs.contains(resource->url())) {
|
| - context().dispatchDidLoadResourceFromMemoryCache(
|
| - identifier, resource, request.resourceRequest().frameType(),
|
| - request.resourceRequest().requestContext());
|
| - }
|
| -
|
| - if (isStaticData)
|
| - return;
|
| -
|
| - if (type == ResourceLoadingFromCache && !resource->stillNeedsLoad() &&
|
| - !m_validatedURLs.contains(request.resourceRequest().url())) {
|
| - // Resources loaded from memory cache should be reported the first time
|
| - // they're used.
|
| - std::unique_ptr<ResourceTimingInfo> info = ResourceTimingInfo::create(
|
| - request.options().initiatorInfo.name, monotonicallyIncreasingTime(),
|
| - resource->getType() == Resource::MainResource);
|
| - populateTimingInfo(info.get(), resource);
|
| - info->clearLoadTimings();
|
| - info->setLoadFinishTime(info->initialTime());
|
| - m_scheduledResourceTimingReports.push_back(std::move(info));
|
| - if (!m_resourceTimingReportTimer.isActive())
|
| - m_resourceTimingReportTimer.startOneShot(0, BLINK_FROM_HERE);
|
| - }
|
| -
|
| - if (m_validatedURLs.size() >= kMaxValidatedURLsSize) {
|
| - m_validatedURLs.clear();
|
| - }
|
| - m_validatedURLs.add(request.resourceRequest().url());
|
| -}
|
| -
|
| -static std::unique_ptr<TracedValue> urlForTraceEvent(const KURL& url) {
|
| - std::unique_ptr<TracedValue> value = TracedValue::create();
|
| - value->setString("url", url.getString());
|
| - return value;
|
| -}
|
| -
|
| -Resource* ResourceFetcher::resourceForStaticData(
|
| - const FetchRequest& request,
|
| - const ResourceFactory& factory,
|
| - const SubstituteData& substituteData) {
|
| - const KURL& url = request.resourceRequest().url();
|
| - DCHECK(url.protocolIsData() || substituteData.isValid() || m_archive);
|
| -
|
| - // TODO(japhet): We only send main resource data: urls through WebURLLoader
|
| - // for the benefit of a service worker test
|
| - // (RenderViewImplTest.ServiceWorkerNetworkProviderSetup), which is at a layer
|
| - // where it isn't easy to mock out a network load. It uses data: urls to
|
| - // emulate the behavior it wants to test, which would otherwise be reserved
|
| - // for network loads.
|
| - if (!m_archive && !substituteData.isValid() &&
|
| - (factory.type() == Resource::MainResource ||
|
| - factory.type() == Resource::Raw))
|
| - return nullptr;
|
| -
|
| - const String cacheIdentifier = getCacheIdentifier();
|
| - if (Resource* oldResource =
|
| - memoryCache()->resourceForURL(url, cacheIdentifier)) {
|
| - // There's no reason to re-parse if we saved the data from the previous
|
| - // parse.
|
| - if (request.options().dataBufferingPolicy != DoNotBufferData)
|
| - return oldResource;
|
| - memoryCache()->remove(oldResource);
|
| - }
|
| -
|
| - AtomicString mimetype;
|
| - AtomicString charset;
|
| - RefPtr<SharedBuffer> data;
|
| - if (substituteData.isValid()) {
|
| - mimetype = substituteData.mimeType();
|
| - charset = substituteData.textEncoding();
|
| - data = substituteData.content();
|
| - } else if (url.protocolIsData()) {
|
| - data = PassRefPtr<SharedBuffer>(
|
| - NetworkUtils::parseDataURL(url, mimetype, charset));
|
| - if (!data)
|
| - return nullptr;
|
| - } else {
|
| - ArchiveResource* archiveResource =
|
| - m_archive->subresourceForURL(request.url());
|
| - // Fall back to the network if the archive doesn't contain the resource.
|
| - if (!archiveResource)
|
| - return nullptr;
|
| - mimetype = archiveResource->mimeType();
|
| - charset = archiveResource->textEncoding();
|
| - data = archiveResource->data();
|
| - }
|
| -
|
| - ResourceResponse response(url, mimetype, data->size(), charset, String());
|
| - response.setHTTPStatusCode(200);
|
| - response.setHTTPStatusText("OK");
|
| -
|
| - Resource* resource = factory.create(request.resourceRequest(),
|
| - request.options(), request.charset());
|
| - resource->setNeedsSynchronousCacheHit(substituteData.forceSynchronousLoad());
|
| - // FIXME: We should provide a body stream here.
|
| - resource->responseReceived(response, nullptr);
|
| - resource->setDataBufferingPolicy(BufferData);
|
| - if (data->size())
|
| - resource->setResourceBuffer(data);
|
| - resource->setIdentifier(createUniqueIdentifier());
|
| - resource->setCacheIdentifier(cacheIdentifier);
|
| - resource->finish();
|
| -
|
| - if (!substituteData.isValid())
|
| - memoryCache()->add(resource);
|
| -
|
| - return resource;
|
| -}
|
| -
|
| -Resource* ResourceFetcher::resourceForBlockedRequest(
|
| - const FetchRequest& request,
|
| - const ResourceFactory& factory,
|
| - ResourceRequestBlockedReason blockedReason) {
|
| - Resource* resource = factory.create(request.resourceRequest(),
|
| - request.options(), request.charset());
|
| - resource->error(ResourceError::cancelledDueToAccessCheckError(request.url(),
|
| - blockedReason));
|
| - return resource;
|
| -}
|
| -
|
| -void ResourceFetcher::moveCachedNonBlockingResourceToBlocking(
|
| - Resource* resource,
|
| - const FetchRequest& request) {
|
| - // TODO(yoav): Test that non-blocking resources (video/audio/track) continue
|
| - // to not-block even after being preloaded and discovered.
|
| - if (resource && resource->loader() &&
|
| - resource->isLoadEventBlockingResourceType() &&
|
| - m_nonBlockingLoaders.contains(resource->loader()) &&
|
| - resource->isLinkPreload() && !request.forPreload()) {
|
| - m_nonBlockingLoaders.remove(resource->loader());
|
| - m_loaders.add(resource->loader());
|
| - }
|
| -}
|
| -
|
| -void ResourceFetcher::updateMemoryCacheStats(Resource* resource,
|
| - RevalidationPolicy policy,
|
| - const FetchRequest& request,
|
| - const ResourceFactory& factory,
|
| - bool isStaticData) const {
|
| - if (isStaticData)
|
| - return;
|
| -
|
| - if (request.forPreload()) {
|
| - DEFINE_RESOURCE_HISTOGRAM("Preload.");
|
| - } else {
|
| - DEFINE_RESOURCE_HISTOGRAM("");
|
| - }
|
| -
|
| - // Aims to count Resource only referenced from MemoryCache (i.e. what would be
|
| - // dead if MemoryCache holds weak references to Resource). Currently we check
|
| - // references to Resource from ResourceClient and |m_preloads| only, because
|
| - // they are major sources of references.
|
| - if (resource && !resource->isAlive() &&
|
| - (!m_preloads || !m_preloads->contains(resource))) {
|
| - DEFINE_RESOURCE_HISTOGRAM("Dead.");
|
| - }
|
| -}
|
| -
|
| -Resource* ResourceFetcher::requestResource(
|
| - FetchRequest& request,
|
| - const ResourceFactory& factory,
|
| - const SubstituteData& substituteData) {
|
| - ResourceRequest& resourceRequest = request.mutableResourceRequest();
|
| -
|
| - unsigned long identifier = createUniqueIdentifier();
|
| - network_instrumentation::ScopedResourceLoadTracker scopedResourceLoadTracker(
|
| - identifier, resourceRequest);
|
| - SCOPED_BLINK_UMA_HISTOGRAM_TIMER("Blink.Fetch.RequestResourceTime");
|
| - DCHECK(request.options().synchronousPolicy == RequestAsynchronously ||
|
| - factory.type() == Resource::Raw ||
|
| - factory.type() == Resource::XSLStyleSheet);
|
| -
|
| - context().populateResourceRequest(
|
| - factory.type(), request.clientHintsPreferences(),
|
| - request.getResourceWidth(), resourceRequest);
|
| -
|
| - // TODO(dproy): Remove this. http://crbug.com/659666
|
| - TRACE_EVENT1("blink", "ResourceFetcher::requestResource", "url",
|
| - urlForTraceEvent(request.url()));
|
| -
|
| - if (!request.url().isValid())
|
| - return nullptr;
|
| -
|
| - resourceRequest.setPriority(computeLoadPriority(
|
| - factory.type(), request, ResourcePriority::NotVisible));
|
| - initializeResourceRequest(resourceRequest, factory.type(), request.defer());
|
| - network_instrumentation::resourcePrioritySet(identifier,
|
| - resourceRequest.priority());
|
| -
|
| - ResourceRequestBlockedReason blockedReason = context().canRequest(
|
| - factory.type(), resourceRequest,
|
| - MemoryCache::removeFragmentIdentifierIfNeeded(request.url()),
|
| - request.options(), request.forPreload(), request.getOriginRestriction());
|
| - if (blockedReason != ResourceRequestBlockedReason::None) {
|
| - DCHECK(!substituteData.forceSynchronousLoad());
|
| - return resourceForBlockedRequest(request, factory, blockedReason);
|
| - }
|
| -
|
| - context().willStartLoadingResource(
|
| - identifier, resourceRequest, factory.type(),
|
| - request.options().initiatorInfo.name, request.forPreload());
|
| - if (!request.url().isValid())
|
| - return nullptr;
|
| -
|
| - bool isDataUrl = resourceRequest.url().protocolIsData();
|
| - bool isStaticData = isDataUrl || substituteData.isValid() || m_archive;
|
| - Resource* resource(nullptr);
|
| - if (isStaticData) {
|
| - resource = resourceForStaticData(request, factory, substituteData);
|
| - // Abort the request if the archive doesn't contain the resource, except in
|
| - // the case of data URLs which might have resources such as fonts that need
|
| - // to be decoded only on demand. These data URLs are allowed to be
|
| - // processed using the normal ResourceFetcher machinery.
|
| - if (!resource && !isDataUrl && m_archive)
|
| - return nullptr;
|
| - }
|
| - if (!resource) {
|
| - resource =
|
| - memoryCache()->resourceForURL(request.url(), getCacheIdentifier());
|
| - }
|
| -
|
| - // See if we can use an existing resource from the cache. If so, we need to
|
| - // move it to be load blocking.
|
| - moveCachedNonBlockingResourceToBlocking(resource, request);
|
| -
|
| - const RevalidationPolicy policy = determineRevalidationPolicy(
|
| - factory.type(), request, resource, isStaticData);
|
| - TRACE_EVENT_INSTANT1("blink", "ResourceFetcher::determineRevalidationPolicy",
|
| - TRACE_EVENT_SCOPE_THREAD, "revalidationPolicy", policy);
|
| -
|
| - updateMemoryCacheStats(resource, policy, request, factory, isStaticData);
|
| -
|
| - resourceRequest.setAllowStoredCredentials(
|
| - request.options().allowCredentials == AllowStoredCredentials);
|
| -
|
| - switch (policy) {
|
| - case Reload:
|
| - memoryCache()->remove(resource);
|
| - // Fall through
|
| - case Load:
|
| - resource = createResourceForLoading(request, request.charset(), factory);
|
| - break;
|
| - case Revalidate:
|
| - initializeRevalidation(resourceRequest, resource);
|
| - break;
|
| - case Use:
|
| - if (resource->isLinkPreload() && !request.isLinkPreload())
|
| - resource->setLinkPreload(false);
|
| - break;
|
| - }
|
| - if (!resource)
|
| - return nullptr;
|
| - if (resource->getType() != factory.type()) {
|
| - DCHECK(request.forPreload());
|
| - return nullptr;
|
| - }
|
| -
|
| - if (!resource->isAlive())
|
| - m_deadStatsRecorder.update(policy);
|
| -
|
| - if (policy != Use)
|
| - resource->setIdentifier(identifier);
|
| -
|
| - if (!request.forPreload() || policy != Use) {
|
| - // When issuing another request for a resource that is already in-flight
|
| - // make sure to not demote the priority of the in-flight request. If the new
|
| - // request isn't at the same priority as the in-flight request, only allow
|
| - // promotions. This can happen when a visible image's priority is increased
|
| - // and then another reference to the image is parsed (which would be at a
|
| - // lower priority).
|
| - if (resourceRequest.priority() > resource->resourceRequest().priority())
|
| - resource->didChangePriority(resourceRequest.priority(), 0);
|
| - }
|
| -
|
| - // If only the fragment identifiers differ, it is the same resource.
|
| - DCHECK(equalIgnoringFragmentIdentifier(resource->url(), request.url()));
|
| - requestLoadStarted(
|
| - identifier, resource, request,
|
| - policy == Use ? ResourceLoadingFromCache : ResourceLoadingFromNetwork,
|
| - isStaticData);
|
| - m_documentResources.set(
|
| - MemoryCache::removeFragmentIdentifierIfNeeded(request.url()), resource);
|
| -
|
| - // Returns with an existing resource if the resource does not need to start
|
| - // loading immediately. If revalidation policy was determined as |Revalidate|,
|
| - // the resource was already initialized for the revalidation here, but won't
|
| - // start loading.
|
| - if (!resourceNeedsLoad(resource, request, policy))
|
| - return resource;
|
| -
|
| - if (!startLoad(resource))
|
| - return nullptr;
|
| - scopedResourceLoadTracker.resourceLoadContinuesBeyondScope();
|
| -
|
| - DCHECK(!resource->errorOccurred() ||
|
| - request.options().synchronousPolicy == RequestSynchronously);
|
| - return resource;
|
| -}
|
| -
|
| -void ResourceFetcher::resourceTimingReportTimerFired(TimerBase* timer) {
|
| - DCHECK_EQ(timer, &m_resourceTimingReportTimer);
|
| - Vector<std::unique_ptr<ResourceTimingInfo>> timingReports;
|
| - timingReports.swap(m_scheduledResourceTimingReports);
|
| - for (const auto& timingInfo : timingReports)
|
| - context().addResourceTiming(*timingInfo);
|
| -}
|
| -
|
| -void ResourceFetcher::determineRequestContext(ResourceRequest& request,
|
| - Resource::Type type,
|
| - bool isMainFrame) {
|
| - WebURLRequest::RequestContext requestContext =
|
| - requestContextFromType(isMainFrame, type);
|
| - request.setRequestContext(requestContext);
|
| -}
|
| -
|
| -void ResourceFetcher::determineRequestContext(ResourceRequest& request,
|
| - Resource::Type type) {
|
| - determineRequestContext(request, type, context().isMainFrame());
|
| -}
|
| -
|
| -void ResourceFetcher::initializeResourceRequest(
|
| - ResourceRequest& request,
|
| - Resource::Type type,
|
| - FetchRequest::DeferOption defer) {
|
| - if (request.getCachePolicy() == WebCachePolicy::UseProtocolCachePolicy) {
|
| - request.setCachePolicy(
|
| - context().resourceRequestCachePolicy(request, type, defer));
|
| - }
|
| - if (request.requestContext() == WebURLRequest::RequestContextUnspecified)
|
| - determineRequestContext(request, type);
|
| - if (type == Resource::LinkPrefetch)
|
| - request.setHTTPHeaderField(HTTPNames::Purpose, "prefetch");
|
| -
|
| - context().addAdditionalRequestHeaders(
|
| - request,
|
| - (type == Resource::MainResource) ? FetchMainResource : FetchSubresource);
|
| -}
|
| -
|
| -void ResourceFetcher::initializeRevalidation(
|
| - ResourceRequest& revalidatingRequest,
|
| - Resource* resource) {
|
| - DCHECK(resource);
|
| - DCHECK(memoryCache()->contains(resource));
|
| - DCHECK(resource->isLoaded());
|
| - DCHECK(resource->canUseCacheValidator());
|
| - DCHECK(!resource->isCacheValidator());
|
| - DCHECK(!context().isControlledByServiceWorker());
|
| -
|
| - const AtomicString& lastModified =
|
| - resource->response().httpHeaderField(HTTPNames::Last_Modified);
|
| - const AtomicString& eTag =
|
| - resource->response().httpHeaderField(HTTPNames::ETag);
|
| - if (!lastModified.isEmpty() || !eTag.isEmpty()) {
|
| - DCHECK_NE(context().getCachePolicy(), CachePolicyReload);
|
| - if (context().getCachePolicy() == CachePolicyRevalidate) {
|
| - revalidatingRequest.setHTTPHeaderField(HTTPNames::Cache_Control,
|
| - "max-age=0");
|
| - }
|
| - }
|
| - if (!lastModified.isEmpty()) {
|
| - revalidatingRequest.setHTTPHeaderField(HTTPNames::If_Modified_Since,
|
| - lastModified);
|
| - }
|
| - if (!eTag.isEmpty())
|
| - revalidatingRequest.setHTTPHeaderField(HTTPNames::If_None_Match, eTag);
|
| -
|
| - double stalenessLifetime = resource->stalenessLifetime();
|
| - if (std::isfinite(stalenessLifetime) && stalenessLifetime > 0) {
|
| - revalidatingRequest.setHTTPHeaderField(
|
| - HTTPNames::Resource_Freshness,
|
| - AtomicString(String::format(
|
| - "max-age=%.0lf,stale-while-revalidate=%.0lf,age=%.0lf",
|
| - resource->freshnessLifetime(), stalenessLifetime,
|
| - resource->currentAge())));
|
| - }
|
| -
|
| - resource->setRevalidatingRequest(revalidatingRequest);
|
| -}
|
| -
|
| -Resource* ResourceFetcher::createResourceForLoading(
|
| - FetchRequest& request,
|
| - const String& charset,
|
| - const ResourceFactory& factory) {
|
| - const String cacheIdentifier = getCacheIdentifier();
|
| - DCHECK(!memoryCache()->resourceForURL(request.resourceRequest().url(),
|
| - cacheIdentifier));
|
| -
|
| - RESOURCE_LOADING_DVLOG(1) << "Loading Resource for "
|
| - << request.resourceRequest().url().elidedString();
|
| -
|
| - Resource* resource =
|
| - factory.create(request.resourceRequest(), request.options(), charset);
|
| - resource->setLinkPreload(request.isLinkPreload());
|
| - if (request.forPreload()) {
|
| - resource->setPreloadDiscoveryTime(request.preloadDiscoveryTime());
|
| - }
|
| - resource->setCacheIdentifier(cacheIdentifier);
|
| -
|
| - // - Don't add main resource to cache to prevent reuse.
|
| - // - Don't add the resource if its body will not be stored.
|
| - if (factory.type() != Resource::MainResource &&
|
| - request.options().dataBufferingPolicy != DoNotBufferData) {
|
| - memoryCache()->add(resource);
|
| - }
|
| - return resource;
|
| -}
|
| -
|
| -void ResourceFetcher::storePerformanceTimingInitiatorInformation(
|
| - Resource* resource) {
|
| - const AtomicString& fetchInitiator = resource->options().initiatorInfo.name;
|
| - if (fetchInitiator == FetchInitiatorTypeNames::internal)
|
| - return;
|
| -
|
| - bool isMainResource = resource->getType() == Resource::MainResource;
|
| -
|
| - // The request can already be fetched in a previous navigation. Thus
|
| - // startTime must be set accordingly.
|
| - double startTime = resource->resourceRequest().navigationStartTime()
|
| - ? resource->resourceRequest().navigationStartTime()
|
| - : monotonicallyIncreasingTime();
|
| -
|
| - // This buffer is created and populated for providing transferSize
|
| - // and redirect timing opt-in information.
|
| - if (isMainResource) {
|
| - DCHECK(!m_navigationTimingInfo);
|
| - m_navigationTimingInfo =
|
| - ResourceTimingInfo::create(fetchInitiator, startTime, isMainResource);
|
| - }
|
| -
|
| - std::unique_ptr<ResourceTimingInfo> info =
|
| - ResourceTimingInfo::create(fetchInitiator, startTime, isMainResource);
|
| -
|
| - if (resource->isCacheValidator()) {
|
| - const AtomicString& timingAllowOrigin =
|
| - resource->response().httpHeaderField(HTTPNames::Timing_Allow_Origin);
|
| - if (!timingAllowOrigin.isEmpty())
|
| - info->setOriginalTimingAllowOrigin(timingAllowOrigin);
|
| - }
|
| -
|
| - if (!isMainResource ||
|
| - context().updateTimingInfoForIFrameNavigation(info.get())) {
|
| - m_resourceTimingInfoMap.add(resource, std::move(info));
|
| - }
|
| -}
|
| -
|
| -void ResourceFetcher::recordResourceTimingOnRedirect(
|
| - Resource* resource,
|
| - const ResourceResponse& redirectResponse,
|
| - bool crossOrigin) {
|
| - ResourceTimingInfoMap::iterator it = m_resourceTimingInfoMap.find(resource);
|
| - if (it != m_resourceTimingInfoMap.end()) {
|
| - it->value->addRedirect(redirectResponse, crossOrigin);
|
| - }
|
| -
|
| - if (resource->getType() == Resource::MainResource) {
|
| - DCHECK(m_navigationTimingInfo);
|
| - m_navigationTimingInfo->addRedirect(redirectResponse, crossOrigin);
|
| - }
|
| -}
|
| -
|
| -ResourceFetcher::RevalidationPolicy
|
| -ResourceFetcher::determineRevalidationPolicy(Resource::Type type,
|
| - const FetchRequest& fetchRequest,
|
| - Resource* existingResource,
|
| - bool isStaticData) const {
|
| - const ResourceRequest& request = fetchRequest.resourceRequest();
|
| -
|
| - if (!existingResource)
|
| - return Load;
|
| -
|
| - // Checks if the resource has an explicit policy about integrity metadata.
|
| - //
|
| - // This is necessary because ScriptResource and CSSStyleSheetResource objects
|
| - // do not keep the raw data around after the source is accessed once, so if
|
| - // the resource is accessed from the MemoryCache for a second time, there is
|
| - // no way to redo an integrity check.
|
| - //
|
| - // Thus, Blink implements a scheme where it caches the integrity information
|
| - // for those resources after the first time it is checked, and if there is
|
| - // another request for that resource, with the same integrity metadata, Blink
|
| - // skips the integrity calculation. However, if the integrity metadata is a
|
| - // mismatch, the MemoryCache must be skipped here, and a new request for the
|
| - // resource must be made to get the raw data. This is expected to be an
|
| - // uncommon case, however, as it implies two same-origin requests to the same
|
| - // resource, but with different integrity metadata.
|
| - RecordSriResourceIntegrityMismatchEvent(CheckingForIntegrityMismatch);
|
| - if (existingResource->mustRefetchDueToIntegrityMetadata(fetchRequest)) {
|
| - RecordSriResourceIntegrityMismatchEvent(RefetchDueToIntegrityMismatch);
|
| - return Reload;
|
| - }
|
| -
|
| - // Service Worker's CORS fallback message must not be cached.
|
| - if (existingResource->response().wasFallbackRequiredByServiceWorker())
|
| - return Reload;
|
| -
|
| - // We already have a preload going for this URL.
|
| - if (fetchRequest.forPreload() && existingResource->isPreloaded())
|
| - return Use;
|
| -
|
| - // If the same URL has been loaded as a different type, we need to reload.
|
| - if (existingResource->getType() != type) {
|
| - // FIXME: If existingResource is a Preload and the new type is LinkPrefetch
|
| - // We really should discard the new prefetch since the preload has more
|
| - // specific type information! crbug.com/379893
|
| - // fast/dom/HTMLLinkElement/link-and-subresource-test hits this case.
|
| - RESOURCE_LOADING_DVLOG(1) << "ResourceFetcher::determineRevalidationPolicy "
|
| - "reloading due to type mismatch.";
|
| - return Reload;
|
| - }
|
| -
|
| - // Do not load from cache if images are not enabled. There are two general
|
| - // cases:
|
| - //
|
| - // 1. Images are disabled. Don't ever load images, even if the image is cached
|
| - // or it is a data: url. In this case, we "Reload" the image, then defer it
|
| - // with resourceNeedsLoad() so that it never actually goes to the network.
|
| - //
|
| - // 2. Images are enabled, but not loaded automatically. In this case, we will
|
| - // Use cached resources or data: urls, but will similarly fall back to a
|
| - // deferred network load if we don't have the data available without a network
|
| - // request. We check allowImage() here, which is affected by m_imagesEnabled
|
| - // but not m_autoLoadImages, in order to allow for this differing behavior.
|
| - //
|
| - // TODO(japhet): Can we get rid of one of these settings?
|
| - if (existingResource->isImage() &&
|
| - !context().allowImage(m_imagesEnabled, existingResource->url()))
|
| - return Reload;
|
| -
|
| - // Never use cache entries for downloadToFile / useStreamOnResponse requests.
|
| - // The data will be delivered through other paths.
|
| - if (request.downloadToFile() || request.useStreamOnResponse())
|
| - return Reload;
|
| -
|
| - // Never reuse opaque responses from a service worker for requests that are
|
| - // not no-cors. https://crbug.com/625575
|
| - if (existingResource->response().wasFetchedViaServiceWorker() &&
|
| - existingResource->response().serviceWorkerResponseType() ==
|
| - WebServiceWorkerResponseTypeOpaque &&
|
| - request.fetchRequestMode() != WebURLRequest::FetchRequestModeNoCORS)
|
| - return Reload;
|
| -
|
| - // If resource was populated from a SubstituteData load or data: url, use it.
|
| - if (isStaticData)
|
| - return Use;
|
| -
|
| - if (!existingResource->canReuse(request))
|
| - return Reload;
|
| -
|
| - // Certain requests (e.g., XHRs) might have manually set headers that require
|
| - // revalidation. 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.
|
| - //
|
| - // Similarly, a request with manually added revalidation headers can lead to a
|
| - // 304 response for a request that wasn't flagged as a revalidation attempt.
|
| - // Normally, successful revalidation will maintain the original response's
|
| - // status code, but for a manual revalidation the response code remains 304.
|
| - // In this case, the Resource likely has insufficient context to provide a
|
| - // useful cache hit or revalidation. See http://crbug.com/643659
|
| - if (request.isConditional() ||
|
| - existingResource->response().httpStatusCode() == 304)
|
| - return Reload;
|
| -
|
| - // Don't reload resources while pasting.
|
| - if (m_allowStaleResources)
|
| - return Use;
|
| -
|
| - if (!fetchRequest.options().canReuseRequest(existingResource->options()))
|
| - return Reload;
|
| -
|
| - // Always use preloads.
|
| - if (existingResource->isPreloaded())
|
| - return Use;
|
| -
|
| - // CachePolicyHistoryBuffer uses the cache no matter what.
|
| - CachePolicy cachePolicy = context().getCachePolicy();
|
| - if (cachePolicy == CachePolicyHistoryBuffer)
|
| - return Use;
|
| -
|
| - // Don't reuse resources with Cache-control: no-store.
|
| - if (existingResource->hasCacheControlNoStoreHeader()) {
|
| - RESOURCE_LOADING_DVLOG(1) << "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()) {
|
| - RESOURCE_LOADING_DVLOG(1) << "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 (!context().isLoadComplete() &&
|
| - m_validatedURLs.contains(existingResource->url()))
|
| - return Use;
|
| - if (existingResource->isLoading())
|
| - return Use;
|
| - }
|
| -
|
| - if (request.getCachePolicy() == WebCachePolicy::BypassingCache)
|
| - return Reload;
|
| -
|
| - // CachePolicyReload always reloads
|
| - if (cachePolicy == CachePolicyReload) {
|
| - RESOURCE_LOADING_DVLOG(1) << "ResourceFetcher::determineRevalidationPolicy "
|
| - "reloading due to CachePolicyReload.";
|
| - return Reload;
|
| - }
|
| -
|
| - // We'll try to reload the resource if it failed last time.
|
| - if (existingResource->errorOccurred()) {
|
| - RESOURCE_LOADING_DVLOG(1) << "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;
|
| -
|
| - // Defer to the browser process cache for Vary header handling.
|
| - if (existingResource->hasVaryHeader())
|
| - return Reload;
|
| -
|
| - // If any of the redirects in the chain to loading the resource were not
|
| - // cacheable, we cannot reuse our cached resource.
|
| - if (!existingResource->canReuseRedirectChain()) {
|
| - RESOURCE_LOADING_DVLOG(1) << "ResourceFetcher::determineRevalidationPolicy "
|
| - "reloading due to an uncacheable redirect";
|
| - return Reload;
|
| - }
|
| -
|
| - // 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 the page
|
| - // is controlled by the ServiceWorker, we choose the Reload policy because
|
| - // the revalidation headers should not be exposed to the
|
| - // ServiceWorker.(crbug.com/429570)
|
| - if (existingResource->canUseCacheValidator() &&
|
| - !context().isControlledByServiceWorker()) {
|
| - // If the resource is already a cache validator but not started yet, the
|
| - // |Use| policy should be applied to subsequent requests.
|
| - if (existingResource->isCacheValidator()) {
|
| - DCHECK(existingResource->stillNeedsLoad());
|
| - return Use;
|
| - }
|
| - return Revalidate;
|
| - }
|
| -
|
| - // No, must reload.
|
| - RESOURCE_LOADING_DVLOG(1) << "ResourceFetcher::determineRevalidationPolicy "
|
| - "reloading due to missing cache validators.";
|
| - return Reload;
|
| - }
|
| -
|
| - return Use;
|
| -}
|
| -
|
| -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::shouldDeferImageLoad(const KURL& url) const {
|
| - return !context().allowImage(m_imagesEnabled, url) || !m_autoLoadImages;
|
| -}
|
| -
|
| -void ResourceFetcher::reloadImagesIfNotDeferred() {
|
| - for (Resource* resource : m_documentResources.values()) {
|
| - if (resource->getType() == Resource::Image && resource->stillNeedsLoad() &&
|
| - !shouldDeferImageLoad(resource->url()))
|
| - startLoad(resource);
|
| - }
|
| -}
|
| -
|
| -void ResourceFetcher::clearContext() {
|
| - clearPreloads(ResourceFetcher::ClearAllPreloads);
|
| - m_context.clear();
|
| -}
|
| -
|
| -int ResourceFetcher::requestCount() const {
|
| - return m_loaders.size();
|
| -}
|
| -
|
| -bool ResourceFetcher::hasPendingRequest() const {
|
| - return m_loaders.size() > 0 || m_nonBlockingLoaders.size() > 0;
|
| -}
|
| -
|
| -void ResourceFetcher::preloadStarted(Resource* resource) {
|
| - if (m_preloads && m_preloads->contains(resource))
|
| - return;
|
| - resource->increasePreloadCount();
|
| -
|
| - if (!m_preloads)
|
| - m_preloads = new HeapListHashSet<Member<Resource>>;
|
| - m_preloads->add(resource);
|
| -
|
| - if (m_preloadedURLsForTest)
|
| - m_preloadedURLsForTest->add(resource->url().getString());
|
| -}
|
| -
|
| -void ResourceFetcher::enableIsPreloadedForTest() {
|
| - if (m_preloadedURLsForTest)
|
| - return;
|
| - m_preloadedURLsForTest = WTF::wrapUnique(new HashSet<String>);
|
| -
|
| - if (m_preloads) {
|
| - for (const auto& resource : *m_preloads)
|
| - m_preloadedURLsForTest->add(resource->url().getString());
|
| - }
|
| -}
|
| -
|
| -bool ResourceFetcher::isPreloadedForTest(const KURL& url) const {
|
| - DCHECK(m_preloadedURLsForTest);
|
| - return m_preloadedURLsForTest->contains(url.getString());
|
| -}
|
| -
|
| -void ResourceFetcher::clearPreloads(ClearPreloadsPolicy policy) {
|
| - if (!m_preloads)
|
| - return;
|
| -
|
| - logPreloadStats(policy);
|
| -
|
| - for (const auto& resource : *m_preloads) {
|
| - if (policy == ClearAllPreloads || !resource->isLinkPreload()) {
|
| - resource->decreasePreloadCount();
|
| - if (resource->getPreloadResult() == Resource::PreloadNotReferenced)
|
| - memoryCache()->remove(resource.get());
|
| - m_preloads->remove(resource);
|
| - }
|
| - }
|
| - if (!m_preloads->size())
|
| - m_preloads.clear();
|
| -}
|
| -
|
| -void ResourceFetcher::warnUnusedPreloads() {
|
| - if (!m_preloads)
|
| - return;
|
| - for (const auto& resource : *m_preloads) {
|
| - if (resource && resource->isLinkPreload() &&
|
| - resource->getPreloadResult() == Resource::PreloadNotReferenced) {
|
| - context().addConsoleMessage(
|
| - "The resource " + resource->url().getString() +
|
| - " was preloaded using link preload but not used within a few "
|
| - "seconds from the window's load event. Please make sure it "
|
| - "wasn't preloaded for nothing.",
|
| - FetchContext::LogWarningMessage);
|
| - }
|
| - }
|
| -}
|
| -
|
| -ArchiveResource* ResourceFetcher::createArchive(Resource* resource) {
|
| - // Only the top-frame can load MHTML.
|
| - if (!context().isMainFrame())
|
| - return nullptr;
|
| - m_archive = MHTMLArchive::create(resource->url(), resource->resourceBuffer());
|
| - return m_archive ? m_archive->mainResource() : nullptr;
|
| -}
|
| -
|
| -ResourceTimingInfo* ResourceFetcher::getNavigationTimingInfo() {
|
| - return m_navigationTimingInfo.get();
|
| -}
|
| -
|
| -void ResourceFetcher::handleLoadCompletion(Resource* resource) {
|
| - context().didLoadResource(resource);
|
| -
|
| - resource->reloadIfLoFiOrPlaceholderImage(this, Resource::kReloadIfNeeded);
|
| -}
|
| -
|
| -void ResourceFetcher::handleLoaderFinish(Resource* resource,
|
| - double finishTime,
|
| - LoaderFinishType type) {
|
| - DCHECK(resource);
|
| -
|
| - ResourceLoader* loader = resource->loader();
|
| - if (type == DidFinishFirstPartInMultipart) {
|
| - // When loading a multipart resource, make the loader non-block when
|
| - // finishing loading the first part.
|
| - moveResourceLoaderToNonBlocking(loader);
|
| - } else {
|
| - removeResourceLoader(loader);
|
| - DCHECK(!m_nonBlockingLoaders.contains(loader));
|
| - }
|
| - DCHECK(!m_loaders.contains(loader));
|
| -
|
| - const int64_t encodedDataLength = resource->response().encodedDataLength();
|
| -
|
| - if (resource->getType() == Resource::MainResource) {
|
| - DCHECK(m_navigationTimingInfo);
|
| - // Store redirect responses that were packed inside the final response.
|
| - addRedirectsToTimingInfo(resource, m_navigationTimingInfo.get());
|
| - if (resource->response().isHTTP()) {
|
| - populateTimingInfo(m_navigationTimingInfo.get(), resource);
|
| - m_navigationTimingInfo->addFinalTransferSize(
|
| - encodedDataLength == -1 ? 0 : encodedDataLength);
|
| - }
|
| - }
|
| - if (std::unique_ptr<ResourceTimingInfo> info =
|
| - m_resourceTimingInfoMap.take(resource)) {
|
| - // Store redirect responses that were packed inside the final response.
|
| - addRedirectsToTimingInfo(resource, info.get());
|
| -
|
| - if (resource->response().isHTTP() &&
|
| - resource->response().httpStatusCode() < 400) {
|
| - populateTimingInfo(info.get(), resource);
|
| - info->setLoadFinishTime(finishTime);
|
| - // encodedDataLength == -1 means "not available".
|
| - // TODO(ricea): Find cases where it is not available but the
|
| - // PerformanceResourceTiming spec requires it to be available and fix
|
| - // them.
|
| - info->addFinalTransferSize(encodedDataLength == -1 ? 0
|
| - : encodedDataLength);
|
| -
|
| - if (resource->options().requestInitiatorContext == DocumentContext)
|
| - context().addResourceTiming(*info);
|
| - resource->reportResourceTimingToClients(*info);
|
| - }
|
| - }
|
| -
|
| - context().dispatchDidFinishLoading(resource->identifier(), finishTime,
|
| - encodedDataLength);
|
| -
|
| - if (type == DidFinishLoading)
|
| - resource->finish(finishTime);
|
| -
|
| - handleLoadCompletion(resource);
|
| -}
|
| -
|
| -void ResourceFetcher::handleLoaderError(Resource* resource,
|
| - const ResourceError& error) {
|
| - DCHECK(resource);
|
| -
|
| - removeResourceLoader(resource->loader());
|
| -
|
| - m_resourceTimingInfoMap.take(resource);
|
| -
|
| - bool isInternalRequest = resource->options().initiatorInfo.name ==
|
| - FetchInitiatorTypeNames::internal;
|
| -
|
| - context().dispatchDidFail(resource->identifier(), error,
|
| - resource->response().encodedDataLength(),
|
| - isInternalRequest);
|
| -
|
| - resource->error(error);
|
| -
|
| - handleLoadCompletion(resource);
|
| -}
|
| -
|
| -void ResourceFetcher::moveResourceLoaderToNonBlocking(ResourceLoader* loader) {
|
| - DCHECK(loader);
|
| - // TODO(yoav): Convert CHECK to DCHECK if no crash reports come in.
|
| - CHECK(m_loaders.contains(loader));
|
| - m_nonBlockingLoaders.add(loader);
|
| - m_loaders.remove(loader);
|
| -}
|
| -
|
| -bool ResourceFetcher::startLoad(Resource* resource) {
|
| - DCHECK(resource);
|
| - DCHECK(resource->stillNeedsLoad());
|
| - if (!context().shouldLoadNewResource(resource->getType())) {
|
| - memoryCache()->remove(resource);
|
| - return false;
|
| - }
|
| -
|
| - ResourceRequest request(resource->resourceRequest());
|
| - context().dispatchWillSendRequest(resource->identifier(), request,
|
| - ResourceResponse(),
|
| - resource->options().initiatorInfo);
|
| -
|
| - // TODO(shaochuan): Saving modified ResourceRequest back to |resource|, remove
|
| - // once dispatchWillSendRequest() takes const ResourceRequest.
|
| - // crbug.com/632580
|
| - resource->setResourceRequest(request);
|
| -
|
| - // Resource requests from suborigins should not be intercepted by the service
|
| - // worker of the physical origin. This has the effect that, for now,
|
| - // suborigins do not work with service workers. See
|
| - // https://w3c.github.io/webappsec-suborigins/.
|
| - SecurityOrigin* sourceOrigin = context().getSecurityOrigin();
|
| - if (sourceOrigin && sourceOrigin->hasSuborigin())
|
| - request.setSkipServiceWorker(WebURLRequest::SkipServiceWorker::All);
|
| -
|
| - ResourceLoader* loader = ResourceLoader::create(this, resource);
|
| - if (resource->shouldBlockLoadEvent())
|
| - m_loaders.add(loader);
|
| - else
|
| - m_nonBlockingLoaders.add(loader);
|
| -
|
| - storePerformanceTimingInitiatorInformation(resource);
|
| - resource->setFetcherSecurityOrigin(sourceOrigin);
|
| -
|
| - loader->activateCacheAwareLoadingIfNeeded(request);
|
| - loader->start(request);
|
| - return true;
|
| -}
|
| -
|
| -void ResourceFetcher::removeResourceLoader(ResourceLoader* loader) {
|
| - DCHECK(loader);
|
| - if (m_loaders.contains(loader))
|
| - m_loaders.remove(loader);
|
| - else if (m_nonBlockingLoaders.contains(loader))
|
| - m_nonBlockingLoaders.remove(loader);
|
| - else
|
| - NOTREACHED();
|
| -}
|
| -
|
| -void ResourceFetcher::stopFetching() {
|
| - HeapVector<Member<ResourceLoader>> loadersToCancel;
|
| - for (const auto& loader : m_nonBlockingLoaders)
|
| - loadersToCancel.push_back(loader);
|
| - for (const auto& loader : m_loaders)
|
| - loadersToCancel.push_back(loader);
|
| -
|
| - for (const auto& loader : loadersToCancel) {
|
| - if (m_loaders.contains(loader) || m_nonBlockingLoaders.contains(loader))
|
| - loader->cancel();
|
| - }
|
| -}
|
| -
|
| -bool ResourceFetcher::isFetching() const {
|
| - return !m_loaders.isEmpty();
|
| -}
|
| -
|
| -void ResourceFetcher::setDefersLoading(bool defers) {
|
| - for (const auto& loader : m_nonBlockingLoaders)
|
| - loader->setDefersLoading(defers);
|
| - for (const auto& loader : m_loaders)
|
| - loader->setDefersLoading(defers);
|
| -}
|
| -
|
| -void ResourceFetcher::updateAllImageResourcePriorities() {
|
| - TRACE_EVENT0(
|
| - "blink",
|
| - "ResourceLoadPriorityOptimizer::updateAllImageResourcePriorities");
|
| - for (const auto& documentResource : m_documentResources) {
|
| - Resource* resource = documentResource.value.get();
|
| - if (!resource || !resource->isImage() || !resource->isLoading())
|
| - continue;
|
| -
|
| - ResourcePriority resourcePriority = resource->priorityFromObservers();
|
| - ResourceLoadPriority resourceLoadPriority = computeLoadPriority(
|
| - Resource::Image,
|
| - FetchRequest(resource->resourceRequest(), FetchInitiatorInfo()),
|
| - resourcePriority.visibility);
|
| - if (resourceLoadPriority == resource->resourceRequest().priority())
|
| - continue;
|
| -
|
| - resource->didChangePriority(resourceLoadPriority,
|
| - resourcePriority.intraPriorityValue);
|
| - network_instrumentation::resourcePrioritySet(resource->identifier(),
|
| - resourceLoadPriority);
|
| - context().dispatchDidChangeResourcePriority(
|
| - resource->identifier(), resourceLoadPriority,
|
| - resourcePriority.intraPriorityValue);
|
| - }
|
| -}
|
| -
|
| -void ResourceFetcher::reloadLoFiImages() {
|
| - for (const auto& documentResource : m_documentResources) {
|
| - Resource* resource = documentResource.value.get();
|
| - if (resource)
|
| - resource->reloadIfLoFiOrPlaceholderImage(this, Resource::kReloadAlways);
|
| - }
|
| -}
|
| -
|
| -void ResourceFetcher::logPreloadStats(ClearPreloadsPolicy policy) {
|
| - if (!m_preloads)
|
| - return;
|
| - unsigned scripts = 0;
|
| - unsigned scriptMisses = 0;
|
| - unsigned stylesheets = 0;
|
| - unsigned stylesheetMisses = 0;
|
| - unsigned images = 0;
|
| - unsigned imageMisses = 0;
|
| - unsigned fonts = 0;
|
| - unsigned fontMisses = 0;
|
| - unsigned medias = 0;
|
| - unsigned mediaMisses = 0;
|
| - unsigned textTracks = 0;
|
| - unsigned textTrackMisses = 0;
|
| - unsigned imports = 0;
|
| - unsigned importMisses = 0;
|
| - unsigned raws = 0;
|
| - unsigned rawMisses = 0;
|
| - for (const auto& resource : *m_preloads) {
|
| - // Do not double count link rel preloads. These do not get cleared if the
|
| - // ClearPreloadsPolicy is only clearing speculative markup preloads.
|
| - if (resource->isLinkPreload() && policy == ClearSpeculativeMarkupPreloads) {
|
| - continue;
|
| - }
|
| - int missCount =
|
| - resource->getPreloadResult() == Resource::PreloadNotReferenced ? 1 : 0;
|
| - switch (resource->getType()) {
|
| - case Resource::Image:
|
| - images++;
|
| - imageMisses += missCount;
|
| - break;
|
| - case Resource::Script:
|
| - scripts++;
|
| - scriptMisses += missCount;
|
| - break;
|
| - case Resource::CSSStyleSheet:
|
| - stylesheets++;
|
| - stylesheetMisses += missCount;
|
| - break;
|
| - case Resource::Font:
|
| - fonts++;
|
| - fontMisses += missCount;
|
| - break;
|
| - case Resource::Media:
|
| - medias++;
|
| - mediaMisses += missCount;
|
| - break;
|
| - case Resource::TextTrack:
|
| - textTracks++;
|
| - textTrackMisses += missCount;
|
| - break;
|
| - case Resource::ImportResource:
|
| - imports++;
|
| - importMisses += missCount;
|
| - break;
|
| - case Resource::Raw:
|
| - raws++;
|
| - rawMisses += missCount;
|
| - break;
|
| - case Resource::Mock:
|
| - // Do not count Resource::Mock because this type is only for testing.
|
| - break;
|
| - default:
|
| - NOTREACHED();
|
| - }
|
| - }
|
| - DEFINE_STATIC_LOCAL(CustomCountHistogram, imagePreloads,
|
| - ("PreloadScanner.Counts2.Image", 0, 100, 25));
|
| - DEFINE_STATIC_LOCAL(CustomCountHistogram, imagePreloadMisses,
|
| - ("PreloadScanner.Counts2.Miss.Image", 0, 100, 25));
|
| - DEFINE_STATIC_LOCAL(CustomCountHistogram, scriptPreloads,
|
| - ("PreloadScanner.Counts2.Script", 0, 100, 25));
|
| - DEFINE_STATIC_LOCAL(CustomCountHistogram, scriptPreloadMisses,
|
| - ("PreloadScanner.Counts2.Miss.Script", 0, 100, 25));
|
| - DEFINE_STATIC_LOCAL(CustomCountHistogram, stylesheetPreloads,
|
| - ("PreloadScanner.Counts2.CSSStyleSheet", 0, 100, 25));
|
| - DEFINE_STATIC_LOCAL(
|
| - CustomCountHistogram, stylesheetPreloadMisses,
|
| - ("PreloadScanner.Counts2.Miss.CSSStyleSheet", 0, 100, 25));
|
| - DEFINE_STATIC_LOCAL(CustomCountHistogram, fontPreloads,
|
| - ("PreloadScanner.Counts2.Font", 0, 100, 25));
|
| - DEFINE_STATIC_LOCAL(CustomCountHistogram, fontPreloadMisses,
|
| - ("PreloadScanner.Counts2.Miss.Font", 0, 100, 25));
|
| - DEFINE_STATIC_LOCAL(CustomCountHistogram, mediaPreloads,
|
| - ("PreloadScanner.Counts2.Media", 0, 100, 25));
|
| - DEFINE_STATIC_LOCAL(CustomCountHistogram, mediaPreloadMisses,
|
| - ("PreloadScanner.Counts2.Miss.Media", 0, 100, 25));
|
| - DEFINE_STATIC_LOCAL(CustomCountHistogram, textTrackPreloads,
|
| - ("PreloadScanner.Counts2.TextTrack", 0, 100, 25));
|
| - DEFINE_STATIC_LOCAL(CustomCountHistogram, textTrackPreloadMisses,
|
| - ("PreloadScanner.Counts2.Miss.TextTrack", 0, 100, 25));
|
| - DEFINE_STATIC_LOCAL(CustomCountHistogram, importPreloads,
|
| - ("PreloadScanner.Counts2.Import", 0, 100, 25));
|
| - DEFINE_STATIC_LOCAL(CustomCountHistogram, importPreloadMisses,
|
| - ("PreloadScanner.Counts2.Miss.Import", 0, 100, 25));
|
| - DEFINE_STATIC_LOCAL(CustomCountHistogram, rawPreloads,
|
| - ("PreloadScanner.Counts2.Raw", 0, 100, 25));
|
| - DEFINE_STATIC_LOCAL(CustomCountHistogram, rawPreloadMisses,
|
| - ("PreloadScanner.Counts2.Miss.Raw", 0, 100, 25));
|
| - if (images)
|
| - imagePreloads.count(images);
|
| - if (imageMisses)
|
| - imagePreloadMisses.count(imageMisses);
|
| - if (scripts)
|
| - scriptPreloads.count(scripts);
|
| - if (scriptMisses)
|
| - scriptPreloadMisses.count(scriptMisses);
|
| - if (stylesheets)
|
| - stylesheetPreloads.count(stylesheets);
|
| - if (stylesheetMisses)
|
| - stylesheetPreloadMisses.count(stylesheetMisses);
|
| - if (fonts)
|
| - fontPreloads.count(fonts);
|
| - if (fontMisses)
|
| - fontPreloadMisses.count(fontMisses);
|
| - if (medias)
|
| - mediaPreloads.count(medias);
|
| - if (mediaMisses)
|
| - mediaPreloadMisses.count(mediaMisses);
|
| - if (textTracks)
|
| - textTrackPreloads.count(textTracks);
|
| - if (textTrackMisses)
|
| - textTrackPreloadMisses.count(textTrackMisses);
|
| - if (imports)
|
| - importPreloads.count(imports);
|
| - if (importMisses)
|
| - importPreloadMisses.count(importMisses);
|
| - if (raws)
|
| - rawPreloads.count(raws);
|
| - if (rawMisses)
|
| - rawPreloadMisses.count(rawMisses);
|
| -}
|
| -
|
| -const ResourceLoaderOptions& ResourceFetcher::defaultResourceOptions() {
|
| - DEFINE_STATIC_LOCAL(
|
| - ResourceLoaderOptions, options,
|
| - (BufferData, AllowStoredCredentials, ClientRequestedCredentials,
|
| - CheckContentSecurityPolicy, DocumentContext));
|
| - return options;
|
| -}
|
| -
|
| -String ResourceFetcher::getCacheIdentifier() const {
|
| - if (context().isControlledByServiceWorker())
|
| - return String::number(context().serviceWorkerID());
|
| - return MemoryCache::defaultCacheIdentifier();
|
| -}
|
| -
|
| -void ResourceFetcher::emulateLoadStartedForInspector(
|
| - Resource* resource,
|
| - const KURL& url,
|
| - WebURLRequest::RequestContext requestContext,
|
| - const AtomicString& initiatorName) {
|
| - if (cachedResource(url))
|
| - return;
|
| - ResourceRequest resourceRequest(url);
|
| - resourceRequest.setRequestContext(requestContext);
|
| - FetchRequest request(resourceRequest, initiatorName, resource->options());
|
| - context().canRequest(resource->getType(), resource->lastResourceRequest(),
|
| - resource->lastResourceRequest().url(), request.options(),
|
| - false, request.getOriginRestriction());
|
| - requestLoadStarted(resource->identifier(), resource, request,
|
| - ResourceLoadingFromCache);
|
| -}
|
| -
|
| -ResourceFetcher::DeadResourceStatsRecorder::DeadResourceStatsRecorder()
|
| - : m_useCount(0), m_revalidateCount(0), m_loadCount(0) {}
|
| -
|
| -ResourceFetcher::DeadResourceStatsRecorder::~DeadResourceStatsRecorder() {
|
| - DEFINE_THREAD_SAFE_STATIC_LOCAL(
|
| - CustomCountHistogram, hitCountHistogram,
|
| - new CustomCountHistogram("WebCore.ResourceFetcher.HitCount", 0, 1000,
|
| - 50));
|
| - hitCountHistogram.count(m_useCount);
|
| - DEFINE_THREAD_SAFE_STATIC_LOCAL(
|
| - CustomCountHistogram, revalidateCountHistogram,
|
| - new CustomCountHistogram("WebCore.ResourceFetcher.RevalidateCount", 0,
|
| - 1000, 50));
|
| - revalidateCountHistogram.count(m_revalidateCount);
|
| - DEFINE_THREAD_SAFE_STATIC_LOCAL(
|
| - CustomCountHistogram, loadCountHistogram,
|
| - new CustomCountHistogram("WebCore.ResourceFetcher.LoadCount", 0, 1000,
|
| - 50));
|
| - loadCountHistogram.count(m_loadCount);
|
| -}
|
| -
|
| -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;
|
| - }
|
| -}
|
| -
|
| -DEFINE_TRACE(ResourceFetcher) {
|
| - visitor->trace(m_context);
|
| - visitor->trace(m_archive);
|
| - visitor->trace(m_loaders);
|
| - visitor->trace(m_nonBlockingLoaders);
|
| - visitor->trace(m_documentResources);
|
| - visitor->trace(m_preloads);
|
| - visitor->trace(m_resourceTimingInfoMap);
|
| -}
|
| -
|
| -} // namespace blink
|
|
|