<!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>[159746] 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/159746">159746</a></dd>
<dt>Author</dt> <dd>commit-queue@webkit.org</dd>
<dt>Date</dt> <dd>2013-11-25 06:03:25 -0800 (Mon, 25 Nov 2013)</dd>
</dl>

<h3>Log Message</h3>
<pre>[GStreamer] Seeking fails on media content provided by servers not supporting Range requests
https://bugs.webkit.org/show_bug.cgi?id=85994

Patch by Andres Gomez &lt;agomez@igalia.com&gt; on 2013-11-25
Reviewed by Philippe Normand.

Source/WebCore:

Based on the patch written by Andre Moreira Magalhaes.

When the GStreamer web source was doing a &quot;Range&quot; request we were
expecting to receive a 206 status reply with the &quot;Content-Range&quot;
header and just the requested data. Supporting &quot;Range&quot; requests is
not mandatory so, for the servers not supporting it they will be
replying with a 200 status and the whole content of the media
element. Now, we are properly handling these replies too.

Test: http/tests/media/media-seeking-no-ranges-server.html

* platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp:
(StreamingClient::handleResponseReceived): Do not fail when
receiving 200 as response for HTTP range requests.
(StreamingClient::handleDataReceived): Manually seek on stream
when receiving the full data after a seek.

LayoutTests:

Added test to check that seeking media files on http servers not
supporting &quot;Range&quot; requests doesn't trigger an error.

* http/tests/media/media-seeking-no-ranges-server-expected.txt: Added.
* http/tests/media/media-seeking-no-ranges-server.html: Added.
* http/tests/media/resources/load-video.php: Added support for new
&quot;ranges&quot; paramenter.
* http/tests/media/resources/serve-video.php: Added support for
new &quot;ranges&quot; paramenter. When &quot;ranges&quot; is &quot;no&quot; we always answer
the HTTP status &quot;200 OK&quot; and send the whole file.
* platform/mac/TestExpectations: New test skipped in Mac port as
media playback download control is passed to AVFoundation that
doesn't like fancy PHP URLs like the one used in the test and, in
addition, they won't care about HTTP servers not supporting
&quot;Range&quot; requests.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestshttptestsmediaresourcesloadvideophp">trunk/LayoutTests/http/tests/media/resources/load-video.php</a></li>
<li><a href="#trunkLayoutTestshttptestsmediaresourcesservevideophp">trunk/LayoutTests/http/tests/media/resources/serve-video.php</a></li>
<li><a href="#trunkLayoutTestsplatformmacTestExpectations">trunk/LayoutTests/platform/mac/TestExpectations</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsgstreamerWebKitWebSourceGStreamercpp">trunk/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestshttptestsmediamediaseekingnorangesserverexpectedtxt">trunk/LayoutTests/http/tests/media/media-seeking-no-ranges-server-expected.txt</a></li>
<li><a href="#trunkLayoutTestshttptestsmediamediaseekingnorangesserverhtml">trunk/LayoutTests/http/tests/media/media-seeking-no-ranges-server.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (159745 => 159746)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2013-11-25 12:14:52 UTC (rev 159745)
+++ trunk/LayoutTests/ChangeLog        2013-11-25 14:03:25 UTC (rev 159746)
</span><span class="lines">@@ -1,3 +1,26 @@
</span><ins>+2013-11-25  Andres Gomez  &lt;agomez@igalia.com&gt;
+
+        [GStreamer] Seeking fails on media content provided by servers not supporting Range requests
+        https://bugs.webkit.org/show_bug.cgi?id=85994
+
+        Reviewed by Philippe Normand.
+
+        Added test to check that seeking media files on http servers not
+        supporting &quot;Range&quot; requests doesn't trigger an error.
+
+        * http/tests/media/media-seeking-no-ranges-server-expected.txt: Added.
+        * http/tests/media/media-seeking-no-ranges-server.html: Added.
+        * http/tests/media/resources/load-video.php: Added support for new
+        &quot;ranges&quot; paramenter.
+        * http/tests/media/resources/serve-video.php: Added support for
+        new &quot;ranges&quot; paramenter. When &quot;ranges&quot; is &quot;no&quot; we always answer
+        the HTTP status &quot;200 OK&quot; and send the whole file.
+        * platform/mac/TestExpectations: New test skipped in Mac port as
+        media playback download control is passed to AVFoundation that
+        doesn't like fancy PHP URLs like the one used in the test and, in
+        addition, they won't care about HTTP servers not supporting
+        &quot;Range&quot; requests.
+
</ins><span class="cx"> 2013-11-25  Mario Sanchez Prada  &lt;mario.prada@samsung.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Unreviewed GTK gardening. Removed expectations for test that is no
</span></span></pre></div>
<a id="trunkLayoutTestshttptestsmediamediaseekingnorangesserverexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/http/tests/media/media-seeking-no-ranges-server-expected.txt (0 => 159746)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/media/media-seeking-no-ranges-server-expected.txt                                (rev 0)
+++ trunk/LayoutTests/http/tests/media/media-seeking-no-ranges-server-expected.txt        2013-11-25 14:03:25 UTC (rev 159746)
</span><span class="lines">@@ -0,0 +1,12 @@
</span><ins>+
+Test ended by:
+
+Seek media file served by a server not supporting &quot;Range&quot; requests.
+Verify that the 'ended' event fires upon finishing the playback.
+
+EVENT(playing)
+RUN(audio.currentTime = 0.5)
+
+EVENT(ended)
+END OF TEST
+
</ins></span></pre></div>
<a id="trunkLayoutTestshttptestsmediamediaseekingnorangesserverhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/http/tests/media/media-seeking-no-ranges-server.html (0 => 159746)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/media/media-seeking-no-ranges-server.html                                (rev 0)
+++ trunk/LayoutTests/http/tests/media/media-seeking-no-ranges-server.html        2013-11-25 14:03:25 UTC (rev 159746)
</span><span class="lines">@@ -0,0 +1,39 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+    &lt;head&gt;
+        &lt;script src=../../media-resources/video-test.js&gt;&lt;/script&gt;
+        &lt;script src=../../media-resources/media-file.js&gt;&lt;/script&gt;
+
+        &lt;script&gt;
+            var media = findMediaFile('audio', '../../../../media/content/silence');
+            var type = mimeTypeForExtension(media.split('.').pop());
+            var audio;
+
+            function start()
+            {
+                audio = document.querySelector(&quot;audio&quot;);
+                audio.src = 'http://127.0.0.1:8000/media/resources/load-video.php?name=' + media + '&amp;type=' + type + '&amp;ranges=no';
+
+                waitForEventAndFail('error');
+                waitForEventAndEnd('ended');
+                waitForEventOnce('playing', canPlay);
+            }
+
+            function canPlay()
+            {
+                run(&quot;audio.currentTime = 0.5&quot;);
+                consoleWrite(&quot;&quot;);
+            }
+        &lt;/script&gt;
+    &lt;/head&gt;
+    &lt;body onload=&quot;start()&quot;&gt;
+        &lt;audio controls autoplay&gt;&lt;/audio&gt;
+        &lt;p&gt;&lt;b&gt;Test ended by:&lt;/b&gt;
+        &lt;ol&gt;
+            &lt;li&gt;Seek media file served by a server not supporting &quot;Range&quot; requests.&lt;/li&gt;
+            &lt;li&gt;Verify that the 'ended' event fires upon finishing the playback.&lt;/li&gt;
+        &lt;/ol&gt;
+        &lt;/p&gt;
+        &lt;br/&gt;
+    &lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestshttptestsmediaresourcesloadvideophp"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/http/tests/media/resources/load-video.php (159745 => 159746)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/media/resources/load-video.php        2013-11-25 12:14:52 UTC (rev 159745)
+++ trunk/LayoutTests/http/tests/media/resources/load-video.php        2013-11-25 14:03:25 UTC (rev 159746)
</span><span class="lines">@@ -2,10 +2,14 @@
</span><span class="cx"> 
</span><span class="cx">     $fileName = $_GET[&quot;name&quot;];
</span><span class="cx">     $type = $_GET[&quot;type&quot;];
</span><ins>+    if (array_key_exists(&quot;ranges&quot;, $_GET))
+        $ranges = $_GET[&quot;ranges&quot;];
</ins><span class="cx"> 
</span><span class="cx">     $_GET = array();
</span><span class="cx">     $_GET['name'] = $fileName;
</span><span class="cx">     $_GET['type'] = $type;
</span><del>-    @include(&quot;./serve-video.php&quot;); 
</del><ins>+    if (isset($ranges))
+        $_GET[&quot;ranges&quot;] = $ranges;
+    @include(&quot;./serve-video.php&quot;);
</ins><span class="cx"> 
</span><span class="cx"> ?&gt;
</span></span></pre></div>
<a id="trunkLayoutTestshttptestsmediaresourcesservevideophp"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/http/tests/media/resources/serve-video.php (159745 => 159746)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/media/resources/serve-video.php        2013-11-25 12:14:52 UTC (rev 159745)
+++ trunk/LayoutTests/http/tests/media/resources/serve-video.php        2013-11-25 14:03:25 UTC (rev 159746)
</span><span class="lines">@@ -2,14 +2,17 @@
</span><span class="cx"> 
</span><span class="cx">     $fileName = $_GET[&quot;name&quot;];
</span><span class="cx">     $type = $_GET[&quot;type&quot;];
</span><ins>+    if (array_key_exists(&quot;ranges&quot;, $_GET))
+        $ranges = $_GET[&quot;ranges&quot;];
</ins><span class="cx"> 
</span><span class="cx">     $fileSize = filesize($fileName);
</span><span class="cx">     $start = 0;
</span><span class="cx">     $end = $fileSize - 1;
</span><del>-    $contentRange = $_SERVER[&quot;HTTP_RANGE&quot;];
</del><ins>+    if ($ranges != &quot;no&quot;)
+        $contentRange = $_SERVER[&quot;HTTP_RANGE&quot;];
</ins><span class="cx">     if (isset($contentRange)) {
</span><del>-        $range = explode(&quot;-&quot;, substr($contentRange, strlen(&quot;bytes=&quot;))); 
-        $start = intval($range[0]); 
</del><ins>+        $range = explode(&quot;-&quot;, substr($contentRange, strlen(&quot;bytes=&quot;)));
+        $start = intval($range[0]);
</ins><span class="cx">         if (!empty($range[1]))
</span><span class="cx">             $end = intval($range[1]);
</span><span class="cx">         $httpStatus = &quot;206 Partial Content&quot;;
</span><span class="lines">@@ -22,10 +25,11 @@
</span><span class="cx">     header(&quot;Pragma: no-cache&quot;);
</span><span class="cx">     header(&quot;Etag: &quot; . '&quot;' . $fileSize . &quot;-&quot; . filemtime($fileName) . '&quot;');
</span><span class="cx">     header(&quot;Content-Type: &quot; . $type);
</span><del>-    header(&quot;Accept-Ranges: bytes&quot;);
</del><ins>+    if ($ranges != &quot;no&quot;)
+        header(&quot;Accept-Ranges: bytes&quot;);
</ins><span class="cx">     header(&quot;Content-Length: &quot; . ($end - $start + 1));
</span><del>-    if ($contentRange)
-                header(&quot;Content-Range: bytes &quot; . $start . &quot;-&quot; . $end . &quot;/&quot; . $fileSize); 
</del><ins>+    if (isset($contentRange))
+        header(&quot;Content-Range: bytes &quot; . $start . &quot;-&quot; . $end . &quot;/&quot; . $fileSize);
</ins><span class="cx">     header(&quot;Connection: close&quot;);
</span><span class="cx"> 
</span><span class="cx">     $chunkSize = 1024 * 256;
</span></span></pre></div>
<a id="trunkLayoutTestsplatformmacTestExpectations"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/platform/mac/TestExpectations (159745 => 159746)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/platform/mac/TestExpectations        2013-11-25 12:14:52 UTC (rev 159745)
+++ trunk/LayoutTests/platform/mac/TestExpectations        2013-11-25 14:03:25 UTC (rev 159746)
</span><span class="lines">@@ -590,6 +590,9 @@
</span><span class="cx"> http/tests/media/video-play-stall.html
</span><span class="cx"> media/media-fullscreen-not-in-document.html
</span><span class="cx"> 
</span><ins>+# Media playback control is passed to AVFoundation that doesn't like fancy PHP URLs like the one used in the test and, in addition, they won't care about HTTP servers not supporting &quot;Range&quot; requests
+http/tests/media/media-seeking-no-ranges-server.html
+
</ins><span class="cx"> # &lt;rdar://problem/11358748&gt; http/tests/multipart/multipart-wait-before-boundary.html fails on ML as of r115745
</span><span class="cx"> http/tests/multipart/multipart-wait-before-boundary.html
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (159745 => 159746)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2013-11-25 12:14:52 UTC (rev 159745)
+++ trunk/Source/WebCore/ChangeLog        2013-11-25 14:03:25 UTC (rev 159746)
</span><span class="lines">@@ -1,3 +1,27 @@
</span><ins>+2013-11-25  Andres Gomez  &lt;agomez@igalia.com&gt;
+
+        [GStreamer] Seeking fails on media content provided by servers not supporting Range requests
+        https://bugs.webkit.org/show_bug.cgi?id=85994
+
+        Reviewed by Philippe Normand.
+
+        Based on the patch written by Andre Moreira Magalhaes.
+
+        When the GStreamer web source was doing a &quot;Range&quot; request we were
+        expecting to receive a 206 status reply with the &quot;Content-Range&quot;
+        header and just the requested data. Supporting &quot;Range&quot; requests is
+        not mandatory so, for the servers not supporting it they will be
+        replying with a 200 status and the whole content of the media
+        element. Now, we are properly handling these replies too.
+
+        Test: http/tests/media/media-seeking-no-ranges-server.html
+
+        * platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp:
+        (StreamingClient::handleResponseReceived): Do not fail when
+        receiving 200 as response for HTTP range requests.
+        (StreamingClient::handleDataReceived): Manually seek on stream
+        when receiving the full data after a seek.
+
</ins><span class="cx"> 2013-11-25  Radu Stavila  &lt;stavila@adobe.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Removed obsolete FIXME after the landing of visual overflow patch (https://bugs.webkit.org/show_bug.cgi?id=118665).
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsgstreamerWebKitWebSourceGStreamercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp (159745 => 159746)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp        2013-11-25 12:14:52 UTC (rev 159745)
+++ trunk/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp        2013-11-25 14:03:25 UTC (rev 159746)
</span><span class="lines">@@ -839,17 +839,28 @@
</span><span class="cx"> 
</span><span class="cx">     GMutexLocker locker(GST_OBJECT_GET_LOCK(src));
</span><span class="cx"> 
</span><del>-    // If we seeked we need 206 == PARTIAL_CONTENT
-    if (priv-&gt;requestedOffset &amp;&amp; response.httpStatusCode() != 206) {
-        locker.unlock();
-        GST_ELEMENT_ERROR(src, RESOURCE, READ, (0), (0));
-        gst_app_src_end_of_stream(priv-&gt;appsrc);
-        webKitWebSrcStop(src);
</del><ins>+    if (priv-&gt;seekID) {
+        GST_DEBUG_OBJECT(src, &quot;Seek in progress, ignoring response&quot;);
</ins><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    if (priv-&gt;requestedOffset) {
+        // Seeking ... we expect a 206 == PARTIAL_CONTENT
+        if (response.httpStatusCode() == 200) {
+            // Range request didn't have a ranged response; resetting offset.
+            priv-&gt;offset = 0;
+        } else if (response.httpStatusCode() != 206) {
+            // Range request completely failed.
+            locker.unlock();
+            GST_ELEMENT_ERROR(src, RESOURCE, READ, (&quot;Received unexpected %d HTTP status code&quot;, response.httpStatusCode()), (0));
+            gst_app_src_end_of_stream(priv-&gt;appsrc);
+            webKitWebSrcStop(src);
+            return;
+        }
+    }
+
</ins><span class="cx">     long long length = response.expectedContentLength();
</span><del>-    if (length &gt; 0)
</del><ins>+    if (length &gt; 0 &amp;&amp; priv-&gt;requestedOffset &amp;&amp; response.httpStatusCode() == 206)
</ins><span class="cx">         length += priv-&gt;requestedOffset;
</span><span class="cx"> 
</span><span class="cx">     priv-&gt;size = length &gt;= 0 ? length : 0;
</span><span class="lines">@@ -934,6 +945,27 @@
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    if (priv-&gt;offset &lt; priv-&gt;requestedOffset) {
+        // Range request failed; seeking manually.
+        if (priv-&gt;offset + length &lt;= priv-&gt;requestedOffset) {
+            // Discard all the buffers coming before the requested seek position.
+            priv-&gt;offset += length;
+            priv-&gt;buffer.clear();
+            return;
+        }
+
+        if (priv-&gt;offset + length &gt; priv-&gt;requestedOffset) {
+            guint64 offset = priv-&gt;requestedOffset - priv-&gt;offset;
+            data += offset;
+            length -= offset;
+            if (priv-&gt;buffer)
+                gst_buffer_resize(priv-&gt;buffer.get(), offset, -1);
+            priv-&gt;offset = priv-&gt;requestedOffset;
+        }
+
+        priv-&gt;requestedOffset = 0;
+    }
+
</ins><span class="cx">     // Ports using the GStreamer backend but not the soup implementation of ResourceHandle
</span><span class="cx">     // won't be using buffers provided by this client, the buffer is created here in that case.
</span><span class="cx">     if (!priv-&gt;buffer)
</span></span></pre>
</div>
</div>

</body>
</html>