Bug 1092311 - Fix IndexedDB profiler markers and logging, r=khuey.
☠☠ backed out by efc1e23d4122 ☠ ☠
authorBen Turner <bent.mozilla@gmail.com>
Wed, 15 Oct 2014 21:56:52 -0700
changeset 219152 58982e7b5c45e9f33f5c13e6e08710b3f8459510
parent 219151 225f36741b2fc3aae6e63fc87a218b9eb60cdc93
child 219153 7525b48b6ed53f21bf75e91c226afd258e3671a1
push id52751
push userbturner@mozilla.com
push dateThu, 11 Dec 2014 04:05:36 +0000
treeherdermozilla-inbound@58982e7b5c45 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey
bugs1092311
milestone37.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1092311 - Fix IndexedDB profiler markers and logging, r=khuey.
dom/indexedDB/ActorsChild.cpp
dom/indexedDB/ActorsChild.h
dom/indexedDB/ActorsParent.cpp
dom/indexedDB/ActorsParent.h
dom/indexedDB/IDBCursor.cpp
dom/indexedDB/IDBDatabase.cpp
dom/indexedDB/IDBFactory.cpp
dom/indexedDB/IDBFactory.h
dom/indexedDB/IDBIndex.cpp
dom/indexedDB/IDBObjectStore.cpp
dom/indexedDB/IDBObjectStore.h
dom/indexedDB/IDBRequest.cpp
dom/indexedDB/IDBRequest.h
dom/indexedDB/IDBTransaction.cpp
dom/indexedDB/IDBTransaction.h
dom/indexedDB/IndexedDatabaseManager.cpp
dom/indexedDB/IndexedDatabaseManager.h
dom/indexedDB/PBackgroundIDBFactory.ipdl
dom/indexedDB/PBackgroundIDBSharedTypes.ipdlh
dom/indexedDB/ProfilerHelpers.h
dom/indexedDB/TransactionThreadPool.cpp
dom/indexedDB/TransactionThreadPool.h
ipc/glue/BackgroundChildImpl.cpp
ipc/glue/BackgroundChildImpl.h
ipc/glue/BackgroundParentImpl.cpp
ipc/glue/BackgroundParentImpl.h
ipc/glue/PBackground.ipdl
ipc/glue/moz.build
modules/libpref/init/all.js
--- a/dom/indexedDB/ActorsChild.cpp
+++ b/dom/indexedDB/ActorsChild.cpp
@@ -52,16 +52,47 @@
 
 #endif // DEBUG || GC_ON_IPC_MESSAGES
 
 namespace mozilla {
 namespace dom {
 namespace indexedDB {
 
 /*******************************************************************************
+ * ThreadLocal
+ ******************************************************************************/
+
+ThreadLocal::ThreadLocal(const nsID& aBackgroundChildLoggingId)
+  : mLoggingInfo(aBackgroundChildLoggingId, 1, -1, 1)
+  , mCurrentTransaction(0)
+#ifdef DEBUG
+  , mOwningThread(PR_GetCurrentThread())
+#endif
+{
+  MOZ_ASSERT(mOwningThread);
+
+  MOZ_COUNT_CTOR(mozilla::dom::indexedDB::ThreadLocal);
+}
+
+ThreadLocal::~ThreadLocal()
+{
+  MOZ_COUNT_DTOR(mozilla::dom::indexedDB::ThreadLocal);
+}
+
+#ifdef DEBUG
+
+void
+ThreadLocal::AssertIsOnOwningThread() const
+{
+  MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
+}
+
+#endif // DEBUG
+
+/*******************************************************************************
  * Helpers
  ******************************************************************************/
 
 namespace {
 
 void
 MaybeCollectGarbageOnIPCMessage()
 {
@@ -99,49 +130,50 @@ MaybeCollectGarbageOnIPCMessage()
 }
 
 class MOZ_STACK_CLASS AutoSetCurrentTransaction MOZ_FINAL
 {
   typedef mozilla::ipc::BackgroundChildImpl BackgroundChildImpl;
 
   IDBTransaction* const mTransaction;
   IDBTransaction* mPreviousTransaction;
-  IDBTransaction** mThreadLocalSlot;
+  ThreadLocal* mThreadLocal;
 
 public:
   explicit AutoSetCurrentTransaction(IDBTransaction* aTransaction)
     : mTransaction(aTransaction)
     , mPreviousTransaction(nullptr)
-    , mThreadLocalSlot(nullptr)
+    , mThreadLocal(nullptr)
   {
     if (aTransaction) {
       BackgroundChildImpl::ThreadLocal* threadLocal =
         BackgroundChildImpl::GetThreadLocalForCurrentThread();
       MOZ_ASSERT(threadLocal);
 
-      // Hang onto this location for resetting later.
-      mThreadLocalSlot = &threadLocal->mCurrentTransaction;
+      // Hang onto this for resetting later.
+      mThreadLocal = threadLocal->mIndexedDBThreadLocal;
+      MOZ_ASSERT(mThreadLocal);
 
       // Save the current value.
-      mPreviousTransaction = *mThreadLocalSlot;
+      mPreviousTransaction = mThreadLocal->GetCurrentTransaction();
 
       // Set the new value.
-      *mThreadLocalSlot = aTransaction;
+      mThreadLocal->SetCurrentTransaction(aTransaction);
     }
   }
 
   ~AutoSetCurrentTransaction()
   {
-    MOZ_ASSERT_IF(mThreadLocalSlot, mTransaction);
-
-    if (mThreadLocalSlot) {
-      MOZ_ASSERT(*mThreadLocalSlot == mTransaction);
-
+    MOZ_ASSERT_IF(mThreadLocal, mTransaction);
+    MOZ_ASSERT_IF(mThreadLocal,
+                  mThreadLocal->GetCurrentTransaction() == mTransaction);
+
+    if (mThreadLocal) {
       // Reset old value.
-      *mThreadLocalSlot = mPreviousTransaction;
+      mThreadLocal->SetCurrentTransaction(mPreviousTransaction);
     }
   }
 
   IDBTransaction*
   Transaction() const
   {
     return mTransaction;
   }
@@ -584,16 +616,35 @@ DispatchErrorEvent(IDBRequest* aRequest,
     aEvent = errorEvent;
   }
 
   Maybe<AutoSetCurrentTransaction> asct;
   if (aTransaction) {
     asct.emplace(aTransaction);
   }
 
+  if (transaction) {
+    IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                   "Firing %s event with error 0x%x",
+                 "IndexedDB %s: C T[%lld] R[%llu]: %s (0x%x)",
+                 IDB_LOG_ID_STRING(),
+                 transaction->LoggingSerialNumber(),
+                 request->LoggingSerialNumber(),
+                 IDB_LOG_STRINGIFY(aEvent, kErrorEventType),
+                 aErrorCode);
+  } else {
+    IDB_LOG_MARK("IndexedDB %s: Child  Request[%llu]: "
+                   "Firing %s event with error 0x%x",
+                 "IndexedDB %s: C R[%llu]: %s (0x%x)",
+                 IDB_LOG_ID_STRING(),
+                 request->LoggingSerialNumber(),
+                 IDB_LOG_STRINGIFY(aEvent, kErrorEventType),
+                 aErrorCode);
+  }
+
   bool doDefault;
   nsresult rv = request->DispatchEvent(aEvent, &doDefault);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
   MOZ_ASSERT(!transaction || transaction->IsOpen() || transaction->IsAborted());
 
@@ -643,16 +694,32 @@ DispatchSuccessEvent(ResultHelper* aResu
     aEvent = successEvent;
   }
 
   request->SetResultCallback(aResultHelper);
 
   MOZ_ASSERT(aEvent);
   MOZ_ASSERT_IF(transaction, transaction->IsOpen());
 
+  if (transaction) {
+    IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                   "Firing %s event",
+                 "IndexedDB %s: C T[%lld] R[%llu]: %s",
+                 IDB_LOG_ID_STRING(),
+                 transaction->LoggingSerialNumber(),
+                 request->LoggingSerialNumber(),
+                 IDB_LOG_STRINGIFY(aEvent, kSuccessEventType));
+  } else {
+    IDB_LOG_MARK("IndexedDB %s: Child  Request[%llu]: Firing %s event",
+                 "IndexedDB %s: C R[%llu]: %s",
+                 IDB_LOG_ID_STRING(),
+                 request->LoggingSerialNumber(),
+                 IDB_LOG_STRINGIFY(aEvent, kSuccessEventType));
+  }
+
   bool dummy;
   nsresult rv = request->DispatchEvent(aEvent, &dummy);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
   MOZ_ASSERT_IF(transaction,
                 transaction->IsOpen() || transaction->IsAborted());
@@ -1063,16 +1130,21 @@ BackgroundFactoryRequestChild::RecvBlock
   }
 
   if (NS_WARN_IF(!blockedEvent)) {
     return false;
   }
 
   nsRefPtr<IDBRequest> kungFuDeathGrip = mRequest;
 
+  IDB_LOG_MARK("IndexedDB %s: Child  Request[%llu]: Firing \"blocked\" event",
+               "IndexedDB %s: C R[%llu]: \"blocked\"",
+               IDB_LOG_ID_STRING(),
+               mRequest->LoggingSerialNumber());
+
   bool dummy;
   if (NS_FAILED(mRequest->DispatchEvent(blockedEvent, &dummy))) {
     NS_WARNING("Failed to dispatch event!");
   }
 
   return true;
 }
 
@@ -1352,16 +1424,20 @@ BackgroundDatabaseChild::RecvVersionChan
     default:
       MOZ_CRASH("Should never get here!");
   }
 
   if (NS_WARN_IF(!versionChangeEvent)) {
     return false;
   }
 
+  IDB_LOG_MARK("IndexedDB %s: Child : Firing \"versionchange\" event",
+               "IndexedDB %s: C: IDBDatabase \"versionchange\" event",
+               IDB_LOG_ID_STRING());
+
   bool dummy;
   if (NS_FAILED(mDatabase->DispatchEvent(versionChangeEvent, &dummy))) {
     NS_WARNING("Failed to dispatch event!");
   }
 
   if (!mDatabase->IsClosed()) {
     SendBlocked();
   }
--- a/dom/indexedDB/ActorsChild.h
+++ b/dom/indexedDB/ActorsChild.h
@@ -1,29 +1,32 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_indexeddb_actorschild_h__
 #define mozilla_dom_indexeddb_actorschild_h__
 
+#include "IDBTransaction.h"
 #include "js/RootingAPI.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBCursorChild.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseChild.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryRequestChild.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBRequestChild.h"
+#include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBTransactionChild.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBVersionChangeTransactionChild.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsTArray.h"
 
 class nsIEventTarget;
+struct nsID;
 struct PRThread;
 
 namespace mozilla {
 namespace ipc {
 
 class BackgroundChildImpl;
 
 } // namespace ipc
@@ -33,23 +36,107 @@ namespace indexedDB {
 
 class FileInfo;
 class IDBCursor;
 class IDBDatabase;
 class IDBFactory;
 class IDBMutableFile;
 class IDBOpenDBRequest;
 class IDBRequest;
-class IDBTransaction;
 class Key;
 class PBackgroundIDBFileChild;
 class PermissionRequestChild;
 class PermissionRequestParent;
 class SerializedStructuredCloneReadInfo;
 
+class ThreadLocal
+{
+  friend class nsAutoPtr<ThreadLocal>;
+  friend class IDBFactory;
+
+  LoggingInfo mLoggingInfo;
+  IDBTransaction* mCurrentTransaction;
+
+#ifdef DEBUG
+  PRThread* mOwningThread;
+#endif
+
+public:
+  void
+  AssertIsOnOwningThread() const
+#ifdef DEBUG
+  ;
+#else
+  { }
+#endif
+
+  const LoggingInfo&
+  GetLoggingInfo() const
+  {
+    AssertIsOnOwningThread();
+
+    return mLoggingInfo;
+  }
+
+  const nsID&
+  Id() const
+  {
+    AssertIsOnOwningThread();
+
+    return mLoggingInfo.backgroundChildLoggingId();
+  }
+
+  int64_t
+  NextTransactionSN(IDBTransaction::Mode aMode)
+  {
+    AssertIsOnOwningThread();
+    MOZ_ASSERT(mLoggingInfo.nextTransactionSerialNumber() < INT64_MAX);
+    MOZ_ASSERT(mLoggingInfo.nextVersionChangeTransactionSerialNumber() >
+                 INT64_MIN);
+
+    if (aMode == IDBTransaction::VERSION_CHANGE) {
+      return mLoggingInfo.nextVersionChangeTransactionSerialNumber()--;
+    }
+
+    return mLoggingInfo.nextTransactionSerialNumber()++;
+  }
+
+  uint64_t
+  NextRequestSN()
+  {
+    AssertIsOnOwningThread();
+    MOZ_ASSERT(mLoggingInfo.nextRequestSerialNumber() < UINT64_MAX);
+
+    return mLoggingInfo.nextRequestSerialNumber()++;
+  }
+
+  void
+  SetCurrentTransaction(IDBTransaction* aCurrentTransaction)
+  {
+    AssertIsOnOwningThread();
+
+    mCurrentTransaction = aCurrentTransaction;
+  }
+
+  IDBTransaction*
+  GetCurrentTransaction() const
+  {
+    AssertIsOnOwningThread();
+
+    return mCurrentTransaction;
+  }
+
+private:
+  ThreadLocal(const nsID& aBackgroundChildLoggingId);
+  ~ThreadLocal();
+
+  ThreadLocal() MOZ_DELETE;
+  ThreadLocal(const ThreadLocal& aOther) MOZ_DELETE;
+};
+
 class BackgroundFactoryChild MOZ_FINAL
   : public PBackgroundIDBFactoryChild
 {
   friend class mozilla::ipc::BackgroundChildImpl;
   friend class IDBFactory;
 
   IDBFactory* mFactory;
 
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -102,16 +102,17 @@ using namespace mozilla::ipc;
 #define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__)
 #endif
 
 namespace {
 
 class Cursor;
 class Database;
 struct DatabaseActorInfo;
+class DatabaseLoggingInfo;
 class DatabaseFile;
 class DatabaseOfflineStorage;
 class Factory;
 class OpenDatabaseOp;
 class TransactionBase;
 class VersionChangeTransaction;
 
 /*******************************************************************************
@@ -2548,27 +2549,24 @@ GetDatabaseConnection(const nsAString& a
 /*******************************************************************************
  * Actor class declarations
  ******************************************************************************/
 
 class DatabaseOperationBase
   : public nsRunnable
   , public mozIStorageProgressHandler
 {
-  // Uniquely tracks each operation for logging purposes. Only modified on the
-  // PBackground thread.
-  static uint64_t sNextSerialNumber;
-
 protected:
   class AutoSetProgressHandler;
 
   typedef nsDataHashtable<nsUint64HashKey, bool> UniqueIndexTable;
 
   nsCOMPtr<nsIEventTarget> mOwningThread;
-  const uint64_t mSerialNumber;
+  const nsID mBackgroundChildLoggingId;
+  const uint64_t mLoggingSerialNumber;
   nsresult mResultCode;
 
 private:
   Atomic<bool> mOperationMayProceed;
   bool mActorDestroyed;
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
@@ -2605,20 +2603,26 @@ public:
 
   // May be called on any thread.
   bool
   OperationMayProceed() const
   {
     return mOperationMayProceed;
   }
 
+  const nsID&
+  BackgroundChildLoggingId() const
+  {
+    return mBackgroundChildLoggingId;
+  }
+
   uint64_t
-  SerialNumber() const
-  {
-    return mSerialNumber;
+  LoggingSerialNumber() const
+  {
+    return mLoggingSerialNumber;
   }
 
   nsresult
   ResultCode() const
   {
     return mResultCode;
   }
 
@@ -2627,19 +2631,21 @@ public:
   {
     MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
     MOZ_ASSERT(NS_FAILED(aErrorCode));
 
     mResultCode = aErrorCode;
   }
 
 protected:
-  DatabaseOperationBase()
+  DatabaseOperationBase(const nsID& aBackgroundChildLoggingId,
+                        uint64_t aLoggingSerialNumber)
     : mOwningThread(NS_GetCurrentThread())
-    , mSerialNumber(++sNextSerialNumber)
+    , mBackgroundChildLoggingId(aBackgroundChildLoggingId)
+    , mLoggingSerialNumber(aLoggingSerialNumber)
     , mResultCode(NS_OK)
     , mOperationMayProceed(true)
     , mActorDestroyed(false)
   {
     AssertIsOnOwningThread();
   }
 
   virtual
@@ -2704,16 +2710,17 @@ public:
   Register(DatabaseOperationBase* aDatabaseOp,
            const nsCOMPtr<mozIStorageConnection>& aConnection);
 };
 
 class TransactionDatabaseOperationBase
   : public DatabaseOperationBase
 {
   nsRefPtr<TransactionBase> mTransaction;
+  const int64_t mTransactionLoggingSerialNumber;
   const bool mTransactionIsAborted;
 
 public:
   void
   AssertIsOnTransactionThread() const
 #ifdef DEBUG
   ;
 #else
@@ -2731,17 +2738,21 @@ public:
 
   // This callback will be called on the background thread before releasing the
   // final reference to this request object. Subclasses may perform any
   // additional cleanup here but must always call the base class implementation.
   virtual void
   Cleanup();
 
 protected:
-  explicit TransactionDatabaseOperationBase(TransactionBase* aTransaction);
+  explicit
+  TransactionDatabaseOperationBase(TransactionBase* aTransaction);
+
+  TransactionDatabaseOperationBase(TransactionBase* aTransaction,
+                                   uint64_t aLoggingSerialNumber);
 
   virtual
   ~TransactionDatabaseOperationBase();
 
   // Must be overridden in subclasses. Called on the target thread to allow the
   // subclass to perform necessary database or file operations. A successful
   // return value will trigger a SendSuccessResult callback on the background
   // thread while a failure value will trigger a SendFailureResult callback.
@@ -2774,38 +2785,53 @@ private:
 
 class Factory MOZ_FINAL
   : public PBackgroundIDBFactoryParent
 {
   // Counts the number of "live" Factory instances that have not yet had
   // ActorDestroy called.
   static uint64_t sFactoryInstanceCount;
 
+  nsRefPtr<DatabaseLoggingInfo> mLoggingInfo;
+
   DebugOnly<bool> mActorDestroyed;
 
 public:
   static already_AddRefed<Factory>
-  Create();
+  Create(const LoggingInfo& aLoggingInfo);
+
+  DatabaseLoggingInfo*
+  GetLoggingInfo() const
+  {
+    AssertIsOnBackgroundThread();
+    MOZ_ASSERT(mLoggingInfo);
+
+    return mLoggingInfo;
+  }
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::Factory)
 
 private:
   // Only constructed in Create().
-  explicit Factory();
+  explicit
+  Factory(already_AddRefed<DatabaseLoggingInfo> aLoggingInfo);
 
   // Reference counted.
   ~Factory();
 
   // IPDL methods are only called by IPDL.
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
 
   virtual bool
   RecvDeleteMe() MOZ_OVERRIDE;
 
+  virtual bool
+  RecvIncrementLoggingRequestSerialNumber() MOZ_OVERRIDE;
+
   virtual PBackgroundIDBFactoryRequestParent*
   AllocPBackgroundIDBFactoryRequestParent(const FactoryRequestParams& aParams)
                                           MOZ_OVERRIDE;
 
   virtual bool
   RecvPBackgroundIDBFactoryRequestConstructor(
                                      PBackgroundIDBFactoryRequestParent* aActor,
                                      const FactoryRequestParams& aParams)
@@ -2919,16 +2945,25 @@ public:
   GetBackgroundParent() const
   {
     AssertIsOnBackgroundThread();
     MOZ_ASSERT(!IsActorDestroyed());
 
     return Manager()->Manager();
   }
 
+  DatabaseLoggingInfo*
+  GetLoggingInfo() const
+  {
+    AssertIsOnBackgroundThread();
+    MOZ_ASSERT(mFactory);
+
+    return mFactory->GetLoggingInfo();
+  }
+
   bool
   RegisterTransaction(TransactionBase* aTransaction);
 
   void
   UnregisterTransaction(TransactionBase* aTransaction);
 
   void
   SetActorAlive();
@@ -3125,16 +3160,17 @@ private:
   nsCOMPtr<mozIStorageConnection> mConnection;
   nsRefPtr<UpdateRefcountFunction> mUpdateFileRefcountFunction;
   nsInterfaceHashtable<nsCStringHashKey, mozIStorageStatement>
     mCachedStatements;
   nsTArray<nsRefPtr<FullObjectStoreMetadata>>
     mModifiedAutoIncrementObjectStoreMetadataArray;
   const uint64_t mTransactionId;
   const nsCString mDatabaseId;
+  const int64_t mLoggingSerialNumber;
   uint64_t mActiveRequestCount;
   Atomic<bool> mInvalidatedOnAnyThread;
   Mode mMode;
   bool mHasBeenActive;
   bool mActorDestroyed;
   bool mInvalidated;
 
 protected:
@@ -3243,16 +3279,31 @@ public:
   GetDatabase() const
   {
     AssertIsOnBackgroundThread();
     MOZ_ASSERT(mDatabase);
 
     return mDatabase;
   }
 
+  DatabaseLoggingInfo*
+  GetLoggingInfo() const
+  {
+    AssertIsOnBackgroundThread();
+    MOZ_ASSERT(mDatabase);
+
+    return mDatabase->GetLoggingInfo();
+  }
+
+  int64_t
+  LoggingSerialNumber() const
+  {
+    return mLoggingSerialNumber;
+  }
+
   bool
   IsAborted() const
   {
     AssertIsOnBackgroundThread();
 
     return NS_FAILED(mResultCode);
   }
 
@@ -3292,18 +3343,17 @@ public:
 
   void
   NoteFinishedRequest();
 
   void
   Invalidate();
 
 protected:
-  TransactionBase(Database* aDatabase,
-                  Mode aMode);
+  TransactionBase(Database* aDatabase, Mode aMode);
 
   virtual
   ~TransactionBase();
 
   void
   NoteActorDestroyed()
   {
     AssertIsOnBackgroundThread();
@@ -3415,23 +3465,17 @@ class TransactionBase::CommitOp MOZ_FINA
   , public TransactionThreadPool::FinishCallback
 {
   friend class TransactionBase;
 
   nsRefPtr<TransactionBase> mTransaction;
   nsresult mResultCode;
 
 private:
-  CommitOp(TransactionBase* aTransaction,
-           nsresult aResultCode)
-    : mTransaction(aTransaction)
-    , mResultCode(aResultCode)
-  {
-    MOZ_ASSERT(aTransaction);
-  }
+  CommitOp(TransactionBase* aTransaction, nsresult aResultCode);
 
   ~CommitOp()
   { }
 
   // Writes new autoIncrement counts to database.
   nsresult
   WriteAutoIncrementCounts();
 
@@ -3665,18 +3709,18 @@ class NormalTransaction MOZ_FINAL
 {
   friend class Database;
 
   nsTArray<nsRefPtr<FullObjectStoreMetadata>> mObjectStores;
 
 private:
   // This constructor is only called by Database.
   NormalTransaction(Database* aDatabase,
-                    nsTArray<nsRefPtr<FullObjectStoreMetadata>>& aObjectStores,
-                    TransactionBase::Mode aMode);
+                    TransactionBase::Mode aMode,
+                    nsTArray<nsRefPtr<FullObjectStoreMetadata>>& aObjectStores);
 
   // Reference counted.
   ~NormalTransaction()
   { }
 
   bool
   IsSameProcessActor();
 
@@ -4128,19 +4172,21 @@ class OpenDatabaseOp::VersionChangeOp MO
 {
   friend class OpenDatabaseOp;
 
   nsRefPtr<OpenDatabaseOp> mOpenDatabaseOp;
   const uint64_t mRequestedVersion;
   uint64_t mPreviousVersion;
 
 private:
-  explicit VersionChangeOp(OpenDatabaseOp* aOpenDatabaseOp)
+  explicit
+  VersionChangeOp(OpenDatabaseOp* aOpenDatabaseOp)
     : TransactionDatabaseOperationBase(
-                                     aOpenDatabaseOp->mVersionChangeTransaction)
+                                     aOpenDatabaseOp->mVersionChangeTransaction,
+                                     aOpenDatabaseOp->LoggingSerialNumber())
     , mOpenDatabaseOp(aOpenDatabaseOp)
     , mRequestedVersion(aOpenDatabaseOp->mRequestedVersion)
     , mPreviousVersion(aOpenDatabaseOp->mMetadata->mCommonMetadata.version())
   {
     MOZ_ASSERT(aOpenDatabaseOp);
     MOZ_ASSERT(mRequestedVersion);
   }
 
@@ -4209,18 +4255,21 @@ private:
 class DeleteDatabaseOp::VersionChangeOp MOZ_FINAL
   : public DatabaseOperationBase
 {
   friend class DeleteDatabaseOp;
 
   nsRefPtr<DeleteDatabaseOp> mDeleteDatabaseOp;
 
 private:
-  explicit VersionChangeOp(DeleteDatabaseOp* aDeleteDatabaseOp)
-    : mDeleteDatabaseOp(aDeleteDatabaseOp)
+  explicit
+  VersionChangeOp(DeleteDatabaseOp* aDeleteDatabaseOp)
+    : DatabaseOperationBase(aDeleteDatabaseOp->BackgroundChildLoggingId(),
+                            aDeleteDatabaseOp->LoggingSerialNumber())
+    , mDeleteDatabaseOp(aDeleteDatabaseOp)
   {
     MOZ_ASSERT(aDeleteDatabaseOp);
     MOZ_ASSERT(!aDeleteDatabaseOp->mDatabaseDirectoryPath.IsEmpty());
   }
 
   ~VersionChangeOp()
   { }
 
@@ -4986,17 +5035,17 @@ private:
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
 };
 
 /*******************************************************************************
  * Other class declarations
  ******************************************************************************/
 
-struct DatabaseActorInfo
+struct DatabaseActorInfo MOZ_FINAL
 {
   friend class nsAutoPtr<DatabaseActorInfo>;
 
   nsRefPtr<FullDatabaseMetadata> mMetadata;
   nsTArray<Database*> mLiveDatabases;
   nsRefPtr<FactoryOp> mWaitingFactoryOp;
 
   DatabaseActorInfo(FullDatabaseMetadata* aMetadata,
@@ -5016,16 +5065,70 @@ private:
     MOZ_ASSERT(mLiveDatabases.IsEmpty());
     MOZ_ASSERT(!mWaitingFactoryOp ||
                !mWaitingFactoryOp->HasBlockedDatabases());
 
     MOZ_COUNT_DTOR(DatabaseActorInfo);
   }
 };
 
+class DatabaseLoggingInfo MOZ_FINAL
+{
+#ifdef DEBUG
+  // Just for potential warnings.
+  friend class Factory;
+#endif
+
+  LoggingInfo mLoggingInfo;
+
+public:
+  DatabaseLoggingInfo(const LoggingInfo& aLoggingInfo)
+    : mLoggingInfo(aLoggingInfo)
+  {
+    AssertIsOnBackgroundThread();
+  }
+
+  const nsID&
+  Id() const
+  {
+    AssertIsOnBackgroundThread();
+
+    return mLoggingInfo.backgroundChildLoggingId();
+  }
+
+  int64_t
+  NextTransactionSN(IDBTransaction::Mode aMode)
+  {
+    AssertIsOnBackgroundThread();
+    MOZ_ASSERT(mLoggingInfo.nextTransactionSerialNumber() < INT64_MAX);
+    MOZ_ASSERT(mLoggingInfo.nextVersionChangeTransactionSerialNumber() >
+                 INT64_MIN);
+
+    if (aMode == IDBTransaction::VERSION_CHANGE) {
+      return mLoggingInfo.nextVersionChangeTransactionSerialNumber()--;
+    }
+
+    return mLoggingInfo.nextTransactionSerialNumber()++;
+  }
+
+  uint64_t
+  NextRequestSN()
+  {
+    AssertIsOnBackgroundThread();
+    MOZ_ASSERT(mLoggingInfo.nextRequestSerialNumber() < UINT64_MAX);
+
+    return mLoggingInfo.nextRequestSerialNumber()++;
+  }
+
+  NS_INLINE_DECL_REFCOUNTING(DatabaseLoggingInfo)
+
+private:
+  ~DatabaseLoggingInfo();
+};
+
 class NonMainThreadHackBlobImpl MOZ_FINAL
   : public FileImplFile
 {
 public:
   NonMainThreadHackBlobImpl(nsIFile* aFile, FileInfo* aFileInfo)
     : FileImplFile(aFile, aFileInfo)
   {
     // Getting the content type is not currently supported off the main thread.
@@ -5519,43 +5622,51 @@ typedef nsClassHashtable<nsCStringHashKe
         DatabaseActorHashtable;
 
 StaticAutoPtr<DatabaseActorHashtable> gLiveDatabaseHashtable;
 
 StaticRefPtr<nsRunnable> gStartTransactionRunnable;
 
 StaticRefPtr<TransactionThreadPool> gTransactionThreadPool;
 
+typedef nsDataHashtable<nsIDHashKey, DatabaseLoggingInfo*>
+        DatabaseLoggingInfoHashtable;
+
+StaticAutoPtr<DatabaseLoggingInfoHashtable> gLoggingInfoHashtable;
+
 #ifdef DEBUG
 
 StaticRefPtr<DEBUGThreadSlower> gDEBUGThreadSlower;
 
 #endif // DEBUG
 
 } // anonymous namespace
 
 /*******************************************************************************
  * Exported functions
  ******************************************************************************/
 
 PBackgroundIDBFactoryParent*
-AllocPBackgroundIDBFactoryParent()
+AllocPBackgroundIDBFactoryParent(const LoggingInfo& aLoggingInfo)
 {
   AssertIsOnBackgroundThread();
 
   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread())) {
     return nullptr;
   }
 
-  nsRefPtr<Factory> actor = Factory::Create();
+  nsRefPtr<Factory> actor = Factory::Create(aLoggingInfo);
+  MOZ_ASSERT(actor);
+
   return actor.forget().take();
 }
 
 bool
-RecvPBackgroundIDBFactoryConstructor(PBackgroundIDBFactoryParent* aActor)
+RecvPBackgroundIDBFactoryConstructor(PBackgroundIDBFactoryParent* aActor,
+                                     const LoggingInfo& /* aLoggingInfo */)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
   MOZ_ASSERT(!QuotaClient::IsShuttingDownOnNonMainThread());
 
   return true;
 }
 
@@ -5722,37 +5833,51 @@ FullDatabaseMetadata::Duplicate() const
   if (NS_WARN_IF(mObjectStores.Count() !=
                  newMetadata->mObjectStores.Count())) {
     return nullptr;
   }
 
   return newMetadata.forget();
 }
 
+DatabaseLoggingInfo::~DatabaseLoggingInfo()
+{
+  MOZ_ASSERT(gLoggingInfoHashtable);
+
+  const nsID& backgroundChildLoggingId =
+    mLoggingInfo.backgroundChildLoggingId();
+
+  MOZ_ASSERT(gLoggingInfoHashtable->Get(backgroundChildLoggingId) == this);
+
+  gLoggingInfoHashtable->Remove(backgroundChildLoggingId);
+}
+
 /*******************************************************************************
  * Factory
  ******************************************************************************/
 
 uint64_t Factory::sFactoryInstanceCount = 0;
 
-Factory::Factory()
-  : mActorDestroyed(false)
+Factory::Factory(already_AddRefed<DatabaseLoggingInfo> aLoggingInfo)
+  : mLoggingInfo(Move(aLoggingInfo))
+  , mActorDestroyed(false)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(!QuotaClient::IsShuttingDownOnNonMainThread());
 }
 
 Factory::~Factory()
 {
+  MOZ_ASSERT(!mLoggingInfo);
   MOZ_ASSERT(mActorDestroyed);
 }
 
 // static
 already_AddRefed<Factory>
-Factory::Create()
+Factory::Create(const LoggingInfo& aLoggingInfo)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(!QuotaClient::IsShuttingDownOnNonMainThread());
 
   // If this is the first instance then we need to do some initialization.
   if (!sFactoryInstanceCount) {
     if (!gTransactionThreadPool) {
       nsRefPtr<TransactionThreadPool> threadPool =
@@ -5765,16 +5890,19 @@ Factory::Create()
     }
 
     MOZ_ASSERT(!gLiveDatabaseHashtable);
     gLiveDatabaseHashtable = new DatabaseActorHashtable();
 
     MOZ_ASSERT(!gStartTransactionRunnable);
     gStartTransactionRunnable = new nsRunnable();
 
+    MOZ_ASSERT(!gLoggingInfoHashtable);
+    gLoggingInfoHashtable = new DatabaseLoggingInfoHashtable();
+
 #ifdef DEBUG
     if (kDEBUGThreadPriority != nsISupportsPriority::PRIORITY_NORMAL) {
       NS_WARNING("PBackground thread debugging enabled, priority has been "
                  "modified!");
       nsCOMPtr<nsISupportsPriority> thread =
         do_QueryInterface(NS_GetCurrentThread());
       MOZ_ASSERT(thread);
 
@@ -5790,33 +5918,61 @@ Factory::Create()
 
       gDEBUGThreadSlower = new DEBUGThreadSlower();
 
       MOZ_ALWAYS_TRUE(NS_SUCCEEDED(thread->AddObserver(gDEBUGThreadSlower)));
     }
 #endif // DEBUG
   }
 
-  nsRefPtr<Factory> actor = new Factory();
+  nsRefPtr<DatabaseLoggingInfo> loggingInfo =
+    gLoggingInfoHashtable->Get(aLoggingInfo.backgroundChildLoggingId());
+  if (loggingInfo) {
+    MOZ_ASSERT(aLoggingInfo.backgroundChildLoggingId() == loggingInfo->Id());
+#if !DISABLE_ASSERTS_FOR_FUZZING
+    NS_WARN_IF_FALSE(aLoggingInfo.nextTransactionSerialNumber() ==
+                       loggingInfo->mLoggingInfo.nextTransactionSerialNumber(),
+                     "NextTransactionSerialNumber doesn't match!");
+    NS_WARN_IF_FALSE(aLoggingInfo.nextVersionChangeTransactionSerialNumber() ==
+                       loggingInfo->mLoggingInfo.
+                         nextVersionChangeTransactionSerialNumber(),
+                     "NextVersionChangeTransactionSerialNumber doesn't match!");
+    NS_WARN_IF_FALSE(aLoggingInfo.nextRequestSerialNumber() ==
+                       loggingInfo->mLoggingInfo.nextRequestSerialNumber(),
+                     "NextRequestSerialNumber doesn't match!");
+#endif // !DISABLE_ASSERTS_FOR_FUZZING
+  } else {
+    loggingInfo = new DatabaseLoggingInfo(aLoggingInfo);
+    gLoggingInfoHashtable->Put(aLoggingInfo.backgroundChildLoggingId(),
+                               loggingInfo);
+  }
+
+  nsRefPtr<Factory> actor = new Factory(loggingInfo.forget());
 
   sFactoryInstanceCount++;
 
   return actor.forget();
 }
 
 void
 Factory::ActorDestroy(ActorDestroyReason aWhy)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(!mActorDestroyed);
 
   mActorDestroyed = true;
 
+  mLoggingInfo = nullptr;
+
   // Clean up if there are no more instances.
   if (!(--sFactoryInstanceCount)) {
+    MOZ_ASSERT(gLoggingInfoHashtable);
+    MOZ_ASSERT(!gLoggingInfoHashtable->Count());
+    gLoggingInfoHashtable = nullptr;
+
     MOZ_ASSERT(gStartTransactionRunnable);
     gStartTransactionRunnable = nullptr;
 
     MOZ_ASSERT(gLiveDatabaseHashtable);
     MOZ_ASSERT(!gLiveDatabaseHashtable->Count());
     gLiveDatabaseHashtable = nullptr;
 
 #ifdef DEBUG
@@ -5848,16 +6004,26 @@ bool
 Factory::RecvDeleteMe()
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(!mActorDestroyed);
 
   return PBackgroundIDBFactoryParent::Send__delete__(this);
 }
 
+bool
+Factory::RecvIncrementLoggingRequestSerialNumber()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(mLoggingInfo);
+
+  mLoggingInfo->NextRequestSN();
+  return true;
+}
+
 PBackgroundIDBFactoryRequestParent*
 Factory::AllocPBackgroundIDBFactoryRequestParent(
                                             const FactoryRequestParams& aParams)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aParams.type() != FactoryRequestParams::T__None);
 
   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread())) {
@@ -6072,17 +6238,17 @@ Database::Invalidate()
   };
 
   if (mInvalidated) {
     return;
   }
 
   mInvalidated = true;
 
-  if (!mActorDestroyed) {
+  if (mActorWasAlive && !mActorDestroyed) {
     unused << SendInvalidate();
   }
 
   if (!Helper::InvalidateTransactions(mTransactions)) {
     NS_WARNING("Failed to abort all transactions!");
   }
 
   MOZ_ALWAYS_TRUE(CloseInternal());
@@ -6324,31 +6490,40 @@ Database::AllocPBackgroundIDBTransaction
 
   FallibleTArray<nsRefPtr<FullObjectStoreMetadata>> fallibleObjectStores;
   if (NS_WARN_IF(!fallibleObjectStores.SetCapacity(nameCount))) {
     return nullptr;
   }
 
   for (uint32_t nameIndex = 0; nameIndex < nameCount; nameIndex++) {
     const nsString& name = aObjectStoreNames[nameIndex];
+
+    if (nameIndex) {
+      // Make sure that this name is sorted properly and not a duplicate.
+      if (NS_WARN_IF(name <= aObjectStoreNames[nameIndex - 1])) {
+        ASSERT_UNLESS_FUZZING();
+        return nullptr;
+      }
+    }
+
     const uint32_t oldLength = fallibleObjectStores.Length();
 
     Closure closure(name, fallibleObjectStores);
     objectStores.EnumerateRead(Closure::Find, &closure);
 
     if (NS_WARN_IF((oldLength + 1) != fallibleObjectStores.Length())) {
       return nullptr;
     }
   }
 
   nsTArray<nsRefPtr<FullObjectStoreMetadata>> infallibleObjectStores;
   infallibleObjectStores.SwapElements(fallibleObjectStores);
 
   nsRefPtr<NormalTransaction> transaction =
-    new NormalTransaction(this, infallibleObjectStores, aMode);
+    new NormalTransaction(this, aMode, infallibleObjectStores);
 
   MOZ_ASSERT(infallibleObjectStores.IsEmpty());
 
   return transaction.forget().take();
 }
 
 bool
 Database::RecvPBackgroundIDBTransactionConstructor(
@@ -6367,23 +6542,23 @@ Database::RecvPBackgroundIDBTransactionC
     // This is an expected race. We don't want the child to die here, just don't
     // actually do any work.
     return true;
   }
 
   auto* transaction = static_cast<NormalTransaction*>(aActor);
 
   // Add a placeholder for this transaction immediately.
-  gTransactionThreadPool->Dispatch(transaction->TransactionId(),
-                                   mMetadata->mDatabaseId,
-                                   aObjectStoreNames,
-                                   aMode,
-                                   gStartTransactionRunnable,
-                                   /* aFinish */ false,
-                                   /* aFinishCallback */ nullptr);
+  gTransactionThreadPool->Start(transaction->TransactionId(),
+                                mMetadata->mDatabaseId,
+                                aObjectStoreNames,
+                                aMode,
+                                GetLoggingInfo()->Id(),
+                                transaction->LoggingSerialNumber(),
+                                gStartTransactionRunnable);
 
   transaction->SetActive();
 
   if (NS_WARN_IF(!RegisterTransaction(transaction))) {
     IDB_REPORT_INTERNAL_ERR();
     transaction->Abort(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR, /* aForce */ false);
     return true;
   }
@@ -6467,36 +6642,38 @@ Database::RecvClose()
 
   return true;
 }
 
 /*******************************************************************************
  * TransactionBase
  ******************************************************************************/
 
-TransactionBase::TransactionBase(Database* aDatabase,
-                                 Mode aMode)
+TransactionBase::TransactionBase(Database* aDatabase, Mode aMode)
   : mDatabase(aDatabase)
   , mTransactionId(gTransactionThreadPool->NextTransactionId())
   , mDatabaseId(aDatabase->Id())
+  , mLoggingSerialNumber(aDatabase->GetLoggingInfo()->NextTransactionSN(aMode))
   , mActiveRequestCount(0)
   , mInvalidatedOnAnyThread(false)
   , mMode(aMode)
   , mHasBeenActive(false)
   , mActorDestroyed(false)
   , mInvalidated(false)
   , mResultCode(NS_OK)
   , mCommitOrAbortReceived(false)
   , mCommittedOrAborted(false)
   , mForceAborted(false)
   , mTransactionThread(nullptr)
   , mSavepointCount(0)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aDatabase);
+  MOZ_ASSERT(mTransactionId);
+  MOZ_ASSERT(mLoggingSerialNumber);
 }
 
 TransactionBase::~TransactionBase()
 {
   MOZ_ASSERT(!mSavepointCount);
   MOZ_ASSERT(!mActiveRequestCount);
   MOZ_ASSERT(mActorDestroyed);
   MOZ_ASSERT_IF(mHasBeenActive, mCommittedOrAborted);
@@ -7616,18 +7793,18 @@ TransactionBase::ReleaseBackgroundThread
 }
 
 /*******************************************************************************
  * NormalTransaction
  ******************************************************************************/
 
 NormalTransaction::NormalTransaction(
                      Database* aDatabase,
-                     nsTArray<nsRefPtr<FullObjectStoreMetadata>>& aObjectStores,
-                     TransactionBase::Mode aMode)
+                     TransactionBase::Mode aMode,
+                     nsTArray<nsRefPtr<FullObjectStoreMetadata>>& aObjectStores)
   : TransactionBase(aDatabase, aMode)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(!aObjectStores.IsEmpty());
 
   mObjectStores.SwapElements(aObjectStores);
 }
 
@@ -9914,18 +10091,16 @@ DatabaseOfflineStorage::Invalidate()
 
 /*******************************************************************************
  * Local class implementations
  ******************************************************************************/
 
 NS_IMPL_ISUPPORTS(CompressDataBlobsFunction, mozIStorageFunction)
 NS_IMPL_ISUPPORTS(EncodeKeysFunction, mozIStorageFunction)
 
-uint64_t DatabaseOperationBase::sNextSerialNumber = 0;
-
 // static
 void
 DatabaseOperationBase::GetBindingClauseForKeyRange(
                                             const SerializedKeyRange& aKeyRange,
                                             const nsACString& aKeyColumnName,
                                             nsAutoCString& aBindingClause)
 {
   MOZ_ASSERT(!IsOnBackgroundThread());
@@ -10324,17 +10499,19 @@ AutoSetProgressHandler::Register(
 
   return NS_OK;
 }
 
 FactoryOp::FactoryOp(Factory* aFactory,
                      already_AddRefed<ContentParent> aContentParent,
                      const CommonFactoryRequestParams& aCommonParams,
                      bool aDeleting)
-  : mFactory(aFactory)
+  : DatabaseOperationBase(aFactory->GetLoggingInfo()->Id(),
+                          aFactory->GetLoggingInfo()->NextRequestSN())
+  , mFactory(aFactory)
   , mContentParent(Move(aContentParent))
   , mCommonParams(aCommonParams)
   , mState(State_Initial)
   , mIsApp(false)
   , mHasUnlimStoragePerm(false)
   , mEnforcingQuota(true)
   , mDeleting(aDeleting)
   , mBlockedQuotaManager(false)
@@ -11071,17 +11248,17 @@ OpenDatabaseOp::QuotaManagerOpen()
 
 nsresult
 OpenDatabaseOp::DoDatabaseWork()
 {
   AssertIsOnIOThread();
   MOZ_ASSERT(mState == State_DatabaseWorkOpen);
 
   PROFILER_LABEL("IndexedDB",
-                 "OpenDatabaseHelper::DoDatabaseWork",
+                 "OpenDatabaseOp::DoDatabaseWork",
                  js::ProfileEntry::Category::STORAGE);
 
   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) ||
       !OperationMayProceed()) {
     IDB_REPORT_INTERNAL_ERR();
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
@@ -11644,37 +11821,44 @@ OpenDatabaseOp::SendBlockedNotification(
 }
 
 nsresult
 OpenDatabaseOp::DispatchToWorkThread()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mState == State_WaitingForTransactionsToComplete);
   MOZ_ASSERT(mVersionChangeTransaction);
+  MOZ_ASSERT(mVersionChangeTransaction->GetMode() ==
+               IDBTransaction::VERSION_CHANGE);
   MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
 
   if (IsActorDestroyed()) {
     IDB_REPORT_INTERNAL_ERR();
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   mState = State_DatabaseWorkVersionChange;
 
   // Intentionally empty.
   nsTArray<nsString> objectStoreNames;
 
+  const int64_t loggingSerialNumber =
+    mVersionChangeTransaction->LoggingSerialNumber();
+  const nsID& backgroundChildLoggingId =
+    mVersionChangeTransaction->GetLoggingInfo()->Id();
+
   nsRefPtr<VersionChangeOp> versionChangeOp = new VersionChangeOp(this);
 
-  gTransactionThreadPool->Dispatch(mVersionChangeTransaction->TransactionId(),
-                                   mVersionChangeTransaction->DatabaseId(),
-                                   objectStoreNames,
-                                   mVersionChangeTransaction->GetMode(),
-                                   versionChangeOp,
-                                   /* aFinish */ false,
-                                   /* aFinishCallback */ nullptr);
+  gTransactionThreadPool->Start(mVersionChangeTransaction->TransactionId(),
+                                mVersionChangeTransaction->DatabaseId(),
+                                objectStoreNames,
+                                mVersionChangeTransaction->GetMode(),
+                                backgroundChildLoggingId,
+                                loggingSerialNumber,
+                                versionChangeOp);
 
   mVersionChangeTransaction->SetActive();
 
   mVersionChangeTransaction->NoteActiveRequest();
 
   if (NS_WARN_IF(!mDatabase->RegisterTransaction(mVersionChangeTransaction))) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
@@ -12104,17 +12288,17 @@ VersionChangeOp::DoDatabaseWork(Transact
 
   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) ||
       !OperationMayProceed()) {
     IDB_REPORT_INTERNAL_ERR();
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   PROFILER_LABEL("IndexedDB",
-                 "VersionChangeOp::DoDatabaseWork",
+                 "OpenDatabaseOp::VersionChangeOp::DoDatabaseWork",
                  js::ProfileEntry::Category::STORAGE);
 
   mozIStorageConnection* connection = aTransaction->Connection();
   MOZ_ASSERT(connection);
 
   TransactionBase::AutoSavepoint autoSave;
   nsresult rv = autoSave.Start(aTransaction);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -12792,20 +12976,37 @@ VersionChangeOp::Run()
                                                          NS_DISPATCH_NORMAL)));
   }
 
   return NS_OK;
 }
 
 TransactionDatabaseOperationBase::TransactionDatabaseOperationBase(
                                                   TransactionBase* aTransaction)
-  : mTransaction(aTransaction)
+  : DatabaseOperationBase(aTransaction->GetLoggingInfo()->Id(),
+                          aTransaction->GetLoggingInfo()->NextRequestSN())
+  , mTransaction(aTransaction)
+  , mTransactionLoggingSerialNumber(aTransaction->LoggingSerialNumber())
   , mTransactionIsAborted(aTransaction->IsAborted())
 {
   MOZ_ASSERT(aTransaction);
+  MOZ_ASSERT(LoggingSerialNumber());
+}
+
+TransactionDatabaseOperationBase::TransactionDatabaseOperationBase(
+                                                  TransactionBase* aTransaction,
+                                                  uint64_t aLoggingSerialNumber)
+  : DatabaseOperationBase(aTransaction->GetLoggingInfo()->Id(),
+                          aLoggingSerialNumber)
+  , mTransaction(aTransaction)
+  , mTransactionLoggingSerialNumber(aTransaction->LoggingSerialNumber())
+  , mTransactionIsAborted(aTransaction->IsAborted())
+{
+  MOZ_ASSERT(aTransaction);
+  MOZ_ASSERT(LoggingSerialNumber());
 }
 
 TransactionDatabaseOperationBase::~TransactionDatabaseOperationBase()
 {
   MOZ_ASSERT(!mTransaction,
              "TransactionDatabaseOperationBase::Cleanup() was not called by a "
              "subclass!");
 }
@@ -12837,16 +13038,20 @@ TransactionDatabaseOperationBase::Dispat
 
 void
 TransactionDatabaseOperationBase::RunOnTransactionThread()
 {
   MOZ_ASSERT(!IsOnBackgroundThread());
   MOZ_ASSERT(mTransaction);
   MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
 
+  PROFILER_LABEL("IndexedDB",
+                 "TransactionDatabaseOperationBase::RunOnTransactionThread",
+                 js::ProfileEntry::Category::STORAGE);
+
   // There are several cases where we don't actually have to to any work here.
 
   if (mTransactionIsAborted) {
     // This transaction is already set to be aborted.
     mResultCode = NS_ERROR_DOM_INDEXEDDB_ABORT_ERR;
   } else if (mTransaction->IsInvalidatedOnAnyThread()) {
     // This transaction is being invalidated.
     mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
@@ -12863,17 +13068,32 @@ TransactionDatabaseOperationBase::RunOnT
     } else {
       mTransaction->AssertIsOnTransactionThread();
 
       AutoSetProgressHandler autoProgress;
       rv = autoProgress.Register(this, mTransaction->Connection());
       if (NS_WARN_IF(NS_FAILED(rv))) {
         mResultCode = rv;
       } else {
+        IDB_LOG_MARK("IndexedDB %s: Parent Transaction[%lld] Request[%llu]: "
+                       "Beginning database work",
+                     "IndexedDB %s: P T[%lld] R[%llu]: DB Start",
+                     IDB_LOG_ID_STRING(mBackgroundChildLoggingId),
+                     mTransactionLoggingSerialNumber,
+                     mLoggingSerialNumber);
+
         rv = DoDatabaseWork(mTransaction);
+
+        IDB_LOG_MARK("IndexedDB %s: Parent Transaction[%lld] Request[%llu]: "
+                       "Finished database work",
+                     "IndexedDB %s: P T[%lld] R[%llu]: DB End",
+                     IDB_LOG_ID_STRING(mBackgroundChildLoggingId),
+                     mTransactionLoggingSerialNumber,
+                     mLoggingSerialNumber);
+
         if (NS_FAILED(rv)) {
           mResultCode = rv;
         }
       }
     }
   }
 
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->Dispatch(this,
@@ -12945,16 +13165,28 @@ TransactionDatabaseOperationBase::Run()
     RunOnOwningThread();
   } else {
     RunOnTransactionThread();
   }
 
   return NS_OK;
 }
 
+TransactionBase::
+
+CommitOp::CommitOp(TransactionBase* aTransaction, nsresult aResultCode)
+  : DatabaseOperationBase(aTransaction->GetLoggingInfo()->Id(),
+                          aTransaction->GetLoggingInfo()->NextRequestSN())
+  , mTransaction(aTransaction)
+  , mResultCode(aResultCode)
+{
+  MOZ_ASSERT(aTransaction);
+  MOZ_ASSERT(LoggingSerialNumber());
+}
+
 nsresult
 TransactionBase::
 CommitOp::WriteAutoIncrementCounts()
 {
   AssertIsOnTransactionThread();
   MOZ_ASSERT(mTransaction);
 
   const nsTArray<nsRefPtr<FullObjectStoreMetadata>>& metadataArray =
@@ -13050,16 +13282,23 @@ CommitOp::Run()
   nsCOMPtr<mozIStorageConnection>& connection = mTransaction->mConnection;
 
   if (!connection) {
     return NS_OK;
   }
 
   AssertIsOnTransactionThread();
 
+  IDB_LOG_MARK("IndexedDB %s: Parent Transaction[%lld] Request[%llu]: "
+                 "Beginning database work",
+               "IndexedDB %s: P T[%lld] R[%llu]: DB Start",
+               IDB_LOG_ID_STRING(mBackgroundChildLoggingId),
+               mTransaction->LoggingSerialNumber(),
+               mLoggingSerialNumber);
+
   if (NS_SUCCEEDED(mResultCode) && mTransaction->mUpdateFileRefcountFunction) {
     mResultCode = mTransaction->
       mUpdateFileRefcountFunction->WillCommit(connection);
   }
 
   if (NS_SUCCEEDED(mResultCode)) {
     mResultCode = WriteAutoIncrementCounts();
   }
@@ -13090,16 +13329,23 @@ CommitOp::Run()
 
   if (mTransaction->mUpdateFileRefcountFunction) {
     NS_NAMED_LITERAL_CSTRING(functionName, "update_refcount");
     MOZ_ALWAYS_TRUE(NS_SUCCEEDED(connection->RemoveFunction(functionName)));
   }
 
   mTransaction->ReleaseTransactionThreadObjects();
 
+  IDB_LOG_MARK("IndexedDB %s: Parent Transaction[%lld] Request[%llu]: "
+                 "Finished database work",
+               "IndexedDB %s: P T[%lld] R[%llu]: DB End",
+               IDB_LOG_ID_STRING(mBackgroundChildLoggingId),
+               mTransaction->LoggingSerialNumber(),
+               mLoggingSerialNumber);
+
   return NS_OK;
 }
 
 void
 TransactionBase::
 CommitOp::TransactionFinishedBeforeUnblock()
 {
   AssertIsOnBackgroundThread();
@@ -13116,23 +13362,24 @@ CommitOp::TransactionFinishedBeforeUnblo
 
 void
 TransactionBase::
 CommitOp::TransactionFinishedAfterUnblock()
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(mTransaction);
 
-  PROFILER_LABEL("IndexedDB",
-                 "CommitOp::TransactionFinishedAfterUnblock",
-                 js::ProfileEntry::Category::STORAGE);
-
-  IDB_PROFILER_MARK("IndexedDB Transaction %llu: Complete (rv = %lu)",
-                    "IDBTransaction[%llu] MT Complete",
-                    mTransaction->TransactionId(), mResultCode);
+  if (!mTransaction->IsActorDestroyed()) {
+    IDB_LOG_MARK("IndexedDB %s: Parent Transaction[%lld]: "
+                   "Finished with result 0x%x",
+                 "IndexedDB %s: P T[%lld]: Transaction finished (0x%x)",
+                 IDB_LOG_ID_STRING(mTransaction->GetLoggingInfo()->Id()),
+                 mTransaction->LoggingSerialNumber(),
+                 mResultCode);
+  }
 
   mTransaction->ReleaseBackgroundThreadObjects();
 
   if (!mTransaction->IsActorDestroyed()) {
     mTransaction->SendCompleteNotification(ClampResultCode(mResultCode));
   }
 
   mTransaction->GetDatabase()->UnregisterTransaction(mTransaction);
@@ -16599,16 +16846,20 @@ OpenOp::DoDatabaseWork(TransactionBase* 
   MOZ_ASSERT(aTransaction);
   aTransaction->AssertIsOnTransactionThread();
   MOZ_ASSERT(mCursor);
   MOZ_ASSERT(mCursor->mContinueQuery.IsEmpty());
   MOZ_ASSERT(mCursor->mContinueToQuery.IsEmpty());
   MOZ_ASSERT(mCursor->mKey.IsUnset());
   MOZ_ASSERT(mCursor->mRangeKey.IsUnset());
 
+  PROFILER_LABEL("IndexedDB",
+                 "Cursor::OpenOp::DoDatabaseWork",
+                 js::ProfileEntry::Category::STORAGE);
+
   nsresult rv;
 
   switch (mCursor->mType) {
     case OpenCursorParams::TObjectStoreOpenCursorParams:
       rv = DoObjectStoreDatabaseWork(aTransaction);
       break;
 
     case OpenCursorParams::TObjectStoreOpenKeyCursorParams:
--- a/dom/indexedDB/ActorsParent.h
+++ b/dom/indexedDB/ActorsParent.h
@@ -2,40 +2,43 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_indexeddb_actorsparent_h__
 #define mozilla_dom_indexeddb_actorsparent_h__
 
 template <class> struct already_AddRefed;
 class nsCString;
+struct nsID;
 class nsIPrincipal;
 class nsPIDOMWindow;
 
 namespace mozilla {
 namespace dom {
 
 class TabParent;
 
 namespace quota {
 
 class Client;
 
 } // namespace quota
 
 namespace indexedDB {
 
+class LoggingInfo;
 class PBackgroundIDBFactoryParent;
 class PIndexedDBPermissionRequestParent;
 
 PBackgroundIDBFactoryParent*
-AllocPBackgroundIDBFactoryParent();
+AllocPBackgroundIDBFactoryParent(const LoggingInfo& aLoggingInfo);
 
 bool
-RecvPBackgroundIDBFactoryConstructor(PBackgroundIDBFactoryParent* aActor);
+RecvPBackgroundIDBFactoryConstructor(PBackgroundIDBFactoryParent* aActor,
+                                     const LoggingInfo& aLoggingInfo);
 
 bool
 DeallocPBackgroundIDBFactoryParent(PBackgroundIDBFactoryParent* aActor);
 
 PIndexedDBPermissionRequestParent*
 AllocPIndexedDBPermissionRequestParent(nsPIDOMWindow* aWindow,
                                        nsIPrincipal* aPrincipal);
 
--- a/dom/indexedDB/IDBCursor.cpp
+++ b/dom/indexedDB/IDBCursor.cpp
@@ -421,46 +421,51 @@ IDBCursor::Continue(JSContext* aCx,
         }
         break;
 
       default:
         MOZ_CRASH("Unknown direction type!");
     }
   }
 
+  const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
+  mRequest->SetLoggingSerialNumber(requestSerialNumber);
+
+  if (mType == Type_ObjectStore || mType == Type_ObjectStoreKey) {
+    IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                   "database(%s).transaction(%s).objectStore(%s)."
+                   "cursor(%s).continue(%s)",
+                 "IndexedDB %s: C T[%lld] R[%llu]: IDBCursor.continue()",
+                 IDB_LOG_ID_STRING(),
+                 mTransaction->LoggingSerialNumber(),
+                 requestSerialNumber,
+                 IDB_LOG_STRINGIFY(mTransaction->Database()),
+                 IDB_LOG_STRINGIFY(mTransaction),
+                 IDB_LOG_STRINGIFY(mSourceObjectStore),
+                 IDB_LOG_STRINGIFY(mDirection),
+                 IDB_LOG_STRINGIFY(key));
+  } else {
+    IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                   "database(%s).transaction(%s).objectStore(%s)."
+                   "index(%s).cursor(%s).continue(%s)",
+                 "IndexedDB %s: C T[%lld] R[%llu]: IDBCursor.continue()",
+                 IDB_LOG_ID_STRING(),
+                 mTransaction->LoggingSerialNumber(),
+                 requestSerialNumber,
+                 IDB_LOG_STRINGIFY(mTransaction->Database()),
+                 IDB_LOG_STRINGIFY(mTransaction),
+                 IDB_LOG_STRINGIFY(mSourceIndex->ObjectStore()),
+                 IDB_LOG_STRINGIFY(mSourceIndex),
+                 IDB_LOG_STRINGIFY(mDirection),
+                 IDB_LOG_STRINGIFY(key));
+  }
+
   mBackgroundActor->SendContinueInternal(ContinueParams(key));
 
   mContinueCalled = true;
-
-#ifdef IDB_PROFILER_USE_MARKS
-  if (mType == Type_ObjectStore || mType == Type_ObjectStoreKey) {
-    IDB_PROFILER_MARK("IndexedDB Request %llu: "
-                      "database(%s).transaction(%s).objectStore(%s).cursor(%s)."
-                      "continue(%s)",
-                      "IDBRequest[%llu] MT IDBCursor.continue()",
-                      Request()->GetSerialNumber(),
-                      IDB_PROFILER_STRING(Transaction()->Database()),
-                      IDB_PROFILER_STRING(Transaction()),
-                      IDB_PROFILER_STRING(mSourceObjectStore),
-                      IDB_PROFILER_STRING(mDirection),
-                      key.IsUnset() ? "" : IDB_PROFILER_STRING(key));
-  } else {
-    IDB_PROFILER_MARK("IndexedDB Request %llu: "
-                      "database(%s).transaction(%s).objectStore(%s).index(%s)."
-                      "cursor(%s).continue(%s)",
-                      "IDBRequest[%llu] MT IDBCursor.continue()",
-                      Request()->GetSerialNumber(),
-                      IDB_PROFILER_STRING(Transaction()->Database()),
-                      IDB_PROFILER_STRING(Transaction()),
-                      IDB_PROFILER_STRING(mSourceIndex->ObjectStore()),
-                      IDB_PROFILER_STRING(mSourceIndex),
-                      IDB_PROFILER_STRING(mDirection),
-                      key.IsUnset() ? "" : IDB_PROFILER_STRING(key));
-  }
-#endif
 }
 
 void
 IDBCursor::Advance(uint32_t aCount, ErrorResult &aRv)
 {
   AssertIsOnOwningThread();
 
   if (!mTransaction->IsOpen()) {
@@ -473,46 +478,51 @@ IDBCursor::Advance(uint32_t aCount, Erro
     return;
   }
 
   if (!aCount) {
     aRv.ThrowTypeError(MSG_INVALID_ADVANCE_COUNT);
     return;
   }
 
+  const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
+  mRequest->SetLoggingSerialNumber(requestSerialNumber);
+
+  if (mType == Type_ObjectStore || mType == Type_ObjectStoreKey) {
+    IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                   "database(%s).transaction(%s).objectStore(%s)."
+                   "cursor(%s).advance(%ld)",
+                 "IndexedDB %s: C T[%lld] R[%llu]: IDBCursor.advance()",
+                 IDB_LOG_ID_STRING(),
+                 mTransaction->LoggingSerialNumber(),
+                 requestSerialNumber,
+                 IDB_LOG_STRINGIFY(mTransaction->Database()),
+                 IDB_LOG_STRINGIFY(mTransaction),
+                 IDB_LOG_STRINGIFY(mSourceObjectStore),
+                 IDB_LOG_STRINGIFY(mDirection),
+                 aCount);
+  } else {
+    IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                   "database(%s).transaction(%s).objectStore(%s)."
+                   "index(%s).cursor(%s).advance(%ld)",
+                 "IndexedDB %s: C T[%lld] R[%llu]: IDBCursor.advance()",
+                 IDB_LOG_ID_STRING(),
+                 mTransaction->LoggingSerialNumber(),
+                 requestSerialNumber,
+                 IDB_LOG_STRINGIFY(mTransaction->Database()),
+                 IDB_LOG_STRINGIFY(mTransaction),
+                 IDB_LOG_STRINGIFY(mSourceIndex->ObjectStore()),
+                 IDB_LOG_STRINGIFY(mSourceIndex),
+                 IDB_LOG_STRINGIFY(mDirection),
+                 aCount);
+  }
+
   mBackgroundActor->SendContinueInternal(AdvanceParams(aCount));
 
   mContinueCalled = true;
-
-#ifdef IDB_PROFILER_USE_MARKS
-  {
-    if (mType == Type_ObjectStore || mType == Type_ObjectStoreKey) {
-      IDB_PROFILER_MARK("IndexedDB Request %llu: "
-                        "database(%s).transaction(%s).objectStore(%s)."
-                        "cursor(%s).advance(%ld)",
-                        "IDBRequest[%llu] MT IDBCursor.advance()",
-                        Request()->GetSerialNumber(),
-                        IDB_PROFILER_STRING(Transaction()->Database()),
-                        IDB_PROFILER_STRING(Transaction()),
-                        IDB_PROFILER_STRING(mSourceObjectStore),
-                        IDB_PROFILER_STRING(mDirection), aCount);
-    } else {
-      IDB_PROFILER_MARK("IndexedDB Request %llu: "
-                        "database(%s).transaction(%s).objectStore(%s)."
-                        "index(%s).cursor(%s).advance(%ld)",
-                        "IDBRequest[%llu] MT IDBCursor.advance()",
-                        Request()->GetSerialNumber(),
-                        IDB_PROFILER_STRING(Transaction()->Database()),
-                        IDB_PROFILER_STRING(Transaction()),
-                        IDB_PROFILER_STRING(mSourceIndex->ObjectStore()),
-                        IDB_PROFILER_STRING(mSourceIndex),
-                        IDB_PROFILER_STRING(mDirection), aCount);
-    }
-  }
-#endif
 }
 
 already_AddRefed<IDBRequest>
 IDBCursor::Update(JSContext* aCx, JS::Handle<JS::Value> aValue,
                   ErrorResult& aRv)
 {
   AssertIsOnOwningThread();
 
@@ -558,67 +568,74 @@ IDBCursor::Update(JSContext* aCx, JS::Ha
       return nullptr;
     }
 
     if (key != primaryKey) {
       aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
       return nullptr;
     }
 
-    request = objectStore->Put(aCx, aValue, JS::UndefinedHandleValue, aRv);
+    request = objectStore->AddOrPut(aCx,
+                                    aValue,
+                                    /* aKey */ JS::UndefinedHandleValue,
+                                    /* aOverwrite */ true,
+                                    /* aFromCursor */ true,
+                                    aRv);
     if (aRv.Failed()) {
       return nullptr;
     }
   }
   else {
     JS::Rooted<JS::Value> keyVal(aCx);
     aRv = primaryKey.ToJSVal(aCx, &keyVal);
     if (aRv.Failed()) {
       return nullptr;
     }
 
-    request = objectStore->Put(aCx, aValue, keyVal, aRv);
+    request = objectStore->AddOrPut(aCx,
+                                    aValue,
+                                    keyVal,
+                                    /* aOverwrite */ true,
+                                    /* aFromCursor */ true,
+                                    aRv);
     if (aRv.Failed()) {
       return nullptr;
     }
   }
 
   request->SetSource(this);
 
-#ifdef IDB_PROFILER_USE_MARKS
-  {
-    uint64_t requestSerial = request->GetSerialNumber();
-    if (mType == Type_ObjectStore) {
-      IDB_PROFILER_MARK("IndexedDB Request %llu: "
-                        "database(%s).transaction(%s).objectStore(%s)."
-                        "cursor(%s).update(%s)",
-                        "IDBRequest[%llu] MT IDBCursor.update()",
-                        requestSerial,
-                        IDB_PROFILER_STRING(mTransaction->Database()),
-                        IDB_PROFILER_STRING(mTransaction),
-                        IDB_PROFILER_STRING(objectStore),
-                        IDB_PROFILER_STRING(mDirection),
-                        mObjectStore->HasValidKeyPath() ? "" :
-                          IDB_PROFILER_STRING(primaryKey));
-    } else {
-      IDB_PROFILER_MARK("IndexedDB Request %llu: "
-                        "database(%s).transaction(%s).objectStore(%s)."
-                        "index(%s).cursor(%s).update(%s)",
-                        "IDBRequest[%llu] MT IDBCursor.update()",
-                        requestSerial,
-                        IDB_PROFILER_STRING(mTransaction->Database()),
-                        IDB_PROFILER_STRING(mTransaction),
-                        IDB_PROFILER_STRING(objectStore),
-                        IDB_PROFILER_STRING(mSourceIndex),
-                        IDB_PROFILER_STRING(mDirection),
-                        mObjectStore->HasValidKeyPath() ? "" :
-                          IDB_PROFILER_STRING(primaryKey));
-    }
+  if (mType == Type_ObjectStore) {
+    IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                   "database(%s).transaction(%s).objectStore(%s)."
+                   "cursor(%s).update(%s)",
+                 "IndexedDB %s: C T[%lld] R[%llu]: IDBCursor.update()",
+                 IDB_LOG_ID_STRING(),
+                 mTransaction->LoggingSerialNumber(),
+                 request->LoggingSerialNumber(),
+                 IDB_LOG_STRINGIFY(mTransaction->Database()),
+                 IDB_LOG_STRINGIFY(mTransaction),
+                 IDB_LOG_STRINGIFY(objectStore),
+                 IDB_LOG_STRINGIFY(mDirection),
+                 IDB_LOG_STRINGIFY(objectStore, primaryKey));
+  } else {
+    IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                   "database(%s).transaction(%s).objectStore(%s)."
+                   "index(%s).cursor(%s).update(%s)",
+                 "IndexedDB %s: C T[%lld] R[%llu]: IDBCursor.update()",
+                 IDB_LOG_ID_STRING(),
+                 mTransaction->LoggingSerialNumber(),
+                 request->LoggingSerialNumber(),
+                 IDB_LOG_STRINGIFY(mTransaction->Database()),
+                 IDB_LOG_STRINGIFY(mTransaction),
+                 IDB_LOG_STRINGIFY(objectStore),
+                 IDB_LOG_STRINGIFY(mSourceIndex),
+                 IDB_LOG_STRINGIFY(mDirection),
+                 IDB_LOG_STRINGIFY(objectStore, primaryKey));
   }
-#endif
 
   return request.forget();
 }
 
 already_AddRefed<IDBRequest>
 IDBCursor::Delete(JSContext* aCx, ErrorResult& aRv)
 {
   AssertIsOnOwningThread();
@@ -653,54 +670,52 @@ IDBCursor::Delete(JSContext* aCx, ErrorR
   const Key& primaryKey = (mType == Type_ObjectStore) ? mKey : mPrimaryKey;
 
   JS::Rooted<JS::Value> key(aCx);
   aRv = primaryKey.ToJSVal(aCx, &key);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  nsRefPtr<IDBRequest> request = objectStore->Delete(aCx, key, aRv);
+  nsRefPtr<IDBRequest> request =
+    objectStore->DeleteInternal(aCx, key, /* aFromCursor */ true, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   request->SetSource(this);
 
-#ifdef IDB_PROFILER_USE_MARKS
-  {
-    uint64_t requestSerial = request->GetSerialNumber();
-    if (mType == Type_ObjectStore) {
-      IDB_PROFILER_MARK("IndexedDB Request %llu: "
-                        "database(%s).transaction(%s).objectStore(%s)."
-                        "cursor(%s).delete(%s)",
-                        "IDBRequest[%llu] MT IDBCursor.delete()",
-                        requestSerial,
-                        IDB_PROFILER_STRING(mTransaction->Database()),
-                        IDB_PROFILER_STRING(mTransaction),
-                        IDB_PROFILER_STRING(objectStore),
-                        IDB_PROFILER_STRING(mDirection),
-                        mObjectStore->HasValidKeyPath() ? "" :
-                          IDB_PROFILER_STRING(primaryKey));
-    } else {
-      IDB_PROFILER_MARK("IndexedDB Request %llu: "
-                        "database(%s).transaction(%s).objectStore(%s)."
-                        "index(%s).cursor(%s).delete(%s)",
-                        "IDBRequest[%llu] MT IDBCursor.delete()",
-                        requestSerial,
-                        IDB_PROFILER_STRING(mTransaction->Database()),
-                        IDB_PROFILER_STRING(mTransaction),
-                        IDB_PROFILER_STRING(objectStore),
-                        IDB_PROFILER_STRING(mSourceIndex),
-                        IDB_PROFILER_STRING(mDirection),
-                        mObjectStore->HasValidKeyPath() ? "" :
-                          IDB_PROFILER_STRING(primaryKey));
-    }
+  if (mType == Type_ObjectStore) {
+  IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                 "database(%s).transaction(%s).objectStore(%s)."
+                 "cursor(%s).delete(%s)",
+               "IndexedDB %s: C T[%lld] R[%llu]: IDBCursor.delete()",
+               IDB_LOG_ID_STRING(),
+               mTransaction->LoggingSerialNumber(),
+               request->LoggingSerialNumber(),
+               IDB_LOG_STRINGIFY(mTransaction->Database()),
+               IDB_LOG_STRINGIFY(mTransaction),
+               IDB_LOG_STRINGIFY(objectStore),
+               IDB_LOG_STRINGIFY(mDirection),
+               IDB_LOG_STRINGIFY(objectStore, primaryKey));
+  } else {
+    IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                   "database(%s).transaction(%s).objectStore(%s)."
+                   "index(%s).cursor(%s).delete(%s)",
+                 "IndexedDB %s: C T[%lld] R[%llu]: IDBCursor.delete()",
+                 IDB_LOG_ID_STRING(),
+                 mTransaction->LoggingSerialNumber(),
+                 request->LoggingSerialNumber(),
+                 IDB_LOG_STRINGIFY(mTransaction->Database()),
+                 IDB_LOG_STRINGIFY(mTransaction),
+                 IDB_LOG_STRINGIFY(objectStore),
+                 IDB_LOG_STRINGIFY(mSourceIndex),
+                 IDB_LOG_STRINGIFY(mDirection),
+                 IDB_LOG_STRINGIFY(objectStore, primaryKey));
   }
-#endif
 
   return request.forget();
 }
 
 void
 IDBCursor::Reset(Key&& aKey, StructuredCloneReadInfo&& aValue)
 {
   AssertIsOnOwningThread();
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -502,22 +502,30 @@ IDBDatabase::CreateObjectStore(
     // indexes.
     RefreshSpec(/* aMayDelete */ false);
   }
 
   nsRefPtr<IDBObjectStore> objectStore =
     transaction->CreateObjectStore(*newSpec);
   MOZ_ASSERT(objectStore);
 
-  IDB_PROFILER_MARK("IndexedDB Pseudo-request: "
-                    "database(%s).transaction(%s).createObjectStore(%s)",
-                    "MT IDBDatabase.createObjectStore()",
-                    IDB_PROFILER_STRING(this),
-                    IDB_PROFILER_STRING(aTransaction),
-                    IDB_PROFILER_STRING(objectStore));
+  // Don't do this in the macro because we always need to increment the serial
+  // number to keep in sync with the parent.
+  const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
+
+  IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                 "database(%s).transaction(%s).createObjectStore(%s)",
+               "IndexedDB %s: C T[%lld] R[%llu]: "
+                 "IDBDatabase.createObjectStore()",
+               IDB_LOG_ID_STRING(),
+               transaction->LoggingSerialNumber(),
+               requestSerialNumber,
+               IDB_LOG_STRINGIFY(this),
+               IDB_LOG_STRINGIFY(transaction),
+               IDB_LOG_STRINGIFY(objectStore));
 
   return objectStore.forget();
 }
 
 void
 IDBDatabase::DeleteObjectStore(const nsAString& aName, ErrorResult& aRv)
 {
   AssertIsOnOwningThread();
@@ -555,22 +563,30 @@ IDBDatabase::DeleteObjectStore(const nsA
     }
   }
 
   if (!objectStoreId) {
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR);
     return;
   }
 
-  IDB_PROFILER_MARK("IndexedDB Pseudo-request: "
-                    "database(%s).transaction(%s).deleteObjectStore(\"%s\")",
-                    "MT IDBDatabase.deleteObjectStore()",
-                    IDB_PROFILER_STRING(this),
-                    IDB_PROFILER_STRING(transaction),
-                    NS_ConvertUTF16toUTF8(aName).get());
+  // Don't do this in the macro because we always need to increment the serial
+  // number to keep in sync with the parent.
+  const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
+
+  IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                 "database(%s).transaction(%s).deleteObjectStore(\"%s\")",
+               "IndexedDB %s: C T[%lld] R[%llu]: "
+                 "IDBDatabase.deleteObjectStore()",
+               IDB_LOG_ID_STRING(),
+               transaction->LoggingSerialNumber(),
+               requestSerialNumber,
+               IDB_LOG_STRINGIFY(this),
+               IDB_LOG_STRINGIFY(transaction),
+               NS_ConvertUTF16toUTF8(aName).get());
 }
 
 already_AddRefed<IDBTransaction>
 IDBDatabase::Transaction(const nsAString& aStoreName,
                          IDBTransactionMode aMode,
                          ErrorResult& aRv)
 {
   AssertIsOnOwningThread();
@@ -665,28 +681,31 @@ IDBDatabase::Transaction(const Sequence<
 
   nsRefPtr<IDBTransaction> transaction =
     IDBTransaction::Create(this, sortedStoreNames, mode);
   MOZ_ASSERT(transaction);
 
   BackgroundTransactionChild* actor =
     new BackgroundTransactionChild(transaction);
 
+  IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld]: "
+                 "database(%s).transaction(%s)",
+               "IndexedDB %s: C T[%lld]: IDBDatabase.transaction()",
+               IDB_LOG_ID_STRING(),
+               transaction->LoggingSerialNumber(),
+               IDB_LOG_STRINGIFY(this),
+               IDB_LOG_STRINGIFY(transaction));
+
   MOZ_ALWAYS_TRUE(
     mBackgroundActor->SendPBackgroundIDBTransactionConstructor(actor,
                                                                sortedStoreNames,
                                                                mode));
 
   transaction->SetBackgroundActor(actor);
 
-  IDB_PROFILER_MARK("IndexedDB Transaction %llu: database(%s).transaction(%s)",
-                    "IDBTransaction[%llu] MT Started",
-                    transaction->GetSerialNumber(), IDB_PROFILER_STRING(this),
-                    IDB_PROFILER_STRING(transaction));
-
   return transaction.forget();
 }
 
 StorageType
 IDBDatabase::Storage() const
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mSpec);
@@ -718,16 +737,18 @@ IDBDatabase::CreateMutableFile(const nsA
 
   nsRefPtr<IDBRequest> request = IDBRequest::Create(this, nullptr);
 
   nsString type;
   if (aType.WasPassed()) {
     type = aType.Value();
   }
 
+  mFactory->IncrementParentLoggingRequestSerialNumber();
+
   aRv = CreateFileHelper::CreateAndDispatch(this, request, aName, type);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return request.forget();
 }
 
--- a/dom/indexedDB/IDBFactory.cpp
+++ b/dom/indexedDB/IDBFactory.cpp
@@ -1,32 +1,35 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "IDBFactory.h"
 
+#include "BackgroundChildImpl.h"
 #include "IDBRequest.h"
 #include "IndexedDatabaseManager.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/IDBFactoryBinding.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/PBackground.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "nsGlobalWindow.h"
 #include "nsIIPCBackgroundChildCreateCallback.h"
 #include "nsILoadContext.h"
 #include "nsIPrincipal.h"
+#include "nsIUUIDGenerator.h"
 #include "nsIWebNavigation.h"
+#include "nsServiceManagerUtils.h"
 #include "ProfilerHelpers.h"
 #include "ReportInternalError.h"
 
 // Include this last to avoid path problems on Windows.
 #include "ActorsChild.h"
 
 #ifdef DEBUG
 #include "nsContentUtils.h" // For assertions.
@@ -71,20 +74,24 @@ GetPrincipalInfoFromPrincipal(nsIPrincip
 }
 
 } // anonymous namespace
 
 class IDBFactory::BackgroundCreateCallback MOZ_FINAL
   : public nsIIPCBackgroundChildCreateCallback
 {
   nsRefPtr<IDBFactory> mFactory;
+  LoggingInfo mLoggingInfo;
 
 public:
-  explicit BackgroundCreateCallback(IDBFactory* aFactory)
-  : mFactory(aFactory)
+  explicit
+  BackgroundCreateCallback(IDBFactory* aFactory,
+                           const LoggingInfo& aLoggingInfo)
+    : mFactory(aFactory)
+    , mLoggingInfo(aLoggingInfo)
   {
     MOZ_ASSERT(aFactory);
   }
 
   NS_DECL_ISUPPORTS
 
 private:
   ~BackgroundCreateCallback()
@@ -301,16 +308,25 @@ IDBFactory::SetBackgroundActor(Backgroun
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aBackgroundActor);
   MOZ_ASSERT(!mBackgroundActor);
 
   mBackgroundActor = aBackgroundActor;
 }
 
+void
+IDBFactory::IncrementParentLoggingRequestSerialNumber()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mBackgroundActor);
+
+  mBackgroundActor->SendIncrementLoggingRequestSerialNumber();
+}
+
 already_AddRefed<IDBOpenDBRequest>
 IDBFactory::Open(const nsAString& aName,
                  uint64_t aVersion,
                  ErrorResult& aRv)
 {
   return OpenInternal(/* aPrincipal */ nullptr,
                       aName,
                       Optional<uint64_t>(aVersion),
@@ -496,37 +512,54 @@ IDBFactory::OpenInternal(nsIPrincipal* a
   if (aDeleting) {
     metadata.version() = 0;
     params = DeleteDatabaseRequestParams(commonParams);
   } else {
     metadata.version() = version;
     params = OpenDatabaseRequestParams(commonParams);
   }
 
-  if (!mBackgroundActor) {
-    // If another consumer has already created a background actor for this
-    // thread then we can start this request immediately.
-    if (PBackgroundChild* bgActor = BackgroundChild::GetForCurrentThread()) {
-      nsresult rv = BackgroundActorCreated(bgActor);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        IDB_REPORT_INTERNAL_ERR();
-        aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-        return nullptr;
+  if (!mBackgroundActor && mPendingRequests.IsEmpty()) {
+    // We need to start the sequence to create a background actor for this
+    // thread.
+    BackgroundChildImpl::ThreadLocal* threadLocal =
+      BackgroundChildImpl::GetThreadLocalForCurrentThread();
+
+    nsAutoPtr<ThreadLocal> newIDBThreadLocal;
+    ThreadLocal* idbThreadLocal;
+
+    if (threadLocal && threadLocal->mIndexedDBThreadLocal) {
+      idbThreadLocal = threadLocal->mIndexedDBThreadLocal;
+    } else {
+      nsCOMPtr<nsIUUIDGenerator> uuidGen =
+        do_GetService("@mozilla.org/uuid-generator;1");
+      MOZ_ASSERT(uuidGen);
+
+      nsID id;
+      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(uuidGen->GenerateUUIDInPlace(&id)));
+
+      newIDBThreadLocal = idbThreadLocal = new ThreadLocal(id);
+    }
+
+    nsRefPtr<BackgroundCreateCallback> cb =
+      new BackgroundCreateCallback(this, idbThreadLocal->GetLoggingInfo());
+    if (NS_WARN_IF(!BackgroundChild::GetOrCreateForCurrentThread(cb))) {
+      IDB_REPORT_INTERNAL_ERR();
+      aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+      return nullptr;
+    }
+
+    if (newIDBThreadLocal) {
+      if (!threadLocal) {
+        threadLocal = BackgroundChildImpl::GetThreadLocalForCurrentThread();
       }
-      MOZ_ASSERT(mBackgroundActor);
-    } else if (mPendingRequests.IsEmpty()) {
-      // We need to start the sequence to create a background actor for this
-      // thread.
-      nsRefPtr<BackgroundCreateCallback> cb =
-        new BackgroundCreateCallback(this);
-      if (NS_WARN_IF(!BackgroundChild::GetOrCreateForCurrentThread(cb))) {
-        IDB_REPORT_INTERNAL_ERR();
-        aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-        return nullptr;
-      }
+      MOZ_ASSERT(threadLocal);
+      MOZ_ASSERT(!threadLocal->mIndexedDBThreadLocal);
+
+      threadLocal->mIndexedDBThreadLocal = newIDBThreadLocal.forget();
     }
   }
 
   AutoJSAPI autoJS;
   nsRefPtr<IDBOpenDBRequest> request;
 
   if (mWindow) {
     AutoJSContext cx;
@@ -545,62 +578,65 @@ IDBFactory::OpenInternal(nsIPrincipal* a
     autoJS.Init(mOwningObject.get());
     JS::Rooted<JSObject*> scriptOwner(autoJS.cx(), mOwningObject);
 
     request = IDBOpenDBRequest::CreateForJS(this, scriptOwner);
   }
 
   MOZ_ASSERT(request);
 
+  if (aDeleting) {
+    IDB_LOG_MARK("IndexedDB %s: Child  Request[%llu]: "
+                   "indexedDB.deleteDatabase(\"%s\")",
+                 "IndexedDB %s: C R[%llu]: IDBFactory.deleteDatabase()",
+                 IDB_LOG_ID_STRING(),
+                 request->LoggingSerialNumber(),
+                 NS_ConvertUTF16toUTF8(aName).get());
+  } else {
+    IDB_LOG_MARK("IndexedDB %s: Child  Request[%llu]: "
+                   "indexedDB.open(\"%s\", %s)",
+                 "IndexedDB %s: C R[%llu]: IDBFactory.open()",
+                 IDB_LOG_ID_STRING(),
+                 request->LoggingSerialNumber(),
+                 NS_ConvertUTF16toUTF8(aName).get(),
+                 IDB_LOG_STRINGIFY(aVersion));
+  }
+
   // If we already have a background actor then we can start this request now.
   if (mBackgroundActor) {
     nsresult rv = InitiateRequest(request, params);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       IDB_REPORT_INTERNAL_ERR();
       aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
       return nullptr;
     }
   } else {
     mPendingRequests.AppendElement(new PendingRequestInfo(request, params));
   }
 
-#ifdef IDB_PROFILER_USE_MARKS
-  {
-    NS_ConvertUTF16toUTF8 profilerName(aName);
-    if (aDeleting) {
-      IDB_PROFILER_MARK("IndexedDB Request %llu: deleteDatabase(\"%s\")",
-                        "MT IDBFactory.deleteDatabase()",
-                        request->GetSerialNumber(), profilerName.get());
-    } else {
-      IDB_PROFILER_MARK("IndexedDB Request %llu: open(\"%s\", %lld)",
-                        "MT IDBFactory.open()",
-                        request->GetSerialNumber(), profilerName.get(),
-                        aVersion);
-    }
-  }
-#endif
-
   return request.forget();
 }
 
 nsresult
-IDBFactory::BackgroundActorCreated(PBackgroundChild* aBackgroundActor)
+IDBFactory::BackgroundActorCreated(PBackgroundChild* aBackgroundActor,
+                                   const LoggingInfo& aLoggingInfo)
 {
   MOZ_ASSERT(aBackgroundActor);
   MOZ_ASSERT(!mBackgroundActor);
   MOZ_ASSERT(!mBackgroundActorFailed);
 
   {
     BackgroundFactoryChild* actor = new BackgroundFactoryChild(this);
 
     MOZ_ASSERT(NS_IsMainThread(), "Fix this windowId stuff for workers!");
 
     mBackgroundActor =
       static_cast<BackgroundFactoryChild*>(
-        aBackgroundActor->SendPBackgroundIDBFactoryConstructor(actor));
+        aBackgroundActor->SendPBackgroundIDBFactoryConstructor(actor,
+                                                               aLoggingInfo));
   }
 
   if (NS_WARN_IF(!mBackgroundActor)) {
     BackgroundActorFailed();
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   nsresult rv = NS_OK;
@@ -730,17 +766,17 @@ void
 IDBFactory::BackgroundCreateCallback::ActorCreated(PBackgroundChild* aActor)
 {
   MOZ_ASSERT(aActor);
   MOZ_ASSERT(mFactory);
 
   nsRefPtr<IDBFactory> factory;
   mFactory.swap(factory);
 
-  factory->BackgroundActorCreated(aActor);
+  factory->BackgroundActorCreated(aActor, mLoggingInfo);
 }
 
 void
 IDBFactory::BackgroundCreateCallback::ActorFailed()
 {
   MOZ_ASSERT(mFactory);
 
   nsRefPtr<IDBFactory> factory;
--- a/dom/indexedDB/IDBFactory.h
+++ b/dom/indexedDB/IDBFactory.h
@@ -37,16 +37,17 @@ namespace dom {
 struct IDBOpenDBOptions;
 class TabChild;
 
 namespace indexedDB {
 
 class BackgroundFactoryChild;
 class FactoryRequestParams;
 class IDBOpenDBRequest;
+class LoggingInfo;
 
 class IDBFactory MOZ_FINAL
   : public nsISupports
   , public nsWrapperCache
 {
   typedef mozilla::dom::StorageType StorageType;
   typedef mozilla::ipc::PBackgroundChild PBackgroundChild;
   typedef mozilla::ipc::PrincipalInfo PrincipalInfo;
@@ -107,16 +108,19 @@ public:
   void
   ClearBackgroundActor()
   {
     AssertIsOnOwningThread();
 
     mBackgroundActor = nullptr;
   }
 
+  void
+  IncrementParentLoggingRequestSerialNumber();
+
   nsPIDOMWindow*
   GetParentObject() const
   {
     return mWindow;
   }
 
   TabChild*
   GetTabChild() const
@@ -203,17 +207,18 @@ private:
   OpenInternal(nsIPrincipal* aPrincipal,
                const nsAString& aName,
                const Optional<uint64_t>& aVersion,
                const Optional<StorageType>& aStorageType,
                bool aDeleting,
                ErrorResult& aRv);
 
   nsresult
-  BackgroundActorCreated(PBackgroundChild* aBackgroundActor);
+  BackgroundActorCreated(PBackgroundChild* aBackgroundActor,
+                         const LoggingInfo& aLoggingInfo);
 
   void
   BackgroundActorFailed();
 
   nsresult
   InitiateRequest(IDBOpenDBRequest* aRequest,
                   const FactoryRequestParams& aParams);
 };
--- a/dom/indexedDB/IDBIndex.cpp
+++ b/dom/indexedDB/IDBIndex.cpp
@@ -259,43 +259,48 @@ IDBIndex::GetInternal(bool aKeyOnly,
     params = IndexGetKeyParams(objectStoreId, indexId, optionalKeyRange);
   } else {
     params = IndexGetParams(objectStoreId, indexId, optionalKeyRange);
   }
 
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   MOZ_ASSERT(request);
 
+  if (aKeyOnly) {
+    IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                   "database(%s).transaction(%s).objectStore(%s).index(%s)."
+                   "getKey(%s)",
+                 "IndexedDB %s: C T[%lld] R[%llu]: IDBIndex.getKey()",
+                 IDB_LOG_ID_STRING(),
+                 transaction->LoggingSerialNumber(),
+                 request->LoggingSerialNumber(),
+                 IDB_LOG_STRINGIFY(transaction->Database()),
+                 IDB_LOG_STRINGIFY(transaction),
+                 IDB_LOG_STRINGIFY(mObjectStore),
+                 IDB_LOG_STRINGIFY(this),
+                 IDB_LOG_STRINGIFY(keyRange));
+  } else {
+    IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                   "database(%s).transaction(%s).objectStore(%s).index(%s)."
+                   "get(%s)",
+                 "IndexedDB %s: C T[%lld] R[%llu]: IDBIndex.get()",
+                 IDB_LOG_ID_STRING(),
+                 transaction->LoggingSerialNumber(),
+                 request->LoggingSerialNumber(),
+                 IDB_LOG_STRINGIFY(transaction->Database()),
+                 IDB_LOG_STRINGIFY(transaction),
+                 IDB_LOG_STRINGIFY(mObjectStore),
+                 IDB_LOG_STRINGIFY(this),
+                 IDB_LOG_STRINGIFY(keyRange));
+  }
+
   BackgroundRequestChild* actor = new BackgroundRequestChild(request);
 
   transaction->StartRequest(actor, params);
 
-  if (aKeyOnly) {
-    IDB_PROFILER_MARK("IndexedDB Request %llu: "
-                      "database(%s).transaction(%s).objectStore(%s).index(%s)."
-                      "getKey(%s)",
-                      "IDBRequest[%llu] MT IDBIndex.getKey()",
-                      request->GetSerialNumber(),
-                      IDB_PROFILER_STRING(transaction->Database()),
-                      IDB_PROFILER_STRING(transaction),
-                      IDB_PROFILER_STRING(mObjectStore),
-                      IDB_PROFILER_STRING(this),
-                      IDB_PROFILER_STRING(aKey));
-  } else {
-    IDB_PROFILER_MARK("IndexedDB Request %llu: "
-                      "database(%s).transaction(%s).objectStore(%s).index(%s)."
-                      "get(%s)",
-                      "IDBRequest[%llu] MT IDBIndex.get()",
-                      request->GetSerialNumber(),
-                      IDB_PROFILER_STRING(transaction->Database()),
-                      IDB_PROFILER_STRING(transaction),
-                      IDB_PROFILER_STRING(mObjectStore),
-                      IDB_PROFILER_STRING(this),
-                      IDB_PROFILER_STRING(aKey));
-  }
   return request.forget();
 }
 
 already_AddRefed<IDBRequest>
 IDBIndex::GetAllInternal(bool aKeysOnly,
                          JSContext* aCx,
                          JS::Handle<JS::Value> aKey,
                          const Optional<uint32_t>& aLimit,
@@ -335,46 +340,50 @@ IDBIndex::GetAllInternal(bool aKeysOnly,
                                    limit);
   } else {
     params = IndexGetAllParams(objectStoreId, indexId, optionalKeyRange, limit);
   }
 
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   MOZ_ASSERT(request);
 
+  if (aKeysOnly) {
+    IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                   "database(%s).transaction(%s).objectStore(%s).index(%s)."
+                   "getAllKeys(%s, %s)",
+                 "IndexedDB %s: C T[%lld] R[%llu]: IDBIndex.getAllKeys()",
+                 IDB_LOG_ID_STRING(),
+                 transaction->LoggingSerialNumber(),
+                 request->LoggingSerialNumber(),
+                 IDB_LOG_STRINGIFY(transaction->Database()),
+                 IDB_LOG_STRINGIFY(transaction),
+                 IDB_LOG_STRINGIFY(mObjectStore),
+                 IDB_LOG_STRINGIFY(this),
+                 IDB_LOG_STRINGIFY(keyRange),
+                 IDB_LOG_STRINGIFY(aLimit));
+  } else {
+    IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                   "database(%s).transaction(%s).objectStore(%s).index(%s)."
+                   "getAll(%s, %s)",
+                 "IndexedDB %s: C T[%lld] R[%llu]: IDBIndex.getAll()",
+                 IDB_LOG_ID_STRING(),
+                 transaction->LoggingSerialNumber(),
+                 request->LoggingSerialNumber(),
+                 IDB_LOG_STRINGIFY(transaction->Database()),
+                 IDB_LOG_STRINGIFY(transaction),
+                 IDB_LOG_STRINGIFY(mObjectStore),
+                 IDB_LOG_STRINGIFY(this),
+                 IDB_LOG_STRINGIFY(keyRange),
+                 IDB_LOG_STRINGIFY(aLimit));
+  }
+
   BackgroundRequestChild* actor = new BackgroundRequestChild(request);
 
   transaction->StartRequest(actor, params);
 
-  if (aKeysOnly) {
-    IDB_PROFILER_MARK("IndexedDB Request %llu: "
-                      "database(%s).transaction(%s).objectStore(%s).index(%s)."
-                      "getAllKeys(%s, %lu)",
-                      "IDBRequest[%llu] MT IDBIndex.getAllKeys()",
-                      request->GetSerialNumber(),
-                      IDB_PROFILER_STRING(transaction->Database()),
-                      IDB_PROFILER_STRING(transaction),
-                      IDB_PROFILER_STRING(mObjectStore),
-                      IDB_PROFILER_STRING(this),
-                      IDB_PROFILER_STRING(aKeyRange),
-                      aLimit);
-  } else {
-    IDB_PROFILER_MARK("IndexedDB Request %llu: "
-                      "database(%s).transaction(%s).objectStore(%s).index(%s)."
-                      "getAll(%s, %lu)",
-                      "IDBRequest[%llu] MT IDBIndex.getAll()",
-                      request->GetSerialNumber(),
-                      IDB_PROFILER_STRING(transaction->Database()),
-                      IDB_PROFILER_STRING(transaction),
-                      IDB_PROFILER_STRING(mObjectStore),
-                      IDB_PROFILER_STRING(this),
-                      IDB_PROFILER_STRING(aKeyRange),
-                      aLimit);
-  }
-
   return request.forget();
 }
 
 already_AddRefed<IDBRequest>
 IDBIndex::OpenCursorInternal(bool aKeysOnly,
                              JSContext* aCx,
                              JS::Handle<JS::Value> aRange,
                              IDBCursorDirection aDirection,
@@ -427,16 +436,47 @@ IDBIndex::OpenCursorInternal(bool aKeysO
     openParams.direction() = direction;
 
     params = Move(openParams);
   }
 
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   MOZ_ASSERT(request);
 
+  if (aKeysOnly) {
+    IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                   "database(%s).transaction(%s).objectStore(%s).index(%s)."
+                   "openKeyCursor(%s, %s)",
+                 "IndexedDB %s: C T[%lld] R[%llu]: IDBIndex.openKeyCursor()",
+                 IDB_LOG_ID_STRING(),
+                 transaction->LoggingSerialNumber(),
+                 request->LoggingSerialNumber(),
+                 IDB_LOG_STRINGIFY(transaction->Database()),
+                 IDB_LOG_STRINGIFY(transaction),
+                 IDB_LOG_STRINGIFY(mObjectStore),
+                 IDB_LOG_STRINGIFY(this),
+                 IDB_LOG_STRINGIFY(keyRange),
+                 IDB_LOG_STRINGIFY(direction));
+  } else {
+    IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                   "database(%s).transaction(%s).objectStore(%s).index(%s)."
+                   "openCursor(%s, %s)",
+                 "IndexedDB %s: C T[%lld] R[%llu]: "
+                   "IDBObjectStore.openKeyCursor()",
+                 IDB_LOG_ID_STRING(),
+                 transaction->LoggingSerialNumber(),
+                 request->LoggingSerialNumber(),
+                 IDB_LOG_STRINGIFY(transaction->Database()),
+                 IDB_LOG_STRINGIFY(transaction),
+                 IDB_LOG_STRINGIFY(mObjectStore),
+                 IDB_LOG_STRINGIFY(this),
+                 IDB_LOG_STRINGIFY(keyRange),
+                 IDB_LOG_STRINGIFY(direction));
+  }
+
   BackgroundCursorChild* actor =
     new BackgroundCursorChild(request, this, direction);
 
   mObjectStore->Transaction()->OpenCursor(actor, params);
 
   return request.forget();
 }
 
@@ -469,31 +509,33 @@ IDBIndex::Count(JSContext* aCx,
     params.optionalKeyRange() = serializedKeyRange;
   } else {
     params.optionalKeyRange() = void_t();
   }
 
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   MOZ_ASSERT(request);
 
+  IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                 "database(%s).transaction(%s).objectStore(%s).index(%s)."
+                 "count(%s)",
+               "IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.count()",
+               IDB_LOG_ID_STRING(),
+               transaction->LoggingSerialNumber(),
+               request->LoggingSerialNumber(),
+               IDB_LOG_STRINGIFY(transaction->Database()),
+               IDB_LOG_STRINGIFY(transaction),
+               IDB_LOG_STRINGIFY(mObjectStore),
+               IDB_LOG_STRINGIFY(this),
+               IDB_LOG_STRINGIFY(keyRange));
+
   BackgroundRequestChild* actor = new BackgroundRequestChild(request);
 
   transaction->StartRequest(actor, params);
 
-  IDB_PROFILER_MARK("IndexedDB Request %llu: "
-                    "database(%s).transaction(%s).objectStore(%s).index(%s)."
-                    "count(%s)",
-                    "IDBRequest[%llu] MT IDBObjectStore.count()",
-                    request->GetSerialNumber(),
-                    IDB_PROFILER_STRING(transaction->Database()),
-                    IDB_PROFILER_STRING(transaction),
-                    IDB_PROFILER_STRING(mObjectStore),
-                    IDB_PROFILER_STRING(this),
-                    IDB_PROFILER_STRING(aKey));
-
   return request.forget();
 }
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBIndex)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBIndex)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBIndex)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -1138,20 +1138,22 @@ IDBObjectStore::GetAddInfo(JSContext* aC
   return rv;
 }
 
 already_AddRefed<IDBRequest>
 IDBObjectStore::AddOrPut(JSContext* aCx,
                          JS::Handle<JS::Value> aValue,
                          JS::Handle<JS::Value> aKey,
                          bool aOverwrite,
+                         bool aFromCursor,
                          ErrorResult& aRv)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aCx);
+  MOZ_ASSERT_IF(aFromCursor, aOverwrite);
 
   if (!mTransaction->IsOpen()) {
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
     return nullptr;
   }
 
   if (!mTransaction->IsWriteAllowed()) {
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
@@ -1244,50 +1246,54 @@ IDBObjectStore::AddOrPut(JSContext* aCx,
     params = ObjectStorePutParams(commonParams);
   } else {
     params = ObjectStoreAddParams(commonParams);
   }
 
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   MOZ_ASSERT(request);
 
+  if (!aFromCursor) {
+    if (aOverwrite) {
+      IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                     "database(%s).transaction(%s).objectStore(%s).put(%s)",
+                   "IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.put()",
+                   IDB_LOG_ID_STRING(),
+                   mTransaction->LoggingSerialNumber(),
+                   request->LoggingSerialNumber(),
+                   IDB_LOG_STRINGIFY(mTransaction->Database()),
+                   IDB_LOG_STRINGIFY(mTransaction),
+                   IDB_LOG_STRINGIFY(this),
+                   IDB_LOG_STRINGIFY(key));
+    } else {
+      IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                     "database(%s).transaction(%s).objectStore(%s).add(%s)",
+                   "IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.add()",
+                   IDB_LOG_ID_STRING(),
+                   mTransaction->LoggingSerialNumber(),
+                   request->LoggingSerialNumber(),
+                   IDB_LOG_STRINGIFY(mTransaction->Database()),
+                   IDB_LOG_STRINGIFY(mTransaction),
+                   IDB_LOG_STRINGIFY(this),
+                   IDB_LOG_STRINGIFY(key));
+    }
+  }
+
   BackgroundRequestChild* actor = new BackgroundRequestChild(request);
 
   mTransaction->StartRequest(actor, params);
 
   if (!fileInfosToKeepAlive.IsEmpty()) {
     nsTArray<nsRefPtr<FileInfo>> fileInfos;
     fileInfosToKeepAlive.SwapElements(fileInfos);
 
     actor->HoldFileInfosUntilComplete(fileInfos);
     MOZ_ASSERT(fileInfos.IsEmpty());
   }
 
-#ifdef IDB_PROFILER_USE_MARKS
-  if (aOverwrite) {
-    IDB_PROFILER_MARK("IndexedDB Request %llu: "
-                      "database(%s).transaction(%s).objectStore(%s).%s(%s)",
-                      "IDBRequest[%llu] MT IDBObjectStore.put()",
-                      request->GetSerialNumber(),
-                      IDB_PROFILER_STRING(Transaction()->Database()),
-                      IDB_PROFILER_STRING(Transaction()),
-                      IDB_PROFILER_STRING(this),
-                      key.IsUnset() ? "" : IDB_PROFILER_STRING(key));
-  } else {
-    IDB_PROFILER_MARK("IndexedDB Request %llu: "
-                      "database(%s).transaction(%s).objectStore(%s).add(%s)",
-                      "IDBRequest[%llu] MT IDBObjectStore.add()",
-                      request->GetSerialNumber(),
-                      IDB_PROFILER_STRING(Transaction()->Database()),
-                      IDB_PROFILER_STRING(Transaction()),
-                      IDB_PROFILER_STRING(this),
-                      key.IsUnset() ? "" : IDB_PROFILER_STRING(key));
-  }
-#endif
-
   return request.forget();
 }
 
 already_AddRefed<IDBRequest>
 IDBObjectStore::GetAllInternal(bool aKeysOnly,
                                JSContext* aCx,
                                JS::Handle<JS::Value> aKey,
                                const Optional<uint32_t>& aLimit,
@@ -1324,46 +1330,48 @@ IDBObjectStore::GetAllInternal(bool aKey
     params = ObjectStoreGetAllKeysParams(id, optionalKeyRange, limit);
   } else {
     params = ObjectStoreGetAllParams(id, optionalKeyRange, limit);
   }
 
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   MOZ_ASSERT(request);
 
+  if (aKeysOnly) {
+    IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                   "database(%s).transaction(%s).objectStore(%s)."
+                   "getAllKeys(%s, %s)",
+                 "IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.getAllKeys()",
+                 IDB_LOG_ID_STRING(),
+                 mTransaction->LoggingSerialNumber(),
+                 request->LoggingSerialNumber(),
+                 IDB_LOG_STRINGIFY(mTransaction->Database()),
+                 IDB_LOG_STRINGIFY(mTransaction),
+                 IDB_LOG_STRINGIFY(this),
+                 IDB_LOG_STRINGIFY(keyRange),
+                 IDB_LOG_STRINGIFY(aLimit));
+  } else {
+    IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                   "database(%s).transaction(%s).objectStore(%s)."
+                   "getAll(%s, %s)",
+                 "IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.getAll()",
+                 IDB_LOG_ID_STRING(),
+                 mTransaction->LoggingSerialNumber(),
+                 request->LoggingSerialNumber(),
+                 IDB_LOG_STRINGIFY(mTransaction->Database()),
+                 IDB_LOG_STRINGIFY(mTransaction),
+                 IDB_LOG_STRINGIFY(this),
+                 IDB_LOG_STRINGIFY(keyRange),
+                 IDB_LOG_STRINGIFY(aLimit));
+  }
+
   BackgroundRequestChild* actor = new BackgroundRequestChild(request);
 
   mTransaction->StartRequest(actor, params);
 
-#ifdef IDB_PROFILER_USE_MARKS
-  if (aKeysOnly) {
-    IDB_PROFILER_MARK("IndexedDB Request %llu: "
-                      "database(%s).transaction(%s).objectStore(%s)."
-                      "getAllKeys(%s, %lu)",
-                      "IDBRequest[%llu] MT IDBObjectStore.getAllKeys()",
-                      request->GetSerialNumber(),
-                      IDB_PROFILER_STRING(Transaction()->Database()),
-                      IDB_PROFILER_STRING(Transaction()),
-                      IDB_PROFILER_STRING(this),
-                      IDB_PROFILER_STRING(aKeyRange),
-                      aLimit);
-  } else {
-    IDB_PROFILER_MARK("IndexedDB Request %llu: "
-                      "database(%s).transaction(%s).objectStore(%s)."
-                      "getAll(%s, %lu)",
-                      "IDBRequest[%llu] MT IDBObjectStore.getAll()",
-                      request->GetSerialNumber(),
-                      IDB_PROFILER_STRING(Transaction()->Database()),
-                      IDB_PROFILER_STRING(Transaction()),
-                      IDB_PROFILER_STRING(this),
-                      IDB_PROFILER_STRING(aKeyRange),
-                      aLimit);
-  }
-#endif
-
   return request.forget();
 }
 
 already_AddRefed<IDBRequest>
 IDBObjectStore::Clear(ErrorResult& aRv)
 {
   AssertIsOnOwningThread();
 
@@ -1378,28 +1386,30 @@ IDBObjectStore::Clear(ErrorResult& aRv)
   }
 
   ObjectStoreClearParams params;
   params.objectStoreId() = Id();
 
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   MOZ_ASSERT(request);
 
+  IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                 "database(%s).transaction(%s).objectStore(%s).clear()",
+               "IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.clear()",
+               IDB_LOG_ID_STRING(),
+               mTransaction->LoggingSerialNumber(),
+               request->LoggingSerialNumber(),
+               IDB_LOG_STRINGIFY(mTransaction->Database()),
+               IDB_LOG_STRINGIFY(mTransaction),
+               IDB_LOG_STRINGIFY(this));
+
   BackgroundRequestChild* actor = new BackgroundRequestChild(request);
 
   mTransaction->StartRequest(actor, params);
 
-  IDB_PROFILER_MARK("IndexedDB Request %llu: "
-                    "database(%s).transaction(%s).objectStore(%s).clear()",
-                    "IDBRequest[%llu] MT IDBObjectStore.clear()",
-                    request->GetSerialNumber(),
-                    IDB_PROFILER_STRING(Transaction()->Database()),
-                    IDB_PROFILER_STRING(Transaction()),
-                    IDB_PROFILER_STRING(this));
-
   return request.forget();
 }
 
 already_AddRefed<IDBIndex>
 IDBObjectStore::Index(const nsAString& aName, ErrorResult &aRv)
 {
   AssertIsOnOwningThread();
 
@@ -1574,35 +1584,39 @@ IDBObjectStore::Get(JSContext* aCx,
 
   ObjectStoreGetParams params;
   params.objectStoreId() = Id();
   keyRange->ToSerialized(params.keyRange());
 
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   MOZ_ASSERT(request);
 
+  IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                 "database(%s).transaction(%s).objectStore(%s).get(%s)",
+               "IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.get()",
+               IDB_LOG_ID_STRING(),
+               mTransaction->LoggingSerialNumber(),
+               request->LoggingSerialNumber(),
+               IDB_LOG_STRINGIFY(mTransaction->Database()),
+               IDB_LOG_STRINGIFY(mTransaction),
+               IDB_LOG_STRINGIFY(this),
+               IDB_LOG_STRINGIFY(keyRange));
+
   BackgroundRequestChild* actor = new BackgroundRequestChild(request);
 
   mTransaction->StartRequest(actor, params);
 
-  IDB_PROFILER_MARK("IndexedDB Request %llu: "
-                    "database(%s).transaction(%s).objectStore(%s).get(%s)",
-                    "IDBRequest[%llu] MT IDBObjectStore.get()",
-                    request->GetSerialNumber(),
-                    IDB_PROFILER_STRING(Transaction()->Database()),
-                    IDB_PROFILER_STRING(Transaction()),
-                    IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange));
-
   return request.forget();
 }
 
 already_AddRefed<IDBRequest>
-IDBObjectStore::Delete(JSContext* aCx,
-                       JS::Handle<JS::Value> aKey,
-                       ErrorResult& aRv)
+IDBObjectStore::DeleteInternal(JSContext* aCx,
+                               JS::Handle<JS::Value> aKey,
+                               bool aFromCursor,
+                               ErrorResult& aRv)
 {
   AssertIsOnOwningThread();
 
   if (!mTransaction->IsOpen()) {
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
     return nullptr;
   }
 
@@ -1625,28 +1639,33 @@ IDBObjectStore::Delete(JSContext* aCx,
 
   ObjectStoreDeleteParams params;
   params.objectStoreId() = Id();
   keyRange->ToSerialized(params.keyRange());
 
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   MOZ_ASSERT(request);
 
+  if (!aFromCursor) {
+    IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                   "database(%s).transaction(%s).objectStore(%s).delete(%s)",
+                 "IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.delete()",
+                 IDB_LOG_ID_STRING(),
+                 mTransaction->LoggingSerialNumber(),
+                 request->LoggingSerialNumber(),
+                 IDB_LOG_STRINGIFY(mTransaction->Database()),
+                 IDB_LOG_STRINGIFY(mTransaction),
+                 IDB_LOG_STRINGIFY(this),
+                 IDB_LOG_STRINGIFY(keyRange));
+  }
+
   BackgroundRequestChild* actor = new BackgroundRequestChild(request);
 
   mTransaction->StartRequest(actor, params);
 
-  IDB_PROFILER_MARK("IndexedDB Request %llu: "
-                    "database(%s).transaction(%s).objectStore(%s).delete(%s)",
-                    "IDBRequest[%llu] MT IDBObjectStore.delete()",
-                    request->GetSerialNumber(),
-                    IDB_PROFILER_STRING(Transaction()->Database()),
-                    IDB_PROFILER_STRING(Transaction()),
-                    IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange));
-
   return request.forget();
 }
 
 already_AddRefed<IDBIndex>
 IDBObjectStore::CreateIndex(JSContext* aCx,
                             const nsAString& aName,
                             const nsAString& aKeyPath,
                             const IDBIndexParameters& aOptionalParameters,
@@ -1746,23 +1765,30 @@ IDBObjectStore::CreateIndexInternal(
 
   transaction->CreateIndex(this, *metadata);
 
   nsRefPtr<IDBIndex> index = IDBIndex::Create(this, *metadata);
   MOZ_ASSERT(index);
 
   mIndexes.AppendElement(index);
 
-  IDB_PROFILER_MARK("IndexedDB Pseudo-request: "
-                    "database(%s).transaction(%s).objectStore(%s)."
-                    "createIndex(%s)",
-                    "MT IDBObjectStore.createIndex()",
-                    IDB_PROFILER_STRING(Transaction()->Database()),
-                    IDB_PROFILER_STRING(Transaction()),
-                    IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(index));
+  // Don't do this in the macro because we always need to increment the serial
+  // number to keep in sync with the parent.
+  const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
+
+  IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                 "database(%s).transaction(%s).objectStore(%s).createIndex(%s)",
+               "IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.createIndex()",
+               IDB_LOG_ID_STRING(),
+               mTransaction->LoggingSerialNumber(),
+               requestSerialNumber,
+               IDB_LOG_STRINGIFY(mTransaction->Database()),
+               IDB_LOG_STRINGIFY(mTransaction),
+               IDB_LOG_STRINGIFY(this),
+               IDB_LOG_STRINGIFY(index));
 
   return index.forget();
 }
 
 void
 IDBObjectStore::DeleteIndex(const nsAString& aName, ErrorResult& aRv)
 {
   AssertIsOnOwningThread();
@@ -1811,26 +1837,33 @@ IDBObjectStore::DeleteIndex(const nsAStr
     }
   }
 
   if (!foundId) {
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR);
     return;
   }
 
-  transaction->DeleteIndex(this, foundId);
+  // Don't do this in the macro because we always need to increment the serial
+  // number to keep in sync with the parent.
+  const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
 
-  IDB_PROFILER_MARK("IndexedDB Pseudo-request: "
-                    "database(%s).transaction(%s).objectStore(%s)."
-                    "deleteIndex(\"%s\")",
-                    "MT IDBObjectStore.deleteIndex()",
-                    IDB_PROFILER_STRING(Transaction()->Database()),
-                    IDB_PROFILER_STRING(Transaction()),
-                    IDB_PROFILER_STRING(this),
-                    NS_ConvertUTF16toUTF8(aName).get());
+  IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                 "database(%s).transaction(%s).objectStore(%s)."
+                 "deleteIndex(\"%s\")",
+               "IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.deleteIndex()",
+               IDB_LOG_ID_STRING(),
+               mTransaction->LoggingSerialNumber(),
+               requestSerialNumber,
+               IDB_LOG_STRINGIFY(mTransaction->Database()),
+               IDB_LOG_STRINGIFY(mTransaction),
+               IDB_LOG_STRINGIFY(this),
+               NS_ConvertUTF16toUTF8(aName).get());
+
+  transaction->DeleteIndex(this, foundId);
 }
 
 already_AddRefed<IDBRequest>
 IDBObjectStore::Count(JSContext* aCx,
                       JS::Handle<JS::Value> aKey,
                       ErrorResult& aRv)
 {
   if (!mTransaction->IsOpen()) {
@@ -1853,28 +1886,31 @@ IDBObjectStore::Count(JSContext* aCx,
     params.optionalKeyRange() = serializedKeyRange;
   } else {
     params.optionalKeyRange() = void_t();
   }
 
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   MOZ_ASSERT(request);
 
+  IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                 "database(%s).transaction(%s).objectStore(%s).count(%s)",
+               "IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.count()",
+               IDB_LOG_ID_STRING(),
+               mTransaction->LoggingSerialNumber(),
+               request->LoggingSerialNumber(),
+               IDB_LOG_STRINGIFY(mTransaction->Database()),
+               IDB_LOG_STRINGIFY(mTransaction),
+               IDB_LOG_STRINGIFY(this),
+               IDB_LOG_STRINGIFY(keyRange));
+
   BackgroundRequestChild* actor = new BackgroundRequestChild(request);
 
   mTransaction->StartRequest(actor, params);
 
-  IDB_PROFILER_MARK("IndexedDB Request %llu: "
-                    "database(%s).transaction(%s).objectStore(%s).count(%s)",
-                    "IDBRequest[%llu] MT IDBObjectStore.count()",
-                    request->GetSerialNumber(),
-                    IDB_PROFILER_STRING(Transaction()->Database()),
-                    IDB_PROFILER_STRING(Transaction()),
-                    IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange));
-
   return request.forget();
 }
 
 already_AddRefed<IDBRequest>
 IDBObjectStore::OpenCursorInternal(bool aKeysOnly,
                                    JSContext* aCx,
                                    JS::Handle<JS::Value> aRange,
                                    IDBCursorDirection aDirection,
@@ -1923,44 +1959,50 @@ IDBObjectStore::OpenCursorInternal(bool 
     openParams.direction() = direction;
 
     params = Move(openParams);
   }
 
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   MOZ_ASSERT(request);
 
+  if (aKeysOnly) {
+    IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                   "database(%s).transaction(%s).objectStore(%s)."
+                   "openKeyCursor(%s, %s)",
+                 "IndexedDB %s: C T[%lld] R[%llu]: "
+                   "IDBObjectStore.openKeyCursor()",
+                 IDB_LOG_ID_STRING(),
+                 mTransaction->LoggingSerialNumber(),
+                 request->LoggingSerialNumber(),
+                 IDB_LOG_STRINGIFY(mTransaction->Database()),
+                 IDB_LOG_STRINGIFY(mTransaction),
+                 IDB_LOG_STRINGIFY(this),
+                 IDB_LOG_STRINGIFY(keyRange),
+                 IDB_LOG_STRINGIFY(direction));
+  } else {
+    IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                   "database(%s).transaction(%s).objectStore(%s)."
+                   "openCursor(%s, %s)",
+                 "IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.openCursor()",
+                 IDB_LOG_ID_STRING(),
+                 mTransaction->LoggingSerialNumber(),
+                 request->LoggingSerialNumber(),
+                 IDB_LOG_STRINGIFY(mTransaction->Database()),
+                 IDB_LOG_STRINGIFY(mTransaction),
+                 IDB_LOG_STRINGIFY(this),
+                 IDB_LOG_STRINGIFY(keyRange),
+                 IDB_LOG_STRINGIFY(direction));
+  }
+
   BackgroundCursorChild* actor =
     new BackgroundCursorChild(request, this, direction);
 
   mTransaction->OpenCursor(actor, params);
 
-#ifdef IDB_PROFILER_USE_MARKS
-  if (aKeysOnly) {
-    IDB_PROFILER_MARK("IndexedDB Request %llu: "
-                      "database(%s).transaction(%s).objectStore(%s)."
-                      "openKeyCursor(%s, %s)",
-                      "IDBRequest[%llu] MT IDBObjectStore.openKeyCursor()",
-                      request->GetSerialNumber(),
-                      IDB_PROFILER_STRING(Transaction()->Database()),
-                      IDB_PROFILER_STRING(Transaction()),
-                      IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange),
-                      IDB_PROFILER_STRING(direction));
-  } else {
-    IDB_PROFILER_MARK("IndexedDB Request %llu: "
-                      "database(%s).transaction(%s).objectStore(%s)."
-                      "openCursor(%s, %s)",
-                      "IDBRequest[%llu] MT IDBObjectStore.openKeyCursor()",
-                      request->GetSerialNumber(),
-                      IDB_PROFILER_STRING(Transaction()->Database()),
-                      IDB_PROFILER_STRING(Transaction()),
-                      IDB_PROFILER_STRING(this), IDB_PROFILER_STRING(aKeyRange),
-                      IDB_PROFILER_STRING(direction));
-  }
-#endif
   return request.forget();
 }
 
 void
 IDBObjectStore::RefreshSpec(bool aMayDelete)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT_IF(mDeletedSpec, mSpec == mDeletedSpec);
--- a/dom/indexedDB/IDBObjectStore.h
+++ b/dom/indexedDB/IDBObjectStore.h
@@ -28,30 +28,34 @@ namespace dom {
 
 class DOMStringList;
 class nsIContentParent;
 template <typename> class Sequence;
 
 namespace indexedDB {
 
 class FileManager;
+class IDBCursor;
 class IDBKeyRange;
 class IDBRequest;
 class IDBTransaction;
 class IndexUpdateInfo;
 class Key;
 class KeyPath;
 class ObjectStoreSpec;
 struct StructuredCloneFile;
 struct StructuredCloneReadInfo;
 
 class IDBObjectStore MOZ_FINAL
   : public nsISupports
   , public nsWrapperCache
 {
+  // For AddOrPut() and DeleteInternal().
+  friend class IDBCursor;
+
   static const JSClass sDummyPropJSClass;
 
   nsRefPtr<IDBTransaction> mTransaction;
   JS::Heap<JS::Value> mCachedKeyPath;
 
   // This normally points to the ObjectStoreSpec owned by the parent IDBDatabase
   // object. However, if this objectStore is part of a versionchange transaction
   // and it gets deleted then the spec is copied into mDeletedSpec and mSpec is
@@ -155,32 +159,39 @@ public:
   already_AddRefed<IDBRequest>
   Add(JSContext* aCx,
       JS::Handle<JS::Value> aValue,
       JS::Handle<JS::Value> aKey,
       ErrorResult& aRv)
   {
     AssertIsOnOwningThread();
 
-    return AddOrPut(aCx, aValue, aKey, false, aRv);
+    return AddOrPut(aCx, aValue, aKey, false, /* aFromCursor */ false, aRv);
   }
 
   already_AddRefed<IDBRequest>
   Put(JSContext* aCx,
       JS::Handle<JS::Value> aValue,
       JS::Handle<JS::Value> aKey,
       ErrorResult& aRv)
   {
     AssertIsOnOwningThread();
 
-    return AddOrPut(aCx, aValue, aKey, true, aRv);
+    return AddOrPut(aCx, aValue, aKey, true, /* aFromCursor */ false, aRv);
   }
 
   already_AddRefed<IDBRequest>
-  Delete(JSContext* aCx, JS::Handle<JS::Value> aKey, ErrorResult& aRv);
+  Delete(JSContext* aCx,
+         JS::Handle<JS::Value> aKey,
+         ErrorResult& aRv)
+  {
+    AssertIsOnOwningThread();
+
+    return DeleteInternal(aCx, aKey, /* aFromCursor */ false, aRv);
+  }
 
   already_AddRefed<IDBRequest>
   Get(JSContext* aCx, JS::Handle<JS::Value> aKey, ErrorResult& aRv);
 
   already_AddRefed<IDBRequest>
   Clear(ErrorResult& aRv);
 
   already_AddRefed<IDBIndex>
@@ -283,19 +294,26 @@ private:
              Key& aKey,
              nsTArray<IndexUpdateInfo>& aUpdateInfoArray);
 
   already_AddRefed<IDBRequest>
   AddOrPut(JSContext* aCx,
            JS::Handle<JS::Value> aValue,
            JS::Handle<JS::Value> aKey,
            bool aOverwrite,
+           bool aFromCursor,
            ErrorResult& aRv);
 
   already_AddRefed<IDBRequest>
+  DeleteInternal(JSContext* aCx,
+                 JS::Handle<JS::Value> aKey,
+                 bool aFromCursor,
+                 ErrorResult& aRv);
+
+  already_AddRefed<IDBRequest>
   GetAllInternal(bool aKeysOnly,
                  JSContext* aCx,
                  JS::Handle<JS::Value> aKey,
                  const Optional<uint32_t>& aLimit,
                  ErrorResult& aRv);
 
   already_AddRefed<IDBIndex>
   CreateIndexInternal(JSContext* aCx,
--- a/dom/indexedDB/IDBRequest.cpp
+++ b/dom/indexedDB/IDBRequest.cpp
@@ -24,16 +24,19 @@
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h"
 #include "nsIScriptContext.h"
 #include "nsJSUtils.h"
 #include "nsPIDOMWindow.h"
 #include "nsString.h"
 #include "ReportInternalError.h"
 
+// Include this last to avoid path problems on Windows.
+#include "ActorsChild.h"
+
 namespace mozilla {
 namespace dom {
 namespace indexedDB {
 
 using namespace mozilla::ipc;
 
 IDBRequest::IDBRequest(IDBDatabase* aDatabase)
   : IDBWrapperCache(aDatabase)
@@ -70,29 +73,20 @@ void
 IDBRequest::InitMembers()
 {
 #ifdef DEBUG
   mOwningThread = PR_GetCurrentThread();
 #endif
   AssertIsOnOwningThread();
 
   mResultVal.setUndefined();
+  mLoggingSerialNumber = NextSerialNumber();
   mErrorCode = NS_OK;
   mLineNo = 0;
   mHaveResultOrErrorCode = false;
-
-#ifdef MOZ_ENABLE_PROFILER_SPS
-  {
-    BackgroundChildImpl::ThreadLocal* threadLocal =
-      BackgroundChildImpl::GetThreadLocalForCurrentThread();
-    MOZ_ASSERT(threadLocal);
-
-    mSerialNumber = threadLocal->mNextRequestSerialNumber++;
-  }
-#endif
 }
 
 // static
 already_AddRefed<IDBRequest>
 IDBRequest::Create(IDBDatabase* aDatabase,
                    IDBTransaction* aTransaction)
 {
   MOZ_ASSERT(aDatabase);
@@ -135,16 +129,38 @@ IDBRequest::Create(IDBIndex* aSourceAsIn
   nsRefPtr<IDBRequest> request = Create(aDatabase, aTransaction);
 
   request->mSourceAsIndex = aSourceAsIndex;
 
   return request.forget();
 }
 
 // static
+uint64_t
+IDBRequest::NextSerialNumber()
+{
+  BackgroundChildImpl::ThreadLocal* threadLocal =
+    BackgroundChildImpl::GetThreadLocalForCurrentThread();
+  MOZ_ASSERT(threadLocal);
+
+  ThreadLocal* idbThreadLocal = threadLocal->mIndexedDBThreadLocal;
+  MOZ_ASSERT(idbThreadLocal);
+
+  return idbThreadLocal->NextRequestSN();
+}
+
+void
+IDBRequest::SetLoggingSerialNumber(uint64_t aLoggingSerialNumber)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aLoggingSerialNumber > mLoggingSerialNumber);
+
+  mLoggingSerialNumber = aLoggingSerialNumber;
+}
+
 void
 IDBRequest::CaptureCaller(nsAString& aFilename, uint32_t* aLineNo)
 {
   MOZ_ASSERT(aFilename.IsEmpty());
   MOZ_ASSERT(aLineNo);
 
   ThreadsafeAutoJSContext cx;
 
--- a/dom/indexedDB/IDBRequest.h
+++ b/dom/indexedDB/IDBRequest.h
@@ -53,19 +53,17 @@ protected:
 #ifdef DEBUG
   PRThread* mOwningThread;
 #endif
 
   JS::Heap<JS::Value> mResultVal;
   nsRefPtr<DOMError> mError;
 
   nsString mFilename;
-#ifdef MOZ_ENABLE_PROFILER_SPS
-  uint64_t mSerialNumber;
-#endif
+  uint64_t mLoggingSerialNumber;
   nsresult mErrorCode;
   uint32_t mLineNo;
   bool mHaveResultOrErrorCode;
 
 public:
   class ResultCallback;
 
   static already_AddRefed<IDBRequest>
@@ -79,16 +77,19 @@ public:
   static already_AddRefed<IDBRequest>
   Create(IDBIndex* aSource,
          IDBDatabase* aDatabase,
          IDBTransaction* aTransaction);
 
   static void
   CaptureCaller(nsAString& aFilename, uint32_t* aLineNo);
 
+  static uint64_t
+  NextSerialNumber();
+
   // nsIDOMEventTarget
   virtual nsresult
   PreHandleEvent(EventChainPreVisitor& aVisitor) MOZ_OVERRIDE;
 
   void
   GetSource(Nullable<OwningIDBObjectStoreOrIDBIndexOrIDBCursor>& aSource) const;
 
   void
@@ -120,23 +121,26 @@ public:
   GetCallerLocation(nsAString& aFilename, uint32_t* aLineNo) const;
 
   bool
   IsPending() const
   {
     return !mHaveResultOrErrorCode;
   }
 
-#ifdef MOZ_ENABLE_PROFILER_SPS
   uint64_t
-  GetSerialNumber() const
+  LoggingSerialNumber() const
   {
-    return mSerialNumber;
+    AssertIsOnOwningThread();
+
+    return mLoggingSerialNumber;
   }
-#endif
+
+  void
+  SetLoggingSerialNumber(uint64_t aLoggingSerialNumber);
 
   nsPIDOMWindow*
   GetParentObject() const
   {
     return GetOwner();
   }
 
   void
--- a/dom/indexedDB/IDBTransaction.cpp
+++ b/dom/indexedDB/IDBTransaction.cpp
@@ -27,28 +27,31 @@
 
 // Include this last to avoid path problems on Windows.
 #include "ActorsChild.h"
 
 namespace mozilla {
 namespace dom {
 namespace indexedDB {
 
+using namespace mozilla::ipc;
+
 namespace {
 
 NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
 
 } // anonymous namespace
 
 IDBTransaction::IDBTransaction(IDBDatabase* aDatabase,
                                const nsTArray<nsString>& aObjectStoreNames,
                                Mode aMode)
   : IDBWrapperCache(aDatabase)
   , mDatabase(aDatabase)
   , mObjectStoreNames(aObjectStoreNames)
+  , mLoggingSerialNumber(0)
   , mNextObjectStoreId(0)
   , mNextIndexId(0)
   , mAbortCode(NS_OK)
   , mPendingRequestCount(0)
   , mLineNo(0)
   , mReadyState(IDBTransaction::INITIAL)
   , mMode(aMode)
   , mCreating(false)
@@ -58,26 +61,25 @@ IDBTransaction::IDBTransaction(IDBDataba
   , mFiredCompleteOrAbort(false)
 #endif
 {
   MOZ_ASSERT(aDatabase);
   aDatabase->AssertIsOnOwningThread();
 
   mBackgroundActor.mNormalBackgroundActor = nullptr;
 
-#ifdef MOZ_ENABLE_PROFILER_SPS
-  {
-    using namespace mozilla::ipc;
-    BackgroundChildImpl::ThreadLocal* threadLocal =
-      BackgroundChildImpl::GetThreadLocalForCurrentThread();
-    MOZ_ASSERT(threadLocal);
+  BackgroundChildImpl::ThreadLocal* threadLocal =
+    BackgroundChildImpl::GetThreadLocalForCurrentThread();
+  MOZ_ASSERT(threadLocal);
 
-    mSerialNumber = threadLocal->mNextTransactionSerialNumber++;
-  }
-#endif
+  ThreadLocal* idbThreadLocal = threadLocal->mIndexedDBThreadLocal;
+  MOZ_ASSERT(idbThreadLocal);
+
+  const_cast<int64_t&>(mLoggingSerialNumber) =
+    idbThreadLocal->NextTransactionSN(aMode);
 
 #ifdef DEBUG
   if (!aObjectStoreNames.IsEmpty()) {
     nsTArray<nsString> sortedNames(aObjectStoreNames);
     sortedNames.Sort();
 
     const uint32_t count = sortedNames.Length();
     MOZ_ASSERT(count == aObjectStoreNames.Length());
@@ -106,23 +108,25 @@ IDBTransaction::~IDBTransaction()
                 mFiredCompleteOrAbort);
   MOZ_ASSERT_IF(mMode != VERSION_CHANGE &&
                   mBackgroundActor.mNormalBackgroundActor,
                 mFiredCompleteOrAbort);
 
   mDatabase->UnregisterTransaction(this);
 
   if (mMode == VERSION_CHANGE) {
-    if (mBackgroundActor.mVersionChangeBackgroundActor) {
-      mBackgroundActor.mVersionChangeBackgroundActor->SendDeleteMeInternal();
+    if (auto* actor = mBackgroundActor.mVersionChangeBackgroundActor) {
+      actor->SendDeleteMeInternal();
+
       MOZ_ASSERT(!mBackgroundActor.mVersionChangeBackgroundActor,
                  "SendDeleteMeInternal should have cleared!");
     }
-  } else if (mBackgroundActor.mNormalBackgroundActor) {
-    mBackgroundActor.mNormalBackgroundActor->SendDeleteMeInternal();
+  } else if (auto* actor = mBackgroundActor.mNormalBackgroundActor) {
+    actor->SendDeleteMeInternal();
+
     MOZ_ASSERT(!mBackgroundActor.mNormalBackgroundActor,
                "SendDeleteMeInternal should have cleared!");
   }
 }
 
 // static
 already_AddRefed<IDBTransaction>
 IDBTransaction::CreateVersionChange(
@@ -207,17 +211,20 @@ IDBTransaction::GetCurrent()
   using namespace mozilla::ipc;
 
   MOZ_ASSERT(BackgroundChild::GetForCurrentThread());
 
   BackgroundChildImpl::ThreadLocal* threadLocal =
     BackgroundChildImpl::GetThreadLocalForCurrentThread();
   MOZ_ASSERT(threadLocal);
 
-  return threadLocal->mCurrentTransaction;
+  ThreadLocal* idbThreadLocal = threadLocal->mIndexedDBThreadLocal;
+  MOZ_ASSERT(idbThreadLocal);
+
+  return idbThreadLocal->GetCurrentTransaction();
 }
 
 #ifdef DEBUG
 
 void
 IDBTransaction::AssertIsOnOwningThread() const
 {
   MOZ_ASSERT(mDatabase);
@@ -334,16 +341,28 @@ IDBTransaction::OnRequestFinished()
 
 void
 IDBTransaction::SendCommit()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(NS_SUCCEEDED(mAbortCode));
   MOZ_ASSERT(IsFinished());
   MOZ_ASSERT(!mSentCommitOrAbort);
+  MOZ_ASSERT(!mPendingRequestCount);
+
+  // Don't do this in the macro because we always need to increment the serial
+  // number to keep in sync with the parent.
+  const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
+
+  IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                 "All requests complete, committing transaction",
+               "IndexedDB %s: C T[%lld] R[%llu]: IDBTransaction commit",
+               IDB_LOG_ID_STRING(),
+               LoggingSerialNumber(),
+               requestSerialNumber);
 
   if (mMode == VERSION_CHANGE) {
     MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
     mBackgroundActor.mVersionChangeBackgroundActor->SendCommit();
   } else {
     MOZ_ASSERT(mBackgroundActor.mNormalBackgroundActor);
     mBackgroundActor.mNormalBackgroundActor->SendCommit();
   }
@@ -356,16 +375,28 @@ IDBTransaction::SendCommit()
 void
 IDBTransaction::SendAbort(nsresult aResultCode)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(NS_FAILED(aResultCode));
   MOZ_ASSERT(IsFinished());
   MOZ_ASSERT(!mSentCommitOrAbort);
 
+  // Don't do this in the macro because we always need to increment the serial
+  // number to keep in sync with the parent.
+  const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
+
+  IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld] Request[%llu]: "
+                 "Aborting transaction with result 0x%x",
+               "IndexedDB %s: C T[%lld] R[%llu]: IDBTransaction abort (0x%x)",
+               IDB_LOG_ID_STRING(),
+               LoggingSerialNumber(),
+               requestSerialNumber,
+               aResultCode);
+
   if (mMode == VERSION_CHANGE) {
     MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
     mBackgroundActor.mVersionChangeBackgroundActor->SendAbort(aResultCode);
   } else {
     MOZ_ASSERT(mBackgroundActor.mNormalBackgroundActor);
     mBackgroundActor.mNormalBackgroundActor->SendAbort(aResultCode);
   }
 
@@ -512,21 +543,24 @@ IDBTransaction::AbortInternal(nsresult a
     // Already finished, nothing to do here.
     return;
   }
 
   const bool isVersionChange = mMode == VERSION_CHANGE;
   const bool isInvalidated = mDatabase->IsInvalidated();
   bool needToSendAbort = mReadyState == INITIAL && !isInvalidated;
 
+  if (isInvalidated) {
 #ifdef DEBUG
-  if (isInvalidated) {
     mSentCommitOrAbort = true;
+#endif
+    // Increment the serial number counter here to account for the aborted
+    // transaction and keep the parent in sync.
+    IDBRequest::NextSerialNumber();
   }
-#endif
 
   mAbortCode = aAbortCode;
   mReadyState = DONE;
   mError = error.forget();
 
   if (isVersionChange) {
     // If a version change transaction is aborted, we must revert the world
     // back to its previous state unless we're being invalidated after the
@@ -634,20 +668,16 @@ IDBTransaction::Abort(ErrorResult& aRv)
 }
 
 void
 IDBTransaction::FireCompleteOrAbortEvents(nsresult aResult)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(!mFiredCompleteOrAbort);
 
-  IDB_PROFILER_MARK("IndexedDB Transaction %llu: Complete (rv = %lu)",
-                    "IDBTransaction[%llu] MT Complete",
-                    mTransaction->GetSerialNumber(), mAbortCode);
-
   mReadyState = DONE;
 
 #ifdef DEBUG
   mFiredCompleteOrAbort = true;
 #endif
 
   nsCOMPtr<nsIDOMEvent> event;
   if (NS_SUCCEEDED(aResult)) {
@@ -665,16 +695,31 @@ IDBTransaction::FireCompleteOrAbortEvent
                                eDoesBubble,
                                eNotCancelable);
   }
 
   if (NS_WARN_IF(!event)) {
     return;
   }
 
+  if (NS_SUCCEEDED(mAbortCode)) {
+    IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld]: "
+                   "Firing 'complete' event",
+                 "IndexedDB %s: C T[%lld]: IDBTransaction 'complete' event",
+                 IDB_LOG_ID_STRING(),
+                 mLoggingSerialNumber);
+  } else {
+    IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld]: "
+                   "Firing 'abort' event with error 0x%x",
+                 "IndexedDB %s: C T[%lld]: IDBTransaction 'abort' event (0x%x)",
+                 IDB_LOG_ID_STRING(),
+                 mLoggingSerialNumber,
+                 mAbortCode);
+  }
+
   bool dummy;
   if (NS_FAILED(DispatchEvent(event, &dummy))) {
     NS_WARNING("DispatchEvent failed!");
   }
 
   mDatabase->DelayedMaybeExpireFileActors();
 }
 
--- a/dom/indexedDB/IDBTransaction.h
+++ b/dom/indexedDB/IDBTransaction.h
@@ -78,25 +78,22 @@ private:
   // Tagged with mMode. If mMode is VERSION_CHANGE then mBackgroundActor will be
   // a BackgroundVersionChangeTransactionChild. Otherwise it will be a
   // BackgroundTransactionChild.
   union {
     BackgroundTransactionChild* mNormalBackgroundActor;
     BackgroundVersionChangeTransactionChild* mVersionChangeBackgroundActor;
   } mBackgroundActor;
 
+  const int64_t mLoggingSerialNumber;
 
   // Only used for VERSION_CHANGE transactions.
   int64_t mNextObjectStoreId;
   int64_t mNextIndexId;
 
-#ifdef MOZ_ENABLE_PROFILER_SPS
-  uint64_t mSerialNumber;
-#endif
-
   nsresult mAbortCode;
   uint32_t mPendingRequestCount;
 
   nsString mFilename;
   uint32_t mLineNo;
 
   ReadyState mReadyState;
   Mode mMode;
@@ -240,24 +237,23 @@ public:
   DeleteIndex(IDBObjectStore* aObjectStore, int64_t aIndexId);
 
   void
   Abort(IDBRequest* aRequest);
 
   void
   Abort(nsresult aAbortCode);
 
-#ifdef MOZ_ENABLE_PROFILER_SPS
-  uint32_t
-  GetSerialNumber() const
+  int64_t
+  LoggingSerialNumber() const
   {
     AssertIsOnOwningThread();
-    return mSerialNumber;
+
+    return mLoggingSerialNumber;
   }
-#endif
 
   nsPIDOMWindow*
   GetParentObject() const;
 
   IDBTransactionMode
   GetMode(ErrorResult& aRv) const;
 
   DOMError*
--- a/dom/indexedDB/IndexedDatabaseManager.cpp
+++ b/dom/indexedDB/IndexedDatabaseManager.cpp
@@ -27,21 +27,23 @@
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "mozilla/Services.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/storage.h"
 #include "nsContentUtils.h"
 #include "nsThreadUtils.h"
+#include "prlog.h"
 
 #include "IDBEvents.h"
 #include "IDBFactory.h"
 #include "IDBKeyRange.h"
 #include "IDBRequest.h"
+#include "ProfilerHelpers.h"
 
 // Bindings for ResolveConstructors
 #include "mozilla/dom/IDBCursorBinding.h"
 #include "mozilla/dom/IDBDatabaseBinding.h"
 #include "mozilla/dom/IDBFactoryBinding.h"
 #include "mozilla/dom/IDBIndexBinding.h"
 #include "mozilla/dom/IDBKeyRangeBinding.h"
 #include "mozilla/dom/IDBMutableFileBinding.h"
@@ -106,17 +108,32 @@ private:
 
   nsTArray<nsRefPtr<FileManager> > mPersistentStorageFileManagers;
   nsTArray<nsRefPtr<FileManager> > mTemporaryStorageFileManagers;
   nsTArray<nsRefPtr<FileManager> > mDefaultStorageFileManagers;
 };
 
 namespace {
 
-const char kTestingPref[] = "dom.indexedDB.testing";
+#define IDB_PREF_BRANCH_ROOT "dom.indexedDB."
+
+const char kTestingPref[] = IDB_PREF_BRANCH_ROOT "testing";
+
+#define IDB_PREF_LOGGING_BRANCH_ROOT IDB_PREF_BRANCH_ROOT "logging."
+
+const char kPrefLoggingEnabled[] = IDB_PREF_LOGGING_BRANCH_ROOT "enabled";
+const char kPrefLoggingDetails[] = IDB_PREF_LOGGING_BRANCH_ROOT "details";
+
+#if defined(DEBUG) || defined(MOZ_ENABLE_PROFILER_SPS)
+const char kPrefLoggingProfiler[] =
+  IDB_PREF_LOGGING_BRANCH_ROOT "profiler-marks";
+#endif
+
+#undef IDB_PREF_LOGGING_BRANCH_ROOT
+#undef IDB_PREF_BRANCH_ROOT
 
 mozilla::StaticRefPtr<IndexedDatabaseManager> gDBManager;
 
 mozilla::Atomic<bool> gInitialized(false);
 mozilla::Atomic<bool> gClosed(false);
 mozilla::Atomic<bool> gTestingMode(false);
 
 class AsyncDeleteFileRunnable MOZ_FINAL : public nsIRunnable
@@ -200,32 +217,43 @@ IndexedDatabaseManager::IndexedDatabaseM
 
 IndexedDatabaseManager::~IndexedDatabaseManager()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 }
 
 bool IndexedDatabaseManager::sIsMainProcess = false;
 bool IndexedDatabaseManager::sFullSynchronousMode = false;
+
+PRLogModuleInfo* IndexedDatabaseManager::sLoggingModule;
+
+Atomic<IndexedDatabaseManager::LoggingMode>
+  IndexedDatabaseManager::sLoggingMode(
+    IndexedDatabaseManager::Logging_Disabled);
+
 mozilla::Atomic<bool> IndexedDatabaseManager::sLowDiskSpaceMode(false);
 
 // static
 IndexedDatabaseManager*
 IndexedDatabaseManager::GetOrCreate()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (IsClosed()) {
     NS_ERROR("Calling GetOrCreate() after shutdown!");
     return nullptr;
   }
 
   if (!gDBManager) {
     sIsMainProcess = XRE_GetProcessType() == GeckoProcessType_Default;
 
+    if (!sLoggingModule) {
+      sLoggingModule = PR_NewLogModule("IndexedDB");
+    }
+
     if (sIsMainProcess && Preferences::GetBool("disk_space_watcher.enabled", false)) {
       // See if we're starting up in low disk space conditions.
       nsCOMPtr<nsIDiskSpaceWatcher> watcher =
         do_GetService(DISKSPACEWATCHER_CONTRACTID);
       if (watcher) {
         bool isDiskFull;
         if (NS_SUCCEEDED(watcher->GetIsDiskFull(&isDiskFull))) {
           sLowDiskSpaceMode = isDiskFull;
@@ -295,30 +323,48 @@ IndexedDatabaseManager::Init()
   // By default IndexedDB uses SQLite with PRAGMA synchronous = NORMAL. This
   // guarantees (unlike synchronous = OFF) atomicity and consistency, but not
   // necessarily durability in situations such as power loss. This preference
   // allows enabling PRAGMA synchronous = FULL on SQLite, which does guarantee
   // durability, but with an extra fsync() and the corresponding performance
   // hit.
   sFullSynchronousMode = Preferences::GetBool("dom.indexedDB.fullSynchronous");
 
+  Preferences::RegisterCallback(LoggingModePrefChangedCallback,
+                                kPrefLoggingDetails);
+#ifdef MOZ_ENABLE_PROFILER_SPS
+  Preferences::RegisterCallback(LoggingModePrefChangedCallback,
+                                kPrefLoggingProfiler);
+#endif
+  Preferences::RegisterCallbackAndCall(LoggingModePrefChangedCallback,
+                                       kPrefLoggingEnabled);
+
   return NS_OK;
 }
 
 void
 IndexedDatabaseManager::Destroy()
 {
   // Setting the closed flag prevents the service from being recreated.
   // Don't set it though if there's no real instance created.
   if (gInitialized && gClosed.exchange(true)) {
     NS_ERROR("Shutdown more than once?!");
   }
 
   Preferences::UnregisterCallback(TestingPrefChangedCallback, kTestingPref);
 
+  Preferences::UnregisterCallback(LoggingModePrefChangedCallback,
+                                  kPrefLoggingDetails);
+#ifdef MOZ_ENABLE_PROFILER_SPS
+  Preferences::UnregisterCallback(LoggingModePrefChangedCallback,
+                                  kPrefLoggingProfiler);
+#endif
+  Preferences::UnregisterCallback(LoggingModePrefChangedCallback,
+                                  kPrefLoggingEnabled);
+
   delete this;
 }
 
 // static
 nsresult
 IndexedDatabaseManager::FireWindowOnError(nsPIDOMWindow* aOwner,
                                           EventChainPostVisitor& aVisitor)
 {
@@ -486,17 +532,40 @@ IndexedDatabaseManager::IsMainProcess()
 bool
 IndexedDatabaseManager::InLowDiskSpaceMode()
 {
   NS_ASSERTION(gDBManager,
                "InLowDiskSpaceMode() called before indexedDB has been "
                "initialized!");
   return sLowDiskSpaceMode;
 }
-#endif
+
+// static
+IndexedDatabaseManager::LoggingMode
+IndexedDatabaseManager::GetLoggingMode()
+{
+  MOZ_ASSERT(gDBManager,
+             "GetLoggingMode called before IndexedDatabaseManager has been "
+             "initialized!");
+
+  return sLoggingMode;
+}
+
+// static
+PRLogModuleInfo*
+IndexedDatabaseManager::GetLoggingModule()
+{
+  MOZ_ASSERT(gDBManager,
+             "GetLoggingModule called before IndexedDatabaseManager has been "
+             "initialized!");
+
+  return sLoggingModule;
+}
+
+#endif // DEBUG
 
 // static
 bool
 IndexedDatabaseManager::InTestingMode()
 {
   MOZ_ASSERT(gDBManager,
              "InTestingMode() called before indexedDB has been initialized!");
 
@@ -682,16 +751,54 @@ IndexedDatabaseManager::BlockAndGetFileR
                                              aResult)) {
       return NS_ERROR_FAILURE;
     }
   }
 
   return NS_OK;
 }
 
+// static
+void
+IndexedDatabaseManager::LoggingModePrefChangedCallback(
+                                                    const char* /* aPrefName */,
+                                                    void* /* aClosure */)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!Preferences::GetBool(kPrefLoggingEnabled)) {
+    sLoggingMode = Logging_Disabled;
+    return;
+  }
+
+  bool useProfiler =
+#if defined(DEBUG) || defined(MOZ_ENABLE_PROFILER_SPS)
+    Preferences::GetBool(kPrefLoggingProfiler);
+#if !defined(MOZ_ENABLE_PROFILER_SPS)
+  if (useProfiler) {
+    NS_WARNING("IndexedDB cannot create profiler marks because this build does "
+               "not have profiler extensions enabled!");
+    useProfiler = false;
+  }
+#endif
+#else
+    false;
+#endif
+
+  const bool logDetails = Preferences::GetBool(kPrefLoggingDetails);
+
+  if (useProfiler) {
+    sLoggingMode = logDetails ?
+                   Logging_DetailedProfilerMarks :
+                   Logging_ConciseProfilerMarks;
+  } else {
+    sLoggingMode = logDetails ? Logging_Detailed : Logging_Concise;
+  }
+}
+
 NS_IMPL_ADDREF(IndexedDatabaseManager)
 NS_IMPL_RELEASE_WITH_DESTROY(IndexedDatabaseManager, Destroy())
 NS_IMPL_QUERY_INTERFACE(IndexedDatabaseManager, nsIObserver)
 
 NS_IMETHODIMP
 IndexedDatabaseManager::Observe(nsISupports* aSubject, const char* aTopic,
                                 const char16_t* aData)
 {
--- a/dom/indexedDB/IndexedDatabaseManager.h
+++ b/dom/indexedDB/IndexedDatabaseManager.h
@@ -12,16 +12,17 @@
 #include "js/TypeDecls.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/dom/quota/PersistenceType.h"
 #include "mozilla/Mutex.h"
 #include "nsClassHashtable.h"
 #include "nsHashKeys.h"
 
 class nsPIDOMWindow;
+struct PRLogModuleInfo;
 
 namespace mozilla {
 
 class EventChainPostVisitor;
 
 namespace dom {
 
 class TabContext;
@@ -31,16 +32,25 @@ namespace indexedDB {
 class FileManager;
 class FileManagerInfo;
 
 class IndexedDatabaseManager MOZ_FINAL : public nsIObserver
 {
   typedef mozilla::dom::quota::PersistenceType PersistenceType;
 
 public:
+  enum LoggingMode
+  {
+    Logging_Disabled = 0,
+    Logging_Concise,
+    Logging_Detailed,
+    Logging_ConciseProfilerMarks,
+    Logging_DetailedProfilerMarks
+  };
+
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
   // Returns a non-owning reference.
   static IndexedDatabaseManager*
   GetOrCreate();
 
   // Returns a non-owning reference.
@@ -71,16 +81,36 @@ public:
 #endif
 
   static bool
   InTestingMode();
 
   static bool
   FullSynchronous();
 
+  static LoggingMode
+  GetLoggingMode()
+#ifdef DEBUG
+  ;
+#else
+  {
+    return sLoggingMode;
+  }
+#endif
+
+  static PRLogModuleInfo*
+  GetLoggingModule()
+#ifdef DEBUG
+  ;
+#else
+  {
+    return sLoggingModule;
+  }
+#endif
+
   already_AddRefed<FileManager>
   GetFileManager(PersistenceType aPersistenceType,
                  const nsACString& aOrigin,
                  const nsAString& aDatabaseName);
 
   void
   AddFileManager(FileManager* aFileManager);
 
@@ -138,27 +168,32 @@ private:
   ~IndexedDatabaseManager();
 
   nsresult
   Init();
 
   void
   Destroy();
 
+  static void
+  LoggingModePrefChangedCallback(const char* aPrefName, void* aClosure);
+
   // Maintains a list of all file managers per origin. This list isn't
   // protected by any mutex but it is only ever touched on the IO thread.
   nsClassHashtable<nsCStringHashKey, FileManagerInfo> mFileManagerInfos;
 
   // Lock protecting FileManager.mFileInfos and FileImplBase.mFileInfos
   // It's s also used to atomically update FileInfo.mRefCnt, FileInfo.mDBRefCnt
   // and FileInfo.mSliceRefCnt
   mozilla::Mutex mFileMutex;
 
   static bool sIsMainProcess;
   static bool sFullSynchronousMode;
+  static PRLogModuleInfo* sLoggingModule;
+  static Atomic<LoggingMode> sLoggingMode;
   static mozilla::Atomic<bool> sLowDiskSpaceMode;
 };
 
 } // namespace indexedDB
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_indexeddb_indexeddatabasemanager_h__
--- a/dom/indexedDB/PBackgroundIDBFactory.ipdl
+++ b/dom/indexedDB/PBackgroundIDBFactory.ipdl
@@ -46,16 +46,18 @@ protocol PBackgroundIDBFactory
   manages PBackgroundIDBDatabase;
   manages PBackgroundIDBFactoryRequest;
 
 parent:
   DeleteMe();
 
   PBackgroundIDBFactoryRequest(FactoryRequestParams params);
 
+  IncrementLoggingRequestSerialNumber();
+
 child:
   __delete__();
 
   PBackgroundIDBDatabase(DatabaseSpec spec,
                          PBackgroundIDBFactoryRequest request);
 };
 
 } // namespace indexedDB
--- a/dom/indexedDB/PBackgroundIDBSharedTypes.ipdlh
+++ b/dom/indexedDB/PBackgroundIDBSharedTypes.ipdlh
@@ -1,15 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PBlob;
 include protocol PBackgroundIDBDatabaseFile;
 
+include DOMTypes;
+
 include "mozilla/dom/indexedDB/SerializationHelpers.h";
 
 using struct mozilla::void_t
   from "ipc/IPCMessageUtils.h";
 
 using mozilla::dom::indexedDB::IDBCursor::Direction
   from "mozilla/dom/indexedDB/IDBCursor.h";
 
@@ -248,11 +250,21 @@ union RequestParams
   ObjectStoreCountParams;
   IndexGetParams;
   IndexGetKeyParams;
   IndexGetAllParams;
   IndexGetAllKeysParams;
   IndexCountParams;
 };
 
+struct LoggingInfo
+{
+  nsID backgroundChildLoggingId;
+  // This counter is always positive.
+  int64_t nextTransactionSerialNumber;
+  // This counter is always negative.
+  int64_t nextVersionChangeTransactionSerialNumber;
+  uint64_t nextRequestSerialNumber;
+};
+
 } // namespace indexedDB
 } // namespace dom
 } // namespace mozilla
--- a/dom/indexedDB/ProfilerHelpers.h
+++ b/dom/indexedDB/ProfilerHelpers.h
@@ -2,206 +2,330 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_indexeddb_profilerhelpers_h__
 #define mozilla_dom_indexeddb_profilerhelpers_h__
 
-#include "GeckoProfiler.h"
-
-// Uncomment this if you want IndexedDB operations to be marked in the profiler.
-//#define IDB_PROFILER_USE_MARKS
-
-// Uncomment this if you want extended details to appear in profiler marks.
-//#define IDB_PROFILER_MARK_DETAILS 0
+// This file is not exported and is only meant to be included in IndexedDB
+// source files.
 
-// Sanity check the options above.
-#if defined(IDB_PROFILER_USE_MARKS) && !defined(MOZ_ENABLE_PROFILER_SPS)
-#error Cannot use IDB_PROFILER_USE_MARKS without MOZ_ENABLE_PROFILER_SPS!
-#endif
-
-#if defined(IDB_PROFILER_MARK_DETAILS) && !defined(IDB_PROFILER_USE_MARKS)
-#error Cannot use IDB_PROFILER_MARK_DETAILS without IDB_PROFILER_USE_MARKS!
-#endif
-
-#ifdef IDB_PROFILER_USE_MARKS
-
-#ifdef IDB_PROFILER_MARK_DETAILS
-
+#include "BackgroundChildImpl.h"
+#include "GeckoProfiler.h"
 #include "IDBCursor.h"
 #include "IDBDatabase.h"
 #include "IDBIndex.h"
 #include "IDBKeyRange.h"
 #include "IDBObjectStore.h"
 #include "IDBTransaction.h"
+#include "IndexedDatabaseManager.h"
 #include "Key.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/BindingDeclarations.h"
+#include "nsDebug.h"
+#include "nsID.h"
+#include "nsIDOMEvent.h"
+#include "nsString.h"
+#include "prlog.h"
+
+// Include this last to avoid path problems on Windows.
+#include "ActorsChild.h"
 
 namespace mozilla {
 namespace dom {
 namespace indexedDB {
 
-class ProfilerString : public nsAutoCString
+class MOZ_STACK_CLASS LoggingIdString MOZ_FINAL
+  : public nsAutoCString
+{
+public:
+  LoggingIdString()
+  {
+    using mozilla::ipc::BackgroundChildImpl;
+
+    BackgroundChildImpl::ThreadLocal* threadLocal =
+      BackgroundChildImpl::GetThreadLocalForCurrentThread();
+    MOZ_ASSERT(threadLocal);
+
+    ThreadLocal* idbThreadLocal = threadLocal->mIndexedDBThreadLocal;
+    MOZ_ASSERT(idbThreadLocal);
+
+    Init(idbThreadLocal->Id());
+  }
+
+  explicit
+  LoggingIdString(const nsID& aID)
+  {
+    Init(aID);
+  }
+
+private:
+  void
+  Init(const nsID& aID)
+  {
+    static_assert(NSID_LENGTH > 1, "NSID_LENGTH is set incorrectly!");
+    MOZ_ASSERT(Capacity() > NSID_LENGTH);
+
+    // NSID_LENGTH counts the null terminator, SetLength() does not.
+    SetLength(NSID_LENGTH - 1);
+
+    aID.ToProvidedString(
+      *reinterpret_cast<char(*)[NSID_LENGTH]>(BeginWriting()));
+  }
+};
+
+class MOZ_STACK_CLASS LoggingString MOZ_FINAL
+  : public nsAutoCString
 {
   static const char kQuote = '\"';
   static const char kOpenBracket = '[';
   static const char kCloseBracket = ']';
   static const char kOpenParen = '(';
   static const char kCloseParen = ')';
 
 public:
   explicit
-  ProfilerString(IDBDatabase* aDatabase)
+  LoggingString(IDBDatabase* aDatabase)
+    : nsAutoCString(kQuote)
   {
     MOZ_ASSERT(aDatabase);
 
-    Append(kQuote);
     AppendUTF16toUTF8(aDatabase->Name(), *this);
     Append(kQuote);
   }
 
   explicit
-  ProfilerString(IDBTransaction* aTransaction)
+  LoggingString(IDBTransaction* aTransaction)
+    : nsAutoCString(kOpenBracket)
   {
     MOZ_ASSERT(aTransaction);
 
+    NS_NAMED_LITERAL_CSTRING(kCommaSpace, ", ");
+
+    const nsTArray<nsString>& stores = aTransaction->ObjectStoreNamesInternal();
+
+    for (uint32_t count = stores.Length(), index = 0; index < count; index++) {
+      Append(kQuote);
+      AppendUTF16toUTF8(stores[index], *this);
+      Append(kQuote);
+
+      if (index != count - 1) {
+        Append(kCommaSpace);
+      }
+    }
+
+    Append(kCloseBracket);
+    Append(kCommaSpace);
+
     switch (aTransaction->GetMode()) {
       case IDBTransaction::READ_ONLY:
         AppendLiteral("\"readonly\"");
         break;
       case IDBTransaction::READ_WRITE:
         AppendLiteral("\"readwrite\"");
         break;
       case IDBTransaction::VERSION_CHANGE:
         AppendLiteral("\"versionchange\"");
         break;
       default:
         MOZ_CRASH("Unknown mode!");
     };
   }
 
   explicit
-  ProfilerString(IDBObjectStore* aObjectStore)
+  LoggingString(IDBObjectStore* aObjectStore)
+    : nsAutoCString(kQuote)
   {
     MOZ_ASSERT(aObjectStore);
 
-    Append(kQuote);
     AppendUTF16toUTF8(aObjectStore->Name(), *this);
     Append(kQuote);
   }
 
   explicit
-  ProfilerString(IDBIndex* aIndex)
+  LoggingString(IDBIndex* aIndex)
+    : nsAutoCString(kQuote)
   {
     MOZ_ASSERT(aIndex);
 
-    Append(kQuote);
     AppendUTF16toUTF8(aIndex->Name(), *this);
     Append(kQuote);
   }
 
   explicit
-  ProfilerString(IDBKeyRange* aKeyRange)
+  LoggingString(IDBKeyRange* aKeyRange)
   {
     if (aKeyRange) {
       if (aKeyRange->IsOnly()) {
-        Append(ProfilerString(aKeyRange->Lower()));
+        Assign(LoggingString(aKeyRange->Lower()));
+      } else {
+        Assign(aKeyRange->LowerOpen() ? kOpenParen : kOpenBracket);
+        Append(LoggingString(aKeyRange->Lower()));
+        AppendLiteral(", ");
+        Append(LoggingString(aKeyRange->Upper()));
+        Append(aKeyRange->UpperOpen() ? kCloseParen : kCloseBracket);
       }
-      else {
-        Append(aKeyRange->IsLowerOpen() ? kOpenParen : kOpenBracket);
-        Append(ProfilerString(aKeyRange->Lower()));
-        AppendLiteral(", ");
-        Append(ProfilerString(aKeyRange->Upper()));
-        Append(aKeyRange->IsUpperOpen() ? kCloseParen : kCloseBracket);
-      }
+    } else {
+      AssignLiteral("<undefined>");
     }
   }
 
   explicit
-  ProfilerString(const Key& aKey)
+  LoggingString(const Key& aKey)
   {
     if (aKey.IsUnset()) {
-      AssignLiteral("null");
-    }
-    else if (aKey.IsFloat()) {
+      AssignLiteral("<undefined>");
+    } else if (aKey.IsFloat()) {
       AppendPrintf("%g", aKey.ToFloat());
-    }
-    else if (aKey.IsDate()) {
+    } else if (aKey.IsDate()) {
       AppendPrintf("<Date %g>", aKey.ToDateMsec());
-    }
-    else if (aKey.IsString()) {
+    } else if (aKey.IsString()) {
       nsAutoString str;
       aKey.ToString(str);
       AppendPrintf("\"%s\"", NS_ConvertUTF16toUTF8(str).get());
-    }
-    else {
+    } else {
       MOZ_ASSERT(aKey.IsArray());
-      AppendLiteral("<Array>");
+      AssignLiteral("[...]");
     }
   }
 
   explicit
-  ProfilerString(const IDBCursor::Direction aDirection)
+  LoggingString(const IDBCursor::Direction aDirection)
   {
     switch (aDirection) {
       case IDBCursor::NEXT:
-        AppendLiteral("\"next\"");
+        AssignLiteral("\"next\"");
         break;
       case IDBCursor::NEXT_UNIQUE:
-        AppendLiteral("\"nextunique\"");
+        AssignLiteral("\"nextunique\"");
         break;
       case IDBCursor::PREV:
-        AppendLiteral("\"prev\"");
+        AssignLiteral("\"prev\"");
         break;
       case IDBCursor::PREV_UNIQUE:
-        AppendLiteral("\"prevunique\"");
+        AssignLiteral("\"prevunique\"");
         break;
       default:
         MOZ_CRASH("Unknown direction!");
     };
   }
+
+  explicit
+  LoggingString(const Optional<uint64_t>& aVersion)
+  {
+    if (aVersion.WasPassed()) {
+      AppendInt(aVersion.Value());
+    } else {
+      AssignLiteral("<undefined>");
+    }
+  }
+
+  explicit
+  LoggingString(const Optional<uint32_t>& aLimit)
+  {
+    if (aLimit.WasPassed()) {
+      AppendInt(aLimit.Value());
+    } else {
+      AssignLiteral("<undefined>");
+    }
+  }
+
+  LoggingString(IDBObjectStore* aObjectStore, const Key& aKey)
+  {
+    MOZ_ASSERT(aObjectStore);
+
+    if (!aObjectStore->HasValidKeyPath()) {
+      Append(LoggingString(aKey));
+    }
+  }
+
+  LoggingString(nsIDOMEvent* aEvent, const char16_t* aDefault)
+    : nsAutoCString(kQuote)
+  {
+    MOZ_ASSERT(aDefault);
+
+    nsString eventType;
+
+    if (aEvent) {
+      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aEvent->GetType(eventType)));
+    } else {
+      eventType = nsDependentString(aDefault);
+    }
+
+    AppendUTF16toUTF8(eventType, *this);
+    Append(kQuote);
+  }
 };
 
+inline void
+LoggingHelper(bool aUseProfiler, const char* aFmt, ...)
+{
+  MOZ_ASSERT(IndexedDatabaseManager::GetLoggingMode() !=
+               IndexedDatabaseManager::Logging_Disabled);
+  MOZ_ASSERT(aFmt);
+
+  PRLogModuleInfo* logModule = IndexedDatabaseManager::GetLoggingModule();
+  MOZ_ASSERT(logModule);
+
+  static const PRLogModuleLevel logLevel = PR_LOG_DEBUG;
+
+  if (PR_LOG_TEST(logModule, logLevel) ||
+      (aUseProfiler && profiler_is_active())) {
+    nsAutoCString message;
+
+    {
+      va_list args;
+      va_start(args, aFmt);
+
+      message.AppendPrintf(aFmt, args);
+
+      va_end(args);
+    }
+
+    PR_LOG(logModule, logLevel, ("%s", message.get()));
+
+    if (aUseProfiler) {
+      PROFILER_MARKER(message.get());
+    }
+  }
+}
+
 } // namespace indexedDB
 } // namespace dom
 } // namespace mozilla
 
-#define IDB_PROFILER_MARK(_detailedFmt, _conciseFmt, ...)                      \
-  do {                                                                         \
-    nsAutoCString _mark;                                                       \
-    _mark.AppendPrintf(_detailedFmt, ##__VA_ARGS__);                           \
-    PROFILER_MARKER(_mark.get());                                              \
-  } while (0)
-
-#define IDB_PROFILER_STRING(_arg)                                              \
-  mozilla::dom::indexedDB::ProfilerString((_arg)).get()
-
-#else // IDB_PROFILER_MARK_DETAILS
-
-#define IDB_PROFILER_MARK(_detailedFmt, _conciseFmt, ...)                      \
+#define IDB_LOG_MARK(_detailedFmt, _conciseFmt, ...)                           \
   do {                                                                         \
-    nsAutoCString _mark;                                                       \
-    _mark.AppendPrintf(_conciseFmt, ##__VA_ARGS__);                            \
-    PROFILER_MARKER(_mark.get());                                              \
-  } while (0)
-
-#define IDB_PROFILER_STRING(_arg) ""
-
-#endif // IDB_PROFILER_MARK_DETAILS
-
-#define IDB_PROFILER_MARK_IF(_cond, _detailedFmt, _conciseFmt, ...)            \
-  do {                                                                         \
-    if (_cond) {                                                               \
-      IDB_PROFILER_MARK(_detailedFmt, _conciseFmt, __VA_ARGS__);               \
+    using namespace mozilla::dom::indexedDB;                                   \
+                                                                               \
+    const IndexedDatabaseManager::LoggingMode mode =                           \
+      IndexedDatabaseManager::GetLoggingMode();                                \
+                                                                               \
+    if (mode != IndexedDatabaseManager::Logging_Disabled) {                    \
+      const char* _fmt;                                                        \
+      if (mode == IndexedDatabaseManager::Logging_Concise ||                   \
+          mode == IndexedDatabaseManager::Logging_ConciseProfilerMarks) {      \
+        _fmt = _conciseFmt;                                                    \
+      } else {                                                                 \
+        MOZ_ASSERT(                                                            \
+          mode == IndexedDatabaseManager::Logging_Detailed ||                  \
+          mode == IndexedDatabaseManager::Logging_DetailedProfilerMarks);      \
+        _fmt = _detailedFmt;                                                   \
+      }                                                                        \
+                                                                               \
+      const bool _useProfiler =                                                \
+        mode == IndexedDatabaseManager::Logging_ConciseProfilerMarks ||        \
+        mode == IndexedDatabaseManager::Logging_DetailedProfilerMarks;         \
+                                                                               \
+      LoggingHelper(_useProfiler, _fmt, ##__VA_ARGS__);                        \
     }                                                                          \
   } while (0)
 
-#else // IDB_PROFILER_USE_MARKS
+#define IDB_LOG_ID_STRING(...)                                                 \
+  mozilla::dom::indexedDB::LoggingIdString(__VA_ARGS__).get()
 
-#define IDB_PROFILER_MARK(...) do { } while(0)
-#define IDB_PROFILER_MARK_IF(_cond, ...) do { } while(0)
-#define IDB_PROFILER_MARK2(_detailedFmt, _notdetailedFmt, ...) do { } while(0)
-#define IDB_PROFILER_STRING(_arg) ""
-
-#endif // IDB_PROFILER_USE_MARKS
+#define IDB_LOG_STRINGIFY(...)                                                 \
+  mozilla::dom::indexedDB::LoggingString(__VA_ARGS__).get()
 
 #endif // mozilla_dom_indexeddb_profilerhelpers_h__
--- a/dom/indexedDB/TransactionThreadPool.cpp
+++ b/dom/indexedDB/TransactionThreadPool.cpp
@@ -139,31 +139,35 @@ private:
 };
 
 class TransactionThreadPool::TransactionQueue MOZ_FINAL
   : public nsRunnable
 {
   Monitor mMonitor;
 
   nsRefPtr<TransactionThreadPool> mOwningThreadPool;
-  uint64_t mTransactionId;
+  const uint64_t mTransactionId;
+  const nsID mBackgroundChildLoggingId;
+  const int64_t mLoggingSerialNumber;
   const nsCString mDatabaseId;
   const nsTArray<nsString> mObjectStoreNames;
   uint16_t mMode;
 
   nsAutoTArray<nsCOMPtr<nsIRunnable>, 10> mQueue;
   nsRefPtr<FinishCallback> mFinishCallback;
   bool mShouldFinish;
 
 public:
   TransactionQueue(TransactionThreadPool* aThreadPool,
-                    uint64_t aTransactionId,
-                    const nsACString& aDatabaseId,
-                    const nsTArray<nsString>& aObjectStoreNames,
-                    uint16_t aMode);
+                   uint64_t aTransactionId,
+                   const nsACString& aDatabaseId,
+                   const nsTArray<nsString>& aObjectStoreNames,
+                   uint16_t aMode,
+                   const nsID& aBackgroundChildLoggingId,
+                   int64_t aLoggingSerialNumber);
 
   NS_DECL_ISUPPORTS_INHERITED
 
   void Unblock();
 
   void Dispatch(nsIRunnable* aRunnable);
 
   void Finish(FinishCallback* aFinishCallback);
@@ -182,23 +186,31 @@ struct TransactionThreadPool::Transactio
   nsRefPtr<TransactionQueue> queue;
   nsTHashtable<nsPtrHashKey<TransactionInfo>> blockedOn;
   nsTHashtable<nsPtrHashKey<TransactionInfo>> blocking;
 
   TransactionInfo(TransactionThreadPool* aThreadPool,
                   uint64_t aTransactionId,
                   const nsACString& aDatabaseId,
                   const nsTArray<nsString>& aObjectStoreNames,
-                  uint16_t aMode)
+                  uint16_t aMode,
+                  const nsID& aBackgroundChildLoggingId,
+                  int64_t aLoggingSerialNumber)
   : transactionId(aTransactionId), databaseId(aDatabaseId)
   {
     MOZ_COUNT_CTOR(TransactionInfo);
 
-    queue = new TransactionQueue(aThreadPool, aTransactionId, aDatabaseId,
-                                  aObjectStoreNames, aMode);
+    queue =
+      new TransactionQueue(aThreadPool,
+                           aTransactionId,
+                           aDatabaseId,
+                           aObjectStoreNames,
+                           aMode,
+                           aBackgroundChildLoggingId,
+                           aLoggingSerialNumber);
   }
 
   ~TransactionInfo()
   {
     MOZ_COUNT_DTOR(TransactionInfo);
   }
 };
 
@@ -297,17 +309,16 @@ TransactionThreadPool::Shutdown()
   nsIThread* currentThread = NS_GetCurrentThread();
   MOZ_ASSERT(currentThread);
 
   while (!mShutdownComplete) {
     MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(currentThread));
   }
 }
 
-// static
 uint64_t
 TransactionThreadPool::NextTransactionId()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mNextTransactionId < UINT64_MAX);
 
   return ++mNextTransactionId;
 }
@@ -543,30 +554,27 @@ TransactionThreadPool::GetQueueForTransa
       return info->queue;
     }
   }
 
   return nullptr;
 }
 
 TransactionThreadPool::TransactionQueue&
-TransactionThreadPool::GetQueueForTransaction(
+TransactionThreadPool::CreateQueueForTransaction(
                                     uint64_t aTransactionId,
                                     const nsACString& aDatabaseId,
                                     const nsTArray<nsString>& aObjectStoreNames,
-                                    uint16_t aMode)
+                                    uint16_t aMode,
+                                    const nsID& aBackgroundChildLoggingId,
+                                    int64_t aLoggingSerialNumber)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aTransactionId <= mNextTransactionId);
-
-  TransactionQueue* existingQueue =
-    GetQueueForTransaction(aTransactionId, aDatabaseId);
-  if (existingQueue) {
-    return *existingQueue;
-  }
+  MOZ_ASSERT(!GetQueueForTransaction(aTransactionId, aDatabaseId));
 
   // See if we can run this transaction now.
   DatabaseTransactionInfo* dbTransactionInfo;
   if (!mTransactionsInProgress.Get(aDatabaseId, &dbTransactionInfo)) {
     // First transaction for this database.
     dbTransactionInfo = new DatabaseTransactionInfo();
     mTransactionsInProgress.Put(aDatabaseId, dbTransactionInfo);
   }
@@ -574,21 +582,24 @@ TransactionThreadPool::GetQueueForTransa
   DatabaseTransactionInfo::TransactionHashtable& transactionsInProgress =
     dbTransactionInfo->transactions;
   TransactionInfo* info = transactionsInProgress.Get(aTransactionId);
   if (info) {
     // We recognize this one.
     return *info->queue;
   }
 
-  TransactionInfo* transactionInfo = new TransactionInfo(this,
-                                                         aTransactionId,
-                                                         aDatabaseId,
-                                                         aObjectStoreNames,
-                                                         aMode);
+  TransactionInfo* transactionInfo =
+    new TransactionInfo(this,
+                        aTransactionId,
+                        aDatabaseId,
+                        aObjectStoreNames,
+                        aMode,
+                        aBackgroundChildLoggingId,
+                        aLoggingSerialNumber);
 
   dbTransactionInfo->transactions.Put(aTransactionId, transactionInfo);;
 
   for (uint32_t index = 0, count = aObjectStoreNames.Length(); index < count;
        index++) {
     TransactionInfoPair* blockInfo =
       dbTransactionInfo->blockingTransactions.Get(aObjectStoreNames[index]);
     if (!blockInfo) {
@@ -628,36 +639,37 @@ TransactionThreadPool::GetQueueForTransa
   if (!transactionInfo->blockedOn.Count()) {
     transactionInfo->queue->Unblock();
   }
 
   return *transactionInfo->queue;
 }
 
 void
-TransactionThreadPool::Dispatch(uint64_t aTransactionId,
-                                const nsACString& aDatabaseId,
-                                const nsTArray<nsString>& aObjectStoreNames,
-                                uint16_t aMode,
-                                nsIRunnable* aRunnable,
-                                bool aFinish,
-                                FinishCallback* aFinishCallback)
+TransactionThreadPool::Start(uint64_t aTransactionId,
+                             const nsACString& aDatabaseId,
+                             const nsTArray<nsString>& aObjectStoreNames,
+                             uint16_t aMode,
+                             const nsID& aBackgroundChildLoggingId,
+                             int64_t aLoggingSerialNumber,
+                             nsIRunnable* aRunnable)
 {
+  AssertIsOnOwningThread();
   MOZ_ASSERT(aTransactionId <= mNextTransactionId);
+  MOZ_ASSERT(aRunnable);
   MOZ_ASSERT(!mShutdownRequested);
 
-  TransactionQueue& queue = GetQueueForTransaction(aTransactionId,
-                                                   aDatabaseId,
-                                                   aObjectStoreNames,
-                                                   aMode);
+  TransactionQueue& queue = CreateQueueForTransaction(aTransactionId,
+                                                      aDatabaseId,
+                                                      aObjectStoreNames,
+                                                      aMode,
+                                                      aBackgroundChildLoggingId,
+                                                      aLoggingSerialNumber);
 
   queue.Dispatch(aRunnable);
-  if (aFinish) {
-    queue.Finish(aFinishCallback);
-  }
 }
 
 void
 TransactionThreadPool::Dispatch(uint64_t aTransactionId,
                                 const nsACString& aDatabaseId,
                                 nsIRunnable* aRunnable,
                                 bool aFinish,
                                 FinishCallback* aFinishCallback)
@@ -760,24 +772,28 @@ TransactionThreadPool::MaybeFireCallback
   return true;
 }
 
 TransactionThreadPool::
 TransactionQueue::TransactionQueue(TransactionThreadPool* aThreadPool,
                                    uint64_t aTransactionId,
                                    const nsACString& aDatabaseId,
                                    const nsTArray<nsString>& aObjectStoreNames,
-                                   uint16_t aMode)
-: mMonitor("TransactionQueue::mMonitor"),
-  mOwningThreadPool(aThreadPool),
-  mTransactionId(aTransactionId),
-  mDatabaseId(aDatabaseId),
-  mObjectStoreNames(aObjectStoreNames),
-  mMode(aMode),
-  mShouldFinish(false)
+                                   uint16_t aMode,
+                                   const nsID& aBackgroundChildLoggingId,
+                                   int64_t aLoggingSerialNumber)
+  : mMonitor("TransactionQueue::mMonitor")
+  , mOwningThreadPool(aThreadPool)
+  , mTransactionId(aTransactionId)
+  , mBackgroundChildLoggingId(aBackgroundChildLoggingId)
+  , mLoggingSerialNumber(aLoggingSerialNumber)
+  , mDatabaseId(aDatabaseId)
+  , mObjectStoreNames(aObjectStoreNames)
+  , mMode(aMode)
+  , mShouldFinish(false)
 {
   MOZ_ASSERT(aThreadPool);
   aThreadPool->AssertIsOnOwningThread();
 }
 
 void
 TransactionThreadPool::TransactionQueue::Unblock()
 {
@@ -819,19 +835,21 @@ NS_IMPL_ISUPPORTS_INHERITED0(Transaction
 
 NS_IMETHODIMP
 TransactionThreadPool::TransactionQueue::Run()
 {
   PROFILER_LABEL("IndexedDB",
                  "TransactionThreadPool::TransactionQueue""Run",
                  js::ProfileEntry::Category::STORAGE);
 
-  IDB_PROFILER_MARK("IndexedDB Transaction %llu: Beginning database work",
-                    "IDBTransaction[%llu] DT Start",
-                    mTransaction->GetSerialNumber());
+  IDB_LOG_MARK("IndexedDB %s: Parent Transaction[%lld]: "
+                 "Beginning database work",
+               "IndexedDB %s: P T[%lld]: DB Start",
+               IDB_LOG_ID_STRING(mBackgroundChildLoggingId),
+               mLoggingSerialNumber);
 
   nsAutoTArray<nsCOMPtr<nsIRunnable>, 10> queue;
   nsRefPtr<FinishCallback> finishCallback;
   bool shouldFinish = false;
 
   do {
     NS_ASSERTION(queue.IsEmpty(), "Should have cleared this!");
 
@@ -872,19 +890,21 @@ TransactionThreadPool::TransactionQueue:
 
 #ifdef DEBUG
   if (kDEBUGThreadSleepMS) {
     MOZ_ALWAYS_TRUE(
       PR_Sleep(PR_MillisecondsToInterval(kDEBUGThreadSleepMS)) == PR_SUCCESS);
   }
 #endif // DEBUG
 
-  IDB_PROFILER_MARK("IndexedDB Transaction %llu: Finished database work",
-                    "IDBTransaction[%llu] DT Done",
-                    mTransaction->GetSerialNumber());
+  IDB_LOG_MARK("IndexedDB %s: Parent Transaction[%lld]: "
+                 "Finished database work",
+               "IndexedDB %s: P T[%lld]: DB End",
+               IDB_LOG_ID_STRING(mBackgroundChildLoggingId),
+               mLoggingSerialNumber);
 
   nsRefPtr<FinishTransactionRunnable> finishTransactionRunnable =
     new FinishTransactionRunnable(mOwningThreadPool.forget(),
                                   mTransactionId,
                                   mDatabaseId,
                                   mObjectStoreNames,
                                   mMode,
                                   finishCallback.forget());
--- a/dom/indexedDB/TransactionThreadPool.h
+++ b/dom/indexedDB/TransactionThreadPool.h
@@ -10,16 +10,17 @@
 #include "mozilla/Attributes.h"
 #include "nsAutoPtr.h"
 #include "nsClassHashtable.h"
 #include "nsCOMPtr.h"
 #include "nsHashKeys.h"
 #include "nsISupportsImpl.h"
 #include "nsTArray.h"
 
+struct nsID;
 class nsIEventTarget;
 class nsIRunnable;
 class nsIThreadPool;
 
 namespace mozilla {
 namespace dom {
 namespace indexedDB {
 
@@ -50,23 +51,23 @@ class TransactionThreadPool MOZ_FINAL
 
 public:
   class FinishCallback;
 
   static already_AddRefed<TransactionThreadPool> Create();
 
   uint64_t NextTransactionId();
 
-  void Dispatch(uint64_t aTransactionId,
-                const nsACString& aDatabaseId,
-                const nsTArray<nsString>& aObjectStoreNames,
-                uint16_t aMode,
-                nsIRunnable* aRunnable,
-                bool aFinish,
-                FinishCallback* aFinishCallback);
+  void Start(uint64_t aTransactionId,
+             const nsACString& aDatabaseId,
+             const nsTArray<nsString>& aObjectStoreNames,
+             uint16_t aMode,
+             const nsID& aBackgroundChildLoggingId,
+             int64_t aLoggingSerialNumber,
+             nsIRunnable* aRunnable);
 
   void Dispatch(uint64_t aTransactionId,
                 const nsACString& aDatabaseId,
                 nsIRunnable* aRunnable,
                 bool aFinish,
                 FinishCallback* aFinishCallback);
 
   void WaitForDatabasesToComplete(nsTArray<nsCString>& aDatabaseIds,
@@ -109,21 +110,23 @@ private:
   void FinishTransaction(uint64_t aTransactionId,
                          const nsACString& aDatabaseId,
                          const nsTArray<nsString>& aObjectStoreNames,
                          uint16_t aMode);
 
   TransactionQueue* GetQueueForTransaction(uint64_t aTransactionId,
                                            const nsACString& aDatabaseId);
 
-  TransactionQueue& GetQueueForTransaction(
+  TransactionQueue& CreateQueueForTransaction(
                                     uint64_t aTransactionId,
                                     const nsACString& aDatabaseId,
                                     const nsTArray<nsString>& aObjectStoreNames,
-                                    uint16_t aMode);
+                                    uint16_t aMode,
+                                    const nsID& aBackgroundChildLoggingId,
+                                    int64_t aLoggingSerialNumber);
 
   bool MaybeFireCallback(DatabasesCompleteCallback* aCallback);
 
   void CleanupAsync();
 };
 
 class NS_NO_VTABLE TransactionThreadPool::FinishCallback
 {
--- a/ipc/glue/BackgroundChildImpl.cpp
+++ b/ipc/glue/BackgroundChildImpl.cpp
@@ -1,20 +1,22 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "BackgroundChildImpl.h"
 
+#include "ActorsChild.h" // IndexedDB
 #include "FileDescriptorSetChild.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/PBlobChild.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/ipc/PBackgroundTestChild.h"
+#include "nsID.h"
 #include "nsTraceRefcnt.h"
 
 namespace {
 
 class TestChild MOZ_FINAL : public mozilla::ipc::PBackgroundTestChild
 {
   friend class mozilla::ipc::BackgroundChildImpl;
 
@@ -43,21 +45,16 @@ namespace mozilla {
 namespace ipc {
 
 // -----------------------------------------------------------------------------
 // BackgroundChildImpl::ThreadLocal
 // -----------------------------------------------------------------------------
 
 BackgroundChildImpl::
 ThreadLocal::ThreadLocal()
-  : mCurrentTransaction(nullptr)
-#ifdef MOZ_ENABLE_PROFILER_SPS
-  , mNextTransactionSerialNumber(1)
-  , mNextRequestSerialNumber(1)
-#endif
 {
   // May happen on any thread!
   MOZ_COUNT_CTOR(mozilla::ipc::BackgroundChildImpl::ThreadLocal);
 }
 
 BackgroundChildImpl::
 ThreadLocal::~ThreadLocal()
 {
@@ -131,17 +128,18 @@ BackgroundChildImpl::DeallocPBackgroundT
 {
   MOZ_ASSERT(aActor);
 
   delete static_cast<TestChild*>(aActor);
   return true;
 }
 
 BackgroundChildImpl::PBackgroundIDBFactoryChild*
-BackgroundChildImpl::AllocPBackgroundIDBFactoryChild()
+BackgroundChildImpl::AllocPBackgroundIDBFactoryChild(
+                                                const LoggingInfo& aLoggingInfo)
 {
   MOZ_CRASH("PBackgroundIDBFactoryChild actors should be manually "
             "constructed!");
 }
 
 bool
 BackgroundChildImpl::DeallocPBackgroundIDBFactoryChild(
                                              PBackgroundIDBFactoryChild* aActor)
--- a/ipc/glue/BackgroundChildImpl.h
+++ b/ipc/glue/BackgroundChildImpl.h
@@ -2,24 +2,23 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_ipc_backgroundchildimpl_h__
 #define mozilla_ipc_backgroundchildimpl_h__
 
 #include "mozilla/Attributes.h"
 #include "mozilla/ipc/PBackgroundChild.h"
-
-template <class> class nsAutoPtr;
+#include "nsAutoPtr.h"
 
 namespace mozilla {
 namespace dom {
 namespace indexedDB {
 
-class IDBTransaction;
+class ThreadLocal;
 
 } // namespace indexedDB
 } // namespace dom
 
 namespace ipc {
 
 // Instances of this class should never be created directly. This class is meant
 // to be inherited in BackgroundImpl.
@@ -48,17 +47,17 @@ protected:
 
   virtual PBackgroundTestChild*
   AllocPBackgroundTestChild(const nsCString& aTestArg) MOZ_OVERRIDE;
 
   virtual bool
   DeallocPBackgroundTestChild(PBackgroundTestChild* aActor) MOZ_OVERRIDE;
 
   virtual PBackgroundIDBFactoryChild*
-  AllocPBackgroundIDBFactoryChild() MOZ_OVERRIDE;
+  AllocPBackgroundIDBFactoryChild(const LoggingInfo& aLoggingInfo) MOZ_OVERRIDE;
 
   virtual bool
   DeallocPBackgroundIDBFactoryChild(PBackgroundIDBFactoryChild* aActor)
                                     MOZ_OVERRIDE;
 
   virtual PBlobChild*
   AllocPBlobChild(const BlobConstructorParams& aParams) MOZ_OVERRIDE;
 
@@ -73,22 +72,17 @@ protected:
   DeallocPFileDescriptorSetChild(PFileDescriptorSetChild* aActor) MOZ_OVERRIDE;
 };
 
 class BackgroundChildImpl::ThreadLocal MOZ_FINAL
 {
   friend class nsAutoPtr<ThreadLocal>;
 
 public:
-  mozilla::dom::indexedDB::IDBTransaction* mCurrentTransaction;
-
-#ifdef MOZ_ENABLE_PROFILER_SPS
-  uint64_t mNextTransactionSerialNumber;
-  uint64_t mNextRequestSerialNumber;
-#endif
+  nsAutoPtr<mozilla::dom::indexedDB::ThreadLocal> mIndexedDBThreadLocal;
 
 public:
   ThreadLocal();
 
 private:
   // Only destroyed by nsAutoPtr<ThreadLocal>.
   ~ThreadLocal();
 };
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -114,49 +114,53 @@ BackgroundParentImpl::DeallocPBackground
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   delete static_cast<TestParent*>(aActor);
   return true;
 }
 
 auto
-BackgroundParentImpl::AllocPBackgroundIDBFactoryParent()
+BackgroundParentImpl::AllocPBackgroundIDBFactoryParent(
+                                                const LoggingInfo& aLoggingInfo)
   -> PBackgroundIDBFactoryParent*
 {
   using mozilla::dom::indexedDB::AllocPBackgroundIDBFactoryParent;
 
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
 
-  return AllocPBackgroundIDBFactoryParent();
+  return AllocPBackgroundIDBFactoryParent(aLoggingInfo);
 }
 
 bool
 BackgroundParentImpl::RecvPBackgroundIDBFactoryConstructor(
-                                            PBackgroundIDBFactoryParent* aActor)
+                                            PBackgroundIDBFactoryParent* aActor,
+                                            const LoggingInfo& aLoggingInfo)
 {
   using mozilla::dom::indexedDB::RecvPBackgroundIDBFactoryConstructor;
 
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
-  return RecvPBackgroundIDBFactoryConstructor(aActor);
+  return RecvPBackgroundIDBFactoryConstructor(aActor, aLoggingInfo);
 }
 
 bool
 BackgroundParentImpl::DeallocPBackgroundIDBFactoryParent(
                                             PBackgroundIDBFactoryParent* aActor)
 {
+  using mozilla::dom::indexedDB::DeallocPBackgroundIDBFactoryParent;
+
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
-  return mozilla::dom::indexedDB::DeallocPBackgroundIDBFactoryParent(aActor);
+  return DeallocPBackgroundIDBFactoryParent(aActor);
 }
 
 auto
 BackgroundParentImpl::AllocPBlobParent(const BlobConstructorParams& aParams)
   -> PBlobParent*
 {
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
--- a/ipc/glue/BackgroundParentImpl.h
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -28,20 +28,22 @@ protected:
   virtual bool
   RecvPBackgroundTestConstructor(PBackgroundTestParent* aActor,
                                  const nsCString& aTestArg) MOZ_OVERRIDE;
 
   virtual bool
   DeallocPBackgroundTestParent(PBackgroundTestParent* aActor) MOZ_OVERRIDE;
 
   virtual PBackgroundIDBFactoryParent*
-  AllocPBackgroundIDBFactoryParent() MOZ_OVERRIDE;
+  AllocPBackgroundIDBFactoryParent(const LoggingInfo& aLoggingInfo)
+                                   MOZ_OVERRIDE;
 
   virtual bool
-  RecvPBackgroundIDBFactoryConstructor(PBackgroundIDBFactoryParent* aActor)
+  RecvPBackgroundIDBFactoryConstructor(PBackgroundIDBFactoryParent* aActor,
+                                       const LoggingInfo& aLoggingInfo)
                                        MOZ_OVERRIDE;
 
   virtual bool
   DeallocPBackgroundIDBFactoryParent(PBackgroundIDBFactoryParent* aActor)
                                      MOZ_OVERRIDE;
 
   virtual PBlobParent*
   AllocPBlobParent(const BlobConstructorParams& aParams) MOZ_OVERRIDE;
--- a/ipc/glue/PBackground.ipdl
+++ b/ipc/glue/PBackground.ipdl
@@ -3,32 +3,33 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PBackgroundIDBFactory;
 include protocol PBackgroundTest;
 include protocol PBlob;
 include protocol PFileDescriptorSet;
 
 include DOMTypes;
+include PBackgroundIDBSharedTypes;
 
 namespace mozilla {
 namespace ipc {
 
 sync protocol PBackground
 {
   manages PBackgroundIDBFactory;
   manages PBackgroundTest;
   manages PBlob;
   manages PFileDescriptorSet;
 
 parent:
   // Only called at startup during mochitests to check the basic infrastructure.
   PBackgroundTest(nsCString testArg);
 
-  PBackgroundIDBFactory();
+  PBackgroundIDBFactory(LoggingInfo loggingInfo);
 
 both:
   PBlob(BlobConstructorParams params);
 
   PFileDescriptorSet(FileDescriptor fd);
 };
 
 } // namespace ipc
--- a/ipc/glue/moz.build
+++ b/ipc/glue/moz.build
@@ -125,16 +125,17 @@ SOURCES += [
     'BackgroundParentImpl.cpp',
     'FileDescriptorSetChild.cpp',
     'FileDescriptorSetParent.cpp',
     'GeckoChildProcessHost.cpp',
     'URIUtils.cpp',
 ]
 
 LOCAL_INCLUDES += [
+    '/dom/indexedDB',
     '/xpcom/build',
 ]
 
 IPDL_SOURCES = [
     'InputStreamParams.ipdlh',
     'PBackground.ipdl',
     'PBackgroundSharedTypes.ipdlh',
     'PBackgroundTest.ipdl',
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -114,16 +114,22 @@ pref("dom.abortablepromise.enabled", fal
 pref("dom.quotaManager.testing", false);
 
 // Whether or not indexedDB is enabled.
 pref("dom.indexedDB.enabled", true);
 // Space to allow indexedDB databases before prompting (in MB).
 pref("dom.indexedDB.warningQuota", 50);
 // Whether or not indexedDB experimental features are enabled.
 pref("dom.indexedDB.experimental", false);
+// Enable indexedDB logging.
+pref("dom.indexedDB.logging.enabled", true);
+// Detailed output in log messages.
+pref("dom.indexedDB.logging.details", true);
+// Enable profiler marks for indexedDB events.
+pref("dom.indexedDB.logging.profiler-marks", false);
 
 // Whether or not Web Workers are enabled.
 pref("dom.workers.enabled", true);
 // The number of workers per domain allowed to run concurrently.
 pref("dom.workers.maxPerDomain", 20);
 
 // Whether or not Shared Web Workers are enabled.
 pref("dom.workers.sharedWorkers.enabled", true);