<!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>[208511] 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/208511">208511</a></dd>
<dt>Author</dt> <dd>commit-queue@webkit.org</dd>
<dt>Date</dt> <dd>2016-11-09 17:13:05 -0800 (Wed, 09 Nov 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>Change the decoding for some animated images to be asynchronous
https://bugs.webkit.org/show_bug.cgi?id=161566

Patch by Said Abou-Hallawa &lt;sabouhallawa@apple.com&gt; on 2016-11-09
Reviewed by Simon Fraser.

Source/WebCore:

Tests: fast/images/slower-animation-than-decoding-image.html
       fast/images/slower-decoding-than-animation-image.html
       fast/images/stopped-animation-deleted-image.html

Request the next frame before firing the animation timer. The asynchronous
image decoding work queue notifies the BitmapImage when the frame finishes
decoding. If the timer fires before the frame is decoded, no repaint will
be requested. Only when the image frame is ready, the animation will be
advanced and the image will be repainted.

* loader/cache/CachedImage.cpp:
(WebCore::CachedImage::load): Cache the image settings in CachedImage.
(WebCore::CachedImage::createImage): No need to pass allowSubsampling to BitmapImage. It can be retrieved through Image::imageObserver().
(WebCore::CachedImage::changedInRect): Change the parameter to notifyObservers() to be a pointer.
* loader/cache/CachedImage.h: Cache the settings: allowSubsampling, allowAsyncImageDecoding and showDebugBackground through m_loader.
* platform/graphics/BitmapImage.cpp:
(WebCore::BitmapImage::dataChanged): Fix a logging message.
(WebCore::BitmapImage::draw): Store the current SubsamplingLevel to be used when requesting decoding the image of the next frame.
Draw a debug rectangle if the next frame is missed because it is being decoded and the setting showDebugBackground is on.
(WebCore::BitmapImage::startAnimation): Deleted. Moved to the header file.
(WebCore::BitmapImage::internalStartAnimation): Added. Request asynchronous image decoding for the next frame if required. Return the
result of starting the animation.
(WebCore::BitmapImage::advanceAnimation): Call internalAdvanceAnimation() if the frame image is not being decoded. If it is being decoded
and the setting showDebugBackground is on, force repaint so the debug rectangle is drawn.
(WebCore::BitmapImage::internalAdvanceAnimation): This is the old body of advanceAnimation().
(WebCore::BitmapImage::stopAnimation): Stop the asynchronous image decoding if it is started.
(WebCore::BitmapImage::newFrameNativeImageAvailableAtIndex): This function is called from the async image decoding work queue when finishing decoding a native image frame.
* platform/graphics/BitmapImage.h:
(WebCore::BitmapImage::startAnimation): Added. It is now calls internalStartAnimation().
* platform/graphics/Color.h: Define a constant for the yellow color.
* platform/graphics/ImageFrameCache.cpp:
(WebCore::ImageFrameCache::clearMetadata): Delete unreferenced member.
(WebCore::ImageFrameCache::requestFrameAsyncDecodingAtIndex): Return true if the frame is requested for async decoding.
* platform/graphics/ImageFrameCache.h:
* platform/graphics/ImageObserver.h:  Add virtual functions for allowSubsampling, allowAsyncImageDecoding and showDebugBackground.
* platform/graphics/ImageSource.cpp:
(WebCore::ImageSource::maximumSubsamplingLevel): Move checking allowSubsampling() to the caller BitmapImage::draw().
* platform/graphics/ImageSource.h: Remove the setting allowSubsampling(); it can be retrieved from imageObserver().
(WebCore::ImageSource::setAllowSubsampling): Deleted.
* rendering/RenderImageResource.cpp:
(WebCore::RenderImageResource::shutdown): Stop the animation of an image when shutting down the resource.
* rendering/RenderImageResourceStyleImage.cpp:
(WebCore::RenderImageResourceStyleImage::shutdown): Ditto.
svg/graphics/SVGImageClients.h: Change the parameter to ImageObserver::changedInRect() to be a pointer.
(WebCore::SVGImageChromeClient::invalidateContentsAndRootView):
* testing/Internals.cpp:
(WebCore::Internals::setImageFrameDecodingDuration): Sets a fixed frame decoding duration for testing.
* testing/Internals.h:
* testing/Internals.idl: Adds an internal option for ImageFrameDecodingDuration.

LayoutTests:

* fast/images/slower-animation-than-decoding-image-expected.txt: Added.
* fast/images/slower-animation-than-decoding-image.html: Added.
* fast/images/slower-decoding-than-animation-image-expected.txt: Added.
* fast/images/slower-decoding-than-animation-image.html: Added.
In these tests, CanvasRenderingContext2D.drawImage() is used to better
control advancing the animation of an animated image. A setTimeout() is
used instead of the frame duration to schedule when the drawing happens.
The first test ensures that faster decoding does not overrule the frame
duration; the setTimeout interval in this case. The second test ensures
the animation is not advanced unless decoding the next frame has finished.

* fast/images/stopped-animation-deleted-image-expected.txt: Added.
* fast/images/stopped-animation-deleted-image.html: Added.
This test ensures that if an animated image is removed from the document,
its draw() method won't be called even if the animation timer fires or the
decoding new frame availability notification is received.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</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="#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="#trunkSourceWebCoreplatformgraphicsColorh">trunk/Source/WebCore/platform/graphics/Color.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="#trunkSourceWebCoreplatformgraphicsImageObserverh">trunk/Source/WebCore/platform/graphics/ImageObserver.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>
<li><a href="#trunkSourceWebCorerenderingRenderImageResourcecpp">trunk/Source/WebCore/rendering/RenderImageResource.cpp</a></li>
<li><a href="#trunkSourceWebCorerenderingRenderImageResourceStyleImagecpp">trunk/Source/WebCore/rendering/RenderImageResourceStyleImage.cpp</a></li>
<li><a href="#trunkSourceWebCoresvggraphicsSVGImageClientsh">trunk/Source/WebCore/svg/graphics/SVGImageClients.h</a></li>
<li><a href="#trunkSourceWebCoretestingInternalscpp">trunk/Source/WebCore/testing/Internals.cpp</a></li>
<li><a href="#trunkSourceWebCoretestingInternalsh">trunk/Source/WebCore/testing/Internals.h</a></li>
<li><a href="#trunkSourceWebCoretestingInternalsidl">trunk/Source/WebCore/testing/Internals.idl</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsfastimagessloweranimationthandecodingimageexpectedtxt">trunk/LayoutTests/fast/images/slower-animation-than-decoding-image-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfastimagessloweranimationthandecodingimagehtml">trunk/LayoutTests/fast/images/slower-animation-than-decoding-image.html</a></li>
<li><a href="#trunkLayoutTestsfastimagesslowerdecodingthananimationimageexpectedtxt">trunk/LayoutTests/fast/images/slower-decoding-than-animation-image-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfastimagesslowerdecodingthananimationimagehtml">trunk/LayoutTests/fast/images/slower-decoding-than-animation-image.html</a></li>
<li><a href="#trunkLayoutTestsfastimagesstoppedanimationdeletedimageexpectedtxt">trunk/LayoutTests/fast/images/stopped-animation-deleted-image-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfastimagesstoppedanimationdeletedimagehtml">trunk/LayoutTests/fast/images/stopped-animation-deleted-image.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (208510 => 208511)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2016-11-10 01:11:36 UTC (rev 208510)
+++ trunk/LayoutTests/ChangeLog        2016-11-10 01:13:05 UTC (rev 208511)
</span><span class="lines">@@ -1,3 +1,27 @@
</span><ins>+2016-11-09  Said Abou-Hallawa  &lt;sabouhallawa@apple.com&gt;
+
+        Change the decoding for some animated images to be asynchronous
+        https://bugs.webkit.org/show_bug.cgi?id=161566
+
+        Reviewed by Simon Fraser.
+
+        * fast/images/slower-animation-than-decoding-image-expected.txt: Added.
+        * fast/images/slower-animation-than-decoding-image.html: Added.
+        * fast/images/slower-decoding-than-animation-image-expected.txt: Added.
+        * fast/images/slower-decoding-than-animation-image.html: Added.
+        In these tests, CanvasRenderingContext2D.drawImage() is used to better
+        control advancing the animation of an animated image. A setTimeout() is
+        used instead of the frame duration to schedule when the drawing happens.
+        The first test ensures that faster decoding does not overrule the frame
+        duration; the setTimeout interval in this case. The second test ensures
+        the animation is not advanced unless decoding the next frame has finished.
+
+        * fast/images/stopped-animation-deleted-image-expected.txt: Added.
+        * fast/images/stopped-animation-deleted-image.html: Added.
+        This test ensures that if an animated image is removed from the document,
+        its draw() method won't be called even if the animation timer fires or the
+        decoding new frame availability notification is received.
+
</ins><span class="cx"> 2016-11-04  Brent Fulgham  &lt;bfulgham@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Local HTML should be blocked from localStorage access unless &quot;Disable Local File Restrictions&quot; is checked
</span></span></pre></div>
<a id="trunkLayoutTestsfastimagessloweranimationthandecodingimageexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/images/slower-animation-than-decoding-image-expected.txt (0 => 208511)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/images/slower-animation-than-decoding-image-expected.txt                                (rev 0)
+++ trunk/LayoutTests/fast/images/slower-animation-than-decoding-image-expected.txt        2016-11-10 01:13:05 UTC (rev 208511)
</span><span class="lines">@@ -0,0 +1,10 @@
</span><ins>+Ensure the image frame duration is respected even if the frame finishes decoding early.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+PASS internals.imageFrameIndex(image) is 2
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsfastimagessloweranimationthandecodingimagehtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/images/slower-animation-than-decoding-image.html (0 => 208511)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/images/slower-animation-than-decoding-image.html                                (rev 0)
+++ trunk/LayoutTests/fast/images/slower-animation-than-decoding-image.html        2016-11-10 01:13:05 UTC (rev 208511)
</span><span class="lines">@@ -0,0 +1,53 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+&lt;head&gt;
+    &lt;script src=&quot;../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;/head&gt;
+&lt;body&gt;
+    &lt;canvas id=&quot;canvas&quot;&gt;&lt;/canvas&gt;
+    &lt;script&gt;
+        description(&quot;Ensure the image frame duration is respected even if the frame finishes decoding early.&quot;);
+        jsTestIsAsync = true;
+
+        internals.clearMemoryCache();
+
+        var image = new Image;
+        image.onload = imageLoaded;
+        image.src = &quot;resources/animated-red-green-blue.gif&quot;;
+
+        function imageLoaded()
+        {
+            if (!window.internals)
+                return;
+            internals.setImageFrameDecodingDuration(image, 0.040);
+            drawImage();
+            drawLoop();
+        }
+
+        function drawImage()
+        {
+            if (drawImage.count == undefined)
+                drawImage.count = 0;
+            var canvas = document.getElementById(&quot;canvas&quot;);
+            var ctx = canvas.getContext(&quot;2d&quot;);
+            ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
+            return ++drawImage.count;
+        }
+                
+        function drawLoop()
+        {
+            // 1st time the image is drawn, time = 0, current_frame = 0
+            // 2nd time the image is drawn, time = 50, current_frame = 1
+            // 3rd time the image is drawn, time = 100, current_frame = 2
+            setTimeout(function() {
+                if (drawImage() == 3) {
+                    shouldBe(&quot;internals.imageFrameIndex(image)&quot;, &quot;2&quot;);
+                    finishJSTest();
+                } else
+                    drawLoop();
+            }, 50);
+        }
+    &lt;/script&gt;
+    &lt;script src=&quot;../../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsfastimagesslowerdecodingthananimationimageexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/images/slower-decoding-than-animation-image-expected.txt (0 => 208511)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/images/slower-decoding-than-animation-image-expected.txt                                (rev 0)
+++ trunk/LayoutTests/fast/images/slower-decoding-than-animation-image-expected.txt        2016-11-10 01:13:05 UTC (rev 208511)
</span><span class="lines">@@ -0,0 +1,10 @@
</span><ins>+Ensure the image frame is drawn when it finishes decoding even if it takes more than the previous frame duration.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+PASS internals.imageFrameIndex(image) is 1
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsfastimagesslowerdecodingthananimationimagehtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/images/slower-decoding-than-animation-image.html (0 => 208511)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/images/slower-decoding-than-animation-image.html                                (rev 0)
+++ trunk/LayoutTests/fast/images/slower-decoding-than-animation-image.html        2016-11-10 01:13:05 UTC (rev 208511)
</span><span class="lines">@@ -0,0 +1,53 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+&lt;head&gt;
+    &lt;script src=&quot;../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;/head&gt;
+&lt;body&gt;
+    &lt;canvas id=&quot;canvas&quot;&gt;&lt;/canvas&gt;
+    &lt;script&gt;
+        description(&quot;Ensure the image frame is drawn when it finishes decoding even if it takes more than the previous frame duration.&quot;);
+        jsTestIsAsync = true;
+
+        internals.clearMemoryCache();
+
+        var image = new Image;
+        image.onload = imageLoaded;
+        image.src = &quot;resources/animated-red-green-blue.gif&quot;;
+
+        function imageLoaded()
+        {
+            if (!window.internals)
+                return;
+            internals.setImageFrameDecodingDuration(image, 0.050);
+            drawImage();
+            drawLoop();
+        }
+
+        function drawImage()
+        {
+            if (drawImage.count == undefined)
+                drawImage.count = 0;
+            var canvas = document.getElementById(&quot;canvas&quot;);
+            var ctx = canvas.getContext(&quot;2d&quot;);
+            ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
+            return ++drawImage.count;
+        }
+                
+        function drawLoop()
+        {
+            // 1st time the image is drawn, time = 0, current_frame = 0
+            // 2nd time the image is drawn, time = 40, current_frame = 0
+            // 3rd time the image is drawn, time = 80, current_frame = 1
+            setTimeout(function() {
+                if (drawImage() == 3) {
+                    shouldBe(&quot;internals.imageFrameIndex(image)&quot;, &quot;1&quot;);
+                    finishJSTest();
+                } else
+                    drawLoop();
+            }, 40);
+        }
+    &lt;/script&gt;
+    &lt;script src=&quot;../../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsfastimagesstoppedanimationdeletedimageexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/images/stopped-animation-deleted-image-expected.txt (0 => 208511)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/images/stopped-animation-deleted-image-expected.txt                                (rev 0)
+++ trunk/LayoutTests/fast/images/stopped-animation-deleted-image-expected.txt        2016-11-10 01:13:05 UTC (rev 208511)
</span><span class="lines">@@ -0,0 +1,10 @@
</span><ins>+Ensure the image stops animating if it is removed from the document before finishing decoding the current frame.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+PASS internals.imageFrameIndex(image) is frameIndex
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsfastimagesstoppedanimationdeletedimagehtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/images/stopped-animation-deleted-image.html (0 => 208511)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/images/stopped-animation-deleted-image.html                                (rev 0)
+++ trunk/LayoutTests/fast/images/stopped-animation-deleted-image.html        2016-11-10 01:13:05 UTC (rev 208511)
</span><span class="lines">@@ -0,0 +1,40 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+&lt;head&gt;
+    &lt;script src=&quot;../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;/head&gt;
+&lt;body&gt;
+    &lt;img src=&quot;resources/animated-red-green-blue.gif&quot; onload=&quot;imageLoaded()&quot;&gt;
+    &lt;script&gt;
+        description(&quot;Ensure the image stops animating if it is removed from the document before finishing decoding the current frame.&quot;);
+        jsTestIsAsync = true;
+
+        internals.clearMemoryCache();
+
+        var image = document.getElementsByTagName(&quot;img&quot;)[0];
+        var loopCount = 0;
+        var frameIndex;
+
+        function imageLoaded()
+        {
+            if (!window.internals)
+                return;
+            internals.setImageFrameDecodingDuration(image, 0.040);
+            imageRemove();
+        }
+
+        function imageRemove()
+        {
+            setTimeout(function() {
+                frameIndex = internals.imageFrameIndex(image);
+                image.remove();
+                setTimeout(function() {
+                    shouldBe(&quot;internals.imageFrameIndex(image)&quot;, &quot;frameIndex&quot;);
+                    finishJSTest();
+                }, 50);
+             }, 50);
+        }
+    &lt;/script&gt;
+    &lt;script src=&quot;../../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (208510 => 208511)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-11-10 01:11:36 UTC (rev 208510)
+++ trunk/Source/WebCore/ChangeLog        2016-11-10 01:13:05 UTC (rev 208511)
</span><span class="lines">@@ -1,3 +1,60 @@
</span><ins>+2016-11-09  Said Abou-Hallawa  &lt;sabouhallawa@apple.com&gt;
+
+        Change the decoding for some animated images to be asynchronous
+        https://bugs.webkit.org/show_bug.cgi?id=161566
+
+        Reviewed by Simon Fraser.
+
+        Tests: fast/images/slower-animation-than-decoding-image.html
+               fast/images/slower-decoding-than-animation-image.html
+               fast/images/stopped-animation-deleted-image.html
+               
+        Request the next frame before firing the animation timer. The asynchronous
+        image decoding work queue notifies the BitmapImage when the frame finishes
+        decoding. If the timer fires before the frame is decoded, no repaint will
+        be requested. Only when the image frame is ready, the animation will be
+        advanced and the image will be repainted.
+
+        * loader/cache/CachedImage.cpp:
+        (WebCore::CachedImage::load): Cache the image settings in CachedImage.
+        (WebCore::CachedImage::createImage): No need to pass allowSubsampling to BitmapImage. It can be retrieved through Image::imageObserver().
+        (WebCore::CachedImage::changedInRect): Change the parameter to notifyObservers() to be a pointer.
+        * loader/cache/CachedImage.h: Cache the settings: allowSubsampling, allowAsyncImageDecoding and showDebugBackground through m_loader.
+        * platform/graphics/BitmapImage.cpp:
+        (WebCore::BitmapImage::dataChanged): Fix a logging message.
+        (WebCore::BitmapImage::draw): Store the current SubsamplingLevel to be used when requesting decoding the image of the next frame.
+        Draw a debug rectangle if the next frame is missed because it is being decoded and the setting showDebugBackground is on.
+        (WebCore::BitmapImage::startAnimation): Deleted. Moved to the header file.
+        (WebCore::BitmapImage::internalStartAnimation): Added. Request asynchronous image decoding for the next frame if required. Return the
+        result of starting the animation.
+        (WebCore::BitmapImage::advanceAnimation): Call internalAdvanceAnimation() if the frame image is not being decoded. If it is being decoded
+        and the setting showDebugBackground is on, force repaint so the debug rectangle is drawn.
+        (WebCore::BitmapImage::internalAdvanceAnimation): This is the old body of advanceAnimation().
+        (WebCore::BitmapImage::stopAnimation): Stop the asynchronous image decoding if it is started.
+        (WebCore::BitmapImage::newFrameNativeImageAvailableAtIndex): This function is called from the async image decoding work queue when finishing decoding a native image frame.
+        * platform/graphics/BitmapImage.h:
+        (WebCore::BitmapImage::startAnimation): Added. It is now calls internalStartAnimation().
+        * platform/graphics/Color.h: Define a constant for the yellow color.
+        * platform/graphics/ImageFrameCache.cpp:
+        (WebCore::ImageFrameCache::clearMetadata): Delete unreferenced member.
+        (WebCore::ImageFrameCache::requestFrameAsyncDecodingAtIndex): Return true if the frame is requested for async decoding.
+        * platform/graphics/ImageFrameCache.h:
+        * platform/graphics/ImageObserver.h:  Add virtual functions for allowSubsampling, allowAsyncImageDecoding and showDebugBackground.
+        * platform/graphics/ImageSource.cpp:
+        (WebCore::ImageSource::maximumSubsamplingLevel): Move checking allowSubsampling() to the caller BitmapImage::draw().
+        * platform/graphics/ImageSource.h: Remove the setting allowSubsampling(); it can be retrieved from imageObserver().
+        (WebCore::ImageSource::setAllowSubsampling): Deleted.
+        * rendering/RenderImageResource.cpp:
+        (WebCore::RenderImageResource::shutdown): Stop the animation of an image when shutting down the resource.
+        * rendering/RenderImageResourceStyleImage.cpp:
+        (WebCore::RenderImageResourceStyleImage::shutdown): Ditto.
+        svg/graphics/SVGImageClients.h: Change the parameter to ImageObserver::changedInRect() to be a pointer.
+        (WebCore::SVGImageChromeClient::invalidateContentsAndRootView):
+        * testing/Internals.cpp:
+        (WebCore::Internals::setImageFrameDecodingDuration): Sets a fixed frame decoding duration for testing.
+        * testing/Internals.h:
+        * testing/Internals.idl: Adds an internal option for ImageFrameDecodingDuration.
+
</ins><span class="cx"> 2016-11-04  Brent Fulgham  &lt;bfulgham@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Local HTML should be blocked from localStorage access unless &quot;Disable Local File Restrictions&quot; is checked
</span></span></pre></div>
<a id="trunkSourceWebCoreloadercacheCachedImagecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/loader/cache/CachedImage.cpp (208510 => 208511)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/loader/cache/CachedImage.cpp        2016-11-10 01:11:36 UTC (rev 208510)
+++ trunk/Source/WebCore/loader/cache/CachedImage.cpp        2016-11-10 01:13:05 UTC (rev 208511)
</span><span class="lines">@@ -89,6 +89,12 @@
</span><span class="cx">         CachedResource::load(loader);
</span><span class="cx">     else
</span><span class="cx">         setLoading(false);
</span><ins>+
+    if (m_loader) {
+        m_allowSubsampling = m_loader-&gt;frameLoader()-&gt;frame().settings().imageSubsamplingEnabled();
+        m_allowAsyncImageDecoding = m_loader-&gt;frameLoader()-&gt;frame().settings().asyncImageDecodingEnabled();
+        m_showDebugBackground = m_loader-&gt;frameLoader()-&gt;frame().settings().showDebugBorders();
+    }
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void CachedImage::setBodyDataFrom(const CachedResource&amp; resource)
</span><span class="lines">@@ -325,10 +331,8 @@
</span><span class="cx">         auto svgImage = SVGImage::create(*this, url());
</span><span class="cx">         m_svgImageCache = std::make_unique&lt;SVGImageCache&gt;(svgImage.ptr());
</span><span class="cx">         m_image = WTFMove(svgImage);
</span><del>-    } else {
</del><ins>+    } else
</ins><span class="cx">         m_image = BitmapImage::create(this);
</span><del>-        downcast&lt;BitmapImage&gt;(*m_image).setAllowSubsampling(m_loader &amp;&amp; m_loader-&gt;frameLoader()-&gt;frame().settings().imageSubsamplingEnabled());
-    }
</del><span class="cx"> 
</span><span class="cx">     if (m_image) {
</span><span class="cx">         // Send queued container size requests.
</span><span class="lines">@@ -481,11 +485,11 @@
</span><span class="cx">         client-&gt;newImageAnimationFrameAvailable(*this);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void CachedImage::changedInRect(const Image* image, const IntRect&amp; rect)
</del><ins>+void CachedImage::changedInRect(const Image* image, const IntRect* rect)
</ins><span class="cx"> {
</span><span class="cx">     if (!image || image != m_image)
</span><span class="cx">         return;
</span><del>-    notifyObservers(&amp;rect);
</del><ins>+    notifyObservers(rect);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> bool CachedImage::currentFrameKnownToBeOpaque(const RenderElement* renderer)
</span></span></pre></div>
<a id="trunkSourceWebCoreloadercacheCachedImageh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/loader/cache/CachedImage.h (208510 => 208511)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/loader/cache/CachedImage.h        2016-11-10 01:11:36 UTC (rev 208510)
+++ trunk/Source/WebCore/loader/cache/CachedImage.h        2016-11-10 01:13:05 UTC (rev 208511)
</span><span class="lines">@@ -118,11 +118,14 @@
</span><span class="cx">     bool stillNeedsLoad() const override { return !errorOccurred() &amp;&amp; status() == Unknown &amp;&amp; !isLoading(); }
</span><span class="cx"> 
</span><span class="cx">     // ImageObserver
</span><ins>+    bool allowSubsampling() const override { return m_allowSubsampling; }
+    bool allowAsyncImageDecoding() const override { return m_allowAsyncImageDecoding; }
+    bool showDebugBackground() const override { return m_showDebugBackground; }
</ins><span class="cx">     void decodedSizeChanged(const Image*, long long delta) override;
</span><span class="cx">     void didDraw(const Image*) override;
</span><span class="cx"> 
</span><span class="cx">     void animationAdvanced(const Image*) override;
</span><del>-    void changedInRect(const Image*, const IntRect&amp;) override;
</del><ins>+    void changedInRect(const Image*, const IntRect* changeRect = nullptr) override;
</ins><span class="cx"> 
</span><span class="cx">     void addIncrementalDataBuffer(SharedBuffer&amp;);
</span><span class="cx"> 
</span><span class="lines">@@ -136,6 +139,15 @@
</span><span class="cx">     std::unique_ptr&lt;SVGImageCache&gt; m_svgImageCache;
</span><span class="cx">     bool m_isManuallyCached { false };
</span><span class="cx">     bool m_shouldPaintBrokenImage { true };
</span><ins>+
+    // The default value of m_allowSubsampling should be the same as defaultImageSubsamplingEnabled in Settings.cpp
+#if PLATFORM(IOS)
+    bool m_allowSubsampling { true };
+#else
+    bool m_allowSubsampling { false };
+#endif
+    bool m_allowAsyncImageDecoding { true };
+    bool m_showDebugBackground { false };
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsBitmapImagecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/BitmapImage.cpp (208510 => 208511)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/BitmapImage.cpp        2016-11-10 01:11:36 UTC (rev 208510)
+++ trunk/Source/WebCore/platform/graphics/BitmapImage.cpp        2016-11-10 01:13:05 UTC (rev 208511)
</span><span class="lines">@@ -83,7 +83,7 @@
</span><span class="cx"> NativeImagePtr BitmapImage::frameImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel, const GraphicsContext* targetContext)
</span><span class="cx"> {
</span><span class="cx">     if (!frameHasValidNativeImageAtIndex(index, subsamplingLevel)) {
</span><del>-        LOG(Images, &quot;BitmapImage %p frameImageAtIndex - subsamplingLevel was %d, resampling&quot;, this, static_cast&lt;int&gt;(frameSubsamplingLevelAtIndex(index)));
</del><ins>+        LOG(Images, &quot;BitmapImage %p %s - subsamplingLevel was %d, resampling&quot;, this, __FUNCTION__, static_cast&lt;int&gt;(frameSubsamplingLevelAtIndex(index)));
</ins><span class="cx">         invalidatePlatformData();
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -141,9 +141,14 @@
</span><span class="cx">     if (destRect.isEmpty() || srcRect.isEmpty())
</span><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    startAnimation();
</del><ins>+    StartAnimationResult result = internalStartAnimation();
</ins><span class="cx"> 
</span><del>-    Color color = singlePixelSolidColor();
</del><ins>+    Color color;
+    if (result == StartAnimationResult::DecodingActive &amp;&amp; showDebugBackground())
+        color = Color::yellow;
+    else
+        color = singlePixelSolidColor();
+
</ins><span class="cx">     if (color.isValid()) {
</span><span class="cx">         fillWithSolidColor(context, destRect, color, op);
</span><span class="cx">         return;
</span><span class="lines">@@ -150,10 +155,10 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     float scale = subsamplingScale(context, destRect, srcRect);
</span><del>-    SubsamplingLevel subsamplingLevel = m_source.subsamplingLevelForScale(scale);
-    LOG(Images, &quot;BitmapImage %p draw - subsamplingLevel %d at scale %.4f&quot;, this, static_cast&lt;int&gt;(subsamplingLevel), scale);
</del><ins>+    m_currentSubsamplingLevel = allowSubsampling() ? m_source.subsamplingLevelForScale(scale) : SubsamplingLevel::Default;
+    LOG(Images, &quot;BitmapImage %p draw - subsamplingLevel %d at scale %.4f&quot;, this, static_cast&lt;int&gt;(m_currentSubsamplingLevel), scale);
</ins><span class="cx"> 
</span><del>-    auto image = frameImageAtIndex(m_currentFrame, subsamplingLevel, &amp;context);
</del><ins>+    auto image = frameImageAtIndex(m_currentFrame, m_currentSubsamplingLevel, &amp;context);
</ins><span class="cx">     if (!image) // If it's too early we won't have an image yet.
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="lines">@@ -223,11 +228,19 @@
</span><span class="cx">     m_frameTimer-&gt;startOneShot(delay);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void BitmapImage::startAnimation()
</del><ins>+BitmapImage::StartAnimationResult BitmapImage::internalStartAnimation()
</ins><span class="cx"> {
</span><del>-    if (m_frameTimer || !shouldAnimate() || frameCount() &lt;= 1)
-        return;
</del><ins>+    if (!canAnimate())
+        return StartAnimationResult::CannotStart;
</ins><span class="cx"> 
</span><ins>+    if (m_frameTimer)
+        return StartAnimationResult::TimerActive;
+    
+    // Don't start a new animation until we draw the frame that is currently being decoded.
+    size_t nextFrame = (m_currentFrame + 1) % frameCount();
+    if (frameIsBeingDecodedAtIndex(nextFrame))
+        return StartAnimationResult::DecodingActive;
+
</ins><span class="cx">     if (m_currentFrame &gt;= frameCount() - 1) {
</span><span class="cx">         // Don't advance past the last frame if we haven't decoded the whole image
</span><span class="cx">         // yet and our repetition count is potentially unset. The repetition count
</span><span class="lines">@@ -234,7 +247,7 @@
</span><span class="cx">         // in a GIF can potentially come after all the rest of the image data, so
</span><span class="cx">         // wait on it.
</span><span class="cx">         if (!m_source.isAllDataReceived() &amp;&amp; repetitionCount() == RepetitionCountOnce)
</span><del>-            return;
</del><ins>+            return StartAnimationResult::IncompleteData;
</ins><span class="cx"> 
</span><span class="cx">         ++m_repetitionsComplete;
</span><span class="cx"> 
</span><span class="lines">@@ -242,7 +255,7 @@
</span><span class="cx">         if (repetitionCount() != RepetitionCountInfinite &amp;&amp; m_repetitionsComplete &gt; repetitionCount()) {
</span><span class="cx">             m_animationFinished = true;
</span><span class="cx">             destroyDecodedDataIfNecessary(false);
</span><del>-            return;
</del><ins>+            return StartAnimationResult::CannotStart;
</ins><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         destroyDecodedDataIfNecessary(true);
</span><span class="lines">@@ -249,9 +262,8 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     // Don't advance the animation to an incomplete frame.
</span><del>-    size_t nextFrame = (m_currentFrame + 1) % frameCount();
</del><span class="cx">     if (!m_source.isAllDataReceived() &amp;&amp; !frameIsCompleteAtIndex(nextFrame))
</span><del>-        return;
</del><ins>+        return StartAnimationResult::IncompleteData;
</ins><span class="cx"> 
</span><span class="cx">     double time = monotonicallyIncreasingTime();
</span><span class="cx"> 
</span><span class="lines">@@ -262,8 +274,19 @@
</span><span class="cx">     // Setting 'm_desiredFrameStartTime' to 'time' means we are late; otherwise we are early.
</span><span class="cx">     m_desiredFrameStartTime = std::max(time, m_desiredFrameStartTime + frameDurationAtIndex(m_currentFrame));
</span><span class="cx"> 
</span><ins>+    // Request async decoding for nextFrame only if this is required. If nextFrame is not in the frameCache,
+    // it will be decoded on a separate work queue. When decoding nextFrame finishes, we will be notified
+    // through the callback newFrameNativeImageAvailableAtIndex(). Otherwise, advanceAnimation() will be called
+    // when the timer fires and m_currentFrame will be advanced to nextFrame since it is not being decoded.
+    if ((allowAsyncImageDecoding() &amp;&amp; m_source.isAsyncDecodingRequired()) || isAsyncDecodingForcedForTesting()) {
+        if (!m_source.requestFrameAsyncDecodingAtIndex(nextFrame, m_currentSubsamplingLevel))
+            LOG(Images, &quot;BitmapImage %p %s - cachedFrameCount %ld nextFrame %ld&quot;, this, __FUNCTION__, ++m_cachedFrameCount, nextFrame);
+        m_desiredFrameDecodeTimeForTesting = time + std::max(m_frameDecodingDurationForTesting, 0.0f);
+    }
+
</ins><span class="cx">     ASSERT(!m_frameTimer);
</span><span class="cx">     startTimer(m_desiredFrameStartTime - time);
</span><ins>+    return StartAnimationResult::Started;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void BitmapImage::advanceAnimation()
</span><span class="lines">@@ -270,7 +293,34 @@
</span><span class="cx"> {
</span><span class="cx">     clearTimer();
</span><span class="cx"> 
</span><ins>+    // Pretend as if decoding nextFrame has taken m_frameDecodingDurationForTesting from
+    // the time this decoding was requested.
+    if (isAsyncDecodingForcedForTesting()) {
+        double time = monotonicallyIncreasingTime();
+        // Start a timer with the remaining time from now till the m_desiredFrameDecodeTime.
+        if (m_desiredFrameDecodeTimeForTesting &gt; std::max(time, m_desiredFrameStartTime)) {
+            startTimer(m_desiredFrameDecodeTimeForTesting - time);
+            return;
+        }
+    }
+    
+    // Don't advance to nextFrame unless its decoding has finished or was not required.
+    size_t nextFrame = (m_currentFrame + 1) % frameCount();
+    if (!frameIsBeingDecodedAtIndex(nextFrame))
+        internalAdvanceAnimation();
+    else {
+        // Force repaint if showDebugBackground() is on.
+        if (showDebugBackground())
+            imageObserver()-&gt;changedInRect(this);
+        LOG(Images, &quot;BitmapImage %p %s - lateFrameCount %ld nextFrame %ld&quot;, this, __FUNCTION__, ++m_lateFrameCount, nextFrame);
+    }
+}
+
+void BitmapImage::internalAdvanceAnimation()
+{
</ins><span class="cx">     m_currentFrame = (m_currentFrame + 1) % frameCount();
</span><ins>+    ASSERT(!frameIsBeingDecodedAtIndex(m_currentFrame));
+
</ins><span class="cx">     destroyDecodedDataIfNecessary(false);
</span><span class="cx"> 
</span><span class="cx">     if (imageObserver())
</span><span class="lines">@@ -282,6 +332,7 @@
</span><span class="cx">     // This timer is used to animate all occurrences of this image. Don't invalidate
</span><span class="cx">     // the timer unless all renderers have stopped drawing.
</span><span class="cx">     clearTimer();
</span><ins>+    m_source.stopAsyncDecodingQueue();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void BitmapImage::resetAnimation()
</span><span class="lines">@@ -296,6 +347,18 @@
</span><span class="cx">     destroyDecodedDataIfNecessary(true);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void BitmapImage::newFrameNativeImageAvailableAtIndex(size_t index)
+{
+    UNUSED_PARAM(index);
+    ASSERT(index == (m_currentFrame + 1) % frameCount());
+
+    // Don't advance to nextFrame unless the timer was fired before its decoding finishes.
+    if (canAnimate() &amp;&amp; !m_frameTimer)
+        internalAdvanceAnimation();
+    else
+        LOG(Images, &quot;BitmapImage %p %s - earlyFrameCount %ld nextFrame %ld&quot;, this, __FUNCTION__, ++m_earlyFrameCount, index);
+}
+
</ins><span class="cx"> void BitmapImage::dump(TextStream&amp; ts) const
</span><span class="cx"> {
</span><span class="cx">     Image::dump(ts);
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsBitmapImageh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/BitmapImage.h (208510 => 208511)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/BitmapImage.h        2016-11-10 01:11:36 UTC (rev 208510)
+++ trunk/Source/WebCore/platform/graphics/BitmapImage.h        2016-11-10 01:13:05 UTC (rev 208511)
</span><span class="lines">@@ -29,6 +29,7 @@
</span><span class="cx"> 
</span><span class="cx"> #include &quot;Image.h&quot;
</span><span class="cx"> #include &quot;Color.h&quot;
</span><ins>+#include &quot;ImageObserver.h&quot;
</ins><span class="cx"> #include &quot;ImageOrientation.h&quot;
</span><span class="cx"> #include &quot;ImageSource.h&quot;
</span><span class="cx"> #include &quot;IntSize.h&quot;
</span><span class="lines">@@ -80,8 +81,6 @@
</span><span class="cx">     IntSize sizeRespectingOrientation() const { return m_source.sizeRespectingOrientation(); }
</span><span class="cx">     Color singlePixelSolidColor() const override { return m_source.singlePixelSolidColor(); }
</span><span class="cx"> 
</span><del>-    void setAllowSubsampling(bool allowSubsampling) { m_source.setAllowSubsampling(allowSubsampling); }
-
</del><span class="cx">     bool frameIsBeingDecodedAtIndex(size_t index) const { return m_source.frameIsBeingDecodedAtIndex(index); }
</span><span class="cx">     bool frameIsCompleteAtIndex(size_t index) const { return m_source.frameIsCompleteAtIndex(index); }
</span><span class="cx">     bool frameHasAlphaAtIndex(size_t index) const { return m_source.frameHasAlphaAtIndex(index); }
</span><span class="lines">@@ -95,6 +94,9 @@
</span><span class="cx">     bool currentFrameKnownToBeOpaque() const override { return !frameHasAlphaAtIndex(currentFrame()); }
</span><span class="cx">     ImageOrientation orientationForCurrentFrame() const override { return frameOrientationAtIndex(currentFrame()); }
</span><span class="cx"> 
</span><ins>+    bool isAsyncDecodingForcedForTesting() const { return m_frameDecodingDurationForTesting &gt; 0; }
+    void setFrameDecodingDurationForTesting(float duration) { m_frameDecodingDurationForTesting = duration; }
+
</ins><span class="cx">     // Accessors for native image formats.
</span><span class="cx"> #if USE(APPKIT)
</span><span class="cx">     NSImage *nsImage() override;
</span><span class="lines">@@ -131,6 +133,10 @@
</span><span class="cx"> 
</span><span class="cx">     NativeImagePtr frameImageAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default, const GraphicsContext* = nullptr);
</span><span class="cx"> 
</span><ins>+    bool allowSubsampling() const { return imageObserver() &amp;&amp; imageObserver()-&gt;allowSubsampling(); }
+    bool allowAsyncImageDecoding() const { return imageObserver() &amp;&amp; imageObserver()-&gt;allowAsyncImageDecoding(); }
+    bool showDebugBackground() const { return imageObserver() &amp;&amp; imageObserver()-&gt;showDebugBackground(); }
+
</ins><span class="cx">     // Called to invalidate cached data. When |destroyAll| is true, we wipe out
</span><span class="cx">     // the entire frame buffer cache and tell the image source to destroy
</span><span class="cx">     // everything; this is used when e.g. we want to free some room in the image
</span><span class="lines">@@ -150,11 +156,14 @@
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx">     // Animation.
</span><ins>+    enum class StartAnimationResult { CannotStart, IncompleteData, TimerActive, DecodingActive, Started };
</ins><span class="cx">     bool isAnimated() const override { return m_source.frameCount() &gt; 1; }
</span><span class="cx">     bool shouldAnimate();
</span><span class="cx">     bool canAnimate();
</span><del>-    void startAnimation() override;
</del><ins>+    void startAnimation() override { internalStartAnimation(); }
+    StartAnimationResult internalStartAnimation();
</ins><span class="cx">     void advanceAnimation();
</span><ins>+    void internalAdvanceAnimation();
</ins><span class="cx"> 
</span><span class="cx">     // It may look unusual that there is no start animation call as public API. This is because
</span><span class="cx">     // we start and stop animating lazily. Animation begins whenever someone draws the image. It will
</span><span class="lines">@@ -161,6 +170,7 @@
</span><span class="cx">     // automatically pause once all observers no longer want to render the image anywhere.
</span><span class="cx">     void stopAnimation() override;
</span><span class="cx">     void resetAnimation() override;
</span><ins>+    void newFrameNativeImageAvailableAtIndex(size_t) override;
</ins><span class="cx"> 
</span><span class="cx">     // Handle platform-specific data
</span><span class="cx">     void invalidatePlatformData();
</span><span class="lines">@@ -182,11 +192,20 @@
</span><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><ins>+    SubsamplingLevel m_currentSubsamplingLevel { SubsamplingLevel::Default };
</ins><span class="cx">     std::unique_ptr&lt;Timer&gt; m_frameTimer;
</span><span class="cx">     RepetitionCount m_repetitionsComplete { RepetitionCountNone }; // How many repetitions we've finished.
</span><span class="cx">     double m_desiredFrameStartTime { 0 }; // The system time at which we hope to see the next call to startAnimation().
</span><span class="cx">     bool m_animationFinished { false };
</span><span class="cx"> 
</span><ins>+    float m_frameDecodingDurationForTesting { 0 };
+    double m_desiredFrameDecodeTimeForTesting { 0 };
+#if !LOG_DISABLED
+    size_t m_lateFrameCount { 0 };
+    size_t m_earlyFrameCount { 0 };
+    size_t m_cachedFrameCount { 0 };
+#endif
+
</ins><span class="cx"> #if USE(APPKIT)
</span><span class="cx">     mutable RetainPtr&lt;NSImage&gt; m_nsImage; // A cached NSImage of all the frames. Only built lazily if someone actually queries for one.
</span><span class="cx"> #endif
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsColorh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/Color.h (208510 => 208511)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/Color.h        2016-11-10 01:11:36 UTC (rev 208510)
+++ trunk/Source/WebCore/platform/graphics/Color.h        2016-11-10 01:13:05 UTC (rev 208511)
</span><span class="lines">@@ -264,6 +264,7 @@
</span><span class="cx">     static const RGBA32 lightGray = 0xFFC0C0C0;
</span><span class="cx">     WEBCORE_EXPORT static const RGBA32 transparent = 0x00000000;
</span><span class="cx">     static const RGBA32 cyan = 0xFF00FFFF;
</span><ins>+    static const RGBA32 yellow = 0xFFFFFF00;
</ins><span class="cx"> 
</span><span class="cx"> #if PLATFORM(IOS)
</span><span class="cx">     static const RGBA32 compositionFill = 0x3CAFC0E3;
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsImageFrameCachecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/ImageFrameCache.cpp (208510 => 208511)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/ImageFrameCache.cpp        2016-11-10 01:11:36 UTC (rev 208510)
+++ trunk/Source/WebCore/platform/graphics/ImageFrameCache.cpp        2016-11-10 01:13:05 UTC (rev 208511)
</span><span class="lines">@@ -284,10 +284,10 @@
</span><span class="cx">     });
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void ImageFrameCache::requestFrameAsyncDecodingAtIndex(size_t index, SubsamplingLevel subsamplingLevel)
</del><ins>+bool ImageFrameCache::requestFrameAsyncDecodingAtIndex(size_t index, SubsamplingLevel subsamplingLevel)
</ins><span class="cx"> {
</span><span class="cx">     if (!isDecoderAvailable())
</span><del>-        return;
</del><ins>+        return false;
</ins><span class="cx"> 
</span><span class="cx">     if (!hasDecodingQueue())
</span><span class="cx">         startAsyncDecodingQueue();
</span><span class="lines">@@ -299,10 +299,11 @@
</span><span class="cx">         subsamplingLevel = frame.subsamplingLevel();
</span><span class="cx">     
</span><span class="cx">     if (frame.hasValidNativeImage(subsamplingLevel))
</span><del>-        return;
</del><ins>+        return false;
</ins><span class="cx">     
</span><span class="cx">     frame.setDecoding(ImageFrame::Decoding::BeingDecoded);
</span><span class="cx">     m_frameRequestQueue.enqueue({ index, subsamplingLevel });
</span><ins>+    return true;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void ImageFrameCache::stopAsyncDecodingQueue()
</span><span class="lines">@@ -336,7 +337,6 @@
</span><span class="cx"> {
</span><span class="cx">     m_frameCount = Nullopt;
</span><span class="cx">     m_singlePixelSolidColor = Nullopt;
</span><del>-    m_maximumSubsamplingLevel = Nullopt;
</del><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> template&lt;typename T, T (ImageDecoder::*functor)() const&gt;
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsImageFrameCacheh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/ImageFrameCache.h (208510 => 208511)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/ImageFrameCache.h        2016-11-10 01:11:36 UTC (rev 208510)
+++ trunk/Source/WebCore/platform/graphics/ImageFrameCache.h        2016-11-10 01:13:05 UTC (rev 208511)
</span><span class="lines">@@ -68,7 +68,7 @@
</span><span class="cx">     
</span><span class="cx">     // Asynchronous image decoding
</span><span class="cx">     void startAsyncDecodingQueue();
</span><del>-    void requestFrameAsyncDecodingAtIndex(size_t, SubsamplingLevel);
</del><ins>+    bool requestFrameAsyncDecodingAtIndex(size_t, SubsamplingLevel);
</ins><span class="cx">     void stopAsyncDecodingQueue();
</span><span class="cx">     bool hasDecodingQueue() { return m_decodingQueue; }
</span><span class="cx"> 
</span><span class="lines">@@ -162,7 +162,6 @@
</span><span class="cx">     // Image metadata which is calculated from the first ImageFrame.
</span><span class="cx">     Optional&lt;IntSize&gt; m_size;
</span><span class="cx">     Optional&lt;IntSize&gt; m_sizeRespectingOrientation;
</span><del>-    Optional&lt;SubsamplingLevel&gt; m_maximumSubsamplingLevel;
</del><span class="cx">     Optional&lt;Color&gt; m_singlePixelSolidColor;
</span><span class="cx"> };
</span><span class="cx">     
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsImageObserverh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/ImageObserver.h (208510 => 208511)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/ImageObserver.h        2016-11-10 01:11:36 UTC (rev 208510)
+++ trunk/Source/WebCore/platform/graphics/ImageObserver.h        2016-11-10 01:13:05 UTC (rev 208511)
</span><span class="lines">@@ -37,12 +37,16 @@
</span><span class="cx"> protected:
</span><span class="cx">     virtual ~ImageObserver() {}
</span><span class="cx"> public:
</span><ins>+    virtual bool allowSubsampling() const = 0;
+    virtual bool allowAsyncImageDecoding() const = 0;
+    virtual bool showDebugBackground() const = 0;
</ins><span class="cx">     virtual void decodedSizeChanged(const Image*, long long delta) = 0;
</span><ins>+
</ins><span class="cx">     virtual void didDraw(const Image*) = 0;
</span><span class="cx"> 
</span><span class="cx">     virtual void animationAdvanced(const Image*) = 0;
</span><span class="cx"> 
</span><del>-    virtual void changedInRect(const Image*, const IntRect&amp;) = 0;
</del><ins>+    virtual void changedInRect(const Image*, const IntRect* changeRect = nullptr) = 0;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsImageSourcecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/ImageSource.cpp (208510 => 208511)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/ImageSource.cpp        2016-11-10 01:11:36 UTC (rev 208510)
+++ trunk/Source/WebCore/platform/graphics/ImageSource.cpp        2016-11-10 01:13:05 UTC (rev 208511)
</span><span class="lines">@@ -186,7 +186,7 @@
</span><span class="cx">     if (m_maximumSubsamplingLevel)
</span><span class="cx">         return m_maximumSubsamplingLevel.value();
</span><span class="cx"> 
</span><del>-    if (!m_allowSubsampling || !isDecoderAvailable() || !m_decoder-&gt;frameAllowSubsamplingAtIndex(0))
</del><ins>+    if (!isDecoderAvailable() || !m_decoder-&gt;frameAllowSubsamplingAtIndex(0))
</ins><span class="cx">         return SubsamplingLevel::Default;
</span><span class="cx"> 
</span><span class="cx">     // FIXME: this value was chosen to be appropriate for iOS since the image
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsImageSourceh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/ImageSource.h (208510 => 208511)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/ImageSource.h        2016-11-10 01:11:36 UTC (rev 208510)
+++ trunk/Source/WebCore/platform/graphics/ImageSource.h        2016-11-10 01:13:05 UTC (rev 208511)
</span><span class="lines">@@ -66,7 +66,7 @@
</span><span class="cx">     bool isAllDataReceived();
</span><span class="cx"> 
</span><span class="cx">     bool isAsyncDecodingRequired();
</span><del>-    void requestFrameAsyncDecodingAtIndex(size_t index, SubsamplingLevel subsamplingLevel) { m_frameCache-&gt;requestFrameAsyncDecodingAtIndex(index, subsamplingLevel); }
</del><ins>+    bool requestFrameAsyncDecodingAtIndex(size_t index, SubsamplingLevel subsamplingLevel) { return m_frameCache-&gt;requestFrameAsyncDecodingAtIndex(index, subsamplingLevel); }
</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">@@ -98,7 +98,6 @@
</span><span class="cx"> 
</span><span class="cx">     SubsamplingLevel maximumSubsamplingLevel();
</span><span class="cx">     SubsamplingLevel subsamplingLevelForScale(float);
</span><del>-    void setAllowSubsampling(bool allowSubsampling) { m_allowSubsampling = allowSubsampling; }
</del><span class="cx">     NativeImagePtr createFrameImageAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default);
</span><span class="cx"> 
</span><span class="cx"> private:
</span><span class="lines">@@ -113,14 +112,7 @@
</span><span class="cx"> 
</span><span class="cx">     Optional&lt;SubsamplingLevel&gt; m_maximumSubsamplingLevel;
</span><span class="cx"> 
</span><del>-    // The default value of m_allowSubsampling should be the same as defaultImageSubsamplingEnabled in Settings.cpp
</del><span class="cx"> #if PLATFORM(IOS)
</span><del>-    bool m_allowSubsampling { true };
-#else
-    bool m_allowSubsampling { false };
-#endif
-
-#if PLATFORM(IOS)
</del><span class="cx">     // FIXME: We should expose a setting to enable/disable progressive loading so that we can remove the PLATFORM(IOS)-guard.
</span><span class="cx">     double m_progressiveLoadChunkTime { 0 };
</span><span class="cx">     uint16_t m_progressiveLoadChunkCount { 0 };
</span></span></pre></div>
<a id="trunkSourceWebCorerenderingRenderImageResourcecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/rendering/RenderImageResource.cpp (208510 => 208511)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/rendering/RenderImageResource.cpp        2016-11-10 01:11:36 UTC (rev 208510)
+++ trunk/Source/WebCore/rendering/RenderImageResource.cpp        2016-11-10 01:13:05 UTC (rev 208511)
</span><span class="lines">@@ -56,8 +56,10 @@
</span><span class="cx"> {
</span><span class="cx">     ASSERT(m_renderer);
</span><span class="cx"> 
</span><del>-    if (m_cachedImage)
</del><ins>+    if (m_cachedImage) {
+        image()-&gt;stopAnimation();
</ins><span class="cx">         m_cachedImage-&gt;removeClient(*m_renderer);
</span><ins>+    }
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void RenderImageResource::setCachedImage(CachedImage* newImage)
</span></span></pre></div>
<a id="trunkSourceWebCorerenderingRenderImageResourceStyleImagecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/rendering/RenderImageResourceStyleImage.cpp (208510 => 208511)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/rendering/RenderImageResourceStyleImage.cpp        2016-11-10 01:11:36 UTC (rev 208510)
+++ trunk/Source/WebCore/rendering/RenderImageResourceStyleImage.cpp        2016-11-10 01:13:05 UTC (rev 208511)
</span><span class="lines">@@ -57,7 +57,10 @@
</span><span class="cx"> {
</span><span class="cx">     ASSERT(m_renderer);
</span><span class="cx">     m_styleImage-&gt;removeClient(m_renderer);
</span><del>-    m_cachedImage = nullptr;
</del><ins>+    if (m_cachedImage) {
+        image()-&gt;stopAnimation();
+        m_cachedImage = nullptr;
+    }
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> RefPtr&lt;Image&gt; RenderImageResourceStyleImage::image(int width, int height) const
</span></span></pre></div>
<a id="trunkSourceWebCoresvggraphicsSVGImageClientsh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/svg/graphics/SVGImageClients.h (208510 => 208511)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/svg/graphics/SVGImageClients.h        2016-11-10 01:11:36 UTC (rev 208510)
+++ trunk/Source/WebCore/svg/graphics/SVGImageClients.h        2016-11-10 01:13:05 UTC (rev 208511)
</span><span class="lines">@@ -54,7 +54,7 @@
</span><span class="cx">     {
</span><span class="cx">         // If m_image-&gt;m_page is null, we're being destructed, don't fire changedInRect() in that case.
</span><span class="cx">         if (m_image &amp;&amp; m_image-&gt;imageObserver() &amp;&amp; m_image-&gt;m_page)
</span><del>-            m_image-&gt;imageObserver()-&gt;changedInRect(m_image, r);
</del><ins>+            m_image-&gt;imageObserver()-&gt;changedInRect(m_image, &amp;r);
</ins><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     SVGImage* m_image;
</span></span></pre></div>
<a id="trunkSourceWebCoretestingInternalscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/testing/Internals.cpp (208510 => 208511)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/testing/Internals.cpp        2016-11-10 01:11:36 UTC (rev 208510)
+++ trunk/Source/WebCore/testing/Internals.cpp        2016-11-10 01:13:05 UTC (rev 208511)
</span><span class="lines">@@ -649,6 +649,19 @@
</span><span class="cx">     return is&lt;BitmapImage&gt;(image) ? downcast&lt;BitmapImage&gt;(*image).currentFrame() : 0;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void Internals::setImageFrameDecodingDuration(HTMLImageElement&amp; element, float duration)
+{
+    auto* cachedImage = element.cachedImage();
+    if (!cachedImage)
+        return;
+    
+    auto* image = cachedImage-&gt;image();
+    if (!is&lt;BitmapImage&gt;(image))
+        return;
+    
+    downcast&lt;BitmapImage&gt;(*image).setFrameDecodingDurationForTesting(duration);
+}
+
</ins><span class="cx"> void Internals::clearPageCache()
</span><span class="cx"> {
</span><span class="cx">     PageCache::singleton().pruneToSizeNow(0, PruningReason::None);
</span></span></pre></div>
<a id="trunkSourceWebCoretestingInternalsh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/testing/Internals.h (208510 => 208511)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/testing/Internals.h        2016-11-10 01:11:36 UTC (rev 208510)
+++ trunk/Source/WebCore/testing/Internals.h        2016-11-10 01:13:05 UTC (rev 208511)
</span><span class="lines">@@ -103,6 +103,7 @@
</span><span class="cx">     unsigned memoryCacheSize() const;
</span><span class="cx"> 
</span><span class="cx">     unsigned imageFrameIndex(HTMLImageElement&amp;);
</span><ins>+    void setImageFrameDecodingDuration(HTMLImageElement&amp;, float duration);
</ins><span class="cx"> 
</span><span class="cx">     void clearPageCache();
</span><span class="cx">     unsigned pageCacheSize() const;
</span></span></pre></div>
<a id="trunkSourceWebCoretestingInternalsidl"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/testing/Internals.idl (208510 => 208511)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/testing/Internals.idl        2016-11-10 01:11:36 UTC (rev 208510)
+++ trunk/Source/WebCore/testing/Internals.idl        2016-11-10 01:13:05 UTC (rev 208511)
</span><span class="lines">@@ -221,6 +221,7 @@
</span><span class="cx">     [MayThrowException] boolean isPageBoxVisible(long pageNumber);
</span><span class="cx"> 
</span><span class="cx">     unsigned long imageFrameIndex(HTMLImageElement element);
</span><ins>+    void setImageFrameDecodingDuration(HTMLImageElement element, unrestricted float duration);
</ins><span class="cx"> 
</span><span class="cx">     readonly attribute InternalSettings settings;
</span><span class="cx">     readonly attribute unsigned long workerThreadCount;
</span></span></pre>
</div>
</div>

</body>
</html>