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(); }
IDBRequest* IDBObjectStore::put(ScriptState* scriptState, WebIDBPutMode putMode, IDBAny* source, const ScriptValue& value, IDBKey* key, ExceptionState& exceptionState) { if (isDeleted()) { exceptionState.throwDOMException(InvalidStateError, IDBDatabase::objectStoreDeletedErrorMessage); 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, IDBDatabase::transactionReadOnlyErrorMessage); return nullptr; } Vector<WebBlobInfo> blobInfo; RefPtr<SerializedScriptValue> serializedValue = SerializedScriptValueFactory::instance().create(scriptState->isolate(), value, &blobInfo, exceptionState); if (exceptionState.hadException()) return nullptr; // Keys that need to be extracted must be taken from a clone so that // side effects (i.e. getters) are not triggered. Construct the // clone lazily since the operation may be expensive. ScriptValue clone; const IDBKeyPath& keyPath = m_metadata.keyPath; const bool usesInLineKeys = !keyPath.isNull(); const bool hasKeyGenerator = autoIncrement(); if (putMode != WebIDBPutModeCursorUpdate && usesInLineKeys && key) { exceptionState.throwDOMException(DataError, "The object store uses in-line keys and the key parameter was provided."); return nullptr; } // This test logically belongs in IDBCursor, but must operate on the cloned value. if (putMode == WebIDBPutModeCursorUpdate && usesInLineKeys) { ASSERT(key); if (clone.isEmpty()) clone = deserializeScriptValue(scriptState, serializedValue.get(), &blobInfo); IDBKey* keyPathKey = ScriptValue::to<IDBKey*>(scriptState->isolate(), clone, exceptionState, keyPath); if (exceptionState.hadException()) return nullptr; if (!keyPathKey || !keyPathKey->isEqual(key)) { 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 nullptr; } } if (!usesInLineKeys && !hasKeyGenerator && !key) { exceptionState.throwDOMException(DataError, "The object store uses out-of-line keys and has no key generator and the key parameter was not provided."); return nullptr; } if (usesInLineKeys) { if (clone.isEmpty()) clone = deserializeScriptValue(scriptState, serializedValue.get(), &blobInfo); IDBKey* keyPathKey = ScriptValue::to<IDBKey*>(scriptState->isolate(), clone, exceptionState, keyPath); if (exceptionState.hadException()) return nullptr; if (keyPathKey && !keyPathKey->isValid()) { exceptionState.throwDOMException(DataError, "Evaluating the object store's key path yielded a value that is not a valid key."); return nullptr; } if (!hasKeyGenerator && !keyPathKey) { exceptionState.throwDOMException(DataError, "Evaluating the object store's key path did not yield a value."); return nullptr; } if (hasKeyGenerator && !keyPathKey) { if (!canInjectIDBKeyIntoScriptValue(scriptState->isolate(), clone, keyPath)) { exceptionState.throwDOMException(DataError, "A generated key could not be inserted into the value."); return nullptr; } } if (keyPathKey) key = keyPathKey; } if (key && !key->isValid()) { exceptionState.throwDOMException(DataError, IDBDatabase::notValidKeyErrorMessage); return nullptr; } if (!backendDB()) { exceptionState.throwDOMException(InvalidStateError, IDBDatabase::databaseClosedErrorMessage); return nullptr; } Vector<int64_t> indexIds; HeapVector<IndexKeys> indexKeys; for (const auto& it : m_metadata.indexes) { if (clone.isEmpty()) clone = deserializeScriptValue(scriptState, serializedValue.get(), &blobInfo); IndexKeys keys; generateIndexKeysForValue(scriptState->isolate(), it.value, clone, &keys); indexIds.append(it.key); indexKeys.append(keys); } IDBRequest* request = IDBRequest::create(scriptState, source, m_transaction.get()); Vector<char> wireBytes; serializedValue->toWireBytes(wireBytes); RefPtr<SharedBuffer> valueBuffer = SharedBuffer::adoptVector(wireBytes); backendDB()->put(m_transaction->id(), id(), WebData(valueBuffer), blobInfo, key, static_cast<WebIDBPutMode>(putMode), WebIDBCallbacksImpl::create(request).leakPtr(), indexIds, indexKeys); return request; }
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(); }