PassRefPtr<IDBRequest> IDBCursor::update(ScriptState* state, ScriptValue& value, ExceptionCode& ec) { IDB_TRACE("IDBCursor::update"); if (!m_gotValue || isKeyCursor()) { ec = IDBDatabaseException::InvalidStateError; return 0; } if (!m_transaction->isActive()) { ec = IDBDatabaseException::TransactionInactiveError; return 0; } if (m_transaction->isReadOnly()) { ec = IDBDatabaseException::ReadOnlyError; return 0; } RefPtr<IDBObjectStore> objectStore = effectiveObjectStore(); const IDBKeyPath& keyPath = objectStore->metadata().keyPath; const bool usesInLineKeys = !keyPath.isNull(); if (usesInLineKeys) { RefPtr<IDBKey> keyPathKey = createIDBKeyFromScriptValueAndKeyPath(m_request->requestState(), value, keyPath); if (!keyPathKey || !keyPathKey->isEqual(m_currentPrimaryKey.get())) { ec = IDBDatabaseException::DataError; return 0; } } return objectStore->put(IDBObjectStoreBackendInterface::CursorUpdate, IDBAny::create(this), state, value, m_currentPrimaryKey, ec); }
void LegacyRequest::onSuccess(PassRefPtr<SharedBuffer> valueBuffer, PassRefPtr<IDBKey> prpPrimaryKey, const IDBKeyPath& keyPath) { LOG(StorageAPI, "LegacyRequest::onSuccess(SharedBuffer, IDBKey, IDBKeyPath)"); if (!shouldEnqueueEvent()) return; #ifndef NDEBUG ASSERT(keyPath == effectiveObjectStore(m_source.get())->keyPath()); #endif DOMRequestState::Scope scope(m_requestState); // FIXME: By not knowing whether or not the key is defined here, we don't know // if a null valueBuffer means the value is null or the value is undefined. Deprecated::ScriptValue value = deserializeIDBValueBuffer(requestState(), valueBuffer, true); RefPtr<IDBKey> primaryKey = prpPrimaryKey; if (!keyPath.isNull()) { #ifndef NDEBUG RefPtr<IDBKey> expectedKey = createIDBKeyFromScriptValueAndKeyPath(requestState()->exec(), value, keyPath); ASSERT(!expectedKey || expectedKey->isEqual(primaryKey.get())); #endif bool injected = injectIDBKeyIntoScriptValue(requestState(), primaryKey, value, keyPath); ASSERT_UNUSED(injected, injected); } onSuccessInternal(value); }
void IDBCursor::setValueReady(DOMRequestState* state, PassRefPtr<IDBKey> key, PassRefPtr<IDBKey> primaryKey, ScriptValue& value) { m_currentKey = key; m_currentKeyValue = idbKeyToScriptValue(state, m_currentKey); m_currentPrimaryKey = primaryKey; m_currentPrimaryKeyValue = idbKeyToScriptValue(state, m_currentPrimaryKey); if (!isKeyCursor()) { RefPtr<IDBObjectStore> objectStore = effectiveObjectStore(); const IDBObjectStoreMetadata metadata = objectStore->metadata(); if (metadata.autoIncrement && !metadata.keyPath.isNull()) { #ifndef NDEBUG RefPtr<IDBKey> expectedKey = createIDBKeyFromScriptValueAndKeyPath(m_request->requestState(), value, metadata.keyPath); ASSERT(!expectedKey || expectedKey->isEqual(m_currentPrimaryKey.get())); #endif bool injected = injectIDBKeyIntoScriptValue(m_request->requestState(), m_currentPrimaryKey, value, metadata.keyPath); // FIXME: There is no way to report errors here. Move this into onSuccessWithContinuation so that we can abort the transaction there. See: https://bugs.webkit.org/show_bug.cgi?id=92278 ASSERT_UNUSED(injected, injected); } } m_currentValue = value; m_gotValue = true; }
PassRefPtr<IDBRequest> IDBCursor::update(ScriptExecutionContext* context, ScriptValue& value, ExceptionCode& ec) { IDB_TRACE("IDBCursor::update"); if (!m_gotValue || isKeyCursor()) { ec = IDBDatabaseException::IDB_INVALID_STATE_ERR; return 0; } if (!m_transaction->isActive()) { ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR; return 0; } if (m_transaction->isReadOnly()) { ec = IDBDatabaseException::READ_ONLY_ERR; return 0; } RefPtr<IDBObjectStore> objectStore = effectiveObjectStore(); const IDBKeyPath& keyPath = objectStore->metadata().keyPath; const bool usesInLineKeys = !keyPath.isNull(); if (usesInLineKeys) { RefPtr<IDBKey> keyPathKey = createIDBKeyFromScriptValueAndKeyPath(value, keyPath); if (!keyPathKey || !keyPathKey->isEqual(m_currentPrimaryKey.get())) { ec = IDBDatabaseException::DATA_ERR; return 0; } } return objectStore->put(IDBObjectStoreBackendInterface::CursorUpdate, IDBAny::create(this), context, value, m_currentPrimaryKey, ec); }
PassRefPtr<IDBKey> createIDBKeyFromScriptValueAndKeyPath(DOMRequestState* state, const ScriptValue& value, const IDBKeyPath& keyPath) { IDB_TRACE("createIDBKeyFromScriptValueAndKeyPath"); ASSERT(!keyPath.isNull()); v8::Isolate* isolate = state ? state->context()->GetIsolate() : v8::Isolate::GetCurrent(); ASSERT(isolate->InContext()); v8::HandleScope handleScope(isolate); if (keyPath.type() == IDBKeyPath::ArrayType) { IDBKey::KeyArray result; const Vector<String>& array = keyPath.array(); for (size_t i = 0; i < array.size(); ++i) { RefPtr<IDBKey> key = createIDBKeyFromScriptValueAndKeyPath(value, array[i], isolate); if (!key) return 0; result.append(key); } return IDBKey::createArray(result); } ASSERT(keyPath.type() == IDBKeyPath::StringType); return createIDBKeyFromScriptValueAndKeyPath(value, keyPath.string(), isolate); }
PassRefPtr<IDBKey> createIDBKeyFromScriptValueAndKeyPath(DOMRequestState* requestState, const ScriptValue& value, const IDBKeyPath& keyPath) { IDB_TRACE("createIDBKeyFromScriptValueAndKeyPath"); ASSERT(!keyPath.isNull()); ExecState* exec = requestState->exec(); if (keyPath.type() == IDBKeyPath::ArrayType) { IDBKey::KeyArray result; const Vector<String>& array = keyPath.array(); for (size_t i = 0; i < array.size(); i++) { RefPtr<IDBKey> key = createIDBKeyFromScriptValueAndKeyPath(exec, value, array[i]); if (!key) return 0; result.append(key); } return IDBKey::createArray(result); } ASSERT(keyPath.type() == IDBKeyPath::StringType); return createIDBKeyFromScriptValueAndKeyPath(exec, value, keyPath.string()); }
void assertPrimaryKeyValidOrInjectable(DOMRequestState* state, PassRefPtr<SharedBuffer> buffer, PassRefPtr<IDBKey> prpKey, const IDBKeyPath& keyPath) { RefPtr<IDBKey> key(prpKey); DOMRequestState::Scope scope(*state); v8::Isolate* isolate = state ? state->context()->GetIsolate() : v8::Isolate::GetCurrent(); ScriptValue keyValue = idbKeyToScriptValue(state, key); ScriptValue scriptValue(deserializeIDBValueBuffer(buffer.get(), isolate), isolate); RefPtr<IDBKey> expectedKey = createIDBKeyFromScriptValueAndKeyPath(state, scriptValue, keyPath); ASSERT(!expectedKey || expectedKey->isEqual(key.get())); bool injected = injectV8KeyIntoV8Value(keyValue.v8Value(), scriptValue.v8Value(), keyPath, isolate); ASSERT_UNUSED(injected, injected); }
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); }
void IDBRequest::onSuccess(PassRefPtr<SerializedScriptValue> prpSerializedScriptValue, PassRefPtr<IDBKey> prpPrimaryKey, const IDBKeyPath& keyPath) { IDB_TRACE("IDBRequest::onSuccess(SerializedScriptValue, IDBKey, IDBKeyPath)"); if (!shouldEnqueueEvent()) return; #ifndef NDEBUG ASSERT(keyPath == effectiveObjectStore(m_source)->keyPath()); #endif ScriptValue value = deserializeIDBValue(scriptExecutionContext(), prpSerializedScriptValue); RefPtr<IDBKey> primaryKey = prpPrimaryKey; #ifndef NDEBUG RefPtr<IDBKey> expectedKey = createIDBKeyFromScriptValueAndKeyPath(value, keyPath); ASSERT(!expectedKey || expectedKey->isEqual(primaryKey.get())); #endif bool injected = injectIDBKeyIntoScriptValue(primaryKey, value, keyPath); ASSERT_UNUSED(injected, injected); onSuccessInternal(value); }
void IDBRequest::onSuccess(PassRefPtr<SharedBuffer> valueBuffer, PassRefPtr<IDBKey> prpPrimaryKey, const IDBKeyPath& keyPath) { IDB_TRACE("IDBRequest::onSuccess(SharedBuffer, IDBKey, IDBKeyPath)"); if (!shouldEnqueueEvent()) return; #ifndef NDEBUG ASSERT(keyPath == effectiveObjectStore(m_source)->keyPath()); #endif DOMRequestState::Scope scope(m_requestState); ScriptValue value = deserializeIDBValueBuffer(requestState(), valueBuffer); RefPtr<IDBKey> primaryKey = prpPrimaryKey; #ifndef NDEBUG RefPtr<IDBKey> expectedKey = createIDBKeyFromScriptValueAndKeyPath(requestState(), value, keyPath); ASSERT(!expectedKey || expectedKey->isEqual(primaryKey.get())); #endif bool injected = injectIDBKeyIntoScriptValue(requestState(), primaryKey, value, keyPath); ASSERT_UNUSED(injected, injected); onSuccessInternal(value); }
static void generateIndexKeysForValue(DOMRequestState* requestState, const IDBIndexMetadata& indexMetadata, const Deprecated::ScriptValue& objectValue, IDBObjectStore::IndexKeys* indexKeys) { ASSERT(indexKeys); RefPtr<IDBKey> indexKey = createIDBKeyFromScriptValueAndKeyPath(requestState, objectValue, indexMetadata.keyPath); if (!indexKey) return; if (!indexMetadata.multiEntry || indexKey->type() != IDBKey::ArrayType) { if (!indexKey->isValid()) return; indexKeys->append(indexKey); } else { ASSERT(indexMetadata.multiEntry); ASSERT(indexKey->type() == IDBKey::ArrayType); indexKey = IDBKey::createMultiEntryArray(indexKey->array()); for (size_t i = 0; i < indexKey->array().size(); ++i) indexKeys->append(indexKey->array()[i]); } }
void generateIndexKeysForValue(ExecState* exec, const IDBIndexMetadata& indexMetadata, const Deprecated::ScriptValue& objectValue, Vector<IDBKeyData>& indexKeys) { RefPtr<IDBKey> indexKey = createIDBKeyFromScriptValueAndKeyPath(exec, objectValue, indexMetadata.keyPath); if (!indexKey) return; if (!indexMetadata.multiEntry || indexKey->type() != KeyType::Array) { if (!indexKey->isValid()) return; indexKeys.append(IDBKeyData(indexKey.get())); } else { ASSERT(indexMetadata.multiEntry); ASSERT(indexKey->type() == KeyType::Array); indexKey = IDBKey::createMultiEntryArray(indexKey->array()); if (!indexKey->isValid()) return; for (auto& i : indexKey->array()) indexKeys.append(IDBKeyData(i.get())); } }
PassRefPtr<IDBRequest> IDBObjectStore::put(IDBDatabaseBackend::PutMode putMode, PassRefPtr<IDBAny> source, JSC::ExecState* state, Deprecated::ScriptValue& value, PassRefPtr<IDBKey> prpKey, ExceptionCode& ec) { RefPtr<IDBKey> key = prpKey; if (m_deleted) { ec = IDBDatabaseException::InvalidStateError; return 0; } if (!m_transaction->isActive()) { ec = IDBDatabaseException::TransactionInactiveError; return 0; } if (m_transaction->isReadOnly()) { ec = IDBDatabaseException::ReadOnlyError; return 0; } // FIXME: Expose the JS engine exception state through ScriptState. bool didThrow = false; RefPtr<SerializedScriptValue> serializedValue = SerializedScriptValue::serialize(value, state, nullptr, nullptr, didThrow); if (didThrow) { // Setting an explicit ExceptionCode here would defer handling the already thrown exception. return 0; } if (serializedValue->hasBlobURLs()) { // FIXME: Add Blob/File/FileList support ec = IDBDatabaseException::DataCloneError; return 0; } const IDBKeyPath& keyPath = m_metadata.keyPath; const bool usesInLineKeys = !keyPath.isNull(); const bool hasKeyGenerator = autoIncrement(); ScriptExecutionContext* context = scriptExecutionContextFromExecState(state); DOMRequestState requestState(context); if (putMode != IDBDatabaseBackend::CursorUpdate && usesInLineKeys && key) { ec = IDBDatabaseException::DataError; return 0; } if (!usesInLineKeys && !hasKeyGenerator && !key) { ec = IDBDatabaseException::DataError; return 0; } if (usesInLineKeys) { RefPtr<IDBKey> keyPathKey = createIDBKeyFromScriptValueAndKeyPath(&requestState, value, keyPath); if (keyPathKey && !keyPathKey->isValid()) { ec = IDBDatabaseException::DataError; return 0; } if (!hasKeyGenerator && !keyPathKey) { ec = IDBDatabaseException::DataError; return 0; } if (hasKeyGenerator && !keyPathKey) { if (!canInjectIDBKeyIntoScriptValue(&requestState, value, keyPath)) { ec = IDBDatabaseException::DataError; return 0; } } if (keyPathKey) key = keyPathKey; } if (key && !key->isValid()) { ec = IDBDatabaseException::DataError; return 0; } Vector<int64_t> indexIds; Vector<IndexKeys> indexKeys; for (IDBObjectStoreMetadata::IndexMap::const_iterator it = m_metadata.indexes.begin(); it != m_metadata.indexes.end(); ++it) { IndexKeys keys; generateIndexKeysForValue(&requestState, it->value, value, &keys); indexIds.append(it->key); indexKeys.append(keys); } RefPtr<IDBRequest> request = IDBRequest::create(context, source, m_transaction.get()); Vector<uint8_t> valueBytes = serializedValue->toWireBytes(); // This is a hack to account for disagreements about whether SerializedScriptValue should deal in Vector<uint8_t> or Vector<char>. // See https://lists.webkit.org/pipermail/webkit-dev/2013-February/023682.html Vector<char>* valueBytesSigned = reinterpret_cast<Vector<char>*>(&valueBytes); RefPtr<SharedBuffer> valueBuffer = SharedBuffer::adoptVector(*valueBytesSigned); backendDB()->put(m_transaction->id(), id(), valueBuffer, key.release(), static_cast<IDBDatabaseBackend::PutMode>(putMode), request, indexIds, indexKeys); return request.release(); }
PassRefPtr<IDBRequest> IDBObjectStore::put(IDBDatabaseBackend::PutMode putMode, PassRefPtr<IDBAny> source, JSC::ExecState* state, Deprecated::ScriptValue& value, PassRefPtr<IDBKey> prpKey, ExceptionCode& ec) { RefPtr<IDBKey> key = prpKey; if (m_deleted) { ec = IDBDatabaseException::InvalidStateError; return nullptr; } if (!m_transaction->isActive()) { ec = IDBDatabaseException::TransactionInactiveError; return nullptr; } if (m_transaction->isReadOnly()) { ec = IDBDatabaseException::ReadOnlyError; return nullptr; } RefPtr<SerializedScriptValue> serializedValue = SerializedScriptValue::create(state, value.jsValue(), nullptr, nullptr); if (state->hadException()) return nullptr; if (serializedValue->hasBlobURLs()) { // FIXME: Add Blob/File/FileList support ec = IDBDatabaseException::DataCloneError; return nullptr; } const IDBKeyPath& keyPath = m_metadata.keyPath; const bool usesInLineKeys = !keyPath.isNull(); const bool hasKeyGenerator = autoIncrement(); ScriptExecutionContext* context = scriptExecutionContextFromExecState(state); DOMRequestState requestState(context); if (putMode != IDBDatabaseBackend::CursorUpdate && usesInLineKeys && key) { ec = IDBDatabaseException::DataError; return nullptr; } if (!usesInLineKeys && !hasKeyGenerator && !key) { ec = IDBDatabaseException::DataError; return nullptr; } if (usesInLineKeys) { RefPtr<IDBKey> keyPathKey = createIDBKeyFromScriptValueAndKeyPath(requestState.exec(), value, keyPath); if (keyPathKey && !keyPathKey->isValid()) { ec = IDBDatabaseException::DataError; return nullptr; } if (!hasKeyGenerator && !keyPathKey) { ec = IDBDatabaseException::DataError; return nullptr; } if (hasKeyGenerator && !keyPathKey) { if (!canInjectIDBKeyIntoScriptValue(&requestState, value, keyPath)) { ec = IDBDatabaseException::DataError; return nullptr; } } if (keyPathKey) key = keyPathKey; } if (key && !key->isValid()) { ec = IDBDatabaseException::DataError; return nullptr; } Vector<int64_t> indexIds; Vector<IndexKeys> indexKeys; for (auto& index : m_metadata.indexes) { Vector<IDBKeyData> keyDatas; generateIndexKeysForValue(requestState.exec(), index.value, value, keyDatas); indexIds.append(index.key); // FIXME: Much of the Indexed DB code needs to use IDBKeyData directly to avoid wasteful conversions like this. Vector<RefPtr<IDBKey>> keys; for (auto& keyData : keyDatas) { RefPtr<IDBKey> key = keyData.maybeCreateIDBKey(); if (key) keys.append(key.release()); } indexKeys.append(keys); } RefPtr<IDBRequest> request = IDBRequest::create(context, source, m_transaction.get()); Vector<uint8_t> valueBytes = serializedValue->toWireBytes(); // This is a hack to account for disagreements about whether SerializedScriptValue should deal in Vector<uint8_t> or Vector<char>. // See https://lists.webkit.org/pipermail/webkit-dev/2013-February/023682.html Vector<char>* valueBytesSigned = reinterpret_cast<Vector<char>*>(&valueBytes); RefPtr<SharedBuffer> valueBuffer = SharedBuffer::adoptVector(*valueBytesSigned); backendDB()->put(m_transaction->id(), id(), valueBuffer, key.release(), static_cast<IDBDatabaseBackend::PutMode>(putMode), request, indexIds, indexKeys); return request.release(); }