Changeset 276653 in webkit
- Timestamp:
- Apr 27, 2021, 12:02:53 PM (5 years ago)
- Location:
- trunk/Source
- Files:
-
- 14 edited
-
WebCore/ChangeLog (modified) (1 diff)
-
WebCore/platform/sql/SQLiteDatabase.h (modified) (2 diffs)
-
WebCore/platform/sql/SQLiteFileSystem.h (modified) (1 diff)
-
WebCore/storage/StorageMap.cpp (modified) (1 diff)
-
WebCore/storage/StorageMap.h (modified) (1 diff)
-
WebKit/ChangeLog (modified) (1 diff)
-
WebKit/NetworkProcess/WebStorage/LocalStorageDatabase.cpp (modified) (9 diffs)
-
WebKit/NetworkProcess/WebStorage/LocalStorageDatabase.h (modified) (5 diffs)
-
WebKit/NetworkProcess/WebStorage/LocalStorageNamespace.cpp (modified) (1 diff)
-
WebKit/NetworkProcess/WebStorage/StorageArea.cpp (modified) (10 diffs)
-
WebKit/NetworkProcess/WebStorage/StorageArea.h (modified) (2 diffs)
-
WebKit/NetworkProcess/WebStorage/StorageManagerSet.cpp (modified) (1 diff)
-
WebKitLegacy/ChangeLog (modified) (1 diff)
-
WebKitLegacy/Storage/StorageAreaImpl.cpp (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r276649 r276653 1 2021-04-27 Chris Dumez <cdumez@apple.com> 2 3 Don't keep local storage data in memory in the NetworkProcess 4 https://bugs.webkit.org/show_bug.cgi?id=225065 5 6 Reviewed by Alex Christensen. 7 8 * platform/sql/SQLiteDatabase.h: 9 * platform/sql/SQLiteFileSystem.h: 10 Export a couple more symbols. 11 12 * storage/StorageMap.cpp: 13 (WebCore::StorageMap::clear): 14 * storage/StorageMap.h: 15 Add a clear() function to StorageMap so that StorageArea doesn't reconstruct a 16 new StorageMap object unnecessarily when: 17 - The StorageMap is not shared 18 - The StorageMap is empty (and clear() is a no-op) 19 1 20 2021-04-27 Youenn Fablet <youenn@apple.com> 2 21 -
trunk/Source/WebCore/platform/sql/SQLiteDatabase.h
r276648 r276653 59 59 WEBCORE_EXPORT void close(); 60 60 61 void updateLastChangesCount();61 WEBCORE_EXPORT void updateLastChangesCount(); 62 62 63 63 WEBCORE_EXPORT bool executeCommand(const String&); … … 75 75 76 76 int64_t lastInsertRowID(); 77 int lastChanges();77 WEBCORE_EXPORT int lastChanges(); 78 78 79 79 void setBusyTimeout(int ms); -
trunk/Source/WebCore/platform/sql/SQLiteFileSystem.h
r243019 r276653 90 90 #endif 91 91 92 static long long getDatabaseFileSize(const String& fileName);92 WEBCORE_EXPORT static long long getDatabaseFileSize(const String& fileName); 93 93 WEBCORE_EXPORT static Optional<WallTime> databaseCreationTime(const String& fileName); 94 94 WEBCORE_EXPORT static Optional<WallTime> databaseModificationTime(const String& fileName); -
trunk/Source/WebCore/storage/StorageMap.cpp
r254859 r276653 176 176 } 177 177 178 RefPtr<StorageMap> StorageMap::clear() 179 { 180 // Implement copy-on-write semantics here. We're guaranteed that the only refs of StorageMaps belong to Storage objects 181 // so if more than one Storage object refs this map, copy it before mutating it. 182 if (refCount() > 1 && !m_map.isEmpty()) 183 return StorageMap::create(m_quotaSize); 184 185 m_map.clear(); 186 m_currentLength = 0; 187 return nullptr; 188 } 189 178 190 bool StorageMap::contains(const String& key) const 179 191 { -
trunk/Source/WebCore/storage/StorageMap.h
r254859 r276653 44 44 WEBCORE_EXPORT RefPtr<StorageMap> setItemIgnoringQuota(const String& key, const String& value); 45 45 WEBCORE_EXPORT RefPtr<StorageMap> removeItem(const String&, String& oldValue); 46 WEBCORE_EXPORT RefPtr<StorageMap> clear(); 46 47 47 48 WEBCORE_EXPORT bool contains(const String& key) const; -
trunk/Source/WebKit/ChangeLog
r276651 r276653 1 2021-04-27 Chris Dumez <cdumez@apple.com> 2 3 Don't keep local storage data in memory in the NetworkProcess 4 https://bugs.webkit.org/show_bug.cgi?id=225065 5 6 Reviewed by Alex Christensen. 7 8 When a WebPage would start to use the local storage API, the WebProcess 9 would send a sync IPC to the NetworkProcess to retrieve all the local 10 storage entries for the origin. The NetworkProcess would read those 11 entries from a SQLite database and send them back to the WebProcess. 12 Both the NetworkProcess would keep the entries in memory, in a 13 StorageMap object (which is basically a HashMap). On some sites, the 14 strings in the local storage may be very large. It is useful for the 15 WebProcess to keep them in memory for performance reasons, especially 16 considering that the Web API is synchronous. However, there is no real 17 need to keep them in memory in the Network Process side, especially 18 given that the WebProcess already has its own copy. On the network 19 process side, we can get rid of the StorageMap entirely in the local 20 storage case and 100% rely on the SQLite database. This is what this 21 patch implements. 22 23 A/B testing shows this is a 2-3% progression on Membuster. There does 24 not appear to be a meaningful progression on PLUM sadly. This is also 25 neutral on PLT. 26 27 * NetworkProcess/WebStorage/LocalStorageDatabase.cpp: 28 (WebKit::estimateEntrySize): 29 (WebKit::LocalStorageDatabase::create): 30 (WebKit::LocalStorageDatabase::LocalStorageDatabase): 31 (WebKit::LocalStorageDatabase::openDatabase): 32 (WebKit::LocalStorageDatabase::tryToOpenDatabase): 33 (WebKit::LocalStorageDatabase::items const): 34 (WebKit::LocalStorageDatabase::removeItem): 35 (WebKit::LocalStorageDatabase::item const): 36 (WebKit::LocalStorageDatabase::setItem): 37 (WebKit::LocalStorageDatabase::clear): 38 (WebKit::LocalStorageDatabase::close): 39 (WebKit::LocalStorageDatabase::databaseIsEmpty const): 40 (WebKit::LocalStorageDatabase::openIfExisting): 41 (WebKit::LocalStorageDatabase::scopedStatement const): 42 * NetworkProcess/WebStorage/LocalStorageDatabase.h: 43 - Update LocalStorageDatabase API to match more closely the API of 44 StorageMap. This way StorageArea can rely on the LocalStorageDatabase 45 entirely instead of the StorageMap with minimal changes. 46 - Add quota support similarly to what is done in StorageArea so that 47 LocalStorageDatabase::setItem() fails when the quota is reached. 48 - Cache the SQLiteStatements for performance reasons 49 50 * NetworkProcess/WebStorage/LocalStorageNamespace.cpp: 51 (WebKit::LocalStorageNamespace::flushAndClose): 52 We no longer need to call syncToDatabase() before closing since 53 LocalStorageDatabase no longer has a queue on pending operations. 54 55 * NetworkProcess/WebStorage/StorageArea.cpp: 56 (WebKit::StorageArea::StorageArea): 57 (WebKit::StorageArea::addListener): 58 (WebKit::StorageArea::removeListener): 59 (WebKit::StorageArea::setItem): 60 (WebKit::StorageArea::removeItem): 61 (WebKit::StorageArea::clear): 62 (WebKit::StorageArea::items const): 63 (WebKit::StorageArea::ensureDatabase const): 64 * NetworkProcess/WebStorage/StorageArea.h: 65 If the StorageArea is used for local storage, we now don't even initialize 66 a StorageMap and rely entirely on LocalStorageDatabase. If the StorageArea 67 is used for session storage, we keep using a StorageMap since there is no 68 backing database. This is fairly straightforward because I updated the 69 LocalStorageDatabase API to match fairly closely the StorageMap one. 70 71 * NetworkProcess/WebStorage/StorageManagerSet.cpp: 72 (WebKit::StorageManagerSet::waitUntilSyncingLocalStorageFinished): 73 1 74 2021-04-27 Chris Dumez <cdumez@apple.com> 2 75 -
trunk/Source/WebKit/NetworkProcess/WebStorage/LocalStorageDatabase.cpp
r276648 r276653 1 1 /* 2 * Copyright (C) 2008 , 2009, 2010, 2013, 2019Apple Inc. All rights reserved.2 * Copyright (C) 2008-2021 Apple Inc. All rights reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 28 28 29 29 #include "LocalStorageDatabaseTracker.h" 30 #include <WebCore/SQLiteFileSystem.h> 30 31 #include <WebCore/SQLiteStatement.h> 32 #include <WebCore/SQLiteStatementAutoResetScope.h> 31 33 #include <WebCore/SQLiteTransaction.h> 32 #include <WebCore/SecurityOrigin .h>34 #include <WebCore/SecurityOriginData.h> 33 35 #include <WebCore/StorageMap.h> 34 36 #include <WebCore/SuddenTermination.h> 35 37 #include <wtf/FileSystem.h> 38 #include <wtf/HashMap.h> 36 39 #include <wtf/RefPtr.h> 37 40 #include <wtf/RunLoop.h> … … 40 43 #include <wtf/text/WTFString.h> 41 44 42 static const auto databaseUpdateInterval = 1_s;43 44 static const int maximumItemsToUpdate = 100;45 46 45 namespace WebKit { 47 46 using namespace WebCore; 48 47 49 Ref<LocalStorageDatabase> LocalStorageDatabase::create(Ref<WorkQueue>&& queue, Ref<LocalStorageDatabaseTracker>&& tracker, const SecurityOriginData& securityOrigin) 50 { 51 return adoptRef(*new LocalStorageDatabase(WTFMove(queue), WTFMove(tracker), securityOrigin)); 52 } 53 54 LocalStorageDatabase::LocalStorageDatabase(Ref<WorkQueue>&& queue, Ref<LocalStorageDatabaseTracker>&& tracker, const SecurityOriginData& securityOrigin) 48 static const char getItemsQueryString[] = "SELECT key, value FROM ItemTable"; 49 50 static CheckedUint64 estimateEntrySize(const String& key, const String& value) 51 { 52 CheckedUint64 entrySize; 53 entrySize += key.length() * sizeof(UChar); 54 entrySize += value.length() * sizeof(UChar); 55 return entrySize; 56 } 57 58 Ref<LocalStorageDatabase> LocalStorageDatabase::create(Ref<WorkQueue>&& queue, Ref<LocalStorageDatabaseTracker>&& tracker, const SecurityOriginData& securityOrigin, unsigned quotaInBytes) 59 { 60 return adoptRef(*new LocalStorageDatabase(WTFMove(queue), WTFMove(tracker), securityOrigin, quotaInBytes)); 61 } 62 63 LocalStorageDatabase::LocalStorageDatabase(Ref<WorkQueue>&& queue, Ref<LocalStorageDatabaseTracker>&& tracker, const SecurityOriginData& securityOrigin, unsigned quotaInBytes) 55 64 : m_queue(WTFMove(queue)) 56 65 , m_tracker(WTFMove(tracker)) 57 66 , m_securityOrigin(securityOrigin) 58 67 , m_databasePath(m_tracker->databasePath(m_securityOrigin)) 68 , m_quotaInBytes(quotaInBytes) 59 69 { 60 70 ASSERT(!RunLoop::isMain()); … … 67 77 } 68 78 69 void LocalStorageDatabase::openDatabase(DatabaseOpeningStrategy openingStrategy) 70 { 79 void LocalStorageDatabase::openDatabase(ShouldCreateDatabase shouldCreateDatabase) 80 { 81 ASSERT(!RunLoop::isMain()); 71 82 ASSERT(!m_database.isOpen()); 72 83 ASSERT(!m_failedToOpenDatabase); 73 84 74 if (!tryToOpenDatabase( openingStrategy)) {85 if (!tryToOpenDatabase(shouldCreateDatabase)) { 75 86 m_failedToOpenDatabase = true; 76 87 return; … … 81 92 } 82 93 83 bool LocalStorageDatabase::tryToOpenDatabase( DatabaseOpeningStrategy openingStrategy)84 { 85 ASSERT(!RunLoop::isMain()); 86 if (!FileSystem::fileExists(m_databasePath) && openingStrategy == SkipIfNonExistent)94 bool LocalStorageDatabase::tryToOpenDatabase(ShouldCreateDatabase shouldCreateDatabase) 95 { 96 ASSERT(!RunLoop::isMain()); 97 if (!FileSystem::fileExists(m_databasePath) && shouldCreateDatabase == ShouldCreateDatabase::No) 87 98 return true; 88 99 … … 118 129 bool LocalStorageDatabase::migrateItemTableIfNeeded() 119 130 { 131 ASSERT(!RunLoop::isMain()); 120 132 if (!m_database.tableExists("ItemTable")) 121 133 return true; … … 154 166 } 155 167 156 void LocalStorageDatabase::importItems(StorageMap& storageMap) 157 { 158 if (m_didImportItems) 159 return; 160 161 // FIXME: If it can't import, then the default WebKit behavior should be that of private browsing, 162 // not silently ignoring it. https://bugs.webkit.org/show_bug.cgi?id=25894 163 164 // We set this to true even if we don't end up importing any items due to failure because 165 // there's really no good way to recover other than not importing anything. 166 m_didImportItems = true; 167 168 openDatabase(SkipIfNonExistent); 169 if (!m_database.isOpen()) 170 return; 171 172 SQLiteStatement query(m_database, "SELECT key, value FROM ItemTable"_str); 173 if (query.prepare() != SQLITE_OK) { 168 HashMap<String, String> LocalStorageDatabase::items() const 169 { 170 ASSERT(!RunLoop::isMain()); 171 if (!m_database.isOpen()) 172 return { }; 173 174 auto query = scopedStatement(m_getItemsStatement, getItemsQueryString); 175 if (!query) { 174 176 LOG_ERROR("Unable to select items from ItemTable for local storage"); 175 return ;177 return { }; 176 178 } 177 179 178 180 HashMap<String, String> items; 179 180 int result = query.step(); 181 int result = query->step(); 181 182 while (result == SQLITE_ROW) { 182 String key = query .getColumnText(0);183 String value = query .getColumnBlobAsString(1);183 String key = query->getColumnText(0); 184 String value = query->getColumnBlobAsString(1); 184 185 if (!key.isNull() && !value.isNull()) 185 186 items.add(WTFMove(key), WTFMove(value)); 186 result = query.step(); 187 } 188 187 result = query->step(); 188 } 189 190 if (result != SQLITE_DONE) 191 LOG_ERROR("Error reading items from ItemTable for local storage"); 192 193 return items; 194 } 195 196 void LocalStorageDatabase::removeItem(const String& key, String& oldValue) 197 { 198 ASSERT(!RunLoop::isMain()); 199 if (!m_database.isOpen()) 200 return; 201 202 oldValue = item(key); 203 if (oldValue.isNull()) 204 return; 205 206 auto deleteStatement = scopedStatement(m_deleteItemStatement, "DELETE FROM ItemTable WHERE key=?"_s); 207 if (!deleteStatement) { 208 LOG_ERROR("Failed to prepare delete statement - cannot write to local storage database"); 209 return; 210 } 211 deleteStatement->bindText(1, key); 212 213 int result = deleteStatement->step(); 189 214 if (result != SQLITE_DONE) { 190 LOG_ERROR("Error reading items from ItemTable for local storage"); 191 return; 192 } 193 194 storageMap.importItems(WTFMove(items)); 195 } 196 197 void LocalStorageDatabase::setItem(const String& key, const String& value) 198 { 199 itemDidChange(key, value); 200 } 201 202 void LocalStorageDatabase::removeItem(const String& key) 203 { 204 itemDidChange(key, String()); 205 } 206 207 void LocalStorageDatabase::clear() 208 { 209 m_changedItems.clear(); 210 m_shouldClearItems = true; 211 212 scheduleDatabaseUpdate(); 215 LOG_ERROR("Failed to delete item in the local storage database - %i", result); 216 return; 217 } 218 219 if (m_databaseSize) { 220 CheckedUint64 entrySize = estimateEntrySize(key, oldValue); 221 if (entrySize.hasOverflowed() || entrySize.unsafeGet() >= *m_databaseSize) 222 *m_databaseSize = 0; 223 else 224 *m_databaseSize -= entrySize.unsafeGet(); 225 } 226 } 227 228 String LocalStorageDatabase::item(const String& key) const 229 { 230 ASSERT(!RunLoop::isMain()); 231 if (!m_database.isOpen()) 232 return { }; 233 234 auto query = scopedStatement(m_getItemStatement, "SELECT value FROM ItemTable WHERE key=?"_s); 235 if (!query) { 236 LOG_ERROR("Unable to get item from ItemTable for local storage"); 237 return { }; 238 } 239 query->bindText(1, key); 240 241 int result = query->step(); 242 if (result == SQLITE_ROW) 243 return query->getColumnBlobAsString(0); 244 if (result != SQLITE_DONE) 245 LOG_ERROR("Error get item from ItemTable for local storage"); 246 return { }; 247 } 248 249 void LocalStorageDatabase::setItem(const String& key, const String& value, String& oldValue, bool& quotaException) 250 { 251 ASSERT(!RunLoop::isMain()); 252 if (!m_database.isOpen()) 253 openDatabase(ShouldCreateDatabase::Yes); 254 if (!m_database.isOpen()) 255 return; 256 257 if (m_quotaInBytes != WebCore::StorageMap::noQuota) { 258 if (!m_databaseSize) 259 m_databaseSize = SQLiteFileSystem::getDatabaseFileSize(m_databasePath); 260 if (*m_databaseSize >= m_quotaInBytes) { 261 quotaException = true; 262 return; 263 } 264 CheckedUint64 newDatabaseSize = *m_databaseSize; 265 newDatabaseSize += estimateEntrySize(key, value); 266 if (newDatabaseSize.hasOverflowed() || newDatabaseSize.unsafeGet() > m_quotaInBytes) { 267 quotaException = true; 268 return; 269 } 270 m_databaseSize = newDatabaseSize.unsafeGet(); 271 } 272 273 oldValue = item(key); 274 275 auto insertStatement = scopedStatement(m_insertStatement, "INSERT INTO ItemTable VALUES (?, ?)"_s); 276 if (!insertStatement) { 277 LOG_ERROR("Failed to prepare insert statement - cannot write to local storage database"); 278 return; 279 } 280 281 insertStatement->bindText(1, key); 282 insertStatement->bindBlob(2, value); 283 284 int result = insertStatement->step(); 285 if (result != SQLITE_DONE) 286 LOG_ERROR("Failed to update item in the local storage database - %i", result); 287 } 288 289 bool LocalStorageDatabase::clear() 290 { 291 ASSERT(!RunLoop::isMain()); 292 if (!m_database.isOpen()) 293 return false; 294 295 auto clearStatement = scopedStatement(m_clearStatement, "DELETE FROM ItemTable"_s); 296 if (!clearStatement) { 297 LOG_ERROR("Failed to prepare clear statement - cannot write to local storage database"); 298 return false; 299 } 300 301 int result = clearStatement->step(); 302 if (result != SQLITE_DONE) { 303 LOG_ERROR("Failed to clear all items in the local storage database - %i", result); 304 return false; 305 } 306 307 m_databaseSize = 0; 308 309 return m_database.lastChanges() > 0; 213 310 } 214 311 215 312 void LocalStorageDatabase::close() 216 313 { 314 ASSERT(!RunLoop::isMain()); 217 315 if (m_isClosed) 218 316 return; 219 317 m_isClosed = true; 220 318 221 if (m_didScheduleDatabaseUpdate) {222 updateDatabaseWithChangedItems(m_changedItems);223 m_changedItems.clear();224 }225 226 319 bool isEmpty = databaseIsEmpty(); 320 321 m_clearStatement = nullptr; 322 m_insertStatement = nullptr; 323 m_getItemStatement = nullptr; 324 m_getItemsStatement = nullptr; 325 m_deleteItemStatement = nullptr; 227 326 228 327 if (m_database.isOpen()) … … 233 332 } 234 333 235 void LocalStorageDatabase::itemDidChange(const String& key, const String& value) 236 { 237 m_changedItems.set(key, value); 238 scheduleDatabaseUpdate(); 239 } 240 241 void LocalStorageDatabase::scheduleDatabaseUpdate() 242 { 243 if (m_didScheduleDatabaseUpdate) 244 return; 245 246 if (!m_disableSuddenTerminationWhileWritingToLocalStorage) 247 m_disableSuddenTerminationWhileWritingToLocalStorage = makeUnique<SuddenTerminationDisabler>(); 248 249 m_didScheduleDatabaseUpdate = true; 250 251 m_queue->dispatch([protectedThis = makeRef(*this)] { 252 protectedThis->updateDatabase(); 253 }); 254 } 255 256 void LocalStorageDatabase::updateDatabase() 257 { 258 if (m_isClosed) 259 return; 260 261 m_didScheduleDatabaseUpdate = false; 262 263 HashMap<String, String> changedItems; 264 if (m_changedItems.size() <= maximumItemsToUpdate) { 265 // There are few enough changed items that we can just always write all of them. 266 m_changedItems.swap(changedItems); 267 updateDatabaseWithChangedItems(changedItems); 268 m_disableSuddenTerminationWhileWritingToLocalStorage = nullptr; 269 } else { 270 for (int i = 0; i < maximumItemsToUpdate; ++i) { 271 auto it = m_changedItems.begin(); 272 changedItems.add(it->key, it->value); 273 274 m_changedItems.remove(it); 275 } 276 277 ASSERT(changedItems.size() <= maximumItemsToUpdate); 278 279 // Reschedule the update for the remaining items. 280 scheduleDatabaseUpdate(); 281 updateDatabaseWithChangedItems(changedItems); 282 } 283 } 284 285 void LocalStorageDatabase::updateDatabaseWithChangedItems(const HashMap<String, String>& changedItems) 286 { 287 if (!m_database.isOpen()) 288 openDatabase(CreateIfNonExistent); 289 if (!m_database.isOpen()) 290 return; 291 292 if (m_shouldClearItems) { 293 m_shouldClearItems = false; 294 295 SQLiteStatement clearStatement(m_database, "DELETE FROM ItemTable"); 296 if (clearStatement.prepare() != SQLITE_OK) { 297 LOG_ERROR("Failed to prepare clear statement - cannot write to local storage database"); 298 return; 299 } 300 301 int result = clearStatement.step(); 302 if (result != SQLITE_DONE) { 303 LOG_ERROR("Failed to clear all items in the local storage database - %i", result); 304 return; 305 } 306 } 307 308 SQLiteStatement insertStatement(m_database, "INSERT INTO ItemTable VALUES (?, ?)"); 309 if (insertStatement.prepare() != SQLITE_OK) { 310 LOG_ERROR("Failed to prepare insert statement - cannot write to local storage database"); 311 return; 312 } 313 314 SQLiteStatement deleteStatement(m_database, "DELETE FROM ItemTable WHERE key=?"); 315 if (deleteStatement.prepare() != SQLITE_OK) { 316 LOG_ERROR("Failed to prepare delete statement - cannot write to local storage database"); 317 return; 318 } 319 320 SQLiteTransaction transaction(m_database); 321 transaction.begin(); 322 323 for (auto it = changedItems.begin(), end = changedItems.end(); it != end; ++it) { 324 // A null value means that the key/value pair should be deleted. 325 SQLiteStatement& statement = it->value.isNull() ? deleteStatement : insertStatement; 326 327 statement.bindText(1, it->key); 328 329 // If we're inserting a key/value pair, bind the value as well. 330 if (!it->value.isNull()) 331 statement.bindBlob(2, it->value); 332 333 int result = statement.step(); 334 if (result != SQLITE_DONE) { 335 LOG_ERROR("Failed to update item in the local storage database - %i", result); 336 break; 337 } 338 339 statement.reset(); 340 } 341 342 transaction.commit(); 343 } 344 345 bool LocalStorageDatabase::databaseIsEmpty() 346 { 334 bool LocalStorageDatabase::databaseIsEmpty() const 335 { 336 ASSERT(!RunLoop::isMain()); 347 337 if (!m_database.isOpen()) 348 338 return false; … … 363 353 } 364 354 355 void LocalStorageDatabase::openIfExisting() 356 { 357 ASSERT(!RunLoop::isMain()); 358 if (m_database.isOpen()) 359 return; 360 361 openDatabase(ShouldCreateDatabase::No); 362 363 // Prewarm the getItems statement for performance since the pages block synchronously on retrieving the items. 364 if (m_database.isOpen()) 365 scopedStatement(m_getItemsStatement, getItemsQueryString); 366 } 367 368 SQLiteStatementAutoResetScope LocalStorageDatabase::scopedStatement(std::unique_ptr<SQLiteStatement>& statement, const String& query) const 369 { 370 ASSERT(!RunLoop::isMain()); 371 if (!statement) { 372 statement = makeUnique<SQLiteStatement>(m_database, query); 373 ASSERT(m_database.isOpen()); 374 if (statement->prepare() != SQLITE_OK) 375 return SQLiteStatementAutoResetScope { }; 376 } 377 return SQLiteStatementAutoResetScope { statement.get() }; 378 } 379 365 380 void LocalStorageDatabase::handleLowMemoryWarning() 366 381 { -
trunk/Source/WebKit/NetworkProcess/WebStorage/LocalStorageDatabase.h
r276648 r276653 1 1 /* 2 * Copyright (C) 2008 , 2009, 2010, 2013, 2019Apple Inc. All rights reserved.2 * Copyright (C) 2008-2021 Apple Inc. All rights reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 27 27 28 28 #include <WebCore/SQLiteDatabase.h> 29 #include <WebCore/SecurityOriginData.h>30 29 #include <wtf/Forward.h> 31 #include <wtf/HashMap.h>32 30 #include <wtf/RefCounted.h> 33 #include <wtf/WorkQueue.h>34 31 35 32 namespace WebCore { 36 class SecurityOrigin; 37 class StorageMap; 33 class SQLiteStatementAutoResetScope; 38 34 class SuddenTerminationDisabler; 35 36 struct SecurityOriginData; 39 37 } 40 38 … … 45 43 class LocalStorageDatabase : public RefCounted<LocalStorageDatabase> { 46 44 public: 47 static Ref<LocalStorageDatabase> create(Ref<WorkQueue>&&, Ref<LocalStorageDatabaseTracker>&&, const WebCore::SecurityOriginData& );45 static Ref<LocalStorageDatabase> create(Ref<WorkQueue>&&, Ref<LocalStorageDatabaseTracker>&&, const WebCore::SecurityOriginData&, unsigned quotaInBytes); 48 46 ~LocalStorageDatabase(); 49 47 50 // Will block until the import is complete. 51 void importItems(WebCore::StorageMap&); 52 53 void setItem(const String& key, const String& value); 54 void removeItem(const String& key); 55 void clear(); 56 57 void updateDatabase(); 48 HashMap<String, String> items() const; 49 void openIfExisting(); 50 void removeItem(const String& key, String& oldValue); 51 bool clear(); 52 String item(const String& key) const; 53 void setItem(const String& key, const String& value, String& oldValue, bool& quotaException); 58 54 59 55 // Will block until all pending changes have been written to disk. … … 63 59 64 60 private: 65 LocalStorageDatabase(Ref<WorkQueue>&&, Ref<LocalStorageDatabaseTracker>&&, const WebCore::SecurityOriginData& );61 LocalStorageDatabase(Ref<WorkQueue>&&, Ref<LocalStorageDatabaseTracker>&&, const WebCore::SecurityOriginData&, unsigned quotaInBytes); 66 62 67 enum DatabaseOpeningStrategy { 68 CreateIfNonExistent, 69 SkipIfNonExistent 70 }; 71 bool tryToOpenDatabase(DatabaseOpeningStrategy); 72 void openDatabase(DatabaseOpeningStrategy); 63 enum class ShouldCreateDatabase : bool { No, Yes }; 64 bool tryToOpenDatabase(ShouldCreateDatabase); 65 void openDatabase(ShouldCreateDatabase); 73 66 74 67 bool migrateItemTableIfNeeded(); 68 bool databaseIsEmpty() const; 75 69 76 void itemDidChange(const String& key, const String& value); 77 78 void scheduleDatabaseUpdate(); 79 void updateDatabaseWithChangedItems(const HashMap<String, String>&); 80 81 bool databaseIsEmpty(); 70 WebCore::SQLiteStatementAutoResetScope scopedStatement(std::unique_ptr<WebCore::SQLiteStatement>&, const String& query) const; 82 71 83 72 Ref<WorkQueue> m_queue; … … 86 75 87 76 String m_databasePath; 88 WebCore::SQLiteDatabase m_database; 77 mutable WebCore::SQLiteDatabase m_database; 78 unsigned m_quotaInBytes { 0 }; 89 79 bool m_failedToOpenDatabase { false }; 90 bool m_didImportItems { false };91 80 bool m_isClosed { false }; 92 93 bool m_didScheduleDatabaseUpdate { false }; 94 bool m_shouldClearItems { false }; 95 HashMap<String, String> m_changedItems; 81 Optional<uint64_t> m_databaseSize; 96 82 97 83 std::unique_ptr<WebCore::SuddenTerminationDisabler> m_disableSuddenTerminationWhileWritingToLocalStorage; 84 mutable std::unique_ptr<WebCore::SQLiteStatement> m_clearStatement; 85 mutable std::unique_ptr<WebCore::SQLiteStatement> m_insertStatement; 86 mutable std::unique_ptr<WebCore::SQLiteStatement> m_getItemStatement; 87 mutable std::unique_ptr<WebCore::SQLiteStatement> m_getItemsStatement; 88 mutable std::unique_ptr<WebCore::SQLiteStatement> m_deleteItemStatement; 98 89 }; 99 90 -
trunk/Source/WebKit/NetworkProcess/WebStorage/LocalStorageNamespace.cpp
r275886 r276653 74 74 { 75 75 ASSERT(!RunLoop::isMain()); 76 if (auto* storageArea = m_storageAreaMap.get(origin)) { 77 storageArea->syncToDatabase(); 76 if (auto* storageArea = m_storageAreaMap.get(origin)) 78 77 storageArea->close(); 79 }80 78 } 81 79 -
trunk/Source/WebKit/NetworkProcess/WebStorage/StorageArea.cpp
r276648 r276653 32 32 #include "StorageManager.h" 33 33 #include <WebCore/StorageMap.h> 34 #include <wtf/Scope.h> 34 35 35 36 namespace WebKit { … … 41 42 , m_securityOrigin(securityOrigin) 42 43 , m_quotaInBytes(quotaInBytes) 43 , m_storageMap(StorageMap::create(m_quotaInBytes))44 44 , m_identifier(Identifier::generate()) 45 45 , m_queue(WTFMove(queue)) 46 46 { 47 47 ASSERT(!RunLoop::isMain()); 48 if (isEphemeral()) 49 m_storageMap = StorageMap::create(m_quotaInBytes); 48 50 } 49 51 … … 62 64 63 65 if (m_eventListeners.isEmpty() && !isEphemeral()) 64 openDatabaseAndImportItemsIfNeeded();66 ensureDatabase(); 65 67 66 68 m_eventListeners.add(connectionID); … … 75 77 return; 76 78 77 if (!m_localStorageNamespace) 78 return; 79 80 syncToDatabase(); 81 m_localStorageNamespace->removeStorageArea(m_securityOrigin); 79 if (m_localStorageNamespace) 80 m_localStorageNamespace->removeStorageArea(m_securityOrigin); 82 81 } 83 82 … … 102 101 { 103 102 ASSERT(!RunLoop::isMain()); 104 openDatabaseAndImportItemsIfNeeded();105 103 106 104 String oldValue; 107 108 auto newStorageMap = m_storageMap->setItem(key, value, oldValue, quotaException); 109 if (newStorageMap) 110 m_storageMap = WTFMove(newStorageMap); 105 if (isEphemeral()) { 106 auto newStorageMap = m_storageMap->setItem(key, value, oldValue, quotaException); 107 if (newStorageMap) 108 m_storageMap = WTFMove(newStorageMap); 109 } else 110 ensureDatabase().setItem(key, value, oldValue, quotaException); 111 111 112 112 if (quotaException) 113 113 return; 114 114 115 if (m_localStorageDatabase)116 m_localStorageDatabase->setItem(key, value);117 118 115 dispatchEvents(sourceConnection, storageAreaImplID, key, oldValue, value, urlString); 119 116 } … … 122 119 { 123 120 ASSERT(!RunLoop::isMain()); 124 openDatabaseAndImportItemsIfNeeded();125 121 126 122 String oldValue; 127 auto newStorageMap = m_storageMap->removeItem(key, oldValue); 128 if (newStorageMap) 129 m_storageMap = WTFMove(newStorageMap); 130 123 if (isEphemeral()) { 124 auto newStorageMap = m_storageMap->removeItem(key, oldValue); 125 if (newStorageMap) 126 m_storageMap = WTFMove(newStorageMap); 127 } else 128 ensureDatabase().removeItem(key, oldValue); 131 129 if (oldValue.isNull()) 132 130 return; 133 131 134 if (m_localStorageDatabase)135 m_localStorageDatabase->removeItem(key);136 137 132 dispatchEvents(sourceConnection, storageAreaImplID, key, oldValue, String(), urlString); 138 133 } … … 141 136 { 142 137 ASSERT(!RunLoop::isMain()); 143 openDatabaseAndImportItemsIfNeeded(); 144 145 if (!m_storageMap->length()) 146 return; 147 148 m_storageMap = StorageMap::create(m_quotaInBytes); 149 150 if (m_localStorageDatabase) 151 m_localStorageDatabase->clear(); 138 139 if (isEphemeral()) { 140 if (!m_storageMap->length()) 141 return; 142 if (auto newStorageMap = m_storageMap->clear()) 143 m_storageMap = WTFMove(newStorageMap); 144 } else { 145 if (!ensureDatabase().clear()) 146 return; 147 } 152 148 153 149 dispatchEvents(sourceConnection, storageAreaImplID, String(), String(), String(), urlString); 154 150 } 155 151 156 const HashMap<String, String>& StorageArea::items() const 157 { 158 ASSERT(!RunLoop::isMain()); 159 openDatabaseAndImportItemsIfNeeded(); 160 161 return m_storageMap->items(); 152 HashMap<String, String> StorageArea::items() const 153 { 154 ASSERT(!RunLoop::isMain()); 155 if (isEphemeral()) 156 return m_storageMap->items(); 157 158 return ensureDatabase().items(); 162 159 } 163 160 … … 165 162 { 166 163 ASSERT(!RunLoop::isMain()); 167 m_storageMap = StorageMap::create(m_quotaInBytes); 168 169 if (m_localStorageDatabase) { 170 m_localStorageDatabase->close(); 171 m_localStorageDatabase = nullptr; 164 if (isEphemeral()) { 165 if (auto newStorageMap = m_storageMap->clear()) 166 m_storageMap = WTFMove(newStorageMap); 167 } else { 168 if (m_localStorageDatabase) { 169 m_localStorageDatabase->close(); 170 m_localStorageDatabase = nullptr; 171 } 172 172 } 173 173 … … 180 180 } 181 181 182 void StorageArea::openDatabaseAndImportItemsIfNeeded() const 183 { 184 ASSERT(!RunLoop::isMain()); 185 if (!m_localStorageNamespace) 186 return; 182 LocalStorageDatabase& StorageArea::ensureDatabase() const 183 { 184 ASSERT(!isEphemeral()); 187 185 188 186 ASSERT(m_localStorageNamespace->storageManager()->localStorageDatabaseTracker()); 189 187 // 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. 190 if (!m_localStorageDatabase) 191 m_localStorageDatabase = LocalStorageDatabase::create(m_queue.copyRef(), *m_localStorageNamespace->storageManager()->localStorageDatabaseTracker(), m_securityOrigin); 192 193 if (m_didImportItemsFromDatabase) 194 return; 195 196 m_localStorageDatabase->importItems(*m_storageMap); 197 m_didImportItemsFromDatabase = true; 188 if (!m_localStorageDatabase) { 189 m_localStorageDatabase = LocalStorageDatabase::create(m_queue.copyRef(), *m_localStorageNamespace->storageManager()->localStorageDatabaseTracker(), m_securityOrigin, m_quotaInBytes); 190 m_localStorageDatabase->openIfExisting(); 191 } 192 return *m_localStorageDatabase; 198 193 } 199 194 … … 212 207 } 213 208 214 void StorageArea::syncToDatabase()215 {216 if (!m_localStorageDatabase)217 return;218 219 m_localStorageDatabase->updateDatabase();220 }221 222 209 void StorageArea::close() 223 210 { -
trunk/Source/WebKit/NetworkProcess/WebStorage/StorageArea.h
r276648 r276653 64 64 void clear(IPC::Connection::UniqueID sourceConnection, StorageAreaImplIdentifier, const String& urlString); 65 65 66 const HashMap<String, String>&items() const;66 HashMap<String, String> items() const; 67 67 void clear(); 68 68 69 69 bool isEphemeral() const { return !m_localStorageNamespace; } 70 70 71 void openDatabaseAndImportItemsIfNeeded() const;72 73 void syncToDatabase();74 71 void close(); 75 72 … … 79 76 void dispatchEvents(IPC::Connection::UniqueID sourceConnection, StorageAreaImplIdentifier, const String& key, const String& oldValue, const String& newValue, const String& urlString) const; 80 77 78 LocalStorageDatabase& ensureDatabase() const; 79 81 80 // Will be null if the storage area belongs to a session storage namespace or the storage area is in an ephemeral session. 82 81 WeakPtr<LocalStorageNamespace> m_localStorageNamespace; 83 82 mutable RefPtr<LocalStorageDatabase> m_localStorageDatabase; 84 mutable bool m_didImportItemsFromDatabase { false };85 83 86 84 WebCore::SecurityOriginData m_securityOrigin; -
trunk/Source/WebKit/NetworkProcess/WebStorage/StorageManagerSet.cpp
r276648 r276653 156 156 ASSERT(RunLoop::isMain()); 157 157 158 BinarySemaphore semaphore; 159 m_queue->dispatch([this, &semaphore] { 160 for (const auto& storageArea : m_storageAreas.values()) { 161 ASSERT(storageArea); 162 if (storageArea) 163 storageArea->syncToDatabase(); 164 } 165 semaphore.signal(); 166 }); 167 semaphore.wait(); 158 m_queue->dispatchSync([] { }); 168 159 } 169 160 -
trunk/Source/WebKitLegacy/ChangeLog
r276614 r276653 1 2021-04-27 Chris Dumez <cdumez@apple.com> 2 3 Don't keep local storage data in memory in the NetworkProcess 4 https://bugs.webkit.org/show_bug.cgi?id=225065 5 6 Reviewed by Alex Christensen. 7 8 * Storage/StorageAreaImpl.cpp: 9 (WebKit::StorageAreaImpl::clear): 10 (WebKit::StorageAreaImpl::clearForOriginDeletion): 11 Use the new StorageMap::clear() to avoid constructing a new StorageMap unnecessarily 12 when: 13 - The StorageMap is not shared 14 - The StorageMap is empty 15 1 16 2021-04-26 Alex Christensen <achristensen@webkit.org> 2 17 -
trunk/Source/WebKitLegacy/Storage/StorageAreaImpl.cpp
r254169 r276653 175 175 return; 176 176 177 unsigned quota = m_storageMap->quota();178 m_storageMap = StorageMap::create(quota);177 if (auto newStorageMap = m_storageMap->clear()) 178 m_storageMap = WTFMove(newStorageMap); 179 179 180 180 if (m_storageAreaSync) … … 215 215 blockUntilImportComplete(); 216 216 217 if (m_storageMap->length()) { 218 unsigned quota = m_storageMap->quota(); 219 m_storageMap = StorageMap::create(quota); 220 } 217 if (auto newStorageMap = m_storageMap->clear()) 218 m_storageMap = WTFMove(newStorageMap); 221 219 222 220 if (m_storageAreaSync) {
Note:
See TracChangeset
for help on using the changeset viewer.