From 2687ad7d62c737430aa780f9db9a0a166931a735 Mon Sep 17 00:00:00 2001 From: "michaeln@chromium.org" Date: Thu, 14 Oct 2010 00:44:08 +0000 Subject: SimpleFileWriter for test_shell and DRT. BUG=none TEST=manual Review URL: http://codereview.chromium.org/3683004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@62486 0039d316-1c4b-4281-b951-d872f2087c98 --- webkit/fileapi/file_system_callback_dispatcher.h | 2 + webkit/fileapi/webfilewriter_base.cc | 145 ++++++++++++++++ webkit/fileapi/webfilewriter_base.h | 69 ++++++++ webkit/fileapi/webkit_fileapi.gypi | 2 + webkit/support/webkit_support.gypi | 2 + webkit/tools/test_shell/simple_file_system.cc | 13 +- webkit/tools/test_shell/simple_file_system.h | 2 + webkit/tools/test_shell/simple_file_writer.cc | 186 +++++++++++++++++++++ webkit/tools/test_shell/simple_file_writer.h | 45 +++++ .../test_shell/simple_resource_loader_bridge.cc | 3 + webkit/tools/test_shell/test_webview_delegate.cc | 1 + 11 files changed, 469 insertions(+), 1 deletion(-) create mode 100644 webkit/fileapi/webfilewriter_base.cc create mode 100644 webkit/fileapi/webfilewriter_base.h create mode 100644 webkit/tools/test_shell/simple_file_writer.cc create mode 100644 webkit/tools/test_shell/simple_file_writer.h (limited to 'webkit') diff --git a/webkit/fileapi/file_system_callback_dispatcher.h b/webkit/fileapi/file_system_callback_dispatcher.h index e33dd69..ea7c442 100644 --- a/webkit/fileapi/file_system_callback_dispatcher.h +++ b/webkit/fileapi/file_system_callback_dispatcher.h @@ -16,6 +16,8 @@ namespace fileapi { // but uses chromium types. class FileSystemCallbackDispatcher { public: + virtual ~FileSystemCallbackDispatcher() {} + // Callback for various operations that don't require return values. virtual void DidSucceed() = 0; diff --git a/webkit/fileapi/webfilewriter_base.cc b/webkit/fileapi/webfilewriter_base.cc new file mode 100644 index 0000000..9d3939f --- /dev/null +++ b/webkit/fileapi/webfilewriter_base.cc @@ -0,0 +1,145 @@ +// 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 "webkit/fileapi/webfilewriter_base.h" + +#include "base/logging.h" +#include "third_party/WebKit/WebKit/chromium/public/WebFileError.h" +#include "third_party/WebKit/WebKit/chromium/public/WebFileWriterClient.h" +#include "third_party/WebKit/WebKit/chromium/public/WebURL.h" +#include "webkit/glue/webkit_glue.h" + +namespace fileapi { + +WebFileWriterBase::WebFileWriterBase( + const WebKit::WebString& path, WebKit::WebFileWriterClient* client) + : path_(webkit_glue::WebStringToFilePath(path)), + client_(client), + operation_(kOperationNone), + cancel_state_(kCancelNotInProgress) { +} + +WebFileWriterBase::~WebFileWriterBase() { +} + +void WebFileWriterBase::truncate(long long length) { + DCHECK(kOperationNone == operation_); + DCHECK(kCancelNotInProgress == cancel_state_); + operation_ = kOperationTruncate; + DoTruncate(path_, length); +} + +void WebFileWriterBase::write( + long long position, + const WebKit::WebURL& blob_url) { + DCHECK(kOperationNone == operation_); + DCHECK(kCancelNotInProgress == cancel_state_); + operation_ = kOperationWrite; + DoWrite(path_, blob_url, position); +} + +// When we cancel a write/truncate, we always get back the result of the write +// before the result of the cancel, no matter what happens. +// So we'll get back either +// success [of the write/truncate, in a DidWrite(XXX, true)/DidSucceed() call] +// followed by failure [of the cancel]; or +// failure [of the write, either from cancel or other reasons] followed by +// the result of the cancel. +// In the write case, there could also be queued up non-terminal DidWrite calls +// before any of that comes back, but there will always be a terminal write +// response [success or failure] after them, followed by the cancel result, so +// we can ignore non-terminal write responses, take the terminal write success +// or the first failure as the last write response, then know that the next +// thing to come back is the cancel response. We only notify the +// AsyncFileWriterClient when it's all over. +void WebFileWriterBase::cancel() { + DCHECK(kOperationWrite == operation_ || kOperationTruncate == operation_); + if (kCancelNotInProgress != cancel_state_) + return; + cancel_state_ = kCancelSent; + DoCancel(); +} + +void WebFileWriterBase::DidSucceed() { + // Write never gets a DidSucceed call, so this is either a cancel or truncate + // response. + switch (cancel_state_) { + case kCancelNotInProgress: + // A truncate succeeded, with no complications. + DCHECK(kOperationTruncate == operation_); + operation_ = kOperationNone; + client_->didTruncate(); + break; + case kCancelSent: + DCHECK(kOperationTruncate == operation_); + // This is the success call of the truncate, which we'll eat, even though + // it succeeded before the cancel got there. We accepted the cancel call, + // so the truncate will eventually return an error. + cancel_state_ = kCancelReceivedWriteResponse; + break; + case kCancelReceivedWriteResponse: + // This is the success of the cancel operation. + FinishCancel(); + break; + default: + NOTREACHED(); + } +} + +void WebFileWriterBase::DidFail(base::PlatformFileError error_code) { + DCHECK(kOperationNone != operation_); + switch (cancel_state_) { + case kCancelNotInProgress: + // A write or truncate failed. + operation_ = kOperationNone; + client_->didFail( + webkit_glue::PlatformFileErrorToWebFileError(error_code)); + break; + case kCancelSent: + // This is the failure of a write or truncate; the next message should be + // the result of the cancel. We don't assume that it'll be a success, as + // the write/truncate could have failed for other reasons. + cancel_state_ = kCancelReceivedWriteResponse; + break; + case kCancelReceivedWriteResponse: + // The cancel reported failure, meaning that the write or truncate + // finished before the cancel got there. But we suppressed the + // write/truncate's response, and will now report that it was cancelled. + FinishCancel(); + break; + default: + NOTREACHED(); + } +} + +void WebFileWriterBase::DidWrite(int64 bytes, bool complete) { + DCHECK(kOperationWrite == operation_); + switch (cancel_state_) { + case kCancelNotInProgress: + if (complete) + operation_ = kOperationNone; + client_->didWrite(bytes, complete); + break; + case kCancelSent: + // This is the success call of the write, which we'll eat, even though + // it succeeded before the cancel got there. We accepted the cancel call, + // so the write will eventually return an error. + if (complete) + cancel_state_ = kCancelReceivedWriteResponse; + break; + case kCancelReceivedWriteResponse: + default: + NOTREACHED(); + } +} + +void WebFileWriterBase::FinishCancel() { + DCHECK(kCancelReceivedWriteResponse == cancel_state_); + DCHECK(kOperationNone != operation_); + cancel_state_ = kCancelNotInProgress; + operation_ = kOperationNone; + client_->didFail(WebKit::WebFileErrorAbort); +} + +} // namespace fileapi diff --git a/webkit/fileapi/webfilewriter_base.h b/webkit/fileapi/webfilewriter_base.h new file mode 100644 index 0000000..790e48b --- /dev/null +++ b/webkit/fileapi/webfilewriter_base.h @@ -0,0 +1,69 @@ +// 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 WEBKIT_FILEAPI_WEBFILEWRITER_BASE_H_ +#define WEBKIT_FILEAPI_WEBFILEWRITER_BASE_H_ + +#include "base/file_path.h" +#include "base/platform_file.h" +#include "third_party/WebKit/WebKit/chromium/public/WebFileWriter.h" + +class GURL; + +namespace WebKit { +class WebFileWriterClient; +class WebString; +class WebURL; +} + +namespace fileapi { + +class WebFileWriterBase : public WebKit::WebFileWriter { + public: + WebFileWriterBase( + const WebKit::WebString& path, WebKit::WebFileWriterClient* client); + virtual ~WebFileWriterBase(); + + // WebFileWriter implementation + virtual void truncate(long long length); + virtual void write(long long position, const WebKit::WebURL& blobURL); + virtual void cancel(); + + protected: + // Derived classes must provide these methods to asynchronously perform + // the requested operation, and they must call the appropiate DidSomething + // method upon completion and as progress is made in the Write case. + virtual void DoTruncate(const FilePath& path, int64 offset) = 0; + virtual void DoWrite(const FilePath& path, const GURL& blob_url, + int64 offset) = 0; + virtual void DoCancel() = 0; + + void DidSucceed(); + void DidFail(base::PlatformFileError error_code); + void DidWrite(int64 bytes, bool complete); + + private: + enum OperationType { + kOperationNone, + kOperationWrite, + kOperationTruncate + }; + + enum CancelState { + kCancelNotInProgress, + kCancelSent, + kCancelReceivedWriteResponse, + }; + + void FinishCancel(); + + FilePath path_; + WebKit::WebFileWriterClient* client_; + OperationType operation_; + CancelState cancel_state_; +}; + +} // namespace fileapi + +#endif // WEBKIT_FILEAPI_WEBFILEWRITER_BASE_H_ diff --git a/webkit/fileapi/webkit_fileapi.gypi b/webkit/fileapi/webkit_fileapi.gypi index 5189564..73d49838 100644 --- a/webkit/fileapi/webkit_fileapi.gypi +++ b/webkit/fileapi/webkit_fileapi.gypi @@ -24,6 +24,8 @@ 'file_system_types.h', 'file_writer_delegate.cc', 'file_writer_delegate.h', + 'webfilewriter_base.cc', + 'webfilewriter_base.h', ], 'conditions': [ ['inside_chromium_build==0', { diff --git a/webkit/support/webkit_support.gypi b/webkit/support/webkit_support.gypi index 99c8ae0..05dd565 100644 --- a/webkit/support/webkit_support.gypi +++ b/webkit/support/webkit_support.gypi @@ -54,6 +54,8 @@ '<(DEPTH)/webkit/tools/test_shell/simple_appcache_system.h', '<(DEPTH)/webkit/tools/test_shell/simple_file_system.cc', '<(DEPTH)/webkit/tools/test_shell/simple_file_system.h', + '<(DEPTH)/webkit/tools/test_shell/simple_file_writer.cc', + '<(DEPTH)/webkit/tools/test_shell/simple_file_writer.h', '<(DEPTH)/webkit/tools/test_shell/simple_clipboard_impl.cc', '<(DEPTH)/webkit/tools/test_shell/simple_database_system.cc', '<(DEPTH)/webkit/tools/test_shell/simple_database_system.h', diff --git a/webkit/tools/test_shell/simple_file_system.cc b/webkit/tools/test_shell/simple_file_system.cc index 411d987..ae77ce4 100644 --- a/webkit/tools/test_shell/simple_file_system.cc +++ b/webkit/tools/test_shell/simple_file_system.cc @@ -12,10 +12,13 @@ #include "third_party/WebKit/WebKit/chromium/public/WebVector.h" #include "webkit/fileapi/file_system_callback_dispatcher.h" #include "webkit/glue/webkit_glue.h" +#include "webkit/tools/test_shell/simple_file_writer.h" using WebKit::WebFileInfo; using WebKit::WebFileSystemCallbacks; using WebKit::WebFileSystemEntry; +using WebKit::WebFileWriter; +using WebKit::WebFileWriterClient; using WebKit::WebString; using WebKit::WebVector; @@ -61,7 +64,10 @@ class TestShellFileSystemCallbackDispatcher virtual void DidReadMetadata(const base::PlatformFileInfo& info) { WebFileInfo web_file_info; + web_file_info.length = info.size; web_file_info.modificationTime = info.last_modified.ToDoubleT(); + web_file_info.type = info.is_directory ? + WebFileInfo::TypeDirectory : WebFileInfo::TypeFile; callbacks_->didReadMetadata(web_file_info); file_system_->RemoveCompletedOperation(request_id_); } @@ -165,7 +171,7 @@ void SimpleFileSystem::createDirectory( } void SimpleFileSystem::fileExists( - const WebString& path, WebFileSystemCallbacks* callbacks) { + const WebString& path, WebFileSystemCallbacks* callbacks) { FilePath filepath(webkit_glue::WebStringToFilePath(path)); GetNewOperation(callbacks)->FileExists(filepath); @@ -185,6 +191,11 @@ void SimpleFileSystem::readDirectory( GetNewOperation(callbacks)->ReadDirectory(filepath); } +WebFileWriter* SimpleFileSystem::createFileWriter( + const WebString& path, WebFileWriterClient* client) { + return new SimpleFileWriter(path, client); +} + fileapi::FileSystemOperation* SimpleFileSystem::GetNewOperation( WebFileSystemCallbacks* callbacks) { // This pointer will be owned by |operation|. diff --git a/webkit/tools/test_shell/simple_file_system.h b/webkit/tools/test_shell/simple_file_system.h index 031ed8b..e1a8236 100644 --- a/webkit/tools/test_shell/simple_file_system.h +++ b/webkit/tools/test_shell/simple_file_system.h @@ -44,6 +44,8 @@ class SimpleFileSystem : public WebKit::WebFileSystem { WebKit::WebFileSystemCallbacks* callbacks); virtual void readDirectory(const WebKit::WebString& path, WebKit::WebFileSystemCallbacks* callbacks); + virtual WebKit::WebFileWriter* createFileWriter( + const WebKit::WebString& path, WebKit::WebFileWriterClient* client); private: // Helpers. diff --git a/webkit/tools/test_shell/simple_file_writer.cc b/webkit/tools/test_shell/simple_file_writer.cc new file mode 100644 index 0000000..0c1a434 --- /dev/null +++ b/webkit/tools/test_shell/simple_file_writer.cc @@ -0,0 +1,186 @@ +// 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 "webkit/tools/test_shell/simple_file_writer.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" +#include "webkit/tools/test_shell/simple_resource_loader_bridge.h" + +using fileapi::FileSystemOperation; +using fileapi::FileSystemCallbackDispatcher; +using fileapi::WebFileWriterBase; +using WebKit::WebFileWriterClient; +using WebKit::WebString; +using WebKit::WebURL; + +URLRequestContext* SimpleFileWriter::request_context_ = NULL; + +// Helper class to proxy to 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 SimpleFileWriters and IOThreadBackends. +class SimpleFileWriter::IOThreadProxy + : public base::RefCountedThreadSafe { + public: + explicit IOThreadProxy(const base::WeakPtr& simple_writer) + : simple_writer_(simple_writer) { + // The IO thread needs to be running for this class to work. + SimpleResourceLoaderBridge::EnsureIOThread(); + io_thread_ = SimpleResourceLoaderBridge::GetIoThread(); + 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_; +}; + + +SimpleFileWriter::SimpleFileWriter( + const WebString& path, WebFileWriterClient* client) + : WebFileWriterBase(path, client), + io_thread_proxy_(new IOThreadProxy(AsWeakPtr())) { +} + +SimpleFileWriter::~SimpleFileWriter() { +} + +void SimpleFileWriter::DoTruncate(const FilePath& path, int64 offset) { + io_thread_proxy_->Truncate(path, offset); +} + +void SimpleFileWriter::DoWrite( + const FilePath& path, const GURL& blob_url, int64 offset) { + io_thread_proxy_->Write(path, blob_url, offset); +} + +void SimpleFileWriter::DoCancel() { + io_thread_proxy_->Cancel(); +} diff --git a/webkit/tools/test_shell/simple_file_writer.h b/webkit/tools/test_shell/simple_file_writer.h new file mode 100644 index 0000000..285fc02 --- /dev/null +++ b/webkit/tools/test_shell/simple_file_writer.h @@ -0,0 +1,45 @@ +// 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 WEBKIT_TOOLS_TEST_SHELL_SIMPLE_FILE_WRITER_H_ +#define WEBKIT_TOOLS_TEST_SHELL_SIMPLE_FILE_WRITER_H_ + +#include "base/ref_counted.h" +#include "base/weak_ptr.h" +#include "webkit/fileapi/webfilewriter_base.h" + +class URLRequestContext; + + +// An implementation of WebFileWriter for use in test_shell and DRT. +class SimpleFileWriter : public fileapi::WebFileWriterBase, + public base::SupportsWeakPtr { + public: + SimpleFileWriter( + const WebKit::WebString& path, WebKit::WebFileWriterClient* client); + virtual ~SimpleFileWriter(); + + // Called by SimpleResourceLoaderBridge when the context is + // created and destroyed. + static void InitializeOnIOThread(URLRequestContext* request_context) { + request_context_ = request_context; + } + static void CleanupOnIOThread() { + request_context_ = NULL; + } + + protected: + // WebFileWriterBase overrides + virtual void DoTruncate(const FilePath& path, int64 offset); + virtual void DoWrite(const FilePath& path, const GURL& blob_url, + int64 offset); + virtual void DoCancel(); + + private: + class IOThreadProxy; + scoped_refptr io_thread_proxy_; + static URLRequestContext* request_context_; +}; + +#endif // WEBKIT_TOOLS_TEST_SHELL_SIMPLE_FILE_WRITER_H_ diff --git a/webkit/tools/test_shell/simple_resource_loader_bridge.cc b/webkit/tools/test_shell/simple_resource_loader_bridge.cc index a4ef5e7..954b258 100644 --- a/webkit/tools/test_shell/simple_resource_loader_bridge.cc +++ b/webkit/tools/test_shell/simple_resource_loader_bridge.cc @@ -68,6 +68,7 @@ #include "webkit/blob/deletable_file_reference.h" #include "webkit/glue/resource_loader_bridge.h" #include "webkit/tools/test_shell/simple_appcache_system.h" +#include "webkit/tools/test_shell/simple_file_writer.h" #include "webkit/tools/test_shell/simple_socket_stream_bridge.h" #include "webkit/tools/test_shell/test_shell_request_context.h" #include "webkit/tools/test_shell/test_shell_webblobregistry_impl.h" @@ -142,6 +143,7 @@ class IOThread : public base::Thread { SimpleAppCacheSystem::InitializeOnIOThread(g_request_context); SimpleSocketStreamBridge::InitializeOnIOThread(g_request_context); + SimpleFileWriter::InitializeOnIOThread(g_request_context); TestShellWebBlobRegistryImpl::InitializeOnIOThread( static_cast(g_request_context)-> @@ -152,6 +154,7 @@ class IOThread : public base::Thread { virtual void CleanUp() { SimpleSocketStreamBridge::Cleanup(); TestShellWebBlobRegistryImpl::Cleanup(); + SimpleFileWriter::CleanupOnIOThread(); if (g_request_context) { g_request_context->Release(); g_request_context = NULL; diff --git a/webkit/tools/test_shell/test_webview_delegate.cc b/webkit/tools/test_shell/test_webview_delegate.cc index 41bf9d3..5727cab 100644 --- a/webkit/tools/test_shell/test_webview_delegate.cc +++ b/webkit/tools/test_shell/test_webview_delegate.cc @@ -1126,6 +1126,7 @@ void TestWebViewDelegate::openFileSystem( // The FileSystem temp directory was not initialized successfully. callbacks->didFail(WebKit::WebFileErrorSecurity); } else { + // TODO(michaeln): need to put origin/type in the path. callbacks->didOpenFileSystem( "TestShellFileSystem", webkit_glue::FilePathToWebString(shell_->file_system_root())); -- cgit v1.1