bool PageCache::canCache(Page& page) const { if (!m_maxSize) { logPageCacheFailureDiagnosticMessage(&page, DiagnosticLoggingKeys::isDisabledKey()); return false; } if (MemoryPressureHandler::singleton().isUnderMemoryPressure()) { logPageCacheFailureDiagnosticMessage(&page, DiagnosticLoggingKeys::underMemoryPressureKey()); return false; } return canCachePage(page); }
static inline void logPageCacheFailureDiagnosticMessage(Page* page, const String& reason) { if (!page) return; logPageCacheFailureDiagnosticMessage(page->diagnosticLoggingClient(), reason); }
CachedPage* PageCache::get(HistoryItem& item, Page* page) { CachedPage* cachedPage = item.m_cachedPage.get(); if (!cachedPage) { if (item.m_pruningReason != PruningReason::None) logPageCacheFailureDiagnosticMessage(page, pruningReasonToDiagnosticLoggingKey(item.m_pruningReason)); return nullptr; } if (cachedPage->hasExpired() || (page && page->isResourceCachingDisabled())) { LOG(PageCache, "Not restoring page for %s from back/forward cache because cache entry has expired", item.url().string().ascii().data()); logPageCacheFailureDiagnosticMessage(page, DiagnosticLoggingKeys::expiredKey()); remove(item); return nullptr; } return cachedPage; }
std::unique_ptr<CachedPage> PageCache::take(HistoryItem& item, Page* page) { if (!item.m_cachedPage) { if (item.m_pruningReason != PruningReason::None) logPageCacheFailureDiagnosticMessage(page, pruningReasonToDiagnosticLoggingKey(item.m_pruningReason)); return nullptr; } std::unique_ptr<CachedPage> cachedPage = WTF::move(item.m_cachedPage); m_items.remove(&item); if (cachedPage->hasExpired()) { LOG(PageCache, "Not restoring page for %s from back/forward cache because cache entry has expired", item.url().string().ascii().data()); logPageCacheFailureDiagnosticMessage(page, DiagnosticLoggingKeys::expiredKey()); return nullptr; } return cachedPage; }
static bool canCacheFrame(Frame& frame, DiagnosticLoggingClient& diagnosticLoggingClient, unsigned indentLevel) { PCLOG("+---"); FrameLoader& frameLoader = frame.loader(); // Prevent page caching if a subframe is still in provisional load stage. // We only do this check for subframes because the main frame is reused when navigating to a new page. if (!frame.isMainFrame() && frameLoader.state() == FrameStateProvisional) { PCLOG(" -Frame is in provisional load stage"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::provisionalLoadKey()); return false; } DocumentLoader* documentLoader = frameLoader.documentLoader(); if (!documentLoader) { PCLOG(" -There is no DocumentLoader object"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::noDocumentLoaderKey()); return false; } URL currentURL = documentLoader->url(); URL newURL = frameLoader.provisionalDocumentLoader() ? frameLoader.provisionalDocumentLoader()->url() : URL(); if (!newURL.isEmpty()) PCLOG(" Determining if frame can be cached navigating from (", currentURL.string(), ") to (", newURL.string(), "):"); else PCLOG(" Determining if subframe with URL (", currentURL.string(), ") can be cached:"); bool isCacheable = true; if (!documentLoader->mainDocumentError().isNull()) { PCLOG(" -Main document has an error"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::mainDocumentErrorKey()); if (documentLoader->mainDocumentError().isCancellation() && documentLoader->subresourceLoadersArePageCacheAcceptable()) PCLOG(" -But, it was a cancellation and all loaders during the cancelation were loading images or XHR."); else isCacheable = false; } // Do not cache error pages (these can be recognized as pages with substitute data or unreachable URLs). if (documentLoader->substituteData().isValid() && !documentLoader->substituteData().failingURL().isEmpty()) { PCLOG(" -Frame is an error page"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::isErrorPageKey()); isCacheable = false; } if (frameLoader.subframeLoader().containsPlugins() && !frame.page()->settings().pageCacheSupportsPlugins()) { PCLOG(" -Frame contains plugins"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::hasPluginsKey()); isCacheable = false; } if (frame.isMainFrame() && frame.document() && frame.document()->url().protocolIs("https") && documentLoader->response().cacheControlContainsNoStore()) { PCLOG(" -Frame is HTTPS, and cache control prohibits storing"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::httpsNoStoreKey()); isCacheable = false; } if (frame.isMainFrame() && !frameLoader.history().currentItem()) { PCLOG(" -Main frame has no current history item"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::noCurrentHistoryItemKey()); isCacheable = false; } if (frameLoader.quickRedirectComing()) { PCLOG(" -Quick redirect is coming"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::quirkRedirectComingKey()); isCacheable = false; } if (documentLoader->isLoading()) { PCLOG(" -DocumentLoader is still loading"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::isLoadingKey()); isCacheable = false; } if (documentLoader->isStopping()) { PCLOG(" -DocumentLoader is in the middle of stopping"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::documentLoaderStoppingKey()); isCacheable = false; } Vector<ActiveDOMObject*> unsuspendableObjects; if (frame.document() && !frame.document()->canSuspendActiveDOMObjectsForDocumentSuspension(&unsuspendableObjects)) { PCLOG(" -The document cannot suspend its active DOM Objects"); for (auto* activeDOMObject : unsuspendableObjects) { PCLOG(" - Unsuspendable: ", activeDOMObject->activeDOMObjectName()); diagnosticLoggingClient.logDiagnosticMessageWithValue(DiagnosticLoggingKeys::pageCacheKey(), DiagnosticLoggingKeys::unsuspendableDOMObjectKey(), activeDOMObject->activeDOMObjectName(), ShouldSample::Yes); UNUSED_PARAM(activeDOMObject); } logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::cannotSuspendActiveDOMObjectsKey()); isCacheable = false; } // FIXME: We should investigating caching frames that have an associated // application cache. <rdar://problem/5917899> tracks that work. if (!documentLoader->applicationCacheHost()->canCacheInPageCache()) { PCLOG(" -The DocumentLoader uses an application cache"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::applicationCacheKey()); isCacheable = false; } if (!frameLoader.client().canCachePage()) { PCLOG(" -The client says this frame cannot be cached"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::deniedByClientKey()); isCacheable = false; } for (Frame* child = frame.tree().firstChild(); child; child = child->tree().nextSibling()) { if (!canCacheFrame(*child, diagnosticLoggingClient, indentLevel + 1)) isCacheable = false; } PCLOG(isCacheable ? " Frame CAN be cached" : " Frame CANNOT be cached"); PCLOG("+---"); return isCacheable; }
static bool canCachePage(Page& page) { unsigned indentLevel = 0; PCLOG("--------\n Determining if page can be cached:"); DiagnosticLoggingClient& diagnosticLoggingClient = page.diagnosticLoggingClient(); bool isCacheable = canCacheFrame(page.mainFrame(), diagnosticLoggingClient, indentLevel + 1); if (!page.settings().usesPageCache() || page.isResourceCachingDisabled()) { PCLOG(" -Page settings says b/f cache disabled"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::isDisabledKey()); isCacheable = false; } #if ENABLE(DEVICE_ORIENTATION) && !PLATFORM(IOS) if (DeviceMotionController::isActiveAt(&page)) { PCLOG(" -Page is using DeviceMotion"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::deviceMotionKey()); isCacheable = false; } if (DeviceOrientationController::isActiveAt(&page)) { PCLOG(" -Page is using DeviceOrientation"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::deviceOrientationKey()); isCacheable = false; } #endif #if ENABLE(PROXIMITY_EVENTS) if (DeviceProximityController::isActiveAt(page)) { PCLOG(" -Page is using DeviceProximity"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, deviceProximityKey); isCacheable = false; } #endif FrameLoadType loadType = page.mainFrame().loader().loadType(); switch (loadType) { case FrameLoadType::Reload: // No point writing to the cache on a reload, since we will just write over it again when we leave that page. PCLOG(" -Load type is: Reload"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::reloadKey()); isCacheable = false; break; case FrameLoadType::Same: // user loads same URL again (but not reload button) // No point writing to the cache on a same load, since we will just write over it again when we leave that page. PCLOG(" -Load type is: Same"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::sameLoadKey()); isCacheable = false; break; case FrameLoadType::RedirectWithLockedBackForwardList: // Don't write to the cache if in the middle of a redirect, since we will want to store the final page we end up on. PCLOG(" -Load type is: RedirectWithLockedBackForwardList"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::redirectKey()); isCacheable = false; break; case FrameLoadType::Replace: // No point writing to the cache on a replace, since we will just write over it again when we leave that page. PCLOG(" -Load type is: Replace"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::replaceKey()); isCacheable = false; break; case FrameLoadType::ReloadFromOrigin: { // No point writing to the cache on a reload, since we will just write over it again when we leave that page. PCLOG(" -Load type is: ReloadFromOrigin"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::reloadFromOriginKey()); isCacheable = false; break; } case FrameLoadType::Standard: case FrameLoadType::Back: case FrameLoadType::Forward: case FrameLoadType::IndexedBackForward: // a multi-item hop in the backforward list // Cacheable. break; } if (isCacheable) PCLOG(" Page CAN be cached\n--------"); else PCLOG(" Page CANNOT be cached\n--------"); diagnosticLoggingClient.logDiagnosticMessageWithResult(DiagnosticLoggingKeys::pageCacheKey(), DiagnosticLoggingKeys::canCacheKey(), isCacheable ? DiagnosticLoggingResultPass : DiagnosticLoggingResultFail, ShouldSample::Yes); return isCacheable; }
static void logCanCachePageDecision(Page& page) { // Only bother logging for main frames that have actually loaded and have content. if (page.mainFrame().loader().stateMachine().creatingInitialEmptyDocument()) return; URL currentURL = page.mainFrame().loader().documentLoader() ? page.mainFrame().loader().documentLoader()->url() : URL(); if (currentURL.isEmpty()) return; unsigned indentLevel = 0; PCLOG("--------\n Determining if page can be cached:"); unsigned rejectReasons = 0; MainFrame& mainFrame = page.mainFrame(); DiagnosticLoggingClient& diagnosticLoggingClient = mainFrame.diagnosticLoggingClient(); unsigned frameRejectReasons = logCanCacheFrameDecision(mainFrame, diagnosticLoggingClient, indentLevel + 1); if (frameRejectReasons) rejectReasons |= 1 << FrameCannotBeInPageCache; if (!page.settings().usesPageCache()) { PCLOG(" -Page settings says b/f cache disabled"); rejectReasons |= 1 << DisabledPageCache; } #if ENABLE(DEVICE_ORIENTATION) && !PLATFORM(IOS) if (DeviceMotionController::isActiveAt(page)) { PCLOG(" -Page is using DeviceMotion"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::deviceMotionKey()); rejectReasons |= 1 << UsesDeviceMotion; } if (DeviceOrientationController::isActiveAt(page)) { PCLOG(" -Page is using DeviceOrientation"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::deviceOrientationKey()); rejectReasons |= 1 << UsesDeviceOrientation; } #endif #if ENABLE(PROXIMITY_EVENTS) if (DeviceProximityController::isActiveAt(page)) { PCLOG(" -Page is using DeviceProximity"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, deviceProximityKey); rejectReasons |= 1 << UsesDeviceMotion; } #endif FrameLoadType loadType = page.mainFrame().loader().loadType(); if (loadType == FrameLoadType::Reload) { PCLOG(" -Load type is: Reload"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::reloadKey()); rejectReasons |= 1 << IsReload; } if (loadType == FrameLoadType::ReloadFromOrigin) { PCLOG(" -Load type is: Reload from origin"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::reloadFromOriginKey()); rejectReasons |= 1 << IsReloadFromOrigin; } if (loadType == FrameLoadType::Same) { PCLOG(" -Load type is: Same"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::sameLoadKey()); rejectReasons |= 1 << IsSameLoad; } if (rejectReasons) PCLOG(" Page CANNOT be cached\n--------"); else PCLOG(" Page CAN be cached\n--------"); diagnosticLoggingClient.logDiagnosticMessageWithResult(DiagnosticLoggingKeys::pageCacheKey(), emptyString(), rejectReasons ? DiagnosticLoggingResultFail : DiagnosticLoggingResultPass, ShouldSample::Yes); }
static unsigned logCanCacheFrameDecision(Frame& frame, DiagnosticLoggingClient& diagnosticLoggingClient, unsigned indentLevel) { PCLOG("+---"); if (!frame.loader().documentLoader()) { PCLOG(" -There is no DocumentLoader object"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::noDocumentLoaderKey()); return 1 << NoDocumentLoader; } URL currentURL = frame.loader().documentLoader()->url(); URL newURL = frame.loader().provisionalDocumentLoader() ? frame.loader().provisionalDocumentLoader()->url() : URL(); if (!newURL.isEmpty()) PCLOG(" Determining if frame can be cached navigating from (", currentURL.string(), ") to (", newURL.string(), "):"); else PCLOG(" Determining if subframe with URL (", currentURL.string(), ") can be cached:"); unsigned rejectReasons = 0; if (!frame.loader().documentLoader()->mainDocumentError().isNull()) { PCLOG(" -Main document has an error"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::mainDocumentErrorKey()); if (frame.loader().documentLoader()->mainDocumentError().isCancellation() && frame.loader().documentLoader()->subresourceLoadersArePageCacheAcceptable()) PCLOG(" -But, it was a cancellation and all loaders during the cancelation were loading images or XHR."); else rejectReasons |= 1 << MainDocumentError; } if (frame.loader().documentLoader()->substituteData().isValid() && frame.loader().documentLoader()->substituteData().failingURL().isEmpty()) { PCLOG(" -Frame is an error page"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::isErrorPageKey()); rejectReasons |= 1 << IsErrorPage; } if (frame.loader().subframeLoader().containsPlugins() && !frame.page()->settings().pageCacheSupportsPlugins()) { PCLOG(" -Frame contains plugins"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::hasPluginsKey()); rejectReasons |= 1 << HasPlugins; } if (frame.isMainFrame() && frame.document()->url().protocolIs("https") && frame.loader().documentLoader()->response().cacheControlContainsNoStore()) { PCLOG(" -Frame is HTTPS, and cache control prohibits storing"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::httpsNoStoreKey()); rejectReasons |= 1 << IsHttpsAndCacheControlled; } if (!frame.loader().history().currentItem()) { PCLOG(" -No current history item"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::noCurrentHistoryItemKey()); rejectReasons |= 1 << NoHistoryItem; } if (frame.loader().quickRedirectComing()) { PCLOG(" -Quick redirect is coming"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::quirkRedirectComingKey()); rejectReasons |= 1 << QuickRedirectComing; } if (frame.loader().documentLoader()->isLoading()) { PCLOG(" -DocumentLoader is still loading"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::isLoadingKey()); rejectReasons |= 1 << IsLoading; } if (frame.loader().documentLoader()->isStopping()) { PCLOG(" -DocumentLoader is in the middle of stopping"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::documentLoaderStoppingKey()); rejectReasons |= 1 << IsStopping; } Vector<ActiveDOMObject*> unsuspendableObjects; if (!frame.document()->canSuspendActiveDOMObjectsForPageCache(&unsuspendableObjects)) { PCLOG(" -The document cannot suspend its active DOM Objects"); for (auto* activeDOMObject : unsuspendableObjects) { PCLOG(" - Unsuspendable: ", activeDOMObject->activeDOMObjectName()); diagnosticLoggingClient.logDiagnosticMessageWithValue(DiagnosticLoggingKeys::pageCacheKey(), DiagnosticLoggingKeys::unsuspendableDOMObjectKey(), activeDOMObject->activeDOMObjectName(), ShouldSample::Yes); UNUSED_PARAM(activeDOMObject); } logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::cannotSuspendActiveDOMObjectsKey()); rejectReasons |= 1 << CannotSuspendActiveDOMObjects; } if (!frame.loader().documentLoader()->applicationCacheHost()->canCacheInPageCache()) { PCLOG(" -The DocumentLoader uses an application cache"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::applicationCacheKey()); rejectReasons |= 1 << DocumentLoaderUsesApplicationCache; } if (!frame.loader().client().canCachePage()) { PCLOG(" -The client says this frame cannot be cached"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::deniedByClientKey()); rejectReasons |= 1 << ClientDeniesCaching; } for (Frame* child = frame.tree().firstChild(); child; child = child->tree().nextSibling()) rejectReasons |= logCanCacheFrameDecision(*child, diagnosticLoggingClient, indentLevel + 1); PCLOG(rejectReasons ? " Frame CANNOT be cached" : " Frame CAN be cached"); PCLOG("+---"); return rejectReasons; }