Title: [271379] trunk
Revision
271379
Author
[email protected]
Date
2021-01-11 14:00:11 -0800 (Mon, 11 Jan 2021)

Log Message

Keep newly created IDBObjectStores in deleted map when IDBTransaction is aborted
https://bugs.webkit.org/show_bug.cgi?id=220483
<rdar://problem/71934293>

Patch by Sihui Liu <[email protected]> on 2021-01-11
Reviewed by Darin Adler.

Source/WebCore:

When an upgrade transaction is aborted, we move objects from m_deletedObjectStores to m_referencedObjectStores
to revert the deletion operation. When updating m_referencedObjectStores, we did not check whether key already
exists (this can happen when an object store gets deleted and a new object store with the same name is
created; see updated layout test). Therefore, some object store in m_referencedObjectStores would be replaced
and destroyed (since m_referencedObjectStores holds unique pointers) when the object store is referenced by JS
object.

Test: storage/indexeddb/modern/abort-objectstore-info.html

* Modules/indexeddb/IDBTransaction.cpp:
(WebCore::IDBTransaction::internalAbort):

LayoutTests:

* storage/indexeddb/modern/abort-objectstore-info-expected.txt:
* storage/indexeddb/modern/abort-objectstore-info-private-expected.txt:
* storage/indexeddb/modern/resources/abort-objectstore-info.js:
(prepareDatabase):
(secondUpgradeNeeded):
(checkState):

Modified Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (271378 => 271379)


--- trunk/LayoutTests/ChangeLog	2021-01-11 21:36:41 UTC (rev 271378)
+++ trunk/LayoutTests/ChangeLog	2021-01-11 22:00:11 UTC (rev 271379)
@@ -1,3 +1,18 @@
+2021-01-11  Sihui Liu  <[email protected]>
+
+        Keep newly created IDBObjectStores in deleted map when IDBTransaction is aborted
+        https://bugs.webkit.org/show_bug.cgi?id=220483
+        <rdar://problem/71934293>
+
+        Reviewed by Darin Adler.
+
+        * storage/indexeddb/modern/abort-objectstore-info-expected.txt:
+        * storage/indexeddb/modern/abort-objectstore-info-private-expected.txt:
+        * storage/indexeddb/modern/resources/abort-objectstore-info.js:
+        (prepareDatabase):
+        (secondUpgradeNeeded):
+        (checkState):
+
 2021-01-11  Peng Liu  <[email protected]>
 
         A video element needs to ignore the request to enter/exit fullscreen before the current fullscreen mode change is completed

Modified: trunk/LayoutTests/storage/indexeddb/modern/abort-objectstore-info-expected.txt (271378 => 271379)


--- trunk/LayoutTests/storage/indexeddb/modern/abort-objectstore-info-expected.txt	2021-01-11 21:36:41 UTC (rev 271378)
+++ trunk/LayoutTests/storage/indexeddb/modern/abort-objectstore-info-expected.txt	2021-01-11 22:00:11 UTC (rev 271379)
@@ -11,11 +11,14 @@
 objectStore1_1 = connection1.createObjectStore('objectStore1');
 objectStore1_2 = connection1.createObjectStore('objectStore2');
 objectStore1_2.createIndex('index', 'foo');
+objectStore1_4 = connection1.createObjectStore('objectStore4');
+objectStore1_4.createIndex('index', 'foo');
 
 PASS connection1.version is 1
-PASS connection1.objectStoreNames.length is 2
+PASS connection1.objectStoreNames.length is 3
 PASS objectStore1_1.indexNames.length is 0
 PASS objectStore1_2.indexNames.length is 1
+PASS objectStore1_4.indexNames.length is 1
 
 connection1.close();
 secondRequest = indexedDB.open(dbname, 2);
@@ -24,22 +27,30 @@
 objectStore2_1 = secondRequest.transaction.objectStore('objectStore1');
 objectStore2_2 = secondRequest.transaction.objectStore('objectStore2');
 objectStore2_3 = connection2.createObjectStore('objectStore3');
+objectStore2_4 = secondRequest.transaction.objectStore('objectStore4');
 
 PASS connection2.version is 2
-PASS connection2.objectStoreNames.length is 3
+PASS connection2.objectStoreNames.length is 4
 PASS objectStore2_1.indexNames.length is 0
 PASS objectStore2_2.indexNames.length is 1
 PASS objectStore2_3.indexNames.length is 0
+PASS objectStore2_4.indexNames.length is 1
 
 objectStore2_1.createIndex('index', 'foo');
 objectStore2_2.deleteIndex('index');
 objectStore2_3.createIndex('index', 'foo');
+objectStore2_4.deleteIndex('index');
+connection2.deleteObjectStore('objectStore4');
+new_objectStore2_4 = connection2.createObjectStore('objectStore4');
+new_objectStore2_4.createIndex('index1', 'foo');
+new_objectStore2_4.createIndex('index2', 'bar');
 
 PASS connection2.version is 2
-PASS connection2.objectStoreNames.length is 3
+PASS connection2.objectStoreNames.length is 4
 PASS objectStore2_1.indexNames.length is 1
 PASS objectStore2_2.indexNames.length is 0
 PASS objectStore2_3.indexNames.length is 1
+PASS new_objectStore2_4.indexNames.length is 2
 
 secondRequest.transaction.abort();
 connection2.close()
@@ -46,15 +57,18 @@
 
 checkState():
 PASS connection1.version is 1
-PASS connection1.objectStoreNames.length is 2
+PASS connection1.objectStoreNames.length is 3
 PASS objectStore1_1.indexNames.length is 0
 PASS objectStore1_2.indexNames.length is 1
+PASS objectStore1_4.indexNames.length is 1
 
 PASS connection2.version is 1
-PASS connection2.objectStoreNames.length is 2
+PASS connection2.objectStoreNames.length is 3
 PASS objectStore2_1.indexNames.length is 0
 PASS objectStore2_2.indexNames.length is 1
 PASS objectStore2_3.indexNames.length is 0
+PASS objectStore2_4.indexNames.length is 1
+PASS new_objectStore2_4.indexNames.length is 0
 PASS successfullyParsed is true
 
 TEST COMPLETE

Modified: trunk/LayoutTests/storage/indexeddb/modern/abort-objectstore-info-private-expected.txt (271378 => 271379)


--- trunk/LayoutTests/storage/indexeddb/modern/abort-objectstore-info-private-expected.txt	2021-01-11 21:36:41 UTC (rev 271378)
+++ trunk/LayoutTests/storage/indexeddb/modern/abort-objectstore-info-private-expected.txt	2021-01-11 22:00:11 UTC (rev 271379)
@@ -11,11 +11,14 @@
 objectStore1_1 = connection1.createObjectStore('objectStore1');
 objectStore1_2 = connection1.createObjectStore('objectStore2');
 objectStore1_2.createIndex('index', 'foo');
+objectStore1_4 = connection1.createObjectStore('objectStore4');
+objectStore1_4.createIndex('index', 'foo');
 
 PASS connection1.version is 1
-PASS connection1.objectStoreNames.length is 2
+PASS connection1.objectStoreNames.length is 3
 PASS objectStore1_1.indexNames.length is 0
 PASS objectStore1_2.indexNames.length is 1
+PASS objectStore1_4.indexNames.length is 1
 
 connection1.close();
 secondRequest = indexedDB.open(dbname, 2);
@@ -24,22 +27,30 @@
 objectStore2_1 = secondRequest.transaction.objectStore('objectStore1');
 objectStore2_2 = secondRequest.transaction.objectStore('objectStore2');
 objectStore2_3 = connection2.createObjectStore('objectStore3');
+objectStore2_4 = secondRequest.transaction.objectStore('objectStore4');
 
 PASS connection2.version is 2
-PASS connection2.objectStoreNames.length is 3
+PASS connection2.objectStoreNames.length is 4
 PASS objectStore2_1.indexNames.length is 0
 PASS objectStore2_2.indexNames.length is 1
 PASS objectStore2_3.indexNames.length is 0
+PASS objectStore2_4.indexNames.length is 1
 
 objectStore2_1.createIndex('index', 'foo');
 objectStore2_2.deleteIndex('index');
 objectStore2_3.createIndex('index', 'foo');
+objectStore2_4.deleteIndex('index');
+connection2.deleteObjectStore('objectStore4');
+new_objectStore2_4 = connection2.createObjectStore('objectStore4');
+new_objectStore2_4.createIndex('index1', 'foo');
+new_objectStore2_4.createIndex('index2', 'bar');
 
 PASS connection2.version is 2
-PASS connection2.objectStoreNames.length is 3
+PASS connection2.objectStoreNames.length is 4
 PASS objectStore2_1.indexNames.length is 1
 PASS objectStore2_2.indexNames.length is 0
 PASS objectStore2_3.indexNames.length is 1
+PASS new_objectStore2_4.indexNames.length is 2
 
 secondRequest.transaction.abort();
 connection2.close()
@@ -46,15 +57,18 @@
 
 checkState():
 PASS connection1.version is 1
-PASS connection1.objectStoreNames.length is 2
+PASS connection1.objectStoreNames.length is 3
 PASS objectStore1_1.indexNames.length is 0
 PASS objectStore1_2.indexNames.length is 1
+PASS objectStore1_4.indexNames.length is 1
 
 PASS connection2.version is 1
-PASS connection2.objectStoreNames.length is 2
+PASS connection2.objectStoreNames.length is 3
 PASS objectStore2_1.indexNames.length is 0
 PASS objectStore2_2.indexNames.length is 1
 PASS objectStore2_3.indexNames.length is 0
+PASS objectStore2_4.indexNames.length is 1
+PASS new_objectStore2_4.indexNames.length is 0
 PASS successfullyParsed is true
 
 TEST COMPLETE

Modified: trunk/LayoutTests/storage/indexeddb/modern/resources/abort-objectstore-info.js (271378 => 271379)


--- trunk/LayoutTests/storage/indexeddb/modern/resources/abort-objectstore-info.js	2021-01-11 21:36:41 UTC (rev 271378)
+++ trunk/LayoutTests/storage/indexeddb/modern/resources/abort-objectstore-info.js	2021-01-11 22:00:11 UTC (rev 271379)
@@ -8,12 +8,15 @@
     evalAndLog("objectStore1_1 = connection1.createObjectStore('objectStore1');");
     evalAndLog("objectStore1_2 = connection1.createObjectStore('objectStore2');");
     evalAndLog("objectStore1_2.createIndex('index', 'foo');");
+    evalAndLog("objectStore1_4 = connection1.createObjectStore('objectStore4');");
+    evalAndLog("objectStore1_4.createIndex('index', 'foo');");
 
     debug("");
     shouldBe("connection1.version", "1");
-    shouldBe("connection1.objectStoreNames.length", "2");
+    shouldBe("connection1.objectStoreNames.length", "3");
     shouldBe("objectStore1_1.indexNames.length", "0");
     shouldBe("objectStore1_2.indexNames.length", "1");
+    shouldBe("objectStore1_4.indexNames.length", "1");
     debug("");
 }
 
@@ -35,25 +38,33 @@
     evalAndLog("objectStore2_1 = secondRequest.transaction.objectStore('objectStore1');");
     evalAndLog("objectStore2_2 = secondRequest.transaction.objectStore('objectStore2');");
     evalAndLog("objectStore2_3 = connection2.createObjectStore('objectStore3');");
+    evalAndLog("objectStore2_4 = secondRequest.transaction.objectStore('objectStore4');");
 
     debug("");
     shouldBe("connection2.version", "2");
-    shouldBe("connection2.objectStoreNames.length", "3");
+    shouldBe("connection2.objectStoreNames.length", "4");
     shouldBe("objectStore2_1.indexNames.length", "0");
     shouldBe("objectStore2_2.indexNames.length", "1");
     shouldBe("objectStore2_3.indexNames.length", "0");
+    shouldBe("objectStore2_4.indexNames.length", "1");
     
     debug("");
     evalAndLog("objectStore2_1.createIndex('index', 'foo');");
     evalAndLog("objectStore2_2.deleteIndex('index');");
     evalAndLog("objectStore2_3.createIndex('index', 'foo');");
+    evalAndLog("objectStore2_4.deleteIndex('index');");
+    evalAndLog("connection2.deleteObjectStore('objectStore4');");
+    evalAndLog("new_objectStore2_4 = connection2.createObjectStore('objectStore4');");
+    evalAndLog("new_objectStore2_4.createIndex('index1', 'foo');");
+    evalAndLog("new_objectStore2_4.createIndex('index2', 'bar');");
+
     debug("");
-
     shouldBe("connection2.version", "2");
-    shouldBe("connection2.objectStoreNames.length", "3");
+    shouldBe("connection2.objectStoreNames.length", "4");
     shouldBe("objectStore2_1.indexNames.length", "1");
     shouldBe("objectStore2_2.indexNames.length", "0");
     shouldBe("objectStore2_3.indexNames.length", "1");
+    shouldBe("new_objectStore2_4.indexNames.length", "2");
 
     debug("");
     evalAndLog("secondRequest.transaction.abort();");
@@ -65,16 +76,19 @@
     debug("checkState():");
 
     shouldBe("connection1.version", "1");
-    shouldBe("connection1.objectStoreNames.length", "2");
+    shouldBe("connection1.objectStoreNames.length", "3");
     shouldBe("objectStore1_1.indexNames.length", "0");
     shouldBe("objectStore1_2.indexNames.length", "1");
+    shouldBe("objectStore1_4.indexNames.length", "1");
     debug("");
 
     shouldBe("connection2.version", "1");
-    shouldBe("connection2.objectStoreNames.length", "2");
+    shouldBe("connection2.objectStoreNames.length", "3");
     shouldBe("objectStore2_1.indexNames.length", "0");
     shouldBe("objectStore2_2.indexNames.length", "1");
     shouldBe("objectStore2_3.indexNames.length", "0");
+    shouldBe("objectStore2_4.indexNames.length", "1");
+    shouldBe("new_objectStore2_4.indexNames.length", "0");
 
     finishJSTest();
 }

Modified: trunk/Source/WebCore/ChangeLog (271378 => 271379)


--- trunk/Source/WebCore/ChangeLog	2021-01-11 21:36:41 UTC (rev 271378)
+++ trunk/Source/WebCore/ChangeLog	2021-01-11 22:00:11 UTC (rev 271379)
@@ -1,3 +1,23 @@
+2021-01-11  Sihui Liu  <[email protected]>
+
+        Keep newly created IDBObjectStores in deleted map when IDBTransaction is aborted
+        https://bugs.webkit.org/show_bug.cgi?id=220483
+        <rdar://problem/71934293>
+
+        Reviewed by Darin Adler.
+
+        When an upgrade transaction is aborted, we move objects from m_deletedObjectStores to m_referencedObjectStores 
+        to revert the deletion operation. When updating m_referencedObjectStores, we did not check whether key already 
+        exists (this can happen when an object store gets deleted and a new object store with the same name is 
+        created; see updated layout test). Therefore, some object store in m_referencedObjectStores would be replaced 
+        and destroyed (since m_referencedObjectStores holds unique pointers) when the object store is referenced by JS 
+        object.
+
+        Test: storage/indexeddb/modern/abort-objectstore-info.html
+
+        * Modules/indexeddb/IDBTransaction.cpp:
+        (WebCore::IDBTransaction::internalAbort):
+
 2021-01-11  Alex Christensen  <[email protected]>
 
         Use sendWithAsyncReply instead of dataCallback for icon loading

Modified: trunk/Source/WebCore/Modules/indexeddb/IDBTransaction.cpp (271378 => 271379)


--- trunk/Source/WebCore/Modules/indexeddb/IDBTransaction.cpp	2021-01-11 21:36:41 UTC (rev 271378)
+++ trunk/Source/WebCore/Modules/indexeddb/IDBTransaction.cpp	2021-01-11 22:00:11 UTC (rev 271379)
@@ -232,10 +232,14 @@
 
         auto& info = m_database->info();
         Vector<uint64_t> identifiersToRemove;
+        Vector<std::unique_ptr<IDBObjectStore>> objectStoresToDelete;
         for (auto& iterator : m_deletedObjectStores) {
             if (info.infoForExistingObjectStore(iterator.key)) {
                 auto name = iterator.value->info().name();
-                m_referencedObjectStores.set(name, WTFMove(iterator.value));
+                auto result = m_referencedObjectStores.add(name, nullptr);
+                if (!result.isNewEntry)
+                    objectStoresToDelete.append(std::exchange(result.iterator->value, nullptr));
+                result.iterator->value = std::exchange(iterator.value, nullptr);
                 identifiersToRemove.append(iterator.key);
             }
         }
@@ -245,6 +249,12 @@
 
         for (auto& objectStore : m_referencedObjectStores.values())
             objectStore->rollbackForVersionChangeAbort();
+
+        for (auto& objectStore : objectStoresToDelete) {
+            objectStore->rollbackForVersionChangeAbort();
+            auto objectStoreIdentifier = objectStore->info().identifier();
+            m_deletedObjectStores.set(objectStoreIdentifier, std::exchange(objectStore, nullptr));
+        }
     }
 
     transitionedToFinishing(IndexedDB::TransactionState::Aborting);
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to