Diff
Modified: trunk/LayoutTests/ChangeLog (261532 => 261533)
--- trunk/LayoutTests/ChangeLog 2020-05-12 00:34:35 UTC (rev 261532)
+++ trunk/LayoutTests/ChangeLog 2020-05-12 00:38:17 UTC (rev 261533)
@@ -1,3 +1,19 @@
+2020-05-11 Ben Nham <[email protected]>
+
+ Improve accuracy of IndexedDB estimated write size computation
+ https://bugs.webkit.org/show_bug.cgi?id=211360
+
+ Reviewed by Brady Eidson.
+
+ Added a layout test to check that the size estimate associated with adding an object to a
+ store with many indices is reasonable.
+
+ * platform/mac-wk1/TestExpectations: Skip test on Mac WK1 since it doesn't implement quota checks.
+ * platform/win/TestExpectations: Skip test on Windows since it doesn't implement quota checks.
+ * storage/indexeddb/resources/storage-limit-with-indices.js: Added.
+ * storage/indexeddb/storage-limit-with-indices-expected.txt: Added.
+ * storage/indexeddb/storage-limit-with-indices.html: Added.
+
2020-05-11 Kenneth Russell <[email protected]>
Enable conformance2/textures/canvas/ and image_data/ tests
Modified: trunk/LayoutTests/platform/mac-wk1/TestExpectations (261532 => 261533)
--- trunk/LayoutTests/platform/mac-wk1/TestExpectations 2020-05-12 00:34:35 UTC (rev 261532)
+++ trunk/LayoutTests/platform/mac-wk1/TestExpectations 2020-05-12 00:38:17 UTC (rev 261533)
@@ -332,6 +332,7 @@
http/tests/IndexedDB/storage-limit-1.https.html [ Skip ]
http/tests/IndexedDB/storage-limit-2.https.html [ Skip ]
storage/indexeddb/storage-limit.html [ Skip ]
+storage/indexeddb/storage-limit-with-indices.html [ Skip ]
storage/indexeddb/request-size-estimate.html [ Skip ]
# Skip WebRTC for now in WK1
Modified: trunk/LayoutTests/platform/win/TestExpectations (261532 => 261533)
--- trunk/LayoutTests/platform/win/TestExpectations 2020-05-12 00:34:35 UTC (rev 261532)
+++ trunk/LayoutTests/platform/win/TestExpectations 2020-05-12 00:38:17 UTC (rev 261533)
@@ -2399,6 +2399,7 @@
http/tests/xmlhttprequest/web-apps/013.html
storage/indexeddb/storage-limit.html [ Skip ]
+storage/indexeddb/storage-limit-with-indices.html [ Skip ]
storage/indexeddb/request-size-estimate.html [ Skip ]
http/tests/IndexedDB/storage-limit.https.html [ Skip ]
http/tests/IndexedDB/storage-limit-1.https.html [ Skip ]
Added: trunk/LayoutTests/storage/indexeddb/resources/storage-limit-with-indices.js (0 => 261533)
--- trunk/LayoutTests/storage/indexeddb/resources/storage-limit-with-indices.js (rev 0)
+++ trunk/LayoutTests/storage/indexeddb/resources/storage-limit-with-indices.js 2020-05-12 00:38:17 UTC (rev 261533)
@@ -0,0 +1,50 @@
+if (this.importScripts) {
+ importScripts('../../../resources/js-test.js');
+ importScripts('shared.js');
+}
+
+if (window.testRunner)
+ testRunner.setAllowStorageQuotaIncrease(false);
+
+var quota = 400 * 1024; // default quota for testing.
+var numIndices = 10;
+
+description("This test checks that the size estimate associated with adding an object to a store with many indices is reasonable.");
+
+indexedDBTest(prepareDatabase, onOpenSuccess);
+
+function prepareDatabase(event)
+{
+ preamble(event);
+
+ evalAndLog("db = event.target.result");
+ evalAndLog("store = db.createObjectStore('store')");
+
+ let db = event.target;
+ for (let i = 0; i < numIndices; i++) {
+ let indexName = 'a' + i;
+ store.createIndex(indexName, indexName);
+ }
+}
+
+function onOpenSuccess(event)
+{
+ preamble(event);
+ evalAndLog("db = event.target.result");
+ evalAndLog("store = db.transaction('store', 'readwrite').objectStore('store')");
+
+ // The size of the indexed attributes a0, a1, ... is small, so they shouldn't have a material
+ // effect on the estimated put size for quota purposes.
+ evalAndLog(`request = store.add({a0: 0, a1: 1, payload: new Uint8Array(${quota / numIndices})}, 42)`);
+
+ request._onsuccess_ = function(event) {
+ reqEvent = event;
+ shouldBe("reqEvent.target.result", "42");
+ finishJSTest();
+ }
+
+ request._onerror_ = function(event) {
+ testFailed("Add operation failed, but it should succeed because it uses much less space than the storage limit.");
+ finishJSTest();
+ }
+}
Added: trunk/LayoutTests/storage/indexeddb/storage-limit-with-indices-expected.txt (0 => 261533)
--- trunk/LayoutTests/storage/indexeddb/storage-limit-with-indices-expected.txt (rev 0)
+++ trunk/LayoutTests/storage/indexeddb/storage-limit-with-indices-expected.txt 2020-05-12 00:38:17 UTC (rev 261533)
@@ -0,0 +1,23 @@
+This test checks that the size estimate associated with adding an object to a store with many indices is reasonable.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+indexedDB = self.indexedDB || self.webkitIndexedDB || self.mozIndexedDB || self.msIndexedDB || self.OIndexedDB;
+
+indexedDB.deleteDatabase(dbname)
+indexedDB.open(dbname)
+
+prepareDatabase():
+db = event.target.result
+store = db.createObjectStore('store')
+
+onOpenSuccess():
+db = event.target.result
+store = db.transaction('store', 'readwrite').objectStore('store')
+request = store.add({a0: 0, a1: 1, payload: new Uint8Array(40960)}, 42)
+PASS reqEvent.target.result is 42
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/storage/indexeddb/storage-limit-with-indices.html (0 => 261533)
--- trunk/LayoutTests/storage/indexeddb/storage-limit-with-indices.html (rev 0)
+++ trunk/LayoutTests/storage/indexeddb/storage-limit-with-indices.html 2020-05-12 00:38:17 UTC (rev 261533)
@@ -0,0 +1,9 @@
+<html>
+<head>
+<script src=""
+<script src=""
+</head>
+<body>
+<script src=""
+</body>
+</html>
\ No newline at end of file
Modified: trunk/Source/WebCore/ChangeLog (261532 => 261533)
--- trunk/Source/WebCore/ChangeLog 2020-05-12 00:34:35 UTC (rev 261532)
+++ trunk/Source/WebCore/ChangeLog 2020-05-12 00:38:17 UTC (rev 261533)
@@ -1,3 +1,51 @@
+2020-05-11 Ben Nham <[email protected]>
+
+ Improve accuracy of IndexedDB estimated write size computation
+ https://bugs.webkit.org/show_bug.cgi?id=211360
+
+ Reviewed by Brady Eidson.
+
+ We currently estimate the size of a put in IndexedDB for quota check purposes with something
+ like:
+
+ estimatedWriteSize = (1 + numIndices) * (keySize + valueSize)
+
+ However, this can lead to large overestimates of write sizes. This is because secondary
+ indices only store a mapping of secondary index key => primary key; they do not store the
+ entire value. In the example site attached to 202137 (another DB quota-related bug), the
+ the heuristic estimates that one of the put operations would use more than 800 MB when it
+ actually uses 220 MB. This inaccuracy leads to spurious disk quota permission modals being
+ presented in Safari.
+
+ This patch improves the write size computation by generating the secondary index keys before
+ estimating the write size. The performance should be about the same since we save the
+ generated index keys for later usage when we actually add the record to the DB.
+
+ * Headers.cmake:
+ * Modules/indexeddb/server/IDBBackingStore.h:
+ * Modules/indexeddb/server/MemoryIDBBackingStore.cpp:
+ (WebCore::IDBServer::MemoryIDBBackingStore::MemoryIDBBackingStore):
+ (WebCore::IDBServer::MemoryIDBBackingStore::addRecord):
+ (WebCore::IDBServer::MemoryIDBBackingStore::serializationContext):
+ * Modules/indexeddb/server/MemoryIDBBackingStore.h:
+ * Modules/indexeddb/server/MemoryObjectStore.cpp:
+ (WebCore::IDBServer::MemoryObjectStore::addRecord):
+ (WebCore::IDBServer::MemoryObjectStore::updateIndexesForPutRecord):
+ * Modules/indexeddb/server/MemoryObjectStore.h:
+ * Modules/indexeddb/server/SQLiteIDBBackingStore.cpp:
+ (WebCore::IDBServer::SQLiteIDBBackingStore::updateAllIndexesForAddRecord):
+ (WebCore::IDBServer::SQLiteIDBBackingStore::addRecord):
+ (WebCore::IDBServer::SQLiteIDBBackingStore::serializationContext):
+ * Modules/indexeddb/server/SQLiteIDBBackingStore.h:
+ * Modules/indexeddb/server/UniqueIDBDatabase.cpp:
+ (WebCore::IDBServer::estimateSize):
+ (WebCore::IDBServer::UniqueIDBDatabase::putOrAdd):
+ * Modules/indexeddb/shared/IndexKey.h:
+ * WebCore.xcodeproj/project.pbxproj:
+ * bindings/js/IDBBindingUtilities.cpp:
+ (WebCore::generateIndexKeyMapForValue):
+ * bindings/js/IDBBindingUtilities.h:
+
2020-05-11 Kate Cheney <[email protected]>
Fail navigations to non app-bound domains after use of app-bound APIs
Modified: trunk/Source/WebCore/Headers.cmake (261532 => 261533)
--- trunk/Source/WebCore/Headers.cmake 2020-05-12 00:34:35 UTC (rev 261532)
+++ trunk/Source/WebCore/Headers.cmake 2020-05-12 00:38:17 UTC (rev 261533)
@@ -68,6 +68,7 @@
Modules/indexeddb/server/UniqueIDBDatabaseConnection.h
Modules/indexeddb/server/UniqueIDBDatabaseTransaction.h
+ Modules/indexeddb/shared/IndexKey.h
Modules/indexeddb/shared/IDBCursorInfo.h
Modules/indexeddb/shared/IDBCursorRecord.h
Modules/indexeddb/shared/IDBDatabaseInfo.h
Modified: trunk/Source/WebCore/Modules/indexeddb/server/IDBBackingStore.h (261532 => 261533)
--- trunk/Source/WebCore/Modules/indexeddb/server/IDBBackingStore.h 2020-05-12 00:34:35 UTC (rev 261532)
+++ trunk/Source/WebCore/Modules/indexeddb/server/IDBBackingStore.h 2020-05-12 00:38:17 UTC (rev 261533)
@@ -29,6 +29,7 @@
#include "IDBDatabaseInfo.h"
#include "IDBError.h"
+#include "IndexKey.h"
#include <wtf/MainThread.h>
namespace WebCore {
@@ -56,6 +57,8 @@
namespace IDBServer {
+class IDBSerializationContext;
+
class IDBBackingStore {
public:
virtual ~IDBBackingStore() { RELEASE_ASSERT(!isMainThread()); }
@@ -75,7 +78,7 @@
virtual IDBError renameIndex(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName) = 0;
virtual IDBError keyExistsInObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyData&, bool& keyExists) = 0;
virtual IDBError deleteRange(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData&) = 0;
- virtual IDBError addRecord(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo&, const IDBKeyData&, const IDBValue&) = 0;
+ virtual IDBError addRecord(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo&, const IDBKeyData&, const IndexIDToIndexKeyMap&, const IDBValue&) = 0;
virtual IDBError getRecord(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData&, IDBGetRecordDataType, IDBGetResult& outValue) = 0;
virtual IDBError getAllRecords(const IDBResourceIdentifier& transactionIdentifier, const IDBGetAllRecordsData&, IDBGetAllResult& outValue) = 0;
virtual IDBError getIndexRecord(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, IndexedDB::IndexRecordType, const IDBKeyRangeData&, IDBGetResult& outValue) = 0;
@@ -86,6 +89,8 @@
virtual IDBError openCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBCursorInfo&, IDBGetResult& outResult) = 0;
virtual IDBError iterateCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier, const IDBIterateCursorData&, IDBGetResult& outResult) = 0;
+ virtual IDBSerializationContext& serializationContext() = 0;
+
virtual IDBObjectStoreInfo* infoForObjectStore(uint64_t objectStoreIdentifier) = 0;
virtual void deleteBackingStore() = 0;
Modified: trunk/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.cpp (261532 => 261533)
--- trunk/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.cpp 2020-05-12 00:34:35 UTC (rev 261532)
+++ trunk/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.cpp 2020-05-12 00:38:17 UTC (rev 261533)
@@ -35,6 +35,7 @@
#include "IDBIndexInfo.h"
#include "IDBIterateCursorData.h"
#include "IDBKeyRangeData.h"
+#include "IDBSerializationContext.h"
#include "Logging.h"
#include "MemoryIndexCursor.h"
#include "MemoryObjectStore.h"
@@ -49,6 +50,7 @@
MemoryIDBBackingStore::MemoryIDBBackingStore(PAL::SessionID sessionID, const IDBDatabaseIdentifier& identifier)
: m_identifier(identifier)
, m_sessionID(sessionID)
+ , m_serializationContext(IDBSerializationContext::getOrCreateIDBSerializationContext(sessionID))
{
}
@@ -355,7 +357,7 @@
return IDBError { };
}
-IDBError MemoryIDBBackingStore::addRecord(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo& objectStoreInfo, const IDBKeyData& keyData, const IDBValue& value)
+IDBError MemoryIDBBackingStore::addRecord(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo& objectStoreInfo, const IDBKeyData& keyData, const IndexIDToIndexKeyMap& indexKeys, const IDBValue& value)
{
LOG(IndexedDB, "MemoryIDBBackingStore::addRecord");
@@ -369,7 +371,7 @@
if (!objectStore)
return IDBError { UnknownError, "No backing store object store found to put record"_s };
- return objectStore->addRecord(*transaction, keyData, value);
+ return objectStore->addRecord(*transaction, keyData, indexKeys, value);
}
IDBError MemoryIDBBackingStore::getRecord(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData& range, IDBGetRecordDataType type, IDBGetResult& outValue)
@@ -611,6 +613,11 @@
return objectStoreByIdentifier;
}
+IDBSerializationContext& MemoryIDBBackingStore::serializationContext()
+{
+ return m_serializationContext.get();
+}
+
IDBObjectStoreInfo* MemoryIDBBackingStore::infoForObjectStore(uint64_t objectStoreIdentifier)
{
ASSERT(m_databaseInfo);
Modified: trunk/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.h (261532 => 261533)
--- trunk/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.h 2020-05-12 00:34:35 UTC (rev 261532)
+++ trunk/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.h 2020-05-12 00:38:17 UTC (rev 261533)
@@ -30,6 +30,7 @@
#include "IDBBackingStore.h"
#include "IDBDatabaseIdentifier.h"
#include "IDBResourceIdentifier.h"
+#include "IndexKey.h"
#include "MemoryBackingStoreTransaction.h"
#include <pal/SessionID.h>
#include <wtf/HashMap.h>
@@ -37,6 +38,7 @@
namespace WebCore {
namespace IDBServer {
+class IDBSerializationContext;
class MemoryObjectStore;
class MemoryIDBBackingStore final : public IDBBackingStore {
@@ -51,6 +53,8 @@
void removeObjectStoreForVersionChangeAbort(MemoryObjectStore&);
void restoreObjectStoreForVersionChangeAbort(Ref<MemoryObjectStore>&&);
+ IDBSerializationContext& serializationContext() final;
+
private:
IDBError beginTransaction(const IDBTransactionInfo&) final;
IDBError abortTransaction(const IDBResourceIdentifier& transactionIdentifier) final;
@@ -64,7 +68,7 @@
IDBError renameIndex(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName) final;
IDBError keyExistsInObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyData&, bool& keyExists) final;
IDBError deleteRange(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData&) final;
- IDBError addRecord(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo&, const IDBKeyData&, const IDBValue&) final;
+ IDBError addRecord(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo&, const IDBKeyData&, const IndexIDToIndexKeyMap&, const IDBValue&) final;
IDBError getRecord(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData&, IDBGetRecordDataType, IDBGetResult& outValue) final;
IDBError getAllRecords(const IDBResourceIdentifier& transactionIdentifier, const IDBGetAllRecordsData&, IDBGetAllResult& outValue) final;
IDBError getIndexRecord(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, IndexedDB::IndexRecordType, const IDBKeyRangeData&, IDBGetResult& outValue) final;
@@ -92,6 +96,7 @@
IDBDatabaseIdentifier m_identifier;
PAL::SessionID m_sessionID;
+ Ref<IDBSerializationContext> m_serializationContext;
std::unique_ptr<IDBDatabaseInfo> m_databaseInfo;
HashMap<IDBResourceIdentifier, std::unique_ptr<MemoryBackingStoreTransaction>> m_transactions;
Modified: trunk/Source/WebCore/Modules/indexeddb/server/MemoryObjectStore.cpp (261532 => 261533)
--- trunk/Source/WebCore/Modules/indexeddb/server/MemoryObjectStore.cpp 2020-05-12 00:34:35 UTC (rev 261532)
+++ trunk/Source/WebCore/Modules/indexeddb/server/MemoryObjectStore.cpp 2020-05-12 00:38:17 UTC (rev 261533)
@@ -252,6 +252,12 @@
IDBError MemoryObjectStore::addRecord(MemoryBackingStoreTransaction& transaction, const IDBKeyData& keyData, const IDBValue& value)
{
+ auto indexKeys = generateIndexKeyMapForValue(m_serializationContext->execState(), m_info, keyData, value);
+ return addRecord(transaction, keyData, indexKeys, value);
+}
+
+IDBError MemoryObjectStore::addRecord(MemoryBackingStoreTransaction& transaction, const IDBKeyData& keyData, const IndexIDToIndexKeyMap& indexKeys, const IDBValue& value)
+{
LOG(IndexedDB, "MemoryObjectStore::addRecord");
ASSERT(m_writeTransaction);
@@ -271,7 +277,7 @@
ASSERT(listResult.second);
// If there was an error indexing this addition, then revert it.
- auto error = updateIndexesForPutRecord(keyData, value.data());
+ auto error = updateIndexesForPutRecord(keyData, indexKeys);
if (!error.isNull()) {
m_keyValueStore->remove(mapResult.iterator);
m_orderedKeys->erase(listResult.first);
@@ -299,29 +305,24 @@
index->removeEntriesWithValueKey(value);
}
-IDBError MemoryObjectStore::updateIndexesForPutRecord(const IDBKeyData& key, const ThreadSafeDataBuffer& value)
+IDBError MemoryObjectStore::updateIndexesForPutRecord(const IDBKeyData& key, const IndexIDToIndexKeyMap& indexKeys)
{
- JSLockHolder locker(m_serializationContext->vm());
-
- auto jsValue = deserializeIDBValueToJSValue(m_serializationContext->execState(), value);
- if (jsValue.isUndefinedOrNull())
- return IDBError { };
-
IDBError error;
Vector<std::pair<MemoryIndex*, IndexKey>> changedIndexRecords;
- for (auto& index : m_indexesByName.values()) {
- IndexKey indexKey;
- generateIndexKeyForValue(m_serializationContext->execState(), index->info(), jsValue, indexKey, m_info.keyPath(), key);
+ for (const auto& [indexID, indexKey] : indexKeys) {
+ auto* index = m_indexesByIdentifier.get(indexID);
+ ASSERT(index);
+ if (!index) {
+ error = IDBError { InvalidStateError, "Missing index metadata" };
+ break;
+ }
- if (indexKey.isNull())
- continue;
-
error = index->putIndexKey(key, indexKey);
if (!error.isNull())
break;
- changedIndexRecords.append(std::make_pair(index.get(), indexKey));
+ changedIndexRecords.append(std::make_pair(index, indexKey));
}
// If any of the index puts failed, revert all of the ones that went through.
Modified: trunk/Source/WebCore/Modules/indexeddb/server/MemoryObjectStore.h (261532 => 261533)
--- trunk/Source/WebCore/Modules/indexeddb/server/MemoryObjectStore.h 2020-05-12 00:34:35 UTC (rev 261532)
+++ trunk/Source/WebCore/Modules/indexeddb/server/MemoryObjectStore.h 2020-05-12 00:38:17 UTC (rev 261533)
@@ -29,6 +29,7 @@
#include "IDBKeyData.h"
#include "IDBObjectStoreInfo.h"
+#include "IndexKey.h"
#include "MemoryIndex.h"
#include "MemoryObjectStoreCursor.h"
#include "ThreadSafeDataBuffer.h"
@@ -80,6 +81,7 @@
void deleteRecord(const IDBKeyData&);
void deleteRange(const IDBKeyRangeData&);
IDBError addRecord(MemoryBackingStoreTransaction&, const IDBKeyData&, const IDBValue&);
+ IDBError addRecord(MemoryBackingStoreTransaction&, const IDBKeyData&, const IndexIDToIndexKeyMap&, const IDBValue&);
uint64_t currentKeyGeneratorValue() const { return m_keyGeneratorValue; }
void setKeyGeneratorValue(uint64_t value) { m_keyGeneratorValue = value; }
@@ -114,7 +116,7 @@
IDBKeyDataSet::iterator lowestIteratorInRange(const IDBKeyRangeData&, bool reverse) const;
IDBError populateIndexWithExistingRecords(MemoryIndex&);
- IDBError updateIndexesForPutRecord(const IDBKeyData&, const ThreadSafeDataBuffer& value);
+ IDBError updateIndexesForPutRecord(const IDBKeyData&, const IndexIDToIndexKeyMap&);
void updateIndexesForDeleteRecord(const IDBKeyData& value);
void updateCursorsForPutRecord(IDBKeyDataSet::iterator);
void updateCursorsForDeleteRecord(const IDBKeyData&);
Modified: trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp (261532 => 261533)
--- trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp 2020-05-12 00:34:35 UTC (rev 261532)
+++ trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp 2020-05-12 00:38:17 UTC (rev 261533)
@@ -1852,24 +1852,22 @@
return uncheckedPutIndexKey(info, key, indexKey, recordID);
}
-IDBError SQLiteIDBBackingStore::updateAllIndexesForAddRecord(const IDBObjectStoreInfo& info, const IDBKeyData& key, const ThreadSafeDataBuffer& value, int64_t recordID)
+IDBError SQLiteIDBBackingStore::updateAllIndexesForAddRecord(const IDBObjectStoreInfo& info, const IDBKeyData& key, const IndexIDToIndexKeyMap& indexKeys, int64_t recordID)
{
- JSLockHolder locker(m_serializationContext->vm());
-
- auto jsValue = deserializeIDBValueToJSValue(m_serializationContext->execState(), value);
- if (jsValue.isUndefinedOrNull())
- return IDBError { };
-
IDBError error;
+ const auto& indexMap = info.indexMap();
bool anyRecordsSucceeded = false;
- for (auto& index : info.indexMap().values()) {
- IndexKey indexKey;
- generateIndexKeyForValue(m_serializationContext->execState(), index, jsValue, indexKey, info.keyPath(), key);
- if (indexKey.isNull())
- continue;
+ for (const auto& [indexID, indexKey] : indexKeys) {
+ auto indexIterator = indexMap.find(indexID);
+ ASSERT(indexIterator != indexMap.end());
- error = uncheckedPutIndexKey(index, key, indexKey, recordID);
+ if (indexIterator == indexMap.end()) {
+ error = IDBError { InvalidStateError, "Missing index metadata" };
+ break;
+ }
+
+ error = uncheckedPutIndexKey(indexIterator->value, key, indexKey, recordID);
if (!error.isNull())
break;
@@ -1891,7 +1889,7 @@
return error;
}
-IDBError SQLiteIDBBackingStore::addRecord(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo& objectStoreInfo, const IDBKeyData& keyData, const IDBValue& value)
+IDBError SQLiteIDBBackingStore::addRecord(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo& objectStoreInfo, const IDBKeyData& keyData, const IndexIDToIndexKeyMap& indexKeys, const IDBValue& value)
{
LOG(IndexedDB, "SQLiteIDBBackingStore::addRecord - key %s, object store %" PRIu64, keyData.loggingString().utf8().data(), objectStoreInfo.identifier());
@@ -1930,7 +1928,7 @@
recordID = m_sqliteDB->lastInsertRowID();
}
- auto error = updateAllIndexesForAddRecord(objectStoreInfo, keyData, value.data(), recordID);
+ auto error = updateAllIndexesForAddRecord(objectStoreInfo, keyData, indexKeys, recordID);
if (!error.isNull()) {
auto sql = cachedStatement(SQL::DeleteObjectStoreRecord, "DELETE FROM Records WHERE objectStoreID = ? AND key = CAST(? AS TEXT);"_s);
@@ -2737,6 +2735,11 @@
return IDBError { };
}
+IDBSerializationContext& SQLiteIDBBackingStore::serializationContext()
+{
+ return m_serializationContext.get();
+}
+
IDBObjectStoreInfo* SQLiteIDBBackingStore::infoForObjectStore(uint64_t objectStoreIdentifier)
{
ASSERT(m_databaseInfo);
Modified: trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h (261532 => 261533)
--- trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h 2020-05-12 00:34:35 UTC (rev 261532)
+++ trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h 2020-05-12 00:38:17 UTC (rev 261533)
@@ -71,7 +71,7 @@
IDBError renameIndex(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName) final;
IDBError keyExistsInObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyData&, bool& keyExists) final;
IDBError deleteRange(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData&) final;
- IDBError addRecord(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo&, const IDBKeyData&, const IDBValue&) final;
+ IDBError addRecord(const IDBResourceIdentifier& transactionIdentifier, const IDBObjectStoreInfo&, const IDBKeyData&, const IndexIDToIndexKeyMap&, const IDBValue&) final;
IDBError getRecord(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyRangeData&, IDBGetRecordDataType, IDBGetResult& outValue) final;
IDBError getAllRecords(const IDBResourceIdentifier& transactionIdentifier, const IDBGetAllRecordsData&, IDBGetAllResult& outValue) final;
IDBError getIndexRecord(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, IndexedDB::IndexRecordType, const IDBKeyRangeData&, IDBGetResult& outValue) final;
@@ -82,6 +82,8 @@
IDBError openCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBCursorInfo&, IDBGetResult& outResult) final;
IDBError iterateCursor(const IDBResourceIdentifier& transactionIdentifier, const IDBResourceIdentifier& cursorIdentifier, const IDBIterateCursorData&, IDBGetResult& outResult) final;
+ IDBSerializationContext& serializationContext() final;
+
IDBObjectStoreInfo* infoForObjectStore(uint64_t objectStoreIdentifier) final;
void deleteBackingStore() final;
@@ -123,7 +125,7 @@
IDBError uncheckedGetKeyGeneratorValue(int64_t objectStoreID, uint64_t& outValue);
IDBError uncheckedSetKeyGeneratorValue(int64_t objectStoreID, uint64_t value);
- IDBError updateAllIndexesForAddRecord(const IDBObjectStoreInfo&, const IDBKeyData&, const ThreadSafeDataBuffer& value, int64_t recordID);
+ IDBError updateAllIndexesForAddRecord(const IDBObjectStoreInfo&, const IDBKeyData&, const IndexIDToIndexKeyMap&, int64_t recordID);
IDBError updateOneIndexForAddRecord(const IDBIndexInfo&, const IDBKeyData&, const ThreadSafeDataBuffer& value, int64_t recordID);
IDBError uncheckedPutIndexKey(const IDBIndexInfo&, const IDBKeyData& keyValue, const IndexKey&, int64_t recordID);
IDBError uncheckedPutIndexRecord(int64_t objectStoreID, int64_t indexID, const IDBKeyData& keyValue, const IDBKeyData& indexKey, int64_t recordID);
Modified: trunk/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.cpp (261532 => 261533)
--- trunk/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.cpp 2020-05-12 00:34:35 UTC (rev 261532)
+++ trunk/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.cpp 2020-05-12 00:38:17 UTC (rev 261533)
@@ -38,6 +38,7 @@
#include "IDBServer.h"
#include "IDBTransactionInfo.h"
#include "IDBValue.h"
+#include "IndexKey.h"
#include "Logging.h"
#include "StorageQuotaManager.h"
#include "UniqueIDBDatabaseConnection.h"
@@ -70,6 +71,29 @@
return size;
}
+static inline uint64_t estimateSize(const IDBObjectStoreInfo& info, const IndexIDToIndexKeyMap& indexKeys, uint64_t primaryKeySize)
+{
+ // Each IndexRecord has 5 columns:
+ // - indexID, objectStoreID, recordID: these are varints, estimate 4 bytes each = 12 bytes
+ // - primary key: use primaryKeySize
+ // - secondary index key: use estimateSize(secondary key)
+ static constexpr uint64_t baseIndexRowSize = 12;
+ uint64_t size = 0;
+
+ for (const auto& [indexID, indexKey] : indexKeys) {
+ auto indexIterator = info.indexMap().find(indexID);
+ ASSERT(indexIterator != info.indexMap().end());
+
+ if (indexIterator != info.indexMap().end() && indexIterator->value.multiEntry()) {
+ for (const auto& secondaryKey : indexKey.multiEntry())
+ size += (baseIndexRowSize + primaryKeySize + estimateSize(secondaryKey));
+ } else
+ size += (baseIndexRowSize + primaryKeySize + estimateSize(indexKey.asOneKey()));
+ }
+
+ return size;
+}
+
static inline uint64_t estimateSize(const IDBValue& value)
{
uint64_t size = 4;
@@ -712,16 +736,6 @@
return;
}
- // Quota check.
- auto taskSize = defaultWriteOperationCost + estimateSize(keyData) + estimateSize(value);
- auto* objectStore = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier);
- if (objectStore)
- taskSize += objectStore->indexNames().size() * taskSize;
- if (m_server.requestSpace(m_identifier.origin(), taskSize) == StorageQuotaManager::Decision::Deny) {
- callback(IDBError { QuotaExceededError, quotaErrorMessageName("PutOrAdd") }, usedKey);
- return;
- }
-
bool usedKeyIsGenerated = false;
uint64_t keyNumber;
auto transactionIdentifier = requestData.transactionIdentifier();
@@ -741,6 +755,9 @@
} else
usedKey = keyData;
+ // Generate index keys up front for more accurate quota check.
+ auto indexKeys = generateIndexKeyMapForValue(m_backingStore->serializationContext().execState(), *objectStoreInfo, usedKey, value);
+
if (overwriteMode == IndexedDB::ObjectStoreOverwriteMode::NoOverwrite) {
bool keyExists;
error = m_backingStore->keyExistsInObjectStore(transactionIdentifier, objectStoreIdentifier, usedKey, keyExists);
@@ -752,6 +769,20 @@
return;
}
}
+
+ // Quota check.
+ auto keySize = estimateSize(usedKey);
+ auto valueSize = estimateSize(value);
+ auto indexSize = estimateSize(*objectStoreInfo, indexKeys, keySize);
+ auto taskSize = defaultWriteOperationCost + keySize + valueSize + indexSize;
+
+ LOG(IndexedDB, "UniqueIDBDatabase::putOrAdd quota check with task size: %llu key size: %llu value size: %llu index size: %llu", taskSize, keySize, valueSize, indexSize);
+
+ if (m_server.requestSpace(m_identifier.origin(), taskSize) == StorageQuotaManager::Decision::Deny) {
+ callback(IDBError { QuotaExceededError, quotaErrorMessageName("PutOrAdd") }, usedKey);
+ return;
+ }
+
// If a record already exists in store, then remove the record from store using the steps for deleting records from an object store.
// This is important because formally deleting it from from the object store also removes it from the appropriate indexes.
error = m_backingStore->deleteRange(transactionIdentifier, objectStoreIdentifier, usedKey);
@@ -760,7 +791,7 @@
return;
}
- error = m_backingStore->addRecord(transactionIdentifier, *objectStoreInfo, usedKey, value);
+ error = m_backingStore->addRecord(transactionIdentifier, *objectStoreInfo, usedKey, indexKeys, value);
if (!error.isNull()) {
callback(error, usedKey);
return;
Modified: trunk/Source/WebCore/Modules/indexeddb/shared/IndexKey.h (261532 => 261533)
--- trunk/Source/WebCore/Modules/indexeddb/shared/IndexKey.h 2020-05-12 00:34:35 UTC (rev 261532)
+++ trunk/Source/WebCore/Modules/indexeddb/shared/IndexKey.h 2020-05-12 00:38:17 UTC (rev 261533)
@@ -28,6 +28,7 @@
#if ENABLE(INDEXED_DATABASE)
#include "IDBKeyData.h"
+#include <wtf/HashMap.h>
#include <wtf/Vector.h>
namespace WebCore {
@@ -48,6 +49,8 @@
Vector<IDBKeyData> m_keys;
};
+typedef HashMap<uint64_t, IndexKey> IndexIDToIndexKeyMap;
+
} // namespace WebCore
#endif // ENABLE(INDEXED_DATABASE)
Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (261532 => 261533)
--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2020-05-12 00:34:35 UTC (rev 261532)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2020-05-12 00:38:17 UTC (rev 261533)
@@ -1451,7 +1451,7 @@
516D7D701BB5F0BD00AF7C77 /* IDBConnectionToServerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 5185FCBD1BB5CB770012898F /* IDBConnectionToServerDelegate.h */; settings = {ATTRIBUTES = (Private, ); }; };
516D7D721BB5F0BD00AF7C77 /* IDBConnectionToClientDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 516D7D6E1BB5F06500AF7C77 /* IDBConnectionToClientDelegate.h */; settings = {ATTRIBUTES = (Private, ); }; };
516F7F6D1C31E39A00F111DC /* ServerOpenDBRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 516F7F6C1C31C79D00F111DC /* ServerOpenDBRequest.h */; settings = {ATTRIBUTES = (Private, ); }; };
- 517138F01BED1D1A000D5F01 /* IndexKey.h in Headers */ = {isa = PBXBuildFile; fileRef = 517138EE1BED1D17000D5F01 /* IndexKey.h */; };
+ 517138F01BED1D1A000D5F01 /* IndexKey.h in Headers */ = {isa = PBXBuildFile; fileRef = 517138EE1BED1D17000D5F01 /* IndexKey.h */; settings = {ATTRIBUTES = (Private, ); }; };
517138F81BF128BB000D5F01 /* IndexValueStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 517138F61BF12262000D5F01 /* IndexValueStore.h */; };
517138FC1BF3ADF4000D5F01 /* IDBCursorInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 517138FA1BF3ADAC000D5F01 /* IDBCursorInfo.h */; settings = {ATTRIBUTES = (Private, ); }; };
517139061BF64DEC000D5F01 /* MemoryObjectStoreCursor.h in Headers */ = {isa = PBXBuildFile; fileRef = 517139041BF64DE3000D5F01 /* MemoryObjectStoreCursor.h */; };
Modified: trunk/Source/WebCore/bindings/js/IDBBindingUtilities.cpp (261532 => 261533)
--- trunk/Source/WebCore/bindings/js/IDBBindingUtilities.cpp 2020-05-12 00:34:35 UTC (rev 261532)
+++ trunk/Source/WebCore/bindings/js/IDBBindingUtilities.cpp 2020-05-12 00:38:17 UTC (rev 261533)
@@ -36,6 +36,7 @@
#include "IDBKey.h"
#include "IDBKeyData.h"
#include "IDBKeyPath.h"
+#include "IDBObjectStoreInfo.h"
#include "IDBValue.h"
#include "IndexKey.h"
#include "JSBlob.h"
@@ -463,6 +464,34 @@
outKey = IndexKey(WTFMove(keyDatas));
}
+IndexIDToIndexKeyMap generateIndexKeyMapForValue(JSC::JSGlobalObject& lexicalGlobalObject, const IDBObjectStoreInfo& storeInfo, const IDBKeyData& key, const IDBValue& value)
+{
+ auto& indexMap = storeInfo.indexMap();
+ auto indexCount = indexMap.size();
+ if (!indexCount)
+ return IndexIDToIndexKeyMap { };
+
+ JSLockHolder locker(lexicalGlobalObject.vm());
+ auto jsValue = deserializeIDBValueToJSValue(lexicalGlobalObject, value);
+ if (jsValue.isUndefinedOrNull())
+ return IndexIDToIndexKeyMap { };
+
+ IndexIDToIndexKeyMap indexKeys;
+ indexKeys.reserveInitialCapacity(indexCount);
+
+ for (const auto& [indexID, indexInfo] : indexMap) {
+ IndexKey indexKey;
+ generateIndexKeyForValue(lexicalGlobalObject, indexInfo, jsValue, indexKey, storeInfo.keyPath(), key);
+
+ if (indexKey.isNull())
+ continue;
+
+ indexKeys.add(indexID, WTFMove(indexKey));
+ }
+
+ return indexKeys;
+}
+
Optional<JSC::JSValue> deserializeIDBValueWithKeyInjection(JSGlobalObject& lexicalGlobalObject, const IDBValue& value, const IDBKeyData& key, const Optional<IDBKeyPath>& keyPath)
{
auto jsValue = deserializeIDBValueToJSValue(lexicalGlobalObject, value);
Modified: trunk/Source/WebCore/bindings/js/IDBBindingUtilities.h (261532 => 261533)
--- trunk/Source/WebCore/bindings/js/IDBBindingUtilities.h 2020-05-12 00:34:35 UTC (rev 261532)
+++ trunk/Source/WebCore/bindings/js/IDBBindingUtilities.h 2020-05-12 00:38:17 UTC (rev 261533)
@@ -29,6 +29,7 @@
#if ENABLE(INDEXED_DATABASE)
#include "IDBKeyPath.h"
+#include "IndexKey.h"
#include <wtf/Forward.h>
namespace JSC {
@@ -42,6 +43,7 @@
class IDBIndexInfo;
class IDBKey;
class IDBKeyData;
+class IDBObjectStoreInfo;
class IDBValue;
class IndexKey;
class JSDOMGlobalObject;
@@ -52,6 +54,8 @@
void generateIndexKeyForValue(JSC::JSGlobalObject&, const IDBIndexInfo&, JSC::JSValue, IndexKey& outKey, const Optional<IDBKeyPath>&, const IDBKeyData&);
+IndexIDToIndexKeyMap generateIndexKeyMapForValue(JSC::JSGlobalObject&, const IDBObjectStoreInfo&, const IDBKeyData&, const IDBValue&);
+
Ref<IDBKey> scriptValueToIDBKey(JSC::JSGlobalObject&, const JSC::JSValue&);
JSC::JSValue deserializeIDBValueToJSValue(JSC::JSGlobalObject&, const IDBValue&, Vector<std::pair<String, String>>&);