| /* |
| * Copyright (C) 2015 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "MemoryBackingStoreTransaction.h" |
| |
| #if ENABLE(INDEXED_DATABASE) |
| |
| #include "IDBKeyRangeData.h" |
| #include "IDBValue.h" |
| #include "IndexedDB.h" |
| #include "Logging.h" |
| #include "MemoryIDBBackingStore.h" |
| #include "MemoryObjectStore.h" |
| #include <wtf/SetForScope.h> |
| |
| namespace WebCore { |
| namespace IDBServer { |
| |
| std::unique_ptr<MemoryBackingStoreTransaction> MemoryBackingStoreTransaction::create(MemoryIDBBackingStore& backingStore, const IDBTransactionInfo& info) |
| { |
| return makeUnique<MemoryBackingStoreTransaction>(backingStore, info); |
| } |
| |
| MemoryBackingStoreTransaction::MemoryBackingStoreTransaction(MemoryIDBBackingStore& backingStore, const IDBTransactionInfo& info) |
| : m_backingStore(backingStore) |
| , m_info(info) |
| { |
| if (m_info.mode() == IDBTransactionMode::Versionchange) { |
| IDBDatabaseInfo info; |
| auto error = m_backingStore.getOrEstablishDatabaseInfo(info); |
| if (error.isNull()) |
| m_originalDatabaseInfo = makeUnique<IDBDatabaseInfo>(info); |
| } |
| } |
| |
| MemoryBackingStoreTransaction::~MemoryBackingStoreTransaction() |
| { |
| ASSERT(!m_inProgress); |
| } |
| |
| void MemoryBackingStoreTransaction::addNewObjectStore(MemoryObjectStore& objectStore) |
| { |
| LOG(IndexedDB, "MemoryBackingStoreTransaction::addNewObjectStore()"); |
| |
| ASSERT(isVersionChange()); |
| m_versionChangeAddedObjectStores.add(&objectStore); |
| |
| addExistingObjectStore(objectStore); |
| } |
| |
| void MemoryBackingStoreTransaction::addNewIndex(MemoryIndex& index) |
| { |
| LOG(IndexedDB, "MemoryBackingStoreTransaction::addNewIndex()"); |
| |
| ASSERT(isVersionChange()); |
| m_versionChangeAddedIndexes.add(&index); |
| |
| addExistingIndex(index); |
| } |
| |
| void MemoryBackingStoreTransaction::addExistingIndex(MemoryIndex& index) |
| { |
| LOG(IndexedDB, "MemoryBackingStoreTransaction::addExistingIndex"); |
| |
| ASSERT(isWriting()); |
| |
| ASSERT(!m_indexes.contains(&index)); |
| m_indexes.add(&index); |
| } |
| |
| void MemoryBackingStoreTransaction::indexDeleted(Ref<MemoryIndex>&& index) |
| { |
| m_indexes.remove(&index.get()); |
| |
| // If this MemoryIndex belongs to an object store that will not get restored if this transaction aborts, |
| // then we can forget about it altogether. |
| auto& objectStore = index->objectStore(); |
| if (auto deletedObjectStore = m_deletedObjectStores.get(objectStore.info().name())) { |
| if (deletedObjectStore != &objectStore) |
| return; |
| } |
| |
| auto addResult = m_deletedIndexes.add(index->info().name(), nullptr); |
| if (addResult.isNewEntry) |
| addResult.iterator->value = WTFMove(index); |
| } |
| |
| void MemoryBackingStoreTransaction::addExistingObjectStore(MemoryObjectStore& objectStore) |
| { |
| LOG(IndexedDB, "MemoryBackingStoreTransaction::addExistingObjectStore"); |
| |
| ASSERT(isWriting()); |
| |
| ASSERT(!m_objectStores.contains(&objectStore)); |
| m_objectStores.add(&objectStore); |
| |
| objectStore.writeTransactionStarted(*this); |
| |
| m_originalKeyGenerators.add(&objectStore, objectStore.currentKeyGeneratorValue()); |
| } |
| |
| void MemoryBackingStoreTransaction::objectStoreDeleted(Ref<MemoryObjectStore>&& objectStore) |
| { |
| ASSERT(m_objectStores.contains(&objectStore.get())); |
| m_objectStores.remove(&objectStore.get()); |
| |
| objectStore->deleteAllIndexes(*this); |
| |
| auto addResult = m_deletedObjectStores.add(objectStore->info().name(), nullptr); |
| if (addResult.isNewEntry) |
| addResult.iterator->value = WTFMove(objectStore); |
| } |
| |
| void MemoryBackingStoreTransaction::objectStoreCleared(MemoryObjectStore& objectStore, std::unique_ptr<KeyValueMap>&& keyValueMap, std::unique_ptr<IDBKeyDataSet>&& orderedKeys) |
| { |
| ASSERT(m_objectStores.contains(&objectStore)); |
| |
| auto addResult = m_clearedKeyValueMaps.add(&objectStore, nullptr); |
| |
| // If this object store has already been cleared during this transaction, we shouldn't remember this clearing. |
| if (!addResult.isNewEntry) |
| return; |
| |
| addResult.iterator->value = WTFMove(keyValueMap); |
| |
| ASSERT(!m_clearedOrderedKeys.contains(&objectStore)); |
| m_clearedOrderedKeys.add(&objectStore, WTFMove(orderedKeys)); |
| } |
| |
| void MemoryBackingStoreTransaction::objectStoreRenamed(MemoryObjectStore& objectStore, const String& oldName) |
| { |
| ASSERT(m_objectStores.contains(&objectStore)); |
| ASSERT(m_info.mode() == IDBTransactionMode::Versionchange); |
| |
| // We only care about the first rename in a given transaction, because if the transaction is aborted we want |
| // to go back to the first 'oldName' |
| m_originalObjectStoreNames.add(&objectStore, oldName); |
| } |
| |
| void MemoryBackingStoreTransaction::indexRenamed(MemoryIndex& index, const String& oldName) |
| { |
| ASSERT(m_objectStores.contains(&index.objectStore())); |
| ASSERT(m_info.mode() == IDBTransactionMode::Versionchange); |
| |
| // We only care about the first rename in a given transaction, because if the transaction is aborted we want |
| // to go back to the first 'oldName' |
| m_originalIndexNames.add(&index, oldName); |
| } |
| |
| void MemoryBackingStoreTransaction::indexCleared(MemoryIndex& index, std::unique_ptr<IndexValueStore>&& valueStore) |
| { |
| auto addResult = m_clearedIndexValueStores.add(&index, nullptr); |
| |
| // If this index has already been cleared during this transaction, we shouldn't remember this clearing. |
| if (!addResult.isNewEntry) |
| return; |
| |
| addResult.iterator->value = WTFMove(valueStore); |
| } |
| |
| void MemoryBackingStoreTransaction::recordValueChanged(MemoryObjectStore& objectStore, const IDBKeyData& key, ThreadSafeDataBuffer* value) |
| { |
| ASSERT(m_objectStores.contains(&objectStore)); |
| |
| if (m_isAborting) |
| return; |
| |
| // If this object store had been cleared during the transaction, no point in recording this |
| // individual key/value change as its entire key/value map will be restored upon abort. |
| if (m_clearedKeyValueMaps.contains(&objectStore)) |
| return; |
| |
| auto originalAddResult = m_originalValues.add(&objectStore, nullptr); |
| if (originalAddResult.isNewEntry) |
| originalAddResult.iterator->value = makeUnique<KeyValueMap>(); |
| |
| auto* map = originalAddResult.iterator->value.get(); |
| |
| auto addResult = map->add(key, ThreadSafeDataBuffer()); |
| if (!addResult.isNewEntry) |
| return; |
| |
| if (value) |
| addResult.iterator->value = *value; |
| } |
| |
| void MemoryBackingStoreTransaction::abort() |
| { |
| LOG(IndexedDB, "MemoryBackingStoreTransaction::abort()"); |
| |
| SetForScope<bool> change(m_isAborting, true); |
| |
| for (const auto& iterator : m_originalIndexNames) |
| iterator.key->rename(iterator.value); |
| m_originalIndexNames.clear(); |
| |
| for (const auto& iterator : m_originalObjectStoreNames) |
| iterator.key->rename(iterator.value); |
| m_originalObjectStoreNames.clear(); |
| |
| for (const auto& objectStore : m_versionChangeAddedObjectStores) |
| m_backingStore.removeObjectStoreForVersionChangeAbort(*objectStore); |
| m_versionChangeAddedObjectStores.clear(); |
| |
| for (auto& objectStore : m_deletedObjectStores.values()) { |
| m_backingStore.restoreObjectStoreForVersionChangeAbort(*objectStore); |
| ASSERT(!m_objectStores.contains(objectStore.get())); |
| m_objectStores.add(objectStore); |
| } |
| m_deletedObjectStores.clear(); |
| |
| if (m_originalDatabaseInfo) { |
| ASSERT(m_info.mode() == IDBTransactionMode::Versionchange); |
| m_backingStore.setDatabaseInfo(*m_originalDatabaseInfo); |
| } |
| |
| // Restore cleared index value stores before we re-insert values into object stores |
| // because inserting those values will regenerate the appropriate index values. |
| for (auto& iterator : m_clearedIndexValueStores) |
| iterator.key->replaceIndexValueStore(WTFMove(iterator.value)); |
| m_clearedIndexValueStores.clear(); |
| |
| for (auto& objectStore : m_objectStores) { |
| ASSERT(m_originalKeyGenerators.contains(objectStore.get())); |
| objectStore->setKeyGeneratorValue(m_originalKeyGenerators.get(objectStore.get())); |
| |
| auto clearedKeyValueMap = m_clearedKeyValueMaps.take(objectStore.get()); |
| if (clearedKeyValueMap) { |
| ASSERT(m_clearedOrderedKeys.contains(objectStore.get())); |
| objectStore->replaceKeyValueStore(WTFMove(clearedKeyValueMap), m_clearedOrderedKeys.take(objectStore.get())); |
| } |
| |
| auto keyValueMap = m_originalValues.take(objectStore.get()); |
| if (!keyValueMap) |
| continue; |
| |
| for (const auto& entry : *keyValueMap) { |
| objectStore->deleteRecord(entry.key); |
| objectStore->addRecord(*this, entry.key, { entry.value }); |
| } |
| } |
| |
| for (auto& index : m_deletedIndexes.values()) |
| index->objectStore().maybeRestoreDeletedIndex(*index); |
| m_deletedIndexes.clear(); |
| |
| finish(); |
| } |
| |
| void MemoryBackingStoreTransaction::commit() |
| { |
| LOG(IndexedDB, "MemoryBackingStoreTransaction::commit()"); |
| |
| finish(); |
| } |
| |
| void MemoryBackingStoreTransaction::finish() |
| { |
| m_inProgress = false; |
| |
| if (!isWriting()) |
| return; |
| |
| for (auto& objectStore : m_objectStores) |
| objectStore->writeTransactionFinished(*this); |
| for (auto& objectStore : m_deletedObjectStores.values()) |
| objectStore->writeTransactionFinished(*this); |
| } |
| |
| } // namespace IDBServer |
| } // namespace WebCore |
| |
| #endif // ENABLE(INDEXED_DATABASE) |