diff options
Diffstat (limited to 'content/child')
38 files changed, 5534 insertions, 0 deletions
diff --git a/content/child/fileapi/OWNERS b/content/child/fileapi/OWNERS new file mode 100644 index 0000000..9e9046e --- /dev/null +++ b/content/child/fileapi/OWNERS @@ -0,0 +1,14 @@ +ericu@chromium.org +kinuko@chromium.org +michaeln@chromium.org +jianli@chromium.org + +# For security review of IPC message files. +per-file *_messages.h=set noparent +per-file *_messages.h=cdn@chromium.org +per-file *_messages.h=cevans@chromium.org +per-file *_messages.h=inferno@chromium.org +per-file *_messages.h=jschuh@chromium.org +per-file *_messages.h=palmer@chromium.org +per-file *_messages.h=tsepez@chromium.org +per-file *_messages.h=kenrb@chromium.org diff --git a/content/child/fileapi/webfilesystem_callback_adapters.cc b/content/child/fileapi/webfilesystem_callback_adapters.cc new file mode 100644 index 0000000..3935522 --- /dev/null +++ b/content/child/fileapi/webfilesystem_callback_adapters.cc @@ -0,0 +1,77 @@ +// 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/webfilesystem_callback_adapters.h" + +#include <string> +#include <vector> + +#include "base/logging.h" +#include "base/utf_string_conversions.h" +#include "googleurl/src/gurl.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebFileSystemCallbacks.h" +#include "third_party/WebKit/public/platform/WebFileInfo.h" +#include "third_party/WebKit/public/platform/WebString.h" +#include "webkit/base/file_path_string_conversions.h" +#include "webkit/common/fileapi/directory_entry.h" +#include "webkit/common/fileapi/file_system_util.h" +#include "webkit/glue/webkit_glue.h" + +using WebKit::WebFileInfo; +using WebKit::WebFileSystemCallbacks; +using WebKit::WebFileSystemEntry; +using WebKit::WebString; +using WebKit::WebVector; + +namespace content { + +void FileStatusCallbackAdapter( + WebKit::WebFileSystemCallbacks* callbacks, + base::PlatformFileError error) { + if (error == base::PLATFORM_FILE_OK) + callbacks->didSucceed(); + else + callbacks->didFail(::fileapi::PlatformFileErrorToWebFileError(error)); +} + +void ReadMetadataCallbackAdapter( + WebKit::WebFileSystemCallbacks* callbacks, + const base::PlatformFileInfo& file_info, + const base::FilePath& platform_path) { + WebFileInfo web_file_info; + webkit_glue::PlatformFileInfoToWebFileInfo(file_info, &web_file_info); + web_file_info.platformPath = webkit_base::FilePathToWebString(platform_path); + callbacks->didReadMetadata(web_file_info); +} + +void CreateSnapshotFileCallbackAdapter( + WebKit::WebFileSystemCallbacks* callbacks, + const base::PlatformFileInfo& file_info, + const base::FilePath& platform_path) { + WebFileInfo web_file_info; + webkit_glue::PlatformFileInfoToWebFileInfo(file_info, &web_file_info); + web_file_info.platformPath = webkit_base::FilePathToWebString(platform_path); + callbacks->didCreateSnapshotFile(web_file_info); +} + +void ReadDirectoryCallbackAdapater( + WebKit::WebFileSystemCallbacks* callbacks, + const std::vector<fileapi::DirectoryEntry>& entries, + bool has_more) { + WebVector<WebFileSystemEntry> file_system_entries(entries.size()); + for (size_t i = 0; i < entries.size(); i++) { + file_system_entries[i].name = + webkit_base::FilePathStringToWebString(entries[i].name); + file_system_entries[i].isDirectory = entries[i].is_directory; + } + callbacks->didReadDirectory(file_system_entries, has_more); +} + +void OpenFileSystemCallbackAdapter( + WebKit::WebFileSystemCallbacks* callbacks, + const std::string& name, const GURL& root) { + callbacks->didOpenFileSystem(UTF8ToUTF16(name), root); +} + +} // namespace content diff --git a/content/child/fileapi/webfilesystem_callback_adapters.h b/content/child/fileapi/webfilesystem_callback_adapters.h new file mode 100644 index 0000000..408d71d --- /dev/null +++ b/content/child/fileapi/webfilesystem_callback_adapters.h @@ -0,0 +1,48 @@ +// 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. + +#ifndef CONTENT_CHILD_FILEAPI_WEBFILESYSTEM_CALLBACK_ADAPTERS_H_ +#define CONTENT_CHILD_FILEAPI_WEBFILESYSTEM_CALLBACK_ADAPTERS_H_ + +#include "base/basictypes.h" +#include "base/platform_file.h" + +class GURL; + +namespace fileapi { +struct DirectoryEntry; +} + +namespace WebKit { +class WebFileSystemCallbacks; +} + +namespace content { + +void FileStatusCallbackAdapter( + WebKit::WebFileSystemCallbacks* callbacks, + base::PlatformFileError error); + +void ReadMetadataCallbackAdapter( + WebKit::WebFileSystemCallbacks* callbacks, + const base::PlatformFileInfo& file_info, + const base::FilePath& platform_path); + +void CreateSnapshotFileCallbackAdapter( + WebKit::WebFileSystemCallbacks* callbacks, + const base::PlatformFileInfo& file_info, + const base::FilePath& platform_path); + +void ReadDirectoryCallbackAdapater( + WebKit::WebFileSystemCallbacks* callbacks, + const std::vector<fileapi::DirectoryEntry>& entries, + bool has_more); + +void OpenFileSystemCallbackAdapter( + WebKit::WebFileSystemCallbacks* callbacks, + const std::string& name, const GURL& root); + +} // namespace content + +#endif // CONTENT_CHILD_FILEAPI_WEBFILESYSTEM_CALLBACK_ADAPTERS_H_ diff --git a/content/child/fileapi/webfilesystem_impl.cc b/content/child/fileapi/webfilesystem_impl.cc new file mode 100644 index 0000000..0e6ed21 --- /dev/null +++ b/content/child/fileapi/webfilesystem_impl.cc @@ -0,0 +1,144 @@ +// 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/webfilesystem_impl.h" + +#include "base/bind.h" +#include "content/child/fileapi/webfilesystem_callback_adapters.h" +#include "content/child/fileapi/webfilewriter_impl.h" +#include "content/common/child_thread.h" +#include "content/common/fileapi/file_system_dispatcher.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebFileSystemCallbacks.h" +#include "third_party/WebKit/public/platform/WebFileInfo.h" +#include "third_party/WebKit/public/platform/WebString.h" +#include "third_party/WebKit/public/platform/WebURL.h" +#include "webkit/glue/webkit_glue.h" + +using WebKit::WebFileInfo; +using WebKit::WebFileSystemCallbacks; +using WebKit::WebFileSystemEntry; +using WebKit::WebString; +using WebKit::WebURL; +using WebKit::WebVector; + +namespace content { + +WebFileSystemImpl::WebFileSystemImpl() { +} + +void WebFileSystemImpl::move(const WebURL& src_path, + const WebURL& dest_path, + WebFileSystemCallbacks* callbacks) { + FileSystemDispatcher* dispatcher = + ChildThread::current()->file_system_dispatcher(); + dispatcher->Move(GURL(src_path), + GURL(dest_path), + base::Bind(&FileStatusCallbackAdapter, callbacks)); +} + +void WebFileSystemImpl::copy(const WebURL& src_path, + const WebURL& dest_path, + WebFileSystemCallbacks* callbacks) { + FileSystemDispatcher* dispatcher = + ChildThread::current()->file_system_dispatcher(); + dispatcher->Copy(GURL(src_path), + GURL(dest_path), + base::Bind(&FileStatusCallbackAdapter, callbacks)); +} + +void WebFileSystemImpl::remove(const WebURL& path, + WebFileSystemCallbacks* callbacks) { + FileSystemDispatcher* dispatcher = + ChildThread::current()->file_system_dispatcher(); + dispatcher->Remove( + GURL(path), + false /* recursive */, + base::Bind(&FileStatusCallbackAdapter, callbacks)); +} + +void WebFileSystemImpl::removeRecursively(const WebURL& path, + WebFileSystemCallbacks* callbacks) { + FileSystemDispatcher* dispatcher = + ChildThread::current()->file_system_dispatcher(); + dispatcher->Remove( + GURL(path), + true /* recursive */, + base::Bind(&FileStatusCallbackAdapter, callbacks)); +} + +void WebFileSystemImpl::readMetadata(const WebURL& path, + WebFileSystemCallbacks* callbacks) { + FileSystemDispatcher* dispatcher = + ChildThread::current()->file_system_dispatcher(); + dispatcher->ReadMetadata( + GURL(path), + base::Bind(&ReadMetadataCallbackAdapter, callbacks), + base::Bind(&FileStatusCallbackAdapter, callbacks)); +} + +void WebFileSystemImpl::createFile(const WebURL& path, + bool exclusive, + WebFileSystemCallbacks* callbacks) { + FileSystemDispatcher* dispatcher = + ChildThread::current()->file_system_dispatcher(); + dispatcher->Create( + GURL(path), exclusive, false /* directory */, false /* recursive */, + base::Bind(&FileStatusCallbackAdapter, callbacks)); +} + +void WebFileSystemImpl::createDirectory(const WebURL& path, + bool exclusive, + WebFileSystemCallbacks* callbacks) { + FileSystemDispatcher* dispatcher = + ChildThread::current()->file_system_dispatcher(); + dispatcher->Create( + GURL(path), exclusive, true /* directory */, false /* recursive */, + base::Bind(&FileStatusCallbackAdapter, callbacks)); +} + +void WebFileSystemImpl::fileExists(const WebURL& path, + WebFileSystemCallbacks* callbacks) { + FileSystemDispatcher* dispatcher = + ChildThread::current()->file_system_dispatcher(); + dispatcher->Exists( + GURL(path), false /* directory */, + base::Bind(&FileStatusCallbackAdapter, callbacks)); +} + +void WebFileSystemImpl::directoryExists(const WebURL& path, + WebFileSystemCallbacks* callbacks) { + FileSystemDispatcher* dispatcher = + ChildThread::current()->file_system_dispatcher(); + dispatcher->Exists( + GURL(path), true /* directory */, + base::Bind(&FileStatusCallbackAdapter, callbacks)); +} + +void WebFileSystemImpl::readDirectory(const WebURL& path, + WebFileSystemCallbacks* callbacks) { + FileSystemDispatcher* dispatcher = + ChildThread::current()->file_system_dispatcher(); + dispatcher->ReadDirectory( + GURL(path), + base::Bind(&ReadDirectoryCallbackAdapater, callbacks), + base::Bind(&FileStatusCallbackAdapter, callbacks)); +} + +WebKit::WebFileWriter* WebFileSystemImpl::createFileWriter( + const WebURL& path, WebKit::WebFileWriterClient* client) { + return new WebFileWriterImpl(GURL(path), client); +} + +void WebFileSystemImpl::createSnapshotFileAndReadMetadata( + const WebKit::WebURL& path, + WebKit::WebFileSystemCallbacks* callbacks) { + FileSystemDispatcher* dispatcher = + ChildThread::current()->file_system_dispatcher(); + dispatcher->CreateSnapshotFile( + GURL(path), + base::Bind(&CreateSnapshotFileCallbackAdapter, callbacks), + base::Bind(&FileStatusCallbackAdapter, callbacks)); +} + +} // namespace content diff --git a/content/child/fileapi/webfilesystem_impl.h b/content/child/fileapi/webfilesystem_impl.h new file mode 100644 index 0000000..2bcf535 --- /dev/null +++ b/content/child/fileapi/webfilesystem_impl.h @@ -0,0 +1,69 @@ +// 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. + +#ifndef CONTENT_CHILD_FILEAPI_WEBFILESYSTEM_IMPL_H_ +#define CONTENT_CHILD_FILEAPI_WEBFILESYSTEM_IMPL_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "third_party/WebKit/public/platform/WebFileSystem.h" + +namespace WebKit { +class WebURL; +class WebFileWriter; +class WebFileWriterClient; +} + +namespace content { + +class WebFileSystemImpl : public WebKit::WebFileSystem { + public: + WebFileSystemImpl(); + virtual ~WebFileSystemImpl() { } + + // WebFileSystem implementation. + virtual void move( + const WebKit::WebURL& src_path, + const WebKit::WebURL& dest_path, + WebKit::WebFileSystemCallbacks*) OVERRIDE; + virtual void copy( + const WebKit::WebURL& src_path, + const WebKit::WebURL& dest_path, + WebKit::WebFileSystemCallbacks*) OVERRIDE; + virtual void remove( + const WebKit::WebURL& path, + WebKit::WebFileSystemCallbacks*) OVERRIDE; + virtual void removeRecursively( + const WebKit::WebURL& path, + WebKit::WebFileSystemCallbacks*) OVERRIDE; + virtual void readMetadata( + const WebKit::WebURL& path, + WebKit::WebFileSystemCallbacks*) OVERRIDE; + virtual void createFile( + const WebKit::WebURL& path, + bool exclusive, + WebKit::WebFileSystemCallbacks*) OVERRIDE; + virtual void createDirectory( + const WebKit::WebURL& path, + bool exclusive, + WebKit::WebFileSystemCallbacks*) OVERRIDE; + virtual void fileExists( + const WebKit::WebURL& path, + WebKit::WebFileSystemCallbacks*) OVERRIDE; + virtual void directoryExists( + const WebKit::WebURL& path, + WebKit::WebFileSystemCallbacks*) OVERRIDE; + virtual void readDirectory( + const WebKit::WebURL& path, + WebKit::WebFileSystemCallbacks*) OVERRIDE; + virtual WebKit::WebFileWriter* createFileWriter( + const WebKit::WebURL& path, WebKit::WebFileWriterClient*) OVERRIDE; + virtual void createSnapshotFileAndReadMetadata( + const WebKit::WebURL& path, + WebKit::WebFileSystemCallbacks*); +}; + +} // namespace content + +#endif // CONTENT_CHILD_FILEAPI_WEBFILESYSTEM_IMPL_H_ diff --git a/content/child/fileapi/webfilewriter_impl.cc b/content/child/fileapi/webfilewriter_impl.cc new file mode 100644 index 0000000..dc37c99 --- /dev/null +++ b/content/child/fileapi/webfilewriter_impl.cc @@ -0,0 +1,51 @@ +// 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 "content/common/child_thread.h" +#include "content/common/fileapi/file_system_dispatcher.h" + +namespace content { + +namespace { + +inline FileSystemDispatcher* GetFileSystemDispatcher() { + return ChildThread::current()->file_system_dispatcher(); +} + +} // namespace + +WebFileWriterImpl::WebFileWriterImpl( + const GURL& path, WebKit::WebFileWriterClient* client) + : WebFileWriterBase(path, client), + request_id_(0) { +} + +WebFileWriterImpl::~WebFileWriterImpl() { +} + +void WebFileWriterImpl::DoTruncate(const GURL& path, int64 offset) { + // The FileSystemDispatcher takes ownership of the CallbackDispatcher. + GetFileSystemDispatcher()->Truncate( + path, offset, &request_id_, + base::Bind(&WebFileWriterImpl::DidFinish, AsWeakPtr())); +} + +void WebFileWriterImpl::DoWrite( + const GURL& path, const GURL& blob_url, int64 offset) { + GetFileSystemDispatcher()->Write( + path, blob_url, offset, &request_id_, + base::Bind(&WebFileWriterImpl::DidWrite, AsWeakPtr()), + base::Bind(&WebFileWriterImpl::DidFinish, AsWeakPtr())); +} + +void WebFileWriterImpl::DoCancel() { + GetFileSystemDispatcher()->Cancel( + request_id_, + base::Bind(&WebFileWriterImpl::DidFinish, AsWeakPtr())); +} + +} // namespace content diff --git a/content/child/fileapi/webfilewriter_impl.h b/content/child/fileapi/webfilewriter_impl.h new file mode 100644 index 0000000..718139d --- /dev/null +++ b/content/child/fileapi/webfilewriter_impl.h @@ -0,0 +1,35 @@ +// 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. + +#ifndef CONTENT_CHILD_FILEAPI_WEBFILEWRITER_IMPL_H_ +#define CONTENT_CHILD_FILEAPI_WEBFILEWRITER_IMPL_H_ + +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "webkit/renderer/fileapi/webfilewriter_base.h" + +namespace content { + +// An implementation of WebFileWriter for use in chrome renderers and workers. +class WebFileWriterImpl : public fileapi::WebFileWriterBase, + public base::SupportsWeakPtr<WebFileWriterImpl> { + public: + WebFileWriterImpl(const GURL& path, WebKit::WebFileWriterClient* client); + virtual ~WebFileWriterImpl(); + + protected: + // WebFileWriterBase overrides + virtual void DoTruncate(const GURL& path, int64 offset) OVERRIDE; + virtual void DoWrite(const GURL& path, const GURL& blob_url, + int64 offset) OVERRIDE; + virtual void DoCancel() OVERRIDE; + + private: + class CallbackDispatcher; + int request_id_; +}; + +} // namespace content + +#endif // CONTENT_CHILD_FILEAPI_WEBFILEWRITER_IMPL_H_ diff --git a/content/child/indexed_db/OWNERS b/content/child/indexed_db/OWNERS new file mode 100644 index 0000000..ed5258c --- /dev/null +++ b/content/child/indexed_db/OWNERS @@ -0,0 +1,15 @@ +dgrogan@chromium.org +hans@chromium.org +michaeln@chromium.org +jsbell@chromium.org +alecflett@chromium.org + +# For security review of IPC message files. +per-file *_messages.h=set noparent +per-file *_messages.h=cdn@chromium.org +per-file *_messages.h=cevans@chromium.org +per-file *_messages.h=inferno@chromium.org +per-file *_messages.h=jschuh@chromium.org +per-file *_messages.h=palmer@chromium.org +per-file *_messages.h=tsepez@chromium.org +per-file *_messages.h=kenrb@chromium.org diff --git a/content/child/indexed_db/indexed_db_dispatcher.cc b/content/child/indexed_db/indexed_db_dispatcher.cc new file mode 100644 index 0000000..24c403d --- /dev/null +++ b/content/child/indexed_db/indexed_db_dispatcher.cc @@ -0,0 +1,713 @@ +// 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/indexed_db/indexed_db_dispatcher.h" + +#include "base/format_macros.h" +#include "base/lazy_instance.h" +#include "base/stringprintf.h" +#include "base/threading/thread_local.h" +#include "content/child/indexed_db/proxy_webidbcursor_impl.h" +#include "content/child/indexed_db/proxy_webidbdatabase_impl.h" +#include "content/common/child_thread.h" +#include "content/common/indexed_db/indexed_db_messages.h" +#include "ipc/ipc_channel.h" +#include "third_party/WebKit/public/platform/WebIDBDatabaseCallbacks.h" +#include "third_party/WebKit/public/platform/WebIDBDatabaseError.h" +#include "third_party/WebKit/public/platform/WebIDBDatabaseException.h" +#include "third_party/WebKit/public/platform/WebIDBKeyRange.h" + +using WebKit::WebData; +using WebKit::WebIDBCallbacks; +using WebKit::WebIDBDatabase; +using WebKit::WebIDBDatabaseCallbacks; +using WebKit::WebIDBDatabaseError; +using WebKit::WebIDBKey; +using WebKit::WebIDBKeyRange; +using WebKit::WebIDBMetadata; +using WebKit::WebString; +using WebKit::WebVector; +using base::ThreadLocalPointer; +using webkit_glue::WorkerTaskRunner; + +namespace content { +static base::LazyInstance<ThreadLocalPointer<IndexedDBDispatcher> >::Leaky + g_idb_dispatcher_tls = LAZY_INSTANCE_INITIALIZER; + +namespace { + +IndexedDBDispatcher* const kHasBeenDeleted = + reinterpret_cast<IndexedDBDispatcher*>(0x1); + +int32 CurrentWorkerId() { + return WorkerTaskRunner::Instance()->CurrentWorkerId(); +} +} // unnamed namespace + +const size_t kMaxIDBValueSizeInBytes = 64 * 1024 * 1024; + +IndexedDBDispatcher::IndexedDBDispatcher() { + g_idb_dispatcher_tls.Pointer()->Set(this); +} + +IndexedDBDispatcher::~IndexedDBDispatcher() { + // Clear any pending callbacks - which may result in dispatch requests - + // before marking the dispatcher as deleted. + pending_callbacks_.Clear(); + pending_database_callbacks_.Clear(); + + DCHECK(pending_callbacks_.IsEmpty()); + DCHECK(pending_database_callbacks_.IsEmpty()); + + g_idb_dispatcher_tls.Pointer()->Set(kHasBeenDeleted); +} + +IndexedDBDispatcher* IndexedDBDispatcher::ThreadSpecificInstance() { + if (g_idb_dispatcher_tls.Pointer()->Get() == kHasBeenDeleted) { + NOTREACHED() << "Re-instantiating TLS IndexedDBDispatcher."; + g_idb_dispatcher_tls.Pointer()->Set(NULL); + } + if (g_idb_dispatcher_tls.Pointer()->Get()) + return g_idb_dispatcher_tls.Pointer()->Get(); + + IndexedDBDispatcher* dispatcher = new IndexedDBDispatcher; + if (WorkerTaskRunner::Instance()->CurrentWorkerId()) + webkit_glue::WorkerTaskRunner::Instance()->AddStopObserver(dispatcher); + return dispatcher; +} + +void IndexedDBDispatcher::OnWorkerRunLoopStopped() { delete this; } + +WebIDBMetadata IndexedDBDispatcher::ConvertMetadata( + const IndexedDBDatabaseMetadata& idb_metadata) { + WebIDBMetadata web_metadata; + web_metadata.id = idb_metadata.id; + web_metadata.name = idb_metadata.name; + web_metadata.version = idb_metadata.version; + web_metadata.intVersion = idb_metadata.int_version; + web_metadata.maxObjectStoreId = idb_metadata.max_object_store_id; + web_metadata.objectStores = + WebVector<WebIDBMetadata::ObjectStore>(idb_metadata.object_stores.size()); + + for (size_t i = 0; i < idb_metadata.object_stores.size(); ++i) { + const IndexedDBObjectStoreMetadata& idb_store_metadata = + idb_metadata.object_stores[i]; + WebIDBMetadata::ObjectStore& web_store_metadata = + web_metadata.objectStores[i]; + + web_store_metadata.id = idb_store_metadata.id; + web_store_metadata.name = idb_store_metadata.name; + web_store_metadata.keyPath = idb_store_metadata.keyPath; + web_store_metadata.autoIncrement = idb_store_metadata.autoIncrement; + web_store_metadata.maxIndexId = idb_store_metadata.max_index_id; + web_store_metadata.indexes = + WebVector<WebIDBMetadata::Index>(idb_store_metadata.indexes.size()); + + for (size_t j = 0; j < idb_store_metadata.indexes.size(); ++j) { + const IndexedDBIndexMetadata& idb_index_metadata = + idb_store_metadata.indexes[j]; + WebIDBMetadata::Index& web_index_metadata = web_store_metadata.indexes[j]; + + web_index_metadata.id = idb_index_metadata.id; + web_index_metadata.name = idb_index_metadata.name; + web_index_metadata.keyPath = idb_index_metadata.keyPath; + web_index_metadata.unique = idb_index_metadata.unique; + web_index_metadata.multiEntry = idb_index_metadata.multiEntry; + } + } + + return web_metadata; +} + +void IndexedDBDispatcher::OnMessageReceived(const IPC::Message& msg) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(IndexedDBDispatcher, msg) + IPC_MESSAGE_HANDLER(IndexedDBMsg_CallbacksSuccessIDBCursor, + OnSuccessOpenCursor) + IPC_MESSAGE_HANDLER(IndexedDBMsg_CallbacksSuccessCursorAdvance, + OnSuccessCursorContinue) + IPC_MESSAGE_HANDLER(IndexedDBMsg_CallbacksSuccessCursorContinue, + OnSuccessCursorContinue) + IPC_MESSAGE_HANDLER(IndexedDBMsg_CallbacksSuccessCursorPrefetch, + OnSuccessCursorPrefetch) + IPC_MESSAGE_HANDLER(IndexedDBMsg_CallbacksSuccessIDBDatabase, + OnSuccessIDBDatabase) + IPC_MESSAGE_HANDLER(IndexedDBMsg_CallbacksSuccessIndexedDBKey, + OnSuccessIndexedDBKey) + IPC_MESSAGE_HANDLER(IndexedDBMsg_CallbacksSuccessStringList, + OnSuccessStringList) + IPC_MESSAGE_HANDLER(IndexedDBMsg_CallbacksSuccessValue, OnSuccessValue) + IPC_MESSAGE_HANDLER(IndexedDBMsg_CallbacksSuccessValueWithKey, + OnSuccessValueWithKey) + IPC_MESSAGE_HANDLER(IndexedDBMsg_CallbacksSuccessInteger, OnSuccessInteger) + IPC_MESSAGE_HANDLER(IndexedDBMsg_CallbacksSuccessUndefined, + OnSuccessUndefined) + IPC_MESSAGE_HANDLER(IndexedDBMsg_CallbacksError, OnError) + IPC_MESSAGE_HANDLER(IndexedDBMsg_CallbacksIntBlocked, OnIntBlocked) + IPC_MESSAGE_HANDLER(IndexedDBMsg_CallbacksUpgradeNeeded, OnUpgradeNeeded) + IPC_MESSAGE_HANDLER(IndexedDBMsg_DatabaseCallbacksForcedClose, + OnForcedClose) + IPC_MESSAGE_HANDLER(IndexedDBMsg_DatabaseCallbacksIntVersionChange, + OnIntVersionChange) + IPC_MESSAGE_HANDLER(IndexedDBMsg_DatabaseCallbacksAbort, OnAbort) + IPC_MESSAGE_HANDLER(IndexedDBMsg_DatabaseCallbacksComplete, OnComplete) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + // If a message gets here, IndexedDBMessageFilter already determined that it + // is an IndexedDB message. + DCHECK(handled) << "Didn't handle a message defined at line " + << IPC_MESSAGE_ID_LINE(msg.type()); +} + +bool IndexedDBDispatcher::Send(IPC::Message* msg) { + if (!ChildThread::current()) { + // Unexpected - this may be happening during shutdown. + NOTREACHED(); + return false; + } + if (CurrentWorkerId()) { + scoped_refptr<IPC::SyncMessageFilter> filter( + ChildThread::current()->sync_message_filter()); + return filter->Send(msg); + } + return ChildThread::current()->Send(msg); +} + +void IndexedDBDispatcher::RequestIDBCursorAdvance( + unsigned long count, + WebIDBCallbacks* callbacks_ptr, + int32 ipc_cursor_id) { + // Reset all cursor prefetch caches except for this cursor. + ResetCursorPrefetchCaches(ipc_cursor_id); + + scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr); + + int32 ipc_callbacks_id = pending_callbacks_.Add(callbacks.release()); + Send(new IndexedDBHostMsg_CursorAdvance( + ipc_cursor_id, CurrentWorkerId(), ipc_callbacks_id, count)); +} + +void IndexedDBDispatcher::RequestIDBCursorContinue( + const IndexedDBKey& key, + WebIDBCallbacks* callbacks_ptr, + int32 ipc_cursor_id) { + // Reset all cursor prefetch caches except for this cursor. + ResetCursorPrefetchCaches(ipc_cursor_id); + + scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr); + + int32 ipc_callbacks_id = pending_callbacks_.Add(callbacks.release()); + Send(new IndexedDBHostMsg_CursorContinue( + ipc_cursor_id, CurrentWorkerId(), ipc_callbacks_id, key)); +} + +void IndexedDBDispatcher::RequestIDBCursorPrefetch( + int n, + WebIDBCallbacks* callbacks_ptr, + int32 ipc_cursor_id) { + scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr); + + int32 ipc_callbacks_id = pending_callbacks_.Add(callbacks.release()); + Send(new IndexedDBHostMsg_CursorPrefetch( + ipc_cursor_id, CurrentWorkerId(), ipc_callbacks_id, n)); +} + +void IndexedDBDispatcher::RequestIDBCursorPrefetchReset(int used_prefetches, + int unused_prefetches, + int32 ipc_cursor_id) { + Send(new IndexedDBHostMsg_CursorPrefetchReset( + ipc_cursor_id, used_prefetches, unused_prefetches)); +} + +void IndexedDBDispatcher::RequestIDBFactoryOpen( + const string16& name, + int64 version, + int64 transaction_id, + WebIDBCallbacks* callbacks_ptr, + WebIDBDatabaseCallbacks* database_callbacks_ptr, + const string16& database_identifier) { + ResetCursorPrefetchCaches(); + scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr); + scoped_ptr<WebIDBDatabaseCallbacks> database_callbacks( + database_callbacks_ptr); + + IndexedDBHostMsg_FactoryOpen_Params params; + params.ipc_thread_id = CurrentWorkerId(); + params.ipc_callbacks_id = pending_callbacks_.Add(callbacks.release()); + params.ipc_database_callbacks_id = + pending_database_callbacks_.Add(database_callbacks.release()); + params.database_identifier = database_identifier; + params.name = name; + params.transaction_id = transaction_id; + params.version = version; + Send(new IndexedDBHostMsg_FactoryOpen(params)); +} + +void IndexedDBDispatcher::RequestIDBFactoryGetDatabaseNames( + WebIDBCallbacks* callbacks_ptr, + const string16& database_identifier) { + ResetCursorPrefetchCaches(); + scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr); + + IndexedDBHostMsg_FactoryGetDatabaseNames_Params params; + params.ipc_thread_id = CurrentWorkerId(); + params.ipc_callbacks_id = pending_callbacks_.Add(callbacks.release()); + params.database_identifier = database_identifier; + Send(new IndexedDBHostMsg_FactoryGetDatabaseNames(params)); +} + +void IndexedDBDispatcher::RequestIDBFactoryDeleteDatabase( + const string16& name, + WebIDBCallbacks* callbacks_ptr, + const string16& database_identifier) { + ResetCursorPrefetchCaches(); + scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr); + + IndexedDBHostMsg_FactoryDeleteDatabase_Params params; + params.ipc_thread_id = CurrentWorkerId(); + params.ipc_callbacks_id = pending_callbacks_.Add(callbacks.release()); + params.database_identifier = database_identifier; + params.name = name; + Send(new IndexedDBHostMsg_FactoryDeleteDatabase(params)); +} + +void IndexedDBDispatcher::RequestIDBDatabaseClose( + int32 ipc_database_id, + int32 ipc_database_callbacks_id) { + ResetCursorPrefetchCaches(); + Send(new IndexedDBHostMsg_DatabaseClose(ipc_database_id)); + // There won't be pending database callbacks if the transaction was aborted in + // the initial upgradeneeded event handler. + if (pending_database_callbacks_.Lookup(ipc_database_callbacks_id)) + pending_database_callbacks_.Remove(ipc_database_callbacks_id); +} + +void IndexedDBDispatcher::RequestIDBDatabaseCreateTransaction( + int32 ipc_database_id, + int64 transaction_id, + WebIDBDatabaseCallbacks* database_callbacks_ptr, + WebKit::WebVector<long long> object_store_ids, + unsigned short mode) { + scoped_ptr<WebIDBDatabaseCallbacks> database_callbacks( + database_callbacks_ptr); + IndexedDBHostMsg_DatabaseCreateTransaction_Params params; + params.ipc_thread_id = CurrentWorkerId(); + params.ipc_database_id = ipc_database_id; + params.transaction_id = transaction_id; + params.ipc_database_callbacks_id = + pending_database_callbacks_.Add(database_callbacks.release()); + params.object_store_ids + .assign(object_store_ids.data(), + object_store_ids.data() + object_store_ids.size()); + params.mode = mode; + + Send(new IndexedDBHostMsg_DatabaseCreateTransaction(params)); +} + +void IndexedDBDispatcher::RequestIDBDatabaseGet( + int32 ipc_database_id, + int64 transaction_id, + int64 object_store_id, + int64 index_id, + const IndexedDBKeyRange& key_range, + bool key_only, + WebIDBCallbacks* callbacks) { + ResetCursorPrefetchCaches(); + IndexedDBHostMsg_DatabaseGet_Params params; + init_params(params, callbacks); + params.ipc_database_id = ipc_database_id; + params.transaction_id = transaction_id; + params.object_store_id = object_store_id; + params.index_id = index_id; + params.key_range = key_range; + params.key_only = key_only; + Send(new IndexedDBHostMsg_DatabaseGet(params)); +} + +void IndexedDBDispatcher::RequestIDBDatabasePut( + int32 ipc_database_id, + int64 transaction_id, + int64 object_store_id, + const WebData& value, + const IndexedDBKey& key, + WebIDBDatabase::PutMode put_mode, + WebIDBCallbacks* callbacks, + const WebVector<long long>& index_ids, + const WebVector<WebKit::WebVector<WebIDBKey> >& index_keys) { + + if (value.size() > kMaxIDBValueSizeInBytes) { + callbacks->onError(WebIDBDatabaseError( + WebKit::WebIDBDatabaseExceptionUnknownError, + WebString::fromUTF8(base::StringPrintf( + "The serialized value is too large" + " (size=%" PRIuS " bytes, max=%" PRIuS " bytes).", + value.size(), + kMaxIDBValueSizeInBytes).c_str()))); + return; + } + + ResetCursorPrefetchCaches(); + IndexedDBHostMsg_DatabasePut_Params params; + init_params(params, callbacks); + params.ipc_database_id = ipc_database_id; + params.transaction_id = transaction_id; + params.object_store_id = object_store_id; + + params.value.assign(value.data(), value.data() + value.size()); + params.key = key; + params.put_mode = put_mode; + + COMPILE_ASSERT(sizeof(params.index_ids[0]) == sizeof(index_ids[0]), + Cant_copy); + params.index_ids + .assign(index_ids.data(), index_ids.data() + index_ids.size()); + + params.index_keys.resize(index_keys.size()); + for (size_t i = 0; i < index_keys.size(); ++i) { + params.index_keys[i].resize(index_keys[i].size()); + for (size_t j = 0; j < index_keys[i].size(); ++j) { + params.index_keys[i][j] = IndexedDBKey(index_keys[i][j]); + } + } + Send(new IndexedDBHostMsg_DatabasePut(params)); +} + +void IndexedDBDispatcher::RequestIDBDatabaseOpenCursor( + int32 ipc_database_id, + int64 transaction_id, + int64 object_store_id, + int64 index_id, + const IndexedDBKeyRange& key_range, + unsigned short direction, + bool key_only, + WebKit::WebIDBDatabase::TaskType task_type, + WebIDBCallbacks* callbacks) { + ResetCursorPrefetchCaches(); + IndexedDBHostMsg_DatabaseOpenCursor_Params params; + init_params(params, callbacks); + params.ipc_database_id = ipc_database_id; + params.transaction_id = transaction_id; + params.object_store_id = object_store_id; + params.index_id = index_id; + params.key_range = IndexedDBKeyRange(key_range); + params.direction = direction; + params.key_only = key_only; + params.task_type = task_type; + Send(new IndexedDBHostMsg_DatabaseOpenCursor(params)); +} + +void IndexedDBDispatcher::RequestIDBDatabaseCount( + int32 ipc_database_id, + int64 transaction_id, + int64 object_store_id, + int64 index_id, + const IndexedDBKeyRange& key_range, + WebKit::WebIDBCallbacks* callbacks) { + ResetCursorPrefetchCaches(); + IndexedDBHostMsg_DatabaseCount_Params params; + init_params(params, callbacks); + params.ipc_database_id = ipc_database_id; + params.transaction_id = transaction_id; + params.object_store_id = object_store_id; + params.index_id = index_id; + params.key_range = IndexedDBKeyRange(key_range); + Send(new IndexedDBHostMsg_DatabaseCount(params)); +} + +void IndexedDBDispatcher::RequestIDBDatabaseDeleteRange( + int32 ipc_database_id, + int64 transaction_id, + int64 object_store_id, + const IndexedDBKeyRange& key_range, + WebKit::WebIDBCallbacks* callbacks) { + ResetCursorPrefetchCaches(); + IndexedDBHostMsg_DatabaseDeleteRange_Params params; + init_params(params, callbacks); + params.ipc_database_id = ipc_database_id; + params.transaction_id = transaction_id; + params.object_store_id = object_store_id; + params.key_range = key_range; + Send(new IndexedDBHostMsg_DatabaseDeleteRange(params)); +} + +void IndexedDBDispatcher::RequestIDBDatabaseClear( + int32 ipc_database_id, + int64 transaction_id, + int64 object_store_id, + WebKit::WebIDBCallbacks* callbacks_ptr) { + scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr); + int32 ipc_callbacks_id = pending_callbacks_.Add(callbacks.release()); + Send(new IndexedDBHostMsg_DatabaseClear(CurrentWorkerId(), + ipc_callbacks_id, + ipc_database_id, + transaction_id, + object_store_id)); +} + +void IndexedDBDispatcher::CursorDestroyed(int32 ipc_cursor_id) { + cursors_.erase(ipc_cursor_id); +} + +void IndexedDBDispatcher::DatabaseDestroyed(int32 ipc_database_id) { + DCHECK_EQ(databases_.count(ipc_database_id), 1u); + databases_.erase(ipc_database_id); +} + +void IndexedDBDispatcher::OnSuccessIDBDatabase( + int32 ipc_thread_id, + int32 ipc_callbacks_id, + int32 ipc_database_callbacks_id, + int32 ipc_object_id, + const IndexedDBDatabaseMetadata& idb_metadata) { + DCHECK_EQ(ipc_thread_id, CurrentWorkerId()); + WebIDBCallbacks* callbacks = pending_callbacks_.Lookup(ipc_callbacks_id); + if (!callbacks) + return; + WebIDBMetadata metadata(ConvertMetadata(idb_metadata)); + // If an upgrade was performed, count will be non-zero. + if (!databases_.count(ipc_object_id)) + databases_[ipc_object_id] = new RendererWebIDBDatabaseImpl( + ipc_object_id, ipc_database_callbacks_id); + DCHECK_EQ(databases_.count(ipc_object_id), 1u); + callbacks->onSuccess(databases_[ipc_object_id], metadata); + pending_callbacks_.Remove(ipc_callbacks_id); +} + +void IndexedDBDispatcher::OnSuccessIndexedDBKey(int32 ipc_thread_id, + int32 ipc_callbacks_id, + const IndexedDBKey& key) { + DCHECK_EQ(ipc_thread_id, CurrentWorkerId()); + WebIDBCallbacks* callbacks = pending_callbacks_.Lookup(ipc_callbacks_id); + if (!callbacks) + return; + callbacks->onSuccess(WebIDBKey(key)); + pending_callbacks_.Remove(ipc_callbacks_id); +} + +void IndexedDBDispatcher::OnSuccessStringList( + int32 ipc_thread_id, + int32 ipc_callbacks_id, + const std::vector<string16>& value) { + DCHECK_EQ(ipc_thread_id, CurrentWorkerId()); + WebIDBCallbacks* callbacks = pending_callbacks_.Lookup(ipc_callbacks_id); + if (!callbacks) + return; + callbacks->onSuccess(WebVector<WebString>(value)); + pending_callbacks_.Remove(ipc_callbacks_id); +} + +void IndexedDBDispatcher::OnSuccessValue(int32 ipc_thread_id, + int32 ipc_callbacks_id, + const std::vector<char>& value) { + DCHECK_EQ(ipc_thread_id, CurrentWorkerId()); + WebIDBCallbacks* callbacks = pending_callbacks_.Lookup(ipc_callbacks_id); + if (!callbacks) + return; + WebData web_value; + if (value.size()) + web_value.assign(&value.front(), value.size()); + callbacks->onSuccess(web_value); + pending_callbacks_.Remove(ipc_callbacks_id); +} + +void IndexedDBDispatcher::OnSuccessValueWithKey( + int32 ipc_thread_id, + int32 ipc_callbacks_id, + const std::vector<char>& value, + const IndexedDBKey& primary_key, + const IndexedDBKeyPath& key_path) { + DCHECK_EQ(ipc_thread_id, CurrentWorkerId()); + WebIDBCallbacks* callbacks = pending_callbacks_.Lookup(ipc_callbacks_id); + if (!callbacks) + return; + WebData web_value; + if (value.size()) + web_value.assign(&value.front(), value.size()); + callbacks->onSuccess(web_value, primary_key, key_path); + pending_callbacks_.Remove(ipc_callbacks_id); +} + +void IndexedDBDispatcher::OnSuccessInteger(int32 ipc_thread_id, + int32 ipc_callbacks_id, + int64 value) { + DCHECK_EQ(ipc_thread_id, CurrentWorkerId()); + WebIDBCallbacks* callbacks = pending_callbacks_.Lookup(ipc_callbacks_id); + if (!callbacks) + return; + callbacks->onSuccess(value); + pending_callbacks_.Remove(ipc_callbacks_id); +} + +void IndexedDBDispatcher::OnSuccessUndefined(int32 ipc_thread_id, + int32 ipc_callbacks_id) { + DCHECK_EQ(ipc_thread_id, CurrentWorkerId()); + WebIDBCallbacks* callbacks = pending_callbacks_.Lookup(ipc_callbacks_id); + if (!callbacks) + return; + callbacks->onSuccess(); + pending_callbacks_.Remove(ipc_callbacks_id); +} + +void IndexedDBDispatcher::OnSuccessOpenCursor( + const IndexedDBMsg_CallbacksSuccessIDBCursor_Params& p) { + DCHECK_EQ(p.ipc_thread_id, CurrentWorkerId()); + int32 ipc_callbacks_id = p.ipc_callbacks_id; + int32 ipc_object_id = p.ipc_cursor_id; + const IndexedDBKey& key = p.key; + const IndexedDBKey& primary_key = p.primary_key; + WebData web_value; + if (p.value.size()) + web_value.assign(&p.value.front(), p.value.size()); + + WebIDBCallbacks* callbacks = pending_callbacks_.Lookup(ipc_callbacks_id); + if (!callbacks) + return; + + RendererWebIDBCursorImpl* cursor = + new RendererWebIDBCursorImpl(ipc_object_id); + cursors_[ipc_object_id] = cursor; + callbacks->onSuccess(cursor, key, primary_key, web_value); + + pending_callbacks_.Remove(ipc_callbacks_id); +} + +void IndexedDBDispatcher::OnSuccessCursorContinue( + const IndexedDBMsg_CallbacksSuccessCursorContinue_Params& p) { + DCHECK_EQ(p.ipc_thread_id, CurrentWorkerId()); + int32 ipc_callbacks_id = p.ipc_callbacks_id; + int32 ipc_cursor_id = p.ipc_cursor_id; + const IndexedDBKey& key = p.key; + const IndexedDBKey& primary_key = p.primary_key; + const std::vector<char>& value = p.value; + + RendererWebIDBCursorImpl* cursor = cursors_[ipc_cursor_id]; + DCHECK(cursor); + + WebIDBCallbacks* callbacks = pending_callbacks_.Lookup(ipc_callbacks_id); + if (!callbacks) + return; + + WebData web_value; + if (value.size()) + web_value.assign(&value.front(), value.size()); + callbacks->onSuccess(key, primary_key, web_value); + + pending_callbacks_.Remove(ipc_callbacks_id); +} + +void IndexedDBDispatcher::OnSuccessCursorPrefetch( + const IndexedDBMsg_CallbacksSuccessCursorPrefetch_Params& p) { + DCHECK_EQ(p.ipc_thread_id, CurrentWorkerId()); + int32 ipc_callbacks_id = p.ipc_callbacks_id; + int32 ipc_cursor_id = p.ipc_cursor_id; + const std::vector<IndexedDBKey>& keys = p.keys; + const std::vector<IndexedDBKey>& primary_keys = p.primary_keys; + std::vector<WebData> values(p.values.size()); + for (size_t i = 0; i < p.values.size(); ++i) { + if (p.values[i].size()) + values[i].assign(&p.values[i].front(), p.values[i].size()); + } + RendererWebIDBCursorImpl* cursor = cursors_[ipc_cursor_id]; + DCHECK(cursor); + cursor->SetPrefetchData(keys, primary_keys, values); + + WebIDBCallbacks* callbacks = pending_callbacks_.Lookup(ipc_callbacks_id); + DCHECK(callbacks); + cursor->CachedContinue(callbacks); + pending_callbacks_.Remove(ipc_callbacks_id); +} + +void IndexedDBDispatcher::OnIntBlocked(int32 ipc_thread_id, + int32 ipc_callbacks_id, + int64 existing_version) { + DCHECK_EQ(ipc_thread_id, CurrentWorkerId()); + WebIDBCallbacks* callbacks = pending_callbacks_.Lookup(ipc_callbacks_id); + DCHECK(callbacks); + callbacks->onBlocked(existing_version); +} + +void IndexedDBDispatcher::OnUpgradeNeeded( + const IndexedDBMsg_CallbacksUpgradeNeeded_Params& p) { + DCHECK_EQ(p.ipc_thread_id, CurrentWorkerId()); + WebIDBCallbacks* callbacks = pending_callbacks_.Lookup(p.ipc_callbacks_id); + DCHECK(callbacks); + WebIDBMetadata metadata(ConvertMetadata(p.idb_metadata)); + DCHECK(!databases_.count(p.ipc_database_id)); + databases_[p.ipc_database_id] = new RendererWebIDBDatabaseImpl( + p.ipc_database_id, p.ipc_database_callbacks_id); + callbacks->onUpgradeNeeded( + p.old_version, databases_[p.ipc_database_id], metadata); +} + +void IndexedDBDispatcher::OnError(int32 ipc_thread_id, + int32 ipc_callbacks_id, + int code, + const string16& message) { + DCHECK_EQ(ipc_thread_id, CurrentWorkerId()); + WebIDBCallbacks* callbacks = pending_callbacks_.Lookup(ipc_callbacks_id); + if (!callbacks) + return; + callbacks->onError(WebIDBDatabaseError(code, message)); + pending_callbacks_.Remove(ipc_callbacks_id); +} + +void IndexedDBDispatcher::OnAbort(int32 ipc_thread_id, + int32 ipc_database_callbacks_id, + int64 transaction_id, + int code, + const string16& message) { + DCHECK_EQ(ipc_thread_id, CurrentWorkerId()); + WebIDBDatabaseCallbacks* callbacks = + pending_database_callbacks_.Lookup(ipc_database_callbacks_id); + if (!callbacks) + return; + callbacks->onAbort(transaction_id, WebIDBDatabaseError(code, message)); +} + +void IndexedDBDispatcher::OnComplete(int32 ipc_thread_id, + int32 ipc_database_callbacks_id, + int64 transaction_id) { + DCHECK_EQ(ipc_thread_id, CurrentWorkerId()); + WebIDBDatabaseCallbacks* callbacks = + pending_database_callbacks_.Lookup(ipc_database_callbacks_id); + if (!callbacks) + return; + callbacks->onComplete(transaction_id); +} + +void IndexedDBDispatcher::OnForcedClose(int32 ipc_thread_id, + int32 ipc_database_callbacks_id) { + DCHECK_EQ(ipc_thread_id, CurrentWorkerId()); + WebIDBDatabaseCallbacks* callbacks = + pending_database_callbacks_.Lookup(ipc_database_callbacks_id); + if (!callbacks) + return; + callbacks->onForcedClose(); +} + +void IndexedDBDispatcher::OnIntVersionChange(int32 ipc_thread_id, + int32 ipc_database_callbacks_id, + int64 old_version, + int64 new_version) { + DCHECK_EQ(ipc_thread_id, CurrentWorkerId()); + WebIDBDatabaseCallbacks* callbacks = + pending_database_callbacks_.Lookup(ipc_database_callbacks_id); + // callbacks would be NULL if a versionchange event is received after close + // has been called. + if (!callbacks) + return; + callbacks->onVersionChange(old_version, new_version); +} + +void IndexedDBDispatcher::ResetCursorPrefetchCaches( + int32 ipc_exception_cursor_id) { + typedef std::map<int32, RendererWebIDBCursorImpl*>::iterator Iterator; + for (Iterator i = cursors_.begin(); i != cursors_.end(); ++i) { + if (i->first == ipc_exception_cursor_id) + continue; + i->second->ResetPrefetchCache(); + } +} + +} // namespace content diff --git a/content/child/indexed_db/indexed_db_dispatcher.h b/content/child/indexed_db/indexed_db_dispatcher.h new file mode 100644 index 0000000..f8b2102 --- /dev/null +++ b/content/child/indexed_db/indexed_db_dispatcher.h @@ -0,0 +1,241 @@ +// 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. + +#ifndef CONTENT_CHILD_INDEXED_DB_INDEXED_DB_DISPATCHER_H_ +#define CONTENT_CHILD_INDEXED_DB_INDEXED_DB_DISPATCHER_H_ + +#include <map> +#include <vector> + +#include "base/gtest_prod_util.h" +#include "base/id_map.h" +#include "base/nullable_string16.h" +#include "content/common/content_export.h" +#include "ipc/ipc_sync_message_filter.h" +#include "third_party/WebKit/public/platform/WebIDBCallbacks.h" +#include "third_party/WebKit/public/platform/WebIDBCursor.h" +#include "third_party/WebKit/public/platform/WebIDBDatabase.h" +#include "third_party/WebKit/public/platform/WebIDBDatabaseCallbacks.h" +#include "webkit/glue/worker_task_runner.h" + +struct IndexedDBDatabaseMetadata; +struct IndexedDBMsg_CallbacksSuccessCursorContinue_Params; +struct IndexedDBMsg_CallbacksSuccessCursorPrefetch_Params; +struct IndexedDBMsg_CallbacksSuccessIDBCursor_Params; +struct IndexedDBMsg_CallbacksUpgradeNeeded_Params; + +namespace WebKit { +class WebData; +} + +namespace content { +class IndexedDBKey; +class IndexedDBKeyPath; +class IndexedDBKeyRange; +class RendererWebIDBCursorImpl; +class RendererWebIDBDatabaseImpl; + +CONTENT_EXPORT extern const size_t kMaxIDBValueSizeInBytes; + +// Handle the indexed db related communication for this context thread - the +// main thread and each worker thread have their own copies. +class CONTENT_EXPORT IndexedDBDispatcher + : public webkit_glue::WorkerTaskRunner::Observer { + public: + // Constructor made public to allow RenderThreadImpl to own a copy without + // failing a NOTREACHED in ThreadSpecificInstance in tests that instantiate + // two copies of RenderThreadImpl on the same thread. Everyone else probably + // wants to use ThreadSpecificInstance(). + IndexedDBDispatcher(); + virtual ~IndexedDBDispatcher(); + static IndexedDBDispatcher* ThreadSpecificInstance(); + + // webkit_glue::WorkerTaskRunner::Observer implementation. + virtual void OnWorkerRunLoopStopped() OVERRIDE; + + static WebKit::WebIDBMetadata ConvertMetadata( + const IndexedDBDatabaseMetadata& idb_metadata); + + void OnMessageReceived(const IPC::Message& msg); + static bool Send(IPC::Message* msg); + + void RequestIDBFactoryGetDatabaseNames(WebKit::WebIDBCallbacks* callbacks, + const string16& database_identifier); + + void RequestIDBFactoryOpen( + const string16& name, + int64 version, + int64 transaction_id, + WebKit::WebIDBCallbacks* callbacks, + WebKit::WebIDBDatabaseCallbacks* database_callbacks, + const string16& database_identifier); + + void RequestIDBFactoryDeleteDatabase(const string16& name, + WebKit::WebIDBCallbacks* callbacks, + const string16& database_identifier); + + void RequestIDBCursorAdvance(unsigned long count, + WebKit::WebIDBCallbacks* callbacks_ptr, + int32 ipc_cursor_id); + + virtual void RequestIDBCursorContinue(const IndexedDBKey& key, + WebKit::WebIDBCallbacks* callbacks_ptr, + int32 ipc_cursor_id); + + virtual void RequestIDBCursorPrefetch(int n, + WebKit::WebIDBCallbacks* callbacks_ptr, + int32 ipc_cursor_id); + + void RequestIDBCursorPrefetchReset(int used_prefetches, + int unused_prefetches, + int32 ipc_cursor_id); + + void RequestIDBDatabaseClose(int32 ipc_database_id, + int32 ipc_database_callbacks_id); + + void RequestIDBDatabaseCreateTransaction( + int32 ipc_database_id, + int64 transaction_id, + WebKit::WebIDBDatabaseCallbacks* database_callbacks_ptr, + WebKit::WebVector<long long> object_store_ids, + unsigned short mode); + + void RequestIDBDatabaseGet(int32 ipc_database_id, + int64 transaction_id, + int64 object_store_id, + int64 index_id, + const IndexedDBKeyRange& key_range, + bool key_only, + WebKit::WebIDBCallbacks* callbacks); + + void RequestIDBDatabasePut( + int32 ipc_database_id, + int64 transaction_id, + int64 object_store_id, + const WebKit::WebData& value, + const IndexedDBKey& key, + WebKit::WebIDBDatabase::PutMode put_mode, + WebKit::WebIDBCallbacks* callbacks, + const WebKit::WebVector<long long>& index_ids, + const WebKit::WebVector<WebKit::WebVector<WebKit::WebIDBKey> >& + index_keys); + + void RequestIDBDatabaseOpenCursor(int32 ipc_database_id, + int64 transaction_id, + int64 object_store_id, + int64 index_id, + const IndexedDBKeyRange& key_range, + unsigned short direction, + bool key_only, + WebKit::WebIDBDatabase::TaskType task_type, + WebKit::WebIDBCallbacks* callbacks); + + void RequestIDBDatabaseCount(int32 ipc_database_id, + int64 transaction_id, + int64 object_store_id, + int64 index_id, + const IndexedDBKeyRange& key_range, + WebKit::WebIDBCallbacks* callbacks); + + void RequestIDBDatabaseDeleteRange(int32 ipc_database_id, + int64 transaction_id, + int64 object_store_id, + const IndexedDBKeyRange& key_range, + WebKit::WebIDBCallbacks* callbacks); + + void RequestIDBDatabaseClear(int32 ipc_database_id, + int64 transaction_id, + int64 object_store_id, + WebKit::WebIDBCallbacks* callbacks); + + virtual void CursorDestroyed(int32 ipc_cursor_id); + void DatabaseDestroyed(int32 ipc_database_id); + + private: + FRIEND_TEST_ALL_PREFIXES(IndexedDBDispatcherTest, ValueSizeTest); + + static int32 CurrentWorkerId() { + return webkit_glue::WorkerTaskRunner::Instance()->CurrentWorkerId(); + } + + template <typename T> + void init_params(T& params, WebKit::WebIDBCallbacks* callbacks_ptr) { + scoped_ptr<WebKit::WebIDBCallbacks> callbacks(callbacks_ptr); + params.ipc_thread_id = CurrentWorkerId(); + params.ipc_callbacks_id = pending_callbacks_.Add(callbacks.release()); + } + + // IDBCallback message handlers. + void OnSuccessIDBDatabase(int32 ipc_thread_id, + int32 ipc_callbacks_id, + int32 ipc_database_callbacks_id, + int32 ipc_object_id, + const IndexedDBDatabaseMetadata& idb_metadata); + void OnSuccessIndexedDBKey(int32 ipc_thread_id, + int32 ipc_callbacks_id, + const IndexedDBKey& key); + + void OnSuccessOpenCursor( + const IndexedDBMsg_CallbacksSuccessIDBCursor_Params& p); + void OnSuccessCursorContinue( + const IndexedDBMsg_CallbacksSuccessCursorContinue_Params& p); + void OnSuccessCursorPrefetch( + const IndexedDBMsg_CallbacksSuccessCursorPrefetch_Params& p); + void OnSuccessStringList(int32 ipc_thread_id, + int32 ipc_callbacks_id, + const std::vector<string16>& value); + void OnSuccessValue(int32 ipc_thread_id, + int32 ipc_callbacks_id, + const std::vector<char>& value); + void OnSuccessValueWithKey(int32 ipc_thread_id, + int32 ipc_callbacks_id, + const std::vector<char>& value, + const IndexedDBKey& primary_key, + const IndexedDBKeyPath& key_path); + void OnSuccessInteger(int32 ipc_thread_id, + int32 ipc_callbacks_id, + int64 value); + void OnSuccessUndefined(int32 ipc_thread_id, int32 ipc_callbacks_id); + void OnError(int32 ipc_thread_id, + int32 ipc_callbacks_id, + int code, + const string16& message); + void OnIntBlocked(int32 ipc_thread_id, + int32 ipc_callbacks_id, + int64 existing_version); + void OnUpgradeNeeded(const IndexedDBMsg_CallbacksUpgradeNeeded_Params& p); + void OnAbort(int32 ipc_thread_id, + int32 ipc_database_id, + int64 transaction_id, + int code, + const string16& message); + void OnComplete(int32 ipc_thread_id, + int32 ipc_database_id, + int64 transaction_id); + void OnForcedClose(int32 ipc_thread_id, int32 ipc_database_id); + void OnIntVersionChange(int32 ipc_thread_id, + int32 ipc_database_id, + int64 old_version, + int64 new_version); + + // Reset cursor prefetch caches for all cursors except exception_cursor_id. + void ResetCursorPrefetchCaches(int32 ipc_exception_cursor_id = -1); + + // Careful! WebIDBCallbacks wraps non-threadsafe data types. It must be + // destroyed and used on the same thread it was created on. + IDMap<WebKit::WebIDBCallbacks, IDMapOwnPointer> pending_callbacks_; + IDMap<WebKit::WebIDBDatabaseCallbacks, IDMapOwnPointer> + pending_database_callbacks_; + + // Map from cursor id to RendererWebIDBCursorImpl. + std::map<int32, RendererWebIDBCursorImpl*> cursors_; + + std::map<int32, RendererWebIDBDatabaseImpl*> databases_; + + DISALLOW_COPY_AND_ASSIGN(IndexedDBDispatcher); +}; + +} // namespace content + +#endif // CONTENT_CHILD_INDEXED_DB_INDEXED_DB_DISPATCHER_H_ diff --git a/content/child/indexed_db/indexed_db_dispatcher_unittest.cc b/content/child/indexed_db/indexed_db_dispatcher_unittest.cc new file mode 100644 index 0000000..155ae7f --- /dev/null +++ b/content/child/indexed_db/indexed_db_dispatcher_unittest.cc @@ -0,0 +1,62 @@ +// 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 "base/memory/scoped_ptr.h" +#include "base/values.h" +#include "content/child/indexed_db/indexed_db_dispatcher.h" +#include "content/common/indexed_db/indexed_db_key.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/public/platform/WebData.h" +#include "third_party/WebKit/public/platform/WebIDBCallbacks.h" + +using WebKit::WebData; +using WebKit::WebIDBCallbacks; +using WebKit::WebIDBDatabase; +using WebKit::WebIDBDatabaseError; +using WebKit::WebIDBKey; +using WebKit::WebVector; + +namespace content { +namespace { + +class MockCallbacks : public WebIDBCallbacks { + public: + MockCallbacks() : error_seen_(false) {} + + virtual void onError(const WebIDBDatabaseError&) OVERRIDE { + error_seen_ = true; + } + + bool error_seen() const { return error_seen_; } + + private: + bool error_seen_; +}; + +} // namespace + +TEST(IndexedDBDispatcherTest, ValueSizeTest) { + const std::vector<char> data(kMaxIDBValueSizeInBytes + 1); + const WebData value(&data.front(), data.size()); + const int32 ipc_dummy_id = -1; + const int64 transaction_id = 1; + const int64 object_store_id = 2; + + MockCallbacks callbacks; + IndexedDBDispatcher dispatcher; + IndexedDBKey key(0, WebIDBKey::NumberType); + dispatcher.RequestIDBDatabasePut(ipc_dummy_id, + transaction_id, + object_store_id, + value, + key, + WebIDBDatabase::AddOrUpdate, + &callbacks, + WebVector<long long>(), + WebVector<WebVector<WebIDBKey> >()); + + EXPECT_TRUE(callbacks.error_seen()); +} + +} // namespace content diff --git a/content/child/indexed_db/indexed_db_message_filter.cc b/content/child/indexed_db/indexed_db_message_filter.cc new file mode 100644 index 0000000..949c167 --- /dev/null +++ b/content/child/indexed_db/indexed_db_message_filter.cc @@ -0,0 +1,79 @@ +// 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/indexed_db/indexed_db_message_filter.h" + +#include "base/bind.h" +#include "base/location.h" +#include "base/message_loop_proxy.h" +#include "base/pickle.h" +#include "content/child/indexed_db/indexed_db_dispatcher.h" +#include "content/common/child_thread.h" +#include "content/common/indexed_db/indexed_db_messages.h" +#include "webkit/glue/worker_task_runner.h" + +using webkit_glue::WorkerTaskRunner; + +namespace content { + +IndexedDBMessageFilter::IndexedDBMessageFilter() : + main_thread_loop_proxy_(base::MessageLoopProxy::current()) { +} + +bool IndexedDBMessageFilter::OnMessageReceived(const IPC::Message& msg) { + if (IPC_MESSAGE_CLASS(msg) != IndexedDBMsgStart) + return false; + int ipc_thread_id = -1; + bool result = PickleIterator(msg).ReadInt(&ipc_thread_id); + DCHECK(result); + base::Closure closure = base::Bind( + &IndexedDBMessageFilter::DispatchMessage, this, msg); + if (!ipc_thread_id) { + main_thread_loop_proxy_->PostTask(FROM_HERE, closure); + return true; + } + if (WorkerTaskRunner::Instance()->PostTask(ipc_thread_id, closure)) + return true; + + // Message for a terminated worker - perform necessary cleanup + OnStaleMessageReceived(msg); + return true; +} + +IndexedDBMessageFilter::~IndexedDBMessageFilter() {} + +void IndexedDBMessageFilter::DispatchMessage(const IPC::Message& msg) { + IndexedDBDispatcher::ThreadSpecificInstance()->OnMessageReceived(msg); +} + +void IndexedDBMessageFilter::OnStaleMessageReceived(const IPC::Message& msg) { + IPC_BEGIN_MESSAGE_MAP(IndexedDBMessageFilter, msg) + IPC_MESSAGE_HANDLER(IndexedDBMsg_CallbacksSuccessIDBDatabase, + OnStaleSuccessIDBDatabase) + IPC_MESSAGE_HANDLER(IndexedDBMsg_CallbacksUpgradeNeeded, + OnStaleUpgradeNeeded) + IPC_END_MESSAGE_MAP() +} + +void IndexedDBMessageFilter::OnStaleSuccessIDBDatabase( + int32 ipc_thread_id, + int32 ipc_callbacks_id, + int32 ipc_database_callbacks_id, + int32 ipc_database_id, + const IndexedDBDatabaseMetadata& idb_metadata) { + scoped_refptr<IPC::SyncMessageFilter> filter( + ChildThread::current()->sync_message_filter()); + filter->Send( + new IndexedDBHostMsg_DatabaseClose(ipc_database_id)); +} + +void IndexedDBMessageFilter::OnStaleUpgradeNeeded( + const IndexedDBMsg_CallbacksUpgradeNeeded_Params& p) { + scoped_refptr<IPC::SyncMessageFilter> filter( + ChildThread::current()->sync_message_filter()); + filter->Send( + new IndexedDBHostMsg_DatabaseClose(p.ipc_database_id)); +} + +} // namespace content diff --git a/content/child/indexed_db/indexed_db_message_filter.h b/content/child/indexed_db/indexed_db_message_filter.h new file mode 100644 index 0000000..04708d3 --- /dev/null +++ b/content/child/indexed_db/indexed_db_message_filter.h @@ -0,0 +1,47 @@ +// 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. + +#ifndef CONTENT_CHILD_INDEXED_DB_INDEXED_DB_MESSAGE_FILTER_H_ +#define CONTENT_CHILD_INDEXED_DB_INDEXED_DB_MESSAGE_FILTER_H_ + +#include "ipc/ipc_channel_proxy.h" + +struct IndexedDBDatabaseMetadata; +struct IndexedDBMsg_CallbacksUpgradeNeeded_Params; + +namespace base { +class MessageLoopProxy; +} // namespace base + +namespace content { +class IndexedDBDispatcher; + +class IndexedDBMessageFilter : public IPC::ChannelProxy::MessageFilter { + public: + IndexedDBMessageFilter(); + + // IPC::Listener implementation. + virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE; + + protected: + virtual ~IndexedDBMessageFilter(); + + private: + void DispatchMessage(const IPC::Message& msg); + void OnStaleMessageReceived(const IPC::Message& msg); + void OnStaleSuccessIDBDatabase(int32 ipc_thread_id, + int32 ipc_callbacks_id, + int32 ipc_database_callbacks_id, + int32 ipc_object_id, + const IndexedDBDatabaseMetadata&); + void OnStaleUpgradeNeeded(const IndexedDBMsg_CallbacksUpgradeNeeded_Params&); + + scoped_refptr<base::MessageLoopProxy> main_thread_loop_proxy_; + + DISALLOW_COPY_AND_ASSIGN(IndexedDBMessageFilter); +}; + +} // namespace content + +#endif // CONTENT_CHILD_INDEXED_DB_INDEXED_DB_MESSAGE_FILTER_H_ diff --git a/content/child/indexed_db/proxy_webidbcursor_impl.cc b/content/child/indexed_db/proxy_webidbcursor_impl.cc new file mode 100644 index 0000000..4afd8d9 --- /dev/null +++ b/content/child/indexed_db/proxy_webidbcursor_impl.cc @@ -0,0 +1,157 @@ +// 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/indexed_db/proxy_webidbcursor_impl.h" + +#include <vector> + +#include "content/child/indexed_db/indexed_db_dispatcher.h" +#include "content/common/child_thread.h" +#include "content/common/indexed_db/indexed_db_messages.h" + +using WebKit::WebData; +using WebKit::WebIDBCallbacks; +using WebKit::WebIDBKey; + +namespace content { + +RendererWebIDBCursorImpl::RendererWebIDBCursorImpl(int32 ipc_cursor_id) + : ipc_cursor_id_(ipc_cursor_id), + continue_count_(0), + used_prefetches_(0), + pending_onsuccess_callbacks_(0), + prefetch_amount_(kMinPrefetchAmount) {} + +RendererWebIDBCursorImpl::~RendererWebIDBCursorImpl() { + // It's not possible for there to be pending callbacks that address this + // object since inside WebKit, they hold a reference to the object which owns + // this object. But, if that ever changed, then we'd need to invalidate + // any such pointers. + + if (ipc_cursor_id_ != kInvalidCursorId) { + // Invalid ID used in tests to avoid really sending this message. + IndexedDBDispatcher::Send( + new IndexedDBHostMsg_CursorDestroyed(ipc_cursor_id_)); + } + IndexedDBDispatcher* dispatcher = + IndexedDBDispatcher::ThreadSpecificInstance(); + dispatcher->CursorDestroyed(ipc_cursor_id_); +} + +void RendererWebIDBCursorImpl::advance(unsigned long count, + WebIDBCallbacks* callbacks_ptr) { + IndexedDBDispatcher* dispatcher = + IndexedDBDispatcher::ThreadSpecificInstance(); + scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr); + ResetPrefetchCache(); + dispatcher->RequestIDBCursorAdvance( + count, callbacks.release(), ipc_cursor_id_); +} + +void RendererWebIDBCursorImpl::continueFunction( + const WebIDBKey& key, + WebIDBCallbacks* callbacks_ptr) { + IndexedDBDispatcher* dispatcher = + IndexedDBDispatcher::ThreadSpecificInstance(); + scoped_ptr<WebIDBCallbacks> callbacks(callbacks_ptr); + + if (key.type() == WebIDBKey::NullType) { + // No key, so this would qualify for a prefetch. + ++continue_count_; + + if (!prefetch_keys_.empty()) { + // We have a prefetch cache, so serve the result from that. + CachedContinue(callbacks.get()); + return; + } + + if (continue_count_ > kPrefetchContinueThreshold) { + // Request pre-fetch. + ++pending_onsuccess_callbacks_; + dispatcher->RequestIDBCursorPrefetch( + prefetch_amount_, callbacks.release(), ipc_cursor_id_); + + // Increase prefetch_amount_ exponentially. + prefetch_amount_ *= 2; + if (prefetch_amount_ > kMaxPrefetchAmount) + prefetch_amount_ = kMaxPrefetchAmount; + + return; + } + } else { + // Key argument supplied. We couldn't prefetch this. + ResetPrefetchCache(); + } + + dispatcher->RequestIDBCursorContinue( + IndexedDBKey(key), callbacks.release(), ipc_cursor_id_); +} + +void RendererWebIDBCursorImpl::postSuccessHandlerCallback() { + pending_onsuccess_callbacks_--; + + // If the onsuccess callback called continue() on the cursor again, + // and that continue was served by the prefetch cache, then + // pending_onsuccess_callbacks_ would be incremented. + // If not, it means the callback did something else, or nothing at all, + // in which case we need to reset the cache. + + if (pending_onsuccess_callbacks_ == 0) + ResetPrefetchCache(); +} + +void RendererWebIDBCursorImpl::SetPrefetchData( + const std::vector<IndexedDBKey>& keys, + const std::vector<IndexedDBKey>& primary_keys, + const std::vector<WebData>& values) { + prefetch_keys_.assign(keys.begin(), keys.end()); + prefetch_primary_keys_.assign(primary_keys.begin(), primary_keys.end()); + prefetch_values_.assign(values.begin(), values.end()); + + used_prefetches_ = 0; + pending_onsuccess_callbacks_ = 0; +} + +void RendererWebIDBCursorImpl::CachedContinue( + WebKit::WebIDBCallbacks* callbacks) { + DCHECK_GT(prefetch_keys_.size(), 0ul); + DCHECK(prefetch_primary_keys_.size() == prefetch_keys_.size()); + DCHECK(prefetch_values_.size() == prefetch_keys_.size()); + + IndexedDBKey key = prefetch_keys_.front(); + IndexedDBKey primary_key = prefetch_primary_keys_.front(); + // this could be a real problem.. we need 2 CachedContinues + WebData value = prefetch_values_.front(); + + prefetch_keys_.pop_front(); + prefetch_primary_keys_.pop_front(); + prefetch_values_.pop_front(); + used_prefetches_++; + + pending_onsuccess_callbacks_++; + + callbacks->onSuccess(key, primary_key, value); +} + +void RendererWebIDBCursorImpl::ResetPrefetchCache() { + continue_count_ = 0; + prefetch_amount_ = kMinPrefetchAmount; + + if (!prefetch_keys_.size()) { + // No prefetch cache, so no need to reset the cursor in the back-end. + return; + } + + IndexedDBDispatcher* dispatcher = + IndexedDBDispatcher::ThreadSpecificInstance(); + dispatcher->RequestIDBCursorPrefetchReset( + used_prefetches_, prefetch_keys_.size(), ipc_cursor_id_); + prefetch_keys_.clear(); + prefetch_primary_keys_.clear(); + prefetch_values_.clear(); + + pending_onsuccess_callbacks_ = 0; +} + +} // namespace content diff --git a/content/child/indexed_db/proxy_webidbcursor_impl.h b/content/child/indexed_db/proxy_webidbcursor_impl.h new file mode 100644 index 0000000..d3a1c6b --- /dev/null +++ b/content/child/indexed_db/proxy_webidbcursor_impl.h @@ -0,0 +1,71 @@ +// 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. + +#ifndef CONTENT_CHILD_INDEXED_DB_PROXY_WEBIDBCURSOR_IMPL_H_ +#define CONTENT_CHILD_INDEXED_DB_PROXY_WEBIDBCURSOR_IMPL_H_ + +#include <deque> +#include <vector> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/gtest_prod_util.h" +#include "content/common/content_export.h" +#include "content/common/indexed_db/indexed_db_key.h" +#include "third_party/WebKit/public/platform/WebData.h" +#include "third_party/WebKit/public/platform/WebIDBCallbacks.h" +#include "third_party/WebKit/public/platform/WebIDBCursor.h" +#include "third_party/WebKit/public/platform/WebIDBKey.h" + +namespace content { + +class CONTENT_EXPORT RendererWebIDBCursorImpl + : NON_EXPORTED_BASE(public WebKit::WebIDBCursor) { + public: + explicit RendererWebIDBCursorImpl(int32 ipc_cursor_id); + virtual ~RendererWebIDBCursorImpl(); + + virtual void advance(unsigned long count, WebKit::WebIDBCallbacks* callback); + virtual void continueFunction(const WebKit::WebIDBKey& key, + WebKit::WebIDBCallbacks* callback); + virtual void postSuccessHandlerCallback(); + + void SetPrefetchData(const std::vector<IndexedDBKey>& keys, + const std::vector<IndexedDBKey>& primary_keys, + const std::vector<WebKit::WebData>& values); + + void CachedContinue(WebKit::WebIDBCallbacks* callbacks); + void ResetPrefetchCache(); + + private: + FRIEND_TEST_ALL_PREFIXES(RendererWebIDBCursorImplTest, PrefetchTest); + + int32 ipc_cursor_id_; + + // Prefetch cache. + std::deque<IndexedDBKey> prefetch_keys_; + std::deque<IndexedDBKey> prefetch_primary_keys_; + std::deque<WebKit::WebData> prefetch_values_; + + // Number of continue calls that would qualify for a pre-fetch. + int continue_count_; + + // Number of items used from the last prefetch. + int used_prefetches_; + + // Number of onsuccess handlers we are waiting for. + int pending_onsuccess_callbacks_; + + // Number of items to request in next prefetch. + int prefetch_amount_; + + enum { kInvalidCursorId = -1 }; + enum { kPrefetchContinueThreshold = 2 }; + enum { kMinPrefetchAmount = 5 }; + enum { kMaxPrefetchAmount = 100 }; +}; + +} // namespace content + +#endif // CONTENT_CHILD_INDEXED_DB_PROXY_WEBIDBCURSOR_IMPL_H_ diff --git a/content/child/indexed_db/proxy_webidbcursor_impl_unittest.cc b/content/child/indexed_db/proxy_webidbcursor_impl_unittest.cc new file mode 100644 index 0000000..a9c6e24 --- /dev/null +++ b/content/child/indexed_db/proxy_webidbcursor_impl_unittest.cc @@ -0,0 +1,151 @@ +// 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 "base/memory/scoped_ptr.h" +#include "base/values.h" +#include "content/child/indexed_db/indexed_db_dispatcher.h" +#include "content/child/indexed_db/proxy_webidbcursor_impl.h" +#include "content/common/indexed_db/indexed_db_key.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/public/platform/WebData.h" +#include "third_party/WebKit/public/platform/WebIDBCallbacks.h" + +using WebKit::WebData; +using WebKit::WebIDBCallbacks; +using WebKit::WebIDBDatabase; +using WebKit::WebIDBDatabaseError; +using WebKit::WebIDBKey; + +namespace content { + +namespace { + +class MockDispatcher : public IndexedDBDispatcher { + public: + MockDispatcher() + : prefetch_calls_(0), + last_prefetch_count_(0), + continue_calls_(0), + destroyed_cursor_id_(0) {} + + virtual void RequestIDBCursorPrefetch(int n, + WebIDBCallbacks* callbacks, + int32 ipc_cursor_id) OVERRIDE { + ++prefetch_calls_; + last_prefetch_count_ = n; + callbacks_.reset(callbacks); + } + + virtual void RequestIDBCursorContinue(const IndexedDBKey&, + WebIDBCallbacks* callbacks, + int32 ipc_cursor_id) OVERRIDE { + ++continue_calls_; + callbacks_.reset(callbacks); + } + + virtual void CursorDestroyed(int32 ipc_cursor_id) OVERRIDE { + destroyed_cursor_id_ = ipc_cursor_id; + } + + int prefetch_calls() { return prefetch_calls_; } + int continue_calls() { return continue_calls_; } + int last_prefetch_count() { return last_prefetch_count_; } + int32 destroyed_cursor_id() { return destroyed_cursor_id_; } + + private: + int prefetch_calls_; + int last_prefetch_count_; + int continue_calls_; + int32 destroyed_cursor_id_; + scoped_ptr<WebIDBCallbacks> callbacks_; +}; + +class MockContinueCallbacks : public WebIDBCallbacks { + public: + MockContinueCallbacks(IndexedDBKey* key = 0) : key_(key) {} + + virtual void onSuccess(const WebIDBKey& key, + const WebIDBKey& primaryKey, + const WebData& value) { + + if (key_) + *key_ = IndexedDBKey(key); + } + + private: + IndexedDBKey* key_; +}; + +} // namespace + +TEST(RendererWebIDBCursorImplTest, PrefetchTest) { + + WebIDBKey null_key; + null_key.assignNull(); + + MockDispatcher dispatcher; + + { + RendererWebIDBCursorImpl cursor(RendererWebIDBCursorImpl::kInvalidCursorId); + + // Call continue() until prefetching should kick in. + int continue_calls = 0; + EXPECT_EQ(dispatcher.continue_calls(), 0); + for (int i = 0; i < RendererWebIDBCursorImpl::kPrefetchContinueThreshold; + ++i) { + cursor.continueFunction(null_key, new MockContinueCallbacks()); + EXPECT_EQ(++continue_calls, dispatcher.continue_calls()); + EXPECT_EQ(0, dispatcher.prefetch_calls()); + } + + // Do enough repetitions to verify that the count grows each time, + // but not so many that the maximum limit is hit. + const int kPrefetchRepetitions = 5; + + int expected_key = 0; + int last_prefetch_count = 0; + for (int repetitions = 0; repetitions < kPrefetchRepetitions; + ++repetitions) { + + // Initiate the prefetch + cursor.continueFunction(null_key, new MockContinueCallbacks()); + EXPECT_EQ(continue_calls, dispatcher.continue_calls()); + EXPECT_EQ(repetitions + 1, dispatcher.prefetch_calls()); + + // Verify that the requested count has increased since last time. + int prefetch_count = dispatcher.last_prefetch_count(); + EXPECT_GT(prefetch_count, last_prefetch_count); + last_prefetch_count = prefetch_count; + + // Fill the prefetch cache as requested. + std::vector<IndexedDBKey> keys; + std::vector<IndexedDBKey> primary_keys(prefetch_count); + std::vector<WebData> values(prefetch_count); + for (int i = 0; i < prefetch_count; ++i) { + keys.push_back(IndexedDBKey(expected_key + i, WebIDBKey::NumberType)); + } + cursor.SetPrefetchData(keys, primary_keys, values); + + // Note that the real dispatcher would call cursor->CachedContinue() + // immediately after cursor->SetPrefetchData() to service the request + // that initiated the prefetch. + + // Verify that the cache is used for subsequent continue() calls. + for (int i = 0; i < prefetch_count; ++i) { + IndexedDBKey key; + cursor.continueFunction(null_key, new MockContinueCallbacks(&key)); + EXPECT_EQ(continue_calls, dispatcher.continue_calls()); + EXPECT_EQ(repetitions + 1, dispatcher.prefetch_calls()); + + EXPECT_EQ(WebIDBKey::NumberType, key.type()); + EXPECT_EQ(expected_key++, key.number()); + } + } + } + + EXPECT_EQ(dispatcher.destroyed_cursor_id(), + RendererWebIDBCursorImpl::kInvalidCursorId); +} + +} // namespace content diff --git a/content/child/indexed_db/proxy_webidbdatabase_impl.cc b/content/child/indexed_db/proxy_webidbdatabase_impl.cc new file mode 100644 index 0000000..d120a85 --- /dev/null +++ b/content/child/indexed_db/proxy_webidbdatabase_impl.cc @@ -0,0 +1,264 @@ +// 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/indexed_db/proxy_webidbdatabase_impl.h" + +#include <vector> + +#include "content/child/indexed_db/indexed_db_dispatcher.h" +#include "content/common/child_thread.h" +#include "content/common/indexed_db/indexed_db_messages.h" +#include "third_party/WebKit/public/platform/WebIDBKeyPath.h" +#include "third_party/WebKit/public/platform/WebIDBMetadata.h" +#include "third_party/WebKit/public/platform/WebString.h" +#include "third_party/WebKit/public/platform/WebVector.h" +#include "webkit/glue/worker_task_runner.h" + +using WebKit::WebIDBCallbacks; +using WebKit::WebIDBDatabaseCallbacks; +using WebKit::WebIDBMetadata; +using WebKit::WebIDBKeyPath; +using WebKit::WebString; +using WebKit::WebVector; +using webkit_glue::WorkerTaskRunner; + +namespace content { + +RendererWebIDBDatabaseImpl::RendererWebIDBDatabaseImpl( + int32 ipc_database_id, int32 ipc_database_callbacks_id) + : ipc_database_id_(ipc_database_id), + ipc_database_callbacks_id_(ipc_database_callbacks_id) { +} + +RendererWebIDBDatabaseImpl::~RendererWebIDBDatabaseImpl() { + // It's not possible for there to be pending callbacks that address this + // object since inside WebKit, they hold a reference to the object which owns + // this object. But, if that ever changed, then we'd need to invalidate + // any such pointers. + IndexedDBDispatcher::Send(new IndexedDBHostMsg_DatabaseDestroyed( + ipc_database_id_)); + IndexedDBDispatcher* dispatcher = + IndexedDBDispatcher::ThreadSpecificInstance(); + dispatcher->DatabaseDestroyed(ipc_database_id_); +} + +void RendererWebIDBDatabaseImpl::createObjectStore( + long long transaction_id, + long long object_store_id, + const WebKit::WebString& name, + const WebKit::WebIDBKeyPath& key_path, + bool auto_increment) { + IndexedDBHostMsg_DatabaseCreateObjectStore_Params params; + params.ipc_database_id = ipc_database_id_; + params.transaction_id = transaction_id; + params.object_store_id = object_store_id; + params.name = name; + params.key_path = IndexedDBKeyPath(key_path); + params.auto_increment = auto_increment; + + IndexedDBDispatcher::Send( + new IndexedDBHostMsg_DatabaseCreateObjectStore(params)); +} + +void RendererWebIDBDatabaseImpl::deleteObjectStore( + long long transaction_id, + long long object_store_id) { + IndexedDBDispatcher::Send( + new IndexedDBHostMsg_DatabaseDeleteObjectStore( + ipc_database_id_, + transaction_id, + object_store_id)); +} + +void RendererWebIDBDatabaseImpl::createTransaction( + long long transaction_id, + WebKit::WebIDBDatabaseCallbacks* callbacks, + const WebVector<long long>& object_store_ids, + unsigned short mode) +{ + IndexedDBDispatcher* dispatcher = + IndexedDBDispatcher::ThreadSpecificInstance(); + dispatcher->RequestIDBDatabaseCreateTransaction(ipc_database_id_, + transaction_id, + callbacks, + object_store_ids, + mode); +} + +void RendererWebIDBDatabaseImpl::close() { + IndexedDBDispatcher* dispatcher = + IndexedDBDispatcher::ThreadSpecificInstance(); + dispatcher->RequestIDBDatabaseClose(ipc_database_id_, + ipc_database_callbacks_id_); +} + +void RendererWebIDBDatabaseImpl::get( + long long transaction_id, + long long object_store_id, + long long index_id, + const WebKit::WebIDBKeyRange& key_range, + bool key_only, + WebIDBCallbacks* callbacks) { + IndexedDBDispatcher* dispatcher = + IndexedDBDispatcher::ThreadSpecificInstance(); + dispatcher->RequestIDBDatabaseGet( + ipc_database_id_, transaction_id, object_store_id, index_id, + IndexedDBKeyRange(key_range), key_only, callbacks); +} + +void RendererWebIDBDatabaseImpl::put( + long long transaction_id, + long long object_store_id, + const WebKit::WebData& value, + const WebKit::WebIDBKey& key, + PutMode put_mode, + WebIDBCallbacks* callbacks, + const WebVector<long long>& web_index_ids, + const WebVector<WebIndexKeys>& web_index_keys) { + IndexedDBDispatcher* dispatcher = + IndexedDBDispatcher::ThreadSpecificInstance(); + dispatcher->RequestIDBDatabasePut( + ipc_database_id_, transaction_id, object_store_id, + value, IndexedDBKey(key), put_mode, callbacks, + web_index_ids, web_index_keys); +} + +void RendererWebIDBDatabaseImpl::setIndexKeys( + long long transaction_id, + long long object_store_id, + const WebKit::WebIDBKey& primary_key, + const WebVector<long long>& index_ids, + const WebVector<WebIndexKeys>& index_keys) { + IndexedDBHostMsg_DatabaseSetIndexKeys_Params params; + params.ipc_database_id = ipc_database_id_; + params.transaction_id = transaction_id; + params.object_store_id = object_store_id; + params.primary_key = IndexedDBKey(primary_key); + COMPILE_ASSERT(sizeof(params.index_ids[0]) == + sizeof(index_ids[0]), Cant_copy); + params.index_ids.assign(index_ids.data(), + index_ids.data() + index_ids.size()); + + params.index_keys.resize(index_keys.size()); + for (size_t i = 0; i < index_keys.size(); ++i) { + params.index_keys[i].resize(index_keys[i].size()); + for (size_t j = 0; j < index_keys[i].size(); ++j) { + params.index_keys[i][j] = content::IndexedDBKey(index_keys[i][j]); + } + } + IndexedDBDispatcher::Send(new IndexedDBHostMsg_DatabaseSetIndexKeys( + params)); +} + +void RendererWebIDBDatabaseImpl::setIndexesReady( + long long transaction_id, + long long object_store_id, + const WebVector<long long>& web_index_ids) { + std::vector<int64> index_ids(web_index_ids.data(), + web_index_ids.data() + web_index_ids.size()); + IndexedDBDispatcher::Send(new IndexedDBHostMsg_DatabaseSetIndexesReady( + ipc_database_id_, transaction_id, object_store_id, index_ids)); +} + +void RendererWebIDBDatabaseImpl::openCursor( + long long transaction_id, + long long object_store_id, + long long index_id, + const WebKit::WebIDBKeyRange& key_range, + unsigned short direction, + bool key_only, + TaskType task_type, + WebIDBCallbacks* callbacks) { + IndexedDBDispatcher* dispatcher = + IndexedDBDispatcher::ThreadSpecificInstance(); + dispatcher->RequestIDBDatabaseOpenCursor( + ipc_database_id_, + transaction_id, object_store_id, index_id, + IndexedDBKeyRange(key_range), direction, key_only, task_type, callbacks); +} + +void RendererWebIDBDatabaseImpl::count( + long long transaction_id, + long long object_store_id, + long long index_id, + const WebKit::WebIDBKeyRange& key_range, + WebIDBCallbacks* callbacks) { + IndexedDBDispatcher* dispatcher = + IndexedDBDispatcher::ThreadSpecificInstance(); + dispatcher->RequestIDBDatabaseCount( + ipc_database_id_, + transaction_id, object_store_id, index_id, + IndexedDBKeyRange(key_range), callbacks); +} + +void RendererWebIDBDatabaseImpl::deleteRange( + long long transaction_id, + long long object_store_id, + const WebKit::WebIDBKeyRange& key_range, + WebIDBCallbacks* callbacks) { + IndexedDBDispatcher* dispatcher = + IndexedDBDispatcher::ThreadSpecificInstance(); + dispatcher->RequestIDBDatabaseDeleteRange( + ipc_database_id_, + transaction_id, object_store_id, + IndexedDBKeyRange(key_range), callbacks); +} + +void RendererWebIDBDatabaseImpl::clear( + long long transaction_id, + long long object_store_id, + WebIDBCallbacks* callbacks) { + IndexedDBDispatcher* dispatcher = + IndexedDBDispatcher::ThreadSpecificInstance(); + dispatcher->RequestIDBDatabaseClear( + ipc_database_id_, + transaction_id, object_store_id, callbacks); +} + +void RendererWebIDBDatabaseImpl::createIndex( + long long transaction_id, + long long object_store_id, + long long index_id, + const WebString& name, + const WebIDBKeyPath& key_path, + bool unique, + bool multi_entry) +{ + IndexedDBHostMsg_DatabaseCreateIndex_Params params; + params.ipc_database_id = ipc_database_id_; + params.transaction_id = transaction_id; + params.object_store_id = object_store_id; + params.index_id = index_id; + params.name = name; + params.key_path = IndexedDBKeyPath(key_path); + params.unique = unique; + params.multi_entry = multi_entry; + + IndexedDBDispatcher::Send( + new IndexedDBHostMsg_DatabaseCreateIndex(params)); +} + +void RendererWebIDBDatabaseImpl::deleteIndex( + long long transaction_id, + long long object_store_id, + long long index_id) +{ + IndexedDBDispatcher::Send( + new IndexedDBHostMsg_DatabaseDeleteIndex( + ipc_database_id_, + transaction_id, + object_store_id, index_id)); +} + +void RendererWebIDBDatabaseImpl::abort(long long transaction_id) { + IndexedDBDispatcher::Send(new IndexedDBHostMsg_DatabaseAbort( + ipc_database_id_, transaction_id)); +} + +void RendererWebIDBDatabaseImpl::commit(long long transaction_id) { + IndexedDBDispatcher::Send(new IndexedDBHostMsg_DatabaseCommit( + ipc_database_id_, transaction_id)); +} + +} // namespace content diff --git a/content/child/indexed_db/proxy_webidbdatabase_impl.h b/content/child/indexed_db/proxy_webidbdatabase_impl.h new file mode 100644 index 0000000..7a1b31a --- /dev/null +++ b/content/child/indexed_db/proxy_webidbdatabase_impl.h @@ -0,0 +1,103 @@ +// 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. + +#ifndef CONTENT_CHILD_INDEXED_DB_PROXY_WEBIDBDATABASE_IMPL_H_ +#define CONTENT_CHILD_INDEXED_DB_PROXY_WEBIDBDATABASE_IMPL_H_ + +#include "base/basictypes.h" +#include "third_party/WebKit/public/platform/WebIDBDatabase.h" + +namespace WebKit { +class WebIDBCallbacks; +class WebIDBDatabaseCallbacks; +class WebString; +} + +namespace content { + +class RendererWebIDBDatabaseImpl : public WebKit::WebIDBDatabase { + public: + explicit RendererWebIDBDatabaseImpl(int32 ipc_database_id, + int32 ipc_database_callbacks_id); + virtual ~RendererWebIDBDatabaseImpl(); + + // WebKit::WebIDBDatabase + virtual void createObjectStore( + long long transaction_id, + long long objectstore_id, + const WebKit::WebString& name, + const WebKit::WebIDBKeyPath& key_path, + bool auto_increment); + virtual void deleteObjectStore( + long long transaction_id, + long long object_store_id); + virtual void createTransaction( + long long transaction_id, + WebKit::WebIDBDatabaseCallbacks* callbacks, + const WebKit::WebVector<long long>& scope, + unsigned short mode); + virtual void close(); + virtual void get(long long transactionId, + long long objectStoreId, + long long indexId, + const WebKit::WebIDBKeyRange&, + bool keyOnly, + WebKit::WebIDBCallbacks*); + virtual void put(long long transactionId, + long long objectStoreId, + const WebKit::WebData& value, + const WebKit::WebIDBKey&, + PutMode, + WebKit::WebIDBCallbacks*, + const WebKit::WebVector<long long>& indexIds, + const WebKit::WebVector<WebIndexKeys>&); + virtual void setIndexKeys(long long transactionId, + long long objectStoreId, + const WebKit::WebIDBKey&, + const WebKit::WebVector<long long>& indexIds, + const WebKit::WebVector<WebIndexKeys>&); + virtual void setIndexesReady(long long transactionId, + long long objectStoreId, + const WebKit::WebVector<long long>& indexIds); + virtual void openCursor(long long transactionId, + long long objectStoreId, + long long indexId, + const WebKit::WebIDBKeyRange&, + unsigned short direction, + bool keyOnly, + TaskType, + WebKit::WebIDBCallbacks*); + virtual void count(long long transactionId, + long long objectStoreId, + long long indexId, + const WebKit::WebIDBKeyRange&, + WebKit::WebIDBCallbacks*); + virtual void deleteRange(long long transactionId, + long long objectStoreId, + const WebKit::WebIDBKeyRange&, + WebKit::WebIDBCallbacks*); + virtual void clear(long long transactionId, + long long objectStoreId, + WebKit::WebIDBCallbacks*); + virtual void createIndex(long long transactionId, + long long objectStoreId, + long long indexId, + const WebKit::WebString& name, + const WebKit::WebIDBKeyPath&, + bool unique, + bool multiEntry); + virtual void deleteIndex(long long transactionId, long + long objectStoreId, + long long indexId); + virtual void abort(long long transaction_id); + virtual void commit(long long transaction_id); + + private: + int32 ipc_database_id_; + int32 ipc_database_callbacks_id_; +}; + +} // namespace content + +#endif // CONTENT_CHILD_INDEXED_DB_PROXY_WEBIDBDATABASE_IMPL_H_ diff --git a/content/child/indexed_db/proxy_webidbfactory_impl.cc b/content/child/indexed_db/proxy_webidbfactory_impl.cc new file mode 100644 index 0000000..42a561d --- /dev/null +++ b/content/child/indexed_db/proxy_webidbfactory_impl.cc @@ -0,0 +1,64 @@ +// 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/indexed_db/proxy_webidbfactory_impl.h" + +#include "content/child/indexed_db/indexed_db_dispatcher.h" +#include "content/common/child_thread.h" +#include "third_party/WebKit/public/platform/WebString.h" + +using WebKit::WebIDBCallbacks; +using WebKit::WebIDBDatabase; +using WebKit::WebIDBDatabaseCallbacks; +using WebKit::WebString; + +namespace content { + +RendererWebIDBFactoryImpl::RendererWebIDBFactoryImpl() { +} + +RendererWebIDBFactoryImpl::~RendererWebIDBFactoryImpl() { +} + +void RendererWebIDBFactoryImpl::getDatabaseNames( + WebIDBCallbacks* callbacks, + const WebString& database_identifier, + const WebString& data_dir_unused) { + IndexedDBDispatcher* dispatcher = + IndexedDBDispatcher::ThreadSpecificInstance(); + dispatcher->RequestIDBFactoryGetDatabaseNames( + callbacks, database_identifier); +} + +void RendererWebIDBFactoryImpl::open( + const WebString& name, + long long version, + long long transaction_id, + WebIDBCallbacks* callbacks, + WebIDBDatabaseCallbacks* database_callbacks, + const WebString& database_identifier, + const WebString& data_dir) { + // Don't send the data_dir. We know what we want on the Browser side of + // things. + IndexedDBDispatcher* dispatcher = + IndexedDBDispatcher::ThreadSpecificInstance(); + dispatcher->RequestIDBFactoryOpen( + name, version, transaction_id, callbacks, database_callbacks, + database_identifier); +} + +void RendererWebIDBFactoryImpl::deleteDatabase( + const WebString& name, + WebIDBCallbacks* callbacks, + const WebString& database_identifier, + const WebString& data_dir) { + // Don't send the data_dir. We know what we want on the Browser side of + // things. + IndexedDBDispatcher* dispatcher = + IndexedDBDispatcher::ThreadSpecificInstance(); + dispatcher->RequestIDBFactoryDeleteDatabase( + name, callbacks, database_identifier); +} + +} // namespace content diff --git a/content/child/indexed_db/proxy_webidbfactory_impl.h b/content/child/indexed_db/proxy_webidbfactory_impl.h new file mode 100644 index 0000000..778838b --- /dev/null +++ b/content/child/indexed_db/proxy_webidbfactory_impl.h @@ -0,0 +1,46 @@ +// 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. + +#ifndef CONTENT_CHILD_INDEXED_DB_PROXY_WEBIDBFACTORY_IMPL_H_ +#define CONTENT_CHILD_INDEXED_DB_PROXY_WEBIDBFACTORY_IMPL_H_ + +#include "third_party/WebKit/public/platform/WebIDBCallbacks.h" +#include "third_party/WebKit/public/platform/WebIDBDatabaseCallbacks.h" +#include "third_party/WebKit/public/platform/WebIDBFactory.h" +#include "third_party/WebKit/public/platform/WebVector.h" + +namespace WebKit { +class WebString; +} + +namespace content { + +class RendererWebIDBFactoryImpl : public WebKit::WebIDBFactory { + public: + RendererWebIDBFactoryImpl(); + virtual ~RendererWebIDBFactoryImpl(); + + // See WebIDBFactory.h for documentation on these functions. + virtual void getDatabaseNames( + WebKit::WebIDBCallbacks* callbacks, + const WebKit::WebString& database_identifier, + const WebKit::WebString& data_dir); + virtual void open( + const WebKit::WebString& name, + long long version, + long long transaction_id, + WebKit::WebIDBCallbacks* callbacks, + WebKit::WebIDBDatabaseCallbacks* databaseCallbacks, + const WebKit::WebString& database_identifier, + const WebKit::WebString& data_dir); + virtual void deleteDatabase( + const WebKit::WebString& name, + WebKit::WebIDBCallbacks* callbacks, + const WebKit::WebString& database_identifier, + const WebKit::WebString& data_dir); +}; + +} // namespace content + +#endif // CONTENT_CHILD_INDEXED_DB_PROXY_WEBIDBFACTORY_IMPL_H_ diff --git a/content/child/np_channel_base.cc b/content/child/np_channel_base.cc new file mode 100644 index 0000000..34aa22b --- /dev/null +++ b/content/child/np_channel_base.cc @@ -0,0 +1,305 @@ +// 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/np_channel_base.h" + +#include <stack> + +#include "base/auto_reset.h" +#include "base/hash_tables.h" +#include "base/lazy_instance.h" +#include "base/string_number_conversions.h" +#include "ipc/ipc_sync_message.h" + +#if defined(OS_POSIX) +#include "ipc/ipc_channel_posix.h" +#endif + +namespace content { + +typedef base::hash_map<std::string, scoped_refptr<NPChannelBase> > ChannelMap; +static base::LazyInstance<ChannelMap>::Leaky + g_channels = LAZY_INSTANCE_INITIALIZER; + +typedef std::stack<scoped_refptr<NPChannelBase> > NPChannelRefStack; +static base::LazyInstance<NPChannelRefStack>::Leaky + g_lazy_channel_stack = LAZY_INSTANCE_INITIALIZER; + +NPChannelBase* NPChannelBase::GetChannel( + const IPC::ChannelHandle& channel_handle, IPC::Channel::Mode mode, + ChannelFactory factory, base::MessageLoopProxy* ipc_message_loop, + bool create_pipe_now, base::WaitableEvent* shutdown_event) { + scoped_refptr<NPChannelBase> channel; + std::string channel_key = channel_handle.name; + ChannelMap::const_iterator iter = g_channels.Get().find(channel_key); + if (iter == g_channels.Get().end()) { + channel = factory(); + } else { + channel = iter->second; + } + + DCHECK(channel.get() != NULL); + + if (!channel->channel_valid()) { + channel->channel_handle_ = channel_handle; + if (mode & IPC::Channel::MODE_SERVER_FLAG) { + channel->channel_handle_.name = + IPC::Channel::GenerateVerifiedChannelID(channel_key); + } + channel->mode_ = mode; + if (channel->Init(ipc_message_loop, create_pipe_now, shutdown_event)) { + g_channels.Get()[channel_key] = channel; + } else { + channel = NULL; + } + } + + return channel.get(); +} + +void NPChannelBase::Broadcast(IPC::Message* message) { + for (ChannelMap::iterator iter = g_channels.Get().begin(); + iter != g_channels.Get().end(); + ++iter) { + iter->second->Send(new IPC::Message(*message)); + } + delete message; +} + +NPChannelBase::NPChannelBase() + : mode_(IPC::Channel::MODE_NONE), + non_npobject_count_(0), + peer_pid_(0), + in_remove_route_(false), + channel_valid_(false), + in_unblock_dispatch_(0), + send_unblocking_only_during_unblock_dispatch_(false) { +} + +NPChannelBase::~NPChannelBase() { +} + +NPChannelBase* NPChannelBase::GetCurrentChannel() { + return g_lazy_channel_stack.Pointer()->top().get(); +} + +void NPChannelBase::CleanupChannels() { + // Make a copy of the references as we can't iterate the map since items will + // be removed from it as we clean them up. + std::vector<scoped_refptr<NPChannelBase> > channels; + for (ChannelMap::const_iterator iter = g_channels.Get().begin(); + iter != g_channels.Get().end(); + ++iter) { + channels.push_back(iter->second); + } + + for (size_t i = 0; i < channels.size(); ++i) + channels[i]->CleanUp(); + + // This will clean up channels added to the map for which subsequent + // AddRoute wasn't called + g_channels.Get().clear(); +} + +NPObjectBase* NPChannelBase::GetNPObjectListenerForRoute(int route_id) { + ListenerMap::iterator iter = npobject_listeners_.find(route_id); + if (iter == npobject_listeners_.end()) { + DLOG(WARNING) << "Invalid route id passed in:" << route_id; + return NULL; + } + return iter->second; +} + +base::WaitableEvent* NPChannelBase::GetModalDialogEvent(int render_view_id) { + return NULL; +} + +bool NPChannelBase::Init(base::MessageLoopProxy* ipc_message_loop, + bool create_pipe_now, + base::WaitableEvent* shutdown_event) { +#if defined(OS_POSIX) + // Attempting to initialize with an invalid channel handle. + // See http://crbug.com/97285 for details. + if (mode_ == IPC::Channel::MODE_CLIENT && -1 == channel_handle_.socket.fd) + return false; +#endif + + channel_.reset(new IPC::SyncChannel( + channel_handle_, mode_, this, ipc_message_loop, create_pipe_now, + shutdown_event)); + +#if defined(OS_POSIX) + // Check the validity of fd for bug investigation. Remove after fixed. + // See crbug.com/97285 for details. + if (mode_ == IPC::Channel::MODE_SERVER) + CHECK_NE(-1, channel_->GetClientFileDescriptor()); +#endif + + channel_valid_ = true; + return true; +} + +bool NPChannelBase::Send(IPC::Message* message) { + if (!channel_) { + VLOG(1) << "Channel is NULL; dropping message"; + delete message; + return false; + } + + if (send_unblocking_only_during_unblock_dispatch_ && + in_unblock_dispatch_ == 0 && + message->is_sync()) { + message->set_unblock(false); + } + + return channel_->Send(message); +} + +int NPChannelBase::Count() { + return static_cast<int>(g_channels.Get().size()); +} + +bool NPChannelBase::OnMessageReceived(const IPC::Message& message) { + // This call might cause us to be deleted, so keep an extra reference to + // ourself so that we can send the reply and decrement back in_dispatch_. + g_lazy_channel_stack.Pointer()->push( + scoped_refptr<NPChannelBase>(this)); + + bool handled; + if (message.should_unblock()) + in_unblock_dispatch_++; + if (message.routing_id() == MSG_ROUTING_CONTROL) { + handled = OnControlMessageReceived(message); + } else { + handled = router_.RouteMessage(message); + if (!handled && message.is_sync()) { + // The listener has gone away, so we must respond or else the caller will + // hang waiting for a reply. + IPC::Message* reply = IPC::SyncMessage::GenerateReply(&message); + reply->set_reply_error(); + Send(reply); + } + } + if (message.should_unblock()) + in_unblock_dispatch_--; + + g_lazy_channel_stack.Pointer()->pop(); + return handled; +} + +void NPChannelBase::OnChannelConnected(int32 peer_pid) { + peer_pid_ = peer_pid; +} + +void NPChannelBase::AddRoute(int route_id, + IPC::Listener* listener, + NPObjectBase* npobject) { + if (npobject) { + npobject_listeners_[route_id] = npobject; + } else { + non_npobject_count_++; + } + + router_.AddRoute(route_id, listener); +} + +void NPChannelBase::RemoveRoute(int route_id) { + router_.RemoveRoute(route_id); + + ListenerMap::iterator iter = npobject_listeners_.find(route_id); + if (iter != npobject_listeners_.end()) { + // This was an NPObject proxy or stub, it's not involved in the refcounting. + + // If this RemoveRoute call from the NPObject is a result of us calling + // OnChannelError below, don't call erase() here because that'll corrupt + // the iterator below. + if (in_remove_route_) { + iter->second = NULL; + } else { + npobject_listeners_.erase(iter); + } + + return; + } + + non_npobject_count_--; + DCHECK(non_npobject_count_ >= 0); + + if (!non_npobject_count_) { + base::AutoReset<bool> auto_reset_in_remove_route(&in_remove_route_, true); + for (ListenerMap::iterator npobj_iter = npobject_listeners_.begin(); + npobj_iter != npobject_listeners_.end(); ++npobj_iter) { + if (npobj_iter->second) { + npobj_iter->second->GetChannelListener()->OnChannelError(); + } + } + + for (ChannelMap::iterator iter = g_channels.Get().begin(); + iter != g_channels.Get().end(); ++iter) { + if (iter->second.get() == this) { + g_channels.Get().erase(iter); + return; + } + } + + NOTREACHED(); + } +} + +bool NPChannelBase::OnControlMessageReceived(const IPC::Message& msg) { + NOTREACHED() << + "should override in subclass if you care about control messages"; + return false; +} + +void NPChannelBase::OnChannelError() { + channel_valid_ = false; + + // TODO(shess): http://crbug.com/97285 + // Once an error is seen on a channel, remap the channel to prevent + // it from being vended again. Keep the channel in the map so + // RemoveRoute() can clean things up correctly. + for (ChannelMap::iterator iter = g_channels.Get().begin(); + iter != g_channels.Get().end(); ++iter) { + if (iter->second.get() == this) { + // Insert new element before invalidating |iter|. + g_channels.Get()[iter->first + "-error"] = iter->second; + g_channels.Get().erase(iter); + break; + } + } +} + +NPObject* NPChannelBase::GetExistingNPObjectProxy(int route_id) { + ProxyMap::iterator iter = proxy_map_.find(route_id); + return iter != proxy_map_.end() ? iter->second : NULL; +} + +int NPChannelBase::GetExistingRouteForNPObjectStub(NPObject* npobject) { + StubMap::iterator iter = stub_map_.find(npobject); + return iter != stub_map_.end() ? iter->second : MSG_ROUTING_NONE; +} + +void NPChannelBase::AddMappingForNPObjectProxy(int route_id, + NPObject* object) { + proxy_map_[route_id] = object; +} + +void NPChannelBase::AddMappingForNPObjectStub(int route_id, + NPObject* object) { + DCHECK(object != NULL); + stub_map_[object] = route_id; +} + +void NPChannelBase::RemoveMappingForNPObjectStub(int route_id, + NPObject* object) { + DCHECK(object != NULL); + stub_map_.erase(object); +} + +void NPChannelBase::RemoveMappingForNPObjectProxy(int route_id) { + proxy_map_.erase(route_id); +} + +} // namespace content diff --git a/content/child/np_channel_base.h b/content/child/np_channel_base.h new file mode 100644 index 0000000..02ada3e --- /dev/null +++ b/content/child/np_channel_base.h @@ -0,0 +1,203 @@ +// 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. + +#ifndef CONTENT_CHILD_NP_CHANNEL_BASE_H_ +#define CONTENT_CHILD_NP_CHANNEL_BASE_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/hash_tables.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/process.h" +#include "content/child/npobject_base.h" +#include "content/common/message_router.h" +#include "ipc/ipc_channel_handle.h" +#include "ipc/ipc_sync_channel.h" + +namespace base { +class MessageLoopProxy; +} + +#if defined(COMPILER_GCC) +namespace BASE_HASH_NAMESPACE { + +template<> +struct hash<NPObject*> { + std::size_t operator()(NPObject* const& ptr) const { + return hash<size_t>()(reinterpret_cast<size_t>(ptr)); + } +}; + +} // namespace __gnu_cxx +#elif defined(COMPILER_MSVC) +namespace stdext { + +template<> +inline size_t hash_value(NPObject* const& ptr) { + return hash_value(reinterpret_cast<size_t>(ptr)); +} + +} // namespace stdext +#endif // COMPILER + +namespace content { + +// Encapsulates an IPC channel between a renderer and another process. Used to +// proxy access to NP objects. +class NPChannelBase : public IPC::Listener, + public IPC::Sender, + public base::RefCountedThreadSafe<NPChannelBase> { + public: + + // WebPlugin[Delegate] call these on construction and destruction to setup + // the routing and manage lifetime of this object (they pass NULL for + // npobject). These are also called by NPObjectProxy and NPObjectStub (which + // pass themselves for npobject). However the latter don't control the + // lifetime of this object because we don't want a leak of an NPObject to + // keep the channel around longer than necessary. + void AddRoute(int route_id, IPC::Listener* listener, NPObjectBase* npobject); + void RemoveRoute(int route_id); + + + void AddMappingForNPObjectProxy(int route_id, NPObject* object); + void RemoveMappingForNPObjectProxy(int route_id); + + void AddMappingForNPObjectStub(int route_id, NPObject* object); + void RemoveMappingForNPObjectStub(int route_id, NPObject* object); + + NPObject* GetExistingNPObjectProxy(int route_id); + int GetExistingRouteForNPObjectStub(NPObject* npobject); + + + // IPC::Sender implementation: + virtual bool Send(IPC::Message* msg) OVERRIDE; + + base::ProcessId peer_pid() { return channel_->peer_pid(); } + IPC::ChannelHandle channel_handle() const { return channel_handle_; } + + // Returns the number of open NPObject channels in this process. + static int Count(); + + // Returns a new route id. + virtual int GenerateRouteID() = 0; + + // Returns whether the channel is valid or not. A channel is invalid + // if it is disconnected due to a channel error. + bool channel_valid() { + return channel_valid_; + } + + // Returns the most recent NPChannelBase to have received a message + // in this process. + static NPChannelBase* GetCurrentChannel(); + + static void CleanupChannels(); + + // Returns the NPObjectBase object for the route id passed in. + // Returns NULL on failure. + NPObjectBase* GetNPObjectListenerForRoute(int route_id); + + // Returns the event that's set when a call to the renderer causes a modal + // dialog to come up. The default implementation returns NULL. Derived + // classes should override this method if this functionality is required. + virtual base::WaitableEvent* GetModalDialogEvent(int render_view_id); + + protected: + typedef NPChannelBase* (*ChannelFactory)(); + + friend class base::RefCountedThreadSafe<NPChannelBase>; + + virtual ~NPChannelBase(); + + // Returns a NPChannelBase derived object for the given channel name. + // If an existing channel exists returns that object, otherwise creates a + // new one. Even though on creation the object is refcounted, each caller + // must still ref count the returned value. When there are no more routes + // on the channel and its ref count is 0, the object deletes itself. + static NPChannelBase* GetChannel( + const IPC::ChannelHandle& channel_handle, IPC::Channel::Mode mode, + ChannelFactory factory, base::MessageLoopProxy* ipc_message_loop, + bool create_pipe_now, base::WaitableEvent* shutdown_event); + + // Sends a message to all instances. + static void Broadcast(IPC::Message* message); + + // Called on the worker thread + NPChannelBase(); + + virtual void CleanUp() { } + + // Implemented by derived classes to handle control messages + virtual bool OnControlMessageReceived(const IPC::Message& msg); + + // IPC::Listener implementation: + virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE; + virtual void OnChannelConnected(int32 peer_pid) OVERRIDE; + virtual void OnChannelError() OVERRIDE; + + void set_send_unblocking_only_during_unblock_dispatch() { + send_unblocking_only_during_unblock_dispatch_ = true; + } + + virtual bool Init(base::MessageLoopProxy* ipc_message_loop, + bool create_pipe_now, + base::WaitableEvent* shutdown_event); + + scoped_ptr<IPC::SyncChannel> channel_; + IPC::ChannelHandle channel_handle_; + + private: + IPC::Channel::Mode mode_; + // This tracks the number of routes registered without an NPObject. It's used + // to manage the lifetime of this object. See comment for AddRoute() and + // RemoveRoute(). + int non_npobject_count_; + int peer_pid_; + + // true when in the middle of a RemoveRoute call + bool in_remove_route_; + + // Keep track of all the registered NPObjects proxies/stubs so that when the + // channel is closed we can inform them. + typedef base::hash_map<int, NPObjectBase*> ListenerMap; + ListenerMap npobject_listeners_; + + typedef base::hash_map<int, NPObject*> ProxyMap; + ProxyMap proxy_map_; + + typedef base::hash_map<NPObject*, int> StubMap; + StubMap stub_map_; + + // Used to implement message routing functionality to WebPlugin[Delegate] + // objects + MessageRouter router_; + + // A channel is invalid if it is disconnected as a result of a channel + // error. This flag is used to indicate the same. + bool channel_valid_; + + // Track whether we're dispatching a message with the unblock flag; works like + // a refcount, 0 when we're not. + int in_unblock_dispatch_; + + // If true, sync messages will only be marked as unblocking if the channel is + // in the middle of dispatching an unblocking message. The non-renderer + // process wants to avoid setting the unblock flag on its sync messages + // unless necessary, since it can potentially introduce reentrancy into + // WebKit in ways that it doesn't expect (i.e. causing layout during paint). + // However to avoid deadlock, we must ensure that any message that's sent as + // a result of a sync call from the renderer must unblock the renderer. We + // additionally have to do this for async messages from the renderer that + // have the unblock flag set, since they could be followed by a sync message + // that won't get dispatched until the call to the renderer is complete. + bool send_unblocking_only_during_unblock_dispatch_; + + DISALLOW_COPY_AND_ASSIGN(NPChannelBase); +}; + +} // namespace content + +#endif // CONTENT_CHILD_NP_CHANNEL_BASE_H_ diff --git a/content/child/npobject_base.h b/content/child/npobject_base.h new file mode 100644 index 0000000..042daf7 --- /dev/null +++ b/content/child/npobject_base.h @@ -0,0 +1,31 @@ +// 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. +// +// Base interface used by NPChannelBase and implemented by NPObjectProxy and +// NPObjectStub. + +#ifndef CONTENT_CHILD_NPOBJECT_BASE_H_ +#define CONTENT_CHILD_NPOBJECT_BASE_H_ + +#include "ipc/ipc_listener.h" +#include "third_party/npapi/bindings/npruntime.h" + +struct NPObject; + +namespace content { + +class NPObjectBase { + public: + virtual ~NPObjectBase() {} + + // Returns the underlying NPObject handled by this NPObjectBase instance. + virtual NPObject* GetUnderlyingNPObject() = 0; + + // Returns the channel listener for this NPObjectBase instance. + virtual IPC::Listener* GetChannelListener() = 0; +}; + +} // namespace content + +#endif // CONTENT_CHILD_NPOBJECT_BASE_H_ diff --git a/content/child/npobject_proxy.cc b/content/child/npobject_proxy.cc new file mode 100644 index 0000000..0d0b84d --- /dev/null +++ b/content/child/npobject_proxy.cc @@ -0,0 +1,507 @@ +// 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/npobject_proxy.h" + +#include "content/child/np_channel_base.h" +#include "content/child/npobject_util.h" +#include "content/child/plugin_messages.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebBindings.h" +#include "webkit/glue/webkit_glue.h" +#include "webkit/plugins/npapi/plugin_host.h" +#include "webkit/plugins/npapi/plugin_instance.h" + +using WebKit::WebBindings; + +namespace content { + +struct NPObjectWrapper { + NPObject object; + NPObjectProxy* proxy; +}; + +NPClass NPObjectProxy::npclass_proxy_ = { + NP_CLASS_STRUCT_VERSION, + NPObjectProxy::NPAllocate, + NPObjectProxy::NPDeallocate, + NPObjectProxy::NPPInvalidate, + NPObjectProxy::NPHasMethod, + NPObjectProxy::NPInvoke, + NPObjectProxy::NPInvokeDefault, + NPObjectProxy::NPHasProperty, + NPObjectProxy::NPGetProperty, + NPObjectProxy::NPSetProperty, + NPObjectProxy::NPRemoveProperty, + NPObjectProxy::NPNEnumerate, + NPObjectProxy::NPNConstruct +}; + +NPObjectProxy* NPObjectProxy::GetProxy(NPObject* object) { + NPObjectProxy* proxy = NULL; + + // Wrapper exists only for NPObjects that we had created. + if (&npclass_proxy_ == object->_class) { + NPObjectWrapper* wrapper = reinterpret_cast<NPObjectWrapper*>(object); + proxy = wrapper->proxy; + } + + return proxy; +} + +NPObject* NPObjectProxy::GetUnderlyingNPObject() { + return NULL; +} + +IPC::Listener* NPObjectProxy::GetChannelListener() { + return static_cast<IPC::Listener*>(this); +} + +NPObjectProxy::NPObjectProxy( + NPChannelBase* channel, + int route_id, + int render_view_id, + const GURL& page_url) + : channel_(channel), + route_id_(route_id), + render_view_id_(render_view_id), + page_url_(page_url) { + channel_->AddRoute(route_id, this, this); +} + +NPObjectProxy::~NPObjectProxy() { + if (channel_.get()) { + // This NPObjectProxy instance is now invalid and should not be reused for + // requests initiated by plugins. We may receive requests for the + // same NPObject in the context of the outgoing NPObjectMsg_Release call. + // We should be creating new NPObjectProxy instances to wrap these + // NPObjects. + channel_->RemoveMappingForNPObjectProxy(route_id_); + channel_->RemoveRoute(route_id_); + Send(new NPObjectMsg_Release(route_id_)); + } +} + +NPObject* NPObjectProxy::Create(NPChannelBase* channel, + int route_id, + int render_view_id, + const GURL& page_url) { + NPObjectWrapper* obj = reinterpret_cast<NPObjectWrapper*>( + WebBindings::createObject(0, &npclass_proxy_)); + obj->proxy = new NPObjectProxy(channel, route_id, render_view_id, page_url); + channel->AddMappingForNPObjectProxy(route_id, &obj->object); + return reinterpret_cast<NPObject*>(obj); +} + +bool NPObjectProxy::Send(IPC::Message* msg) { + if (channel_.get()) + return channel_->Send(msg); + + delete msg; + return false; +} + +NPObject* NPObjectProxy::NPAllocate(NPP, NPClass*) { + return reinterpret_cast<NPObject*>(new NPObjectWrapper); +} + +void NPObjectProxy::NPDeallocate(NPObject* npObj) { + NPObjectWrapper* obj = reinterpret_cast<NPObjectWrapper*>(npObj); + delete obj->proxy; + delete obj; +} + +bool NPObjectProxy::OnMessageReceived(const IPC::Message& msg) { + NOTREACHED(); + return false; +} + +void NPObjectProxy::OnChannelError() { + // Release our ref count of the plugin channel object, as it addrefs the + // process. + channel_ = NULL; +} + +bool NPObjectProxy::NPHasMethod(NPObject *obj, + NPIdentifier name) { + if (obj == NULL) + return false; + + bool result = false; + NPObjectProxy* proxy = GetProxy(obj); + + if (!proxy) { + return obj->_class->hasMethod(obj, name); + } + + NPIdentifier_Param name_param; + CreateNPIdentifierParam(name, &name_param); + + proxy->Send(new NPObjectMsg_HasMethod(proxy->route_id(), name_param, + &result)); + return result; +} + +bool NPObjectProxy::NPInvoke(NPObject *obj, + NPIdentifier name, + const NPVariant *args, + uint32_t arg_count, + NPVariant *result) { + return NPInvokePrivate(0, obj, false, name, args, arg_count, result); +} + +bool NPObjectProxy::NPInvokeDefault(NPObject *npobj, + const NPVariant *args, + uint32_t arg_count, + NPVariant *result) { + return NPInvokePrivate(0, npobj, true, 0, args, arg_count, result); +} + +bool NPObjectProxy::NPInvokePrivate(NPP npp, + NPObject *obj, + bool is_default, + NPIdentifier name, + const NPVariant *args, + uint32_t arg_count, + NPVariant *np_result) { + if (obj == NULL) + return false; + + NPObjectProxy* proxy = GetProxy(obj); + if (!proxy) { + if (is_default) { + return obj->_class->invokeDefault(obj, args, arg_count, np_result); + } else { + return obj->_class->invoke(obj, name, args, arg_count, np_result); + } + } + + bool result = false; + int render_view_id = proxy->render_view_id_; + NPIdentifier_Param name_param; + if (is_default) { + // The data won't actually get used, but set it so we don't send random + // data. + name_param.identifier = NULL; + } else { + CreateNPIdentifierParam(name, &name_param); + } + + // Note: This instance can get destroyed in the context of + // Send so addref the channel in this scope. + scoped_refptr<NPChannelBase> channel_copy = proxy->channel_; + std::vector<NPVariant_Param> args_param; + for (unsigned int i = 0; i < arg_count; ++i) { + NPVariant_Param param; + CreateNPVariantParam(args[i], + channel_copy.get(), + ¶m, + false, + render_view_id, + proxy->page_url_); + args_param.push_back(param); + } + + NPVariant_Param param_result; + NPObjectMsg_Invoke* msg = new NPObjectMsg_Invoke( + proxy->route_id_, is_default, name_param, args_param, ¶m_result, + &result); + + // If we're in the plugin process and this invoke leads to a dialog box, the + // plugin will hang the window hierarchy unless we pump the window message + // queue while waiting for a reply. We need to do this to simulate what + // happens when everything runs in-process (while calling MessageBox window + // messages are pumped). + if (IsPluginProcess() && proxy->channel()) { + msg->set_pump_messages_event( + proxy->channel()->GetModalDialogEvent(render_view_id)); + } + + GURL page_url = proxy->page_url_; + proxy->Send(msg); + + // Send may delete proxy. + proxy = NULL; + + if (!result) + return false; + + CreateNPVariant( + param_result, channel_copy.get(), np_result, render_view_id, page_url); + return true; +} + +bool NPObjectProxy::NPHasProperty(NPObject *obj, + NPIdentifier name) { + if (obj == NULL) + return false; + + bool result = false; + NPObjectProxy* proxy = GetProxy(obj); + if (!proxy) { + return obj->_class->hasProperty(obj, name); + } + + NPIdentifier_Param name_param; + CreateNPIdentifierParam(name, &name_param); + + NPVariant_Param param; + proxy->Send(new NPObjectMsg_HasProperty( + proxy->route_id(), name_param, &result)); + + // Send may delete proxy. + proxy = NULL; + + return result; +} + +bool NPObjectProxy::NPGetProperty(NPObject *obj, + NPIdentifier name, + NPVariant *np_result) { + // Please refer to http://code.google.com/p/chromium/issues/detail?id=2556, + // which was a crash in the XStandard plugin during plugin shutdown. The + // crash occured because the plugin requests the plugin script object, + // which fails. The plugin does not check the result of the operation and + // invokes NPN_GetProperty on a NULL object which lead to the crash. If + // we observe similar crashes in other methods in the future, these null + // checks may have to be replicated in the other methods in this class. + if (obj == NULL) + return false; + + NPObjectProxy* proxy = GetProxy(obj); + if (!proxy) { + return obj->_class->getProperty(obj, name, np_result); + } + + bool result = false; + int render_view_id = proxy->render_view_id_; + NPIdentifier_Param name_param; + CreateNPIdentifierParam(name, &name_param); + + NPVariant_Param param; + scoped_refptr<NPChannelBase> channel(proxy->channel_); + + GURL page_url = proxy->page_url_; + proxy->Send(new NPObjectMsg_GetProperty( + proxy->route_id(), name_param, ¶m, &result)); + // Send may delete proxy. + proxy = NULL; + if (!result) + return false; + + CreateNPVariant( + param, channel.get(), np_result, render_view_id, page_url); + + return true; +} + +bool NPObjectProxy::NPSetProperty(NPObject *obj, + NPIdentifier name, + const NPVariant *value) { + if (obj == NULL) + return false; + + NPObjectProxy* proxy = GetProxy(obj); + if (!proxy) { + return obj->_class->setProperty(obj, name, value); + } + + bool result = false; + int render_view_id = proxy->render_view_id_; + NPIdentifier_Param name_param; + CreateNPIdentifierParam(name, &name_param); + + NPVariant_Param value_param; + CreateNPVariantParam( + *value, proxy->channel(), &value_param, false, render_view_id, + proxy->page_url_); + + proxy->Send(new NPObjectMsg_SetProperty( + proxy->route_id(), name_param, value_param, &result)); + // Send may delete proxy. + proxy = NULL; + + return result; +} + +bool NPObjectProxy::NPRemoveProperty(NPObject *obj, + NPIdentifier name) { + if (obj == NULL) + return false; + + bool result = false; + NPObjectProxy* proxy = GetProxy(obj); + if (!proxy) { + return obj->_class->removeProperty(obj, name); + } + + NPIdentifier_Param name_param; + CreateNPIdentifierParam(name, &name_param); + + NPVariant_Param param; + proxy->Send(new NPObjectMsg_RemoveProperty( + proxy->route_id(), name_param, &result)); + // Send may delete proxy. + proxy = NULL; + + return result; +} + +void NPObjectProxy::NPPInvalidate(NPObject *obj) { + if (obj == NULL) + return; + + NPObjectProxy* proxy = GetProxy(obj); + if (!proxy) { + obj->_class->invalidate(obj); + return; + } + + proxy->Send(new NPObjectMsg_Invalidate(proxy->route_id())); + // Send may delete proxy. + proxy = NULL; +} + +bool NPObjectProxy::NPNEnumerate(NPObject *obj, + NPIdentifier **value, + uint32_t *count) { + if (obj == NULL) + return false; + + bool result = false; + NPObjectProxy* proxy = GetProxy(obj); + if (!proxy) { + if (obj->_class->structVersion >= NP_CLASS_STRUCT_VERSION_ENUM) { + return obj->_class->enumerate(obj, value, count); + } else { + return false; + } + } + + std::vector<NPIdentifier_Param> value_param; + proxy->Send(new NPObjectMsg_Enumeration( + proxy->route_id(), &value_param, &result)); + // Send may delete proxy. + proxy = NULL; + + if (!result) + return false; + + *count = static_cast<unsigned int>(value_param.size()); + *value = static_cast<NPIdentifier *>( + webkit::npapi::PluginHost::Singleton()->host_functions()->memalloc( + sizeof(NPIdentifier) * *count)); + for (unsigned int i = 0; i < *count; ++i) + (*value)[i] = CreateNPIdentifier(value_param[i]); + + return true; +} + +bool NPObjectProxy::NPNConstruct(NPObject *obj, + const NPVariant *args, + uint32_t arg_count, + NPVariant *np_result) { + if (obj == NULL) + return false; + + NPObjectProxy* proxy = GetProxy(obj); + if (!proxy) { + if (obj->_class->structVersion >= NP_CLASS_STRUCT_VERSION_CTOR) { + return obj->_class->construct(obj, args, arg_count, np_result); + } else { + return false; + } + } + + bool result = false; + int render_view_id = proxy->render_view_id_; + + // Note: This instance can get destroyed in the context of + // Send so addref the channel in this scope. + scoped_refptr<NPChannelBase> channel_copy = proxy->channel_; + std::vector<NPVariant_Param> args_param; + for (unsigned int i = 0; i < arg_count; ++i) { + NPVariant_Param param; + CreateNPVariantParam(args[i], + channel_copy.get(), + ¶m, + false, + render_view_id, + proxy->page_url_); + args_param.push_back(param); + } + + NPVariant_Param param_result; + NPObjectMsg_Construct* msg = new NPObjectMsg_Construct( + proxy->route_id_, args_param, ¶m_result, &result); + + // See comment in NPObjectProxy::NPInvokePrivate. + if (IsPluginProcess() && proxy->channel()) { + msg->set_pump_messages_event( + proxy->channel()->GetModalDialogEvent(proxy->render_view_id_)); + } + + GURL page_url = proxy->page_url_; + proxy->Send(msg); + + // Send may delete proxy. + proxy = NULL; + + if (!result) + return false; + + CreateNPVariant( + param_result, channel_copy.get(), np_result, render_view_id, page_url); + return true; +} + +bool NPObjectProxy::NPNEvaluate(NPP npp, + NPObject *obj, + NPString *script, + NPVariant *result_var) { + NPObjectProxy* proxy = GetProxy(obj); + if (!proxy) { + return false; + } + + bool result = false; + int render_view_id = proxy->render_view_id_; + bool popups_allowed = false; + + if (npp) { + webkit::npapi::PluginInstance* plugin_instance = + reinterpret_cast<webkit::npapi::PluginInstance*>(npp->ndata); + if (plugin_instance) + popups_allowed = plugin_instance->popups_allowed(); + } + + NPVariant_Param result_param; + std::string script_str = std::string( + script->UTF8Characters, script->UTF8Length); + + NPObjectMsg_Evaluate* msg = new NPObjectMsg_Evaluate(proxy->route_id(), + script_str, + popups_allowed, + &result_param, + &result); + + // See comment in NPObjectProxy::NPInvokePrivate. + if (IsPluginProcess() && proxy->channel()) { + msg->set_pump_messages_event( + proxy->channel()->GetModalDialogEvent(render_view_id)); + } + scoped_refptr<NPChannelBase> channel(proxy->channel_); + + GURL page_url = proxy->page_url_; + proxy->Send(msg); + // Send may delete proxy. + proxy = NULL; + if (!result) + return false; + + CreateNPVariant( + result_param, channel.get(), result_var, render_view_id, page_url); + return true; +} + +} // namespace content diff --git a/content/child/npobject_proxy.h b/content/child/npobject_proxy.h new file mode 100644 index 0000000..ab7815c --- /dev/null +++ b/content/child/npobject_proxy.h @@ -0,0 +1,127 @@ +// 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. +// +// A proxy for NPObject that sends all calls to the object to an NPObjectStub +// running in a different process. + +#ifndef CONTENT_CHILD_NPOBJECT_PROXY_H_ +#define CONTENT_CHILD_NPOBJECT_PROXY_H_ + +#include "base/memory/ref_counted.h" +#include "content/child/npobject_base.h" +#include "googleurl/src/gurl.h" +#include "ipc/ipc_listener.h" +#include "ipc/ipc_sender.h" +#include "third_party/npapi/bindings/npruntime.h" +#include "ui/gfx/native_widget_types.h" + +struct NPObject; + +namespace content { +class NPChannelBase; + +// When running a plugin in a different process from the renderer, we need to +// proxy calls to NPObjects across process boundaries. This happens both ways, +// as a plugin can get an NPObject for the window, and a page can get an +// NPObject for the plugin. In the process that interacts with the NPobject we +// give it an NPObjectProxy instead. All calls to it are sent across an IPC +// channel (specifically, a NPChannelBase). The NPObjectStub on the other +// side translates the IPC messages into calls to the actual NPObject, and +// returns the marshalled result. +class NPObjectProxy : public IPC::Listener, + public IPC::Sender, + public NPObjectBase { + public: + virtual ~NPObjectProxy(); + + static NPObject* Create(NPChannelBase* channel, + int route_id, + int render_view_id, + const GURL& page_url); + + // IPC::Sender implementation: + virtual bool Send(IPC::Message* msg) OVERRIDE; + int route_id() { return route_id_; } + NPChannelBase* channel() { return channel_.get(); } + + // The next 9 functions are called on NPObjects from the plugin and browser. + static bool NPHasMethod(NPObject *obj, + NPIdentifier name); + static bool NPInvoke(NPObject *obj, + NPIdentifier name, + const NPVariant *args, + uint32_t arg_count, + NPVariant *result); + static bool NPInvokeDefault(NPObject *npobj, + const NPVariant *args, + uint32_t arg_count, + NPVariant *result); + static bool NPHasProperty(NPObject *obj, + NPIdentifier name); + static bool NPGetProperty(NPObject *obj, + NPIdentifier name, + NPVariant *result); + static bool NPSetProperty(NPObject *obj, + NPIdentifier name, + const NPVariant *value); + static bool NPRemoveProperty(NPObject *obj, + NPIdentifier name); + static bool NPNEnumerate(NPObject *obj, + NPIdentifier **value, + uint32_t *count); + static bool NPNConstruct(NPObject *npobj, + const NPVariant *args, + uint32_t arg_count, + NPVariant *result); + + // The next two functions are only called on NPObjects from the browser. + static bool NPNEvaluate(NPP npp, + NPObject *obj, + NPString *script, + NPVariant *result); + + static bool NPInvokePrivate(NPP npp, + NPObject *obj, + bool is_default, + NPIdentifier name, + const NPVariant *args, + uint32_t arg_count, + NPVariant *result); + + static NPObjectProxy* GetProxy(NPObject* object); + static const NPClass* npclass() { return &npclass_proxy_; } + + // NPObjectBase implementation. + virtual NPObject* GetUnderlyingNPObject() OVERRIDE; + + virtual IPC::Listener* GetChannelListener() OVERRIDE; + + private: + NPObjectProxy(NPChannelBase* channel, + int route_id, + int render_view_id, + const GURL& page_url); + + // IPC::Listener implementation: + virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE; + virtual void OnChannelError() OVERRIDE; + + static NPObject* NPAllocate(NPP, NPClass*); + static void NPDeallocate(NPObject* npObj); + + // This function is only caled on NPObjects from the plugin. + static void NPPInvalidate(NPObject *obj); + static NPClass npclass_proxy_; + + scoped_refptr<NPChannelBase> channel_; + int route_id_; + int render_view_id_; + + // The url of the main frame hosting the plugin. + GURL page_url_; +}; + +} // namespace content + +#endif // CONTENT_CHILD_NPOBJECT_PROXY_H_ diff --git a/content/child/npobject_stub.cc b/content/child/npobject_stub.cc new file mode 100644 index 0000000..d3ef7a2 --- /dev/null +++ b/content/child/npobject_stub.cc @@ -0,0 +1,424 @@ +// 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/npobject_stub.h" + +#include "content/child/np_channel_base.h" +#include "content/child/npobject_util.h" +#include "content/child/plugin_messages.h" +#include "content/public/common/content_client.h" +#include "content/public/common/content_switches.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebBindings.h" +#include "third_party/npapi/bindings/npapi.h" +#include "third_party/npapi/bindings/npruntime.h" +#include "webkit/plugins/npapi/plugin_host.h" + +#if defined(OS_WIN) +#include "base/command_line.h" +#include "webkit/plugins/npapi/plugin_constants_win.h" +#endif + +using WebKit::WebBindings; + +namespace content { + +NPObjectStub::NPObjectStub( + NPObject* npobject, + NPChannelBase* channel, + int route_id, + int render_view_id, + const GURL& page_url) + : npobject_(npobject), + channel_(channel), + route_id_(route_id), + render_view_id_(render_view_id), + page_url_(page_url) { + channel_->AddMappingForNPObjectStub(route_id, npobject); + channel_->AddRoute(route_id, this, this); + + // We retain the object just as PluginHost does if everything was in-process. + WebBindings::retainObject(npobject_); +} + +NPObjectStub::~NPObjectStub() { + channel_->RemoveRoute(route_id_); + DCHECK(!npobject_); +} + +void NPObjectStub::DeleteSoon() { + if (npobject_) { + channel_->RemoveMappingForNPObjectStub(route_id_, npobject_); + + // We need to NULL npobject_ prior to calling releaseObject() to avoid + // problems with re-entrancy. See http://crbug.com/94179#c17 for more + // details on how this can happen. + NPObject* npobject = npobject_; + npobject_ = NULL; + + WebBindings::releaseObject(npobject); + + base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); + } +} + +bool NPObjectStub::Send(IPC::Message* msg) { + return channel_->Send(msg); +} + +NPObject* NPObjectStub::GetUnderlyingNPObject() { + return npobject_; +} + +IPC::Listener* NPObjectStub::GetChannelListener() { + return static_cast<IPC::Listener*>(this); +} + +bool NPObjectStub::OnMessageReceived(const IPC::Message& msg) { + GetContentClient()->SetActiveURL(page_url_); + if (!npobject_) { + if (msg.is_sync()) { + // The object could be garbage because the frame has gone away, so + // just send an error reply to the caller. + IPC::Message* reply = IPC::SyncMessage::GenerateReply(&msg); + reply->set_reply_error(); + Send(reply); + } + + return true; + } + + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(NPObjectStub, msg) + IPC_MESSAGE_HANDLER_DELAY_REPLY(NPObjectMsg_Release, OnRelease); + IPC_MESSAGE_HANDLER(NPObjectMsg_HasMethod, OnHasMethod); + IPC_MESSAGE_HANDLER_DELAY_REPLY(NPObjectMsg_Invoke, OnInvoke); + IPC_MESSAGE_HANDLER(NPObjectMsg_HasProperty, OnHasProperty); + IPC_MESSAGE_HANDLER(NPObjectMsg_GetProperty, OnGetProperty); + IPC_MESSAGE_HANDLER_DELAY_REPLY(NPObjectMsg_SetProperty, OnSetProperty); + IPC_MESSAGE_HANDLER(NPObjectMsg_RemoveProperty, OnRemoveProperty); + IPC_MESSAGE_HANDLER(NPObjectMsg_Invalidate, OnInvalidate); + IPC_MESSAGE_HANDLER(NPObjectMsg_Enumeration, OnEnumeration); + IPC_MESSAGE_HANDLER_DELAY_REPLY(NPObjectMsg_Construct, OnConstruct); + IPC_MESSAGE_HANDLER_DELAY_REPLY(NPObjectMsg_Evaluate, OnEvaluate); + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + DCHECK(handled); + return handled; +} + +void NPObjectStub::OnChannelError() { + DeleteSoon(); +} + +void NPObjectStub::OnRelease(IPC::Message* reply_msg) { + Send(reply_msg); + DeleteSoon(); +} + +void NPObjectStub::OnHasMethod(const NPIdentifier_Param& name, + bool* result) { + NPIdentifier id = CreateNPIdentifier(name); + // If we're in the plugin process, then the stub is holding onto an NPObject + // from the plugin, so all function calls on it need to go through the + // functions in NPClass. If we're in the renderer process, then we just call + // the NPN_ functions. + if (IsPluginProcess()) { + if (npobject_->_class->hasMethod) { + *result = npobject_->_class->hasMethod(npobject_, id); + } else { + *result = false; + } + } else { + *result = WebBindings::hasMethod(0, npobject_, id); + } +} + +void NPObjectStub::OnInvoke(bool is_default, + const NPIdentifier_Param& method, + const std::vector<NPVariant_Param>& args, + IPC::Message* reply_msg) { + bool return_value = false; + NPVariant_Param result_param; + NPVariant result_var; + + VOID_TO_NPVARIANT(result_var); + result_param.type = NPVARIANT_PARAM_VOID; + + int arg_count = static_cast<int>(args.size()); + NPVariant* args_var = new NPVariant[arg_count]; + for (int i = 0; i < arg_count; ++i) { + if (!CreateNPVariant(args[i], + channel_.get(), + &(args_var[i]), + render_view_id_, + page_url_)) { + NPObjectMsg_Invoke::WriteReplyParams( + reply_msg, result_param, return_value); + channel_->Send(reply_msg); + delete[] args_var; + return; + } + } + + if (is_default) { + if (IsPluginProcess()) { + if (npobject_->_class->invokeDefault) { + return_value = npobject_->_class->invokeDefault( + npobject_, args_var, arg_count, &result_var); + } else { + return_value = false; + } + } else { + return_value = WebBindings::invokeDefault( + 0, npobject_, args_var, arg_count, &result_var); + } + } else { + NPIdentifier id = CreateNPIdentifier(method); + if (IsPluginProcess()) { + if (npobject_->_class->invoke) { + return_value = npobject_->_class->invoke( + npobject_, id, args_var, arg_count, &result_var); + } else { + return_value = false; + } + } else { + return_value = WebBindings::invoke( + 0, npobject_, id, args_var, arg_count, &result_var); + } + } + + for (int i = 0; i < arg_count; ++i) + WebBindings::releaseVariantValue(&(args_var[i])); + + delete[] args_var; + + CreateNPVariantParam(result_var, + channel_.get(), + &result_param, + true, + render_view_id_, + page_url_); + NPObjectMsg_Invoke::WriteReplyParams(reply_msg, result_param, return_value); + channel_->Send(reply_msg); +} + +void NPObjectStub::OnHasProperty(const NPIdentifier_Param& name, + bool* result) { + NPIdentifier id = CreateNPIdentifier(name); + if (IsPluginProcess()) { + if (npobject_->_class->hasProperty) { + *result = npobject_->_class->hasProperty(npobject_, id); + } else { + *result = false; + } + } else { + *result = WebBindings::hasProperty(0, npobject_, id); + } +} + +void NPObjectStub::OnGetProperty(const NPIdentifier_Param& name, + NPVariant_Param* property, + bool* result) { + NPVariant result_var; + VOID_TO_NPVARIANT(result_var); + NPIdentifier id = CreateNPIdentifier(name); + + if (IsPluginProcess()) { + if (npobject_->_class->getProperty) { + *result = npobject_->_class->getProperty(npobject_, id, &result_var); + } else { + *result = false; + } + } else { + *result = WebBindings::getProperty(0, npobject_, id, &result_var); + } + + CreateNPVariantParam( + result_var, channel_.get(), property, true, render_view_id_, page_url_); +} + +void NPObjectStub::OnSetProperty(const NPIdentifier_Param& name, + const NPVariant_Param& property, + IPC::Message* reply_msg) { + bool result = false; + NPIdentifier id = CreateNPIdentifier(name); + NPVariant property_var; + if (!CreateNPVariant(property, + channel_.get(), + &property_var, + render_view_id_, + page_url_)) { + NPObjectMsg_SetProperty::WriteReplyParams(reply_msg, result); + channel_->Send(reply_msg); + return; + } + + if (IsPluginProcess()) { + if (npobject_->_class->setProperty) { +#if defined(OS_WIN) + static base::FilePath plugin_path = + CommandLine::ForCurrentProcess()->GetSwitchValuePath( + switches::kPluginPath); + static std::wstring filename = StringToLowerASCII( + plugin_path.BaseName().value()); + static NPIdentifier fullscreen = + WebBindings::getStringIdentifier("fullScreen"); + if (filename == webkit::npapi::kNewWMPPlugin && id == fullscreen) { + // Workaround for bug 15985, which is if Flash causes WMP to go + // full screen a deadlock can occur when WMP calls SetFocus. + NPObjectMsg_SetProperty::WriteReplyParams(reply_msg, true); + Send(reply_msg); + reply_msg = NULL; + } +#endif + result = npobject_->_class->setProperty(npobject_, id, &property_var); + } else { + result = false; + } + } else { + result = WebBindings::setProperty(0, npobject_, id, &property_var); + } + + WebBindings::releaseVariantValue(&property_var); + + if (reply_msg) { + NPObjectMsg_SetProperty::WriteReplyParams(reply_msg, result); + Send(reply_msg); + } +} + +void NPObjectStub::OnRemoveProperty(const NPIdentifier_Param& name, + bool* result) { + NPIdentifier id = CreateNPIdentifier(name); + if (IsPluginProcess()) { + if (npobject_->_class->removeProperty) { + *result = npobject_->_class->removeProperty(npobject_, id); + } else { + *result = false; + } + } else { + *result = WebBindings::removeProperty(0, npobject_, id); + } +} + +void NPObjectStub::OnInvalidate() { + if (!IsPluginProcess()) { + NOTREACHED() << "Should only be called on NPObjects in the plugin"; + return; + } + + if (!npobject_->_class->invalidate) + return; + + npobject_->_class->invalidate(npobject_); +} + +void NPObjectStub::OnEnumeration(std::vector<NPIdentifier_Param>* value, + bool* result) { + NPIdentifier* value_np = NULL; + unsigned int count = 0; + if (!IsPluginProcess()) { + *result = WebBindings::enumerate(0, npobject_, &value_np, &count); + } else { + if (npobject_->_class->structVersion < NP_CLASS_STRUCT_VERSION_ENUM || + !npobject_->_class->enumerate) { + *result = false; + return; + } + + *result = npobject_->_class->enumerate(npobject_, &value_np, &count); + } + + if (!*result) + return; + + for (unsigned int i = 0; i < count; ++i) { + NPIdentifier_Param param; + CreateNPIdentifierParam(value_np[i], ¶m); + value->push_back(param); + } + + webkit::npapi::PluginHost::Singleton()->host_functions()->memfree(value_np); +} + +void NPObjectStub::OnConstruct(const std::vector<NPVariant_Param>& args, + IPC::Message* reply_msg) { + bool return_value = false; + NPVariant_Param result_param; + NPVariant result_var; + + VOID_TO_NPVARIANT(result_var); + + int arg_count = static_cast<int>(args.size()); + NPVariant* args_var = new NPVariant[arg_count]; + for (int i = 0; i < arg_count; ++i) { + if (!CreateNPVariant(args[i], + channel_.get(), + &(args_var[i]), + render_view_id_, + page_url_)) { + NPObjectMsg_Invoke::WriteReplyParams( + reply_msg, result_param, return_value); + channel_->Send(reply_msg); + delete[] args_var; + return; + } + } + + if (IsPluginProcess()) { + if (npobject_->_class->structVersion >= NP_CLASS_STRUCT_VERSION_CTOR && + npobject_->_class->construct) { + return_value = npobject_->_class->construct( + npobject_, args_var, arg_count, &result_var); + } else { + return_value = false; + } + } else { + return_value = WebBindings::construct( + 0, npobject_, args_var, arg_count, &result_var); + } + + for (int i = 0; i < arg_count; ++i) + WebBindings::releaseVariantValue(&(args_var[i])); + + delete[] args_var; + + CreateNPVariantParam(result_var, + channel_.get(), + &result_param, + true, + render_view_id_, + page_url_); + NPObjectMsg_Invoke::WriteReplyParams(reply_msg, result_param, return_value); + channel_->Send(reply_msg); +} + +void NPObjectStub::OnEvaluate(const std::string& script, + bool popups_allowed, + IPC::Message* reply_msg) { + if (IsPluginProcess()) { + NOTREACHED() << "Should only be called on NPObjects in the renderer"; + return; + } + + NPVariant result_var; + NPString script_string; + script_string.UTF8Characters = script.c_str(); + script_string.UTF8Length = static_cast<unsigned int>(script.length()); + + bool return_value = WebBindings::evaluateHelper(0, popups_allowed, npobject_, + &script_string, &result_var); + + NPVariant_Param result_param; + CreateNPVariantParam(result_var, + channel_.get(), + &result_param, + true, + render_view_id_, + page_url_); + NPObjectMsg_Evaluate::WriteReplyParams(reply_msg, result_param, return_value); + channel_->Send(reply_msg); +} + +} // namespace content diff --git a/content/child/npobject_stub.h b/content/child/npobject_stub.h new file mode 100644 index 0000000..76b19b4 --- /dev/null +++ b/content/child/npobject_stub.h @@ -0,0 +1,99 @@ +// 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. +// +// A class that receives IPC messages from an NPObjectProxy and calls the real +// NPObject. + +#ifndef CONTENT_CHILD_NPOBJECT_STUB_H_ +#define CONTENT_CHILD_NPOBJECT_STUB_H_ + +#include <vector> + +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "content/child/npobject_base.h" +#include "googleurl/src/gurl.h" +#include "ipc/ipc_listener.h" +#include "ipc/ipc_sender.h" + +struct NPObject; + +namespace content { +class NPChannelBase; +struct NPIdentifier_Param; +struct NPVariant_Param; + +// This wraps an NPObject and converts IPC messages from NPObjectProxy to calls +// to the object. The results are marshalled back. See npobject_proxy.h for +// more information. +class NPObjectStub : public IPC::Listener, + public IPC::Sender, + public base::SupportsWeakPtr<NPObjectStub>, + public NPObjectBase { + public: + NPObjectStub(NPObject* npobject, + NPChannelBase* channel, + int route_id, + int render_view_id, + const GURL& page_url); + virtual ~NPObjectStub(); + + // Schedules tear-down of this stub. The underlying NPObject reference is + // released, and further invokations form the IPC channel will fail once this + // call has returned. Deletion of the stub is deferred to the main loop, in + // case it is touched as the stack unwinds. DeleteSoon() is safe to call + // more than once, until control returns to the main loop. + void DeleteSoon(); + + // IPC::Sender implementation: + virtual bool Send(IPC::Message* msg) OVERRIDE; + + // NPObjectBase implementation. + virtual NPObject* GetUnderlyingNPObject() OVERRIDE; + virtual IPC::Listener* GetChannelListener() OVERRIDE; + + private: + // IPC::Listener implementation: + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + virtual void OnChannelError() OVERRIDE; + + // message handlers + void OnRelease(IPC::Message* reply_msg); + void OnHasMethod(const NPIdentifier_Param& name, + bool* result); + void OnInvoke(bool is_default, + const NPIdentifier_Param& method, + const std::vector<NPVariant_Param>& args, + IPC::Message* reply_msg); + void OnHasProperty(const NPIdentifier_Param& name, + bool* result); + void OnGetProperty(const NPIdentifier_Param& name, + NPVariant_Param* property, + bool* result); + void OnSetProperty(const NPIdentifier_Param& name, + const NPVariant_Param& property, + IPC::Message* reply_msg); + void OnRemoveProperty(const NPIdentifier_Param& name, + bool* result); + void OnInvalidate(); + void OnEnumeration(std::vector<NPIdentifier_Param>* value, + bool* result); + void OnConstruct(const std::vector<NPVariant_Param>& args, + IPC::Message* reply_msg); + void OnEvaluate(const std::string& script, bool popups_allowed, + IPC::Message* reply_msg); + + private: + NPObject* npobject_; + scoped_refptr<NPChannelBase> channel_; + int route_id_; + int render_view_id_; + + // The url of the main frame hosting the plugin. + GURL page_url_; +}; + +} // namespace content + +#endif // CONTENT_CHILD_NPOBJECT_STUB_H_ diff --git a/content/child/npobject_util.cc b/content/child/npobject_util.cc new file mode 100644 index 0000000..c86ce91 --- /dev/null +++ b/content/child/npobject_util.cc @@ -0,0 +1,292 @@ +// 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/npobject_util.h" + +#include "base/string_util.h" +#include "content/child/np_channel_base.h" +#include "content/child/npobject_proxy.h" +#include "content/child/plugin_messages.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebBindings.h" +#include "third_party/npapi/bindings/nphostapi.h" +#include "webkit/glue/webkit_glue.h" +#include "webkit/plugins/npapi/plugin_host.h" + +using WebKit::WebBindings; + +namespace content { + +// true if the current process is a plugin process, false otherwise. +static bool g_plugin_process; + +namespace { +// The next 7 functions are called by the plugin code when it's using the +// NPObject. Plugins always ignore the functions in NPClass (except allocate +// and deallocate), and instead just use the function pointers that were +// passed in NPInitialize. +// When the renderer interacts with an NPObject from the plugin, it of course +// uses the function pointers in its NPClass structure. +static bool NPN_HasMethodPatch(NPP npp, + NPObject *npobj, + NPIdentifier methodName) { + return NPObjectProxy::NPHasMethod(npobj, methodName); +} + +static bool NPN_InvokePatch(NPP npp, NPObject *npobj, + NPIdentifier methodName, + const NPVariant *args, + uint32_t argCount, + NPVariant *result) { + return NPObjectProxy::NPInvokePrivate(npp, npobj, false, methodName, args, + argCount, result); +} + +static bool NPN_InvokeDefaultPatch(NPP npp, + NPObject *npobj, + const NPVariant *args, + uint32_t argCount, + NPVariant *result) { + return NPObjectProxy::NPInvokePrivate(npp, npobj, true, 0, args, argCount, + result); +} + +static bool NPN_HasPropertyPatch(NPP npp, + NPObject *npobj, + NPIdentifier propertyName) { + return NPObjectProxy::NPHasProperty(npobj, propertyName); +} + +static bool NPN_GetPropertyPatch(NPP npp, + NPObject *npobj, + NPIdentifier propertyName, + NPVariant *result) { + return NPObjectProxy::NPGetProperty(npobj, propertyName, result); +} + +static bool NPN_SetPropertyPatch(NPP npp, + NPObject *npobj, + NPIdentifier propertyName, + const NPVariant *value) { + return NPObjectProxy::NPSetProperty(npobj, propertyName, value); +} + +static bool NPN_RemovePropertyPatch(NPP npp, + NPObject *npobj, + NPIdentifier propertyName) { + return NPObjectProxy::NPRemoveProperty(npobj, propertyName); +} + +static bool NPN_EvaluatePatch(NPP npp, + NPObject *npobj, + NPString *script, + NPVariant *result) { + return NPObjectProxy::NPNEvaluate(npp, npobj, script, result); +} + + +static void NPN_SetExceptionPatch(NPObject *obj, const NPUTF8 *message) { + std::string message_str(message); + if (IsPluginProcess()) { + NPChannelBase* renderer_channel = NPChannelBase::GetCurrentChannel(); + if (renderer_channel) + renderer_channel->Send(new PluginHostMsg_SetException(message_str)); + } else { + WebBindings::setException(obj, message_str.c_str()); + } +} + +static bool NPN_EnumeratePatch(NPP npp, NPObject *obj, + NPIdentifier **identifier, uint32_t *count) { + return NPObjectProxy::NPNEnumerate(obj, identifier, count); +} + +// The overrided table of functions provided to the plugin. +NPNetscapeFuncs *GetHostFunctions() { + static bool init = false; + static NPNetscapeFuncs host_funcs; + if (init) + return &host_funcs; + + memset(&host_funcs, 0, sizeof(host_funcs)); + host_funcs.invoke = NPN_InvokePatch; + host_funcs.invokeDefault = NPN_InvokeDefaultPatch; + host_funcs.evaluate = NPN_EvaluatePatch; + host_funcs.getproperty = NPN_GetPropertyPatch; + host_funcs.setproperty = NPN_SetPropertyPatch; + host_funcs.removeproperty = NPN_RemovePropertyPatch; + host_funcs.hasproperty = NPN_HasPropertyPatch; + host_funcs.hasmethod = NPN_HasMethodPatch; + host_funcs.setexception = NPN_SetExceptionPatch; + host_funcs.enumerate = NPN_EnumeratePatch; + + init = true; + return &host_funcs; +} + +} + +void PatchNPNFunctions() { + g_plugin_process = true; + NPNetscapeFuncs* funcs = GetHostFunctions(); + webkit::npapi::PluginHost::Singleton()->PatchNPNetscapeFuncs(funcs); +} + +bool IsPluginProcess() { + return g_plugin_process; +} + +void CreateNPIdentifierParam(NPIdentifier id, NPIdentifier_Param* param) { + param->identifier = id; +} + +NPIdentifier CreateNPIdentifier(const NPIdentifier_Param& param) { + return param.identifier; +} + +void CreateNPVariantParam(const NPVariant& variant, + NPChannelBase* channel, + NPVariant_Param* param, + bool release, + int render_view_id, + const GURL& page_url) { + switch (variant.type) { + case NPVariantType_Void: + param->type = NPVARIANT_PARAM_VOID; + break; + case NPVariantType_Null: + param->type = NPVARIANT_PARAM_NULL; + break; + case NPVariantType_Bool: + param->type = NPVARIANT_PARAM_BOOL; + param->bool_value = variant.value.boolValue; + break; + case NPVariantType_Int32: + param->type = NPVARIANT_PARAM_INT; + param->int_value = variant.value.intValue; + break; + case NPVariantType_Double: + param->type = NPVARIANT_PARAM_DOUBLE; + param->double_value = variant.value.doubleValue; + break; + case NPVariantType_String: + param->type = NPVARIANT_PARAM_STRING; + if (variant.value.stringValue.UTF8Length) { + param->string_value.assign(variant.value.stringValue.UTF8Characters, + variant.value.stringValue.UTF8Length); + } + break; + case NPVariantType_Object: { + if (variant.value.objectValue->_class == NPObjectProxy::npclass()) { + param->type = NPVARIANT_PARAM_RECEIVER_OBJECT_ROUTING_ID; + NPObjectProxy* proxy = + NPObjectProxy::GetProxy(variant.value.objectValue); + DCHECK(proxy); + param->npobject_routing_id = proxy->route_id(); + // Don't release, because our original variant is the same as our proxy. + release = false; + } else { + // The channel could be NULL if there was a channel error. The caller's + // Send call will fail anyways. + if (channel) { + // NPObjectStub adds its own reference to the NPObject it owns, so if + // we were supposed to release the corresponding variant + // (release==true), we should still do that. + param->type = NPVARIANT_PARAM_SENDER_OBJECT_ROUTING_ID; + int route_id = channel->GetExistingRouteForNPObjectStub( + variant.value.objectValue); + if (route_id != MSG_ROUTING_NONE) { + param->npobject_routing_id = route_id; + } else { + route_id = channel->GenerateRouteID(); + new NPObjectStub( + variant.value.objectValue, channel, route_id, render_view_id, + page_url); + param->npobject_routing_id = route_id; + } + } else { + param->type = NPVARIANT_PARAM_VOID; + } + } + break; + } + default: + NOTREACHED(); + } + + if (release) + WebBindings::releaseVariantValue(const_cast<NPVariant*>(&variant)); +} + +bool CreateNPVariant(const NPVariant_Param& param, + NPChannelBase* channel, + NPVariant* result, + int render_view_id, + const GURL& page_url) { + switch (param.type) { + case NPVARIANT_PARAM_VOID: + result->type = NPVariantType_Void; + break; + case NPVARIANT_PARAM_NULL: + result->type = NPVariantType_Null; + break; + case NPVARIANT_PARAM_BOOL: + result->type = NPVariantType_Bool; + result->value.boolValue = param.bool_value; + break; + case NPVARIANT_PARAM_INT: + result->type = NPVariantType_Int32; + result->value.intValue = param.int_value; + break; + case NPVARIANT_PARAM_DOUBLE: + result->type = NPVariantType_Double; + result->value.doubleValue = param.double_value; + break; + case NPVARIANT_PARAM_STRING: { + result->type = NPVariantType_String; + void* buffer = malloc(param.string_value.size()); + size_t size = param.string_value.size(); + result->value.stringValue.UTF8Characters = static_cast<NPUTF8*>(buffer); + memcpy(buffer, param.string_value.c_str(), size); + result->value.stringValue.UTF8Length = static_cast<int>(size); + break; + } + case NPVARIANT_PARAM_SENDER_OBJECT_ROUTING_ID: { + result->type = NPVariantType_Object; + NPObject* object = + channel->GetExistingNPObjectProxy(param.npobject_routing_id); + if (object) { + WebBindings::retainObject(object); + result->value.objectValue = object; + } else { + result->value.objectValue = + NPObjectProxy::Create(channel, + param.npobject_routing_id, + render_view_id, + page_url); + } + break; + } + case NPVARIANT_PARAM_RECEIVER_OBJECT_ROUTING_ID: { + NPObjectBase* npobject_base = + channel->GetNPObjectListenerForRoute(param.npobject_routing_id); + if (!npobject_base) { + DLOG(WARNING) << "Invalid routing id passed in" + << param.npobject_routing_id; + return false; + } + + DCHECK(npobject_base->GetUnderlyingNPObject() != NULL); + + result->type = NPVariantType_Object; + result->value.objectValue = npobject_base->GetUnderlyingNPObject(); + WebBindings::retainObject(result->value.objectValue); + break; + } + default: + NOTREACHED(); + } + return true; +} + +} // namespace content diff --git a/content/child/npobject_util.h b/content/child/npobject_util.h new file mode 100644 index 0000000..bbf52e8 --- /dev/null +++ b/content/child/npobject_util.h @@ -0,0 +1,74 @@ +// 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. +// +// Helper functions that are used by the NPObject proxy and stub. + +#ifndef CONTENT_CHILD_NPOBJECT_UTIL_H_ +#define CONTENT_CHILD_NPOBJECT_UTIL_H_ + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include <windows.h> +#endif + +#include "content/child/npobject_stub.h" + +class GURL; + +struct _NPVariant; + +typedef _NPVariant NPVariant; +typedef void *NPIdentifier; + +namespace content { +class NPChannelBase; +struct NPIdentifier_Param; +struct NPVariant_Param; + +// Needs to be called early in the plugin process lifetime, before any +// plugin instances are initialized. +void PatchNPNFunctions(); + +// Returns true if the current process is a plugin process, or false otherwise. +bool IsPluginProcess(); + +// Creates an object similar to NPIdentifier that can be marshalled. +void CreateNPIdentifierParam(NPIdentifier id, NPIdentifier_Param* param); + +// Creates an NPIdentifier from the marshalled object. +NPIdentifier CreateNPIdentifier(const NPIdentifier_Param& param); + +// Creates an object similar to NPVariant that can be marshalled. +// If the containing NPObject happens to be an NPObject, then a stub +// is created around it and param holds the routing id for it. +// If release is true, the NPVariant object is released (except if +// it contains an NPObject, since the stub will manage its lifetime). +void CreateNPVariantParam(const NPVariant& variant, + NPChannelBase* channel, + NPVariant_Param* param, + bool release, + int render_view_id, + const GURL& page_url); + +// Creates an NPVariant from the marshalled object. +// Returns true on success. +bool CreateNPVariant(const NPVariant_Param& param, + NPChannelBase* channel, + NPVariant* result, + int render_view_id, + const GURL& page_url); + +#if defined(OS_WIN) +// Given a plugin's HWND, returns an event associated with the WebContentsImpl +// that's set when inside a messagebox. This tells the plugin process that +// the message queue should be pumped (as what would happen if everything was +// in-process). This avoids deadlocks when a plugin invokes javascript that +// causes a message box to come up. +HANDLE GetMessageBoxEvent(HWND hwnd); +#endif // defined(OS_WIN) + +} // namespace content + +#endif // CONTENT_CHILD_NPOBJECT_UTIL_H_ diff --git a/content/child/plugin_message_generator.cc b/content/child/plugin_message_generator.cc new file mode 100644 index 0000000..d490345 --- /dev/null +++ b/content/child/plugin_message_generator.cc @@ -0,0 +1,33 @@ +// 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. + +// Get basic type definitions. +#define IPC_MESSAGE_IMPL +#include "content/child/plugin_message_generator.h" + +// Generate constructors. +#include "ipc/struct_constructor_macros.h" +#include "content/child/plugin_message_generator.h" + +// Generate destructors. +#include "ipc/struct_destructor_macros.h" +#include "content/child/plugin_message_generator.h" + +// Generate param traits write methods. +#include "ipc/param_traits_write_macros.h" +namespace IPC { +#include "content/child/plugin_message_generator.h" +} // namespace IPC + +// Generate param traits read methods. +#include "ipc/param_traits_read_macros.h" +namespace IPC { +#include "content/child/plugin_message_generator.h" +} // namespace IPC + +// Generate param traits log methods. +#include "ipc/param_traits_log_macros.h" +namespace IPC { +#include "content/child/plugin_message_generator.h" +} // namespace IPC diff --git a/content/child/plugin_message_generator.h b/content/child/plugin_message_generator.h new file mode 100644 index 0000000..9eddea2 --- /dev/null +++ b/content/child/plugin_message_generator.h @@ -0,0 +1,7 @@ +// 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. + +// Multiply-included file, hence no include guard. + +#include "content/child/plugin_messages.h" diff --git a/content/child/plugin_messages.h b/content/child/plugin_messages.h new file mode 100644 index 0000000..7c0d825 --- /dev/null +++ b/content/child/plugin_messages.h @@ -0,0 +1,380 @@ +// 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. +// +// Multiply-included message file, hence no include guard. + +#include "build/build_config.h" +#include "content/child/plugin_param_traits.h" +#include "content/common/content_export.h" +#include "content/common/content_param_traits.h" +#include "content/public/common/common_param_traits.h" +#include "ipc/ipc_channel_handle.h" +#include "ipc/ipc_message_macros.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/gfx/rect.h" +#include "webkit/common/cursors/webcursor.h" + +#if defined(OS_POSIX) +#include "base/file_descriptor_posix.h" +#endif + +#undef IPC_MESSAGE_EXPORT +#define IPC_MESSAGE_EXPORT CONTENT_EXPORT + +#define IPC_MESSAGE_START PluginMsgStart + +IPC_STRUCT_BEGIN(PluginMsg_Init_Params) + IPC_STRUCT_MEMBER(GURL, url) + IPC_STRUCT_MEMBER(GURL, page_url) + IPC_STRUCT_MEMBER(std::vector<std::string>, arg_names) + IPC_STRUCT_MEMBER(std::vector<std::string>, arg_values) + IPC_STRUCT_MEMBER(bool, load_manually) + IPC_STRUCT_MEMBER(int, host_render_view_routing_id) +IPC_STRUCT_END() + +IPC_STRUCT_BEGIN(PluginHostMsg_URLRequest_Params) + IPC_STRUCT_MEMBER(std::string, url) + IPC_STRUCT_MEMBER(std::string, method) + IPC_STRUCT_MEMBER(std::string, target) + IPC_STRUCT_MEMBER(std::vector<char>, buffer) + IPC_STRUCT_MEMBER(int, notify_id) + IPC_STRUCT_MEMBER(bool, popups_allowed) + IPC_STRUCT_MEMBER(bool, notify_redirects) +IPC_STRUCT_END() + +IPC_STRUCT_BEGIN(PluginMsg_DidReceiveResponseParams) + IPC_STRUCT_MEMBER(unsigned long, id) + IPC_STRUCT_MEMBER(std::string, mime_type) + IPC_STRUCT_MEMBER(std::string, headers) + IPC_STRUCT_MEMBER(uint32, expected_length) + IPC_STRUCT_MEMBER(uint32, last_modified) + IPC_STRUCT_MEMBER(bool, request_is_seekable) +IPC_STRUCT_END() + +IPC_STRUCT_BEGIN(PluginMsg_UpdateGeometry_Param) + IPC_STRUCT_MEMBER(gfx::Rect, window_rect) + IPC_STRUCT_MEMBER(gfx::Rect, clip_rect) + IPC_STRUCT_MEMBER(TransportDIB::Handle, windowless_buffer0) + IPC_STRUCT_MEMBER(TransportDIB::Handle, windowless_buffer1) + IPC_STRUCT_MEMBER(int, windowless_buffer_index) +IPC_STRUCT_END() + +//----------------------------------------------------------------------------- +// Plugin messages +// These are messages sent from the renderer process to the plugin process. +// Tells the plugin process to create a new plugin instance with the given +// id. A corresponding WebPluginDelegateStub is created which hosts the +// WebPluginDelegateImpl. +IPC_SYNC_MESSAGE_CONTROL1_1(PluginMsg_CreateInstance, + std::string /* mime_type */, + int /* instance_id */) + +// The WebPluginDelegateProxy sends this to the WebPluginDelegateStub in its +// destructor, so that the stub deletes the actual WebPluginDelegateImpl +// object that it's hosting. +IPC_SYNC_MESSAGE_CONTROL1_0(PluginMsg_DestroyInstance, + int /* instance_id */) + +IPC_SYNC_MESSAGE_CONTROL0_1(PluginMsg_GenerateRouteID, + int /* id */) + +// The messages below all map to WebPluginDelegate methods. +IPC_SYNC_MESSAGE_ROUTED1_2(PluginMsg_Init, + PluginMsg_Init_Params, + bool /* transparent */, + bool /* result */) + +// Used to synchronously request a paint for windowless plugins. +IPC_SYNC_MESSAGE_ROUTED1_0(PluginMsg_Paint, + gfx::Rect /* damaged_rect */) + +// Sent by the renderer after it paints from its backing store so that the +// plugin knows it can send more invalidates. +IPC_MESSAGE_ROUTED0(PluginMsg_DidPaint) + +IPC_SYNC_MESSAGE_ROUTED0_1(PluginMsg_GetPluginScriptableObject, + int /* route_id */) + +// Gets the form value of the plugin instance synchronously. +IPC_SYNC_MESSAGE_ROUTED0_2(PluginMsg_GetFormValue, + string16 /* value */, + bool /* success */) + +IPC_MESSAGE_ROUTED3(PluginMsg_DidFinishLoadWithReason, + GURL /* url */, + int /* reason */, + int /* notify_id */) + +// Updates the plugin location. +IPC_MESSAGE_ROUTED1(PluginMsg_UpdateGeometry, + PluginMsg_UpdateGeometry_Param) + +// A synchronous version of above. +IPC_SYNC_MESSAGE_ROUTED1_0(PluginMsg_UpdateGeometrySync, + PluginMsg_UpdateGeometry_Param) + +IPC_SYNC_MESSAGE_ROUTED1_0(PluginMsg_SetFocus, + bool /* focused */) + +IPC_SYNC_MESSAGE_ROUTED1_2(PluginMsg_HandleInputEvent, + IPC::WebInputEventPointer /* event */, + bool /* handled */, + WebCursor /* cursor type*/) + +IPC_MESSAGE_ROUTED1(PluginMsg_SetContentAreaFocus, + bool /* has_focus */) + +#if defined(OS_WIN) +IPC_MESSAGE_ROUTED4(PluginMsg_ImeCompositionUpdated, + string16 /* text */, + std::vector<int> /* clauses */, + std::vector<int>, /* target */ + int /* cursor_position */) + +IPC_MESSAGE_ROUTED1(PluginMsg_ImeCompositionCompleted, + string16 /* text */) +#endif + +#if defined(OS_MACOSX) +IPC_MESSAGE_ROUTED1(PluginMsg_SetWindowFocus, + bool /* has_focus */) + +IPC_MESSAGE_ROUTED0(PluginMsg_ContainerHidden) + +IPC_MESSAGE_ROUTED3(PluginMsg_ContainerShown, + gfx::Rect /* window_frame */, + gfx::Rect /* view_frame */, + bool /* has_focus */) + +IPC_MESSAGE_ROUTED2(PluginMsg_WindowFrameChanged, + gfx::Rect /* window_frame */, + gfx::Rect /* view_frame */) + +IPC_MESSAGE_ROUTED1(PluginMsg_ImeCompositionCompleted, + string16 /* text */) +#endif + +IPC_SYNC_MESSAGE_ROUTED3_0(PluginMsg_WillSendRequest, + unsigned long /* id */, + GURL /* url */, + int /* http_status_code */) + +IPC_MESSAGE_ROUTED1(PluginMsg_DidReceiveResponse, + PluginMsg_DidReceiveResponseParams) + +IPC_MESSAGE_ROUTED3(PluginMsg_DidReceiveData, + unsigned long /* id */, + std::vector<char> /* buffer */, + int /* data_offset */) + +IPC_MESSAGE_ROUTED1(PluginMsg_DidFinishLoading, + unsigned long /* id */) + +IPC_MESSAGE_ROUTED1(PluginMsg_DidFail, + unsigned long /* id */) + +IPC_MESSAGE_ROUTED4(PluginMsg_SendJavaScriptStream, + GURL /* url */, + std::string /* result */, + bool /* success */, + int /* notify_id */) + +IPC_MESSAGE_ROUTED2(PluginMsg_DidReceiveManualResponse, + GURL /* url */, + PluginMsg_DidReceiveResponseParams) + +IPC_MESSAGE_ROUTED1(PluginMsg_DidReceiveManualData, + std::vector<char> /* buffer */) + +IPC_MESSAGE_ROUTED0(PluginMsg_DidFinishManualLoading) + +IPC_MESSAGE_ROUTED0(PluginMsg_DidManualLoadFail) + +IPC_MESSAGE_ROUTED3(PluginMsg_HandleURLRequestReply, + unsigned long /* resource_id */, + GURL /* url */, + int /* notify_id */) + +IPC_MESSAGE_ROUTED2(PluginMsg_HTTPRangeRequestReply, + unsigned long /* resource_id */, + int /* range_request_id */) + +IPC_MESSAGE_CONTROL1(PluginMsg_SignalModalDialogEvent, + int /* render_view_id */) + +IPC_MESSAGE_CONTROL1(PluginMsg_ResetModalDialogEvent, + int /* render_view_id */) + +#if defined(OS_MACOSX) +// This message, used only on 10.6 and later, transmits the "fake" +// window handle allocated by the browser on behalf of the renderer +// to the GPU plugin. +IPC_MESSAGE_ROUTED1(PluginMsg_SetFakeAcceleratedSurfaceWindowHandle, + gfx::PluginWindowHandle /* window */) +#endif + +//----------------------------------------------------------------------------- +// PluginHost messages +// These are messages sent from the plugin process to the renderer process. +// They all map to the corresponding WebPlugin methods. +// Sends the plugin window information to the renderer. +// The window parameter is a handle to the window if the plugin is a windowed +// plugin. It is NULL for windowless plugins. +IPC_SYNC_MESSAGE_ROUTED1_0(PluginHostMsg_SetWindow, + gfx::PluginWindowHandle /* window */) + +#if defined(OS_WIN) +// The modal_loop_pump_messages_event parameter is an event handle which is +// passed in for windowless plugins and is used to indicate if messages +// are to be pumped in sync calls to the plugin process. Currently used +// in HandleEvent calls. +IPC_SYNC_MESSAGE_ROUTED2_0(PluginHostMsg_SetWindowlessData, + HANDLE /* modal_loop_pump_messages_event */, + gfx::NativeViewId /* dummy_activation_window*/) + +// Send the IME status retrieved from a windowless plug-in. A windowless plug-in +// uses the IME attached to a browser process as a renderer does. A plug-in +// sends this message to control the IME status of a browser process. I would +// note that a plug-in sends this message to a renderer process that hosts this +// plug-in (not directly to a browser process) so the renderer process can +// update its IME status. +IPC_MESSAGE_ROUTED2(PluginHostMsg_NotifyIMEStatus, + int /* input_type */, + gfx::Rect /* caret_rect */) +#endif + +IPC_MESSAGE_ROUTED1(PluginHostMsg_URLRequest, + PluginHostMsg_URLRequest_Params) + +IPC_MESSAGE_ROUTED1(PluginHostMsg_CancelResource, + int /* id */) + +IPC_MESSAGE_ROUTED1(PluginHostMsg_InvalidateRect, + gfx::Rect /* rect */) + +IPC_SYNC_MESSAGE_ROUTED1_1(PluginHostMsg_GetWindowScriptNPObject, + int /* route id */, + bool /* success */) + +IPC_SYNC_MESSAGE_ROUTED1_1(PluginHostMsg_GetPluginElement, + int /* route id */, + bool /* success */) + +IPC_SYNC_MESSAGE_ROUTED1_2(PluginHostMsg_ResolveProxy, + GURL /* url */, + bool /* result */, + std::string /* proxy list */) + +IPC_MESSAGE_ROUTED3(PluginHostMsg_SetCookie, + GURL /* url */, + GURL /* first_party_for_cookies */, + std::string /* cookie */) + +IPC_SYNC_MESSAGE_ROUTED2_1(PluginHostMsg_GetCookies, + GURL /* url */, + GURL /* first_party_for_cookies */, + std::string /* cookies */) + +IPC_MESSAGE_ROUTED0(PluginHostMsg_CancelDocumentLoad) + +IPC_MESSAGE_ROUTED3(PluginHostMsg_InitiateHTTPRangeRequest, + std::string /* url */, + std::string /* range_info */, + int /* range_request_id */) + +IPC_MESSAGE_ROUTED2(PluginHostMsg_DeferResourceLoading, + unsigned long /* resource_id */, + bool /* defer */) + +IPC_SYNC_MESSAGE_CONTROL1_0(PluginHostMsg_SetException, + std::string /* message */) + +IPC_MESSAGE_CONTROL0(PluginHostMsg_PluginShuttingDown) + +#if defined(OS_MACOSX) +IPC_MESSAGE_ROUTED1(PluginHostMsg_FocusChanged, + bool /* focused */) + +IPC_MESSAGE_ROUTED0(PluginHostMsg_StartIme) + +//---------------------------------------------------------------------- +// Core Animation plugin implementation rendering via compositor. + +// Notifies the renderer process that this plugin will be using the +// accelerated rendering path. +IPC_MESSAGE_ROUTED0(PluginHostMsg_AcceleratedPluginEnabledRendering) + +// Notifies the renderer process that the plugin allocated a new +// IOSurface into which it is rendering. The renderer process forwards +// this IOSurface to the GPU process, causing it to be bound to a +// texture from which the compositor can render. Any previous +// IOSurface allocated by this plugin must be implicitly released by +// the receipt of this message. +IPC_MESSAGE_ROUTED3(PluginHostMsg_AcceleratedPluginAllocatedIOSurface, + int32 /* width */, + int32 /* height */, + uint32 /* surface_id */) + +// Notifies the renderer process that the plugin produced a new frame +// of content into its IOSurface, and therefore that the compositor +// needs to redraw. +IPC_MESSAGE_ROUTED0(PluginHostMsg_AcceleratedPluginSwappedIOSurface) +#endif + +IPC_MESSAGE_ROUTED2(PluginHostMsg_URLRedirectResponse, + bool /* allow */, + int /* resource_id */) + + +//----------------------------------------------------------------------------- +// NPObject messages +// These are messages used to marshall NPObjects. They are sent both from the +// plugin to the renderer and from the renderer to the plugin. +IPC_SYNC_MESSAGE_ROUTED0_0(NPObjectMsg_Release) + +IPC_SYNC_MESSAGE_ROUTED1_1(NPObjectMsg_HasMethod, + content::NPIdentifier_Param /* name */, + bool /* result */) + +IPC_SYNC_MESSAGE_ROUTED3_2(NPObjectMsg_Invoke, + bool /* is_default */, + content::NPIdentifier_Param /* method */, + std::vector<content::NPVariant_Param> /* args */, + content::NPVariant_Param /* result_param */, + bool /* result */) + +IPC_SYNC_MESSAGE_ROUTED1_1(NPObjectMsg_HasProperty, + content::NPIdentifier_Param /* name */, + bool /* result */) + +IPC_SYNC_MESSAGE_ROUTED1_2(NPObjectMsg_GetProperty, + content::NPIdentifier_Param /* name */, + content::NPVariant_Param /* property */, + bool /* result */) + +IPC_SYNC_MESSAGE_ROUTED2_1(NPObjectMsg_SetProperty, + content::NPIdentifier_Param /* name */, + content::NPVariant_Param /* property */, + bool /* result */) + +IPC_SYNC_MESSAGE_ROUTED1_1(NPObjectMsg_RemoveProperty, + content::NPIdentifier_Param /* name */, + bool /* result */) + +IPC_SYNC_MESSAGE_ROUTED0_0(NPObjectMsg_Invalidate) + +IPC_SYNC_MESSAGE_ROUTED0_2(NPObjectMsg_Enumeration, + std::vector<content::NPIdentifier_Param> /* value */, + bool /* result */) + +IPC_SYNC_MESSAGE_ROUTED1_2(NPObjectMsg_Construct, + std::vector<content::NPVariant_Param> /* args */, + content::NPVariant_Param /* result_param */, + bool /* result */) + +IPC_SYNC_MESSAGE_ROUTED2_2(NPObjectMsg_Evaluate, + std::string /* script */, + bool /* popups_allowed */, + content::NPVariant_Param /* result_param */, + bool /* result */) diff --git a/content/child/plugin_param_traits.cc b/content/child/plugin_param_traits.cc new file mode 100644 index 0000000..53e75a4 --- /dev/null +++ b/content/child/plugin_param_traits.cc @@ -0,0 +1,133 @@ +// 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/plugin_param_traits.h" + +#include "base/strings/string_number_conversions.h" +#include "ipc/ipc_message_utils.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebBindings.h" +#include "webkit/glue/npruntime_util.h" +#include "webkit/plugins/npapi/plugin_host.h" + +namespace content { + +NPIdentifier_Param::NPIdentifier_Param() + : identifier() { +} + +NPIdentifier_Param::~NPIdentifier_Param() { +} + +NPVariant_Param::NPVariant_Param() + : type(NPVARIANT_PARAM_VOID), + bool_value(false), + int_value(0), + double_value(0), + npobject_routing_id(-1) { +} + +NPVariant_Param::~NPVariant_Param() { +} + +} // namespace content + +using content::NPIdentifier_Param; +using content::NPVariant_Param; + +namespace IPC { + +void ParamTraits<NPVariant_Param>::Write(Message* m, const param_type& p) { + WriteParam(m, static_cast<int>(p.type)); + if (p.type == content::NPVARIANT_PARAM_BOOL) { + WriteParam(m, p.bool_value); + } else if (p.type == content::NPVARIANT_PARAM_INT) { + WriteParam(m, p.int_value); + } else if (p.type == content::NPVARIANT_PARAM_DOUBLE) { + WriteParam(m, p.double_value); + } else if (p.type == content::NPVARIANT_PARAM_STRING) { + WriteParam(m, p.string_value); + } else if (p.type == content::NPVARIANT_PARAM_SENDER_OBJECT_ROUTING_ID || + p.type == content::NPVARIANT_PARAM_RECEIVER_OBJECT_ROUTING_ID) { + // This is the routing id used to connect NPObjectProxy in the other + // process with NPObjectStub in this process or to identify the raw + // npobject pointer to be used in the callee process. + WriteParam(m, p.npobject_routing_id); + } else { + DCHECK(p.type == content::NPVARIANT_PARAM_VOID || + p.type == content::NPVARIANT_PARAM_NULL); + } +} + +bool ParamTraits<NPVariant_Param>::Read(const Message* m, + PickleIterator* iter, + param_type* r) { + int type; + if (!ReadParam(m, iter, &type)) + return false; + + bool result = false; + r->type = static_cast<content::NPVariant_ParamEnum>(type); + if (r->type == content::NPVARIANT_PARAM_BOOL) { + result = ReadParam(m, iter, &r->bool_value); + } else if (r->type == content::NPVARIANT_PARAM_INT) { + result = ReadParam(m, iter, &r->int_value); + } else if (r->type == content::NPVARIANT_PARAM_DOUBLE) { + result = ReadParam(m, iter, &r->double_value); + } else if (r->type == content::NPVARIANT_PARAM_STRING) { + result = ReadParam(m, iter, &r->string_value); + } else if (r->type == content::NPVARIANT_PARAM_SENDER_OBJECT_ROUTING_ID || + r->type == content::NPVARIANT_PARAM_RECEIVER_OBJECT_ROUTING_ID) { + result = ReadParam(m, iter, &r->npobject_routing_id); + } else if ((r->type == content::NPVARIANT_PARAM_VOID) || + (r->type == content::NPVARIANT_PARAM_NULL)) { + result = true; + } else { + NOTREACHED(); + } + + return result; +} + +void ParamTraits<NPVariant_Param>::Log(const param_type& p, std::string* l) { + l->append( + base::StringPrintf("NPVariant_Param(%d, ", static_cast<int>(p.type))); + if (p.type == content::NPVARIANT_PARAM_BOOL) { + LogParam(p.bool_value, l); + } else if (p.type == content::NPVARIANT_PARAM_INT) { + LogParam(p.int_value, l); + } else if (p.type == content::NPVARIANT_PARAM_DOUBLE) { + LogParam(p.double_value, l); + } else if (p.type == content::NPVARIANT_PARAM_STRING) { + LogParam(p.string_value, l); + } else if (p.type == content::NPVARIANT_PARAM_SENDER_OBJECT_ROUTING_ID || + p.type == content::NPVARIANT_PARAM_RECEIVER_OBJECT_ROUTING_ID) { + LogParam(p.npobject_routing_id, l); + } else { + l->append("<none>"); + } + l->append(")"); +} + +void ParamTraits<NPIdentifier_Param>::Write(Message* m, const param_type& p) { + webkit_glue::SerializeNPIdentifier(p.identifier, m); +} + +bool ParamTraits<NPIdentifier_Param>::Read(const Message* m, + PickleIterator* iter, + param_type* r) { + return webkit_glue::DeserializeNPIdentifier(iter, &r->identifier); +} + +void ParamTraits<NPIdentifier_Param>::Log(const param_type& p, std::string* l) { + if (WebKit::WebBindings::identifierIsString(p.identifier)) { + NPUTF8* str = WebKit::WebBindings::utf8FromIdentifier(p.identifier); + l->append(str); + webkit::npapi::PluginHost::Singleton()->host_functions()->memfree(str); + } else { + l->append(base::IntToString( + WebKit::WebBindings::intFromIdentifier(p.identifier))); + } +} + +} // namespace IPC diff --git a/content/child/plugin_param_traits.h b/content/child/plugin_param_traits.h new file mode 100644 index 0000000..b8495c6 --- /dev/null +++ b/content/child/plugin_param_traits.h @@ -0,0 +1,83 @@ +// 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. + +// This file is used to define IPC::ParamTraits<> specializations for a number +// of types so that they can be serialized over IPC. IPC::ParamTraits<> +// specializations for basic types (like int and std::string) and types in the +// 'base' project can be found in ipc/ipc_message_utils.h. This file contains +// specializations for types that are used by the content code, and which need +// manual serialization code. This is usually because they're not structs with +// public members, or because the same type is being used in multiple +// *_messages.h headers. + +#ifndef CONTENT_CHILD_PLUGIN_PARAM_TRAITS_H_ +#define CONTENT_CHILD_PLUGIN_PARAM_TRAITS_H_ + +#include <string> +#include "ipc/ipc_message.h" +#include "ipc/ipc_param_traits.h" +#include "webkit/glue/npruntime_util.h" + +namespace content { + +// Define the NPVariant_Param struct and its enum here since it needs manual +// serialization code. +enum NPVariant_ParamEnum { + NPVARIANT_PARAM_VOID, + NPVARIANT_PARAM_NULL, + NPVARIANT_PARAM_BOOL, + NPVARIANT_PARAM_INT, + NPVARIANT_PARAM_DOUBLE, + NPVARIANT_PARAM_STRING, + // Used when when the NPObject is running in the caller's process, so we + // create an NPObjectProxy in the other process. + NPVARIANT_PARAM_SENDER_OBJECT_ROUTING_ID, + // Used when the NPObject we're sending is running in the callee's process + // (i.e. we have an NPObjectProxy for it). In that case we want the callee + // to just use the raw pointer. + NPVARIANT_PARAM_RECEIVER_OBJECT_ROUTING_ID, +}; + +struct NPVariant_Param { + NPVariant_Param(); + ~NPVariant_Param(); + + NPVariant_ParamEnum type; + bool bool_value; + int int_value; + double double_value; + std::string string_value; + int npobject_routing_id; +}; + +struct NPIdentifier_Param { + NPIdentifier_Param(); + ~NPIdentifier_Param(); + + NPIdentifier identifier; +}; + +} // namespace content + +namespace IPC { + +template <> +struct ParamTraits<content::NPVariant_Param> { + typedef content::NPVariant_Param param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, PickleIterator* iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +template <> +struct ParamTraits<content::NPIdentifier_Param> { + typedef content::NPIdentifier_Param param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, PickleIterator* iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +} // namespace IPC + +#endif // CONTENT_CHILD_PLUGIN_PARAM_TRAITS_H_ diff --git a/content/child/web_database_observer_impl.cc b/content/child/web_database_observer_impl.cc new file mode 100644 index 0000000..00002aa --- /dev/null +++ b/content/child/web_database_observer_impl.cc @@ -0,0 +1,169 @@ +// 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/web_database_observer_impl.h" + +#include "base/metrics/histogram.h" +#include "base/string16.h" +#include "content/common/database_messages.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebDatabase.h" +#include "third_party/WebKit/public/platform/WebString.h" +#include "third_party/sqlite/sqlite3.h" + +using WebKit::WebDatabase; + +namespace content { +namespace { + +const int kResultHistogramSize = 50; +const int kCallsiteHistogramSize = 10; + +int DetermineHistogramResult(int websql_error, int sqlite_error) { + // If we have a sqlite error, log it after trimming the extended bits. + // There are 26 possible values, but we leave room for some new ones. + if (sqlite_error) + return std::min(sqlite_error & 0xff, 30); + + // Otherwise, websql_error may be an SQLExceptionCode, SQLErrorCode + // or a DOMExceptionCode, or -1 for success. + if (websql_error == -1) + return 0; // no error + + // SQLExceptionCode starts at 1000 + if (websql_error >= 1000) + websql_error -= 1000; + + return std::min(websql_error + 30, kResultHistogramSize - 1); +} + +#define HISTOGRAM_WEBSQL_RESULT(name, database, callsite, \ + websql_error, sqlite_error) \ + do { \ + DCHECK(callsite < kCallsiteHistogramSize); \ + int result = DetermineHistogramResult(websql_error, sqlite_error); \ + if (database.isSyncDatabase()) { \ + UMA_HISTOGRAM_ENUMERATION("websql.Sync." name, \ + result, kResultHistogramSize); \ + if (result) { \ + UMA_HISTOGRAM_ENUMERATION("websql.Sync." name ".ErrorSite", \ + callsite, kCallsiteHistogramSize); \ + } \ + } else { \ + UMA_HISTOGRAM_ENUMERATION("websql.Async." name, \ + result, kResultHistogramSize); \ + if (result) { \ + UMA_HISTOGRAM_ENUMERATION("websql.Async." name ".ErrorSite", \ + callsite, kCallsiteHistogramSize); \ + } \ + } \ + } while (0) + +} // namespace + +WebDatabaseObserverImpl::WebDatabaseObserverImpl( + IPC::SyncMessageFilter* sender) + : sender_(sender), + open_connections_(new webkit_database::DatabaseConnectionsWrapper) { + DCHECK(sender); +} + +WebDatabaseObserverImpl::~WebDatabaseObserverImpl() { +} + +void WebDatabaseObserverImpl::databaseOpened( + const WebDatabase& database) { + string16 origin_identifier = database.securityOrigin().databaseIdentifier(); + string16 database_name = database.name(); + open_connections_->AddOpenConnection(origin_identifier, database_name); + sender_->Send(new DatabaseHostMsg_Opened( + origin_identifier, database_name, + database.displayName(), database.estimatedSize())); +} + +void WebDatabaseObserverImpl::databaseModified( + const WebDatabase& database) { + sender_->Send(new DatabaseHostMsg_Modified( + database.securityOrigin().databaseIdentifier(), database.name())); +} + +void WebDatabaseObserverImpl::databaseClosed( + const WebDatabase& database) { + string16 origin_identifier = database.securityOrigin().databaseIdentifier(); + string16 database_name = database.name(); + sender_->Send(new DatabaseHostMsg_Closed( + origin_identifier, database_name)); + open_connections_->RemoveOpenConnection(origin_identifier, database_name); +} + +void WebDatabaseObserverImpl::reportOpenDatabaseResult( + const WebDatabase& database, int callsite, int websql_error, + int sqlite_error) { + HISTOGRAM_WEBSQL_RESULT("OpenResult", database, callsite, + websql_error, sqlite_error); + HandleSqliteError(database, sqlite_error); +} + +void WebDatabaseObserverImpl::reportChangeVersionResult( + const WebDatabase& database, int callsite, int websql_error, + int sqlite_error) { + HISTOGRAM_WEBSQL_RESULT("ChangeVersionResult", database, callsite, + websql_error, sqlite_error); + HandleSqliteError(database, sqlite_error); +} + +void WebDatabaseObserverImpl::reportStartTransactionResult( + const WebDatabase& database, int callsite, int websql_error, + int sqlite_error) { + HISTOGRAM_WEBSQL_RESULT("BeginResult", database, callsite, + websql_error, sqlite_error); + HandleSqliteError(database, sqlite_error); +} + +void WebDatabaseObserverImpl::reportCommitTransactionResult( + const WebDatabase& database, int callsite, int websql_error, + int sqlite_error) { + HISTOGRAM_WEBSQL_RESULT("CommitResult", database, callsite, + websql_error, sqlite_error); + HandleSqliteError(database, sqlite_error); +} + +void WebDatabaseObserverImpl::reportExecuteStatementResult( + const WebDatabase& database, int callsite, int websql_error, + int sqlite_error) { + HISTOGRAM_WEBSQL_RESULT("StatementResult", database, callsite, + websql_error, sqlite_error); + HandleSqliteError(database, sqlite_error); +} + +void WebDatabaseObserverImpl::reportVacuumDatabaseResult( + const WebDatabase& database, int sqlite_error) { + int result = DetermineHistogramResult(-1, sqlite_error); + if (database.isSyncDatabase()) { + UMA_HISTOGRAM_ENUMERATION("websql.Sync.VacuumResult", + result, kResultHistogramSize); + } else { + UMA_HISTOGRAM_ENUMERATION("websql.Async.VacuumResult", + result, kResultHistogramSize); + } + HandleSqliteError(database, sqlite_error); +} + +void WebDatabaseObserverImpl::WaitForAllDatabasesToClose() { + open_connections_->WaitForAllDatabasesToClose(); +} + +void WebDatabaseObserverImpl::HandleSqliteError( + const WebDatabase& database, int error) { + // We filter out errors which the backend doesn't act on to avoid + // a unnecessary ipc traffic, this method can get called at a fairly + // high frequency (per-sqlstatement). + if (error == SQLITE_CORRUPT || error == SQLITE_NOTADB) { + sender_->Send(new DatabaseHostMsg_HandleSqliteError( + database.securityOrigin().databaseIdentifier(), + database.name(), + error)); + } +} + +} // namespace content diff --git a/content/child/web_database_observer_impl.h b/content/child/web_database_observer_impl.h new file mode 100644 index 0000000..a5b7da3 --- /dev/null +++ b/content/child/web_database_observer_impl.h @@ -0,0 +1,53 @@ +// 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. + +#ifndef CONTENT_CHILD_WEB_DATABASE_OBSERVER_IMPL_H_ +#define CONTENT_CHILD_WEB_DATABASE_OBSERVER_IMPL_H_ + +#include "base/memory/ref_counted.h" +#include "ipc/ipc_sync_message_filter.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebDatabaseObserver.h" +#include "webkit/common/database/database_connections.h" + +namespace content { + +class WebDatabaseObserverImpl : public WebKit::WebDatabaseObserver { + public: + explicit WebDatabaseObserverImpl(IPC::SyncMessageFilter* sender); + virtual ~WebDatabaseObserverImpl(); + + virtual void databaseOpened(const WebKit::WebDatabase& database) OVERRIDE; + virtual void databaseModified(const WebKit::WebDatabase& database) OVERRIDE; + virtual void databaseClosed(const WebKit::WebDatabase& database) OVERRIDE; + + virtual void reportOpenDatabaseResult( + const WebKit::WebDatabase& database, int callsite, + int websql_error, int sqlite_error) OVERRIDE; + virtual void reportChangeVersionResult( + const WebKit::WebDatabase& database, int callsite, + int websql_error, int sqlite_error) OVERRIDE; + virtual void reportStartTransactionResult( + const WebKit::WebDatabase& database, int callsite, + int websql_error, int sqlite_error) OVERRIDE; + virtual void reportCommitTransactionResult( + const WebKit::WebDatabase& database, int callsite, + int websql_error, int sqlite_error) OVERRIDE; + virtual void reportExecuteStatementResult( + const WebKit::WebDatabase& database, int callsite, + int websql_error, int sqlite_error) OVERRIDE; + virtual void reportVacuumDatabaseResult( + const WebKit::WebDatabase& database, int sqlite_error) OVERRIDE; + + void WaitForAllDatabasesToClose(); + + private: + void HandleSqliteError(const WebKit::WebDatabase& database, int error); + + scoped_refptr<IPC::SyncMessageFilter> sender_; + scoped_refptr<webkit_database::DatabaseConnectionsWrapper> open_connections_; +}; + +} // namespace content + +#endif // CONTENT_CHILD_WEB_DATABASE_OBSERVER_IMPL_H_ diff --git a/content/child/webblobregistry_impl.cc b/content/child/webblobregistry_impl.cc new file mode 100644 index 0000000..b70b883 --- /dev/null +++ b/content/child/webblobregistry_impl.cc @@ -0,0 +1,127 @@ +// 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/webblobregistry_impl.h" + +#include "base/memory/ref_counted.h" +#include "base/message_loop.h" +#include "base/shared_memory.h" +#include "content/common/child_thread.h" +#include "content/common/fileapi/webblob_messages.h" +#include "content/common/thread_safe_sender.h" +#include "third_party/WebKit/public/platform/WebBlobData.h" +#include "third_party/WebKit/public/platform/WebString.h" +#include "third_party/WebKit/public/platform/WebURL.h" +#include "webkit/base/file_path_string_conversions.h" +#include "webkit/common/blob/blob_data.h" + +using WebKit::WebBlobData; +using WebKit::WebString; +using WebKit::WebURL; + +namespace content { + +WebBlobRegistryImpl::WebBlobRegistryImpl(ThreadSafeSender* sender) + : sender_(sender) { +} + +WebBlobRegistryImpl::~WebBlobRegistryImpl() { +} + +void WebBlobRegistryImpl::registerBlobURL( + const WebURL& url, WebBlobData& data) { + DCHECK(ChildThread::current()->message_loop() == + base::MessageLoop::current()); + const size_t kLargeThresholdBytes = 250 * 1024; + const size_t kMaxSharedMemoryBytes = 10 * 1024 * 1024; + + sender_->Send(new BlobHostMsg_StartBuildingBlob(url)); + size_t i = 0; + WebBlobData::Item data_item; + while (data.itemAt(i++, data_item)) { + webkit_blob::BlobData::Item item; + switch (data_item.type) { + case WebBlobData::Item::TypeData: { + // WebBlobData does not allow partial data items. + DCHECK(!data_item.offset && data_item.length == -1); + if (data_item.data.size() == 0) + break; + if (data_item.data.size() < kLargeThresholdBytes) { + item.SetToBytes(data_item.data.data(), data_item.data.size()); + sender_->Send(new BlobHostMsg_AppendBlobDataItem(url, item)); + } else { + // We handle larger amounts of data via SharedMemory instead of + // writing it directly to the IPC channel. + size_t data_size = data_item.data.size(); + const char* data_ptr = data_item.data.data(); + size_t shared_memory_size = std::min( + data_size, kMaxSharedMemoryBytes); + scoped_ptr<base::SharedMemory> shared_memory( + ChildThread::AllocateSharedMemory(shared_memory_size, + sender_.get())); + CHECK(shared_memory.get()); + while (data_size) { + size_t chunk_size = std::min(data_size, shared_memory_size); + memcpy(shared_memory->memory(), data_ptr, chunk_size); + sender_->Send(new BlobHostMsg_SyncAppendSharedMemory( + url, shared_memory->handle(), chunk_size)); + data_size -= chunk_size; + data_ptr += chunk_size; + } + } + break; + } + case WebBlobData::Item::TypeFile: + if (data_item.length) { + item.SetToFilePathRange( + webkit_base::WebStringToFilePath(data_item.filePath), + static_cast<uint64>(data_item.offset), + static_cast<uint64>(data_item.length), + base::Time::FromDoubleT(data_item.expectedModificationTime)); + sender_->Send(new BlobHostMsg_AppendBlobDataItem(url, item)); + } + break; + case WebBlobData::Item::TypeBlob: + if (data_item.length) { + item.SetToBlobUrlRange( + data_item.blobURL, + static_cast<uint64>(data_item.offset), + static_cast<uint64>(data_item.length)); + sender_->Send(new BlobHostMsg_AppendBlobDataItem(url, item)); + } + break; + case WebBlobData::Item::TypeURL: + if (data_item.length) { + // We only support filesystem URL as of now. + DCHECK(GURL(data_item.url).SchemeIsFileSystem()); + item.SetToFileSystemUrlRange( + data_item.url, + static_cast<uint64>(data_item.offset), + static_cast<uint64>(data_item.length), + base::Time::FromDoubleT(data_item.expectedModificationTime)); + sender_->Send(new BlobHostMsg_AppendBlobDataItem(url, item)); + } + break; + default: + NOTREACHED(); + } + } + sender_->Send(new BlobHostMsg_FinishBuildingBlob( + url, data.contentType().utf8().data())); +} + +void WebBlobRegistryImpl::registerBlobURL( + const WebURL& url, const WebURL& src_url) { + DCHECK(ChildThread::current()->message_loop() == + base::MessageLoop::current()); + sender_->Send(new BlobHostMsg_CloneBlob(url, src_url)); +} + +void WebBlobRegistryImpl::unregisterBlobURL(const WebURL& url) { + DCHECK(ChildThread::current()->message_loop() == + base::MessageLoop::current()); + sender_->Send(new BlobHostMsg_RemoveBlob(url)); +} + +} // namespace content diff --git a/content/child/webblobregistry_impl.h b/content/child/webblobregistry_impl.h new file mode 100644 index 0000000..a624cfc --- /dev/null +++ b/content/child/webblobregistry_impl.h @@ -0,0 +1,36 @@ +// 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. + +#ifndef CONTENT_CHILD_FILEAPI_WEBBLOBREGISTRY_IMPL_H_ +#define CONTENT_CHILD_FILEAPI_WEBBLOBREGISTRY_IMPL_H_ + +#include "base/memory/ref_counted.h" +#include "third_party/WebKit/public/platform/WebBlobRegistry.h" + +namespace WebKit { +class WebBlobData; +class WebURL; +} + +namespace content { +class ThreadSafeSender; + +class WebBlobRegistryImpl : public WebKit::WebBlobRegistry { + public: + explicit WebBlobRegistryImpl(ThreadSafeSender* sender); + virtual ~WebBlobRegistryImpl(); + + virtual void registerBlobURL(const WebKit::WebURL& url, + WebKit::WebBlobData& data); + virtual void registerBlobURL(const WebKit::WebURL& url, + const WebKit::WebURL& src_url); + virtual void unregisterBlobURL(const WebKit::WebURL& url); + + private: + scoped_refptr<ThreadSafeSender> sender_; +}; + +} // namespace content + +#endif // CONTENT_CHILD_FILEAPI_WEBBLOBREGISTRY_IMPL_H_ |