PageCache.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  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. #include "config.h"
  26. #include "PageCache.h"
  27. #include "ApplicationCacheHost.h"
  28. #include "BackForwardController.h"
  29. #include "MemoryCache.h"
  30. #include "CachedPage.h"
  31. #include "DOMWindow.h"
  32. #include "DatabaseManager.h"
  33. #include "DeviceMotionController.h"
  34. #include "DeviceOrientationController.h"
  35. #include "Document.h"
  36. #include "DocumentLoader.h"
  37. #include "Frame.h"
  38. #include "FrameLoader.h"
  39. #include "FrameLoaderClient.h"
  40. #include "FrameLoaderStateMachine.h"
  41. #include "FrameView.h"
  42. #include "HistogramSupport.h"
  43. #include "HistoryController.h"
  44. #include "HistoryItem.h"
  45. #include "Logging.h"
  46. #include "Page.h"
  47. #include "Settings.h"
  48. #include "SharedWorkerRepository.h"
  49. #include <wtf/CurrentTime.h>
  50. #include <wtf/text/CString.h>
  51. #include <wtf/text/StringConcatenate.h>
  52. #if ENABLE(PROXIMITY_EVENTS)
  53. #include "DeviceProximityController.h"
  54. #endif
  55. using namespace std;
  56. namespace WebCore {
  57. #if !defined(NDEBUG)
  58. #define PCLOG(...) LOG(PageCache, "%*s%s", indentLevel*4, "", makeString(__VA_ARGS__).utf8().data())
  59. // Used in histograms, please only add at the end, and do not remove elements (renaming e.g. to "FooEnumUnused1" is fine).
  60. // This is because statistics may be gathered from histograms between versions over time, and re-using values causes collisions.
  61. enum ReasonFrameCannotBeInPageCache {
  62. NoDocumentLoader = 0,
  63. MainDocumentError,
  64. IsErrorPage,
  65. HasPlugins,
  66. IsHttpsAndCacheControlled,
  67. HasUnloadListener,
  68. HasDatabaseHandles,
  69. HasSharedWorkers,
  70. NoHistoryItem,
  71. QuickRedirectComing,
  72. IsLoadingInAPISense,
  73. IsStopping,
  74. CannotSuspendActiveDOMObjects,
  75. DocumentLoaderUsesApplicationCache,
  76. ClientDeniesCaching,
  77. NumberOfReasonsFramesCannotBeInPageCache,
  78. };
  79. COMPILE_ASSERT(NumberOfReasonsFramesCannotBeInPageCache <= sizeof(unsigned)*8, ReasonFrameCannotBeInPageCacheDoesNotFitInBitmap);
  80. static unsigned logCanCacheFrameDecision(Frame* frame, int indentLevel)
  81. {
  82. PCLOG("+---");
  83. if (!frame->loader()->documentLoader()) {
  84. PCLOG(" -There is no DocumentLoader object");
  85. return 1 << NoDocumentLoader;
  86. }
  87. KURL currentURL = frame->loader()->documentLoader()->url();
  88. KURL newURL = frame->loader()->provisionalDocumentLoader() ? frame->loader()->provisionalDocumentLoader()->url() : KURL();
  89. if (!newURL.isEmpty())
  90. PCLOG(" Determining if frame can be cached navigating from (", currentURL.string(), ") to (", newURL.string(), "):");
  91. else
  92. PCLOG(" Determining if subframe with URL (", currentURL.string(), ") can be cached:");
  93. unsigned rejectReasons = 0;
  94. if (!frame->loader()->documentLoader()->mainDocumentError().isNull()) {
  95. PCLOG(" -Main document has an error");
  96. rejectReasons |= 1 << MainDocumentError;
  97. }
  98. if (frame->loader()->documentLoader()->substituteData().isValid() && frame->loader()->documentLoader()->substituteData().failingURL().isEmpty()) {
  99. PCLOG(" -Frame is an error page");
  100. rejectReasons |= 1 << IsErrorPage;
  101. }
  102. if (frame->loader()->subframeLoader()->containsPlugins() && !frame->page()->settings()->pageCacheSupportsPlugins()) {
  103. PCLOG(" -Frame contains plugins");
  104. rejectReasons |= 1 << HasPlugins;
  105. }
  106. if (frame->document()->url().protocolIs("https")
  107. && (frame->loader()->documentLoader()->response().cacheControlContainsNoCache()
  108. || frame->loader()->documentLoader()->response().cacheControlContainsNoStore())) {
  109. PCLOG(" -Frame is HTTPS, and cache control prohibits caching or storing");
  110. rejectReasons |= 1 << IsHttpsAndCacheControlled;
  111. }
  112. if (frame->document()->domWindow() && frame->document()->domWindow()->hasEventListeners(eventNames().unloadEvent)) {
  113. PCLOG(" -Frame has an unload event listener");
  114. rejectReasons |= 1 << HasUnloadListener;
  115. }
  116. #if ENABLE(SQL_DATABASE)
  117. if (DatabaseManager::manager().hasOpenDatabases(frame->document())) {
  118. PCLOG(" -Frame has open database handles");
  119. rejectReasons |= 1 << HasDatabaseHandles;
  120. }
  121. #endif
  122. #if ENABLE(SHARED_WORKERS)
  123. if (SharedWorkerRepository::hasSharedWorkers(frame->document())) {
  124. PCLOG(" -Frame has associated SharedWorkers");
  125. rejectReasons |= 1 << HasSharedWorkers;
  126. }
  127. #endif
  128. if (!frame->loader()->history()->currentItem()) {
  129. PCLOG(" -No current history item");
  130. rejectReasons |= 1 << NoHistoryItem;
  131. }
  132. if (frame->loader()->quickRedirectComing()) {
  133. PCLOG(" -Quick redirect is coming");
  134. rejectReasons |= 1 << QuickRedirectComing;
  135. }
  136. if (frame->loader()->documentLoader()->isLoadingInAPISense()) {
  137. PCLOG(" -DocumentLoader is still loading in API sense");
  138. rejectReasons |= 1 << IsLoadingInAPISense;
  139. }
  140. if (frame->loader()->documentLoader()->isStopping()) {
  141. PCLOG(" -DocumentLoader is in the middle of stopping");
  142. rejectReasons |= 1 << IsStopping;
  143. }
  144. if (!frame->document()->canSuspendActiveDOMObjects()) {
  145. PCLOG(" -The document cannot suspect its active DOM Objects");
  146. rejectReasons |= 1 << CannotSuspendActiveDOMObjects;
  147. }
  148. if (!frame->loader()->documentLoader()->applicationCacheHost()->canCacheInPageCache()) {
  149. PCLOG(" -The DocumentLoader uses an application cache");
  150. rejectReasons |= 1 << DocumentLoaderUsesApplicationCache;
  151. }
  152. if (!frame->loader()->client()->canCachePage()) {
  153. PCLOG(" -The client says this frame cannot be cached");
  154. rejectReasons |= 1 << ClientDeniesCaching;
  155. }
  156. HistogramSupport::histogramEnumeration("PageCache.FrameCacheable", !rejectReasons, 2);
  157. int reasonCount = 0;
  158. for (int i = 0; i < NumberOfReasonsFramesCannotBeInPageCache; ++i) {
  159. if (rejectReasons & (1 << i)) {
  160. ++reasonCount;
  161. HistogramSupport::histogramEnumeration("PageCache.FrameRejectReason", i, NumberOfReasonsFramesCannotBeInPageCache);
  162. }
  163. }
  164. HistogramSupport::histogramEnumeration("PageCache.FrameRejectReasonCount", reasonCount, 1 + NumberOfReasonsFramesCannotBeInPageCache);
  165. for (Frame* child = frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
  166. rejectReasons |= logCanCacheFrameDecision(child, indentLevel + 1);
  167. PCLOG(rejectReasons ? " Frame CANNOT be cached" : " Frame CAN be cached");
  168. PCLOG("+---");
  169. return rejectReasons;
  170. }
  171. // Used in histograms, please only add at the end, and do not remove elements (renaming e.g. to "FooEnumUnused1" is fine).
  172. // This is because statistics may be gathered from histograms between versions over time, and re-using values causes collisions.
  173. enum ReasonPageCannotBeInPageCache {
  174. FrameCannotBeInPageCache = 0,
  175. DisabledBackForwardList,
  176. DisabledPageCache,
  177. UsesDeviceMotion,
  178. UsesDeviceOrientation,
  179. IsReload,
  180. IsReloadFromOrigin,
  181. IsSameLoad,
  182. NumberOfReasonsPagesCannotBeInPageCache,
  183. };
  184. COMPILE_ASSERT(NumberOfReasonsPagesCannotBeInPageCache <= sizeof(unsigned)*8, ReasonPageCannotBeInPageCacheDoesNotFitInBitmap);
  185. static void logCanCachePageDecision(Page* page)
  186. {
  187. // Only bother logging for main frames that have actually loaded and have content.
  188. if (page->mainFrame()->loader()->stateMachine()->creatingInitialEmptyDocument())
  189. return;
  190. KURL currentURL = page->mainFrame()->loader()->documentLoader() ? page->mainFrame()->loader()->documentLoader()->url() : KURL();
  191. if (currentURL.isEmpty())
  192. return;
  193. int indentLevel = 0;
  194. PCLOG("--------\n Determining if page can be cached:");
  195. unsigned rejectReasons = 0;
  196. unsigned frameRejectReasons = logCanCacheFrameDecision(page->mainFrame(), indentLevel+1);
  197. if (frameRejectReasons)
  198. rejectReasons |= 1 << FrameCannotBeInPageCache;
  199. if (!page->backForward()->isActive()) {
  200. PCLOG(" -The back/forward list is disabled or has 0 capacity");
  201. rejectReasons |= 1 << DisabledBackForwardList;
  202. }
  203. if (!page->settings()->usesPageCache()) {
  204. PCLOG(" -Page settings says b/f cache disabled");
  205. rejectReasons |= 1 << DisabledPageCache;
  206. }
  207. #if ENABLE(DEVICE_ORIENTATION)
  208. if (DeviceMotionController::isActiveAt(page)) {
  209. PCLOG(" -Page is using DeviceMotion");
  210. rejectReasons |= 1 << UsesDeviceMotion;
  211. }
  212. if (DeviceOrientationController::isActiveAt(page)) {
  213. PCLOG(" -Page is using DeviceOrientation");
  214. rejectReasons |= 1 << UsesDeviceOrientation;
  215. }
  216. #endif
  217. #if ENABLE(PROXIMITY_EVENTS)
  218. if (DeviceProximityController::isActiveAt(page)) {
  219. PCLOG(" -Page is using DeviceProximity");
  220. rejectReasons |= 1 << UsesDeviceMotion;
  221. }
  222. #endif
  223. FrameLoadType loadType = page->mainFrame()->loader()->loadType();
  224. if (loadType == FrameLoadTypeReload) {
  225. PCLOG(" -Load type is: Reload");
  226. rejectReasons |= 1 << IsReload;
  227. }
  228. if (loadType == FrameLoadTypeReloadFromOrigin) {
  229. PCLOG(" -Load type is: Reload from origin");
  230. rejectReasons |= 1 << IsReloadFromOrigin;
  231. }
  232. if (loadType == FrameLoadTypeSame) {
  233. PCLOG(" -Load type is: Same");
  234. rejectReasons |= 1 << IsSameLoad;
  235. }
  236. PCLOG(rejectReasons ? " Page CANNOT be cached\n--------" : " Page CAN be cached\n--------");
  237. HistogramSupport::histogramEnumeration("PageCache.PageCacheable", !rejectReasons, 2);
  238. int reasonCount = 0;
  239. for (int i = 0; i < NumberOfReasonsPagesCannotBeInPageCache; ++i) {
  240. if (rejectReasons & (1 << i)) {
  241. ++reasonCount;
  242. HistogramSupport::histogramEnumeration("PageCache.PageRejectReason", i, NumberOfReasonsPagesCannotBeInPageCache);
  243. }
  244. }
  245. HistogramSupport::histogramEnumeration("PageCache.PageRejectReasonCount", reasonCount, 1 + NumberOfReasonsPagesCannotBeInPageCache);
  246. const bool settingsDisabledPageCache = rejectReasons & (1 << DisabledPageCache);
  247. HistogramSupport::histogramEnumeration("PageCache.PageRejectReasonCountExcludingSettings", reasonCount - settingsDisabledPageCache, NumberOfReasonsPagesCannotBeInPageCache);
  248. // Report also on the frame reasons by page; this is distinct from the per frame statistics since it coalesces the
  249. // causes from all subframes together.
  250. HistogramSupport::histogramEnumeration("PageCache.FrameCacheableByPage", !frameRejectReasons, 2);
  251. int frameReasonCount = 0;
  252. for (int i = 0; i <= NumberOfReasonsFramesCannotBeInPageCache; ++i) {
  253. if (frameRejectReasons & (1 << i)) {
  254. ++frameReasonCount;
  255. HistogramSupport::histogramEnumeration("PageCache.FrameRejectReasonByPage", i, NumberOfReasonsFramesCannotBeInPageCache);
  256. }
  257. }
  258. HistogramSupport::histogramEnumeration("PageCache.FrameRejectReasonCountByPage", frameReasonCount, 1 + NumberOfReasonsFramesCannotBeInPageCache);
  259. }
  260. #endif // !defined(NDEBUG)
  261. PageCache* pageCache()
  262. {
  263. static PageCache* staticPageCache = new PageCache;
  264. return staticPageCache;
  265. }
  266. PageCache::PageCache()
  267. : m_capacity(0)
  268. , m_size(0)
  269. , m_head(0)
  270. , m_tail(0)
  271. #if USE(ACCELERATED_COMPOSITING)
  272. , m_shouldClearBackingStores(false)
  273. #endif
  274. {
  275. }
  276. bool PageCache::canCachePageContainingThisFrame(Frame* frame)
  277. {
  278. for (Frame* child = frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) {
  279. if (!canCachePageContainingThisFrame(child))
  280. return false;
  281. }
  282. FrameLoader* frameLoader = frame->loader();
  283. DocumentLoader* documentLoader = frameLoader->documentLoader();
  284. Document* document = frame->document();
  285. return documentLoader
  286. && documentLoader->mainDocumentError().isNull()
  287. // Do not cache error pages (these can be recognized as pages with substitute data or unreachable URLs).
  288. && !(documentLoader->substituteData().isValid() && !documentLoader->substituteData().failingURL().isEmpty())
  289. && (!frameLoader->subframeLoader()->containsPlugins() || frame->page()->settings()->pageCacheSupportsPlugins())
  290. && (!document->url().protocolIs("https") || (!documentLoader->response().cacheControlContainsNoCache() && !documentLoader->response().cacheControlContainsNoStore()))
  291. && (!document->domWindow() || !document->domWindow()->hasEventListeners(eventNames().unloadEvent))
  292. #if ENABLE(SQL_DATABASE)
  293. && !DatabaseManager::manager().hasOpenDatabases(document)
  294. #endif
  295. #if ENABLE(SHARED_WORKERS)
  296. && !SharedWorkerRepository::hasSharedWorkers(document)
  297. #endif
  298. && frameLoader->history()->currentItem()
  299. && !frameLoader->quickRedirectComing()
  300. && !documentLoader->isLoadingInAPISense()
  301. && !documentLoader->isStopping()
  302. && document->canSuspendActiveDOMObjects()
  303. // FIXME: We should investigating caching frames that have an associated
  304. // application cache. <rdar://problem/5917899> tracks that work.
  305. && documentLoader->applicationCacheHost()->canCacheInPageCache()
  306. && frameLoader->client()->canCachePage();
  307. }
  308. bool PageCache::canCache(Page* page) const
  309. {
  310. if (!page)
  311. return false;
  312. #if !defined(NDEBUG)
  313. logCanCachePageDecision(page);
  314. #endif
  315. // Cache the page, if possible.
  316. // Don't write to the cache if in the middle of a redirect, since we will want to
  317. // store the final page we end up on.
  318. // No point writing to the cache on a reload or loadSame, since we will just write
  319. // over it again when we leave that page.
  320. FrameLoadType loadType = page->mainFrame()->loader()->loadType();
  321. return m_capacity > 0
  322. && canCachePageContainingThisFrame(page->mainFrame())
  323. && page->backForward()->isActive()
  324. && page->settings()->usesPageCache()
  325. #if ENABLE(DEVICE_ORIENTATION)
  326. && !DeviceMotionController::isActiveAt(page)
  327. && !DeviceOrientationController::isActiveAt(page)
  328. #endif
  329. #if ENABLE(PROXIMITY_EVENTS)
  330. && !DeviceProximityController::isActiveAt(page)
  331. #endif
  332. && (loadType == FrameLoadTypeStandard
  333. || loadType == FrameLoadTypeBack
  334. || loadType == FrameLoadTypeForward
  335. || loadType == FrameLoadTypeIndexedBackForward);
  336. }
  337. void PageCache::setCapacity(int capacity)
  338. {
  339. ASSERT(capacity >= 0);
  340. m_capacity = max(capacity, 0);
  341. prune();
  342. }
  343. int PageCache::frameCount() const
  344. {
  345. int frameCount = 0;
  346. for (HistoryItem* current = m_head; current; current = current->m_next) {
  347. ++frameCount;
  348. ASSERT(current->m_cachedPage);
  349. frameCount += current->m_cachedPage ? current->m_cachedPage->cachedMainFrame()->descendantFrameCount() : 0;
  350. }
  351. return frameCount;
  352. }
  353. void PageCache::markPagesForVistedLinkStyleRecalc()
  354. {
  355. for (HistoryItem* current = m_head; current; current = current->m_next)
  356. current->m_cachedPage->markForVistedLinkStyleRecalc();
  357. }
  358. void PageCache::markPagesForFullStyleRecalc(Page* page)
  359. {
  360. Frame* mainFrame = page->mainFrame();
  361. for (HistoryItem* current = m_head; current; current = current->m_next) {
  362. CachedPage* cachedPage = current->m_cachedPage.get();
  363. if (cachedPage->cachedMainFrame()->view()->frame() == mainFrame)
  364. cachedPage->markForFullStyleRecalc();
  365. }
  366. }
  367. #if USE(ACCELERATED_COMPOSITING)
  368. void PageCache::markPagesForDeviceScaleChanged(Page* page)
  369. {
  370. Frame* mainFrame = page->mainFrame();
  371. for (HistoryItem* current = m_head; current; current = current->m_next) {
  372. CachedPage* cachedPage = current->m_cachedPage.get();
  373. if (cachedPage->cachedMainFrame()->view()->frame() == mainFrame)
  374. cachedPage->markForDeviceScaleChanged();
  375. }
  376. }
  377. #endif
  378. #if ENABLE(VIDEO_TRACK)
  379. void PageCache::markPagesForCaptionPreferencesChanged()
  380. {
  381. for (HistoryItem* current = m_head; current; current = current->m_next)
  382. current->m_cachedPage->markForCaptionPreferencesChanged();
  383. }
  384. #endif
  385. void PageCache::add(PassRefPtr<HistoryItem> prpItem, Page* page)
  386. {
  387. ASSERT(prpItem);
  388. ASSERT(page);
  389. ASSERT(canCache(page));
  390. HistoryItem* item = prpItem.leakRef(); // Balanced in remove().
  391. // Remove stale cache entry if necessary.
  392. if (item->m_cachedPage)
  393. remove(item);
  394. item->m_cachedPage = CachedPage::create(page);
  395. addToLRUList(item);
  396. ++m_size;
  397. prune();
  398. }
  399. CachedPage* PageCache::get(HistoryItem* item)
  400. {
  401. if (!item)
  402. return 0;
  403. if (CachedPage* cachedPage = item->m_cachedPage.get()) {
  404. if (!cachedPage->hasExpired())
  405. return cachedPage;
  406. LOG(PageCache, "Not restoring page for %s from back/forward cache because cache entry has expired", item->url().string().ascii().data());
  407. pageCache()->remove(item);
  408. }
  409. return 0;
  410. }
  411. void PageCache::remove(HistoryItem* item)
  412. {
  413. // Safely ignore attempts to remove items not in the cache.
  414. if (!item || !item->m_cachedPage)
  415. return;
  416. item->m_cachedPage.clear();
  417. removeFromLRUList(item);
  418. --m_size;
  419. item->deref(); // Balanced in add().
  420. }
  421. void PageCache::prune()
  422. {
  423. while (m_size > m_capacity) {
  424. ASSERT(m_tail && m_tail->m_cachedPage);
  425. remove(m_tail);
  426. }
  427. }
  428. void PageCache::addToLRUList(HistoryItem* item)
  429. {
  430. item->m_next = m_head;
  431. item->m_prev = 0;
  432. if (m_head) {
  433. ASSERT(m_tail);
  434. m_head->m_prev = item;
  435. } else {
  436. ASSERT(!m_tail);
  437. m_tail = item;
  438. }
  439. m_head = item;
  440. }
  441. void PageCache::removeFromLRUList(HistoryItem* item)
  442. {
  443. if (!item->m_next) {
  444. ASSERT(item == m_tail);
  445. m_tail = item->m_prev;
  446. } else {
  447. ASSERT(item != m_tail);
  448. item->m_next->m_prev = item->m_prev;
  449. }
  450. if (!item->m_prev) {
  451. ASSERT(item == m_head);
  452. m_head = item->m_next;
  453. } else {
  454. ASSERT(item != m_head);
  455. item->m_prev->m_next = item->m_next;
  456. }
  457. }
  458. } // namespace WebCore