<!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>[162339] releases/WebKitGTK/webkit-2.2</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/162339">162339</a></dd>
<dt>Author</dt> <dd>carlosgc@webkit.org</dd>
<dt>Date</dt> <dd>2014-01-20 06:36:11 -0800 (Mon, 20 Jan 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>Merge <a href="http://trac.webkit.org/projects/webkit/changeset/161555">r161555</a> - [SOUP] Partial file left on disk after a download fails or is cancelled in WebKit2
https://bugs.webkit.org/show_bug.cgi?id=126686

Reviewed by Martin Robinson.

Source/WebKit2:

We are currently writing the downloads directly into the
destination, and when a download fails or is cancelled after the
destination has been decided, the partial file is left on the
disk. Deleting the final file is not safe because there might be a
race condition, so we can use an intermediate file like other
browsers do, a file in the same directory than the target
destination but with .wkdownload suffix, that is removed when the
download fails or is cancelled. If the download finishes
successfully the intermediate file is renamed to the final
destination.

* Shared/Downloads/soup/DownloadSoup.cpp:
(WebKit::DownloadClient::deleteIntermediateFileInNeeded): Delete
the intermdiate file if it's been created already.
(WebKit::DownloadClient::downloadFailed): Call deleteIntermediateFileInNeeded.
(WebKit::DownloadClient::didReceiveResponse): Do not create a
SoupMessage for the given ResourceResponse that is not used, cache
the ResourceResponse instead. Create the intermediate file and use
it instead of the final destination.
(WebKit::DownloadClient::didReceiveData): Use the cached
ResourceResponse directly.
(WebKit::DownloadClient::didFinishLoading): Rename the
intermediate file to the final destination and write the metadata
in the final target destination.
(WebKit::DownloadClient::cancel): Handle the download cancellation
here, removing the intermediate file is needed and cancelling the
ResourceHandle and the download.
(WebKit::DownloadClient::handleResponseLater):
(WebKit::Download::cancel): Let the client handle the cancellation.

Tools:

Test that partial files are not left on disk after a download has
been cancelled after the destination has been decided. To make
sure the download is cancelled after the destination has been
decided and before the operation finishes, we cancel the download
in the destination decided callback, and we use an infinite
resource that writes chunks to the response body and never
completes the body.

* TestWebKitAPI/Tests/WebKit2Gtk/TestDownloads.cpp:
(addContentDispositionHTTPHeaderToResponse): Helper function to
add the Content-Disposition to the response headers.
(writeNextChunkIdle): Write next chunk to response body.
(writeNextChunk): Write next chunk in an idle to avoid flooding
the network with the inifnite resource.
(serverCallback): Add an inifinite resource.
(testDownloadRemoteFileError): Check that partial file is not
present after the download has been cancelled.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#releasesWebKitGTKwebkit22SourceWebKit2ChangeLog">releases/WebKitGTK/webkit-2.2/Source/WebKit2/ChangeLog</a></li>
<li><a href="#releasesWebKitGTKwebkit22SourceWebKit2SharedDownloadssoupDownloadSoupcpp">releases/WebKitGTK/webkit-2.2/Source/WebKit2/Shared/Downloads/soup/DownloadSoup.cpp</a></li>
<li><a href="#releasesWebKitGTKwebkit22ToolsChangeLog">releases/WebKitGTK/webkit-2.2/Tools/ChangeLog</a></li>
<li><a href="#releasesWebKitGTKwebkit22ToolsTestWebKitAPITestsWebKit2GtkTestDownloadscpp">releases/WebKitGTK/webkit-2.2/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDownloads.cpp</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="releasesWebKitGTKwebkit22SourceWebKit2ChangeLog"></a>
<div class="modfile"><h4>Modified: releases/WebKitGTK/webkit-2.2/Source/WebKit2/ChangeLog (162338 => 162339)</h4>
<pre class="diff"><span>
<span class="info">--- releases/WebKitGTK/webkit-2.2/Source/WebKit2/ChangeLog        2014-01-20 14:09:21 UTC (rev 162338)
+++ releases/WebKitGTK/webkit-2.2/Source/WebKit2/ChangeLog        2014-01-20 14:36:11 UTC (rev 162339)
</span><span class="lines">@@ -1,3 +1,40 @@
</span><ins>+2014-01-09  Carlos Garcia Campos  &lt;cgarcia@igalia.com&gt;
+
+        [SOUP] Partial file left on disk after a download fails or is cancelled in WebKit2
+        https://bugs.webkit.org/show_bug.cgi?id=126686
+
+        Reviewed by Martin Robinson.
+
+        We are currently writing the downloads directly into the
+        destination, and when a download fails or is cancelled after the
+        destination has been decided, the partial file is left on the
+        disk. Deleting the final file is not safe because there might be a
+        race condition, so we can use an intermediate file like other
+        browsers do, a file in the same directory than the target
+        destination but with .wkdownload suffix, that is removed when the
+        download fails or is cancelled. If the download finishes
+        successfully the intermediate file is renamed to the final
+        destination.
+
+        * Shared/Downloads/soup/DownloadSoup.cpp:
+        (WebKit::DownloadClient::deleteIntermediateFileInNeeded): Delete
+        the intermdiate file if it's been created already.
+        (WebKit::DownloadClient::downloadFailed): Call deleteIntermediateFileInNeeded.
+        (WebKit::DownloadClient::didReceiveResponse): Do not create a
+        SoupMessage for the given ResourceResponse that is not used, cache
+        the ResourceResponse instead. Create the intermediate file and use
+        it instead of the final destination.
+        (WebKit::DownloadClient::didReceiveData): Use the cached
+        ResourceResponse directly.
+        (WebKit::DownloadClient::didFinishLoading): Rename the
+        intermediate file to the final destination and write the metadata
+        in the final target destination.
+        (WebKit::DownloadClient::cancel): Handle the download cancellation
+        here, removing the intermediate file is needed and cancelling the
+        ResourceHandle and the download.
+        (WebKit::DownloadClient::handleResponseLater):
+        (WebKit::Download::cancel): Let the client handle the cancellation.
+
</ins><span class="cx"> 2013-12-30  Carlos Garcia Campos  &lt;cgarcia@igalia.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [SOUP] willSendRequest doesn't work after a redirect
</span></span></pre></div>
<a id="releasesWebKitGTKwebkit22SourceWebKit2SharedDownloadssoupDownloadSoupcpp"></a>
<div class="modfile"><h4>Modified: releases/WebKitGTK/webkit-2.2/Source/WebKit2/Shared/Downloads/soup/DownloadSoup.cpp (162338 => 162339)</h4>
<pre class="diff"><span>
<span class="info">--- releases/WebKitGTK/webkit-2.2/Source/WebKit2/Shared/Downloads/soup/DownloadSoup.cpp        2014-01-20 14:09:21 UTC (rev 162338)
+++ releases/WebKitGTK/webkit-2.2/Source/WebKit2/Shared/Downloads/soup/DownloadSoup.cpp        2014-01-20 14:36:11 UTC (rev 162339)
</span><span class="lines">@@ -59,14 +59,22 @@
</span><span class="cx">             g_source_remove(m_handleResponseLaterID);
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    void deleteIntermediateFileInNeeded()
+    {
+        if (!m_intermediateFile)
+            return;
+        g_file_delete(m_intermediateFile.get(), nullptr, nullptr);
+    }
+
</ins><span class="cx">     void downloadFailed(const ResourceError&amp; error)
</span><span class="cx">     {
</span><ins>+        deleteIntermediateFileInNeeded();
</ins><span class="cx">         m_download-&gt;didFail(error, CoreIPC::DataReference());
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     void didReceiveResponse(ResourceHandle*, const ResourceResponse&amp; response)
</span><span class="cx">     {
</span><del>-        m_response = adoptGRef(response.toSoupMessage());
</del><ins>+        m_response = response;
</ins><span class="cx">         m_download-&gt;didReceiveResponse(response);
</span><span class="cx"> 
</span><span class="cx">         if (response.httpStatusCode() &gt;= 400) {
</span><span class="lines">@@ -83,8 +91,8 @@
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         bool overwrite;
</span><del>-        String destinationURI = m_download-&gt;decideDestinationWithSuggestedFilename(suggestedFilename, overwrite);
-        if (destinationURI.isEmpty()) {
</del><ins>+        m_destinationURI = m_download-&gt;decideDestinationWithSuggestedFilename(suggestedFilename, overwrite);
+        if (m_destinationURI.isEmpty()) {
</ins><span class="cx"> #if PLATFORM(GTK)
</span><span class="cx">             GOwnPtr&lt;char&gt; buffer(g_strdup_printf(_(&quot;Cannot determine destination URI for download with suggested filename %s&quot;), suggestedFilename.utf8().data()));
</span><span class="cx">             String errorMessage = String::fromUTF8(buffer.get());
</span><span class="lines">@@ -95,19 +103,16 @@
</span><span class="cx">             return;
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        GRefPtr&lt;GFile&gt; file = adoptGRef(g_file_new_for_uri(destinationURI.utf8().data()));
</del><ins>+        String intermediateURI = m_destinationURI + &quot;.wkdownload&quot;;
+        m_intermediateFile = adoptGRef(g_file_new_for_uri(intermediateURI.utf8().data()));
</ins><span class="cx">         GOwnPtr&lt;GError&gt; error;
</span><del>-        m_outputStream = adoptGRef(g_file_replace(file.get(), 0, TRUE, G_FILE_CREATE_NONE, 0, &amp;error.outPtr()));
</del><ins>+        m_outputStream = adoptGRef(g_file_replace(m_intermediateFile.get(), 0, TRUE, G_FILE_CREATE_NONE, 0, &amp;error.outPtr()));
</ins><span class="cx">         if (!m_outputStream) {
</span><span class="cx">             downloadFailed(platformDownloadDestinationError(response, error-&gt;message));
</span><span class="cx">             return;
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        GRefPtr&lt;GFileInfo&gt; info = adoptGRef(g_file_info_new());
-        g_file_info_set_attribute_string(info.get(), &quot;metadata::download-uri&quot;, response.url().string().utf8().data());
-        g_file_set_attributes_async(file.get(), info.get(), G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT, 0, 0, 0);
-
-        m_download-&gt;didCreateDestination(destinationURI);
</del><ins>+        m_download-&gt;didCreateDestination(m_destinationURI);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     void didReceiveData(ResourceHandle*, const char* data, int length, int /*encodedDataLength*/)
</span><span class="lines">@@ -121,7 +126,7 @@
</span><span class="cx">         GOwnPtr&lt;GError&gt; error;
</span><span class="cx">         g_output_stream_write_all(G_OUTPUT_STREAM(m_outputStream.get()), data, length, &amp;bytesWritten, 0, &amp;error.outPtr());
</span><span class="cx">         if (error) {
</span><del>-            downloadFailed(platformDownloadDestinationError(ResourceResponse(m_response.get()), error-&gt;message));
</del><ins>+            downloadFailed(platformDownloadDestinationError(m_response, error-&gt;message));
</ins><span class="cx">             return;
</span><span class="cx">         }
</span><span class="cx">         m_download-&gt;didReceiveData(bytesWritten);
</span><span class="lines">@@ -130,6 +135,21 @@
</span><span class="cx">     void didFinishLoading(ResourceHandle*, double)
</span><span class="cx">     {
</span><span class="cx">         m_outputStream = 0;
</span><ins>+
+        ASSERT(m_intermediateFile);
+        GRefPtr&lt;GFile&gt; destinationFile = adoptGRef(g_file_new_for_uri(m_destinationURI.utf8().data()));
+        GOwnPtr&lt;GError&gt; error;
+        if (!g_file_move(m_intermediateFile.get(), destinationFile.get(), G_FILE_COPY_NONE, nullptr, nullptr, nullptr, &amp;error.outPtr())) {
+            downloadFailed(platformDownloadDestinationError(m_response, error-&gt;message));
+            return;
+        }
+
+        GRefPtr&lt;GFileInfo&gt; info = adoptGRef(g_file_info_new());
+        CString uri = m_response.url().string().utf8();
+        g_file_info_set_attribute_string(info.get(), &quot;metadata::download-uri&quot;, uri.data());
+        g_file_info_set_attribute_string(info.get(), &quot;xattr::xdg.origin.url&quot;, uri.data());
+        g_file_set_attributes_async(destinationFile.get(), info.get(), G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT, nullptr, nullptr, nullptr);
+
</ins><span class="cx">         m_download-&gt;didFinish();
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -148,6 +168,13 @@
</span><span class="cx">         notImplemented();
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    void cancel(ResourceHandle* handle)
+    {
+        handle-&gt;cancel();
+        deleteIntermediateFileInNeeded();
+        m_download-&gt;didCancel(CoreIPC::DataReference());
+    }
+
</ins><span class="cx">     void handleResponse()
</span><span class="cx">     {
</span><span class="cx">         m_handleResponseLaterID = 0;
</span><span class="lines">@@ -162,7 +189,7 @@
</span><span class="cx"> 
</span><span class="cx">     void handleResponseLater(const ResourceResponse&amp; response)
</span><span class="cx">     {
</span><del>-        ASSERT(!m_response);
</del><ins>+        ASSERT(m_response.isNull());
</ins><span class="cx">         ASSERT(!m_handleResponseLaterID);
</span><span class="cx"> 
</span><span class="cx">         m_delayedResponse = response;
</span><span class="lines">@@ -174,7 +201,9 @@
</span><span class="cx"> 
</span><span class="cx">     Download* m_download;
</span><span class="cx">     GRefPtr&lt;GFileOutputStream&gt; m_outputStream;
</span><del>-    GRefPtr&lt;SoupMessage&gt; m_response;
</del><ins>+    ResourceResponse m_response;
+    String m_destinationURI;
+    GRefPtr&lt;GFile&gt; m_intermediateFile;
</ins><span class="cx">     ResourceResponse m_delayedResponse;
</span><span class="cx">     unsigned m_handleResponseLaterID;
</span><span class="cx"> };
</span><span class="lines">@@ -203,8 +232,7 @@
</span><span class="cx"> {
</span><span class="cx">     if (!m_resourceHandle)
</span><span class="cx">         return;
</span><del>-    m_resourceHandle-&gt;cancel();
-    didCancel(CoreIPC::DataReference());
</del><ins>+    static_cast&lt;DownloadClient*&gt;(m_downloadClient.get())-&gt;cancel(m_resourceHandle.get());
</ins><span class="cx">     m_resourceHandle = 0;
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="releasesWebKitGTKwebkit22ToolsChangeLog"></a>
<div class="modfile"><h4>Modified: releases/WebKitGTK/webkit-2.2/Tools/ChangeLog (162338 => 162339)</h4>
<pre class="diff"><span>
<span class="info">--- releases/WebKitGTK/webkit-2.2/Tools/ChangeLog        2014-01-20 14:09:21 UTC (rev 162338)
+++ releases/WebKitGTK/webkit-2.2/Tools/ChangeLog        2014-01-20 14:36:11 UTC (rev 162339)
</span><span class="lines">@@ -1,3 +1,28 @@
</span><ins>+2014-01-09  Carlos Garcia Campos  &lt;cgarcia@igalia.com&gt;
+
+        [SOUP] Partial file left on disk after a download fails or is cancelled in WebKit2
+        https://bugs.webkit.org/show_bug.cgi?id=126686
+
+        Reviewed by Martin Robinson.
+
+        Test that partial files are not left on disk after a download has
+        been cancelled after the destination has been decided. To make
+        sure the download is cancelled after the destination has been
+        decided and before the operation finishes, we cancel the download
+        in the destination decided callback, and we use an infinite
+        resource that writes chunks to the response body and never
+        completes the body.
+
+        * TestWebKitAPI/Tests/WebKit2Gtk/TestDownloads.cpp:
+        (addContentDispositionHTTPHeaderToResponse): Helper function to
+        add the Content-Disposition to the response headers.
+        (writeNextChunkIdle): Write next chunk to response body.
+        (writeNextChunk): Write next chunk in an idle to avoid flooding
+        the network with the inifnite resource.
+        (serverCallback): Add an inifinite resource.
+        (testDownloadRemoteFileError): Check that partial file is not
+        present after the download has been cancelled.
+
</ins><span class="cx"> 2014-01-04  Carlos Garcia Campos  &lt;cgarcia@igalia.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [GTK] Move all GTK/GObject unit tests to Tools/TestWebKitAPI
</span></span></pre></div>
<a id="releasesWebKitGTKwebkit22ToolsTestWebKitAPITestsWebKit2GtkTestDownloadscpp"></a>
<div class="modfile"><h4>Modified: releases/WebKitGTK/webkit-2.2/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDownloads.cpp (162338 => 162339)</h4>
<pre class="diff"><span>
<span class="info">--- releases/WebKitGTK/webkit-2.2/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDownloads.cpp        2014-01-20 14:09:21 UTC (rev 162338)
+++ releases/WebKitGTK/webkit-2.2/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDownloads.cpp        2014-01-20 14:36:11 UTC (rev 162339)
</span><span class="lines">@@ -223,13 +223,14 @@
</span><span class="cx">     void receivedResponse(WebKitDownload* download)
</span><span class="cx">     {
</span><span class="cx">         DownloadTest::receivedResponse(download);
</span><del>-        if (m_expectedError == WEBKIT_DOWNLOAD_ERROR_CANCELLED_BY_USER)
-            webkit_download_cancel(download);
</del><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     void createdDestination(WebKitDownload* download, const char* destination)
</span><span class="cx">     {
</span><del>-        g_assert_not_reached();
</del><ins>+        if (m_expectedError == WEBKIT_DOWNLOAD_ERROR_CANCELLED_BY_USER)
+            webkit_download_cancel(download);
+        else
+            g_assert_not_reached();
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     void failed(WebKitDownload* download, GError* error)
</span><span class="lines">@@ -297,6 +298,23 @@
</span><span class="cx"> static WebKitTestServer* kServer;
</span><span class="cx"> static const char* kServerSuggestedFilename = &quot;webkit-downloaded-file&quot;;
</span><span class="cx"> 
</span><ins>+static void addContentDispositionHTTPHeaderToResponse(SoupMessage* message)
+{
+    GOwnPtr&lt;char&gt; contentDisposition(g_strdup_printf(&quot;filename=%s&quot;, kServerSuggestedFilename));
+    soup_message_headers_append(message-&gt;response_headers, &quot;Content-Disposition&quot;, contentDisposition.get());
+}
+
+static gboolean writeNextChunkIdle(SoupMessage* message)
+{
+    soup_message_body_append(message-&gt;response_body, SOUP_MEMORY_STATIC, &quot;chunk&quot;, 5);
+    return FALSE;
+}
+
+static void writeNextChunk(SoupMessage* message)
+{
+    g_timeout_add(100, reinterpret_cast&lt;GSourceFunc&gt;(writeNextChunkIdle), message);
+}
+
</ins><span class="cx"> static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer)
</span><span class="cx"> {
</span><span class="cx">     if (message-&gt;method != SOUP_METHOD_GET) {
</span><span class="lines">@@ -304,6 +322,17 @@
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    soup_message_set_status(message, SOUP_STATUS_OK);
+
+    if (g_str_equal(path, &quot;/cancel-after-destination&quot;)) {
+        // Use an infinite message to make sure it's cancelled before it finishes.
+        soup_message_headers_set_encoding(message-&gt;response_headers, SOUP_ENCODING_CHUNKED);
+        addContentDispositionHTTPHeaderToResponse(message);
+        g_signal_connect(message, &quot;wrote_headers&quot;, G_CALLBACK(writeNextChunk), nullptr);
+        g_signal_connect(message, &quot;wrote_chunk&quot;, G_CALLBACK(writeNextChunk), nullptr);
+        return;
+    }
+
</ins><span class="cx">     GOwnPtr&lt;char&gt; filePath(g_build_filename(Test::getWebKit1TestResoucesDir().data(), path, NULL));
</span><span class="cx">     char* contents;
</span><span class="cx">     gsize contentsLength;
</span><span class="lines">@@ -313,10 +342,7 @@
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    soup_message_set_status(message, SOUP_STATUS_OK);
-
-    GOwnPtr&lt;char&gt; contentDisposition(g_strdup_printf(&quot;filename=%s&quot;, kServerSuggestedFilename));
-    soup_message_headers_append(message-&gt;response_headers, &quot;Content-Disposition&quot;, contentDisposition.get());
</del><ins>+    addContentDispositionHTTPHeaderToResponse(message);
</ins><span class="cx">     soup_message_body_append(message-&gt;response_body, SOUP_MEMORY_TAKE, contents, contentsLength);
</span><span class="cx"> 
</span><span class="cx">     soup_message_body_complete(message-&gt;response_body);
</span><span class="lines">@@ -376,7 +402,7 @@
</span><span class="cx">     test-&gt;checkDestinationAndDeleteFile(download.get(), &quot;bar&quot;);
</span><span class="cx"> 
</span><span class="cx">     test-&gt;m_expectedError = WEBKIT_DOWNLOAD_ERROR_CANCELLED_BY_USER;
</span><del>-    download = adoptGRef(test-&gt;downloadURIAndWaitUntilFinishes(kServer-&gt;getURIForPath(&quot;/test.pdf&quot;)));
</del><ins>+    download = adoptGRef(test-&gt;downloadURIAndWaitUntilFinishes(kServer-&gt;getURIForPath(&quot;/cancel-after-destination&quot;)));
</ins><span class="cx">     g_assert(!webkit_download_get_web_view(download.get()));
</span><span class="cx"> 
</span><span class="cx">     g_assert_cmpint(events.size(), ==, 4);
</span><span class="lines">@@ -386,7 +412,10 @@
</span><span class="cx">     g_assert_cmpint(events[3], ==, DownloadTest::Finished);
</span><span class="cx">     events.clear();
</span><span class="cx">     g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), &lt;, 1);
</span><del>-    test-&gt;checkDestinationAndDeleteFile(download.get(), kServerSuggestedFilename);
</del><ins>+    // Check the intermediate file is deleted when the download is cancelled.
+    GOwnPtr&lt;char&gt; intermediateURI(g_strdup_printf(&quot;%s.wkdownload&quot;, webkit_download_get_destination(download.get())));
+    GRefPtr&lt;GFile&gt; intermediateFile = adoptGRef(g_file_new_for_uri(intermediateURI.get()));
+    g_assert(!g_file_query_exists(intermediateFile.get(), nullptr));
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> class WebViewDownloadTest: public WebViewTest {
</span></span></pre>
</div>
</div>

</body>
</html>