summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorasanka <asanka@chromium.org>2016-03-25 14:40:57 -0700
committerCommit bot <commit-bot@chromium.org>2016-03-25 21:45:28 +0000
commit6150c52a037d3b05eeac0faaf792ab0203b5580f (patch)
treed57f03afa4e0008845b018112d5448eae468af73
parentb1473f4c28417710cb703f87076a01e58c15a1cc (diff)
downloadchromium_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}
-rw-r--r--content/browser/download/download_browsertest.cc12
-rw-r--r--content/browser/download/download_manager_impl.cc7
-rw-r--r--content/browser/loader/resource_dispatcher_host_impl.cc3
-rw-r--r--content/browser/renderer_host/render_message_filter.cc13
-rw-r--r--content/public/browser/download_url_parameters.h20
-rw-r--r--content/test/data/download/download-attribute-blob.html21
-rw-r--r--content/test/data/download/download-attribute-blob.html.mock-http-headers2
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(&parameters)));
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