| 1 | /* |
| 2 | * Copyright (C) 2015 Apple Inc. All rights reserved. |
| 3 | * |
| 4 | * Redistribution and use in source and binary forms, with or without |
| 5 | * modification, are permitted provided that the following conditions |
| 6 | * are met: |
| 7 | * 1. Redistributions of source code must retain the above copyright |
| 8 | * notice, this list of conditions and the following disclaimer. |
| 9 | * 2. Redistributions in binary form must reproduce the above copyright |
| 10 | * notice, this list of conditions and the following disclaimer in the |
| 11 | * documentation and/or other materials provided with the distribution. |
| 12 | * |
| 13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| 14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| 17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| 18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| 19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| 20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| 21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 24 | */ |
| 25 | |
| 26 | #include "config.h" |
| 27 | #include "MemoryBackingStoreTransaction.h" |
| 28 | |
| 29 | #if ENABLE(INDEXED_DATABASE) |
| 30 | |
| 31 | #include "IDBKeyRangeData.h" |
| 32 | #include "IDBValue.h" |
| 33 | #include "IndexedDB.h" |
| 34 | #include "Logging.h" |
| 35 | #include "MemoryIDBBackingStore.h" |
| 36 | #include "MemoryObjectStore.h" |
| 37 | #include <wtf/SetForScope.h> |
| 38 | |
| 39 | namespace WebCore { |
| 40 | namespace IDBServer { |
| 41 | |
| 42 | std::unique_ptr<MemoryBackingStoreTransaction> MemoryBackingStoreTransaction::create(MemoryIDBBackingStore& backingStore, const IDBTransactionInfo& info) |
| 43 | { |
| 44 | return std::make_unique<MemoryBackingStoreTransaction>(backingStore, info); |
| 45 | } |
| 46 | |
| 47 | MemoryBackingStoreTransaction::MemoryBackingStoreTransaction(MemoryIDBBackingStore& backingStore, const IDBTransactionInfo& info) |
| 48 | : m_backingStore(backingStore) |
| 49 | , m_info(info) |
| 50 | { |
| 51 | if (m_info.mode() == IDBTransactionMode::Versionchange) { |
| 52 | IDBDatabaseInfo info; |
| 53 | auto error = m_backingStore.getOrEstablishDatabaseInfo(info); |
| 54 | if (error.isNull()) |
| 55 | m_originalDatabaseInfo = std::make_unique<IDBDatabaseInfo>(info); |
| 56 | } |
| 57 | } |
| 58 | |
| 59 | MemoryBackingStoreTransaction::~MemoryBackingStoreTransaction() |
| 60 | { |
| 61 | ASSERT(!m_inProgress); |
| 62 | } |
| 63 | |
| 64 | void MemoryBackingStoreTransaction::addNewObjectStore(MemoryObjectStore& objectStore) |
| 65 | { |
| 66 | LOG(IndexedDB, "MemoryBackingStoreTransaction::addNewObjectStore()" ); |
| 67 | |
| 68 | ASSERT(isVersionChange()); |
| 69 | m_versionChangeAddedObjectStores.add(&objectStore); |
| 70 | |
| 71 | addExistingObjectStore(objectStore); |
| 72 | } |
| 73 | |
| 74 | void MemoryBackingStoreTransaction::addNewIndex(MemoryIndex& index) |
| 75 | { |
| 76 | LOG(IndexedDB, "MemoryBackingStoreTransaction::addNewIndex()" ); |
| 77 | |
| 78 | ASSERT(isVersionChange()); |
| 79 | m_versionChangeAddedIndexes.add(&index); |
| 80 | |
| 81 | addExistingIndex(index); |
| 82 | } |
| 83 | |
| 84 | void MemoryBackingStoreTransaction::addExistingIndex(MemoryIndex& index) |
| 85 | { |
| 86 | LOG(IndexedDB, "MemoryBackingStoreTransaction::addExistingIndex" ); |
| 87 | |
| 88 | ASSERT(isWriting()); |
| 89 | |
| 90 | ASSERT(!m_indexes.contains(&index)); |
| 91 | m_indexes.add(&index); |
| 92 | } |
| 93 | |
| 94 | void MemoryBackingStoreTransaction::indexDeleted(Ref<MemoryIndex>&& index) |
| 95 | { |
| 96 | m_indexes.remove(&index.get()); |
| 97 | |
| 98 | // If this MemoryIndex belongs to an object store that will not get restored if this transaction aborts, |
| 99 | // then we can forget about it altogether. |
| 100 | auto& objectStore = index->objectStore(); |
| 101 | if (auto deletedObjectStore = m_deletedObjectStores.get(objectStore.info().name())) { |
| 102 | if (deletedObjectStore != &objectStore) |
| 103 | return; |
| 104 | } |
| 105 | |
| 106 | auto addResult = m_deletedIndexes.add(index->info().name(), nullptr); |
| 107 | if (addResult.isNewEntry) |
| 108 | addResult.iterator->value = WTFMove(index); |
| 109 | } |
| 110 | |
| 111 | void MemoryBackingStoreTransaction::addExistingObjectStore(MemoryObjectStore& objectStore) |
| 112 | { |
| 113 | LOG(IndexedDB, "MemoryBackingStoreTransaction::addExistingObjectStore" ); |
| 114 | |
| 115 | ASSERT(isWriting()); |
| 116 | |
| 117 | ASSERT(!m_objectStores.contains(&objectStore)); |
| 118 | m_objectStores.add(&objectStore); |
| 119 | |
| 120 | objectStore.writeTransactionStarted(*this); |
| 121 | |
| 122 | m_originalKeyGenerators.add(&objectStore, objectStore.currentKeyGeneratorValue()); |
| 123 | } |
| 124 | |
| 125 | void MemoryBackingStoreTransaction::objectStoreDeleted(Ref<MemoryObjectStore>&& objectStore) |
| 126 | { |
| 127 | ASSERT(m_objectStores.contains(&objectStore.get())); |
| 128 | m_objectStores.remove(&objectStore.get()); |
| 129 | |
| 130 | objectStore->deleteAllIndexes(*this); |
| 131 | |
| 132 | auto addResult = m_deletedObjectStores.add(objectStore->info().name(), nullptr); |
| 133 | if (addResult.isNewEntry) |
| 134 | addResult.iterator->value = WTFMove(objectStore); |
| 135 | } |
| 136 | |
| 137 | void MemoryBackingStoreTransaction::objectStoreCleared(MemoryObjectStore& objectStore, std::unique_ptr<KeyValueMap>&& keyValueMap, std::unique_ptr<IDBKeyDataSet>&& orderedKeys) |
| 138 | { |
| 139 | ASSERT(m_objectStores.contains(&objectStore)); |
| 140 | |
| 141 | auto addResult = m_clearedKeyValueMaps.add(&objectStore, nullptr); |
| 142 | |
| 143 | // If this object store has already been cleared during this transaction, we shouldn't remember this clearing. |
| 144 | if (!addResult.isNewEntry) |
| 145 | return; |
| 146 | |
| 147 | addResult.iterator->value = WTFMove(keyValueMap); |
| 148 | |
| 149 | ASSERT(!m_clearedOrderedKeys.contains(&objectStore)); |
| 150 | m_clearedOrderedKeys.add(&objectStore, WTFMove(orderedKeys)); |
| 151 | } |
| 152 | |
| 153 | void MemoryBackingStoreTransaction::objectStoreRenamed(MemoryObjectStore& objectStore, const String& oldName) |
| 154 | { |
| 155 | ASSERT(m_objectStores.contains(&objectStore)); |
| 156 | ASSERT(m_info.mode() == IDBTransactionMode::Versionchange); |
| 157 | |
| 158 | // We only care about the first rename in a given transaction, because if the transaction is aborted we want |
| 159 | // to go back to the first 'oldName' |
| 160 | m_originalObjectStoreNames.add(&objectStore, oldName); |
| 161 | } |
| 162 | |
| 163 | void MemoryBackingStoreTransaction::indexRenamed(MemoryIndex& index, const String& oldName) |
| 164 | { |
| 165 | ASSERT(m_objectStores.contains(&index.objectStore())); |
| 166 | ASSERT(m_info.mode() == IDBTransactionMode::Versionchange); |
| 167 | |
| 168 | // We only care about the first rename in a given transaction, because if the transaction is aborted we want |
| 169 | // to go back to the first 'oldName' |
| 170 | m_originalIndexNames.add(&index, oldName); |
| 171 | } |
| 172 | |
| 173 | void MemoryBackingStoreTransaction::indexCleared(MemoryIndex& index, std::unique_ptr<IndexValueStore>&& valueStore) |
| 174 | { |
| 175 | auto addResult = m_clearedIndexValueStores.add(&index, nullptr); |
| 176 | |
| 177 | // If this index has already been cleared during this transaction, we shouldn't remember this clearing. |
| 178 | if (!addResult.isNewEntry) |
| 179 | return; |
| 180 | |
| 181 | addResult.iterator->value = WTFMove(valueStore); |
| 182 | } |
| 183 | |
| 184 | void MemoryBackingStoreTransaction::recordValueChanged(MemoryObjectStore& objectStore, const IDBKeyData& key, ThreadSafeDataBuffer* value) |
| 185 | { |
| 186 | ASSERT(m_objectStores.contains(&objectStore)); |
| 187 | |
| 188 | if (m_isAborting) |
| 189 | return; |
| 190 | |
| 191 | // If this object store had been cleared during the transaction, no point in recording this |
| 192 | // individual key/value change as its entire key/value map will be restored upon abort. |
| 193 | if (m_clearedKeyValueMaps.contains(&objectStore)) |
| 194 | return; |
| 195 | |
| 196 | auto originalAddResult = m_originalValues.add(&objectStore, nullptr); |
| 197 | if (originalAddResult.isNewEntry) |
| 198 | originalAddResult.iterator->value = std::make_unique<KeyValueMap>(); |
| 199 | |
| 200 | auto* map = originalAddResult.iterator->value.get(); |
| 201 | |
| 202 | auto addResult = map->add(key, ThreadSafeDataBuffer()); |
| 203 | if (!addResult.isNewEntry) |
| 204 | return; |
| 205 | |
| 206 | if (value) |
| 207 | addResult.iterator->value = *value; |
| 208 | } |
| 209 | |
| 210 | void MemoryBackingStoreTransaction::abort() |
| 211 | { |
| 212 | LOG(IndexedDB, "MemoryBackingStoreTransaction::abort()" ); |
| 213 | |
| 214 | SetForScope<bool> change(m_isAborting, true); |
| 215 | |
| 216 | for (const auto& iterator : m_originalIndexNames) |
| 217 | iterator.key->rename(iterator.value); |
| 218 | m_originalIndexNames.clear(); |
| 219 | |
| 220 | for (const auto& iterator : m_originalObjectStoreNames) |
| 221 | iterator.key->rename(iterator.value); |
| 222 | m_originalObjectStoreNames.clear(); |
| 223 | |
| 224 | for (const auto& objectStore : m_versionChangeAddedObjectStores) |
| 225 | m_backingStore.removeObjectStoreForVersionChangeAbort(*objectStore); |
| 226 | m_versionChangeAddedObjectStores.clear(); |
| 227 | |
| 228 | for (auto& objectStore : m_deletedObjectStores.values()) { |
| 229 | m_backingStore.restoreObjectStoreForVersionChangeAbort(*objectStore); |
| 230 | ASSERT(!m_objectStores.contains(objectStore.get())); |
| 231 | m_objectStores.add(objectStore); |
| 232 | } |
| 233 | m_deletedObjectStores.clear(); |
| 234 | |
| 235 | if (m_originalDatabaseInfo) { |
| 236 | ASSERT(m_info.mode() == IDBTransactionMode::Versionchange); |
| 237 | m_backingStore.setDatabaseInfo(*m_originalDatabaseInfo); |
| 238 | } |
| 239 | |
| 240 | // Restore cleared index value stores before we re-insert values into object stores |
| 241 | // because inserting those values will regenerate the appropriate index values. |
| 242 | for (auto& iterator : m_clearedIndexValueStores) |
| 243 | iterator.key->replaceIndexValueStore(WTFMove(iterator.value)); |
| 244 | m_clearedIndexValueStores.clear(); |
| 245 | |
| 246 | for (auto& objectStore : m_objectStores) { |
| 247 | ASSERT(m_originalKeyGenerators.contains(objectStore.get())); |
| 248 | objectStore->setKeyGeneratorValue(m_originalKeyGenerators.get(objectStore.get())); |
| 249 | |
| 250 | auto clearedKeyValueMap = m_clearedKeyValueMaps.take(objectStore.get()); |
| 251 | if (clearedKeyValueMap) { |
| 252 | ASSERT(m_clearedOrderedKeys.contains(objectStore.get())); |
| 253 | objectStore->replaceKeyValueStore(WTFMove(clearedKeyValueMap), m_clearedOrderedKeys.take(objectStore.get())); |
| 254 | } |
| 255 | |
| 256 | auto keyValueMap = m_originalValues.take(objectStore.get()); |
| 257 | if (!keyValueMap) |
| 258 | continue; |
| 259 | |
| 260 | for (const auto& entry : *keyValueMap) { |
| 261 | objectStore->deleteRecord(entry.key); |
| 262 | objectStore->addRecord(*this, entry.key, { entry.value }); |
| 263 | } |
| 264 | } |
| 265 | |
| 266 | for (auto& index : m_deletedIndexes.values()) |
| 267 | index->objectStore().maybeRestoreDeletedIndex(*index); |
| 268 | m_deletedIndexes.clear(); |
| 269 | |
| 270 | finish(); |
| 271 | } |
| 272 | |
| 273 | void MemoryBackingStoreTransaction::commit() |
| 274 | { |
| 275 | LOG(IndexedDB, "MemoryBackingStoreTransaction::commit()" ); |
| 276 | |
| 277 | finish(); |
| 278 | } |
| 279 | |
| 280 | void MemoryBackingStoreTransaction::finish() |
| 281 | { |
| 282 | m_inProgress = false; |
| 283 | |
| 284 | if (!isWriting()) |
| 285 | return; |
| 286 | |
| 287 | for (auto& objectStore : m_objectStores) |
| 288 | objectStore->writeTransactionFinished(*this); |
| 289 | for (auto& objectStore : m_deletedObjectStores.values()) |
| 290 | objectStore->writeTransactionFinished(*this); |
| 291 | } |
| 292 | |
| 293 | } // namespace IDBServer |
| 294 | } // namespace WebCore |
| 295 | |
| 296 | #endif // ENABLE(INDEXED_DATABASE) |
| 297 | |