8fae41bbf6655f7820a2ac06302aeed7461f7669
[WebKit-https.git] / Source / WebCore / history / PageCache.cpp
1 /*
2  * Copyright (C) 2007 Apple Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "PageCache.h"
28
29 #include "ApplicationCacheHost.h"
30 #include "BackForwardController.h"
31 #include "MemoryCache.h"
32 #include "CachedPage.h"
33 #include "DOMWindow.h"
34 #include "DatabaseManager.h"
35 #include "DeviceMotionController.h"
36 #include "DeviceOrientationController.h"
37 #include "DeviceProximityController.h"
38 #include "Document.h"
39 #include "DocumentLoader.h"
40 #include "Frame.h"
41 #include "FrameLoader.h"
42 #include "FrameLoaderClient.h"
43 #include "FrameLoaderStateMachine.h"
44 #include "FrameView.h"
45 #include "HistogramSupport.h"
46 #include "HistoryItem.h"
47 #include "Logging.h"
48 #include "Page.h"
49 #include "Settings.h"
50 #include "SharedWorkerRepository.h"
51 #include "SystemTime.h"
52 #include <wtf/CurrentTime.h>
53 #include <wtf/text/CString.h>
54 #include <wtf/text/StringConcatenate.h>
55
56 using namespace std;
57
58 namespace WebCore {
59
60 #if PLATFORM(CHROMIUM) || !defined(NDEBUG)
61
62 #define PCLOG(...) LOG(PageCache, "%*s%s", indentLevel*4, "", makeString(__VA_ARGS__).utf8().data())
63     
64 // Used in histograms, please only add at the end, and do not remove elements (renaming e.g. to "FooEnumUnused1" is fine).
65 // This is because statistics may be gathered from histograms between versions over time, and re-using values causes collisions.
66 enum ReasonFrameCannotBeInPageCache {
67     NoDocumentLoader = 0,
68     MainDocumentError,
69     IsErrorPage,
70     HasPlugins,
71     IsHttpsAndCacheControlled,
72     HasUnloadListener,
73     HasDatabaseHandles,
74     HasSharedWorkers,
75     NoHistoryItem,
76     QuickRedirectComing,
77     IsLoadingInAPISense,
78     IsStopping,
79     CannotSuspendActiveDOMObjects,
80     DocumentLoaderUsesApplicationCache,
81     ClientDeniesCaching,
82     NumberOfReasonsFramesCannotBeInPageCache,
83 };
84 COMPILE_ASSERT(NumberOfReasonsFramesCannotBeInPageCache <= sizeof(unsigned)*8, ReasonFrameCannotBeInPageCacheDoesNotFitInBitmap);
85
86 #if PLATFORM(CHROMIUM)
87 static int indexOfSingleBit(int32_t v)
88 {
89     int index = 0;
90     if (v & 0xFFFF0000)
91         index = 16;
92     if (v & 0xFF00FF00)
93         index += 8;
94     if (v & 0xF0F0F0F0)
95         index += 4;
96     if (v & 0xCCCCCCCC)
97         index += 2;
98     if (v & 0xAAAAAAAA)
99         index += 1;
100     return index;
101 }
102 #endif // PLATFORM(CHROMIUM)
103
104 static unsigned logCanCacheFrameDecision(Frame* frame, int indentLevel)
105 {
106 #ifdef NDEBUG
107     UNUSED_PARAM(indentLevel);
108 #endif
109     PCLOG("+---");
110     if (!frame->loader()->documentLoader()) {
111         PCLOG("   -There is no DocumentLoader object");
112         return 1 << NoDocumentLoader;
113     }
114
115     KURL currentURL = frame->loader()->documentLoader()->url();
116     KURL newURL = frame->loader()->provisionalDocumentLoader() ? frame->loader()->provisionalDocumentLoader()->url() : KURL();
117     if (!newURL.isEmpty())
118         PCLOG(" Determining if frame can be cached navigating from (", currentURL.string(), ") to (", newURL.string(), "):");
119     else
120         PCLOG(" Determining if subframe with URL (", currentURL.string(), ") can be cached:");
121      
122     unsigned rejectReasons = 0;
123     if (!frame->loader()->documentLoader()->mainDocumentError().isNull()) {
124         PCLOG("   -Main document has an error");
125         rejectReasons |= 1 << MainDocumentError;
126     }
127     if (frame->loader()->documentLoader()->substituteData().isValid() && frame->loader()->documentLoader()->substituteData().failingURL().isEmpty()) {
128         PCLOG("   -Frame is an error page");
129         rejectReasons |= 1 << IsErrorPage;
130     }
131     if (frame->loader()->subframeLoader()->containsPlugins() && !frame->page()->settings()->pageCacheSupportsPlugins()) {
132         PCLOG("   -Frame contains plugins");
133         rejectReasons |= 1 << HasPlugins;
134     }
135     if (frame->document()->url().protocolIs("https")
136         && (frame->loader()->documentLoader()->response().cacheControlContainsNoCache()
137             || frame->loader()->documentLoader()->response().cacheControlContainsNoStore())) {
138         PCLOG("   -Frame is HTTPS, and cache control prohibits caching or storing");
139         rejectReasons |= 1 << IsHttpsAndCacheControlled;
140     }
141     if (frame->document()->domWindow() && frame->document()->domWindow()->hasEventListeners(eventNames().unloadEvent)) {
142         PCLOG("   -Frame has an unload event listener");
143         rejectReasons |= 1 << HasUnloadListener;
144     }
145 #if ENABLE(SQL_DATABASE)
146     if (DatabaseManager::manager().hasOpenDatabases(frame->document())) {
147         PCLOG("   -Frame has open database handles");
148         rejectReasons |= 1 << HasDatabaseHandles;
149     }
150 #endif
151 #if ENABLE(SHARED_WORKERS)
152     if (SharedWorkerRepository::hasSharedWorkers(frame->document())) {
153         PCLOG("   -Frame has associated SharedWorkers");
154         rejectReasons |= 1 << HasSharedWorkers;
155     }
156 #endif
157     if (!frame->loader()->history()->currentItem()) {
158         PCLOG("   -No current history item");
159         rejectReasons |= 1 << NoHistoryItem;
160     }
161     if (frame->loader()->quickRedirectComing()) {
162         PCLOG("   -Quick redirect is coming");
163         rejectReasons |= 1 << QuickRedirectComing;
164     }
165     if (frame->loader()->documentLoader()->isLoadingInAPISense()) {
166         PCLOG("   -DocumentLoader is still loading in API sense");
167         rejectReasons |= 1 << IsLoadingInAPISense;
168     }
169     if (frame->loader()->documentLoader()->isStopping()) {
170         PCLOG("   -DocumentLoader is in the middle of stopping");
171         rejectReasons |= 1 << IsStopping;
172     }
173     if (!frame->document()->canSuspendActiveDOMObjects()) {
174         PCLOG("   -The document cannot suspect its active DOM Objects");
175         rejectReasons |= 1 << CannotSuspendActiveDOMObjects;
176     }
177     if (!frame->loader()->documentLoader()->applicationCacheHost()->canCacheInPageCache()) {
178         PCLOG("   -The DocumentLoader uses an application cache");
179         rejectReasons |= 1 << DocumentLoaderUsesApplicationCache;
180     }
181     if (!frame->loader()->client()->canCachePage()) {
182         PCLOG("   -The client says this frame cannot be cached");
183         rejectReasons |= 1 << ClientDeniesCaching;
184     }
185
186     HistogramSupport::histogramEnumeration("PageCache.FrameCacheable", !rejectReasons, 2);
187     int reasonCount = 0;
188     for (int i = 0; i < NumberOfReasonsFramesCannotBeInPageCache; ++i) {
189         if (rejectReasons & (1 << i)) {
190             ++reasonCount;
191             HistogramSupport::histogramEnumeration("PageCache.FrameRejectReason", i, NumberOfReasonsFramesCannotBeInPageCache);
192         }
193     }
194     HistogramSupport::histogramEnumeration("PageCache.FrameRejectReasonCount", reasonCount, 1 + NumberOfReasonsFramesCannotBeInPageCache);
195
196     for (Frame* child = frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
197         rejectReasons |= logCanCacheFrameDecision(child, indentLevel + 1);
198     
199     PCLOG(rejectReasons ? " Frame CANNOT be cached" : " Frame CAN be cached");
200     PCLOG("+---");
201     
202     return rejectReasons;
203 }
204
205 // Used in histograms, please only add at the end, and do not remove elements (renaming e.g. to "FooEnumUnused1" is fine).
206 // This is because statistics may be gathered from histograms between versions over time, and re-using values causes collisions.
207 enum ReasonPageCannotBeInPageCache {
208     FrameCannotBeInPageCache = 0,
209     DisabledBackForwardList,
210     DisabledPageCache,
211     UsesDeviceMotion,
212     UsesDeviceOrientation,
213     IsReload,
214     IsReloadFromOrigin,
215     IsSameLoad,
216     NumberOfReasonsPagesCannotBeInPageCache,
217 };
218 COMPILE_ASSERT(NumberOfReasonsPagesCannotBeInPageCache <= sizeof(unsigned)*8, ReasonPageCannotBeInPageCacheDoesNotFitInBitmap);
219
220 static void logCanCachePageDecision(Page* page)
221 {
222     // Only bother logging for main frames that have actually loaded and have content.
223     if (page->mainFrame()->loader()->stateMachine()->creatingInitialEmptyDocument())
224         return;
225     KURL currentURL = page->mainFrame()->loader()->documentLoader() ? page->mainFrame()->loader()->documentLoader()->url() : KURL();
226     if (currentURL.isEmpty())
227         return;
228     
229     int indentLevel = 0;    
230     PCLOG("--------\n Determining if page can be cached:");
231     
232     unsigned rejectReasons = 0;
233     unsigned frameRejectReasons = logCanCacheFrameDecision(page->mainFrame(), indentLevel+1);
234     if (frameRejectReasons)
235         rejectReasons |= 1 << FrameCannotBeInPageCache;
236     
237     if (!page->backForward()->isActive()) {
238         PCLOG("   -The back/forward list is disabled or has 0 capacity");
239         rejectReasons |= 1 << DisabledBackForwardList;
240     }
241     if (!page->settings()->usesPageCache()) {
242         PCLOG("   -Page settings says b/f cache disabled");
243         rejectReasons |= 1 << DisabledPageCache;
244     }
245 #if ENABLE(DEVICE_ORIENTATION)
246     if (DeviceMotionController::isActiveAt(page)) {
247         PCLOG("   -Page is using DeviceMotion");
248         rejectReasons |= 1 << UsesDeviceMotion;
249     }
250     if (DeviceOrientationController::isActiveAt(page)) {
251         PCLOG("   -Page is using DeviceOrientation");
252         rejectReasons |= 1 << UsesDeviceOrientation;
253     }
254 #endif
255 #if ENABLE(PROXIMITY_EVENTS)
256     if (DeviceProximityController::isActiveAt(page)) {
257         PCLOG("   -Page is using DeviceProximity");
258         rejectReasons |= 1 << UsesDeviceMotion;
259     }
260 #endif
261     FrameLoadType loadType = page->mainFrame()->loader()->loadType();
262     if (loadType == FrameLoadTypeReload) {
263         PCLOG("   -Load type is: Reload");
264         rejectReasons |= 1 << IsReload;
265     }
266     if (loadType == FrameLoadTypeReloadFromOrigin) {
267         PCLOG("   -Load type is: Reload from origin");
268         rejectReasons |= 1 << IsReloadFromOrigin;
269     }
270     if (loadType == FrameLoadTypeSame) {
271         PCLOG("   -Load type is: Same");
272         rejectReasons |= 1 << IsSameLoad;
273     }
274     
275     PCLOG(rejectReasons ? " Page CANNOT be cached\n--------" : " Page CAN be cached\n--------");
276
277     HistogramSupport::histogramEnumeration("PageCache.PageCacheable", !rejectReasons, 2);
278     int reasonCount = 0;
279     for (int i = 0; i < NumberOfReasonsPagesCannotBeInPageCache; ++i) {
280         if (rejectReasons & (1 << i)) {
281             ++reasonCount;
282             HistogramSupport::histogramEnumeration("PageCache.PageRejectReason", i, NumberOfReasonsPagesCannotBeInPageCache);
283         }
284     }
285     HistogramSupport::histogramEnumeration("PageCache.PageRejectReasonCount", reasonCount, 1 + NumberOfReasonsPagesCannotBeInPageCache);
286     const bool settingsDisabledPageCache = rejectReasons & (1 << DisabledPageCache);
287     HistogramSupport::histogramEnumeration("PageCache.PageRejectReasonCountExcludingSettings", reasonCount - settingsDisabledPageCache, NumberOfReasonsPagesCannotBeInPageCache);
288
289     // Report also on the frame reasons by page; this is distinct from the per frame statistics since it coalesces the
290     // causes from all subframes together.
291     HistogramSupport::histogramEnumeration("PageCache.FrameCacheableByPage", !frameRejectReasons, 2);
292     int frameReasonCount = 0;
293     for (int i = 0; i <= NumberOfReasonsFramesCannotBeInPageCache; ++i) {
294         if (frameRejectReasons & (1 << i)) {
295             ++frameReasonCount;
296             HistogramSupport::histogramEnumeration("PageCache.FrameRejectReasonByPage", i, NumberOfReasonsFramesCannotBeInPageCache);
297         }
298     }
299 #if PLATFORM(CHROMIUM)
300     // This strangely specific histogram is particular to chromium: as of 2012-03-16, the FrameClientImpl always denies caching, so
301     // of particular interest are solitary reasons other than the frameRejectReasons. If we didn't get to the ClientDeniesCaching, we
302     // took the early exit for the boring reason NoDocumentLoader, so we should have only one reason, and not two.
303     // FIXME: remove this histogram after data is gathered.
304     if (frameReasonCount == 2) {
305         ASSERT(frameRejectReasons & (1 << ClientDeniesCaching));
306         const unsigned singleReasonForRejectingFrameOtherThanClientDeniesCaching = frameRejectReasons & ~(1 << ClientDeniesCaching);
307         COMPILE_ASSERT(NumberOfReasonsPagesCannotBeInPageCache <= 32, ReasonPageCannotBeInPageCacheDoesNotFitInInt32);
308         const int index = indexOfSingleBit(static_cast<int32_t>(singleReasonForRejectingFrameOtherThanClientDeniesCaching));
309         HistogramSupport::histogramEnumeration("PageCache.FrameRejectReasonByPageWhenSingleExcludingFrameClient", index, NumberOfReasonsPagesCannotBeInPageCache);
310     }
311 #endif
312
313     HistogramSupport::histogramEnumeration("PageCache.FrameRejectReasonCountByPage", frameReasonCount, 1 + NumberOfReasonsFramesCannotBeInPageCache);
314 }
315
316 #endif 
317
318 PageCache* pageCache()
319 {
320     static PageCache* staticPageCache = new PageCache;
321     return staticPageCache;
322 }
323
324 PageCache::PageCache()
325     : m_capacity(0)
326     , m_size(0)
327     , m_head(0)
328     , m_tail(0)
329     , m_autoreleaseTimer(this, &PageCache::releaseAutoreleasedPagesNowDueToTimer)
330 #if USE(ACCELERATED_COMPOSITING)
331     , m_shouldClearBackingStores(false)
332 #endif
333 {
334 }
335     
336 bool PageCache::canCachePageContainingThisFrame(Frame* frame)
337 {
338     for (Frame* child = frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) {
339         if (!canCachePageContainingThisFrame(child))
340             return false;
341     }
342     
343     FrameLoader* frameLoader = frame->loader();
344     DocumentLoader* documentLoader = frameLoader->documentLoader();
345     Document* document = frame->document();
346     
347     return documentLoader
348         && documentLoader->mainDocumentError().isNull()
349         // Do not cache error pages (these can be recognized as pages with substitute data or unreachable URLs).
350         && !(documentLoader->substituteData().isValid() && !documentLoader->substituteData().failingURL().isEmpty())
351         && (!frameLoader->subframeLoader()->containsPlugins() || frame->page()->settings()->pageCacheSupportsPlugins())
352         && (!document->url().protocolIs("https") || (!documentLoader->response().cacheControlContainsNoCache() && !documentLoader->response().cacheControlContainsNoStore()))
353         && (!document->domWindow() || !document->domWindow()->hasEventListeners(eventNames().unloadEvent))
354 #if ENABLE(SQL_DATABASE)
355         && !DatabaseManager::manager().hasOpenDatabases(document)
356 #endif
357 #if ENABLE(SHARED_WORKERS)
358         && !SharedWorkerRepository::hasSharedWorkers(document)
359 #endif
360         && frameLoader->history()->currentItem()
361         && !frameLoader->quickRedirectComing()
362         && !documentLoader->isLoadingInAPISense()
363         && !documentLoader->isStopping()
364         && document->canSuspendActiveDOMObjects()
365         // FIXME: We should investigating caching frames that have an associated
366         // application cache. <rdar://problem/5917899> tracks that work.
367         && documentLoader->applicationCacheHost()->canCacheInPageCache()
368         && frameLoader->client()->canCachePage();
369 }
370     
371 bool PageCache::canCache(Page* page) const
372 {
373     if (!page)
374         return false;
375     
376 #if PLATFORM(CHROMIUM) || !defined(NDEBUG)
377     logCanCachePageDecision(page);
378 #endif
379     
380     // Cache the page, if possible.
381     // Don't write to the cache if in the middle of a redirect, since we will want to
382     // store the final page we end up on.
383     // No point writing to the cache on a reload or loadSame, since we will just write
384     // over it again when we leave that page.
385     FrameLoadType loadType = page->mainFrame()->loader()->loadType();
386     
387     return m_capacity > 0
388         && canCachePageContainingThisFrame(page->mainFrame())
389         && page->backForward()->isActive()
390         && page->settings()->usesPageCache()
391 #if ENABLE(DEVICE_ORIENTATION)
392         && !DeviceMotionController::isActiveAt(page)
393         && !DeviceOrientationController::isActiveAt(page)
394 #endif
395 #if ENABLE(PROXIMITY_EVENTS)
396         && !DeviceProximityController::isActiveAt(page)
397 #endif
398         && loadType != FrameLoadTypeReload
399         && loadType != FrameLoadTypeReloadFromOrigin
400         && loadType != FrameLoadTypeSame;
401 }
402
403 void PageCache::setCapacity(int capacity)
404 {
405     ASSERT(capacity >= 0);
406     m_capacity = max(capacity, 0);
407
408     prune();
409 }
410
411 int PageCache::frameCount() const
412 {
413     int frameCount = 0;
414     for (HistoryItem* current = m_head; current; current = current->m_next) {
415         ++frameCount;
416         ASSERT(current->m_cachedPage);
417         frameCount += current->m_cachedPage ? current->m_cachedPage->cachedMainFrame()->descendantFrameCount() : 0;
418     }
419     
420     return frameCount;
421 }
422
423 int PageCache::autoreleasedPageCount() const
424 {
425     return m_autoreleaseSet.size();
426 }
427
428 void PageCache::markPagesForVistedLinkStyleRecalc()
429 {
430     for (HistoryItem* current = m_head; current; current = current->m_next)
431         current->m_cachedPage->markForVistedLinkStyleRecalc();
432 }
433
434 void PageCache::markPagesForFullStyleRecalc(Page* page)
435 {
436     Frame* mainFrame = page->mainFrame();
437
438     for (HistoryItem* current = m_head; current; current = current->m_next) {
439         CachedPage* cachedPage = current->m_cachedPage.get();
440         if (cachedPage->cachedMainFrame()->view()->frame() == mainFrame)
441             cachedPage->markForFullStyleRecalc();
442     }
443 }
444
445 void PageCache::add(PassRefPtr<HistoryItem> prpItem, Page* page)
446 {
447     ASSERT(prpItem);
448     ASSERT(page);
449     ASSERT(canCache(page));
450     
451     HistoryItem* item = prpItem.leakRef(); // Balanced in remove().
452
453     // Remove stale cache entry if necessary.
454     if (item->m_cachedPage)
455         remove(item);
456
457     item->m_cachedPage = CachedPage::create(page);
458     addToLRUList(item);
459     ++m_size;
460     
461     prune();
462 }
463
464 CachedPage* PageCache::get(HistoryItem* item)
465 {
466     if (!item)
467         return 0;
468
469     if (CachedPage* cachedPage = item->m_cachedPage.get()) {
470         // FIXME: 1800 should not be hardcoded, it should come from
471         // WebKitBackForwardCacheExpirationIntervalKey in WebKit.
472         // Or we should remove WebKitBackForwardCacheExpirationIntervalKey.
473         if (currentTime() - cachedPage->timeStamp() <= 1800)
474             return cachedPage;
475         
476         LOG(PageCache, "Not restoring page for %s from back/forward cache because cache entry has expired", item->url().string().ascii().data());
477         pageCache()->remove(item);
478     }
479     return 0;
480 }
481
482 void PageCache::remove(HistoryItem* item)
483 {
484     // Safely ignore attempts to remove items not in the cache.
485     if (!item || !item->m_cachedPage)
486         return;
487
488     autorelease(item->m_cachedPage.release());
489     removeFromLRUList(item);
490     --m_size;
491
492     item->deref(); // Balanced in add().
493 }
494
495 void PageCache::prune()
496 {
497     while (m_size > m_capacity) {
498         ASSERT(m_tail && m_tail->m_cachedPage);
499         remove(m_tail);
500     }
501 }
502
503 void PageCache::addToLRUList(HistoryItem* item)
504 {
505     item->m_next = m_head;
506     item->m_prev = 0;
507
508     if (m_head) {
509         ASSERT(m_tail);
510         m_head->m_prev = item;
511     } else {
512         ASSERT(!m_tail);
513         m_tail = item;
514     }
515
516     m_head = item;
517 }
518
519 void PageCache::removeFromLRUList(HistoryItem* item)
520 {
521     if (!item->m_next) {
522         ASSERT(item == m_tail);
523         m_tail = item->m_prev;
524     } else {
525         ASSERT(item != m_tail);
526         item->m_next->m_prev = item->m_prev;
527     }
528
529     if (!item->m_prev) {
530         ASSERT(item == m_head);
531         m_head = item->m_next;
532     } else {
533         ASSERT(item != m_head);
534         item->m_prev->m_next = item->m_next;
535     }
536 }
537
538 void PageCache::releaseAutoreleasedPagesNowDueToTimer(Timer<PageCache>*)
539 {
540     LOG(PageCache, "WebCorePageCache: Releasing page caches - %i objects pending release", m_autoreleaseSet.size());
541     releaseAutoreleasedPagesNow();
542 }
543
544 void PageCache::releaseAutoreleasedPagesNow()
545 {
546     m_autoreleaseTimer.stop();
547
548     // Postpone dead pruning until all our resources have gone dead.
549     memoryCache()->setPruneEnabled(false);
550
551     CachedPageSet tmp;
552     tmp.swap(m_autoreleaseSet);
553
554     CachedPageSet::iterator end = tmp.end();
555     for (CachedPageSet::iterator it = tmp.begin(); it != end; ++it)
556         (*it)->destroy();
557
558     // Now do the prune.
559     memoryCache()->setPruneEnabled(true);
560     memoryCache()->prune();
561 }
562
563 void PageCache::autorelease(PassRefPtr<CachedPage> page)
564 {
565     ASSERT(page);
566     ASSERT(!m_autoreleaseSet.contains(page.get()));
567     m_autoreleaseSet.add(page);
568     if (!m_autoreleaseTimer.isActive())
569         m_autoreleaseTimer.startOneShot(0);
570 }
571
572 } // namespace WebCore