diff options
26 files changed, 811 insertions, 15 deletions
diff --git a/base/platform_file.h b/base/platform_file.h index 52484c7..a584d42 100644 --- a/base/platform_file.h +++ b/base/platform_file.h @@ -31,7 +31,10 @@ enum PlatformFileFlags { PLATFORM_FILE_WRITE = 32, PLATFORM_FILE_EXCLUSIVE_READ = 64, // EXCLUSIVE is opposite of Windows SHARE PLATFORM_FILE_EXCLUSIVE_WRITE = 128, - PLATFORM_FILE_ASYNC = 256 + PLATFORM_FILE_ASYNC = 256, + PLATFORM_FILE_TEMPORARY = 512, // Used on Windows only + PLATFORM_FILE_HIDDEN = 1024, // Used on Windows only + PLATFORM_FILE_DELETE_ON_CLOSE = 2048 // Used on Windows only }; // Creates or open the given file. If PLATFORM_FILE_OPEN_ALWAYS is used, and diff --git a/base/platform_file_win.cc b/base/platform_file_win.cc index 7624798..b389a16 100644 --- a/base/platform_file_win.cc +++ b/base/platform_file_win.cc @@ -47,6 +47,12 @@ PlatformFile CreatePlatformFile(const std::wstring& name, DWORD create_flags = 0; if (flags & PLATFORM_FILE_ASYNC) create_flags |= FILE_FLAG_OVERLAPPED; + if (flags & PLATFORM_FILE_TEMPORARY) + create_flags |= FILE_ATTRIBUTE_TEMPORARY; + if (flags & PLATFORM_FILE_HIDDEN) + create_flags |= FILE_ATTRIBUTE_HIDDEN; + if (flags & PLATFORM_FILE_DELETE_ON_CLOSE) + create_flags |= FILE_FLAG_DELETE_ON_CLOSE; HANDLE file = CreateFile(name.c_str(), access, sharing, NULL, disposition, create_flags, NULL); diff --git a/chrome/browser/renderer_host/browser_render_process_host.cc b/chrome/browser/renderer_host/browser_render_process_host.cc index 26e7f92..345416d 100644 --- a/chrome/browser/renderer_host/browser_render_process_host.cc +++ b/chrome/browser/renderer_host/browser_render_process_host.cc @@ -340,6 +340,7 @@ bool BrowserRenderProcessHost::Init() { switches::kDisableAudio, switches::kSimpleDataSource, switches::kEnableBenchmarking, + switches::kEnableDatabases, }; for (size_t i = 0; i < arraysize(switch_names); ++i) { diff --git a/chrome/browser/renderer_host/database_dispatcher_host.cc b/chrome/browser/renderer_host/database_dispatcher_host.cc new file mode 100644 index 0000000..0e182da --- /dev/null +++ b/chrome/browser/renderer_host/database_dispatcher_host.cc @@ -0,0 +1,291 @@ +// Copyright (c) 2009 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 "chrome/browser/renderer_host/database_dispatcher_host.h" + +#if defined(OS_WIN) +#include <windows.h> +#endif + +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/message_loop.h" +#include "base/platform_file.h" +#include "base/process.h" +#include "base/scoped_ptr.h" +#include "base/task.h" +#include "base/thread.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/renderer_host/resource_message_filter.h" +#include "chrome/common/render_messages.h" +#include "ipc/ipc_message.h" +#include "third_party/sqlite/preprocessed/sqlite3.h" + +const int kNumDeleteRetries = 5; +const int kDelayDeleteRetryMs = 100; + +namespace { + +struct OpenFileParams { + FilePath db_dir; // directory where all DB files are stored + FilePath file_name; // DB file + int desired_flags; // flags to be used to open the file + base::ProcessHandle handle; // the handle of the renderer process +}; + +// Scheduled by the file Thread on the IO thread. +// Sends back to the renderer process the given message. +static void SendMessage(ResourceMessageFilter* sender, + IPC::Message* message) { + sender->Send(message); + + // Every time we get a DB-related message, we AddRef() the resource + // message filterto make sure it doesn't get destroyed before we have + // a chance to send the reply back. So we need to Release() is here + // and allow it to be destroyed if needed. + sender->Release(); +} + +// Scheduled by the IO thread on the file thread. +// Opens the given database file, then schedules +// a task on the IO thread's message loop to send an IPC back to +// corresponding renderer process with the file handle. +static void DatabaseOpenFile(MessageLoop* io_thread_message_loop, + const OpenFileParams& params, + int32 message_id, + ResourceMessageFilter* sender) { + base::PlatformFile target_handle = base::kInvalidPlatformFileValue; + // Create the database directory if it doesn't exist. + if (file_util::CreateDirectory(params.db_dir)) { + int flags = 0; + flags |= base::PLATFORM_FILE_READ; + if (params.desired_flags & SQLITE_OPEN_READWRITE) { + flags |= base::PLATFORM_FILE_WRITE; + } + + if (!(params.desired_flags & SQLITE_OPEN_MAIN_DB)) { + flags |= base::PLATFORM_FILE_EXCLUSIVE_READ | + base::PLATFORM_FILE_EXCLUSIVE_WRITE; + } + + if (params.desired_flags & SQLITE_OPEN_CREATE) { + flags |= base::PLATFORM_FILE_OPEN_ALWAYS; + } else { + flags |= base::PLATFORM_FILE_OPEN; + } + + if (params.desired_flags & SQLITE_OPEN_DELETEONCLOSE) { + flags |= base::PLATFORM_FILE_TEMPORARY | base::PLATFORM_FILE_HIDDEN | + base::PLATFORM_FILE_DELETE_ON_CLOSE; + } + + // Try to open/create the DB file. +#if defined(OS_WIN) + base::PlatformFile file_handle = + base::CreatePlatformFile(params.file_name.value(), flags, NULL); + if (file_handle != base::kInvalidPlatformFileValue) { + // Duplicate the file handle. + if (!DuplicateHandle(GetCurrentProcess(), file_handle, + params.handle, &target_handle, 0, false, + DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { + // file_handle is closed whether or not DuplicateHandle succeeds. + target_handle = INVALID_HANDLE_VALUE; + } + } +#endif + } + + io_thread_message_loop->PostTask(FROM_HERE, + NewRunnableFunction(SendMessage, sender, + new ViewMsg_DatabaseOpenFileResponse(message_id, target_handle))); +} + +// Scheduled by the IO thread on the file thread. +// Deletes the given database file, then schedules +// a task on the IO thread's message loop to send an IPC back to +// corresponding renderer process with the error code. +static void DatabaseDeleteFile( + MessageLoop* io_thread_message_loop, + const FilePath& file_name, + int32 message_id, + int reschedule_count, + ResourceMessageFilter* sender) { + if (reschedule_count > 0 && file_util::PathExists(file_name) && + !file_util::Delete(file_name, false)) { + MessageLoop::current()->PostDelayedTask(FROM_HERE, + NewRunnableFunction(DatabaseDeleteFile, io_thread_message_loop, + file_name, message_id, reschedule_count - 1, sender), + kDelayDeleteRetryMs); + } else { + io_thread_message_loop->PostTask(FROM_HERE, + NewRunnableFunction(SendMessage, sender, + new ViewMsg_DatabaseDeleteFileResponse( + message_id, reschedule_count > 0))); + } +} + +// Scheduled by the IO thread on the file thread. +// Gets the attributes of the given database file, then schedules +// a task on the IO thread's message loop to send an IPC back to +// corresponding renderer process. +static void DatabaseGetFileAttributes( + MessageLoop* io_thread_message_loop, + const FilePath& file_name, + int32 message_id, + ResourceMessageFilter* sender) { +#if defined(OS_WIN) + uint32 attributes = GetFileAttributes(file_name.value().c_str()); +#else + uint32 attributes = -1L; +#endif + + io_thread_message_loop->PostTask(FROM_HERE, + NewRunnableFunction(SendMessage, sender, + new ViewMsg_DatabaseGetFileAttributesResponse(message_id, attributes))); +} + +// Scheduled by the IO thread on the file thread. +// Gets the size of the given file, then schedules a task +// on the IO thread's message loop to send an IPC back to +// the corresponding renderer process. +static void DatabaseGetFileSize( + MessageLoop* io_thread_message_loop, + const FilePath& file_name, + int32 message_id, + ResourceMessageFilter* sender) { + int64 size = 0; + if (!file_util::GetFileSize(file_name, &size)) { + size = 0; + } + + io_thread_message_loop->PostTask(FROM_HERE, + NewRunnableFunction(SendMessage, sender, + new ViewMsg_DatabaseGetFileSizeResponse(message_id, size))); +} + +} // namespace + +DatabaseDispatcherHost::DatabaseDispatcherHost( + const FilePath& profile_path, + ResourceMessageFilter* resource_message_filter) + : profile_path_(profile_path), + resource_message_filter_(resource_message_filter), + file_thread_message_loop_( + g_browser_process->file_thread()->message_loop()) { +} + +DatabaseDispatcherHost::~DatabaseDispatcherHost() { +} + +bool DatabaseDispatcherHost::IsDBMessage(const IPC::Message& message) { + switch (message.type()) { + case ViewHostMsg_DatabaseOpenFile::ID: + case ViewHostMsg_DatabaseDeleteFile::ID: + case ViewHostMsg_DatabaseGetFileAttributes::ID: + case ViewHostMsg_DatabaseGetFileSize::ID: + return true; + } + return false; +} + +bool DatabaseDispatcherHost::OnMessageReceived( + const IPC::Message& message, bool* message_was_ok) { + if (!IsDBMessage(message)) { + return false; + } + *message_was_ok = true; + + bool handled = true; + IPC_BEGIN_MESSAGE_MAP_EX(DatabaseDispatcherHost, message, *message_was_ok) + IPC_MESSAGE_HANDLER(ViewHostMsg_DatabaseOpenFile, OnDatabaseOpenFile); + IPC_MESSAGE_HANDLER(ViewHostMsg_DatabaseDeleteFile, OnDatabaseDeleteFile); + IPC_MESSAGE_HANDLER(ViewHostMsg_DatabaseGetFileAttributes, + OnDatabaseGetFileAttributes); + IPC_MESSAGE_HANDLER(ViewHostMsg_DatabaseGetFileSize, + OnDatabaseGetFileSize); + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP_EX() + return handled; +} + +FilePath DatabaseDispatcherHost::GetDBDir() { + return profile_path_.Append(FILE_PATH_LITERAL("databases")); +} + +FilePath DatabaseDispatcherHost::GetDBFileFullPath(const FilePath& file_name) { + // Do not allow '\', '/' and ':' in file names. + FilePath::StringType file = file_name.value(); + if ((file.find('\\') != std::wstring::npos) || + (file.find('/') != std::wstring::npos) || + (file.find(':') != std::wstring::npos)) { + return FilePath(); + } + return GetDBDir().Append(file_name); +} + +void DatabaseDispatcherHost::OnDatabaseOpenFile( + const FilePath& file_name, int desired_flags, + int32 message_id) { + FilePath db_dir = GetDBDir(); + FilePath db_file_name = GetDBFileFullPath(file_name); + + if (db_file_name.empty()) { + resource_message_filter_->Send(new ViewMsg_DatabaseOpenFileResponse( + message_id, base::kInvalidPlatformFileValue)); + return; + } + + OpenFileParams params = { db_dir, db_file_name, desired_flags, + resource_message_filter_->handle() }; + resource_message_filter_->AddRef(); + file_thread_message_loop_->PostTask(FROM_HERE, + NewRunnableFunction(DatabaseOpenFile, MessageLoop::current(), + params, message_id, resource_message_filter_)); +} + +void DatabaseDispatcherHost::OnDatabaseDeleteFile( + const FilePath& file_name, int32 message_id) { + FilePath db_file_name = GetDBFileFullPath(file_name); + if (db_file_name.empty()) { + resource_message_filter_->Send(new ViewMsg_DatabaseDeleteFileResponse( + message_id, false)); + return; + } + + resource_message_filter_->AddRef(); + file_thread_message_loop_->PostTask(FROM_HERE, + NewRunnableFunction(DatabaseDeleteFile, MessageLoop::current(), + db_file_name, message_id, kNumDeleteRetries, resource_message_filter_)); +} + +void DatabaseDispatcherHost::OnDatabaseGetFileAttributes( + const FilePath& file_name, int32 message_id) { + FilePath db_file_name = GetDBFileFullPath(file_name); + if (db_file_name.empty()) { + resource_message_filter_->Send( + new ViewMsg_DatabaseGetFileAttributesResponse( + message_id, -1)); + return; + } + + resource_message_filter_->AddRef(); + file_thread_message_loop_->PostTask(FROM_HERE, + NewRunnableFunction(DatabaseGetFileAttributes, MessageLoop::current(), + db_file_name, message_id, resource_message_filter_)); +} + +void DatabaseDispatcherHost::OnDatabaseGetFileSize( + const FilePath& file_name, int32 message_id) { + FilePath db_file_name = GetDBFileFullPath(file_name); + if (db_file_name.empty()) { + resource_message_filter_->Send(new ViewMsg_DatabaseGetFileSizeResponse( + message_id, 0)); + return; + } + + resource_message_filter_->AddRef(); + file_thread_message_loop_->PostTask(FROM_HERE, + NewRunnableFunction(DatabaseGetFileSize, MessageLoop::current(), + db_file_name, message_id, resource_message_filter_)); +} diff --git a/chrome/browser/renderer_host/database_dispatcher_host.h b/chrome/browser/renderer_host/database_dispatcher_host.h new file mode 100644 index 0000000..90dc00d --- /dev/null +++ b/chrome/browser/renderer_host/database_dispatcher_host.h @@ -0,0 +1,59 @@ +// Copyright (c) 2009 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 CHROME_BROWSER_RENDERER_HOST_DATABASE_DISPATCHER_HOST_H_ +#define CHROME_BROWSER_RENDERER_HOST_DATABASE_DISPATCHER_HOST_H_ + +#include "base/file_path.h" +#include "base/ref_counted.h" + +class MessageLoop; +class ResourceMessageFilter; + +namespace IPC { +class Message; +} + +class DatabaseDispatcherHost { + public: + DatabaseDispatcherHost(const FilePath& profile_path, + ResourceMessageFilter* resource_message_filter); + ~DatabaseDispatcherHost(); + + // Returns true iff the message is HTML5 DB related and was processed. + bool OnMessageReceived(const IPC::Message& message, bool* message_was_ok); + + // Processes the request to return a handle to the given DB file. + void OnDatabaseOpenFile(const FilePath& file_name, + int desired_flags, int32 message_id); + // Processes the request to delete the given DB file. + void OnDatabaseDeleteFile(const FilePath& file_name, + int32 message_id); + // Processes the request to return the attributes of the given DB file. + void OnDatabaseGetFileAttributes(const FilePath& file_name, + int32 message_id); + // Processes the request to return the size of the given file. + void OnDatabaseGetFileSize(const FilePath& file_name, + int32 message_id); + + private: + // Determines if the message is HTML5 DB related. + bool IsDBMessage(const IPC::Message& message); + + // Returns the directory where all DB files are stored. + FilePath GetDBDir(); + // Returns the absolute name of the given DB file. + FilePath GetDBFileFullPath(const FilePath& file_name); + + // The user data directory. + FilePath profile_path_; + + // The ResourceMessageFilter instance of this renderer process. + ResourceMessageFilter* resource_message_filter_; + + // The message loop of the file thread. + MessageLoop* file_thread_message_loop_; +}; + +#endif // CHROME_BROWSER_RENDERER_HOST_DATABASE_DISPATCHER_HOST_H_ diff --git a/chrome/browser/renderer_host/render_sandbox_host_linux.cc b/chrome/browser/renderer_host/render_sandbox_host_linux.cc index 31f6631..6b274f1 100644 --- a/chrome/browser/renderer_host/render_sandbox_host_linux.cc +++ b/chrome/browser/renderer_host/render_sandbox_host_linux.cc @@ -12,6 +12,7 @@ #include <time.h> #include "base/eintr_wrapper.h" +#include "base/platform_file.h" #include "base/process_util.h" #include "base/logging.h" #include "base/message_loop.h" @@ -158,6 +159,20 @@ class SandboxIPCProcess : public WebKitClient { virtual void callOnMainThread(void (*)()) { } + virtual base::PlatformFile databaseOpenFile( + const WebString& fileName, int desiredFlags) { + return base::kInvalidPlatformFileValue; + } + virtual bool databaseDeleteFile(const WebString& fileName) { + return false; + } + virtual long databaseGetFileAttributes(const WebString& fileName) { + return -1; + } + virtual long long databaseGetFileSize(const WebString& fileName) { + return 0; + } + private: // --------------------------------------------------------------------------- // Requests from the renderer... diff --git a/chrome/browser/renderer_host/resource_message_filter.cc b/chrome/browser/renderer_host/resource_message_filter.cc index f369191..ff376aa 100644 --- a/chrome/browser/renderer_host/resource_message_filter.cc +++ b/chrome/browser/renderer_host/resource_message_filter.cc @@ -21,6 +21,7 @@ #include "chrome/browser/privacy_blacklist/blacklist.h" #include "chrome/browser/renderer_host/audio_renderer_host.h" #include "chrome/browser/renderer_host/browser_render_process_host.h" +#include "chrome/browser/renderer_host/database_dispatcher_host.h" #include "chrome/browser/renderer_host/file_system_accessor.h" #include "chrome/browser/renderer_host/render_widget_helper.h" #include "chrome/browser/spellchecker.h" @@ -155,6 +156,8 @@ ResourceMessageFilter::ResourceMessageFilter( ALLOW_THIS_IN_INITIALIZER_LIST(dom_storage_dispatcher_host_( new DOMStorageDispatcherHost(this, profile->GetWebKitContext(), resource_dispatcher_host->webkit_thread()))), + db_dispatcher_host_( + new DatabaseDispatcherHost(profile->GetPath(), this)), off_the_record_(profile->IsOffTheRecord()) { DCHECK(request_context_.get()); DCHECK(request_context_->cookie_store()); @@ -249,7 +252,9 @@ bool ResourceMessageFilter::OnMessageReceived(const IPC::Message& message) { message, &msg_is_ok) || dom_storage_dispatcher_host_->OnMessageReceived( message, &msg_is_ok) || - audio_renderer_host_->OnMessageReceived(message, &msg_is_ok); + audio_renderer_host_->OnMessageReceived( + message, &msg_is_ok) || + db_dispatcher_host_->OnMessageReceived(message, &msg_is_ok); if (!handled) { DCHECK(msg_is_ok); // It should have been marked handled if it wasn't OK. diff --git a/chrome/browser/renderer_host/resource_message_filter.h b/chrome/browser/renderer_host/resource_message_filter.h index 379cfdb..ee92565 100644 --- a/chrome/browser/renderer_host/resource_message_filter.h +++ b/chrome/browser/renderer_host/resource_message_filter.h @@ -32,6 +32,7 @@ class AppCacheDispatcherHost; class AudioRendererHost; class Clipboard; +class DatabaseDispatcherHost; class DOMStorageDispatcherHost; class ExtensionMessageService; class Profile; @@ -310,6 +311,9 @@ class ResourceMessageFilter : public IPC::ChannelProxy::MessageFilter, // Handles DOM Storage related messages. scoped_refptr<DOMStorageDispatcherHost> dom_storage_dispatcher_host_; + // Handles HTML5 DB related messages + scoped_ptr<DatabaseDispatcherHost> db_dispatcher_host_; + // Whether this process is used for off the record tabs. bool off_the_record_; diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index 6c96f2e..c353740 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -381,6 +381,8 @@ 'common/common_glue.cc', 'common/common_param_traits.cc', 'common/common_param_traits.h', + 'common/db_message_filter.cc', + 'common/db_message_filter.h', 'common/debug_flags.cc', 'common/debug_flags.h', 'common/devtools_messages.h', @@ -1402,6 +1404,8 @@ 'browser/renderer_host/buffered_resource_handler.h', 'browser/renderer_host/cross_site_resource_handler.cc', 'browser/renderer_host/cross_site_resource_handler.h', + 'browser/renderer_host/database_dispatcher_host.cc', + 'browser/renderer_host/database_dispatcher_host.h', 'browser/renderer_host/download_resource_handler.cc', 'browser/renderer_host/download_resource_handler.h', 'browser/renderer_host/download_throttling_resource_handler.cc', diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index 4c321bb..d6a1ab6 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -557,4 +557,6 @@ const wchar_t kDiagnostics[] = L"diagnostics"; // Enables the custom JumpList on Windows 7. const wchar_t kEnableCustomJumpList[] = L"enable-custom-jumplist"; +// Enables HTML5 DB support. +const wchar_t kEnableDatabases[] = L"enable-databases"; } // namespace switches diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index 6b8e4387..d77182e 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -219,6 +219,7 @@ extern const wchar_t kDiagnostics[]; extern const wchar_t kEnableCustomJumpList[]; +extern const wchar_t kEnableDatabases[]; } // namespace switches #endif // CHROME_COMMON_CHROME_SWITCHES_H_ diff --git a/chrome/common/db_message_filter.cc b/chrome/common/db_message_filter.cc new file mode 100644 index 0000000..cba4aea --- /dev/null +++ b/chrome/common/db_message_filter.cc @@ -0,0 +1,88 @@ +// Copyright (c) 2009 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 "db_message_filter.h" + +#include "base/atomic_sequence_num.h" +#include "base/id_map.h" +#include "base/lock.h" +#include "base/message_loop.h" +#include "base/platform_file.h" +#include "base/task.h" +#include "chrome/common/child_process.h" +#include "chrome/common/render_messages.h" +#include "ipc/ipc_channel.h" +#include "ipc/ipc_message.h" + +DBMessageFilter* DBMessageFilter::instance_ = NULL; + +DBMessageFilter::DBMessageFilter() + : io_thread_message_loop_(ChildProcess::current()->io_message_loop()), + channel_(NULL), channel_lock_(new Lock()), + shutdown_event_(ChildProcess::current()->GetShutDownEvent()), + messages_awaiting_replies_(new IDMap<DBMessageState>()), + unique_id_generator_(new base::AtomicSequenceNumber()) { + DCHECK(!instance_); + instance_ = this; +} + +DBMessageFilter::~DBMessageFilter() { + instance_ = NULL; +} + +DBMessageFilter* DBMessageFilter::GetInstance() { + return instance_; +} + +int DBMessageFilter::GetUniqueID() { + return unique_id_generator_->GetNext(); +} + +static void SendMessageOnIOThread(IPC::Message* message, + IPC::Channel* channel, + Lock* channel_lock) { + AutoLock channel_auto_lock(*channel_lock); + if (!channel) { + delete message; + } else { + channel->Send(message); + } +} + +void DBMessageFilter::Send(IPC::Message* message) { + io_thread_message_loop_->PostTask(FROM_HERE, + NewRunnableFunction(SendMessageOnIOThread, message, + channel_, channel_lock_.get())); +} + +void DBMessageFilter::OnFilterAdded(IPC::Channel* channel) { + AutoLock channel_auto_lock(*channel_lock_); + channel_ = channel; +} + +void DBMessageFilter::OnChannelError() { + AutoLock channel_auto_lock(*channel_lock_); + channel_ = NULL; +} + +void DBMessageFilter::OnChannelClosing() { + AutoLock channel_auto_lock(*channel_lock_); + channel_ = NULL; +} + +bool DBMessageFilter::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(DBMessageFilter, message) + IPC_MESSAGE_HANDLER(ViewMsg_DatabaseOpenFileResponse, + OnResponse<base::PlatformFile>) + IPC_MESSAGE_HANDLER(ViewMsg_DatabaseDeleteFileResponse, + OnResponse<bool>) + IPC_MESSAGE_HANDLER(ViewMsg_DatabaseGetFileAttributesResponse, + OnResponse<uint32>) + IPC_MESSAGE_HANDLER(ViewMsg_DatabaseGetFileSizeResponse, + OnResponse<int64>) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} diff --git a/chrome/common/db_message_filter.h b/chrome/common/db_message_filter.h new file mode 100644 index 0000000..1376db0 --- /dev/null +++ b/chrome/common/db_message_filter.h @@ -0,0 +1,137 @@ +// Copyright (c) 2009 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 CHROME_COMMON_DB_MESSAGE_FILTER_H_ +#define CHROME_COMMON_DB_MESSAGE_FILTER_H_ + +#include "base/id_map.h" +#include "base/scoped_ptr.h" +#include "base/waitable_event.h" +#include "ipc/ipc_channel_proxy.h" + +class Lock; +class MessageLoop; + +namespace base { +class AtomicSequenceNumber; +} + +namespace IPC { +class Channel; +} + +// A thread-safe message filter used to send IPCs from DB threads and +// process replies from the browser process. +// +// This class should not be instantianted anywhere but RenderThread::Init(). +// It is meant to be a singleton in each renderer process. To get the +// singleton, use DBMessageFilter::GetInstance(). +class DBMessageFilter : public IPC::ChannelProxy::MessageFilter { + public: + // Creates a new DBMessageFilter instance. + DBMessageFilter(); + + // Returns the DBMessageFilter singleton created in this renderer process. + static DBMessageFilter* GetInstance(); + + // Returns a ID that uniquely identifies each message that will be sent + // using the SendAndWait() method. + virtual int GetUniqueID(); + + // Posts a task to the IO thread to send the given message to the browser. + // + // message - The message. + virtual void Send(IPC::Message* message); + + // Sends a message and blocks the current thread until a reply for that + // message is received or the renderer process is about to be destroyed. + // + // Returns the result from the reply message or the default value if the + // renderer process is being destroyed, or the message could not be sent. + // + // message - The message to be sent. + // message_id - An ID that uniquely identifies this message. + // default_result - The default value; returned when the renderer process + // is being destroyed before the reply got back, or if + // the message could not be sent. + template<class ResultType> + ResultType SendAndWait(IPC::Message* message, int message_id, + ResultType default_result) { + ResultType result; + base::WaitableEvent waitable_event(false, false); + DBMessageState state = + { reinterpret_cast<intptr_t>(&result), &waitable_event }; + messages_awaiting_replies_->AddWithID(&state, message_id); + + Send(message); + + base::WaitableEvent* events[2] = { shutdown_event_, &waitable_event }; + size_t event_index = base::WaitableEvent::WaitMany(events, 2); + return (event_index ? result : default_result); + } + + // Processes the incoming message from the browser process. + // + // message - The message. + virtual bool OnMessageReceived(const IPC::Message& message); + + private: + // The state we store for each message we send + struct DBMessageState { + intptr_t result_address_; + base::WaitableEvent* waitable_event_; + }; + + // This is a RefCount'ed class, do not allow anybody to destroy + // instances of this class + virtual ~DBMessageFilter(); + + // Invoked when this filter is added to a channel. + // + // channel - The channel this filter was added to. + virtual void OnFilterAdded(IPC::Channel* channel); + + // Called when the channel encounters a problem. The filter should + // clean up its internal data and not accept any more messages. + virtual void OnChannelError(); + + // Called when the channel is closing. The filter should clean up + // its internal data and not accept any more messages. + virtual void OnChannelClosing(); + + // Processes the reply to a DB request + template<class ResultType> + void OnResponse(int32 message_id, ResultType result) { + DBMessageState *state = messages_awaiting_replies_->Lookup(message_id); + if (state) { + messages_awaiting_replies_->Remove(message_id); + *reinterpret_cast<ResultType*>(state->result_address_) = result; + state->waitable_event_->Signal(); + } + } + + // The message loop of the IO thread + MessageLoop* io_thread_message_loop_; + + // The channel this filter was added to + IPC::Channel* channel_; + + // A lock around the channel + scoped_ptr<Lock> channel_lock_; + + // The shutdown event + base::WaitableEvent* shutdown_event_; + + // The list of messages awaiting replies + // For each such message we store a DBMessageState instance + scoped_ptr<IDMap<DBMessageState> > messages_awaiting_replies_; + + // A thread-safe unique number generator + scoped_ptr<base::AtomicSequenceNumber> unique_id_generator_; + + // The singleton + static DBMessageFilter* instance_; +}; + +#endif // CHROME_COMMON_DB_MESSAGE_FILTER_H_ diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h index 36afb03..ae5e38b 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -13,6 +13,7 @@ #include "base/clipboard.h" #include "base/file_path.h" +#include "base/platform_file.h" #include "base/gfx/rect.h" #include "base/gfx/native_widget_types.h" #include "base/shared_memory.h" @@ -627,6 +628,26 @@ IPC_BEGIN_MESSAGES(View) IPC_MESSAGE_CONTROL1(UtilityMsg_UnpackWebResource, std::string /* JSON data */) + // Returns a file handle + IPC_MESSAGE_CONTROL2(ViewMsg_DatabaseOpenFileResponse, + int32 /* the ID of the message we're replying to */, + base::PlatformFile /* the HANDLE/fd of the DB file */) + + // Returns the error code returned by a call to DeleteFile() + IPC_MESSAGE_CONTROL2(ViewMsg_DatabaseDeleteFileResponse, + int32 /* the ID of the message we're replying to */, + bool /* whether or not the DB file was deleted */) + + // Returns the attributes of a file + IPC_MESSAGE_CONTROL2(ViewMsg_DatabaseGetFileAttributesResponse, + int32 /* the ID of the message we're replying to */, + int32 /* the attributes for the given DB file */) + + // Returns the size of a file + IPC_MESSAGE_CONTROL2(ViewMsg_DatabaseGetFileSizeResponse, + int32 /* the ID of the message we're replying to */, + int64 /* the size of the given DB file */) + IPC_END_MESSAGES(View) @@ -1588,4 +1609,25 @@ IPC_BEGIN_MESSAGES(ViewHost) // ViewMsg_CSSInsertRequest message and css has been inserted into the frame. IPC_MESSAGE_ROUTED0(ViewHostMsg_OnCSSInserted) + // Asks the browser process to open a DB file with the given name + IPC_MESSAGE_CONTROL3(ViewHostMsg_DatabaseOpenFile, + FilePath /* file name */, + int /* desired flags */, + int32 /* a unique message ID */) + + // Asks the browser process to delete a DB file + IPC_MESSAGE_CONTROL2(ViewHostMsg_DatabaseDeleteFile, + FilePath /* the name of the file */, + int32 /* a unique message ID */) + + // Asks the browser process to return the attributes of a DB file + IPC_MESSAGE_CONTROL2(ViewHostMsg_DatabaseGetFileAttributes, + FilePath /* the name of the file */, + int32 /* a unique message ID */) + + // Asks the browser process to return the size of a DB file + IPC_MESSAGE_CONTROL2(ViewHostMsg_DatabaseGetFileSize, + FilePath /* the name of the file */, + int32 /* a unique message ID */) + IPC_END_MESSAGES(ViewHost) diff --git a/chrome/renderer/render_thread.cc b/chrome/renderer/render_thread.cc index 80811b2..0067c65 100644 --- a/chrome/renderer/render_thread.cc +++ b/chrome/renderer/render_thread.cc @@ -15,6 +15,7 @@ #include "chrome/common/app_cache/app_cache_context_impl.h" #include "chrome/common/app_cache/app_cache_dispatcher.h" #include "chrome/common/chrome_switches.h" +#include "chrome/common/db_message_filter.h" #include "chrome/common/render_messages.h" #include "chrome/common/renderer_preferences.h" #include "chrome/common/url_constants.h" @@ -117,6 +118,8 @@ void RenderThread::Init() { WebAppCacheContext::SetFactory(CreateAppCacheContextForRenderer); devtools_agent_filter_ = new DevToolsAgentFilter(); AddFilter(devtools_agent_filter_.get()); + db_message_filter_ = new DBMessageFilter(); + AddFilter(db_message_filter_.get()); #if defined(OS_POSIX) suicide_on_channel_error_filter_ = new SuicideOnChannelErrorFilter; @@ -127,6 +130,8 @@ void RenderThread::Init() { RenderThread::~RenderThread() { // Shutdown in reverse of the initialization order. RemoveFilter(devtools_agent_filter_.get()); + RemoveFilter(db_message_filter_.get()); + db_message_filter_ = NULL; WebAppCacheContext::SetFactory(NULL); if (webkit_client_.get()) WebKit::shutdown(); @@ -369,6 +374,10 @@ void RenderThread::EnsureWebKitInitialized() { if (RenderProcess::current()->initialized_media_library()) WebKit::enableMediaPlayer(); + + if (command_line.HasSwitch(switches::kEnableDatabases)) { + WebKit::enableDatabases(); + } } void RenderThread::OnExtensionMessageInvoke(const std::string& function_name, diff --git a/chrome/renderer/render_thread.h b/chrome/renderer/render_thread.h index d2a1fcd..c242c61 100644 --- a/chrome/renderer/render_thread.h +++ b/chrome/renderer/render_thread.h @@ -17,6 +17,7 @@ #include "chrome/renderer/visitedlink_slave.h" class AppCacheDispatcher; +class DBMessageFilter; class DevToolsAgentFilter; class FilePath; class ListValue; @@ -161,6 +162,8 @@ class RenderThread : public RenderThreadBase, scoped_ptr<RendererHistogramSnapshots> histogram_snapshots_; scoped_ptr<RendererWebKitClientImpl> webkit_client_; + scoped_refptr<DBMessageFilter> db_message_filter_; + #if defined(OS_POSIX) scoped_refptr<IPC::ChannelProxy::MessageFilter> suicide_on_channel_error_filter_; diff --git a/chrome/renderer/renderer_webkitclient_impl.cc b/chrome/renderer/renderer_webkitclient_impl.cc index a863971..f14a4cf 100644 --- a/chrome/renderer/renderer_webkitclient_impl.cc +++ b/chrome/renderer/renderer_webkitclient_impl.cc @@ -5,7 +5,10 @@ #include "chrome/renderer/renderer_webkitclient_impl.h" #include "base/command_line.h" +#include "base/file_path.h" +#include "base/platform_file.h" #include "chrome/common/chrome_switches.h" +#include "chrome/common/db_message_filter.h" #include "chrome/common/render_messages.h" #include "chrome/plugin/npobject_util.h" #include "chrome/renderer/net/render_dns_master.h" @@ -192,3 +195,48 @@ WebString RendererWebKitClientImpl::SandboxSupport::getFontFamilyForCharacters( } #endif + +//------------------------------------------------------------------------------ + +base::PlatformFile RendererWebKitClientImpl::databaseOpenFile( + const WebString& file_name, int desired_flags) { + DBMessageFilter* db_message_filter = DBMessageFilter::GetInstance(); + int message_id = db_message_filter->GetUniqueID(); + return db_message_filter->SendAndWait( + new ViewHostMsg_DatabaseOpenFile( + FilePath(webkit_glue::WebStringToFilePathString(file_name)), + desired_flags, message_id), + message_id, base::kInvalidPlatformFileValue); +} + +bool RendererWebKitClientImpl::databaseDeleteFile(const WebString& file_name) { + DBMessageFilter* db_message_filter = DBMessageFilter::GetInstance(); + int message_id = db_message_filter->GetUniqueID(); + return db_message_filter->SendAndWait( + new ViewHostMsg_DatabaseDeleteFile( + FilePath(webkit_glue::WebStringToFilePathString(file_name)), + message_id), + message_id, false); +} + +long RendererWebKitClientImpl::databaseGetFileAttributes( + const WebString& file_name) { + DBMessageFilter* db_message_filter = DBMessageFilter::GetInstance(); + int message_id = db_message_filter->GetUniqueID(); + return db_message_filter->SendAndWait( + new ViewHostMsg_DatabaseGetFileAttributes( + FilePath(webkit_glue::WebStringToFilePathString(file_name)), + message_id), + message_id, -1L); +} + +long long RendererWebKitClientImpl::databaseGetFileSize( + const WebString& file_name) { + DBMessageFilter* db_message_filter = DBMessageFilter::GetInstance(); + int message_id = db_message_filter->GetUniqueID(); + return db_message_filter->SendAndWait( + new ViewHostMsg_DatabaseGetFileSize( + FilePath(webkit_glue::WebStringToFilePathString(file_name)), + message_id), + message_id, 0LL); +} diff --git a/chrome/renderer/renderer_webkitclient_impl.h b/chrome/renderer/renderer_webkitclient_impl.h index 0190027..1f1880d 100644 --- a/chrome/renderer/renderer_webkitclient_impl.h +++ b/chrome/renderer/renderer_webkitclient_impl.h @@ -5,6 +5,7 @@ #ifndef CHROME_RENDERER_RENDERER_WEBKIT_CLIENT_IMPL_H_ #define CHROME_RENDERER_RENDERER_WEBKIT_CLIENT_IMPL_H_ +#include "base/platform_file.h" #include "webkit/glue/simple_webmimeregistry_impl.h" #include "webkit/glue/webclipboard_impl.h" #include "webkit/glue/webkitclient_impl.h" @@ -40,6 +41,12 @@ class RendererWebKitClientImpl : public webkit_glue::WebKitClientImpl { const WebKit::WebString& path); virtual WebKit::WebStorageNamespace* createSessionStorageNamespace(); + virtual base::PlatformFile databaseOpenFile( + const WebKit::WebString& file_name, int desired_flags); + virtual bool databaseDeleteFile(const WebKit::WebString& file_name); + virtual long databaseGetFileAttributes(const WebKit::WebString& file_name); + virtual long long databaseGetFileSize(const WebKit::WebString& file_name); + private: class MimeRegistry : public webkit_glue::SimpleWebMimeRegistryImpl { public: diff --git a/webkit/api/public/WebKit.h b/webkit/api/public/WebKit.h index f824808..94bb8b0 100644 --- a/webkit/api/public/WebKit.h +++ b/webkit/api/public/WebKit.h @@ -86,6 +86,10 @@ namespace WebKit { // Purge the plugin list cache. WEBKIT_API void resetPluginCache(); + // Enables HTML5 database support. + WEBKIT_API void enableDatabases(); + WEBKIT_API bool databasesEnabled(); + } // namespace WebKit #endif diff --git a/webkit/api/public/WebKitClient.h b/webkit/api/public/WebKitClient.h index 6e84c8d..a63ae3d 100644 --- a/webkit/api/public/WebKitClient.h +++ b/webkit/api/public/WebKitClient.h @@ -34,6 +34,10 @@ #include "WebCommon.h" #include "WebLocalizedString.h" +#if defined(OS_WIN) +#include <windows.h> +#endif + namespace WebKit { class WebClipboard; class WebData; @@ -154,6 +158,26 @@ namespace WebKit { // Callable from a background WebKit thread. virtual void callOnMainThread(void (*func)()) = 0; + + // HTML5 DB ------------------------------------------------------------ + +#if defined(OS_WIN) +typedef HANDLE FileType; +#else +typedef int FileType; +#endif + + // Opens a database file + virtual FileType databaseOpenFile(const WebString& fileName, int desiredFlags) = 0; + + // Deletes a database file and returns the error code + virtual bool databaseDeleteFile(const WebString& fileName) = 0; + + // Returns the attributes of the given database file + virtual long databaseGetFileAttributes(const WebString& fileName) = 0; + + // Returns the size of the given database file + virtual long long databaseGetFileSize(const WebString& fileName) = 0; }; } // namespace WebKit diff --git a/webkit/api/src/ChromiumBridge.cpp b/webkit/api/src/ChromiumBridge.cpp index 93ac857..5f0461f 100644 --- a/webkit/api/src/ChromiumBridge.cpp +++ b/webkit/api/src/ChromiumBridge.cpp @@ -429,30 +429,22 @@ bool ChromiumBridge::isLinkVisited(WebCore::LinkHash visitedLinkHash) PlatformFileHandle ChromiumBridge::databaseOpenFile(const String& fileName, int desiredFlags) { - // FIXME: un-stub when the code on the browser process side is submitted - //return webKitClient()->databaseOpenFile(WebString(fileName), desiredFlags); - return invalidPlatformFileHandle; + return webKitClient()->databaseOpenFile(WebString(fileName), desiredFlags); } bool ChromiumBridge::databaseDeleteFile(const String& fileName) { - // FIXME: un-stub when the code on the browser process side is submitted - //return webKitClient()->databaseDeleteFile(WebString(fileName)); - return false; + return webKitClient()->databaseDeleteFile(WebString(fileName)); } long ChromiumBridge::databaseGetFileAttributes(const String& fileName) { - // FIXME: un-stub when the code on the browser process side is submitted - //return webKitClient()->databaseGetFileAttributes(WebString(fileName)); - return 0L; + return webKitClient()->databaseGetFileAttributes(WebString(fileName)); } long long ChromiumBridge::databaseGetFileSize(const String& fileName) { - // FIXME: un-stub when the code on the browser process side is submitted - //return webKitClient()->databaseGetFileSize(WebString(fileName)); - return 0LL; + return webKitClient()->databaseGetFileSize(WebString(fileName)); } #endif diff --git a/webkit/api/src/WebKit.cpp b/webkit/api/src/WebKit.cpp index 0bdbf42..e88a7a8 100644 --- a/webkit/api/src/WebKit.cpp +++ b/webkit/api/src/WebKit.cpp @@ -47,6 +47,7 @@ namespace WebKit { static WebKitClient* s_webKitClient = 0; static bool s_layoutTestMode = false; +static bool s_databasesEnabled = false; void initialize(WebKitClient* webKitClient) { @@ -123,4 +124,17 @@ void resetPluginCache() WebCore::Page::refreshPlugins(false); } +void enableDatabases() +{ +#if ENABLE(DATABASE) + s_databasesEnabled = true; +#endif +} + +bool databasesEnabled() +{ + return s_databasesEnabled; +} + + } // namespace WebKit diff --git a/webkit/glue/chrome_client_impl.cc b/webkit/glue/chrome_client_impl.cc index fa965ef..1390dfa 100644 --- a/webkit/glue/chrome_client_impl.cc +++ b/webkit/glue/chrome_client_impl.cc @@ -14,6 +14,7 @@ MSVC_PUSH_WARNING_LEVEL(0); #include "Cursor.h" #include "Document.h" #include "DocumentLoader.h" +#include "DatabaseTracker.h" #include "FloatRect.h" #include "FileChooser.h" #include "FrameLoadRequest.h" @@ -541,7 +542,10 @@ void ChromeClientImpl::print(WebCore::Frame* frame) { void ChromeClientImpl::exceededDatabaseQuota(WebCore::Frame* frame, const WebCore::String& databaseName) { - // TODO(tc): If we enable the storage API, we need to implement this function. + // set a reasonable quota for now -- 5Mb should be enough for anybody + // TODO(dglazkov): this should be configurable + WebCore::SecurityOrigin* origin = frame->document()->securityOrigin(); + WebCore::DatabaseTracker::tracker().setQuota(origin, 1024 * 1024 * 5); } void ChromeClientImpl::runOpenPanel(WebCore::Frame* frame, diff --git a/webkit/glue/webkitclient_impl.cc b/webkit/glue/webkitclient_impl.cc index c6bd4c8..78c52be 100644 --- a/webkit/glue/webkitclient_impl.cc +++ b/webkit/glue/webkitclient_impl.cc @@ -5,6 +5,7 @@ #include "webkit/glue/webkitclient_impl.h" #include "base/message_loop.h" +#include "base/platform_file.h" #include "base/stats_counters.h" #include "base/string_util.h" #include "base/trace_event.h" @@ -224,4 +225,24 @@ void WebKitClientImpl::callOnMainThread(void (*func)()) { main_loop_->PostTask(FROM_HERE, NewRunnableFunction(func)); } +base::PlatformFile WebKitClientImpl::databaseOpenFile( + const WebKit::WebString& file_name, int desired_flags) { + return base::kInvalidPlatformFileValue; +} + +bool WebKitClientImpl::databaseDeleteFile( + const WebKit::WebString& file_name) { + return false; +} + +long WebKitClientImpl::databaseGetFileAttributes( + const WebKit::WebString& file_name) { + return 0; +} + +long long WebKitClientImpl::databaseGetFileSize( + const WebKit::WebString& file_name) { + return 0; +} + } // namespace webkit_glue diff --git a/webkit/glue/webkitclient_impl.h b/webkit/glue/webkitclient_impl.h index b97d63a..e3035ae 100644 --- a/webkit/glue/webkitclient_impl.h +++ b/webkit/glue/webkitclient_impl.h @@ -5,6 +5,7 @@ #ifndef WEBKIT_CLIENT_IMPL_H_ #define WEBKIT_CLIENT_IMPL_H_ +#include "base/platform_file.h" #include "base/timer.h" #include "webkit/api/public/WebKitClient.h" #if defined(OS_WIN) @@ -39,6 +40,12 @@ class WebKitClientImpl : public WebKit::WebKitClient { virtual void callOnMainThread(void (*func)()); virtual void suddenTerminationChanged(bool enabled) { } + virtual base::PlatformFile databaseOpenFile( + const WebKit::WebString& file_name, int desired_flags); + virtual bool databaseDeleteFile(const WebKit::WebString& file_name); + virtual long databaseGetFileAttributes(const WebKit::WebString& file_name); + virtual long long databaseGetFileSize(const WebKit::WebString& file_name); + private: void DoTimeout() { if (shared_timer_func_) diff --git a/webkit/glue/webview_impl.cc b/webkit/glue/webview_impl.cc index 78d4610..b40f60c 100644 --- a/webkit/glue/webview_impl.cc +++ b/webkit/glue/webview_impl.cc @@ -90,6 +90,7 @@ MSVC_POP_WARNING(); #include "base/string_util.h" #include "webkit/api/public/WebDragData.h" #include "webkit/api/public/WebInputEvent.h" +#include "webkit/api/public/WebKit.h" #include "webkit/api/public/WebPoint.h" #include "webkit/api/public/WebRect.h" #include "webkit/api/public/WebString.h" @@ -1447,6 +1448,10 @@ void WebViewImpl::SetPreferences(const WebPreferences& preferences) { // Turn this on to cause WebCore to paint the resize corner for us. settings->setShouldPaintCustomScrollbars(true); +#if ENABLE(DATABASE) + settings->setDatabasesEnabled(WebKit::databasesEnabled()); +#endif + // Mitigate attacks from local HTML files by not granting file:// URLs // universal access. settings->setAllowUniversalAccessFromFileURLs(false); |