// 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 "browser_file_writer.h" #include "cef_thread.h" #include "base/logging.h" #include "base/message_loop_proxy.h" #include "net/url_request/url_request_context.h" #include "webkit/fileapi/file_system_callback_dispatcher.h" #include "webkit/fileapi/file_system_operation.h" #include "webkit/glue/webkit_glue.h" using fileapi::FileSystemOperation; using fileapi::FileSystemCallbackDispatcher; using fileapi::WebFileWriterBase; using WebKit::WebFileWriterClient; using WebKit::WebString; using WebKit::WebURL; URLRequestContext* BrowserFileWriter::request_context_ = NULL; // Helper class to proxy the write and truncate calls to the IO thread, // and to proxy the results back to the main thead. There is a one-to-one // relationship between BrowserFileWriters and IOThreadBackends. class BrowserFileWriter::IOThreadProxy : public base::RefCountedThreadSafe { public: explicit IOThreadProxy(const base::WeakPtr& simple_writer) : simple_writer_(simple_writer) { io_thread_ = CefThread::GetMessageLoopProxyForThread(CefThread::IO); main_thread_ = base::MessageLoopProxy::CreateForCurrentThread(); } virtual ~IOThreadProxy() { } void Truncate(const FilePath& path, int64 offset) { if (!io_thread_->BelongsToCurrentThread()) { io_thread_->PostTask(FROM_HERE, NewRunnableMethod( this, &IOThreadProxy::Truncate, path, offset)); return; } DCHECK(!operation_.get()); operation_.reset(GetNewOperation()); operation_->Truncate(path, offset); } void Write(const FilePath& path, const GURL& blob_url, int64 offset) { if (!io_thread_->BelongsToCurrentThread()) { io_thread_->PostTask(FROM_HERE, NewRunnableMethod( this, &IOThreadProxy::Write, path, blob_url, offset)); return; } DCHECK(request_context_); DCHECK(!operation_.get()); operation_.reset(GetNewOperation()); operation_->Write(request_context_, path, blob_url, offset); } void Cancel() { if (!io_thread_->BelongsToCurrentThread()) { io_thread_->PostTask(FROM_HERE, NewRunnableMethod( this, &IOThreadProxy::Cancel)); return; } if (!operation_.get()) { DidFail(base::PLATFORM_FILE_ERROR_INVALID_OPERATION); return; } cancel_operation_.reset(GetNewOperation()); operation_->Cancel(cancel_operation_.get()); } private: // Inner class to receive callbacks from FileSystemOperation. class CallbackDispatcher : public FileSystemCallbackDispatcher { public: explicit CallbackDispatcher(IOThreadProxy* proxy) : proxy_(proxy) { } virtual void DidSucceed() { proxy_->DidSucceed(); } virtual void DidFail(base::PlatformFileError error_code) { proxy_->DidFail(error_code); } virtual void DidWrite(int64 bytes, bool complete) { proxy_->DidWrite(bytes, complete); } virtual void DidReadMetadata(const base::PlatformFileInfo&) { NOTREACHED(); } virtual void DidReadDirectory( const std::vector& entries, bool has_more) { NOTREACHED(); } virtual void DidOpenFileSystem(const std::string& name, const FilePath& root_path) { NOTREACHED(); } scoped_refptr proxy_; }; FileSystemOperation* GetNewOperation() { // The FileSystemOperation takes ownership of the CallbackDispatcher. return new FileSystemOperation(new CallbackDispatcher(this), io_thread_); } void DidSucceed() { if (!main_thread_->BelongsToCurrentThread()) { operation_.reset(); main_thread_->PostTask(FROM_HERE, NewRunnableMethod( this, &IOThreadProxy::DidSucceed)); return; } if (simple_writer_) simple_writer_->DidSucceed(); } void DidFail(base::PlatformFileError error_code) { if (!main_thread_->BelongsToCurrentThread()) { operation_.reset(); main_thread_->PostTask(FROM_HERE, NewRunnableMethod( this, &IOThreadProxy::DidFail, error_code)); return; } if (simple_writer_) simple_writer_->DidFail(error_code); } void DidWrite(int64 bytes, bool complete) { if (!main_thread_->BelongsToCurrentThread()) { if (complete) operation_.reset(); main_thread_->PostTask(FROM_HERE, NewRunnableMethod( this, &IOThreadProxy::DidWrite, bytes, complete)); return; } if (simple_writer_) simple_writer_->DidWrite(bytes, complete); } scoped_refptr io_thread_; scoped_refptr main_thread_; // Only used on the main thread. base::WeakPtr simple_writer_; // Only used on the io thread. scoped_ptr operation_; scoped_ptr cancel_operation_; }; BrowserFileWriter::BrowserFileWriter( const WebString& path, WebFileWriterClient* client) : WebFileWriterBase(path, client), io_thread_proxy_(new IOThreadProxy(AsWeakPtr())) { } BrowserFileWriter::~BrowserFileWriter() { } void BrowserFileWriter::DoTruncate(const FilePath& path, int64 offset) { io_thread_proxy_->Truncate(path, offset); } void BrowserFileWriter::DoWrite( const FilePath& path, const GURL& blob_url, int64 offset) { io_thread_proxy_->Write(path, blob_url, offset); } void BrowserFileWriter::DoCancel() { io_thread_proxy_->Cancel(); }