// 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/thread_task_runner_handle.h" #include "content/child/child_thread_impl.h" #include "content/child/fileapi/file_system_dispatcher.h" #include "content/child/worker_task_runner.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 { public: WriterBridge(WebFileWriterImpl::Type type) : request_id_(0), running_on_worker_(WorkerTaskRunner::Instance()->CurrentWorkerId() > 0), task_runner_(running_on_worker_ ? base::ThreadTaskRunnerHandle::Get() : nullptr), 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; 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 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; 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::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 task_runner_; int written_bytes_; scoped_ptr waitable_event_; base::Closure results_closure_; }; WebFileWriterImpl::WebFileWriterImpl( const GURL& path, blink::WebFileWriterClient* client, Type type, const scoped_refptr& 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 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_task_runner_->RunsTasksOnCurrentThread()) { DCHECK(!bridge_->waitable_event()); closure.Run(); return; } main_thread_task_runner_->PostTask(FROM_HERE, closure); if (bridge_->waitable_event()) bridge_->WaitAndRun(); } } // namespace content