<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[202231] trunk/Source/WebCore</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/202231">202231</a></dd>
<dt>Author</dt> <dd>akling@apple.com</dd>
<dt>Date</dt> <dd>2016-06-20 10:23:49 -0700 (Mon, 20 Jun 2016)</dd>
</dl>
<h3>Log Message</h3>
<pre>When navigating, discard decoded image data that is only live due to page cache.
<https://webkit.org/b/158941>
Reviewed by Antti Koivisto.
A resource is "live" if it's currently in use by a web page, and "dead" if it's
only kept alive by the memory cache.
This patch adds a mechanism that looks at CachedImage resources to see if all the
clients that make them appear "live" are actually pages in the page cache.
If so, we let the "jettison expensive objects on top-level navigation" mechanism
discard the decoded data for such half-live images. This can reduce the peak
memory usage during navigations quite a bit.
* loader/FrameLoader.cpp:
(WebCore::FrameLoader::commitProvisionalLoad): Move the call to MemoryPressureHandler
before we add the outgoing page to the page cache. This allows the jettisoning code
to make decisions based on which pages were cached *before* the navigation.
* loader/cache/CachedImageClient.h:
(WebCore::CachedImageClient::inPageCache):
* loader/ImageLoader.h:
* loader/ImageLoader.cpp:
(WebCore::ImageLoader::inPageCache):
* rendering/RenderObject.h:
(WebCore::RenderObject::inPageCache): Added a CachedImageClient::inPageCache() virtual
to determine which clients are currently in page cache (answered by their Document.)
* loader/cache/CachedImage.h:
* loader/cache/CachedImage.cpp:
(WebCore::CachedImage::areAllClientsInPageCache): Walks all CachedImageClient clients
and returns true if all of them are inPageCache().
* platform/MemoryPressureHandler.cpp:
(WebCore::MemoryPressureHandler::jettisonExpensiveObjectsOnTopLevelNavigation):
Walk all the known CachedImages and nuke decoded data for those that have some but
are only considered live due to clients in the page cache.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreloaderFrameLoadercpp">trunk/Source/WebCore/loader/FrameLoader.cpp</a></li>
<li><a href="#trunkSourceWebCoreloaderImageLoadercpp">trunk/Source/WebCore/loader/ImageLoader.cpp</a></li>
<li><a href="#trunkSourceWebCoreloaderImageLoaderh">trunk/Source/WebCore/loader/ImageLoader.h</a></li>
<li><a href="#trunkSourceWebCoreloadercacheCachedImagecpp">trunk/Source/WebCore/loader/cache/CachedImage.cpp</a></li>
<li><a href="#trunkSourceWebCoreloadercacheCachedImageh">trunk/Source/WebCore/loader/cache/CachedImage.h</a></li>
<li><a href="#trunkSourceWebCoreloadercacheCachedImageClienth">trunk/Source/WebCore/loader/cache/CachedImageClient.h</a></li>
<li><a href="#trunkSourceWebCoreplatformMemoryPressureHandlercpp">trunk/Source/WebCore/platform/MemoryPressureHandler.cpp</a></li>
<li><a href="#trunkSourceWebCorerenderingRenderObjecth">trunk/Source/WebCore/rendering/RenderObject.h</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (202230 => 202231)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-06-20 17:13:02 UTC (rev 202230)
+++ trunk/Source/WebCore/ChangeLog        2016-06-20 17:23:49 UTC (rev 202231)
</span><span class="lines">@@ -1,3 +1,44 @@
</span><ins>+2016-06-20 Andreas Kling <akling@apple.com>
+
+ When navigating, discard decoded image data that is only live due to page cache.
+ <https://webkit.org/b/158941>
+
+ Reviewed by Antti Koivisto.
+
+ A resource is "live" if it's currently in use by a web page, and "dead" if it's
+ only kept alive by the memory cache.
+
+ This patch adds a mechanism that looks at CachedImage resources to see if all the
+ clients that make them appear "live" are actually pages in the page cache.
+
+ If so, we let the "jettison expensive objects on top-level navigation" mechanism
+ discard the decoded data for such half-live images. This can reduce the peak
+ memory usage during navigations quite a bit.
+
+ * loader/FrameLoader.cpp:
+ (WebCore::FrameLoader::commitProvisionalLoad): Move the call to MemoryPressureHandler
+ before we add the outgoing page to the page cache. This allows the jettisoning code
+ to make decisions based on which pages were cached *before* the navigation.
+
+ * loader/cache/CachedImageClient.h:
+ (WebCore::CachedImageClient::inPageCache):
+ * loader/ImageLoader.h:
+ * loader/ImageLoader.cpp:
+ (WebCore::ImageLoader::inPageCache):
+ * rendering/RenderObject.h:
+ (WebCore::RenderObject::inPageCache): Added a CachedImageClient::inPageCache() virtual
+ to determine which clients are currently in page cache (answered by their Document.)
+
+ * loader/cache/CachedImage.h:
+ * loader/cache/CachedImage.cpp:
+ (WebCore::CachedImage::areAllClientsInPageCache): Walks all CachedImageClient clients
+ and returns true if all of them are inPageCache().
+
+ * platform/MemoryPressureHandler.cpp:
+ (WebCore::MemoryPressureHandler::jettisonExpensiveObjectsOnTopLevelNavigation):
+ Walk all the known CachedImages and nuke decoded data for those that have some but
+ are only considered live due to clients in the page cache.
+
</ins><span class="cx"> 2016-06-20 Chris Dumez <cdumez@apple.com>
</span><span class="cx">
</span><span class="cx"> Unreviewed, fix post-landing review comment from Darin on r202188.
</span></span></pre></div>
<a id="trunkSourceWebCoreloaderFrameLoadercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/loader/FrameLoader.cpp (202230 => 202231)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/loader/FrameLoader.cpp        2016-06-20 17:13:02 UTC (rev 202230)
+++ trunk/Source/WebCore/loader/FrameLoader.cpp        2016-06-20 17:23:49 UTC (rev 202231)
</span><span class="lines">@@ -1783,11 +1783,11 @@
</span><span class="cx"> willTransitionToCommitted();
</span><span class="cx">
</span><span class="cx"> if (!m_frame.tree().parent() && history().currentItem()) {
</span><ins>+ MemoryPressureHandler::singleton().jettisonExpensiveObjectsOnTopLevelNavigation();
+
</ins><span class="cx"> // Check to see if we need to cache the page we are navigating away from into the back/forward cache.
</span><span class="cx"> // We are doing this here because we know for sure that a new page is about to be loaded.
</span><span class="cx"> PageCache::singleton().addIfCacheable(*history().currentItem(), m_frame.page());
</span><del>-
- MemoryPressureHandler::singleton().jettisonExpensiveObjectsOnTopLevelNavigation();
</del><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> if (m_loadType != FrameLoadType::Replace)
</span></span></pre></div>
<a id="trunkSourceWebCoreloaderImageLoadercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/loader/ImageLoader.cpp (202230 => 202231)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/loader/ImageLoader.cpp        2016-06-20 17:13:02 UTC (rev 202230)
+++ trunk/Source/WebCore/loader/ImageLoader.cpp        2016-06-20 17:23:49 UTC (rev 202231)
</span><span class="lines">@@ -472,4 +472,9 @@
</span><span class="cx"> m_failedLoadURL = AtomicString();
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+bool ImageLoader::inPageCache() const
+{
+ return m_element.document().inPageCache();
</ins><span class="cx"> }
</span><ins>+
+}
</ins></span></pre></div>
<a id="trunkSourceWebCoreloaderImageLoaderh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/loader/ImageLoader.h (202230 => 202231)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/loader/ImageLoader.h        2016-06-20 17:13:02 UTC (rev 202230)
+++ trunk/Source/WebCore/loader/ImageLoader.h        2016-06-20 17:23:49 UTC (rev 202231)
</span><span class="lines">@@ -79,6 +79,8 @@
</span><span class="cx"> virtual void dispatchLoadEvent() = 0;
</span><span class="cx"> virtual String sourceURI(const AtomicString&) const = 0;
</span><span class="cx">
</span><ins>+ bool inPageCache() const final;
+
</ins><span class="cx"> void updatedHasPendingEvent();
</span><span class="cx">
</span><span class="cx"> void dispatchPendingBeforeLoadEvent();
</span></span></pre></div>
<a id="trunkSourceWebCoreloadercacheCachedImagecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/loader/cache/CachedImage.cpp (202230 => 202231)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/loader/cache/CachedImage.cpp        2016-06-20 17:13:02 UTC (rev 202230)
+++ trunk/Source/WebCore/loader/cache/CachedImage.cpp        2016-06-20 17:23:49 UTC (rev 202231)
</span><span class="lines">@@ -527,4 +527,16 @@
</span><span class="cx"> return CachedResource::makeRevalidationDecision(cachePolicy);
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+bool CachedImage::areAllClientsInPageCache() const
+{
+ for (auto& entry : m_clients) {
+ auto& client = *entry.key;
+ if (client.resourceClientType() != CachedImageClient::expectedType())
+ continue;
+ if (!static_cast<CachedImageClient&>(client).inPageCache())
+ return false;
+ }
+ return true;
+}
+
</ins><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCoreloadercacheCachedImageh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/loader/cache/CachedImage.h (202230 => 202231)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/loader/cache/CachedImage.h        2016-06-20 17:13:02 UTC (rev 202230)
+++ trunk/Source/WebCore/loader/cache/CachedImage.h        2016-06-20 17:23:49 UTC (rev 202231)
</span><span class="lines">@@ -75,6 +75,8 @@
</span><span class="cx"> void addDataBuffer(SharedBuffer&) override;
</span><span class="cx"> void finishLoading(SharedBuffer*) override;
</span><span class="cx">
</span><ins>+ bool areAllClientsInPageCache() const;
+
</ins><span class="cx"> enum SizeType {
</span><span class="cx"> UsedSize,
</span><span class="cx"> IntrinsicSize
</span></span></pre></div>
<a id="trunkSourceWebCoreloadercacheCachedImageClienth"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/loader/cache/CachedImageClient.h (202230 => 202231)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/loader/cache/CachedImageClient.h        2016-06-20 17:13:02 UTC (rev 202230)
+++ trunk/Source/WebCore/loader/cache/CachedImageClient.h        2016-06-20 17:23:49 UTC (rev 202231)
</span><span class="lines">@@ -42,6 +42,8 @@
</span><span class="cx">
</span><span class="cx"> // Called when GIF animation progresses.
</span><span class="cx"> virtual void newImageAnimationFrameAvailable(CachedImage& image) { imageChanged(&image); }
</span><ins>+
+ virtual bool inPageCache() const { return false; }
</ins><span class="cx"> };
</span><span class="cx">
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformMemoryPressureHandlercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/MemoryPressureHandler.cpp (202230 => 202231)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/MemoryPressureHandler.cpp        2016-06-20 17:13:02 UTC (rev 202230)
+++ trunk/Source/WebCore/platform/MemoryPressureHandler.cpp        2016-06-20 17:23:49 UTC (rev 202231)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><span class="cx"> /*
</span><del>- * Copyright (C) 2011, 2014 Apple Inc. All Rights Reserved.
</del><ins>+ * Copyright (C) 2011-2016 Apple Inc. All Rights Reserved.
</ins><span class="cx"> *
</span><span class="cx"> * Redistribution and use in source and binary forms, with or without
</span><span class="cx"> * modification, are permitted provided that the following conditions
</span><span class="lines">@@ -27,6 +27,7 @@
</span><span class="cx"> #include "MemoryPressureHandler.h"
</span><span class="cx">
</span><span class="cx"> #include "CSSValuePool.h"
</span><ins>+#include "CachedImage.h"
</ins><span class="cx"> #include "Chrome.h"
</span><span class="cx"> #include "ChromeClient.h"
</span><span class="cx"> #include "Document.h"
</span><span class="lines">@@ -162,7 +163,6 @@
</span><span class="cx">
</span><span class="cx"> void MemoryPressureHandler::jettisonExpensiveObjectsOnTopLevelNavigation()
</span><span class="cx"> {
</span><del>-#if PLATFORM(IOS)
</del><span class="cx"> // Protect against doing excessive jettisoning during repeated navigations.
</span><span class="cx"> const auto minimumTimeSinceNavigation = 2s;
</span><span class="cx">
</span><span class="lines">@@ -174,6 +174,15 @@
</span><span class="cx"> if (!shouldJettison)
</span><span class="cx"> return;
</span><span class="cx">
</span><ins>+ MemoryCache::singleton().forEachResource([](CachedResource& resource) {
+ if (resource.isImage()
+ && resource.decodedSize()
+ && downcast<CachedImage>(resource).areAllClientsInPageCache()) {
+ resource.destroyDecodedData();
+ }
+ });
+
+#if PLATFORM(IOS)
</ins><span class="cx"> // Throw away linked JS code. Linked code is tied to a global object and is not reusable.
</span><span class="cx"> // The immediate memory savings outweigh the cost of recompilation in case we go back again.
</span><span class="cx"> GCController::singleton().deleteAllLinkedCode();
</span></span></pre></div>
<a id="trunkSourceWebCorerenderingRenderObjecth"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/rendering/RenderObject.h (202230 => 202231)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/rendering/RenderObject.h        2016-06-20 17:13:02 UTC (rev 202230)
+++ trunk/Source/WebCore/rendering/RenderObject.h        2016-06-20 17:23:49 UTC (rev 202231)
</span><span class="lines">@@ -844,6 +844,8 @@
</span><span class="cx"> void setNeedsLayoutIsForbidden(bool flag) { m_setNeedsLayoutForbidden = flag; }
</span><span class="cx"> #endif
</span><span class="cx">
</span><ins>+ bool inPageCache() const final { return document().inPageCache(); }
+
</ins><span class="cx"> void addAbsoluteRectForLayer(LayoutRect& result);
</span><span class="cx"> void setLayerNeedsFullRepaint();
</span><span class="cx"> void setLayerNeedsFullRepaintForPositionedMovementLayout();
</span></span></pre>
</div>
</div>
</body>
</html>