summaryrefslogtreecommitdiffstats
path: root/WebCore/storage
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/storage')
-rw-r--r--WebCore/storage/ChangeVersionWrapper.cpp77
-rw-r--r--WebCore/storage/ChangeVersionWrapper.h55
-rw-r--r--WebCore/storage/Database.cpp585
-rw-r--r--WebCore/storage/Database.h148
-rw-r--r--WebCore/storage/Database.idl37
-rw-r--r--WebCore/storage/DatabaseAuthorizer.cpp212
-rw-r--r--WebCore/storage/DatabaseAuthorizer.h105
-rw-r--r--WebCore/storage/DatabaseDetails.h67
-rw-r--r--WebCore/storage/DatabaseTask.cpp177
-rw-r--r--WebCore/storage/DatabaseTask.h151
-rw-r--r--WebCore/storage/DatabaseThread.cpp130
-rw-r--r--WebCore/storage/DatabaseThread.h72
-rw-r--r--WebCore/storage/DatabaseTracker.cpp827
-rw-r--r--WebCore/storage/DatabaseTracker.h132
-rw-r--r--WebCore/storage/DatabaseTrackerClient.h45
-rw-r--r--WebCore/storage/LocalStorage.cpp171
-rw-r--r--WebCore/storage/LocalStorage.h81
-rw-r--r--WebCore/storage/LocalStorageArea.cpp416
-rw-r--r--WebCore/storage/LocalStorageArea.h105
-rw-r--r--WebCore/storage/LocalStorageTask.cpp84
-rw-r--r--WebCore/storage/LocalStorageTask.h64
-rw-r--r--WebCore/storage/LocalStorageThread.cpp132
-rw-r--r--WebCore/storage/LocalStorageThread.h73
-rw-r--r--WebCore/storage/OriginQuotaManager.cpp123
-rw-r--r--WebCore/storage/OriginQuotaManager.h70
-rw-r--r--WebCore/storage/OriginUsageRecord.cpp104
-rw-r--r--WebCore/storage/OriginUsageRecord.h67
-rw-r--r--WebCore/storage/SQLError.h52
-rw-r--r--WebCore/storage/SQLError.idl35
-rw-r--r--WebCore/storage/SQLResultSet.cpp81
-rw-r--r--WebCore/storage/SQLResultSet.h63
-rw-r--r--WebCore/storage/SQLResultSet.idl38
-rw-r--r--WebCore/storage/SQLResultSetRowList.cpp44
-rw-r--r--WebCore/storage/SQLResultSetRowList.h58
-rw-r--r--WebCore/storage/SQLResultSetRowList.idl35
-rw-r--r--WebCore/storage/SQLStatement.cpp197
-rw-r--r--WebCore/storage/SQLStatement.h83
-rw-r--r--WebCore/storage/SQLStatementCallback.h48
-rw-r--r--WebCore/storage/SQLStatementCallback.idl35
-rw-r--r--WebCore/storage/SQLStatementErrorCallback.h49
-rw-r--r--WebCore/storage/SQLStatementErrorCallback.idl35
-rw-r--r--WebCore/storage/SQLTransaction.cpp550
-rw-r--r--WebCore/storage/SQLTransaction.h131
-rw-r--r--WebCore/storage/SQLTransaction.idl34
-rw-r--r--WebCore/storage/SQLTransactionCallback.h47
-rw-r--r--WebCore/storage/SQLTransactionCallback.idl35
-rw-r--r--WebCore/storage/SQLTransactionErrorCallback.h48
-rw-r--r--WebCore/storage/SQLTransactionErrorCallback.idl35
-rw-r--r--WebCore/storage/SessionStorage.cpp75
-rw-r--r--WebCore/storage/SessionStorage.h64
-rw-r--r--WebCore/storage/SessionStorageArea.cpp89
-rw-r--r--WebCore/storage/SessionStorageArea.h57
-rw-r--r--WebCore/storage/Storage.cpp106
-rw-r--r--WebCore/storage/Storage.h65
-rw-r--r--WebCore/storage/Storage.idl45
-rw-r--r--WebCore/storage/StorageArea.cpp125
-rw-r--r--WebCore/storage/StorageArea.h83
-rw-r--r--WebCore/storage/StorageEvent.cpp57
-rw-r--r--WebCore/storage/StorageEvent.h72
-rw-r--r--WebCore/storage/StorageEvent.idl42
-rw-r--r--WebCore/storage/StorageMap.cpp158
-rw-r--r--WebCore/storage/StorageMap.h65
62 files changed, 7146 insertions, 0 deletions
diff --git a/WebCore/storage/ChangeVersionWrapper.cpp b/WebCore/storage/ChangeVersionWrapper.cpp
new file mode 100644
index 0000000..54c317c
--- /dev/null
+++ b/WebCore/storage/ChangeVersionWrapper.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "config.h"
+#include "ChangeVersionWrapper.h"
+
+#include "Database.h"
+
+namespace WebCore {
+
+ChangeVersionWrapper::ChangeVersionWrapper(const String& oldVersion, const String& newVersion)
+ : m_oldVersion(oldVersion.copy())
+ , m_newVersion(newVersion.copy())
+{
+}
+
+bool ChangeVersionWrapper::performPreflight(SQLTransaction* transaction)
+{
+ ASSERT(transaction && transaction->database());
+
+ String actualVersion;
+
+ if (!transaction->database()->getVersionFromDatabase(actualVersion)) {
+ LOG_ERROR("Unable to retrieve actual current version from database");
+ m_sqlError = SQLError::create(0, "unable to verify current version of database");
+ return false;
+ }
+
+ if (actualVersion != m_oldVersion) {
+ LOG_ERROR("Old version doesn't match actual version");
+ m_sqlError = SQLError::create(2, "current version of the database and `oldVersion` argument do not match");
+ return false;
+ }
+
+ return true;
+}
+
+bool ChangeVersionWrapper::performPostflight(SQLTransaction* transaction)
+{
+ ASSERT(transaction && transaction->database());
+
+ if (!transaction->database()->setVersionInDatabase(m_newVersion)) {
+ LOG_ERROR("Unable to set new version in database");
+ m_sqlError = SQLError::create(0, "unable to set new version in database");
+ return false;
+ }
+
+ transaction->database()->setExpectedVersion(m_newVersion);
+
+ return true;
+}
+
+} // namespace WebCore
diff --git a/WebCore/storage/ChangeVersionWrapper.h b/WebCore/storage/ChangeVersionWrapper.h
new file mode 100644
index 0000000..c2983eb
--- /dev/null
+++ b/WebCore/storage/ChangeVersionWrapper.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef ChangeVersionWrapper_h
+#define ChangeVersionWrapper_h
+
+#include "PlatformString.h"
+#include "SQLTransaction.h"
+
+namespace WebCore {
+
+class ChangeVersionWrapper : public SQLTransactionWrapper {
+public:
+ static PassRefPtr<ChangeVersionWrapper> create(const String& oldVersion, const String& newVersion) { return adoptRef(new ChangeVersionWrapper(oldVersion, newVersion)); }
+
+ virtual bool performPreflight(SQLTransaction*);
+ virtual bool performPostflight(SQLTransaction*);
+
+ virtual SQLError* sqlError() const { return m_sqlError.get(); }
+
+private:
+ ChangeVersionWrapper(const String& oldVersion, const String& newVersion);
+
+ String m_oldVersion;
+ String m_newVersion;
+ RefPtr<SQLError> m_sqlError;
+};
+
+} // namespace WebCore
+
+#endif // ChangeVersionWrapper_h
diff --git a/WebCore/storage/Database.cpp b/WebCore/storage/Database.cpp
new file mode 100644
index 0000000..1055504
--- /dev/null
+++ b/WebCore/storage/Database.cpp
@@ -0,0 +1,585 @@
+/*
+ * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "Database.h"
+
+#include "ChangeVersionWrapper.h"
+#include "CString.h"
+#include "DatabaseAuthorizer.h"
+#include "DatabaseTask.h"
+#include "DatabaseThread.h"
+#include "DatabaseTracker.h"
+#include "Document.h"
+#include "ExceptionCode.h"
+#include "FileSystem.h"
+#include "Frame.h"
+#include "InspectorController.h"
+#include "JSDOMWindow.h"
+#include "Logging.h"
+#include "NotImplemented.h"
+#include "Page.h"
+#include "OriginQuotaManager.h"
+#include "SQLiteDatabase.h"
+#include "SQLiteStatement.h"
+#include "SQLResultSet.h"
+#include <runtime/InitializeThreading.h>
+#include <wtf/MainThread.h>
+
+namespace WebCore {
+
+static Mutex& guidMutex()
+{
+ // FIXME: this is not a thread-safe way to initialize a shared global.
+ static Mutex mutex;
+ return mutex;
+}
+
+static HashMap<int, String>& guidToVersionMap()
+{
+ static HashMap<int, String> map;
+ return map;
+}
+
+static HashMap<int, HashSet<Database*>*>& guidToDatabaseMap()
+{
+ static HashMap<int, HashSet<Database*>*> map;
+ return map;
+}
+
+const String& Database::databaseInfoTableName()
+{
+ static String name = "__WebKitDatabaseInfoTable__";
+ return name;
+}
+
+static const String& databaseVersionKey()
+{
+ static String key = "WebKitDatabaseVersionKey";
+ return key;
+}
+
+static int guidForOriginAndName(const String& origin, const String& name);
+
+PassRefPtr<Database> Database::openDatabase(Document* document, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize, ExceptionCode& e)
+{
+ if (!DatabaseTracker::tracker().canEstablishDatabase(document, name, displayName, estimatedSize)) {
+ // FIXME: There should be an exception raised here in addition to returning a null Database object. The question has been raised with the WHATWG.
+ LOG(StorageAPI, "Database %s for origin %s not allowed to be established", name.ascii().data(), document->securityOrigin()->toString().ascii().data());
+ return 0;
+ }
+
+ RefPtr<Database> database = adoptRef(new Database(document, name, expectedVersion));
+
+ if (!database->openAndVerifyVersion(e)) {
+ LOG(StorageAPI, "Failed to open and verify version (expected %s) of database %s", expectedVersion.ascii().data(), database->databaseDebugName().ascii().data());
+ return 0;
+ }
+
+ DatabaseTracker::tracker().setDatabaseDetails(document->securityOrigin(), name, displayName, estimatedSize);
+
+ document->setHasOpenDatabases();
+
+ if (Page* page = document->frame()->page())
+ page->inspectorController()->didOpenDatabase(database.get(), document->domain(), name, expectedVersion);
+
+ return database;
+}
+
+Database::Database(Document* document, const String& name, const String& expectedVersion)
+ : m_transactionInProgress(false)
+ , m_document(document)
+ , m_name(name.copy())
+ , m_guid(0)
+ , m_expectedVersion(expectedVersion)
+ , m_deleted(false)
+ , m_stopped(false)
+{
+ ASSERT(document);
+ m_securityOrigin = document->securityOrigin();
+
+ if (m_name.isNull())
+ m_name = "";
+
+ JSC::initializeThreading();
+ // Database code violates the normal JSCore contract by calling jsUnprotect from a secondary thread, and thus needs additional locking.
+ JSDOMWindow::commonJSGlobalData()->heap.setGCProtectNeedsLocking();
+
+ m_guid = guidForOriginAndName(m_securityOrigin->toString(), name);
+
+ {
+ MutexLocker locker(guidMutex());
+
+ HashSet<Database*>* hashSet = guidToDatabaseMap().get(m_guid);
+ if (!hashSet) {
+ hashSet = new HashSet<Database*>;
+ guidToDatabaseMap().set(m_guid, hashSet);
+ }
+
+ hashSet->add(this);
+ }
+
+ ASSERT(m_document->databaseThread());
+
+ m_filename = DatabaseTracker::tracker().fullPathForDatabase(m_securityOrigin.get(), m_name);
+
+ DatabaseTracker::tracker().addOpenDatabase(this);
+ m_document->addOpenDatabase(this);
+}
+
+Database::~Database()
+{
+ {
+ MutexLocker locker(guidMutex());
+
+ HashSet<Database*>* hashSet = guidToDatabaseMap().get(m_guid);
+ ASSERT(hashSet);
+ ASSERT(hashSet->contains(this));
+ hashSet->remove(this);
+ if (hashSet->isEmpty()) {
+ guidToDatabaseMap().remove(m_guid);
+ delete hashSet;
+ guidToVersionMap().remove(m_guid);
+ }
+ }
+
+ if (m_document->databaseThread())
+ m_document->databaseThread()->unscheduleDatabaseTasks(this);
+
+ DatabaseTracker::tracker().removeOpenDatabase(this);
+ m_document->removeOpenDatabase(this);
+}
+
+bool Database::openAndVerifyVersion(ExceptionCode& e)
+{
+ m_databaseAuthorizer = DatabaseAuthorizer::create();
+
+ RefPtr<DatabaseOpenTask> task = DatabaseOpenTask::create(this);
+
+ task->lockForSynchronousScheduling();
+ m_document->databaseThread()->scheduleImmediateTask(task);
+ task->waitForSynchronousCompletion();
+
+ ASSERT(task->isComplete());
+ e = task->exceptionCode();
+ return task->openSuccessful();
+}
+
+
+static bool retrieveTextResultFromDatabase(SQLiteDatabase& db, const String& query, String& resultString)
+{
+ SQLiteStatement statement(db, query);
+ int result = statement.prepare();
+
+ if (result != SQLResultOk) {
+ LOG_ERROR("Error (%i) preparing statement to read text result from database (%s)", result, query.ascii().data());
+ return false;
+ }
+
+ result = statement.step();
+ if (result == SQLResultRow) {
+ resultString = statement.getColumnText(0);
+ return true;
+ } else if (result == SQLResultDone) {
+ resultString = String();
+ return true;
+ } else {
+ LOG_ERROR("Error (%i) reading text result from database (%s)", result, query.ascii().data());
+ return false;
+ }
+}
+
+bool Database::getVersionFromDatabase(String& version)
+{
+ static String getVersionQuery = "SELECT value FROM " + databaseInfoTableName() + " WHERE key = '" + databaseVersionKey() + "';";
+
+ m_databaseAuthorizer->disable();
+
+ bool result = retrieveTextResultFromDatabase(m_sqliteDatabase, getVersionQuery.copy(), version);
+ if (!result)
+ LOG_ERROR("Failed to retrieve version from database %s", databaseDebugName().ascii().data());
+
+ m_databaseAuthorizer->enable();
+
+ return result;
+}
+
+static bool setTextValueInDatabase(SQLiteDatabase& db, const String& query, const String& value)
+{
+ SQLiteStatement statement(db, query);
+ int result = statement.prepare();
+
+ if (result != SQLResultOk) {
+ LOG_ERROR("Failed to prepare statement to set value in database (%s)", query.ascii().data());
+ return false;
+ }
+
+ statement.bindText(1, value);
+
+ result = statement.step();
+ if (result != SQLResultDone) {
+ LOG_ERROR("Failed to step statement to set value in database (%s)", query.ascii().data());
+ return false;
+ }
+
+ return true;
+}
+
+bool Database::setVersionInDatabase(const String& version)
+{
+ static String setVersionQuery = "INSERT INTO " + databaseInfoTableName() + " (key, value) VALUES ('" + databaseVersionKey() + "', ?);";
+
+ m_databaseAuthorizer->disable();
+
+ bool result = setTextValueInDatabase(m_sqliteDatabase, setVersionQuery.copy(), version);
+ if (!result)
+ LOG_ERROR("Failed to set version %s in database (%s)", version.ascii().data(), setVersionQuery.ascii().data());
+
+ m_databaseAuthorizer->enable();
+
+ return result;
+}
+
+bool Database::versionMatchesExpected() const
+{
+ if (!m_expectedVersion.isEmpty()) {
+ MutexLocker locker(guidMutex());
+ return m_expectedVersion == guidToVersionMap().get(m_guid);
+ }
+
+ return true;
+}
+
+void Database::markAsDeletedAndClose()
+{
+ if (m_deleted)
+ return;
+
+ LOG(StorageAPI, "Marking %s (%p) as deleted", stringIdentifier().ascii().data(), this);
+ m_deleted = true;
+
+ if (m_document->databaseThread()->terminationRequested()) {
+ LOG(StorageAPI, "Database handle %p is on a terminated DatabaseThread, cannot be marked for normal closure\n", this);
+ return;
+ }
+
+ document()->databaseThread()->unscheduleDatabaseTasks(this);
+
+ RefPtr<DatabaseCloseTask> task = DatabaseCloseTask::create(this);
+
+ task->lockForSynchronousScheduling();
+ m_document->databaseThread()->scheduleImmediateTask(task);
+ task->waitForSynchronousCompletion();
+}
+
+void Database::close()
+{
+ m_sqliteDatabase.close();
+}
+
+void Database::stop()
+{
+ // FIXME: The net effect of the following code is to remove all pending transactions and statements, but allow the current statement
+ // to run to completion. In the future we can use the sqlite3_progress_handler or sqlite3_interrupt interfaces to cancel the current
+ // statement in response to close(), as well.
+
+ // This method is meant to be used as an analog to cancelling a loader, and is used when a document is shut down as the result of
+ // a page load or closing the page
+ m_stopped = true;
+
+ {
+ MutexLocker locker(m_transactionInProgressMutex);
+ m_transactionQueue.kill();
+ m_transactionInProgress = false;
+ }
+}
+
+unsigned long long Database::databaseSize() const
+{
+ long long size;
+ if (!getFileSize(m_filename, size))
+ size = 0;
+ return size;
+}
+
+unsigned long long Database::maximumSize() const
+{
+ // The maximum size for this database is the full quota for this origin, minus the current usage within this origin,
+ // except for the current usage of this database
+
+ OriginQuotaManager& manager(DatabaseTracker::tracker().originQuotaManager());
+ Locker<OriginQuotaManager> locker(manager);
+
+ return DatabaseTracker::tracker().quotaForOrigin(m_securityOrigin.get()) - manager.diskUsage(m_securityOrigin.get()) + databaseSize();
+}
+
+void Database::disableAuthorizer()
+{
+ ASSERT(m_databaseAuthorizer);
+ m_databaseAuthorizer->disable();
+}
+
+void Database::enableAuthorizer()
+{
+ ASSERT(m_databaseAuthorizer);
+ m_databaseAuthorizer->enable();
+}
+
+static int guidForOriginAndName(const String& origin, const String& name)
+{
+ static int currentNewGUID = 1;
+ static Mutex stringIdentifierMutex;
+ static HashMap<String, int> stringIdentifierToGUIDMap;
+
+ String stringID;
+ if (origin.endsWith("/"))
+ stringID = origin + name;
+ else
+ stringID = origin + "/" + name;
+
+ MutexLocker locker(stringIdentifierMutex);
+ int guid = stringIdentifierToGUIDMap.get(stringID);
+ if (!guid) {
+ guid = currentNewGUID++;
+ stringIdentifierToGUIDMap.set(stringID, guid);
+ }
+
+ return guid;
+}
+
+void Database::resetAuthorizer()
+{
+ if (m_databaseAuthorizer)
+ m_databaseAuthorizer->reset();
+}
+
+void Database::performPolicyChecks()
+{
+ // FIXME: Code similar to the following will need to be run to enforce the per-origin size limit the spec mandates.
+ // Additionally, we might need a way to pause the database thread while the UA prompts the user for permission to
+ // increase the size limit
+
+ /*
+ if (m_databaseAuthorizer->lastActionIncreasedSize())
+ DatabaseTracker::scheduleFileSizeCheckOnMainThread(this);
+ */
+
+ notImplemented();
+}
+
+bool Database::performOpenAndVerify(ExceptionCode& e)
+{
+ if (!m_sqliteDatabase.open(m_filename)) {
+ LOG_ERROR("Unable to open database at path %s", m_filename.ascii().data());
+ e = INVALID_STATE_ERR;
+ return false;
+ }
+
+ ASSERT(m_databaseAuthorizer);
+ m_sqliteDatabase.setAuthorizer(m_databaseAuthorizer);
+
+ if (!m_sqliteDatabase.tableExists(databaseInfoTableName())) {
+ if (!m_sqliteDatabase.executeCommand("CREATE TABLE " + databaseInfoTableName() + " (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,value TEXT NOT NULL ON CONFLICT FAIL);")) {
+ LOG_ERROR("Unable to create table %s in database %s", databaseInfoTableName().ascii().data(), databaseDebugName().ascii().data());
+ e = INVALID_STATE_ERR;
+ return false;
+ }
+ }
+
+
+ String currentVersion;
+ {
+ MutexLocker locker(guidMutex());
+ currentVersion = guidToVersionMap().get(m_guid);
+
+ if (currentVersion.isNull())
+ LOG(StorageAPI, "Current cached version for guid %i is null", m_guid);
+ else
+ LOG(StorageAPI, "Current cached version for guid %i is %s", m_guid, currentVersion.ascii().data());
+
+ if (currentVersion.isNull()) {
+ if (!getVersionFromDatabase(currentVersion)) {
+ LOG_ERROR("Failed to get current version from database %s", databaseDebugName().ascii().data());
+ e = INVALID_STATE_ERR;
+ return false;
+ }
+ if (currentVersion.length()) {
+ LOG(StorageAPI, "Retrieved current version %s from database %s", currentVersion.ascii().data(), databaseDebugName().ascii().data());
+ } else {
+ LOG(StorageAPI, "Setting version %s in database %s that was just created", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data());
+ if (!setVersionInDatabase(m_expectedVersion)) {
+ LOG_ERROR("Failed to set version %s in database %s", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data());
+ e = INVALID_STATE_ERR;
+ return false;
+ }
+
+ currentVersion = m_expectedVersion;
+ }
+
+ guidToVersionMap().set(m_guid, currentVersion.copy());
+ }
+ }
+
+ if (currentVersion.isNull()) {
+ LOG(StorageAPI, "Database %s does not have its version set", databaseDebugName().ascii().data());
+ currentVersion = "";
+ }
+
+ // FIXME: For now, the spec says that if the database has no version, it is valid for any "Expected version" string. That seems silly and I think it should be
+ // changed, and here's where we would change it
+ if (m_expectedVersion.length()) {
+ if (currentVersion.length() && m_expectedVersion != currentVersion) {
+ LOG(StorageAPI, "page expects version %s from database %s, which actually has version name %s - openDatabase() call will fail", m_expectedVersion.ascii().data(),
+ databaseDebugName().ascii().data(), currentVersion.ascii().data());
+ e = INVALID_STATE_ERR;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void Database::changeVersion(const String& oldVersion, const String& newVersion,
+ PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback,
+ PassRefPtr<VoidCallback> successCallback)
+{
+ m_transactionQueue.append(SQLTransaction::create(this, callback, errorCallback, successCallback, ChangeVersionWrapper::create(oldVersion, newVersion)));
+ MutexLocker locker(m_transactionInProgressMutex);
+ if (!m_transactionInProgress)
+ scheduleTransaction();
+}
+
+void Database::transaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback,
+ PassRefPtr<VoidCallback> successCallback)
+{
+ m_transactionQueue.append(SQLTransaction::create(this, callback, errorCallback, successCallback, 0));
+ MutexLocker locker(m_transactionInProgressMutex);
+ if (!m_transactionInProgress)
+ scheduleTransaction();
+}
+
+void Database::scheduleTransaction()
+{
+ ASSERT(!m_transactionInProgressMutex.tryLock()); // Locked by caller.
+ RefPtr<SQLTransaction> transaction;
+ if (m_transactionQueue.tryGetMessage(transaction) && m_document->databaseThread()) {
+ RefPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction);
+ LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for transaction %p\n", task.get(), task->transaction());
+ m_transactionInProgress = true;
+ m_document->databaseThread()->scheduleTask(task.release());
+ } else
+ m_transactionInProgress = false;
+}
+
+void Database::scheduleTransactionStep(SQLTransaction* transaction)
+{
+ if (m_document->databaseThread()) {
+ RefPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction);
+ LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for the transaction step\n", task.get());
+ m_document->databaseThread()->scheduleTask(task.release());
+ }
+}
+
+void Database::scheduleTransactionCallback(SQLTransaction* transaction)
+{
+ transaction->ref();
+ callOnMainThread(deliverPendingCallback, transaction);
+}
+
+Vector<String> Database::performGetTableNames()
+{
+ disableAuthorizer();
+
+ SQLiteStatement statement(m_sqliteDatabase, "SELECT name FROM sqlite_master WHERE type='table';");
+ if (statement.prepare() != SQLResultOk) {
+ LOG_ERROR("Unable to retrieve list of tables for database %s", databaseDebugName().ascii().data());
+ enableAuthorizer();
+ return Vector<String>();
+ }
+
+ Vector<String> tableNames;
+ int result;
+ while ((result = statement.step()) == SQLResultRow) {
+ String name = statement.getColumnText(0);
+ if (name != databaseInfoTableName())
+ tableNames.append(name);
+ }
+
+ enableAuthorizer();
+
+ if (result != SQLResultDone) {
+ LOG_ERROR("Error getting tables for database %s", databaseDebugName().ascii().data());
+ return Vector<String>();
+ }
+
+ return tableNames;
+}
+
+String Database::version() const
+{
+ if (m_deleted)
+ return String();
+ MutexLocker locker(guidMutex());
+ return guidToVersionMap().get(m_guid).copy();
+}
+
+void Database::deliverPendingCallback(void* context)
+{
+ SQLTransaction* transaction = static_cast<SQLTransaction*>(context);
+ transaction->performPendingCallback();
+ transaction->deref(); // Was ref'd in scheduleTransactionCallback().
+}
+
+Vector<String> Database::tableNames()
+{
+ RefPtr<DatabaseTableNamesTask> task = DatabaseTableNamesTask::create(this);
+
+ task->lockForSynchronousScheduling();
+ m_document->databaseThread()->scheduleImmediateTask(task);
+ task->waitForSynchronousCompletion();
+
+ return task->tableNames();
+}
+
+void Database::setExpectedVersion(const String& version)
+{
+ m_expectedVersion = version.copy();
+}
+
+PassRefPtr<SecurityOrigin> Database::securityOriginCopy() const
+{
+ return m_securityOrigin->copy();
+}
+
+String Database::stringIdentifier() const
+{
+ // Return a deep copy for ref counting thread safety
+ return m_name.copy();
+}
+
+}
diff --git a/WebCore/storage/Database.h b/WebCore/storage/Database.h
new file mode 100644
index 0000000..955f023
--- /dev/null
+++ b/WebCore/storage/Database.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef Database_h
+#define Database_h
+
+#include <wtf/MessageQueue.h>
+#include "PlatformString.h"
+#include "SecurityOrigin.h"
+#include "SQLiteDatabase.h"
+#include "SQLTransaction.h"
+#include "StringHash.h"
+#include "Timer.h"
+#include "VoidCallback.h"
+
+#include <wtf/Forward.h>
+#include <wtf/HashSet.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Deque.h>
+
+namespace WebCore {
+
+class DatabaseAuthorizer;
+class DatabaseThread;
+class Document;
+class SQLResultSet;
+class SQLTransactionCallback;
+class SQLTransactionErrorCallback;
+class SQLValue;
+
+typedef int ExceptionCode;
+
+class Database : public ThreadSafeShared<Database> {
+ friend class DatabaseTransactionTask;
+ friend class SQLStatement;
+ friend class SQLTransaction;
+public:
+ ~Database();
+
+// Direct support for the DOM API
+ static PassRefPtr<Database> openDatabase(Document* document, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize, ExceptionCode&);
+ String version() const;
+ void changeVersion(const String& oldVersion, const String& newVersion,
+ PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback,
+ PassRefPtr<VoidCallback> successCallback);
+ void transaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback,
+ PassRefPtr<VoidCallback> successCallback);
+
+// Internal engine support
+ static const String& databaseInfoTableName();
+
+ void disableAuthorizer();
+ void enableAuthorizer();
+
+ Vector<String> tableNames();
+
+ Document* document() const { return m_document.get(); }
+ PassRefPtr<SecurityOrigin> securityOriginCopy() const;
+ String stringIdentifier() const;
+
+ bool getVersionFromDatabase(String&);
+ bool setVersionInDatabase(const String&);
+ void setExpectedVersion(const String&);
+ bool versionMatchesExpected() const;
+
+ void markAsDeletedAndClose();
+ bool deleted() const { return m_deleted; }
+
+ void close();
+
+ void stop();
+ bool stopped() const { return m_stopped; }
+
+ unsigned long long databaseSize() const;
+ unsigned long long maximumSize() const;
+
+// Called from DatabaseThread, must be prepared to work on the background thread
+ void resetAuthorizer();
+ void performPolicyChecks();
+
+ bool performOpenAndVerify(ExceptionCode&);
+
+ Vector<String> performGetTableNames();
+
+private:
+ Database(Document* document, const String& name, const String& expectedVersion);
+
+ bool openAndVerifyVersion(ExceptionCode&);
+
+ void scheduleTransaction();
+ void scheduleTransactionCallback(SQLTransaction*);
+ void scheduleTransactionStep(SQLTransaction* transaction);
+
+ MessageQueue<RefPtr<SQLTransaction> > m_transactionQueue;
+ Mutex m_transactionInProgressMutex;
+ bool m_transactionInProgress;
+
+ static void deliverPendingCallback(void*);
+
+ RefPtr<Document> m_document;
+ RefPtr<SecurityOrigin> m_securityOrigin;
+ String m_name;
+ int m_guid;
+ String m_expectedVersion;
+ String m_filename;
+
+ bool m_deleted;
+
+ bool m_stopped;
+
+ SQLiteDatabase m_sqliteDatabase;
+ RefPtr<DatabaseAuthorizer> m_databaseAuthorizer;
+
+#ifndef NDEBUG
+ String databaseDebugName() const { return m_securityOrigin->toString() + "::" + m_name; }
+#endif
+};
+
+} // namespace WebCore
+
+#endif // Database_h
diff --git a/WebCore/storage/Database.idl b/WebCore/storage/Database.idl
new file mode 100644
index 0000000..e28db9b
--- /dev/null
+++ b/WebCore/storage/Database.idl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+module storage {
+
+ interface Database {
+ readonly attribute DOMString version;
+ [Custom] void changeVersion(in DOMString oldVersion, in DOMString newVersion, in SQLTransactionCallback callback, in SQLTransactionErrorCallback errorCallback, in VoidCallback successCallback);
+ [Custom] void transaction(in SQLTransactionCallback callback, in SQLTransactionErrorCallback errorCallback, in VoidCallback successCallback);
+ };
+
+}
diff --git a/WebCore/storage/DatabaseAuthorizer.cpp b/WebCore/storage/DatabaseAuthorizer.cpp
new file mode 100644
index 0000000..333dd52
--- /dev/null
+++ b/WebCore/storage/DatabaseAuthorizer.cpp
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "DatabaseAuthorizer.h"
+
+#include "Database.h"
+#include "PlatformString.h"
+
+namespace WebCore {
+
+DatabaseAuthorizer::DatabaseAuthorizer()
+ : m_securityEnabled(false)
+{
+ reset();
+}
+
+void DatabaseAuthorizer::reset()
+{
+ m_lastActionWasInsert = false;
+ m_lastActionChangedDatabase = false;
+}
+
+int DatabaseAuthorizer::createTable(const String& tableName)
+{
+ m_lastActionChangedDatabase = true;
+ return denyBasedOnTableName(tableName);
+}
+
+int DatabaseAuthorizer::createTempTable(const String& tableName)
+{
+ return denyBasedOnTableName(tableName);
+}
+
+int DatabaseAuthorizer::dropTable(const String& tableName)
+{
+ return denyBasedOnTableName(tableName);
+}
+
+int DatabaseAuthorizer::dropTempTable(const String& tableName)
+{
+ return denyBasedOnTableName(tableName);
+}
+
+int DatabaseAuthorizer::allowAlterTable(const String& databaseName, const String& tableName)
+{
+ m_lastActionChangedDatabase = true;
+ return denyBasedOnTableName(tableName);
+}
+
+int DatabaseAuthorizer::createIndex(const String& indexName, const String& tableName)
+{
+ m_lastActionChangedDatabase = true;
+ return denyBasedOnTableName(tableName);
+}
+
+int DatabaseAuthorizer::createTempIndex(const String& indexName, const String& tableName)
+{
+ return denyBasedOnTableName(tableName);
+}
+
+int DatabaseAuthorizer::dropIndex(const String& indexName, const String& tableName)
+{
+ return denyBasedOnTableName(tableName);
+}
+
+int DatabaseAuthorizer::dropTempIndex(const String& indexName, const String& tableName)
+{
+ return denyBasedOnTableName(tableName);
+}
+
+int DatabaseAuthorizer::createTrigger(const String& triggerName, const String& tableName)
+{
+ m_lastActionChangedDatabase = true;
+ return denyBasedOnTableName(tableName);
+}
+
+int DatabaseAuthorizer::createTempTrigger(const String& triggerName, const String& tableName)
+{
+ return denyBasedOnTableName(tableName);
+}
+
+int DatabaseAuthorizer::dropTrigger(const String& triggerName, const String& tableName)
+{
+ return denyBasedOnTableName(tableName);
+}
+
+int DatabaseAuthorizer::dropTempTrigger(const String& triggerName, const String& tableName)
+{
+ return denyBasedOnTableName(tableName);
+}
+
+int DatabaseAuthorizer::createVTable(const String& tableName, const String& moduleName)
+{
+ m_lastActionChangedDatabase = true;
+ return m_securityEnabled ? SQLAuthDeny : SQLAuthAllow;
+}
+
+int DatabaseAuthorizer::dropVTable(const String& tableName, const String& moduleName)
+{
+ return m_securityEnabled ? SQLAuthDeny : SQLAuthAllow;
+}
+
+int DatabaseAuthorizer::allowDelete(const String& tableName)
+{
+ return denyBasedOnTableName(tableName);
+}
+
+int DatabaseAuthorizer::allowInsert(const String& tableName)
+{
+ m_lastActionChangedDatabase = true;
+ m_lastActionWasInsert = true;
+ return denyBasedOnTableName(tableName);
+}
+
+int DatabaseAuthorizer::allowUpdate(const String& tableName, const String& columnName)
+{
+ m_lastActionChangedDatabase = true;
+ return denyBasedOnTableName(tableName);
+}
+
+int DatabaseAuthorizer::allowTransaction()
+{
+ return m_securityEnabled ? SQLAuthDeny : SQLAuthAllow;
+}
+
+int DatabaseAuthorizer::allowRead(const String& tableName, const String& columnName)
+{
+ return denyBasedOnTableName(tableName);
+}
+
+int DatabaseAuthorizer::allowAnalyze(const String& tableName)
+{
+ return denyBasedOnTableName(tableName);
+}
+
+int DatabaseAuthorizer::allowPragma(const String& pragmaName, const String& firstArgument)
+{
+ return m_securityEnabled ? SQLAuthDeny : SQLAuthAllow;
+}
+
+int DatabaseAuthorizer::allowAttach(const String& filename)
+{
+ return m_securityEnabled ? SQLAuthDeny : SQLAuthAllow;
+}
+
+int DatabaseAuthorizer::allowDetach(const String& databaseName)
+{
+ return m_securityEnabled ? SQLAuthDeny : SQLAuthAllow;
+}
+
+int DatabaseAuthorizer::allowFunction(const String& functionName)
+{
+ // FIXME: Are there any of these we need to prevent? One might guess current_date, current_time, current_timestamp because
+ // they would violate the "sandbox environment" part of 4.11.3, but scripts can generate the local client side information via
+ // javascript directly, anyways. Are there any other built-ins we need to be worried about?
+ return SQLAuthAllow;
+}
+
+void DatabaseAuthorizer::disable()
+{
+ m_securityEnabled = false;
+}
+
+void DatabaseAuthorizer::enable()
+{
+ m_securityEnabled = true;
+}
+
+int DatabaseAuthorizer::denyBasedOnTableName(const String& tableName)
+{
+ if (!m_securityEnabled)
+ return SQLAuthAllow;
+
+ // Sadly, normal creates and drops end up affecting sqlite_master in an authorizer callback, so
+ // it will be tough to enforce all of the following policies
+ //if (equalIgnoringCase(tableName, "sqlite_master") || equalIgnoringCase(tableName, "sqlite_temp_master") ||
+ // equalIgnoringCase(tableName, "sqlite_sequence") || equalIgnoringCase(tableName, Database::databaseInfoTableName()))
+ // return SQLAuthDeny;
+
+ if (equalIgnoringCase(tableName, Database::databaseInfoTableName()))
+ return SQLAuthDeny;
+
+ return SQLAuthAllow;
+}
+
+} // namespace WebCore
diff --git a/WebCore/storage/DatabaseAuthorizer.h b/WebCore/storage/DatabaseAuthorizer.h
new file mode 100644
index 0000000..d02d810
--- /dev/null
+++ b/WebCore/storage/DatabaseAuthorizer.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef DatabaseAuthorizer_h
+#define DatabaseAuthorizer_h
+
+#include <wtf/PassRefPtr.h>
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+class String;
+
+extern const int SQLAuthAllow;
+extern const int SQLAuthIgnore;
+extern const int SQLAuthDeny;
+
+class DatabaseAuthorizer : public ThreadSafeShared<DatabaseAuthorizer> {
+public:
+ static PassRefPtr<DatabaseAuthorizer> create() { return adoptRef(new DatabaseAuthorizer); }
+
+ int createTable(const String& tableName);
+ int createTempTable(const String& tableName);
+ int dropTable(const String& tableName);
+ int dropTempTable(const String& tableName);
+ int allowAlterTable(const String& databaseName, const String& tableName);
+
+ int createIndex(const String& indexName, const String& tableName);
+ int createTempIndex(const String& indexName, const String& tableName);
+ int dropIndex(const String& indexName, const String& tableName);
+ int dropTempIndex(const String& indexName, const String& tableName);
+
+ int createTrigger(const String& triggerName, const String& tableName);
+ int createTempTrigger(const String& triggerName, const String& tableName);
+ int dropTrigger(const String& triggerName, const String& tableName);
+ int dropTempTrigger(const String& triggerName, const String& tableName);
+
+ int createView(const String& viewName) { return SQLAuthAllow; }
+ int createTempView(const String& viewName) { return SQLAuthAllow; }
+ int dropView(const String& viewName) { return SQLAuthAllow; }
+ int dropTempView(const String& viewName) { return SQLAuthAllow; }
+
+ int createVTable(const String& tableName, const String& moduleName);
+ int dropVTable(const String& tableName, const String& moduleName);
+
+ int allowDelete(const String& tableName);
+ int allowInsert(const String& tableName);
+ int allowUpdate(const String& tableName, const String& columnName);
+ int allowTransaction();
+
+ int allowSelect() { return SQLAuthAllow; }
+ int allowRead(const String& tableName, const String& columnName);
+
+ int allowReindex(const String& indexName) { return SQLAuthAllow; }
+ int allowAnalyze(const String& tableName);
+ int allowFunction(const String& functionName);
+ int allowPragma(const String& pragmaName, const String& firstArgument);
+
+ int allowAttach(const String& filename);
+ int allowDetach(const String& databaseName);
+
+ void disable();
+ void enable();
+
+ void reset();
+
+ bool lastActionWasInsert() const { return m_lastActionWasInsert; }
+ bool lastActionChangedDatabase() const { return m_lastActionChangedDatabase; }
+
+private:
+ DatabaseAuthorizer();
+ int denyBasedOnTableName(const String&);
+
+ bool m_securityEnabled;
+ bool m_lastActionWasInsert;
+ bool m_lastActionChangedDatabase;
+};
+
+} // namespace WebCore
+
+#endif // DatabaseAuthorizer_h
diff --git a/WebCore/storage/DatabaseDetails.h b/WebCore/storage/DatabaseDetails.h
new file mode 100644
index 0000000..677362b
--- /dev/null
+++ b/WebCore/storage/DatabaseDetails.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DatabaseDetails_h
+#define DatabaseDetails_h
+
+#include "PlatformString.h"
+
+namespace WebCore {
+
+class DatabaseDetails {
+public:
+ DatabaseDetails()
+ : m_expectedUsage(0)
+ , m_currentUsage(0)
+ {
+ }
+
+ DatabaseDetails(const String& databaseName, const String& displayName, unsigned long long expectedUsage, unsigned long long currentUsage)
+ : m_name(databaseName)
+ , m_displayName(displayName)
+ , m_expectedUsage(expectedUsage)
+ , m_currentUsage(currentUsage)
+ {
+ }
+
+ const String& name() const { return m_name; }
+ const String& displayName() const { return m_displayName; }
+ unsigned long long expectedUsage() const { return m_expectedUsage; }
+ unsigned long long currentUsage() const { return m_currentUsage; }
+
+private:
+ String m_name;
+ String m_displayName;
+ unsigned long long m_expectedUsage;
+ unsigned long long m_currentUsage;
+
+};
+
+} // namespace WebCore
+
+#endif // DatabaseDetails_h
diff --git a/WebCore/storage/DatabaseTask.cpp b/WebCore/storage/DatabaseTask.cpp
new file mode 100644
index 0000000..7957d2a
--- /dev/null
+++ b/WebCore/storage/DatabaseTask.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "config.h"
+#include "DatabaseTask.h"
+
+#include "Database.h"
+#include "Logging.h"
+
+namespace WebCore {
+
+DatabaseTask::DatabaseTask(Database* database)
+ : m_database(database)
+ , m_complete(false)
+{
+}
+
+DatabaseTask::~DatabaseTask()
+{
+}
+
+void DatabaseTask::performTask()
+{
+ // Database tasks are meant to be used only once, so make sure this one hasn't been performed before.
+ ASSERT(!m_complete);
+
+ LOG(StorageAPI, "Performing %s %p\n", debugTaskName(), this);
+
+ m_database->resetAuthorizer();
+ doPerformTask();
+ m_database->performPolicyChecks();
+
+ if (m_synchronousMutex)
+ m_synchronousMutex->lock();
+
+ m_complete = true;
+
+ if (m_synchronousMutex) {
+ m_synchronousCondition->signal();
+ m_synchronousMutex->unlock();
+ }
+}
+
+void DatabaseTask::lockForSynchronousScheduling()
+{
+ // Called from main thread.
+ ASSERT(!m_synchronousMutex);
+ ASSERT(!m_synchronousCondition);
+ m_synchronousMutex.set(new Mutex);
+ m_synchronousCondition.set(new ThreadCondition);
+}
+
+void DatabaseTask::waitForSynchronousCompletion()
+{
+ // Called from main thread.
+ m_synchronousMutex->lock();
+ if (!m_complete)
+ m_synchronousCondition->wait(*m_synchronousMutex);
+ m_synchronousMutex->unlock();
+}
+
+// *** DatabaseOpenTask ***
+// Opens the database file and verifies the version matches the expected version.
+
+DatabaseOpenTask::DatabaseOpenTask(Database* database)
+ : DatabaseTask(database)
+ , m_code(0)
+ , m_success(false)
+{
+}
+
+void DatabaseOpenTask::doPerformTask()
+{
+ m_success = database()->performOpenAndVerify(m_code);
+}
+
+#ifndef NDEBUG
+const char* DatabaseOpenTask::debugTaskName() const
+{
+ return "DatabaseOpenTask";
+}
+#endif
+
+// *** DatabaseCloseTask ***
+// Closes the database.
+
+DatabaseCloseTask::DatabaseCloseTask(Database* database)
+ : DatabaseTask(database)
+{
+}
+
+void DatabaseCloseTask::doPerformTask()
+{
+ database()->close();
+}
+
+#ifndef NDEBUG
+const char* DatabaseCloseTask::debugTaskName() const
+{
+ return "DatabaseCloseTask";
+}
+#endif
+
+// *** DatabaseTransactionTask ***
+// Starts a transaction that will report its results via a callback.
+
+DatabaseTransactionTask::DatabaseTransactionTask(PassRefPtr<SQLTransaction> transaction)
+ : DatabaseTask(transaction->database())
+ , m_transaction(transaction)
+{
+}
+
+DatabaseTransactionTask::~DatabaseTransactionTask()
+{
+}
+
+void DatabaseTransactionTask::doPerformTask()
+{
+ if (m_transaction->performNextStep()) {
+ // The transaction is complete, we can move on to the next one.
+ MutexLocker locker(m_transaction->database()->m_transactionInProgressMutex);
+ m_transaction->database()->scheduleTransaction();
+ }
+}
+
+#ifndef NDEBUG
+const char* DatabaseTransactionTask::debugTaskName() const
+{
+ return "DatabaseTransactionTask";
+}
+#endif
+
+// *** DatabaseTableNamesTask ***
+// Retrieves a list of all tables in the database - for WebInspector support.
+
+DatabaseTableNamesTask::DatabaseTableNamesTask(Database* database)
+ : DatabaseTask(database)
+{
+}
+
+void DatabaseTableNamesTask::doPerformTask()
+{
+ m_tableNames = database()->performGetTableNames();
+}
+
+#ifndef NDEBUG
+const char* DatabaseTableNamesTask::debugTaskName() const
+{
+ return "DatabaseTableNamesTask";
+}
+#endif
+
+} // namespace WebCore
diff --git a/WebCore/storage/DatabaseTask.h b/WebCore/storage/DatabaseTask.h
new file mode 100644
index 0000000..50d297f
--- /dev/null
+++ b/WebCore/storage/DatabaseTask.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef DatabaseTask_h
+#define DatabaseTask_h
+
+#include "ExceptionCode.h"
+#include "PlatformString.h"
+#include <wtf/OwnPtr.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/Threading.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class Database;
+class DatabaseThread;
+class SQLValue;
+class SQLCallback;
+class SQLTransaction;
+class VersionChangeCallback;
+
+class DatabaseTask : public ThreadSafeShared<DatabaseTask>
+{
+ friend class Database;
+public:
+ virtual ~DatabaseTask();
+
+ void performTask();
+
+ Database* database() const { return m_database; }
+ bool isComplete() const { return m_complete; }
+
+protected:
+ DatabaseTask(Database*);
+
+private:
+ virtual void doPerformTask() = 0;
+#ifndef NDEBUG
+ virtual const char* debugTaskName() const = 0;
+#endif
+
+ void lockForSynchronousScheduling();
+ void waitForSynchronousCompletion();
+
+ Database* m_database;
+
+ bool m_complete;
+
+ OwnPtr<Mutex> m_synchronousMutex;
+ OwnPtr<ThreadCondition> m_synchronousCondition;
+};
+
+class DatabaseOpenTask : public DatabaseTask
+{
+public:
+ static PassRefPtr<DatabaseOpenTask> create(Database* db) { return adoptRef(new DatabaseOpenTask(db)); }
+
+ ExceptionCode exceptionCode() const { return m_code; }
+ bool openSuccessful() const { return m_success; }
+
+private:
+ DatabaseOpenTask(Database*);
+
+ virtual void doPerformTask();
+#ifndef NDEBUG
+ virtual const char* debugTaskName() const;
+#endif
+
+ ExceptionCode m_code;
+ bool m_success;
+};
+
+class DatabaseCloseTask : public DatabaseTask
+{
+public:
+ static PassRefPtr<DatabaseCloseTask> create(Database* db) { return adoptRef(new DatabaseCloseTask(db)); }
+
+private:
+ DatabaseCloseTask(Database*);
+
+ virtual void doPerformTask();
+#ifndef NDEBUG
+ virtual const char* debugTaskName() const;
+#endif
+};
+
+class DatabaseTransactionTask : public DatabaseTask
+{
+public:
+ static PassRefPtr<DatabaseTransactionTask> create(PassRefPtr<SQLTransaction> transaction) { return adoptRef(new DatabaseTransactionTask(transaction)); }
+
+ SQLTransaction* transaction() const { return m_transaction.get(); }
+
+ virtual ~DatabaseTransactionTask();
+private:
+ DatabaseTransactionTask(PassRefPtr<SQLTransaction>);
+
+ virtual void doPerformTask();
+#ifndef NDEBUG
+ virtual const char* debugTaskName() const;
+#endif
+
+ RefPtr<SQLTransaction> m_transaction;
+};
+
+class DatabaseTableNamesTask : public DatabaseTask
+{
+public:
+ static PassRefPtr<DatabaseTableNamesTask> create(Database* db) { return adoptRef(new DatabaseTableNamesTask(db)); }
+
+ Vector<String>& tableNames() { return m_tableNames; }
+
+private:
+ DatabaseTableNamesTask(Database*);
+
+ virtual void doPerformTask();
+#ifndef NDEBUG
+ virtual const char* debugTaskName() const;
+#endif
+
+ Vector<String> m_tableNames;
+};
+
+} // namespace WebCore
+
+#endif // DatabaseTask_h
diff --git a/WebCore/storage/DatabaseThread.cpp b/WebCore/storage/DatabaseThread.cpp
new file mode 100644
index 0000000..7eeed9b
--- /dev/null
+++ b/WebCore/storage/DatabaseThread.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "config.h"
+#include "DatabaseThread.h"
+
+#include "AutodrainedPool.h"
+#include "Database.h"
+#include "DatabaseTask.h"
+#include "Logging.h"
+
+namespace WebCore {
+
+DatabaseThread::DatabaseThread(Document* document)
+ : m_threadID(0)
+{
+ m_selfRef = this;
+}
+
+DatabaseThread::~DatabaseThread()
+{
+ // FIXME: Any cleanup required here? Since the thread deletes itself after running its detached course, I don't think so. Lets be sure.
+}
+
+bool DatabaseThread::start()
+{
+ if (m_threadID)
+ return true;
+
+ m_threadID = createThread(DatabaseThread::databaseThreadStart, this, "WebCore::Database");
+
+ return m_threadID;
+}
+
+void DatabaseThread::requestTermination()
+{
+ LOG(StorageAPI, "DatabaseThread %p was asked to terminate\n", this);
+ m_queue.kill();
+}
+
+bool DatabaseThread::terminationRequested() const
+{
+ return m_queue.killed();
+}
+
+void* DatabaseThread::databaseThreadStart(void* vDatabaseThread)
+{
+ DatabaseThread* dbThread = static_cast<DatabaseThread*>(vDatabaseThread);
+ return dbThread->databaseThread();
+}
+
+void* DatabaseThread::databaseThread()
+{
+ LOG(StorageAPI, "Starting DatabaseThread %p", this);
+
+ AutodrainedPool pool;
+ while (true) {
+ RefPtr<DatabaseTask> task;
+ if (!m_queue.waitForMessage(task))
+ break;
+
+ task->performTask();
+
+ pool.cycle();
+ }
+
+ LOG(StorageAPI, "About to detach thread %i and clear the ref to DatabaseThread %p, which currently has %i ref(s)", m_threadID, this, refCount());
+
+ // Detach the thread so its resources are no longer of any concern to anyone else
+ detachThread(m_threadID);
+
+ // Clear the self refptr, possibly resulting in deletion
+ m_selfRef = 0;
+
+ return 0;
+}
+
+void DatabaseThread::scheduleTask(PassRefPtr<DatabaseTask> task)
+{
+ m_queue.append(task);
+}
+
+void DatabaseThread::scheduleImmediateTask(PassRefPtr<DatabaseTask> task)
+{
+ m_queue.prepend(task);
+}
+
+void DatabaseThread::unscheduleDatabaseTasks(Database* database)
+{
+ // Note that the thread loop is running, so some tasks for the database
+ // may still be executed. This is unavoidable.
+
+ Deque<RefPtr<DatabaseTask> > filteredReverseQueue;
+ RefPtr<DatabaseTask> task;
+ while (m_queue.tryGetMessage(task)) {
+ if (task->database() != database)
+ filteredReverseQueue.append(task);
+ }
+
+ while (!filteredReverseQueue.isEmpty()) {
+ m_queue.append(filteredReverseQueue.first());
+ filteredReverseQueue.removeFirst();
+ }
+}
+
+} // namespace WebCore
diff --git a/WebCore/storage/DatabaseThread.h b/WebCore/storage/DatabaseThread.h
new file mode 100644
index 0000000..654c2dd
--- /dev/null
+++ b/WebCore/storage/DatabaseThread.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef DatabaseThread_h
+#define DatabaseThread_h
+
+#include <wtf/Deque.h>
+#include <wtf/HashMap.h>
+#include <wtf/HashSet.h>
+#include <wtf/MessageQueue.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+class Database;
+class DatabaseTask;
+class Document;
+
+class DatabaseThread : public ThreadSafeShared<DatabaseThread>
+{
+public:
+ static PassRefPtr<DatabaseThread> create(Document* doc) { return adoptRef(new DatabaseThread(doc)); }
+ ~DatabaseThread();
+
+ bool start();
+ void requestTermination();
+ bool terminationRequested() const;
+
+ void scheduleTask(PassRefPtr<DatabaseTask>);
+ void scheduleImmediateTask(PassRefPtr<DatabaseTask>); // This just adds the task to the front of the queue - the caller needs to be extremely careful not to create deadlocks when waiting for completion.
+ void unscheduleDatabaseTasks(Database*);
+
+private:
+ DatabaseThread(Document*);
+
+ static void* databaseThreadStart(void*);
+ void* databaseThread();
+
+ ThreadIdentifier m_threadID;
+ RefPtr<DatabaseThread> m_selfRef;
+
+ MessageQueue<RefPtr<DatabaseTask> > m_queue;
+};
+
+} // namespace WebCore
+
+#endif // DatabaseThread_h
diff --git a/WebCore/storage/DatabaseTracker.cpp b/WebCore/storage/DatabaseTracker.cpp
new file mode 100644
index 0000000..9a78dc8
--- /dev/null
+++ b/WebCore/storage/DatabaseTracker.cpp
@@ -0,0 +1,827 @@
+/*
+ * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "DatabaseTracker.h"
+
+#include "ChromeClient.h"
+#include "Database.h"
+#include "DatabaseTrackerClient.h"
+#include "Document.h"
+#include "FileSystem.h"
+#include "Logging.h"
+#include "OriginQuotaManager.h"
+#include "Page.h"
+#include "SecurityOrigin.h"
+#include "SecurityOriginHash.h"
+#include "SQLiteStatement.h"
+#include <wtf/MainThread.h>
+
+using namespace std;
+
+namespace WebCore {
+
+OriginQuotaManager& DatabaseTracker::originQuotaManager()
+{
+ populateOrigins();
+ ASSERT(m_quotaManager);
+ return *m_quotaManager;
+}
+
+DatabaseTracker& DatabaseTracker::tracker()
+{
+ static DatabaseTracker tracker;
+ return tracker;
+}
+
+DatabaseTracker::DatabaseTracker()
+ : m_client(0)
+ , m_proposedDatabase(0)
+#ifndef NDEBUG
+ , m_thread(currentThread())
+#endif
+{
+}
+
+void DatabaseTracker::setDatabaseDirectoryPath(const String& path)
+{
+ ASSERT(currentThread() == m_thread);
+ ASSERT(!m_database.isOpen());
+ m_databaseDirectoryPath = path;
+}
+
+const String& DatabaseTracker::databaseDirectoryPath() const
+{
+ ASSERT(currentThread() == m_thread);
+ return m_databaseDirectoryPath;
+}
+
+String DatabaseTracker::trackerDatabasePath() const
+{
+ ASSERT(currentThread() == m_thread);
+ if (m_databaseDirectoryPath.isEmpty())
+ return String();
+ return pathByAppendingComponent(m_databaseDirectoryPath, "Databases.db");
+}
+
+void DatabaseTracker::openTrackerDatabase(bool createIfDoesNotExist)
+{
+ ASSERT(currentThread() == m_thread);
+
+ if (m_database.isOpen())
+ return;
+
+ String databasePath = trackerDatabasePath();
+ if (databasePath.isEmpty())
+ return;
+
+ if (!createIfDoesNotExist && !fileExists(databasePath))
+ return;
+
+ makeAllDirectories(m_databaseDirectoryPath);
+ if (!m_database.open(databasePath)) {
+ // FIXME: What do do here?
+ return;
+ }
+ if (!m_database.tableExists("Origins")) {
+ if (!m_database.executeCommand("CREATE TABLE Origins (origin TEXT UNIQUE ON CONFLICT REPLACE, quota INTEGER NOT NULL ON CONFLICT FAIL);")) {
+ // FIXME: and here
+ }
+ }
+ if (!m_database.tableExists("Databases")) {
+ if (!m_database.executeCommand("CREATE TABLE Databases (guid INTEGER PRIMARY KEY AUTOINCREMENT, origin TEXT, name TEXT, displayName TEXT, estimatedSize INTEGER, path TEXT);")) {
+ // FIXME: and here
+ }
+ }
+}
+
+bool DatabaseTracker::canEstablishDatabase(Document* document, const String& name, const String& displayName, unsigned long estimatedSize)
+{
+ ASSERT(currentThread() == m_thread);
+
+ // Populate the origins before we establish a database; this guarantees that quotaForOrigin
+ // can run on the database thread later.
+ populateOrigins();
+
+ SecurityOrigin* origin = document->securityOrigin();
+
+ // Since we're imminently opening a database within this Document's origin, make sure this origin is being tracked by the QuotaTracker
+ // by fetching it's current usage now
+ unsigned long long usage = usageForOrigin(origin);
+
+ // If a database already exists, ignore the passed-in estimated size and say it's OK.
+ if (hasEntryForDatabase(origin, name))
+ return true;
+
+ // If the database will fit, allow its creation.
+ unsigned long long requirement = usage + max(1UL, estimatedSize);
+ if (requirement < usage)
+ return false; // If the estimated size is so big it causes an overflow, don't allow creation.
+ if (requirement <= quotaForOrigin(origin))
+ return true;
+
+ // Give the chrome client a chance to increase the quota.
+ // Temporarily make the details of the proposed database available, so the client can get at them.
+ Page* page = document->page();
+ if (!page)
+ return false;
+ pair<SecurityOrigin*, DatabaseDetails> details(origin, DatabaseDetails(name, displayName, estimatedSize, 0));
+ m_proposedDatabase = &details;
+ page->chrome()->client()->exceededDatabaseQuota(document->frame(), name);
+ m_proposedDatabase = 0;
+
+ // If the database will fit now, allow its creation.
+ return requirement <= quotaForOrigin(origin);
+}
+
+bool DatabaseTracker::hasEntryForOrigin(SecurityOrigin* origin)
+{
+ ASSERT(currentThread() == m_thread);
+ populateOrigins();
+ MutexLocker lockQuotaMap(m_quotaMapGuard);
+ return m_quotaMap->contains(origin);
+}
+
+bool DatabaseTracker::hasEntryForDatabase(SecurityOrigin* origin, const String& databaseIdentifier)
+{
+ ASSERT(currentThread() == m_thread);
+ openTrackerDatabase(false);
+ if (!m_database.isOpen())
+ return false;
+ SQLiteStatement statement(m_database, "SELECT guid FROM Databases WHERE origin=? AND name=?;");
+
+ if (statement.prepare() != SQLResultOk)
+ return false;
+
+ statement.bindText(1, origin->databaseIdentifier());
+ statement.bindText(2, databaseIdentifier);
+
+ return statement.step() == SQLResultRow;
+}
+
+String DatabaseTracker::originPath(SecurityOrigin* origin) const
+{
+ ASSERT(currentThread() == m_thread);
+ if (m_databaseDirectoryPath.isEmpty())
+ return String();
+ return pathByAppendingComponent(m_databaseDirectoryPath, origin->databaseIdentifier());
+}
+
+String DatabaseTracker::fullPathForDatabase(SecurityOrigin* origin, const String& name, bool createIfNotExists)
+{
+ ASSERT(currentThread() == m_thread);
+
+ if (m_proposedDatabase && m_proposedDatabase->first == origin && m_proposedDatabase->second.name() == name)
+ return String();
+
+ String originIdentifier = origin->databaseIdentifier();
+ String originPath = this->originPath(origin);
+
+ // Make sure the path for this SecurityOrigin exists
+ if (createIfNotExists && !makeAllDirectories(originPath))
+ return String();
+
+ // See if we have a path for this database yet
+ openTrackerDatabase(false);
+ if (!m_database.isOpen())
+ return String();
+ SQLiteStatement statement(m_database, "SELECT path FROM Databases WHERE origin=? AND name=?;");
+
+ if (statement.prepare() != SQLResultOk)
+ return String();
+
+ statement.bindText(1, originIdentifier);
+ statement.bindText(2, name);
+
+ int result = statement.step();
+
+ if (result == SQLResultRow)
+ return pathByAppendingComponent(originPath, statement.getColumnText(0));
+ if (!createIfNotExists)
+ return String();
+
+ if (result != SQLResultDone) {
+ LOG_ERROR("Failed to retrieve filename from Database Tracker for origin %s, name %s", origin->databaseIdentifier().ascii().data(), name.ascii().data());
+ return String();
+ }
+ statement.finalize();
+
+ SQLiteStatement sequenceStatement(m_database, "SELECT seq FROM sqlite_sequence WHERE name='Databases';");
+
+ // FIXME: More informative error handling here, even though these steps should never fail
+ if (sequenceStatement.prepare() != SQLResultOk)
+ return String();
+ result = sequenceStatement.step();
+
+ // This has a range of 2^63 and starts at 0 for every time a user resets Safari -
+ // I can't imagine it'd over overflow
+ int64_t seq = 0;
+ if (result == SQLResultRow) {
+ seq = sequenceStatement.getColumnInt64(0);
+ } else if (result != SQLResultDone)
+ return String();
+ sequenceStatement.finalize();
+
+ String filename;
+ do {
+ ++seq;
+ filename = pathByAppendingComponent(originPath, String::format("%016llx.db", seq));
+ } while (fileExists(filename));
+
+ if (!addDatabase(origin, name, String::format("%016llx.db", seq)))
+ return String();
+
+ // If this origin's quota is being tracked (open handle to a database in this origin), add this new database
+ // to the quota manager now
+ {
+ Locker<OriginQuotaManager> locker(originQuotaManager());
+ if (originQuotaManager().tracksOrigin(origin))
+ originQuotaManager().addDatabase(origin, name, filename);
+ }
+
+ return filename;
+}
+
+void DatabaseTracker::populateOrigins()
+{
+ if (m_quotaMap)
+ return;
+
+ ASSERT(currentThread() == m_thread);
+
+ m_quotaMap.set(new QuotaMap);
+ m_quotaManager.set(new OriginQuotaManager);
+
+ openTrackerDatabase(false);
+ if (!m_database.isOpen())
+ return;
+
+ SQLiteStatement statement(m_database, "SELECT origin, quota FROM Origins");
+
+ if (statement.prepare() != SQLResultOk)
+ return;
+
+ int result;
+ while ((result = statement.step()) == SQLResultRow) {
+ RefPtr<SecurityOrigin> origin = SecurityOrigin::createFromDatabaseIdentifier(statement.getColumnText(0));
+ m_quotaMap->set(origin.get(), statement.getColumnInt64(1));
+ }
+
+ if (result != SQLResultDone)
+ LOG_ERROR("Failed to read in all origins from the database");
+}
+
+void DatabaseTracker::origins(Vector<RefPtr<SecurityOrigin> >& result)
+{
+ ASSERT(currentThread() == m_thread);
+ populateOrigins();
+ MutexLocker lockQuotaMap(m_quotaMapGuard);
+ copyKeysToVector(*m_quotaMap, result);
+}
+
+bool DatabaseTracker::databaseNamesForOrigin(SecurityOrigin* origin, Vector<String>& resultVector)
+{
+ ASSERT(currentThread() == m_thread);
+ openTrackerDatabase(false);
+ if (!m_database.isOpen())
+ return false;
+
+ SQLiteStatement statement(m_database, "SELECT name FROM Databases where origin=?;");
+
+ if (statement.prepare() != SQLResultOk)
+ return false;
+
+ statement.bindText(1, origin->databaseIdentifier());
+
+ int result;
+ while ((result = statement.step()) == SQLResultRow)
+ resultVector.append(statement.getColumnText(0));
+
+ if (result != SQLResultDone) {
+ LOG_ERROR("Failed to retrieve all database names for origin %s", origin->databaseIdentifier().ascii().data());
+ return false;
+ }
+
+ return true;
+}
+
+DatabaseDetails DatabaseTracker::detailsForNameAndOrigin(const String& name, SecurityOrigin* origin)
+{
+ ASSERT(currentThread() == m_thread);
+
+ if (m_proposedDatabase && m_proposedDatabase->first == origin && m_proposedDatabase->second.name() == name)
+ return m_proposedDatabase->second;
+
+ String originIdentifier = origin->databaseIdentifier();
+
+ openTrackerDatabase(false);
+ if (!m_database.isOpen())
+ return DatabaseDetails();
+ SQLiteStatement statement(m_database, "SELECT displayName, estimatedSize FROM Databases WHERE origin=? AND name=?");
+ if (statement.prepare() != SQLResultOk)
+ return DatabaseDetails();
+
+ statement.bindText(1, originIdentifier);
+ statement.bindText(2, name);
+
+ int result = statement.step();
+ if (result == SQLResultDone)
+ return DatabaseDetails();
+
+ if (result != SQLResultRow) {
+ LOG_ERROR("Error retrieving details for database %s in origin %s from tracker database", name.ascii().data(), originIdentifier.ascii().data());
+ return DatabaseDetails();
+ }
+
+ return DatabaseDetails(name, statement.getColumnText(0), statement.getColumnInt64(1), usageForDatabase(name, origin));
+}
+
+void DatabaseTracker::setDatabaseDetails(SecurityOrigin* origin, const String& name, const String& displayName, unsigned long estimatedSize)
+{
+ ASSERT(currentThread() == m_thread);
+
+ String originIdentifier = origin->databaseIdentifier();
+ int64_t guid = 0;
+
+ openTrackerDatabase(true);
+ if (!m_database.isOpen())
+ return;
+ SQLiteStatement statement(m_database, "SELECT guid FROM Databases WHERE origin=? AND name=?");
+ if (statement.prepare() != SQLResultOk)
+ return;
+
+ statement.bindText(1, originIdentifier);
+ statement.bindText(2, name);
+
+ int result = statement.step();
+ if (result == SQLResultRow)
+ guid = statement.getColumnInt64(0);
+ statement.finalize();
+
+ if (guid == 0) {
+ if (result != SQLResultDone)
+ LOG_ERROR("Error to determing existence of database %s in origin %s in tracker database", name.ascii().data(), originIdentifier.ascii().data());
+ else {
+ // This case should never occur - we should never be setting database details for a database that doesn't already exist in the tracker
+ // But since the tracker file is an external resource not under complete control of our code, it's somewhat invalid to make this an ASSERT case
+ // So we'll print an error instead
+ LOG_ERROR("Could not retrieve guid for database %s in origin %s from the tracker database - it is invalid to set database details on a database that doesn't already exist in the tracker",
+ name.ascii().data(), originIdentifier.ascii().data());
+ }
+ return;
+ }
+
+ SQLiteStatement updateStatement(m_database, "UPDATE Databases SET displayName=?, estimatedSize=? WHERE guid=?");
+ if (updateStatement.prepare() != SQLResultOk)
+ return;
+
+ updateStatement.bindText(1, displayName);
+ updateStatement.bindInt64(2, estimatedSize);
+ updateStatement.bindInt64(3, guid);
+
+ if (updateStatement.step() != SQLResultDone) {
+ LOG_ERROR("Failed to update details for database %s in origin %s", name.ascii().data(), originIdentifier.ascii().data());
+ return;
+ }
+
+ if (m_client)
+ m_client->dispatchDidModifyDatabase(origin, name);
+}
+
+unsigned long long DatabaseTracker::usageForDatabase(const String& name, SecurityOrigin* origin)
+{
+ ASSERT(currentThread() == m_thread);
+ String path = fullPathForDatabase(origin, name, false);
+ if (path.isEmpty())
+ return 0;
+
+ long long size;
+ return getFileSize(path, size) ? size : 0;
+}
+
+void DatabaseTracker::addOpenDatabase(Database* database)
+{
+ if (!database)
+ return;
+
+ MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
+
+ if (!m_openDatabaseMap)
+ m_openDatabaseMap.set(new DatabaseOriginMap);
+
+ RefPtr<SecurityOrigin> origin(database->securityOriginCopy());
+ String name(database->stringIdentifier());
+
+ DatabaseNameMap* nameMap = m_openDatabaseMap->get(origin);
+ if (!nameMap) {
+ nameMap = new DatabaseNameMap;
+ m_openDatabaseMap->set(origin, nameMap);
+ }
+
+ DatabaseSet* databaseSet = nameMap->get(name);
+ if (!databaseSet) {
+ databaseSet = new DatabaseSet;
+ nameMap->set(name, databaseSet);
+ }
+
+ databaseSet->add(database);
+
+ LOG(StorageAPI, "Added open Database %s (%p)\n", database->stringIdentifier().ascii().data(), database);
+}
+
+void DatabaseTracker::removeOpenDatabase(Database* database)
+{
+ if (!database)
+ return;
+
+ MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
+
+ if (!m_openDatabaseMap) {
+ ASSERT_NOT_REACHED();
+ return;
+ }
+
+ RefPtr<SecurityOrigin> origin(database->securityOriginCopy());
+ String name(database->stringIdentifier());
+
+ DatabaseNameMap* nameMap = m_openDatabaseMap->get(origin);
+ if (!nameMap) {
+ ASSERT_NOT_REACHED();
+ return;
+ }
+
+ DatabaseSet* databaseSet = nameMap->get(name);
+ if (!databaseSet) {
+ ASSERT_NOT_REACHED();
+ return;
+ }
+
+ databaseSet->remove(database);
+
+ LOG(StorageAPI, "Removed open Database %s (%p)\n", database->stringIdentifier().ascii().data(), database);
+
+ if (!databaseSet->isEmpty())
+ return;
+
+ nameMap->remove(name);
+ delete databaseSet;
+
+ if (!nameMap->isEmpty())
+ return;
+
+ m_openDatabaseMap->remove(origin);
+ delete nameMap;
+}
+
+unsigned long long DatabaseTracker::usageForOrigin(SecurityOrigin* origin)
+{
+ ASSERT(currentThread() == m_thread);
+ Locker<OriginQuotaManager> locker(originQuotaManager());
+
+ // Use the OriginQuotaManager mechanism to calculate the usage
+ if (originQuotaManager().tracksOrigin(origin))
+ return originQuotaManager().diskUsage(origin);
+
+ // If the OriginQuotaManager doesn't track this origin already, prime it to do so
+ originQuotaManager().trackOrigin(origin);
+
+ Vector<String> names;
+ databaseNamesForOrigin(origin, names);
+
+ for (unsigned i = 0; i < names.size(); ++i)
+ originQuotaManager().addDatabase(origin, names[i], fullPathForDatabase(origin, names[i], false));
+
+ if (!originQuotaManager().tracksOrigin(origin))
+ return 0;
+ return originQuotaManager().diskUsage(origin);
+}
+
+unsigned long long DatabaseTracker::quotaForOrigin(SecurityOrigin* origin)
+{
+ ASSERT(currentThread() == m_thread || m_quotaMap);
+ populateOrigins();
+ MutexLocker lockQuotaMap(m_quotaMapGuard);
+ return m_quotaMap->get(origin);
+}
+
+void DatabaseTracker::setQuota(SecurityOrigin* origin, unsigned long long quota)
+{
+ ASSERT(currentThread() == m_thread);
+ if (quotaForOrigin(origin) == quota)
+ return;
+
+ openTrackerDatabase(true);
+ if (!m_database.isOpen())
+ return;
+
+ {
+ MutexLocker lockQuotaMap(m_quotaMapGuard);
+
+ if (!m_quotaMap->contains(origin)) {
+ SQLiteStatement statement(m_database, "INSERT INTO Origins VALUES (?, ?)");
+ if (statement.prepare() != SQLResultOk) {
+ LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data());
+ } else {
+ statement.bindText(1, origin->databaseIdentifier());
+ statement.bindInt64(2, quota);
+
+ if (statement.step() != SQLResultDone)
+ LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data());
+ }
+ } else {
+ SQLiteStatement statement(m_database, "UPDATE Origins SET quota=? WHERE origin=?");
+ bool error = statement.prepare() != SQLResultOk;
+ if (!error) {
+ statement.bindInt64(1, quota);
+ statement.bindText(2, origin->databaseIdentifier());
+
+ error = !statement.executeCommand();
+ }
+
+ if (error)
+ LOG_ERROR("Failed to set quota %llu in tracker database for origin %s", quota, origin->databaseIdentifier().ascii().data());
+ }
+
+ // FIXME: Is it really OK to update the quota in memory if we failed to update it on disk?
+ m_quotaMap->set(origin, quota);
+ }
+
+ if (m_client)
+ m_client->dispatchDidModifyOrigin(origin);
+}
+
+bool DatabaseTracker::addDatabase(SecurityOrigin* origin, const String& name, const String& path)
+{
+ ASSERT(currentThread() == m_thread);
+ openTrackerDatabase(true);
+ if (!m_database.isOpen())
+ return false;
+
+ // New database should never be added until the origin has been established
+ ASSERT(hasEntryForOrigin(origin));
+
+ SQLiteStatement statement(m_database, "INSERT INTO Databases (origin, name, path) VALUES (?, ?, ?);");
+
+ if (statement.prepare() != SQLResultOk)
+ return false;
+
+ statement.bindText(1, origin->databaseIdentifier());
+ statement.bindText(2, name);
+ statement.bindText(3, path);
+
+ if (!statement.executeCommand()) {
+ LOG_ERROR("Failed to add database %s to origin %s: %s\n", name.ascii().data(), origin->databaseIdentifier().ascii().data(), m_database.lastErrorMsg());
+ return false;
+ }
+
+ if (m_client)
+ m_client->dispatchDidModifyOrigin(origin);
+
+ return true;
+}
+
+void DatabaseTracker::deleteAllDatabases()
+{
+ ASSERT(currentThread() == m_thread);
+
+ Vector<RefPtr<SecurityOrigin> > originsCopy;
+ origins(originsCopy);
+
+ for (unsigned i = 0; i < originsCopy.size(); ++i)
+ deleteOrigin(originsCopy[i].get());
+}
+
+void DatabaseTracker::deleteOrigin(SecurityOrigin* origin)
+{
+ ASSERT(currentThread() == m_thread);
+ openTrackerDatabase(false);
+ if (!m_database.isOpen())
+ return;
+
+ Vector<String> databaseNames;
+ if (!databaseNamesForOrigin(origin, databaseNames)) {
+ LOG_ERROR("Unable to retrieve list of database names for origin %s", origin->databaseIdentifier().ascii().data());
+ return;
+ }
+
+ for (unsigned i = 0; i < databaseNames.size(); ++i) {
+ if (!deleteDatabaseFile(origin, databaseNames[i])) {
+ LOG_ERROR("Unable to delete file for database %s in origin %s", databaseNames[i].ascii().data(), origin->databaseIdentifier().ascii().data());
+ return;
+ }
+ }
+
+ SQLiteStatement statement(m_database, "DELETE FROM Databases WHERE origin=?");
+ if (statement.prepare() != SQLResultOk) {
+ LOG_ERROR("Unable to prepare deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());
+ return;
+ }
+
+ statement.bindText(1, origin->databaseIdentifier());
+
+ if (!statement.executeCommand()) {
+ LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());
+ return;
+ }
+
+ SQLiteStatement originStatement(m_database, "DELETE FROM Origins WHERE origin=?");
+ if (originStatement.prepare() != SQLResultOk) {
+ LOG_ERROR("Unable to prepare deletion of origin %s from tracker", origin->databaseIdentifier().ascii().data());
+ return;
+ }
+
+ originStatement.bindText(1, origin->databaseIdentifier());
+
+ if (!originStatement.executeCommand()) {
+ LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data());
+ return;
+ }
+
+ deleteEmptyDirectory(originPath(origin));
+
+ RefPtr<SecurityOrigin> originPossiblyLastReference = origin;
+ {
+ MutexLocker lockQuotaMap(m_quotaMapGuard);
+ m_quotaMap->remove(origin);
+
+ Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
+ originQuotaManager().removeOrigin(origin);
+
+ // If we removed the last origin, do some additional deletion.
+ if (m_quotaMap->isEmpty()) {
+ if (m_database.isOpen())
+ m_database.close();
+ deleteFile(trackerDatabasePath());
+ deleteEmptyDirectory(m_databaseDirectoryPath);
+ }
+ }
+
+ if (m_client) {
+ m_client->dispatchDidModifyOrigin(origin);
+ for (unsigned i = 0; i < databaseNames.size(); ++i)
+ m_client->dispatchDidModifyDatabase(origin, databaseNames[i]);
+ }
+}
+
+void DatabaseTracker::deleteDatabase(SecurityOrigin* origin, const String& name)
+{
+ ASSERT(currentThread() == m_thread);
+ openTrackerDatabase(false);
+ if (!m_database.isOpen())
+ return;
+
+ if (!deleteDatabaseFile(origin, name)) {
+ LOG_ERROR("Unable to delete file for database %s in origin %s", name.ascii().data(), origin->databaseIdentifier().ascii().data());
+ return;
+ }
+
+ SQLiteStatement statement(m_database, "DELETE FROM Databases WHERE origin=? AND name=?");
+ if (statement.prepare() != SQLResultOk) {
+ LOG_ERROR("Unable to prepare deletion of database %s from origin %s from tracker", name.ascii().data(), origin->databaseIdentifier().ascii().data());
+ return;
+ }
+
+ statement.bindText(1, origin->databaseIdentifier());
+ statement.bindText(2, name);
+
+ if (!statement.executeCommand()) {
+ LOG_ERROR("Unable to execute deletion of database %s from origin %s from tracker", name.ascii().data(), origin->databaseIdentifier().ascii().data());
+ return;
+ }
+
+ {
+ Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
+ originQuotaManager().removeDatabase(origin, name);
+ }
+
+ if (m_client) {
+ m_client->dispatchDidModifyOrigin(origin);
+ m_client->dispatchDidModifyDatabase(origin, name);
+ }
+}
+
+bool DatabaseTracker::deleteDatabaseFile(SecurityOrigin* origin, const String& name)
+{
+ ASSERT(currentThread() == m_thread);
+ String fullPath = fullPathForDatabase(origin, name, false);
+ if (fullPath.isEmpty())
+ return true;
+
+ Vector<RefPtr<Database> > deletedDatabases;
+
+ // Make sure not to hold the m_openDatabaseMapGuard mutex when calling
+ // Database::markAsDeletedAndClose(), since that can cause a deadlock
+ // during the synchronous DatabaseThread call it triggers.
+
+ {
+ MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard);
+ if (m_openDatabaseMap) {
+ // There are some open databases, lets check if they are for this origin.
+ DatabaseNameMap* nameMap = m_openDatabaseMap->get(origin);
+ if (nameMap && nameMap->size()) {
+ // There are some open databases for this origin, lets check
+ // if they are this database by name.
+ DatabaseSet* databaseSet = nameMap->get(name);
+ if (databaseSet && databaseSet->size()) {
+ // We have some database open with this name. Mark them as deleted.
+ DatabaseSet::const_iterator end = databaseSet->end();
+ for (DatabaseSet::const_iterator it = databaseSet->begin(); it != end; ++it)
+ deletedDatabases.append(*it);
+ }
+ }
+ }
+ }
+
+ for (unsigned i = 0; i < deletedDatabases.size(); ++i)
+ deletedDatabases[i]->markAsDeletedAndClose();
+
+ return deleteFile(fullPath);
+}
+
+void DatabaseTracker::setClient(DatabaseTrackerClient* client)
+{
+ ASSERT(currentThread() == m_thread);
+ m_client = client;
+}
+
+static Mutex& notificationMutex()
+{
+ static Mutex mutex;
+ return mutex;
+}
+
+static Vector<pair<SecurityOrigin*, String> >& notificationQueue()
+{
+ static Vector<pair<SecurityOrigin*, String> > queue;
+ return queue;
+}
+
+void DatabaseTracker::scheduleNotifyDatabaseChanged(SecurityOrigin* origin, const String& name)
+{
+ MutexLocker locker(notificationMutex());
+
+ notificationQueue().append(pair<SecurityOrigin*, String>(origin, name.copy()));
+ scheduleForNotification();
+}
+
+static bool notificationScheduled = false;
+
+void DatabaseTracker::scheduleForNotification()
+{
+ ASSERT(!notificationMutex().tryLock());
+
+ if (!notificationScheduled) {
+ callOnMainThread(DatabaseTracker::notifyDatabasesChanged, 0);
+ notificationScheduled = true;
+ }
+}
+
+void DatabaseTracker::notifyDatabasesChanged(void*)
+{
+ // Note that if DatabaseTracker ever becomes non-singleton, we'll have to amend this notification
+ // mechanism to include which tracker the notification goes out on as well.
+ DatabaseTracker& theTracker(tracker());
+
+ Vector<pair<SecurityOrigin*, String> > notifications;
+ {
+ MutexLocker locker(notificationMutex());
+
+ notifications.swap(notificationQueue());
+
+ notificationScheduled = false;
+ }
+
+ if (!theTracker.m_client)
+ return;
+
+ for (unsigned i = 0; i < notifications.size(); ++i)
+ theTracker.m_client->dispatchDidModifyDatabase(notifications[i].first, notifications[i].second);
+}
+
+
+} // namespace WebCore
diff --git a/WebCore/storage/DatabaseTracker.h b/WebCore/storage/DatabaseTracker.h
new file mode 100644
index 0000000..3760822
--- /dev/null
+++ b/WebCore/storage/DatabaseTracker.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DatabaseTracker_h
+#define DatabaseTracker_h
+
+#include "DatabaseDetails.h"
+#include "PlatformString.h"
+#include "SQLiteDatabase.h"
+#include "StringHash.h"
+#include <wtf/HashSet.h>
+#include <wtf/OwnPtr.h>
+
+namespace WebCore {
+
+class Database;
+class DatabaseTrackerClient;
+class Document;
+class OriginQuotaManager;
+class SecurityOrigin;
+
+struct SecurityOriginHash;
+struct SecurityOriginTraits;
+
+class DatabaseTracker {
+public:
+ void setDatabaseDirectoryPath(const String&);
+ const String& databaseDirectoryPath() const;
+
+ bool canEstablishDatabase(Document*, const String& name, const String& displayName, unsigned long estimatedSize);
+ void setDatabaseDetails(SecurityOrigin*, const String& name, const String& displayName, unsigned long estimatedSize);
+ String fullPathForDatabase(SecurityOrigin*, const String& name, bool createIfDoesNotExist = true);
+
+ void origins(Vector<RefPtr<SecurityOrigin> >& result);
+ bool databaseNamesForOrigin(SecurityOrigin*, Vector<String>& result);
+
+ DatabaseDetails detailsForNameAndOrigin(const String&, SecurityOrigin*);
+
+ void addOpenDatabase(Database*);
+ void removeOpenDatabase(Database*);
+
+ unsigned long long usageForDatabase(const String&, SecurityOrigin*);
+ unsigned long long usageForOrigin(SecurityOrigin*);
+ unsigned long long quotaForOrigin(SecurityOrigin*);
+ void setQuota(SecurityOrigin*, unsigned long long);
+
+ void deleteAllDatabases();
+ void deleteOrigin(SecurityOrigin*);
+ void deleteDatabase(SecurityOrigin*, const String& name);
+
+ void setClient(DatabaseTrackerClient*);
+
+ // From a secondary thread, must be thread safe with its data
+ void scheduleNotifyDatabaseChanged(SecurityOrigin*, const String& name);
+
+ OriginQuotaManager& originQuotaManager();
+
+ static DatabaseTracker& tracker();
+
+private:
+ DatabaseTracker();
+
+ String trackerDatabasePath() const;
+ void openTrackerDatabase(bool createIfDoesNotExist);
+
+ String originPath(SecurityOrigin*) const;
+
+ bool hasEntryForOrigin(SecurityOrigin*);
+ bool hasEntryForDatabase(SecurityOrigin*, const String& databaseIdentifier);
+
+ bool addDatabase(SecurityOrigin*, const String& name, const String& path);
+ void populateOrigins();
+
+ bool deleteDatabaseFile(SecurityOrigin*, const String& name);
+
+ SQLiteDatabase m_database;
+
+ typedef HashMap<RefPtr<SecurityOrigin>, unsigned long long, SecurityOriginHash> QuotaMap;
+ Mutex m_quotaMapGuard;
+ mutable OwnPtr<QuotaMap> m_quotaMap;
+
+ typedef HashSet<Database*> DatabaseSet;
+ typedef HashMap<String, DatabaseSet*> DatabaseNameMap;
+ typedef HashMap<RefPtr<SecurityOrigin>, DatabaseNameMap*, SecurityOriginHash> DatabaseOriginMap;
+
+ Mutex m_openDatabaseMapGuard;
+ mutable OwnPtr<DatabaseOriginMap> m_openDatabaseMap;
+
+ OwnPtr<OriginQuotaManager> m_quotaManager;
+
+ String m_databaseDirectoryPath;
+
+ DatabaseTrackerClient* m_client;
+
+ std::pair<SecurityOrigin*, DatabaseDetails>* m_proposedDatabase;
+
+#ifndef NDEBUG
+ ThreadIdentifier m_thread;
+#endif
+
+ static void scheduleForNotification();
+ static void notifyDatabasesChanged(void*);
+};
+
+} // namespace WebCore
+
+#endif // DatabaseTracker_h
diff --git a/WebCore/storage/DatabaseTrackerClient.h b/WebCore/storage/DatabaseTrackerClient.h
new file mode 100644
index 0000000..f9b0f70
--- /dev/null
+++ b/WebCore/storage/DatabaseTrackerClient.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef DatabaseTrackerClient_h
+#define DatabaseTrackerClient_h
+
+namespace WebCore {
+
+class SecurityOrigin;
+class String;
+
+class DatabaseTrackerClient {
+public:
+ virtual ~DatabaseTrackerClient() { }
+ virtual void dispatchDidModifyOrigin(SecurityOrigin*) = 0;
+ virtual void dispatchDidModifyDatabase(SecurityOrigin*, const String& databaseName) = 0;
+};
+
+} // namespace WebCore
+
+#endif // DatabaseTrackerClient_h
diff --git a/WebCore/storage/LocalStorage.cpp b/WebCore/storage/LocalStorage.cpp
new file mode 100644
index 0000000..0177593
--- /dev/null
+++ b/WebCore/storage/LocalStorage.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "LocalStorage.h"
+
+#include "CString.h"
+#include "EventNames.h"
+#include "FileSystem.h"
+#include "Frame.h"
+#include "FrameTree.h"
+#include "LocalStorageArea.h"
+#include "Page.h"
+#include "PageGroup.h"
+#include "StorageArea.h"
+
+namespace WebCore {
+
+typedef HashMap<String, LocalStorage*> LocalStorageMap;
+
+static LocalStorageMap& localStorageMap()
+{
+ static LocalStorageMap localStorageMap;
+ return localStorageMap;
+}
+
+PassRefPtr<LocalStorage> LocalStorage::localStorage(const String& path)
+{
+ const String lookupPath = path.isNull() ? String("") : path;
+ LocalStorageMap::iterator it = localStorageMap().find(lookupPath);
+ if (it == localStorageMap().end()) {
+ RefPtr<LocalStorage> localStorage = adoptRef(new LocalStorage(lookupPath));
+ localStorageMap().set(lookupPath, localStorage.get());
+ return localStorage.release();
+ }
+
+ return it->second;
+}
+
+LocalStorage::LocalStorage(const String& path)
+ : m_path(path.copy())
+{
+ // If the path is empty, we know we're never going to be using the thread for anything, so don't start it.
+ // In the future, we might also want to consider removing it from the DOM in that case - <rdar://problem/5960470>
+ if (path.isEmpty())
+ return;
+
+ m_thread = LocalStorageThread::create();
+ m_thread->start();
+ m_thread->scheduleImport(this);
+}
+
+LocalStorage::~LocalStorage()
+{
+ ASSERT(localStorageMap().get(m_path) == this);
+ localStorageMap().remove(m_path);
+}
+
+PassRefPtr<StorageArea> LocalStorage::storageArea(Frame* sourceFrame, SecurityOrigin* origin)
+{
+ ASSERT(isMainThread());
+
+ // FIXME: If the security origin in question has never had a storage area established,
+ // we need to ask a client call if establishing it is okay. If the client denies the request,
+ // this method will return null.
+ // The sourceFrame argument exists for the purpose of asking a client.
+ // To know if an area has previously been established, we need to wait until this LocalStorage
+ // object has finished it's AreaImport task.
+
+
+ // FIXME: If the storage area is being established for the first time here, we need to
+ // sync its existance and quota out to disk via an task of type AreaSync
+
+ RefPtr<LocalStorageArea> storageArea;
+ if (storageArea = m_storageAreaMap.get(origin))
+ return storageArea.release();
+
+ storageArea = LocalStorageArea::create(origin, this);
+ m_storageAreaMap.set(origin, storageArea);
+ return storageArea.release();
+}
+
+String LocalStorage::fullDatabaseFilename(SecurityOrigin* origin)
+{
+ // FIXME: Once we actually track origin/quota entries to see which origins have local storage established,
+ // we will return an empty path name if the origin isn't allowed to have LocalStorage.
+ // We'll need to wait here until the AreaImport task to complete before making that decision.
+
+ if (m_path.isEmpty())
+ return String();
+
+ ASSERT(origin);
+ if (!origin)
+ return String();
+
+ if (!makeAllDirectories(m_path)) {
+ LOG_ERROR("Unabled to create LocalStorage database path %s", m_path.utf8().data());
+ return String();
+ }
+
+ return pathByAppendingComponent(m_path, origin->databaseIdentifier() + ".localstorage");
+}
+
+void LocalStorage::performImport()
+{
+ ASSERT(!isMainThread());
+
+ // FIXME: Import all known local storage origins here along with their quotas
+}
+
+void LocalStorage::performSync()
+{
+ ASSERT(!isMainThread());
+
+ // FIXME: Write out new origins and quotas here
+}
+
+void LocalStorage::close()
+{
+ ASSERT(isMainThread());
+
+ LocalStorageAreaMap::iterator end = m_storageAreaMap.end();
+ for (LocalStorageAreaMap::iterator it = m_storageAreaMap.begin(); it != end; ++it)
+ it->second->scheduleFinalSync();
+
+ if (m_thread) {
+ m_thread->terminate();
+ m_thread = 0;
+ }
+}
+
+bool LocalStorage::scheduleImport(PassRefPtr<LocalStorageArea> area)
+{
+ ASSERT(isMainThread());
+
+ if (m_thread)
+ m_thread->scheduleImport(area);
+
+ return m_thread;
+}
+
+void LocalStorage::scheduleSync(PassRefPtr<LocalStorageArea> area)
+{
+ ASSERT(isMainThread());
+ if (m_thread)
+ m_thread->scheduleSync(area);
+}
+
+} // namespace WebCore
diff --git a/WebCore/storage/LocalStorage.h b/WebCore/storage/LocalStorage.h
new file mode 100644
index 0000000..4816de7
--- /dev/null
+++ b/WebCore/storage/LocalStorage.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef LocalStorage_h
+#define LocalStorage_h
+
+#include "LocalStorageArea.h"
+#include "LocalStorageTask.h"
+#include "LocalStorageThread.h"
+#include "SecurityOriginHash.h"
+
+#include <wtf/HashMap.h>
+#include <wtf/HashSet.h>
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+ class PageGroup;
+ class StorageArea;
+
+ class LocalStorage : public ThreadSafeShared<LocalStorage> {
+ public:
+ ~LocalStorage();
+
+ static PassRefPtr<LocalStorage> localStorage(const String& path);
+
+ PassRefPtr<StorageArea> storageArea(Frame* sourceFrame, SecurityOrigin*);
+
+ bool scheduleImport(PassRefPtr<LocalStorageArea>);
+ void scheduleSync(PassRefPtr<LocalStorageArea>);
+
+ void close();
+
+ private:
+ LocalStorage(const String& path);
+
+ typedef HashMap<RefPtr<SecurityOrigin>, RefPtr<LocalStorageArea>, SecurityOriginHash> LocalStorageAreaMap;
+ LocalStorageAreaMap m_storageAreaMap;
+
+ RefPtr<LocalStorageThread> m_thread;
+
+ // The following members are subject to thread synchronization issues
+ public:
+ // To be called from the background thread:
+ String fullDatabaseFilename(SecurityOrigin*);
+
+ void performImport();
+ void performSync();
+
+ private:
+ String m_path;
+
+ typedef HashMap<RefPtr<SecurityOrigin>, unsigned long long, SecurityOriginHash> SecurityOriginQuoteMap;
+ SecurityOriginQuoteMap m_securityOriginQuoteMap;
+ };
+
+} // namespace WebCore
+
+#endif // LocalStorage_h
diff --git a/WebCore/storage/LocalStorageArea.cpp b/WebCore/storage/LocalStorageArea.cpp
new file mode 100644
index 0000000..56dc246
--- /dev/null
+++ b/WebCore/storage/LocalStorageArea.cpp
@@ -0,0 +1,416 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "LocalStorageArea.h"
+
+#include "CString.h"
+#include "EventNames.h"
+#include "Frame.h"
+#include "FrameTree.h"
+#include "LocalStorage.h"
+#include "LocalStorageTask.h"
+#include "LocalStorageThread.h"
+#include "Page.h"
+#include "PageGroup.h"
+#include "PlatformString.h"
+#include "SecurityOrigin.h"
+#include "SQLiteStatement.h"
+
+namespace WebCore {
+
+// If the LocalStorageArea undergoes rapid changes, don't sync each change to disk.
+// Instead, queue up a batch of items to sync and actually do the sync at the following interval.
+static const double LocalStorageSyncInterval = 1.0;
+
+LocalStorageArea::LocalStorageArea(SecurityOrigin* origin, LocalStorage* localStorage)
+ : StorageArea(origin)
+ , m_syncTimer(this, &LocalStorageArea::syncTimerFired)
+ , m_itemsCleared(false)
+ , m_finalSyncScheduled(false)
+ , m_localStorage(localStorage)
+ , m_clearItemsWhileSyncing(false)
+ , m_syncScheduled(false)
+ , m_importComplete(false)
+{
+ ASSERT(m_localStorage);
+
+ if (!m_localStorage->scheduleImport(this))
+ m_importComplete = true;
+}
+
+LocalStorageArea::~LocalStorageArea()
+{
+ ASSERT(!m_syncTimer.isActive());
+}
+
+void LocalStorageArea::scheduleFinalSync()
+{
+ m_syncTimer.stop();
+ syncTimerFired(&m_syncTimer);
+ m_finalSyncScheduled = true;
+}
+
+unsigned LocalStorageArea::length() const
+{
+ ASSERT(isMainThread());
+
+ if (m_importComplete)
+ return internalLength();
+
+ MutexLocker locker(m_importLock);
+ if (m_importComplete)
+ return internalLength();
+
+ while (!m_importComplete)
+ m_importCondition.wait(m_importLock);
+ ASSERT(m_importComplete);
+
+ return internalLength();
+}
+
+String LocalStorageArea::key(unsigned index, ExceptionCode& ec) const
+{
+ ASSERT(isMainThread());
+
+ if (m_importComplete)
+ return internalKey(index, ec);
+
+ MutexLocker locker(m_importLock);
+ if (m_importComplete)
+ return internalKey(index, ec);
+
+ while (!m_importComplete)
+ m_importCondition.wait(m_importLock);
+ ASSERT(m_importComplete);
+
+ return internalKey(index, ec);
+}
+
+String LocalStorageArea::getItem(const String& key) const
+{
+ ASSERT(isMainThread());
+
+ if (m_importComplete)
+ return internalGetItem(key);
+
+ MutexLocker locker(m_importLock);
+ if (m_importComplete)
+ return internalGetItem(key);
+
+ String item = internalGetItem(key);
+ if (!item.isNull())
+ return item;
+
+ while (!m_importComplete)
+ m_importCondition.wait(m_importLock);
+ ASSERT(m_importComplete);
+
+ return internalGetItem(key);
+}
+
+void LocalStorageArea::setItem(const String& key, const String& value, ExceptionCode& ec, Frame* frame)
+{
+ ASSERT(isMainThread());
+
+ if (m_importComplete) {
+ internalSetItem(key, value, ec, frame);
+ return;
+ }
+
+ MutexLocker locker(m_importLock);
+ internalSetItem(key, value, ec, frame);
+}
+
+void LocalStorageArea::removeItem(const String& key, Frame* frame)
+{
+ ASSERT(isMainThread());
+
+ if (m_importComplete) {
+ internalRemoveItem(key, frame);
+ return;
+ }
+
+ MutexLocker locker(m_importLock);
+ internalRemoveItem(key, frame);
+}
+
+bool LocalStorageArea::contains(const String& key) const
+{
+ ASSERT(isMainThread());
+
+ if (m_importComplete)
+ return internalContains(key);
+
+ MutexLocker locker(m_importLock);
+ if (m_importComplete)
+ return internalContains(key);
+
+ bool contained = internalContains(key);
+ if (contained)
+ return true;
+
+ while (!m_importComplete)
+ m_importCondition.wait(m_importLock);
+ ASSERT(m_importComplete);
+
+ return internalContains(key);
+}
+
+void LocalStorageArea::itemChanged(const String& key, const String& oldValue, const String& newValue, Frame* sourceFrame)
+{
+ ASSERT(isMainThread());
+
+ scheduleItemForSync(key, newValue);
+ dispatchStorageEvent(key, oldValue, newValue, sourceFrame);
+}
+
+void LocalStorageArea::itemRemoved(const String& key, const String& oldValue, Frame* sourceFrame)
+{
+ ASSERT(isMainThread());
+
+ scheduleItemForSync(key, String());
+ dispatchStorageEvent(key, oldValue, String(), sourceFrame);
+}
+
+void LocalStorageArea::areaCleared(Frame* sourceFrame)
+{
+ ASSERT(isMainThread());
+
+ scheduleClear();
+ dispatchStorageEvent(String(), String(), String(), sourceFrame);
+}
+
+void LocalStorageArea::dispatchStorageEvent(const String& key, const String& oldValue, const String& newValue, Frame* sourceFrame)
+{
+ ASSERT(isMainThread());
+
+ Page* page = sourceFrame->page();
+ if (!page)
+ return;
+
+ // Need to copy all relevant frames from every page to a vector, since sending the event to one frame might mutate the frame tree
+ // of any given page in the group, or mutate the page group itself
+ Vector<RefPtr<Frame> > frames;
+ const HashSet<Page*>& pages = page->group().pages();
+
+ HashSet<Page*>::const_iterator end = pages.end();
+ for (HashSet<Page*>::const_iterator it = pages.begin(); it != end; ++it) {
+ for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
+ if (Document* document = frame->document())
+ if (document->securityOrigin()->equal(securityOrigin()))
+ frames.append(frame);
+ }
+ }
+
+ for (unsigned i = 0; i < frames.size(); ++i) {
+ if (HTMLElement* body = frames[i]->document()->body())
+ body->dispatchStorageEvent(eventNames().storageEvent, key, oldValue, newValue, sourceFrame);
+ }
+}
+
+void LocalStorageArea::scheduleItemForSync(const String& key, const String& value)
+{
+ ASSERT(isMainThread());
+ ASSERT(!m_finalSyncScheduled);
+
+ m_changedItems.set(key, value);
+ if (!m_syncTimer.isActive())
+ m_syncTimer.startOneShot(LocalStorageSyncInterval);
+}
+
+void LocalStorageArea::scheduleClear()
+{
+ ASSERT(isMainThread());
+ ASSERT(!m_finalSyncScheduled);
+
+ m_changedItems.clear();
+ m_itemsCleared = true;
+ if (!m_syncTimer.isActive())
+ m_syncTimer.startOneShot(LocalStorageSyncInterval);
+}
+
+void LocalStorageArea::syncTimerFired(Timer<LocalStorageArea>*)
+{
+ ASSERT(isMainThread());
+
+ HashMap<String, String>::iterator it = m_changedItems.begin();
+ HashMap<String, String>::iterator end = m_changedItems.end();
+
+ {
+ MutexLocker locker(m_syncLock);
+
+ if (m_itemsCleared) {
+ m_itemsPendingSync.clear();
+ m_clearItemsWhileSyncing = true;
+ m_itemsCleared = false;
+ }
+
+ for (; it != end; ++it)
+ m_itemsPendingSync.set(it->first.copy(), it->second.copy());
+
+ if (!m_syncScheduled) {
+ m_syncScheduled = true;
+ m_localStorage->scheduleSync(this);
+ }
+ }
+
+ m_changedItems.clear();
+}
+
+void LocalStorageArea::performImport()
+{
+ ASSERT(!isMainThread());
+ ASSERT(!m_database.isOpen());
+
+ String databaseFilename = m_localStorage->fullDatabaseFilename(securityOrigin());
+
+ if (databaseFilename.isEmpty()) {
+ LOG_ERROR("Filename for local storage database is empty - cannot open for persistent storage");
+ markImported();
+ return;
+ }
+
+ if (!m_database.open(databaseFilename)) {
+ LOG_ERROR("Failed to open database file %s for local storage", databaseFilename.utf8().data());
+ markImported();
+ return;
+ }
+
+ if (!m_database.executeCommand("CREATE TABLE IF NOT EXISTS ItemTable (key TEXT UNIQUE ON CONFLICT REPLACE, value TEXT NOT NULL ON CONFLICT FAIL)")) {
+ LOG_ERROR("Failed to create table ItemTable for local storage");
+ markImported();
+ return;
+ }
+
+ SQLiteStatement query(m_database, "SELECT key, value FROM ItemTable");
+ if (query.prepare() != SQLResultOk) {
+ LOG_ERROR("Unable to select items from ItemTable for local storage");
+ markImported();
+ return;
+ }
+
+ HashMap<String, String> itemMap;
+
+ int result = query.step();
+ while (result == SQLResultRow) {
+ itemMap.set(query.getColumnText(0), query.getColumnText(1));
+ result = query.step();
+ }
+
+ if (result != SQLResultDone) {
+ LOG_ERROR("Error reading items from ItemTable for local storage");
+ markImported();
+ return;
+ }
+
+ MutexLocker locker(m_importLock);
+
+ HashMap<String, String>::iterator it = itemMap.begin();
+ HashMap<String, String>::iterator end = itemMap.end();
+
+ for (; it != end; ++it)
+ importItem(it->first, it->second);
+
+ m_importComplete = true;
+ m_importCondition.signal();
+}
+
+void LocalStorageArea::markImported()
+{
+ ASSERT(!isMainThread());
+
+ MutexLocker locker(m_importLock);
+ m_importComplete = true;
+ m_importCondition.signal();
+}
+
+void LocalStorageArea::performSync()
+{
+ ASSERT(!isMainThread());
+
+ if (!m_database.isOpen())
+ return;
+
+ HashMap<String, String> items;
+ bool clearFirst = false;
+ {
+ MutexLocker locker(m_syncLock);
+ m_itemsPendingSync.swap(items);
+ clearFirst = m_clearItemsWhileSyncing;
+ m_clearItemsWhileSyncing = false;
+ m_syncScheduled = false;
+ }
+
+ // If the clear flag is marked, then we clear all items out before we write any new ones in
+ if (clearFirst) {
+ SQLiteStatement clear(m_database, "DELETE FROM ItemTable");
+ if (clear.prepare() != SQLResultOk) {
+ LOG_ERROR("Failed to prepare clear statement - cannot write to local storage database");
+ return;
+ }
+
+ int result = clear.step();
+ if (result != SQLResultDone) {
+ LOG_ERROR("Failed to clear all items in the local storage database - %i", result);
+ return;
+ }
+ }
+
+ SQLiteStatement insert(m_database, "INSERT INTO ItemTable VALUES (?, ?)");
+ if (insert.prepare() != SQLResultOk) {
+ LOG_ERROR("Failed to prepare insert statement - cannot write to local storage database");
+ return;
+ }
+
+ SQLiteStatement remove(m_database, "DELETE FROM ItemTable WHERE key=?");
+ if (remove.prepare() != SQLResultOk) {
+ LOG_ERROR("Failed to prepare delete statement - cannot write to local storage database");
+ return;
+ }
+
+ HashMap<String, String>::iterator end = items.end();
+
+ for (HashMap<String, String>::iterator it = items.begin(); it != end; ++it) {
+ // Based on the null-ness of the second argument, decide whether this is an insert or a delete
+ SQLiteStatement& query = it->second.isNull() ? remove : insert;
+
+ query.bindText(1, it->first);
+
+ // If the second argument is non-null, we're doing an insert, so bind it as the value.
+ if (!it->second.isNull())
+ query.bindText(2, it->second);
+
+ int result = query.step();
+ if (result != SQLResultDone) {
+ LOG_ERROR("Failed to update item in the local storage database - %i", result);
+ break;
+ }
+
+ query.reset();
+ }
+}
+
+} // namespace WebCore
diff --git a/WebCore/storage/LocalStorageArea.h b/WebCore/storage/LocalStorageArea.h
new file mode 100644
index 0000000..c6e7015
--- /dev/null
+++ b/WebCore/storage/LocalStorageArea.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef LocalStorageArea_h
+#define LocalStorageArea_h
+
+#include "LocalStorageTask.h"
+#include "LocalStorageThread.h"
+
+#include "SQLiteDatabase.h"
+#include "StorageArea.h"
+#include "StringHash.h"
+#include "Timer.h"
+
+#include <wtf/HashMap.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+ class LocalStorage;
+
+ class LocalStorageArea : public StorageArea {
+ public:
+ virtual ~LocalStorageArea();
+
+ static PassRefPtr<LocalStorageArea> create(SecurityOrigin* origin, LocalStorage* localStorage) { return adoptRef(new LocalStorageArea(origin, localStorage)); }
+
+ void scheduleFinalSync();
+
+ private:
+ LocalStorageArea(SecurityOrigin*, LocalStorage*);
+
+ virtual void itemChanged(const String& key, const String& oldValue, const String& newValue, Frame* sourceFrame);
+ virtual void itemRemoved(const String& key, const String& oldValue, Frame* sourceFrame);
+ virtual void areaCleared(Frame* sourceFrame);
+
+ void scheduleItemForSync(const String& key, const String& value);
+ void scheduleClear();
+ void dispatchStorageEvent(const String& key, const String& oldValue, const String& newValue, Frame* sourceFrame);
+
+ Timer<LocalStorageArea> m_syncTimer;
+ HashMap<String, String> m_changedItems;
+ bool m_itemsCleared;
+
+ bool m_finalSyncScheduled;
+
+ LocalStorage* m_localStorage;
+
+ // The database handle will only ever be opened and used on the background thread.
+ SQLiteDatabase m_database;
+
+ // The following members are subject to thread synchronization issues.
+ public:
+ // Called on the main thread
+ virtual unsigned length() const;
+ virtual String key(unsigned index, ExceptionCode&) const;
+ virtual String getItem(const String&) const;
+ virtual void setItem(const String& key, const String& value, ExceptionCode&, Frame* sourceFrame);
+ virtual void removeItem(const String&, Frame* sourceFrame);
+ virtual bool contains(const String& key) const;
+
+ // Called from the background thread
+ virtual void performImport();
+ virtual void performSync();
+
+ private:
+ void syncTimerFired(Timer<LocalStorageArea>*);
+
+ Mutex m_syncLock;
+ HashMap<String, String> m_itemsPendingSync;
+ bool m_clearItemsWhileSyncing;
+ bool m_syncScheduled;
+
+ mutable Mutex m_importLock;
+ mutable ThreadCondition m_importCondition;
+ mutable bool m_importComplete;
+ void markImported();
+ };
+
+} // namespace WebCore
+
+#endif // LocalStorageArea_h
diff --git a/WebCore/storage/LocalStorageTask.cpp b/WebCore/storage/LocalStorageTask.cpp
new file mode 100644
index 0000000..3d5b987
--- /dev/null
+++ b/WebCore/storage/LocalStorageTask.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "LocalStorageTask.h"
+
+#include "LocalStorage.h"
+#include "LocalStorageArea.h"
+#include "LocalStorageThread.h"
+
+namespace WebCore {
+
+LocalStorageTask::LocalStorageTask(Type type, PassRefPtr<LocalStorageArea> area)
+ : m_type(type)
+ , m_area(area)
+{
+ ASSERT(m_area);
+ ASSERT(m_type == AreaImport || m_type == AreaSync);
+}
+
+LocalStorageTask::LocalStorageTask(Type type, PassRefPtr<LocalStorage> storage)
+ : m_type(type)
+ , m_storage(storage)
+{
+ ASSERT(m_storage);
+ ASSERT(m_type == StorageImport || m_type == StorageSync);
+}
+
+LocalStorageTask::LocalStorageTask(Type type, PassRefPtr<LocalStorageThread> thread)
+ : m_type(type)
+ , m_thread(thread)
+{
+ ASSERT(m_thread);
+ ASSERT(m_type == TerminateThread);
+}
+
+void LocalStorageTask::performTask()
+{
+ switch (m_type) {
+ case StorageImport:
+ ASSERT(m_storage);
+ m_storage->performImport();
+ break;
+ case StorageSync:
+ ASSERT(m_storage);
+ m_storage->performSync();
+ break;
+ case AreaImport:
+ ASSERT(m_area);
+ m_area->performImport();
+ break;
+ case AreaSync:
+ ASSERT(m_area);
+ m_area->performSync();
+ break;
+ case TerminateThread:
+ m_thread->performTerminate();
+ break;
+ }
+}
+
+}
diff --git a/WebCore/storage/LocalStorageTask.h b/WebCore/storage/LocalStorageTask.h
new file mode 100644
index 0000000..79063d0
--- /dev/null
+++ b/WebCore/storage/LocalStorageTask.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef LocalStorageTask_h
+#define LocalStorageTask_h
+
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+ class LocalStorage;
+ class LocalStorageArea;
+ class LocalStorageThread;
+
+ class LocalStorageTask : public ThreadSafeShared<LocalStorageTask> {
+ public:
+ enum Type { StorageImport, StorageSync, AreaImport, AreaSync, TerminateThread };
+
+ static PassRefPtr<LocalStorageTask> createImport(PassRefPtr<LocalStorage> storage) { return adoptRef(new LocalStorageTask(StorageImport, storage)); }
+ static PassRefPtr<LocalStorageTask> createImport(PassRefPtr<LocalStorageArea> area) { return adoptRef(new LocalStorageTask(AreaImport, area)); }
+ static PassRefPtr<LocalStorageTask> createSync(PassRefPtr<LocalStorage> storage) { return adoptRef(new LocalStorageTask(StorageSync, storage)); }
+ static PassRefPtr<LocalStorageTask> createSync(PassRefPtr<LocalStorageArea> area) { return adoptRef(new LocalStorageTask(AreaSync, area)); }
+ static PassRefPtr<LocalStorageTask> createTerminate(PassRefPtr<LocalStorageThread> thread) { return adoptRef(new LocalStorageTask(TerminateThread, thread)); }
+
+ void performTask();
+
+ private:
+ LocalStorageTask(Type, PassRefPtr<LocalStorageArea>);
+ LocalStorageTask(Type, PassRefPtr<LocalStorage>);
+ LocalStorageTask(Type, PassRefPtr<LocalStorageThread>);
+
+ Type m_type;
+ RefPtr<LocalStorageArea> m_area;
+ RefPtr<LocalStorage> m_storage;
+ RefPtr<LocalStorageThread> m_thread;
+ };
+
+} // namespace WebCore
+
+#endif // LocalStorageTask_h
diff --git a/WebCore/storage/LocalStorageThread.cpp b/WebCore/storage/LocalStorageThread.cpp
new file mode 100644
index 0000000..8f1829c
--- /dev/null
+++ b/WebCore/storage/LocalStorageThread.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "LocalStorageThread.h"
+
+#include "LocalStorage.h"
+#include "LocalStorageArea.h"
+#include "LocalStorageTask.h"
+
+namespace WebCore {
+
+PassRefPtr<LocalStorageThread> LocalStorageThread::create()
+{
+ return adoptRef(new LocalStorageThread);
+}
+
+LocalStorageThread::LocalStorageThread()
+ : m_threadID(0)
+{
+ m_selfRef = this;
+}
+
+bool LocalStorageThread::start()
+{
+ if (m_threadID)
+ return true;
+
+ m_threadID = createThread(LocalStorageThread::localStorageThreadStart, this, "WebCore::LocalStorage");
+
+ return m_threadID;
+}
+
+void* LocalStorageThread::localStorageThreadStart(void* thread)
+{
+ return static_cast<LocalStorageThread*>(thread)->localStorageThread();
+}
+
+void* LocalStorageThread::localStorageThread()
+{
+ while (true) {
+ RefPtr<LocalStorageTask> task;
+ if (!m_queue.waitForMessage(task))
+ break;
+
+ task->performTask();
+ }
+
+ // Detach the thread so its resources are no longer of any concern to anyone else
+ detachThread(m_threadID);
+ m_threadID = 0;
+
+ // Clear the self refptr, possibly resulting in deletion
+ m_selfRef = 0;
+
+ return 0;
+}
+
+void LocalStorageThread::scheduleImport(PassRefPtr<LocalStorage> storage)
+{
+ ASSERT(!m_queue.killed() && m_threadID);
+ m_queue.append(LocalStorageTask::createImport(storage));
+}
+
+void LocalStorageThread::scheduleSync(PassRefPtr<LocalStorage> storage)
+{
+ ASSERT(!m_queue.killed() && m_threadID);
+ m_queue.append(LocalStorageTask::createSync(storage));
+}
+
+void LocalStorageThread::scheduleImport(PassRefPtr<LocalStorageArea> area)
+{
+ ASSERT(!m_queue.killed() && m_threadID);
+ m_queue.append(LocalStorageTask::createImport(area));
+}
+
+void LocalStorageThread::scheduleSync(PassRefPtr<LocalStorageArea> area)
+{
+ ASSERT(!m_queue.killed() && m_threadID);
+ m_queue.append(LocalStorageTask::createSync(area));
+}
+
+void LocalStorageThread::terminate()
+{
+ ASSERT(isMainThread());
+
+ // Ideally we'd never be killing a thread that wasn't live, so ASSERT it.
+ // But if we do in a release build, make sure to not wait on a condition that will never get signalled
+ ASSERT(!m_queue.killed() && m_threadID);
+ if (!m_threadID)
+ return;
+
+ MutexLocker locker(m_terminateLock);
+
+ m_queue.append(LocalStorageTask::createTerminate(this));
+
+ m_terminateCondition.wait(m_terminateLock);
+}
+
+void LocalStorageThread::performTerminate()
+{
+ ASSERT(!isMainThread());
+
+ m_queue.kill();
+
+ MutexLocker locker(m_terminateLock);
+ m_terminateCondition.signal();
+}
+
+}
diff --git a/WebCore/storage/LocalStorageThread.h b/WebCore/storage/LocalStorageThread.h
new file mode 100644
index 0000000..fcca712
--- /dev/null
+++ b/WebCore/storage/LocalStorageThread.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef LocalStorageThread_h
+#define LocalStorageThread_h
+
+#include <wtf/HashSet.h>
+#include <wtf/MessageQueue.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+ class LocalStorage;
+ class LocalStorageArea;
+ class LocalStorageTask;
+
+ class LocalStorageThread : public ThreadSafeShared<LocalStorageThread> {
+ public:
+ static PassRefPtr<LocalStorageThread> create();
+
+ bool start();
+
+ void scheduleImport(PassRefPtr<LocalStorage>);
+ void scheduleSync(PassRefPtr<LocalStorage>);
+ void scheduleImport(PassRefPtr<LocalStorageArea>);
+ void scheduleSync(PassRefPtr<LocalStorageArea>);
+
+ // Called from the main thread to synchronously shut down this thread
+ void terminate();
+ // Background thread part of the terminate procedure
+ void performTerminate();
+
+ private:
+ LocalStorageThread();
+
+ static void* localStorageThreadStart(void*);
+ void* localStorageThread();
+
+ ThreadIdentifier m_threadID;
+ RefPtr<LocalStorageThread> m_selfRef;
+
+ MessageQueue<RefPtr<LocalStorageTask> > m_queue;
+
+ Mutex m_terminateLock;
+ ThreadCondition m_terminateCondition;
+ };
+
+} // namespace WebCore
+
+#endif // LocalStorageThread_h
diff --git a/WebCore/storage/OriginQuotaManager.cpp b/WebCore/storage/OriginQuotaManager.cpp
new file mode 100644
index 0000000..fa2a580
--- /dev/null
+++ b/WebCore/storage/OriginQuotaManager.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "config.h"
+#include "OriginQuotaManager.h"
+
+#include "Database.h"
+#include "OriginUsageRecord.h"
+
+namespace WebCore {
+
+OriginQuotaManager::OriginQuotaManager()
+#ifndef NDEBUG
+ : m_usageRecordGuardLocked(false)
+#endif
+{
+}
+
+void OriginQuotaManager::lock()
+{
+ m_usageRecordGuard.lock();
+#ifndef NDEBUG
+ m_usageRecordGuardLocked = true;
+#endif
+}
+
+void OriginQuotaManager::unlock()
+{
+#ifndef NDEBUG
+ m_usageRecordGuardLocked = false;
+#endif
+ m_usageRecordGuard.unlock();
+}
+
+void OriginQuotaManager::trackOrigin(PassRefPtr<SecurityOrigin> origin)
+{
+ ASSERT(m_usageRecordGuardLocked);
+ ASSERT(!m_usageMap.contains(origin.get()));
+
+ m_usageMap.set(origin, new OriginUsageRecord);
+}
+
+bool OriginQuotaManager::tracksOrigin(SecurityOrigin* origin) const
+{
+ ASSERT(m_usageRecordGuardLocked);
+ return m_usageMap.contains(origin);
+}
+
+void OriginQuotaManager::addDatabase(SecurityOrigin* origin, const String& databaseIdentifier, const String& fullPath)
+{
+ ASSERT(m_usageRecordGuardLocked);
+
+ OriginUsageRecord* usageRecord = m_usageMap.get(origin);
+ ASSERT(usageRecord);
+
+ usageRecord->addDatabase(databaseIdentifier.copy(), fullPath.copy());
+}
+
+void OriginQuotaManager::removeDatabase(SecurityOrigin* origin, const String& databaseIdentifier)
+{
+ ASSERT(m_usageRecordGuardLocked);
+
+ if (OriginUsageRecord* usageRecord = m_usageMap.get(origin))
+ usageRecord->removeDatabase(databaseIdentifier);
+}
+
+void OriginQuotaManager::removeOrigin(SecurityOrigin* origin)
+{
+ ASSERT(m_usageRecordGuardLocked);
+
+ if (OriginUsageRecord* usageRecord = m_usageMap.get(origin)) {
+ m_usageMap.remove(origin);
+ delete usageRecord;
+ }
+}
+
+void OriginQuotaManager::markDatabase(Database* database)
+{
+ ASSERT(database);
+ ASSERT(m_usageRecordGuardLocked);
+ RefPtr<SecurityOrigin> origin = database->securityOriginCopy();
+ OriginUsageRecord* usageRecord = m_usageMap.get(origin);
+ ASSERT(usageRecord);
+
+ usageRecord->markDatabase(database->stringIdentifier());
+}
+
+unsigned long long OriginQuotaManager::diskUsage(SecurityOrigin* origin) const
+{
+ ASSERT(m_usageRecordGuardLocked);
+
+ OriginUsageRecord* usageRecord = m_usageMap.get(origin);
+ ASSERT(usageRecord);
+
+ return usageRecord->diskUsage();
+}
+
+
+}
diff --git a/WebCore/storage/OriginQuotaManager.h b/WebCore/storage/OriginQuotaManager.h
new file mode 100644
index 0000000..81a29e5
--- /dev/null
+++ b/WebCore/storage/OriginQuotaManager.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef OriginQuotaManager_h
+#define OriginQuotaManager_h
+
+#include "StringHash.h"
+#include "SecurityOriginHash.h"
+#include <wtf/HashMap.h>
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+class Database;
+class OriginUsageRecord;
+
+class OriginQuotaManager : public Noncopyable {
+public:
+ OriginQuotaManager();
+
+ void lock();
+ void unlock();
+
+ void trackOrigin(PassRefPtr<SecurityOrigin>);
+ bool tracksOrigin(SecurityOrigin*) const;
+ void addDatabase(SecurityOrigin*, const String& databaseIdentifier, const String& fullPath);
+ void removeDatabase(SecurityOrigin*, const String& databaseIdentifier);
+ void removeOrigin(SecurityOrigin*);
+
+ void markDatabase(Database*); // Mark dirtiness of a specific database.
+ unsigned long long diskUsage(SecurityOrigin*) const;
+
+private:
+ mutable Mutex m_usageRecordGuard;
+#ifndef NDEBUG
+ bool m_usageRecordGuardLocked;
+#endif
+
+ typedef HashMap<RefPtr<SecurityOrigin>, OriginUsageRecord*, SecurityOriginHash> OriginUsageMap;
+ OriginUsageMap m_usageMap;
+};
+
+} // namespace WebCore
+
+#endif // OriginQuotaManager_h
diff --git a/WebCore/storage/OriginUsageRecord.cpp b/WebCore/storage/OriginUsageRecord.cpp
new file mode 100644
index 0000000..d7a7b11
--- /dev/null
+++ b/WebCore/storage/OriginUsageRecord.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "config.h"
+#include "OriginUsageRecord.h"
+
+#include "FileSystem.h"
+
+namespace WebCore {
+
+OriginUsageRecord::OriginUsageRecord()
+ : m_cachedDiskUsageIsValid(false)
+{
+}
+
+void OriginUsageRecord::addDatabase(const String& identifier, const String& fullPath)
+{
+ ASSERT(!m_databaseMap.contains(identifier));
+ ASSERT_ARG(identifier, identifier.impl()->refCount() == 1);
+ ASSERT_ARG(fullPath, fullPath.impl()->refCount() == 1);
+
+ m_databaseMap.set(identifier, DatabaseEntry(fullPath));
+ m_unknownSet.add(identifier);
+
+ m_cachedDiskUsageIsValid = false;
+}
+
+void OriginUsageRecord::removeDatabase(const String& identifier)
+{
+ ASSERT(m_databaseMap.contains(identifier));
+
+ m_databaseMap.remove(identifier);
+ m_unknownSet.remove(identifier);
+ m_cachedDiskUsageIsValid = false;
+}
+
+void OriginUsageRecord::markDatabase(const String& identifier)
+{
+ ASSERT(m_databaseMap.contains(identifier));
+ ASSERT_ARG(identifier, identifier.impl()->refCount() == 1);
+
+ m_unknownSet.add(identifier);
+ m_cachedDiskUsageIsValid = false;
+}
+
+unsigned long long OriginUsageRecord::diskUsage()
+{
+ // Use the last cached usage value if we have it.
+ if (m_cachedDiskUsageIsValid)
+ return m_cachedDiskUsage;
+
+ // stat() for the sizes known to be dirty.
+ HashSet<String>::iterator iUnknown = m_unknownSet.begin();
+ HashSet<String>::iterator endUnknown = m_unknownSet.end();
+ for (; iUnknown != endUnknown; ++iUnknown) {
+ const String& path = m_databaseMap.get(*iUnknown).filename;
+ ASSERT(!path.isEmpty());
+
+ long long size;
+ if (getFileSize(path, size))
+ m_databaseMap.set(*iUnknown, DatabaseEntry(path, size));
+ else {
+ // When we can't determine the file size, we'll just have to assume the file is missing/inaccessible.
+ m_databaseMap.set(*iUnknown, DatabaseEntry(path, 0));
+ }
+ }
+ m_unknownSet.clear();
+
+ // Recalculate the cached usage value.
+ m_cachedDiskUsage = 0;
+ HashMap<String, DatabaseEntry>::iterator iDatabase = m_databaseMap.begin();
+ HashMap<String, DatabaseEntry>::iterator endDatabase = m_databaseMap.end();
+ for (; iDatabase != endDatabase; ++iDatabase)
+ m_cachedDiskUsage += iDatabase->second.size;
+
+ m_cachedDiskUsageIsValid = true;
+ return m_cachedDiskUsage;
+}
+
+}
diff --git a/WebCore/storage/OriginUsageRecord.h b/WebCore/storage/OriginUsageRecord.h
new file mode 100644
index 0000000..094ce3d
--- /dev/null
+++ b/WebCore/storage/OriginUsageRecord.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef OriginUsageRecord_h
+#define OriginUsageRecord_h
+
+#include "PlatformString.h"
+#include "StringHash.h"
+
+#include <wtf/HashMap.h>
+#include <wtf/HashSet.h>
+
+namespace WebCore {
+
+// Objects of this class can be used from multiple threads with external synchronization.
+// String arguments are also supposed to be deeply copied by the caller when necessary.
+class OriginUsageRecord {
+public:
+ OriginUsageRecord();
+
+ void addDatabase(const String& identifier, const String& fullPath);
+ void removeDatabase(const String& identifier);
+ void markDatabase(const String& identifier); // Size may have changed, and will need to be recalculated.
+ unsigned long long diskUsage();
+
+private:
+ struct DatabaseEntry {
+ DatabaseEntry() { }
+ DatabaseEntry(const String& filename) : filename(filename) { }
+ DatabaseEntry(const String& filename, unsigned long long size) : filename(filename), size(size) { }
+ String filename;
+ unsigned long long size; // This member remains uninitialized until disk usage is calculated.
+ };
+ HashMap<String, DatabaseEntry> m_databaseMap;
+ HashSet<String> m_unknownSet;
+
+ unsigned long long m_cachedDiskUsage;
+ bool m_cachedDiskUsageIsValid;
+};
+
+} // namespace WebCore
+
+#endif
diff --git a/WebCore/storage/SQLError.h b/WebCore/storage/SQLError.h
new file mode 100644
index 0000000..69c1228
--- /dev/null
+++ b/WebCore/storage/SQLError.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SQLError_h
+#define SQLError_h
+
+#include "PlatformString.h"
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+class SQLError : public ThreadSafeShared<SQLError> {
+public:
+ static PassRefPtr<SQLError> create(unsigned code, const String& message) { return adoptRef(new SQLError(code, message)); }
+
+ unsigned code() const { return m_code; }
+ String message() const { return m_message.copy(); }
+
+private:
+ SQLError(unsigned code, const String& message) : m_code(code), m_message(message.copy()) { }
+ unsigned m_code;
+ String m_message;
+};
+
+}
+
+#endif // SQLError_h
diff --git a/WebCore/storage/SQLError.idl b/WebCore/storage/SQLError.idl
new file mode 100644
index 0000000..188b9db
--- /dev/null
+++ b/WebCore/storage/SQLError.idl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+module storage {
+
+ interface SQLError {
+ readonly attribute unsigned int code;
+ readonly attribute DOMString message;
+ };
+}
diff --git a/WebCore/storage/SQLResultSet.cpp b/WebCore/storage/SQLResultSet.cpp
new file mode 100644
index 0000000..37024a1
--- /dev/null
+++ b/WebCore/storage/SQLResultSet.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "SQLResultSet.h"
+
+#include "ExceptionCode.h"
+#include "SQLValue.h"
+
+namespace WebCore {
+
+static unsigned const MaxErrorCode = 2;
+
+SQLResultSet::SQLResultSet()
+ : m_rows(SQLResultSetRowList::create())
+ , m_insertId(0)
+ , m_insertIdSet(false)
+ , m_rowsAffected(0)
+{
+}
+
+int64_t SQLResultSet::insertId(ExceptionCode& e) const
+{
+ // 4.11.4 - Return the id of the last row inserted as a result of the query
+ // If the query didn't result in any rows being added, raise an INVALID_ACCESS_ERR exception
+ if (m_insertIdSet)
+ return m_insertId;
+
+ e = INVALID_ACCESS_ERR;
+ return -1;
+}
+
+int SQLResultSet::rowsAffected() const
+{
+ return m_rowsAffected;
+}
+
+SQLResultSetRowList* SQLResultSet::rows() const
+{
+ return m_rows.get();
+}
+
+void SQLResultSet::setInsertId(int64_t id)
+{
+ ASSERT(!m_insertIdSet);
+
+ m_insertId = id;
+ m_insertIdSet = true;
+}
+
+void SQLResultSet::setRowsAffected(int count)
+{
+ m_rowsAffected = count;
+}
+
+}
diff --git a/WebCore/storage/SQLResultSet.h b/WebCore/storage/SQLResultSet.h
new file mode 100644
index 0000000..58f13b7
--- /dev/null
+++ b/WebCore/storage/SQLResultSet.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SQLResultSet_h
+#define SQLResultSet_h
+
+#include "SQLResultSetRowList.h"
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+typedef int ExceptionCode;
+
+class SQLResultSet : public ThreadSafeShared<SQLResultSet> {
+public:
+ static PassRefPtr<SQLResultSet> create() { return adoptRef(new SQLResultSet); }
+
+ SQLResultSetRowList* rows() const;
+
+ int64_t insertId(ExceptionCode&) const;
+ int rowsAffected() const;
+
+// For internal (non-JS) use
+ void setInsertId(int64_t);
+ void setRowsAffected(int);
+
+private:
+ SQLResultSet();
+
+ RefPtr<SQLResultSetRowList> m_rows;
+ int64_t m_insertId;
+ bool m_insertIdSet;
+ int m_rowsAffected;
+};
+
+} // namespace WebCore
+
+#endif // SQLResultSet_h
diff --git a/WebCore/storage/SQLResultSet.idl b/WebCore/storage/SQLResultSet.idl
new file mode 100644
index 0000000..fbad936
--- /dev/null
+++ b/WebCore/storage/SQLResultSet.idl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+module storage {
+
+ interface SQLResultSet {
+ readonly attribute SQLResultSetRowList rows;
+
+ readonly attribute int insertId
+ getter raises(DOMException);
+ readonly attribute int rowsAffected;
+ };
+}
diff --git a/WebCore/storage/SQLResultSetRowList.cpp b/WebCore/storage/SQLResultSetRowList.cpp
new file mode 100644
index 0000000..5b5b943
--- /dev/null
+++ b/WebCore/storage/SQLResultSetRowList.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "SQLResultSetRowList.h"
+
+namespace WebCore {
+
+unsigned SQLResultSetRowList::length() const
+{
+ if (m_result.size() == 0)
+ return 0;
+
+ ASSERT(m_result.size() % m_columns.size() == 0);
+
+ return m_result.size() / m_columns.size();
+}
+
+}
diff --git a/WebCore/storage/SQLResultSetRowList.h b/WebCore/storage/SQLResultSetRowList.h
new file mode 100644
index 0000000..1b8a8b4
--- /dev/null
+++ b/WebCore/storage/SQLResultSetRowList.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SQLResultSetRowList_h
+#define SQLResultSetRowList_h
+
+#include <wtf/PassRefPtr.h>
+#include "SQLValue.h"
+
+namespace WebCore {
+
+class SQLResultSetRowList : public RefCounted<SQLResultSetRowList> {
+public:
+ static PassRefPtr<SQLResultSetRowList> create() { return adoptRef(new SQLResultSetRowList); }
+
+ const Vector<String>& columnNames() const { return m_columns; }
+ const Vector<SQLValue>& values() const { return m_result; }
+
+ void addColumn(const String& name) { m_columns.append(name); }
+ void addResult(const SQLValue& result) { m_result.append(result); }
+
+ unsigned length() const;
+
+private:
+ SQLResultSetRowList() { }
+
+ Vector<String> m_columns;
+ Vector<SQLValue> m_result;
+};
+
+}
+
+#endif
diff --git a/WebCore/storage/SQLResultSetRowList.idl b/WebCore/storage/SQLResultSetRowList.idl
new file mode 100644
index 0000000..f853e90
--- /dev/null
+++ b/WebCore/storage/SQLResultSetRowList.idl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+module storage {
+
+ interface SQLResultSetRowList {
+ readonly attribute unsigned long length;
+ [Custom] DOMObject item(in unsigned long index);
+ };
+}
diff --git a/WebCore/storage/SQLStatement.cpp b/WebCore/storage/SQLStatement.cpp
new file mode 100644
index 0000000..9bd62dd
--- /dev/null
+++ b/WebCore/storage/SQLStatement.cpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "config.h"
+#include "SQLStatement.h"
+
+#include "Database.h"
+#include "DatabaseAuthorizer.h"
+#include "Logging.h"
+#include "SQLError.h"
+#include "SQLiteDatabase.h"
+#include "SQLiteStatement.h"
+#include "SQLResultSet.h"
+#include "SQLStatementCallback.h"
+#include "SQLStatementErrorCallback.h"
+#include "SQLTransaction.h"
+#include "SQLValue.h"
+
+namespace WebCore {
+
+PassRefPtr<SQLStatement> SQLStatement::create(const String& statement, const Vector<SQLValue>& arguments, PassRefPtr<SQLStatementCallback> callback, PassRefPtr<SQLStatementErrorCallback> errorCallback)
+{
+ return adoptRef(new SQLStatement(statement, arguments, callback, errorCallback));
+}
+
+SQLStatement::SQLStatement(const String& statement, const Vector<SQLValue>& arguments, PassRefPtr<SQLStatementCallback> callback, PassRefPtr<SQLStatementErrorCallback> errorCallback)
+ : m_statement(statement.copy())
+ , m_arguments(arguments)
+ , m_statementCallback(callback)
+ , m_statementErrorCallback(errorCallback)
+{
+}
+
+bool SQLStatement::execute(Database* db)
+{
+ ASSERT(!m_resultSet);
+
+ // If we're re-running this statement after a quota violation, we need to clear that error now
+ clearFailureDueToQuota();
+
+ // This transaction might have been marked bad while it was being set up on the main thread,
+ // so if there is still an error, return false.
+ if (m_error)
+ return false;
+
+ SQLiteDatabase* database = &db->m_sqliteDatabase;
+
+ SQLiteStatement statement(*database, m_statement);
+ int result = statement.prepare();
+
+ if (result != SQLResultOk) {
+ LOG(StorageAPI, "Unable to verify correctness of statement %s - error %i (%s)", m_statement.ascii().data(), result, database->lastErrorMsg());
+ m_error = SQLError::create(1, database->lastErrorMsg());
+ return false;
+ }
+
+ // FIXME: If the statement uses the ?### syntax supported by sqlite, the bind parameter count is very likely off from the number of question marks.
+ // If this is the case, they might be trying to do something fishy or malicious
+ if (statement.bindParameterCount() != m_arguments.size()) {
+ LOG(StorageAPI, "Bind parameter count doesn't match number of question marks");
+ m_error = SQLError::create(1, "number of '?'s in statement string does not match argument count");
+ return false;
+ }
+
+ for (unsigned i = 0; i < m_arguments.size(); ++i) {
+ result = statement.bindValue(i + 1, m_arguments[i]);
+ if (result == SQLResultFull) {
+ setFailureDueToQuota();
+ return false;
+ }
+
+ if (result != SQLResultOk) {
+ LOG(StorageAPI, "Failed to bind value index %i to statement for query '%s'", i + 1, m_statement.ascii().data());
+ m_error = SQLError::create(1, database->lastErrorMsg());
+ return false;
+ }
+ }
+
+ RefPtr<SQLResultSet> resultSet = SQLResultSet::create();
+
+ // Step so we can fetch the column names.
+ result = statement.step();
+ if (result == SQLResultRow) {
+ int columnCount = statement.columnCount();
+ SQLResultSetRowList* rows = resultSet->rows();
+
+ for (int i = 0; i < columnCount; i++)
+ rows->addColumn(statement.getColumnName(i));
+
+ do {
+ for (int i = 0; i < columnCount; i++)
+ rows->addResult(statement.getColumnValue(i));
+
+ result = statement.step();
+ } while (result == SQLResultRow);
+
+ if (result != SQLResultDone) {
+ m_error = SQLError::create(1, database->lastErrorMsg());
+ return false;
+ }
+ } else if (result == SQLResultDone) {
+ // Didn't find anything, or was an insert
+ if (db->m_databaseAuthorizer->lastActionWasInsert())
+ resultSet->setInsertId(database->lastInsertRowID());
+ } else if (result == SQLResultFull) {
+ // Return the Quota error - the delegate will be asked for more space and this statement might be re-run
+ setFailureDueToQuota();
+ return false;
+ } else {
+ m_error = SQLError::create(1, database->lastErrorMsg());
+ return false;
+ }
+
+ // FIXME: If the spec allows triggers, and we want to be "accurate" in a different way, we'd use
+ // sqlite3_total_changes() here instead of sqlite3_changed, because that includes rows modified from within a trigger
+ // For now, this seems sufficient
+ resultSet->setRowsAffected(database->lastChanges());
+
+ m_resultSet = resultSet;
+ return true;
+}
+
+void SQLStatement::setDatabaseDeletedError()
+{
+ ASSERT(!m_error && !m_resultSet);
+ m_error = SQLError::create(0, "unable to execute statement, because the user deleted the database");
+}
+
+void SQLStatement::setVersionMismatchedError()
+{
+ ASSERT(!m_error && !m_resultSet);
+ m_error = SQLError::create(2, "current version of the database and `oldVersion` argument do not match");
+}
+
+bool SQLStatement::performCallback(SQLTransaction* transaction)
+{
+ ASSERT(transaction);
+
+ bool callbackError = false;
+
+ // Call the appropriate statement callback and track if it resulted in an error,
+ // because then we need to jump to the transaction error callback.
+ if (m_error) {
+ ASSERT(m_statementErrorCallback);
+ callbackError = m_statementErrorCallback->handleEvent(transaction, m_error.get());
+ } else if (m_statementCallback)
+ m_statementCallback->handleEvent(transaction, m_resultSet.get(), callbackError);
+
+ // Now release our callbacks, to break reference cycles.
+ m_statementCallback = 0;
+ m_statementErrorCallback = 0;
+
+ return callbackError;
+}
+
+void SQLStatement::setFailureDueToQuota()
+{
+ ASSERT(!m_error && !m_resultSet);
+ m_error = SQLError::create(4, "there was not enough remaining storage space, or the storage quota was reached and the user declined to allow more space");
+}
+
+void SQLStatement::clearFailureDueToQuota()
+{
+ if (lastExecutionFailedDueToQuota())
+ m_error = 0;
+}
+
+bool SQLStatement::lastExecutionFailedDueToQuota() const
+{
+ return m_error && m_error->code() == 4;
+}
+
+} // namespace WebCore
diff --git a/WebCore/storage/SQLStatement.h b/WebCore/storage/SQLStatement.h
new file mode 100644
index 0000000..e72fcf7
--- /dev/null
+++ b/WebCore/storage/SQLStatement.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef SQLStatement_h
+#define SQLStatement_h
+
+#include "PlatformString.h"
+
+#include "SQLError.h"
+#include "SQLResultSet.h"
+#include "SQLStatementCallback.h"
+#include "SQLStatementErrorCallback.h"
+#include "SQLValue.h"
+
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Threading.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class Database;
+class SQLTransaction;
+class String;
+
+class SQLStatement : public ThreadSafeShared<SQLStatement> {
+public:
+ static PassRefPtr<SQLStatement> create(const String&, const Vector<SQLValue>&, PassRefPtr<SQLStatementCallback>, PassRefPtr<SQLStatementErrorCallback>);
+
+ bool execute(Database*);
+ bool lastExecutionFailedDueToQuota() const;
+
+ bool hasStatementCallback() const { return m_statementCallback; }
+ bool hasStatementErrorCallback() const { return m_statementErrorCallback; }
+
+ void setDatabaseDeletedError();
+ void setVersionMismatchedError();
+
+ bool performCallback(SQLTransaction*);
+
+ SQLError* sqlError() const { return m_error.get(); }
+private:
+ SQLStatement(const String& statement, const Vector<SQLValue>& arguments, PassRefPtr<SQLStatementCallback> callback, PassRefPtr<SQLStatementErrorCallback> errorCallback);
+
+ void setFailureDueToQuota();
+ void clearFailureDueToQuota();
+
+ String m_statement;
+ Vector<SQLValue> m_arguments;
+ RefPtr<SQLStatementCallback> m_statementCallback;
+ RefPtr<SQLStatementErrorCallback> m_statementErrorCallback;
+
+ RefPtr<SQLError> m_error;
+ RefPtr<SQLResultSet> m_resultSet;
+};
+
+} // namespace WebCore
+
+#endif // SQLStatement_h
diff --git a/WebCore/storage/SQLStatementCallback.h b/WebCore/storage/SQLStatementCallback.h
new file mode 100644
index 0000000..58a9436
--- /dev/null
+++ b/WebCore/storage/SQLStatementCallback.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef SQLStatementCallback_h
+#define SQLStatementCallback_h
+
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+class SQLTransaction;
+class SQLResultSet;
+
+class SQLStatementCallback : public ThreadSafeShared<SQLStatementCallback> {
+public:
+ virtual ~SQLStatementCallback() { }
+ virtual void handleEvent(SQLTransaction*, SQLResultSet*, bool& raisedException) = 0;
+};
+
+}
+
+#endif // SQLStatementErrorCallback_h
+
+
diff --git a/WebCore/storage/SQLStatementCallback.idl b/WebCore/storage/SQLStatementCallback.idl
new file mode 100644
index 0000000..783e8c4
--- /dev/null
+++ b/WebCore/storage/SQLStatementCallback.idl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+module storage {
+
+ interface SQLStatementCallback {
+ void handleEvent(in SQLTransaction transaction, in SQLResultSet resultSet);
+ };
+
+}
diff --git a/WebCore/storage/SQLStatementErrorCallback.h b/WebCore/storage/SQLStatementErrorCallback.h
new file mode 100644
index 0000000..5dc3f5a
--- /dev/null
+++ b/WebCore/storage/SQLStatementErrorCallback.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SQLStatementErrorCallback_h
+#define SQLStatementErrorCallback_h
+
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+class SQLTransaction;
+class SQLError;
+
+class SQLStatementErrorCallback : public ThreadSafeShared<SQLStatementErrorCallback> {
+public:
+ virtual ~SQLStatementErrorCallback() { }
+ virtual bool handleEvent(SQLTransaction*, SQLError*) = 0;
+};
+
+}
+
+#endif // SQLStatementErrorCallback_h
+
+
diff --git a/WebCore/storage/SQLStatementErrorCallback.idl b/WebCore/storage/SQLStatementErrorCallback.idl
new file mode 100644
index 0000000..9fae8bf
--- /dev/null
+++ b/WebCore/storage/SQLStatementErrorCallback.idl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+module storage {
+
+ interface SQLStatementErrorCallback {
+ boolean handleEvent(in SQLTransaction transaction, in SQLError error);
+ };
+
+}
diff --git a/WebCore/storage/SQLTransaction.cpp b/WebCore/storage/SQLTransaction.cpp
new file mode 100644
index 0000000..5820d7f
--- /dev/null
+++ b/WebCore/storage/SQLTransaction.cpp
@@ -0,0 +1,550 @@
+/*
+ * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "SQLTransaction.h"
+
+#include "ChromeClient.h"
+#include "Database.h"
+#include "DatabaseAuthorizer.h"
+#include "DatabaseDetails.h"
+#include "DatabaseTracker.h"
+#include "Document.h"
+#include "ExceptionCode.h"
+#include "Logging.h"
+#include "OriginQuotaManager.h"
+#include "Page.h"
+#include "PlatformString.h"
+#include "SecurityOrigin.h"
+#include "SQLError.h"
+#include "SQLiteTransaction.h"
+#include "SQLResultSet.h"
+#include "SQLStatement.h"
+#include "SQLStatementCallback.h"
+#include "SQLStatementErrorCallback.h"
+#include "SQLValue.h"
+
+// There's no way of knowing exactly how much more space will be required when a statement hits the quota limit.
+// For now, we'll arbitrarily choose currentQuota + 1mb.
+// In the future we decide to track if a size increase wasn't enough, and ask for larger-and-larger increases until its enough.
+static const int DefaultQuotaSizeIncrease = 1048576;
+
+namespace WebCore {
+
+PassRefPtr<SQLTransaction> SQLTransaction::create(Database* db, PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback,
+ PassRefPtr<VoidCallback> successCallback, PassRefPtr<SQLTransactionWrapper> wrapper)
+{
+ return adoptRef(new SQLTransaction(db, callback, errorCallback, successCallback, wrapper));
+}
+
+SQLTransaction::SQLTransaction(Database* db, PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback,
+ PassRefPtr<SQLTransactionWrapper> wrapper)
+ : m_nextStep(&SQLTransaction::openTransactionAndPreflight)
+ , m_executeSqlAllowed(false)
+ , m_database(db)
+ , m_wrapper(wrapper)
+ , m_callback(callback)
+ , m_successCallback(successCallback)
+ , m_errorCallback(errorCallback)
+ , m_shouldRetryCurrentStatement(false)
+ , m_shouldCommitAfterErrorCallback(true)
+ , m_modifiedDatabase(false)
+{
+ ASSERT(m_database);
+}
+
+SQLTransaction::~SQLTransaction()
+{
+}
+
+void SQLTransaction::executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, PassRefPtr<SQLStatementCallback> callback, PassRefPtr<SQLStatementErrorCallback> callbackError, ExceptionCode& e)
+{
+ if (!m_executeSqlAllowed || m_database->stopped()) {
+ e = INVALID_STATE_ERR;
+ return;
+ }
+
+ RefPtr<SQLStatement> statement = SQLStatement::create(sqlStatement.copy(), arguments, callback, callbackError);
+
+ if (m_database->deleted())
+ statement->setDatabaseDeletedError();
+
+ if (!m_database->versionMatchesExpected())
+ statement->setVersionMismatchedError();
+
+ enqueueStatement(statement);
+}
+
+void SQLTransaction::enqueueStatement(PassRefPtr<SQLStatement> statement)
+{
+ MutexLocker locker(m_statementMutex);
+ m_statementQueue.append(statement);
+}
+
+#ifndef NDEBUG
+const char* SQLTransaction::debugStepName(SQLTransaction::TransactionStepMethod step)
+{
+ if (step == &SQLTransaction::openTransactionAndPreflight)
+ return "openTransactionAndPreflight";
+ else if (step == &SQLTransaction::runStatements)
+ return "runStatements";
+ else if (step == &SQLTransaction::postflightAndCommit)
+ return "postflightAndCommit";
+ else if (step == &SQLTransaction::cleanupAfterTransactionErrorCallback)
+ return "cleanupAfterTransactionErrorCallback";
+ else if (step == &SQLTransaction::deliverTransactionCallback)
+ return "deliverTransactionCallback";
+ else if (step == &SQLTransaction::deliverTransactionErrorCallback)
+ return "deliverTransactionErrorCallback";
+ else if (step == &SQLTransaction::deliverStatementCallback)
+ return "deliverStatementCallback";
+ else if (step == &SQLTransaction::deliverQuotaIncreaseCallback)
+ return "deliverQuotaIncreaseCallback";
+ else if (step == &SQLTransaction::deliverSuccessCallback)
+ return "deliverSuccessCallback";
+ else if (step == &SQLTransaction::cleanupAfterSuccessCallback)
+ return "cleanupAfterSuccessCallback";
+ else
+ return "UNKNOWN";
+}
+#endif
+
+void SQLTransaction::checkAndHandleClosedDatabase()
+{
+ if (!m_database->stopped())
+ return;
+
+ // If the database was stopped, don't do anything and cancel queued work
+ LOG(StorageAPI, "Database was stopped - cancelling work for this transaction");
+ MutexLocker locker(m_statementMutex);
+ m_statementQueue.clear();
+ m_nextStep = 0;
+
+ // The current SQLite transaction should be stopped, as well
+ if (m_sqliteTransaction) {
+ m_sqliteTransaction->stop();
+ m_sqliteTransaction.clear();
+ }
+}
+
+
+bool SQLTransaction::performNextStep()
+{
+ LOG(StorageAPI, "Step %s\n", debugStepName(m_nextStep));
+
+ ASSERT(m_nextStep == &SQLTransaction::openTransactionAndPreflight ||
+ m_nextStep == &SQLTransaction::runStatements ||
+ m_nextStep == &SQLTransaction::postflightAndCommit ||
+ m_nextStep == &SQLTransaction::cleanupAfterSuccessCallback ||
+ m_nextStep == &SQLTransaction::cleanupAfterTransactionErrorCallback);
+
+ checkAndHandleClosedDatabase();
+
+ if (m_nextStep)
+ (this->*m_nextStep)();
+
+ // If there is no nextStep after performing the above step, the transaction is complete
+ return !m_nextStep;
+}
+
+void SQLTransaction::performPendingCallback()
+{
+ LOG(StorageAPI, "Callback %s\n", debugStepName(m_nextStep));
+
+ ASSERT(m_nextStep == &SQLTransaction::deliverTransactionCallback ||
+ m_nextStep == &SQLTransaction::deliverTransactionErrorCallback ||
+ m_nextStep == &SQLTransaction::deliverStatementCallback ||
+ m_nextStep == &SQLTransaction::deliverQuotaIncreaseCallback ||
+ m_nextStep == &SQLTransaction::deliverSuccessCallback);
+
+ checkAndHandleClosedDatabase();
+
+ if (m_nextStep)
+ (this->*m_nextStep)();
+}
+
+void SQLTransaction::openTransactionAndPreflight()
+{
+ ASSERT(!m_database->m_sqliteDatabase.transactionInProgress());
+
+ LOG(StorageAPI, "Opening and preflighting transaction %p", this);
+
+ // If the database was deleted, jump to the error callback
+ if (m_database->deleted()) {
+ m_transactionError = SQLError::create(0, "unable to open a transaction, because the user deleted the database");
+ handleTransactionError(false);
+ return;
+ }
+
+ // Set the maximum usage for this transaction
+ m_database->m_sqliteDatabase.setMaximumSize(m_database->maximumSize());
+
+ ASSERT(!m_sqliteTransaction);
+ m_sqliteTransaction.set(new SQLiteTransaction(m_database->m_sqliteDatabase));
+
+ m_database->m_databaseAuthorizer->disable();
+ m_sqliteTransaction->begin();
+ m_database->m_databaseAuthorizer->enable();
+
+ // Transaction Steps 1+2 - Open a transaction to the database, jumping to the error callback if that fails
+ if (!m_sqliteTransaction->inProgress()) {
+ ASSERT(!m_database->m_sqliteDatabase.transactionInProgress());
+ m_sqliteTransaction.clear();
+ m_transactionError = SQLError::create(0, "unable to open a transaction to the database");
+ handleTransactionError(false);
+ return;
+ }
+
+ // Transaction Steps 3 - Peform preflight steps, jumping to the error callback if they fail
+ if (m_wrapper && !m_wrapper->performPreflight(this)) {
+ ASSERT(!m_database->m_sqliteDatabase.transactionInProgress());
+ m_sqliteTransaction.clear();
+ m_transactionError = m_wrapper->sqlError();
+ if (!m_transactionError)
+ m_transactionError = SQLError::create(0, "unknown error occured setting up transaction");
+
+ handleTransactionError(false);
+ return;
+ }
+
+ // Transaction Step 4 - Invoke the transaction callback with the new SQLTransaction object
+ m_nextStep = &SQLTransaction::deliverTransactionCallback;
+ LOG(StorageAPI, "Scheduling deliverTransactionCallback for transaction %p\n", this);
+ m_database->scheduleTransactionCallback(this);
+}
+
+void SQLTransaction::deliverTransactionCallback()
+{
+ bool shouldDeliverErrorCallback = false;
+
+ if (m_callback) {
+ m_executeSqlAllowed = true;
+ m_callback->handleEvent(this, shouldDeliverErrorCallback);
+ m_executeSqlAllowed = false;
+ } else
+ shouldDeliverErrorCallback = true;
+
+ // Transaction Step 5 - If the transaction callback was null or raised an exception, jump to the error callback
+ if (shouldDeliverErrorCallback) {
+ m_transactionError = SQLError::create(0, "the SQLTransactionCallback was null or threw an exception");
+ deliverTransactionErrorCallback();
+ } else
+ scheduleToRunStatements();
+}
+
+void SQLTransaction::scheduleToRunStatements()
+{
+ m_nextStep = &SQLTransaction::runStatements;
+ LOG(StorageAPI, "Scheduling runStatements for transaction %p\n", this);
+ m_database->scheduleTransactionStep(this);
+}
+
+void SQLTransaction::runStatements()
+{
+ // If there is a series of statements queued up that are all successful and have no associated
+ // SQLStatementCallback objects, then we can burn through the queue
+ do {
+ if (m_shouldRetryCurrentStatement) {
+ m_shouldRetryCurrentStatement = false;
+ // FIXME - Another place that needs fixing up after <rdar://problem/5628468> is addressed.
+ // See ::openTransactionAndPreflight() for discussion
+
+ // Reset the maximum size here, as it was increased to allow us to retry this statement
+ m_database->m_sqliteDatabase.setMaximumSize(m_database->maximumSize());
+ } else {
+ // If the current statement has already been run, failed due to quota constraints, and we're not retrying it,
+ // that means it ended in an error. Handle it now
+ if (m_currentStatement && m_currentStatement->lastExecutionFailedDueToQuota()) {
+ handleCurrentStatementError();
+ break;
+ }
+
+ // Otherwise, advance to the next statement
+ getNextStatement();
+ }
+ } while (runCurrentStatement());
+
+ // If runCurrentStatement() returned false, that means either there was no current statement to run,
+ // or the current statement requires a callback to complete. In the later case, it also scheduled
+ // the callback or performed any other additional work so we can return
+ if (!m_currentStatement)
+ postflightAndCommit();
+}
+
+void SQLTransaction::getNextStatement()
+{
+ m_currentStatement = 0;
+
+ MutexLocker locker(m_statementMutex);
+ if (!m_statementQueue.isEmpty()) {
+ m_currentStatement = m_statementQueue.first();
+ m_statementQueue.removeFirst();
+ }
+}
+
+bool SQLTransaction::runCurrentStatement()
+{
+ if (!m_currentStatement)
+ return false;
+
+ m_database->m_databaseAuthorizer->reset();
+
+ if (m_currentStatement->execute(m_database.get())) {
+ if (m_database->m_databaseAuthorizer->lastActionChangedDatabase()) {
+ // Flag this transaction as having changed the database for later delegate notification
+ m_modifiedDatabase = true;
+ // Also dirty the size of this database file for calculating quota usage
+ OriginQuotaManager& manager(DatabaseTracker::tracker().originQuotaManager());
+ Locker<OriginQuotaManager> locker(manager);
+
+ manager.markDatabase(m_database.get());
+ }
+
+ if (m_currentStatement->hasStatementCallback()) {
+ m_nextStep = &SQLTransaction::deliverStatementCallback;
+ LOG(StorageAPI, "Scheduling deliverStatementCallback for transaction %p\n", this);
+ m_database->scheduleTransactionCallback(this);
+ return false;
+ }
+ return true;
+ }
+
+ if (m_currentStatement->lastExecutionFailedDueToQuota()) {
+ m_nextStep = &SQLTransaction::deliverQuotaIncreaseCallback;
+ LOG(StorageAPI, "Scheduling deliverQuotaIncreaseCallback for transaction %p\n", this);
+ m_database->scheduleTransactionCallback(this);
+ return false;
+ }
+
+ handleCurrentStatementError();
+
+ return false;
+}
+
+void SQLTransaction::handleCurrentStatementError()
+{
+ // Transaction Steps 6.error - Call the statement's error callback, but if there was no error callback,
+ // jump to the transaction error callback
+ if (m_currentStatement->hasStatementErrorCallback()) {
+ m_nextStep = &SQLTransaction::deliverStatementCallback;
+ LOG(StorageAPI, "Scheduling deliverStatementCallback for transaction %p\n", this);
+ m_database->scheduleTransactionCallback(this);
+ } else {
+ m_transactionError = m_currentStatement->sqlError();
+ if (!m_transactionError)
+ m_transactionError = SQLError::create(1, "the statement failed to execute");
+ handleTransactionError(false);
+ }
+}
+
+void SQLTransaction::deliverStatementCallback()
+{
+ ASSERT(m_currentStatement);
+
+ // Transaction Step 6.6 and 6.3(error) - If the statement callback went wrong, jump to the transaction error callback
+ // Otherwise, continue to loop through the statement queue
+ m_executeSqlAllowed = true;
+ bool result = m_currentStatement->performCallback(this);
+ m_executeSqlAllowed = false;
+
+ if (result) {
+ m_transactionError = SQLError::create(0, "the statement callback raised an exception or statement error callback did not return false");
+ handleTransactionError(true);
+ } else
+ scheduleToRunStatements();
+}
+
+void SQLTransaction::deliverQuotaIncreaseCallback()
+{
+ ASSERT(m_currentStatement);
+ ASSERT(!m_shouldRetryCurrentStatement);
+
+ Page* page = m_database->document()->page();
+ ASSERT(page);
+
+ RefPtr<SecurityOrigin> origin = m_database->securityOriginCopy();
+
+ unsigned long long currentQuota = DatabaseTracker::tracker().quotaForOrigin(origin.get());
+ page->chrome()->client()->exceededDatabaseQuota(m_database->document()->frame(), m_database->stringIdentifier());
+ unsigned long long newQuota = DatabaseTracker::tracker().quotaForOrigin(origin.get());
+
+ // If the new quota ended up being larger than the old quota, we will retry the statement.
+ if (newQuota > currentQuota)
+ m_shouldRetryCurrentStatement = true;
+
+ m_nextStep = &SQLTransaction::runStatements;
+ LOG(StorageAPI, "Scheduling runStatements for transaction %p\n", this);
+ m_database->scheduleTransactionStep(this);
+}
+
+void SQLTransaction::postflightAndCommit()
+{
+ // Transaction Step 7 - Peform postflight steps, jumping to the error callback if they fail
+ if (m_wrapper && !m_wrapper->performPostflight(this)) {
+ m_transactionError = m_wrapper->sqlError();
+ if (!m_transactionError)
+ m_transactionError = SQLError::create(0, "unknown error occured setting up transaction");
+ handleTransactionError(false);
+ return;
+ }
+
+ // Transacton Step 8+9 - Commit the transaction, jumping to the error callback if that fails
+ ASSERT(m_sqliteTransaction);
+
+ m_database->m_databaseAuthorizer->disable();
+ m_sqliteTransaction->commit();
+ m_database->m_databaseAuthorizer->enable();
+
+ // If the commit failed, the transaction will still be marked as "in progress"
+ if (m_sqliteTransaction->inProgress()) {
+ m_shouldCommitAfterErrorCallback = false;
+ m_transactionError = SQLError::create(0, "failed to commit the transaction");
+ handleTransactionError(false);
+ return;
+ }
+
+ // The commit was successful, notify the delegates if the transaction modified this database
+ if (m_modifiedDatabase)
+ DatabaseTracker::tracker().scheduleNotifyDatabaseChanged(m_database->m_securityOrigin.get(), m_database->m_name);
+
+ // Now release our unneeded callbacks, to break reference cycles.
+ m_callback = 0;
+ m_errorCallback = 0;
+
+ // Transaction Step 10 - Deliver success callback, if there is one
+ if (m_successCallback) {
+ m_nextStep = &SQLTransaction::deliverSuccessCallback;
+ LOG(StorageAPI, "Scheduling deliverSuccessCallback for transaction %p\n", this);
+ m_database->scheduleTransactionCallback(this);
+ } else
+ cleanupAfterSuccessCallback();
+}
+
+void SQLTransaction::deliverSuccessCallback()
+{
+ // Transaction Step 10 - Deliver success callback
+ ASSERT(m_successCallback);
+ m_successCallback->handleEvent();
+
+ // Release the last callback to break reference cycle
+ m_successCallback = 0;
+
+ // Schedule a "post-success callback" step to return control to the database thread in case there
+ // are further transactions queued up for this Database
+ m_nextStep = &SQLTransaction::cleanupAfterSuccessCallback;
+ LOG(StorageAPI, "Scheduling cleanupAfterSuccessCallback for transaction %p\n", this);
+ m_database->scheduleTransactionStep(this);
+}
+
+void SQLTransaction::cleanupAfterSuccessCallback()
+{
+ // Transaction Step 11 - End transaction steps
+ // There is no next step
+ LOG(StorageAPI, "Transaction %p is complete\n", this);
+ ASSERT(!m_database->m_sqliteDatabase.transactionInProgress());
+ m_nextStep = 0;
+}
+
+void SQLTransaction::handleTransactionError(bool inCallback)
+{
+ if (m_errorCallback) {
+ if (inCallback)
+ deliverTransactionErrorCallback();
+ else {
+ m_nextStep = &SQLTransaction::deliverTransactionErrorCallback;
+ LOG(StorageAPI, "Scheduling deliverTransactionErrorCallback for transaction %p\n", this);
+ m_database->scheduleTransactionCallback(this);
+ }
+ return;
+ }
+
+ // Transaction Step 12 - If the callback couldn't be called, then rollback the transaction.
+ m_shouldCommitAfterErrorCallback = false;
+ if (inCallback) {
+ m_nextStep = &SQLTransaction::cleanupAfterTransactionErrorCallback;
+ LOG(StorageAPI, "Scheduling cleanupAfterTransactionErrorCallback for transaction %p\n", this);
+ m_database->scheduleTransactionStep(this);
+ } else {
+ cleanupAfterTransactionErrorCallback();
+ }
+}
+
+void SQLTransaction::deliverTransactionErrorCallback()
+{
+ ASSERT(m_transactionError);
+
+ // Transaction Step 12 - If the callback didn't return false, then rollback the transaction.
+ // This includes the callback not existing, returning true, or throwing an exception
+ if (!m_errorCallback || m_errorCallback->handleEvent(m_transactionError.get()))
+ m_shouldCommitAfterErrorCallback = false;
+
+ m_nextStep = &SQLTransaction::cleanupAfterTransactionErrorCallback;
+ LOG(StorageAPI, "Scheduling cleanupAfterTransactionErrorCallback for transaction %p\n", this);
+ m_database->scheduleTransactionStep(this);
+}
+
+void SQLTransaction::cleanupAfterTransactionErrorCallback()
+{
+ m_database->m_databaseAuthorizer->disable();
+ if (m_sqliteTransaction) {
+ // Transaction Step 12 -If the error callback returned false, and the last error wasn't itself a
+ // failure when committing the transaction, then try to commit the transaction
+ if (m_shouldCommitAfterErrorCallback)
+ m_sqliteTransaction->commit();
+
+ if (m_sqliteTransaction->inProgress()) {
+ // Transaction Step 12 - If that fails, or if the callback couldn't be called
+ // or if it didn't return false, then rollback the transaction.
+ m_sqliteTransaction->rollback();
+ } else if (m_modifiedDatabase) {
+ // But if the commit was successful, notify the delegates if the transaction modified this database
+ DatabaseTracker::tracker().scheduleNotifyDatabaseChanged(m_database->m_securityOrigin.get(), m_database->m_name);
+ }
+
+ ASSERT(!m_database->m_sqliteDatabase.transactionInProgress());
+ m_sqliteTransaction.clear();
+ }
+ m_database->m_databaseAuthorizer->enable();
+
+ // Transaction Step 12 - Any still-pending statements in the transaction are discarded.
+ {
+ MutexLocker locker(m_statementMutex);
+ m_statementQueue.clear();
+ }
+
+ // Transaction is complete! There is no next step
+ LOG(StorageAPI, "Transaction %p is complete with an error\n", this);
+ ASSERT(!m_database->m_sqliteDatabase.transactionInProgress());
+ m_nextStep = 0;
+
+ // Now release our callbacks, to break reference cycles.
+ m_callback = 0;
+ m_errorCallback = 0;
+}
+
+} // namespace WebCore
diff --git a/WebCore/storage/SQLTransaction.h b/WebCore/storage/SQLTransaction.h
new file mode 100644
index 0000000..87d438e
--- /dev/null
+++ b/WebCore/storage/SQLTransaction.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef SQLTransaction_h
+#define SQLTransaction_h
+
+#include <wtf/Threading.h>
+
+#include "SQLiteTransaction.h"
+#include "SQLStatement.h"
+#include "SQLTransactionCallback.h"
+#include "SQLTransactionErrorCallback.h"
+#include <wtf/Deque.h>
+#include <wtf/Forward.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+typedef int ExceptionCode;
+
+class Database;
+class SQLError;
+class SQLStatementCallback;
+class SQLStatementErrorCallback;
+class SQLTransaction;
+class SQLValue;
+class String;
+class VoidCallback;
+
+class SQLTransactionWrapper : public ThreadSafeShared<SQLTransactionWrapper> {
+public:
+ virtual ~SQLTransactionWrapper() { }
+ virtual bool performPreflight(SQLTransaction*) = 0;
+ virtual bool performPostflight(SQLTransaction*) = 0;
+
+ virtual SQLError* sqlError() const = 0;
+};
+
+class SQLTransaction : public ThreadSafeShared<SQLTransaction> {
+public:
+ static PassRefPtr<SQLTransaction> create(Database*, PassRefPtr<SQLTransactionCallback>, PassRefPtr<SQLTransactionErrorCallback>, PassRefPtr<VoidCallback>, PassRefPtr<SQLTransactionWrapper>);
+
+ ~SQLTransaction();
+
+ void executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments,
+ PassRefPtr<SQLStatementCallback> callback, PassRefPtr<SQLStatementErrorCallback> callbackError, ExceptionCode& e);
+
+ bool performNextStep();
+ void performPendingCallback();
+
+ Database* database() { return m_database.get(); }
+
+private:
+ SQLTransaction(Database*, PassRefPtr<SQLTransactionCallback>, PassRefPtr<SQLTransactionErrorCallback>, PassRefPtr<VoidCallback>, PassRefPtr<SQLTransactionWrapper>);
+
+ typedef void (SQLTransaction::*TransactionStepMethod)();
+ TransactionStepMethod m_nextStep;
+
+ void enqueueStatement(PassRefPtr<SQLStatement>);
+
+ void checkAndHandleClosedDatabase();
+
+ void openTransactionAndPreflight();
+ void deliverTransactionCallback();
+ void scheduleToRunStatements();
+ void runStatements();
+ void getNextStatement();
+ bool runCurrentStatement();
+ void handleCurrentStatementError();
+ void deliverStatementCallback();
+ void deliverQuotaIncreaseCallback();
+ void postflightAndCommit();
+ void deliverSuccessCallback();
+ void cleanupAfterSuccessCallback();
+ void handleTransactionError(bool inCallback);
+ void deliverTransactionErrorCallback();
+ void cleanupAfterTransactionErrorCallback();
+
+#ifndef NDEBUG
+ static const char* debugStepName(TransactionStepMethod);
+#endif
+
+ RefPtr<SQLStatement> m_currentStatement;
+
+ bool m_executeSqlAllowed;
+
+ RefPtr<Database> m_database;
+ RefPtr<SQLTransactionWrapper> m_wrapper;
+ RefPtr<SQLTransactionCallback> m_callback;
+ RefPtr<VoidCallback> m_successCallback;
+ RefPtr<SQLTransactionErrorCallback> m_errorCallback;
+ RefPtr<SQLError> m_transactionError;
+ bool m_shouldRetryCurrentStatement;
+ bool m_shouldCommitAfterErrorCallback;
+ bool m_modifiedDatabase;
+
+ Mutex m_statementMutex;
+ Deque<RefPtr<SQLStatement> > m_statementQueue;
+
+ OwnPtr<SQLiteTransaction> m_sqliteTransaction;
+};
+
+} // namespace WebCore
+
+#endif // SQLTransaction_h
diff --git a/WebCore/storage/SQLTransaction.idl b/WebCore/storage/SQLTransaction.idl
new file mode 100644
index 0000000..41436f1
--- /dev/null
+++ b/WebCore/storage/SQLTransaction.idl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+module storage {
+
+ interface SQLTransaction {
+ [Custom] void executeSql(in DOMString sqlStatement, in ObjectArray arguments, in SQLStatementCallback callback, in SQLStatementErrorCallback errorCallback);
+ };
+}
diff --git a/WebCore/storage/SQLTransactionCallback.h b/WebCore/storage/SQLTransactionCallback.h
new file mode 100644
index 0000000..bbe62a5
--- /dev/null
+++ b/WebCore/storage/SQLTransactionCallback.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *Transaction
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SQLTransactionCallback_h
+#define SQLTransactionCallback_h
+
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+class SQLTransaction;
+class SQLError;
+
+class SQLTransactionCallback : public ThreadSafeShared<SQLTransactionCallback> {
+public:
+ virtual ~SQLTransactionCallback() { }
+ virtual void handleEvent(SQLTransaction*, bool& raisedException) = 0;
+};
+
+}
+
+#endif // SQLTransactionCallback_h
diff --git a/WebCore/storage/SQLTransactionCallback.idl b/WebCore/storage/SQLTransactionCallback.idl
new file mode 100644
index 0000000..cee726f
--- /dev/null
+++ b/WebCore/storage/SQLTransactionCallback.idl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+module storage {
+
+ interface SQLTransactionCallback {
+ void handleEvent(in SQLTransaction transaction);
+ };
+
+}
diff --git a/WebCore/storage/SQLTransactionErrorCallback.h b/WebCore/storage/SQLTransactionErrorCallback.h
new file mode 100644
index 0000000..2890556
--- /dev/null
+++ b/WebCore/storage/SQLTransactionErrorCallback.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SQLTransactionErrorCallback_h
+#define SQLTransactionErrorCallback_h
+
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+ class SQLError;
+
+ class SQLTransactionErrorCallback : public ThreadSafeShared<SQLTransactionErrorCallback> {
+ public:
+ virtual ~SQLTransactionErrorCallback() { }
+ virtual bool handleEvent(SQLError*) = 0;
+ };
+
+}
+
+#endif // SQLTransactionErrorCallback_h
+
+
diff --git a/WebCore/storage/SQLTransactionErrorCallback.idl b/WebCore/storage/SQLTransactionErrorCallback.idl
new file mode 100644
index 0000000..9355d9a
--- /dev/null
+++ b/WebCore/storage/SQLTransactionErrorCallback.idl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+module storage {
+
+ interface SQLTransactionErrorCallback {
+ void handleEvent(in SQLError error);
+ };
+
+}
diff --git a/WebCore/storage/SessionStorage.cpp b/WebCore/storage/SessionStorage.cpp
new file mode 100644
index 0000000..e70227a
--- /dev/null
+++ b/WebCore/storage/SessionStorage.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "SessionStorage.h"
+
+#include "EventNames.h"
+#include "Frame.h"
+#include "FrameTree.h"
+#include "Page.h"
+#include "SecurityOrigin.h"
+#include "StorageArea.h"
+#include "StorageMap.h"
+
+namespace WebCore {
+
+PassRefPtr<SessionStorage> SessionStorage::create(Page* page)
+{
+ return adoptRef(new SessionStorage(page));
+}
+
+SessionStorage::SessionStorage(Page* page)
+ : m_page(page)
+{
+ ASSERT(m_page);
+}
+
+PassRefPtr<SessionStorage> SessionStorage::copy(Page* newPage)
+{
+ ASSERT(newPage);
+ RefPtr<SessionStorage> newSession = SessionStorage::create(newPage);
+
+ SessionStorageAreaMap::iterator end = m_storageAreaMap.end();
+ for (SessionStorageAreaMap::iterator i = m_storageAreaMap.begin(); i != end; ++i) {
+ RefPtr<SessionStorageArea> areaCopy = i->second->copy(i->first.get(), newPage);
+ newSession->m_storageAreaMap.set(i->first, areaCopy.release());
+ }
+
+ return newSession.release();
+}
+
+PassRefPtr<StorageArea> SessionStorage::storageArea(SecurityOrigin* origin)
+{
+ RefPtr<SessionStorageArea> storageArea;
+ if (storageArea = m_storageAreaMap.get(origin))
+ return storageArea.release();
+
+ storageArea = SessionStorageArea::create(origin, m_page);
+ m_storageAreaMap.set(origin, storageArea);
+ return storageArea.release();
+}
+
+}
diff --git a/WebCore/storage/SessionStorage.h b/WebCore/storage/SessionStorage.h
new file mode 100644
index 0000000..7b59aeb
--- /dev/null
+++ b/WebCore/storage/SessionStorage.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SessionStorage_h
+#define SessionStorage_h
+
+#include "SecurityOriginHash.h"
+#include "SessionStorageArea.h"
+
+#include <wtf/HashMap.h>
+#include <wtf/HashSet.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+ class Page;
+
+ class SessionStorage : public RefCounted<SessionStorage> {
+ public:
+ static PassRefPtr<SessionStorage> create(Page*);
+ PassRefPtr<SessionStorage> copy(Page*);
+
+ PassRefPtr<StorageArea> storageArea(SecurityOrigin*);
+
+#ifndef NDEBUG
+ Page* page() { return m_page; }
+#endif
+
+ private:
+ SessionStorage(Page*);
+
+ void dispatchStorageEvent(StorageArea*, const String& key, const String& oldValue, const String& newValue, Frame* sourceFrame);
+
+ Page* m_page;
+
+ typedef HashMap<RefPtr<SecurityOrigin>, RefPtr<SessionStorageArea>, SecurityOriginHash> SessionStorageAreaMap;
+ SessionStorageAreaMap m_storageAreaMap;
+ };
+
+} // namespace WebCore
+
+#endif // SessionStorage_h
diff --git a/WebCore/storage/SessionStorageArea.cpp b/WebCore/storage/SessionStorageArea.cpp
new file mode 100644
index 0000000..048c488
--- /dev/null
+++ b/WebCore/storage/SessionStorageArea.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "SessionStorageArea.h"
+
+#include "EventNames.h"
+#include "Frame.h"
+#include "FrameTree.h"
+#include "Page.h"
+#include "PlatformString.h"
+#include "SecurityOrigin.h"
+#include "StorageMap.h"
+
+namespace WebCore {
+
+PassRefPtr<SessionStorageArea> SessionStorageArea::copy(SecurityOrigin* origin, Page* page)
+{
+ return adoptRef(new SessionStorageArea(origin, page, this));
+}
+
+SessionStorageArea::SessionStorageArea(SecurityOrigin* origin, Page* page)
+ : StorageArea(origin)
+ , m_page(page)
+{
+ ASSERT(page);
+}
+
+SessionStorageArea::SessionStorageArea(SecurityOrigin* origin, Page* page, SessionStorageArea* area)
+ : StorageArea(origin, area)
+ , m_page(page)
+{
+ ASSERT(page);
+}
+
+void SessionStorageArea::itemChanged(const String& key, const String& oldValue, const String& newValue, Frame* sourceFrame)
+{
+ dispatchStorageEvent(key, oldValue, newValue, sourceFrame);
+}
+
+void SessionStorageArea::itemRemoved(const String& key, const String& oldValue, Frame* sourceFrame)
+{
+ dispatchStorageEvent(key, oldValue, String(), sourceFrame);
+}
+
+void SessionStorageArea::areaCleared(Frame* sourceFrame)
+{
+ dispatchStorageEvent(String(), String(), String(), sourceFrame);
+}
+
+void SessionStorageArea::dispatchStorageEvent(const String& key, const String& oldValue, const String& newValue, Frame* sourceFrame)
+{
+ // For SessionStorage events, each frame in the page's frametree with the same origin as this StorageArea needs to be notified of the change
+ Vector<RefPtr<Frame> > frames;
+ for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree()->traverseNext()) {
+ if (Document* document = frame->document())
+ if (document->securityOrigin()->equal(securityOrigin()))
+ frames.append(frame);
+ }
+
+ for (unsigned i = 0; i < frames.size(); ++i) {
+ if (HTMLElement* body = frames[i]->document()->body())
+ body->dispatchStorageEvent(eventNames().storageEvent, key, oldValue, newValue, sourceFrame);
+ }
+}
+
+} // namespace WebCore
diff --git a/WebCore/storage/SessionStorageArea.h b/WebCore/storage/SessionStorageArea.h
new file mode 100644
index 0000000..3dad1af
--- /dev/null
+++ b/WebCore/storage/SessionStorageArea.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SessionStorageArea_h
+#define SessionStorageArea_h
+
+#include "StorageArea.h"
+
+namespace WebCore {
+
+ class Page;
+
+ class SessionStorageArea : public StorageArea {
+ public:
+ static PassRefPtr<SessionStorageArea> create(SecurityOrigin* origin, Page* page) { return adoptRef(new SessionStorageArea(origin, page)); }
+ PassRefPtr<SessionStorageArea> copy(SecurityOrigin*, Page*);
+
+ Page* page() { return m_page; }
+
+ private:
+ SessionStorageArea(SecurityOrigin*, Page*);
+ SessionStorageArea(SecurityOrigin*, Page*, SessionStorageArea*);
+
+ virtual void itemChanged(const String& key, const String& oldValue, const String& newValue, Frame* sourceFrame);
+ virtual void itemRemoved(const String& key, const String& oldValue, Frame* sourceFrame);
+ virtual void areaCleared(Frame* sourceFrame);
+
+ void dispatchStorageEvent(const String& key, const String& oldValue, const String& newValue, Frame* sourceFrame);
+
+ Page* m_page;
+ };
+
+} // namespace WebCore
+
+#endif // SessionStorageArea_h
diff --git a/WebCore/storage/Storage.cpp b/WebCore/storage/Storage.cpp
new file mode 100644
index 0000000..cf4413a
--- /dev/null
+++ b/WebCore/storage/Storage.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "Storage.h"
+
+#include "StorageArea.h"
+#include "PlatformString.h"
+#include <wtf/PassRefPtr.h>
+
+namespace WebCore {
+
+PassRefPtr<Storage> Storage::create(Frame* frame, PassRefPtr<StorageArea> storageArea)
+{
+ return adoptRef(new Storage(frame, storageArea));
+}
+
+Storage::Storage(Frame* frame, PassRefPtr<StorageArea> storageArea)
+ : m_frame(frame)
+ , m_storageArea(storageArea)
+{
+ ASSERT(m_frame);
+ ASSERT(m_storageArea);
+}
+
+unsigned Storage::length() const
+{
+ if (!m_frame)
+ return 0;
+
+ return m_storageArea->length();
+}
+
+String Storage::key(unsigned index, ExceptionCode& ec) const
+{
+ ec = 0;
+ if (!m_frame)
+ return String();
+
+ return m_storageArea->key(index, ec);
+}
+
+String Storage::getItem(const String& key) const
+{
+ if (!m_frame)
+ return String();
+
+ return m_storageArea->getItem(key);
+}
+
+void Storage::setItem(const String& key, const String& value, ExceptionCode& ec)
+{
+ ec = 0;
+ if (!m_frame)
+ return;
+
+ m_storageArea->setItem(key, value, ec, m_frame);
+}
+
+void Storage::removeItem(const String& key)
+{
+ if (!m_frame)
+ return;
+
+ m_storageArea->removeItem(key, m_frame);
+}
+
+void Storage::clear()
+{
+ if (!m_frame)
+ return;
+
+ m_storageArea->clear(m_frame);
+}
+
+bool Storage::contains(const String& key) const
+{
+ if (!m_frame)
+ return false;
+
+ return m_storageArea->contains(key);
+}
+
+}
diff --git a/WebCore/storage/Storage.h b/WebCore/storage/Storage.h
new file mode 100644
index 0000000..061f7c4
--- /dev/null
+++ b/WebCore/storage/Storage.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef Storage_h
+#define Storage_h
+
+#include "StorageArea.h"
+
+#include <wtf/Forward.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+ class Frame;
+ class String;
+ typedef int ExceptionCode;
+
+ class Storage : public RefCounted<Storage> {
+ public:
+ static PassRefPtr<Storage> create(Frame*, PassRefPtr<StorageArea>);
+
+ unsigned length() const;
+ String key(unsigned index, ExceptionCode&) const;
+ String getItem(const String&) const;
+ void setItem(const String& key, const String& value, ExceptionCode&);
+ void removeItem(const String&);
+ void clear();
+
+ bool contains(const String& key) const;
+
+ void disconnectFrame() { m_frame = 0; }
+
+ private:
+ Storage(Frame*, PassRefPtr<StorageArea>);
+
+ Frame* m_frame;
+ RefPtr<StorageArea> m_storageArea;
+ };
+
+} // namespace WebCore
+
+#endif // Storage_h
diff --git a/WebCore/storage/Storage.idl b/WebCore/storage/Storage.idl
new file mode 100644
index 0000000..8ff9cd9
--- /dev/null
+++ b/WebCore/storage/Storage.idl
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+module storage {
+
+ interface [
+ GenerateConstructor,
+ HasNameGetter,
+ CustomDeleteProperty,
+ CustomGetPropertyNames,
+ CustomPutFunction
+ ] Storage {
+ readonly attribute [DontEnum] unsigned long length;
+ [DontEnum] DOMString key(in unsigned long index)
+ raises(DOMException);
+ [DontEnum, ConvertNullStringTo=Null] DOMString getItem(in DOMString key);
+ [DontEnum] void setItem(in DOMString key, in DOMString data)
+ raises(DOMException);
+ [DontEnum] void removeItem(in DOMString key);
+ [DontEnum] void clear();
+ };
+
+}
diff --git a/WebCore/storage/StorageArea.cpp b/WebCore/storage/StorageArea.cpp
new file mode 100644
index 0000000..6a1b7aa
--- /dev/null
+++ b/WebCore/storage/StorageArea.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "StorageArea.h"
+
+#include "CString.h"
+#include "ExceptionCode.h"
+#include "SecurityOrigin.h"
+#include "StorageMap.h"
+
+namespace WebCore {
+
+StorageArea::StorageArea(SecurityOrigin* origin)
+ : m_securityOrigin(origin)
+ , m_storageMap(StorageMap::create())
+{
+}
+
+StorageArea::StorageArea(SecurityOrigin* origin, StorageArea* area)
+ : m_securityOrigin(origin)
+ , m_storageMap(area->m_storageMap)
+{
+}
+
+StorageArea::~StorageArea()
+{
+}
+
+unsigned StorageArea::internalLength() const
+{
+ return m_storageMap->length();
+}
+
+String StorageArea::internalKey(unsigned index, ExceptionCode& ec) const
+{
+ String key;
+
+ if (!m_storageMap->key(index, key)) {
+ ec = INDEX_SIZE_ERR;
+ return String();
+ }
+
+ return key;
+}
+
+String StorageArea::internalGetItem(const String& key) const
+{
+ return m_storageMap->getItem(key);
+}
+
+void StorageArea::internalSetItem(const String& key, const String& value, ExceptionCode& ec, Frame* frame)
+{
+ ASSERT(!value.isNull());
+
+ // FIXME: For LocalStorage where a disk quota will be enforced, here is where we need to do quota checking.
+ // If we decide to enforce a memory quota for SessionStorage, this is where we'd do that, also.
+ // if (<over quota>) {
+ // ec = INVALID_ACCESS_ERR;
+ // return;
+ // }
+
+ String oldValue;
+ RefPtr<StorageMap> newMap = m_storageMap->setItem(key, value, oldValue);
+
+ if (newMap)
+ m_storageMap = newMap.release();
+
+ // Only notify the client if an item was actually changed
+ if (oldValue != value)
+ itemChanged(key, oldValue, value, frame);
+}
+
+void StorageArea::internalRemoveItem(const String& key, Frame* frame)
+{
+ String oldValue;
+ RefPtr<StorageMap> newMap = m_storageMap->removeItem(key, oldValue);
+ if (newMap)
+ m_storageMap = newMap.release();
+
+ // Only notify the client if an item was actually removed
+ if (!oldValue.isNull())
+ itemRemoved(key, oldValue, frame);
+}
+
+void StorageArea::internalClear(Frame* frame)
+{
+ m_storageMap = StorageMap::create();
+
+ areaCleared(frame);
+}
+
+bool StorageArea::internalContains(const String& key) const
+{
+ return m_storageMap->contains(key);
+}
+
+void StorageArea::importItem(const String& key, const String& value)
+{
+ m_storageMap->importItem(key, value);
+}
+
+}
diff --git a/WebCore/storage/StorageArea.h b/WebCore/storage/StorageArea.h
new file mode 100644
index 0000000..2163287
--- /dev/null
+++ b/WebCore/storage/StorageArea.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef StorageArea_h
+#define StorageArea_h
+
+#include "PlatformString.h"
+
+#include <wtf/Forward.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+ class Frame;
+ class SecurityOrigin;
+ class StorageMap;
+ typedef int ExceptionCode;
+
+ class StorageArea : public RefCounted<StorageArea> {
+ public:
+ virtual ~StorageArea();
+
+ virtual unsigned length() const { return internalLength(); }
+ virtual String key(unsigned index, ExceptionCode& ec) const { return internalKey(index, ec); }
+ virtual String getItem(const String& key) const { return internalGetItem(key); }
+ virtual void setItem(const String& key, const String& value, ExceptionCode& ec, Frame* sourceFrame) { internalSetItem(key, value, ec, sourceFrame); }
+ virtual void removeItem(const String& key, Frame* sourceFrame) { internalRemoveItem(key, sourceFrame); }
+ virtual void clear(Frame* sourceFrame) { internalClear(sourceFrame); }
+ virtual bool contains(const String& key) const { return internalContains(key); }
+
+ SecurityOrigin* securityOrigin() { return m_securityOrigin.get(); }
+
+ protected:
+ StorageArea(SecurityOrigin*);
+ StorageArea(SecurityOrigin*, StorageArea*);
+
+ unsigned internalLength() const;
+ String internalKey(unsigned index, ExceptionCode&) const;
+ String internalGetItem(const String&) const;
+ void internalSetItem(const String& key, const String& value, ExceptionCode&, Frame* sourceFrame);
+ void internalRemoveItem(const String&, Frame* sourceFrame);
+ void internalClear(Frame* sourceFrame);
+ bool internalContains(const String& key) const;
+
+ // This is meant to be called from a background thread for LocalStorageArea's background thread import procedure.
+ void importItem(const String& key, const String& value);
+
+ private:
+ virtual void itemChanged(const String& key, const String& oldValue, const String& newValue, Frame* sourceFrame) = 0;
+ virtual void itemRemoved(const String& key, const String& oldValue, Frame* sourceFrame) = 0;
+ virtual void areaCleared(Frame* sourceFrame) = 0;
+
+ RefPtr<SecurityOrigin> m_securityOrigin;
+ RefPtr<StorageMap> m_storageMap;
+ };
+
+} // namespace WebCore
+
+#endif // StorageArea_h
diff --git a/WebCore/storage/StorageEvent.cpp b/WebCore/storage/StorageEvent.cpp
new file mode 100644
index 0000000..54fd9ad
--- /dev/null
+++ b/WebCore/storage/StorageEvent.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "StorageEvent.h"
+
+#include "DOMWindow.h"
+
+namespace WebCore {
+
+StorageEvent::StorageEvent(const AtomicString& type, const String& key, const String& oldValue, const String& newValue, const String& uri, PassRefPtr<DOMWindow> source)
+ : Event(type, false, true)
+ , m_key(key)
+ , m_oldValue(oldValue)
+ , m_newValue(newValue)
+ , m_uri(uri)
+ , m_source(source)
+{
+}
+
+void StorageEvent::initStorageEvent(const AtomicString& type, bool canBubble, bool cancelable, const String& key, const String& oldValue, const String& newValue, const String& uri, PassRefPtr<DOMWindow> source)
+{
+ if (dispatched())
+ return;
+
+ initEvent(type, canBubble, cancelable);
+
+ m_key = key;
+ m_oldValue = oldValue;
+ m_newValue = newValue;
+ m_uri = uri;
+ m_source = source;
+}
+
+}
diff --git a/WebCore/storage/StorageEvent.h b/WebCore/storage/StorageEvent.h
new file mode 100644
index 0000000..1cd87c6
--- /dev/null
+++ b/WebCore/storage/StorageEvent.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef StorageEvent_h
+#define StorageEvent_h
+
+#include "Event.h"
+
+namespace WebCore {
+
+ class DOMWindow;
+
+ class StorageEvent : public Event {
+ public:
+ static PassRefPtr<StorageEvent> create()
+ {
+ return adoptRef(new StorageEvent);
+ }
+ static PassRefPtr<StorageEvent> create(const AtomicString& type, const String& key, const String& oldValue, const String& newValue, const String& uri, PassRefPtr<DOMWindow> source)
+ {
+ return adoptRef(new StorageEvent(type, key, oldValue, newValue, uri, source));
+ }
+
+ const String& key() const { return m_key; }
+ const String& oldValue() const { return m_oldValue; }
+ const String& newValue() const { return m_newValue; }
+ const String& uri() const { return m_uri; }
+ DOMWindow* source() const { return m_source.get(); }
+
+ void initStorageEvent(const AtomicString& type, bool canBubble, bool cancelable, const String& key, const String& oldValue, const String& newValue, const String& uri, PassRefPtr<DOMWindow> source);
+
+ // Needed once we support init<blank>EventNS
+ // void initStorageEventNS(in DOMString namespaceURI, in DOMString typeArg, in boolean canBubbleArg, in boolean cancelableArg, in DOMString keyArg, in DOMString oldValueArg, in DOMString newValueArg, in DOMString uriArg, in Window sourceArg);
+
+ virtual bool isStorageEvent() const { return true; }
+
+ private:
+ StorageEvent() { }
+ StorageEvent(const AtomicString& type, const String& key, const String& oldValue, const String& newValue, const String& uri, PassRefPtr<DOMWindow> source);
+
+ String m_key;
+ String m_oldValue;
+ String m_newValue;
+ String m_uri;
+ RefPtr<DOMWindow> m_source;
+ };
+
+} // namespace WebCore
+
+#endif // StorageEvent_h
diff --git a/WebCore/storage/StorageEvent.idl b/WebCore/storage/StorageEvent.idl
new file mode 100644
index 0000000..057770c
--- /dev/null
+++ b/WebCore/storage/StorageEvent.idl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+module storage {
+
+ interface [
+ GenerateConstructor
+ ] StorageEvent : Event {
+ readonly attribute DOMString key;
+ readonly attribute [ConvertNullStringTo=Null] DOMString oldValue;
+ readonly attribute [ConvertNullStringTo=Null] DOMString newValue;
+ readonly attribute DOMString uri;
+ readonly attribute DOMWindow source;
+ void initStorageEvent(in DOMString typeArg, in boolean canBubbleArg, in boolean cancelableArg, in DOMString keyArg, in DOMString oldValueArg, in DOMString newValueArg, in DOMString uriArg, in DOMWindow sourceArg);
+
+ // Needed once we support init<blank>EventNS
+ // void initStorageEventNS(in DOMString namespaceURI, in DOMString typeArg, in boolean canBubbleArg, in boolean cancelableArg, in DOMString keyArg, in DOMString oldValueArg, in DOMString newValueArg, in DOMString uriArg, in Window sourceArg);
+ };
+
+}
diff --git a/WebCore/storage/StorageMap.cpp b/WebCore/storage/StorageMap.cpp
new file mode 100644
index 0000000..880fc93
--- /dev/null
+++ b/WebCore/storage/StorageMap.cpp
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "StorageMap.h"
+
+namespace WebCore {
+
+PassRefPtr<StorageMap> StorageMap::create()
+{
+ return adoptRef(new StorageMap);
+}
+
+StorageMap::StorageMap()
+ : m_iterator(m_map.end())
+ , m_iteratorIndex(UINT_MAX)
+{
+}
+
+PassRefPtr<StorageMap> StorageMap::copy()
+{
+ RefPtr<StorageMap> newMap = create();
+ newMap->m_map = m_map;
+ return newMap.release();
+}
+
+void StorageMap::invalidateIterator()
+{
+ m_iterator = m_map.end();
+ m_iteratorIndex = UINT_MAX;
+}
+
+void StorageMap::setIteratorToIndex(unsigned index) const
+{
+ // FIXME: Once we have bidirectional iterators for HashMap we can be more intelligent about this.
+ // The requested index will be closest to begin(), our current iterator, or end(), and we
+ // can take the shortest route.
+ // Until that mechanism is available, we'll always increment our iterator from begin() or current.
+
+ if (m_iteratorIndex == index)
+ return;
+
+ if (index < m_iteratorIndex) {
+ m_iteratorIndex = 0;
+ m_iterator = m_map.begin();
+ ASSERT(m_iterator != m_map.end());
+ }
+
+ while (m_iteratorIndex < index) {
+ ++m_iteratorIndex;
+ ++m_iterator;
+ ASSERT(m_iterator != m_map.end());
+ }
+}
+
+unsigned StorageMap::length() const
+{
+ return m_map.size();
+}
+
+bool StorageMap::key(unsigned index, String& key) const
+{
+ if (index >= length())
+ return false;
+
+ setIteratorToIndex(index);
+
+ key = m_iterator->first;
+ return true;
+}
+
+String StorageMap::getItem(const String& key) const
+{
+ return m_map.get(key);
+}
+
+PassRefPtr<StorageMap> StorageMap::setItem(const String& key, const String& value, String& oldValue)
+{
+ ASSERT(!value.isNull());
+
+ // Implement copy-on-write semantics here. We're guaranteed that the only refs of StorageMaps belong to Storage objects
+ // so if more than one Storage object refs this map, copy it before mutating it.
+ if (refCount() > 1) {
+ RefPtr<StorageMap> newStorageMap = copy();
+ newStorageMap->setItem(key, value, oldValue);
+ return newStorageMap.release();
+ }
+
+ pair<HashMap<String, String>::iterator, bool> addResult = m_map.add(key, value);
+
+ if (addResult.second) {
+ // There was no "oldValue" so null it out.
+ oldValue = String();
+ } else {
+ oldValue = addResult.first->second;
+ addResult.first->second = value;
+ }
+
+ invalidateIterator();
+
+ return 0;
+}
+
+PassRefPtr<StorageMap> StorageMap::removeItem(const String& key, String& oldValue)
+{
+ // Implement copy-on-write semantics here. We're guaranteed that the only refs of StorageMaps belong to Storage objects
+ // so if more than one Storage object refs this map, copy it before mutating it.
+ if (refCount() > 1) {
+ RefPtr<StorageMap> newStorage = copy();
+ newStorage->removeItem(key, oldValue);
+ return newStorage.release();
+ }
+
+ oldValue = m_map.take(key);
+ if (!oldValue.isNull())
+ invalidateIterator();
+
+ return 0;
+}
+
+bool StorageMap::contains(const String& key) const
+{
+ return m_map.contains(key);
+}
+
+void StorageMap::importItem(const String& key, const String& value) const
+{
+ // Be sure to copy the keys/values as items imported on a background thread are destined
+ // to cross a thread boundary
+ pair<HashMap<String, String>::iterator, bool> result = m_map.add(key.copy(), String());
+
+ if (result.second)
+ result.first->second = value.copy();
+}
+
+}
diff --git a/WebCore/storage/StorageMap.h b/WebCore/storage/StorageMap.h
new file mode 100644
index 0000000..d5d00a9
--- /dev/null
+++ b/WebCore/storage/StorageMap.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef StorageMap_h
+#define StorageMap_h
+
+#include "PlatformString.h"
+#include "StringHash.h"
+
+#include <wtf/HashMap.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+ class StorageMap : public RefCounted<StorageMap> {
+ public:
+ static PassRefPtr<StorageMap> create();
+
+ unsigned length() const;
+ bool key(unsigned index, String& key) const;
+ String getItem(const String&) const;
+ PassRefPtr<StorageMap> setItem(const String& key, const String& value, String& oldValue);
+ PassRefPtr<StorageMap> removeItem(const String&, String& oldValue);
+
+ bool contains(const String& key) const;
+
+ void importItem(const String& key, const String& value) const;
+
+ private:
+ StorageMap();
+ PassRefPtr<StorageMap> copy();
+ void invalidateIterator();
+ void setIteratorToIndex(unsigned) const;
+
+ mutable HashMap<String, String> m_map;
+ mutable HashMap<String, String>::iterator m_iterator;
+ mutable unsigned m_iteratorIndex;
+ };
+
+} // namespace WebCore
+
+#endif // StorageMap_h