| // 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/synchronization/waitable_event.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "content/child/child_thread_impl.h" |
| #include "content/child/fileapi/file_system_dispatcher.h" |
| #include "content/public/child/worker_thread.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| FileSystemDispatcher* GetFileSystemDispatcher() { |
| return ChildThreadImpl::current() ? |
| ChildThreadImpl::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), |
| running_on_worker_(WorkerThread::GetCurrentId() > 0), |
| task_runner_(running_on_worker_ ? base::ThreadTaskRunnerHandle::Get() |
| : nullptr), |
| written_bytes_(0) { |
| if (type == WebFileWriterImpl::TYPE_SYNC) { |
| waitable_event_.reset(new base::WaitableEvent( |
| base::WaitableEvent::ResetPolicy::AUTOMATIC, |
| base::WaitableEvent::InitialState::NOT_SIGNALED)); |
| } |
| } |
| |
| void Truncate(const GURL& path, |
| int64_t offset, |
| const StatusCallback& status_callback) { |
| status_callback_ = status_callback; |
| if (!GetFileSystemDispatcher()) |
| return; |
| ChildThreadImpl::current()->file_system_dispatcher()->Truncate( |
| path, offset, &request_id_, |
| base::Bind(&WriterBridge::DidFinish, this)); |
| } |
| |
| void Write(const GURL& path, |
| const std::string& id, |
| int64_t offset, |
| const WriteCallback& write_callback, |
| const StatusCallback& error_callback) { |
| write_callback_ = write_callback; |
| status_callback_ = error_callback; |
| if (!GetFileSystemDispatcher()) |
| return; |
| ChildThreadImpl::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; |
| ChildThreadImpl::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_t bytes, bool complete) { |
| written_bytes_ += bytes; |
| if (waitable_event_ && !complete) |
| return; |
| PostTaskToWorker(base::Bind(write_callback_, written_bytes_, complete)); |
| } |
| |
| void DidFinish(base::File::Error status) { |
| PostTaskToWorker(base::Bind(status_callback_, status)); |
| } |
| |
| void PostTaskToWorker(const base::Closure& closure) { |
| written_bytes_ = 0; |
| if (!running_on_worker_) { |
| DCHECK(!waitable_event_); |
| closure.Run(); |
| return; |
| } |
| DCHECK(task_runner_); |
| if (waitable_event_) { |
| results_closure_ = closure; |
| waitable_event_->Signal(); |
| return; |
| } |
| task_runner_->PostTask(FROM_HERE, closure); |
| } |
| |
| StatusCallback status_callback_; |
| WriteCallback write_callback_; |
| int request_id_; |
| const bool running_on_worker_; |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner_; |
| int written_bytes_; |
| std::unique_ptr<base::WaitableEvent> waitable_event_; |
| base::Closure results_closure_; |
| }; |
| |
| WebFileWriterImpl::WebFileWriterImpl( |
| const GURL& path, blink::WebFileWriterClient* client, |
| Type type, |
| const scoped_refptr<base::SingleThreadTaskRunner>& main_thread_task_runner) |
| : WebFileWriterBase(path, client), |
| main_thread_task_runner_(main_thread_task_runner), |
| bridge_(new WriterBridge(type)) { |
| } |
| |
| WebFileWriterImpl::~WebFileWriterImpl() { |
| } |
| |
| void WebFileWriterImpl::DoTruncate(const GURL& path, int64_t 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_t 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_task_runner_->RunsTasksInCurrentSequence()) { |
| DCHECK(!bridge_->waitable_event()); |
| closure.Run(); |
| return; |
| } |
| main_thread_task_runner_->PostTask(FROM_HERE, closure); |
| if (bridge_->waitable_event()) |
| bridge_->WaitAndRun(); |
| } |
| |
| } // namespace content |