Title: [276653] trunk/Source
Revision
276653
Author
[email protected]
Date
2021-04-27 12:02:53 -0700 (Tue, 27 Apr 2021)

Log Message

Don't keep local storage data in memory in the NetworkProcess
https://bugs.webkit.org/show_bug.cgi?id=225065

Reviewed by Alex Christensen.

Source/WebCore:

* platform/sql/SQLiteDatabase.h:
* platform/sql/SQLiteFileSystem.h:
Export a couple more symbols.

* storage/StorageMap.cpp:
(WebCore::StorageMap::clear):
* storage/StorageMap.h:
Add a clear() function to StorageMap so that StorageArea doesn't reconstruct a
new StorageMap object unnecessarily when:
- The StorageMap is not shared
- The StorageMap is empty (and clear() is a no-op)

Source/WebKit:

When a WebPage would start to use the local storage API, the WebProcess
would send a sync IPC to the NetworkProcess to retrieve all the local
storage entries for the origin. The NetworkProcess would read those
entries from a SQLite database and send them back to the WebProcess.
Both the NetworkProcess would keep the entries in memory, in a
StorageMap object (which is basically a HashMap). On some sites, the
strings in the local storage may be very large. It is useful for the
WebProcess to keep them in memory for performance reasons, especially
considering that the Web API is synchronous. However, there is no real
need to keep them in memory in the Network Process side, especially
given that the WebProcess already has its own copy. On the network
process side, we can get rid of the StorageMap entirely in the local
storage case and 100% rely on the SQLite database. This is what this
patch implements.

A/B testing shows this is a 2-3% progression on Membuster. There does
not appear to be a meaningful progression on PLUM sadly. This is also
neutral on PLT.

* NetworkProcess/WebStorage/LocalStorageDatabase.cpp:
(WebKit::estimateEntrySize):
(WebKit::LocalStorageDatabase::create):
(WebKit::LocalStorageDatabase::LocalStorageDatabase):
(WebKit::LocalStorageDatabase::openDatabase):
(WebKit::LocalStorageDatabase::tryToOpenDatabase):
(WebKit::LocalStorageDatabase::items const):
(WebKit::LocalStorageDatabase::removeItem):
(WebKit::LocalStorageDatabase::item const):
(WebKit::LocalStorageDatabase::setItem):
(WebKit::LocalStorageDatabase::clear):
(WebKit::LocalStorageDatabase::close):
(WebKit::LocalStorageDatabase::databaseIsEmpty const):
(WebKit::LocalStorageDatabase::openIfExisting):
(WebKit::LocalStorageDatabase::scopedStatement const):
* NetworkProcess/WebStorage/LocalStorageDatabase.h:
- Update LocalStorageDatabase API to match more closely the API of
  StorageMap. This way StorageArea can rely on the LocalStorageDatabase
  entirely instead of the StorageMap with minimal changes.
- Add quota support similarly to what is done in StorageArea so that
  LocalStorageDatabase::setItem() fails when the quota is reached.
- Cache the SQLiteStatements for performance reasons

* NetworkProcess/WebStorage/LocalStorageNamespace.cpp:
(WebKit::LocalStorageNamespace::flushAndClose):
We no longer need to call syncToDatabase() before closing since
LocalStorageDatabase no longer has a queue on pending operations.

* NetworkProcess/WebStorage/StorageArea.cpp:
(WebKit::StorageArea::StorageArea):
(WebKit::StorageArea::addListener):
(WebKit::StorageArea::removeListener):
(WebKit::StorageArea::setItem):
(WebKit::StorageArea::removeItem):
(WebKit::StorageArea::clear):
(WebKit::StorageArea::items const):
(WebKit::StorageArea::ensureDatabase const):
* NetworkProcess/WebStorage/StorageArea.h:
If the StorageArea is used for local storage, we now don't even initialize
a StorageMap and rely entirely on LocalStorageDatabase. If the StorageArea
is used for session storage, we keep using a StorageMap since there is no
backing database. This is fairly straightforward because I updated the
LocalStorageDatabase API to match fairly closely the StorageMap one.

* NetworkProcess/WebStorage/StorageManagerSet.cpp:
(WebKit::StorageManagerSet::waitUntilSyncingLocalStorageFinished):

Source/WebKitLegacy:

* Storage/StorageAreaImpl.cpp:
(WebKit::StorageAreaImpl::clear):
(WebKit::StorageAreaImpl::clearForOriginDeletion):
Use the new StorageMap::clear() to avoid constructing a new StorageMap unnecessarily
when:
- The StorageMap is not shared
- The StorageMap is empty

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (276652 => 276653)


--- trunk/Source/WebCore/ChangeLog	2021-04-27 18:52:48 UTC (rev 276652)
+++ trunk/Source/WebCore/ChangeLog	2021-04-27 19:02:53 UTC (rev 276653)
@@ -1,3 +1,22 @@
+2021-04-27  Chris Dumez  <[email protected]>
+
+        Don't keep local storage data in memory in the NetworkProcess
+        https://bugs.webkit.org/show_bug.cgi?id=225065
+
+        Reviewed by Alex Christensen.
+
+        * platform/sql/SQLiteDatabase.h:
+        * platform/sql/SQLiteFileSystem.h:
+        Export a couple more symbols.
+
+        * storage/StorageMap.cpp:
+        (WebCore::StorageMap::clear):
+        * storage/StorageMap.h:
+        Add a clear() function to StorageMap so that StorageArea doesn't reconstruct a
+        new StorageMap object unnecessarily when:
+        - The StorageMap is not shared
+        - The StorageMap is empty (and clear() is a no-op)
+
 2021-04-27  Youenn Fablet  <[email protected]>
 
         SWContextManager::postMessageToServiceWorker should check for valid service worker

Modified: trunk/Source/WebCore/platform/sql/SQLiteDatabase.h (276652 => 276653)


--- trunk/Source/WebCore/platform/sql/SQLiteDatabase.h	2021-04-27 18:52:48 UTC (rev 276652)
+++ trunk/Source/WebCore/platform/sql/SQLiteDatabase.h	2021-04-27 19:02:53 UTC (rev 276653)
@@ -58,7 +58,7 @@
     bool isOpen() const { return m_db; }
     WEBCORE_EXPORT void close();
 
-    void updateLastChangesCount();
+    WEBCORE_EXPORT void updateLastChangesCount();
 
     WEBCORE_EXPORT bool executeCommand(const String&);
     bool returnsAtLeastOneResult(const String&);
@@ -74,7 +74,7 @@
     WEBCORE_EXPORT void interrupt();
 
     int64_t lastInsertRowID();
-    int lastChanges();
+    WEBCORE_EXPORT int lastChanges();
 
     void setBusyTimeout(int ms);
     void setBusyHandler(int(*)(void*, int));

Modified: trunk/Source/WebCore/platform/sql/SQLiteFileSystem.h (276652 => 276653)


--- trunk/Source/WebCore/platform/sql/SQLiteFileSystem.h	2021-04-27 18:52:48 UTC (rev 276652)
+++ trunk/Source/WebCore/platform/sql/SQLiteFileSystem.h	2021-04-27 19:02:53 UTC (rev 276653)
@@ -89,7 +89,7 @@
     static bool truncateDatabaseFile(sqlite3* database);
 #endif
     
-    static long long getDatabaseFileSize(const String& fileName);
+    WEBCORE_EXPORT static long long getDatabaseFileSize(const String& fileName);
     WEBCORE_EXPORT static Optional<WallTime> databaseCreationTime(const String& fileName);
     WEBCORE_EXPORT static Optional<WallTime> databaseModificationTime(const String& fileName);
 

Modified: trunk/Source/WebCore/storage/StorageMap.cpp (276652 => 276653)


--- trunk/Source/WebCore/storage/StorageMap.cpp	2021-04-27 18:52:48 UTC (rev 276652)
+++ trunk/Source/WebCore/storage/StorageMap.cpp	2021-04-27 19:02:53 UTC (rev 276653)
@@ -175,6 +175,18 @@
     return nullptr;
 }
 
+RefPtr<StorageMap> StorageMap::clear()
+{
+    // Implement copy-on-write semantics here. We're guaranteed that the only refs of StorageMaps belong to Storage objects
+    // so if more than one Storage object refs this map, copy it before mutating it.
+    if (refCount() > 1 && !m_map.isEmpty())
+        return StorageMap::create(m_quotaSize);
+
+    m_map.clear();
+    m_currentLength = 0;
+    return nullptr;
+}
+
 bool StorageMap::contains(const String& key) const
 {
     return m_map.contains(key);

Modified: trunk/Source/WebCore/storage/StorageMap.h (276652 => 276653)


--- trunk/Source/WebCore/storage/StorageMap.h	2021-04-27 18:52:48 UTC (rev 276652)
+++ trunk/Source/WebCore/storage/StorageMap.h	2021-04-27 19:02:53 UTC (rev 276653)
@@ -43,6 +43,7 @@
     WEBCORE_EXPORT RefPtr<StorageMap> setItem(const String& key, const String& value, String& oldValue, bool& quotaException);
     WEBCORE_EXPORT RefPtr<StorageMap> setItemIgnoringQuota(const String& key, const String& value);
     WEBCORE_EXPORT RefPtr<StorageMap> removeItem(const String&, String& oldValue);
+    WEBCORE_EXPORT RefPtr<StorageMap> clear();
 
     WEBCORE_EXPORT bool contains(const String& key) const;
 

Modified: trunk/Source/WebKit/ChangeLog (276652 => 276653)


--- trunk/Source/WebKit/ChangeLog	2021-04-27 18:52:48 UTC (rev 276652)
+++ trunk/Source/WebKit/ChangeLog	2021-04-27 19:02:53 UTC (rev 276653)
@@ -1,5 +1,78 @@
 2021-04-27  Chris Dumez  <[email protected]>
 
+        Don't keep local storage data in memory in the NetworkProcess
+        https://bugs.webkit.org/show_bug.cgi?id=225065
+
+        Reviewed by Alex Christensen.
+
+        When a WebPage would start to use the local storage API, the WebProcess
+        would send a sync IPC to the NetworkProcess to retrieve all the local
+        storage entries for the origin. The NetworkProcess would read those
+        entries from a SQLite database and send them back to the WebProcess.
+        Both the NetworkProcess would keep the entries in memory, in a
+        StorageMap object (which is basically a HashMap). On some sites, the
+        strings in the local storage may be very large. It is useful for the
+        WebProcess to keep them in memory for performance reasons, especially
+        considering that the Web API is synchronous. However, there is no real
+        need to keep them in memory in the Network Process side, especially
+        given that the WebProcess already has its own copy. On the network
+        process side, we can get rid of the StorageMap entirely in the local
+        storage case and 100% rely on the SQLite database. This is what this
+        patch implements.
+
+        A/B testing shows this is a 2-3% progression on Membuster. There does
+        not appear to be a meaningful progression on PLUM sadly. This is also
+        neutral on PLT.
+
+        * NetworkProcess/WebStorage/LocalStorageDatabase.cpp:
+        (WebKit::estimateEntrySize):
+        (WebKit::LocalStorageDatabase::create):
+        (WebKit::LocalStorageDatabase::LocalStorageDatabase):
+        (WebKit::LocalStorageDatabase::openDatabase):
+        (WebKit::LocalStorageDatabase::tryToOpenDatabase):
+        (WebKit::LocalStorageDatabase::items const):
+        (WebKit::LocalStorageDatabase::removeItem):
+        (WebKit::LocalStorageDatabase::item const):
+        (WebKit::LocalStorageDatabase::setItem):
+        (WebKit::LocalStorageDatabase::clear):
+        (WebKit::LocalStorageDatabase::close):
+        (WebKit::LocalStorageDatabase::databaseIsEmpty const):
+        (WebKit::LocalStorageDatabase::openIfExisting):
+        (WebKit::LocalStorageDatabase::scopedStatement const):
+        * NetworkProcess/WebStorage/LocalStorageDatabase.h:
+        - Update LocalStorageDatabase API to match more closely the API of
+          StorageMap. This way StorageArea can rely on the LocalStorageDatabase
+          entirely instead of the StorageMap with minimal changes.
+        - Add quota support similarly to what is done in StorageArea so that
+          LocalStorageDatabase::setItem() fails when the quota is reached.
+        - Cache the SQLiteStatements for performance reasons
+
+        * NetworkProcess/WebStorage/LocalStorageNamespace.cpp:
+        (WebKit::LocalStorageNamespace::flushAndClose):
+        We no longer need to call syncToDatabase() before closing since
+        LocalStorageDatabase no longer has a queue on pending operations.
+
+        * NetworkProcess/WebStorage/StorageArea.cpp:
+        (WebKit::StorageArea::StorageArea):
+        (WebKit::StorageArea::addListener):
+        (WebKit::StorageArea::removeListener):
+        (WebKit::StorageArea::setItem):
+        (WebKit::StorageArea::removeItem):
+        (WebKit::StorageArea::clear):
+        (WebKit::StorageArea::items const):
+        (WebKit::StorageArea::ensureDatabase const):
+        * NetworkProcess/WebStorage/StorageArea.h:
+        If the StorageArea is used for local storage, we now don't even initialize
+        a StorageMap and rely entirely on LocalStorageDatabase. If the StorageArea
+        is used for session storage, we keep using a StorageMap since there is no
+        backing database. This is fairly straightforward because I updated the
+        LocalStorageDatabase API to match fairly closely the StorageMap one.
+
+        * NetworkProcess/WebStorage/StorageManagerSet.cpp:
+        (WebKit::StorageManagerSet::waitUntilSyncingLocalStorageFinished):
+
+2021-04-27  Chris Dumez  <[email protected]>
+
         [IPC Hardening] Make sure IPC::Decoder constructors consistently call markInvalid() when decoding fails
         https://bugs.webkit.org/show_bug.cgi?id=225110
         <rdar://76547775>

Modified: trunk/Source/WebKit/NetworkProcess/WebStorage/LocalStorageDatabase.cpp (276652 => 276653)


--- trunk/Source/WebKit/NetworkProcess/WebStorage/LocalStorageDatabase.cpp	2021-04-27 18:52:48 UTC (rev 276652)
+++ trunk/Source/WebKit/NetworkProcess/WebStorage/LocalStorageDatabase.cpp	2021-04-27 19:02:53 UTC (rev 276653)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, 2009, 2010, 2013, 2019 Apple Inc. All rights reserved.
+ * Copyright (C) 2008-2021 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -27,12 +27,15 @@
 #include "LocalStorageDatabase.h"
 
 #include "LocalStorageDatabaseTracker.h"
+#include <WebCore/SQLiteFileSystem.h>
 #include <WebCore/SQLiteStatement.h>
+#include <WebCore/SQLiteStatementAutoResetScope.h>
 #include <WebCore/SQLiteTransaction.h>
-#include <WebCore/SecurityOrigin.h>
+#include <WebCore/SecurityOriginData.h>
 #include <WebCore/StorageMap.h>
 #include <WebCore/SuddenTermination.h>
 #include <wtf/FileSystem.h>
+#include <wtf/HashMap.h>
 #include <wtf/RefPtr.h>
 #include <wtf/RunLoop.h>
 #include <wtf/WorkQueue.h>
@@ -39,23 +42,30 @@
 #include <wtf/text/StringHash.h>
 #include <wtf/text/WTFString.h>
 
-static const auto databaseUpdateInterval = 1_s;
-
-static const int maximumItemsToUpdate = 100;
-
 namespace WebKit {
 using namespace WebCore;
 
-Ref<LocalStorageDatabase> LocalStorageDatabase::create(Ref<WorkQueue>&& queue, Ref<LocalStorageDatabaseTracker>&& tracker, const SecurityOriginData& securityOrigin)
+static const char getItemsQueryString[] = "SELECT key, value FROM ItemTable";
+
+static CheckedUint64 estimateEntrySize(const String& key, const String& value)
 {
-    return adoptRef(*new LocalStorageDatabase(WTFMove(queue), WTFMove(tracker), securityOrigin));
+    CheckedUint64 entrySize;
+    entrySize += key.length() * sizeof(UChar);
+    entrySize += value.length() * sizeof(UChar);
+    return entrySize;
 }
 
-LocalStorageDatabase::LocalStorageDatabase(Ref<WorkQueue>&& queue, Ref<LocalStorageDatabaseTracker>&& tracker, const SecurityOriginData& securityOrigin)
+Ref<LocalStorageDatabase> LocalStorageDatabase::create(Ref<WorkQueue>&& queue, Ref<LocalStorageDatabaseTracker>&& tracker, const SecurityOriginData& securityOrigin, unsigned quotaInBytes)
+{
+    return adoptRef(*new LocalStorageDatabase(WTFMove(queue), WTFMove(tracker), securityOrigin, quotaInBytes));
+}
+
+LocalStorageDatabase::LocalStorageDatabase(Ref<WorkQueue>&& queue, Ref<LocalStorageDatabaseTracker>&& tracker, const SecurityOriginData& securityOrigin, unsigned quotaInBytes)
     : m_queue(WTFMove(queue))
     , m_tracker(WTFMove(tracker))
     , m_securityOrigin(securityOrigin)
     , m_databasePath(m_tracker->databasePath(m_securityOrigin))
+    , m_quotaInBytes(quotaInBytes)
 {
     ASSERT(!RunLoop::isMain());
 }
@@ -66,12 +76,13 @@
     ASSERT(m_isClosed);
 }
 
-void LocalStorageDatabase::openDatabase(DatabaseOpeningStrategy openingStrategy)
+void LocalStorageDatabase::openDatabase(ShouldCreateDatabase shouldCreateDatabase)
 {
+    ASSERT(!RunLoop::isMain());
     ASSERT(!m_database.isOpen());
     ASSERT(!m_failedToOpenDatabase);
 
-    if (!tryToOpenDatabase(openingStrategy)) {
+    if (!tryToOpenDatabase(shouldCreateDatabase)) {
         m_failedToOpenDatabase = true;
         return;
     }
@@ -80,10 +91,10 @@
         m_tracker->didOpenDatabaseWithOrigin(m_securityOrigin);
 }
 
-bool LocalStorageDatabase::tryToOpenDatabase(DatabaseOpeningStrategy openingStrategy)
+bool LocalStorageDatabase::tryToOpenDatabase(ShouldCreateDatabase shouldCreateDatabase)
 {
     ASSERT(!RunLoop::isMain());
-    if (!FileSystem::fileExists(m_databasePath) && openingStrategy == SkipIfNonExistent)
+    if (!FileSystem::fileExists(m_databasePath) && shouldCreateDatabase == ShouldCreateDatabase::No)
         return true;
 
     if (m_databasePath.isEmpty()) {
@@ -117,6 +128,7 @@
 
 bool LocalStorageDatabase::migrateItemTableIfNeeded()
 {
+    ASSERT(!RunLoop::isMain());
     if (!m_database.tableExists("ItemTable"))
         return true;
 
@@ -153,197 +165,175 @@
     return true;
 }
 
-void LocalStorageDatabase::importItems(StorageMap& storageMap)
+HashMap<String, String> LocalStorageDatabase::items() const
 {
-    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);
+    ASSERT(!RunLoop::isMain());
     if (!m_database.isOpen())
-        return;
+        return { };
 
-    SQLiteStatement query(m_database, "SELECT key, value FROM ItemTable"_str);
-    if (query.prepare() != SQLITE_OK) {
+    auto query = scopedStatement(m_getItemsStatement, getItemsQueryString);
+    if (!query) {
         LOG_ERROR("Unable to select items from ItemTable for local storage");
-        return;
+        return { };
     }
 
     HashMap<String, String> items;
-
-    int result = query.step();
+    int result = query->step();
     while (result == SQLITE_ROW) {
-        String key = query.getColumnText(0);
-        String value = query.getColumnBlobAsString(1);
+        String key = query->getColumnText(0);
+        String value = query->getColumnBlobAsString(1);
         if (!key.isNull() && !value.isNull())
             items.add(WTFMove(key), WTFMove(value));
-        result = query.step();
+        result = query->step();
     }
 
-    if (result != SQLITE_DONE) {
+    if (result != SQLITE_DONE)
         LOG_ERROR("Error reading items from ItemTable for local storage");
-        return;
-    }
 
-    storageMap.importItems(WTFMove(items));
+    return items;
 }
 
-void LocalStorageDatabase::setItem(const String& key, const String& value)
+void LocalStorageDatabase::removeItem(const String& key, String& oldValue)
 {
-    itemDidChange(key, value);
-}
+    ASSERT(!RunLoop::isMain());
+    if (!m_database.isOpen())
+        return;
 
-void LocalStorageDatabase::removeItem(const String& key)
-{
-    itemDidChange(key, String());
-}
+    oldValue = item(key);
+    if (oldValue.isNull())
+        return;
 
-void LocalStorageDatabase::clear()
-{
-    m_changedItems.clear();
-    m_shouldClearItems = true;
-
-    scheduleDatabaseUpdate();
-}
-
-void LocalStorageDatabase::close()
-{
-    if (m_isClosed)
+    auto deleteStatement = scopedStatement(m_deleteItemStatement, "DELETE FROM ItemTable WHERE key=?"_s);
+    if (!deleteStatement) {
+        LOG_ERROR("Failed to prepare delete statement - cannot write to local storage database");
         return;
-    m_isClosed = true;
-
-    if (m_didScheduleDatabaseUpdate) {
-        updateDatabaseWithChangedItems(m_changedItems);
-        m_changedItems.clear();
     }
+    deleteStatement->bindText(1, key);
 
-    bool isEmpty = databaseIsEmpty();
-
-    if (m_database.isOpen())
-        m_database.close();
-
-    if (isEmpty)
-        m_tracker->deleteDatabaseWithOrigin(m_securityOrigin);
-}
-
-void LocalStorageDatabase::itemDidChange(const String& key, const String& value)
-{
-    m_changedItems.set(key, value);
-    scheduleDatabaseUpdate();
-}
-
-void LocalStorageDatabase::scheduleDatabaseUpdate()
-{
-    if (m_didScheduleDatabaseUpdate)
+    int result = deleteStatement->step();
+    if (result != SQLITE_DONE) {
+        LOG_ERROR("Failed to delete item in the local storage database - %i", result);
         return;
+    }
 
-    if (!m_disableSuddenTerminationWhileWritingToLocalStorage)
-        m_disableSuddenTerminationWhileWritingToLocalStorage = makeUnique<SuddenTerminationDisabler>();
-
-    m_didScheduleDatabaseUpdate = true;
-
-    m_queue->dispatch([protectedThis = makeRef(*this)] {
-        protectedThis->updateDatabase();
-    });
+    if (m_databaseSize) {
+        CheckedUint64 entrySize = estimateEntrySize(key, oldValue);
+        if (entrySize.hasOverflowed() || entrySize.unsafeGet() >= *m_databaseSize)
+            *m_databaseSize = 0;
+        else
+            *m_databaseSize -= entrySize.unsafeGet();
+    }
 }
 
-void LocalStorageDatabase::updateDatabase()
+String LocalStorageDatabase::item(const String& key) const
 {
-    if (m_isClosed)
-        return;
+    ASSERT(!RunLoop::isMain());
+    if (!m_database.isOpen())
+        return { };
 
-    m_didScheduleDatabaseUpdate = false;
+    auto query = scopedStatement(m_getItemStatement, "SELECT value FROM ItemTable WHERE key=?"_s);
+    if (!query) {
+        LOG_ERROR("Unable to get item from ItemTable for local storage");
+        return { };
+    }
+    query->bindText(1, key);
 
-    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);
-        updateDatabaseWithChangedItems(changedItems);
-        m_disableSuddenTerminationWhileWritingToLocalStorage = nullptr;
-    } 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);
-    }
+    int result = query->step();
+    if (result == SQLITE_ROW)
+        return query->getColumnBlobAsString(0);
+    if (result != SQLITE_DONE)
+        LOG_ERROR("Error get item from ItemTable for local storage");
+    return { };
 }
 
-void LocalStorageDatabase::updateDatabaseWithChangedItems(const HashMap<String, String>& changedItems)
+void LocalStorageDatabase::setItem(const String& key, const String& value, String& oldValue, bool& quotaException)
 {
+    ASSERT(!RunLoop::isMain());
     if (!m_database.isOpen())
-        openDatabase(CreateIfNonExistent);
+        openDatabase(ShouldCreateDatabase::Yes);
     if (!m_database.isOpen())
         return;
 
-    if (m_shouldClearItems) {
-        m_shouldClearItems = false;
-
-        SQLiteStatement clearStatement(m_database, "DELETE FROM ItemTable");
-        if (clearStatement.prepare() != SQLITE_OK) {
-            LOG_ERROR("Failed to prepare clear statement - cannot write to local storage database");
+    if (m_quotaInBytes != WebCore::StorageMap::noQuota) {
+        if (!m_databaseSize)
+            m_databaseSize = SQLiteFileSystem::getDatabaseFileSize(m_databasePath);
+        if (*m_databaseSize >= m_quotaInBytes) {
+            quotaException = true;
             return;
         }
-
-        int result = clearStatement.step();
-        if (result != SQLITE_DONE) {
-            LOG_ERROR("Failed to clear all items in the local storage database - %i", result);
+        CheckedUint64 newDatabaseSize = *m_databaseSize;
+        newDatabaseSize += estimateEntrySize(key, value);
+        if (newDatabaseSize.hasOverflowed() || newDatabaseSize.unsafeGet() > m_quotaInBytes) {
+            quotaException = true;
             return;
         }
+        m_databaseSize = newDatabaseSize.unsafeGet();
     }
 
-    SQLiteStatement insertStatement(m_database, "INSERT INTO ItemTable VALUES (?, ?)");
-    if (insertStatement.prepare() != SQLITE_OK) {
+    oldValue = item(key);
+
+    auto insertStatement = scopedStatement(m_insertStatement, "INSERT INTO ItemTable VALUES (?, ?)"_s);
+    if (!insertStatement) {
         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() != SQLITE_OK) {
-        LOG_ERROR("Failed to prepare delete statement - cannot write to local storage database");
-        return;
+    insertStatement->bindText(1, key);
+    insertStatement->bindBlob(2, value);
+
+    int result = insertStatement->step();
+    if (result != SQLITE_DONE)
+        LOG_ERROR("Failed to update item in the local storage database - %i", result);
+}
+
+bool LocalStorageDatabase::clear()
+{
+    ASSERT(!RunLoop::isMain());
+    if (!m_database.isOpen())
+        return false;
+
+    auto clearStatement = scopedStatement(m_clearStatement, "DELETE FROM ItemTable"_s);
+    if (!clearStatement) {
+        LOG_ERROR("Failed to prepare clear statement - cannot write to local storage database");
+        return false;
     }
 
-    SQLiteTransaction transaction(m_database);
-    transaction.begin();
+    int result = clearStatement->step();
+    if (result != SQLITE_DONE) {
+        LOG_ERROR("Failed to clear all items in the local storage database - %i", result);
+        return false;
+    }
 
-    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;
+    m_databaseSize = 0;
 
-        statement.bindText(1, it->key);
+    return m_database.lastChanges() > 0;
+}
 
-        // If we're inserting a key/value pair, bind the value as well.
-        if (!it->value.isNull())
-            statement.bindBlob(2, it->value);
+void LocalStorageDatabase::close()
+{
+    ASSERT(!RunLoop::isMain());
+    if (m_isClosed)
+        return;
+    m_isClosed = true;
 
-        int result = statement.step();
-        if (result != SQLITE_DONE) {
-            LOG_ERROR("Failed to update item in the local storage database - %i", result);
-            break;
-        }
+    bool isEmpty = databaseIsEmpty();
 
-        statement.reset();
-    }
+    m_clearStatement = nullptr;
+    m_insertStatement = nullptr;
+    m_getItemStatement = nullptr;
+    m_getItemsStatement = nullptr;
+    m_deleteItemStatement = nullptr;
 
-    transaction.commit();
+    if (m_database.isOpen())
+        m_database.close();
+
+    if (isEmpty)
+        m_tracker->deleteDatabaseWithOrigin(m_securityOrigin);
 }
 
-bool LocalStorageDatabase::databaseIsEmpty()
+bool LocalStorageDatabase::databaseIsEmpty() const
 {
+    ASSERT(!RunLoop::isMain());
     if (!m_database.isOpen())
         return false;
 
@@ -362,6 +352,31 @@
     return !query.getColumnInt(0);
 }
 
+void LocalStorageDatabase::openIfExisting()
+{
+    ASSERT(!RunLoop::isMain());
+    if (m_database.isOpen())
+        return;
+
+    openDatabase(ShouldCreateDatabase::No);
+
+    // Prewarm the getItems statement for performance since the pages block synchronously on retrieving the items.
+    if (m_database.isOpen())
+        scopedStatement(m_getItemsStatement, getItemsQueryString);
+}
+
+SQLiteStatementAutoResetScope LocalStorageDatabase::scopedStatement(std::unique_ptr<SQLiteStatement>& statement, const String& query) const
+{
+    ASSERT(!RunLoop::isMain());
+    if (!statement) {
+        statement = makeUnique<SQLiteStatement>(m_database, query);
+        ASSERT(m_database.isOpen());
+        if (statement->prepare() != SQLITE_OK)
+            return SQLiteStatementAutoResetScope { };
+    }
+    return SQLiteStatementAutoResetScope { statement.get() };
+}
+
 void LocalStorageDatabase::handleLowMemoryWarning()
 {
     if (m_database.isOpen())

Modified: trunk/Source/WebKit/NetworkProcess/WebStorage/LocalStorageDatabase.h (276652 => 276653)


--- trunk/Source/WebKit/NetworkProcess/WebStorage/LocalStorageDatabase.h	2021-04-27 18:52:48 UTC (rev 276652)
+++ trunk/Source/WebKit/NetworkProcess/WebStorage/LocalStorageDatabase.h	2021-04-27 19:02:53 UTC (rev 276653)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, 2009, 2010, 2013, 2019 Apple Inc. All rights reserved.
+ * Copyright (C) 2008-2021 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -26,16 +26,14 @@
 #pragma once
 
 #include <WebCore/SQLiteDatabase.h>
-#include <WebCore/SecurityOriginData.h>
 #include <wtf/Forward.h>
-#include <wtf/HashMap.h>
 #include <wtf/RefCounted.h>
-#include <wtf/WorkQueue.h>
 
 namespace WebCore {
-class SecurityOrigin;
-class StorageMap;
+class SQLiteStatementAutoResetScope;
 class SuddenTerminationDisabler;
+
+struct SecurityOriginData;
 }
 
 namespace WebKit {
@@ -44,18 +42,16 @@
 
 class LocalStorageDatabase : public RefCounted<LocalStorageDatabase> {
 public:
-    static Ref<LocalStorageDatabase> create(Ref<WorkQueue>&&, Ref<LocalStorageDatabaseTracker>&&, const WebCore::SecurityOriginData&);
+    static Ref<LocalStorageDatabase> create(Ref<WorkQueue>&&, Ref<LocalStorageDatabaseTracker>&&, const WebCore::SecurityOriginData&, unsigned quotaInBytes);
     ~LocalStorageDatabase();
 
-    // Will block until the import is complete.
-    void importItems(WebCore::StorageMap&);
+    HashMap<String, String> items() const;
+    void openIfExisting();
+    void removeItem(const String& key, String& oldValue);
+    bool clear();
+    String item(const String& key) const;
+    void setItem(const String& key, const String& value, String& oldValue, bool& quotaException);
 
-    void setItem(const String& key, const String& value);
-    void removeItem(const String& key);
-    void clear();
-
-    void updateDatabase();
-
     // Will block until all pending changes have been written to disk.
     void close();
 
@@ -62,39 +58,34 @@
     void handleLowMemoryWarning();
 
 private:
-    LocalStorageDatabase(Ref<WorkQueue>&&, Ref<LocalStorageDatabaseTracker>&&, const WebCore::SecurityOriginData&);
+    LocalStorageDatabase(Ref<WorkQueue>&&, Ref<LocalStorageDatabaseTracker>&&, const WebCore::SecurityOriginData&, unsigned quotaInBytes);
 
-    enum DatabaseOpeningStrategy {
-        CreateIfNonExistent,
-        SkipIfNonExistent
-    };
-    bool tryToOpenDatabase(DatabaseOpeningStrategy);
-    void openDatabase(DatabaseOpeningStrategy);
+    enum class ShouldCreateDatabase : bool { No, Yes };
+    bool tryToOpenDatabase(ShouldCreateDatabase);
+    void openDatabase(ShouldCreateDatabase);
 
     bool migrateItemTableIfNeeded();
+    bool databaseIsEmpty() const;
 
-    void itemDidChange(const String& key, const String& value);
+    WebCore::SQLiteStatementAutoResetScope scopedStatement(std::unique_ptr<WebCore::SQLiteStatement>&, const String& query) const;
 
-    void scheduleDatabaseUpdate();
-    void updateDatabaseWithChangedItems(const HashMap<String, String>&);
-
-    bool databaseIsEmpty();
-
     Ref<WorkQueue> m_queue;
     Ref<LocalStorageDatabaseTracker> m_tracker;
     WebCore::SecurityOriginData m_securityOrigin;
 
     String m_databasePath;
-    WebCore::SQLiteDatabase m_database;
+    mutable WebCore::SQLiteDatabase m_database;
+    unsigned m_quotaInBytes { 0 };
     bool m_failedToOpenDatabase { false };
-    bool m_didImportItems { false };
     bool m_isClosed { false };
+    Optional<uint64_t> m_databaseSize;
 
-    bool m_didScheduleDatabaseUpdate { false };
-    bool m_shouldClearItems { false };
-    HashMap<String, String> m_changedItems;
-
     std::unique_ptr<WebCore::SuddenTerminationDisabler> m_disableSuddenTerminationWhileWritingToLocalStorage;
+    mutable std::unique_ptr<WebCore::SQLiteStatement> m_clearStatement;
+    mutable std::unique_ptr<WebCore::SQLiteStatement> m_insertStatement;
+    mutable std::unique_ptr<WebCore::SQLiteStatement> m_getItemStatement;
+    mutable std::unique_ptr<WebCore::SQLiteStatement> m_getItemsStatement;
+    mutable std::unique_ptr<WebCore::SQLiteStatement> m_deleteItemStatement;
 };
 
 

Modified: trunk/Source/WebKit/NetworkProcess/WebStorage/LocalStorageNamespace.cpp (276652 => 276653)


--- trunk/Source/WebKit/NetworkProcess/WebStorage/LocalStorageNamespace.cpp	2021-04-27 18:52:48 UTC (rev 276652)
+++ trunk/Source/WebKit/NetworkProcess/WebStorage/LocalStorageNamespace.cpp	2021-04-27 19:02:53 UTC (rev 276653)
@@ -73,10 +73,8 @@
 void LocalStorageNamespace::flushAndClose(const SecurityOriginData& origin)
 {
     ASSERT(!RunLoop::isMain());
-    if (auto* storageArea = m_storageAreaMap.get(origin)) {
-        storageArea->syncToDatabase();
+    if (auto* storageArea = m_storageAreaMap.get(origin))
         storageArea->close();
-    }
 }
 
 

Modified: trunk/Source/WebKit/NetworkProcess/WebStorage/StorageArea.cpp (276652 => 276653)


--- trunk/Source/WebKit/NetworkProcess/WebStorage/StorageArea.cpp	2021-04-27 18:52:48 UTC (rev 276652)
+++ trunk/Source/WebKit/NetworkProcess/WebStorage/StorageArea.cpp	2021-04-27 19:02:53 UTC (rev 276653)
@@ -31,6 +31,7 @@
 #include "StorageAreaMapMessages.h"
 #include "StorageManager.h"
 #include <WebCore/StorageMap.h>
+#include <wtf/Scope.h>
 
 namespace WebKit {
 
@@ -40,11 +41,12 @@
     : m_localStorageNamespace(makeWeakPtr(localStorageNamespace))
     , m_securityOrigin(securityOrigin)
     , m_quotaInBytes(quotaInBytes)
-    , m_storageMap(StorageMap::create(m_quotaInBytes))
     , m_identifier(Identifier::generate())
     , m_queue(WTFMove(queue))
 {
     ASSERT(!RunLoop::isMain());
+    if (isEphemeral())
+        m_storageMap = StorageMap::create(m_quotaInBytes);
 }
 
 StorageArea::~StorageArea()
@@ -61,7 +63,7 @@
     ASSERT(!m_eventListeners.contains(connectionID));
 
     if (m_eventListeners.isEmpty() && !isEphemeral())
-        openDatabaseAndImportItemsIfNeeded();
+        ensureDatabase();
 
     m_eventListeners.add(connectionID);
 }
@@ -74,11 +76,8 @@
     if (!m_eventListeners.isEmpty())
         return;
 
-    if (!m_localStorageNamespace)
-        return;
-
-    syncToDatabase();
-    m_localStorageNamespace->removeStorageArea(m_securityOrigin);
+    if (m_localStorageNamespace)
+        m_localStorageNamespace->removeStorageArea(m_securityOrigin);
 }
 
 bool StorageArea::hasListener(IPC::Connection::UniqueID connectionID) const
@@ -101,20 +100,18 @@
 void StorageArea::setItem(IPC::Connection::UniqueID sourceConnection, StorageAreaImplIdentifier storageAreaImplID, const String& key, const String& value, const String& urlString, bool& quotaException)
 {
     ASSERT(!RunLoop::isMain());
-    openDatabaseAndImportItemsIfNeeded();
 
     String oldValue;
+    if (isEphemeral()) {
+        auto newStorageMap = m_storageMap->setItem(key, value, oldValue, quotaException);
+        if (newStorageMap)
+            m_storageMap = WTFMove(newStorageMap);
+    } else
+        ensureDatabase().setItem(key, value, oldValue, quotaException);
 
-    auto newStorageMap = m_storageMap->setItem(key, value, oldValue, quotaException);
-    if (newStorageMap)
-        m_storageMap = WTFMove(newStorageMap);
-
     if (quotaException)
         return;
 
-    if (m_localStorageDatabase)
-        m_localStorageDatabase->setItem(key, value);
-
     dispatchEvents(sourceConnection, storageAreaImplID, key, oldValue, value, urlString);
 }
 
@@ -121,19 +118,17 @@
 void StorageArea::removeItem(IPC::Connection::UniqueID sourceConnection, StorageAreaImplIdentifier storageAreaImplID, const String& key, const String& urlString)
 {
     ASSERT(!RunLoop::isMain());
-    openDatabaseAndImportItemsIfNeeded();
 
     String oldValue;
-    auto newStorageMap = m_storageMap->removeItem(key, oldValue);
-    if (newStorageMap)
-        m_storageMap = WTFMove(newStorageMap);
-
+    if (isEphemeral()) {
+        auto newStorageMap = m_storageMap->removeItem(key, oldValue);
+        if (newStorageMap)
+            m_storageMap = WTFMove(newStorageMap);
+    } else
+        ensureDatabase().removeItem(key, oldValue);
     if (oldValue.isNull())
         return;
 
-    if (m_localStorageDatabase)
-        m_localStorageDatabase->removeItem(key);
-
     dispatchEvents(sourceConnection, storageAreaImplID, key, oldValue, String(), urlString);
 }
 
@@ -140,35 +135,40 @@
 void StorageArea::clear(IPC::Connection::UniqueID sourceConnection, StorageAreaImplIdentifier storageAreaImplID, const String& urlString)
 {
     ASSERT(!RunLoop::isMain());
-    openDatabaseAndImportItemsIfNeeded();
 
-    if (!m_storageMap->length())
-        return;
+    if (isEphemeral()) {
+        if (!m_storageMap->length())
+            return;
+        if (auto newStorageMap = m_storageMap->clear())
+            m_storageMap = WTFMove(newStorageMap);
+    } else {
+        if (!ensureDatabase().clear())
+            return;
+    }
 
-    m_storageMap = StorageMap::create(m_quotaInBytes);
-
-    if (m_localStorageDatabase)
-        m_localStorageDatabase->clear();
-
     dispatchEvents(sourceConnection, storageAreaImplID, String(), String(), String(), urlString);
 }
 
-const HashMap<String, String>& StorageArea::items() const
+HashMap<String, String> StorageArea::items() const
 {
     ASSERT(!RunLoop::isMain());
-    openDatabaseAndImportItemsIfNeeded();
+    if (isEphemeral())
+        return m_storageMap->items();
 
-    return m_storageMap->items();
+    return ensureDatabase().items();
 }
 
 void StorageArea::clear()
 {
     ASSERT(!RunLoop::isMain());
-    m_storageMap = StorageMap::create(m_quotaInBytes);
-
-    if (m_localStorageDatabase) {
-        m_localStorageDatabase->close();
-        m_localStorageDatabase = nullptr;
+    if (isEphemeral()) {
+        if (auto newStorageMap = m_storageMap->clear())
+            m_storageMap = WTFMove(newStorageMap);
+    } else {
+        if (m_localStorageDatabase) {
+            m_localStorageDatabase->close();
+            m_localStorageDatabase = nullptr;
+        }
     }
 
     for (auto it = m_eventListeners.begin(), end = m_eventListeners.end(); it != end; ++it) {
@@ -179,22 +179,17 @@
     }
 }
 
-void StorageArea::openDatabaseAndImportItemsIfNeeded() const
+LocalStorageDatabase& StorageArea::ensureDatabase() const
 {
-    ASSERT(!RunLoop::isMain());
-    if (!m_localStorageNamespace)
-        return;
+    ASSERT(!isEphemeral());
 
     ASSERT(m_localStorageNamespace->storageManager()->localStorageDatabaseTracker());
     // 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_queue.copyRef(), *m_localStorageNamespace->storageManager()->localStorageDatabaseTracker(), m_securityOrigin);
-
-    if (m_didImportItemsFromDatabase)
-        return;
-
-    m_localStorageDatabase->importItems(*m_storageMap);
-    m_didImportItemsFromDatabase = true;
+    if (!m_localStorageDatabase) {
+        m_localStorageDatabase = LocalStorageDatabase::create(m_queue.copyRef(), *m_localStorageNamespace->storageManager()->localStorageDatabaseTracker(), m_securityOrigin, m_quotaInBytes);
+        m_localStorageDatabase->openIfExisting();
+    }
+    return *m_localStorageDatabase;
 }
 
 void StorageArea::dispatchEvents(IPC::Connection::UniqueID sourceConnection, StorageAreaImplIdentifier storageAreaImplID, const String& key, const String& oldValue, const String& newValue, const String& urlString) const
@@ -211,14 +206,6 @@
     }
 }
 
-void StorageArea::syncToDatabase()
-{
-    if (!m_localStorageDatabase)
-        return;
-
-    m_localStorageDatabase->updateDatabase();
-}
-
 void StorageArea::close()
 {
     if (!m_localStorageDatabase)

Modified: trunk/Source/WebKit/NetworkProcess/WebStorage/StorageArea.h (276652 => 276653)


--- trunk/Source/WebKit/NetworkProcess/WebStorage/StorageArea.h	2021-04-27 18:52:48 UTC (rev 276652)
+++ trunk/Source/WebKit/NetworkProcess/WebStorage/StorageArea.h	2021-04-27 19:02:53 UTC (rev 276653)
@@ -63,14 +63,11 @@
     void removeItem(IPC::Connection::UniqueID sourceConnection, StorageAreaImplIdentifier, const String& key, const String& urlString);
     void clear(IPC::Connection::UniqueID sourceConnection, StorageAreaImplIdentifier, const String& urlString);
 
-    const HashMap<String, String>& items() const;
+    HashMap<String, String> items() const;
     void clear();
 
     bool isEphemeral() const { return !m_localStorageNamespace; }
 
-    void openDatabaseAndImportItemsIfNeeded() const;
-
-    void syncToDatabase();
     void close();
 
     void handleLowMemoryWarning();
@@ -78,10 +75,11 @@
 private:
     void dispatchEvents(IPC::Connection::UniqueID sourceConnection, StorageAreaImplIdentifier, const String& key, const String& oldValue, const String& newValue, const String& urlString) const;
 
+    LocalStorageDatabase& ensureDatabase() const;
+
     // Will be null if the storage area belongs to a session storage namespace or the storage area is in an ephemeral session.
     WeakPtr<LocalStorageNamespace> m_localStorageNamespace;
     mutable RefPtr<LocalStorageDatabase> m_localStorageDatabase;
-    mutable bool m_didImportItemsFromDatabase { false };
 
     WebCore::SecurityOriginData m_securityOrigin;
     unsigned m_quotaInBytes { 0 };

Modified: trunk/Source/WebKit/NetworkProcess/WebStorage/StorageManagerSet.cpp (276652 => 276653)


--- trunk/Source/WebKit/NetworkProcess/WebStorage/StorageManagerSet.cpp	2021-04-27 18:52:48 UTC (rev 276652)
+++ trunk/Source/WebKit/NetworkProcess/WebStorage/StorageManagerSet.cpp	2021-04-27 19:02:53 UTC (rev 276653)
@@ -155,16 +155,7 @@
 {
     ASSERT(RunLoop::isMain());
 
-    BinarySemaphore semaphore;
-    m_queue->dispatch([this, &semaphore] {
-        for (const auto& storageArea : m_storageAreas.values()) {
-            ASSERT(storageArea);
-            if (storageArea)
-                storageArea->syncToDatabase();
-        }
-        semaphore.signal();
-    });
-    semaphore.wait();
+    m_queue->dispatchSync([] { });
 }
 
 void StorageManagerSet::suspend(CompletionHandler<void()>&& completionHandler)

Modified: trunk/Source/WebKitLegacy/ChangeLog (276652 => 276653)


--- trunk/Source/WebKitLegacy/ChangeLog	2021-04-27 18:52:48 UTC (rev 276652)
+++ trunk/Source/WebKitLegacy/ChangeLog	2021-04-27 19:02:53 UTC (rev 276653)
@@ -1,3 +1,18 @@
+2021-04-27  Chris Dumez  <[email protected]>
+
+        Don't keep local storage data in memory in the NetworkProcess
+        https://bugs.webkit.org/show_bug.cgi?id=225065
+
+        Reviewed by Alex Christensen.
+
+        * Storage/StorageAreaImpl.cpp:
+        (WebKit::StorageAreaImpl::clear):
+        (WebKit::StorageAreaImpl::clearForOriginDeletion):
+        Use the new StorageMap::clear() to avoid constructing a new StorageMap unnecessarily
+        when:
+        - The StorageMap is not shared
+        - The StorageMap is empty
+
 2021-04-26  Alex Christensen  <[email protected]>
 
         Update Mac-specific CMake files

Modified: trunk/Source/WebKitLegacy/Storage/StorageAreaImpl.cpp (276652 => 276653)


--- trunk/Source/WebKitLegacy/Storage/StorageAreaImpl.cpp	2021-04-27 18:52:48 UTC (rev 276652)
+++ trunk/Source/WebKitLegacy/Storage/StorageAreaImpl.cpp	2021-04-27 19:02:53 UTC (rev 276653)
@@ -174,8 +174,8 @@
     if (!m_storageMap->length())
         return;
 
-    unsigned quota = m_storageMap->quota();
-    m_storageMap = StorageMap::create(quota);
+    if (auto newStorageMap = m_storageMap->clear())
+        m_storageMap = WTFMove(newStorageMap);
 
     if (m_storageAreaSync)
         m_storageAreaSync->scheduleClear();
@@ -214,10 +214,8 @@
     ASSERT(!m_isShutdown);
     blockUntilImportComplete();
     
-    if (m_storageMap->length()) {
-        unsigned quota = m_storageMap->quota();
-        m_storageMap = StorageMap::create(quota);
-    }
+    if (auto newStorageMap = m_storageMap->clear())
+        m_storageMap = WTFMove(newStorageMap);
 
     if (m_storageAreaSync) {
         m_storageAreaSync->scheduleClear();
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to