Index: third_party/WebKit/Source/core/fetch/Resource.cpp |
diff --git a/third_party/WebKit/Source/core/fetch/Resource.cpp b/third_party/WebKit/Source/core/fetch/Resource.cpp |
deleted file mode 100644 |
index b6888617ab69c500f3b07ab110fa389ffe8bde51..0000000000000000000000000000000000000000 |
--- a/third_party/WebKit/Source/core/fetch/Resource.cpp |
+++ /dev/null |
@@ -1,1124 +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) 2006 Samuel Weinig (sam.weinig@gmail.com) |
- Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All |
- rights reserved. |
- |
- 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. |
-*/ |
- |
-#include "core/fetch/Resource.h" |
- |
-#include "core/fetch/CachedMetadata.h" |
-#include "core/fetch/CrossOriginAccessControl.h" |
-#include "core/fetch/FetchInitiatorTypeNames.h" |
-#include "core/fetch/FetchRequest.h" |
-#include "core/fetch/IntegrityMetadata.h" |
-#include "core/fetch/MemoryCache.h" |
-#include "core/fetch/ResourceClient.h" |
-#include "core/fetch/ResourceClientWalker.h" |
-#include "core/fetch/ResourceLoader.h" |
-#include "platform/Histogram.h" |
-#include "platform/InstanceCounters.h" |
-#include "platform/RuntimeEnabledFeatures.h" |
-#include "platform/SharedBuffer.h" |
-#include "platform/WebTaskRunner.h" |
-#include "platform/instrumentation/tracing/TraceEvent.h" |
-#include "platform/network/HTTPParsers.h" |
-#include "platform/weborigin/KURL.h" |
-#include "public/platform/Platform.h" |
-#include "public/platform/WebCachePolicy.h" |
-#include "public/platform/WebScheduler.h" |
-#include "public/platform/WebSecurityOrigin.h" |
-#include "wtf/CurrentTime.h" |
-#include "wtf/MathExtras.h" |
-#include "wtf/StdLibExtras.h" |
-#include "wtf/Vector.h" |
-#include "wtf/text/CString.h" |
-#include "wtf/text/StringBuilder.h" |
-#include <algorithm> |
-#include <cassert> |
-#include <memory> |
-#include <stdint.h> |
- |
-namespace blink { |
- |
-// These response headers are not copied from a revalidated response to the |
-// cached response headers. For compatibility, this list is based on Chromium's |
-// net/http/http_response_headers.cc. |
-const char* const headersToIgnoreAfterRevalidation[] = { |
- "allow", |
- "connection", |
- "etag", |
- "expires", |
- "keep-alive", |
- "last-modified", |
- "proxy-authenticate", |
- "proxy-connection", |
- "trailer", |
- "transfer-encoding", |
- "upgrade", |
- "www-authenticate", |
- "x-frame-options", |
- "x-xss-protection", |
-}; |
- |
-// Some header prefixes mean "Don't copy this header from a 304 response.". |
-// Rather than listing all the relevant headers, we can consolidate them into |
-// this list, also grabbed from Chromium's net/http/http_response_headers.cc. |
-const char* const headerPrefixesToIgnoreAfterRevalidation[] = { |
- "content-", "x-content-", "x-webkit-"}; |
- |
-static inline bool shouldUpdateHeaderAfterRevalidation( |
- const AtomicString& header) { |
- for (size_t i = 0; i < WTF_ARRAY_LENGTH(headersToIgnoreAfterRevalidation); |
- i++) { |
- if (equalIgnoringCase(header, headersToIgnoreAfterRevalidation[i])) |
- return false; |
- } |
- for (size_t i = 0; |
- i < WTF_ARRAY_LENGTH(headerPrefixesToIgnoreAfterRevalidation); i++) { |
- if (header.startsWith(headerPrefixesToIgnoreAfterRevalidation[i], |
- TextCaseASCIIInsensitive)) |
- return false; |
- } |
- return true; |
-} |
- |
-class Resource::CachedMetadataHandlerImpl : public CachedMetadataHandler { |
- public: |
- static Resource::CachedMetadataHandlerImpl* create(Resource* resource) { |
- return new CachedMetadataHandlerImpl(resource); |
- } |
- ~CachedMetadataHandlerImpl() override {} |
- DECLARE_VIRTUAL_TRACE(); |
- void setCachedMetadata(uint32_t, const char*, size_t, CacheType) override; |
- void clearCachedMetadata(CacheType) override; |
- PassRefPtr<CachedMetadata> cachedMetadata(uint32_t) const override; |
- String encoding() const override; |
- // Sets the serialized metadata retrieved from the platform's cache. |
- void setSerializedCachedMetadata(const char*, size_t); |
- |
- protected: |
- explicit CachedMetadataHandlerImpl(Resource*); |
- virtual void sendToPlatform(); |
- const ResourceResponse& response() const { return m_resource->response(); } |
- |
- RefPtr<CachedMetadata> m_cachedMetadata; |
- |
- private: |
- Member<Resource> m_resource; |
-}; |
- |
-Resource::CachedMetadataHandlerImpl::CachedMetadataHandlerImpl( |
- Resource* resource) |
- : m_resource(resource) {} |
- |
-DEFINE_TRACE(Resource::CachedMetadataHandlerImpl) { |
- visitor->trace(m_resource); |
- CachedMetadataHandler::trace(visitor); |
-} |
- |
-void Resource::CachedMetadataHandlerImpl::setCachedMetadata( |
- uint32_t dataTypeID, |
- const char* data, |
- size_t size, |
- CachedMetadataHandler::CacheType cacheType) { |
- // Currently, only one type of cached metadata per resource is supported. If |
- // the need arises for multiple types of metadata per resource this could be |
- // enhanced to store types of metadata in a map. |
- DCHECK(!m_cachedMetadata); |
- m_cachedMetadata = CachedMetadata::create(dataTypeID, data, size); |
- if (cacheType == CachedMetadataHandler::SendToPlatform) |
- sendToPlatform(); |
-} |
- |
-void Resource::CachedMetadataHandlerImpl::clearCachedMetadata( |
- CachedMetadataHandler::CacheType cacheType) { |
- m_cachedMetadata.clear(); |
- if (cacheType == CachedMetadataHandler::SendToPlatform) |
- sendToPlatform(); |
-} |
- |
-PassRefPtr<CachedMetadata> Resource::CachedMetadataHandlerImpl::cachedMetadata( |
- uint32_t dataTypeID) const { |
- if (!m_cachedMetadata || m_cachedMetadata->dataTypeID() != dataTypeID) |
- return nullptr; |
- return m_cachedMetadata.get(); |
-} |
- |
-String Resource::CachedMetadataHandlerImpl::encoding() const { |
- return m_resource->encoding(); |
-} |
- |
-void Resource::CachedMetadataHandlerImpl::setSerializedCachedMetadata( |
- const char* data, |
- size_t size) { |
- // We only expect to receive cached metadata from the platform once. If this |
- // triggers, it indicates an efficiency problem which is most likely |
- // unexpected in code designed to improve performance. |
- DCHECK(!m_cachedMetadata); |
- m_cachedMetadata = CachedMetadata::createFromSerializedData(data, size); |
-} |
- |
-void Resource::CachedMetadataHandlerImpl::sendToPlatform() { |
- if (m_cachedMetadata) { |
- const Vector<char>& serializedData = m_cachedMetadata->serializedData(); |
- Platform::current()->cacheMetadata( |
- response().url(), response().responseTime(), serializedData.data(), |
- serializedData.size()); |
- } else { |
- Platform::current()->cacheMetadata(response().url(), |
- response().responseTime(), nullptr, 0); |
- } |
-} |
- |
-class Resource::ServiceWorkerResponseCachedMetadataHandler |
- : public Resource::CachedMetadataHandlerImpl { |
- public: |
- static Resource::CachedMetadataHandlerImpl* create( |
- Resource* resource, |
- SecurityOrigin* securityOrigin) { |
- return new ServiceWorkerResponseCachedMetadataHandler(resource, |
- securityOrigin); |
- } |
- ~ServiceWorkerResponseCachedMetadataHandler() override {} |
- DECLARE_VIRTUAL_TRACE(); |
- |
- protected: |
- void sendToPlatform() override; |
- |
- private: |
- explicit ServiceWorkerResponseCachedMetadataHandler(Resource*, |
- SecurityOrigin*); |
- String m_cacheStorageCacheName; |
- RefPtr<SecurityOrigin> m_securityOrigin; |
-}; |
- |
-Resource::ServiceWorkerResponseCachedMetadataHandler:: |
- ServiceWorkerResponseCachedMetadataHandler(Resource* resource, |
- SecurityOrigin* securityOrigin) |
- : CachedMetadataHandlerImpl(resource), m_securityOrigin(securityOrigin) {} |
- |
-DEFINE_TRACE(Resource::ServiceWorkerResponseCachedMetadataHandler) { |
- CachedMetadataHandlerImpl::trace(visitor); |
-} |
- |
-void Resource::ServiceWorkerResponseCachedMetadataHandler::sendToPlatform() { |
- // We don't support sending the metadata to the platform when the response was |
- // directly fetched via a ServiceWorker (eg: |
- // FetchEvent.respondWith(fetch(FetchEvent.request))) to prevent an attacker's |
- // Service Worker from poisoning the metadata cache of HTTPCache. |
- if (response().cacheStorageCacheName().isNull()) |
- return; |
- |
- if (m_cachedMetadata) { |
- const Vector<char>& serializedData = m_cachedMetadata->serializedData(); |
- Platform::current()->cacheMetadataInCacheStorage( |
- response().url(), response().responseTime(), serializedData.data(), |
- serializedData.size(), WebSecurityOrigin(m_securityOrigin), |
- response().cacheStorageCacheName()); |
- } else { |
- Platform::current()->cacheMetadataInCacheStorage( |
- response().url(), response().responseTime(), nullptr, 0, |
- WebSecurityOrigin(m_securityOrigin), |
- response().cacheStorageCacheName()); |
- } |
-} |
- |
-// This class cannot be on-heap because the first callbackHandler() call |
-// instantiates the singleton object while we can call it in the |
-// pre-finalization step. |
-class Resource::ResourceCallback final { |
- public: |
- static ResourceCallback& callbackHandler(); |
- void schedule(Resource*); |
- void cancel(Resource*); |
- bool isScheduled(Resource*) const; |
- |
- private: |
- ResourceCallback(); |
- |
- void runTask(); |
- TaskHandle m_taskHandle; |
- HashSet<Persistent<Resource>> m_resourcesWithPendingClients; |
-}; |
- |
-Resource::ResourceCallback& Resource::ResourceCallback::callbackHandler() { |
- DEFINE_STATIC_LOCAL(ResourceCallback, callbackHandler, ()); |
- return callbackHandler; |
-} |
- |
-Resource::ResourceCallback::ResourceCallback() {} |
- |
-void Resource::ResourceCallback::schedule(Resource* resource) { |
- if (!m_taskHandle.isActive()) { |
- // WTF::unretained(this) is safe because a posted task is canceled when |
- // |m_taskHandle| is destroyed on the dtor of this ResourceCallback. |
- m_taskHandle = |
- Platform::current() |
- ->currentThread() |
- ->scheduler() |
- ->loadingTaskRunner() |
- ->postCancellableTask( |
- BLINK_FROM_HERE, |
- WTF::bind(&ResourceCallback::runTask, WTF::unretained(this))); |
- } |
- m_resourcesWithPendingClients.add(resource); |
-} |
- |
-void Resource::ResourceCallback::cancel(Resource* resource) { |
- m_resourcesWithPendingClients.remove(resource); |
- if (m_taskHandle.isActive() && m_resourcesWithPendingClients.isEmpty()) |
- m_taskHandle.cancel(); |
-} |
- |
-bool Resource::ResourceCallback::isScheduled(Resource* resource) const { |
- return m_resourcesWithPendingClients.contains(resource); |
-} |
- |
-void Resource::ResourceCallback::runTask() { |
- HeapVector<Member<Resource>> resources; |
- for (const Member<Resource>& resource : m_resourcesWithPendingClients) |
- resources.push_back(resource.get()); |
- m_resourcesWithPendingClients.clear(); |
- |
- for (const auto& resource : resources) |
- resource->finishPendingClients(); |
-} |
- |
-constexpr Resource::Status Resource::NotStarted; |
-constexpr Resource::Status Resource::Pending; |
-constexpr Resource::Status Resource::Cached; |
-constexpr Resource::Status Resource::LoadError; |
-constexpr Resource::Status Resource::DecodeError; |
- |
-Resource::Resource(const ResourceRequest& request, |
- Type type, |
- const ResourceLoaderOptions& options) |
- : m_loadFinishTime(0), |
- m_identifier(0), |
- m_encodedSize(0), |
- m_encodedSizeMemoryUsage(0), |
- m_decodedSize(0), |
- m_overheadSize(calculateOverheadSize()), |
- m_preloadCount(0), |
- m_preloadDiscoveryTime(0.0), |
- m_cacheIdentifier(MemoryCache::defaultCacheIdentifier()), |
- m_preloadResult(PreloadNotReferenced), |
- m_type(type), |
- m_status(NotStarted), |
- m_needsSynchronousCacheHit(false), |
- m_linkPreload(false), |
- m_isRevalidating(false), |
- m_isAlive(false), |
- m_isAddRemoveClientProhibited(false), |
- m_integrityDisposition(ResourceIntegrityDisposition::NotChecked), |
- m_options(options), |
- m_responseTimestamp(currentTime()), |
- m_cancelTimer(Platform::current()->mainThread()->getWebTaskRunner(), |
- this, |
- &Resource::cancelTimerFired), |
- m_resourceRequest(request) { |
- InstanceCounters::incrementCounter(InstanceCounters::ResourceCounter); |
- |
- // Currently we support the metadata caching only for HTTP family. |
- if (resourceRequest().url().protocolIsInHTTPFamily()) |
- m_cacheHandler = CachedMetadataHandlerImpl::create(this); |
- MemoryCoordinator::instance().registerClient(this); |
-} |
- |
-Resource::~Resource() { |
- InstanceCounters::decrementCounter(InstanceCounters::ResourceCounter); |
-} |
- |
-DEFINE_TRACE(Resource) { |
- visitor->trace(m_loader); |
- visitor->trace(m_cacheHandler); |
- visitor->trace(m_clients); |
- visitor->trace(m_clientsAwaitingCallback); |
- visitor->trace(m_finishedClients); |
- MemoryCoordinatorClient::trace(visitor); |
-} |
- |
-void Resource::setLoader(ResourceLoader* loader) { |
- CHECK(!m_loader); |
- DCHECK(stillNeedsLoad()); |
- m_loader = loader; |
- m_status = Pending; |
-} |
- |
-void Resource::checkNotify() { |
- if (isLoading()) |
- return; |
- |
- ResourceClientWalker<ResourceClient> w(m_clients); |
- while (ResourceClient* c = w.next()) { |
- markClientFinished(c); |
- c->notifyFinished(this); |
- } |
-} |
- |
-void Resource::markClientFinished(ResourceClient* client) { |
- if (m_clients.contains(client)) { |
- m_finishedClients.add(client); |
- m_clients.remove(client); |
- } |
-} |
- |
-void Resource::appendData(const char* data, size_t length) { |
- TRACE_EVENT0("blink", "Resource::appendData"); |
- DCHECK(!m_isRevalidating); |
- DCHECK(!errorOccurred()); |
- if (m_options.dataBufferingPolicy == DoNotBufferData) |
- return; |
- if (m_data) |
- m_data->append(data, length); |
- else |
- m_data = SharedBuffer::create(data, length); |
- setEncodedSize(m_data->size()); |
-} |
- |
-void Resource::setResourceBuffer(PassRefPtr<SharedBuffer> resourceBuffer) { |
- DCHECK(!m_isRevalidating); |
- DCHECK(!errorOccurred()); |
- DCHECK_EQ(m_options.dataBufferingPolicy, BufferData); |
- m_data = resourceBuffer; |
- setEncodedSize(m_data->size()); |
-} |
- |
-void Resource::clearData() { |
- m_data.clear(); |
- m_encodedSizeMemoryUsage = 0; |
-} |
- |
-void Resource::setDataBufferingPolicy(DataBufferingPolicy dataBufferingPolicy) { |
- m_options.dataBufferingPolicy = dataBufferingPolicy; |
- clearData(); |
- setEncodedSize(0); |
-} |
- |
-void Resource::error(const ResourceError& error) { |
- DCHECK(!error.isNull()); |
- m_error = error; |
- m_isRevalidating = false; |
- |
- if (m_error.isCancellation() || !isPreloaded()) |
- memoryCache()->remove(this); |
- |
- if (!errorOccurred()) |
- setStatus(LoadError); |
- DCHECK(errorOccurred()); |
- clearData(); |
- m_loader = nullptr; |
- checkNotify(); |
-} |
- |
-void Resource::finish(double loadFinishTime) { |
- DCHECK(!m_isRevalidating); |
- m_loadFinishTime = loadFinishTime; |
- if (!errorOccurred()) |
- m_status = Cached; |
- m_loader = nullptr; |
- checkNotify(); |
-} |
- |
-AtomicString Resource::httpContentType() const { |
- return extractMIMETypeFromMediaType( |
- response().httpHeaderField(HTTPNames::Content_Type).lower()); |
-} |
- |
-bool Resource::passesAccessControlCheck(SecurityOrigin* securityOrigin) const { |
- StoredCredentials storedCredentials = |
- lastResourceRequest().allowStoredCredentials() |
- ? AllowStoredCredentials |
- : DoNotAllowStoredCredentials; |
- CrossOriginAccessControl::AccessStatus status = |
- CrossOriginAccessControl::checkAccess(response(), storedCredentials, |
- securityOrigin); |
- |
- return status == CrossOriginAccessControl::kAccessAllowed; |
-} |
- |
-bool Resource::isEligibleForIntegrityCheck( |
- SecurityOrigin* securityOrigin) const { |
- return securityOrigin->canRequest(resourceRequest().url()) || |
- passesAccessControlCheck(securityOrigin); |
-} |
- |
-void Resource::setIntegrityDisposition( |
- ResourceIntegrityDisposition disposition) { |
- DCHECK_NE(disposition, ResourceIntegrityDisposition::NotChecked); |
- DCHECK(m_type == Resource::Script || m_type == Resource::CSSStyleSheet); |
- m_integrityDisposition = disposition; |
-} |
- |
-bool Resource::mustRefetchDueToIntegrityMetadata( |
- const FetchRequest& request) const { |
- if (request.integrityMetadata().isEmpty()) |
- return false; |
- |
- return !IntegrityMetadata::setsEqual(m_integrityMetadata, |
- request.integrityMetadata()); |
-} |
- |
-static double currentAge(const ResourceResponse& response, |
- double responseTimestamp) { |
- // RFC2616 13.2.3 |
- // No compensation for latency as that is not terribly important in practice |
- double dateValue = response.date(); |
- double apparentAge = std::isfinite(dateValue) |
- ? std::max(0., responseTimestamp - dateValue) |
- : 0; |
- double ageValue = response.age(); |
- double correctedReceivedAge = |
- std::isfinite(ageValue) ? std::max(apparentAge, ageValue) : apparentAge; |
- double residentTime = currentTime() - responseTimestamp; |
- return correctedReceivedAge + residentTime; |
-} |
- |
-double Resource::currentAge() const { |
- return blink::currentAge(response(), m_responseTimestamp); |
-} |
- |
-static double freshnessLifetime(const ResourceResponse& response, |
- double responseTimestamp) { |
-#if !OS(ANDROID) |
- // On desktop, local files should be reloaded in case they change. |
- if (response.url().isLocalFile()) |
- return 0; |
-#endif |
- |
- // Cache other non-http / non-filesystem resources liberally. |
- if (!response.url().protocolIsInHTTPFamily() && |
- !response.url().protocolIs("filesystem")) |
- return std::numeric_limits<double>::max(); |
- |
- // RFC2616 13.2.4 |
- double maxAgeValue = response.cacheControlMaxAge(); |
- if (std::isfinite(maxAgeValue)) |
- return maxAgeValue; |
- double expiresValue = response.expires(); |
- double dateValue = response.date(); |
- double creationTime = |
- std::isfinite(dateValue) ? dateValue : responseTimestamp; |
- if (std::isfinite(expiresValue)) |
- return expiresValue - creationTime; |
- double lastModifiedValue = response.lastModified(); |
- if (std::isfinite(lastModifiedValue)) |
- return (creationTime - lastModifiedValue) * 0.1; |
- // If no cache headers are present, the specification leaves the decision to |
- // the UA. Other browsers seem to opt for 0. |
- return 0; |
-} |
- |
-double Resource::freshnessLifetime() const { |
- return blink::freshnessLifetime(response(), m_responseTimestamp); |
-} |
- |
-double Resource::stalenessLifetime() const { |
- return response().cacheControlStaleWhileRevalidate(); |
-} |
- |
-static bool canUseResponse(const ResourceResponse& response, |
- double responseTimestamp) { |
- if (response.isNull()) |
- return false; |
- |
- // FIXME: Why isn't must-revalidate considered a reason we can't use the |
- // response? |
- if (response.cacheControlContainsNoCache() || |
- response.cacheControlContainsNoStore()) |
- return false; |
- |
- if (response.httpStatusCode() == 303) { |
- // Must not be cached. |
- return false; |
- } |
- |
- if (response.httpStatusCode() == 302 || response.httpStatusCode() == 307) { |
- // Default to not cacheable unless explicitly allowed. |
- bool hasMaxAge = std::isfinite(response.cacheControlMaxAge()); |
- bool hasExpires = std::isfinite(response.expires()); |
- // TODO: consider catching Cache-Control "private" and "public" here. |
- if (!hasMaxAge && !hasExpires) |
- return false; |
- } |
- |
- return currentAge(response, responseTimestamp) <= |
- freshnessLifetime(response, responseTimestamp); |
-} |
- |
-const ResourceRequest& Resource::lastResourceRequest() const { |
- if (!m_redirectChain.size()) |
- return resourceRequest(); |
- return m_redirectChain.back().m_request; |
-} |
- |
-void Resource::setRevalidatingRequest(const ResourceRequest& request) { |
- SECURITY_CHECK(m_redirectChain.isEmpty()); |
- DCHECK(!request.isNull()); |
- CHECK(!m_isRevalidationStartForbidden); |
- m_isRevalidating = true; |
- m_resourceRequest = request; |
- m_status = NotStarted; |
-} |
- |
-bool Resource::willFollowRedirect(const ResourceRequest& newRequest, |
- const ResourceResponse& redirectResponse) { |
- if (m_isRevalidating) |
- revalidationFailed(); |
- m_redirectChain.push_back(RedirectPair(newRequest, redirectResponse)); |
- return true; |
-} |
- |
-void Resource::setResponse(const ResourceResponse& response) { |
- m_response = response; |
- if (this->response().wasFetchedViaServiceWorker()) { |
- m_cacheHandler = ServiceWorkerResponseCachedMetadataHandler::create( |
- this, m_fetcherSecurityOrigin.get()); |
- } |
-} |
- |
-void Resource::responseReceived(const ResourceResponse& response, |
- std::unique_ptr<WebDataConsumerHandle>) { |
- m_responseTimestamp = currentTime(); |
- if (m_preloadDiscoveryTime) { |
- int timeSinceDiscovery = static_cast<int>( |
- 1000 * (monotonicallyIncreasingTime() - m_preloadDiscoveryTime)); |
- DEFINE_STATIC_LOCAL(CustomCountHistogram, |
- preloadDiscoveryToFirstByteHistogram, |
- ("PreloadScanner.TTFB", 0, 10000, 50)); |
- preloadDiscoveryToFirstByteHistogram.count(timeSinceDiscovery); |
- } |
- |
- if (m_isRevalidating) { |
- if (response.httpStatusCode() == 304) { |
- revalidationSucceeded(response); |
- return; |
- } |
- revalidationFailed(); |
- } |
- setResponse(response); |
- String encoding = response.textEncodingName(); |
- if (!encoding.isNull()) |
- setEncoding(encoding); |
-} |
- |
-void Resource::setSerializedCachedMetadata(const char* data, size_t size) { |
- DCHECK(!m_isRevalidating); |
- DCHECK(!response().isNull()); |
- if (m_cacheHandler) |
- m_cacheHandler->setSerializedCachedMetadata(data, size); |
-} |
- |
-CachedMetadataHandler* Resource::cacheHandler() { |
- return m_cacheHandler.get(); |
-} |
- |
-String Resource::reasonNotDeletable() const { |
- StringBuilder builder; |
- if (hasClientsOrObservers()) { |
- builder.append("hasClients("); |
- builder.appendNumber(m_clients.size()); |
- if (!m_clientsAwaitingCallback.isEmpty()) { |
- builder.append(", AwaitingCallback="); |
- builder.appendNumber(m_clientsAwaitingCallback.size()); |
- } |
- if (!m_finishedClients.isEmpty()) { |
- builder.append(", Finished="); |
- builder.appendNumber(m_finishedClients.size()); |
- } |
- builder.append(')'); |
- } |
- if (m_loader) { |
- if (!builder.isEmpty()) |
- builder.append(' '); |
- builder.append("m_loader"); |
- } |
- if (m_preloadCount) { |
- if (!builder.isEmpty()) |
- builder.append(' '); |
- builder.append("m_preloadCount("); |
- builder.appendNumber(m_preloadCount); |
- builder.append(')'); |
- } |
- if (memoryCache()->contains(this)) { |
- if (!builder.isEmpty()) |
- builder.append(' '); |
- builder.append("in_memory_cache"); |
- } |
- return builder.toString(); |
-} |
- |
-void Resource::didAddClient(ResourceClient* c) { |
- if (isLoaded()) { |
- c->notifyFinished(this); |
- if (m_clients.contains(c)) { |
- m_finishedClients.add(c); |
- m_clients.remove(c); |
- } |
- } |
-} |
- |
-static bool typeNeedsSynchronousCacheHit(Resource::Type type) { |
- // Some resources types default to return data synchronously. For most of |
- // these, it's because there are layout tests that expect data to return |
- // synchronously in case of cache hit. In the case of fonts, there was a |
- // performance regression. |
- // FIXME: Get to the point where we don't need to special-case sync/async |
- // behavior for different resource types. |
- if (type == Resource::Image) |
- return true; |
- if (type == Resource::CSSStyleSheet) |
- return true; |
- if (type == Resource::Script) |
- return true; |
- if (type == Resource::Font) |
- return true; |
- return false; |
-} |
- |
-void Resource::willAddClientOrObserver(PreloadReferencePolicy policy) { |
- if (policy == MarkAsReferenced && m_preloadResult == PreloadNotReferenced) { |
- if (isLoaded()) |
- m_preloadResult = PreloadReferencedWhileComplete; |
- else if (isLoading()) |
- m_preloadResult = PreloadReferencedWhileLoading; |
- else |
- m_preloadResult = PreloadReferenced; |
- |
- if (m_preloadDiscoveryTime) { |
- int timeSinceDiscovery = static_cast<int>( |
- 1000 * (monotonicallyIncreasingTime() - m_preloadDiscoveryTime)); |
- DEFINE_STATIC_LOCAL(CustomCountHistogram, preloadDiscoveryHistogram, |
- ("PreloadScanner.ReferenceTime", 0, 10000, 50)); |
- preloadDiscoveryHistogram.count(timeSinceDiscovery); |
- } |
- } |
- if (!hasClientsOrObservers()) { |
- m_isAlive = true; |
- } |
-} |
- |
-void Resource::addClient(ResourceClient* client, |
- PreloadReferencePolicy policy) { |
- CHECK(!m_isAddRemoveClientProhibited); |
- |
- willAddClientOrObserver(policy); |
- |
- if (m_isRevalidating) { |
- m_clients.add(client); |
- return; |
- } |
- |
- // If an error has occurred or we have existing data to send to the new client |
- // and the resource type supprts it, send it asynchronously. |
- if ((errorOccurred() || !response().isNull()) && |
- !typeNeedsSynchronousCacheHit(getType()) && !m_needsSynchronousCacheHit) { |
- m_clientsAwaitingCallback.add(client); |
- ResourceCallback::callbackHandler().schedule(this); |
- return; |
- } |
- |
- m_clients.add(client); |
- didAddClient(client); |
- return; |
-} |
- |
-void Resource::removeClient(ResourceClient* client) { |
- CHECK(!m_isAddRemoveClientProhibited); |
- |
- // This code may be called in a pre-finalizer, where weak members in the |
- // HashCountedSet are already swept out. |
- |
- if (m_finishedClients.contains(client)) |
- m_finishedClients.remove(client); |
- else if (m_clientsAwaitingCallback.contains(client)) |
- m_clientsAwaitingCallback.remove(client); |
- else |
- m_clients.remove(client); |
- |
- if (m_clientsAwaitingCallback.isEmpty()) |
- ResourceCallback::callbackHandler().cancel(this); |
- |
- didRemoveClientOrObserver(); |
-} |
- |
-void Resource::didRemoveClientOrObserver() { |
- if (!hasClientsOrObservers() && m_isAlive) { |
- m_isAlive = false; |
- allClientsAndObserversRemoved(); |
- |
- // RFC2616 14.9.2: |
- // "no-store: ... MUST make a best-effort attempt to remove the information |
- // from volatile storage as promptly as possible" |
- // "... History buffers MAY store such responses as part of their normal |
- // operation." |
- // We allow non-secure content to be reused in history, but we do not allow |
- // secure content to be reused. |
- if (hasCacheControlNoStoreHeader() && url().protocolIs("https")) |
- memoryCache()->remove(this); |
- } |
-} |
- |
-void Resource::allClientsAndObserversRemoved() { |
- if (!m_loader) |
- return; |
- if (!m_cancelTimer.isActive()) |
- m_cancelTimer.startOneShot(0, BLINK_FROM_HERE); |
-} |
- |
-void Resource::cancelTimerFired(TimerBase* timer) { |
- DCHECK_EQ(timer, &m_cancelTimer); |
- if (!hasClientsOrObservers() && m_loader) |
- m_loader->cancel(); |
-} |
- |
-void Resource::setDecodedSize(size_t decodedSize) { |
- if (decodedSize == m_decodedSize) |
- return; |
- size_t oldSize = size(); |
- m_decodedSize = decodedSize; |
- memoryCache()->update(this, oldSize, size()); |
-} |
- |
-void Resource::setEncodedSize(size_t encodedSize) { |
- if (encodedSize == m_encodedSize && encodedSize == m_encodedSizeMemoryUsage) |
- return; |
- size_t oldSize = size(); |
- m_encodedSize = encodedSize; |
- m_encodedSizeMemoryUsage = encodedSize; |
- memoryCache()->update(this, oldSize, size()); |
-} |
- |
-void Resource::finishPendingClients() { |
- // We're going to notify clients one by one. It is simple if the client does |
- // nothing. However there are a couple other things that can happen. |
- // |
- // 1. Clients can be added during the loop. Make sure they are not processed. |
- // 2. Clients can be removed during the loop. Make sure they are always |
- // available to be removed. Also don't call removed clients or add them |
- // back. |
- // |
- // Handle case (1) by saving a list of clients to notify. A separate list also |
- // ensure a client is either in m_clients or m_clientsAwaitingCallback. |
- HeapVector<Member<ResourceClient>> clientsToNotify; |
- copyToVector(m_clientsAwaitingCallback, clientsToNotify); |
- |
- for (const auto& client : clientsToNotify) { |
- // Handle case (2) to skip removed clients. |
- if (!m_clientsAwaitingCallback.remove(client)) |
- continue; |
- m_clients.add(client); |
- |
- // When revalidation starts after waiting clients are scheduled and |
- // before they are added here. In such cases, we just add the clients |
- // to |m_clients| without didAddClient(), as in Resource::addClient(). |
- if (!m_isRevalidating) |
- didAddClient(client); |
- } |
- |
- // It is still possible for the above loop to finish a new client |
- // synchronously. If there's no client waiting we should deschedule. |
- bool scheduled = ResourceCallback::callbackHandler().isScheduled(this); |
- if (scheduled && m_clientsAwaitingCallback.isEmpty()) |
- ResourceCallback::callbackHandler().cancel(this); |
- |
- // Prevent the case when there are clients waiting but no callback scheduled. |
- DCHECK(m_clientsAwaitingCallback.isEmpty() || scheduled); |
-} |
- |
-void Resource::prune() { |
- destroyDecodedDataIfPossible(); |
-} |
- |
-void Resource::onMemoryStateChange(MemoryState state) { |
- if (state != MemoryState::SUSPENDED) |
- return; |
- prune(); |
- if (!m_cacheHandler) |
- return; |
- m_cacheHandler->clearCachedMetadata(CachedMetadataHandler::CacheLocally); |
-} |
- |
-void Resource::onMemoryDump(WebMemoryDumpLevelOfDetail levelOfDetail, |
- WebProcessMemoryDump* memoryDump) const { |
- static const size_t kMaxURLReportLength = 128; |
- static const int kMaxResourceClientToShowInMemoryInfra = 10; |
- |
- const String dumpName = getMemoryDumpName(); |
- WebMemoryAllocatorDump* dump = |
- memoryDump->createMemoryAllocatorDump(dumpName); |
- dump->addScalar("encoded_size", "bytes", m_encodedSizeMemoryUsage); |
- if (hasClientsOrObservers()) |
- dump->addScalar("live_size", "bytes", m_encodedSizeMemoryUsage); |
- else |
- dump->addScalar("dead_size", "bytes", m_encodedSizeMemoryUsage); |
- |
- if (m_data) |
- m_data->onMemoryDump(dumpName, memoryDump); |
- |
- if (levelOfDetail == WebMemoryDumpLevelOfDetail::Detailed) { |
- String urlToReport = url().getString(); |
- if (urlToReport.length() > kMaxURLReportLength) { |
- urlToReport.truncate(kMaxURLReportLength); |
- urlToReport = urlToReport + "..."; |
- } |
- dump->addString("url", "", urlToReport); |
- |
- dump->addString("reason_not_deletable", "", reasonNotDeletable()); |
- |
- Vector<String> clientNames; |
- ResourceClientWalker<ResourceClient> walker(m_clients); |
- while (ResourceClient* client = walker.next()) |
- clientNames.push_back(client->debugName()); |
- ResourceClientWalker<ResourceClient> walker2(m_clientsAwaitingCallback); |
- while (ResourceClient* client = walker2.next()) |
- clientNames.push_back("(awaiting) " + client->debugName()); |
- ResourceClientWalker<ResourceClient> walker3(m_finishedClients); |
- while (ResourceClient* client = walker3.next()) |
- clientNames.push_back("(finished) " + client->debugName()); |
- std::sort(clientNames.begin(), clientNames.end(), |
- WTF::codePointCompareLessThan); |
- |
- StringBuilder builder; |
- for (size_t i = 0; |
- i < clientNames.size() && i < kMaxResourceClientToShowInMemoryInfra; |
- ++i) { |
- if (i > 0) |
- builder.append(" / "); |
- builder.append(clientNames[i]); |
- } |
- if (clientNames.size() > kMaxResourceClientToShowInMemoryInfra) { |
- builder.append(" / and "); |
- builder.appendNumber(clientNames.size() - |
- kMaxResourceClientToShowInMemoryInfra); |
- builder.append(" more"); |
- } |
- dump->addString("ResourceClient", "", builder.toString()); |
- } |
- |
- const String overheadName = dumpName + "/metadata"; |
- WebMemoryAllocatorDump* overheadDump = |
- memoryDump->createMemoryAllocatorDump(overheadName); |
- overheadDump->addScalar("size", "bytes", overheadSize()); |
- memoryDump->addSuballocation( |
- overheadDump->guid(), String(WTF::Partitions::kAllocatedObjectPoolName)); |
-} |
- |
-String Resource::getMemoryDumpName() const { |
- return String::format( |
- "web_cache/%s_resources/%ld", |
- resourceTypeToString(getType(), options().initiatorInfo.name), |
- m_identifier); |
-} |
- |
-void Resource::setCachePolicyBypassingCache() { |
- m_resourceRequest.setCachePolicy(WebCachePolicy::BypassingCache); |
-} |
- |
-void Resource::setPreviewsStateNoTransform() { |
- m_resourceRequest.setPreviewsState(WebURLRequest::PreviewsNoTransform); |
-} |
- |
-void Resource::clearRangeRequestHeader() { |
- m_resourceRequest.clearHTTPHeaderField("range"); |
-} |
- |
-void Resource::revalidationSucceeded( |
- const ResourceResponse& validatingResponse) { |
- SECURITY_CHECK(m_redirectChain.isEmpty()); |
- SECURITY_CHECK(equalIgnoringFragmentIdentifier(validatingResponse.url(), |
- response().url())); |
- m_response.setResourceLoadTiming(validatingResponse.resourceLoadTiming()); |
- |
- // RFC2616 10.3.5 |
- // Update cached headers from the 304 response |
- const HTTPHeaderMap& newHeaders = validatingResponse.httpHeaderFields(); |
- for (const auto& header : newHeaders) { |
- // Entity headers should not be sent by servers when generating a 304 |
- // response; misconfigured servers send them anyway. We shouldn't allow such |
- // headers to update the original request. We'll base this on the list |
- // defined by RFC2616 7.1, with a few additions for extension headers we |
- // care about. |
- if (!shouldUpdateHeaderAfterRevalidation(header.key)) |
- continue; |
- m_response.setHTTPHeaderField(header.key, header.value); |
- } |
- |
- m_isRevalidating = false; |
-} |
- |
-void Resource::revalidationFailed() { |
- SECURITY_CHECK(m_redirectChain.isEmpty()); |
- clearData(); |
- m_cacheHandler.clear(); |
- destroyDecodedDataForFailedRevalidation(); |
- m_isRevalidating = false; |
-} |
- |
-bool Resource::canReuseRedirectChain() const { |
- for (auto& redirect : m_redirectChain) { |
- if (!canUseResponse(redirect.m_redirectResponse, m_responseTimestamp)) |
- return false; |
- if (redirect.m_request.cacheControlContainsNoCache() || |
- redirect.m_request.cacheControlContainsNoStore()) |
- return false; |
- } |
- return true; |
-} |
- |
-bool Resource::hasCacheControlNoStoreHeader() const { |
- return response().cacheControlContainsNoStore() || |
- resourceRequest().cacheControlContainsNoStore(); |
-} |
- |
-bool Resource::hasVaryHeader() const { |
- return !response().httpHeaderField(HTTPNames::Vary).isNull(); |
-} |
- |
-bool Resource::mustRevalidateDueToCacheHeaders() const { |
- return !canUseResponse(response(), m_responseTimestamp) || |
- resourceRequest().cacheControlContainsNoCache() || |
- resourceRequest().cacheControlContainsNoStore(); |
-} |
- |
-bool Resource::canUseCacheValidator() const { |
- if (isLoading() || errorOccurred()) |
- return false; |
- |
- if (hasCacheControlNoStoreHeader()) |
- return false; |
- |
- // Do not revalidate Resource with redirects. https://crbug.com/613971 |
- if (!redirectChain().isEmpty()) |
- return false; |
- |
- return response().hasCacheValidatorFields() || |
- resourceRequest().hasCacheValidatorFields(); |
-} |
- |
-size_t Resource::calculateOverheadSize() const { |
- static const int kAverageClientsHashMapSize = 384; |
- return sizeof(Resource) + response().memoryUsage() + |
- kAverageClientsHashMapSize + |
- resourceRequest().url().getString().length() * 2; |
-} |
- |
-void Resource::didChangePriority(ResourceLoadPriority loadPriority, |
- int intraPriorityValue) { |
- m_resourceRequest.setPriority(loadPriority, intraPriorityValue); |
- if (m_loader) |
- m_loader->didChangePriority(loadPriority, intraPriorityValue); |
-} |
- |
-// TODO(toyoshim): Consider to generate automatically. https://crbug.com/675515. |
-static const char* initiatorTypeNameToString( |
- const AtomicString& initiatorTypeName) { |
- if (initiatorTypeName == FetchInitiatorTypeNames::css) |
- return "CSS resource"; |
- if (initiatorTypeName == FetchInitiatorTypeNames::document) |
- return "Document"; |
- if (initiatorTypeName == FetchInitiatorTypeNames::icon) |
- return "Icon"; |
- if (initiatorTypeName == FetchInitiatorTypeNames::internal) |
- return "Internal resource"; |
- if (initiatorTypeName == FetchInitiatorTypeNames::link) |
- return "Link element resource"; |
- if (initiatorTypeName == FetchInitiatorTypeNames::processinginstruction) |
- return "Processing instruction"; |
- if (initiatorTypeName == FetchInitiatorTypeNames::texttrack) |
- return "Text track"; |
- if (initiatorTypeName == FetchInitiatorTypeNames::xml) |
- return "XML resource"; |
- if (initiatorTypeName == FetchInitiatorTypeNames::xmlhttprequest) |
- return "XMLHttpRequest"; |
- |
- static_assert( |
- FetchInitiatorTypeNames::FetchInitiatorTypeNamesCount == 12, |
- "New FetchInitiatorTypeNames should be handled correctly here."); |
- |
- return "Resource"; |
-} |
- |
-const char* Resource::resourceTypeToString( |
- Type type, |
- const AtomicString& fetchInitiatorName) { |
- switch (type) { |
- case Resource::MainResource: |
- return "Main resource"; |
- case Resource::Image: |
- return "Image"; |
- case Resource::CSSStyleSheet: |
- return "CSS stylesheet"; |
- case Resource::Script: |
- return "Script"; |
- case Resource::Font: |
- return "Font"; |
- case Resource::Raw: |
- return initiatorTypeNameToString(fetchInitiatorName); |
- case Resource::SVGDocument: |
- return "SVG document"; |
- case Resource::XSLStyleSheet: |
- return "XSL stylesheet"; |
- case Resource::LinkPrefetch: |
- return "Link prefetch resource"; |
- case Resource::TextTrack: |
- return "Text track"; |
- case Resource::ImportResource: |
- return "Imported resource"; |
- case Resource::Media: |
- return "Media"; |
- case Resource::Manifest: |
- return "Manifest"; |
- case Resource::Mock: |
- return "Mock"; |
- } |
- NOTREACHED(); |
- return initiatorTypeNameToString(fetchInitiatorName); |
-} |
- |
-bool Resource::shouldBlockLoadEvent() const { |
- return !m_linkPreload && isLoadEventBlockingResourceType(); |
-} |
- |
-bool Resource::isLoadEventBlockingResourceType() const { |
- switch (m_type) { |
- case Resource::MainResource: |
- case Resource::Image: |
- case Resource::CSSStyleSheet: |
- case Resource::Script: |
- case Resource::Font: |
- case Resource::SVGDocument: |
- case Resource::XSLStyleSheet: |
- case Resource::ImportResource: |
- return true; |
- case Resource::Raw: |
- case Resource::LinkPrefetch: |
- case Resource::TextTrack: |
- case Resource::Media: |
- case Resource::Manifest: |
- case Resource::Mock: |
- return false; |
- } |
- NOTREACHED(); |
- return false; |
-} |
- |
-} // namespace blink |