<!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>[212265] trunk</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/212265">212265</a></dd>
<dt>Author</dt> <dd>commit-queue@webkit.org</dd>
<dt>Date</dt> <dd>2017-02-13 17:29:24 -0800 (Mon, 13 Feb 2017)</dd>
</dl>

<h3>Log Message</h3>
<pre>The current frame of an image should not deleted if another frame is asynchronously being decoded
https://bugs.webkit.org/show_bug.cgi?id=167618

Patch by Said Abou-Hallawa &lt;sabouhallawa@apple.com&gt; on 2017-02-13
Reviewed by Simon Fraser.

Source/WebCore:

Test: fast/images/animated-image-draw-while-decode.html

If the memory cache asks the BitmapImage to destroy all its frames while
the next frame is being decoded, a thread contention may happen. This can
happen when BitmapImage::draw() is called and the next frame is not ready
yet for drawing, so the current frame has to be drawn. This will invoke
a frame decoding in the same image from the drawing committing thread.

We can avoid that by destroying all the frames except the current frame if
the image is asynchronously decoding its frames. This should not add extra
memory overhead because building the image frame cache and then destroying
it, when needed, is an on-going process. The frames will be allocated and
decoded all the time and all of them can be destroyed except the current one.

* platform/graphics/BitmapImage.cpp:
(WebCore::BitmapImage::destroyDecodedData):
(WebCore::BitmapImage::destroyDecodedDataIfNecessary):
The logic of destroying the ImageFrames was split among BitmapImage, ImageSource
and ImageFrameCache. Move all the logic to BitmapImage and have ImageFrameCache
be responsible only for destroying a range of ImageFrames.

(WebCore::BitmapImage::draw): add an ASSERT_IMPLIES to ensure the current frame
is ready to be rendered if the next frame is being decoded.

* platform/graphics/BitmapImage.h: Move a const from ImageFrameCache.h to BitmapImage.h.

* platform/graphics/ImageFrameCache.cpp:
(WebCore::ImageFrameCache::destroyDecodedData):
(WebCore::ImageFrameCache::destroyDecodedDataIfNecessary): Deleted.
* platform/graphics/ImageFrameCache.h:
(WebCore::ImageFrameCache::destroyAllDecodedData):
(WebCore::ImageFrameCache::destroyAllDecodedDataExcludeFrame):
(WebCore::ImageFrameCache::destroyDecodedDataBeforeFrame):
Make ImageFrameCache be responsible for destroying a range of ImageFrames.
This range might include all the frames, all the frames but up to a specific
frame, or all the frames but exclude one frame in the middle.

* platform/graphics/ImageSource.cpp:
(WebCore::ImageSource::clear): No need to call clearFrameBufferCache() from clear().
The decision to call clearFrameBufferCache() or clear() is moved to
BitmapImage::destroyDecodedData().

(WebCore::ImageSource::destroyDecodedData): Deleted.
(WebCore::ImageSource::destroyDecodedDataIfNecessary): Deleted.
These functions are replaced by another set of functions in ImageSource.h.

* platform/graphics/ImageSource.h:
(WebCore::ImageSource::destroyAllDecodedData):
(WebCore::ImageSource::destroyAllDecodedDataExcludeFrame):
(WebCore::ImageSource::destroyDecodedDataBeforeFrame):
(WebCore::ImageSource::hasDecodingQueue):
These are new wrappers which call the corresponding ImageFrameCache functions.

Source/WTF:

Add ASSERT_IMPLIES() which should fire when a condition is true but the
assertion is false.

* wtf/Assertions.h:

LayoutTests:

This test did not crash on Mac when running it without this patch. But
the new ASSERT_IMPLIES(), which is added to BitmapImage::draw(), fires
when the other changes are not included. So the bug could have happened
without the patch but the crash did not since it requires a thread
contention in the system underlying components.

* fast/images/animated-image-draw-while-decode-expected.txt: Added.
* fast/images/animated-image-draw-while-decode.html: Added.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkSourceWTFChangeLog">trunk/Source/WTF/ChangeLog</a></li>
<li><a href="#trunkSourceWTFwtfAssertionsh">trunk/Source/WTF/wtf/Assertions.h</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsBitmapImagecpp">trunk/Source/WebCore/platform/graphics/BitmapImage.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsBitmapImageh">trunk/Source/WebCore/platform/graphics/BitmapImage.h</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsImageFrameCachecpp">trunk/Source/WebCore/platform/graphics/ImageFrameCache.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsImageFrameCacheh">trunk/Source/WebCore/platform/graphics/ImageFrameCache.h</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsImageSourcecpp">trunk/Source/WebCore/platform/graphics/ImageSource.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsImageSourceh">trunk/Source/WebCore/platform/graphics/ImageSource.h</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsfastimagesanimatedimagedrawwhiledecodeexpectedtxt">trunk/LayoutTests/fast/images/animated-image-draw-while-decode-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfastimagesanimatedimagedrawwhiledecodehtml">trunk/LayoutTests/fast/images/animated-image-draw-while-decode.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (212264 => 212265)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2017-02-14 01:18:59 UTC (rev 212264)
+++ trunk/LayoutTests/ChangeLog        2017-02-14 01:29:24 UTC (rev 212265)
</span><span class="lines">@@ -1,3 +1,19 @@
</span><ins>+2017-02-13  Said Abou-Hallawa  &lt;sabouhallawa@apple.com&gt;
+
+        The current frame of an image should not deleted if another frame is asynchronously being decoded
+        https://bugs.webkit.org/show_bug.cgi?id=167618
+
+        Reviewed by Simon Fraser.
+
+        This test did not crash on Mac when running it without this patch. But
+        the new ASSERT_IMPLIES(), which is added to BitmapImage::draw(), fires
+        when the other changes are not included. So the bug could have happened
+        without the patch but the crash did not since it requires a thread
+        contention in the system underlying components.
+
+        * fast/images/animated-image-draw-while-decode-expected.txt: Added.
+        * fast/images/animated-image-draw-while-decode.html: Added.
+
</ins><span class="cx"> 2017-02-13  Jiewen Tan  &lt;jiewen_tan@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [WebCrypto] WebInspector should indicate webkitSubtle is deprecated
</span></span></pre></div>
<a id="trunkLayoutTestsfastimagesanimatedimagedrawwhiledecodeexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/images/animated-image-draw-while-decode-expected.txt (0 => 212265)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/images/animated-image-draw-while-decode-expected.txt                                (rev 0)
+++ trunk/LayoutTests/fast/images/animated-image-draw-while-decode-expected.txt        2017-02-14 01:29:24 UTC (rev 212265)
</span><span class="lines">@@ -0,0 +1 @@
</span><ins>+PASS. WebKit didn't crash.
</ins></span></pre></div>
<a id="trunkLayoutTestsfastimagesanimatedimagedrawwhiledecodehtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/images/animated-image-draw-while-decode.html (0 => 212265)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/images/animated-image-draw-while-decode.html                                (rev 0)
+++ trunk/LayoutTests/fast/images/animated-image-draw-while-decode.html        2017-02-14 01:29:24 UTC (rev 212265)
</span><span class="lines">@@ -0,0 +1,64 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+&lt;style&gt;
+    canvas {
+        width: 100px;
+        height: 100px;
+    }
+&lt;/style&gt;    
+&lt;body&gt;
+    &lt;canvas id=&quot;canvas&quot;&gt;&lt;/canvas&gt;
+    &lt;script&gt;
+        function drawFrame(image) {
+            return new Promise((resolve) =&gt; {
+                let canvas = document.getElementById(&quot;canvas&quot;);
+                let context = canvas.getContext(&quot;2d&quot;);
+                context.drawImage(image, 0, 0, canvas.width, canvas.height);
+                setTimeout(() =&gt; {
+                    resolve();
+                }, 20);
+            });
+        }
+
+        function drawImage(image, frameCount) {
+            let promise = drawFrame(image);
+            for (let frame = 1; frame &lt; frameCount; ++frame) {
+                promise = promise.then(() =&gt; {
+                    // This forces destroyDecodedData() to be called.
+                    internals.pruneMemoryCacheToSize(0);
+                    return drawFrame(image);
+                });
+            }
+            return promise;
+        }
+
+        function loadImage(src, frameCount) {
+            return new Promise((resolve) =&gt; {
+                let image = new Image;
+                image.onload = (() =&gt; {
+                    if (!window.internals)
+                        return;
+                    // This forces aysnc image decoding.
+                    internals.setImageFrameDecodingDuration(image, 0.030);
+                    drawImage(image, frameCount).then(resolve);
+                });
+                image.src = src;
+            });
+        }
+
+        (function() {
+            if (window.testRunner) {
+                testRunner.dumpAsText();
+                testRunner.waitUntilDone();
+            }
+
+            loadImage(&quot;resources/animated-red-green-blue.gif&quot;, 10).then(() =&gt; {
+                if (window.testRunner) {
+                    document.write(&quot;PASS. WebKit didn't crash.&quot;);
+                    testRunner.notifyDone();
+                }
+            });
+        })();
+    &lt;/script&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkSourceWTFChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WTF/ChangeLog (212264 => 212265)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WTF/ChangeLog        2017-02-14 01:18:59 UTC (rev 212264)
+++ trunk/Source/WTF/ChangeLog        2017-02-14 01:29:24 UTC (rev 212265)
</span><span class="lines">@@ -1,3 +1,15 @@
</span><ins>+2017-02-13  Said Abou-Hallawa  &lt;sabouhallawa@apple.com&gt;
+
+        The current frame of an image should not deleted if another frame is asynchronously being decoded
+        https://bugs.webkit.org/show_bug.cgi?id=167618
+
+        Reviewed by Simon Fraser.
+
+        Add ASSERT_IMPLIES() which should fire when a condition is true but the
+        assertion is false.
+
+        * wtf/Assertions.h:
+
</ins><span class="cx"> 2017-02-13  Brady Eidson  &lt;beidson@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Followup to: Replace all WebKit Library Version checks in WK2 with SDK version checks.
</span></span></pre></div>
<a id="trunkSourceWTFwtfAssertionsh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WTF/wtf/Assertions.h (212264 => 212265)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WTF/wtf/Assertions.h        2017-02-14 01:18:59 UTC (rev 212264)
+++ trunk/Source/WTF/wtf/Assertions.h        2017-02-14 01:29:24 UTC (rev 212265)
</span><span class="lines">@@ -260,6 +260,7 @@
</span><span class="cx"> #define ASSERT(assertion) ((void)0)
</span><span class="cx"> #define ASSERT_AT(assertion, file, line, function) ((void)0)
</span><span class="cx"> #define ASSERT_NOT_REACHED() ((void)0)
</span><ins>+#define ASSERT_IMPLIES(condition, assertion) ((void)0)
</ins><span class="cx"> #define NO_RETURN_DUE_TO_ASSERT
</span><span class="cx"> 
</span><span class="cx"> #define ASSERT_UNUSED(variable, assertion) ((void)variable)
</span><span class="lines">@@ -298,6 +299,13 @@
</span><span class="cx">     CRASH(); \
</span><span class="cx"> } while (0)
</span><span class="cx"> 
</span><ins>+#define ASSERT_IMPLIES(condition, assertion) do { \
+    if ((condition) &amp;&amp; !(assertion)) { \
+        WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #condition &quot; =&gt; &quot; #assertion); \
+        CRASH(); \
+    } \
+} while (0)
+
</ins><span class="cx"> #define ASSERT_UNUSED(variable, assertion) ASSERT(assertion)
</span><span class="cx"> 
</span><span class="cx"> #define NO_RETURN_DUE_TO_ASSERT NO_RETURN_DUE_TO_CRASH
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (212264 => 212265)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2017-02-14 01:18:59 UTC (rev 212264)
+++ trunk/Source/WebCore/ChangeLog        2017-02-14 01:29:24 UTC (rev 212265)
</span><span class="lines">@@ -1,3 +1,63 @@
</span><ins>+2017-02-13  Said Abou-Hallawa  &lt;sabouhallawa@apple.com&gt;
+
+        The current frame of an image should not deleted if another frame is asynchronously being decoded
+        https://bugs.webkit.org/show_bug.cgi?id=167618
+
+        Reviewed by Simon Fraser.
+
+        Test: fast/images/animated-image-draw-while-decode.html
+
+        If the memory cache asks the BitmapImage to destroy all its frames while
+        the next frame is being decoded, a thread contention may happen. This can
+        happen when BitmapImage::draw() is called and the next frame is not ready
+        yet for drawing, so the current frame has to be drawn. This will invoke
+        a frame decoding in the same image from the drawing committing thread.
+
+        We can avoid that by destroying all the frames except the current frame if
+        the image is asynchronously decoding its frames. This should not add extra
+        memory overhead because building the image frame cache and then destroying
+        it, when needed, is an on-going process. The frames will be allocated and
+        decoded all the time and all of them can be destroyed except the current one.
+        
+        * platform/graphics/BitmapImage.cpp:
+        (WebCore::BitmapImage::destroyDecodedData):
+        (WebCore::BitmapImage::destroyDecodedDataIfNecessary):
+        The logic of destroying the ImageFrames was split among BitmapImage, ImageSource
+        and ImageFrameCache. Move all the logic to BitmapImage and have ImageFrameCache
+        be responsible only for destroying a range of ImageFrames.
+
+        (WebCore::BitmapImage::draw): add an ASSERT_IMPLIES to ensure the current frame
+        is ready to be rendered if the next frame is being decoded.
+        
+        * platform/graphics/BitmapImage.h: Move a const from ImageFrameCache.h to BitmapImage.h.
+
+        * platform/graphics/ImageFrameCache.cpp:
+        (WebCore::ImageFrameCache::destroyDecodedData):
+        (WebCore::ImageFrameCache::destroyDecodedDataIfNecessary): Deleted.
+        * platform/graphics/ImageFrameCache.h:
+        (WebCore::ImageFrameCache::destroyAllDecodedData):
+        (WebCore::ImageFrameCache::destroyAllDecodedDataExcludeFrame):
+        (WebCore::ImageFrameCache::destroyDecodedDataBeforeFrame):
+        Make ImageFrameCache be responsible for destroying a range of ImageFrames.
+        This range might include all the frames, all the frames but up to a specific
+        frame, or all the frames but exclude one frame in the middle.
+        
+        * platform/graphics/ImageSource.cpp:
+        (WebCore::ImageSource::clear): No need to call clearFrameBufferCache() from clear().
+        The decision to call clearFrameBufferCache() or clear() is moved to
+        BitmapImage::destroyDecodedData().
+         
+        (WebCore::ImageSource::destroyDecodedData): Deleted.
+        (WebCore::ImageSource::destroyDecodedDataIfNecessary): Deleted.
+        These functions are replaced by another set of functions in ImageSource.h.
+        
+        * platform/graphics/ImageSource.h:
+        (WebCore::ImageSource::destroyAllDecodedData):
+        (WebCore::ImageSource::destroyAllDecodedDataExcludeFrame):
+        (WebCore::ImageSource::destroyDecodedDataBeforeFrame):
+        (WebCore::ImageSource::hasDecodingQueue):
+        These are new wrappers which call the corresponding ImageFrameCache functions.
+
</ins><span class="cx"> 2017-02-13  Myles C. Maxfield  &lt;mmaxfield@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [Cocoa] Stop soft-linking CTRunGetBaseAdvancesAndOrigins()
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsBitmapImagecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/BitmapImage.cpp (212264 => 212265)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/BitmapImage.cpp        2017-02-14 01:18:59 UTC (rev 212264)
+++ trunk/Source/WebCore/platform/graphics/BitmapImage.cpp        2017-02-14 01:29:24 UTC (rev 212265)
</span><span class="lines">@@ -65,13 +65,34 @@
</span><span class="cx"> 
</span><span class="cx"> void BitmapImage::destroyDecodedData(bool destroyAll)
</span><span class="cx"> {
</span><del>-    m_source.destroyDecodedData(data(), destroyAll, m_currentFrame);
</del><ins>+    if (!destroyAll)
+        m_source.destroyDecodedDataBeforeFrame(m_currentFrame);
+    else if (m_source.hasDecodingQueue())
+        m_source.destroyAllDecodedDataExcludeFrame(m_currentFrame);
+    else
+        m_source.destroyAllDecodedData();
+
+    // There's no need to throw away the decoder unless we're explicitly asked
+    // to destroy all of the frames.
+    if (!destroyAll || m_source.hasDecodingQueue())
+        m_source.clearFrameBufferCache(m_currentFrame);
+    else
+        m_source.clear(data());
+
</ins><span class="cx">     invalidatePlatformData();
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void BitmapImage::destroyDecodedDataIfNecessary(bool destroyAll)
</span><span class="cx"> {
</span><del>-    m_source.destroyDecodedDataIfNecessary(data(), destroyAll, m_currentFrame);
</del><ins>+    // If we have decoded frames but there is no encoded data, we shouldn't destroy
+    // the decoded image since we won't be able to reconstruct it later.
+    if (!data() &amp;&amp; frameCount())
+        return;
+
+    if (m_source.decodedSize() &lt; LargeAnimationCutoff)
+        return;
+
+    destroyDecodedData(destroyAll);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> bool BitmapImage::dataChanged(bool allDataReceived)
</span><span class="lines">@@ -157,6 +178,7 @@
</span><span class="cx">     m_currentSubsamplingLevel = allowSubsampling() ? m_source.subsamplingLevelForScale(scale) : SubsamplingLevel::Default;
</span><span class="cx">     LOG(Images, &quot;BitmapImage %p draw - subsamplingLevel %d at scale %.4f&quot;, this, static_cast&lt;int&gt;(m_currentSubsamplingLevel), scale);
</span><span class="cx"> 
</span><ins>+    ASSERT_IMPLIES(result == StartAnimationResult::DecodingActive, m_source.frameHasValidNativeImageAtIndex(m_currentFrame, m_currentSubsamplingLevel));
</ins><span class="cx">     auto image = frameImageAtIndex(m_currentFrame, m_currentSubsamplingLevel, &amp;context);
</span><span class="cx">     if (!image) // If it's too early we won't have an image yet.
</span><span class="cx">         return;
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsBitmapImageh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/BitmapImage.h (212264 => 212265)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/BitmapImage.h        2017-02-14 01:18:59 UTC (rev 212264)
+++ trunk/Source/WebCore/platform/graphics/BitmapImage.h        2017-02-14 01:29:24 UTC (rev 212265)
</span><span class="lines">@@ -190,6 +190,13 @@
</span><span class="cx">     bool isBitmapImage() const override { return true; }
</span><span class="cx">     void dump(TextStream&amp;) const override;
</span><span class="cx"> 
</span><ins>+    // Animated images over a certain size are considered large enough that we'll only hang on to one frame at a time.
+#if !PLATFORM(IOS)
+    static const unsigned LargeAnimationCutoff = 5242880;
+#else
+    static const unsigned LargeAnimationCutoff = 2097152;
+#endif
+
</ins><span class="cx">     mutable ImageSource m_source;
</span><span class="cx"> 
</span><span class="cx">     size_t m_currentFrame { 0 }; // The index of the current frame of animation.
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsImageFrameCachecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/ImageFrameCache.cpp (212264 => 212265)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/ImageFrameCache.cpp        2017-02-14 01:18:59 UTC (rev 212264)
+++ trunk/Source/WebCore/platform/graphics/ImageFrameCache.cpp        2017-02-14 01:29:24 UTC (rev 212265)
</span><span class="lines">@@ -70,31 +70,21 @@
</span><span class="cx">     ASSERT(!hasDecodingQueue());
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void ImageFrameCache::destroyDecodedData(bool destroyAll, size_t count)
</del><ins>+void ImageFrameCache::destroyDecodedData(size_t frameCount, size_t excludeFrame)
</ins><span class="cx"> {
</span><del>-    if (destroyAll)
-        count = m_frames.size();
-    
</del><span class="cx">     unsigned decodedSize = 0;
</span><del>-    for (size_t i = 0; i &lt;  count; ++i)
-        decodedSize += m_frames[i].clearImage();
</del><span class="cx"> 
</span><ins>+    ASSERT(frameCount &lt;= m_frames.size());
+
+    for (size_t index = 0; index &lt; frameCount; ++index) {
+        if (index == excludeFrame)
+            continue;
+        decodedSize += m_frames[index++].clearImage();
+    }
+
</ins><span class="cx">     decodedSizeReset(decodedSize);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-bool ImageFrameCache::destroyDecodedDataIfNecessary(bool destroyAll, size_t count)
-{
-    unsigned decodedSize = 0;
-    for (auto&amp; frame : m_frames)
-        decodedSize += frame.frameBytes();
-    
-    if (decodedSize &lt; LargeAnimationCutoff)
-        return false;
-    
-    destroyDecodedData(destroyAll, count);
-    return true;
-}
-
</del><span class="cx"> void ImageFrameCache::destroyIncompleteDecodedData()
</span><span class="cx"> {
</span><span class="cx">     unsigned decodedSize = 0;
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsImageFrameCacheh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/ImageFrameCache.h (212264 => 212265)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/ImageFrameCache.h        2017-02-14 01:18:59 UTC (rev 212264)
+++ trunk/Source/WebCore/platform/graphics/ImageFrameCache.h        2017-02-14 01:29:24 UTC (rev 212265)
</span><span class="lines">@@ -59,8 +59,9 @@
</span><span class="cx">     ImageDecoder* decoder() const { return m_decoder; }
</span><span class="cx"> 
</span><span class="cx">     unsigned decodedSize() const { return m_decodedSize; }
</span><del>-    void destroyDecodedData(bool destroyAll = true, size_t count = 0);
-    bool destroyDecodedDataIfNecessary(bool destroyAll = true, size_t count = 0);
</del><ins>+    void destroyAllDecodedData() { destroyDecodedData(frameCount(), frameCount()); }
+    void destroyAllDecodedDataExcludeFrame(size_t excludeFrame) { destroyDecodedData(frameCount(), excludeFrame); }
+    void destroyDecodedDataBeforeFrame(size_t beforeFrame) { destroyDecodedData(beforeFrame, beforeFrame); }
</ins><span class="cx">     void destroyIncompleteDecodedData();
</span><span class="cx"> 
</span><span class="cx">     void growFrames();
</span><span class="lines">@@ -112,6 +113,7 @@
</span><span class="cx">     T frameMetadataAtIndex(size_t index, SubsamplingLevel = SubsamplingLevel::Undefinded, ImageFrame::Caching = ImageFrame::Caching::Empty, std::optional&lt;T&gt;* = nullptr);
</span><span class="cx"> 
</span><span class="cx">     bool isDecoderAvailable() const { return m_decoder; }
</span><ins>+    void destroyDecodedData(size_t frameCount, size_t excludeFrame);
</ins><span class="cx">     void decodedSizeChanged(long long decodedSize);
</span><span class="cx">     void didDecodeProperties(unsigned decodedPropertiesSize);
</span><span class="cx">     void decodedSizeIncreased(unsigned decodedSize);
</span><span class="lines">@@ -128,13 +130,6 @@
</span><span class="cx"> 
</span><span class="cx">     const ImageFrame&amp; frameAtIndex(size_t, SubsamplingLevel, ImageFrame::Caching);
</span><span class="cx"> 
</span><del>-    // Animated images over a certain size are considered large enough that we'll only hang on to one frame at a time.
-#if !PLATFORM(IOS)
-    static const unsigned LargeAnimationCutoff = 5242880;
-#else
-    static const unsigned LargeAnimationCutoff = 2097152;
-#endif
-
</del><span class="cx">     Image* m_image { nullptr };
</span><span class="cx">     ImageDecoder* m_decoder { nullptr };
</span><span class="cx">     unsigned m_decodedSize { 0 };
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsImageSourcecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/ImageSource.cpp (212264 => 212265)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/ImageSource.cpp        2017-02-14 01:18:59 UTC (rev 212264)
+++ trunk/Source/WebCore/platform/graphics/ImageSource.cpp        2017-02-14 01:29:24 UTC (rev 212265)
</span><span class="lines">@@ -68,40 +68,13 @@
</span><span class="cx">     m_decoder-&gt;clearFrameBufferCache(clearBeforeFrame);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void ImageSource::clear(bool destroyAll, size_t count, SharedBuffer* data)
</del><ins>+void ImageSource::clear(SharedBuffer* data)
</ins><span class="cx"> {
</span><del>-    // There's no need to throw away the decoder unless we're explicitly asked
-    // to destroy all of the frames.
-    if (!destroyAll || m_frameCache-&gt;hasDecodingQueue()) {
-        clearFrameBufferCache(count);
-        return;
-    }
-
</del><span class="cx">     m_decoder = nullptr;
</span><span class="cx">     m_frameCache-&gt;setDecoder(nullptr);
</span><span class="cx">     setData(data, isAllDataReceived());
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void ImageSource::destroyDecodedData(SharedBuffer* data, bool destroyAll, size_t count)
-{
-    m_frameCache-&gt;destroyDecodedData(destroyAll, count);
-    clear(destroyAll, count, data);
-}
-
-bool ImageSource::destroyDecodedDataIfNecessary(SharedBuffer* data, bool destroyAll, size_t count)
-{
-    // If we have decoded frames but there is no encoded data, we shouldn't destroy
-    // the decoded image since we won't be able to reconstruct it later.
-    if (!data &amp;&amp; m_frameCache-&gt;frameCount())
-        return false;
-
-    if (!m_frameCache-&gt;destroyDecodedDataIfNecessary(destroyAll, count))
-        return false;
-
-    clear(destroyAll, count, data);
-    return true;
-}
-
</del><span class="cx"> bool ImageSource::ensureDecoderAvailable(SharedBuffer* data)
</span><span class="cx"> {
</span><span class="cx">     if (!data || isDecoderAvailable())
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsImageSourceh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/ImageSource.h (212264 => 212265)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/ImageSource.h        2017-02-14 01:18:59 UTC (rev 212264)
+++ trunk/Source/WebCore/platform/graphics/ImageSource.h        2017-02-14 01:29:24 UTC (rev 212265)
</span><span class="lines">@@ -53,8 +53,11 @@
</span><span class="cx">     ImageSource(Image*, AlphaOption = AlphaOption::Premultiplied, GammaAndColorProfileOption = GammaAndColorProfileOption::Applied);
</span><span class="cx">     ~ImageSource();
</span><span class="cx"> 
</span><del>-    void destroyDecodedData(SharedBuffer* data, bool destroyAll = true, size_t count = 0);
-    bool destroyDecodedDataIfNecessary(SharedBuffer* data, bool destroyAll = true, size_t count = 0);
</del><ins>+    void destroyAllDecodedData() { m_frameCache-&gt;destroyAllDecodedData(); }
+    void destroyAllDecodedDataExcludeFrame(size_t excludeFrame) { m_frameCache-&gt;destroyAllDecodedDataExcludeFrame(excludeFrame); }
+    void destroyDecodedDataBeforeFrame(size_t beforeFrame) { m_frameCache-&gt;destroyDecodedDataBeforeFrame(beforeFrame); }
+    void clearFrameBufferCache(size_t);
+    void clear(SharedBuffer* data);
</ins><span class="cx"> 
</span><span class="cx">     bool ensureDecoderAvailable(SharedBuffer*);
</span><span class="cx">     bool isDecoderAvailable() const { return m_decoder.get(); }
</span><span class="lines">@@ -67,6 +70,7 @@
</span><span class="cx"> 
</span><span class="cx">     bool isAsyncDecodingRequired();
</span><span class="cx">     bool requestFrameAsyncDecodingAtIndex(size_t index, SubsamplingLevel subsamplingLevel) { return m_frameCache-&gt;requestFrameAsyncDecodingAtIndex(index, subsamplingLevel); }
</span><ins>+    bool hasDecodingQueue() const { return m_frameCache-&gt;hasDecodingQueue(); }
</ins><span class="cx">     void stopAsyncDecodingQueue() { m_frameCache-&gt;stopAsyncDecodingQueue(); }
</span><span class="cx"> 
</span><span class="cx">     // Image metadata which is calculated by the decoder or can deduced by the case of the memory NativeImage.
</span><span class="lines">@@ -101,8 +105,6 @@
</span><span class="cx">     NativeImagePtr createFrameImageAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default);
</span><span class="cx"> 
</span><span class="cx"> private:
</span><del>-    void clearFrameBufferCache(size_t);
-    void clear(bool destroyAll, size_t count, SharedBuffer* data);
</del><span class="cx">     void dump(TextStream&amp;);
</span><span class="cx"> 
</span><span class="cx">     void setDecoderTargetContext(const GraphicsContext*);
</span></span></pre>
</div>
</div>

</body>
</html>