<!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>[195216] releases/WebKitGTK/webkit-2.10</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/195216">195216</a></dd>
<dt>Author</dt> <dd>carlosgc@webkit.org</dd>
<dt>Date</dt> <dd>2016-01-18 05:20:31 -0800 (Mon, 18 Jan 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>Merge <a href="http://trac.webkit.org/projects/webkit/changeset/194706">r194706</a> - Directly-composited animated GIFs never resume once scrolled offscreen
https://bugs.webkit.org/show_bug.cgi?id=152817
&lt;rdar://problem/19982020&gt;

Reviewed by Daniel Bates.

Source/WebCore:

Directly-composited animated GIFs would never resume once scrolled
offscreen. This is because calling repaint() in this case would not
cause BitmapImage::draw() to be called and the animation would thus
not be resumed. To address the problem,
repaintForPausedImageAnimationsIfNeeded() now calls
RenderBoxModelObject::contentChanged(ImageChanged) in addition to
repaint() to make sure the animation actually gets resumed, even in
the directly-composited animated GIF case.

Test: fast/images/composited-animated-gif-outside-viewport.html

* platform/graphics/BitmapImage.h:
Make currentFrame() public so it can be exposed via Internals for the
purpose of testing.

* rendering/RenderElement.cpp:
(WebCore::RenderElement::repaintForPausedImageAnimationsIfNeeded):
Call RenderBoxModelObject::contentChanged(ImageChanged) in addition to
calling repaint() to make sure the animation actually gets resumed in
the directly-composited animated GIFs case.

* testing/Internals.cpp:
(WebCore::Internals::imageFrameIndex):
* testing/Internals.h:
* testing/Internals.idl:
Expose new &quot;unsigned long imageFrameIndex(Element)&quot; operation on
Internals so layout tests can better check if an image is actually
animating. Previously, we would rely on the output of
internals.hasPausedImageAnimations(Element) but this is not sufficient
to cover this bug as our rendering code believed it has resumed the
animations but the GIF was not actually animating due to it being
directly-composited.

LayoutTests:

Add a layout test to check that directly-composited animated GIFs are
properly suspended / resumed based on visibility inside the viewport.

* fast/images/composited-animated-gif-outside-viewport-expected.txt: Added.
* fast/images/composited-animated-gif-outside-viewport.html: Added.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#releasesWebKitGTKwebkit210LayoutTestsChangeLog">releases/WebKitGTK/webkit-2.10/LayoutTests/ChangeLog</a></li>
<li><a href="#releasesWebKitGTKwebkit210SourceWebCoreChangeLog">releases/WebKitGTK/webkit-2.10/Source/WebCore/ChangeLog</a></li>
<li><a href="#releasesWebKitGTKwebkit210SourceWebCoreplatformgraphicsBitmapImageh">releases/WebKitGTK/webkit-2.10/Source/WebCore/platform/graphics/BitmapImage.h</a></li>
<li><a href="#releasesWebKitGTKwebkit210SourceWebCorerenderingRenderElementcpp">releases/WebKitGTK/webkit-2.10/Source/WebCore/rendering/RenderElement.cpp</a></li>
<li><a href="#releasesWebKitGTKwebkit210SourceWebCoretestingInternalscpp">releases/WebKitGTK/webkit-2.10/Source/WebCore/testing/Internals.cpp</a></li>
<li><a href="#releasesWebKitGTKwebkit210SourceWebCoretestingInternalsh">releases/WebKitGTK/webkit-2.10/Source/WebCore/testing/Internals.h</a></li>
<li><a href="#releasesWebKitGTKwebkit210SourceWebCoretestingInternalsidl">releases/WebKitGTK/webkit-2.10/Source/WebCore/testing/Internals.idl</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#releasesWebKitGTKwebkit210LayoutTestsfastimagescompositedanimatedgifoutsideviewportexpectedtxt">releases/WebKitGTK/webkit-2.10/LayoutTests/fast/images/composited-animated-gif-outside-viewport-expected.txt</a></li>
<li><a href="#releasesWebKitGTKwebkit210LayoutTestsfastimagescompositedanimatedgifoutsideviewporthtml">releases/WebKitGTK/webkit-2.10/LayoutTests/fast/images/composited-animated-gif-outside-viewport.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="releasesWebKitGTKwebkit210LayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: releases/WebKitGTK/webkit-2.10/LayoutTests/ChangeLog (195215 => 195216)</h4>
<pre class="diff"><span>
<span class="info">--- releases/WebKitGTK/webkit-2.10/LayoutTests/ChangeLog        2016-01-18 13:08:58 UTC (rev 195215)
+++ releases/WebKitGTK/webkit-2.10/LayoutTests/ChangeLog        2016-01-18 13:20:31 UTC (rev 195216)
</span><span class="lines">@@ -1,3 +1,17 @@
</span><ins>+2016-01-07  Chris Dumez  &lt;cdumez@apple.com&gt;
+
+        Directly-composited animated GIFs never resume once scrolled offscreen
+        https://bugs.webkit.org/show_bug.cgi?id=152817
+        &lt;rdar://problem/19982020&gt;
+
+        Reviewed by Daniel Bates.
+
+        Add a layout test to check that directly-composited animated GIFs are
+        properly suspended / resumed based on visibility inside the viewport.
+
+        * fast/images/composited-animated-gif-outside-viewport-expected.txt: Added.
+        * fast/images/composited-animated-gif-outside-viewport.html: Added.
+
</ins><span class="cx"> 2016-01-06  Brent Fulgham  &lt;bfulgham@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Port blocking bypass issue using 307 redirect
</span></span></pre></div>
<a id="releasesWebKitGTKwebkit210LayoutTestsfastimagescompositedanimatedgifoutsideviewportexpectedtxt"></a>
<div class="addfile"><h4>Added: releases/WebKitGTK/webkit-2.10/LayoutTests/fast/images/composited-animated-gif-outside-viewport-expected.txt (0 => 195216)</h4>
<pre class="diff"><span>
<span class="info">--- releases/WebKitGTK/webkit-2.10/LayoutTests/fast/images/composited-animated-gif-outside-viewport-expected.txt                                (rev 0)
+++ releases/WebKitGTK/webkit-2.10/LayoutTests/fast/images/composited-animated-gif-outside-viewport-expected.txt        2016-01-18 13:20:31 UTC (rev 195216)
</span><span class="lines">@@ -0,0 +1,15 @@
</span><ins>+Make sure directly-composited animated GIFs properly get suspended / resumed based on visibility inside viewport.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+PASS isImageAnimated() became true
+Scroll down so that the image is no longer visible
+PASS isImageAnimated() became false
+Animation was paused, scroll up so that the image is visible again
+PASS isImageAnimated() became true
+PASS internals.imageFrameIndex(testImage) became different from frameIndexImageWasPausedAt
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="releasesWebKitGTKwebkit210LayoutTestsfastimagescompositedanimatedgifoutsideviewporthtml"></a>
<div class="addfile"><h4>Added: releases/WebKitGTK/webkit-2.10/LayoutTests/fast/images/composited-animated-gif-outside-viewport.html (0 => 195216)</h4>
<pre class="diff"><span>
<span class="info">--- releases/WebKitGTK/webkit-2.10/LayoutTests/fast/images/composited-animated-gif-outside-viewport.html                                (rev 0)
+++ releases/WebKitGTK/webkit-2.10/LayoutTests/fast/images/composited-animated-gif-outside-viewport.html        2016-01-18 13:20:31 UTC (rev 195216)
</span><span class="lines">@@ -0,0 +1,53 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html style=&quot;width: 1600px; height: 1200px&quot;&gt;
+&lt;head&gt;
+&lt;script src=&quot;../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;/head&gt;
+&lt;body onload=&quot;runTest()&quot;&gt;
+&lt;img id=&quot;testImage&quot; src=&quot;resources/animated.gif&quot; style=&quot;-webkit-transform: translatez(0);&quot;&gt;
+&lt;script&gt;
+description(&quot;Make sure directly-composited animated GIFs properly get suspended / resumed based on visibility inside viewport.&quot;);
+jsTestIsAsync = true;
+
+var testImage = document.getElementById(&quot;testImage&quot;);
+var frameIndexImageWasPausedAt = 0;
+
+function isImageAnimated()
+{
+  return !internals.hasPausedImageAnimations(testImage);
+}
+
+function checkFrameIndexAndFinish()
+{
+  // Actually make sure that the frame index changes.
+  shouldBecomeDifferent(&quot;internals.imageFrameIndex(testImage)&quot;, &quot;frameIndexImageWasPausedAt&quot;, finishJSTest);
+}
+
+function scrollUp()
+{
+  frameIndexImageWasPausedAt = internals.imageFrameIndex(testImage);
+  
+  debug(&quot;Animation was paused, scroll up so that the image is visible again&quot;);
+  window.scrollBy(0, -600);
+
+  shouldBecomeEqual(&quot;isImageAnimated()&quot;, &quot;true&quot;, checkFrameIndexAndFinish);
+}
+
+function scrollDown()
+{
+  debug(&quot;Scroll down so that the image is no longer visible&quot;);
+  window.scrollBy(0, 600);
+  shouldBecomeEqual(&quot;isImageAnimated()&quot;, &quot;false&quot;, scrollUp);
+}
+
+function runTest()
+{
+  if (!window.internals)
+    return;
+
+  shouldBecomeEqual(&quot;isImageAnimated()&quot;, &quot;true&quot;, scrollDown);
+}
+&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="releasesWebKitGTKwebkit210SourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: releases/WebKitGTK/webkit-2.10/Source/WebCore/ChangeLog (195215 => 195216)</h4>
<pre class="diff"><span>
<span class="info">--- releases/WebKitGTK/webkit-2.10/Source/WebCore/ChangeLog        2016-01-18 13:08:58 UTC (rev 195215)
+++ releases/WebKitGTK/webkit-2.10/Source/WebCore/ChangeLog        2016-01-18 13:20:31 UTC (rev 195216)
</span><span class="lines">@@ -1,3 +1,44 @@
</span><ins>+2016-01-07  Chris Dumez  &lt;cdumez@apple.com&gt;
+
+        Directly-composited animated GIFs never resume once scrolled offscreen
+        https://bugs.webkit.org/show_bug.cgi?id=152817
+        &lt;rdar://problem/19982020&gt;
+
+        Reviewed by Daniel Bates.
+
+        Directly-composited animated GIFs would never resume once scrolled
+        offscreen. This is because calling repaint() in this case would not
+        cause BitmapImage::draw() to be called and the animation would thus
+        not be resumed. To address the problem,
+        repaintForPausedImageAnimationsIfNeeded() now calls
+        RenderBoxModelObject::contentChanged(ImageChanged) in addition to
+        repaint() to make sure the animation actually gets resumed, even in
+        the directly-composited animated GIF case.
+
+        Test: fast/images/composited-animated-gif-outside-viewport.html
+
+        * platform/graphics/BitmapImage.h:
+        Make currentFrame() public so it can be exposed via Internals for the
+        purpose of testing.
+
+        * rendering/RenderElement.cpp:
+        (WebCore::RenderElement::repaintForPausedImageAnimationsIfNeeded):
+        Call RenderBoxModelObject::contentChanged(ImageChanged) in addition to
+        calling repaint() to make sure the animation actually gets resumed in
+        the directly-composited animated GIFs case.
+
+        * testing/Internals.cpp:
+        (WebCore::Internals::imageFrameIndex):
+        * testing/Internals.h:
+        * testing/Internals.idl:
+        Expose new &quot;unsigned long imageFrameIndex(Element)&quot; operation on
+        Internals so layout tests can better check if an image is actually
+        animating. Previously, we would rely on the output of
+        internals.hasPausedImageAnimations(Element) but this is not sufficient
+        to cover this bug as our rendering code believed it has resumed the
+        animations but the GIF was not actually animating due to it being
+        directly-composited.
+
</ins><span class="cx"> 2016-01-07  Michael Catanzaro  &lt;mcatanzaro@igalia.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [SOUP] Consider reducing max simultaneous connections
</span></span></pre></div>
<a id="releasesWebKitGTKwebkit210SourceWebCoreplatformgraphicsBitmapImageh"></a>
<div class="modfile"><h4>Modified: releases/WebKitGTK/webkit-2.10/Source/WebCore/platform/graphics/BitmapImage.h (195215 => 195216)</h4>
<pre class="diff"><span>
<span class="info">--- releases/WebKitGTK/webkit-2.10/Source/WebCore/platform/graphics/BitmapImage.h        2016-01-18 13:08:58 UTC (rev 195215)
+++ releases/WebKitGTK/webkit-2.10/Source/WebCore/platform/graphics/BitmapImage.h        2016-01-18 13:20:31 UTC (rev 195216)
</span><span class="lines">@@ -185,6 +185,8 @@
</span><span class="cx"> 
</span><span class="cx">     bool allowSubsampling() const { return m_allowSubsampling; }
</span><span class="cx">     void setAllowSubsampling(bool allowSubsampling) { m_allowSubsampling = allowSubsampling; }
</span><ins>+
+    size_t currentFrame() const { return m_currentFrame; }
</ins><span class="cx">     
</span><span class="cx"> private:
</span><span class="cx">     void updateSize(ImageOrientationDescription = ImageOrientationDescription()) const;
</span><span class="lines">@@ -210,7 +212,6 @@
</span><span class="cx">         const FloatPoint&amp; phase, ColorSpace styleColorSpace, CompositeOperator, const FloatRect&amp; destRect);
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><del>-    size_t currentFrame() const { return m_currentFrame; }
</del><span class="cx">     size_t frameCount();
</span><span class="cx"> 
</span><span class="cx">     PassNativeImagePtr frameAtIndex(size_t, float presentationScaleHint = 1);
</span></span></pre></div>
<a id="releasesWebKitGTKwebkit210SourceWebCorerenderingRenderElementcpp"></a>
<div class="modfile"><h4>Modified: releases/WebKitGTK/webkit-2.10/Source/WebCore/rendering/RenderElement.cpp (195215 => 195216)</h4>
<pre class="diff"><span>
<span class="info">--- releases/WebKitGTK/webkit-2.10/Source/WebCore/rendering/RenderElement.cpp        2016-01-18 13:08:58 UTC (rev 195215)
+++ releases/WebKitGTK/webkit-2.10/Source/WebCore/rendering/RenderElement.cpp        2016-01-18 13:20:31 UTC (rev 195216)
</span><span class="lines">@@ -1469,7 +1469,13 @@
</span><span class="cx">     ASSERT(m_hasPausedImageAnimations);
</span><span class="cx">     if (!shouldRepaintForImageAnimation(*this, visibleRect))
</span><span class="cx">         return false;
</span><ins>+
</ins><span class="cx">     repaint();
</span><ins>+
+    // For directly-composited animated GIFs it does not suffice to call repaint() to resume animation. We need to mark the image as changed.
+    if (is&lt;RenderBoxModelObject&gt;(*this))
+        downcast&lt;RenderBoxModelObject&gt;(*this).contentChanged(ImageChanged);
+
</ins><span class="cx">     return true;
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="releasesWebKitGTKwebkit210SourceWebCoretestingInternalscpp"></a>
<div class="modfile"><h4>Modified: releases/WebKitGTK/webkit-2.10/Source/WebCore/testing/Internals.cpp (195215 => 195216)</h4>
<pre class="diff"><span>
<span class="info">--- releases/WebKitGTK/webkit-2.10/Source/WebCore/testing/Internals.cpp        2016-01-18 13:08:58 UTC (rev 195215)
+++ releases/WebKitGTK/webkit-2.10/Source/WebCore/testing/Internals.cpp        2016-01-18 13:20:31 UTC (rev 195216)
</span><span class="lines">@@ -31,6 +31,7 @@
</span><span class="cx"> #include &quot;AnimationController.h&quot;
</span><span class="cx"> #include &quot;ApplicationCacheStorage.h&quot;
</span><span class="cx"> #include &quot;BackForwardController.h&quot;
</span><ins>+#include &quot;BitmapImage.h&quot;
</ins><span class="cx"> #include &quot;CachedImage.h&quot;
</span><span class="cx"> #include &quot;CachedResourceLoader.h&quot;
</span><span class="cx"> #include &quot;Chrome.h&quot;
</span><span class="lines">@@ -530,6 +531,21 @@
</span><span class="cx">     return MemoryCache::singleton().size();
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+size_t Internals::imageFrameIndex(Element* element, ExceptionCode&amp; ec)
+{
+    if (!is&lt;HTMLImageElement&gt;(element)) {
+        ec = TypeError;
+        return 0;
+    }
+
+    auto* cachedImage = downcast&lt;HTMLImageElement&gt;(*element).cachedImage();
+    if (!cachedImage)
+        return 0;
+
+    auto* image = cachedImage-&gt;image();
+    return is&lt;BitmapImage&gt;(image) ? downcast&lt;BitmapImage&gt;(*image).currentFrame() : 0;
+}
+
</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="releasesWebKitGTKwebkit210SourceWebCoretestingInternalsh"></a>
<div class="modfile"><h4>Modified: releases/WebKitGTK/webkit-2.10/Source/WebCore/testing/Internals.h (195215 => 195216)</h4>
<pre class="diff"><span>
<span class="info">--- releases/WebKitGTK/webkit-2.10/Source/WebCore/testing/Internals.h        2016-01-18 13:08:58 UTC (rev 195215)
+++ releases/WebKitGTK/webkit-2.10/Source/WebCore/testing/Internals.h        2016-01-18 13:20:31 UTC (rev 195216)
</span><span class="lines">@@ -102,6 +102,8 @@
</span><span class="cx">     void pruneMemoryCacheToSize(unsigned size);
</span><span class="cx">     unsigned memoryCacheSize() const;
</span><span class="cx"> 
</span><ins>+    size_t imageFrameIndex(Element*, ExceptionCode&amp;);
+
</ins><span class="cx">     void clearPageCache();
</span><span class="cx">     unsigned pageCacheSize() const;
</span><span class="cx"> 
</span></span></pre></div>
<a id="releasesWebKitGTKwebkit210SourceWebCoretestingInternalsidl"></a>
<div class="modfile"><h4>Modified: releases/WebKitGTK/webkit-2.10/Source/WebCore/testing/Internals.idl (195215 => 195216)</h4>
<pre class="diff"><span>
<span class="info">--- releases/WebKitGTK/webkit-2.10/Source/WebCore/testing/Internals.idl        2016-01-18 13:08:58 UTC (rev 195215)
+++ releases/WebKitGTK/webkit-2.10/Source/WebCore/testing/Internals.idl        2016-01-18 13:20:31 UTC (rev 195216)
</span><span class="lines">@@ -205,6 +205,8 @@
</span><span class="cx"> 
</span><span class="cx">     [RaisesException] boolean isPageBoxVisible(long pageNumber);
</span><span class="cx"> 
</span><ins>+    [RaisesException] unsigned long imageFrameIndex(Element element);
+
</ins><span class="cx">     readonly attribute InternalSettings settings;
</span><span class="cx">     readonly attribute unsigned long workerThreadCount;
</span><span class="cx"> 
</span></span></pre>
</div>
</div>

</body>
</html>