summaryrefslogtreecommitdiff
path: root/Source/WebKit2/UIProcess/Storage
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@digia.com>2013-09-13 12:51:20 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-09-19 20:50:05 +0200
commitd441d6f39bb846989d95bcf5caf387b42414718d (patch)
treee367e64a75991c554930278175d403c072de6bb8 /Source/WebKit2/UIProcess/Storage
parent0060b2994c07842f4c59de64b5e3e430525c4b90 (diff)
downloadqtwebkit-d441d6f39bb846989d95bcf5caf387b42414718d.tar.gz
Import Qt5x2 branch of QtWebkit for Qt 5.2
Importing a new snapshot of webkit. Change-Id: I2d01ad12cdc8af8cb015387641120a9d7ea5f10c Reviewed-by: Allan Sandfeld Jensen <allan.jensen@digia.com>
Diffstat (limited to 'Source/WebKit2/UIProcess/Storage')
-rw-r--r--Source/WebKit2/UIProcess/Storage/LocalStorageDatabase.cpp356
-rw-r--r--Source/WebKit2/UIProcess/Storage/LocalStorageDatabase.h99
-rw-r--r--Source/WebKit2/UIProcess/Storage/LocalStorageDatabaseTracker.cpp321
-rw-r--r--Source/WebKit2/UIProcess/Storage/LocalStorageDatabaseTracker.h89
-rw-r--r--Source/WebKit2/UIProcess/Storage/StorageManager.cpp655
-rw-r--r--Source/WebKit2/UIProcess/Storage/StorageManager.h115
-rw-r--r--Source/WebKit2/UIProcess/Storage/StorageManager.messages.in33
7 files changed, 1668 insertions, 0 deletions
diff --git a/Source/WebKit2/UIProcess/Storage/LocalStorageDatabase.cpp b/Source/WebKit2/UIProcess/Storage/LocalStorageDatabase.cpp
new file mode 100644
index 000000000..7c58578eb
--- /dev/null
+++ b/Source/WebKit2/UIProcess/Storage/LocalStorageDatabase.cpp
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2008, 2009, 2010, 2013 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. 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 INC. 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 "LocalStorageDatabase.h"
+
+#include "LocalStorageDatabaseTracker.h"
+#include "WorkQueue.h"
+#include <WebCore/FileSystem.h>
+#include <WebCore/SQLiteStatement.h>
+#include <WebCore/SQLiteTransaction.h>
+#include <WebCore/SecurityOrigin.h>
+#include <WebCore/StorageMap.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/text/StringHash.h>
+#include <wtf/text/WTFString.h>
+
+using namespace WebCore;
+
+static const double databaseUpdateIntervalInSeconds = 1.0;
+
+static const int maximumItemsToUpdate = 100;
+
+namespace WebKit {
+
+PassRefPtr<LocalStorageDatabase> LocalStorageDatabase::create(PassRefPtr<WorkQueue> queue, PassRefPtr<LocalStorageDatabaseTracker> tracker, PassRefPtr<SecurityOrigin> securityOrigin)
+{
+ return adoptRef(new LocalStorageDatabase(queue, tracker, securityOrigin));
+}
+
+LocalStorageDatabase::LocalStorageDatabase(PassRefPtr<WorkQueue> queue, PassRefPtr<LocalStorageDatabaseTracker> tracker, PassRefPtr<SecurityOrigin> securityOrigin)
+ : m_queue(queue)
+ , m_tracker(tracker)
+ , m_securityOrigin(securityOrigin)
+ , m_databasePath(m_tracker->databasePath(m_securityOrigin.get()))
+ , m_failedToOpenDatabase(false)
+ , m_didImportItems(false)
+ , m_isClosed(false)
+ , m_didScheduleDatabaseUpdate(false)
+ , m_shouldClearItems(false)
+{
+}
+
+LocalStorageDatabase::~LocalStorageDatabase()
+{
+ ASSERT(m_isClosed);
+}
+
+void LocalStorageDatabase::openDatabase(DatabaseOpeningStrategy openingStrategy)
+{
+ ASSERT(!m_database.isOpen());
+ ASSERT(!m_failedToOpenDatabase);
+
+ if (!tryToOpenDatabase(openingStrategy)) {
+ m_failedToOpenDatabase = true;
+ return;
+ }
+
+ if (m_database.isOpen())
+ m_tracker->didOpenDatabaseWithOrigin(m_securityOrigin.get());
+}
+
+bool LocalStorageDatabase::tryToOpenDatabase(DatabaseOpeningStrategy openingStrategy)
+{
+ if (!fileExists(m_databasePath) && openingStrategy == SkipIfNonExistent)
+ return true;
+
+ if (m_databasePath.isEmpty()) {
+ LOG_ERROR("Filename for local storage database is empty - cannot open for persistent storage");
+ return false;
+ }
+
+ if (!m_database.open(m_databasePath)) {
+ LOG_ERROR("Failed to open database file %s for local storage", m_databasePath.utf8().data());
+ return false;
+ }
+
+ // Since a WorkQueue isn't bound to a specific thread, we have to disable threading checks
+ // even though we never access the database from different threads simultaneously.
+ m_database.disableThreadingChecks();
+
+ if (!migrateItemTableIfNeeded()) {
+ // We failed to migrate the item table. In order to avoid trying to migrate the table over and over,
+ // just delete it and start from scratch.
+ if (!m_database.executeCommand("DROP TABLE ItemTable"))
+ LOG_ERROR("Failed to delete table ItemTable for local storage");
+ }
+
+ if (!m_database.executeCommand("CREATE TABLE IF NOT EXISTS ItemTable (key TEXT UNIQUE ON CONFLICT REPLACE, value BLOB NOT NULL ON CONFLICT FAIL)")) {
+ LOG_ERROR("Failed to create table ItemTable for local storage");
+ return false;
+ }
+
+ return true;
+}
+
+bool LocalStorageDatabase::migrateItemTableIfNeeded()
+{
+ if (!m_database.tableExists("ItemTable"))
+ return true;
+
+ SQLiteStatement query(m_database, "SELECT value FROM ItemTable LIMIT 1");
+
+ // This query isn't ever executed, it's just used to check the column type.
+ if (query.isColumnDeclaredAsBlob(0))
+ return true;
+
+ // Create a new table with the right type, copy all the data over to it and then replace the new table with the old table.
+ static const char* commands[] = {
+ "DROP TABLE IF EXISTS ItemTable2",
+ "CREATE TABLE ItemTable2 (key TEXT UNIQUE ON CONFLICT REPLACE, value BLOB NOT NULL ON CONFLICT FAIL)",
+ "INSERT INTO ItemTable2 SELECT * from ItemTable",
+ "DROP TABLE ItemTable",
+ "ALTER TABLE ItemTable2 RENAME TO ItemTable",
+ 0,
+ };
+
+ SQLiteTransaction transaction(m_database, false);
+ transaction.begin();
+
+ for (size_t i = 0; commands[i]; ++i) {
+ if (m_database.executeCommand(commands[i]))
+ continue;
+
+ LOG_ERROR("Failed to migrate table ItemTable for local storage when executing: %s", commands[i]);
+ transaction.rollback();
+
+ return false;
+ }
+
+ transaction.commit();
+ return true;
+}
+
+void LocalStorageDatabase::importItems(StorageMap& storageMap)
+{
+ if (m_didImportItems)
+ return;
+
+ // FIXME: If it can't import, then the default WebKit behavior should be that of private browsing,
+ // not silently ignoring it. https://bugs.webkit.org/show_bug.cgi?id=25894
+
+ // We set this to true even if we don't end up importing any items due to failure because
+ // there's really no good way to recover other than not importing anything.
+ m_didImportItems = true;
+
+ openDatabase(SkipIfNonExistent);
+ if (!m_database.isOpen())
+ 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");
+ return;
+ }
+
+ HashMap<String, String> items;
+
+ int result = query.step();
+ while (result == SQLResultRow) {
+ items.set(query.getColumnText(0), query.getColumnBlobAsString(1));
+ result = query.step();
+ }
+
+ if (result != SQLResultDone) {
+ LOG_ERROR("Error reading items from ItemTable for local storage");
+ return;
+ }
+
+ storageMap.importItems(items);
+}
+
+void LocalStorageDatabase::setItem(const String& key, const String& value)
+{
+ itemDidChange(key, value);
+}
+
+void LocalStorageDatabase::removeItem(const String& key)
+{
+ itemDidChange(key, String());
+}
+
+void LocalStorageDatabase::clear()
+{
+ m_changedItems.clear();
+ m_shouldClearItems = true;
+
+ scheduleDatabaseUpdate();
+}
+
+void LocalStorageDatabase::close()
+{
+ ASSERT(!m_isClosed);
+ m_isClosed = true;
+
+ if (m_didScheduleDatabaseUpdate) {
+ updateDatabaseWithChangedItems(m_changedItems);
+ m_changedItems.clear();
+ }
+
+ bool isEmpty = databaseIsEmpty();
+
+ if (m_database.isOpen())
+ m_database.close();
+
+ if (isEmpty)
+ m_tracker->deleteDatabaseWithOrigin(m_securityOrigin.get());
+}
+
+void LocalStorageDatabase::itemDidChange(const String& key, const String& value)
+{
+ m_changedItems.set(key, value);
+ scheduleDatabaseUpdate();
+}
+
+void LocalStorageDatabase::scheduleDatabaseUpdate()
+{
+ if (m_didScheduleDatabaseUpdate)
+ return;
+
+ m_didScheduleDatabaseUpdate = true;
+ m_queue->dispatchAfterDelay(bind(&LocalStorageDatabase::updateDatabase, this), databaseUpdateIntervalInSeconds);
+}
+
+void LocalStorageDatabase::updateDatabase()
+{
+ if (m_isClosed)
+ return;
+
+ ASSERT(m_didScheduleDatabaseUpdate);
+ m_didScheduleDatabaseUpdate = false;
+
+ HashMap<String, String> changedItems;
+ if (m_changedItems.size() <= maximumItemsToUpdate) {
+ // There are few enough changed items that we can just always write all of them.
+ m_changedItems.swap(changedItems);
+ } else {
+ for (int i = 0; i < maximumItemsToUpdate; ++i) {
+ auto it = m_changedItems.begin();
+ changedItems.add(it->key, it->value);
+
+ m_changedItems.remove(it);
+ }
+
+ ASSERT(changedItems.size() <= maximumItemsToUpdate);
+
+ // Reschedule the update for the remaining items.
+ scheduleDatabaseUpdate();
+ }
+
+ updateDatabaseWithChangedItems(changedItems);
+}
+
+void LocalStorageDatabase::updateDatabaseWithChangedItems(const HashMap<String, String>& changedItems)
+{
+ if (!m_database.isOpen())
+ openDatabase(CreateIfNonExistent);
+ if (!m_database.isOpen())
+ return;
+
+ if (m_shouldClearItems) {
+ m_shouldClearItems = false;
+
+ SQLiteStatement clearStatement(m_database, "DELETE FROM ItemTable");
+ if (clearStatement.prepare() != SQLResultOk) {
+ LOG_ERROR("Failed to prepare clear statement - cannot write to local storage database");
+ return;
+ }
+
+ int result = clearStatement.step();
+ if (result != SQLResultDone) {
+ LOG_ERROR("Failed to clear all items in the local storage database - %i", result);
+ return;
+ }
+ }
+
+ SQLiteStatement insertStatement(m_database, "INSERT INTO ItemTable VALUES (?, ?)");
+ if (insertStatement.prepare() != SQLResultOk) {
+ LOG_ERROR("Failed to prepare insert statement - cannot write to local storage database");
+ return;
+ }
+
+ SQLiteStatement deleteStatement(m_database, "DELETE FROM ItemTable WHERE key=?");
+ if (deleteStatement.prepare() != SQLResultOk) {
+ LOG_ERROR("Failed to prepare delete statement - cannot write to local storage database");
+ return;
+ }
+
+ SQLiteTransaction transaction(m_database);
+ transaction.begin();
+
+ for (auto it = changedItems.begin(), end = changedItems.end(); it != end; ++it) {
+ // A null value means that the key/value pair should be deleted.
+ SQLiteStatement& statement = it->value.isNull() ? deleteStatement : insertStatement;
+
+ statement.bindText(1, it->key);
+
+ // If we're inserting a key/value pair, bind the value as well.
+ if (!it->value.isNull())
+ statement.bindBlob(2, it->value);
+
+ int result = statement.step();
+ if (result != SQLResultDone) {
+ LOG_ERROR("Failed to update item in the local storage database - %i", result);
+ break;
+ }
+
+ statement.reset();
+ }
+
+ transaction.commit();
+}
+
+bool LocalStorageDatabase::databaseIsEmpty()
+{
+ if (!m_database.isOpen())
+ return false;
+
+ SQLiteStatement query(m_database, "SELECT COUNT(*) FROM ItemTable");
+ if (query.prepare() != SQLResultOk) {
+ LOG_ERROR("Unable to count number of rows in ItemTable for local storage");
+ return false;
+ }
+
+ int result = query.step();
+ if (result != SQLResultRow) {
+ LOG_ERROR("No results when counting number of rows in ItemTable for local storage");
+ return false;
+ }
+
+ return !query.getColumnInt(0);
+}
+
+} // namespace WebKit
diff --git a/Source/WebKit2/UIProcess/Storage/LocalStorageDatabase.h b/Source/WebKit2/UIProcess/Storage/LocalStorageDatabase.h
new file mode 100644
index 000000000..1acd6b253
--- /dev/null
+++ b/Source/WebKit2/UIProcess/Storage/LocalStorageDatabase.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2008, 2009, 2010, 2013 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. 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 INC. 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 LocalStorageDatabase_h
+#define LocalStorageDatabase_h
+
+#include <WebCore/SQLiteDatabase.h>
+#include <wtf/Forward.h>
+#include <wtf/HashMap.h>
+#include <wtf/RefPtr.h>
+#include <wtf/ThreadSafeRefCounted.h>
+
+class WorkQueue;
+
+namespace WebCore {
+class SecurityOrigin;
+class StorageMap;
+}
+
+namespace WebKit {
+
+class LocalStorageDatabaseTracker;
+
+class LocalStorageDatabase : public ThreadSafeRefCounted<LocalStorageDatabase> {
+public:
+ static PassRefPtr<LocalStorageDatabase> create(PassRefPtr<WorkQueue>, PassRefPtr<LocalStorageDatabaseTracker>, PassRefPtr<WebCore::SecurityOrigin>);
+ ~LocalStorageDatabase();
+
+ // Will block until the import is complete.
+ void importItems(WebCore::StorageMap&);
+
+ void setItem(const String& key, const String& value);
+ void removeItem(const String& key);
+ void clear();
+
+ // Will block until all pending changes have been written to disk.
+ void close();
+
+private:
+ LocalStorageDatabase(PassRefPtr<WorkQueue>, PassRefPtr<LocalStorageDatabaseTracker>, PassRefPtr<WebCore::SecurityOrigin>);
+
+ enum DatabaseOpeningStrategy {
+ CreateIfNonExistent,
+ SkipIfNonExistent
+ };
+ bool tryToOpenDatabase(DatabaseOpeningStrategy);
+ void openDatabase(DatabaseOpeningStrategy);
+
+ bool migrateItemTableIfNeeded();
+
+ void itemDidChange(const String& key, const String& value);
+
+ void scheduleDatabaseUpdate();
+ void updateDatabase();
+ void updateDatabaseWithChangedItems(const HashMap<String, String>&);
+
+ bool databaseIsEmpty();
+
+ RefPtr<WorkQueue> m_queue;
+ RefPtr<LocalStorageDatabaseTracker> m_tracker;
+ RefPtr<WebCore::SecurityOrigin> m_securityOrigin;
+
+ String m_databasePath;
+ WebCore::SQLiteDatabase m_database;
+ bool m_failedToOpenDatabase;
+ bool m_didImportItems;
+ bool m_isClosed;
+
+ bool m_didScheduleDatabaseUpdate;
+ bool m_shouldClearItems;
+ HashMap<String, String> m_changedItems;
+};
+
+
+} // namespace WebKit
+
+#endif // LocalStorageDatabase_h
diff --git a/Source/WebKit2/UIProcess/Storage/LocalStorageDatabaseTracker.cpp b/Source/WebKit2/UIProcess/Storage/LocalStorageDatabaseTracker.cpp
new file mode 100644
index 000000000..f58fe8a79
--- /dev/null
+++ b/Source/WebKit2/UIProcess/Storage/LocalStorageDatabaseTracker.cpp
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2011, 2013 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. 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 INC. 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 "LocalStorageDatabaseTracker.h"
+
+#include "WorkQueue.h"
+#include <WebCore/FileSystem.h>
+#include <WebCore/SQLiteStatement.h>
+#include <WebCore/SecurityOrigin.h>
+#include <wtf/text/CString.h>
+
+using namespace WebCore;
+
+namespace WebKit {
+
+PassRefPtr<LocalStorageDatabaseTracker> LocalStorageDatabaseTracker::create(PassRefPtr<WorkQueue> queue)
+{
+ return adoptRef(new LocalStorageDatabaseTracker(queue));
+}
+
+LocalStorageDatabaseTracker::LocalStorageDatabaseTracker(PassRefPtr<WorkQueue> queue)
+ : m_queue(queue)
+{
+}
+
+LocalStorageDatabaseTracker::~LocalStorageDatabaseTracker()
+{
+}
+
+void LocalStorageDatabaseTracker::setLocalStorageDirectory(const String& localStorageDirectory)
+{
+ // FIXME: We should come up with a better idiom for safely copying strings across threads.
+ RefPtr<StringImpl> copiedLocalStorageDirectory = localStorageDirectory.impl() ? localStorageDirectory.impl()->isolatedCopy() : nullptr;
+
+ m_queue->dispatch(bind(&LocalStorageDatabaseTracker::setLocalStorageDirectoryInternal, this, copiedLocalStorageDirectory.release()));
+}
+
+String LocalStorageDatabaseTracker::databasePath(SecurityOrigin* securityOrigin) const
+{
+ return databasePath(securityOrigin->databaseIdentifier() + ".localstorage");
+}
+
+void LocalStorageDatabaseTracker::didOpenDatabaseWithOrigin(SecurityOrigin* securityOrigin)
+{
+ addDatabaseWithOriginIdentifier(securityOrigin->databaseIdentifier(), databasePath(securityOrigin));
+}
+
+void LocalStorageDatabaseTracker::deleteDatabaseWithOrigin(SecurityOrigin* securityOrigin)
+{
+ removeDatabaseWithOriginIdentifier(securityOrigin->databaseIdentifier());
+}
+
+void LocalStorageDatabaseTracker::deleteAllDatabases()
+{
+ m_origins.clear();
+
+ openTrackerDatabase(SkipIfNonExistent);
+ if (!m_database.isOpen())
+ return;
+
+ SQLiteStatement statement(m_database, "SELECT origin, path FROM Origins");
+ if (statement.prepare() != SQLResultOk) {
+ LOG_ERROR("Failed to prepare statement.");
+ return;
+ }
+
+ int result;
+ while ((result = statement.step()) == SQLResultRow) {
+ deleteFile(statement.getColumnText(1));
+
+ // FIXME: Call out to the client.
+ }
+
+ if (result != SQLResultDone)
+ LOG_ERROR("Failed to read in all origins from the database.");
+
+ if (m_database.isOpen())
+ m_database.close();
+
+ if (!deleteFile(trackerDatabasePath())) {
+ // In the case where it is not possible to delete the database file (e.g some other program
+ // like a virus scanner is accessing it), make sure to remove all entries.
+ openTrackerDatabase(SkipIfNonExistent);
+ if (!m_database.isOpen())
+ return;
+
+ SQLiteStatement deleteStatement(m_database, "DELETE FROM Origins");
+ if (deleteStatement.prepare() != SQLResultOk) {
+ LOG_ERROR("Unable to prepare deletion of all origins");
+ return;
+ }
+ if (!deleteStatement.executeCommand()) {
+ LOG_ERROR("Unable to execute deletion of all origins");
+ return;
+ }
+ }
+
+ deleteEmptyDirectory(m_localStorageDirectory);
+}
+
+Vector<RefPtr<WebCore::SecurityOrigin>> LocalStorageDatabaseTracker::origins() const
+{
+ Vector<RefPtr<SecurityOrigin>> origins;
+ origins.reserveInitialCapacity(m_origins.size());
+
+ for (HashSet<String>::const_iterator it = m_origins.begin(), end = m_origins.end(); it != end; ++it)
+ origins.uncheckedAppend(SecurityOrigin::createFromDatabaseIdentifier(*it));
+
+ return origins;
+}
+
+void LocalStorageDatabaseTracker::setLocalStorageDirectoryInternal(StringImpl* localStorageDirectory)
+{
+ if (m_database.isOpen())
+ m_database.close();
+
+ m_localStorageDirectory = localStorageDirectory;
+ m_origins.clear();
+
+ m_queue->dispatch(bind(&LocalStorageDatabaseTracker::importOriginIdentifiers, this));
+}
+
+String LocalStorageDatabaseTracker::databasePath(const String& filename) const
+{
+ if (!makeAllDirectories(m_localStorageDirectory)) {
+ LOG_ERROR("Unabled to create LocalStorage database path %s", m_localStorageDirectory.utf8().data());
+ return String();
+ }
+
+ return pathByAppendingComponent(m_localStorageDirectory, filename);
+}
+
+String LocalStorageDatabaseTracker::trackerDatabasePath() const
+{
+ return databasePath("StorageTracker.db");
+}
+
+void LocalStorageDatabaseTracker::openTrackerDatabase(DatabaseOpeningStrategy openingStrategy)
+{
+ if (m_database.isOpen())
+ return;
+
+ String databasePath = trackerDatabasePath();
+
+ if (!fileExists(databasePath) && openingStrategy == SkipIfNonExistent)
+ return;
+
+ if (!m_database.open(databasePath)) {
+ LOG_ERROR("Failed to open databasePath %s.", databasePath.ascii().data());
+ return;
+ }
+
+ // Since a WorkQueue isn't bound to a specific thread, we have to disable threading checks
+ // even though we never access the database from different threads simultaneously.
+ m_database.disableThreadingChecks();
+
+ if (m_database.tableExists("Origins"))
+ return;
+
+ if (!m_database.executeCommand("CREATE TABLE Origins (origin TEXT UNIQUE ON CONFLICT REPLACE, path TEXT);"))
+ LOG_ERROR("Failed to create Origins table.");
+}
+
+void LocalStorageDatabaseTracker::importOriginIdentifiers()
+{
+ openTrackerDatabase(SkipIfNonExistent);
+
+ if (m_database.isOpen()) {
+ SQLiteStatement statement(m_database, "SELECT origin FROM Origins");
+ if (statement.prepare() != SQLResultOk) {
+ LOG_ERROR("Failed to prepare statement.");
+ return;
+ }
+
+ int result;
+
+ while ((result = statement.step()) == SQLResultRow)
+ m_origins.add(statement.getColumnText(0));
+
+ if (result != SQLResultDone) {
+ LOG_ERROR("Failed to read in all origins from the database.");
+ return;
+ }
+ }
+
+ updateTrackerDatabaseFromLocalStorageDatabaseFiles();
+}
+
+void LocalStorageDatabaseTracker::updateTrackerDatabaseFromLocalStorageDatabaseFiles()
+{
+ Vector<String> paths = listDirectory(m_localStorageDirectory, "*.localstorage");
+
+ HashSet<String> origins(m_origins);
+ HashSet<String> originsFromLocalStorageDatabaseFiles;
+
+ for (size_t i = 0; i < paths.size(); ++i) {
+ const String& path = paths[i];
+
+ if (!path.endsWith(".localstorage"))
+ continue;
+
+ String filename = pathGetFileName(path);
+
+ String originIdentifier = filename.substring(0, filename.length() - strlen(".localstorage"));
+
+ if (!m_origins.contains(originIdentifier))
+ addDatabaseWithOriginIdentifier(originIdentifier, path);
+
+ originsFromLocalStorageDatabaseFiles.add(originIdentifier);
+ }
+
+ for (auto it = origins.begin(), end = origins.end(); it != end; ++it) {
+ const String& originIdentifier = *it;
+ if (origins.contains(originIdentifier))
+ continue;
+
+ removeDatabaseWithOriginIdentifier(originIdentifier);
+ }
+}
+
+void LocalStorageDatabaseTracker::addDatabaseWithOriginIdentifier(const String& originIdentifier, const String& databasePath)
+{
+ openTrackerDatabase(CreateIfNonExistent);
+ if (!m_database.isOpen())
+ return;
+
+ SQLiteStatement statement(m_database, "INSERT INTO Origins VALUES (?, ?)");
+ if (statement.prepare() != SQLResultOk) {
+ LOG_ERROR("Unable to establish origin '%s' in the tracker", originIdentifier.utf8().data());
+ return;
+ }
+
+ statement.bindText(1, originIdentifier);
+ statement.bindText(2, databasePath);
+
+ if (statement.step() != SQLResultDone)
+ LOG_ERROR("Unable to establish origin '%s' in the tracker", originIdentifier.utf8().data());
+
+ m_origins.add(originIdentifier);
+
+ // FIXME: Tell clients that the origin was added.
+}
+
+void LocalStorageDatabaseTracker::removeDatabaseWithOriginIdentifier(const String& originIdentifier)
+{
+ openTrackerDatabase(SkipIfNonExistent);
+ if (!m_database.isOpen())
+ return;
+
+ String path = pathForDatabaseWithOriginIdentifier(originIdentifier);
+ if (path.isEmpty())
+ return;
+
+ SQLiteStatement deleteStatement(m_database, "DELETE FROM Origins where origin=?");
+ if (deleteStatement.prepare() != SQLResultOk) {
+ LOG_ERROR("Unable to prepare deletion of origin '%s'", originIdentifier.ascii().data());
+ return;
+ }
+ deleteStatement.bindText(1, originIdentifier);
+ if (!deleteStatement.executeCommand()) {
+ LOG_ERROR("Unable to execute deletion of origin '%s'", originIdentifier.ascii().data());
+ return;
+ }
+
+ deleteFile(path);
+
+ m_origins.remove(originIdentifier);
+ if (m_origins.isEmpty()) {
+ // There are no origins left, go ahead and delete the tracker database.
+ m_database.close();
+ deleteFile(trackerDatabasePath());
+ deleteEmptyDirectory(m_localStorageDirectory);
+ }
+
+ // FIXME: Tell clients that the origin was removed.
+}
+
+String LocalStorageDatabaseTracker::pathForDatabaseWithOriginIdentifier(const String& originIdentifier)
+{
+ if (!m_database.isOpen())
+ return String();
+
+ SQLiteStatement pathStatement(m_database, "SELECT path FROM Origins WHERE origin=?");
+ if (pathStatement.prepare() != SQLResultOk) {
+ LOG_ERROR("Unable to prepare selection of path for origin '%s'", originIdentifier.utf8().data());
+ return String();
+ }
+
+ pathStatement.bindText(1, originIdentifier);
+
+ int result = pathStatement.step();
+ if (result != SQLResultRow)
+ return String();
+
+ return pathStatement.getColumnText(0);
+}
+
+} // namespace WebKit
diff --git a/Source/WebKit2/UIProcess/Storage/LocalStorageDatabaseTracker.h b/Source/WebKit2/UIProcess/Storage/LocalStorageDatabaseTracker.h
new file mode 100644
index 000000000..e16428907
--- /dev/null
+++ b/Source/WebKit2/UIProcess/Storage/LocalStorageDatabaseTracker.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2011, 2013 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. 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 INC. 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 LocalStorageDatabaseTracker_h
+#define LocalStorageDatabaseTracker_h
+
+#include <WebCore/SQLiteDatabase.h>
+#include <wtf/HashSet.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefPtr.h>
+#include <wtf/ThreadSafeRefCounted.h>
+#include <wtf/text/StringHash.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+class SecurityOrigin;
+}
+
+class WorkQueue;
+
+namespace WebKit {
+
+class LocalStorageDatabaseTracker : public ThreadSafeRefCounted<LocalStorageDatabaseTracker> {
+public:
+ static PassRefPtr<LocalStorageDatabaseTracker> create(PassRefPtr<WorkQueue>);
+ ~LocalStorageDatabaseTracker();
+
+ void setLocalStorageDirectory(const String&);
+ String databasePath(WebCore::SecurityOrigin*) const;
+
+ void didOpenDatabaseWithOrigin(WebCore::SecurityOrigin*);
+ void deleteDatabaseWithOrigin(WebCore::SecurityOrigin*);
+ void deleteAllDatabases();
+
+ Vector<RefPtr<WebCore::SecurityOrigin>> origins() const;
+
+private:
+ explicit LocalStorageDatabaseTracker(PassRefPtr<WorkQueue>);
+
+ void setLocalStorageDirectoryInternal(StringImpl*);
+
+ String databasePath(const String& filename) const;
+ String trackerDatabasePath() const;
+
+ enum DatabaseOpeningStrategy {
+ CreateIfNonExistent,
+ SkipIfNonExistent
+ };
+ void openTrackerDatabase(DatabaseOpeningStrategy);
+
+ void importOriginIdentifiers();
+ void updateTrackerDatabaseFromLocalStorageDatabaseFiles();
+
+ void addDatabaseWithOriginIdentifier(const String& originIdentifier, const String& databasePath);
+ void removeDatabaseWithOriginIdentifier(const String& originIdentifier);
+ String pathForDatabaseWithOriginIdentifier(const String& originIdentifier);
+
+ RefPtr<WorkQueue> m_queue;
+ String m_localStorageDirectory;
+
+ WebCore::SQLiteDatabase m_database;
+ HashSet<String> m_origins;
+};
+
+} // namespace WebKit
+
+#endif // LocalStorageDatabaseTracker_h
diff --git a/Source/WebKit2/UIProcess/Storage/StorageManager.cpp b/Source/WebKit2/UIProcess/Storage/StorageManager.cpp
new file mode 100644
index 000000000..5350dde05
--- /dev/null
+++ b/Source/WebKit2/UIProcess/Storage/StorageManager.cpp
@@ -0,0 +1,655 @@
+/*
+ * Copyright (C) 2013 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. 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 INC. 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 "StorageManager.h"
+
+#include "LocalStorageDatabase.h"
+#include "LocalStorageDatabaseTracker.h"
+#include "SecurityOriginData.h"
+#include "StorageAreaMapMessages.h"
+#include "StorageManagerMessages.h"
+#include "WebProcessProxy.h"
+#include "WorkQueue.h"
+#include <WebCore/SecurityOriginHash.h>
+#include <WebCore/StorageMap.h>
+#include <WebCore/TextEncoding.h>
+
+using namespace WebCore;
+
+namespace WebKit {
+
+class StorageManager::StorageArea : public ThreadSafeRefCounted<StorageManager::StorageArea> {
+public:
+ static PassRefPtr<StorageArea> create(LocalStorageNamespace*, PassRefPtr<SecurityOrigin>, unsigned quotaInBytes);
+ ~StorageArea();
+
+ SecurityOrigin* securityOrigin() const { return m_securityOrigin.get(); }
+
+ void addListener(CoreIPC::Connection*, uint64_t storageMapID);
+ void removeListener(CoreIPC::Connection*, uint64_t storageMapID);
+
+ PassRefPtr<StorageArea> clone() const;
+
+ void setItem(CoreIPC::Connection* sourceConnection, uint64_t sourceStorageAreaID, const String& key, const String& value, const String& urlString, bool& quotaException);
+ void removeItem(CoreIPC::Connection* sourceConnection, uint64_t sourceStorageAreaID, const String& key, const String& urlString);
+ void clear(CoreIPC::Connection* sourceConnection, uint64_t sourceStorageAreaID, const String& urlString);
+
+ const HashMap<String, String>& items();
+ void clear();
+
+private:
+ explicit StorageArea(LocalStorageNamespace*, PassRefPtr<SecurityOrigin>, unsigned quotaInBytes);
+
+ void openDatabaseAndImportItemsIfNeeded();
+
+ void dispatchEvents(CoreIPC::Connection* sourceConnection, uint64_t sourceStorageAreaID, const String& key, const String& oldValue, const String& newValue, const String& urlString) const;
+
+ // Will be null if the storage area belongs to a session storage namespace.
+ LocalStorageNamespace* m_localStorageNamespace;
+ RefPtr<LocalStorageDatabase> m_localStorageDatabase;
+ bool m_didImportItemsFromDatabase;
+
+ RefPtr<SecurityOrigin> m_securityOrigin;
+ unsigned m_quotaInBytes;
+
+ RefPtr<StorageMap> m_storageMap;
+ HashSet<std::pair<RefPtr<CoreIPC::Connection>, uint64_t>> m_eventListeners;
+};
+
+class StorageManager::LocalStorageNamespace : public ThreadSafeRefCounted<LocalStorageNamespace> {
+public:
+ static PassRefPtr<LocalStorageNamespace> create(StorageManager*, uint64_t storageManagerID);
+ ~LocalStorageNamespace();
+
+ StorageManager* storageManager() const { return m_storageManager; }
+
+ PassRefPtr<StorageArea> getOrCreateStorageArea(PassRefPtr<SecurityOrigin>);
+ void didDestroyStorageArea(StorageArea*);
+
+ void clearStorageAreasMatchingOrigin(SecurityOrigin*);
+ void clearAllStorageAreas();
+
+private:
+ explicit LocalStorageNamespace(StorageManager*, uint64_t storageManagerID);
+
+ StorageManager* m_storageManager;
+ uint64_t m_storageNamespaceID;
+ unsigned m_quotaInBytes;
+
+ // We don't hold an explicit reference to the StorageAreas; they are kept alive by the m_storageAreasByConnection map in StorageManager.
+ HashMap<RefPtr<SecurityOrigin>, StorageArea*> m_storageAreaMap;
+};
+
+PassRefPtr<StorageManager::StorageArea> StorageManager::StorageArea::create(LocalStorageNamespace* localStorageNamespace, PassRefPtr<SecurityOrigin> securityOrigin, unsigned quotaInBytes)
+{
+ return adoptRef(new StorageArea(localStorageNamespace, securityOrigin, quotaInBytes));
+}
+
+StorageManager::StorageArea::StorageArea(LocalStorageNamespace* localStorageNamespace, PassRefPtr<SecurityOrigin> securityOrigin, unsigned quotaInBytes)
+ : m_localStorageNamespace(localStorageNamespace)
+ , m_didImportItemsFromDatabase(false)
+ , m_securityOrigin(securityOrigin)
+ , m_quotaInBytes(quotaInBytes)
+ , m_storageMap(StorageMap::create(m_quotaInBytes))
+{
+}
+
+StorageManager::StorageArea::~StorageArea()
+{
+ ASSERT(m_eventListeners.isEmpty());
+
+ if (m_localStorageDatabase)
+ m_localStorageDatabase->close();
+
+ if (m_localStorageNamespace)
+ m_localStorageNamespace->didDestroyStorageArea(this);
+}
+
+void StorageManager::StorageArea::addListener(CoreIPC::Connection* connection, uint64_t storageMapID)
+{
+ ASSERT(!m_eventListeners.contains(std::make_pair(connection, storageMapID)));
+ m_eventListeners.add(std::make_pair(connection, storageMapID));
+}
+
+void StorageManager::StorageArea::removeListener(CoreIPC::Connection* connection, uint64_t storageMapID)
+{
+ ASSERT(m_eventListeners.contains(std::make_pair(connection, storageMapID)));
+ m_eventListeners.remove(std::make_pair(connection, storageMapID));
+}
+
+PassRefPtr<StorageManager::StorageArea> StorageManager::StorageArea::clone() const
+{
+ ASSERT(!m_localStorageNamespace);
+
+ RefPtr<StorageArea> storageArea = StorageArea::create(0, m_securityOrigin, m_quotaInBytes);
+ storageArea->m_storageMap = m_storageMap;
+
+ return storageArea.release();
+}
+
+void StorageManager::StorageArea::setItem(CoreIPC::Connection* sourceConnection, uint64_t sourceStorageAreaID, const String& key, const String& value, const String& urlString, bool& quotaException)
+{
+ openDatabaseAndImportItemsIfNeeded();
+
+ String oldValue;
+
+ RefPtr<StorageMap> newStorageMap = m_storageMap->setItem(key, value, oldValue, quotaException);
+ if (newStorageMap)
+ m_storageMap = newStorageMap.release();
+
+ if (quotaException)
+ return;
+
+ if (m_localStorageDatabase)
+ m_localStorageDatabase->setItem(key, value);
+
+ dispatchEvents(sourceConnection, sourceStorageAreaID, key, oldValue, value, urlString);
+}
+
+void StorageManager::StorageArea::removeItem(CoreIPC::Connection* sourceConnection, uint64_t sourceStorageAreaID, const String& key, const String& urlString)
+{
+ openDatabaseAndImportItemsIfNeeded();
+
+ String oldValue;
+ RefPtr<StorageMap> newStorageMap = m_storageMap->removeItem(key, oldValue);
+ if (newStorageMap)
+ m_storageMap = newStorageMap.release();
+
+ if (oldValue.isNull())
+ return;
+
+ if (m_localStorageDatabase)
+ m_localStorageDatabase->removeItem(key);
+
+ dispatchEvents(sourceConnection, sourceStorageAreaID, key, oldValue, String(), urlString);
+}
+
+void StorageManager::StorageArea::clear(CoreIPC::Connection* sourceConnection, uint64_t sourceStorageAreaID, const String& urlString)
+{
+ openDatabaseAndImportItemsIfNeeded();
+
+ if (!m_storageMap->length())
+ return;
+
+ m_storageMap = StorageMap::create(m_quotaInBytes);
+
+ if (m_localStorageDatabase)
+ m_localStorageDatabase->clear();
+
+ dispatchEvents(sourceConnection, sourceStorageAreaID, String(), String(), String(), urlString);
+}
+
+const HashMap<String, String>& StorageManager::StorageArea::items()
+{
+ openDatabaseAndImportItemsIfNeeded();
+
+ return m_storageMap->items();
+}
+
+void StorageManager::StorageArea::clear()
+{
+ m_storageMap = StorageMap::create(m_quotaInBytes);
+
+ if (m_localStorageDatabase) {
+ m_localStorageDatabase->close();
+ m_localStorageDatabase = nullptr;
+ }
+
+ for (auto it = m_eventListeners.begin(), end = m_eventListeners.end(); it != end; ++it)
+ it->first->send(Messages::StorageAreaMap::ClearCache(), it->second);
+}
+
+void StorageManager::StorageArea::openDatabaseAndImportItemsIfNeeded()
+{
+ if (!m_localStorageNamespace)
+ return;
+
+ // We open the database here even if we've already imported our items to ensure that the database is open if we need to write to it.
+ if (!m_localStorageDatabase)
+ m_localStorageDatabase = LocalStorageDatabase::create(m_localStorageNamespace->storageManager()->m_queue, m_localStorageNamespace->storageManager()->m_localStorageDatabaseTracker, m_securityOrigin.get());
+
+ if (m_didImportItemsFromDatabase)
+ return;
+
+ m_localStorageDatabase->importItems(*m_storageMap);
+ m_didImportItemsFromDatabase = true;
+}
+
+void StorageManager::StorageArea::dispatchEvents(CoreIPC::Connection* sourceConnection, uint64_t sourceStorageAreaID, const String& key, const String& oldValue, const String& newValue, const String& urlString) const
+{
+ for (HashSet<std::pair<RefPtr<CoreIPC::Connection>, uint64_t>>::const_iterator it = m_eventListeners.begin(), end = m_eventListeners.end(); it != end; ++it) {
+ uint64_t storageAreaID = it->first == sourceConnection ? sourceStorageAreaID : 0;
+
+ it->first->send(Messages::StorageAreaMap::DispatchStorageEvent(storageAreaID, key, oldValue, newValue, urlString), it->second);
+ }
+}
+
+PassRefPtr<StorageManager::LocalStorageNamespace> StorageManager::LocalStorageNamespace::create(StorageManager* storageManager, uint64_t storageNamespaceID)
+{
+ return adoptRef(new LocalStorageNamespace(storageManager, storageNamespaceID));
+}
+
+// FIXME: The quota value is copied from GroupSettings.cpp.
+// We should investigate a way to share it with WebCore.
+StorageManager::LocalStorageNamespace::LocalStorageNamespace(StorageManager* storageManager, uint64_t storageNamespaceID)
+ : m_storageManager(storageManager)
+ , m_storageNamespaceID(storageNamespaceID)
+ , m_quotaInBytes(5 * 1024 * 1024)
+{
+}
+
+StorageManager::LocalStorageNamespace::~LocalStorageNamespace()
+{
+ ASSERT(m_storageAreaMap.isEmpty());
+}
+
+PassRefPtr<StorageManager::StorageArea> StorageManager::LocalStorageNamespace::getOrCreateStorageArea(PassRefPtr<SecurityOrigin> securityOrigin)
+{
+ HashMap<RefPtr<SecurityOrigin>, StorageArea*>::AddResult result = m_storageAreaMap.add(securityOrigin, 0);
+ if (!result.isNewEntry)
+ return result.iterator->value;
+
+ RefPtr<StorageArea> storageArea = StorageArea::create(this, result.iterator->key, m_quotaInBytes);
+ result.iterator->value = storageArea.get();
+
+ return storageArea.release();
+}
+
+void StorageManager::LocalStorageNamespace::didDestroyStorageArea(StorageArea* storageArea)
+{
+ ASSERT(m_storageAreaMap.contains(storageArea->securityOrigin()));
+
+ m_storageAreaMap.remove(storageArea->securityOrigin());
+ if (!m_storageAreaMap.isEmpty())
+ return;
+
+ ASSERT(m_storageManager->m_localStorageNamespaces.contains(m_storageNamespaceID));
+ m_storageManager->m_localStorageNamespaces.remove(m_storageNamespaceID);
+}
+
+void StorageManager::LocalStorageNamespace::clearStorageAreasMatchingOrigin(SecurityOrigin* securityOrigin)
+{
+ for (auto it = m_storageAreaMap.begin(), end = m_storageAreaMap.end(); it != end; ++it) {
+ if (it->key->equal(securityOrigin))
+ it->value->clear();
+ }
+}
+
+void StorageManager::LocalStorageNamespace::clearAllStorageAreas()
+{
+ for (auto it = m_storageAreaMap.begin(), end = m_storageAreaMap.end(); it != end; ++it)
+ it->value->clear();
+}
+
+class StorageManager::SessionStorageNamespace : public ThreadSafeRefCounted<SessionStorageNamespace> {
+public:
+ static PassRefPtr<SessionStorageNamespace> create(CoreIPC::Connection* allowedConnection, unsigned quotaInBytes);
+ ~SessionStorageNamespace();
+
+ bool isEmpty() const { return m_storageAreaMap.isEmpty(); }
+
+ CoreIPC::Connection* allowedConnection() const { return m_allowedConnection.get(); }
+ void setAllowedConnection(CoreIPC::Connection*);
+
+ PassRefPtr<StorageArea> getOrCreateStorageArea(PassRefPtr<SecurityOrigin>);
+
+ void cloneTo(SessionStorageNamespace& newSessionStorageNamespace);
+
+private:
+ SessionStorageNamespace(CoreIPC::Connection* allowedConnection, unsigned quotaInBytes);
+
+ RefPtr<CoreIPC::Connection> m_allowedConnection;
+ unsigned m_quotaInBytes;
+
+ HashMap<RefPtr<SecurityOrigin>, RefPtr<StorageArea>> m_storageAreaMap;
+};
+
+PassRefPtr<StorageManager::SessionStorageNamespace> StorageManager::SessionStorageNamespace::create(CoreIPC::Connection* allowedConnection, unsigned quotaInBytes)
+{
+ return adoptRef(new SessionStorageNamespace(allowedConnection, quotaInBytes));
+}
+
+StorageManager::SessionStorageNamespace::SessionStorageNamespace(CoreIPC::Connection* allowedConnection, unsigned quotaInBytes)
+ : m_allowedConnection(allowedConnection)
+ , m_quotaInBytes(quotaInBytes)
+{
+}
+
+StorageManager::SessionStorageNamespace::~SessionStorageNamespace()
+{
+}
+
+void StorageManager::SessionStorageNamespace::setAllowedConnection(CoreIPC::Connection* allowedConnection)
+{
+ ASSERT(!allowedConnection || !m_allowedConnection);
+
+ m_allowedConnection = allowedConnection;
+}
+
+PassRefPtr<StorageManager::StorageArea> StorageManager::SessionStorageNamespace::getOrCreateStorageArea(PassRefPtr<SecurityOrigin> securityOrigin)
+{
+ HashMap<RefPtr<SecurityOrigin>, RefPtr<StorageArea>>::AddResult result = m_storageAreaMap.add(securityOrigin, 0);
+ if (result.isNewEntry)
+ result.iterator->value = StorageArea::create(0, result.iterator->key, m_quotaInBytes);
+
+ return result.iterator->value;
+}
+
+void StorageManager::SessionStorageNamespace::cloneTo(SessionStorageNamespace& newSessionStorageNamespace)
+{
+ ASSERT_UNUSED(newSessionStorageNamespace, newSessionStorageNamespace.isEmpty());
+
+ for (HashMap<RefPtr<SecurityOrigin>, RefPtr<StorageArea>>::const_iterator it = m_storageAreaMap.begin(), end = m_storageAreaMap.end(); it != end; ++it)
+ newSessionStorageNamespace.m_storageAreaMap.add(it->key, it->value->clone());
+}
+
+PassRefPtr<StorageManager> StorageManager::create()
+{
+ return adoptRef(new StorageManager);
+}
+
+StorageManager::StorageManager()
+ : m_queue(WorkQueue::create("com.apple.WebKit.StorageManager"))
+ , m_localStorageDatabaseTracker(LocalStorageDatabaseTracker::create(m_queue))
+{
+ // Make sure the encoding is initialized before we start dispatching things to the queue.
+ UTF8Encoding();
+}
+
+StorageManager::~StorageManager()
+{
+}
+
+void StorageManager::setLocalStorageDirectory(const String& localStorageDirectory)
+{
+ m_localStorageDatabaseTracker->setLocalStorageDirectory(localStorageDirectory);
+}
+
+void StorageManager::createSessionStorageNamespace(uint64_t storageNamespaceID, CoreIPC::Connection* allowedConnection, unsigned quotaInBytes)
+{
+ m_queue->dispatch(bind(&StorageManager::createSessionStorageNamespaceInternal, this, storageNamespaceID, RefPtr<CoreIPC::Connection>(allowedConnection), quotaInBytes));
+}
+
+void StorageManager::destroySessionStorageNamespace(uint64_t storageNamespaceID)
+{
+ m_queue->dispatch(bind(&StorageManager::destroySessionStorageNamespaceInternal, this, storageNamespaceID));
+}
+
+void StorageManager::setAllowedSessionStorageNamespaceConnection(uint64_t storageNamespaceID, CoreIPC::Connection* allowedConnection)
+{
+ m_queue->dispatch(bind(&StorageManager::setAllowedSessionStorageNamespaceConnectionInternal, this, storageNamespaceID, RefPtr<CoreIPC::Connection>(allowedConnection)));
+}
+
+void StorageManager::cloneSessionStorageNamespace(uint64_t storageNamespaceID, uint64_t newStorageNamespaceID)
+{
+ m_queue->dispatch(bind(&StorageManager::cloneSessionStorageNamespaceInternal, this, storageNamespaceID, newStorageNamespaceID));
+}
+
+void StorageManager::processWillOpenConnection(WebProcessProxy* webProcessProxy)
+{
+ webProcessProxy->connection()->addWorkQueueMessageReceiver(Messages::StorageManager::messageReceiverName(), m_queue.get(), this);
+}
+
+void StorageManager::processWillCloseConnection(WebProcessProxy* webProcessProxy)
+{
+ webProcessProxy->connection()->removeWorkQueueMessageReceiver(Messages::StorageManager::messageReceiverName());
+
+ m_queue->dispatch(bind(&StorageManager::invalidateConnectionInternal, this, RefPtr<CoreIPC::Connection>(webProcessProxy->connection())));
+}
+
+void StorageManager::getOrigins(FunctionDispatcher* callbackDispatcher, void* context, void (*callback)(const Vector<RefPtr<WebCore::SecurityOrigin>>& securityOrigins, void* context))
+{
+ m_queue->dispatch(bind(&StorageManager::getOriginsInternal, this, RefPtr<FunctionDispatcher>(callbackDispatcher), context, callback));
+}
+
+void StorageManager::deleteEntriesForOrigin(SecurityOrigin* securityOrigin)
+{
+ m_queue->dispatch(bind(&StorageManager::deleteEntriesForOriginInternal, this, RefPtr<SecurityOrigin>(securityOrigin)));
+}
+
+void StorageManager::deleteAllEntries()
+{
+ m_queue->dispatch(bind(&StorageManager::deleteAllEntriesInternal, this));
+}
+
+void StorageManager::createLocalStorageMap(CoreIPC::Connection* connection, uint64_t storageMapID, uint64_t storageNamespaceID, const SecurityOriginData& securityOriginData)
+{
+ std::pair<RefPtr<CoreIPC::Connection>, uint64_t> connectionAndStorageMapIDPair(connection, storageMapID);
+
+ // FIXME: This should be a message check.
+ ASSERT((HashMap<std::pair<RefPtr<CoreIPC::Connection>, uint64_t>, RefPtr<StorageArea>>::isValidKey(connectionAndStorageMapIDPair)));
+
+ HashMap<std::pair<RefPtr<CoreIPC::Connection>, uint64_t>, RefPtr<StorageArea>>::AddResult result = m_storageAreasByConnection.add(connectionAndStorageMapIDPair, 0);
+
+ // FIXME: These should be a message checks.
+ ASSERT(result.isNewEntry);
+ ASSERT((HashMap<uint64_t, RefPtr<LocalStorageNamespace>>::isValidKey(storageNamespaceID)));
+
+ LocalStorageNamespace* localStorageNamespace = getOrCreateLocalStorageNamespace(storageNamespaceID);
+
+ // FIXME: This should be a message check.
+ ASSERT(localStorageNamespace);
+
+ RefPtr<StorageArea> storageArea = localStorageNamespace->getOrCreateStorageArea(securityOriginData.securityOrigin());
+ storageArea->addListener(connection, storageMapID);
+
+ result.iterator->value = storageArea.release();
+}
+
+void StorageManager::createSessionStorageMap(CoreIPC::Connection* connection, uint64_t storageMapID, uint64_t storageNamespaceID, const SecurityOriginData& securityOriginData)
+{
+ // FIXME: This should be a message check.
+ ASSERT((HashMap<uint64_t, RefPtr<SessionStorageNamespace>>::isValidKey(storageNamespaceID)));
+ SessionStorageNamespace* sessionStorageNamespace = m_sessionStorageNamespaces.get(storageNamespaceID);
+ if (!sessionStorageNamespace) {
+ // We're getting an incoming message from the web process that's for session storage for a web page
+ // that has already been closed, just ignore it.
+ return;
+ }
+
+ std::pair<RefPtr<CoreIPC::Connection>, uint64_t> connectionAndStorageMapIDPair(connection, storageMapID);
+
+ // FIXME: This should be a message check.
+ ASSERT((HashMap<std::pair<RefPtr<CoreIPC::Connection>, uint64_t>, RefPtr<StorageArea>>::isValidKey(connectionAndStorageMapIDPair)));
+
+ HashMap<std::pair<RefPtr<CoreIPC::Connection>, uint64_t>, RefPtr<StorageArea>>::AddResult result = m_storageAreasByConnection.add(connectionAndStorageMapIDPair, 0);
+
+ // FIXME: This should be a message check.
+ ASSERT(result.isNewEntry);
+
+ // FIXME: This should be a message check.
+ ASSERT(connection == sessionStorageNamespace->allowedConnection());
+
+ RefPtr<StorageArea> storageArea = sessionStorageNamespace->getOrCreateStorageArea(securityOriginData.securityOrigin());
+ storageArea->addListener(connection, storageMapID);
+
+ result.iterator->value = storageArea.release();
+}
+
+void StorageManager::destroyStorageMap(CoreIPC::Connection* connection, uint64_t storageMapID)
+{
+ std::pair<RefPtr<CoreIPC::Connection>, uint64_t> connectionAndStorageMapIDPair(connection, storageMapID);
+
+ // FIXME: This should be a message check.
+ ASSERT((HashMap<std::pair<RefPtr<CoreIPC::Connection>, uint64_t>, RefPtr<StorageArea>>::isValidKey(connectionAndStorageMapIDPair)));
+
+ HashMap<std::pair<RefPtr<CoreIPC::Connection>, uint64_t>, RefPtr<StorageArea>>::iterator it = m_storageAreasByConnection.find(connectionAndStorageMapIDPair);
+ if (it == m_storageAreasByConnection.end()) {
+ // The connection has been removed because the last page was closed.
+ return;
+ }
+
+ it->value->removeListener(connection, storageMapID);
+ m_storageAreasByConnection.remove(connectionAndStorageMapIDPair);
+}
+
+void StorageManager::getValues(CoreIPC::Connection* connection, uint64_t storageMapID, uint64_t storageMapSeed, HashMap<String, String>& values)
+{
+ StorageArea* storageArea = findStorageArea(connection, storageMapID);
+ if (!storageArea) {
+ // This is a session storage area for a page that has already been closed. Ignore it.
+ return;
+ }
+
+ values = storageArea->items();
+ connection->send(Messages::StorageAreaMap::DidGetValues(storageMapSeed), storageMapID);
+}
+
+void StorageManager::setItem(CoreIPC::Connection* connection, uint64_t storageMapID, uint64_t sourceStorageAreaID, uint64_t storageMapSeed, const String& key, const String& value, const String& urlString)
+{
+ StorageArea* storageArea = findStorageArea(connection, storageMapID);
+ if (!storageArea) {
+ // This is a session storage area for a page that has already been closed. Ignore it.
+ return;
+ }
+
+ bool quotaError;
+ storageArea->setItem(connection, sourceStorageAreaID, key, value, urlString, quotaError);
+ connection->send(Messages::StorageAreaMap::DidSetItem(storageMapSeed, key, quotaError), storageMapID);
+}
+
+void StorageManager::removeItem(CoreIPC::Connection* connection, uint64_t storageMapID, uint64_t sourceStorageAreaID, uint64_t storageMapSeed, const String& key, const String& urlString)
+{
+ StorageArea* storageArea = findStorageArea(connection, storageMapID);
+ if (!storageArea) {
+ // This is a session storage area for a page that has already been closed. Ignore it.
+ return;
+ }
+
+ storageArea->removeItem(connection, sourceStorageAreaID, key, urlString);
+ connection->send(Messages::StorageAreaMap::DidRemoveItem(storageMapSeed, key), storageMapID);
+}
+
+void StorageManager::clear(CoreIPC::Connection* connection, uint64_t storageMapID, uint64_t sourceStorageAreaID, uint64_t storageMapSeed, const String& urlString)
+{
+ StorageArea* storageArea = findStorageArea(connection, storageMapID);
+ if (!storageArea) {
+ // This is a session storage area for a page that has already been closed. Ignore it.
+ return;
+ }
+
+ storageArea->clear(connection, sourceStorageAreaID, urlString);
+ connection->send(Messages::StorageAreaMap::DidClear(storageMapSeed), storageMapID);
+}
+
+void StorageManager::createSessionStorageNamespaceInternal(uint64_t storageNamespaceID, CoreIPC::Connection* allowedConnection, unsigned quotaInBytes)
+{
+ ASSERT(!m_sessionStorageNamespaces.contains(storageNamespaceID));
+
+ m_sessionStorageNamespaces.set(storageNamespaceID, SessionStorageNamespace::create(allowedConnection, quotaInBytes));
+}
+
+void StorageManager::destroySessionStorageNamespaceInternal(uint64_t storageNamespaceID)
+{
+ ASSERT(m_sessionStorageNamespaces.contains(storageNamespaceID));
+ m_sessionStorageNamespaces.remove(storageNamespaceID);
+}
+
+void StorageManager::setAllowedSessionStorageNamespaceConnectionInternal(uint64_t storageNamespaceID, CoreIPC::Connection* allowedConnection)
+{
+ ASSERT(m_sessionStorageNamespaces.contains(storageNamespaceID));
+
+ m_sessionStorageNamespaces.get(storageNamespaceID)->setAllowedConnection(allowedConnection);
+}
+
+void StorageManager::cloneSessionStorageNamespaceInternal(uint64_t storageNamespaceID, uint64_t newStorageNamespaceID)
+{
+ SessionStorageNamespace* sessionStorageNamespace = m_sessionStorageNamespaces.get(storageNamespaceID);
+ ASSERT(sessionStorageNamespace);
+
+ SessionStorageNamespace* newSessionStorageNamespace = m_sessionStorageNamespaces.get(newStorageNamespaceID);
+ ASSERT(newSessionStorageNamespace);
+
+ sessionStorageNamespace->cloneTo(*newSessionStorageNamespace);
+}
+
+void StorageManager::invalidateConnectionInternal(CoreIPC::Connection* connection)
+{
+ Vector<std::pair<RefPtr<CoreIPC::Connection>, uint64_t>> connectionAndStorageMapIDPairsToRemove;
+ HashMap<std::pair<RefPtr<CoreIPC::Connection>, uint64_t>, RefPtr<StorageArea>> storageAreasByConnection = m_storageAreasByConnection;
+ for (HashMap<std::pair<RefPtr<CoreIPC::Connection>, uint64_t>, RefPtr<StorageArea>>::const_iterator it = storageAreasByConnection.begin(), end = storageAreasByConnection.end(); it != end; ++it) {
+ if (it->key.first != connection)
+ continue;
+
+ it->value->removeListener(it->key.first.get(), it->key.second);
+ connectionAndStorageMapIDPairsToRemove.append(it->key);
+ }
+
+ for (size_t i = 0; i < connectionAndStorageMapIDPairsToRemove.size(); ++i)
+ m_storageAreasByConnection.remove(connectionAndStorageMapIDPairsToRemove[i]);
+}
+
+StorageManager::StorageArea* StorageManager::findStorageArea(CoreIPC::Connection* connection, uint64_t storageMapID) const
+{
+ std::pair<CoreIPC::Connection*, uint64_t> connectionAndStorageMapIDPair(connection, storageMapID);
+ if (!HashMap<std::pair<RefPtr<CoreIPC::Connection>, uint64_t>, RefPtr<StorageArea>>::isValidKey(connectionAndStorageMapIDPair))
+ return 0;
+
+ return m_storageAreasByConnection.get(connectionAndStorageMapIDPair);
+}
+
+StorageManager::LocalStorageNamespace* StorageManager::getOrCreateLocalStorageNamespace(uint64_t storageNamespaceID)
+{
+ if (!HashMap<uint64_t, RefPtr<LocalStorageNamespace>>::isValidKey(storageNamespaceID))
+ return 0;
+
+ HashMap<uint64_t, RefPtr<LocalStorageNamespace>>::AddResult result = m_localStorageNamespaces.add(storageNamespaceID, 0);
+ if (result.isNewEntry)
+ result.iterator->value = LocalStorageNamespace::create(this, storageNamespaceID);
+
+ return result.iterator->value.get();
+}
+
+static void callCallbackFunction(void* context, void (*callbackFunction)(const Vector<RefPtr<WebCore::SecurityOrigin>>& securityOrigins, void* context), Vector<RefPtr<WebCore::SecurityOrigin>>* securityOriginsPtr)
+{
+ OwnPtr<Vector<RefPtr<WebCore::SecurityOrigin>>> securityOrigins = adoptPtr(securityOriginsPtr);
+ callbackFunction(*securityOrigins, context);
+}
+
+void StorageManager::getOriginsInternal(FunctionDispatcher* dispatcher, void* context, void (*callbackFunction)(const Vector<RefPtr<WebCore::SecurityOrigin>>& securityOrigins, void* context))
+{
+ OwnPtr<Vector<RefPtr<WebCore::SecurityOrigin>>> securityOrigins = adoptPtr(new Vector<RefPtr<WebCore::SecurityOrigin>>(m_localStorageDatabaseTracker->origins()));
+ dispatcher->dispatch(bind(callCallbackFunction, context, callbackFunction, securityOrigins.leakPtr()));
+}
+
+void StorageManager::deleteEntriesForOriginInternal(SecurityOrigin* securityOrigin)
+{
+ for (auto it = m_localStorageNamespaces.begin(), end = m_localStorageNamespaces.end(); it != end; ++it)
+ it->value->clearStorageAreasMatchingOrigin(securityOrigin);
+
+ m_localStorageDatabaseTracker->deleteDatabaseWithOrigin(securityOrigin);
+}
+
+void StorageManager::deleteAllEntriesInternal()
+{
+ for (auto it = m_localStorageNamespaces.begin(), end = m_localStorageNamespaces.end(); it != end; ++it)
+ it->value->clearAllStorageAreas();
+
+ m_localStorageDatabaseTracker->deleteAllDatabases();
+}
+
+
+} // namespace WebKit
diff --git a/Source/WebKit2/UIProcess/Storage/StorageManager.h b/Source/WebKit2/UIProcess/Storage/StorageManager.h
new file mode 100644
index 000000000..5fc5261e4
--- /dev/null
+++ b/Source/WebKit2/UIProcess/Storage/StorageManager.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2013 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. 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 INC. 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 StorageManager_h
+#define StorageManager_h
+
+#include "Connection.h"
+#include <wtf/Forward.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/ThreadSafeRefCounted.h>
+#include <wtf/text/StringHash.h>
+
+class WorkQueue;
+
+namespace WebCore {
+class SecurityOrigin;
+}
+
+namespace WebKit {
+
+struct SecurityOriginData;
+class LocalStorageDatabaseTracker;
+class WebProcessProxy;
+
+class StorageManager : public CoreIPC::Connection::WorkQueueMessageReceiver {
+public:
+ static PassRefPtr<StorageManager> create();
+ ~StorageManager();
+
+ void setLocalStorageDirectory(const String&);
+
+ void createSessionStorageNamespace(uint64_t storageNamespaceID, CoreIPC::Connection* allowedConnection, unsigned quotaInBytes);
+ void destroySessionStorageNamespace(uint64_t storageNamespaceID);
+ void setAllowedSessionStorageNamespaceConnection(uint64_t storageNamespaceID, CoreIPC::Connection* allowedConnection);
+ void cloneSessionStorageNamespace(uint64_t storageNamespaceID, uint64_t newStorageNamespaceID);
+
+ void processWillOpenConnection(WebProcessProxy*);
+ void processWillCloseConnection(WebProcessProxy*);
+
+ // FIXME: Instead of a context + C function, this should take a WTF::Function, but we currently don't
+ // support arguments in functions.
+ void getOrigins(FunctionDispatcher* callbackDispatcher, void* context, void (*callback)(const Vector<RefPtr<WebCore::SecurityOrigin>>& securityOrigins, void* context));
+ void deleteEntriesForOrigin(WebCore::SecurityOrigin*);
+ void deleteAllEntries();
+
+private:
+ StorageManager();
+
+ // CoreIPC::Connection::WorkQueueMessageReceiver.
+ virtual void didReceiveMessage(CoreIPC::Connection*, CoreIPC::MessageDecoder&) OVERRIDE;
+ virtual void didReceiveSyncMessage(CoreIPC::Connection*, CoreIPC::MessageDecoder&, OwnPtr<CoreIPC::MessageEncoder>& replyEncoder) OVERRIDE;
+
+ // Message handlers.
+ void createLocalStorageMap(CoreIPC::Connection*, uint64_t storageMapID, uint64_t storageNamespaceID, const SecurityOriginData&);
+ void createSessionStorageMap(CoreIPC::Connection*, uint64_t storageMapID, uint64_t storageNamespaceID, const SecurityOriginData&);
+ void destroyStorageMap(CoreIPC::Connection*, uint64_t storageMapID);
+
+ void getValues(CoreIPC::Connection*, uint64_t storageMapID, uint64_t storageMapSeed, HashMap<String, String>& values);
+ void setItem(CoreIPC::Connection*, uint64_t storageAreaID, uint64_t sourceStorageAreaID, uint64_t storageMapSeed, const String& key, const String& value, const String& urlString);
+ void removeItem(CoreIPC::Connection*, uint64_t storageMapID, uint64_t sourceStorageAreaID, uint64_t storageMapSeed, const String& key, const String& urlString);
+ void clear(CoreIPC::Connection*, uint64_t storageMapID, uint64_t sourceStorageAreaID, uint64_t storageMapSeed, const String& urlString);
+
+ void createSessionStorageNamespaceInternal(uint64_t storageNamespaceID, CoreIPC::Connection* allowedConnection, unsigned quotaInBytes);
+ void destroySessionStorageNamespaceInternal(uint64_t storageNamespaceID);
+ void setAllowedSessionStorageNamespaceConnectionInternal(uint64_t storageNamespaceID, CoreIPC::Connection* allowedConnection);
+ void cloneSessionStorageNamespaceInternal(uint64_t storageNamespaceID, uint64_t newStorageNamespaceID);
+
+ void invalidateConnectionInternal(CoreIPC::Connection*);
+
+ class StorageArea;
+ StorageArea* findStorageArea(CoreIPC::Connection*, uint64_t) const;
+
+ class LocalStorageNamespace;
+ LocalStorageNamespace* getOrCreateLocalStorageNamespace(uint64_t storageNamespaceID);
+
+ void getOriginsInternal(FunctionDispatcher* callbackDispatcher, void* context, void (*callback)(const Vector<RefPtr<WebCore::SecurityOrigin>>& securityOrigins, void* context));
+ void deleteEntriesForOriginInternal(WebCore::SecurityOrigin*);
+ void deleteAllEntriesInternal();
+
+ RefPtr<WorkQueue> m_queue;
+
+ RefPtr<LocalStorageDatabaseTracker> m_localStorageDatabaseTracker;
+ HashMap<uint64_t, RefPtr<LocalStorageNamespace>> m_localStorageNamespaces;
+
+ class SessionStorageNamespace;
+ HashMap<uint64_t, RefPtr<SessionStorageNamespace>> m_sessionStorageNamespaces;
+
+ HashMap<std::pair<RefPtr<CoreIPC::Connection>, uint64_t>, RefPtr<StorageArea>> m_storageAreasByConnection;
+};
+
+} // namespace WebKit
+
+#endif // StorageManager_h
diff --git a/Source/WebKit2/UIProcess/Storage/StorageManager.messages.in b/Source/WebKit2/UIProcess/Storage/StorageManager.messages.in
new file mode 100644
index 000000000..22d0121fe
--- /dev/null
+++ b/Source/WebKit2/UIProcess/Storage/StorageManager.messages.in
@@ -0,0 +1,33 @@
+# Copyright (C) 2013 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. 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 INC. 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.
+
+messages -> StorageManager {
+ CreateLocalStorageMap(uint64_t storageMapID, uint64_t storageNamespaceID, WebKit::SecurityOriginData securityOriginData) WantsConnection
+ CreateSessionStorageMap(uint64_t storageMapID, uint64_t storageNamespaceID, WebKit::SecurityOriginData securityOriginData) WantsConnection
+ DestroyStorageMap(uint64_t storageMapID) WantsConnection
+
+ GetValues(uint64_t storageMapID, uint64_t storageMapSeed) -> (WTF::HashMap<WTF::String, WTF::String> values) WantsConnection
+
+ SetItem(uint64_t storageMapID, uint64_t sourceStorageAreaID, uint64_t storageMapSeed, WTF::String key, WTF::String value, WTF::String urlString) WantsConnection
+ RemoveItem(uint64_t storageMapID, uint64_t sourceStorageAreaID, uint64_t storageMapSeed, WTF::String key, WTF::String urlString) WantsConnection
+ Clear(uint64_t storageMapID, uint64_t sourceStorageAreaID, uint64_t storageMapSeed, WTF::String urlString) WantsConnection
+}