| Index: webkit/support/simple_database_system.cc
 | 
| diff --git a/webkit/support/simple_database_system.cc b/webkit/support/simple_database_system.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..91f7c4fdf93649ccec32ab61ebbaa00d913f2e65
 | 
| --- /dev/null
 | 
| +++ b/webkit/support/simple_database_system.cc
 | 
| @@ -0,0 +1,319 @@
 | 
| +// Copyright (c) 2012 The Chromium Authors. All rights reserved.
 | 
| +// Use of this source code is governed by a BSD-style license that can be
 | 
| +// found in the LICENSE file.
 | 
| +
 | 
| +#include "webkit/support/simple_database_system.h"
 | 
| +
 | 
| +#include "base/auto_reset.h"
 | 
| +#include "base/bind.h"
 | 
| +#include "base/bind_helpers.h"
 | 
| +#include "base/file_util.h"
 | 
| +#include "base/message_loop.h"
 | 
| +#include "base/message_loop/message_loop_proxy.h"
 | 
| +#include "base/strings/utf_string_conversions.h"
 | 
| +#include "base/synchronization/waitable_event.h"
 | 
| +#include "base/threading/platform_thread.h"
 | 
| +#include "third_party/WebKit/public/platform/WebCString.h"
 | 
| +#include "third_party/WebKit/public/platform/WebString.h"
 | 
| +#include "third_party/WebKit/public/web/WebDatabase.h"
 | 
| +#include "third_party/sqlite/sqlite3.h"
 | 
| +#include "webkit/browser/database/database_util.h"
 | 
| +#include "webkit/browser/database/vfs_backend.h"
 | 
| +
 | 
| +using webkit_database::DatabaseTracker;
 | 
| +using webkit_database::DatabaseUtil;
 | 
| +using webkit_database::OriginInfo;
 | 
| +using webkit_database::VfsBackend;
 | 
| +
 | 
| +SimpleDatabaseSystem* SimpleDatabaseSystem::instance_ = NULL;
 | 
| +
 | 
| +SimpleDatabaseSystem* SimpleDatabaseSystem::GetInstance() {
 | 
| +  DCHECK(instance_);
 | 
| +  return instance_;
 | 
| +}
 | 
| +
 | 
| +SimpleDatabaseSystem::SimpleDatabaseSystem()
 | 
| +    : db_thread_("SimpleDBThread"),
 | 
| +      quota_per_origin_(5 * 1024 * 1024),
 | 
| +      open_connections_(new webkit_database::DatabaseConnectionsWrapper) {
 | 
| +  DCHECK(!instance_);
 | 
| +  instance_ = this;
 | 
| +  CHECK(temp_dir_.CreateUniqueTempDir());
 | 
| +  db_tracker_ =
 | 
| +      new DatabaseTracker(temp_dir_.path(), false, NULL, NULL, NULL);
 | 
| +  db_tracker_->AddObserver(this);
 | 
| +  db_thread_.Start();
 | 
| +  db_thread_proxy_ = db_thread_.message_loop_proxy();
 | 
| +}
 | 
| +
 | 
| +SimpleDatabaseSystem::~SimpleDatabaseSystem() {
 | 
| +  base::WaitableEvent done_event(false, false);
 | 
| +  db_thread_proxy_->PostTask(
 | 
| +      FROM_HERE,
 | 
| +      base::Bind(&SimpleDatabaseSystem::ThreadCleanup,
 | 
| +                 base::Unretained(this), &done_event));
 | 
| +  done_event.Wait();
 | 
| +  instance_ = NULL;
 | 
| +}
 | 
| +
 | 
| +void SimpleDatabaseSystem::databaseOpened(const WebKit::WebDatabase& database) {
 | 
| +  std::string origin_identifier =
 | 
| +      database.securityOrigin().databaseIdentifier().utf8();
 | 
| +  base::string16 database_name = database.name();
 | 
| +  open_connections_->AddOpenConnection(origin_identifier, database_name);
 | 
| +  db_thread_proxy_->PostTask(
 | 
| +      FROM_HERE,
 | 
| +      base::Bind(&SimpleDatabaseSystem::DatabaseOpened,
 | 
| +                 base::Unretained(this),
 | 
| +                 origin_identifier,
 | 
| +                 database_name, database.displayName(),
 | 
| +                 database.estimatedSize()));
 | 
| +}
 | 
| +
 | 
| +void SimpleDatabaseSystem::databaseModified(
 | 
| +    const WebKit::WebDatabase& database) {
 | 
| +  db_thread_proxy_->PostTask(
 | 
| +      FROM_HERE,
 | 
| +      base::Bind(&SimpleDatabaseSystem::DatabaseModified,
 | 
| +                 base::Unretained(this),
 | 
| +                 database.securityOrigin().databaseIdentifier().utf8(),
 | 
| +                 database.name()));
 | 
| +}
 | 
| +
 | 
| +void SimpleDatabaseSystem::databaseClosed(const WebKit::WebDatabase& database) {
 | 
| +  std::string origin_identifier =
 | 
| +      database.securityOrigin().databaseIdentifier().utf8();
 | 
| +  base::string16 database_name = database.name();
 | 
| +  db_thread_proxy_->PostTask(
 | 
| +      FROM_HERE,
 | 
| +      base::Bind(&SimpleDatabaseSystem::DatabaseClosed,
 | 
| +                 base::Unretained(this), origin_identifier, database_name));
 | 
| +}
 | 
| +
 | 
| +base::PlatformFile SimpleDatabaseSystem::OpenFile(
 | 
| +    const base::string16& vfs_file_name, int desired_flags) {
 | 
| +  base::PlatformFile result = base::kInvalidPlatformFileValue;
 | 
| +  base::WaitableEvent done_event(false, false);
 | 
| +  db_thread_proxy_->PostTask(
 | 
| +      FROM_HERE,
 | 
| +      base::Bind(&SimpleDatabaseSystem::VfsOpenFile,
 | 
| +                 base::Unretained(this),
 | 
| +                 vfs_file_name, desired_flags,
 | 
| +                 &result, &done_event));
 | 
| +  done_event.Wait();
 | 
| +  return result;
 | 
| +}
 | 
| +
 | 
| +int SimpleDatabaseSystem::DeleteFile(
 | 
| +    const base::string16& vfs_file_name, bool sync_dir) {
 | 
| +  int result = SQLITE_OK;
 | 
| +  base::WaitableEvent done_event(false, false);
 | 
| +  db_thread_proxy_->PostTask(
 | 
| +      FROM_HERE,
 | 
| +      base::Bind(&SimpleDatabaseSystem::VfsDeleteFile,
 | 
| +                 base::Unretained(this),
 | 
| +                 vfs_file_name, sync_dir,
 | 
| +                 &result, &done_event));
 | 
| +  done_event.Wait();
 | 
| +  return result;
 | 
| +}
 | 
| +
 | 
| +uint32 SimpleDatabaseSystem::GetFileAttributes(
 | 
| +    const base::string16& vfs_file_name) {
 | 
| +  uint32 result = 0;
 | 
| +  base::WaitableEvent done_event(false, false);
 | 
| +  db_thread_proxy_->PostTask(
 | 
| +      FROM_HERE,
 | 
| +      base::Bind(&SimpleDatabaseSystem::VfsGetFileAttributes,
 | 
| +                 base::Unretained(this), vfs_file_name, &result, &done_event));
 | 
| +  done_event.Wait();
 | 
| +  return result;
 | 
| +}
 | 
| +
 | 
| +int64 SimpleDatabaseSystem::GetFileSize(const base::string16& vfs_file_name) {
 | 
| +  int64 result = 0;
 | 
| +  base::WaitableEvent done_event(false, false);
 | 
| +  db_thread_proxy_->PostTask(
 | 
| +      FROM_HERE,
 | 
| +      base::Bind(&SimpleDatabaseSystem::VfsGetFileSize,
 | 
| +                 base::Unretained(this), vfs_file_name, &result, &done_event));
 | 
| +  done_event.Wait();
 | 
| +  return result;
 | 
| +}
 | 
| +
 | 
| +int64 SimpleDatabaseSystem::GetSpaceAvailable(
 | 
| +    const std::string& origin_identifier) {
 | 
| +  int64 result = 0;
 | 
| +  base::WaitableEvent done_event(false, false);
 | 
| +  db_thread_proxy_->PostTask(
 | 
| +      FROM_HERE,
 | 
| +      base::Bind(&SimpleDatabaseSystem::VfsGetSpaceAvailable,
 | 
| +                 base::Unretained(this), origin_identifier,
 | 
| +                 &result, &done_event));
 | 
| +  done_event.Wait();
 | 
| +  return result;
 | 
| +}
 | 
| +
 | 
| +void SimpleDatabaseSystem::ClearAllDatabases() {
 | 
| +  open_connections_->WaitForAllDatabasesToClose();
 | 
| +  db_thread_proxy_->PostTask(
 | 
| +      FROM_HERE,
 | 
| +      base::Bind(&SimpleDatabaseSystem::ResetTracker, base::Unretained(this)));
 | 
| +}
 | 
| +
 | 
| +void SimpleDatabaseSystem::SetDatabaseQuota(int64 quota) {
 | 
| +  if (!db_thread_proxy_->BelongsToCurrentThread()) {
 | 
| +    db_thread_proxy_->PostTask(
 | 
| +        FROM_HERE,
 | 
| +        base::Bind(&SimpleDatabaseSystem::SetDatabaseQuota,
 | 
| +                   base::Unretained(this), quota));
 | 
| +    return;
 | 
| +  }
 | 
| +  quota_per_origin_ = quota;
 | 
| +}
 | 
| +
 | 
| +void SimpleDatabaseSystem::DatabaseOpened(
 | 
| +    const std::string& origin_identifier,
 | 
| +    const base::string16& database_name,
 | 
| +    const base::string16& description,
 | 
| +    int64 estimated_size) {
 | 
| +  DCHECK(db_thread_proxy_->BelongsToCurrentThread());
 | 
| +  int64 database_size = 0;
 | 
| +  db_tracker_->DatabaseOpened(
 | 
| +      origin_identifier, database_name, description,
 | 
| +      estimated_size, &database_size);
 | 
| +  OnDatabaseSizeChanged(origin_identifier, database_name,
 | 
| +                        database_size);
 | 
| +}
 | 
| +
 | 
| +void SimpleDatabaseSystem::DatabaseModified(
 | 
| +    const std::string& origin_identifier,
 | 
| +    const base::string16& database_name) {
 | 
| +  DCHECK(db_thread_proxy_->BelongsToCurrentThread());
 | 
| +  db_tracker_->DatabaseModified(origin_identifier, database_name);
 | 
| +}
 | 
| +
 | 
| +void SimpleDatabaseSystem::DatabaseClosed(
 | 
| +    const std::string& origin_identifier,
 | 
| +    const base::string16& database_name) {
 | 
| +  DCHECK(db_thread_proxy_->BelongsToCurrentThread());
 | 
| +  db_tracker_->DatabaseClosed(origin_identifier, database_name);
 | 
| +  open_connections_->RemoveOpenConnection(origin_identifier, database_name);
 | 
| +}
 | 
| +
 | 
| +void SimpleDatabaseSystem::OnDatabaseSizeChanged(
 | 
| +    const std::string& origin_identifier,
 | 
| +    const base::string16& database_name,
 | 
| +    int64 database_size) {
 | 
| +  DCHECK(db_thread_proxy_->BelongsToCurrentThread());
 | 
| +  // We intentionally call into webkit on our background db_thread_
 | 
| +  // to better emulate what happens in chrome where this method is
 | 
| +  // invoked on the background ipc thread.
 | 
| +  WebKit::WebDatabase::updateDatabaseSize(
 | 
| +      WebKit::WebString::fromUTF8(origin_identifier),
 | 
| +      database_name, database_size);
 | 
| +}
 | 
| +
 | 
| +void SimpleDatabaseSystem::OnDatabaseScheduledForDeletion(
 | 
| +    const std::string& origin_identifier,
 | 
| +    const base::string16& database_name) {
 | 
| +  DCHECK(db_thread_proxy_->BelongsToCurrentThread());
 | 
| +  // We intentionally call into webkit on our background db_thread_
 | 
| +  // to better emulate what happens in chrome where this method is
 | 
| +  // invoked on the background ipc thread.
 | 
| +  WebKit::WebDatabase::closeDatabaseImmediately(
 | 
| +      WebKit::WebString::fromUTF8(origin_identifier), database_name);
 | 
| +}
 | 
| +
 | 
| +void SimpleDatabaseSystem::VfsOpenFile(
 | 
| +    const base::string16& vfs_file_name, int desired_flags,
 | 
| +    base::PlatformFile* file_handle, base::WaitableEvent* done_event ) {
 | 
| +  DCHECK(db_thread_proxy_->BelongsToCurrentThread());
 | 
| +  base::FilePath file_name = GetFullFilePathForVfsFile(vfs_file_name);
 | 
| +  if (file_name.empty()) {
 | 
| +    VfsBackend::OpenTempFileInDirectory(
 | 
| +        db_tracker_->DatabaseDirectory(), desired_flags, file_handle);
 | 
| +  } else {
 | 
| +    VfsBackend::OpenFile(file_name, desired_flags, file_handle);
 | 
| +  }
 | 
| +  done_event->Signal();
 | 
| +}
 | 
| +
 | 
| +void SimpleDatabaseSystem::VfsDeleteFile(
 | 
| +    const base::string16& vfs_file_name, bool sync_dir,
 | 
| +    int* result, base::WaitableEvent* done_event) {
 | 
| +  DCHECK(db_thread_proxy_->BelongsToCurrentThread());
 | 
| +  // We try to delete the file multiple times, because that's what the default
 | 
| +  // VFS does (apparently deleting a file can sometimes fail on Windows).
 | 
| +  // We sleep for 10ms between retries for the same reason.
 | 
| +  const int kNumDeleteRetries = 3;
 | 
| +  int num_retries = 0;
 | 
| +  *result = SQLITE_OK;
 | 
| +  base::FilePath file_name = GetFullFilePathForVfsFile(vfs_file_name);
 | 
| +  do {
 | 
| +    *result = VfsBackend::DeleteFile(file_name, sync_dir);
 | 
| +  } while ((++num_retries < kNumDeleteRetries) &&
 | 
| +           (*result == SQLITE_IOERR_DELETE) &&
 | 
| +           (base::PlatformThread::Sleep(
 | 
| +               base::TimeDelta::FromMilliseconds(10)),
 | 
| +            1));
 | 
| +
 | 
| +  done_event->Signal();
 | 
| +}
 | 
| +
 | 
| +void SimpleDatabaseSystem::VfsGetFileAttributes(
 | 
| +    const base::string16& vfs_file_name,
 | 
| +    uint32* result, base::WaitableEvent* done_event) {
 | 
| +  DCHECK(db_thread_proxy_->BelongsToCurrentThread());
 | 
| +  *result = VfsBackend::GetFileAttributes(
 | 
| +      GetFullFilePathForVfsFile(vfs_file_name));
 | 
| +  done_event->Signal();
 | 
| +}
 | 
| +
 | 
| +void SimpleDatabaseSystem::VfsGetFileSize(
 | 
| +    const base::string16& vfs_file_name,
 | 
| +    int64* result, base::WaitableEvent* done_event) {
 | 
| +  DCHECK(db_thread_proxy_->BelongsToCurrentThread());
 | 
| +  *result = VfsBackend::GetFileSize(GetFullFilePathForVfsFile(vfs_file_name));
 | 
| +  done_event->Signal();
 | 
| +}
 | 
| +
 | 
| +void SimpleDatabaseSystem::VfsGetSpaceAvailable(
 | 
| +    const std::string& origin_identifier,
 | 
| +    int64* result, base::WaitableEvent* done_event) {
 | 
| +  DCHECK(db_thread_proxy_->BelongsToCurrentThread());
 | 
| +  // This method isn't actually part of the "vfs" interface, but it is
 | 
| +  // used from within webcore and handled here in the same fashion.
 | 
| +  OriginInfo info;
 | 
| +  if (db_tracker_->GetOriginInfo(origin_identifier, &info)) {
 | 
| +    int64 space_available = quota_per_origin_ - info.TotalSize();
 | 
| +    *result = space_available < 0 ? 0 : space_available;
 | 
| +  } else {
 | 
| +    NOTREACHED();
 | 
| +    *result = 0;
 | 
| +  }
 | 
| +  done_event->Signal();
 | 
| +}
 | 
| +
 | 
| +base::FilePath SimpleDatabaseSystem::GetFullFilePathForVfsFile(
 | 
| +    const base::string16& vfs_file_name) {
 | 
| +  DCHECK(db_thread_proxy_->BelongsToCurrentThread());
 | 
| +  if (vfs_file_name.empty())  // temp file, used for vacuuming
 | 
| +    return base::FilePath();
 | 
| +  return DatabaseUtil::GetFullFilePathForVfsFile(
 | 
| +      db_tracker_.get(), vfs_file_name);
 | 
| +}
 | 
| +
 | 
| +void SimpleDatabaseSystem::ResetTracker() {
 | 
| +  DCHECK(db_thread_proxy_->BelongsToCurrentThread());
 | 
| +  db_tracker_->CloseTrackerDatabaseAndClearCaches();
 | 
| +  base::Delete(db_tracker_->DatabaseDirectory(), true);
 | 
| +}
 | 
| +
 | 
| +void SimpleDatabaseSystem::ThreadCleanup(base::WaitableEvent* done_event) {
 | 
| +  ResetTracker();
 | 
| +  db_tracker_->RemoveObserver(this);
 | 
| +  db_tracker_ = NULL;
 | 
| +  done_event->Signal();
 | 
| +}
 | 
| +
 | 
| 
 |