Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(209)

Side by Side Diff: third_party/WebKit/Source/core/fetch/MemoryCache.cpp

Issue 2584423002: Loading: move core/fetch to platform/loader/fetch (Closed)
Patch Set: another try Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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, 2005, 2006, 2007, 2008 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 "core/fetch/MemoryCache.h"
24
25 #include "core/fetch/ResourceLoadingLog.h"
26 #include "platform/instrumentation/tracing/TraceEvent.h"
27 #include "platform/weborigin/SecurityOrigin.h"
28 #include "platform/weborigin/SecurityOriginHash.h"
29 #include "public/platform/Platform.h"
30 #include "wtf/Assertions.h"
31 #include "wtf/AutoReset.h"
32 #include "wtf/CurrentTime.h"
33 #include "wtf/MathExtras.h"
34 #include "wtf/text/CString.h"
35
36 namespace blink {
37
38 static Persistent<MemoryCache>* gMemoryCache;
39
40 static const unsigned cDefaultCacheCapacity = 8192 * 1024;
41 static const int cMinDelayBeforeLiveDecodedPrune = 1; // Seconds.
42 static const double cMaxPruneDeferralDelay = 0.5; // Seconds.
43
44 // Percentage of capacity toward which we prune, to avoid immediately pruning
45 // again.
46 static const float cTargetPrunePercentage = .95f;
47
48 MemoryCache* memoryCache() {
49 DCHECK(WTF::isMainThread());
50 if (!gMemoryCache)
51 gMemoryCache = new Persistent<MemoryCache>(MemoryCache::create());
52 return gMemoryCache->get();
53 }
54
55 MemoryCache* replaceMemoryCacheForTesting(MemoryCache* cache) {
56 memoryCache();
57 MemoryCache* oldCache = gMemoryCache->release();
58 *gMemoryCache = cache;
59 MemoryCacheDumpProvider::instance()->setMemoryCache(cache);
60 return oldCache;
61 }
62
63 DEFINE_TRACE(MemoryCacheEntry) {
64 visitor->template registerWeakMembers<MemoryCacheEntry,
65 &MemoryCacheEntry::clearResourceWeak>(
66 this);
67 }
68
69 void MemoryCacheEntry::clearResourceWeak(Visitor* visitor) {
70 if (!m_resource || ThreadHeap::isHeapObjectAlive(m_resource))
71 return;
72 memoryCache()->remove(m_resource.get());
73 m_resource.clear();
74 }
75
76 inline MemoryCache::MemoryCache()
77 : m_inPruneResources(false),
78 m_prunePending(false),
79 m_maxPruneDeferralDelay(cMaxPruneDeferralDelay),
80 m_pruneTimeStamp(0.0),
81 m_pruneFrameTimeStamp(0.0),
82 m_lastFramePaintTimeStamp(0.0),
83 m_capacity(cDefaultCacheCapacity),
84 m_delayBeforeLiveDecodedPrune(cMinDelayBeforeLiveDecodedPrune),
85 m_size(0) {
86 MemoryCacheDumpProvider::instance()->setMemoryCache(this);
87 if (MemoryCoordinator::isLowEndDevice())
88 MemoryCoordinator::instance().registerClient(this);
89 }
90
91 MemoryCache* MemoryCache::create() {
92 return new MemoryCache;
93 }
94
95 MemoryCache::~MemoryCache() {
96 if (m_prunePending)
97 Platform::current()->currentThread()->removeTaskObserver(this);
98 }
99
100 DEFINE_TRACE(MemoryCache) {
101 visitor->trace(m_resourceMaps);
102 MemoryCacheDumpClient::trace(visitor);
103 MemoryCoordinatorClient::trace(visitor);
104 }
105
106 KURL MemoryCache::removeFragmentIdentifierIfNeeded(const KURL& originalURL) {
107 if (!originalURL.hasFragmentIdentifier())
108 return originalURL;
109 // Strip away fragment identifier from HTTP URLs. Data URLs must be
110 // unmodified. For file and custom URLs clients may expect resources to be
111 // unique even when they differ by the fragment identifier only.
112 if (!originalURL.protocolIsInHTTPFamily())
113 return originalURL;
114 KURL url = originalURL;
115 url.removeFragmentIdentifier();
116 return url;
117 }
118
119 String MemoryCache::defaultCacheIdentifier() {
120 return emptyString();
121 }
122
123 MemoryCache::ResourceMap* MemoryCache::ensureResourceMap(
124 const String& cacheIdentifier) {
125 if (!m_resourceMaps.contains(cacheIdentifier)) {
126 ResourceMapIndex::AddResult result =
127 m_resourceMaps.add(cacheIdentifier, new ResourceMap);
128 CHECK(result.isNewEntry);
129 }
130 return m_resourceMaps.get(cacheIdentifier);
131 }
132
133 void MemoryCache::add(Resource* resource) {
134 DCHECK(resource);
135 ResourceMap* resources = ensureResourceMap(resource->cacheIdentifier());
136 addInternal(resources, MemoryCacheEntry::create(resource));
137 RESOURCE_LOADING_DVLOG(1) << "MemoryCache::add Added "
138 << resource->url().getString() << ", resource "
139 << resource;
140 }
141
142 void MemoryCache::addInternal(ResourceMap* resourceMap,
143 MemoryCacheEntry* entry) {
144 DCHECK(WTF::isMainThread());
145 DCHECK(resourceMap);
146
147 Resource* resource = entry->resource();
148 if (!resource)
149 return;
150 DCHECK(resource->url().isValid());
151
152 KURL url = removeFragmentIdentifierIfNeeded(resource->url());
153 ResourceMap::iterator it = resourceMap->find(url);
154 if (it != resourceMap->end()) {
155 Resource* oldResource = it->value->resource();
156 CHECK_NE(oldResource, resource);
157 update(oldResource, oldResource->size(), 0);
158 }
159 resourceMap->set(url, entry);
160 update(resource, 0, resource->size());
161 }
162
163 void MemoryCache::remove(Resource* resource) {
164 DCHECK(WTF::isMainThread());
165 DCHECK(resource);
166 RESOURCE_LOADING_DVLOG(1) << "Evicting resource " << resource << " for "
167 << resource->url().getString() << " from cache";
168 TRACE_EVENT1("blink", "MemoryCache::evict", "resource",
169 resource->url().getString().utf8());
170
171 ResourceMap* resources = m_resourceMaps.get(resource->cacheIdentifier());
172 if (!resources)
173 return;
174
175 KURL url = removeFragmentIdentifierIfNeeded(resource->url());
176 ResourceMap::iterator it = resources->find(url);
177 if (it == resources->end() || it->value->resource() != resource)
178 return;
179 removeInternal(resources, it);
180 }
181
182 void MemoryCache::removeInternal(ResourceMap* resourceMap,
183 const ResourceMap::iterator& it) {
184 DCHECK(WTF::isMainThread());
185 DCHECK(resourceMap);
186
187 Resource* resource = it->value->resource();
188 DCHECK(resource);
189
190 update(resource, resource->size(), 0);
191 resourceMap->remove(it);
192 }
193
194 bool MemoryCache::contains(const Resource* resource) const {
195 if (!resource || resource->url().isEmpty())
196 return false;
197 const ResourceMap* resources =
198 m_resourceMaps.get(resource->cacheIdentifier());
199 if (!resources)
200 return false;
201 KURL url = removeFragmentIdentifierIfNeeded(resource->url());
202 MemoryCacheEntry* entry = resources->get(url);
203 return entry && resource == entry->resource();
204 }
205
206 Resource* MemoryCache::resourceForURL(const KURL& resourceURL) const {
207 return resourceForURL(resourceURL, defaultCacheIdentifier());
208 }
209
210 Resource* MemoryCache::resourceForURL(const KURL& resourceURL,
211 const String& cacheIdentifier) const {
212 DCHECK(WTF::isMainThread());
213 if (!resourceURL.isValid() || resourceURL.isNull())
214 return nullptr;
215 DCHECK(!cacheIdentifier.isNull());
216 const ResourceMap* resources = m_resourceMaps.get(cacheIdentifier);
217 if (!resources)
218 return nullptr;
219 MemoryCacheEntry* entry =
220 resources->get(removeFragmentIdentifierIfNeeded(resourceURL));
221 if (!entry)
222 return nullptr;
223 return entry->resource();
224 }
225
226 HeapVector<Member<Resource>> MemoryCache::resourcesForURL(
227 const KURL& resourceURL) const {
228 DCHECK(WTF::isMainThread());
229 KURL url = removeFragmentIdentifierIfNeeded(resourceURL);
230 HeapVector<Member<Resource>> results;
231 for (const auto& resourceMapIter : m_resourceMaps) {
232 if (MemoryCacheEntry* entry = resourceMapIter.value->get(url)) {
233 Resource* resource = entry->resource();
234 DCHECK(resource);
235 results.push_back(resource);
236 }
237 }
238 return results;
239 }
240
241 void MemoryCache::pruneResources(PruneStrategy strategy) {
242 DCHECK(!m_prunePending);
243 const size_t sizeLimit = (strategy == MaximalPrune) ? 0 : capacity();
244 if (m_size <= sizeLimit)
245 return;
246
247 // Cut by a percentage to avoid immediately pruning again.
248 size_t targetSize = static_cast<size_t>(sizeLimit * cTargetPrunePercentage);
249
250 for (const auto& resourceMapIter : m_resourceMaps) {
251 for (const auto& resourceIter : *resourceMapIter.value) {
252 Resource* resource = resourceIter.value->resource();
253 DCHECK(resource);
254 if (resource->isLoaded() && resource->decodedSize()) {
255 // Check to see if the remaining resources are too new to prune.
256 double elapsedTime =
257 m_pruneFrameTimeStamp - resourceIter.value->m_lastDecodedAccessTime;
258 if (strategy == AutomaticPrune &&
259 elapsedTime < m_delayBeforeLiveDecodedPrune)
260 continue;
261 resource->prune();
262 if (m_size <= targetSize)
263 return;
264 }
265 }
266 }
267 }
268
269 void MemoryCache::setCapacity(size_t totalBytes) {
270 m_capacity = totalBytes;
271 prune();
272 }
273
274 void MemoryCache::update(Resource* resource, size_t oldSize, size_t newSize) {
275 if (!contains(resource))
276 return;
277 ptrdiff_t delta = newSize - oldSize;
278 DCHECK(delta >= 0 || m_size >= static_cast<size_t>(-delta));
279 m_size += delta;
280 }
281
282 void MemoryCache::removeURLFromCache(const KURL& url) {
283 HeapVector<Member<Resource>> resources = resourcesForURL(url);
284 for (Resource* resource : resources)
285 remove(resource);
286 }
287
288 void MemoryCache::TypeStatistic::addResource(Resource* o) {
289 count++;
290 size += o->size();
291 decodedSize += o->decodedSize();
292 encodedSize += o->encodedSize();
293 overheadSize += o->overheadSize();
294 encodedSizeDuplicatedInDataURLs +=
295 o->url().protocolIsData() ? o->encodedSize() : 0;
296 }
297
298 MemoryCache::Statistics MemoryCache::getStatistics() const {
299 Statistics stats;
300 for (const auto& resourceMapIter : m_resourceMaps) {
301 for (const auto& resourceIter : *resourceMapIter.value) {
302 Resource* resource = resourceIter.value->resource();
303 DCHECK(resource);
304 switch (resource->getType()) {
305 case Resource::Image:
306 stats.images.addResource(resource);
307 break;
308 case Resource::CSSStyleSheet:
309 stats.cssStyleSheets.addResource(resource);
310 break;
311 case Resource::Script:
312 stats.scripts.addResource(resource);
313 break;
314 case Resource::XSLStyleSheet:
315 stats.xslStyleSheets.addResource(resource);
316 break;
317 case Resource::Font:
318 stats.fonts.addResource(resource);
319 break;
320 default:
321 stats.other.addResource(resource);
322 break;
323 }
324 }
325 }
326 return stats;
327 }
328
329 void MemoryCache::evictResources(EvictResourcePolicy policy) {
330 for (auto resourceMapIter = m_resourceMaps.begin();
331 resourceMapIter != m_resourceMaps.end();) {
332 ResourceMap* resources = resourceMapIter->value.get();
333 HeapVector<Member<MemoryCacheEntry>> unusedPreloads;
334 for (auto resourceIter = resources->begin();
335 resourceIter != resources->end(); resourceIter = resources->begin()) {
336 DCHECK(resourceIter.get());
337 DCHECK(resourceIter->value.get());
338 DCHECK(resourceIter->value->resource());
339 Resource* resource = resourceIter->value->resource();
340 DCHECK(resource);
341 if (policy != EvictAllResources && resource->isUnusedPreload()) {
342 // Store unused preloads aside, so they could be added back later.
343 // That is in order to avoid the performance impact of iterating over
344 // the same resource multiple times.
345 unusedPreloads.push_back(resourceIter->value.get());
346 }
347 removeInternal(resources, resourceIter);
348 }
349 for (const auto& unusedPreload : unusedPreloads) {
350 addInternal(resources, unusedPreload);
351 }
352 // We may iterate multiple times over resourceMaps with unused preloads.
353 // That's extremely unlikely to have any real-life performance impact.
354 if (!resources->size()) {
355 m_resourceMaps.remove(resourceMapIter);
356 resourceMapIter = m_resourceMaps.begin();
357 } else {
358 ++resourceMapIter;
359 }
360 }
361 }
362
363 void MemoryCache::prune() {
364 TRACE_EVENT0("renderer", "MemoryCache::prune()");
365
366 if (m_inPruneResources)
367 return;
368 if (m_size <= m_capacity) // Fast path.
369 return;
370
371 // To avoid burdening the current thread with repetitive pruning jobs, pruning
372 // is postponed until the end of the current task. If it has been more than
373 // m_maxPruneDeferralDelay since the last prune, then we prune immediately. If
374 // the current thread's run loop is not active, then pruning will happen
375 // immediately only if it has been over m_maxPruneDeferralDelay since the last
376 // prune.
377 double currentTime = WTF::currentTime();
378 if (m_prunePending) {
379 if (currentTime - m_pruneTimeStamp >= m_maxPruneDeferralDelay) {
380 pruneNow(currentTime, AutomaticPrune);
381 }
382 } else {
383 if (currentTime - m_pruneTimeStamp >= m_maxPruneDeferralDelay) {
384 pruneNow(currentTime, AutomaticPrune); // Delay exceeded, prune now.
385 } else {
386 // Defer.
387 Platform::current()->currentThread()->addTaskObserver(this);
388 m_prunePending = true;
389 }
390 }
391 }
392
393 void MemoryCache::willProcessTask() {}
394
395 void MemoryCache::didProcessTask() {
396 // Perform deferred pruning
397 DCHECK(m_prunePending);
398 pruneNow(WTF::currentTime(), AutomaticPrune);
399 }
400
401 void MemoryCache::pruneAll() {
402 double currentTime = WTF::currentTime();
403 pruneNow(currentTime, MaximalPrune);
404 }
405
406 void MemoryCache::pruneNow(double currentTime, PruneStrategy strategy) {
407 if (m_prunePending) {
408 m_prunePending = false;
409 Platform::current()->currentThread()->removeTaskObserver(this);
410 }
411
412 AutoReset<bool> reentrancyProtector(&m_inPruneResources, true);
413
414 pruneResources(strategy);
415 m_pruneFrameTimeStamp = m_lastFramePaintTimeStamp;
416 m_pruneTimeStamp = currentTime;
417 }
418
419 void MemoryCache::updateFramePaintTimestamp() {
420 m_lastFramePaintTimeStamp = currentTime();
421 }
422
423 bool MemoryCache::onMemoryDump(WebMemoryDumpLevelOfDetail levelOfDetail,
424 WebProcessMemoryDump* memoryDump) {
425 if (levelOfDetail == WebMemoryDumpLevelOfDetail::Background) {
426 Statistics stats = getStatistics();
427 WebMemoryAllocatorDump* dump1 =
428 memoryDump->createMemoryAllocatorDump("web_cache/Image_resources");
429 dump1->addScalar("size", "bytes",
430 stats.images.encodedSize + stats.images.overheadSize);
431 WebMemoryAllocatorDump* dump2 = memoryDump->createMemoryAllocatorDump(
432 "web_cache/CSS stylesheet_resources");
433 dump2->addScalar("size", "bytes", stats.cssStyleSheets.encodedSize +
434 stats.cssStyleSheets.overheadSize);
435 WebMemoryAllocatorDump* dump3 =
436 memoryDump->createMemoryAllocatorDump("web_cache/Script_resources");
437 dump3->addScalar("size", "bytes",
438 stats.scripts.encodedSize + stats.scripts.overheadSize);
439 WebMemoryAllocatorDump* dump4 = memoryDump->createMemoryAllocatorDump(
440 "web_cache/XSL stylesheet_resources");
441 dump4->addScalar("size", "bytes", stats.xslStyleSheets.encodedSize +
442 stats.xslStyleSheets.overheadSize);
443 WebMemoryAllocatorDump* dump5 =
444 memoryDump->createMemoryAllocatorDump("web_cache/Font_resources");
445 dump5->addScalar("size", "bytes",
446 stats.fonts.encodedSize + stats.fonts.overheadSize);
447 WebMemoryAllocatorDump* dump6 =
448 memoryDump->createMemoryAllocatorDump("web_cache/Other_resources");
449 dump6->addScalar("size", "bytes",
450 stats.other.encodedSize + stats.other.overheadSize);
451 return true;
452 }
453
454 for (const auto& resourceMapIter : m_resourceMaps) {
455 for (const auto& resourceIter : *resourceMapIter.value) {
456 Resource* resource = resourceIter.value->resource();
457 resource->onMemoryDump(levelOfDetail, memoryDump);
458 }
459 }
460 return true;
461 }
462
463 void MemoryCache::onMemoryPressure(WebMemoryPressureLevel level) {
464 pruneAll();
465 }
466
467 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698