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(); |
+} |
+ |