OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2010 Google Inc. All rights reserved. | 2 * Copyright (C) 2010 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
6 * are met: | 6 * are met: |
7 * | 7 * |
8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
138 DCHECK(m_state == Finished || m_contextStopped); | 138 DCHECK(m_state == Finished || m_contextStopped); |
139 DCHECK(m_requestList.isEmpty() || m_contextStopped); | 139 DCHECK(m_requestList.isEmpty() || m_contextStopped); |
140 } | 140 } |
141 | 141 |
142 DEFINE_TRACE(IDBTransaction) { | 142 DEFINE_TRACE(IDBTransaction) { |
143 visitor->trace(m_database); | 143 visitor->trace(m_database); |
144 visitor->trace(m_openDBRequest); | 144 visitor->trace(m_openDBRequest); |
145 visitor->trace(m_error); | 145 visitor->trace(m_error); |
146 visitor->trace(m_requestList); | 146 visitor->trace(m_requestList); |
147 visitor->trace(m_objectStoreMap); | 147 visitor->trace(m_objectStoreMap); |
148 visitor->trace(m_createdObjectStores); | 148 visitor->trace(m_oldStoreMetadata); |
149 visitor->trace(m_deletedObjectStores); | 149 visitor->trace(m_deletedIndexes); |
150 visitor->trace(m_objectStoreCleanupMap); | |
151 EventTargetWithInlineData::trace(visitor); | 150 EventTargetWithInlineData::trace(visitor); |
152 ActiveDOMObject::trace(visitor); | 151 ActiveDOMObject::trace(visitor); |
153 } | 152 } |
154 | 153 |
155 void IDBTransaction::setError(DOMException* error) { | 154 void IDBTransaction::setError(DOMException* error) { |
156 DCHECK_NE(m_state, Finished); | 155 DCHECK_NE(m_state, Finished); |
157 DCHECK(error); | 156 DCHECK(error); |
158 | 157 |
159 // The first error to be set is the true cause of the | 158 // The first error to be set is the true cause of the |
160 // transaction abort. | 159 // transaction abort. |
161 if (!m_error) { | 160 if (!m_error) |
162 m_error = error; | 161 m_error = error; |
163 } | |
164 } | 162 } |
165 | 163 |
166 IDBObjectStore* IDBTransaction::objectStore(const String& name, | 164 IDBObjectStore* IDBTransaction::objectStore(const String& name, |
167 ExceptionState& exceptionState) { | 165 ExceptionState& exceptionState) { |
168 if (isFinished()) { | 166 if (isFinished()) { |
169 exceptionState.throwDOMException( | 167 exceptionState.throwDOMException( |
170 InvalidStateError, IDBDatabase::transactionFinishedErrorMessage); | 168 InvalidStateError, IDBDatabase::transactionFinishedErrorMessage); |
171 return nullptr; | 169 return nullptr; |
172 } | 170 } |
173 | 171 |
174 IDBObjectStoreMap::iterator it = m_objectStoreMap.find(name); | 172 IDBObjectStoreMap::iterator it = m_objectStoreMap.find(name); |
175 if (it != m_objectStoreMap.end()) | 173 if (it != m_objectStoreMap.end()) |
176 return it->value; | 174 return it->value; |
177 | 175 |
178 if (!isVersionChange() && !m_scope.contains(name)) { | 176 if (!isVersionChange() && !m_scope.contains(name)) { |
179 exceptionState.throwDOMException( | 177 exceptionState.throwDOMException( |
180 NotFoundError, IDBDatabase::noSuchObjectStoreErrorMessage); | 178 NotFoundError, IDBDatabase::noSuchObjectStoreErrorMessage); |
181 return nullptr; | 179 return nullptr; |
182 } | 180 } |
183 | 181 |
184 int64_t objectStoreId = m_database->findObjectStoreId(name); | 182 int64_t objectStoreId = m_database->findObjectStoreId(name); |
185 if (objectStoreId == IDBObjectStoreMetadata::InvalidId) { | 183 if (objectStoreId == IDBObjectStoreMetadata::InvalidId) { |
186 DCHECK(isVersionChange()); | 184 DCHECK(isVersionChange()); |
187 exceptionState.throwDOMException( | 185 exceptionState.throwDOMException( |
188 NotFoundError, IDBDatabase::noSuchObjectStoreErrorMessage); | 186 NotFoundError, IDBDatabase::noSuchObjectStoreErrorMessage); |
189 return nullptr; | 187 return nullptr; |
190 } | 188 } |
191 | 189 |
192 DCHECK(m_database->metadata().objectStores.contains(objectStoreId)); | 190 DCHECK(m_database->metadata().objectStores.contains(objectStoreId)); |
193 const IDBObjectStoreMetadata& objectStoreMetadata = | 191 RefPtr<IDBObjectStoreMetadata> objectStoreMetadata = |
194 m_database->metadata().objectStores.get(objectStoreId); | 192 m_database->metadata().objectStores.get(objectStoreId); |
| 193 DCHECK(objectStoreMetadata.get()); |
195 | 194 |
196 IDBObjectStore* objectStore = | 195 IDBObjectStore* objectStore = |
197 IDBObjectStore::create(objectStoreMetadata, this); | 196 IDBObjectStore::create(std::move(objectStoreMetadata), this); |
198 DCHECK(!m_objectStoreMap.contains(name)); | 197 DCHECK(!m_objectStoreMap.contains(name)); |
199 m_objectStoreMap.set(name, objectStore); | 198 m_objectStoreMap.set(name, objectStore); |
200 m_objectStoreCleanupMap.set(objectStore, objectStore->metadata()); | 199 |
| 200 if (isVersionChange()) { |
| 201 DCHECK(!objectStore->isNewlyCreated()) |
| 202 << "Object store IDs are not assigned sequentially"; |
| 203 RefPtr<IDBObjectStoreMetadata> backupMetadata = |
| 204 objectStore->metadata().createCopy(); |
| 205 m_oldStoreMetadata.set(objectStore, std::move(backupMetadata)); |
| 206 } |
201 return objectStore; | 207 return objectStore; |
202 } | 208 } |
203 | 209 |
204 void IDBTransaction::objectStoreCreated(const String& name, | 210 void IDBTransaction::objectStoreCreated(const String& name, |
205 IDBObjectStore* objectStore) { | 211 IDBObjectStore* objectStore) { |
206 DCHECK_NE(m_state, Finished) | 212 DCHECK_NE(m_state, Finished) |
207 << "A finished transaction created an object store"; | 213 << "A finished transaction created an object store"; |
208 DCHECK_EQ(m_mode, WebIDBTransactionModeVersionChange) | 214 DCHECK_EQ(m_mode, WebIDBTransactionModeVersionChange) |
209 << "A non-versionchange transaction created an object store"; | 215 << "A non-versionchange transaction created an object store"; |
210 DCHECK(!m_objectStoreMap.contains(name)) | 216 DCHECK(!m_objectStoreMap.contains(name)) |
211 << "An object store was created with the name of an existing store"; | 217 << "An object store was created with the name of an existing store"; |
| 218 DCHECK(objectStore->isNewlyCreated()) |
| 219 << "Object store IDs are not assigned sequentially"; |
212 m_objectStoreMap.set(name, objectStore); | 220 m_objectStoreMap.set(name, objectStore); |
213 m_objectStoreCleanupMap.set(objectStore, objectStore->metadata()); | |
214 m_createdObjectStores.add(objectStore); | |
215 } | 221 } |
216 | 222 |
217 void IDBTransaction::objectStoreDeleted(const String& name) { | 223 void IDBTransaction::objectStoreDeleted(const int64_t objectStoreId, |
| 224 const String& name) { |
218 DCHECK_NE(m_state, Finished) | 225 DCHECK_NE(m_state, Finished) |
219 << "A finished transaction deleted an object store"; | 226 << "A finished transaction deleted an object store"; |
220 DCHECK_EQ(m_mode, WebIDBTransactionModeVersionChange) | 227 DCHECK_EQ(m_mode, WebIDBTransactionModeVersionChange) |
221 << "A non-versionchange transaction deleted an object store"; | 228 << "A non-versionchange transaction deleted an object store"; |
222 IDBObjectStoreMap::iterator it = m_objectStoreMap.find(name); | 229 IDBObjectStoreMap::iterator it = m_objectStoreMap.find(name); |
223 if (it != m_objectStoreMap.end()) { | 230 if (it == m_objectStoreMap.end()) { |
| 231 // No IDBObjectStore instance was created for the deleted store in this |
| 232 // transaction. This happens if IDBDatabase.deleteObjectStore() is called |
| 233 // with the name of a store that wasn't instantated. We need to be able to |
| 234 // revert the metadata change if the transaction aborts, in order to return |
| 235 // correct values from IDB{Database, Transaction}.objectStoreNames. |
| 236 DCHECK(m_database->metadata().objectStores.contains(objectStoreId)); |
| 237 RefPtr<IDBObjectStoreMetadata> metadata = |
| 238 m_database->metadata().objectStores.get(objectStoreId); |
| 239 DCHECK(metadata.get()); |
| 240 DCHECK_EQ(metadata->name, name); |
| 241 m_deletedObjectStores.append(std::move(metadata)); |
| 242 } else { |
224 IDBObjectStore* objectStore = it->value; | 243 IDBObjectStore* objectStore = it->value; |
225 m_objectStoreMap.remove(name); | 244 m_objectStoreMap.remove(name); |
226 objectStore->markDeleted(); | 245 objectStore->markDeleted(); |
227 m_objectStoreCleanupMap.set(objectStore, objectStore->metadata()); | 246 if (objectStore->id() > m_oldDatabaseMetadata.maxObjectStoreId) { |
228 m_deletedObjectStores.add(objectStore); | 247 // The store was created and deleted in this transaction, so it will |
| 248 // not be restored even if the transaction aborts. We have just |
| 249 // removed our last reference to it. |
| 250 DCHECK(!m_oldStoreMetadata.contains(objectStore)); |
| 251 objectStore->clearIndexCache(); |
| 252 } else { |
| 253 // The store was created before this transaction, and we created an |
| 254 // IDBObjectStore instance for it. When that happened, we must have |
| 255 // snapshotted the store's metadata as well. |
| 256 DCHECK(m_oldStoreMetadata.contains(objectStore)); |
| 257 } |
229 } | 258 } |
230 } | 259 } |
231 | 260 |
232 void IDBTransaction::objectStoreRenamed(const String& oldName, | 261 void IDBTransaction::objectStoreRenamed(const String& oldName, |
233 const String& newName) { | 262 const String& newName) { |
234 DCHECK_NE(m_state, Finished) | 263 DCHECK_NE(m_state, Finished) |
235 << "A finished transaction renamed an object store"; | 264 << "A finished transaction renamed an object store"; |
236 DCHECK_EQ(m_mode, WebIDBTransactionModeVersionChange) | 265 DCHECK_EQ(m_mode, WebIDBTransactionModeVersionChange) |
237 << "A non-versionchange transaction renamed an object store"; | 266 << "A non-versionchange transaction renamed an object store"; |
238 | 267 |
239 DCHECK(!m_objectStoreMap.contains(newName)); | 268 DCHECK(!m_objectStoreMap.contains(newName)); |
240 DCHECK(m_objectStoreMap.contains(oldName)) | 269 DCHECK(m_objectStoreMap.contains(oldName)) |
241 << "The object store had to be accessed in order to be renamed."; | 270 << "The object store had to be accessed in order to be renamed."; |
242 m_objectStoreMap.set(newName, m_objectStoreMap.take(oldName)); | 271 m_objectStoreMap.set(newName, m_objectStoreMap.take(oldName)); |
243 } | 272 } |
244 | 273 |
| 274 void IDBTransaction::indexDeleted(IDBIndex* index) { |
| 275 DCHECK(index); |
| 276 DCHECK(!index->isDeleted()) << "indexDeleted called twice for the same index"; |
| 277 |
| 278 IDBObjectStore* objectStore = index->objectStore(); |
| 279 DCHECK_EQ(objectStore->transaction(), this); |
| 280 DCHECK(m_objectStoreMap.contains(objectStore->name())) |
| 281 << "An index was deleted without accessing its object store"; |
| 282 |
| 283 const auto& objectStoreIterator = m_oldStoreMetadata.find(objectStore); |
| 284 if (objectStoreIterator == m_oldStoreMetadata.end()) { |
| 285 // The index's object store was created in this transaction, so this |
| 286 // index was also created (and deleted) in this transaction, and will |
| 287 // not be restored if the transaction aborts. |
| 288 // |
| 289 // Subtle proof for the first sentence above: Deleting an index requires |
| 290 // calling deleteIndex() on the store's IDBObjectStore instance. |
| 291 // Whenever we create an IDBObjectStore instance for a previously |
| 292 // created store, we snapshot the store's metadata. So, deleting an |
| 293 // index of an "old" store can only be done after the store's metadata |
| 294 // is snapshotted. |
| 295 return; |
| 296 } |
| 297 |
| 298 const IDBObjectStoreMetadata* oldStoreMetadata = |
| 299 objectStoreIterator->value.get(); |
| 300 DCHECK(oldStoreMetadata); |
| 301 if (!oldStoreMetadata->indexes.contains(index->id())) { |
| 302 // The index's object store was created before this transaction, but the |
| 303 // index was created (and deleted) in this transaction, so it will not |
| 304 // be restored if the transaction aborts. |
| 305 return; |
| 306 } |
| 307 |
| 308 m_deletedIndexes.append(index); |
| 309 } |
| 310 |
245 void IDBTransaction::setActive(bool active) { | 311 void IDBTransaction::setActive(bool active) { |
246 DCHECK_NE(m_state, Finished) << "A finished transaction tried to setActive(" | 312 DCHECK_NE(m_state, Finished) << "A finished transaction tried to setActive(" |
247 << (active ? "true" : "false") << ")"; | 313 << (active ? "true" : "false") << ")"; |
248 if (m_state == Finishing) | 314 if (m_state == Finishing) |
249 return; | 315 return; |
250 DCHECK_NE(active, (m_state == Active)); | 316 DCHECK_NE(active, (m_state == Active)); |
251 m_state = active ? Active : Inactive; | 317 m_state = active ? Active : Inactive; |
252 | 318 |
253 if (!active && m_requestList.isEmpty() && backendDB()) | 319 if (!active && m_requestList.isEmpty() && backendDB()) |
254 backendDB()->commit(m_id); | 320 backendDB()->commit(m_id); |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
341 if (modeString == IndexedDBNames::readonly) | 407 if (modeString == IndexedDBNames::readonly) |
342 return WebIDBTransactionModeReadOnly; | 408 return WebIDBTransactionModeReadOnly; |
343 if (modeString == IndexedDBNames::readwrite) | 409 if (modeString == IndexedDBNames::readwrite) |
344 return WebIDBTransactionModeReadWrite; | 410 return WebIDBTransactionModeReadWrite; |
345 if (modeString == IndexedDBNames::versionchange) | 411 if (modeString == IndexedDBNames::versionchange) |
346 return WebIDBTransactionModeVersionChange; | 412 return WebIDBTransactionModeVersionChange; |
347 NOTREACHED(); | 413 NOTREACHED(); |
348 return WebIDBTransactionModeReadOnly; | 414 return WebIDBTransactionModeReadOnly; |
349 } | 415 } |
350 | 416 |
| 417 WebIDBDatabase* IDBTransaction::backendDB() const { |
| 418 return m_database->backend(); |
| 419 } |
| 420 |
351 const String& IDBTransaction::mode() const { | 421 const String& IDBTransaction::mode() const { |
352 switch (m_mode) { | 422 switch (m_mode) { |
353 case WebIDBTransactionModeReadOnly: | 423 case WebIDBTransactionModeReadOnly: |
354 return IndexedDBNames::readonly; | 424 return IndexedDBNames::readonly; |
355 | 425 |
356 case WebIDBTransactionModeReadWrite: | 426 case WebIDBTransactionModeReadWrite: |
357 return IndexedDBNames::readwrite; | 427 return IndexedDBNames::readwrite; |
358 | 428 |
359 case WebIDBTransactionModeVersionChange: | 429 case WebIDBTransactionModeVersionChange: |
360 return IndexedDBNames::versionchange; | 430 return IndexedDBNames::versionchange; |
361 } | 431 } |
362 | 432 |
363 NOTREACHED(); | 433 NOTREACHED(); |
364 return IndexedDBNames::readonly; | 434 return IndexedDBNames::readonly; |
365 } | 435 } |
366 | 436 |
367 WebIDBDatabase* IDBTransaction::backendDB() const { | |
368 return m_database->backend(); | |
369 } | |
370 | |
371 DOMStringList* IDBTransaction::objectStoreNames() const { | 437 DOMStringList* IDBTransaction::objectStoreNames() const { |
372 if (isVersionChange()) | 438 if (isVersionChange()) |
373 return m_database->objectStoreNames(); | 439 return m_database->objectStoreNames(); |
374 | 440 |
375 DOMStringList* objectStoreNames = | 441 DOMStringList* objectStoreNames = |
376 DOMStringList::create(DOMStringList::IndexedDB); | 442 DOMStringList::create(DOMStringList::IndexedDB); |
377 for (const String& objectStoreName : m_scope) | 443 for (const String& objectStoreName : m_scope) |
378 objectStoreNames->append(objectStoreName); | 444 objectStoreNames->append(objectStoreName); |
379 objectStoreNames->sort(); | 445 objectStoreNames->sort(); |
380 return objectStoreNames; | 446 return objectStoreNames; |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
445 for (IDBRequest* request : m_requestList) | 511 for (IDBRequest* request : m_requestList) |
446 request->abort(); | 512 request->abort(); |
447 m_requestList.clear(); | 513 m_requestList.clear(); |
448 } | 514 } |
449 | 515 |
450 void IDBTransaction::revertDatabaseMetadata() { | 516 void IDBTransaction::revertDatabaseMetadata() { |
451 DCHECK_NE(m_state, Active); | 517 DCHECK_NE(m_state, Active); |
452 if (!isVersionChange()) | 518 if (!isVersionChange()) |
453 return; | 519 return; |
454 | 520 |
455 // Newly created stores must be marked as deleted. | 521 // Mark stores created by this transaction as deleted. |
456 for (IDBObjectStore* store : m_createdObjectStores) { | 522 for (auto& objectStore : m_objectStoreMap.values()) { |
457 store->abort(); | 523 const int64_t objectStoreId = objectStore->id(); |
458 store->markDeleted(); | 524 if (!objectStore->isNewlyCreated()) { |
| 525 DCHECK(m_oldStoreMetadata.contains(objectStore)); |
| 526 continue; |
| 527 } |
| 528 |
| 529 DCHECK(!m_oldStoreMetadata.contains(objectStore)); |
| 530 m_database->revertObjectStoreCreation(objectStoreId); |
| 531 objectStore->markDeleted(); |
459 } | 532 } |
460 | 533 |
461 // Used stores may need to mark indexes as deleted. | 534 for (auto& it : m_oldStoreMetadata) { |
462 for (auto& it : m_objectStoreCleanupMap) { | 535 IDBObjectStore* objectStore = it.key; |
463 it.key->abort(); | 536 RefPtr<IDBObjectStoreMetadata> oldMetadata = it.value; |
464 it.key->setMetadata(it.value); | 537 |
| 538 m_database->revertObjectStoreMetadata(oldMetadata); |
| 539 objectStore->revertMetadata(oldMetadata); |
465 } | 540 } |
| 541 for (auto& index : m_deletedIndexes) |
| 542 index->objectStore()->revertDeletedIndexMetadata(*index); |
| 543 for (auto& oldMedata : m_deletedObjectStores) |
| 544 m_database->revertObjectStoreMetadata(std::move(oldMedata)); |
466 | 545 |
467 m_database->setMetadata(m_oldDatabaseMetadata); | 546 // We only need to revert the database's own metadata because we have reverted |
| 547 // the metadata for the database's object stores above. |
| 548 m_database->setDatabaseMetadata(m_oldDatabaseMetadata); |
468 } | 549 } |
469 | 550 |
470 void IDBTransaction::finished() { | 551 void IDBTransaction::finished() { |
471 #if DCHECK_IS_ON() | 552 #if DCHECK_IS_ON() |
472 DCHECK(!m_finishCalled); | 553 DCHECK(!m_finishCalled); |
473 m_finishCalled = true; | 554 m_finishCalled = true; |
474 #endif // DCHECK_IS_ON() | 555 #endif // DCHECK_IS_ON() |
475 | 556 |
476 m_database->transactionFinished(this); | 557 m_database->transactionFinished(this); |
477 | 558 |
478 // Break reference cycles. | 559 // Remove references to the IDBObjectStore and IDBIndex instances held by |
479 // TODO(jsbell): This can be removed c/o Oilpan. | 560 // this transaction, so Oilpan can garbage-collect the instances that aren't |
480 for (auto& it : m_objectStoreMap) | 561 // used by JavaScript. |
481 it.value->transactionFinished(); | 562 |
| 563 for (auto& it : m_objectStoreMap) { |
| 564 IDBObjectStore* objectStore = it.value; |
| 565 if (!isVersionChange() || objectStore->isNewlyCreated()) { |
| 566 DCHECK(!m_oldStoreMetadata.contains(objectStore)); |
| 567 objectStore->clearIndexCache(); |
| 568 } else { |
| 569 // We'll call clearIndexCache() on this store in the loop below. |
| 570 DCHECK(m_oldStoreMetadata.contains(objectStore)); |
| 571 } |
| 572 } |
482 m_objectStoreMap.clear(); | 573 m_objectStoreMap.clear(); |
483 for (auto& it : m_deletedObjectStores) | 574 |
484 it->transactionFinished(); | 575 for (auto& it : m_oldStoreMetadata) { |
485 m_createdObjectStores.clear(); | 576 IDBObjectStore* objectStore = it.key; |
| 577 objectStore->clearIndexCache(); |
| 578 } |
| 579 m_oldStoreMetadata.clear(); |
| 580 |
| 581 m_deletedIndexes.clear(); |
486 m_deletedObjectStores.clear(); | 582 m_deletedObjectStores.clear(); |
487 | |
488 m_objectStoreCleanupMap.clear(); | |
489 } | 583 } |
490 | 584 |
491 } // namespace blink | 585 } // namespace blink |
OLD | NEW |