Changeset 161555 in webkit
- Timestamp:
- Jan 9, 2014 8:08:48 AM (10 years ago)
- Location:
- trunk
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebKit2/ChangeLog
r161548 r161555 1 2014-01-09 Carlos Garcia Campos <cgarcia@igalia.com> 2 3 [SOUP] Partial file left on disk after a download fails or is cancelled in WebKit2 4 https://bugs.webkit.org/show_bug.cgi?id=126686 5 6 Reviewed by Martin Robinson. 7 8 We are currently writing the downloads directly into the 9 destination, and when a download fails or is cancelled after the 10 destination has been decided, the partial file is left on the 11 disk. Deleting the final file is not safe because there might be a 12 race condition, so we can use an intermediate file like other 13 browsers do, a file in the same directory than the target 14 destination but with .wkdownload suffix, that is removed when the 15 download fails or is cancelled. If the download finishes 16 successfully the intermediate file is renamed to the final 17 destination. 18 19 * Shared/Downloads/soup/DownloadSoup.cpp: 20 (WebKit::DownloadClient::deleteIntermediateFileInNeeded): Delete 21 the intermdiate file if it's been created already. 22 (WebKit::DownloadClient::downloadFailed): Call deleteIntermediateFileInNeeded. 23 (WebKit::DownloadClient::didReceiveResponse): Do not create a 24 SoupMessage for the given ResourceResponse that is not used, cache 25 the ResourceResponse instead. Create the intermediate file and use 26 it instead of the final destination. 27 (WebKit::DownloadClient::didReceiveData): Use the cached 28 ResourceResponse directly. 29 (WebKit::DownloadClient::didFinishLoading): Rename the 30 intermediate file to the final destination and write the metadata 31 in the final target destination. 32 (WebKit::DownloadClient::cancel): Handle the download cancellation 33 here, removing the intermediate file is needed and cancelling the 34 ResourceHandle and the download. 35 (WebKit::DownloadClient::handleResponseLater): 36 (WebKit::Download::cancel): Let the client handle the cancellation. 37 1 38 2014-01-08 Jinwoo Song <jinwoo7.song@samsung.com> 2 39 -
trunk/Source/WebKit2/Shared/Downloads/soup/DownloadSoup.cpp
r161536 r161555 60 60 } 61 61 62 void deleteIntermediateFileInNeeded() 63 { 64 if (!m_intermediateFile) 65 return; 66 g_file_delete(m_intermediateFile.get(), nullptr, nullptr); 67 } 68 62 69 void downloadFailed(const ResourceError& error) 63 70 { 71 deleteIntermediateFileInNeeded(); 64 72 m_download->didFail(error, IPC::DataReference()); 65 73 } … … 67 75 void didReceiveResponse(ResourceHandle*, const ResourceResponse& response) 68 76 { 69 m_response = adoptGRef(response.toSoupMessage());77 m_response = response; 70 78 m_download->didReceiveResponse(response); 71 79 … … 84 92 85 93 bool overwrite; 86 StringdestinationURI = m_download->decideDestinationWithSuggestedFilename(suggestedFilename, overwrite);87 if ( destinationURI.isEmpty()) {94 m_destinationURI = m_download->decideDestinationWithSuggestedFilename(suggestedFilename, overwrite); 95 if (m_destinationURI.isEmpty()) { 88 96 #if PLATFORM(GTK) 89 97 GOwnPtr<char> buffer(g_strdup_printf(_("Cannot determine destination URI for download with suggested filename %s"), suggestedFilename.utf8().data())); … … 96 104 } 97 105 98 GRefPtr<GFile> file = adoptGRef(g_file_new_for_uri(destinationURI.utf8().data())); 106 String intermediateURI = m_destinationURI + ".wkdownload"; 107 m_intermediateFile = adoptGRef(g_file_new_for_uri(intermediateURI.utf8().data())); 99 108 GOwnPtr<GError> error; 100 m_outputStream = adoptGRef(g_file_replace( file.get(), 0, TRUE, G_FILE_CREATE_NONE, 0, &error.outPtr()));109 m_outputStream = adoptGRef(g_file_replace(m_intermediateFile.get(), 0, TRUE, G_FILE_CREATE_NONE, 0, &error.outPtr())); 101 110 if (!m_outputStream) { 102 111 downloadFailed(platformDownloadDestinationError(response, error->message)); … … 104 113 } 105 114 106 GRefPtr<GFileInfo> info = adoptGRef(g_file_info_new()); 107 const char* uri = response.url().string().utf8().data(); 108 g_file_info_set_attribute_string(info.get(), "metadata::download-uri", uri); 109 g_file_info_set_attribute_string(info.get(), "xattr::xdg.origin.url", uri); 110 g_file_set_attributes_async(file.get(), info.get(), G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT, 0, 0, 0); 111 112 m_download->didCreateDestination(destinationURI); 115 m_download->didCreateDestination(m_destinationURI); 113 116 } 114 117 … … 124 127 g_output_stream_write_all(G_OUTPUT_STREAM(m_outputStream.get()), data, length, &bytesWritten, 0, &error.outPtr()); 125 128 if (error) { 126 downloadFailed(platformDownloadDestinationError( ResourceResponse(m_response.get()), error->message));129 downloadFailed(platformDownloadDestinationError(m_response, error->message)); 127 130 return; 128 131 } … … 133 136 { 134 137 m_outputStream = 0; 138 139 ASSERT(m_intermediateFile); 140 GRefPtr<GFile> destinationFile = adoptGRef(g_file_new_for_uri(m_destinationURI.utf8().data())); 141 GOwnPtr<GError> error; 142 if (!g_file_move(m_intermediateFile.get(), destinationFile.get(), G_FILE_COPY_NONE, nullptr, nullptr, nullptr, &error.outPtr())) { 143 downloadFailed(platformDownloadDestinationError(m_response, error->message)); 144 return; 145 } 146 147 GRefPtr<GFileInfo> info = adoptGRef(g_file_info_new()); 148 CString uri = m_response.url().string().utf8(); 149 g_file_info_set_attribute_string(info.get(), "metadata::download-uri", uri.data()); 150 g_file_info_set_attribute_string(info.get(), "xattr::xdg.origin.url", uri.data()); 151 g_file_set_attributes_async(destinationFile.get(), info.get(), G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT, nullptr, nullptr, nullptr); 152 135 153 m_download->didFinish(); 136 154 } … … 149 167 { 150 168 notImplemented(); 169 } 170 171 void cancel(ResourceHandle* handle) 172 { 173 handle->cancel(); 174 deleteIntermediateFileInNeeded(); 175 m_download->didCancel(IPC::DataReference()); 151 176 } 152 177 … … 165 190 void handleResponseLater(const ResourceResponse& response) 166 191 { 167 ASSERT( !m_response);192 ASSERT(m_response.isNull()); 168 193 ASSERT(!m_handleResponseLaterID); 169 194 … … 177 202 Download* m_download; 178 203 GRefPtr<GFileOutputStream> m_outputStream; 179 GRefPtr<SoupMessage> m_response; 204 ResourceResponse m_response; 205 String m_destinationURI; 206 GRefPtr<GFile> m_intermediateFile; 180 207 ResourceResponse m_delayedResponse; 181 208 unsigned m_handleResponseLaterID; … … 206 233 if (!m_resourceHandle) 207 234 return; 208 m_resourceHandle->cancel(); 209 didCancel(IPC::DataReference()); 235 static_cast<DownloadClient*>(m_downloadClient.get())->cancel(m_resourceHandle.get()); 210 236 m_resourceHandle = 0; 211 237 } -
trunk/Tools/ChangeLog
r161552 r161555 1 2014-01-09 Carlos Garcia Campos <cgarcia@igalia.com> 2 3 [SOUP] Partial file left on disk after a download fails or is cancelled in WebKit2 4 https://bugs.webkit.org/show_bug.cgi?id=126686 5 6 Reviewed by Martin Robinson. 7 8 Test that partial files are not left on disk after a download has 9 been cancelled after the destination has been decided. To make 10 sure the download is cancelled after the destination has been 11 decided and before the operation finishes, we cancel the download 12 in the destination decided callback, and we use an infinite 13 resource that writes chunks to the response body and never 14 completes the body. 15 16 * TestWebKitAPI/Tests/WebKit2Gtk/TestDownloads.cpp: 17 (addContentDispositionHTTPHeaderToResponse): Helper function to 18 add the Content-Disposition to the response headers. 19 (writeNextChunkIdle): Write next chunk to response body. 20 (writeNextChunk): Write next chunk in an idle to avoid flooding 21 the network with the inifnite resource. 22 (serverCallback): Add an inifinite resource. 23 (testDownloadRemoteFileError): Check that partial file is not 24 present after the download has been cancelled. 25 1 26 2014-01-09 Roland Takacs <rtakacs@inf.u-szeged.hu> 2 27 -
trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestDownloads.cpp
r161366 r161555 224 224 { 225 225 DownloadTest::receivedResponse(download); 226 } 227 228 void createdDestination(WebKitDownload* download, const char* destination) 229 { 226 230 if (m_expectedError == WEBKIT_DOWNLOAD_ERROR_CANCELLED_BY_USER) 227 231 webkit_download_cancel(download); 228 } 229 230 void createdDestination(WebKitDownload* download, const char* destination) 231 { 232 g_assert_not_reached(); 232 else 233 g_assert_not_reached(); 233 234 } 234 235 … … 298 299 static const char* kServerSuggestedFilename = "webkit-downloaded-file"; 299 300 301 static void addContentDispositionHTTPHeaderToResponse(SoupMessage* message) 302 { 303 GOwnPtr<char> contentDisposition(g_strdup_printf("filename=%s", kServerSuggestedFilename)); 304 soup_message_headers_append(message->response_headers, "Content-Disposition", contentDisposition.get()); 305 } 306 307 static gboolean writeNextChunkIdle(SoupMessage* message) 308 { 309 soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, "chunk", 5); 310 return FALSE; 311 } 312 313 static void writeNextChunk(SoupMessage* message) 314 { 315 g_timeout_add(100, reinterpret_cast<GSourceFunc>(writeNextChunkIdle), message); 316 } 317 300 318 static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer) 301 319 { 302 320 if (message->method != SOUP_METHOD_GET) { 303 321 soup_message_set_status(message, SOUP_STATUS_NOT_IMPLEMENTED); 322 return; 323 } 324 325 soup_message_set_status(message, SOUP_STATUS_OK); 326 327 if (g_str_equal(path, "/cancel-after-destination")) { 328 // Use an infinite message to make sure it's cancelled before it finishes. 329 soup_message_headers_set_encoding(message->response_headers, SOUP_ENCODING_CHUNKED); 330 addContentDispositionHTTPHeaderToResponse(message); 331 g_signal_connect(message, "wrote_headers", G_CALLBACK(writeNextChunk), nullptr); 332 g_signal_connect(message, "wrote_chunk", G_CALLBACK(writeNextChunk), nullptr); 304 333 return; 305 334 } … … 314 343 } 315 344 316 soup_message_set_status(message, SOUP_STATUS_OK); 317 318 GOwnPtr<char> contentDisposition(g_strdup_printf("filename=%s", kServerSuggestedFilename)); 319 soup_message_headers_append(message->response_headers, "Content-Disposition", contentDisposition.get()); 345 addContentDispositionHTTPHeaderToResponse(message); 320 346 soup_message_body_append(message->response_body, SOUP_MEMORY_TAKE, contents, contentsLength); 321 347 … … 377 403 378 404 test->m_expectedError = WEBKIT_DOWNLOAD_ERROR_CANCELLED_BY_USER; 379 download = adoptGRef(test->downloadURIAndWaitUntilFinishes(kServer->getURIForPath("/ test.pdf")));405 download = adoptGRef(test->downloadURIAndWaitUntilFinishes(kServer->getURIForPath("/cancel-after-destination"))); 380 406 g_assert(!webkit_download_get_web_view(download.get())); 381 407 … … 387 413 events.clear(); 388 414 g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), <, 1); 389 test->checkDestinationAndDeleteFile(download.get(), kServerSuggestedFilename); 415 // Check the intermediate file is deleted when the download is cancelled. 416 GOwnPtr<char> intermediateURI(g_strdup_printf("%s.wkdownload", webkit_download_get_destination(download.get()))); 417 GRefPtr<GFile> intermediateFile = adoptGRef(g_file_new_for_uri(intermediateURI.get())); 418 g_assert(!g_file_query_exists(intermediateFile.get(), nullptr)); 390 419 } 391 420
Note: See TracChangeset
for help on using the changeset viewer.