Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(285)

Side by Side Diff: third_party/WebKit/Source/modules/indexeddb/IDBTransaction.cpp

Issue 2314933005: Align IndexedDB metadata rollback on transaction abort to spec. (Closed)
Patch Set: Rebased past the big reformat. Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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->id() <= oldMaxObjectStoreId())
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->id() > oldMaxObjectStoreId())
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. We only need to be able to revert the metadata change
233 // if the transaction aborts.
234 DCHECK(m_database->metadata().objectStores.contains(objectStoreId));
235 RefPtr<IDBObjectStoreMetadata> metadata =
236 m_database->metadata().objectStores.get(objectStoreId);
237 DCHECK(metadata.get());
238 DCHECK_EQ(metadata->name, name);
239 m_deletedObjectStores.append(std::move(metadata));
240 } else {
224 IDBObjectStore* objectStore = it->value; 241 IDBObjectStore* objectStore = it->value;
225 m_objectStoreMap.remove(name); 242 m_objectStoreMap.remove(name);
226 objectStore->markDeleted(); 243 objectStore->markDeleted();
227 m_objectStoreCleanupMap.set(objectStore, objectStore->metadata()); 244 if (objectStore->id() > m_oldDatabaseMetadata.maxObjectStoreId) {
228 m_deletedObjectStores.add(objectStore); 245 // The store was created and deleted in this transaction, so it will
246 // not be restored even if the transaction aborts. We have just
247 // removed our last reference to it.
248 DCHECK(!m_oldStoreMetadata.contains(objectStore));
249 objectStore->clearIndexCache();
250 } else {
251 // The store was created before this transaction, and we created an
252 // IDBObjectStore instance for it. When that happened, we must have
253 // snapshotted the store's metadata as well.
254 DCHECK(m_oldStoreMetadata.contains(objectStore));
255 }
229 } 256 }
230 } 257 }
231 258
232 void IDBTransaction::objectStoreRenamed(const String& oldName, 259 void IDBTransaction::objectStoreRenamed(const String& oldName,
233 const String& newName) { 260 const String& newName) {
234 DCHECK_NE(m_state, Finished) 261 DCHECK_NE(m_state, Finished)
235 << "A finished transaction renamed an object store"; 262 << "A finished transaction renamed an object store";
236 DCHECK_EQ(m_mode, WebIDBTransactionModeVersionChange) 263 DCHECK_EQ(m_mode, WebIDBTransactionModeVersionChange)
237 << "A non-versionchange transaction renamed an object store"; 264 << "A non-versionchange transaction renamed an object store";
238 265
239 DCHECK(!m_objectStoreMap.contains(newName)); 266 DCHECK(!m_objectStoreMap.contains(newName));
240 DCHECK(m_objectStoreMap.contains(oldName)) 267 DCHECK(m_objectStoreMap.contains(oldName))
241 << "The object store had to be accessed in order to be renamed."; 268 << "The object store had to be accessed in order to be renamed.";
242 m_objectStoreMap.set(newName, m_objectStoreMap.take(oldName)); 269 m_objectStoreMap.set(newName, m_objectStoreMap.take(oldName));
243 } 270 }
244 271
272 void IDBTransaction::indexDeleted(IDBIndex* index) {
273 DCHECK(index);
274 DCHECK(!index->isDeleted()) << "indexDeleted called twice for the same index";
275
276 IDBObjectStore* objectStore = index->objectStore();
277 DCHECK_EQ(objectStore->transaction(), this);
278 DCHECK(m_objectStoreMap.contains(objectStore->name()))
279 << "An index was deleted without accessing its object store";
280
281 const auto& objectStoreIterator = m_oldStoreMetadata.find(objectStore);
282 if (objectStoreIterator == m_oldStoreMetadata.end()) {
283 // The index's object store was created in this transaction, so this
284 // index was also created (and deleted) in this transaction, and will
285 // not be restored if the transaction aborts.
286 //
287 // Subtle proof for the first sentence above: Deleting an index requires
288 // calling deleteIndex() on the store's IDBObjectStore instance.
289 // Whenever we create an IDBObjectStore instance for a previously
290 // created store, we snapshot the store's metadata. So, deleting an
291 // index of an "old" store can only be done after the store's metadata
292 // is snapshotted.
293 return;
294 }
295
296 const IDBObjectStoreMetadata* oldStoreMetadata =
297 objectStoreIterator->value.get();
298 DCHECK(oldStoreMetadata);
299 if (!oldStoreMetadata->indexes.contains(index->id())) {
300 // The index's object store was created before this transaction, but the
301 // index was created (and deleted) in this transaction, so it will not
302 // be restored if the transaction aborts.
303 return;
304 }
305
306 m_deletedIndexes.append(index);
307 }
308
245 void IDBTransaction::setActive(bool active) { 309 void IDBTransaction::setActive(bool active) {
246 DCHECK_NE(m_state, Finished) << "A finished transaction tried to setActive(" 310 DCHECK_NE(m_state, Finished) << "A finished transaction tried to setActive("
247 << (active ? "true" : "false") << ")"; 311 << (active ? "true" : "false") << ")";
248 if (m_state == Finishing) 312 if (m_state == Finishing)
249 return; 313 return;
250 DCHECK_NE(active, (m_state == Active)); 314 DCHECK_NE(active, (m_state == Active));
251 m_state = active ? Active : Inactive; 315 m_state = active ? Active : Inactive;
252 316
253 if (!active && m_requestList.isEmpty() && backendDB()) 317 if (!active && m_requestList.isEmpty() && backendDB())
254 backendDB()->commit(m_id); 318 backendDB()->commit(m_id);
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
338 if (modeString == IndexedDBNames::readonly) 402 if (modeString == IndexedDBNames::readonly)
339 return WebIDBTransactionModeReadOnly; 403 return WebIDBTransactionModeReadOnly;
340 if (modeString == IndexedDBNames::readwrite) 404 if (modeString == IndexedDBNames::readwrite)
341 return WebIDBTransactionModeReadWrite; 405 return WebIDBTransactionModeReadWrite;
342 if (modeString == IndexedDBNames::versionchange) 406 if (modeString == IndexedDBNames::versionchange)
343 return WebIDBTransactionModeVersionChange; 407 return WebIDBTransactionModeVersionChange;
344 NOTREACHED(); 408 NOTREACHED();
345 return WebIDBTransactionModeReadOnly; 409 return WebIDBTransactionModeReadOnly;
346 } 410 }
347 411
412 WebIDBDatabase* IDBTransaction::backendDB() const {
cmumford 2016/10/05 21:15:27 Why move method within file?
pwnall 2016/10/05 23:15:45 I moved it to match the order in the header -- I'm
jsbell 2016/10/06 20:01:31 I'd leave it alone for this CL since it's untouche
cmumford 2016/10/06 20:22:55 What Josh said, plus moving it also makes it less
413 return m_database->backend();
414 }
415
348 const String& IDBTransaction::mode() const { 416 const String& IDBTransaction::mode() const {
349 switch (m_mode) { 417 switch (m_mode) {
350 case WebIDBTransactionModeReadOnly: 418 case WebIDBTransactionModeReadOnly:
351 return IndexedDBNames::readonly; 419 return IndexedDBNames::readonly;
352 420
353 case WebIDBTransactionModeReadWrite: 421 case WebIDBTransactionModeReadWrite:
354 return IndexedDBNames::readwrite; 422 return IndexedDBNames::readwrite;
355 423
356 case WebIDBTransactionModeVersionChange: 424 case WebIDBTransactionModeVersionChange:
357 return IndexedDBNames::versionchange; 425 return IndexedDBNames::versionchange;
358 } 426 }
359 427
360 NOTREACHED(); 428 NOTREACHED();
361 return IndexedDBNames::readonly; 429 return IndexedDBNames::readonly;
362 } 430 }
363 431
364 WebIDBDatabase* IDBTransaction::backendDB() const {
365 return m_database->backend();
366 }
367
368 DOMStringList* IDBTransaction::objectStoreNames() const { 432 DOMStringList* IDBTransaction::objectStoreNames() const {
369 if (isVersionChange()) 433 if (isVersionChange())
370 return m_database->objectStoreNames(); 434 return m_database->objectStoreNames();
371 435
372 DOMStringList* objectStoreNames = 436 DOMStringList* objectStoreNames =
373 DOMStringList::create(DOMStringList::IndexedDB); 437 DOMStringList::create(DOMStringList::IndexedDB);
374 for (const String& objectStoreName : m_scope) 438 for (const String& objectStoreName : m_scope)
375 objectStoreNames->append(objectStoreName); 439 objectStoreNames->append(objectStoreName);
376 objectStoreNames->sort(); 440 objectStoreNames->sort();
377 return objectStoreNames; 441 return objectStoreNames;
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
441 for (IDBRequest* request : m_requestList) 505 for (IDBRequest* request : m_requestList)
442 request->abort(); 506 request->abort();
443 m_requestList.clear(); 507 m_requestList.clear();
444 } 508 }
445 509
446 void IDBTransaction::revertDatabaseMetadata() { 510 void IDBTransaction::revertDatabaseMetadata() {
447 DCHECK_NE(m_state, Active); 511 DCHECK_NE(m_state, Active);
448 if (!isVersionChange()) 512 if (!isVersionChange())
449 return; 513 return;
450 514
451 // Newly created stores must be marked as deleted. 515 // Mark stores created by this transaction as deleted.
452 for (IDBObjectStore* store : m_createdObjectStores) { 516 for (auto& it : m_objectStoreMap) {
cmumford 2016/10/05 21:15:27 for (auto& objectStore : m_objectStoreMap.values()
pwnall 2016/10/05 23:15:45 Done. Cool, thank you for teaching me this!
453 store->abort(); 517 IDBObjectStore* objectStore = it.value;
454 store->markDeleted(); 518 const int64_t objectStoreId = objectStore->id();
519 if (objectStoreId <= oldMaxObjectStoreId()) {
520 DCHECK(m_oldStoreMetadata.contains(objectStore));
521 continue;
522 }
523
524 DCHECK(!m_oldStoreMetadata.contains(objectStore));
525 m_database->revertObjectStoreCreation(objectStoreId);
526 objectStore->markDeleted();
455 } 527 }
456 528
457 // Used stores may need to mark indexes as deleted. 529 for (auto& it : m_oldStoreMetadata) {
cmumford 2016/10/05 21:15:27 for (auto& objectStore : m_oldStoreMetadata.keys()
pwnall 2016/10/05 23:15:45 I need the value too, though -- I'm saving it as o
jsbell 2016/10/06 20:01:31 Seems fine to me.
cmumford 2016/10/06 20:22:55 Yeah, if you need both key and value then absolute
458 for (auto& it : m_objectStoreCleanupMap) { 530 IDBObjectStore* objectStore = it.key;
459 it.key->abort(); 531 RefPtr<IDBObjectStoreMetadata> oldMetadata = it.value;
460 it.key->setMetadata(it.value); 532
533 m_database->revertObjectStoreMetadata(oldMetadata);
534 objectStore->revertMetadata(oldMetadata);
461 } 535 }
462 536 for (auto& it : m_deletedIndexes) {
cmumford 2016/10/05 21:15:27 Why not just? for (auto& index : m_deletedIndex
pwnall 2016/10/05 23:15:45 Done. Thank you for catching this!
463 m_database->setMetadata(m_oldDatabaseMetadata); 537 IDBIndex* index = static_cast<IDBIndex*>(it);
538 index->objectStore()->revertDeletedIndexMetadata(*index);
539 }
540 for (auto& it : m_deletedObjectStores) {
541 RefPtr<IDBObjectStoreMetadata> oldMedata =
cmumford 2016/10/05 21:15:27 Do you need oldMetadata?
pwnall 2016/10/05 23:15:45 Done. Derp, I didn't. I thought we'd have a compi
jsbell 2016/10/06 20:01:31 cpplint.py may be handy - not run by default; not
542 static_cast<RefPtr<IDBObjectStoreMetadata>>(it);
543 m_database->revertObjectStoreMetadata(std::move(it));
544 }
545 m_database->setDatabaseMetadata(m_oldDatabaseMetadata);
464 } 546 }
465 547
466 void IDBTransaction::finished() { 548 void IDBTransaction::finished() {
467 #if DCHECK_IS_ON() 549 #if DCHECK_IS_ON()
468 DCHECK(!m_finishCalled); 550 DCHECK(!m_finishCalled);
469 m_finishCalled = true; 551 m_finishCalled = true;
470 #endif // DCHECK_IS_ON() 552 #endif // DCHECK_IS_ON()
471 553
472 m_database->transactionFinished(this); 554 m_database->transactionFinished(this);
473 555
474 // Break reference cycles. 556 // Remove references to the IDBObjectStore and IDBIndex instances held by
475 // TODO(jsbell): This can be removed c/o Oilpan. 557 // this transaction, so OilPan can garbage-collect the instances that aren't
476 for (auto& it : m_objectStoreMap) 558 // used by JavaScript.
477 it.value->transactionFinished(); 559
560 for (auto& it : m_objectStoreMap) {
561 IDBObjectStore* objectStore = it.value;
562 if (!isVersionChange() || objectStore->id() > oldMaxObjectStoreId()) {
563 DCHECK(!m_oldStoreMetadata.contains(objectStore));
564 objectStore->clearIndexCache();
565 } else {
566 // We'll call clearIndexCache() on this store in the loop below.
567 DCHECK(m_oldStoreMetadata.contains(objectStore));
568 }
569 }
478 m_objectStoreMap.clear(); 570 m_objectStoreMap.clear();
479 for (auto& it : m_deletedObjectStores) 571
480 it->transactionFinished(); 572 for (auto& it : m_oldStoreMetadata) {
481 m_createdObjectStores.clear(); 573 IDBObjectStore* objectStore = it.key;
574 objectStore->clearIndexCache();
575 }
576 m_oldStoreMetadata.clear();
577
578 m_deletedIndexes.clear();
482 m_deletedObjectStores.clear(); 579 m_deletedObjectStores.clear();
483
484 m_objectStoreCleanupMap.clear();
485 } 580 }
486 581
487 } // namespace blink 582 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698