void MemoryObjectStore::getAllRecords(const IDBKeyRangeData& keyRangeData, std::optional<uint32_t> count, IndexedDB::GetAllType type, IDBGetAllResult& result) const { result = { type }; uint32_t targetCount; if (count && count.value()) targetCount = count.value(); else targetCount = std::numeric_limits<uint32_t>::max(); IDBKeyRangeData range = keyRangeData; uint32_t currentCount = 0; while (currentCount < targetCount) { IDBKeyData key = lowestKeyWithRecordInRange(range); if (key.isNull()) return; range.lowerKey = key; range.lowerOpen = true; if (type == IndexedDB::GetAllType::Keys) result.addKey(WTFMove(key)); else result.addValue(valueForKey(key)); ++currentCount; } }
IndexValueStore::Iterator IndexValueStore::reverseFind(const IDBKeyData& key, const IDBKeyData& primaryKey, CursorDuplicity duplicity) { ASSERT(!key.isNull()); ASSERT(!primaryKey.isNull()); IDBKeyRangeData range; range.upperKey = key; range.upperOpen = false; auto iterator = highestReverseIteratorInRange(range); if (iterator == m_orderedKeys.rend()) return { }; auto record = m_records.get(*iterator); ASSERT(record); auto primaryIterator = record->reverseFind(primaryKey, duplicity); if (primaryIterator.isValid()) return { *this, duplicity, iterator, primaryIterator }; // If we didn't find a primary key iterator in this entry, // we need to move on to start of the next record. iterator++; if (iterator == m_orderedKeys.rend()) return { }; record = m_records.get(*iterator); ASSERT(record); primaryIterator = record->reverseBegin(duplicity); ASSERT(primaryIterator.isValid()); return { *this, duplicity, iterator, primaryIterator }; }
void MemoryObjectStoreCursor::iterate(const IDBKeyData& key, uint32_t count, IDBGetResult& outData) { LOG(IndexedDB, "MemoryObjectStoreCursor::iterate to key %s", key.loggingString().utf8().data()); if (!m_objectStore.orderedKeys()) { m_currentPositionKey = { }; outData = { }; return; } if (key.isValid() && !m_info.range().containsKey(key)) { m_currentPositionKey = { }; outData = { }; return; } auto* set = m_objectStore.orderedKeys(); if (set) { if (m_info.isDirectionForward()) incrementForwardIterator(*set, key, count); else incrementReverseIterator(*set, key, count); } m_currentPositionKey = { }; if (!hasValidPosition()) { outData = { }; return; } currentData(outData); }
void UniqueIDBDatabase::performPutOrAdd(uint64_t callbackIdentifier, const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreIdentifier, const IDBKeyData& keyData, const ThreadSafeDataBuffer& valueData, IndexedDB::ObjectStoreOverwriteMode overwriteMode) { ASSERT(!isMainThread()); LOG(IndexedDB, "(db) UniqueIDBDatabase::performPutOrAdd"); ASSERT(m_backingStore); ASSERT(objectStoreIdentifier); IDBKeyData usedKey; IDBError error; auto objectStoreInfo = m_databaseInfo->infoForExistingObjectStore(objectStoreIdentifier); if (!objectStoreInfo) { error = IDBError(IDBExceptionCode::InvalidStateError, ASCIILiteral("Object store cannot be found in the backing store")); m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey)); return; } if (objectStoreInfo->autoIncrement() && !keyData.isValid()) { uint64_t keyNumber; error = m_backingStore->generateKeyNumber(transactionIdentifier, objectStoreIdentifier, keyNumber); if (!error.isNull()) { m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey)); return; } usedKey.setNumberValue(keyNumber); } else usedKey = keyData; if (overwriteMode == IndexedDB::ObjectStoreOverwriteMode::NoOverwrite) { bool keyExists; error = m_backingStore->keyExistsInObjectStore(transactionIdentifier, objectStoreIdentifier, usedKey, keyExists); if (error.isNull() && keyExists) error = IDBError(IDBExceptionCode::ConstraintError, ASCIILiteral("Key already exists in the object store")); if (!error.isNull()) { m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey)); return; } } // 3.4.1 Object Store Storage Operation // ...If a record already exists in store ... // then remove the record from store using the steps for deleting records from an object store... // This is important because formally deleting it from from the object store also removes it from the appropriate indexes. error = m_backingStore->deleteRange(transactionIdentifier, objectStoreIdentifier, usedKey); if (!error.isNull()) { m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey)); return; } error = m_backingStore->addRecord(transactionIdentifier, objectStoreIdentifier, usedKey, valueData); m_server.postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::didPerformPutOrAdd, callbackIdentifier, error, usedKey)); }
ThreadSafeDataBuffer MemoryObjectStore::valueForKeyRange(const IDBKeyRangeData& keyRangeData) const { LOG(IndexedDB, "MemoryObjectStore::valueForKey"); IDBKeyData key = lowestKeyWithRecordInRange(keyRangeData); if (key.isNull()) return ThreadSafeDataBuffer(); ASSERT(m_keyValueStore); return m_keyValueStore->get(key); }
bool SQLiteIDBCursor::advanceUnique() { IDBKeyData currentKey = m_currentKey; while (!m_completed) { if (!advanceOnce()) return false; // If the new current key is different from the old current key, we're done. if (currentKey.compare(m_currentKey)) return true; } return false; }
bool injectIDBKeyIntoScriptValue(JSC::ExecState& exec, const IDBKeyData& keyData, JSC::JSValue value, const IDBKeyPath& keyPath) { LOG(IndexedDB, "injectIDBKeyIntoScriptValue"); ASSERT(keyPath.type() == IndexedDB::KeyPathType::String); Vector<String> keyPathElements; IDBKeyPathParseError error; IDBParseKeyPath(keyPath.string(), keyPathElements, error); ASSERT(error == IDBKeyPathParseError::None); if (keyPathElements.isEmpty()) return false; JSValue parent = ensureNthValueOnKeyPath(&exec, value, keyPathElements, keyPathElements.size() - 1); if (parent.isUndefined()) return false; auto key = keyData.maybeCreateIDBKey(); if (!key) return false; if (!set(&exec, parent, keyPathElements.last(), idbKeyToJSValue(&exec, exec.lexicalGlobalObject(), key.get()))) return false; return true; }
bool injectIDBKeyIntoScriptValue(ExecState& exec, const IDBKeyData& keyData, JSValue value, const IDBKeyPath& keyPath) { LOG(IndexedDB, "injectIDBKeyIntoScriptValue"); ASSERT(WTF::holds_alternative<String>(keyPath)); Vector<String> keyPathElements; IDBKeyPathParseError error; IDBParseKeyPath(WTF::get<String>(keyPath), keyPathElements, error); ASSERT(error == IDBKeyPathParseError::None); if (keyPathElements.isEmpty()) return false; JSValue parent = ensureNthValueOnKeyPath(exec, value, keyPathElements, keyPathElements.size() - 1); if (parent.isUndefined()) return false; auto key = keyData.maybeCreateIDBKey(); if (!key) return false; if (!set(exec, parent, keyPathElements.last(), toJS(exec, *exec.lexicalGlobalObject(), key.get()))) return false; return true; }
JSC::JSValue toJS(JSC::ExecState* state, JSDOMGlobalObject* globalObject, const IDBKeyData& keyData) { ASSERT(state); ASSERT(globalObject); return toJS(*state, *globalObject, keyData.maybeCreateIDBKey().get()); }
void MemoryObjectStoreCursor::incrementReverseIterator(std::set<IDBKeyData>& set, const IDBKeyData& key, uint32_t count) { // We might need to re-grab the current iterator. // e.g. If the record it was pointed to had been deleted. bool didResetIterator = false; if (!m_iterator) { if (!m_currentPositionKey.isValid()) return; m_remainingRange.upperKey = m_currentPositionKey; m_remainingRange.upperOpen = false; setFirstInRemainingRange(set); didResetIterator = true; } if (*m_iterator == set.end()) return; if (key.isValid()) { // If iterating to a key, the count passed in must be zero, as there is no way to iterate by both a count and to a key. ASSERT(!count); if (!m_info.range().containsKey(key)) return; if ((*m_iterator)->compare(key) > 0) { m_remainingRange.upperKey = key; m_remainingRange.upperOpen = false; setFirstInRemainingRange(set); } return; } if (!count) count = 1; // If the reverseIterator was reset because it's previous record had been deleted, // we might have already advanced past the current position, eating one one of the iteration counts. if (didResetIterator && (*m_iterator)->compare(m_currentPositionKey) < 0) --count; while (count) { if (*m_iterator == set.begin()) { m_iterator = Nullopt; return; } --count; --*m_iterator; if (!m_info.range().containsKey(**m_iterator)) { m_iterator = Nullopt; return; } } }
IndexValueStore::Iterator IndexValueStore::find(const IDBKeyData& key, const IDBKeyData& primaryKey) { ASSERT(!key.isNull()); ASSERT(!primaryKey.isNull()); IDBKeyRangeData range; range.lowerKey = key; range.lowerOpen = false; auto iterator = lowestIteratorInRange(range); if (iterator == m_orderedKeys.end()) return { }; auto record = m_records.get(*iterator); ASSERT(record); // If the main record iterator is not equal to the key we were looking for, // we know the primary key record should be the first. if (*iterator != key) { auto primaryIterator = record->begin(); ASSERT(primaryIterator.isValid()); return { *this, iterator, primaryIterator }; } auto primaryIterator = record->find(primaryKey); if (primaryIterator.isValid()) return { *this, iterator, primaryIterator }; // If we didn't find a primary key iterator in this entry, // we need to move on to start of the next record. iterator++; if (iterator == m_orderedKeys.end()) return { }; record = m_records.get(*iterator); ASSERT(record); primaryIterator = record->begin(); ASSERT(primaryIterator.isValid()); return { *this, iterator, primaryIterator }; }
IDBGetResult MemoryIndex::getResultForKeyRange(IndexedDB::IndexRecordType type, const IDBKeyRangeData& range) const { LOG(IndexedDB, "MemoryIndex::getResultForKeyRange"); if (!m_records) return { }; IDBKeyData keyToLookFor; if (range.isExactlyOneKey()) keyToLookFor = range.lowerKey; else keyToLookFor = m_records->lowestKeyWithRecordInRange(range); if (keyToLookFor.isNull()) return { }; const IDBKeyData* keyValue = m_records->lowestValueForKey(keyToLookFor); if (!keyValue) return { }; return type == IndexedDB::IndexRecordType::Key ? IDBGetResult(*keyValue) : IDBGetResult(m_objectStore.valueForKeyRange(*keyValue)); }
RefPtr<SharedBuffer> serializeIDBKeyData(const IDBKeyData& key) { #if USE(CF) Vector<char> data; data.append(SIDBKeyVersion); encodeKey(data, key); return SharedBuffer::adoptVector(data); #else auto encoder = KeyedEncoder::encoder(); key.encode(*encoder); return encoder->finishEncoding(); #endif }
IndexValueStore::Iterator IndexValueStore::find(const IDBKeyData& key, bool open) { IDBKeyRangeData range; if (!key.isNull()) range.lowerKey = key; else range.lowerKey = IDBKeyData::minimum(); range.lowerOpen = open; auto iterator = lowestIteratorInRange(range); if (iterator == m_orderedKeys.end()) return { }; auto record = m_records.get(*iterator); ASSERT(record); auto primaryIterator = record->begin(); ASSERT(primaryIterator.isValid()); return { *this, iterator, primaryIterator }; }
IndexValueStore::Iterator IndexValueStore::reverseFind(const IDBKeyData& key, CursorDuplicity duplicity, bool open) { IDBKeyRangeData range; if (!key.isNull()) range.upperKey = key; else range.upperKey = IDBKeyData::maximum(); range.upperOpen = open; auto iterator = highestReverseIteratorInRange(range); if (iterator == m_orderedKeys.rend()) return { }; auto record = m_records.get(*iterator); ASSERT(record); auto primaryIterator = record->reverseBegin(duplicity); ASSERT(primaryIterator.isValid()); return { *this, duplicity, iterator, primaryIterator }; }
void IDBCursor::continueFunction(const IDBKeyData& key, ExceptionCodeWithMessage& ec) { LOG(IndexedDB, "IDBCursor::continueFunction"); if (!m_request) { ec.code = IDBDatabaseException::InvalidStateError; return; } if (sourcesDeleted()) { ec.code = IDBDatabaseException::InvalidStateError; return; } if (!transaction().isActive()) { ec.code = IDBDatabaseException::TransactionInactiveError; ec.message = ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The transaction is inactive or finished."); return; } if (!m_gotValue) { ec.code = IDBDatabaseException::InvalidStateError; ec.message = ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The cursor is being iterated or has iterated past its end."); return; } if (!key.isNull() && !key.isValid()) { ec.code = IDBDatabaseException::DataError; ec.message = ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The parameter is not a valid key."); return; } if (m_info.isDirectionForward()) { if (!key.isNull() && key.compare(m_currentPrimaryKeyData) <= 0) { ec.code = IDBDatabaseException::DataError; ec.message = ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The parameter is less than or equal to this cursor's position."); return; } } else if (!key.isNull() && key.compare(m_currentPrimaryKeyData) >= 0) { ec.code = IDBDatabaseException::DataError; ec.message = ASCIILiteral("Failed to execute 'continue' on 'IDBCursor': The parameter is greater than or equal to this cursor's position."); return; } m_gotValue = false; uncheckedIteratorCursor(key, 0); }
static void encodeKey(Vector<char>& data, const IDBKeyData& key) { SIDBKeyType type = serializedTypeForKeyType(key.type()); data.append(static_cast<char>(type)); switch (type) { case SIDBKeyType::Number: writeDouble(data, key.number()); break; case SIDBKeyType::Date: writeDouble(data, key.date()); break; case SIDBKeyType::String: { auto string = key.string(); uint32_t length = string.length(); writeLittleEndian(data, length); for (size_t i = 0; i < length; ++i) writeLittleEndian(data, string[i]); break; } case SIDBKeyType::Binary: { auto& buffer = key.binary(); uint64_t size = buffer.size(); writeLittleEndian(data, size); auto* bufferData = buffer.data(); ASSERT(bufferData || !size); if (bufferData) data.append(bufferData->data(), bufferData->size()); break; } case SIDBKeyType::Array: { auto& array = key.array(); uint64_t size = array.size(); writeLittleEndian(data, size); for (auto& key : array) encodeKey(data, key); break; } case SIDBKeyType::Min: case SIDBKeyType::Max: break; } }
void IDBCursor::continueFunction(const IDBKeyData& key, ExceptionCode& ec) { LOG(IndexedDB, "IDBCursor::continueFunction"); if (!m_request) { ec = IDBDatabaseException::InvalidStateError; return; } if (sourcesDeleted()) { ec = IDBDatabaseException::InvalidStateError; return; } if (!transaction().isActive()) { ec = IDBDatabaseException::TransactionInactiveError; return; } if (!m_gotValue) { ec = IDBDatabaseException::InvalidStateError; return; } if (!key.isNull() && !key.isValid()) { ec = IDBDatabaseException::DataError; return; } if (m_info.isDirectionForward()) { if (!key.isNull() && key.compare(m_currentPrimaryKeyData) <= 0) { ec = IDBDatabaseException::DataError; return; } } else if (!key.isNull() && key.compare(m_currentPrimaryKeyData) >= 0) { ec = IDBDatabaseException::DataError; return; } m_gotValue = false; uncheckedIteratorCursor(key, 0); }
JSValue idbKeyDataToJSValue(JSC::ExecState& exec, const IDBKeyData& keyData) { if (keyData.isNull()) return jsUndefined(); Locker<JSLock> locker(exec.vm().apiLock()); switch (keyData.type()) { case KeyType::Array: { const Vector<IDBKeyData>& inArray = keyData.array(); size_t size = inArray.size(); JSArray* outArray = constructEmptyArray(&exec, 0, exec.lexicalGlobalObject(), size); for (size_t i = 0; i < size; ++i) { auto& arrayKey = inArray.at(i); outArray->putDirectIndex(&exec, i, idbKeyDataToJSValue(exec, arrayKey)); } return JSValue(outArray); } case KeyType::String: return jsStringWithCache(&exec, keyData.string()); case KeyType::Date: return jsDateOrNull(&exec, keyData.date()); case KeyType::Number: return jsNumber(keyData.number()); case KeyType::Min: case KeyType::Max: case KeyType::Invalid: ASSERT_NOT_REACHED(); return jsUndefined(); } ASSERT_NOT_REACHED(); return jsUndefined(); }
static bool decodeKey(const uint8_t*& data, const uint8_t* end, IDBKeyData& result) { if (!data || data >= end) return false; SIDBKeyType type = static_cast<SIDBKeyType>(data++[0]); switch (type) { case SIDBKeyType::Min: result = IDBKeyData::minimum(); return true; case SIDBKeyType::Max: result = IDBKeyData::maximum(); return true; case SIDBKeyType::Number: { double d; if (!readDouble(data, end, d)) return false; result.setNumberValue(d); return true; } case SIDBKeyType::Date: { double d; if (!readDouble(data, end, d)) return false; result.setDateValue(d); return true; } case SIDBKeyType::String: { uint32_t length; if (!readLittleEndian(data, end, length)) return false; if (static_cast<uint64_t>(end - data) < length * 2) return false; Vector<UChar> buffer; buffer.reserveInitialCapacity(length); for (size_t i = 0; i < length; i++) { uint16_t ch; if (!readLittleEndian(data, end, ch)) return false; buffer.uncheckedAppend(ch); } result.setStringValue(String::adopt(WTFMove(buffer))); return true; } case SIDBKeyType::Binary: { uint64_t size64; if (!readLittleEndian(data, end, size64)) return false; if (static_cast<uint64_t>(end - data) < size64) return false; if (size64 > std::numeric_limits<size_t>::max()) return false; size_t size = static_cast<size_t>(size64); Vector<uint8_t> dataVector; dataVector.append(data, size); data += size; result.setBinaryValue(ThreadSafeDataBuffer::adoptVector(dataVector)); return true; } case SIDBKeyType::Array: { uint64_t size64; if (!readLittleEndian(data, end, size64)) return false; if (size64 > std::numeric_limits<size_t>::max()) return false; size_t size = static_cast<size_t>(size64); Vector<IDBKeyData> array; array.reserveInitialCapacity(size); for (size_t i = 0; i < size; ++i) { IDBKeyData keyData; if (!decodeKey(data, end, keyData)) return false; ASSERT(keyData.isValid()); array.uncheckedAppend(WTFMove(keyData)); } result.setArrayValue(array); return true; } default: LOG_ERROR("decodeKey encountered unexpected type: %i", (int)type); return false; } }
CrossThreadCopierBase<false, false, IDBKeyData>::Type CrossThreadCopierBase<false, false, IDBKeyData>::copy(const IDBKeyData& keyData) { return keyData.isolatedCopy(); }
Deprecated::ScriptValue idbKeyDataToScriptValue(ScriptExecutionContext* context, const IDBKeyData& keyData) { RefPtr<IDBKey> key = keyData.maybeCreateIDBKey(); DOMRequestState requestState(context); return idbKeyToScriptValue(&requestState, key.get()); }