diff options
| author | Allan Sandfeld Jensen <allan.jensen@digia.com> | 2013-09-13 12:51:20 +0200 |
|---|---|---|
| committer | The Qt Project <gerrit-noreply@qt-project.org> | 2013-09-19 20:50:05 +0200 |
| commit | d441d6f39bb846989d95bcf5caf387b42414718d (patch) | |
| tree | e367e64a75991c554930278175d403c072de6bb8 /Source/WebKit2/UIProcess/Storage | |
| parent | 0060b2994c07842f4c59de64b5e3e430525c4b90 (diff) | |
| download | qtwebkit-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')
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 +} |
