| // Copyright 2013 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 "content/child/fileapi/webfilewriter_impl.h" |
| |
| #include "base/bind.h" |
| #include "base/platform_file.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "content/child/child_thread.h" |
| #include "content/child/fileapi/file_system_dispatcher.h" |
| #include "webkit/child/worker_task_runner.h" |
| |
| using webkit_glue::WorkerTaskRunner; |
| |
| namespace content { |
| |
| namespace { |
| |
| FileSystemDispatcher* GetFileSystemDispatcher() { |
| return ChildThread::current() ? |
| ChildThread::current()->file_system_dispatcher() : NULL; |
| } |
| |
| } // namespace |
| |
| typedef FileSystemDispatcher::StatusCallback StatusCallback; |
| typedef FileSystemDispatcher::WriteCallback WriteCallback; |
| |
| // This instance may be created outside main thread but runs mainly |
| // on main thread. |
| class WebFileWriterImpl::WriterBridge |
| : public base::RefCountedThreadSafe<WriterBridge> { |
| public: |
| WriterBridge(WebFileWriterImpl::Type type) |
| : request_id_(0), |
| thread_id_(WorkerTaskRunner::Instance()->CurrentWorkerId()), |
| written_bytes_(0) { |
| if (type == WebFileWriterImpl::TYPE_SYNC) |
| waitable_event_.reset(new base::WaitableEvent(false, false)); |
| } |
| |
| void Truncate(const GURL& path, int64 offset, |
| const StatusCallback& status_callback) { |
| status_callback_ = status_callback; |
| if (!GetFileSystemDispatcher()) |
| return; |
| ChildThread::current()->file_system_dispatcher()->Truncate( |
| path, offset, &request_id_, |
| base::Bind(&WriterBridge::DidFinish, this)); |
| } |
| |
| void Write(const GURL& path, const std::string& id, int64 offset, |
| const WriteCallback& write_callback, |
| const StatusCallback& error_callback) { |
| write_callback_ = write_callback; |
| status_callback_ = error_callback; |
| if (!GetFileSystemDispatcher()) |
| return; |
| ChildThread::current()->file_system_dispatcher()->Write( |
| path, id, offset, &request_id_, |
| base::Bind(&WriterBridge::DidWrite, this), |
| base::Bind(&WriterBridge::DidFinish, this)); |
| } |
| |
| void Cancel(const StatusCallback& status_callback) { |
| status_callback_ = status_callback; |
| if (!GetFileSystemDispatcher()) |
| return; |
| ChildThread::current()->file_system_dispatcher()->Cancel( |
| request_id_, |
| base::Bind(&WriterBridge::DidFinish, this)); |
| } |
| |
| base::WaitableEvent* waitable_event() { |
| return waitable_event_.get(); |
| } |
| |
| void WaitAndRun() { |
| waitable_event_->Wait(); |
| DCHECK(!results_closure_.is_null()); |
| results_closure_.Run(); |
| } |
| |
| private: |
| friend class base::RefCountedThreadSafe<WriterBridge>; |
| virtual ~WriterBridge() {} |
| |
| void DidWrite(int64 bytes, bool complete) { |
| written_bytes_ += bytes; |
| if (waitable_event_ && !complete) |
| return; |
| PostTaskToWorker(base::Bind(write_callback_, written_bytes_, complete)); |
| } |
| |
| void DidFinish(base::PlatformFileError status) { |
| PostTaskToWorker(base::Bind(status_callback_, status)); |
| } |
| |
| void PostTaskToWorker(const base::Closure& closure) { |
| written_bytes_ = 0; |
| if (!thread_id_) { |
| DCHECK(!waitable_event_); |
| closure.Run(); |
| return; |
| } |
| if (waitable_event_) { |
| results_closure_ = closure; |
| waitable_event_->Signal(); |
| return; |
| } |
| WorkerTaskRunner::Instance()->PostTask(thread_id_, closure); |
| } |
| |
| StatusCallback status_callback_; |
| WriteCallback write_callback_; |
| int request_id_; |
| int thread_id_; |
| int written_bytes_; |
| scoped_ptr<base::WaitableEvent> waitable_event_; |
| base::Closure results_closure_; |
| }; |
| |
| WebFileWriterImpl::WebFileWriterImpl( |
| const GURL& path, WebKit::WebFileWriterClient* client, |
| Type type, |
| base::MessageLoopProxy* main_thread_loop) |
| : WebFileWriterBase(path, client), |
| main_thread_loop_(main_thread_loop), |
| bridge_(new WriterBridge(type)) { |
| } |
| |
| WebFileWriterImpl::~WebFileWriterImpl() { |
| } |
| |
| void WebFileWriterImpl::DoTruncate(const GURL& path, int64 offset) { |
| RunOnMainThread(base::Bind(&WriterBridge::Truncate, bridge_, |
| path, offset, |
| base::Bind(&WebFileWriterImpl::DidFinish, AsWeakPtr()))); |
| } |
| |
| void WebFileWriterImpl::DoWrite( |
| const GURL& path, const std::string& blob_id, int64 offset) { |
| RunOnMainThread(base::Bind(&WriterBridge::Write, bridge_, |
| path, blob_id, offset, |
| base::Bind(&WebFileWriterImpl::DidWrite, AsWeakPtr()), |
| base::Bind(&WebFileWriterImpl::DidFinish, AsWeakPtr()))); |
| } |
| |
| void WebFileWriterImpl::DoCancel() { |
| RunOnMainThread(base::Bind(&WriterBridge::Cancel, bridge_, |
| base::Bind(&WebFileWriterImpl::DidFinish, AsWeakPtr()))); |
| } |
| |
| void WebFileWriterImpl::RunOnMainThread(const base::Closure& closure) { |
| if (main_thread_loop_->RunsTasksOnCurrentThread()) { |
| DCHECK(!bridge_->waitable_event()); |
| closure.Run(); |
| return; |
| } |
| main_thread_loop_->PostTask(FROM_HERE, closure); |
| if (bridge_->waitable_event()) |
| bridge_->WaitAndRun(); |
| } |
| |
| } // namespace content |