Blob type cannot be stored correctly in IDB when IDBObjectStore has autoIncrement...
authorsihui_liu@apple.com <sihui_liu@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 18 Apr 2019 21:45:41 +0000 (21:45 +0000)
committersihui_liu@apple.com <sihui_liu@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 18 Apr 2019 21:45:41 +0000 (21:45 +0000)
https://bugs.webkit.org/show_bug.cgi?id=196128
<rdar://problem/49562115>

Reviewed by Geoffrey Garen.

LayoutTests/imported/w3c:

Updated test expectations to PASS.

* web-platform-tests/IndexedDB/nested-cloning-large-expected.txt:
* web-platform-tests/IndexedDB/nested-cloning-large-multiple-expected.txt:
* web-platform-tests/IndexedDB/nested-cloning-small-expected.txt:

Source/WebCore:

If a key is auto-generated, it should become a property of the value object. Network process would perform the
key injection by deserializing IDBValue into script value, setting the property, serializing the result and
storing it in a database record. But network process does not have a JSDOMGlobalObject, so it would fail to
deserialize types including Blob and File.

To solve this issue, we move the key injection to web process and let network process store the original value
it gets. In this case, when web process asks for some value, network process should return key, value and key
path so that web process can decide whether it should perform a key injection before returning the result. Note
that the auto-generated key would always be stored as the key in a ObjectStore record.

Test: storage/indexeddb/modern/objectstore-autoincrement-types.html

* Modules/indexeddb/IDBCursor.cpp:
(WebCore::IDBCursor::setGetResult):
* Modules/indexeddb/IDBCursor.h:
(WebCore::IDBCursor::primaryKeyPath):
* Modules/indexeddb/IDBGetAllResult.cpp:
(WebCore::IDBGetAllResult::isolatedCopy):
(WebCore::IDBGetAllResult::addKey):
(WebCore::IDBGetAllResult::addValue):
(WebCore::IDBGetAllResult::keys const):
(WebCore::IDBGetAllResult::values const):
(WebCore::IDBGetAllResult::allBlobFilePaths const):
(WebCore::isolatedCopyOfVariant): Deleted.

* Modules/indexeddb/IDBGetAllResult.h: Introduce an IDBKeyPath parameter. Also replace Variant with two Vectors,
because we only needed to store either key or value before, and now the stored value could be incomplete.
(WebCore::IDBGetAllResult::IDBGetAllResult):
(WebCore::IDBGetAllResult::keyPath const):
(WebCore::IDBGetAllResult::encode const):
(WebCore::IDBGetAllResult::decode):

* Modules/indexeddb/IDBGetResult.cpp:
(WebCore::IDBGetResult::setValue):
* Modules/indexeddb/IDBGetResult.h:
(WebCore::IDBGetResult::IDBGetResult):
(WebCore::IDBGetResult::keyPath const):
* Modules/indexeddb/IDBRequest.cpp:
(WebCore::IDBRequest::setResult):
(WebCore::IDBRequest::setResultToStructuredClone):
* Modules/indexeddb/IDBRequest.h:
* Modules/indexeddb/IDBTransaction.cpp:
(WebCore::IDBTransaction::didGetAllRecordsOnServer):
(WebCore::IDBTransaction::didGetRecordOnServer):
* Modules/indexeddb/server/MemoryIDBBackingStore.cpp:
(WebCore::IDBServer::MemoryIDBBackingStore::getRecord):
* Modules/indexeddb/server/MemoryIndex.cpp:
(WebCore::IDBServer::MemoryIndex::getResultForKeyRange const):
(WebCore::IDBServer::MemoryIndex::getAllRecords const):
* Modules/indexeddb/server/MemoryIndexCursor.cpp:
(WebCore::IDBServer::MemoryIndexCursor::currentData):
* Modules/indexeddb/server/MemoryObjectStore.cpp:
(WebCore::IDBServer::MemoryObjectStore::updateIndexesForPutRecord):
(WebCore::IDBServer::MemoryObjectStore::populateIndexWithExistingRecords):
(WebCore::IDBServer::MemoryObjectStore::getAllRecords const):
* Modules/indexeddb/server/MemoryObjectStoreCursor.cpp:
(WebCore::IDBServer::MemoryObjectStoreCursor::currentData):
* Modules/indexeddb/server/SQLiteIDBBackingStore.cpp:
(WebCore::IDBServer::SQLiteIDBBackingStore::updateOneIndexForAddRecord):
(WebCore::IDBServer::SQLiteIDBBackingStore::updateAllIndexesForAddRecord):
(WebCore::IDBServer::SQLiteIDBBackingStore::getRecord):
(WebCore::IDBServer::SQLiteIDBBackingStore::cachedStatementForGetAllObjectStoreRecords):
(WebCore::IDBServer::SQLiteIDBBackingStore::getAllObjectStoreRecords):
(WebCore::IDBServer::SQLiteIDBBackingStore::getAllIndexRecords):
(WebCore::IDBServer::SQLiteIDBBackingStore::getIndexRecord):
(WebCore::IDBServer::SQLiteIDBBackingStore::uncheckedGetIndexRecordForOneKey):
(WebCore::IDBServer::SQLiteIDBBackingStore::openCursor):
(WebCore::IDBServer::SQLiteIDBBackingStore::iterateCursor):
* Modules/indexeddb/server/SQLiteIDBCursor.cpp:
(WebCore::IDBServer::SQLiteIDBCursor::currentData):
* Modules/indexeddb/server/SQLiteIDBCursor.h:
* Modules/indexeddb/server/UniqueIDBDatabase.cpp:

(WebCore::IDBServer::UniqueIDBDatabase::performPutOrAdd): Remove the key injection from network process.
UniqueIDBDatabase stores any value it gets from IDBClient.

* Modules/indexeddb/shared/IDBResultData.cpp:
(WebCore::IDBResultData::getResultRef):
* Modules/indexeddb/shared/IDBResultData.h:

* bindings/js/IDBBindingUtilities.cpp:
(WebCore::injectIDBKeyIntoScriptValue): If property is read-only, set would fail and injectKeyIntoResult would
return null, but we expect it to return result as long as the property value is the same as target. Therefore,
we can add an early return here.

(WebCore::createKeyPathArray):
(WebCore::generateIndexKeyForValue): We used to generate IndexKey from value stored in database but now the
value gets stored does not include auto-generated key, as we remove the key injection from network process. In
this case if the IDBIndex has the same key path as the auto-generated key, IndexKey would be failed to create
for it cannot extract auto-generated key from value. Since the auto-generated key would always be the key in
database record, we could use value of that key when we find a match in key path.

(WebCore::deserializeIDBValueWithKeyInjection): If the key path in the result is single entry, the key is
probably auto-generated, so we could inject the result key into the result value unconditionally.

* bindings/js/IDBBindingUtilities.h:
* bindings/js/JSIDBCursorWithValueCustom.cpp:
(WebCore::JSIDBCursorWithValue::value const):
* bindings/js/JSIDBRequestCustom.cpp:
(WebCore::JSIDBRequest::result const):

LayoutTests:

* storage/indexeddb/modern/objectstore-autoincrement-types-expected.txt: Added.
* storage/indexeddb/modern/objectstore-autoincrement-types.html: Added.
* storage/indexeddb/modern/resources/objectstore-autoincrement-types.js: Added.
(prepareDatabase.event.target.onsuccess):
(prepareDatabase):
(compare):
(runGetTest):
(runGetAllTest):
(get store):
(testSteps):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@244436 268f45cc-cd09-0410-ab3c-d52691b4dbfc

33 files changed:
LayoutTests/ChangeLog
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/IndexedDB/nested-cloning-large-expected.txt
LayoutTests/imported/w3c/web-platform-tests/IndexedDB/nested-cloning-large-multiple-expected.txt
LayoutTests/imported/w3c/web-platform-tests/IndexedDB/nested-cloning-small-expected.txt
LayoutTests/storage/indexeddb/modern/objectstore-autoincrement-types-expected.txt [new file with mode: 0644]
LayoutTests/storage/indexeddb/modern/objectstore-autoincrement-types.html [new file with mode: 0644]
LayoutTests/storage/indexeddb/modern/resources/objectstore-autoincrement-types.js [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/Modules/indexeddb/IDBCursor.cpp
Source/WebCore/Modules/indexeddb/IDBCursor.h
Source/WebCore/Modules/indexeddb/IDBGetAllResult.cpp
Source/WebCore/Modules/indexeddb/IDBGetAllResult.h
Source/WebCore/Modules/indexeddb/IDBGetResult.cpp
Source/WebCore/Modules/indexeddb/IDBGetResult.h
Source/WebCore/Modules/indexeddb/IDBRequest.cpp
Source/WebCore/Modules/indexeddb/IDBRequest.h
Source/WebCore/Modules/indexeddb/IDBTransaction.cpp
Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.cpp
Source/WebCore/Modules/indexeddb/server/MemoryIndex.cpp
Source/WebCore/Modules/indexeddb/server/MemoryIndexCursor.cpp
Source/WebCore/Modules/indexeddb/server/MemoryObjectStore.cpp
Source/WebCore/Modules/indexeddb/server/MemoryObjectStoreCursor.cpp
Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp
Source/WebCore/Modules/indexeddb/server/SQLiteIDBCursor.cpp
Source/WebCore/Modules/indexeddb/server/SQLiteIDBCursor.h
Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.cpp
Source/WebCore/Modules/indexeddb/shared/IDBResultData.cpp
Source/WebCore/Modules/indexeddb/shared/IDBResultData.h
Source/WebCore/bindings/js/IDBBindingUtilities.cpp
Source/WebCore/bindings/js/IDBBindingUtilities.h
Source/WebCore/bindings/js/JSIDBCursorWithValueCustom.cpp
Source/WebCore/bindings/js/JSIDBRequestCustom.cpp

index 76ead0c..4cb76d5 100644 (file)
@@ -1,3 +1,22 @@
+2019-04-18  Sihui Liu  <sihui_liu@apple.com>
+
+        Blob type cannot be stored correctly in IDB when IDBObjectStore has autoIncrement and keyPath options
+        https://bugs.webkit.org/show_bug.cgi?id=196128
+        <rdar://problem/49562115>
+
+        Reviewed by Geoffrey Garen.
+
+        * storage/indexeddb/modern/objectstore-autoincrement-types-expected.txt: Added.
+        * storage/indexeddb/modern/objectstore-autoincrement-types.html: Added.
+        * storage/indexeddb/modern/resources/objectstore-autoincrement-types.js: Added.
+        (prepareDatabase.event.target.onsuccess):
+        (prepareDatabase):
+        (compare):
+        (runGetTest):
+        (runGetAllTest):
+        (get store):
+        (testSteps):
+
 2019-04-18  Timothy Hatcher  <timothy@apple.com>
 
         Unreviewed test gardening.
index d14a8fb..465e02b 100644 (file)
@@ -1,3 +1,17 @@
+2019-04-18  Sihui Liu  <sihui_liu@apple.com>
+
+        Blob type cannot be stored correctly in IDB when IDBObjectStore has autoIncrement and keyPath options
+        https://bugs.webkit.org/show_bug.cgi?id=196128
+        <rdar://problem/49562115>
+
+        Reviewed by Geoffrey Garen.
+
+        Updated test expectations to PASS.
+
+        * web-platform-tests/IndexedDB/nested-cloning-large-expected.txt:
+        * web-platform-tests/IndexedDB/nested-cloning-large-multiple-expected.txt:
+        * web-platform-tests/IndexedDB/nested-cloning-small-expected.txt:
+
 2019-04-18  Antoine Quint  <graouts@apple.com>
 
         [Pointer Events WPT] Unskip imported/w3c/web-platform-tests/pointerevents/pointerevent_lostpointercapture_for_disconnected_node.html
index 2125ef0..e0e61e7 100644 (file)
@@ -1,9 +1,9 @@
 
 PASS large typed array 
 PASS blob with large typed array 
-FAIL blob with large typed array with key generator assert_equals: IndexedDB result class should match put() argument expected "[object Blob]" but got "[object Null]"
+PASS blob with large typed array with key generator 
 PASS array of blobs and large typed arrays 
-FAIL array of blobs and large typed arrays with key generator assert_equals: IndexedDB result class should match put() argument expected "[object Blob]" but got "[object Null]"
+PASS array of blobs and large typed arrays with key generator 
 PASS object with blobs and large typed arrays 
-FAIL object with blobs and large typed arrays with key generator assert_equals: IndexedDB result class should match put() argument expected "[object Blob]" but got "[object Null]"
+PASS object with blobs and large typed arrays with key generator 
 
index 064bf4a..f8134a4 100644 (file)
@@ -1,4 +1,4 @@
 
 PASS multiple requests of objects with blobs and large typed arrays 
-FAIL multiple requests of objects with blobs and large typed arrays with key generator assert_equals: IndexedDB result class should match put() argument expected "[object Blob]" but got "[object Null]"
+PASS multiple requests of objects with blobs and large typed arrays with key generator 
 
index 66b5fc4..468ca0b 100644 (file)
@@ -2,9 +2,9 @@
 PASS small typed array 
 PASS blob 
 PASS blob with small typed array 
-FAIL blob with small typed array with key generator assert_equals: IndexedDB result class should match put() argument expected "[object Blob]" but got "[object Null]"
+PASS blob with small typed array with key generator 
 PASS blob array 
-FAIL blob array with key generator assert_equals: IndexedDB result class should match put() argument expected "[object Blob]" but got "[object Null]"
+PASS blob array with key generator 
 PASS array of blobs and small typed arrays 
-FAIL array of blobs and small typed arrays with key generator assert_equals: IndexedDB result class should match put() argument expected "[object Blob]" but got "[object Null]"
+PASS array of blobs and small typed arrays with key generator 
 
diff --git a/LayoutTests/storage/indexeddb/modern/objectstore-autoincrement-types-expected.txt b/LayoutTests/storage/indexeddb/modern/objectstore-autoincrement-types-expected.txt
new file mode 100644 (file)
index 0000000..1944b28
--- /dev/null
@@ -0,0 +1,48 @@
+CONSOLE MESSAGE: line 44: runGetTest(): blob
+CONSOLE MESSAGE: line 44: runGetTest(): file
+CONSOLE MESSAGE: line 44: runGetTest(): imageData
+CONSOLE MESSAGE: line 44: runGetTest(): fileList
+CONSOLE MESSAGE: line 61: runGetAllTest()
+Test IndexedDB's IDBObjectStore auto-increment feature with more types.
+
+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)
+PASS key is 1
+PASS request.result.primaryKey is blob.primaryKey
+PASS request.result.size is blob.size
+PASS request.result.type is blob.type
+PASS key is 2
+PASS request.result.primaryKey is file.primaryKey
+PASS request.result.name is file.name
+PASS request.result.lastModified is file.lastModified
+PASS request.result.size is file.size
+PASS request.result.type is file.type
+PASS key is 3
+PASS request.result.primaryKey is imageData.primaryKey
+PASS request.result.width is imageData.width
+PASS request.result.height is imageData.height
+PASS key is 4
+PASS request.result.primaryKey is fileList.primaryKey
+PASS request.result.length is fileList.length
+PASS request.result[0].primaryKey is blob.primaryKey
+PASS request.result[0].size is blob.size
+PASS request.result[0].type is blob.type
+PASS request.result[1].primaryKey is file.primaryKey
+PASS request.result[1].name is file.name
+PASS request.result[1].lastModified is file.lastModified
+PASS request.result[1].size is file.size
+PASS request.result[1].type is file.type
+PASS request.result[2].primaryKey is imageData.primaryKey
+PASS request.result[2].width is imageData.width
+PASS request.result[2].height is imageData.height
+PASS request.result[3].primaryKey is fileList.primaryKey
+PASS request.result[3].length is fileList.length
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/storage/indexeddb/modern/objectstore-autoincrement-types.html b/LayoutTests/storage/indexeddb/modern/objectstore-autoincrement-types.html
new file mode 100644 (file)
index 0000000..9f4414b
--- /dev/null
@@ -0,0 +1,10 @@
+<html>
+<head>
+<script src="../../../resources/js-test.js"></script>
+<script src="../resources/shared.js"></script>
+</head>
+<body>
+<input id="fileInput" multiple type="file">
+<script src="resources/objectstore-autoincrement-types.js"></script>
+</body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/storage/indexeddb/modern/resources/objectstore-autoincrement-types.js b/LayoutTests/storage/indexeddb/modern/resources/objectstore-autoincrement-types.js
new file mode 100644 (file)
index 0000000..84ce057
--- /dev/null
@@ -0,0 +1,105 @@
+if (this.importScripts) {
+    importScripts('../../../resources/js-test.js');
+    importScripts('shared.js');
+}
+
+description("Test IndexedDB's IDBObjectStore auto-increment feature with more types.");
+
+indexedDBTest(prepareDatabase);
+
+var db;
+var request;
+var testGenerator;
+
+function prepareDatabase()
+{
+    db = event.target.result;
+    event.target.transaction.onabort = unexpectedAbortCallback;
+
+    db.createObjectStore("Store", {keyPath: "primaryKey", autoIncrement: true});
+    event.target.onsuccess = function() {
+        testGenerator = testSteps();
+        testGenerator.next();
+    };
+    event.target.onerror = unexpectedErrorCallback;
+}
+
+function compare(item1, name1, item2, name2) {
+    if (Array.isArray(item1)) {
+        for (let i = 0; i < item1.length; i ++)
+            compare(item1[i], name1[i], item2[i], name2+'['+i+']');
+        return;
+    }
+    for (key in item1) {
+        // Only platform Cocoa has this attribute
+        if (key == "webkitRelativePath")
+            continue;
+
+        if ((typeof item1[key]) != "object" && (typeof item1[key]) != "function")
+            shouldBe(name2+'.'+key, name1+'.'+key);
+    }
+}
+
+function runGetTest(store, item, itemName, expectedKey) {
+    console.log("runGetTest(): " + itemName);
+    request = store.put(item);
+    request.onerror = unexpectedErrorCallback;
+    request.onsuccess = ()=>{
+        key = request.result;
+        shouldBe("key", expectedKey + "");
+        request = store.get(key);
+        request.onerror = unexpectedErrorCallback;
+        request.onsuccess = ()=>{
+            item.primaryKey = key;
+            compare(item, itemName, request.result, "request.result");
+            testGenerator.next();
+        }
+    }
+}
+
+function runGetAllTest(store, item, itemName) {
+    console.log("runGetAllTest()");
+    request = store.getAll();
+    request.onerror = unexpectedErrorCallback;
+    request.onsuccess = ()=>{
+        compare(item, itemName, request.result, "request.result");
+        testGenerator.next();
+    }
+}
+
+function* testSteps()
+{
+    transaction = db.transaction("Store", "readwrite");
+    objectStore = transaction.objectStore("Store");
+    primaryKey = 1;
+
+    // Blob
+    var imageURL = "data:image/png;base64,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";
+    const [typePart, partRest] = imageURL.split(',');
+    const contentType = typePart.split(':')[1].split(';')[0];
+    const raw = atob(partRest);
+    const rawLength = raw.length;
+    const uInt8Array = new Uint8Array(rawLength);
+    for (let i = 0; i < rawLength; ++i)
+        uInt8Array[i] = raw.charCodeAt(i);
+    blob = new Blob([uInt8Array], { type: contentType });
+    file = new File([blob], "Filename");
+    var canvas = document.createElement("canvas");
+    var context = canvas.getContext("2d");
+    imageData = context.createImageData(1, 1);
+    fileList = document.getElementById("fileInput").files;
+
+    types = [blob, file, imageData, fileList];
+    typesName = ["blob", "file", "imageData", "fileList"];
+
+    for (var i = 0; i < types.length; ++ i) {
+        runGetTest(objectStore, types[i], typesName[i], i + 1);
+        yield;
+
+    }
+
+    runGetAllTest(objectStore, types, typesName);
+    yield;
+
+    finishJSTest();
+}
\ No newline at end of file
index 504afa3..ceba552 100644 (file)
@@ -1,3 +1,112 @@
+2019-04-18  Sihui Liu  <sihui_liu@apple.com>
+
+        Blob type cannot be stored correctly in IDB when IDBObjectStore has autoIncrement and keyPath options
+        https://bugs.webkit.org/show_bug.cgi?id=196128
+        <rdar://problem/49562115>
+
+        Reviewed by Geoffrey Garen.
+
+        If a key is auto-generated, it should become a property of the value object. Network process would perform the 
+        key injection by deserializing IDBValue into script value, setting the property, serializing the result and 
+        storing it in a database record. But network process does not have a JSDOMGlobalObject, so it would fail to 
+        deserialize types including Blob and File.
+
+        To solve this issue, we move the key injection to web process and let network process store the original value 
+        it gets. In this case, when web process asks for some value, network process should return key, value and key 
+        path so that web process can decide whether it should perform a key injection before returning the result. Note
+        that the auto-generated key would always be stored as the key in a ObjectStore record.
+
+        Test: storage/indexeddb/modern/objectstore-autoincrement-types.html
+
+        * Modules/indexeddb/IDBCursor.cpp:
+        (WebCore::IDBCursor::setGetResult):
+        * Modules/indexeddb/IDBCursor.h:
+        (WebCore::IDBCursor::primaryKeyPath):
+        * Modules/indexeddb/IDBGetAllResult.cpp:
+        (WebCore::IDBGetAllResult::isolatedCopy):
+        (WebCore::IDBGetAllResult::addKey):
+        (WebCore::IDBGetAllResult::addValue):
+        (WebCore::IDBGetAllResult::keys const):
+        (WebCore::IDBGetAllResult::values const):
+        (WebCore::IDBGetAllResult::allBlobFilePaths const):
+        (WebCore::isolatedCopyOfVariant): Deleted.
+
+        * Modules/indexeddb/IDBGetAllResult.h: Introduce an IDBKeyPath parameter. Also replace Variant with two Vectors,
+        because we only needed to store either key or value before, and now the stored value could be incomplete.
+        (WebCore::IDBGetAllResult::IDBGetAllResult):
+        (WebCore::IDBGetAllResult::keyPath const):
+        (WebCore::IDBGetAllResult::encode const):
+        (WebCore::IDBGetAllResult::decode):
+
+        * Modules/indexeddb/IDBGetResult.cpp:
+        (WebCore::IDBGetResult::setValue):
+        * Modules/indexeddb/IDBGetResult.h:
+        (WebCore::IDBGetResult::IDBGetResult):
+        (WebCore::IDBGetResult::keyPath const):
+        * Modules/indexeddb/IDBRequest.cpp:
+        (WebCore::IDBRequest::setResult):
+        (WebCore::IDBRequest::setResultToStructuredClone):
+        * Modules/indexeddb/IDBRequest.h:
+        * Modules/indexeddb/IDBTransaction.cpp:
+        (WebCore::IDBTransaction::didGetAllRecordsOnServer):
+        (WebCore::IDBTransaction::didGetRecordOnServer):
+        * Modules/indexeddb/server/MemoryIDBBackingStore.cpp:
+        (WebCore::IDBServer::MemoryIDBBackingStore::getRecord):
+        * Modules/indexeddb/server/MemoryIndex.cpp:
+        (WebCore::IDBServer::MemoryIndex::getResultForKeyRange const):
+        (WebCore::IDBServer::MemoryIndex::getAllRecords const):
+        * Modules/indexeddb/server/MemoryIndexCursor.cpp:
+        (WebCore::IDBServer::MemoryIndexCursor::currentData):
+        * Modules/indexeddb/server/MemoryObjectStore.cpp:
+        (WebCore::IDBServer::MemoryObjectStore::updateIndexesForPutRecord):
+        (WebCore::IDBServer::MemoryObjectStore::populateIndexWithExistingRecords):
+        (WebCore::IDBServer::MemoryObjectStore::getAllRecords const):
+        * Modules/indexeddb/server/MemoryObjectStoreCursor.cpp:
+        (WebCore::IDBServer::MemoryObjectStoreCursor::currentData):
+        * Modules/indexeddb/server/SQLiteIDBBackingStore.cpp:
+        (WebCore::IDBServer::SQLiteIDBBackingStore::updateOneIndexForAddRecord):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::updateAllIndexesForAddRecord):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::getRecord):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::cachedStatementForGetAllObjectStoreRecords):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::getAllObjectStoreRecords):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::getAllIndexRecords):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::getIndexRecord):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::uncheckedGetIndexRecordForOneKey):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::openCursor):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::iterateCursor):
+        * Modules/indexeddb/server/SQLiteIDBCursor.cpp:
+        (WebCore::IDBServer::SQLiteIDBCursor::currentData):
+        * Modules/indexeddb/server/SQLiteIDBCursor.h:
+        * Modules/indexeddb/server/UniqueIDBDatabase.cpp:
+
+        (WebCore::IDBServer::UniqueIDBDatabase::performPutOrAdd): Remove the key injection from network process. 
+        UniqueIDBDatabase stores any value it gets from IDBClient.
+
+        * Modules/indexeddb/shared/IDBResultData.cpp:
+        (WebCore::IDBResultData::getResultRef):
+        * Modules/indexeddb/shared/IDBResultData.h:
+
+        * bindings/js/IDBBindingUtilities.cpp:
+        (WebCore::injectIDBKeyIntoScriptValue): If property is read-only, set would fail and injectKeyIntoResult would
+        return null, but we expect it to return result as long as the property value is the same as target. Therefore, 
+        we can add an early return here.
+
+        (WebCore::createKeyPathArray):
+        (WebCore::generateIndexKeyForValue): We used to generate IndexKey from value stored in database but now the
+        value gets stored does not include auto-generated key, as we remove the key injection from network process. In 
+        this case if the IDBIndex has the same key path as the auto-generated key, IndexKey would be failed to create
+        for it cannot extract auto-generated key from value. Since the auto-generated key would always be the key in 
+        database record, we could use value of that key when we find a match in key path.
+
+        (WebCore::deserializeIDBValueWithKeyInjection): If the key path in the result is single entry, the key is 
+        probably auto-generated, so we could inject the result key into the result value unconditionally.
+
+        * bindings/js/IDBBindingUtilities.h:
+        * bindings/js/JSIDBCursorWithValueCustom.cpp:
+        (WebCore::JSIDBCursorWithValue::value const):
+        * bindings/js/JSIDBRequestCustom.cpp:
+        (WebCore::JSIDBRequest::result const):
+
 2019-04-18  Zalan Bujtas  <zalan@apple.com>
 
         Regression (r244291): Broken API Test AutoLayoutRenderingProgressRelativeOrdering
index 5d82ba6..933cba5 100644 (file)
@@ -344,8 +344,10 @@ bool IDBCursor::setGetResult(IDBRequest& request, const IDBGetResult& getResult)
     m_primaryKeyData = getResult.primaryKeyData();
     m_primaryKey = m_primaryKeyData.maybeCreateIDBKey();
 
-    if (isKeyCursorWithValue())
+    if (isKeyCursorWithValue()) {
         m_value = getResult.value();
+        m_keyPath = getResult.keyPath();
+    }
 
     m_gotValue = true;
     return true;
index 46edb80..f7e2062 100644 (file)
@@ -30,6 +30,7 @@
 #include "ExceptionOr.h"
 #include "IDBCursorDirection.h"
 #include "IDBCursorInfo.h"
+#include "IDBKeyPath.h"
 #include "IDBRequest.h"
 #include "IDBValue.h"
 #include "JSValueInWrappedObject.h"
@@ -60,6 +61,7 @@ public:
     IDBKey* key() { return m_key.get(); };
     IDBKey* primaryKey() { return m_primaryKey.get(); };
     IDBValue value() { return m_value; };
+    const Optional<IDBKeyPath>& primaryKeyPath() { return m_keyPath; };
     JSValueInWrappedObject& keyWrapper() { return m_keyWrapper; }
     JSValueInWrappedObject& primaryKeyWrapper() { return m_primaryKeyWrapper; }
     JSValueInWrappedObject& valueWrapper() { return m_valueWrapper; }
@@ -106,6 +108,7 @@ private:
     IDBKeyData m_keyData;
     IDBKeyData m_primaryKeyData;
     IDBValue m_value;
+    Optional<IDBKeyPath> m_keyPath;
 
     JSValueInWrappedObject m_keyWrapper;
     JSValueInWrappedObject m_primaryKeyWrapper;
index 4d2624d..5847da8 100644 (file)
 
 #if ENABLE(INDEXED_DATABASE)
 
+#include <wtf/CrossThreadCopier.h>
 #include <wtf/HashSet.h>
 
 namespace WebCore {
 
-template<typename T> void isolatedCopyOfVariant(const WTF::Variant<Vector<IDBKeyData>, Vector<IDBValue>, std::nullptr_t>& source, WTF::Variant<Vector<IDBKeyData>, Vector<IDBValue>, std::nullptr_t>& target)
-{
-    target = Vector<T>();
-    auto& sourceVector = WTF::get<Vector<T>>(source);
-    auto& targetVector = WTF::get<Vector<T>>(target);
-    targetVector.reserveInitialCapacity(sourceVector.size());
-    for (auto& element : sourceVector)
-        targetVector.uncheckedAppend(element.isolatedCopy());
-}
-
 IDBGetAllResult::IDBGetAllResult(const IDBGetAllResult& that, IsolatedCopyTag)
 {
     isolatedCopy(that, *this);
@@ -55,46 +46,29 @@ IDBGetAllResult IDBGetAllResult::isolatedCopy() const
 void IDBGetAllResult::isolatedCopy(const IDBGetAllResult& source, IDBGetAllResult& destination)
 {
     destination.m_type = source.m_type;
-
-    if (WTF::holds_alternative<std::nullptr_t>(source.m_results))
-        return;
-
-    switch (source.m_type) {
-    case IndexedDB::GetAllType::Keys:
-        isolatedCopyOfVariant<IDBKeyData>(source.m_results, destination.m_results);
-        break;
-    case IndexedDB::GetAllType::Values:
-        isolatedCopyOfVariant<IDBValue>(source.m_results, destination.m_results);
-        break;
-    }
+    destination.m_keys = crossThreadCopy(source.m_keys);
+    destination.m_values = crossThreadCopy(source.m_values);
+    destination.m_keyPath = WebCore::isolatedCopy(source.m_keyPath);
 }
 
 void IDBGetAllResult::addKey(IDBKeyData&& key)
 {
-    ASSERT(m_type == IndexedDB::GetAllType::Keys);
-    ASSERT(WTF::holds_alternative<Vector<IDBKeyData>>(m_results));
-    WTF::get<Vector<IDBKeyData>>(m_results).append(WTFMove(key));
+    m_keys.append(WTFMove(key));
 }
 
 void IDBGetAllResult::addValue(IDBValue&& value)
 {
-    ASSERT(m_type == IndexedDB::GetAllType::Values);
-    ASSERT(WTF::holds_alternative<Vector<IDBValue>>(m_results));
-    WTF::get<Vector<IDBValue>>(m_results).append(WTFMove(value));
+    m_values.append(WTFMove(value));
 }
 
 const Vector<IDBKeyData>& IDBGetAllResult::keys() const
 {
-    ASSERT(m_type == IndexedDB::GetAllType::Keys);
-    ASSERT(WTF::holds_alternative<Vector<IDBKeyData>>(m_results));
-    return WTF::get<Vector<IDBKeyData>>(m_results);
+    return m_keys;
 }
 
 const Vector<IDBValue>& IDBGetAllResult::values() const
 {
-    ASSERT(m_type == IndexedDB::GetAllType::Values);
-    ASSERT(WTF::holds_alternative<Vector<IDBValue>>(m_results));
-    return WTF::get<Vector<IDBValue>>(m_results);
+    return m_values;
 }
 
 Vector<String> IDBGetAllResult::allBlobFilePaths() const
@@ -102,7 +76,7 @@ Vector<String> IDBGetAllResult::allBlobFilePaths() const
     ASSERT(m_type == IndexedDB::GetAllType::Values);
 
     HashSet<String> pathSet;
-    for (auto& value : WTF::get<Vector<IDBValue>>(m_results)) {
+    for (auto& value : m_values) {
         for (auto& path : value.blobFilePaths())
             pathSet.add(path);
     }
index baf8043..3f97428 100644 (file)
@@ -28,6 +28,7 @@
 #if ENABLE(INDEXED_DATABASE)
 
 #include "IDBKeyData.h"
+#include "IDBKeyPath.h"
 #include "IDBValue.h"
 #include "IndexedDB.h"
 
@@ -42,17 +43,10 @@ public:
     {
     }
 
-    IDBGetAllResult(IndexedDB::GetAllType type)
+    IDBGetAllResult(IndexedDB::GetAllType type, const Optional<IDBKeyPath>& keyPath)
         : m_type(type)
+        , m_keyPath(keyPath)
     {
-        switch (m_type) {
-        case IndexedDB::GetAllType::Keys:
-            m_results = Vector<IDBKeyData>();
-            break;
-        case IndexedDB::GetAllType::Values:
-            m_results = Vector<IDBValue>();
-            break;
-        }
     }
 
     enum IsolatedCopyTag { IsolatedCopy };
@@ -60,6 +54,7 @@ public:
     IDBGetAllResult isolatedCopy() const;
 
     IndexedDB::GetAllType type() const { return m_type; }
+    const Optional<IDBKeyPath>& keyPath() const { return m_keyPath; }
     const Vector<IDBKeyData>& keys() const;
     const Vector<IDBValue>& values() const;
 
@@ -75,26 +70,15 @@ private:
     static void isolatedCopy(const IDBGetAllResult& source, IDBGetAllResult& destination);
 
     IndexedDB::GetAllType m_type { IndexedDB::GetAllType::Keys };
-    WTF::Variant<Vector<IDBKeyData>, Vector<IDBValue>, std::nullptr_t> m_results { nullptr };
+    Vector<IDBKeyData> m_keys;
+    Vector<IDBValue> m_values;
+    Optional<IDBKeyPath> m_keyPath;
 };
 
 template<class Encoder>
 void IDBGetAllResult::encode(Encoder& encoder) const
 {
-    encoder << m_type << static_cast<uint64_t>(m_results.index());
-
-    switch (m_results.index()) {
-    case 0:
-        encoder << WTF::get<Vector<IDBKeyData>>(m_results);
-        break;
-    case 1:
-        encoder << WTF::get<Vector<IDBValue>>(m_results);
-        break;
-    case 2:
-        break;
-    default:
-        RELEASE_ASSERT_NOT_REACHED();
-    }
+    encoder << m_type << m_keys << m_values << m_keyPath;
 }
 
 template<class Decoder>
@@ -103,32 +87,14 @@ bool IDBGetAllResult::decode(Decoder& decoder, IDBGetAllResult& result)
     if (!decoder.decode(result.m_type))
         return false;
 
-    uint64_t index;
-    if (!decoder.decode(index))
+    if (!decoder.decode(result.m_keys))
         return false;
 
-    switch (index) {
-    case 0: {
-        result.m_results = Vector<IDBKeyData>();
-        if (!decoder.decode(WTF::get<Vector<IDBKeyData>>(result.m_results)))
-            return false;
-        break;
-    }
-    case 1: {
-        result.m_results = Vector<IDBValue>();
-        Optional<Vector<IDBValue>> optional;
-        decoder >> optional;
-        if (!optional)
-            return false;
-        WTF::get<Vector<IDBValue>>(result.m_results) = WTFMove(*optional);
-        break;
-    }
-    case 2:
-        result.m_results = nullptr;
-        break;
-    default:
-        RELEASE_ASSERT_NOT_REACHED();
-    }
+    if (!decoder.decode(result.m_values))
+        return false;
+    
+    if (!decoder.decode(result.m_keyPath))
+        return false;
 
     return true;
 }
index 5f8e98a..949e813 100644 (file)
@@ -57,6 +57,11 @@ void IDBGetResult::isolatedCopy(const IDBGetResult& source, IDBGetResult& destin
     destination.m_isDefined = source.m_isDefined;
 }
 
+void IDBGetResult::setValue(IDBValue&& value)
+{
+    m_value = WTFMove(value);
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(INDEXED_DATABASE)
index b49f9a6..e87c390 100644 (file)
@@ -43,57 +43,36 @@ public:
     {
     }
 
-    IDBGetResult(const IDBValue& value, const IDBKeyData& currentPrimaryKey)
-        : m_value(value)
-        , m_primaryKeyData(currentPrimaryKey)
-    {
-    }
-
-    IDBGetResult(const ThreadSafeDataBuffer& buffer)
-        : m_value(buffer)
-    {
-    }
-
-    IDBGetResult(IDBValue&& buffer)
-        : m_value(WTFMove(buffer))
-    {
-    }
-
-    IDBGetResult(IDBKey& key)
-        : m_keyData(&key)
-    {
-    }
-
     IDBGetResult(const IDBKeyData& keyData)
         : m_keyData(keyData)
     {
     }
 
-    IDBGetResult(SharedBuffer* buffer, IDBKey& key, const IDBKeyPath& path)
-        : m_keyData(&key)
-        , m_keyPath(path)
-    {
-        if (buffer)
-            dataFromBuffer(*buffer);
-    }
-
     IDBGetResult(const IDBKeyData& keyData, const IDBKeyData& primaryKeyData)
         : m_keyData(keyData)
         , m_primaryKeyData(primaryKeyData)
     {
     }
 
-    IDBGetResult(const IDBKeyData& keyData, const IDBKeyData& primaryKeyData, IDBValue&& value)
+    IDBGetResult(const IDBKeyData& keyData, const ThreadSafeDataBuffer& buffer, const Optional<IDBKeyPath>& keyPath)
+        : m_value(buffer)
+        , m_keyData(keyData)
+        , m_keyPath(keyPath)
+    {
+    }
+
+    IDBGetResult(const IDBKeyData& keyData, IDBValue&& value, const Optional<IDBKeyPath>& keyPath)
         : m_value(WTFMove(value))
         , m_keyData(keyData)
-        , m_primaryKeyData(primaryKeyData)
+        , m_keyPath(keyPath)
     {
     }
 
-    IDBGetResult(const IDBKeyData& keyData, const IDBKeyData& primaryKeyData, const IDBValue& value)
-        : m_value(value)
+    IDBGetResult(const IDBKeyData& keyData, const IDBKeyData& primaryKeyData, IDBValue&& value, const Optional<IDBKeyPath>& keyPath)
+        : m_value(WTFMove(value))
         , m_keyData(keyData)
         , m_primaryKeyData(primaryKeyData)
+        , m_keyPath(keyPath)
     {
     }
 
@@ -102,10 +81,12 @@ public:
 
     IDBGetResult isolatedCopy() const;
 
+    void setValue(IDBValue&&);
+
     const IDBValue& value() const { return m_value; }
     const IDBKeyData& keyData() const { return m_keyData; }
     const IDBKeyData& primaryKeyData() const { return m_primaryKeyData; }
-    const IDBKeyPath& keyPath() const { return m_keyPath; }
+    const Optional<IDBKeyPath>& keyPath() const { return m_keyPath; }
     bool isDefined() const { return m_isDefined; }
 
     template<class Encoder> void encode(Encoder&) const;
@@ -119,7 +100,7 @@ private:
     IDBValue m_value;
     IDBKeyData m_keyData;
     IDBKeyData m_primaryKeyData;
-    IDBKeyPath m_keyPath;
+    Optional<IDBKeyPath> m_keyPath;
     bool m_isDefined { true };
 };
 
index 21a68ba..aa7543a 100644 (file)
@@ -394,7 +394,7 @@ void IDBRequest::setResult(const Vector<IDBKeyData>& keyDatas)
     m_resultWrapper = { };
 }
 
-void IDBRequest::setResult(const Vector<IDBValue>& values)
+void IDBRequest::setResult(const IDBGetAllResult& result)
 {
     ASSERT(&originThread() == &Thread::current());
 
@@ -404,7 +404,7 @@ void IDBRequest::setResult(const Vector<IDBValue>& values)
 
     VM& vm = context->vm();
     JSLockHolder lock(vm);
-    m_result = values;
+    m_result = result;
     m_resultWrapper = { };
 }
 
@@ -422,7 +422,7 @@ void IDBRequest::setResult(uint64_t number)
     m_resultWrapper = { };
 }
 
-void IDBRequest::setResultToStructuredClone(const IDBValue& value)
+void IDBRequest::setResultToStructuredClone(const IDBGetResult& result)
 {
     ASSERT(&originThread() == &Thread::current());
 
@@ -434,7 +434,7 @@ void IDBRequest::setResultToStructuredClone(const IDBValue& value)
 
     VM& vm = context->vm();
     JSLockHolder lock(vm);
-    m_result = value;
+    m_result = result;
     m_resultWrapper = { };
 }
 
index 72202e7..3f66f63 100644 (file)
@@ -31,6 +31,8 @@
 #include "ExceptionOr.h"
 #include "IDBActiveDOMObject.h"
 #include "IDBError.h"
+#include "IDBGetAllResult.h"
+#include "IDBGetResult.h"
 #include "IDBKeyData.h"
 #include "IDBResourceIdentifier.h"
 #include "IDBValue.h"
@@ -76,7 +78,7 @@ public:
 
     virtual ~IDBRequest();
 
-    using Result = Variant<RefPtr<IDBCursor>, RefPtr<IDBDatabase>, IDBKeyData, Vector<IDBKeyData>, IDBValue, Vector<IDBValue>, uint64_t, NullResultType>;
+    using Result = Variant<RefPtr<IDBCursor>, RefPtr<IDBDatabase>, IDBKeyData, Vector<IDBKeyData>, IDBGetResult, IDBGetAllResult, uint64_t, NullResultType>;
     ExceptionOr<Result> result() const;
     JSValueInWrappedObject& resultWrapper() { return m_resultWrapper; }
     JSValueInWrappedObject& cursorWrapper() { return m_cursorWrapper; }
@@ -107,9 +109,9 @@ public:
 
     void setResult(const IDBKeyData&);
     void setResult(const Vector<IDBKeyData>&);
-    void setResult(const Vector<IDBValue>&);
+    void setResultToStructuredClone(const IDBGetResult&);
+    void setResult(const IDBGetAllResult&);
     void setResult(uint64_t);
-    void setResultToStructuredClone(const IDBValue&);
     void setResultToUndefined();
 
     void willIterateCursor(IDBCursor&);
index 4cd6e52..cb0397c 100644 (file)
@@ -989,7 +989,7 @@ void IDBTransaction::didGetAllRecordsOnServer(IDBRequest& request, const IDBResu
         request.setResult(getAllResult.keys());
         break;
     case IndexedDB::GetAllType::Values:
-        request.setResult(getAllResult.values());
+        request.setResult(getAllResult);
         break;
     }
 
@@ -1093,7 +1093,7 @@ void IDBTransaction::didGetRecordOnServer(IDBRequest& request, const IDBResultDa
             request.setResultToUndefined();
     } else {
         if (resultData.getResult().value().data().data())
-            request.setResultToStructuredClone(resultData.getResult().value());
+            request.setResultToStructuredClone(resultData.getResult());
         else
             request.setResultToUndefined();
     }
index f6d1658..7d142b0 100644 (file)
@@ -363,9 +363,11 @@ IDBError MemoryIDBBackingStore::getRecord(const IDBResourceIdentifier& transacti
         return IDBError { UnknownError, "No backing store object store found"_s };
 
     switch (type) {
-    case IDBGetRecordDataType::KeyAndValue:
-        outValue = objectStore->valueForKeyRange(range);
+    case IDBGetRecordDataType::KeyAndValue: {
+        auto key = objectStore->lowestKeyWithRecordInRange(range);
+        outValue = { key, key.isNull() ? ThreadSafeDataBuffer() : objectStore->valueForKey(key), objectStore->info().keyPath() };
         break;
+    }
     case IDBGetRecordDataType::KeyOnly:
         outValue = objectStore->lowestKeyWithRecordInRange(range);
         break;
index 22a6ae8..49ddca6 100644 (file)
@@ -126,7 +126,7 @@ IDBGetResult MemoryIndex::getResultForKeyRange(IndexedDB::IndexRecordType type,
     if (!keyValue)
         return { };
 
-    return type == IndexedDB::IndexRecordType::Key ? IDBGetResult(*keyValue) : IDBGetResult(m_objectStore.valueForKeyRange(*keyValue));
+    return type == IndexedDB::IndexRecordType::Key ? IDBGetResult(*keyValue) : IDBGetResult(*keyValue, m_objectStore.valueForKeyRange(*keyValue), m_objectStore.info().keyPath());
 }
 
 uint64_t MemoryIndex::countForKeyRange(const IDBKeyRangeData& inRange)
@@ -156,7 +156,7 @@ void MemoryIndex::getAllRecords(const IDBKeyRangeData& keyRangeData, Optional<ui
 {
     LOG(IndexedDB, "MemoryIndex::getAllRecords");
 
-    result = { type };
+    result = { type, m_objectStore.info().keyPath() };
 
     if (!m_records)
         return;
@@ -179,10 +179,8 @@ void MemoryIndex::getAllRecords(const IDBKeyRangeData& keyRangeData, Optional<ui
 
         auto allValues = m_records->allValuesForKey(key, targetCount - currentCount);
         for (auto& keyValue : allValues) {
-            if (type == IndexedDB::GetAllType::Keys) {
-                IDBKeyData keyCopy { keyValue };
-                result.addKey(WTFMove(keyCopy));
-            } else
+            result.addKey(IDBKeyData(keyValue));
+            if (type == IndexedDB::GetAllType::Values)
                 result.addValue(m_objectStore.valueForKeyRange(keyValue));
         }
 
index 47b0e95..8fbc890 100644 (file)
@@ -75,7 +75,7 @@ void MemoryIndexCursor::currentData(IDBGetResult& getResult)
         getResult = { m_currentKey, m_currentPrimaryKey };
     else {
         IDBValue value = { m_index.objectStore().valueForKey(m_currentPrimaryKey), { }, { }, { } };
-        getResult = { m_currentKey, m_currentPrimaryKey, WTFMove(value) };
+        getResult = { m_currentKey, m_currentPrimaryKey, WTFMove(value), m_index.objectStore().info().keyPath() };
     }
 }
 
index 30ca143..ed6eba9 100644 (file)
@@ -310,7 +310,7 @@ IDBError MemoryObjectStore::updateIndexesForPutRecord(const IDBKeyData& key, con
 
     for (auto& index : m_indexesByName.values()) {
         IndexKey indexKey;
-        generateIndexKeyForValue(UniqueIDBDatabase::databaseThreadExecState(), index->info(), jsValue, indexKey);
+        generateIndexKeyForValue(UniqueIDBDatabase::databaseThreadExecState(), index->info(), jsValue, indexKey, m_info.keyPath(), key);
 
         if (indexKey.isNull())
             continue;
@@ -344,7 +344,7 @@ IDBError MemoryObjectStore::populateIndexWithExistingRecords(MemoryIndex& index)
             return IDBError { };
 
         IndexKey indexKey;
-        generateIndexKeyForValue(UniqueIDBDatabase::databaseThreadExecState(), index.info(), jsValue, indexKey);
+        generateIndexKeyForValue(UniqueIDBDatabase::databaseThreadExecState(), index.info(), jsValue, indexKey, m_info.keyPath(), iterator.key);
 
         if (indexKey.isNull())
             continue;
@@ -407,7 +407,7 @@ ThreadSafeDataBuffer MemoryObjectStore::valueForKeyRange(const IDBKeyRangeData&
 
 void MemoryObjectStore::getAllRecords(const IDBKeyRangeData& keyRangeData, Optional<uint32_t> count, IndexedDB::GetAllType type, IDBGetAllResult& result) const
 {
-    result = { type };
+    result = { type, m_info.keyPath() };
 
     uint32_t targetCount;
     if (count && count.value())
@@ -424,11 +424,9 @@ void MemoryObjectStore::getAllRecords(const IDBKeyRangeData& keyRangeData, Optio
 
         range.lowerKey = key;
         range.lowerOpen = true;
-
-        if (type == IndexedDB::GetAllType::Keys)
-            result.addKey(WTFMove(key));
-        else
+        if (type == IndexedDB::GetAllType::Values)
             result.addValue(valueForKey(key));
+        result.addKey(WTFMove(key));
 
         ++currentCount;
     }
index f8b23ea..3703347 100644 (file)
@@ -194,7 +194,7 @@ void MemoryObjectStoreCursor::currentData(IDBGetResult& data)
         data = { m_currentPositionKey, m_currentPositionKey };
     else {
         IDBValue value = { m_objectStore.valueForKeyRange(m_currentPositionKey), { }, { }, { } };
-        data = { m_currentPositionKey, m_currentPositionKey, WTFMove(value) };
+        data = { m_currentPositionKey, m_currentPositionKey, WTFMove(value), m_objectStore.info().keyPath() };
     }
 }
 
index 6249976..489ded4 100644 (file)
@@ -29,6 +29,7 @@
 #if ENABLE(INDEXED_DATABASE)
 
 #include "IDBBindingUtilities.h"
+#include "IDBCursorInfo.h"
 #include "IDBGetAllRecordsData.h"
 #include "IDBGetAllResult.h"
 #include "IDBGetRecordData.h"
@@ -1752,7 +1753,9 @@ IDBError SQLiteIDBBackingStore::updateOneIndexForAddRecord(const IDBIndexInfo& i
         return IDBError { };
 
     IndexKey indexKey;
-    generateIndexKeyForValue(*m_globalObject->globalExec(), info, jsValue, indexKey);
+    auto* objectStoreInfo = infoForObjectStore(info.objectStoreIdentifier());
+    ASSERT(objectStoreInfo);
+    generateIndexKeyForValue(*m_globalObject->globalExec(), info, jsValue, indexKey, objectStoreInfo->keyPath(), key);
 
     if (indexKey.isNull())
         return IDBError { };
@@ -1772,7 +1775,7 @@ IDBError SQLiteIDBBackingStore::updateAllIndexesForAddRecord(const IDBObjectStor
     bool anyRecordsSucceeded = false;
     for (auto& index : info.indexMap().values()) {
         IndexKey indexKey;
-        generateIndexKeyForValue(*m_globalObject->globalExec(), index, jsValue, indexKey);
+        generateIndexKeyForValue(*m_globalObject->globalExec(), index, jsValue, indexKey, info.keyPath(), key);
 
         if (indexKey.isNull())
             continue;
@@ -2004,12 +2007,12 @@ IDBError SQLiteIDBBackingStore::getRecord(const IDBResourceIdentifier& transacti
     }
 
     int64_t recordID = 0;
-    ThreadSafeDataBuffer resultBuffer;
+    ThreadSafeDataBuffer keyResultBuffer, valueResultBuffer;
     {
-        static const char* const lowerOpenUpperOpen = "SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;";
-        static const char* const lowerOpenUpperClosed = "SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;";
-        static const char* const lowerClosedUpperOpen = "SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;";
-        static const char* const lowerClosedUpperClosed = "SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;";
+        static const char* const lowerOpenUpperOpen = "SELECT key, value, ROWID FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;";
+        static const char* const lowerOpenUpperClosed = "SELECT key, value, ROWID FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;";
+        static const char* const lowerClosedUpperOpen = "SELECT key, value, ROWID FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;";
+        static const char* const lowerClosedUpperClosed = "SELECT key, value, ROWID FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;";
 
         static const char* const lowerOpenUpperOpenKeyOnly = "SELECT key FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;";
         static const char* const lowerOpenUpperClosedKeyOnly = "SELECT key FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;";
@@ -2066,27 +2069,31 @@ IDBError SQLiteIDBBackingStore::getRecord(const IDBResourceIdentifier& transacti
             return IDBError { UnknownError, "Error looking up record in object store by key range"_s };
         }
 
-        Vector<uint8_t> buffer;
-        sql->getColumnBlobAsVector(0, buffer);
-        resultBuffer = ThreadSafeDataBuffer::create(WTFMove(buffer));
+        Vector<uint8_t> keyBuffer;
+        sql->getColumnBlobAsVector(0, keyBuffer);
+        keyResultBuffer = ThreadSafeDataBuffer::create(WTFMove(keyBuffer));
 
-        if (type == IDBGetRecordDataType::KeyAndValue)
-            recordID = sql->getColumnInt64(1);
-    }
-
-    if (type == IDBGetRecordDataType::KeyOnly) {
-        auto* vector = resultBuffer.data();
-        if (!vector) {
-            LOG_ERROR("Unable to deserialize key data from database for IDBObjectStore.getKey()");
-            return IDBError { UnknownError, "Error extracting key data from database executing IDBObjectStore.getKey()"_s };
+        if (type == IDBGetRecordDataType::KeyAndValue) {
+            Vector<uint8_t> valueBuffer;
+            sql->getColumnBlobAsVector(1, valueBuffer);
+            valueResultBuffer = ThreadSafeDataBuffer::create(WTFMove(valueBuffer));
+            recordID = sql->getColumnInt64(2);
         }
+    }
 
-        IDBKeyData keyData;
-        if (!deserializeIDBKeyData(vector->data(), vector->size(), keyData)) {
-            LOG_ERROR("Unable to deserialize key data from database for IDBObjectStore.getKey()");
-            return IDBError { UnknownError, "Error extracting key data from database executing IDBObjectStore.getKey()"_s };
-        }
+    auto* keyVector = keyResultBuffer.data();
+    if (!keyVector) {
+        LOG_ERROR("Unable to deserialize key data from database for IDBObjectStore");
+        return IDBError { UnknownError, "Error extracting key data from database executing IDBObjectStore get"_s };
+    }
+    
+    IDBKeyData keyData;
+    if (!deserializeIDBKeyData(keyVector->data(), keyVector->size(), keyData)) {
+        LOG_ERROR("Unable to deserialize key data from database for IDBObjectStore");
+        return IDBError { UnknownError, "Error extracting key data from database executing IDBObjectStore get"_s };
+    }
 
+    if (type == IDBGetRecordDataType::KeyOnly) {
         resultValue = { keyData };
         return IDBError { };
     }
@@ -2100,7 +2107,9 @@ IDBError SQLiteIDBBackingStore::getRecord(const IDBResourceIdentifier& transacti
     if (!error.isNull())
         return error;
 
-    resultValue = { { resultBuffer, WTFMove(blobURLs), sessionID, WTFMove(blobFilePaths) } };
+    auto* objectStoreInfo = infoForObjectStore(objectStoreID);
+    ASSERT(objectStoreInfo);
+    resultValue = { keyData, { valueResultBuffer, WTFMove(blobURLs), sessionID, WTFMove(blobFilePaths) }, objectStoreInfo->keyPath()};
     return IDBError { };
 }
 
@@ -2115,10 +2124,10 @@ SQLiteStatement* SQLiteIDBBackingStore::cachedStatementForGetAllObjectStoreRecor
     static const char* const lowerOpenUpperClosedKey = "SELECT key FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;";
     static const char* const lowerClosedUpperOpenKey = "SELECT key FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;";
     static const char* const lowerClosedUpperClosedKey = "SELECT key FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;";
-    static const char* const lowerOpenUpperOpenValue = "SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;";
-    static const char* const lowerOpenUpperClosedValue = "SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;";
-    static const char* const lowerClosedUpperOpenValue = "SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;";
-    static const char* const lowerClosedUpperClosedValue = "SELECT value, ROWID FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;";
+    static const char* const lowerOpenUpperOpenValue = "SELECT key, value, ROWID FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;";
+    static const char* const lowerOpenUpperClosedValue = "SELECT key, value, ROWID FROM Records WHERE objectStoreID = ? AND key > CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;";
+    static const char* const lowerClosedUpperOpenValue = "SELECT key, value, ROWID FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key < CAST(? AS TEXT) ORDER BY key;";
+    static const char* const lowerClosedUpperClosedValue = "SELECT key, value, ROWID FROM Records WHERE objectStoreID = ? AND key >= CAST(? AS TEXT) AND key <= CAST(? AS TEXT) ORDER BY key;";
 
     if (getAllRecordsData.getAllType == IndexedDB::GetAllType::Keys) {
         if (getAllRecordsData.keyRangeData.lowerOpen) {
@@ -2183,7 +2192,9 @@ IDBError SQLiteIDBBackingStore::getAllObjectStoreRecords(const IDBResourceIdenti
         return IDBError { UnknownError, "Failed to look up record in object store by key range"_s };
     }
 
-    result = { getAllRecordsData.getAllType };
+    auto* objectStoreInfo = infoForObjectStore(getAllRecordsData.objectStoreIdentifier);
+    ASSERT(objectStoreInfo);
+    result = { getAllRecordsData.getAllType, objectStoreInfo->keyPath() };
 
     uint32_t targetResults;
     if (getAllRecordsData.count && getAllRecordsData.count.value())
@@ -2195,12 +2206,21 @@ IDBError SQLiteIDBBackingStore::getAllObjectStoreRecords(const IDBResourceIdenti
     uint32_t returnedResults = 0;
 
     while (sqlResult == SQLITE_ROW && returnedResults < targetResults) {
+        Vector<uint8_t> keyBuffer;
+        IDBKeyData keyData;
+        sql->getColumnBlobAsVector(0, keyBuffer);
+        if (!deserializeIDBKeyData(keyBuffer.data(), keyBuffer.size(), keyData)) {
+            LOG_ERROR("Unable to deserialize key data from database while getting all records");
+            return IDBError { UnknownError, "Unable to deserialize key data while getting all records"_s };
+        }
+        result.addKey(WTFMove(keyData));
+
         if (getAllRecordsData.getAllType == IndexedDB::GetAllType::Values) {
-            Vector<uint8_t> buffer;
-            sql->getColumnBlobAsVector(0, buffer);
-            ThreadSafeDataBuffer resultBuffer = ThreadSafeDataBuffer::create(WTFMove(buffer));
+            Vector<uint8_t> valueBuffer;
+            sql->getColumnBlobAsVector(1, valueBuffer);
+            ThreadSafeDataBuffer valueResultBuffer = ThreadSafeDataBuffer::create(WTFMove(valueBuffer));
 
-            auto recordID = sql->getColumnInt64(1);
+            auto recordID = sql->getColumnInt64(2);
 
             ASSERT(recordID);
             Vector<String> blobURLs, blobFilePaths;
@@ -2211,18 +2231,7 @@ IDBError SQLiteIDBBackingStore::getAllObjectStoreRecords(const IDBResourceIdenti
             if (!error.isNull())
                 return error;
 
-            result.addValue({ resultBuffer, WTFMove(blobURLs), sessionID, WTFMove(blobFilePaths) });
-        } else {
-            Vector<uint8_t> keyData;
-            IDBKeyData key;
-            sql->getColumnBlobAsVector(0, keyData);
-
-            if (!deserializeIDBKeyData(keyData.data(), keyData.size(), key)) {
-                LOG_ERROR("Unable to deserialize key data from database while getting all key records");
-                return IDBError { UnknownError, "Unable to deserialize key data while getting all key records"_s };
-            }
-
-            result.addKey(WTFMove(key));
+            result.addValue({ valueResultBuffer, WTFMove(blobURLs), sessionID, WTFMove(blobFilePaths) });
         }
 
         ++returnedResults;
@@ -2263,16 +2272,18 @@ IDBError SQLiteIDBBackingStore::getAllIndexRecords(const IDBResourceIdentifier&
         return IDBError { UnknownError, "Cursor failed while looking up index records in database"_s };
     }
 
-    result = { getAllRecordsData.getAllType };
+    auto* objectStoreInfo = infoForObjectStore(getAllRecordsData.objectStoreIdentifier);
+    ASSERT(objectStoreInfo);
+    result = { getAllRecordsData.getAllType, objectStoreInfo->keyPath() };
+
     uint32_t currentCount = 0;
     uint32_t targetCount = getAllRecordsData.count ? getAllRecordsData.count.value() : 0;
     if (!targetCount)
         targetCount = std::numeric_limits<uint32_t>::max();
     while (!cursor->didComplete() && !cursor->didError() && currentCount < targetCount) {
-        if (getAllRecordsData.getAllType == IndexedDB::GetAllType::Keys) {
-            IDBKeyData keyCopy = cursor->currentPrimaryKey();
-            result.addKey(WTFMove(keyCopy));
-        } else
+        IDBKeyData keyCopy = cursor->currentPrimaryKey();
+        result.addKey(WTFMove(keyCopy));
+        if (getAllRecordsData.getAllType == IndexedDB::GetAllType::Values)
             result.addValue(cursor->currentValue() ? *cursor->currentValue() : IDBValue());
 
         ++currentCount;
@@ -2319,8 +2330,11 @@ IDBError SQLiteIDBBackingStore::getIndexRecord(const IDBResourceIdentifier& tran
     else {
         if (type == IndexedDB::IndexRecordType::Key)
             getResult = { cursor->currentPrimaryKey() };
-        else
-            getResult = { cursor->currentValue() ? *cursor->currentValue() : IDBValue(), cursor->currentPrimaryKey() };
+        else {
+            auto* objectStoreInfo = infoForObjectStore(objectStoreID);
+            ASSERT(objectStoreInfo);
+            getResult = { cursor->currentPrimaryKey(), cursor->currentPrimaryKey(), cursor->currentValue() ? *cursor->currentValue() : IDBValue(), objectStoreInfo->keyPath() };
+        }
     }
 
     return IDBError { };
@@ -2371,7 +2385,8 @@ IDBError SQLiteIDBBackingStore::uncheckedGetIndexRecordForOneKey(int64_t indexID
         return IDBError { };
     }
 
-    sql->getColumnBlobAsVector(1, keyVector);
+    Vector<uint8_t> valueVector;
+    sql->getColumnBlobAsVector(1, valueVector);
 
     int64_t recordID = sql->getColumnInt64(2);
     Vector<String> blobURLs, blobFilePaths;
@@ -2382,7 +2397,9 @@ IDBError SQLiteIDBBackingStore::uncheckedGetIndexRecordForOneKey(int64_t indexID
     if (!error.isNull())
         return error;
 
-    getResult = { { ThreadSafeDataBuffer::create(WTFMove(keyVector)), WTFMove(blobURLs), sessionID, WTFMove(blobFilePaths) }, objectStoreKey };
+    auto* objectStoreInfo = infoForObjectStore(objectStoreID);
+    ASSERT(objectStoreInfo);
+    getResult = { objectStoreKey, objectStoreKey, { ThreadSafeDataBuffer::create(WTFMove(valueVector)), WTFMove(blobURLs), sessionID, WTFMove(blobFilePaths) }, objectStoreInfo->keyPath() };
     return IDBError { };
 }
 
@@ -2547,7 +2564,9 @@ IDBError SQLiteIDBBackingStore::openCursor(const IDBResourceIdentifier& transact
 
     m_cursors.set(cursor->identifier(), cursor);
 
-    cursor->currentData(result);
+    auto* objectStoreInfo = infoForObjectStore(info.objectStoreIdentifier());
+    ASSERT(objectStoreInfo);
+    cursor->currentData(result, objectStoreInfo->keyPath());
     return IDBError { };
 }
 
@@ -2590,7 +2609,9 @@ IDBError SQLiteIDBBackingStore::iterateCursor(const IDBResourceIdentifier& trans
         }
     }
 
-    cursor->currentData(result);
+    auto* objectStoreInfo = infoForObjectStore(cursor->objectStoreID());
+    ASSERT(objectStoreInfo);
+    cursor->currentData(result, objectStoreInfo->keyPath());
     return IDBError { };
 }
 
index 878c8bd..caeacf2 100644 (file)
@@ -101,7 +101,7 @@ SQLiteIDBCursor::~SQLiteIDBCursor()
         m_transaction->closeCursor(*this);
 }
 
-void SQLiteIDBCursor::currentData(IDBGetResult& result)
+void SQLiteIDBCursor::currentData(IDBGetResult& result, const Optional<IDBKeyPath>& keyPath)
 {
     ASSERT(!m_fetchedRecords.isEmpty());
 
@@ -112,7 +112,7 @@ void SQLiteIDBCursor::currentData(IDBGetResult& result)
         return;
     }
 
-    result = { currentRecord.record.key, currentRecord.record.primaryKey, currentRecord.record.value ? *currentRecord.record.value : IDBValue() };
+    result = { currentRecord.record.key, currentRecord.record.primaryKey, currentRecord.record.value ? *currentRecord.record.value : IDBValue(), keyPath};
 }
 
 static String buildIndexStatement(const IDBKeyRangeData& keyRange, IndexedDB::CursorDirection cursorDirection)
index f78ee76..8cc2ab9 100644 (file)
@@ -79,7 +79,7 @@ public:
 
     void objectStoreRecordsChanged();
 
-    void currentData(IDBGetResult&);
+    void currentData(IDBGetResult&, const Optional<IDBKeyPath>&);
 
 private:
     bool establishStatement();
index 6b80001..88ac2d9 100644 (file)
@@ -1247,35 +1247,6 @@ void UniqueIDBDatabase::performPutOrAdd(uint64_t callbackIdentifier, const IDBRe
         }
     }
 
-    // 3.4.1.2 Object Store Storage Operation
-    // If ObjectStore has a key path and the key is autogenerated, then inject the key into the value
-    // using steps to assign a key to a value using a key path.
-    ThreadSafeDataBuffer injectedRecordValue;
-    if (usedKeyIsGenerated && objectStoreInfo->keyPath()) {
-        VM& vm = databaseThreadVM();
-        JSLockHolder locker(vm);
-        auto scope = DECLARE_THROW_SCOPE(vm);
-
-        auto value = deserializeIDBValueToJSValue(databaseThreadExecState(), originalRecordValue.data());
-        if (value.isUndefined()) {
-            postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, IDBError(ConstraintError, "Unable to deserialize record value for record key injection"_s), usedKey));
-            return;
-        }
-
-        if (!injectIDBKeyIntoScriptValue(databaseThreadExecState(), usedKey, value, objectStoreInfo->keyPath().value())) {
-            postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, IDBError(ConstraintError, "Unable to inject record key into record value"_s), usedKey));
-            return;
-        }
-
-        auto serializedValue = SerializedScriptValue::create(databaseThreadExecState(), value);
-        if (UNLIKELY(scope.exception())) {
-            postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, IDBError(ConstraintError, "Unable to serialize record value after injecting record key"_s), usedKey));
-            return;
-        }
-
-        injectedRecordValue = ThreadSafeDataBuffer::copyVector(serializedValue->data());
-    }
-
     // 3.4.1 Object Store Storage Operation
     // ...If a record already exists in store ...
     // then remove the record from store using the steps for deleting records from an object store...
@@ -1286,10 +1257,7 @@ void UniqueIDBDatabase::performPutOrAdd(uint64_t callbackIdentifier, const IDBRe
         return;
     }
 
-    if (injectedRecordValue.data())
-        error = m_backingStore->addRecord(transactionIdentifier, *objectStoreInfo, usedKey, { injectedRecordValue, originalRecordValue.blobURLs(), originalRecordValue.sessionID(), originalRecordValue.blobFilePaths() });
-    else
-        error = m_backingStore->addRecord(transactionIdentifier, *objectStoreInfo, usedKey, originalRecordValue);
+    error = m_backingStore->addRecord(transactionIdentifier, *objectStoreInfo, usedKey, originalRecordValue);
 
     if (!error.isNull()) {
         postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey));
index 7ff08ee..a89454e 100644 (file)
@@ -233,6 +233,12 @@ const IDBGetResult& IDBResultData::getResult() const
     return *m_getResult;
 }
 
+IDBGetResult& IDBResultData::getResultRef()
+{
+    RELEASE_ASSERT(m_getResult);
+    return *m_getResult;
+}
+
 const IDBGetAllResult& IDBResultData::getAllResult() const
 {
     RELEASE_ASSERT(m_getAllResult);
index 80d8ce9..717b3e2 100644 (file)
@@ -107,6 +107,7 @@ public:
     uint64_t resultInteger() const { return m_resultInteger; }
 
     WEBCORE_EXPORT const IDBGetResult& getResult() const;
+    WEBCORE_EXPORT IDBGetResult& getResultRef();
     WEBCORE_EXPORT const IDBGetAllResult& getAllResult() const;
 
     WEBCORE_EXPORT IDBResultData();
index da106ec..ad9234c 100644 (file)
@@ -31,6 +31,7 @@
 
 #include "IDBBindingUtilities.h"
 
+#include "ExceptionCode.h"
 #include "IDBIndexInfo.h"
 #include "IDBKey.h"
 #include "IDBKeyData.h"
@@ -41,6 +42,7 @@
 #include "JSDOMBinding.h"
 #include "JSDOMConvertDate.h"
 #include "JSDOMConvertNullable.h"
+#include "JSDOMExceptionHandling.h"
 #include "JSFile.h"
 #include "Logging.h"
 #include "MessagePort.h"
@@ -324,6 +326,12 @@ bool injectIDBKeyIntoScriptValue(ExecState& exec, const IDBKeyData& keyData, JSV
     if (!key)
         return false;
 
+    // Do not set if object already has the correct property value.
+    auto jsKey = toJS(exec, *exec.lexicalGlobalObject(), key.get());
+    JSValue existingKey;
+    if (get(exec, parent, keyPathElements.last(), existingKey) && existingKey == jsKey)
+        return true;
+
     if (!set(exec, parent, keyPathElements.last(), toJS(exec, *exec.lexicalGlobalObject(), key.get())))
         return false;
 
@@ -411,9 +419,13 @@ JSC::JSValue toJS(JSC::ExecState* state, JSDOMGlobalObject* globalObject, const
     return toJS(*state, *globalObject, keyData.maybeCreateIDBKey().get());
 }
 
-static Vector<IDBKeyData> createKeyPathArray(ExecState& exec, JSValue value, const IDBIndexInfo& info)
+static Vector<IDBKeyData> createKeyPathArray(ExecState& exec, JSValue value, const IDBIndexInfo& info, Optional<IDBKeyPath> objectStoreKeyPath, const IDBKeyData& objectStoreKey)
 {
     auto visitor = WTF::makeVisitor([&](const String& string) -> Vector<IDBKeyData> {
+        // Value doesn't contain auto-generated key, so we need to manually add key if it is possibly auto-generated.
+        if (objectStoreKeyPath && WTF::holds_alternative<String>(objectStoreKeyPath.value()) && IDBKeyPath(string) == objectStoreKeyPath.value())
+            return { objectStoreKey };
+
         auto idbKey = internalCreateIDBKeyFromScriptValueAndKeyPath(exec, value, string);
         if (!idbKey)
             return { };
@@ -428,10 +440,14 @@ static Vector<IDBKeyData> createKeyPathArray(ExecState& exec, JSValue value, con
     }, [&](const Vector<String>& vector) -> Vector<IDBKeyData> {
         Vector<IDBKeyData> keys;
         for (auto& entry : vector) {
-            auto key = internalCreateIDBKeyFromScriptValueAndKeyPath(exec, value, entry);
-            if (!key || !key->isValid())
-                return { };
-            keys.append(key.get());
+            if (objectStoreKeyPath && WTF::holds_alternative<String>(objectStoreKeyPath.value()) && IDBKeyPath(entry) == objectStoreKeyPath.value())
+                keys.append(objectStoreKey);
+            else {
+                auto key = internalCreateIDBKeyFromScriptValueAndKeyPath(exec, value, entry);
+                if (!key || !key->isValid())
+                    return { };
+                keys.append(key.get());
+            }
         }
         return keys;
     });
@@ -439,16 +455,31 @@ static Vector<IDBKeyData> createKeyPathArray(ExecState& exec, JSValue value, con
     return WTF::visit(visitor, info.keyPath());
 }
 
-void generateIndexKeyForValue(ExecState& exec, const IDBIndexInfo& info, JSValue value, IndexKey& outKey)
+void generateIndexKeyForValue(ExecState& exec, const IDBIndexInfo& info, JSValue value, IndexKey& outKey, const Optional<IDBKeyPath>& objectStoreKeyPath, const IDBKeyData& objectStoreKey)
 {
-    auto keyDatas = createKeyPathArray(exec, value, info);
-
+    auto keyDatas = createKeyPathArray(exec, value, info, objectStoreKeyPath, objectStoreKey);
     if (keyDatas.isEmpty())
         return;
 
     outKey = IndexKey(WTFMove(keyDatas));
 }
 
+Optional<JSC::JSValue> deserializeIDBValueWithKeyInjection(ExecState& state, const IDBValue& value, const IDBKeyData& key, const Optional<IDBKeyPath>& keyPath)
+{
+    auto jsValue = deserializeIDBValueToJSValue(state, value);
+    if (jsValue.isUndefined() || !keyPath || !WTF::holds_alternative<String>(keyPath.value()) || !isIDBKeyPathValid(keyPath.value()))
+        return jsValue;
+
+    JSLockHolder locker(state.vm());
+    if (!injectIDBKeyIntoScriptValue(state, key, jsValue, keyPath.value())) {
+        auto throwScope = DECLARE_THROW_SCOPE(state.vm());
+        propagateException(state, throwScope, Exception(UnknownError, "Cannot inject key into script value"_s));
+        return WTF::nullopt;
+    }
+
+    return jsValue;
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(INDEXED_DATABASE)
index c491fee..f067cb0 100644 (file)
@@ -50,15 +50,17 @@ RefPtr<IDBKey> maybeCreateIDBKeyFromScriptValueAndKeyPath(JSC::ExecState&, const
 bool canInjectIDBKeyIntoScriptValue(JSC::ExecState&, const JSC::JSValue&, const IDBKeyPath&);
 bool injectIDBKeyIntoScriptValue(JSC::ExecState&, const IDBKeyData&, JSC::JSValue, const IDBKeyPath&);
 
-void generateIndexKeyForValue(JSC::ExecState&, const IDBIndexInfo&, JSC::JSValue, IndexKey& outKey);
+void generateIndexKeyForValue(JSC::ExecState&, const IDBIndexInfo&, JSC::JSValue, IndexKey& outKey, const Optional<IDBKeyPath>&, const IDBKeyData&);
 
 Ref<IDBKey> scriptValueToIDBKey(JSC::ExecState&, const JSC::JSValue&);
 
+JSC::JSValue deserializeIDBValueToJSValue(JSC::ExecState&, const IDBValue&, Vector<std::pair<String, String>>&);
 JSC::JSValue deserializeIDBValueToJSValue(JSC::ExecState&, const IDBValue&);
 JSC::JSValue toJS(JSC::ExecState*, JSDOMGlobalObject*, const IDBValue&);
 JSC::JSValue toJS(JSC::ExecState&, JSC::JSGlobalObject&, IDBKey*);
 JSC::JSValue toJS(JSC::ExecState*, JSDOMGlobalObject*, const IDBKeyData&);
 
+Optional<JSC::JSValue> deserializeIDBValueWithKeyInjection(JSC::ExecState&, const IDBValue&, const IDBKeyData&, const Optional<IDBKeyPath>&);
 }
 
 #endif // ENABLE(INDEXED_DATABASE)
index 1c65524..228dab4 100644 (file)
@@ -38,7 +38,8 @@ using namespace JSC;
 JSC::JSValue JSIDBCursorWithValue::value(JSC::ExecState& state) const
 {
     return cachedPropertyValue(state, *this, wrapped().valueWrapper(), [&] {
-        return deserializeIDBValueToJSValue(state, wrapped().value());
+        auto result = deserializeIDBValueWithKeyInjection(state, wrapped().value(), wrapped().primaryKey(), wrapped().primaryKeyPath());
+        return result ? result.value() : jsNull();
     });
 }
 
index 5ebb7cc..51902e2 100644 (file)
@@ -57,10 +57,26 @@ JSC::JSValue JSIDBRequest::result(JSC::ExecState& state) const
             return toJS<IDLIDBKeyData>(state, *jsCast<JSDOMGlobalObject*>(state.lexicalGlobalObject()), keyData);
         }, [&state] (Vector<IDBKeyData> keyDatas) {
             return toJS<IDLSequence<IDLIDBKeyData>>(state, *jsCast<JSDOMGlobalObject*>(state.lexicalGlobalObject()), keyDatas);
-        }, [&state] (IDBValue value) {
-            return toJS<IDLIDBValue>(state, *jsCast<JSDOMGlobalObject*>(state.lexicalGlobalObject()), value);
-        }, [&state] (Vector<IDBValue> values) {
-            return toJS<IDLSequence<IDLIDBValue>>(state, *jsCast<JSDOMGlobalObject*>(state.lexicalGlobalObject()), values);
+        }, [&state] (IDBGetResult getResult) {
+            auto result = deserializeIDBValueWithKeyInjection(state, getResult.value(), getResult.keyData(), getResult.keyPath());
+            return result ? result.value() : jsNull();
+        }, [&state] (IDBGetAllResult getAllResult) {
+            auto& keys = getAllResult.keys();
+            auto& values = getAllResult.values();
+            auto& keyPath = getAllResult.keyPath();
+            auto scope = DECLARE_THROW_SCOPE(state.vm());
+            JSC::MarkedArgumentBuffer list;
+            for (unsigned i = 0; i < values.size(); i ++) {
+                auto result = deserializeIDBValueWithKeyInjection(state, values[i], keys[i], keyPath);
+                if (!result)
+                    return jsNull();
+                list.append(result.value());
+                if (UNLIKELY(list.hasOverflowed())) {
+                    propagateException(state, scope, Exception(UnknownError));
+                    return jsNull();
+                }
+            }
+            return JSValue(JSC::constructArray(&state, nullptr, state.lexicalGlobalObject(), list));
         }, [] (uint64_t number) {
             return toJS<IDLUnsignedLongLong>(number);
         }, [] (IDBRequest::NullResultType other) {