diff options
author | darin@chromium.org <darin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-30 23:05:15 +0000 |
---|---|---|
committer | darin@chromium.org <darin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-30 23:05:15 +0000 |
commit | 21ae1f956c163bc0e3b3e239b5c027a7ac93ae7f (patch) | |
tree | 4eee3fe37f83559b1a97f75dcdef0337b68e2cf1 /chrome | |
parent | 0436fcd542e8ce436fe9395a64cc03fa369276b1 (diff) | |
download | chromium_src-21ae1f956c163bc0e3b3e239b5c027a7ac93ae7f.zip chromium_src-21ae1f956c163bc0e3b3e239b5c027a7ac93ae7f.tar.gz chromium_src-21ae1f956c163bc0e3b3e239b5c027a7ac93ae7f.tar.bz2 |
Asynchronously open the temp file used for Pepper StreamToFile, and delete the
temp file once we are done with it.
We observe ResourceHandle::OnRequestClosed as a signal of when we should delete
the temp file. This corresponds to the WebURLLoader being closed (or
canceled).
This patch also includes some helpers:
base/scoped_callback_factory.h
This class makes it easy to allocate Callbacks that hold a weak reference
back to the owning class. It works just like ScopedRunnableMethodFactory
but for Callbacks instead of RunnableMethods.
base/platform_file.h
Added a PassPlatformFile class that is useful for cases where a callback may
decide not to take ownership of a PlatformFile (as can happen when using
ScopedCallbackFactory).
chrome/file_system_proxy.{h,cc}
This class provides static methods for executing file system commands on the
FILE thread. It routes callbacks back to the originating thread to deliver
results (file handles, etc.). Note: this file declares a few functions that
are not yet used. I anticipate that we'll make use of these and add more
functions here to support the Pepper and OWP FileSystem APIs.
chrome/chrome_thread_relay.{h,cc}
This class is a helper class for proxying calls over to a background
ChromeThread and then returning results to the originating ChromeThread.
R=brettw
BUG=49789
TEST=(more to be added in third_party/ppapi/tests)
Review URL: http://codereview.chromium.org/2878062
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@54402 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/chrome_thread_relay.cc | 26 | ||||
-rw-r--r-- | chrome/browser/chrome_thread_relay.h | 55 | ||||
-rw-r--r-- | chrome/browser/file_system_proxy.cc | 180 | ||||
-rw-r--r-- | chrome/browser/file_system_proxy.h | 49 | ||||
-rw-r--r-- | chrome/browser/renderer_host/redirect_to_file_resource_handler.cc | 58 | ||||
-rw-r--r-- | chrome/browser/renderer_host/redirect_to_file_resource_handler.h | 12 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 4 |
7 files changed, 367 insertions, 17 deletions
diff --git a/chrome/browser/chrome_thread_relay.cc b/chrome/browser/chrome_thread_relay.cc new file mode 100644 index 0000000..e773f1d --- /dev/null +++ b/chrome/browser/chrome_thread_relay.cc @@ -0,0 +1,26 @@ +// 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 "chrome/browser/chrome_thread_relay.h" + +ChromeThreadRelay::ChromeThreadRelay() { + if (!ChromeThread::GetCurrentThreadIdentifier(&origin_thread_id_)) + NOTREACHED() << "Must be created on a valid ChromeThread"; +} + +void ChromeThreadRelay::Start(ChromeThread::ID target_thread_id, + const tracked_objects::Location& from_here) { + ChromeThread::PostTask( + target_thread_id, + from_here, + NewRunnableMethod(this, &ChromeThreadRelay::ProcessOnTargetThread)); +} + +void ChromeThreadRelay::ProcessOnTargetThread() { + RunWork(); + ChromeThread::PostTask( + origin_thread_id_, + FROM_HERE, + NewRunnableMethod(this, &ChromeThreadRelay::RunCallback)); +} diff --git a/chrome/browser/chrome_thread_relay.h b/chrome/browser/chrome_thread_relay.h new file mode 100644 index 0000000..657e587 --- /dev/null +++ b/chrome/browser/chrome_thread_relay.h @@ -0,0 +1,55 @@ +// 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. + +// ChromeThreadRelay provides a convenient way to bounce work to a specific +// ChromeThread and then return results back to the originating thread. +// +// EXAMPLE: +// +// class MyRelay : public ChromeThreadRelay { +// public: +// MyRelay(const Params& params) : params_(params) { +// } +// protected: +// virtual void RunWork() { +// results_ = DoWork(params_); +// } +// virtual void RunCallback() { +// ... use results_ on the originating thread ... +// } +// private: +// Params params_; +// Results_ results_; +// }; + +#ifndef CHROME_BROWSER_CHROME_THREAD_RELAY_H_ +#define CHROME_BROWSER_CHROME_THREAD_RELAY_H_ + +#include "base/ref_counted.h" +#include "chrome/browser/chrome_thread.h" + +class ChromeThreadRelay + : public base::RefCountedThreadSafe<ChromeThreadRelay> { + public: + ChromeThreadRelay(); + + void Start(ChromeThread::ID target_thread_id, + const tracked_objects::Location& from_here); + + protected: + friend class base::RefCountedThreadSafe<ChromeThreadRelay>; + virtual ~ChromeThreadRelay() {} + + // Called to perform work on the FILE thread. + virtual void RunWork() = 0; + + // Called to notify the callback on the origin thread. + virtual void RunCallback() = 0; + + private: + void ProcessOnTargetThread(); + ChromeThread::ID origin_thread_id_; +}; + +#endif // CHROME_BROWSER_CHROME_THREAD_RELAY_H_ diff --git a/chrome/browser/file_system_proxy.cc b/chrome/browser/file_system_proxy.cc new file mode 100644 index 0000000..1cf7b55 --- /dev/null +++ b/chrome/browser/file_system_proxy.cc @@ -0,0 +1,180 @@ +// 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 "chrome/browser/file_system_proxy.h" + +#include "base/file_util.h" +#include "chrome/browser/chrome_thread_relay.h" + +namespace { + +class RelayCreateOrOpen : public ChromeThreadRelay { + public: + RelayCreateOrOpen( + const FilePath& file_path, + int file_flags, + FileSystemProxy::CreateOrOpenCallback* callback) + : file_path_(file_path), + file_flags_(file_flags), + callback_(callback), + file_handle_(base::kInvalidPlatformFileValue), + created_(false) { + DCHECK(callback); + } + + protected: + virtual ~RelayCreateOrOpen() { + if (file_handle_ != base::kInvalidPlatformFileValue) + FileSystemProxy::Close(file_handle_, NULL); + } + + virtual void RunWork() { + file_handle_ = base::CreatePlatformFile(file_path_, file_flags_, &created_); + } + + virtual void RunCallback() { + callback_->Run(base::PassPlatformFile(&file_handle_), created_); + delete callback_; + } + + private: + FilePath file_path_; + int file_flags_; + FileSystemProxy::CreateOrOpenCallback* callback_; + base::PlatformFile file_handle_; + bool created_; +}; + +class RelayCreateTemporary : public ChromeThreadRelay { + public: + explicit RelayCreateTemporary( + FileSystemProxy::CreateTemporaryCallback* callback) + : callback_(callback), + file_handle_(base::kInvalidPlatformFileValue) { + DCHECK(callback); + } + + protected: + virtual ~RelayCreateTemporary() { + if (file_handle_ != base::kInvalidPlatformFileValue) + FileSystemProxy::Close(file_handle_, NULL); + } + + virtual void RunWork() { + // TODO(darin): file_util should have a variant of CreateTemporaryFile + // that returns a FilePath and a PlatformFile. + file_util::CreateTemporaryFile(&file_path_); + + // Use a fixed set of flags that are appropriate for writing to a temporary + // file from the IO thread using a net::FileStream. + int file_flags = + base::PLATFORM_FILE_CREATE_ALWAYS | + base::PLATFORM_FILE_WRITE | + base::PLATFORM_FILE_ASYNC | + base::PLATFORM_FILE_TEMPORARY; + file_handle_ = base::CreatePlatformFile(file_path_, file_flags, NULL); + } + + virtual void RunCallback() { + callback_->Run(base::PassPlatformFile(&file_handle_), file_path_); + delete callback_; + } + + private: + FileSystemProxy::CreateTemporaryCallback* callback_; + base::PlatformFile file_handle_; + FilePath file_path_; +}; + +class RelayWithStatusCallback : public ChromeThreadRelay { + public: + explicit RelayWithStatusCallback(FileSystemProxy::StatusCallback* callback) + : callback_(callback), + succeeded_(false) { + // It is OK for callback to be NULL. + } + + protected: + virtual void RunCallback() { + // The caller may not have been interested in the result. + if (callback_) { + callback_->Run(succeeded_); + delete callback_; + } + } + + void SetStatus(bool succeeded) { succeeded_ = succeeded; } + + private: + FileSystemProxy::StatusCallback* callback_; + bool succeeded_; +}; + +class RelayClose : public RelayWithStatusCallback { + public: + RelayClose(base::PlatformFile file_handle, + FileSystemProxy::StatusCallback* callback) + : RelayWithStatusCallback(callback), + file_handle_(file_handle) { + } + + protected: + virtual void RunWork() { + SetStatus(base::ClosePlatformFile(file_handle_)); + } + + private: + base::PlatformFile file_handle_; +}; + +class RelayDelete : public RelayWithStatusCallback { + public: + RelayDelete(const FilePath& file_path, + bool recursive, + FileSystemProxy::StatusCallback* callback) + : RelayWithStatusCallback(callback), + file_path_(file_path), + recursive_(recursive) { + } + + protected: + virtual void RunWork() { + SetStatus(file_util::Delete(file_path_, recursive_)); + } + + private: + FilePath file_path_; + bool recursive_; +}; + +void Start(const tracked_objects::Location& from_here, + scoped_refptr<ChromeThreadRelay> relay) { + relay->Start(ChromeThread::FILE, from_here); +} + +} // namespace + +void FileSystemProxy::CreateOrOpen(const FilePath& file_path, int file_flags, + CreateOrOpenCallback* callback) { + Start(FROM_HERE, new RelayCreateOrOpen(file_path, file_flags, callback)); +} + +void FileSystemProxy::CreateTemporary(CreateTemporaryCallback* callback) { + Start(FROM_HERE, new RelayCreateTemporary(callback)); +} + +void FileSystemProxy::Close(base::PlatformFile file_handle, + StatusCallback* callback) { + Start(FROM_HERE, new RelayClose(file_handle, callback)); +} + +void FileSystemProxy::Delete(const FilePath& file_path, + StatusCallback* callback) { + Start(FROM_HERE, new RelayDelete(file_path, false, callback)); +} + +void FileSystemProxy::RecursiveDelete(const FilePath& file_path, + StatusCallback* callback) { + Start(FROM_HERE, new RelayDelete(file_path, true, callback)); +} diff --git a/chrome/browser/file_system_proxy.h b/chrome/browser/file_system_proxy.h new file mode 100644 index 0000000..74cd215 --- /dev/null +++ b/chrome/browser/file_system_proxy.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 CHROME_BROWSER_FILE_SYSTEM_PROXY_H_ +#define CHROME_BROWSER_FILE_SYSTEM_PROXY_H_ + +#include "base/callback.h" +#include "base/platform_file.h" +#include "base/ref_counted.h" + +// This class provides asynchronous access to common file routines. +class FileSystemProxy { + public: + // This callback is used by methods that report success with a bool. It is + // valid to pass NULL as the callback parameter to any function that takes a + // StatusCallback, in which case the operation will complete silently. + typedef Callback1<bool /* succeeded */>::Type StatusCallback; + + // Creates or opens a file with the given flags. It is invalid to pass NULL + // for the callback. + typedef Callback2<base::PassPlatformFile, bool /* created */>::Type + CreateOrOpenCallback; + static void CreateOrOpen(const FilePath& file_path, + int file_flags, + CreateOrOpenCallback* callback); + + // Creates a temporary file for writing. The path and an open file handle + // are returned. It is invalid to pass NULL for the callback. + typedef Callback2<base::PassPlatformFile, FilePath>::Type + CreateTemporaryCallback; + static void CreateTemporary(CreateTemporaryCallback* callback); + + // Close the given file handle. + static void Close(base::PlatformFile, StatusCallback* callback); + + // Deletes a file or empty directory. + static void Delete(const FilePath& file_path, + StatusCallback* callback); + + // Deletes a directory and all of its contents. + static void RecursiveDelete(const FilePath& file_path, + StatusCallback* callback); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(FileSystemProxy); +}; + +#endif // CHROME_BROWSER_FILE_SYSTEM_PROXY_H_ diff --git a/chrome/browser/renderer_host/redirect_to_file_resource_handler.cc b/chrome/browser/renderer_host/redirect_to_file_resource_handler.cc index 4f523b7..11beb73 100644 --- a/chrome/browser/renderer_host/redirect_to_file_resource_handler.cc +++ b/chrome/browser/renderer_host/redirect_to_file_resource_handler.cc @@ -5,9 +5,13 @@ #include "chrome/browser/renderer_host/redirect_to_file_resource_handler.h" #include "base/file_util.h" +#include "base/logging.h" #include "base/platform_file.h" +#include "base/task.h" +#include "chrome/browser/file_system_proxy.h" #include "chrome/browser/renderer_host/resource_dispatcher_host.h" #include "chrome/common/resource_response.h" +#include "net/base/file_stream.h" #include "net/base/io_buffer.h" #include "net/base/mime_sniffer.h" #include "net/base/net_errors.h" @@ -19,7 +23,8 @@ RedirectToFileResourceHandler::RedirectToFileResourceHandler( ResourceHandler* next_handler, int process_id, ResourceDispatcherHost* host) - : host_(host), + : callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), + host_(host), next_handler_(next_handler), process_id_(process_id), request_id_(-1), @@ -50,8 +55,7 @@ bool RedirectToFileResourceHandler::OnResponseStarted( int request_id, ResourceResponse* response) { if (response->response_head.status.is_success()) { - // TODO(darin): Move this file creation to a background thread. - file_util::CreateTemporaryFile(&file_path_); + DCHECK(!file_path_.empty()); response->response_head.download_file_path = file_path_; } return next_handler_->OnResponseStarted(request_id, response); @@ -60,6 +64,17 @@ bool RedirectToFileResourceHandler::OnResponseStarted( bool RedirectToFileResourceHandler::OnWillStart(int request_id, const GURL& url, bool* defer) { + request_id_ = request_id; + if (file_path_.empty()) { + // Defer starting the request until we have created the temporary file. + // TODO(darin): This is sub-optimal. We should not delay starting the + // network request like this. + *defer = true; + FileSystemProxy::CreateTemporary( + callback_factory_.NewCallback( + &RedirectToFileResourceHandler::DidCreateTemporaryFile)); + return true; + } return next_handler_->OnWillStart(request_id, url, defer); } @@ -103,18 +118,9 @@ bool RedirectToFileResourceHandler::OnReadCompleted(int request_id, DCHECK(new_offset <= buf_->capacity()); buf_->set_offset(new_offset); - if (!file_stream_.IsOpen()) { - int rv = file_stream_.Open(file_path_, base::PLATFORM_FILE_OPEN | - base::PLATFORM_FILE_WRITE | - base::PLATFORM_FILE_ASYNC); - if (rv != net::OK) - return false; - } - if (BufIsFull()) host_->PauseRequest(process_id_, request_id, true); - request_id_ = request_id; return WriteMore(); } @@ -127,9 +133,30 @@ bool RedirectToFileResourceHandler::OnResponseCompleted( void RedirectToFileResourceHandler::OnRequestClosed() { next_handler_->OnRequestClosed(); + + // The renderer no longer has a WebURLLoader open to this request, so we can + // close and unlink the file. + + // We require this explicit call to Close since file_stream_ was constructed + // directly from a PlatformFile. + file_stream_->Close(); + file_stream_.reset(); + + FileSystemProxy::Delete(file_path_, NULL); } RedirectToFileResourceHandler::~RedirectToFileResourceHandler() { + DCHECK(!file_stream_.get()); +} + +void RedirectToFileResourceHandler::DidCreateTemporaryFile( + base::PassPlatformFile file_handle, + FilePath file_path) { + file_path_ = file_path; + file_stream_.reset(new net::FileStream(file_handle.ReleaseValue(), + base::PLATFORM_FILE_WRITE | + base::PLATFORM_FILE_ASYNC)); + host_->StartDeferredRequest(process_id_, request_id_); } void RedirectToFileResourceHandler::DidWriteToFile(int result) { @@ -148,6 +175,7 @@ void RedirectToFileResourceHandler::DidWriteToFile(int result) { } bool RedirectToFileResourceHandler::WriteMore() { + DCHECK(file_stream_.get()); for (;;) { if (write_cursor_ == buf_->offset()) { // We've caught up to the network load, but it may be in the process of @@ -163,9 +191,9 @@ bool RedirectToFileResourceHandler::WriteMore() { if (write_callback_pending_) return true; DCHECK(write_cursor_ < buf_->offset()); - int rv = file_stream_.Write(buf_->StartOfBuffer() + write_cursor_, - buf_->offset() - write_cursor_, - &write_callback_); + int rv = file_stream_->Write(buf_->StartOfBuffer() + write_cursor_, + buf_->offset() - write_cursor_, + &write_callback_); if (rv == net::ERR_IO_PENDING) { write_callback_pending_ = true; return true; diff --git a/chrome/browser/renderer_host/redirect_to_file_resource_handler.h b/chrome/browser/renderer_host/redirect_to_file_resource_handler.h index f9d2b57..5dd4111 100644 --- a/chrome/browser/renderer_host/redirect_to_file_resource_handler.h +++ b/chrome/browser/renderer_host/redirect_to_file_resource_handler.h @@ -6,14 +6,18 @@ #define CHROME_BROWSER_RENDERER_HOST_REDIRECT_TO_FILE_RESOURCE_HANDLER_H_ #include "base/file_path.h" +#include "base/platform_file.h" #include "base/ref_counted.h" +#include "base/scoped_callback_factory.h" +#include "base/scoped_ptr.h" #include "chrome/browser/renderer_host/resource_handler.h" #include "net/base/completion_callback.h" -#include "net/base/file_stream.h" +class RefCountedPlatformFile; class ResourceDispatcherHost; namespace net { +class FileStream; class GrowableIOBuffer; } @@ -42,10 +46,14 @@ class RedirectToFileResourceHandler : public ResourceHandler { private: virtual ~RedirectToFileResourceHandler(); + void DidCreateTemporaryFile(base::PassPlatformFile file_handle, + FilePath file_path); void DidWriteToFile(int result); bool WriteMore(); bool BufIsFull() const; + base::ScopedCallbackFactory<RedirectToFileResourceHandler> callback_factory_; + ResourceDispatcherHost* host_; scoped_refptr<ResourceHandler> next_handler_; int process_id_; @@ -63,7 +71,7 @@ class RedirectToFileResourceHandler : public ResourceHandler { int write_cursor_; FilePath file_path_; - net::FileStream file_stream_; + scoped_ptr<net::FileStream> file_stream_; net::CompletionCallbackImpl<RedirectToFileResourceHandler> write_callback_; bool write_callback_pending_; diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 1b5ea58..25f9996 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -346,6 +346,8 @@ 'browser/chrome_plugin_host.h', 'browser/chrome_thread.cc', 'browser/chrome_thread.h', + 'browser/chrome_thread_relay.cc', + 'browser/chrome_thread_relay.h', 'browser/chromeos/notifications/balloon_collection_impl.h', 'browser/chromeos/notifications/balloon_collection_impl.cc', 'browser/chromeos/notifications/balloon_view.h', @@ -1390,6 +1392,8 @@ 'browser/file_watcher_inotify.cc', 'browser/file_watcher_mac.cc', 'browser/file_watcher_win.cc', + 'browser/file_system_proxy.cc', + 'browser/file_system_proxy.h', 'browser/find_bar.h', 'browser/find_bar_controller.cc', 'browser/find_bar_controller.h', |