2 Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
3 Copyright (C) 2001 Dirk Mueller (mueller@kde.org)
4 Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
5 Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
6 Copyright (C) 2004-2011, 2014 Apple Inc. All rights reserved.
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public
10 License as published by the Free Software Foundation; either
11 version 2 of the License, or (at your option) any later version.
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Library General Public License for more details.
18 You should have received a copy of the GNU Library General Public License
19 along with this library; see the file COPYING.LIB. If not, write to
20 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 Boston, MA 02110-1301, USA.
25 #include "CachedResource.h"
27 #include "CachedResourceClient.h"
28 #include "CachedResourceClientWalker.h"
29 #include "CachedResourceHandle.h"
30 #include "CachedResourceLoader.h"
31 #include "CrossOriginAccessControl.h"
32 #include "DiagnosticLoggingClient.h"
33 #include "DiagnosticLoggingKeys.h"
35 #include "DocumentLoader.h"
37 #include "FrameLoader.h"
38 #include "FrameLoaderClient.h"
39 #include "HTTPHeaderNames.h"
40 #include "InspectorInstrumentation.h"
41 #include "LoaderStrategy.h"
43 #include "MemoryCache.h"
44 #include "PlatformStrategies.h"
45 #include "ProgressTracker.h"
46 #include "ResourceHandle.h"
47 #include "SchemeRegistry.h"
48 #include "SecurityOrigin.h"
49 #include "SubresourceLoader.h"
51 #include <wtf/CompletionHandler.h>
52 #include <wtf/MathExtras.h>
53 #include <wtf/RefCountedLeakCounter.h>
54 #include <wtf/StdLibExtras.h>
55 #include <wtf/Vector.h>
56 #include <wtf/text/CString.h>
59 #include "QuickLook.h"
63 #define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(cachedResourceLoader.isAlwaysOnLoggingAllowed(), Network, "%p - CachedResource::" fmt, this, ##__VA_ARGS__)
68 ResourceLoadPriority CachedResource::defaultPriorityForResourceType(Type type)
71 case CachedResource::MainResource:
72 return ResourceLoadPriority::VeryHigh;
73 case CachedResource::CSSStyleSheet:
74 case CachedResource::Script:
75 return ResourceLoadPriority::High;
77 case CachedResource::SVGFontResource:
79 case CachedResource::MediaResource:
80 case CachedResource::FontResource:
81 case CachedResource::RawResource:
82 case CachedResource::Icon:
83 return ResourceLoadPriority::Medium;
84 case CachedResource::ImageResource:
85 return ResourceLoadPriority::Low;
87 case CachedResource::XSLStyleSheet:
88 return ResourceLoadPriority::High;
90 case CachedResource::SVGDocumentResource:
91 return ResourceLoadPriority::Low;
92 case CachedResource::Beacon:
93 return ResourceLoadPriority::VeryLow;
94 case CachedResource::LinkPrefetch:
95 return ResourceLoadPriority::VeryLow;
96 #if ENABLE(VIDEO_TRACK)
97 case CachedResource::TextTrackResource:
98 return ResourceLoadPriority::Low;
100 #if ENABLE(APPLICATION_MANIFEST)
101 case CachedResource::ApplicationManifest:
102 return ResourceLoadPriority::Low;
105 ASSERT_NOT_REACHED();
106 return ResourceLoadPriority::Low;
109 static Seconds deadDecodedDataDeletionIntervalForResourceType(CachedResource::Type type)
111 if (type == CachedResource::Script)
114 return MemoryCache::singleton().deadDecodedDataDeletionInterval();
117 DEFINE_DEBUG_ONLY_GLOBAL(RefCountedLeakCounter, cachedResourceLeakCounter, ("CachedResource"));
119 CachedResource::CachedResource(CachedResourceRequest&& request, Type type, PAL::SessionID sessionID)
120 : m_resourceRequest(request.releaseResourceRequest())
121 , m_options(request.options())
122 , m_decodedDataDeletionTimer(*this, &CachedResource::destroyDecodedData, deadDecodedDataDeletionIntervalForResourceType(type))
123 , m_sessionID(sessionID)
124 , m_loadPriority(defaultPriorityForResourceType(type))
125 , m_responseTimestamp(WallTime::now())
126 , m_fragmentIdentifierForRequest(request.releaseFragmentIdentifier())
127 , m_origin(request.releaseOrigin())
128 , m_initiatorName(request.initiatorName())
129 , m_isLinkPreload(request.isLinkPreload())
130 , m_hasUnknownEncoding(request.isLinkPreload())
132 , m_ignoreForRequestCount(request.ignoreForRequestCount())
134 ASSERT(sessionID.isValid());
136 setLoadPriority(request.priority());
138 cachedResourceLeakCounter.increment();
141 // FIXME: We should have a better way of checking for Navigation loads, maybe FetchMode::Options::Navigate.
142 ASSERT(m_origin || m_type == CachedResource::MainResource);
144 if (isRequestCrossOrigin(m_origin.get(), m_resourceRequest.url(), m_options))
148 // FIXME: For this constructor, we should probably mandate that the URL has no fragment identifier.
149 CachedResource::CachedResource(const URL& url, Type type, PAL::SessionID sessionID)
150 : m_resourceRequest(url)
151 , m_decodedDataDeletionTimer(*this, &CachedResource::destroyDecodedData, deadDecodedDataDeletionIntervalForResourceType(type))
152 , m_sessionID(sessionID)
153 , m_responseTimestamp(WallTime::now())
154 , m_fragmentIdentifierForRequest(CachedResourceRequest::splitFragmentIdentifierFromRequestURL(m_resourceRequest))
158 ASSERT(sessionID.isValid());
160 cachedResourceLeakCounter.increment();
164 CachedResource::~CachedResource()
166 ASSERT(!m_resourceToRevalidate); // Should be true because canDelete() checks this.
170 ASSERT(url().isNull() || !allowsCaching() || MemoryCache::singleton().resourceForRequest(resourceRequest(), sessionID()) != this);
174 cachedResourceLeakCounter.decrement();
177 if (m_owningCachedResourceLoader)
178 m_owningCachedResourceLoader->removeCachedResource(*this);
181 void CachedResource::failBeforeStarting()
183 // FIXME: What if resources in other frames were waiting for this revalidation?
184 LOG(ResourceLoading, "Cannot start loading '%s'", url().string().latin1().data());
185 if (allowsCaching() && m_resourceToRevalidate)
186 MemoryCache::singleton().revalidationFailed(*this);
187 error(CachedResource::LoadError);
190 void CachedResource::load(CachedResourceLoader& cachedResourceLoader)
192 if (!cachedResourceLoader.frame()) {
193 RELEASE_LOG_IF_ALLOWED("load: No associated frame");
194 failBeforeStarting();
197 Frame& frame = *cachedResourceLoader.frame();
199 // Prevent new loads if we are in the PageCache or being added to the PageCache.
200 // We query the top document because new frames may be created in pagehide event handlers
201 // and their pageCacheState will not reflect the fact that they are about to enter page
203 if (auto* topDocument = frame.mainFrame().document()) {
204 if (topDocument->pageCacheState() != Document::NotInPageCache) {
205 RELEASE_LOG_IF_ALLOWED("load: Already in page cache or being added to it (frame = %p)", &frame);
206 failBeforeStarting();
211 FrameLoader& frameLoader = frame.loader();
212 if (m_options.securityCheck == DoSecurityCheck && (frameLoader.state() == FrameStateProvisional || !frameLoader.activeDocumentLoader() || frameLoader.activeDocumentLoader()->isStopping())) {
213 if (frameLoader.state() == FrameStateProvisional)
214 RELEASE_LOG_IF_ALLOWED("load: Failed security check -- state is provisional (frame = %p)", &frame);
215 else if (!frameLoader.activeDocumentLoader())
216 RELEASE_LOG_IF_ALLOWED("load: Failed security check -- not active document (frame = %p)", &frame);
217 else if (frameLoader.activeDocumentLoader()->isStopping())
218 RELEASE_LOG_IF_ALLOWED("load: Failed security check -- active loader is stopping (frame = %p)", &frame);
219 failBeforeStarting();
225 if (isCacheValidator()) {
226 CachedResource* resourceToRevalidate = m_resourceToRevalidate;
227 ASSERT(resourceToRevalidate->canUseCacheValidator());
228 ASSERT(resourceToRevalidate->isLoaded());
229 const String& lastModified = resourceToRevalidate->response().httpHeaderField(HTTPHeaderName::LastModified);
230 const String& eTag = resourceToRevalidate->response().httpHeaderField(HTTPHeaderName::ETag);
231 if (!lastModified.isEmpty() || !eTag.isEmpty()) {
232 ASSERT(cachedResourceLoader.cachePolicy(type(), url()) != CachePolicyReload);
233 if (cachedResourceLoader.cachePolicy(type(), url()) == CachePolicyRevalidate)
234 m_resourceRequest.setHTTPHeaderField(HTTPHeaderName::CacheControl, "max-age=0");
235 if (!lastModified.isEmpty())
236 m_resourceRequest.setHTTPHeaderField(HTTPHeaderName::IfModifiedSince, lastModified);
238 m_resourceRequest.setHTTPHeaderField(HTTPHeaderName::IfNoneMatch, eTag);
242 if (type() == CachedResource::LinkPrefetch)
243 m_resourceRequest.setHTTPHeaderField(HTTPHeaderName::Purpose, "prefetch");
244 m_resourceRequest.setPriority(loadPriority());
246 // Navigation algorithm is setting up the request before sending it to CachedResourceLoader?CachedResource.
247 // So no need for extra fields for MainResource.
248 if (type() != CachedResource::MainResource)
249 frameLoader.addExtraFieldsToSubresourceRequest(m_resourceRequest);
252 // FIXME: It's unfortunate that the cache layer and below get to know anything about fragment identifiers.
253 // We should look into removing the expectation of that knowledge from the platform network stacks.
254 ResourceRequest request(m_resourceRequest);
255 if (!m_fragmentIdentifierForRequest.isNull()) {
256 URL url = request.url();
257 url.setFragmentIdentifier(m_fragmentIdentifierForRequest);
259 m_fragmentIdentifierForRequest = String();
262 if (m_options.keepAlive) {
263 if (!cachedResourceLoader.keepaliveRequestTracker().tryRegisterRequest(*this)) {
264 setResourceError({ errorDomainWebKitInternal, 0, request.url(), "Reached maximum amount of queued data of 64Kb for keepalive requests"_s, ResourceError::Type::AccessControl });
265 failBeforeStarting();
268 // FIXME: We should not special-case Beacon here.
269 if (shouldUsePingLoad(type())) {
270 ASSERT(m_originalRequest);
271 CachedResourceHandle<CachedResource> protectedThis(this);
273 // FIXME: Move beacon loads to normal subresource loading to get normal inspector request instrumentation hooks.
274 unsigned long identifier = frame.page()->progress().createUniqueIdentifier();
275 InspectorInstrumentation::willSendRequestOfType(&frame, identifier, frameLoader.activeDocumentLoader(), request, InspectorInstrumentation::LoadType::Beacon);
277 platformStrategies()->loaderStrategy()->startPingLoad(frame, request, m_originalRequest->httpHeaderFields(), m_options, [this, protectedThis = WTFMove(protectedThis), protectedFrame = makeRef(frame), identifier] (const ResourceError& error, const ResourceResponse& response) {
278 if (!response.isNull())
279 InspectorInstrumentation::didReceiveResourceResponse(protectedFrame, identifier, protectedFrame->loader().activeDocumentLoader(), response, nullptr);
280 if (error.isNull()) {
281 finishLoading(nullptr);
282 NetworkLoadMetrics emptyMetrics;
283 InspectorInstrumentation::didFinishLoading(protectedFrame.ptr(), protectedFrame->loader().activeDocumentLoader(), identifier, emptyMetrics, nullptr);
285 setResourceError(error);
286 this->error(LoadError);
287 InspectorInstrumentation::didFailLoading(protectedFrame.ptr(), protectedFrame->loader().activeDocumentLoader(), identifier, error);
294 platformStrategies()->loaderStrategy()->loadResource(frame, *this, WTFMove(request), m_options, [this, protectedThis = CachedResourceHandle<CachedResource>(this), frame = makeRef(frame), loggingAllowed = cachedResourceLoader.isAlwaysOnLoggingAllowed()] (RefPtr<SubresourceLoader>&& loader) {
295 m_loader = WTFMove(loader);
297 RELEASE_LOG_IF(loggingAllowed, Network, "%p - CachedResource::load: Unable to create SubresourceLoader (frame = %p)", this, frame.ptr());
298 failBeforeStarting();
305 void CachedResource::loadFrom(const CachedResource& resource)
307 ASSERT(url() == resource.url());
308 ASSERT(type() == resource.type());
309 ASSERT(resource.status() == Status::Cached);
311 if (isCrossOrigin() && m_options.mode == FetchOptions::Mode::Cors) {
314 if (!WebCore::passesAccessControlCheck(resource.response(), m_options.storedCredentialsPolicy, *m_origin, errorMessage)) {
315 setResourceError(ResourceError(String(), 0, url(), errorMessage, ResourceError::Type::AccessControl));
320 setBodyDataFrom(resource);
321 setStatus(Status::Cached);
325 void CachedResource::setBodyDataFrom(const CachedResource& resource)
327 m_data = resource.m_data;
328 m_response = resource.m_response;
329 m_response.setTainting(m_responseTainting);
330 setDecodedSize(resource.decodedSize());
331 setEncodedSize(resource.encodedSize());
334 void CachedResource::checkNotify()
336 if (isLoading() || stillNeedsLoad())
339 CachedResourceClientWalker<CachedResourceClient> walker(m_clients);
340 while (CachedResourceClient* client = walker.next())
341 client->notifyFinished(*this);
344 void CachedResource::updateBuffer(SharedBuffer&)
346 ASSERT(dataBufferingPolicy() == BufferData);
349 void CachedResource::updateData(const char*, unsigned)
351 ASSERT(dataBufferingPolicy() == DoNotBufferData);
354 void CachedResource::finishLoading(SharedBuffer*)
360 void CachedResource::error(CachedResource::Status status)
363 ASSERT(errorOccurred());
370 void CachedResource::cancelLoad()
372 if (!isLoading() && !stillNeedsLoad())
375 setStatus(LoadError);
380 void CachedResource::finish()
382 if (!errorOccurred())
386 void CachedResource::setCrossOrigin()
388 ASSERT(m_options.mode != FetchOptions::Mode::SameOrigin);
389 m_responseTainting = (m_options.mode == FetchOptions::Mode::Cors) ? ResourceResponse::Tainting::Cors : ResourceResponse::Tainting::Opaque;
392 bool CachedResource::isCrossOrigin() const
394 return m_responseTainting != ResourceResponse::Tainting::Basic;
397 bool CachedResource::isCORSSameOrigin() const
399 // Following resource types do not use CORS
400 ASSERT(type() != CachedResource::Type::FontResource);
401 #if ENABLE(SVG_FONTS)
402 ASSERT(type() != CachedResource::Type::SVGFontResource);
405 ASSERT(type() != CachedResource::XSLStyleSheet);
408 // https://html.spec.whatwg.org/multipage/infrastructure.html#cors-same-origin
409 return !loadFailedOrCanceled() && m_responseTainting != ResourceResponse::Tainting::Opaque;
412 bool CachedResource::isExpired() const
414 if (m_response.isNull())
417 return computeCurrentAge(m_response, m_responseTimestamp) > freshnessLifetime(m_response);
420 static inline bool shouldCacheSchemeIndefinitely(StringView scheme)
423 if (equalLettersIgnoringASCIICase(scheme, "applewebdata"))
427 if (equalLettersIgnoringASCIICase(scheme, "resource"))
430 return equalLettersIgnoringASCIICase(scheme, "data");
433 Seconds CachedResource::freshnessLifetime(const ResourceResponse& response) const
435 if (!response.url().protocolIsInHTTPFamily()) {
436 StringView protocol = response.url().protocol();
437 if (!shouldCacheSchemeIndefinitely(protocol)) {
438 // Don't cache non-HTTP main resources since we can't check for freshness.
439 // FIXME: We should not cache subresources either, but when we tried this
440 // it caused performance and flakiness issues in our test infrastructure.
441 if (m_type == MainResource || SchemeRegistry::shouldAlwaysRevalidateURLScheme(protocol.toStringWithoutCopying()))
445 return Seconds::infinity();
448 return computeFreshnessLifetimeForHTTPFamily(response, m_responseTimestamp);
451 void CachedResource::redirectReceived(ResourceRequest&& request, const ResourceResponse& response, CompletionHandler<void(ResourceRequest&&)>&& completionHandler)
453 m_requestedFromNetworkingLayer = true;
454 if (response.isNull())
455 return completionHandler(WTFMove(request));
457 updateRedirectChainStatus(m_redirectChainCacheStatus, response);
458 completionHandler(WTFMove(request));
461 void CachedResource::setResponse(const ResourceResponse& response)
463 ASSERT(m_response.type() == ResourceResponse::Type::Default);
464 m_response = response;
465 m_varyingHeaderValues = collectVaryingRequestHeaders(m_resourceRequest, m_response, m_sessionID);
467 #if ENABLE(SERVICE_WORKER)
468 if (m_response.source() == ResourceResponse::Source::ServiceWorker) {
469 m_responseTainting = m_response.tainting();
473 m_response.setRedirected(m_redirectChainCacheStatus.status != RedirectChainCacheStatus::NoRedirection);
474 if (m_response.tainting() == ResourceResponse::Tainting::Basic || m_response.tainting() == ResourceResponse::Tainting::Cors)
475 m_response.setTainting(m_responseTainting);
478 void CachedResource::responseReceived(const ResourceResponse& response)
480 setResponse(response);
481 m_responseTimestamp = WallTime::now();
482 String encoding = response.textEncodingName();
483 if (!encoding.isNull())
484 setEncoding(encoding);
487 void CachedResource::clearLoader()
490 m_identifierForLoadWithoutResourceLoader = m_loader->identifier();
495 void CachedResource::addClient(CachedResourceClient& client)
497 if (addClientToSet(client))
498 didAddClient(client);
501 void CachedResource::didAddClient(CachedResourceClient& client)
503 if (m_decodedDataDeletionTimer.isActive())
504 m_decodedDataDeletionTimer.stop();
506 if (m_clientsAwaitingCallback.remove(&client))
507 m_clients.add(&client);
509 // FIXME: Make calls to notifyFinished async
510 if (!isLoading() && !stillNeedsLoad())
511 client.notifyFinished(*this);
514 bool CachedResource::addClientToSet(CachedResourceClient& client)
516 if (m_preloadResult == PreloadNotReferenced && client.shouldMarkAsReferenced()) {
518 m_preloadResult = PreloadReferencedWhileComplete;
519 else if (m_requestedFromNetworkingLayer)
520 m_preloadResult = PreloadReferencedWhileLoading;
522 m_preloadResult = PreloadReferenced;
524 if (allowsCaching() && !hasClients() && inCache())
525 MemoryCache::singleton().addToLiveResourcesSize(*this);
527 if ((m_type == RawResource || m_type == MainResource) && !m_response.isNull() && !m_proxyResource) {
528 // Certain resources (especially XHRs and main resources) do crazy things if an asynchronous load returns
529 // synchronously (e.g., scripts may not have set all the state they need to handle the load).
530 // Therefore, rather than immediately sending callbacks on a cache hit like other CachedResources,
531 // we schedule the callbacks and ensure we never finish synchronously.
532 ASSERT(!m_clientsAwaitingCallback.contains(&client));
533 m_clientsAwaitingCallback.add(&client, std::make_unique<Callback>(*this, client));
537 m_clients.add(&client);
541 void CachedResource::removeClient(CachedResourceClient& client)
543 auto callback = m_clientsAwaitingCallback.take(&client);
545 ASSERT(!m_clients.contains(&client));
549 ASSERT(m_clients.contains(&client));
550 m_clients.remove(&client);
551 didRemoveClient(client);
554 if (deleteIfPossible()) {
555 // `this` object is dead here.
562 auto& memoryCache = MemoryCache::singleton();
563 if (allowsCaching() && inCache()) {
564 memoryCache.removeFromLiveResourcesSize(*this);
565 memoryCache.removeFromLiveDecodedResourcesList(*this);
567 if (!m_switchingClientsToRevalidatedResource)
569 destroyDecodedDataIfNeeded();
571 if (!allowsCaching())
574 if (response().cacheControlContainsNoStore() && url().protocolIs("https")) {
576 // "no-store: ... MUST make a best-effort attempt to remove the information from volatile storage as promptly as possible"
577 // "... History buffers MAY store such responses as part of their normal operation."
578 // We allow non-secure content to be reused in history, but we do not allow secure content to be reused.
579 memoryCache.remove(*this);
581 memoryCache.pruneSoon();
584 void CachedResource::allClientsRemoved()
586 if (isLinkPreload() && m_loader)
587 m_loader->cancelIfNotFinishing();
590 void CachedResource::destroyDecodedDataIfNeeded()
594 if (!MemoryCache::singleton().deadDecodedDataDeletionInterval())
596 m_decodedDataDeletionTimer.restart();
599 void CachedResource::decodedDataDeletionTimerFired()
601 destroyDecodedData();
604 bool CachedResource::deleteIfPossible()
608 InspectorInstrumentation::willDestroyCachedResource(*this);
613 m_data->hintMemoryNotNeededSoon();
618 void CachedResource::setDecodedSize(unsigned size)
620 if (size == m_decodedSize)
623 long long delta = static_cast<long long>(size) - m_decodedSize;
625 // The object must be moved to a different queue, since its size has been changed.
626 // Remove before updating m_decodedSize, so we find the resource in the correct LRU list.
627 if (allowsCaching() && inCache())
628 MemoryCache::singleton().removeFromLRUList(*this);
630 m_decodedSize = size;
632 if (allowsCaching() && inCache()) {
633 auto& memoryCache = MemoryCache::singleton();
634 // Now insert into the new LRU list.
635 memoryCache.insertInLRUList(*this);
637 // Insert into or remove from the live decoded list if necessary.
638 // When inserting into the LiveDecodedResourcesList it is possible
639 // that the m_lastDecodedAccessTime is still zero or smaller than
640 // the m_lastDecodedAccessTime of the current list head. This is a
641 // violation of the invariant that the list is to be kept sorted
642 // by access time. The weakening of the invariant does not pose
643 // a problem. For more details please see: https://bugs.webkit.org/show_bug.cgi?id=30209
644 bool inLiveDecodedResourcesList = memoryCache.inLiveDecodedResourcesList(*this);
645 if (m_decodedSize && !inLiveDecodedResourcesList && hasClients())
646 memoryCache.insertInLiveDecodedResourcesList(*this);
647 else if (!m_decodedSize && inLiveDecodedResourcesList)
648 memoryCache.removeFromLiveDecodedResourcesList(*this);
650 // Update the cache's size totals.
651 memoryCache.adjustSize(hasClients(), delta);
655 void CachedResource::setEncodedSize(unsigned size)
657 if (size == m_encodedSize)
660 long long delta = static_cast<long long>(size) - m_encodedSize;
662 // The object must be moved to a different queue, since its size has been changed.
663 // Remove before updating m_encodedSize, so we find the resource in the correct LRU list.
664 if (allowsCaching() && inCache())
665 MemoryCache::singleton().removeFromLRUList(*this);
667 m_encodedSize = size;
669 if (allowsCaching() && inCache()) {
670 auto& memoryCache = MemoryCache::singleton();
671 memoryCache.insertInLRUList(*this);
672 memoryCache.adjustSize(hasClients(), delta);
676 void CachedResource::didAccessDecodedData(MonotonicTime timeStamp)
678 m_lastDecodedAccessTime = timeStamp;
680 if (allowsCaching() && inCache()) {
681 auto& memoryCache = MemoryCache::singleton();
682 if (memoryCache.inLiveDecodedResourcesList(*this)) {
683 memoryCache.removeFromLiveDecodedResourcesList(*this);
684 memoryCache.insertInLiveDecodedResourcesList(*this);
686 memoryCache.pruneSoon();
690 void CachedResource::setResourceToRevalidate(CachedResource* resource)
693 ASSERT(!m_resourceToRevalidate);
694 ASSERT(resource != this);
695 ASSERT(m_handlesToRevalidate.isEmpty());
696 ASSERT(resource->type() == type());
697 ASSERT(!resource->m_proxyResource);
699 LOG(ResourceLoading, "CachedResource %p setResourceToRevalidate %p", this, resource);
701 resource->m_proxyResource = this;
702 m_resourceToRevalidate = resource;
705 void CachedResource::clearResourceToRevalidate()
707 ASSERT(m_resourceToRevalidate);
708 ASSERT(m_resourceToRevalidate->m_proxyResource == this);
710 if (m_switchingClientsToRevalidatedResource)
713 m_resourceToRevalidate->m_proxyResource = nullptr;
714 m_resourceToRevalidate->deleteIfPossible();
716 m_handlesToRevalidate.clear();
717 m_resourceToRevalidate = nullptr;
721 void CachedResource::switchClientsToRevalidatedResource()
723 ASSERT(m_resourceToRevalidate);
724 ASSERT(m_resourceToRevalidate->inCache());
727 LOG(ResourceLoading, "CachedResource %p switchClientsToRevalidatedResource %p", this, m_resourceToRevalidate);
729 m_switchingClientsToRevalidatedResource = true;
730 for (auto& handle : m_handlesToRevalidate) {
731 handle->m_resource = m_resourceToRevalidate;
732 m_resourceToRevalidate->registerHandle(handle);
735 ASSERT(!m_handleCount);
736 m_handlesToRevalidate.clear();
738 Vector<CachedResourceClient*> clientsToMove;
739 for (auto& entry : m_clients) {
740 CachedResourceClient* client = entry.key;
741 unsigned count = entry.value;
743 clientsToMove.append(client);
748 for (auto& client : clientsToMove)
749 removeClient(*client);
750 ASSERT(m_clients.isEmpty());
752 for (auto& client : clientsToMove)
753 m_resourceToRevalidate->addClientToSet(*client);
754 for (auto& client : clientsToMove) {
755 // Calling didAddClient may do anything, including trying to cancel revalidation.
756 // Assert that it didn't succeed.
757 ASSERT(m_resourceToRevalidate);
758 // Calling didAddClient for a client may end up removing another client. In that case it won't be in the set anymore.
759 if (m_resourceToRevalidate->m_clients.contains(client))
760 m_resourceToRevalidate->didAddClient(*client);
762 m_switchingClientsToRevalidatedResource = false;
765 void CachedResource::updateResponseAfterRevalidation(const ResourceResponse& validatingResponse)
767 m_responseTimestamp = WallTime::now();
769 updateResponseHeadersAfterRevalidation(m_response, validatingResponse);
772 void CachedResource::registerHandle(CachedResourceHandleBase* h)
775 if (m_resourceToRevalidate)
776 m_handlesToRevalidate.add(h);
779 void CachedResource::unregisterHandle(CachedResourceHandleBase* h)
781 ASSERT(m_handleCount > 0);
784 if (m_resourceToRevalidate)
785 m_handlesToRevalidate.remove(h);
791 bool CachedResource::canUseCacheValidator() const
793 if (m_loading || errorOccurred())
796 if (m_response.cacheControlContainsNoStore())
798 return m_response.hasCacheValidatorFields();
801 CachedResource::RevalidationDecision CachedResource::makeRevalidationDecision(CachePolicy cachePolicy) const
803 switch (cachePolicy) {
804 case CachePolicyHistoryBuffer:
805 return RevalidationDecision::No;
807 case CachePolicyReload:
808 return RevalidationDecision::YesDueToCachePolicy;
810 case CachePolicyRevalidate:
811 if (m_response.cacheControlContainsImmutable() && m_response.url().protocolIs("https")) {
813 return RevalidationDecision::YesDueToExpired;
814 return RevalidationDecision::No;
816 return RevalidationDecision::YesDueToCachePolicy;
818 case CachePolicyVerify:
819 if (m_response.cacheControlContainsNoCache())
820 return RevalidationDecision::YesDueToNoCache;
821 // FIXME: Cache-Control:no-store should prevent storing, not reuse.
822 if (m_response.cacheControlContainsNoStore())
823 return RevalidationDecision::YesDueToNoStore;
826 return RevalidationDecision::YesDueToExpired;
828 return RevalidationDecision::No;
830 ASSERT_NOT_REACHED();
831 return RevalidationDecision::No;
834 bool CachedResource::redirectChainAllowsReuse(ReuseExpiredRedirectionOrNot reuseExpiredRedirection) const
836 return WebCore::redirectChainAllowsReuse(m_redirectChainCacheStatus, reuseExpiredRedirection);
839 bool CachedResource::varyHeaderValuesMatch(const ResourceRequest& request)
841 if (m_varyingHeaderValues.isEmpty())
844 return verifyVaryingRequestHeaders(m_varyingHeaderValues, request, m_sessionID);
847 unsigned CachedResource::overheadSize() const
849 static const int kAverageClientsHashMapSize = 384;
850 return sizeof(CachedResource) + m_response.memoryUsage() + kAverageClientsHashMapSize + m_resourceRequest.url().string().length() * 2;
853 bool CachedResource::areAllClientsXMLHttpRequests() const
855 if (type() != RawResource)
858 for (auto& client : m_clients) {
859 if (!client.key->isXMLHttpRequest())
865 void CachedResource::setLoadPriority(const std::optional<ResourceLoadPriority>& loadPriority)
868 m_loadPriority = loadPriority.value();
870 m_loadPriority = defaultPriorityForResourceType(type());
873 inline CachedResource::Callback::Callback(CachedResource& resource, CachedResourceClient& client)
874 : m_resource(resource)
876 , m_timer(*this, &Callback::timerFired)
878 m_timer.startOneShot(0_s);
881 inline void CachedResource::Callback::cancel()
883 if (m_timer.isActive())
887 void CachedResource::Callback::timerFired()
889 m_resource.didAddClient(m_client);
892 #if USE(FOUNDATION) || USE(SOUP)
894 void CachedResource::tryReplaceEncodedData(SharedBuffer& newBuffer)
899 if (!mayTryReplaceEncodedData())
902 // We have to do the memcmp because we can't tell if the replacement file backed data is for the
903 // same resource or if we made a second request with the same URL which gave us a different
904 // resource. We have seen this happen for cached POST resources.
905 if (m_data->size() != newBuffer.size() || memcmp(m_data->data(), newBuffer.data(), m_data->size()))
909 m_data->append(newBuffer);
910 didReplaceSharedBufferContents();