| File: | Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp |
| Warning: | line 627, column 19 Local variable 'resource' is uncounted and unsafe |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* |
| 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) 2004-2025 Apple Inc. All rights reserved. |
| 6 | |
| 7 | This library is free software; you can redistribute it and/or |
| 8 | modify it under the terms of the GNU Library General Public |
| 9 | License as published by the Free Software Foundation; either |
| 10 | version 2 of the License, or (at your option) any later version. |
| 11 | |
| 12 | This library is distributed in the hope that it will be useful, |
| 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 15 | Library General Public License for more details. |
| 16 | |
| 17 | You should have received a copy of the GNU Library General Public License |
| 18 | along with this library; see the file COPYING.LIB. If not, write to |
| 19 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| 20 | Boston, MA 02110-1301, USA. |
| 21 | */ |
| 22 | |
| 23 | #include "config.h" |
| 24 | #include "MemoryCache.h" |
| 25 | |
| 26 | #include "BitmapImage.h" |
| 27 | #include "CachedImage.h" |
| 28 | #include "CachedImageClient.h" |
| 29 | #include "CachedResource.h" |
| 30 | #include "CachedResourceHandle.h" |
| 31 | #include "ClientOrigin.h" |
| 32 | #include "Document.h" |
| 33 | #include "FrameLoader.h" |
| 34 | #include "FrameLoaderTypes.h" |
| 35 | #include "Image.h" |
| 36 | #include "LocalFrameView.h" |
| 37 | #include "Logging.h" |
| 38 | #include "PublicSuffixStore.h" |
| 39 | #include "SharedBuffer.h" |
| 40 | #include "WorkerGlobalScope.h" |
| 41 | #include "WorkerLoaderProxy.h" |
| 42 | #include "WorkerThread.h" |
| 43 | #include <pal/Logging.h> |
| 44 | #include <stdio.h> |
| 45 | #include <wtf/MathExtras.h> |
| 46 | #include <wtf/NeverDestroyed.h> |
| 47 | #include <wtf/SetForScope.h> |
| 48 | #include <wtf/text/CString.h> |
| 49 | |
| 50 | namespace WebCore { |
| 51 | |
| 52 | static const int cDefaultCacheCapacity = 8192 * 1024; |
| 53 | static const Seconds cMinDelayBeforeLiveDecodedPrune { 1_s }; |
| 54 | static const float cTargetPrunePercentage = .95f; // Percentage of capacity toward which we prune, to avoid immediately pruning again. |
| 55 | |
| 56 | MemoryCache& MemoryCache::singleton() |
| 57 | { |
| 58 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (58, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 59 | static NeverDestroyed<MemoryCache> memoryCache; |
| 60 | return memoryCache; |
| 61 | } |
| 62 | |
| 63 | MemoryCache::MemoryCache() |
| 64 | : m_capacity(cDefaultCacheCapacity) |
| 65 | , m_maxDeadCapacity(cDefaultCacheCapacity) |
| 66 | , m_pruneTimer(*this, &MemoryCache::prune) |
| 67 | { |
| 68 | static_assert(sizeof(long long) > sizeof(unsigned), "Numerical overflow can happen when adjusting the size of the cached memory."); |
| 69 | |
| 70 | static std::once_flag onceFlag; |
| 71 | std::call_once(onceFlag, [] { |
| 72 | PAL::registerNotifyCallback("com.apple.WebKit.showMemoryCache"_s, [] { |
| 73 | MemoryCache::singleton().dumpStats(); |
| 74 | MemoryCache::singleton().dumpLRULists(true); |
| 75 | }); |
| 76 | }); |
| 77 | } |
| 78 | |
| 79 | auto MemoryCache::sessionResourceMap(PAL::SessionID sessionID) const -> CachedResourceMap* |
| 80 | { |
| 81 | RELEASE_ASSERT(sessionID.isValid())do { if (__builtin_expect(!!(!(sessionID.isValid())), 0)) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (81, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 82 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (82, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 83 | RELEASE_ASSERT(m_sessionResources.isValidKey(sessionID))do { if (__builtin_expect(!!(!(m_sessionResources.isValidKey( sessionID))), 0)) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(83, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 84 | return m_sessionResources.get(sessionID); |
| 85 | } |
| 86 | |
| 87 | auto MemoryCache::ensureSessionResourceMap(PAL::SessionID sessionID) -> CachedResourceMap& |
| 88 | { |
| 89 | RELEASE_ASSERT(sessionID.isValid())do { if (__builtin_expect(!!(!(sessionID.isValid())), 0)) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (89, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 90 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (90, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 91 | RELEASE_ASSERT(m_sessionResources.isValidKey(sessionID))do { if (__builtin_expect(!!(!(m_sessionResources.isValidKey( sessionID))), 0)) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(91, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 92 | auto& map = m_sessionResources.add(sessionID, nullptr).iterator->value; |
| 93 | if (!map) |
| 94 | map = makeUnique<CachedResourceMap>(); |
| 95 | return *map; |
| 96 | } |
| 97 | |
| 98 | bool MemoryCache::shouldRemoveFragmentIdentifier(const URL& originalURL) |
| 99 | { |
| 100 | if (!originalURL.hasFragmentIdentifier()) |
| 101 | return false; |
| 102 | // Strip away fragment identifier from HTTP URLs. |
| 103 | // Data URLs must be unmodified. For file and custom URLs clients may expect resources |
| 104 | // to be unique even when they differ by the fragment identifier only. |
| 105 | return originalURL.protocolIsInHTTPFamily(); |
| 106 | } |
| 107 | |
| 108 | URL MemoryCache::removeFragmentIdentifierIfNeeded(const URL& originalURL) |
| 109 | { |
| 110 | if (!shouldRemoveFragmentIdentifier(originalURL)) |
| 111 | return originalURL; |
| 112 | URL url = originalURL; |
| 113 | url.removeFragmentIdentifier(); |
| 114 | return url; |
| 115 | } |
| 116 | |
| 117 | bool MemoryCache::add(CachedResource& resource) |
| 118 | { |
| 119 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (119, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 120 | |
| 121 | if (disabled()) |
| 122 | return false; |
| 123 | |
| 124 | if (resource.resourceRequest().httpMethod() != "GET"_s) |
| 125 | return false; |
| 126 | |
| 127 | ASSERT(isMainThread())((void)0); |
| 128 | |
| 129 | auto key = std::make_pair(resource.url(), resource.cachePartition()); |
| 130 | |
| 131 | auto& resources = ensureSessionResourceMap(resource.sessionID()); |
| 132 | |
| 133 | RELEASE_ASSERT(!resources.get(key))do { if (__builtin_expect(!!(!(!resources.get(key))), 0)) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (133, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 134 | resources.set(key, &resource); |
| 135 | resource.setInCache(true); |
| 136 | |
| 137 | resourceAccessed(resource); |
| 138 | |
| 139 | LOG(ResourceLoading, "MemoryCache::add Added '%.255s', resource %p\n", resource.url().string().latin1().data(), &resource)((void)0); |
| 140 | return true; |
| 141 | } |
| 142 | |
| 143 | void MemoryCache::revalidationSucceeded(CachedResource& revalidatingResource, const ResourceResponse& response) |
| 144 | { |
| 145 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (145, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 146 | ASSERT(response.source() == ResourceResponse::Source::MemoryCacheAfterValidation)((void)0); |
| 147 | ASSERT(revalidatingResource.resourceToRevalidate())((void)0); |
| 148 | RefPtr protectedRevalidatingResource { revalidatingResource }; |
| 149 | RefPtr resource = *revalidatingResource.resourceToRevalidate(); |
| 150 | ASSERT(!resource->inCache())((void)0); |
| 151 | ASSERT(resource->isLoaded())((void)0); |
| 152 | |
| 153 | remove(revalidatingResource); |
| 154 | |
| 155 | // A resource with the same URL may have been added back in the cache during revalidation. |
| 156 | // In this case, we remove the cached resource and replace it with our freshly revalidated |
| 157 | // one. |
| 158 | std::pair key { resource->url(), resource->cachePartition() }; |
| 159 | if (auto* existingResources = sessionResourceMap(resource->sessionID())) { |
| 160 | if (RefPtr existingResource = existingResources->get(key)) |
| 161 | remove(*existingResource); |
| 162 | } |
| 163 | |
| 164 | // Don't move the call to ensureSessionResourceMap() in this function as the calls to |
| 165 | // remove() above could invalidate the reference returned by ensureSessionResourceMap(). |
| 166 | auto& resources = ensureSessionResourceMap(resource->sessionID()); |
| 167 | ASSERT(!resources.contains(key))((void)0); |
| 168 | resources.add(key, resource.get()); |
| 169 | resource->setInCache(true); |
| 170 | resource->updateResponseAfterRevalidation(response); |
| 171 | insertInLRUList(*resource); |
| 172 | long long delta = resource->size(); |
| 173 | if (resource->decodedSize() && resource->hasClients()) |
| 174 | insertInLiveDecodedResourcesList(*resource); |
| 175 | if (delta) |
| 176 | adjustSize(resource->hasClients(), delta); |
| 177 | |
| 178 | revalidatingResource.switchClientsToRevalidatedResource(); |
| 179 | // This deletes the revalidating resource. |
| 180 | revalidatingResource.clearResourceToRevalidate(); |
| 181 | } |
| 182 | |
| 183 | void MemoryCache::revalidationFailed(CachedResource& revalidatingResource) |
| 184 | { |
| 185 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (185, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 186 | LOG(ResourceLoading, "Revalidation failed for %p", &revalidatingResource)((void)0); |
| 187 | ASSERT(revalidatingResource.resourceToRevalidate())((void)0); |
| 188 | revalidatingResource.clearResourceToRevalidate(); |
| 189 | } |
| 190 | |
| 191 | CachedResource* MemoryCache::resourceForRequest(const ResourceRequest& request, PAL::SessionID sessionID) |
| 192 | { |
| 193 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (193, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 194 | // FIXME: Change all clients to make sure HTTP(s) URLs have no fragment identifiers before calling here. |
| 195 | // CachedResourceLoader is now doing this. Add an assertion once all other clients are doing it too. |
| 196 | auto* resources = sessionResourceMap(sessionID); |
| 197 | return resources ? resourceForRequestImpl(request, *resources) : nullptr; |
| 198 | } |
| 199 | |
| 200 | CachedResource* MemoryCache::resourceForRequestImpl(const ResourceRequest& request, CachedResourceMap& resources) |
| 201 | { |
| 202 | ASSERT(isMainThread())((void)0); |
| 203 | URL url = removeFragmentIdentifierIfNeeded(request.url()); |
| 204 | |
| 205 | auto key = std::make_pair(WTF::move(url), request.cachePartition()); |
| 206 | return resources.get(key); |
| 207 | } |
| 208 | |
| 209 | unsigned MemoryCache::deadCapacity() const |
| 210 | { |
| 211 | // Dead resource capacity is whatever space is not occupied by live resources, bounded by an independent minimum and maximum. |
| 212 | unsigned capacity = m_capacity - std::min(m_liveSize, m_capacity); // Start with available capacity. |
| 213 | capacity = std::max(capacity, m_minDeadCapacity); // Make sure it's above the minimum. |
| 214 | capacity = std::min(capacity, m_maxDeadCapacity); // Make sure it's below the maximum. |
| 215 | return capacity; |
| 216 | } |
| 217 | |
| 218 | unsigned MemoryCache::liveCapacity() const |
| 219 | { |
| 220 | // Live resource capacity is whatever is left over after calculating dead resource capacity. |
| 221 | return m_capacity - deadCapacity(); |
| 222 | } |
| 223 | |
| 224 | void MemoryCache::pruneLiveResources(bool shouldDestroyDecodedDataForAllLiveResources) |
| 225 | { |
| 226 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (226, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 227 | unsigned capacity = shouldDestroyDecodedDataForAllLiveResources ? 0 : liveCapacity(); |
| 228 | if (capacity && m_liveSize <= capacity) |
| 229 | return; |
| 230 | |
| 231 | unsigned targetSize = static_cast<unsigned>(capacity * cTargetPrunePercentage); // Cut by a percentage to avoid immediately pruning again. |
| 232 | |
| 233 | pruneLiveResourcesToSize(targetSize, shouldDestroyDecodedDataForAllLiveResources); |
| 234 | } |
| 235 | |
| 236 | void MemoryCache::forEachResource(NOESCAPE[[clang::noescape]] const Function<void(CachedResource&)>& function) |
| 237 | { |
| 238 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (238, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 239 | Vector<CachedResourceHandle<CachedResource>> allResources; |
| 240 | for (auto& lruList : m_allResources) { |
| 241 | allResources.reserveCapacity(allResources.size() + lruList->computeSize()); |
| 242 | allResources.appendRange(lruList->begin(), lruList->end()); |
| 243 | } |
| 244 | for (auto& resource : allResources) { |
| 245 | if (RefPtr resourceHandle = resource.get()) |
| 246 | function(*resourceHandle); |
| 247 | } |
| 248 | } |
| 249 | |
| 250 | void MemoryCache::forEachSessionResource(PAL::SessionID sessionID, NOESCAPE[[clang::noescape]] const Function<void(CachedResource&)>& function) |
| 251 | { |
| 252 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (252, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 253 | RELEASE_ASSERT(m_sessionResources.isValidKey(sessionID))do { if (__builtin_expect(!!(!(m_sessionResources.isValidKey( sessionID))), 0)) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(253, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 254 | auto it = m_sessionResources.find(sessionID); |
| 255 | if (it == m_sessionResources.end()) |
| 256 | return; |
| 257 | |
| 258 | for (auto& weakResource : copyToVector(it->value->values())) { |
| 259 | if (RefPtr resource = weakResource.get()) |
| 260 | function(*resource); |
| 261 | } |
| 262 | } |
| 263 | |
| 264 | void MemoryCache::destroyDecodedDataForAllImages() |
| 265 | { |
| 266 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (266, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 267 | forEachResource([](CachedResource& resource) { |
| 268 | if (auto cachedImage = dynamicDowncast<CachedImage>(resource)) { |
| 269 | if (RefPtr image = cachedImage->image()) |
| 270 | image->destroyDecodedData(); |
| 271 | } |
| 272 | }); |
| 273 | } |
| 274 | |
| 275 | void MemoryCache::pruneLiveResourcesToSize(unsigned targetSize, bool shouldDestroyDecodedDataForAllLiveResources) |
| 276 | { |
| 277 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (277, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 278 | if (m_inPruneResources) |
| 279 | return; |
| 280 | |
| 281 | LOG(ResourceLoading, "MemoryCache::pruneLiveResourcesToSize(%d, shouldDestroyDecodedDataForAllLiveResources = %d)", targetSize, shouldDestroyDecodedDataForAllLiveResources)((void)0); |
| 282 | |
| 283 | SetForScope reentrancyProtector(m_inPruneResources, true); |
| 284 | |
| 285 | MonotonicTime currentTime = LocalFrameView::currentPaintTimeStamp(); |
| 286 | if (!currentTime) // In case prune is called directly, outside of a Frame paint. |
| 287 | currentTime = MonotonicTime::now(); |
| 288 | |
| 289 | // Destroy any decoded data in live objects that we can. |
| 290 | // Start from the head, since this is the least recently accessed of the objects. |
| 291 | |
| 292 | // The list might not be sorted by the m_lastDecodedAccessTime. The impact |
| 293 | // of this weaker invariant is minor as the below if statement to check the |
| 294 | // elapsedTime will evaluate to false as the currentTime will be a lot |
| 295 | // greater than the current->m_lastDecodedAccessTime. |
| 296 | // For more details see: https://bugs.webkit.org/show_bug.cgi?id=30209 |
| 297 | auto it = m_liveDecodedResources.begin(); |
| 298 | while (it != m_liveDecodedResources.end()) { |
| 299 | RefPtr current = *it; |
| 300 | |
| 301 | LOG(ResourceLoading, " live resource %p %.255s - loaded %d, decodedSize %u", current.get(), current->url().string().utf8().data(), current->isLoaded(), current->decodedSize())((void)0); |
| 302 | |
| 303 | // Increment the iterator now because the call to destroyDecodedData() below |
| 304 | // may cause a call to ListHashSet::remove() and invalidate the current |
| 305 | // iterator. Note that this is safe because unlike iteration of most |
| 306 | // WTF Hash data structures, iteration is guaranteed safe against mutation |
| 307 | // of the ListHashSet, except for removal of the item currently pointed to |
| 308 | // by a given iterator. |
| 309 | ++it; |
| 310 | |
| 311 | ASSERT(current->hasClients())((void)0); |
| 312 | if (current->isLoaded() && current->decodedSize()) { |
| 313 | // Check to see if the remaining resources are too new to prune. |
| 314 | Seconds elapsedTime = currentTime - current->m_lastDecodedAccessTime; |
| 315 | if (!shouldDestroyDecodedDataForAllLiveResources && elapsedTime < cMinDelayBeforeLiveDecodedPrune) { |
| 316 | LOG(ResourceLoading, " current time is less than min delay before pruning (%.3fms)", elapsedTime.milliseconds())((void)0); |
| 317 | return; |
| 318 | } |
| 319 | |
| 320 | // Destroy our decoded data. This will remove us from m_liveDecodedResources, and possibly move us |
| 321 | // to a different LRU list in m_allResources. |
| 322 | current->destroyDecodedData(); |
| 323 | |
| 324 | if (targetSize && m_liveSize <= targetSize) |
| 325 | return; |
| 326 | } |
| 327 | } |
| 328 | } |
| 329 | |
| 330 | void MemoryCache::pruneDeadResources() |
| 331 | { |
| 332 | LOG(ResourceLoading, "MemoryCache::pruneDeadResources")((void)0); |
| 333 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (333, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 334 | |
| 335 | unsigned capacity = deadCapacity(); |
| 336 | if (capacity && m_deadSize <= capacity) |
| 337 | return; |
| 338 | |
| 339 | unsigned targetSize = static_cast<unsigned>(capacity * cTargetPrunePercentage); // Cut by a percentage to avoid immediately pruning again. |
| 340 | pruneDeadResourcesToSize(targetSize); |
| 341 | } |
| 342 | |
| 343 | void MemoryCache::pruneDeadResourcesToSize(unsigned targetSize) |
| 344 | { |
| 345 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (345, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 346 | if (m_inPruneResources) |
| 347 | return; |
| 348 | |
| 349 | LOG(ResourceLoading, "MemoryCache::pruneDeadResourcesToSize(%d)", targetSize)((void)0); |
| 350 | |
| 351 | SetForScope reentrancyProtector(m_inPruneResources, true); |
| 352 | |
| 353 | if (targetSize && m_deadSize <= targetSize) |
| 354 | return; |
| 355 | |
| 356 | bool canShrinkLRULists = true; |
| 357 | for (int i = m_allResources.size() - 1; i >= 0; i--) { |
| 358 | // Make a copy of the LRUList first (and ref the resources) as calling |
| 359 | // destroyDecodedData() can alter the LRUList. |
| 360 | auto lruList = copyToVector(*m_allResources[i]); |
| 361 | |
| 362 | LOG(ResourceLoading, " lru list (size %zu) - flushing stage", lruList.size())((void)0); |
| 363 | |
| 364 | // First flush all the decoded data in this queue. |
| 365 | // Remove from the head, since this is the least frequently accessed of the objects. |
| 366 | for (auto& weakResource : lruList) { |
| 367 | RefPtr resource = weakResource.get(); |
| 368 | if (!resource) |
| 369 | continue; |
| 370 | |
| 371 | LOG(ResourceLoading, " lru resource %p - in cache %d, has clients %d, preloaded %d, loaded %d", resource.get(), resource->inCache(), resource->hasClients(), resource->isPreloaded(), resource->isLoaded())((void)0); |
| 372 | if (!resource->inCache()) |
| 373 | continue; |
| 374 | |
| 375 | if (!resource->hasClients() && !resource->isPreloaded() && resource->isLoaded()) { |
| 376 | // Destroy our decoded data. This will remove us from |
| 377 | // m_liveDecodedResources, and possibly move us to a different |
| 378 | // LRU list in m_allResources. |
| 379 | |
| 380 | LOG(ResourceLoading, " lru resource %p destroyDecodedData", resource.get())((void)0); |
| 381 | |
| 382 | resource->destroyDecodedData(); |
| 383 | |
| 384 | if (targetSize && m_deadSize <= targetSize) |
| 385 | return; |
| 386 | } |
| 387 | } |
| 388 | |
| 389 | LOG(ResourceLoading, " lru list (size %zu) - eviction stage", lruList.size())((void)0); |
| 390 | |
| 391 | // Now evict objects from this list. |
| 392 | // Remove from the head, since this is the least frequently accessed of the objects. |
| 393 | for (auto& weakResource : lruList) { |
| 394 | RefPtr resource = weakResource.get(); |
| 395 | if (!resource) |
| 396 | continue; |
| 397 | |
| 398 | LOG(ResourceLoading, " lru resource %p - in cache %d, has clients %d, preloaded %d, loaded %d", resource.get(), resource->inCache(), resource->hasClients(), resource->isPreloaded(), resource->isLoaded())((void)0); |
| 399 | if (!resource->inCache()) |
| 400 | continue; |
| 401 | |
| 402 | if (!resource->hasClients() && !resource->isPreloaded() && !resource->isCacheValidator()) { |
| 403 | remove(*resource); |
| 404 | if (targetSize && m_deadSize <= targetSize) |
| 405 | return; |
| 406 | } |
| 407 | } |
| 408 | |
| 409 | // Shrink the vector back down so we don't waste time inspecting |
| 410 | // empty LRU lists on future prunes. |
| 411 | if (!m_allResources[i]->isEmptyIgnoringNullReferences()) |
| 412 | canShrinkLRULists = false; |
| 413 | else if (canShrinkLRULists) |
| 414 | m_allResources.shrink(i); |
| 415 | } |
| 416 | } |
| 417 | |
| 418 | void MemoryCache::setCapacities(unsigned minDeadBytes, unsigned maxDeadBytes, unsigned totalBytes) |
| 419 | { |
| 420 | ASSERT(minDeadBytes <= maxDeadBytes)((void)0); |
| 421 | ASSERT(maxDeadBytes <= totalBytes)((void)0); |
| 422 | m_minDeadCapacity = minDeadBytes; |
| 423 | m_maxDeadCapacity = maxDeadBytes; |
| 424 | m_capacity = totalBytes; |
| 425 | prune(); |
| 426 | } |
| 427 | |
| 428 | void MemoryCache::remove(CachedResource& resource) |
| 429 | { |
| 430 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (430, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 431 | RefPtr protectedResource { resource }; |
| 432 | |
| 433 | LOG(ResourceLoading, "Evicting resource %p for '%.255s' from cache", &resource, resource.url().string().latin1().data())((void)0); |
| 434 | // The resource may have already been removed by someone other than our caller, |
| 435 | // who needed a fresh copy for a reload. See <http://bugs.webkit.org/show_bug.cgi?id=12479#c6>. |
| 436 | if (auto* resources = sessionResourceMap(resource.sessionID())) { |
| 437 | auto key = std::make_pair(resource.url(), resource.cachePartition()); |
| 438 | |
| 439 | if (resource.inCache()) { |
| 440 | ASSERT_WITH_MESSAGE(resource.response().source() != ResourceResponse::Source::InspectorOverride, "InspectorOverride responses should not get into the MemoryCache")((void)0); |
| 441 | |
| 442 | // Remove resource from the resource map. |
| 443 | resources->remove(key); |
| 444 | resource.setInCache(false); |
| 445 | |
| 446 | // If the resource map is now empty, remove it from m_sessionResources. |
| 447 | if (resources->isEmpty()) { |
| 448 | RELEASE_ASSERT(m_sessionResources.isValidKey(resource.sessionID()))do { if (__builtin_expect(!!(!(m_sessionResources.isValidKey( resource.sessionID()))), 0)) do { WTF::isIntegralOrPointerType (); compilerFenceForCrash(); WTFCrashWithInfo(448, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 449 | m_sessionResources.remove(resource.sessionID()); |
| 450 | } |
| 451 | |
| 452 | // Remove from the appropriate LRU list. |
| 453 | removeFromLRUList(resource); |
| 454 | removeFromLiveDecodedResourcesList(resource); |
| 455 | adjustSize(resource.hasClients(), -static_cast<long long>(resource.size())); |
| 456 | } else { |
| 457 | RELEASE_ASSERT(resources->get(key) != &resource)do { if (__builtin_expect(!!(!(resources->get(key) != & resource)), 0)) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash (); WTFCrashWithInfo(457, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 458 | LOG(ResourceLoading, " resource %p is not in cache", &resource)((void)0); |
| 459 | } |
| 460 | } |
| 461 | RELEASE_ASSERT(!resource.inCache())do { if (__builtin_expect(!!(!(!resource.inCache())), 0)) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (461, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 462 | } |
| 463 | |
| 464 | auto MemoryCache::lruListFor(CachedResource& resource) -> LRUList& |
| 465 | { |
| 466 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (466, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 467 | unsigned accessCount = std::max(resource.accessCount(), 1U); |
| 468 | unsigned queueIndex = WTF::fastLog2(resource.size() / accessCount); |
| 469 | #if ASSERT_ENABLED0 |
| 470 | resource.m_lruIndex = queueIndex; |
| 471 | #endif |
| 472 | |
| 473 | m_allResources.reserveCapacity(queueIndex + 1); |
| 474 | while (m_allResources.size() <= queueIndex) |
| 475 | m_allResources.append(makeUnique<LRUList>()); |
| 476 | return *m_allResources[queueIndex]; |
| 477 | } |
| 478 | |
| 479 | void MemoryCache::removeFromLRUList(CachedResource& resource) |
| 480 | { |
| 481 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (481, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 482 | // If we've never been accessed, then we're brand new and not in any list. |
| 483 | if (!resource.accessCount()) |
| 484 | return; |
| 485 | |
| 486 | #if ASSERT_ENABLED0 |
| 487 | unsigned oldListIndex = resource.m_lruIndex; |
| 488 | #endif |
| 489 | |
| 490 | LRUList& list = lruListFor(resource); |
| 491 | |
| 492 | // Verify that the list we got is the list we want. |
| 493 | ASSERT(resource.m_lruIndex == oldListIndex)((void)0); |
| 494 | |
| 495 | bool removed = list.remove(resource); |
| 496 | ASSERT_UNUSED(removed, removed)((void)removed); |
| 497 | } |
| 498 | |
| 499 | void MemoryCache::insertInLRUList(CachedResource& resource) |
| 500 | { |
| 501 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (501, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 502 | ASSERT(resource.inCache())((void)0); |
| 503 | ASSERT(resource.accessCount() > 0)((void)0); |
| 504 | |
| 505 | auto addResult = lruListFor(resource).add(resource); |
| 506 | ASSERT_UNUSED(addResult, addResult.isNewEntry)((void)addResult); |
| 507 | } |
| 508 | |
| 509 | void MemoryCache::resourceAccessed(CachedResource& resource) |
| 510 | { |
| 511 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (511, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 512 | ASSERT(resource.inCache())((void)0); |
| 513 | |
| 514 | // Need to make sure to remove before we increase the access count, since |
| 515 | // the queue will possibly change. |
| 516 | removeFromLRUList(resource); |
| 517 | |
| 518 | // If this is the first time the resource has been accessed, adjust the size of the cache to account for its initial size. |
| 519 | if (!resource.accessCount()) |
| 520 | adjustSize(resource.hasClients(), resource.size()); |
| 521 | |
| 522 | // Add to our access count. |
| 523 | resource.increaseAccessCount(); |
| 524 | |
| 525 | // Now insert into the new queue. |
| 526 | insertInLRUList(resource); |
| 527 | } |
| 528 | |
| 529 | bool MemoryCache::inLiveDecodedResourcesList(CachedResource& resource) const |
| 530 | { |
| 531 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (531, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 532 | return m_liveDecodedResources.contains(resource); |
| 533 | } |
| 534 | |
| 535 | void MemoryCache::removeResourcesWithOrigin(const SecurityOrigin& origin, const String& cachePartition) |
| 536 | { |
| 537 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (537, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 538 | Vector<WeakPtr<CachedResource>> resourcesWithOrigin; |
| 539 | for (auto& resources : m_sessionResources.values()) { |
| 540 | for (auto& keyValue : *resources) { |
| 541 | auto& resource = *keyValue.value; |
| 542 | auto& partitionName = keyValue.key.second; |
| 543 | if (partitionName == cachePartition) { |
| 544 | resourcesWithOrigin.append(resource); |
| 545 | continue; |
| 546 | } |
| 547 | auto resourceOrigin = SecurityOrigin::create(resource.url()); |
| 548 | if (resourceOrigin->equal(origin)) |
| 549 | resourcesWithOrigin.append(resource); |
| 550 | } |
| 551 | } |
| 552 | |
| 553 | for (auto& weakResource : resourcesWithOrigin) { |
| 554 | if (RefPtr resource = weakResource.get()) |
| 555 | remove(*resource); |
| 556 | } |
| 557 | } |
| 558 | |
| 559 | void MemoryCache::removeResourcesWithOrigin(const SecurityOrigin& origin) |
| 560 | { |
| 561 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (561, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 562 | String originPartition = ResourceRequest::partitionName(origin.host()); |
| 563 | removeResourcesWithOrigin(origin, originPartition); |
| 564 | } |
| 565 | |
| 566 | void MemoryCache::removeResourcesWithOrigin(const ClientOrigin& origin) |
| 567 | { |
| 568 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (568, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 569 | auto cachePartition = origin.topOrigin == origin.clientOrigin ? emptyString() : ResourceRequest::partitionName(origin.topOrigin.host()); |
| 570 | removeResourcesWithOrigin(origin.clientOrigin.securityOrigin(), cachePartition); |
| 571 | } |
| 572 | |
| 573 | void MemoryCache::removeResourcesWithOrigins(PAL::SessionID sessionID, const HashSet<Ref<SecurityOrigin>>& origins) |
| 574 | { |
| 575 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (575, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 576 | auto* resourceMap = sessionResourceMap(sessionID); |
| 577 | if (!resourceMap) |
| 578 | return; |
| 579 | |
| 580 | HashSet<String> originPartitions; |
| 581 | |
| 582 | for (auto& origin : origins) |
| 583 | originPartitions.add(ResourceRequest::partitionName(origin->host())); |
| 584 | |
| 585 | Vector<WeakPtr<CachedResource>> resourcesToRemove; |
| 586 | for (auto& keyValuePair : *resourceMap) { |
| 587 | auto& resource = *keyValuePair.value; |
| 588 | auto& partitionName = keyValuePair.key.second; |
| 589 | if (originPartitions.contains(partitionName)) { |
| 590 | resourcesToRemove.append(resource); |
| 591 | continue; |
| 592 | } |
| 593 | if (origins.contains(SecurityOrigin::create(resource.url()).ptr())) |
| 594 | resourcesToRemove.append(resource); |
| 595 | } |
| 596 | |
| 597 | for (auto& weakResource : resourcesToRemove) { |
| 598 | if (RefPtr resource = weakResource.get()) |
| 599 | remove(*resource); |
| 600 | } |
| 601 | } |
| 602 | |
| 603 | void MemoryCache::getOriginsWithCache(SecurityOriginSet& origins) |
| 604 | { |
| 605 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (605, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 606 | for (auto& resources : m_sessionResources.values()) { |
| 607 | for (auto& keyValue : *resources) { |
| 608 | auto& resource = *keyValue.value; |
| 609 | auto& partitionName = keyValue.key.second; |
| 610 | if (!partitionName.isEmpty()) |
| 611 | origins.add(SecurityOrigin::create("http"_s, partitionName, 0)); |
| 612 | else |
| 613 | origins.add(SecurityOrigin::create(resource.url())); |
| 614 | } |
| 615 | } |
| 616 | } |
| 617 | |
| 618 | HashSet<Ref<SecurityOrigin>> MemoryCache::originsWithCache(PAL::SessionID sessionID) const |
| 619 | { |
| 620 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (620, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 621 | |
| 622 | HashSet<Ref<SecurityOrigin>> origins; |
| 623 | |
| 624 | auto it = m_sessionResources.find(sessionID); |
| 625 | if (it != m_sessionResources.end()) { |
| 626 | for (auto& keyValue : *it->value) { |
| 627 | auto& resource = *keyValue.value; |
Local variable 'resource' is uncounted and unsafe | |
| 628 | auto& partitionName = keyValue.key.second; |
| 629 | if (!partitionName.isEmpty()) |
| 630 | origins.add(SecurityOrigin::create("http"_s, partitionName, 0)); |
| 631 | else |
| 632 | origins.add(SecurityOrigin::create(resource.url())); |
| 633 | } |
| 634 | } |
| 635 | |
| 636 | return origins; |
| 637 | } |
| 638 | |
| 639 | void MemoryCache::removeFromLiveDecodedResourcesList(CachedResource& resource) |
| 640 | { |
| 641 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (641, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 642 | m_liveDecodedResources.remove(resource); |
| 643 | } |
| 644 | |
| 645 | void MemoryCache::moveToEndOfLiveDecodedResourcesListIfPresent(CachedResource& resource) |
| 646 | { |
| 647 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (647, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 648 | m_liveDecodedResources.moveToLastIfPresent(resource); |
| 649 | } |
| 650 | |
| 651 | void MemoryCache::insertInLiveDecodedResourcesList(CachedResource& resource) |
| 652 | { |
| 653 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (653, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 654 | // Make sure we aren't in the list already. |
| 655 | ASSERT(!m_liveDecodedResources.contains(resource))((void)0); |
| 656 | m_liveDecodedResources.add(resource); |
| 657 | } |
| 658 | |
| 659 | void MemoryCache::addToLiveResourcesSize(CachedResource& resource) |
| 660 | { |
| 661 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (661, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 662 | m_liveSize += resource.size(); |
| 663 | m_deadSize -= resource.size(); |
| 664 | } |
| 665 | |
| 666 | void MemoryCache::removeFromLiveResourcesSize(CachedResource& resource) |
| 667 | { |
| 668 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (668, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 669 | m_liveSize -= resource.size(); |
| 670 | m_deadSize += resource.size(); |
| 671 | } |
| 672 | |
| 673 | void MemoryCache::adjustSize(bool live, long long delta) |
| 674 | { |
| 675 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (675, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 676 | if (live) { |
| 677 | ASSERT(delta >= 0 || (static_cast<long long>(m_liveSize) + delta >= 0))((void)0); |
| 678 | m_liveSize += delta; |
| 679 | } else { |
| 680 | ASSERT(delta >= 0 || (static_cast<long long>(m_deadSize) + delta >= 0))((void)0); |
| 681 | m_deadSize += delta; |
| 682 | } |
| 683 | } |
| 684 | |
| 685 | void MemoryCache::removeRequestFromSessionCaches(ScriptExecutionContext& context, const ResourceRequest& request) |
| 686 | { |
| 687 | if (auto* globalScope = dynamicDowncast<WorkerGlobalScope>(context)) { |
| 688 | CheckedPtr workerLoaderProxy = globalScope->thread()->workerLoaderProxy(); |
| 689 | if (!workerLoaderProxy) |
| 690 | return; |
| 691 | workerLoaderProxy->postTaskToLoader([request = request.isolatedCopy()] (ScriptExecutionContext& context) { |
| 692 | MemoryCache::removeRequestFromSessionCaches(context, request); |
| 693 | }); |
| 694 | return; |
| 695 | } |
| 696 | |
| 697 | Ref memoryCache = MemoryCache::singleton(); |
| 698 | for (auto& resources : memoryCache->m_sessionResources) { |
| 699 | if (RefPtr resource = memoryCache->resourceForRequestImpl(request, *resources.value)) |
| 700 | memoryCache->remove(*resource); |
| 701 | } |
| 702 | } |
| 703 | |
| 704 | void MemoryCache::TypeStatistic::addResource(CachedResource& resource) |
| 705 | { |
| 706 | count++; |
| 707 | size += resource.size(); |
| 708 | liveSize += resource.hasClients() ? resource.size() : 0; |
| 709 | decodedSize += resource.decodedSize(); |
| 710 | } |
| 711 | |
| 712 | MemoryCache::Statistics MemoryCache::getStatistics() |
| 713 | { |
| 714 | Statistics stats; |
| 715 | |
| 716 | for (auto& resources : m_sessionResources.values()) { |
| 717 | for (auto& resource : resources->values()) { |
| 718 | switch (resource->type()) { |
| 719 | case CachedResource::Type::ImageResource: |
| 720 | stats.images.addResource(*resource); |
| 721 | break; |
| 722 | case CachedResource::Type::CSSStyleSheet: |
| 723 | stats.cssStyleSheets.addResource(*resource); |
| 724 | break; |
| 725 | case CachedResource::Type::JSON: |
| 726 | case CachedResource::Type::Script: |
| 727 | stats.scripts.addResource(*resource); |
| 728 | break; |
| 729 | #if ENABLE(XSLT)(defined 1 && 1) |
| 730 | case CachedResource::Type::XSLStyleSheet: |
| 731 | stats.xslStyleSheets.addResource(*resource); |
| 732 | break; |
| 733 | #endif |
| 734 | case CachedResource::Type::SVGFontResource: |
| 735 | case CachedResource::Type::FontResource: |
| 736 | stats.fonts.addResource(*resource); |
| 737 | break; |
| 738 | default: |
| 739 | break; |
| 740 | } |
| 741 | } |
| 742 | } |
| 743 | return stats; |
| 744 | } |
| 745 | |
| 746 | void MemoryCache::setDisabled(bool disabled) |
| 747 | { |
| 748 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (748, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 749 | m_disabled = disabled; |
| 750 | if (!m_disabled) |
| 751 | return; |
| 752 | |
| 753 | while (!m_sessionResources.isEmpty()) { |
| 754 | auto& resources = *m_sessionResources.begin()->value; |
| 755 | ASSERT(!resources.isEmpty())((void)0); |
| 756 | RefPtr resource = *resources.begin()->value; |
| 757 | remove(*resource); |
| 758 | } |
| 759 | } |
| 760 | |
| 761 | void MemoryCache::evictResources() |
| 762 | { |
| 763 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (763, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 764 | if (disabled()) |
| 765 | return; |
| 766 | |
| 767 | setDisabled(true); |
| 768 | setDisabled(false); |
| 769 | } |
| 770 | |
| 771 | void MemoryCache::evictResources(PAL::SessionID sessionID) |
| 772 | { |
| 773 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (773, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 774 | if (disabled()) |
| 775 | return; |
| 776 | |
| 777 | forEachSessionResource(sessionID, [this] (CachedResource& resource) { remove(resource); }); |
| 778 | |
| 779 | ASSERT(!m_sessionResources.contains(sessionID))((void)0); |
| 780 | } |
| 781 | |
| 782 | bool MemoryCache::needsPruning() const |
| 783 | { |
| 784 | return m_liveSize + m_deadSize > m_capacity || m_deadSize > m_maxDeadCapacity; |
| 785 | } |
| 786 | |
| 787 | void MemoryCache::prune() |
| 788 | { |
| 789 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (789, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 790 | if (!needsPruning()) |
| 791 | return; |
| 792 | |
| 793 | pruneDeadResources(); // Prune dead first, in case it was "borrowing" capacity from live. |
| 794 | pruneLiveResources(); |
| 795 | } |
| 796 | |
| 797 | void MemoryCache::pruneSoon() |
| 798 | { |
| 799 | RELEASE_ASSERT(isMainThread())do { if (__builtin_expect(!!(!(isMainThread())), 0)) do { WTF ::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo (799, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/MemoryCache.cpp" , __PRETTY_FUNCTION__ ); } while (false); } while (0); |
| 800 | if (!needsPruning()) |
| 801 | return; |
| 802 | if (m_pruneTimer.isActive()) |
| 803 | return; |
| 804 | m_pruneTimer.startOneShot(0_s); |
| 805 | } |
| 806 | |
| 807 | void MemoryCache::dumpStats() |
| 808 | { |
| 809 | Statistics s = getStatistics(); |
| 810 | WTFLogAlways("\nMemory Cache"); |
| 811 | WTFLogAlways("%-13s %-13s %-13s %-13s %-13s\n", "", "Count", "Size", "LiveSize", "DecodedSize"); |
| 812 | WTFLogAlways("%-13s %-13s %-13s %-13s %-13s\n", "-------------", "-------------", "-------------", "-------------", "-------------"); |
| 813 | WTFLogAlways("%-13s %13d %13d %13d %13d\n", "Images", s.images.count, s.images.size, s.images.liveSize, s.images.decodedSize); |
| 814 | WTFLogAlways("%-13s %13d %13d %13d %13d\n", "CSS", s.cssStyleSheets.count, s.cssStyleSheets.size, s.cssStyleSheets.liveSize, s.cssStyleSheets.decodedSize); |
| 815 | #if ENABLE(XSLT)(defined 1 && 1) |
| 816 | WTFLogAlways("%-13s %13d %13d %13d %13d\n", "XSL", s.xslStyleSheets.count, s.xslStyleSheets.size, s.xslStyleSheets.liveSize, s.xslStyleSheets.decodedSize); |
| 817 | #endif |
| 818 | WTFLogAlways("%-13s %13d %13d %13d %13d\n", "JavaScript", s.scripts.count, s.scripts.size, s.scripts.liveSize, s.scripts.decodedSize); |
| 819 | WTFLogAlways("%-13s %13d %13d %13d %13d\n", "Fonts", s.fonts.count, s.fonts.size, s.fonts.liveSize, s.fonts.decodedSize); |
| 820 | WTFLogAlways("%-13s %-13s %-13s %-13s %-13s\n\n", "-------------", "-------------", "-------------", "-------------", "-------------"); |
| 821 | |
| 822 | unsigned countTotal = s.images.count + s.cssStyleSheets.count + s.scripts.count + s.fonts.count; |
| 823 | unsigned sizeTotal = s.images.size + s.cssStyleSheets.size + s.scripts.size + s.fonts.size; |
| 824 | unsigned liveSizeTotal = s.images.liveSize + s.cssStyleSheets.liveSize + s.scripts.liveSize + s.fonts.liveSize; |
| 825 | unsigned decodedSizeTotal = s.images.decodedSize + s.cssStyleSheets.decodedSize + s.scripts.decodedSize + s.fonts.decodedSize; |
| 826 | #if ENABLE(XSLT)(defined 1 && 1) |
| 827 | countTotal += s.xslStyleSheets.count; |
| 828 | sizeTotal += s.xslStyleSheets.size; |
| 829 | liveSizeTotal += s.xslStyleSheets.liveSize; |
| 830 | decodedSizeTotal += s.xslStyleSheets.decodedSize; |
| 831 | #endif |
| 832 | |
| 833 | WTFLogAlways("%-13s %13d %11.2fKB %11.2fKB %11.2fKB\n", "Total", countTotal, sizeTotal / 1024., liveSizeTotal / 1024., decodedSizeTotal / 1024.); |
| 834 | } |
| 835 | |
| 836 | void MemoryCache::dumpLRULists(bool includeLive) const |
| 837 | { |
| 838 | WTFLogAlways("LRU-SP lists in eviction order (Kilobytes decoded, Kilobytes encoded, Access count, Referenced):\n"); |
| 839 | |
| 840 | int size = m_allResources.size(); |
| 841 | for (int i = size - 1; i >= 0; i--) { |
| 842 | WTFLogAlways("\nList %d:\n", i); |
| 843 | for (auto& resource : *m_allResources[i]) { |
| 844 | if (includeLive || !resource.hasClients()) |
| 845 | WTFLogAlways(" %p %.255s %.1fK, %.1fK, accesses: %u, clients: %d\n", &resource, resource.url().string().utf8().data(), resource.decodedSize() / 1024.0f, (resource.encodedSize() + resource.overheadSize()) / 1024.0f, resource.accessCount(), resource.numberOfClients()); |
| 846 | } |
| 847 | } |
| 848 | } |
| 849 | |
| 850 | } // namespace WebCore |