IndexedDB: Create IDBTransaction interface

This CL creates a bare IDBTransaction interface with just the
CreateObjectStore method.  Later CLs will be responsible for
moving the remaining methods from IDBDatabase to IDBTransaction.

Bug: 717812
Change-Id: Ie1e084957e66f2afef563d37af9243726e48855b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1526172
Reviewed-by: Kinuko Yasuda <[email protected]>
Reviewed-by: Tom Sepez <[email protected]>
Reviewed-by: Daniel Murphy <[email protected]>
Commit-Queue: Chase Phillips <[email protected]>
Cr-Commit-Position: refs/heads/master@{#643406}
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 49ab076..faf64e2 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1056,6 +1056,8 @@
     "indexed_db/scopes/scope_lock_range.h",
     "indexed_db/scopes/scopes_lock_manager.cc",
     "indexed_db/scopes/scopes_lock_manager.h",
+    "indexed_db/transaction_impl.cc",
+    "indexed_db/transaction_impl.h",
     "initiator_csp_context.cc",
     "initiator_csp_context.h",
     "installedapp/installed_app_provider_impl_default.cc",
diff --git a/content/browser/indexed_db/database_impl.cc b/content/browser/indexed_db/database_impl.cc
index 3b3501a..e25f073c 100644
--- a/content/browser/indexed_db/database_impl.cc
+++ b/content/browser/indexed_db/database_impl.cc
@@ -20,6 +20,7 @@
 #include "content/browser/indexed_db/indexed_db_dispatcher_host.h"
 #include "content/browser/indexed_db/indexed_db_transaction.h"
 #include "content/browser/indexed_db/indexed_db_value.h"
+#include "content/browser/indexed_db/transaction_impl.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "storage/browser/blob/blob_storage_context.h"
@@ -108,24 +109,6 @@
   indexed_db_context_->ConnectionClosed(origin_, connection_.get());
 }
 
-void DatabaseImpl::CreateObjectStore(int64_t transaction_id,
-                                     int64_t object_store_id,
-                                     const base::string16& name,
-                                     const IndexedDBKeyPath& key_path,
-                                     bool auto_increment) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!connection_->IsConnected())
-    return;
-
-  IndexedDBTransaction* transaction =
-      connection_->GetTransaction(transaction_id);
-  if (!transaction)
-    return;
-
-  connection_->database()->CreateObjectStore(transaction, object_store_id, name,
-                                             key_path, auto_increment);
-}
-
 void DatabaseImpl::DeleteObjectStore(int64_t transaction_id,
                                      int64_t object_store_id) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -157,6 +140,7 @@
 }
 
 void DatabaseImpl::CreateTransaction(
+    blink::mojom::IDBTransactionAssociatedRequest transaction_request,
     int64_t transaction_id,
     const std::vector<int64_t>& object_store_ids,
     blink::mojom::IDBTransactionMode mode) {
@@ -175,6 +159,9 @@
       new IndexedDBBackingStore::Transaction(
           connection_->database()->backing_store()));
   connection_->database()->RegisterAndScheduleTransaction(transaction);
+
+  dispatcher_host_->CreateAndBindTransactionImpl(
+      std::move(transaction_request), origin_, transaction->AsWeakPtr());
 }
 
 void DatabaseImpl::Close() {
diff --git a/content/browser/indexed_db/database_impl.h b/content/browser/indexed_db/database_impl.h
index 3c17ea0..95753217 100644
--- a/content/browser/indexed_db/database_impl.h
+++ b/content/browser/indexed_db/database_impl.h
@@ -38,19 +38,16 @@
   ~DatabaseImpl() override;
 
   // blink::mojom::IDBDatabase implementation
-  void CreateObjectStore(int64_t transaction_id,
-                         int64_t object_store_id,
-                         const base::string16& name,
-                         const blink::IndexedDBKeyPath& key_path,
-                         bool auto_increment) override;
   void DeleteObjectStore(int64_t transaction_id,
                          int64_t object_store_id) override;
   void RenameObjectStore(int64_t transaction_id,
                          int64_t object_store_id,
                          const base::string16& new_name) override;
-  void CreateTransaction(int64_t transaction_id,
-                         const std::vector<int64_t>& object_store_ids,
-                         blink::mojom::IDBTransactionMode mode) override;
+  void CreateTransaction(
+      blink::mojom::IDBTransactionAssociatedRequest transaction_request,
+      int64_t transaction_id,
+      const std::vector<int64_t>& object_store_ids,
+      blink::mojom::IDBTransactionMode mode) override;
   void Close() override;
   void VersionChangeIgnored() override;
   void AddObserver(int64_t transaction_id,
diff --git a/content/browser/indexed_db/indexed_db_database.cc b/content/browser/indexed_db/indexed_db_database.cc
index 12cd64d..4a15e694 100644
--- a/content/browser/indexed_db/indexed_db_database.cc
+++ b/content/browser/indexed_db/indexed_db_database.cc
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 #include "content/browser/indexed_db/indexed_db_database.h"
-#include <math.h>
 
+#include <math.h>
 #include <algorithm>
 #include <limits>
 #include <set>
@@ -35,6 +35,7 @@
 #include "content/browser/indexed_db/indexed_db_value.h"
 #include "content/browser/indexed_db/scopes/scope_lock.h"
 #include "content/browser/indexed_db/scopes/scopes_lock_manager.h"
+#include "content/browser/indexed_db/transaction_impl.h"
 #include "content/public/common/content_switches.h"
 #include "ipc/ipc_channel.h"
 #include "storage/browser/blob/blob_data_handle.h"
@@ -269,6 +270,11 @@
         std::set<int64_t>(object_store_ids.begin(), object_store_ids.end()),
         blink::mojom::IDBTransactionMode::VersionChange,
         new IndexedDBBackingStore::Transaction(db_->backing_store()));
+
+    // Create transaction binding.
+    std::move(pending_->create_transaction_callback)
+        .Run(transaction->AsWeakPtr());
+
     transaction->ScheduleTask(
         base::BindOnce(&IndexedDBDatabase::VersionChangeOperation, db_,
                        pending_->version, pending_->callbacks));
diff --git a/content/browser/indexed_db/indexed_db_database_unittest.cc b/content/browser/indexed_db/indexed_db_database_unittest.cc
index 7afd674..af626a4 100644
--- a/content/browser/indexed_db/indexed_db_database_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_database_unittest.cc
@@ -45,6 +45,12 @@
 }
 
 namespace content {
+namespace {
+
+void CreateAndBindTransactionPlaceholder(
+    base::WeakPtr<IndexedDBTransaction> transaction) {}
+
+}  // namespace
 
 class IndexedDBDatabaseTest : public ::testing::Test {
  public:
@@ -89,10 +95,13 @@
   scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks1(
       new MockIndexedDBDatabaseCallbacks());
   const int64_t transaction_id1 = 1;
+  auto create_transaction_callback1 =
+      base::BindOnce(&CreateAndBindTransactionPlaceholder);
   std::unique_ptr<IndexedDBPendingConnection> connection1(
       std::make_unique<IndexedDBPendingConnection>(
           request1, callbacks1, kFakeChildProcessId, transaction_id1,
-          IndexedDBDatabaseMetadata::DEFAULT_VERSION));
+          IndexedDBDatabaseMetadata::DEFAULT_VERSION,
+          std::move(create_transaction_callback1)));
   db_->OpenConnection(std::move(connection1));
 
   EXPECT_FALSE(backing_store_->HasOneRef());  // db, connection count > 0
@@ -101,10 +110,13 @@
   scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks2(
       new MockIndexedDBDatabaseCallbacks());
   const int64_t transaction_id2 = 2;
+  auto create_transaction_callback2 =
+      base::BindOnce(&CreateAndBindTransactionPlaceholder);
   std::unique_ptr<IndexedDBPendingConnection> connection2(
       std::make_unique<IndexedDBPendingConnection>(
           request2, callbacks2, kFakeChildProcessId, transaction_id2,
-          IndexedDBDatabaseMetadata::DEFAULT_VERSION));
+          IndexedDBDatabaseMetadata::DEFAULT_VERSION,
+          std::move(create_transaction_callback2)));
   db_->OpenConnection(std::move(connection2));
 
   EXPECT_FALSE(backing_store_->HasOneRef());  // local and connection
@@ -128,10 +140,13 @@
       new MockIndexedDBDatabaseCallbacks());
   scoped_refptr<MockIndexedDBCallbacks> request(new MockIndexedDBCallbacks());
   const int64_t upgrade_transaction_id = 3;
+  auto create_transaction_callback =
+      base::BindOnce(&CreateAndBindTransactionPlaceholder);
   std::unique_ptr<IndexedDBPendingConnection> connection(
       std::make_unique<IndexedDBPendingConnection>(
           request, callbacks, kFakeChildProcessId, upgrade_transaction_id,
-          IndexedDBDatabaseMetadata::DEFAULT_VERSION));
+          IndexedDBDatabaseMetadata::DEFAULT_VERSION,
+          std::move(create_transaction_callback)));
   db_->OpenConnection(std::move(connection));
   EXPECT_EQ(db_.get(), request->connection()->database());
 
@@ -182,10 +197,13 @@
   scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks1(
       new MockIndexedDBDatabaseCallbacks());
   const int64_t transaction_id1 = 1;
+  auto create_transaction_callback1 =
+      base::BindOnce(&CreateAndBindTransactionPlaceholder);
   std::unique_ptr<IndexedDBPendingConnection> connection(
       std::make_unique<IndexedDBPendingConnection>(
           request1, callbacks1, kFakeChildProcessId, transaction_id1,
-          IndexedDBDatabaseMetadata::DEFAULT_VERSION));
+          IndexedDBDatabaseMetadata::DEFAULT_VERSION,
+          std::move(create_transaction_callback1)));
   db_->OpenConnection(std::move(connection));
 
   EXPECT_EQ(db_->ConnectionCount(), 1UL);
@@ -226,10 +244,12 @@
   scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks1(
       new MockIndexedDBDatabaseCallbacks());
   const int64_t transaction_id1 = 1;
+  auto create_transaction_callback1 =
+      base::BindOnce(&CreateAndBindTransactionPlaceholder);
   std::unique_ptr<IndexedDBPendingConnection> connection1(
       std::make_unique<IndexedDBPendingConnection>(
           request1, callbacks1, kFakeChildProcessId, transaction_id1,
-          kDatabaseVersion));
+          kDatabaseVersion, std::move(create_transaction_callback1)));
   db_->OpenConnection(std::move(connection1));
 
   EXPECT_EQ(db_->ConnectionCount(), 1UL);
@@ -242,10 +262,12 @@
   scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks2(
       new MockIndexedDBDatabaseCallbacks());
   const int64_t transaction_id2 = 2;
+  auto create_transaction_callback2 =
+      base::BindOnce(&CreateAndBindTransactionPlaceholder);
   std::unique_ptr<IndexedDBPendingConnection> connection2(
       std::make_unique<IndexedDBPendingConnection>(
           request2, callbacks2, kFakeChildProcessId, transaction_id2,
-          kDatabaseVersion));
+          kDatabaseVersion, std::move(create_transaction_callback2)));
   db_->OpenConnection(std::move(connection2));
 
   EXPECT_EQ(db_->ConnectionCount(), 1UL);
@@ -258,10 +280,12 @@
   scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks3(
       new MockIndexedDBDatabaseCallbacks());
   const int64_t transaction_id3 = 3;
+  auto create_transaction_callback3 =
+      base::BindOnce(&CreateAndBindTransactionPlaceholder);
   std::unique_ptr<IndexedDBPendingConnection> connection3(
       std::make_unique<IndexedDBPendingConnection>(
           request3, callbacks3, kFakeChildProcessId, transaction_id3,
-          kDatabaseVersion));
+          kDatabaseVersion, std::move(create_transaction_callback3)));
   db_->OpenConnection(std::move(connection3));
 
   // This causes the active request to call OnUpgradeNeeded on its callbacks.
@@ -292,10 +316,13 @@
   scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks1(
       new MockIndexedDBDatabaseCallbacks());
   const int64_t transaction_id1 = 1;
+  auto create_transaction_callback1 =
+      base::BindOnce(&CreateAndBindTransactionPlaceholder);
   std::unique_ptr<IndexedDBPendingConnection> connection(
       std::make_unique<IndexedDBPendingConnection>(
           request1, callbacks1, kFakeChildProcessId, transaction_id1,
-          IndexedDBDatabaseMetadata::DEFAULT_VERSION));
+          IndexedDBDatabaseMetadata::DEFAULT_VERSION,
+          std::move(create_transaction_callback1)));
   db_->OpenConnection(std::move(connection));
 
   EXPECT_EQ(db_->ConnectionCount(), 1UL);
@@ -322,10 +349,13 @@
   scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks1(
       new MockIndexedDBDatabaseCallbacks());
   const int64_t transaction_id1 = 1;
+  auto create_transaction_callback1 =
+      base::BindOnce(&CreateAndBindTransactionPlaceholder);
   std::unique_ptr<IndexedDBPendingConnection> connection(
       std::make_unique<IndexedDBPendingConnection>(
           request1, callbacks1, kFakeChildProcessId, transaction_id1,
-          IndexedDBDatabaseMetadata::DEFAULT_VERSION));
+          IndexedDBDatabaseMetadata::DEFAULT_VERSION,
+          std::move(create_transaction_callback1)));
   db_->OpenConnection(std::move(connection));
 
   EXPECT_EQ(db_->ConnectionCount(), 1UL);
@@ -338,9 +368,12 @@
   scoped_refptr<MockIndexedDBDatabaseCallbacks> callbacks2(
       new MockIndexedDBDatabaseCallbacks());
   const int64_t transaction_id2 = 2;
+  auto create_transaction_callback2 =
+      base::BindOnce(&CreateAndBindTransactionPlaceholder);
   std::unique_ptr<IndexedDBPendingConnection> connection2(
       std::make_unique<IndexedDBPendingConnection>(
-          request1, callbacks1, kFakeChildProcessId, transaction_id2, 3));
+          request1, callbacks1, kFakeChildProcessId, transaction_id2, 3,
+          std::move(create_transaction_callback2)));
   db_->OpenConnection(std::move(connection2));
 
   EXPECT_EQ(db_->ConnectionCount(), 1UL);
@@ -380,10 +413,13 @@
     request_ = new MockIndexedDBCallbacks();
     callbacks_ = new MockIndexedDBDatabaseCallbacks();
     const int64_t transaction_id = 1;
+    auto create_transaction_callback1 =
+        base::BindOnce(&CreateAndBindTransactionPlaceholder);
     std::unique_ptr<IndexedDBPendingConnection> connection(
         std::make_unique<IndexedDBPendingConnection>(
             request_, callbacks_, kFakeChildProcessId, transaction_id,
-            IndexedDBDatabaseMetadata::DEFAULT_VERSION));
+            IndexedDBDatabaseMetadata::DEFAULT_VERSION,
+            std::move(create_transaction_callback1)));
     db_->OpenConnection(std::move(connection));
     EXPECT_EQ(IndexedDBDatabaseMetadata::NO_VERSION, db_->metadata().version);
 
diff --git a/content/browser/indexed_db/indexed_db_dispatcher_host.cc b/content/browser/indexed_db/indexed_db_dispatcher_host.cc
index 2667a9e..ec7304f 100644
--- a/content/browser/indexed_db/indexed_db_dispatcher_host.cc
+++ b/content/browser/indexed_db/indexed_db_dispatcher_host.cc
@@ -19,6 +19,7 @@
 #include "content/browser/indexed_db/indexed_db_context_impl.h"
 #include "content/browser/indexed_db/indexed_db_database_callbacks.h"
 #include "content/browser/indexed_db/indexed_db_pending_connection.h"
+#include "content/browser/indexed_db/transaction_impl.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "storage/browser/blob/blob_storage_context.h"
@@ -108,6 +109,13 @@
   cursor_bindings_.RemoveBinding(binding_id);
 }
 
+void IndexedDBDispatcherHost::AddTransactionBinding(
+    std::unique_ptr<blink::mojom::IDBTransaction> transaction,
+    blink::mojom::IDBTransactionAssociatedRequest request) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  transaction_bindings_.AddBinding(std::move(transaction), std::move(request));
+}
+
 void IndexedDBDispatcherHost::RenderProcessExited(
     RenderProcessHost* host,
     const ChildProcessTerminationInfo& info) {
@@ -153,6 +161,7 @@
     blink::mojom::IDBDatabaseCallbacksAssociatedPtrInfo database_callbacks_info,
     const base::string16& name,
     int64_t version,
+    blink::mojom::IDBTransactionAssociatedRequest transaction_request,
     int64_t transaction_id) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
@@ -166,12 +175,15 @@
                                      IDBTaskRunner()));
   base::FilePath indexed_db_path = indexed_db_context_->data_path();
 
-  // TODO(dgrogan): Don't let a non-existing database be opened (and therefore
-  // created) if this origin is already over quota.
+  auto create_transaction_callback = base::BindOnce(
+      &IndexedDBDispatcherHost::CreateAndBindTransactionImpl, AsWeakPtr(),
+      std::move(transaction_request), context.origin);
   std::unique_ptr<IndexedDBPendingConnection> connection =
       std::make_unique<IndexedDBPendingConnection>(
           std::move(callbacks), std::move(database_callbacks), ipc_process_id_,
-          transaction_id, version);
+          transaction_id, version, std::move(create_transaction_callback));
+  // TODO(dgrogan): Don't let a non-existing database be opened (and therefore
+  // created) if this origin is already over quota.
   indexed_db_context_->GetIDBFactory()->Open(name, std::move(connection),
                                              context.origin, indexed_db_path);
 }
@@ -213,11 +225,23 @@
       std::move(callback_on_io), context.origin);
 }
 
+void IndexedDBDispatcherHost::CreateAndBindTransactionImpl(
+    blink::mojom::IDBTransactionAssociatedRequest transaction_request,
+    const url::Origin& origin,
+    base::WeakPtr<IndexedDBTransaction> transaction) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  auto transaction_impl = std::make_unique<TransactionImpl>(
+      transaction, origin, this->AsWeakPtr(), IDBTaskRunner());
+  AddTransactionBinding(std::move(transaction_impl),
+                        std::move(transaction_request));
+}
+
 void IndexedDBDispatcherHost::InvalidateWeakPtrsAndClearBindings() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   weak_factory_.InvalidateWeakPtrs();
   cursor_bindings_.CloseAllBindings();
   database_bindings_.CloseAllBindings();
+  transaction_bindings_.CloseAllBindings();
 }
 
 base::SequencedTaskRunner* IndexedDBDispatcherHost::IDBTaskRunner() const {
diff --git a/content/browser/indexed_db/indexed_db_dispatcher_host.h b/content/browser/indexed_db/indexed_db_dispatcher_host.h
index 6f166ba3..b12fbea 100644
--- a/content/browser/indexed_db/indexed_db_dispatcher_host.h
+++ b/content/browser/indexed_db/indexed_db_dispatcher_host.h
@@ -35,6 +35,7 @@
 namespace content {
 class CursorImpl;
 class IndexedDBContextImpl;
+class IndexedDBTransaction;
 
 // Constructed on UI thread.  All remaining calls (including destruction) should
 // happen on the IDB sequenced task runner.
@@ -58,6 +59,10 @@
                         blink::mojom::IDBCursorAssociatedRequest request);
   void RemoveCursorBinding(mojo::BindingId binding_id);
 
+  void AddTransactionBinding(
+      std::unique_ptr<blink::mojom::IDBTransaction> transaction,
+      blink::mojom::IDBTransactionAssociatedRequest request);
+
   // A shortcut for accessing our context.
   IndexedDBContextImpl* context() const { return indexed_db_context_.get(); }
   scoped_refptr<ChromeBlobStorageContext> blob_storage_context() const {
@@ -70,6 +75,11 @@
     return weak_factory_.GetWeakPtr();
   }
 
+  void CreateAndBindTransactionImpl(
+      blink::mojom::IDBTransactionAssociatedRequest transaction_request,
+      const url::Origin& origin,
+      base::WeakPtr<IndexedDBTransaction> transaction);
+
   // Called by UI thread. Used to kill outstanding bindings and weak pointers
   // in callbacks.
   void RenderProcessExited(RenderProcessHost* host,
@@ -94,6 +104,7 @@
                 database_callbacks_info,
             const base::string16& name,
             int64_t version,
+            blink::mojom::IDBTransactionAssociatedRequest transaction_request,
             int64_t transaction_id) override;
   void DeleteDatabase(
       blink::mojom::IDBCallbacksAssociatedPtrInfo callbacks_info,
@@ -120,11 +131,11 @@
   };
 
   mojo::BindingSet<blink::mojom::IDBFactory, BindingState> bindings_;
-
   mojo::StrongAssociatedBindingSet<blink::mojom::IDBDatabase>
       database_bindings_;
-
   mojo::StrongAssociatedBindingSet<blink::mojom::IDBCursor> cursor_bindings_;
+  mojo::StrongAssociatedBindingSet<blink::mojom::IDBTransaction>
+      transaction_bindings_;
 
   SEQUENCE_CHECKER(sequence_checker_);
 
diff --git a/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc b/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc
index d46c542c..a6c7e3a 100644
--- a/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc
@@ -39,8 +39,6 @@
 #include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_exception.h"
 #include "url/origin.h"
 
-using blink::mojom::IDBValue;
-using blink::mojom::IDBValuePtr;
 using blink::IndexedDBDatabaseMetadata;
 using blink::IndexedDBIndexKeys;
 using blink::IndexedDBKey;
@@ -53,6 +51,8 @@
 using blink::mojom::IDBDatabaseCallbacksAssociatedPtrInfo;
 using blink::mojom::IDBFactory;
 using blink::mojom::IDBFactoryPtr;
+using blink::mojom::IDBValue;
+using blink::mojom::IDBValuePtr;
 using mojo::StrongAssociatedBindingPtr;
 using testing::_;
 using testing::StrictMock;
@@ -101,11 +101,13 @@
 struct TestDatabaseConnection {
   TestDatabaseConnection() = default;
 
-  TestDatabaseConnection(url::Origin origin,
+  TestDatabaseConnection(scoped_refptr<base::SequencedTaskRunner> task_runner,
+                         url::Origin origin,
                          base::string16 db_name,
                          int64_t version,
                          int64_t upgrade_txn_id)
-      : origin(std::move(origin)),
+      : task_runner(std::move(task_runner)),
+        origin(std::move(origin)),
         db_name(std::move(db_name)),
         version(version),
         upgrade_txn_id(upgrade_txn_id),
@@ -119,15 +121,19 @@
   void Open(IDBFactory* factory) {
     factory->Open(open_callbacks->CreateInterfacePtrAndBind(),
                   connection_callbacks->CreateInterfacePtrAndBind(), db_name,
-                  version, upgrade_txn_id);
+                  version,
+                  mojo::MakeRequest(&version_change_transaction, task_runner),
+                  upgrade_txn_id);
   }
 
+  scoped_refptr<base::SequencedTaskRunner> task_runner;
   url::Origin origin;
   base::string16 db_name;
   int64_t version;
   int64_t upgrade_txn_id;
 
   IDBDatabaseAssociatedPtr database;
+  blink::mojom::IDBTransactionAssociatedPtr version_change_transaction;
 
   std::unique_ptr<MockMojoIndexedDBCallbacks> open_callbacks;
   std::unique_ptr<MockMojoIndexedDBDatabaseCallbacks> connection_callbacks;
@@ -236,7 +242,7 @@
   context_impl_->TaskRunner()->PostTask(
       FROM_HERE, base::BindLambdaForTesting([&]() {
         connection = std::make_unique<TestDatabaseConnection>(
-            url::Origin::Create(GURL(kOrigin)),
+            context_impl_->TaskRunner(), url::Origin::Create(GURL(kOrigin)),
             base::UTF8ToUTF16(kDatabaseName), kDBVersion, kTransactionId);
         EXPECT_CALL(*connection->open_callbacks,
                     MockedUpgradeNeeded(IsAssociatedInterfacePtrInfoValid(true),
@@ -278,8 +284,8 @@
       FROM_HERE, base::BindLambdaForTesting([&]() {
         // Open connection.
         connection = std::make_unique<TestDatabaseConnection>(
-            ToOrigin(kOrigin), base::UTF8ToUTF16(kDatabaseName), kDBVersion,
-            kTransactionId);
+            context_impl_->TaskRunner(), ToOrigin(kOrigin),
+            base::UTF8ToUTF16(kDatabaseName), kDBVersion, kTransactionId);
 
         EXPECT_CALL(*connection->open_callbacks,
                     MockedUpgradeNeeded(IsAssociatedInterfacePtrInfoValid(true),
@@ -316,8 +322,9 @@
 
         connection->database.Bind(std::move(database_info));
         ASSERT_TRUE(connection->database.is_bound());
-        connection->database->CreateObjectStore(
-            kTransactionId, kObjectStoreId, base::UTF8ToUTF16(kObjectStoreName),
+        ASSERT_TRUE(connection->version_change_transaction.is_bound());
+        connection->version_change_transaction->CreateObjectStore(
+            kObjectStoreId, base::UTF8ToUTF16(kObjectStoreName),
             blink::IndexedDBKeyPath(), false);
         connection->database->Commit(kTransactionId, 0);
       }));
@@ -346,7 +353,7 @@
       FROM_HERE, base::BindLambdaForTesting([&]() {
         // Open connection 1, and expect the upgrade needed.
         connection1 = std::make_unique<TestDatabaseConnection>(
-            url::Origin::Create(GURL(kOrigin)),
+            context_impl_->TaskRunner(), url::Origin::Create(GURL(kOrigin)),
             base::UTF8ToUTF16(kDatabaseName), kDBVersion, kTransactionId);
 
         EXPECT_CALL(*connection1->open_callbacks,
@@ -371,7 +378,8 @@
   context_impl_->TaskRunner()->PostTask(
       FROM_HERE, base::BindLambdaForTesting([&]() {
         connection2 = std::make_unique<TestDatabaseConnection>(
-            ToOrigin(kOrigin), base::UTF8ToUTF16(kDatabaseName), kDBVersion, 0);
+            context_impl_->TaskRunner(), ToOrigin(kOrigin),
+            base::UTF8ToUTF16(kDatabaseName), kDBVersion, 0);
 
         // Check that we're called in order and the second connection gets it's
         // database after the first connection completes.
@@ -396,13 +404,14 @@
 
         connection1->database.Bind(std::move(database_info1));
         ASSERT_TRUE(connection1->database.is_bound());
+        ASSERT_TRUE(connection1->version_change_transaction.is_bound());
 
         // Open connection 2, but expect that we won't be called back.
         connection2->Open(idb_mojo_factory_.get());
 
         // Create object store.
-        connection1->database->CreateObjectStore(
-            kTransactionId, kObjectStoreId, base::UTF8ToUTF16(kObjectStoreName),
+        connection1->version_change_transaction->CreateObjectStore(
+            kObjectStoreId, base::UTF8ToUTF16(kObjectStoreName),
             blink::IndexedDBKeyPath(), false);
         connection1->database->Commit(kTransactionId, 0);
       }));
@@ -436,7 +445,7 @@
       FROM_HERE, base::BindLambdaForTesting([&]() {
         // Open connection.
         connection = std::make_unique<TestDatabaseConnection>(
-            url::Origin::Create(GURL(kOrigin)),
+            context_impl_->TaskRunner(), url::Origin::Create(GURL(kOrigin)),
             base::UTF8ToUTF16(kDatabaseName), kDBVersion, kTransactionId);
 
         EXPECT_CALL(*connection->open_callbacks,
@@ -487,8 +496,9 @@
 
         connection->database.Bind(std::move(database_info));
         ASSERT_TRUE(connection->database.is_bound());
-        connection->database->CreateObjectStore(
-            kTransactionId, kObjectStoreId, base::UTF8ToUTF16(kObjectStoreName),
+        ASSERT_TRUE(connection->version_change_transaction.is_bound());
+        connection->version_change_transaction->CreateObjectStore(
+            kObjectStoreId, base::UTF8ToUTF16(kObjectStoreName),
             blink::IndexedDBKeyPath(), false);
         // Call Put with an invalid blob.
         std::vector<blink::mojom::IDBBlobInfoPtr> blobs;
@@ -539,8 +549,8 @@
       FROM_HERE, base::BindLambdaForTesting([&]() {
         // Open connection.
         connection = std::make_unique<TestDatabaseConnection>(
-            ToOrigin(kOrigin), base::UTF8ToUTF16(kDatabaseName), kDBVersion,
-            kTransactionId);
+            context_impl_->TaskRunner(), ToOrigin(kOrigin),
+            base::UTF8ToUTF16(kDatabaseName), kDBVersion, kTransactionId);
         EXPECT_CALL(*connection->open_callbacks,
                     MockedUpgradeNeeded(IsAssociatedInterfacePtrInfoValid(true),
                                         IndexedDBDatabaseMetadata::NO_VERSION,
@@ -578,6 +588,8 @@
             .WillOnce(RunClosure(quit_closure));
 
         connection->database.Bind(std::move(database_info));
+        ASSERT_TRUE(connection->database.is_bound());
+        ASSERT_TRUE(connection->version_change_transaction.is_bound());
 
         connection->database->Commit(kTransactionId, 0);
         idb_mojo_factory_->AbortTransactionsAndCompactDatabase(base::BindOnce(
@@ -609,8 +621,8 @@
       FROM_HERE, base::BindLambdaForTesting([&]() {
         // Open connection.
         connection = std::make_unique<TestDatabaseConnection>(
-            ToOrigin(kOrigin), base::UTF8ToUTF16(kDatabaseName), kDBVersion,
-            kTransactionId);
+            context_impl_->TaskRunner(), ToOrigin(kOrigin),
+            base::UTF8ToUTF16(kDatabaseName), kDBVersion, kTransactionId);
         EXPECT_CALL(*connection->open_callbacks,
                     MockedUpgradeNeeded(IsAssociatedInterfacePtrInfoValid(true),
                                         IndexedDBDatabaseMetadata::NO_VERSION,
@@ -653,8 +665,9 @@
 
         connection->database.Bind(std::move(database_info));
         ASSERT_TRUE(connection->database.is_bound());
-        connection->database->CreateObjectStore(
-            kTransactionId, kObjectStoreId, base::UTF8ToUTF16(kObjectStoreName),
+        ASSERT_TRUE(connection->version_change_transaction.is_bound());
+        connection->version_change_transaction->CreateObjectStore(
+            kObjectStoreId, base::UTF8ToUTF16(kObjectStoreName),
             blink::IndexedDBKeyPath(), false);
         idb_mojo_factory_->AbortTransactionsAndCompactDatabase(base::BindOnce(
             &StatusCallback, std::move(quit_closure), &callback_result));
@@ -684,8 +697,8 @@
       FROM_HERE, base::BindLambdaForTesting([&]() {
         // Open connection.
         connection = std::make_unique<TestDatabaseConnection>(
-            ToOrigin(kOrigin), base::UTF8ToUTF16(kDatabaseName), kDBVersion,
-            kTransactionId);
+            context_impl_->TaskRunner(), ToOrigin(kOrigin),
+            base::UTF8ToUTF16(kDatabaseName), kDBVersion, kTransactionId);
         EXPECT_CALL(*connection->open_callbacks,
                     MockedUpgradeNeeded(IsAssociatedInterfacePtrInfoValid(true),
                                         IndexedDBDatabaseMetadata::NO_VERSION,
@@ -728,6 +741,7 @@
 
         connection->database.Bind(std::move(database_info));
         ASSERT_TRUE(connection->database.is_bound());
+        ASSERT_TRUE(connection->version_change_transaction.is_bound());
         idb_mojo_factory_->AbortTransactionsAndCompactDatabase(base::BindOnce(
             &StatusCallback, std::move(quit_closure), &callback_result));
       }));
@@ -757,8 +771,8 @@
       FROM_HERE, base::BindLambdaForTesting([&]() {
         // Open connection.
         connection = std::make_unique<TestDatabaseConnection>(
-            ToOrigin(kOrigin), base::UTF8ToUTF16(kDatabaseName), kDBVersion,
-            kTransactionId);
+            context_impl_->TaskRunner(), ToOrigin(kOrigin),
+            base::UTF8ToUTF16(kDatabaseName), kDBVersion, kTransactionId);
         {
           EXPECT_CALL(*connection->open_callbacks,
                       MockedUpgradeNeeded(
@@ -801,6 +815,7 @@
 
         connection->database.Bind(std::move(database_info));
         ASSERT_TRUE(connection->database.is_bound());
+        ASSERT_TRUE(connection->version_change_transaction.is_bound());
         connection->database->Commit(kTransactionId, 0);
         idb_mojo_factory_->AbortTransactionsForDatabase(base::BindOnce(
             &StatusCallback, std::move(quit_closure), &callback_result));
@@ -832,8 +847,8 @@
       FROM_HERE, base::BindLambdaForTesting([&]() {
         // Open connection.
         connection = std::make_unique<TestDatabaseConnection>(
-            ToOrigin(kOrigin), base::UTF8ToUTF16(kDatabaseName), kDBVersion,
-            kTransactionId);
+            context_impl_->TaskRunner(), ToOrigin(kOrigin),
+            base::UTF8ToUTF16(kDatabaseName), kDBVersion, kTransactionId);
 
         EXPECT_CALL(*connection->open_callbacks,
                     MockedUpgradeNeeded(IsAssociatedInterfacePtrInfoValid(true),
@@ -877,8 +892,9 @@
 
         connection->database.Bind(std::move(database_info));
         ASSERT_TRUE(connection->database.is_bound());
-        connection->database->CreateObjectStore(
-            kTransactionId, kObjectStoreId, base::UTF8ToUTF16(kObjectStoreName),
+        ASSERT_TRUE(connection->version_change_transaction.is_bound());
+        connection->version_change_transaction->CreateObjectStore(
+            kObjectStoreId, base::UTF8ToUTF16(kObjectStoreName),
             blink::IndexedDBKeyPath(), false);
         idb_mojo_factory_->AbortTransactionsForDatabase(base::BindOnce(
             &StatusCallback, std::move(quit_closure), &callback_result));
@@ -908,8 +924,8 @@
       FROM_HERE, base::BindLambdaForTesting([&]() {
         // Open connection.
         connection = std::make_unique<TestDatabaseConnection>(
-            ToOrigin(kOrigin), base::UTF8ToUTF16(kDatabaseName), kDBVersion,
-            kTransactionId);
+            context_impl_->TaskRunner(), ToOrigin(kOrigin),
+            base::UTF8ToUTF16(kDatabaseName), kDBVersion, kTransactionId);
 
         EXPECT_CALL(*connection->open_callbacks,
                     MockedUpgradeNeeded(IsAssociatedInterfacePtrInfoValid(true),
@@ -953,6 +969,7 @@
 
         connection->database.Bind(std::move(database_info));
         ASSERT_TRUE(connection->database.is_bound());
+        ASSERT_TRUE(connection->version_change_transaction.is_bound());
         idb_mojo_factory_->AbortTransactionsForDatabase(base::BindOnce(
             &StatusCallback, std::move(quit_closure), &callback_result));
       }));
@@ -986,9 +1003,9 @@
   context_impl_->AddObserver(&observer);
 
   // Open connection 1.
-  TestDatabaseConnection connection1(ToOrigin(kOrigin),
-                                     base::UTF8ToUTF16(kDatabaseName),
-                                     kDBVersion1, kTransactionId1);
+  TestDatabaseConnection connection1(
+      context_impl_->TaskRunner(), ToOrigin(kOrigin),
+      base::UTF8ToUTF16(kDatabaseName), kDBVersion1, kTransactionId1);
   IndexedDBDatabaseMetadata metadata1;
   IDBDatabaseAssociatedPtrInfo database_info1;
   EXPECT_EQ(0, observer.notify_list_changed_count);
@@ -1013,6 +1030,8 @@
 
   // Create object store and index.
   connection1.database.Bind(std::move(database_info1));
+  ASSERT_TRUE(connection1.database.is_bound());
+  ASSERT_TRUE(connection1.version_change_transaction.is_bound());
   {
     ::testing::InSequence dummy;
     base::RunLoop loop;
@@ -1028,9 +1047,9 @@
         .WillOnce(RunClosure(std::move(quit_closure)));
 
     ASSERT_TRUE(connection1.database.is_bound());
-    connection1.database->CreateObjectStore(kTransactionId1, kObjectStoreId,
-                                            base::UTF8ToUTF16(kObjectStoreName),
-                                            blink::IndexedDBKeyPath(), false);
+    connection1.version_change_transaction->CreateObjectStore(
+        kObjectStoreId, base::UTF8ToUTF16(kObjectStoreName),
+        blink::IndexedDBKeyPath(), false);
     connection1.database->CreateIndex(kTransactionId1, kObjectStoreId, kIndexId,
                                       base::UTF8ToUTF16(kIndexName),
                                       blink::IndexedDBKeyPath(), false, false);
@@ -1041,9 +1060,9 @@
   connection1.database->Close();
 
   // Open connection 2.
-  TestDatabaseConnection connection2(url::Origin::Create(GURL(kOrigin)),
-                                     base::UTF8ToUTF16(kDatabaseName),
-                                     kDBVersion2, kTransactionId2);
+  TestDatabaseConnection connection2(
+      context_impl_->TaskRunner(), url::Origin::Create(GURL(kOrigin)),
+      base::UTF8ToUTF16(kDatabaseName), kDBVersion2, kTransactionId2);
   IndexedDBDatabaseMetadata metadata2;
   IDBDatabaseAssociatedPtrInfo database_info2;
   {
@@ -1067,6 +1086,8 @@
 
   // Delete index.
   connection2.database.Bind(std::move(database_info2));
+  ASSERT_TRUE(connection2.database.is_bound());
+  ASSERT_TRUE(connection2.version_change_transaction.is_bound());
   {
     ::testing::InSequence dummy;
     base::RunLoop loop;
@@ -1091,9 +1112,9 @@
   connection2.database->Close();
 
   // Open connection 3.
-  TestDatabaseConnection connection3(ToOrigin(kOrigin),
-                                     base::UTF8ToUTF16(kDatabaseName),
-                                     kDBVersion3, kTransactionId3);
+  TestDatabaseConnection connection3(
+      context_impl_->TaskRunner(), ToOrigin(kOrigin),
+      base::UTF8ToUTF16(kDatabaseName), kDBVersion3, kTransactionId3);
   IndexedDBDatabaseMetadata metadata3;
   IDBDatabaseAssociatedPtrInfo database_info3;
   {
@@ -1117,6 +1138,8 @@
 
   // Delete object store.
   connection3.database.Bind(std::move(database_info3));
+  ASSERT_TRUE(connection3.database.is_bound());
+  ASSERT_TRUE(connection3.version_change_transaction.is_bound());
   {
     ::testing::InSequence dummy;
     base::RunLoop loop;
@@ -1163,7 +1186,7 @@
       FROM_HERE, base::BindLambdaForTesting([&]() {
         // Open connection 1.
         connection1 = std::make_unique<TestDatabaseConnection>(
-            url::Origin::Create(GURL(kOrigin)),
+            context_impl_->TaskRunner(), url::Origin::Create(GURL(kOrigin)),
             base::UTF8ToUTF16(kDatabaseName), kDBVersion1, kTransactionId1);
 
         EXPECT_CALL(*connection1->open_callbacks,
@@ -1212,10 +1235,10 @@
 
         connection1->database.Bind(std::move(database_info1));
         ASSERT_TRUE(connection1->database.is_bound());
-        connection1->database->CreateObjectStore(
-            kTransactionId1, kObjectStoreId,
-            base::UTF8ToUTF16(kObjectStoreName), blink::IndexedDBKeyPath(),
-            false);
+        ASSERT_TRUE(connection1->version_change_transaction.is_bound());
+        connection1->version_change_transaction->CreateObjectStore(
+            kObjectStoreId, base::UTF8ToUTF16(kObjectStoreName),
+            blink::IndexedDBKeyPath(), false);
 
         std::string value = "value";
         const char* value_data = value.data();
@@ -1261,8 +1284,8 @@
         ::testing::InSequence dummy;
 
         connection2 = std::make_unique<TestDatabaseConnection>(
-            ToOrigin(kOrigin), base::UTF8ToUTF16(kDatabaseName), kDBVersion2,
-            kTransactionId2);
+            context_impl_->TaskRunner(), ToOrigin(kOrigin),
+            base::UTF8ToUTF16(kDatabaseName), kDBVersion2, kTransactionId2);
 
         EXPECT_CALL(*connection2->open_callbacks,
                     MockedUpgradeNeeded(
@@ -1309,6 +1332,7 @@
 
         connection2->database.Bind(std::move(database_info2));
         ASSERT_TRUE(connection2->database.is_bound());
+        ASSERT_TRUE(connection2->version_change_transaction.is_bound());
         connection2->database->Clear(
             kTransactionId2, kObjectStoreId,
             clear_callbacks->CreateInterfacePtrAndBind());
diff --git a/content/browser/indexed_db/indexed_db_factory_unittest.cc b/content/browser/indexed_db/indexed_db_factory_unittest.cc
index 7f6b1e9..c99cd53 100644
--- a/content/browser/indexed_db/indexed_db_factory_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_factory_unittest.cc
@@ -42,6 +42,14 @@
 
 namespace {
 
+base::FilePath CreateAndReturnTempDir(base::ScopedTempDir* temp_dir) {
+  CHECK(temp_dir->CreateUniqueTempDir());
+  return temp_dir->GetPath();
+}
+
+void CreateAndBindTransactionPlaceholder(
+    base::WeakPtr<IndexedDBTransaction> transaction) {}
+
 class MockIDBFactory : public IndexedDBFactoryImpl {
  public:
   explicit MockIDBFactory(IndexedDBContextImpl* context)
@@ -84,14 +92,12 @@
  public:
   IndexedDBFactoryTest()
       : quota_manager_proxy_(
-            base::MakeRefCounted<MockQuotaManagerProxy>(nullptr, nullptr)) {}
-
-  void SetUp() override {
-    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-    context_ = base::MakeRefCounted<IndexedDBContextImpl>(
-        temp_dir_.GetPath(), /*special_storage_policy=*/nullptr,
-        quota_manager_proxy_.get(), indexed_db::GetDefaultLevelDBFactory());
-  }
+            base::MakeRefCounted<MockQuotaManagerProxy>(nullptr, nullptr)),
+        context_(base::MakeRefCounted<IndexedDBContextImpl>(
+            CreateAndReturnTempDir(&temp_dir_),
+            /*special_storage_policy=*/nullptr,
+            quota_manager_proxy_.get(),
+            indexed_db::GetDefaultLevelDBFactory())) {}
 
   void TearDown() override {
     quota_manager_proxy_->SimulateQuotaManagerDestroyed();
@@ -486,9 +492,12 @@
         const Origin origin = Origin::Create(GURL("http://localhost:81"));
         auto factory = base::MakeRefCounted<DiskFullFactory>(context());
         const base::string16 name(ASCIIToUTF16("name"));
+        auto create_transaction_callback =
+            base::BindOnce(&CreateAndBindTransactionPlaceholder);
         auto connection = std::make_unique<IndexedDBPendingConnection>(
             callbacks, dummy_database_callbacks, /*child_process_id=*/0,
-            /*transaction_id=*/2, /*version=*/1);
+            /*transaction_id=*/2, /*version=*/1,
+            std::move(create_transaction_callback));
         factory->Open(name, std::move(connection), origin,
                       context()->data_path());
         EXPECT_TRUE(callbacks->error_called());
@@ -508,9 +517,12 @@
 
         const Origin origin = Origin::Create(GURL("http://localhost:81"));
         const int64_t transaction_id = 1;
+        auto create_transaction_callback =
+            base::BindOnce(&CreateAndBindTransactionPlaceholder);
         auto connection = std::make_unique<IndexedDBPendingConnection>(
             callbacks, db_callbacks, /*child_process_id=*/0, transaction_id,
-            IndexedDBDatabaseMetadata::DEFAULT_VERSION);
+            IndexedDBDatabaseMetadata::DEFAULT_VERSION,
+            std::move(create_transaction_callback));
         factory->Open(ASCIIToUTF16("db"), std::move(connection), origin,
                       context()->data_path());
 
@@ -539,9 +551,12 @@
 
         const Origin origin = Origin::Create(GURL("http://localhost:81"));
         const int64_t transaction_id = 1;
+        auto create_transaction_callback =
+            base::BindOnce(&CreateAndBindTransactionPlaceholder);
         auto connection = std::make_unique<IndexedDBPendingConnection>(
             callbacks, db_callbacks, /*child_process_id=*/0, transaction_id,
-            IndexedDBDatabaseMetadata::DEFAULT_VERSION);
+            IndexedDBDatabaseMetadata::DEFAULT_VERSION,
+            std::move(create_transaction_callback));
         factory->Open(ASCIIToUTF16("db"), std::move(connection), origin,
                       context()->data_path());
 
@@ -638,9 +653,12 @@
 
         const Origin origin = Origin::Create(GURL("http://localhost:81"));
         const int64_t transaction_id = 1;
+        auto create_transaction_callback =
+            base::BindOnce(&CreateAndBindTransactionPlaceholder);
         auto connection = std::make_unique<IndexedDBPendingConnection>(
             callbacks, db_callbacks, /*child_process_id=*/0, transaction_id,
-            IndexedDBDatabaseMetadata::DEFAULT_VERSION);
+            IndexedDBDatabaseMetadata::DEFAULT_VERSION,
+            std::move(create_transaction_callback));
         factory->Open(ASCIIToUTF16("db"), std::move(connection), origin,
                       context()->data_path());
 
@@ -733,11 +751,14 @@
               base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>();
           // Open at version 2.
           const int64_t db_version = 2;
-          factory->Open(db_name,
-                        std::make_unique<IndexedDBPendingConnection>(
-                            upgrade_callbacks, db_callbacks,
-                            /*child_process_id=*/0, transaction_id, db_version),
-                        origin, context()->data_path());
+          auto create_transaction_callback =
+              base::BindOnce(&CreateAndBindTransactionPlaceholder);
+          auto connection = std::make_unique<IndexedDBPendingConnection>(
+              upgrade_callbacks, db_callbacks,
+              /*child_process_id=*/0, transaction_id, db_version,
+              std::move(create_transaction_callback));
+          factory->Open(db_name, std::move(connection), origin,
+                        context()->data_path());
           EXPECT_TRUE(factory->IsDatabaseOpen(origin, db_name));
           loop.Quit();
         }));
@@ -762,9 +783,12 @@
           // retain the database object.
           {
             const int64_t db_version = 1;
+            auto create_transaction_callback =
+                base::BindOnce(&CreateAndBindTransactionPlaceholder);
             auto connection = std::make_unique<IndexedDBPendingConnection>(
                 failed_open_callbacks, db_callbacks2,
-                /*child_process_id=*/0, transaction_id, db_version);
+                /*child_process_id=*/0, transaction_id, db_version,
+                std::move(create_transaction_callback));
             factory->Open(db_name, std::move(connection), origin,
                           context()->data_path());
             EXPECT_TRUE(failed_open_callbacks->saw_error());
@@ -835,10 +859,12 @@
             callbacks = base::MakeRefCounted<DataLossCallbacks>();
             auto db_callbacks =
                 base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>();
-            factory->Open(ASCIIToUTF16("test_db"),
-                          std::make_unique<IndexedDBPendingConnection>(
-                              callbacks, db_callbacks, /*child_process_id=*/0,
-                              transaction_id, /*version=*/1),
+            auto create_transaction_callback =
+                base::BindOnce(&CreateAndBindTransactionPlaceholder);
+            auto connection = std::make_unique<IndexedDBPendingConnection>(
+                callbacks, db_callbacks, /*child_process_id=*/0, transaction_id,
+                /*version=*/1, std::move(create_transaction_callback));
+            factory->Open(ASCIIToUTF16("test_db"), std::move(connection),
                           origin, context()->data_path());
             loop.Quit();
           }));
diff --git a/content/browser/indexed_db/indexed_db_pending_connection.cc b/content/browser/indexed_db/indexed_db_pending_connection.cc
index 0f6dcea..7d2d81a3 100644
--- a/content/browser/indexed_db/indexed_db_pending_connection.cc
+++ b/content/browser/indexed_db/indexed_db_pending_connection.cc
@@ -4,6 +4,8 @@
 
 #include "content/browser/indexed_db/indexed_db_pending_connection.h"
 
+#include <utility>
+
 namespace content {
 
 IndexedDBPendingConnection::IndexedDBPendingConnection(
@@ -11,15 +13,15 @@
     scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
     int child_process_id,
     int64_t transaction_id,
-    int64_t version)
+    int64_t version,
+    base::OnceCallback<void(base::WeakPtr<IndexedDBTransaction>)>
+        create_transaction_callback)
     : callbacks(callbacks),
       database_callbacks(database_callbacks),
       child_process_id(child_process_id),
       transaction_id(transaction_id),
-      version(version) {}
-
-IndexedDBPendingConnection::IndexedDBPendingConnection(
-    const IndexedDBPendingConnection& other) = default;
+      version(version),
+      create_transaction_callback(std::move(create_transaction_callback)) {}
 
 IndexedDBPendingConnection::~IndexedDBPendingConnection() {}
 
diff --git a/content/browser/indexed_db/indexed_db_pending_connection.h b/content/browser/indexed_db/indexed_db_pending_connection.h
index b434ad4..5d1b8946 100644
--- a/content/browser/indexed_db/indexed_db_pending_connection.h
+++ b/content/browser/indexed_db/indexed_db_pending_connection.h
@@ -25,8 +25,9 @@
       scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
       int child_process_id,
       int64_t transaction_id,
-      int64_t version);
-  IndexedDBPendingConnection(const IndexedDBPendingConnection& other);
+      int64_t version,
+      base::OnceCallback<void(base::WeakPtr<IndexedDBTransaction>)>
+          create_transaction_callback);
   ~IndexedDBPendingConnection();
   scoped_refptr<IndexedDBCallbacks> callbacks;
   scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks;
@@ -34,6 +35,8 @@
   int64_t transaction_id;
   int64_t version;
   IndexedDBDataLossInfo data_loss_info;
+  base::OnceCallback<void(base::WeakPtr<IndexedDBTransaction>)>
+      create_transaction_callback;
 };
 
 }  // namespace content
diff --git a/content/browser/indexed_db/indexed_db_unittest.cc b/content/browser/indexed_db/indexed_db_unittest.cc
index 9a81a8d..77b9706 100644
--- a/content/browser/indexed_db/indexed_db_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_unittest.cc
@@ -19,6 +19,7 @@
 #include "content/browser/indexed_db/mock_indexed_db_database_callbacks.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/common/url_constants.h"
+#include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "content/public/test/test_utils.h"
 #include "storage/browser/quota/quota_manager.h"
@@ -34,6 +35,14 @@
 namespace content {
 namespace {
 
+base::FilePath CreateAndReturnTempDir(base::ScopedTempDir* temp_dir) {
+  CHECK(temp_dir->CreateUniqueTempDir());
+  return temp_dir->GetPath();
+}
+
+void CreateAndBindTransactionPlaceholder(
+    base::WeakPtr<IndexedDBTransaction> transaction) {}
+
 class LevelDBLock {
  public:
   LevelDBLock() : env_(nullptr), lock_(nullptr) {}
@@ -75,7 +84,12 @@
         special_storage_policy_(
             base::MakeRefCounted<MockSpecialStoragePolicy>()),
         quota_manager_proxy_(
-            base::MakeRefCounted<MockQuotaManagerProxy>(nullptr, nullptr)) {
+            base::MakeRefCounted<MockQuotaManagerProxy>(nullptr, nullptr)),
+        context_(base::MakeRefCounted<IndexedDBContextImpl>(
+            CreateAndReturnTempDir(&temp_dir_),
+            /*special_storage_policy=*/special_storage_policy_.get(),
+            quota_manager_proxy_.get(),
+            indexed_db::GetDefaultLevelDBFactory())) {
     special_storage_policy_->AddSessionOnly(kSessionOnlyOrigin.GetURL());
   }
   ~IndexedDBTest() override {
@@ -83,28 +97,25 @@
   }
 
  protected:
+  IndexedDBContextImpl* context() const { return context_.get(); }
   scoped_refptr<MockSpecialStoragePolicy> special_storage_policy_;
   scoped_refptr<MockQuotaManagerProxy> quota_manager_proxy_;
 
  private:
   TestBrowserThreadBundle thread_bundle_;
+  TestBrowserContext browser_context_;
+  base::ScopedTempDir temp_dir_;
+  scoped_refptr<IndexedDBContextImpl> context_;
 
   DISALLOW_COPY_AND_ASSIGN(IndexedDBTest);
 };
 
 TEST_F(IndexedDBTest, ClearSessionOnlyDatabases) {
-  base::ScopedTempDir temp_dir;
-  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
-
   base::FilePath normal_path;
   base::FilePath session_only_path;
 
-  auto idb_context = base::MakeRefCounted<IndexedDBContextImpl>(
-      temp_dir.GetPath(), special_storage_policy_.get(),
-      quota_manager_proxy_.get(), indexed_db::GetDefaultLevelDBFactory());
-
-  normal_path = idb_context->GetFilePathForTesting(kNormalOrigin);
-  session_only_path = idb_context->GetFilePathForTesting(kSessionOnlyOrigin);
+  normal_path = context()->GetFilePathForTesting(kNormalOrigin);
+  session_only_path = context()->GetFilePathForTesting(kSessionOnlyOrigin);
   ASSERT_TRUE(base::CreateDirectory(normal_path));
   ASSERT_TRUE(base::CreateDirectory(session_only_path));
   RunAllTasksUntilIdle();
@@ -112,7 +123,7 @@
 
   RunAllTasksUntilIdle();
 
-  idb_context->Shutdown();
+  context()->Shutdown();
 
   RunAllTasksUntilIdle();
 
@@ -121,28 +132,19 @@
 }
 
 TEST_F(IndexedDBTest, SetForceKeepSessionState) {
-  base::ScopedTempDir temp_dir;
-  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
-
   base::FilePath normal_path;
   base::FilePath session_only_path;
 
-  // Create some indexedDB paths.
-  // With the levelDB backend, these are directories.
-  auto idb_context = base::MakeRefCounted<IndexedDBContextImpl>(
-      temp_dir.GetPath(), special_storage_policy_.get(),
-      quota_manager_proxy_.get(), indexed_db::GetDefaultLevelDBFactory());
-
   // Save session state. This should bypass the destruction-time deletion.
-  idb_context->SetForceKeepSessionState();
+  context()->SetForceKeepSessionState();
 
-  normal_path = idb_context->GetFilePathForTesting(kNormalOrigin);
-  session_only_path = idb_context->GetFilePathForTesting(kSessionOnlyOrigin);
+  normal_path = context()->GetFilePathForTesting(kNormalOrigin);
+  session_only_path = context()->GetFilePathForTesting(kSessionOnlyOrigin);
   ASSERT_TRUE(base::CreateDirectory(normal_path));
   ASSERT_TRUE(base::CreateDirectory(session_only_path));
   base::RunLoop().RunUntilIdle();
 
-  idb_context->Shutdown();
+  context()->Shutdown();
 
   base::RunLoop().RunUntilIdle();
 
@@ -180,52 +182,51 @@
 };
 
 TEST_F(IndexedDBTest, ForceCloseOpenDatabasesOnDelete) {
-  base::ScopedTempDir temp_dir;
-  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
-
-  auto idb_context = base::MakeRefCounted<IndexedDBContextImpl>(
-      temp_dir.GetPath(), special_storage_policy_.get(),
-      quota_manager_proxy_.get(), indexed_db::GetDefaultLevelDBFactory());
-
   const Origin kTestOrigin = Origin::Create(GURL("http://test/"));
 
   base::RunLoop loop;
-  idb_context->TaskRunner()->PostTask(
+  context()->TaskRunner()->PostTask(
       FROM_HERE, base::BindLambdaForTesting([&]() {
         const int child_process_id = 0;
         const int64_t host_transaction_id = 0;
         const int64_t version = 0;
 
-        IndexedDBFactory* factory = idb_context->GetIDBFactory();
+        IndexedDBFactory* factory = context()->GetIDBFactory();
 
         base::FilePath test_path =
-            idb_context->GetFilePathForTesting(kTestOrigin);
+            context()->GetFilePathForTesting(kTestOrigin);
 
         auto open_db_callbacks =
             base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>();
         auto closed_db_callbacks =
             base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>();
-        auto open_callbacks = base::MakeRefCounted<ForceCloseDBCallbacks>(
-            idb_context, kTestOrigin);
-        auto closed_callbacks = base::MakeRefCounted<ForceCloseDBCallbacks>(
-            idb_context, kTestOrigin);
+        auto open_callbacks =
+            base::MakeRefCounted<ForceCloseDBCallbacks>(context(), kTestOrigin);
+        auto closed_callbacks =
+            base::MakeRefCounted<ForceCloseDBCallbacks>(context(), kTestOrigin);
 
+        auto create_transaction_callback1 =
+            base::BindOnce(&CreateAndBindTransactionPlaceholder);
         factory->Open(base::ASCIIToUTF16("opendb"),
                       std::make_unique<IndexedDBPendingConnection>(
                           open_callbacks, open_db_callbacks, child_process_id,
-                          host_transaction_id, version),
-                      kTestOrigin, idb_context->data_path());
+                          host_transaction_id, version,
+                          std::move(create_transaction_callback1)),
+                      kTestOrigin, context()->data_path());
         EXPECT_TRUE(base::DirectoryExists(test_path));
 
+        auto create_transaction_callback2 =
+            base::BindOnce(&CreateAndBindTransactionPlaceholder);
         factory->Open(base::ASCIIToUTF16("closeddb"),
                       std::make_unique<IndexedDBPendingConnection>(
                           closed_callbacks, closed_db_callbacks,
-                          child_process_id, host_transaction_id, version),
-                      kTestOrigin, idb_context->data_path());
+                          child_process_id, host_transaction_id, version,
+                          std::move(create_transaction_callback2)),
+                      kTestOrigin, context()->data_path());
 
         closed_callbacks->connection()->Close();
 
-        idb_context->DeleteForOrigin(kTestOrigin);
+        context()->DeleteForOrigin(kTestOrigin);
 
         EXPECT_TRUE(open_db_callbacks->forced_close_called());
         EXPECT_FALSE(closed_db_callbacks->forced_close_called());
@@ -236,48 +237,33 @@
 }
 
 TEST_F(IndexedDBTest, DeleteFailsIfDirectoryLocked) {
-  base::ScopedTempDir temp_dir;
-  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
   const Origin kTestOrigin = Origin::Create(GURL("http://test/"));
 
-  auto idb_context = base::MakeRefCounted<IndexedDBContextImpl>(
-      temp_dir.GetPath(), special_storage_policy_.get(),
-      quota_manager_proxy_.get(), indexed_db::GetDefaultLevelDBFactory());
-
-  base::FilePath test_path = idb_context->GetFilePathForTesting(kTestOrigin);
+  base::FilePath test_path = context()->GetFilePathForTesting(kTestOrigin);
   ASSERT_TRUE(base::CreateDirectory(test_path));
 
   auto lock = LockForTesting(test_path);
   ASSERT_TRUE(lock);
 
   base::RunLoop loop;
-  idb_context->TaskRunner()->PostTask(
-      FROM_HERE, base::BindLambdaForTesting([&]() {
-        idb_context->DeleteForOrigin(kTestOrigin);
-        loop.Quit();
-      }));
+  context()->TaskRunner()->PostTask(FROM_HERE,
+                                    base::BindLambdaForTesting([&]() {
+                                      context()->DeleteForOrigin(kTestOrigin);
+                                      loop.Quit();
+                                    }));
   loop.Run();
 
   EXPECT_TRUE(base::DirectoryExists(test_path));
 }
 
 TEST_F(IndexedDBTest, ForceCloseOpenDatabasesOnCommitFailure) {
-  base::ScopedTempDir temp_dir;
-  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
-
-  auto idb_context = base::MakeRefCounted<IndexedDBContextImpl>(
-      temp_dir.GetPath(), special_storage_policy_.get(),
-      quota_manager_proxy_.get(), indexed_db::GetDefaultLevelDBFactory());
-
-  auto temp_path = temp_dir.GetPath();
-
   base::RunLoop loop;
-  idb_context->TaskRunner()->PostTask(
+  context()->TaskRunner()->PostTask(
       FROM_HERE, base::BindLambdaForTesting([&]() {
         const Origin kTestOrigin = Origin::Create(GURL("http://test/"));
 
         auto* factory =
-            static_cast<IndexedDBFactoryImpl*>(idb_context->GetIDBFactory());
+            static_cast<IndexedDBFactoryImpl*>(context()->GetIDBFactory());
 
         const int child_process_id = 0;
         const int64_t transaction_id = 1;
@@ -285,16 +271,19 @@
         auto callbacks = base::MakeRefCounted<MockIndexedDBCallbacks>();
         auto db_callbacks =
             base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>();
+        auto create_transaction_callback1 =
+            base::BindOnce(&CreateAndBindTransactionPlaceholder);
         auto connection = std::make_unique<IndexedDBPendingConnection>(
             callbacks, db_callbacks, child_process_id, transaction_id,
-            IndexedDBDatabaseMetadata::DEFAULT_VERSION);
+            IndexedDBDatabaseMetadata::DEFAULT_VERSION,
+            std::move(create_transaction_callback1));
         factory->Open(base::ASCIIToUTF16("db"), std::move(connection),
-                      Origin(kTestOrigin), temp_path);
+                      Origin(kTestOrigin), context()->data_path());
 
         EXPECT_TRUE(callbacks->connection());
 
         // ConnectionOpened() is usually called by the dispatcher.
-        idb_context->ConnectionOpened(kTestOrigin, callbacks->connection());
+        context()->ConnectionOpened(kTestOrigin, callbacks->connection());
 
         EXPECT_TRUE(factory->IsBackingStoreOpen(kTestOrigin));
 
diff --git a/content/browser/indexed_db/transaction_impl.cc b/content/browser/indexed_db/transaction_impl.cc
new file mode 100644
index 0000000..dd6b06ce
--- /dev/null
+++ b/content/browser/indexed_db/transaction_impl.cc
@@ -0,0 +1,52 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/indexed_db/transaction_impl.h"
+
+#include <utility>
+
+#include "content/browser/indexed_db/indexed_db_connection.h"
+#include "content/browser/indexed_db/indexed_db_context_impl.h"
+#include "content/browser/indexed_db/indexed_db_dispatcher_host.h"
+#include "content/browser/indexed_db/indexed_db_transaction.h"
+
+namespace content {
+
+TransactionImpl::TransactionImpl(
+    base::WeakPtr<IndexedDBTransaction> transaction,
+    const url::Origin& origin,
+    base::WeakPtr<IndexedDBDispatcherHost> dispatcher_host,
+    scoped_refptr<base::SequencedTaskRunner> idb_runner)
+    : dispatcher_host_(dispatcher_host),
+      indexed_db_context_(dispatcher_host->context()),
+      transaction_(std::move(transaction)),
+      origin_(origin),
+      idb_runner_(std::move(idb_runner)),
+      weak_factory_(this) {
+  DCHECK(idb_runner_->RunsTasksInCurrentSequence());
+  DCHECK(dispatcher_host_);
+  DCHECK(transaction_);
+}
+
+TransactionImpl::~TransactionImpl() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+void TransactionImpl::CreateObjectStore(int64_t object_store_id,
+                                        const base::string16& name,
+                                        const blink::IndexedDBKeyPath& key_path,
+                                        bool auto_increment) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!transaction_)
+    return;
+
+  IndexedDBConnection* connection = transaction_->connection();
+  if (!connection->IsConnected())
+    return;
+
+  connection->database()->CreateObjectStore(transaction_.get(), object_store_id,
+                                            name, key_path, auto_increment);
+}
+
+}  // namespace content
diff --git a/content/browser/indexed_db/transaction_impl.h b/content/browser/indexed_db/transaction_impl.h
new file mode 100644
index 0000000..f5e90a6
--- /dev/null
+++ b/content/browser/indexed_db/transaction_impl.h
@@ -0,0 +1,56 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_INDEXED_DB_TRANSACTION_IMPL_H_
+#define CONTENT_BROWSER_INDEXED_DB_TRANSACTION_IMPL_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/sequence_checker.h"
+#include "content/public/browser/browser_thread.h"
+#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
+
+namespace base {
+class SequencedTaskRunner;
+}
+
+namespace content {
+class IndexedDBContextImpl;
+class IndexedDBDispatcherHost;
+class IndexedDBTransaction;
+
+class TransactionImpl : public blink::mojom::IDBTransaction {
+ public:
+  explicit TransactionImpl(
+      base::WeakPtr<IndexedDBTransaction> transaction,
+      const url::Origin& origin,
+      base::WeakPtr<IndexedDBDispatcherHost> dispatcher_host,
+      scoped_refptr<base::SequencedTaskRunner> idb_runner);
+  ~TransactionImpl() override;
+
+  // blink::mojom::IDBTransaction implementation
+  void CreateObjectStore(int64_t object_store_id,
+                         const base::string16& name,
+                         const blink::IndexedDBKeyPath& key_path,
+                         bool auto_increment) override;
+
+ private:
+  base::WeakPtr<IndexedDBDispatcherHost> dispatcher_host_;
+  scoped_refptr<IndexedDBContextImpl> indexed_db_context_;
+  base::WeakPtr<IndexedDBTransaction> transaction_;
+  const url::Origin origin_;
+  scoped_refptr<base::SequencedTaskRunner> idb_runner_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  base::WeakPtrFactory<TransactionImpl> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(TransactionImpl);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_INDEXED_DB_TRANSACTION_IMPL_H_