diff options
author | michaeln@chromium.org <michaeln@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-23 23:16:45 +0000 |
---|---|---|
committer | michaeln@chromium.org <michaeln@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-23 23:16:45 +0000 |
commit | 145d22229a434580485e7059cf97f2fd5b6f5242 (patch) | |
tree | 5b6bf96cea21ba4e704912077c6de0f4af7b26ed /webkit | |
parent | 508f9b4095e005aaf949f925042d9283c71020a5 (diff) | |
download | chromium_src-145d22229a434580485e7059cf97f2fd5b6f5242.zip chromium_src-145d22229a434580485e7059cf97f2fd5b6f5242.tar.gz chromium_src-145d22229a434580485e7059cf97f2fd5b6f5242.tar.bz2 |
Flesh out URLLoader's download_to_file function.
* tie the lifetime of the resulting temp file to the lifetime of the URLLoader
(the plan is to later extend the lifetime of the temp file to support xhr.responseBlob)
* make it work in test_shell
* make it work for sync requests
* added OnDataDownloaded messages to report progress
A related BlobURL loading change.
* grab a reference to the blob early on to ensure it's still there when the 'job' is finally started.
TEST=manual and deletable_file_reference_unittest.cc
BUG=52486
Review URL: http://codereview.chromium.org/3165062
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@60378 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit')
-rw-r--r-- | webkit/blob/deletable_file_reference.cc | 63 | ||||
-rw-r--r-- | webkit/blob/deletable_file_reference.h | 49 | ||||
-rw-r--r-- | webkit/blob/deletable_file_reference_unittest.cc | 51 | ||||
-rw-r--r-- | webkit/blob/webkit_blob.gypi | 2 | ||||
-rw-r--r-- | webkit/glue/weburlloader_impl.cc | 7 | ||||
-rw-r--r-- | webkit/tools/test_shell/simple_resource_loader_bridge.cc | 56 | ||||
-rw-r--r-- | webkit/tools/test_shell/test_shell.gypi | 1 |
7 files changed, 224 insertions, 5 deletions
diff --git a/webkit/blob/deletable_file_reference.cc b/webkit/blob/deletable_file_reference.cc new file mode 100644 index 0000000..b005eeb --- /dev/null +++ b/webkit/blob/deletable_file_reference.cc @@ -0,0 +1,63 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "webkit/blob/deletable_file_reference.h" + +#include <map> +#include "base/file_util.h" +#include "base/file_util_proxy.h" +#include "base/message_loop_proxy.h" +#include "base/singleton.h" + +namespace webkit_blob { + +namespace { + +typedef std::map<FilePath, DeletableFileReference*> DeleteableFileMap; + +DeleteableFileMap* map() { + return Singleton<DeleteableFileMap>::get(); +} + +} // namespace + +// static +scoped_refptr<DeletableFileReference> DeletableFileReference::Get( + const FilePath& path) { + DeleteableFileMap::iterator found = map()->find(path); + DeletableFileReference* reference = + (found == map()->end()) ? NULL : found->second; + return scoped_refptr<DeletableFileReference>(reference); +} + +// static +scoped_refptr<DeletableFileReference> DeletableFileReference::GetOrCreate( + const FilePath& path, base::MessageLoopProxy* file_thread) { + DCHECK(file_thread); + typedef std::pair<DeleteableFileMap::iterator, bool> InsertResult; + InsertResult result = map()->insert( + DeleteableFileMap::value_type(path, NULL)); + if (result.second == false) + return scoped_refptr<DeletableFileReference>(result.first->second); + + // Wasn't in the map, create a new reference and store the pointer. + scoped_refptr<DeletableFileReference> reference = + new DeletableFileReference(path, file_thread); + result.first->second = reference.get(); + return reference; +} + +DeletableFileReference::DeletableFileReference( + const FilePath& path, base::MessageLoopProxy* file_thread) + : path_(path), file_thread_(file_thread) { + DCHECK(map()->find(path_)->second == NULL); +} + +DeletableFileReference::~DeletableFileReference() { + DCHECK(map()->find(path_)->second == this); + map()->erase(path_); + base::FileUtilProxy::Delete(file_thread_, path_, NULL); +} + +} // namespace webkit_blob diff --git a/webkit/blob/deletable_file_reference.h b/webkit/blob/deletable_file_reference.h new file mode 100644 index 0000000..9578c10 --- /dev/null +++ b/webkit/blob/deletable_file_reference.h @@ -0,0 +1,49 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WEBKIT_BLOB_DELETABLE_FILE_REFERENCE_H_ +#define WEBKIT_BLOB_DELETABLE_FILE_REFERENCE_H_ +#pragma once + +#include "base/file_path.h" +#include "base/ref_counted.h" + +namespace base { +class MessageLoopProxy; +} + +namespace webkit_blob { + +// A refcounted wrapper around a FilePath that schedules the file +// to be deleted upon final release. +class DeletableFileReference : public base::RefCounted<DeletableFileReference> { + public: + // Returns a DeletableFileReference for the given path, if no reference + // for this path exists returns NULL. + static scoped_refptr<DeletableFileReference> Get(const FilePath& path); + + // Returns a DeletableFileReference for the given path, creating a new + // reference if none yet exists. + static scoped_refptr<DeletableFileReference> GetOrCreate( + const FilePath& path, base::MessageLoopProxy* file_thread); + + // The full file path. + const FilePath& path() const { return path_; } + + private: + friend class base::RefCounted<DeletableFileReference>; + + DeletableFileReference( + const FilePath& path, base::MessageLoopProxy* file_thread); + ~DeletableFileReference(); + + const FilePath path_; + scoped_refptr<base::MessageLoopProxy> file_thread_; + + DISALLOW_COPY_AND_ASSIGN(DeletableFileReference); +}; + +} // namespace webkit_blob + +#endif // BASE_DELETABLE_FILE_REFERENCE_H_ diff --git a/webkit/blob/deletable_file_reference_unittest.cc b/webkit/blob/deletable_file_reference_unittest.cc new file mode 100644 index 0000000..4a92612 --- /dev/null +++ b/webkit/blob/deletable_file_reference_unittest.cc @@ -0,0 +1,51 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "webkit/blob/deletable_file_reference.h" + +#include "base/file_util.h" +#include "base/message_loop.h" +#include "base/message_loop_proxy.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace webkit_blob { + +TEST(DeletableFileReferenceTest, TestReferences) { + scoped_refptr<base::MessageLoopProxy> loop_proxy = + base::MessageLoopProxy::CreateForCurrentThread(); + + // Create a file. + FilePath file; + file_util::CreateTemporaryFile(&file); + EXPECT_TRUE(file_util::PathExists(file)); + + // Create a first reference to that file. + scoped_refptr<DeletableFileReference> reference1; + reference1 = DeletableFileReference::Get(file); + EXPECT_FALSE(reference1.get()); + reference1 = DeletableFileReference::GetOrCreate(file, loop_proxy); + EXPECT_TRUE(reference1.get()); + EXPECT_TRUE(file == reference1->path()); + + // Get a second reference to that file. + scoped_refptr<DeletableFileReference> reference2; + reference2 = DeletableFileReference::Get(file); + EXPECT_EQ(reference1.get(), reference2.get()); + reference2 = DeletableFileReference::GetOrCreate(file, loop_proxy); + EXPECT_EQ(reference1.get(), reference2.get()); + + // Drop the first reference, the file and reference should still be there. + reference1 = NULL; + EXPECT_TRUE(DeletableFileReference::Get(file).get()); + MessageLoop::current()->RunAllPending(); + EXPECT_TRUE(file_util::PathExists(file)); + + // Drop the second reference, the file and reference should get deleted. + reference2 = NULL; + EXPECT_FALSE(DeletableFileReference::Get(file).get()); + MessageLoop::current()->RunAllPending(); + EXPECT_FALSE(file_util::PathExists(file)); +} + +} // namespace webkit_blob diff --git a/webkit/blob/webkit_blob.gypi b/webkit/blob/webkit_blob.gypi index e057efb..eb4b3be 100644 --- a/webkit/blob/webkit_blob.gypi +++ b/webkit/blob/webkit_blob.gypi @@ -20,6 +20,8 @@ 'blob_storage_controller.h', 'blob_url_request_job.cc', 'blob_url_request_job.h', + 'deletable_file_reference.cc', + 'deletable_file_reference.h', ], 'conditions': [ ['inside_chromium_build==0', { diff --git a/webkit/glue/weburlloader_impl.cc b/webkit/glue/weburlloader_impl.cc index 528626e..aa63fc2 100644 --- a/webkit/glue/weburlloader_impl.cc +++ b/webkit/glue/weburlloader_impl.cc @@ -284,6 +284,7 @@ class WebURLLoaderImpl::Context : public base::RefCounted<Context>, scoped_ptr<ResourceLoaderBridge> bridge_; scoped_ptr<FtpDirectoryListingResponseDelegate> ftp_listing_delegate_; scoped_ptr<MultipartResponseDelegate> multipart_delegate_; + scoped_ptr<ResourceLoaderBridge> completed_bridge_; // TODO(japhet): Storing this is a temporary hack for site isolation logging. WebURL response_url_; @@ -598,8 +599,10 @@ void WebURLLoaderImpl::Context::OnCompletedRequest( multipart_delegate_.reset(NULL); } - // Prevent any further IPC to the browser now that we're complete. - bridge_.reset(); + // Prevent any further IPC to the browser now that we're complete, but + // don't delete it to keep any downloaded temp files alive. + DCHECK(!completed_bridge_.get()); + completed_bridge_.swap(bridge_); if (client_) { if (status.status() != URLRequestStatus::SUCCESS) { diff --git a/webkit/tools/test_shell/simple_resource_loader_bridge.cc b/webkit/tools/test_shell/simple_resource_loader_bridge.cc index acffef9..1feb389 100644 --- a/webkit/tools/test_shell/simple_resource_loader_bridge.cc +++ b/webkit/tools/test_shell/simple_resource_loader_bridge.cc @@ -33,8 +33,10 @@ #include "webkit/tools/test_shell/simple_resource_loader_bridge.h" #include "base/file_path.h" +#include "base/file_util.h" #include "base/logging.h" #include "base/message_loop.h" +#include "base/message_loop_proxy.h" #if defined(OS_MACOSX) || defined(OS_WIN) #include "base/nss_util.h" #endif @@ -44,6 +46,7 @@ #include "base/thread.h" #include "base/waitable_event.h" #include "net/base/cookie_store.h" +#include "net/base/file_stream.h" #include "net/base/io_buffer.h" #include "net/base/load_flags.h" #include "net/base/net_errors.h" @@ -62,6 +65,7 @@ #include "webkit/appcache/appcache_interfaces.h" #include "webkit/blob/blob_storage_controller.h" #include "webkit/blob/blob_url_request_job.h" +#include "webkit/blob/deletable_file_reference.h" #include "webkit/glue/resource_loader_bridge.h" #include "webkit/tools/test_shell/simple_appcache_system.h" #include "webkit/tools/test_shell/simple_socket_stream_bridge.h" @@ -71,6 +75,7 @@ using webkit_glue::ResourceLoaderBridge; using net::StaticCookiePolicy; using net::HttpResponseHeaders; +using webkit_blob::DeletableFileReference; namespace { @@ -174,6 +179,7 @@ struct RequestParams { int load_flags; ResourceType::Type request_type; int appcache_host_id; + bool download_to_file; scoped_refptr<net::UploadData> upload; }; @@ -188,7 +194,8 @@ class RequestProxy : public URLRequest::Delegate, public: // Takes ownership of the params. RequestProxy() - : buf_(new net::IOBuffer(kDataSize)), + : download_to_file_(false), + buf_(new net::IOBuffer(kDataSize)), last_upload_position_(0) { } @@ -267,6 +274,17 @@ class RequestProxy : public URLRequest::Delegate, peer_->OnReceivedData(buf_copy.get(), bytes_read); } + void NotifyDownloadedData(int bytes_read) { + if (!peer_) + return; + + // Continue reading more data, see the comment in NotifyReceivedData. + g_io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + this, &RequestProxy::AsyncReadData)); + + peer_->OnDownloadedData(bytes_read); + } + void NotifyCompletedRequest(const URLRequestStatus& status, const std::string& security_info, const base::Time& complete_time) { @@ -306,6 +324,17 @@ class RequestProxy : public URLRequest::Delegate, SimpleAppCacheSystem::SetExtraRequestInfo( request_.get(), params->appcache_host_id, params->request_type); + download_to_file_ = params->download_to_file; + if (download_to_file_) { + FilePath path; + if (file_util::CreateTemporaryFile(&path)) { + downloaded_file_ = DeletableFileReference::GetOrCreate( + path, base::MessageLoopProxy::CreateForCurrentThread()); + file_stream_.Open( + path, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE); + } + } + request_->Start(); if (request_->has_upload() && @@ -377,6 +406,13 @@ class RequestProxy : public URLRequest::Delegate, } virtual void OnReceivedData(int bytes_read) { + if (download_to_file_) { + file_stream_.Write(buf_->data(), bytes_read, NULL); + owner_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &RequestProxy::NotifyDownloadedData, bytes_read)); + return; + } + owner_loop_->PostTask(FROM_HERE, NewRunnableMethod( this, &RequestProxy::NotifyReceivedData, bytes_read)); } @@ -384,6 +420,8 @@ class RequestProxy : public URLRequest::Delegate, virtual void OnCompletedRequest(const URLRequestStatus& status, const std::string& security_info, const base::Time& complete_time) { + if (download_to_file_) + file_stream_.Close(); owner_loop_->PostTask(FROM_HERE, NewRunnableMethod( this, &RequestProxy::NotifyCompletedRequest, @@ -486,6 +524,8 @@ class RequestProxy : public URLRequest::Delegate, request->GetMimeType(&info->mime_type); request->GetCharset(&info->charset); info->content_length = request->GetExpectedContentSize(); + if (downloaded_file_) + info->download_file_path = downloaded_file_->path(); SimpleAppCacheSystem::GetExtraResponseInfo( request, &info->appcache_id, @@ -494,6 +534,11 @@ class RequestProxy : public URLRequest::Delegate, scoped_ptr<URLRequest> request_; + // Support for request.download_to_file behavior. + bool download_to_file_; + net::FileStream file_stream_; + scoped_refptr<DeletableFileReference> downloaded_file_; + // Size of our async IO data buffers static const int kDataSize = 16*1024; @@ -553,13 +598,18 @@ class SyncRequestProxy : public RequestProxy { } virtual void OnReceivedData(int bytes_read) { - result_->data.append(buf_->data(), bytes_read); + if (download_to_file_) + file_stream_.Write(buf_->data(), bytes_read, NULL); + else + result_->data.append(buf_->data(), bytes_read); AsyncReadData(); // read more (may recurse) } virtual void OnCompletedRequest(const URLRequestStatus& status, const std::string& security_info, const base::Time& complete_time) { + if (download_to_file_) + file_stream_.Close(); result_->status = status; event_.Signal(); } @@ -577,7 +627,6 @@ class ResourceLoaderBridgeImpl : public ResourceLoaderBridge { const webkit_glue::ResourceLoaderBridge::RequestInfo& request_info) : params_(new RequestParams), proxy_(NULL) { - DCHECK(!request_info.download_to_file); // Not implemented yet! params_->method = request_info.method; params_->url = request_info.url; params_->first_party_for_cookies = request_info.first_party_for_cookies; @@ -586,6 +635,7 @@ class ResourceLoaderBridgeImpl : public ResourceLoaderBridge { params_->load_flags = request_info.load_flags; params_->request_type = request_info.request_type; params_->appcache_host_id = request_info.appcache_host_id; + params_->download_to_file = request_info.download_to_file; } virtual ~ResourceLoaderBridgeImpl() { diff --git a/webkit/tools/test_shell/test_shell.gypi b/webkit/tools/test_shell/test_shell.gypi index 3f23600..e047d37 100644 --- a/webkit/tools/test_shell/test_shell.gypi +++ b/webkit/tools/test_shell/test_shell.gypi @@ -370,6 +370,7 @@ '../../appcache/mock_appcache_storage_unittest.cc', '../../blob/blob_storage_controller_unittest.cc', '../../blob/blob_url_request_job_unittest.cc', + '../../blob/deletable_file_reference_unittest.cc', '../../database/databases_table_unittest.cc', '../../database/database_tracker_unittest.cc', '../../database/database_util_unittest.cc', |