diff options
author | asanka <asanka@chromium.org> | 2016-03-25 14:40:57 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-03-25 21:45:28 +0000 |
commit | 6150c52a037d3b05eeac0faaf792ab0203b5580f (patch) | |
tree | d57f03afa4e0008845b018112d5448eae468af73 | |
parent | b1473f4c28417710cb703f87076a01e58c15a1cc (diff) | |
download | chromium_src-6150c52a037d3b05eeac0faaf792ab0203b5580f.zip chromium_src-6150c52a037d3b05eeac0faaf792ab0203b5580f.tar.gz chromium_src-6150c52a037d3b05eeac0faaf792ab0203b5580f.tar.bz2 |
[Downloads] Retain a BlobDataHandle in DownloadUrlParameters.
This allows a caller to extend the lifetime of a blob until the
downloads / resource loader subsystems take over. Without this, an
unfortunately timed revokeObjectURL() call from the originating page may
invalidate the URL before the download has a chance to start the
associated download request.
BUG=595305
Review URL: https://codereview.chromium.org/1829413002
Cr-Commit-Position: refs/heads/master@{#383370}
7 files changed, 77 insertions, 1 deletions
diff --git a/content/browser/download/download_browsertest.cc b/content/browser/download/download_browsertest.cc index 4f1f6f8..19a05a2 100644 --- a/content/browser/download/download_browsertest.cc +++ b/content/browser/download/download_browsertest.cc @@ -2409,6 +2409,18 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, DownloadAttributeInvalidURL) { EXPECT_FALSE(download->CanResume()); } +IN_PROC_BROWSER_TEST_F(DownloadContentTest, DownloadAttributeBlobURL) { + ASSERT_TRUE(embedded_test_server()->Start()); + + GURL document_url = + embedded_test_server()->GetURL("/download/download-attribute-blob.html"); + DownloadItem* download = StartDownloadAndReturnItem(shell(), document_url); + WaitForCompletion(download); + + EXPECT_STREQ(FILE_PATH_LITERAL("suggested-filename.txt"), + download->GetTargetFilePath().BaseName().value().c_str()); +} + // The file empty.bin is served with a MIME type of application/octet-stream. // The content body is empty. Make sure this case is handled properly and we // don't regress on http://crbug.com/320394. diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc index ca423c4..28f1c3f 100644 --- a/content/browser/download/download_manager_impl.cc +++ b/content/browser/download/download_manager_impl.cc @@ -45,6 +45,7 @@ #include "net/base/request_priority.h" #include "net/base/upload_bytes_element_reader.h" #include "net/url_request/url_request_context.h" +#include "storage/browser/blob/blob_url_request_job_factory.h" #include "url/origin.h" namespace content { @@ -58,6 +59,12 @@ scoped_ptr<UrlDownloader, BrowserThread::DeleteOnIOThread> BeginDownload( scoped_ptr<net::URLRequest> url_request = DownloadRequestCore::CreateRequestOnIOThread(download_id, params.get()); + scoped_ptr<storage::BlobDataHandle> blob_data_handle = + params->GetBlobDataHandle(); + if (blob_data_handle) { + storage::BlobProtocolHandler::SetRequestedBlobDataHandle( + url_request.get(), std::move(blob_data_handle)); + } // If there's a valid renderer process associated with the request, then the // request should be driven by the ResourceLoader. Pass it over to the diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc index b2a1e05..1a276f6 100644 --- a/content/browser/loader/resource_dispatcher_host_impl.cc +++ b/content/browser/loader/resource_dispatcher_host_impl.cc @@ -792,7 +792,8 @@ DownloadInterruptReason ResourceDispatcherHostImpl::BeginDownload( extra_info->set_do_not_prompt_for_login(do_not_prompt_for_login); extra_info->AssociateWithRequest(request.get()); // Request takes ownership. - if (request->url().SchemeIs(url::kBlobScheme)) { + if (request->url().SchemeIs(url::kBlobScheme) && + !storage::BlobProtocolHandler::GetRequestBlobDataHandle(request.get())) { ChromeBlobStorageContext* blob_context = GetChromeBlobStorageContextForResourceContext(context); storage::BlobProtocolHandler::SetRequestedBlobDataHandle( diff --git a/content/browser/renderer_host/render_message_filter.cc b/content/browser/renderer_host/render_message_filter.cc index 9035c9f..e6abdca 100644 --- a/content/browser/renderer_host/render_message_filter.cc +++ b/content/browser/renderer_host/render_message_filter.cc @@ -24,6 +24,7 @@ #include "content/browser/dom_storage/dom_storage_context_wrapper.h" #include "content/browser/dom_storage/session_storage_namespace_impl.h" #include "content/browser/download/download_stats.h" +#include "content/browser/fileapi/chrome_blob_storage_context.h" #include "content/browser/gpu/browser_gpu_memory_buffer_manager.h" #include "content/browser/gpu/gpu_data_manager_impl.h" #include "content/browser/gpu/gpu_process_host.h" @@ -33,6 +34,7 @@ #include "content/browser/renderer_host/render_process_host_impl.h" #include "content/browser/renderer_host/render_view_host_delegate.h" #include "content/browser/renderer_host/render_widget_helper.h" +#include "content/browser/resource_context_impl.h" #include "content/common/child_process_host_impl.h" #include "content/common/child_process_messages.h" #include "content/common/content_constants_internal.h" @@ -65,6 +67,7 @@ #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_getter.h" #include "ppapi/shared_impl/file_type_conversion.h" +#include "storage/browser/blob/blob_storage_context.h" #include "ui/gfx/color_profile.h" #include "url/gurl.h" @@ -403,6 +406,16 @@ void RenderMessageFilter::DownloadUrl(int render_view_id, parameters->set_suggested_name(suggested_name); parameters->set_prompt(use_prompt); parameters->set_referrer(referrer); + + if (url.SchemeIsBlob()) { + ChromeBlobStorageContext* blob_context = + GetChromeBlobStorageContextForResourceContext(resource_context_); + parameters->set_blob_data_handle( + blob_context->context()->GetBlobDataFromPublicURL(url)); + // Don't care if the above fails. We are going to let the download go + // through and allow it to be interrupted so that the embedder can deal. + } + BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&DownloadUrlOnUIThread, base::Passed(¶meters))); diff --git a/content/public/browser/download_url_parameters.h b/content/public/browser/download_url_parameters.h index 1f49274..319f158 100644 --- a/content/public/browser/download_url_parameters.h +++ b/content/public/browser/download_url_parameters.h @@ -16,6 +16,7 @@ #include "content/public/browser/download_interrupt_reasons.h" #include "content/public/browser/download_save_info.h" #include "content/public/common/referrer.h" +#include "storage/browser/blob/blob_data_handle.h" #include "url/gurl.h" namespace content { @@ -180,6 +181,19 @@ class CONTENT_EXPORT DownloadUrlParameters { do_not_prompt_for_login_ = do_not_prompt; } + // For downloads of blob URLs, the caller can store a BlobDataHandle in the + // DownloadUrlParameters object so that the blob will remain valid until + // the download starts. The BlobDataHandle will be attached to the associated + // URLRequest. + // + // This is optional. If left unspecified, and the blob URL cannot be mapped to + // a blob by the time the download request starts, then the download will + // fail. + void set_blob_data_handle( + scoped_ptr<storage::BlobDataHandle> blob_data_handle) { + blob_data_handle_ = std::move(blob_data_handle); + } + const OnStartedCallback& callback() const { return callback_; } bool content_initiated() const { return content_initiated_; } const std::string& last_modified() const { return last_modified_; } @@ -211,6 +225,11 @@ class CONTENT_EXPORT DownloadUrlParameters { const GURL& url() const { return url_; } bool do_not_prompt_for_login() const { return do_not_prompt_for_login_; } + // STATE_CHANGING: Return the BlobDataHandle. + scoped_ptr<storage::BlobDataHandle> GetBlobDataHandle() { + return std::move(blob_data_handle_); + } + // STATE CHANGING: All save_info_ sub-objects will be in an indeterminate // state following this call. DownloadSaveInfo GetSaveInfo() { return std::move(save_info_); } @@ -234,6 +253,7 @@ class CONTENT_EXPORT DownloadUrlParameters { DownloadSaveInfo save_info_; GURL url_; bool do_not_prompt_for_login_; + scoped_ptr<storage::BlobDataHandle> blob_data_handle_; DISALLOW_COPY_AND_ASSIGN(DownloadUrlParameters); }; diff --git a/content/test/data/download/download-attribute-blob.html b/content/test/data/download/download-attribute-blob.html new file mode 100644 index 0000000..b114c0d --- /dev/null +++ b/content/test/data/download/download-attribute-blob.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<html> +<body> +<a download="suggested-filename" href="">link</a> + +<!-- In addition to initiating a download using a Blob URL, the script below + will immediately revoke the URL as soon as the click() event has been + dispatched. This tests that, in addition to handling the blob URL, that + proper lifetime extensions are in place to prevent the blob from going away + before the download is complete. --> + +<script> +var anchorElement = document.querySelector('a[download]'); +var blob = new Blob(['hello world!'], {type: 'text/plain'}); +var url = URL.createObjectURL(blob); +anchorElement.href = url; +anchorElement.click(); +URL.revokeObjectURL(url); +</script> +</body> +</html> diff --git a/content/test/data/download/download-attribute-blob.html.mock-http-headers b/content/test/data/download/download-attribute-blob.html.mock-http-headers new file mode 100644 index 0000000..524e3d8 --- /dev/null +++ b/content/test/data/download/download-attribute-blob.html.mock-http-headers @@ -0,0 +1,2 @@ +HTTP/1.1 200 OK +Content-Type: text/html |