IDBRequest* IDBCursor::update(ScriptState* scriptState, const ScriptValue& value, ExceptionState& exceptionState)
{
    IDB_TRACE("IDBCursor::update");

    if (!m_gotValue) {
        exceptionState.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage);
        return nullptr;
    }
    if (isKeyCursor()) {
        exceptionState.throwDOMException(InvalidStateError, IDBDatabase::isKeyCursorErrorMessage);
        return nullptr;
    }
    if (isDeleted()) {
        exceptionState.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage);
        return nullptr;
    }
    if (m_transaction->isFinished() || m_transaction->isFinishing()) {
        exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
        return nullptr;
    }
    if (!m_transaction->isActive()) {
        exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
        return nullptr;
    }
    if (m_transaction->isReadOnly()) {
        exceptionState.throwDOMException(ReadOnlyError, "The record may not be updated inside a read-only transaction.");
        return nullptr;
    }

    IDBObjectStore* objectStore = effectiveObjectStore();
    return objectStore->put(scriptState, WebIDBPutModeCursorUpdate, IDBAny::create(this), value, m_primaryKey, exceptionState);
}
Example #2
0
void IDBTransaction::revertDatabaseMetadata() {
  DCHECK_NE(m_state, Active);
  if (!isVersionChange())
    return;

  // Mark stores created by this transaction as deleted.
  for (auto& objectStore : m_objectStoreMap.values()) {
    const int64_t objectStoreId = objectStore->id();
    if (!objectStore->isNewlyCreated()) {
      DCHECK(m_oldStoreMetadata.contains(objectStore));
      continue;
    }

    DCHECK(!m_oldStoreMetadata.contains(objectStore));
    m_database->revertObjectStoreCreation(objectStoreId);
    objectStore->markDeleted();
  }

  for (auto& it : m_oldStoreMetadata) {
    IDBObjectStore* objectStore = it.key;
    RefPtr<IDBObjectStoreMetadata> oldMetadata = it.value;

    m_database->revertObjectStoreMetadata(oldMetadata);
    objectStore->revertMetadata(oldMetadata);
  }
  for (auto& index : m_deletedIndexes)
    index->objectStore()->revertDeletedIndexMetadata(*index);
  for (auto& oldMedata : m_deletedObjectStores)
    m_database->revertObjectStoreMetadata(std::move(oldMedata));

  // We only need to revert the database's own metadata because we have reverted
  // the metadata for the database's object stores above.
  m_database->setDatabaseMetadata(m_oldDatabaseMetadata);
}
Example #3
0
void IDBTransaction::finished() {
#if DCHECK_IS_ON()
  DCHECK(!m_finishCalled);
  m_finishCalled = true;
#endif  // DCHECK_IS_ON()

  m_database->transactionFinished(this);

  // Remove references to the IDBObjectStore and IDBIndex instances held by
  // this transaction, so Oilpan can garbage-collect the instances that aren't
  // used by JavaScript.

  for (auto& it : m_objectStoreMap) {
    IDBObjectStore* objectStore = it.value;
    if (!isVersionChange() || objectStore->isNewlyCreated()) {
      DCHECK(!m_oldStoreMetadata.contains(objectStore));
      objectStore->clearIndexCache();
    } else {
      // We'll call clearIndexCache() on this store in the loop below.
      DCHECK(m_oldStoreMetadata.contains(objectStore));
    }
  }
  m_objectStoreMap.clear();

  for (auto& it : m_oldStoreMetadata) {
    IDBObjectStore* objectStore = it.key;
    objectStore->clearIndexCache();
  }
  m_oldStoreMetadata.clear();

  m_deletedIndexes.clear();
  m_deletedObjectStores.clear();
}
Example #4
0
void IDBDatabase::renameObjectStore(IDBObjectStore& objectStore, const String& newName)
{
    ASSERT(&originThread() == &Thread::current());
    ASSERT(m_versionChangeTransaction);
    ASSERT(m_info.hasObjectStore(objectStore.info().name()));

    m_info.renameObjectStore(objectStore.info().identifier(), newName);

    m_versionChangeTransaction->renameObjectStore(objectStore, newName);
}
void IDBTransaction::objectStoreDeleted(const String& name)
{
    ASSERT(m_state != Finished);
    ASSERT(isVersionChange());
    IDBObjectStoreMap::iterator it = m_objectStoreMap.find(name);
    if (it != m_objectStoreMap.end()) {
        IDBObjectStore* objectStore = it->value;
        m_objectStoreMap.remove(name);
        objectStore->markDeleted();
        m_objectStoreCleanupMap.set(objectStore, objectStore->metadata());
        m_deletedObjectStores.add(objectStore);
    }
}
Example #6
0
IDBObjectStore* IDBTransaction::objectStore(const String& name,
                                            ExceptionState& exceptionState) {
  if (isFinished()) {
    exceptionState.throwDOMException(
        InvalidStateError, IDBDatabase::transactionFinishedErrorMessage);
    return nullptr;
  }

  IDBObjectStoreMap::iterator it = m_objectStoreMap.find(name);
  if (it != m_objectStoreMap.end())
    return it->value;

  if (!isVersionChange() && !m_scope.contains(name)) {
    exceptionState.throwDOMException(
        NotFoundError, IDBDatabase::noSuchObjectStoreErrorMessage);
    return nullptr;
  }

  int64_t objectStoreId = m_database->findObjectStoreId(name);
  if (objectStoreId == IDBObjectStoreMetadata::InvalidId) {
    DCHECK(isVersionChange());
    exceptionState.throwDOMException(
        NotFoundError, IDBDatabase::noSuchObjectStoreErrorMessage);
    return nullptr;
  }

  DCHECK(m_database->metadata().objectStores.contains(objectStoreId));
  RefPtr<IDBObjectStoreMetadata> objectStoreMetadata =
      m_database->metadata().objectStores.get(objectStoreId);
  DCHECK(objectStoreMetadata.get());

  IDBObjectStore* objectStore =
      IDBObjectStore::create(std::move(objectStoreMetadata), this);
  DCHECK(!m_objectStoreMap.contains(name));
  m_objectStoreMap.set(name, objectStore);

  if (isVersionChange()) {
    DCHECK(!objectStore->isNewlyCreated())
        << "Object store IDs are not assigned sequentially";
    RefPtr<IDBObjectStoreMetadata> backupMetadata =
        objectStore->metadata().createCopy();
    m_oldStoreMetadata.set(objectStore, std::move(backupMetadata));
  }
  return objectStore;
}
Example #7
0
IDBRequest* IDBCursor::update(ScriptState* scriptState, ScriptValue& value, ExceptionState& exceptionState)
{
    IDB_TRACE("IDBCursor::update");

    if (!m_gotValue) {
        exceptionState.throwDOMException(InvalidStateError, IDBDatabase::noValueErrorMessage);
        return 0;
    }
    if (isKeyCursor()) {
        exceptionState.throwDOMException(InvalidStateError, IDBDatabase::isKeyCursorErrorMessage);
        return 0;
    }
    if (isDeleted()) {
        exceptionState.throwDOMException(InvalidStateError, IDBDatabase::sourceDeletedErrorMessage);
        return 0;
    }
    if (m_transaction->isFinished() || m_transaction->isFinishing()) {
        exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionFinishedErrorMessage);
        return 0;
    }
    if (!m_transaction->isActive()) {
        exceptionState.throwDOMException(TransactionInactiveError, IDBDatabase::transactionInactiveErrorMessage);
        return 0;
    }
    if (m_transaction->isReadOnly()) {
        exceptionState.throwDOMException(ReadOnlyError, "The record may not be updated inside a read-only transaction.");
        return 0;
    }

    IDBObjectStore* objectStore = effectiveObjectStore();
    const IDBKeyPath& keyPath = objectStore->metadata().keyPath;
    const bool usesInLineKeys = !keyPath.isNull();
    if (usesInLineKeys) {
        IDBKey* keyPathKey = createIDBKeyFromScriptValueAndKeyPath(scriptState->isolate(), value, keyPath);
        if (!keyPathKey || !keyPathKey->isEqual(m_primaryKey.get())) {
            exceptionState.throwDOMException(DataError, "The effective object store of this cursor uses in-line keys and evaluating the key path of the value parameter results in a different value than the cursor's effective key.");
            return 0;
        }
    }

    return objectStore->put(scriptState, blink::WebIDBPutModeCursorUpdate, IDBAny::create(this), value, m_primaryKey, exceptionState);
}
Example #8
0
ScriptValue IDBCursor::value(ScriptState* scriptState)
{
    ASSERT(isCursorWithValue());

    IDBObjectStore* objectStore = effectiveObjectStore();
    const IDBObjectStoreMetadata& metadata = objectStore->metadata();
    IDBAny* value;
    if (metadata.autoIncrement && !metadata.keyPath.isNull()) {
        value = IDBAny::create(m_value, m_blobs->getInfo(), m_primaryKey, metadata.keyPath);
#if ENABLE(ASSERT)
        assertPrimaryKeyValidOrInjectable(scriptState, m_value, m_blobs->getInfo(), m_primaryKey, metadata.keyPath);
#endif
    } else {
        value = IDBAny::create(m_value, m_blobs->getInfo());
    }

    m_valueDirty = false;
    ScriptValue scriptValue = idbAnyToScriptValue(scriptState, value);
    return scriptValue;
}
Example #9
0
Ref<IDBRequest> IDBTransaction::requestClearObjectStore(ScriptExecutionContext& context, IDBObjectStore& objectStore)
{
    LOG(IndexedDB, "IDBTransaction::requestClearObjectStore");
    ASSERT(isActive());

    Ref<IDBRequest> request = IDBRequest::create(context, objectStore, *this);
    addRequest(request.get());

    uint64_t objectStoreIdentifier = objectStore.info().identifier();
    auto operation = createTransactionOperation(*this, request.get(), &IDBTransaction::didClearObjectStoreOnServer, &IDBTransaction::clearObjectStoreOnServer, objectStoreIdentifier);
    scheduleOperation(WTFMove(operation));

    return request;
}
Example #10
0
ScriptValue IDBCursor::value(ScriptState* scriptState)
{
    ASSERT(isCursorWithValue());

    IDBObjectStore* objectStore = effectiveObjectStore();
    const IDBObjectStoreMetadata& metadata = objectStore->metadata();
    IDBAny* value;
    if (!m_value) {
        value = IDBAny::createUndefined();
    } else if (metadata.autoIncrement && !metadata.keyPath.isNull()) {
        RefPtr<IDBValue> idbValue = IDBValue::create(m_value.get(), m_primaryKey, metadata.keyPath);
#if ENABLE(ASSERT)
        assertPrimaryKeyValidOrInjectable(scriptState, idbValue.get());
#endif
        value = IDBAny::create(idbValue.release());
    } else {
        value = IDBAny::create(m_value);
    }

    m_valueDirty = false;
    ScriptValue scriptValue = ScriptValue::from(scriptState, value);
    return scriptValue;
}
Example #11
0
void IDBTransaction::objectStoreDeleted(const int64_t objectStoreId,
                                        const String& name) {
  DCHECK_NE(m_state, Finished)
      << "A finished transaction deleted an object store";
  DCHECK_EQ(m_mode, WebIDBTransactionModeVersionChange)
      << "A non-versionchange transaction deleted an object store";
  IDBObjectStoreMap::iterator it = m_objectStoreMap.find(name);
  if (it == m_objectStoreMap.end()) {
    // No IDBObjectStore instance was created for the deleted store in this
    // transaction. This happens if IDBDatabase.deleteObjectStore() is called
    // with the name of a store that wasn't instantated. We need to be able to
    // revert the metadata change if the transaction aborts, in order to return
    // correct values from IDB{Database, Transaction}.objectStoreNames.
    DCHECK(m_database->metadata().objectStores.contains(objectStoreId));
    RefPtr<IDBObjectStoreMetadata> metadata =
        m_database->metadata().objectStores.get(objectStoreId);
    DCHECK(metadata.get());
    DCHECK_EQ(metadata->name, name);
    m_deletedObjectStores.append(std::move(metadata));
  } else {
    IDBObjectStore* objectStore = it->value;
    m_objectStoreMap.remove(name);
    objectStore->markDeleted();
    if (objectStore->id() > m_oldDatabaseMetadata.maxObjectStoreId) {
      // The store was created and deleted in this transaction, so it will
      // not be restored even if the transaction aborts. We have just
      // removed our last reference to it.
      DCHECK(!m_oldStoreMetadata.contains(objectStore));
      objectStore->clearIndexCache();
    } else {
      // The store was created before this transaction, and we created an
      // IDBObjectStore instance for it. When that happened, we must have
      // snapshotted the store's metadata as well.
      DCHECK(m_oldStoreMetadata.contains(objectStore));
    }
  }
}
Example #12
0
bool
IDBCursor::IsSourceDeleted() const
{
  AssertIsOnOwningThread();
  MOZ_ASSERT(mTransaction);
  MOZ_ASSERT(mTransaction->IsOpen());

  IDBObjectStore* sourceObjectStore;
  if (mType == Type_Index || mType == Type_IndexKey) {
    MOZ_ASSERT(mSourceIndex);

    if (mSourceIndex->IsDeleted()) {
      return true;
    }

    sourceObjectStore = mSourceIndex->ObjectStore();
    MOZ_ASSERT(sourceObjectStore);
  } else {
    MOZ_ASSERT(mSourceObjectStore);
    sourceObjectStore = mSourceObjectStore;
  }

  return sourceObjectStore->IsDeleted();
}
Example #13
0
void IDBTransaction::indexDeleted(IDBIndex* index) {
  DCHECK(index);
  DCHECK(!index->isDeleted()) << "indexDeleted called twice for the same index";

  IDBObjectStore* objectStore = index->objectStore();
  DCHECK_EQ(objectStore->transaction(), this);
  DCHECK(m_objectStoreMap.contains(objectStore->name()))
      << "An index was deleted without accessing its object store";

  const auto& objectStoreIterator = m_oldStoreMetadata.find(objectStore);
  if (objectStoreIterator == m_oldStoreMetadata.end()) {
    // The index's object store was created in this transaction, so this
    // index was also created (and deleted) in this transaction, and will
    // not be restored if the transaction aborts.
    //
    // Subtle proof for the first sentence above: Deleting an index requires
    // calling deleteIndex() on the store's IDBObjectStore instance.
    // Whenever we create an IDBObjectStore instance for a previously
    // created store, we snapshot the store's metadata. So, deleting an
    // index of an "old" store can only be done after the store's metadata
    // is snapshotted.
    return;
  }

  const IDBObjectStoreMetadata* oldStoreMetadata =
      objectStoreIterator->value.get();
  DCHECK(oldStoreMetadata);
  if (!oldStoreMetadata->indexes.contains(index->id())) {
    // The index's object store was created before this transaction, but the
    // index was created (and deleted) in this transaction, so it will not
    // be restored if the transaction aborts.
    return;
  }

  m_deletedIndexes.append(index);
}
Example #14
0
Ref<IDBRequest> IDBTransaction::requestPutOrAdd(ScriptExecutionContext& context, IDBObjectStore& objectStore, IDBKey* key, SerializedScriptValue& value, IndexedDB::ObjectStoreOverwriteMode overwriteMode)
{
    LOG(IndexedDB, "IDBTransaction::requestPutOrAdd");
    ASSERT(isActive());
    ASSERT(!isReadOnly());
    ASSERT(objectStore.info().autoIncrement() || key);

    Ref<IDBRequest> request = IDBRequest::create(context, objectStore, *this);
    addRequest(request.get());

    auto operation = createTransactionOperation(*this, request.get(), &IDBTransaction::didPutOrAddOnServer, &IDBTransaction::putOrAddOnServer, key, &value, overwriteMode);
    scheduleOperation(WTFMove(operation));

    return request;
}
Example #15
0
already_AddRefed<IDBRequest>
IDBCursor::Update(JSContext* aCx, JS::Handle<JS::Value> aValue,
                  ErrorResult& aRv)
{
  AssertIsOnOwningThread();

  if (!mTransaction->IsOpen()) {
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
    return nullptr;
  }

  if (!mHaveValue || mType == Type_ObjectStoreKey || mType == Type_IndexKey) {
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
    return nullptr;
  }

  if (!mTransaction->IsWriteAllowed()) {
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
    return nullptr;
  }

  MOZ_ASSERT(mType == Type_ObjectStore || mType == Type_Index);
  MOZ_ASSERT(!mKey.IsUnset());
  MOZ_ASSERT_IF(mType == Type_Index, !mPrimaryKey.IsUnset());

  IDBObjectStore* objectStore;
  if (mType == Type_ObjectStore) {
    objectStore = mSourceObjectStore;
  } else {
    objectStore = mSourceIndex->ObjectStore();
  }

  MOZ_ASSERT(objectStore);

  const Key& primaryKey = (mType == Type_ObjectStore) ? mKey : mPrimaryKey;

  nsRefPtr<IDBRequest> request;

  if (objectStore->HasValidKeyPath()) {
    // Make sure the object given has the correct keyPath value set on it.
    const KeyPath& keyPath = objectStore->GetKeyPath();
    Key key;

    aRv = keyPath.ExtractKey(aCx, aValue, key);
    if (aRv.Failed()) {
      return nullptr;
    }

    if (key != primaryKey) {
      aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
      return nullptr;
    }

    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->AddOrPut(aCx,
                                    aValue,
                                    keyVal,
                                    /* aOverwrite */ true,
                                    /* aFromCursor */ true,
                                    aRv);
    if (aRv.Failed()) {
      return nullptr;
    }
  }

  request->SetSource(this);

  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));
  }

  return request.forget();
}
Example #16
0
already_AddRefed<IDBRequest>
IDBCursor::Delete(JSContext* aCx, ErrorResult& aRv)
{
  AssertIsOnOwningThread();

  if (!mTransaction->IsOpen()) {
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
    return nullptr;
  }

  if (!mHaveValue || mType == Type_ObjectStoreKey || mType == Type_IndexKey) {
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
    return nullptr;
  }

  if (!mTransaction->IsWriteAllowed()) {
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
    return nullptr;
  }

  MOZ_ASSERT(mType == Type_ObjectStore || mType == Type_Index);
  MOZ_ASSERT(!mKey.IsUnset());

  IDBObjectStore* objectStore;
  if (mType == Type_ObjectStore) {
    objectStore = mSourceObjectStore;
  } else {
    objectStore = mSourceIndex->ObjectStore();
  }

  MOZ_ASSERT(objectStore);

  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);
  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));
    }
  }
#endif

  return request.forget();
}
Example #17
0
already_AddRefed<IDBRequest>
IDBCursor::Delete(JSContext* aCx, ErrorResult& aRv)
{
  AssertIsOnOwningThread();

  if (!mTransaction->IsOpen()) {
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
    return nullptr;
  }

  if (!mHaveValue || mType == Type_ObjectStoreKey || mType == Type_IndexKey) {
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
    return nullptr;
  }

  if (!mTransaction->IsWriteAllowed()) {
    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
    return nullptr;
  }

  MOZ_ASSERT(mType == Type_ObjectStore || mType == Type_Index);
  MOZ_ASSERT(!mKey.IsUnset());

  IDBObjectStore* objectStore;
  if (mType == Type_ObjectStore) {
    objectStore = mSourceObjectStore;
  } else {
    objectStore = mSourceIndex->ObjectStore();
  }

  MOZ_ASSERT(objectStore);

  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->DeleteInternal(aCx, key, /* aFromCursor */ true, aRv);
  if (aRv.Failed()) {
    return nullptr;
  }

  request->SetSource(this);

  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));
  }

  return request.forget();
}