Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "IDBTransaction.h"
8 :
9 : #include "BackgroundChildImpl.h"
10 : #include "IDBDatabase.h"
11 : #include "IDBEvents.h"
12 : #include "IDBObjectStore.h"
13 : #include "IDBRequest.h"
14 : #include "mozilla/ErrorResult.h"
15 : #include "mozilla/EventDispatcher.h"
16 : #include "mozilla/dom/DOMError.h"
17 : #include "mozilla/dom/DOMStringList.h"
18 : #include "mozilla/ipc/BackgroundChild.h"
19 : #include "nsAutoPtr.h"
20 : #include "nsPIDOMWindow.h"
21 : #include "nsQueryObject.h"
22 : #include "nsServiceManagerUtils.h"
23 : #include "nsTHashtable.h"
24 : #include "ProfilerHelpers.h"
25 : #include "ReportInternalError.h"
26 : #include "WorkerHolder.h"
27 : #include "WorkerPrivate.h"
28 :
29 : // Include this last to avoid path problems on Windows.
30 : #include "ActorsChild.h"
31 :
32 : namespace mozilla {
33 : namespace dom {
34 :
35 : using namespace mozilla::dom::indexedDB;
36 : using namespace mozilla::dom::workers;
37 : using namespace mozilla::ipc;
38 :
39 : class IDBTransaction::WorkerHolder final
40 : : public mozilla::dom::workers::WorkerHolder
41 : {
42 : WorkerPrivate* mWorkerPrivate;
43 :
44 : // The IDBTransaction owns this object so we only need a weak reference back
45 : // to it.
46 : IDBTransaction* mTransaction;
47 :
48 : public:
49 0 : WorkerHolder(WorkerPrivate* aWorkerPrivate, IDBTransaction* aTransaction)
50 0 : : mWorkerPrivate(aWorkerPrivate)
51 0 : , mTransaction(aTransaction)
52 : {
53 0 : MOZ_ASSERT(aWorkerPrivate);
54 0 : MOZ_ASSERT(aTransaction);
55 0 : aWorkerPrivate->AssertIsOnWorkerThread();
56 0 : aTransaction->AssertIsOnOwningThread();
57 :
58 0 : MOZ_COUNT_CTOR(IDBTransaction::WorkerHolder);
59 0 : }
60 :
61 0 : ~WorkerHolder()
62 0 : {
63 0 : mWorkerPrivate->AssertIsOnWorkerThread();
64 :
65 0 : MOZ_COUNT_DTOR(IDBTransaction::WorkerHolder);
66 0 : }
67 :
68 : private:
69 : virtual bool
70 : Notify(Status aStatus) override;
71 : };
72 :
73 0 : IDBTransaction::IDBTransaction(IDBDatabase* aDatabase,
74 : const nsTArray<nsString>& aObjectStoreNames,
75 0 : Mode aMode)
76 : : IDBWrapperCache(aDatabase)
77 : , mDatabase(aDatabase)
78 : , mObjectStoreNames(aObjectStoreNames)
79 : , mLoggingSerialNumber(0)
80 : , mNextObjectStoreId(0)
81 : , mNextIndexId(0)
82 : , mAbortCode(NS_OK)
83 : , mPendingRequestCount(0)
84 : , mLineNo(0)
85 : , mColumn(0)
86 : , mReadyState(IDBTransaction::INITIAL)
87 : , mMode(aMode)
88 : , mCreating(false)
89 : , mRegistered(false)
90 : , mAbortedByScript(false)
91 : , mNotedActiveTransaction(false)
92 : #ifdef DEBUG
93 : , mSentCommitOrAbort(false)
94 0 : , mFiredCompleteOrAbort(false)
95 : #endif
96 : {
97 0 : MOZ_ASSERT(aDatabase);
98 0 : aDatabase->AssertIsOnOwningThread();
99 :
100 0 : mBackgroundActor.mNormalBackgroundActor = nullptr;
101 :
102 : BackgroundChildImpl::ThreadLocal* threadLocal =
103 0 : BackgroundChildImpl::GetThreadLocalForCurrentThread();
104 0 : MOZ_ASSERT(threadLocal);
105 :
106 0 : ThreadLocal* idbThreadLocal = threadLocal->mIndexedDBThreadLocal;
107 0 : MOZ_ASSERT(idbThreadLocal);
108 :
109 0 : const_cast<int64_t&>(mLoggingSerialNumber) =
110 0 : idbThreadLocal->NextTransactionSN(aMode);
111 :
112 : #ifdef DEBUG
113 0 : if (!aObjectStoreNames.IsEmpty()) {
114 0 : nsTArray<nsString> sortedNames(aObjectStoreNames);
115 0 : sortedNames.Sort();
116 :
117 0 : const uint32_t count = sortedNames.Length();
118 0 : MOZ_ASSERT(count == aObjectStoreNames.Length());
119 :
120 : // Make sure the array is properly sorted.
121 0 : for (uint32_t index = 0; index < count; index++) {
122 0 : MOZ_ASSERT(aObjectStoreNames[index] == sortedNames[index]);
123 : }
124 :
125 : // Make sure there are no duplicates in our objectStore names.
126 0 : for (uint32_t index = 0; index < count - 1; index++) {
127 0 : MOZ_ASSERT(sortedNames[index] != sortedNames[index + 1]);
128 : }
129 : }
130 : #endif
131 0 : }
132 :
133 0 : IDBTransaction::~IDBTransaction()
134 : {
135 0 : AssertIsOnOwningThread();
136 0 : MOZ_ASSERT(!mPendingRequestCount);
137 0 : MOZ_ASSERT(!mCreating);
138 0 : MOZ_ASSERT(!mNotedActiveTransaction);
139 0 : MOZ_ASSERT(mSentCommitOrAbort);
140 0 : MOZ_ASSERT_IF(mMode == VERSION_CHANGE &&
141 : mBackgroundActor.mVersionChangeBackgroundActor,
142 : mFiredCompleteOrAbort);
143 0 : MOZ_ASSERT_IF(mMode != VERSION_CHANGE &&
144 : mBackgroundActor.mNormalBackgroundActor,
145 : mFiredCompleteOrAbort);
146 :
147 0 : if (mRegistered) {
148 0 : mDatabase->UnregisterTransaction(this);
149 : #ifdef DEBUG
150 0 : mRegistered = false;
151 : #endif
152 : }
153 :
154 0 : if (mMode == VERSION_CHANGE) {
155 0 : if (auto* actor = mBackgroundActor.mVersionChangeBackgroundActor) {
156 0 : actor->SendDeleteMeInternal(/* aFailedConstructor */ false);
157 :
158 0 : MOZ_ASSERT(!mBackgroundActor.mVersionChangeBackgroundActor,
159 : "SendDeleteMeInternal should have cleared!");
160 : }
161 0 : } else if (auto* actor = mBackgroundActor.mNormalBackgroundActor) {
162 0 : actor->SendDeleteMeInternal();
163 :
164 0 : MOZ_ASSERT(!mBackgroundActor.mNormalBackgroundActor,
165 : "SendDeleteMeInternal should have cleared!");
166 : }
167 0 : }
168 :
169 : // static
170 : already_AddRefed<IDBTransaction>
171 0 : IDBTransaction::CreateVersionChange(
172 : IDBDatabase* aDatabase,
173 : BackgroundVersionChangeTransactionChild* aActor,
174 : IDBOpenDBRequest* aOpenRequest,
175 : int64_t aNextObjectStoreId,
176 : int64_t aNextIndexId)
177 : {
178 0 : MOZ_ASSERT(aDatabase);
179 0 : aDatabase->AssertIsOnOwningThread();
180 0 : MOZ_ASSERT(aActor);
181 0 : MOZ_ASSERT(aOpenRequest);
182 0 : MOZ_ASSERT(aNextObjectStoreId > 0);
183 0 : MOZ_ASSERT(aNextIndexId > 0);
184 :
185 0 : nsTArray<nsString> emptyObjectStoreNames;
186 :
187 : RefPtr<IDBTransaction> transaction =
188 : new IDBTransaction(aDatabase,
189 : emptyObjectStoreNames,
190 0 : VERSION_CHANGE);
191 0 : aOpenRequest->GetCallerLocation(transaction->mFilename,
192 0 : &transaction->mLineNo, &transaction->mColumn);
193 :
194 0 : transaction->SetScriptOwner(aDatabase->GetScriptOwner());
195 :
196 0 : nsCOMPtr<nsIRunnable> runnable = do_QueryObject(transaction);
197 0 : nsContentUtils::RunInMetastableState(runnable.forget());
198 :
199 0 : transaction->NoteActiveTransaction();
200 :
201 0 : transaction->mBackgroundActor.mVersionChangeBackgroundActor = aActor;
202 0 : transaction->mNextObjectStoreId = aNextObjectStoreId;
203 0 : transaction->mNextIndexId = aNextIndexId;
204 0 : transaction->mCreating = true;
205 :
206 0 : aDatabase->RegisterTransaction(transaction);
207 0 : transaction->mRegistered = true;
208 :
209 0 : return transaction.forget();
210 : }
211 :
212 : // static
213 : already_AddRefed<IDBTransaction>
214 0 : IDBTransaction::Create(JSContext* aCx, IDBDatabase* aDatabase,
215 : const nsTArray<nsString>& aObjectStoreNames,
216 : Mode aMode)
217 : {
218 0 : MOZ_ASSERT(aDatabase);
219 0 : aDatabase->AssertIsOnOwningThread();
220 0 : MOZ_ASSERT(!aObjectStoreNames.IsEmpty());
221 0 : MOZ_ASSERT(aMode == READ_ONLY ||
222 : aMode == READ_WRITE ||
223 : aMode == READ_WRITE_FLUSH ||
224 : aMode == CLEANUP);
225 :
226 : RefPtr<IDBTransaction> transaction =
227 0 : new IDBTransaction(aDatabase, aObjectStoreNames, aMode);
228 0 : IDBRequest::CaptureCaller(aCx, transaction->mFilename, &transaction->mLineNo,
229 0 : &transaction->mColumn);
230 :
231 0 : transaction->SetScriptOwner(aDatabase->GetScriptOwner());
232 :
233 0 : nsCOMPtr<nsIRunnable> runnable = do_QueryObject(transaction);
234 0 : nsContentUtils::RunInMetastableState(runnable.forget());
235 :
236 0 : transaction->mCreating = true;
237 :
238 0 : aDatabase->RegisterTransaction(transaction);
239 0 : transaction->mRegistered = true;
240 :
241 0 : if (!NS_IsMainThread()) {
242 0 : WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
243 0 : MOZ_ASSERT(workerPrivate);
244 :
245 0 : workerPrivate->AssertIsOnWorkerThread();
246 :
247 0 : transaction->mWorkerHolder = new WorkerHolder(workerPrivate, transaction);
248 0 : MOZ_ALWAYS_TRUE(transaction->mWorkerHolder->HoldWorker(workerPrivate, Canceling));
249 : }
250 :
251 0 : return transaction.forget();
252 : }
253 :
254 : // static
255 : IDBTransaction*
256 0 : IDBTransaction::GetCurrent()
257 : {
258 : using namespace mozilla::ipc;
259 :
260 0 : MOZ_ASSERT(BackgroundChild::GetForCurrentThread());
261 :
262 : BackgroundChildImpl::ThreadLocal* threadLocal =
263 0 : BackgroundChildImpl::GetThreadLocalForCurrentThread();
264 0 : MOZ_ASSERT(threadLocal);
265 :
266 0 : ThreadLocal* idbThreadLocal = threadLocal->mIndexedDBThreadLocal;
267 0 : MOZ_ASSERT(idbThreadLocal);
268 :
269 0 : return idbThreadLocal->GetCurrentTransaction();
270 : }
271 :
272 : #ifdef DEBUG
273 :
274 : void
275 0 : IDBTransaction::AssertIsOnOwningThread() const
276 : {
277 0 : MOZ_ASSERT(mDatabase);
278 0 : mDatabase->AssertIsOnOwningThread();
279 0 : }
280 :
281 : #endif // DEBUG
282 :
283 : void
284 0 : IDBTransaction::SetBackgroundActor(indexedDB::BackgroundTransactionChild* aBackgroundActor)
285 : {
286 0 : AssertIsOnOwningThread();
287 0 : MOZ_ASSERT(aBackgroundActor);
288 0 : MOZ_ASSERT(!mBackgroundActor.mNormalBackgroundActor);
289 0 : MOZ_ASSERT(mMode != VERSION_CHANGE);
290 :
291 0 : NoteActiveTransaction();
292 :
293 0 : mBackgroundActor.mNormalBackgroundActor = aBackgroundActor;
294 0 : }
295 :
296 : BackgroundRequestChild*
297 0 : IDBTransaction::StartRequest(IDBRequest* aRequest, const RequestParams& aParams)
298 : {
299 0 : AssertIsOnOwningThread();
300 0 : MOZ_ASSERT(aRequest);
301 0 : MOZ_ASSERT(aParams.type() != RequestParams::T__None);
302 :
303 0 : BackgroundRequestChild* actor = new BackgroundRequestChild(aRequest);
304 :
305 0 : if (mMode == VERSION_CHANGE) {
306 0 : MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
307 :
308 0 : mBackgroundActor.mVersionChangeBackgroundActor->
309 0 : SendPBackgroundIDBRequestConstructor(actor, aParams);
310 : } else {
311 0 : MOZ_ASSERT(mBackgroundActor.mNormalBackgroundActor);
312 :
313 0 : mBackgroundActor.mNormalBackgroundActor->
314 0 : SendPBackgroundIDBRequestConstructor(actor, aParams);
315 : }
316 :
317 0 : MOZ_ASSERT(actor->GetActorEventTarget(),
318 : "The event target shall be inherited from its manager actor.");
319 :
320 : // Balanced in BackgroundRequestChild::Recv__delete__().
321 0 : OnNewRequest();
322 :
323 0 : return actor;
324 : }
325 :
326 : void
327 0 : IDBTransaction::OpenCursor(BackgroundCursorChild* aBackgroundActor,
328 : const OpenCursorParams& aParams)
329 : {
330 0 : AssertIsOnOwningThread();
331 0 : MOZ_ASSERT(aBackgroundActor);
332 0 : MOZ_ASSERT(aParams.type() != OpenCursorParams::T__None);
333 :
334 0 : if (mMode == VERSION_CHANGE) {
335 0 : MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
336 :
337 0 : mBackgroundActor.mVersionChangeBackgroundActor->
338 0 : SendPBackgroundIDBCursorConstructor(aBackgroundActor, aParams);
339 : } else {
340 0 : MOZ_ASSERT(mBackgroundActor.mNormalBackgroundActor);
341 :
342 0 : mBackgroundActor.mNormalBackgroundActor->
343 0 : SendPBackgroundIDBCursorConstructor(aBackgroundActor, aParams);
344 : }
345 :
346 0 : MOZ_ASSERT(aBackgroundActor->GetActorEventTarget(),
347 : "The event target shall be inherited from its manager actor.");
348 :
349 : // Balanced in BackgroundCursorChild::RecvResponse().
350 0 : OnNewRequest();
351 0 : }
352 :
353 : void
354 0 : IDBTransaction::RefreshSpec(bool aMayDelete)
355 : {
356 0 : AssertIsOnOwningThread();
357 :
358 0 : for (uint32_t count = mObjectStores.Length(), index = 0;
359 0 : index < count;
360 : index++) {
361 0 : mObjectStores[index]->RefreshSpec(aMayDelete);
362 : }
363 :
364 0 : for (uint32_t count = mDeletedObjectStores.Length(), index = 0;
365 0 : index < count;
366 : index++) {
367 0 : mDeletedObjectStores[index]->RefreshSpec(false);
368 : }
369 0 : }
370 :
371 : void
372 0 : IDBTransaction::OnNewRequest()
373 : {
374 0 : AssertIsOnOwningThread();
375 :
376 0 : if (!mPendingRequestCount) {
377 0 : MOZ_ASSERT(INITIAL == mReadyState);
378 0 : mReadyState = LOADING;
379 : }
380 :
381 0 : ++mPendingRequestCount;
382 0 : }
383 :
384 : void
385 0 : IDBTransaction::OnRequestFinished(bool aActorDestroyedNormally)
386 : {
387 0 : AssertIsOnOwningThread();
388 0 : MOZ_ASSERT(mPendingRequestCount);
389 :
390 0 : --mPendingRequestCount;
391 :
392 0 : if (!mPendingRequestCount) {
393 0 : mReadyState = COMMITTING;
394 :
395 0 : if (aActorDestroyedNormally) {
396 0 : if (NS_SUCCEEDED(mAbortCode)) {
397 0 : SendCommit();
398 : } else {
399 0 : SendAbort(mAbortCode);
400 : }
401 : } else {
402 : // Don't try to send any more messages to the parent if the request actor
403 : // was killed.
404 : #ifdef DEBUG
405 0 : MOZ_ASSERT(!mSentCommitOrAbort);
406 0 : mSentCommitOrAbort = true;
407 : #endif
408 0 : IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld]: "
409 : "Request actor was killed, transaction will be aborted",
410 : "IndexedDB %s: C T[%lld]: IDBTransaction abort",
411 : IDB_LOG_ID_STRING(),
412 : LoggingSerialNumber());
413 : }
414 : }
415 0 : }
416 :
417 : void
418 0 : IDBTransaction::SendCommit()
419 : {
420 0 : AssertIsOnOwningThread();
421 0 : MOZ_ASSERT(NS_SUCCEEDED(mAbortCode));
422 0 : MOZ_ASSERT(IsCommittingOrDone());
423 0 : MOZ_ASSERT(!mSentCommitOrAbort);
424 0 : MOZ_ASSERT(!mPendingRequestCount);
425 :
426 : // Don't do this in the macro because we always need to increment the serial
427 : // number to keep in sync with the parent.
428 0 : const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
429 :
430 0 : IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: "
431 : "All requests complete, committing transaction",
432 : "IndexedDB %s: C T[%lld] R[%llu]: IDBTransaction commit",
433 : IDB_LOG_ID_STRING(),
434 : LoggingSerialNumber(),
435 : requestSerialNumber);
436 :
437 0 : if (mMode == VERSION_CHANGE) {
438 0 : MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
439 0 : mBackgroundActor.mVersionChangeBackgroundActor->SendCommit();
440 : } else {
441 0 : MOZ_ASSERT(mBackgroundActor.mNormalBackgroundActor);
442 0 : mBackgroundActor.mNormalBackgroundActor->SendCommit();
443 : }
444 :
445 : #ifdef DEBUG
446 0 : mSentCommitOrAbort = true;
447 : #endif
448 0 : }
449 :
450 : void
451 0 : IDBTransaction::SendAbort(nsresult aResultCode)
452 : {
453 0 : AssertIsOnOwningThread();
454 0 : MOZ_ASSERT(NS_FAILED(aResultCode));
455 0 : MOZ_ASSERT(IsCommittingOrDone());
456 0 : MOZ_ASSERT(!mSentCommitOrAbort);
457 :
458 : // Don't do this in the macro because we always need to increment the serial
459 : // number to keep in sync with the parent.
460 0 : const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
461 :
462 0 : IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: "
463 : "Aborting transaction with result 0x%x",
464 : "IndexedDB %s: C T[%lld] R[%llu]: IDBTransaction abort (0x%x)",
465 : IDB_LOG_ID_STRING(),
466 : LoggingSerialNumber(),
467 : requestSerialNumber,
468 : aResultCode);
469 :
470 0 : if (mMode == VERSION_CHANGE) {
471 0 : MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
472 0 : mBackgroundActor.mVersionChangeBackgroundActor->SendAbort(aResultCode);
473 : } else {
474 0 : MOZ_ASSERT(mBackgroundActor.mNormalBackgroundActor);
475 0 : mBackgroundActor.mNormalBackgroundActor->SendAbort(aResultCode);
476 : }
477 :
478 : #ifdef DEBUG
479 0 : mSentCommitOrAbort = true;
480 : #endif
481 0 : }
482 :
483 : void
484 0 : IDBTransaction::NoteActiveTransaction()
485 : {
486 0 : AssertIsOnOwningThread();
487 0 : MOZ_ASSERT(!mNotedActiveTransaction);
488 :
489 0 : mDatabase->NoteActiveTransaction();
490 0 : mNotedActiveTransaction = true;
491 0 : }
492 :
493 : void
494 0 : IDBTransaction::MaybeNoteInactiveTransaction()
495 : {
496 0 : AssertIsOnOwningThread();
497 :
498 0 : if (mNotedActiveTransaction) {
499 0 : mDatabase->NoteInactiveTransaction();
500 0 : mNotedActiveTransaction = false;
501 : }
502 0 : }
503 :
504 : bool
505 0 : IDBTransaction::IsOpen() const
506 : {
507 0 : AssertIsOnOwningThread();
508 :
509 : // If we haven't started anything then we're open.
510 0 : if (mReadyState == IDBTransaction::INITIAL) {
511 0 : return true;
512 : }
513 :
514 : // If we've already started then we need to check to see if we still have the
515 : // mCreating flag set. If we do (i.e. we haven't returned to the event loop
516 : // from the time we were created) then we are open. Otherwise check the
517 : // currently running transaction to see if it's the same. We only allow other
518 : // requests to be made if this transaction is currently running.
519 0 : if (mReadyState == IDBTransaction::LOADING &&
520 0 : (mCreating || GetCurrent() == this)) {
521 0 : return true;
522 : }
523 :
524 0 : return false;
525 : }
526 :
527 : void
528 0 : IDBTransaction::GetCallerLocation(nsAString& aFilename, uint32_t* aLineNo,
529 : uint32_t* aColumn) const
530 : {
531 0 : AssertIsOnOwningThread();
532 0 : MOZ_ASSERT(aLineNo);
533 0 : MOZ_ASSERT(aColumn);
534 :
535 0 : aFilename = mFilename;
536 0 : *aLineNo = mLineNo;
537 0 : *aColumn = mColumn;
538 0 : }
539 :
540 : already_AddRefed<IDBObjectStore>
541 0 : IDBTransaction::CreateObjectStore(const ObjectStoreSpec& aSpec)
542 : {
543 0 : AssertIsOnOwningThread();
544 0 : MOZ_ASSERT(aSpec.metadata().id());
545 0 : MOZ_ASSERT(VERSION_CHANGE == mMode);
546 0 : MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
547 0 : MOZ_ASSERT(IsOpen());
548 :
549 : #ifdef DEBUG
550 : {
551 0 : const nsString& name = aSpec.metadata().name();
552 :
553 0 : for (uint32_t count = mObjectStores.Length(), index = 0;
554 0 : index < count;
555 : index++) {
556 0 : MOZ_ASSERT(mObjectStores[index]->Name() != name);
557 : }
558 : }
559 : #endif
560 :
561 0 : MOZ_ALWAYS_TRUE(mBackgroundActor.mVersionChangeBackgroundActor->
562 : SendCreateObjectStore(aSpec.metadata()));
563 :
564 0 : RefPtr<IDBObjectStore> objectStore = IDBObjectStore::Create(this, aSpec);
565 0 : MOZ_ASSERT(objectStore);
566 :
567 0 : mObjectStores.AppendElement(objectStore);
568 :
569 0 : return objectStore.forget();
570 : }
571 :
572 : void
573 0 : IDBTransaction::DeleteObjectStore(int64_t aObjectStoreId)
574 : {
575 0 : AssertIsOnOwningThread();
576 0 : MOZ_ASSERT(aObjectStoreId);
577 0 : MOZ_ASSERT(VERSION_CHANGE == mMode);
578 0 : MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
579 0 : MOZ_ASSERT(IsOpen());
580 :
581 0 : MOZ_ALWAYS_TRUE(mBackgroundActor.mVersionChangeBackgroundActor->
582 : SendDeleteObjectStore(aObjectStoreId));
583 :
584 0 : for (uint32_t count = mObjectStores.Length(), index = 0;
585 0 : index < count;
586 : index++) {
587 0 : RefPtr<IDBObjectStore>& objectStore = mObjectStores[index];
588 :
589 0 : if (objectStore->Id() == aObjectStoreId) {
590 0 : objectStore->NoteDeletion();
591 :
592 : RefPtr<IDBObjectStore>* deletedObjectStore =
593 0 : mDeletedObjectStores.AppendElement();
594 0 : deletedObjectStore->swap(mObjectStores[index]);
595 :
596 0 : mObjectStores.RemoveElementAt(index);
597 0 : break;
598 : }
599 : }
600 0 : }
601 :
602 : void
603 0 : IDBTransaction::RenameObjectStore(int64_t aObjectStoreId,
604 : const nsAString& aName)
605 : {
606 0 : AssertIsOnOwningThread();
607 0 : MOZ_ASSERT(aObjectStoreId);
608 0 : MOZ_ASSERT(VERSION_CHANGE == mMode);
609 0 : MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
610 0 : MOZ_ASSERT(IsOpen());
611 :
612 0 : MOZ_ALWAYS_TRUE(mBackgroundActor.mVersionChangeBackgroundActor->
613 : SendRenameObjectStore(aObjectStoreId, nsString(aName)));
614 0 : }
615 :
616 : void
617 0 : IDBTransaction::CreateIndex(IDBObjectStore* aObjectStore,
618 : const indexedDB::IndexMetadata& aMetadata)
619 : {
620 0 : AssertIsOnOwningThread();
621 0 : MOZ_ASSERT(aObjectStore);
622 0 : MOZ_ASSERT(aMetadata.id());
623 0 : MOZ_ASSERT(VERSION_CHANGE == mMode);
624 0 : MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
625 0 : MOZ_ASSERT(IsOpen());
626 :
627 0 : MOZ_ALWAYS_TRUE(mBackgroundActor.mVersionChangeBackgroundActor->
628 : SendCreateIndex(aObjectStore->Id(), aMetadata));
629 0 : }
630 :
631 : void
632 0 : IDBTransaction::DeleteIndex(IDBObjectStore* aObjectStore,
633 : int64_t aIndexId)
634 : {
635 0 : AssertIsOnOwningThread();
636 0 : MOZ_ASSERT(aObjectStore);
637 0 : MOZ_ASSERT(aIndexId);
638 0 : MOZ_ASSERT(VERSION_CHANGE == mMode);
639 0 : MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
640 0 : MOZ_ASSERT(IsOpen());
641 :
642 0 : MOZ_ALWAYS_TRUE(mBackgroundActor.mVersionChangeBackgroundActor->
643 : SendDeleteIndex(aObjectStore->Id(), aIndexId));
644 0 : }
645 :
646 : void
647 0 : IDBTransaction::RenameIndex(IDBObjectStore* aObjectStore,
648 : int64_t aIndexId,
649 : const nsAString& aName)
650 : {
651 0 : AssertIsOnOwningThread();
652 0 : MOZ_ASSERT(aObjectStore);
653 0 : MOZ_ASSERT(aIndexId);
654 0 : MOZ_ASSERT(VERSION_CHANGE == mMode);
655 0 : MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
656 0 : MOZ_ASSERT(IsOpen());
657 :
658 0 : MOZ_ALWAYS_TRUE(mBackgroundActor.mVersionChangeBackgroundActor->
659 : SendRenameIndex(aObjectStore->Id(),
660 : aIndexId,
661 : nsString(aName)));
662 0 : }
663 :
664 : void
665 0 : IDBTransaction::AbortInternal(nsresult aAbortCode,
666 : already_AddRefed<DOMError> aError)
667 : {
668 0 : AssertIsOnOwningThread();
669 0 : MOZ_ASSERT(NS_FAILED(aAbortCode));
670 0 : MOZ_ASSERT(!IsCommittingOrDone());
671 :
672 0 : RefPtr<DOMError> error = aError;
673 :
674 0 : const bool isVersionChange = mMode == VERSION_CHANGE;
675 0 : const bool isInvalidated = mDatabase->IsInvalidated();
676 0 : bool needToSendAbort = mReadyState == INITIAL;
677 :
678 0 : mAbortCode = aAbortCode;
679 0 : mReadyState = DONE;
680 0 : mError = error.forget();
681 :
682 0 : if (isVersionChange) {
683 : // If a version change transaction is aborted, we must revert the world
684 : // back to its previous state unless we're being invalidated after the
685 : // transaction already completed.
686 0 : if (!isInvalidated) {
687 0 : mDatabase->RevertToPreviousState();
688 : }
689 :
690 : // We do the reversion only for the mObjectStores/mDeletedObjectStores but
691 : // not for the mIndexes/mDeletedIndexes of each IDBObjectStore because it's
692 : // time-consuming(O(m*n)) and mIndexes/mDeletedIndexes won't be used anymore
693 : // in IDBObjectStore::(Create|Delete)Index() and IDBObjectStore::Index() in
694 : // which all the executions are returned earlier by !transaction->IsOpen().
695 :
696 : const nsTArray<ObjectStoreSpec>& specArray =
697 0 : mDatabase->Spec()->objectStores();
698 :
699 0 : if (specArray.IsEmpty()) {
700 0 : mObjectStores.Clear();
701 0 : mDeletedObjectStores.Clear();
702 : } else {
703 0 : nsTHashtable<nsUint64HashKey> validIds(specArray.Length());
704 :
705 0 : for (uint32_t specCount = specArray.Length(), specIndex = 0;
706 0 : specIndex < specCount;
707 : specIndex++) {
708 0 : const int64_t objectStoreId = specArray[specIndex].metadata().id();
709 0 : MOZ_ASSERT(objectStoreId);
710 :
711 0 : validIds.PutEntry(uint64_t(objectStoreId));
712 : }
713 :
714 0 : for (uint32_t objCount = mObjectStores.Length(), objIndex = 0;
715 0 : objIndex < objCount;
716 : /* incremented conditionally */) {
717 0 : const int64_t objectStoreId = mObjectStores[objIndex]->Id();
718 0 : MOZ_ASSERT(objectStoreId);
719 :
720 0 : if (validIds.Contains(uint64_t(objectStoreId))) {
721 0 : objIndex++;
722 : } else {
723 0 : mObjectStores.RemoveElementAt(objIndex);
724 0 : objCount--;
725 : }
726 : }
727 :
728 0 : if (!mDeletedObjectStores.IsEmpty()) {
729 0 : for (uint32_t objCount = mDeletedObjectStores.Length(), objIndex = 0;
730 0 : objIndex < objCount;
731 : objIndex++) {
732 0 : const int64_t objectStoreId = mDeletedObjectStores[objIndex]->Id();
733 0 : MOZ_ASSERT(objectStoreId);
734 :
735 0 : if (validIds.Contains(uint64_t(objectStoreId))) {
736 : RefPtr<IDBObjectStore>* objectStore =
737 0 : mObjectStores.AppendElement();
738 0 : objectStore->swap(mDeletedObjectStores[objIndex]);
739 : }
740 : }
741 0 : mDeletedObjectStores.Clear();
742 : }
743 : }
744 : }
745 :
746 : // Fire the abort event if there are no outstanding requests. Otherwise the
747 : // abort event will be fired when all outstanding requests finish.
748 0 : if (needToSendAbort) {
749 0 : SendAbort(aAbortCode);
750 : }
751 :
752 0 : if (isVersionChange) {
753 0 : mDatabase->Close();
754 : }
755 0 : }
756 :
757 : void
758 0 : IDBTransaction::Abort(IDBRequest* aRequest)
759 : {
760 0 : AssertIsOnOwningThread();
761 0 : MOZ_ASSERT(aRequest);
762 :
763 0 : if (IsCommittingOrDone()) {
764 : // Already started (and maybe finished) the commit or abort so there is
765 : // nothing to do here.
766 0 : return;
767 : }
768 :
769 0 : ErrorResult rv;
770 0 : RefPtr<DOMError> error = aRequest->GetError(rv);
771 :
772 0 : AbortInternal(aRequest->GetErrorCode(), error.forget());
773 : }
774 :
775 : void
776 0 : IDBTransaction::Abort(nsresult aErrorCode)
777 : {
778 0 : AssertIsOnOwningThread();
779 :
780 0 : if (IsCommittingOrDone()) {
781 : // Already started (and maybe finished) the commit or abort so there is
782 : // nothing to do here.
783 0 : return;
784 : }
785 :
786 0 : RefPtr<DOMError> error = new DOMError(GetOwner(), aErrorCode);
787 0 : AbortInternal(aErrorCode, error.forget());
788 : }
789 :
790 : void
791 0 : IDBTransaction::Abort(ErrorResult& aRv)
792 : {
793 0 : AssertIsOnOwningThread();
794 :
795 0 : if (IsCommittingOrDone()) {
796 0 : aRv = NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
797 0 : return;
798 : }
799 :
800 0 : AbortInternal(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR, nullptr);
801 :
802 0 : MOZ_ASSERT(!mAbortedByScript);
803 0 : mAbortedByScript = true;
804 : }
805 :
806 : void
807 0 : IDBTransaction::FireCompleteOrAbortEvents(nsresult aResult)
808 : {
809 0 : AssertIsOnOwningThread();
810 0 : MOZ_ASSERT(!mFiredCompleteOrAbort);
811 :
812 0 : mReadyState = DONE;
813 :
814 : #ifdef DEBUG
815 0 : mFiredCompleteOrAbort = true;
816 : #endif
817 :
818 : // Make sure we drop the WorkerHolder when this function completes.
819 0 : nsAutoPtr<WorkerHolder> workerHolder = Move(mWorkerHolder);
820 :
821 0 : nsCOMPtr<nsIDOMEvent> event;
822 0 : if (NS_SUCCEEDED(aResult)) {
823 0 : event = CreateGenericEvent(this,
824 0 : nsDependentString(kCompleteEventType),
825 : eDoesNotBubble,
826 0 : eNotCancelable);
827 0 : MOZ_ASSERT(event);
828 : } else {
829 0 : if (aResult == NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR) {
830 0 : mDatabase->SetQuotaExceeded();
831 : }
832 :
833 0 : if (!mError && !mAbortedByScript) {
834 0 : mError = new DOMError(GetOwner(), aResult);
835 : }
836 :
837 0 : event = CreateGenericEvent(this,
838 0 : nsDependentString(kAbortEventType),
839 : eDoesBubble,
840 0 : eNotCancelable);
841 0 : MOZ_ASSERT(event);
842 : }
843 :
844 0 : if (NS_SUCCEEDED(mAbortCode)) {
845 0 : IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld]: "
846 : "Firing 'complete' event",
847 : "IndexedDB %s: C T[%lld]: IDBTransaction 'complete' event",
848 : IDB_LOG_ID_STRING(),
849 : mLoggingSerialNumber);
850 : } else {
851 0 : IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld]: "
852 : "Firing 'abort' event with error 0x%x",
853 : "IndexedDB %s: C T[%lld]: IDBTransaction 'abort' event (0x%x)",
854 : IDB_LOG_ID_STRING(),
855 : mLoggingSerialNumber,
856 : mAbortCode);
857 : }
858 :
859 : bool dummy;
860 0 : if (NS_FAILED(DispatchEvent(event, &dummy))) {
861 0 : NS_WARNING("DispatchEvent failed!");
862 : }
863 :
864 : // Normally, we note inactive transaction here instead of
865 : // IDBTransaction::ClearBackgroundActor() because here is the earliest place
866 : // to know that it becomes non-blocking to allow the scheduler to start the
867 : // preemption as soon as it can.
868 : // Note: If the IDBTransaction object is held by the script,
869 : // ClearBackgroundActor() will be done in ~IDBTransaction() until garbage
870 : // collected after its window is closed which prevents us to preempt its
871 : // window immediately after committed.
872 0 : MaybeNoteInactiveTransaction();
873 0 : }
874 :
875 : int64_t
876 0 : IDBTransaction::NextObjectStoreId()
877 : {
878 0 : AssertIsOnOwningThread();
879 0 : MOZ_ASSERT(VERSION_CHANGE == mMode);
880 :
881 0 : return mNextObjectStoreId++;
882 : }
883 :
884 : int64_t
885 0 : IDBTransaction::NextIndexId()
886 : {
887 0 : AssertIsOnOwningThread();
888 0 : MOZ_ASSERT(VERSION_CHANGE == mMode);
889 :
890 0 : return mNextIndexId++;
891 : }
892 :
893 : nsPIDOMWindowInner*
894 0 : IDBTransaction::GetParentObject() const
895 : {
896 0 : AssertIsOnOwningThread();
897 :
898 0 : return mDatabase->GetParentObject();
899 : }
900 :
901 : IDBTransactionMode
902 0 : IDBTransaction::GetMode(ErrorResult& aRv) const
903 : {
904 0 : AssertIsOnOwningThread();
905 :
906 0 : switch (mMode) {
907 : case READ_ONLY:
908 0 : return IDBTransactionMode::Readonly;
909 :
910 : case READ_WRITE:
911 0 : return IDBTransactionMode::Readwrite;
912 :
913 : case READ_WRITE_FLUSH:
914 0 : return IDBTransactionMode::Readwriteflush;
915 :
916 : case CLEANUP:
917 0 : return IDBTransactionMode::Cleanup;
918 :
919 : case VERSION_CHANGE:
920 0 : return IDBTransactionMode::Versionchange;
921 :
922 : case MODE_INVALID:
923 : default:
924 0 : MOZ_CRASH("Bad mode!");
925 : }
926 : }
927 :
928 : DOMError*
929 0 : IDBTransaction::GetError() const
930 : {
931 0 : AssertIsOnOwningThread();
932 :
933 0 : return mError;
934 : }
935 :
936 : already_AddRefed<DOMStringList>
937 0 : IDBTransaction::ObjectStoreNames() const
938 : {
939 0 : AssertIsOnOwningThread();
940 :
941 0 : if (mMode == IDBTransaction::VERSION_CHANGE) {
942 0 : return mDatabase->ObjectStoreNames();
943 : }
944 :
945 0 : RefPtr<DOMStringList> list = new DOMStringList();
946 0 : list->StringArray() = mObjectStoreNames;
947 0 : return list.forget();
948 : }
949 :
950 : already_AddRefed<IDBObjectStore>
951 0 : IDBTransaction::ObjectStore(const nsAString& aName, ErrorResult& aRv)
952 : {
953 0 : AssertIsOnOwningThread();
954 :
955 0 : if (IsCommittingOrDone()) {
956 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
957 0 : return nullptr;
958 : }
959 :
960 0 : const ObjectStoreSpec* spec = nullptr;
961 :
962 0 : if (IDBTransaction::VERSION_CHANGE == mMode ||
963 0 : mObjectStoreNames.Contains(aName)) {
964 : const nsTArray<ObjectStoreSpec>& objectStores =
965 0 : mDatabase->Spec()->objectStores();
966 :
967 0 : for (uint32_t count = objectStores.Length(), index = 0;
968 0 : index < count;
969 : index++) {
970 0 : const ObjectStoreSpec& objectStore = objectStores[index];
971 0 : if (objectStore.metadata().name() == aName) {
972 0 : spec = &objectStore;
973 0 : break;
974 : }
975 : }
976 : }
977 :
978 0 : if (!spec) {
979 0 : aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR);
980 0 : return nullptr;
981 : }
982 :
983 0 : const int64_t desiredId = spec->metadata().id();
984 :
985 0 : RefPtr<IDBObjectStore> objectStore;
986 :
987 0 : for (uint32_t count = mObjectStores.Length(), index = 0;
988 0 : index < count;
989 : index++) {
990 0 : RefPtr<IDBObjectStore>& existingObjectStore = mObjectStores[index];
991 :
992 0 : if (existingObjectStore->Id() == desiredId) {
993 0 : objectStore = existingObjectStore;
994 0 : break;
995 : }
996 : }
997 :
998 0 : if (!objectStore) {
999 0 : objectStore = IDBObjectStore::Create(this, *spec);
1000 0 : MOZ_ASSERT(objectStore);
1001 :
1002 0 : mObjectStores.AppendElement(objectStore);
1003 : }
1004 :
1005 0 : return objectStore.forget();
1006 : }
1007 :
1008 0 : NS_IMPL_ADDREF_INHERITED(IDBTransaction, IDBWrapperCache)
1009 0 : NS_IMPL_RELEASE_INHERITED(IDBTransaction, IDBWrapperCache)
1010 :
1011 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBTransaction)
1012 0 : NS_INTERFACE_MAP_ENTRY(nsIRunnable)
1013 0 : NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache)
1014 :
1015 : NS_IMPL_CYCLE_COLLECTION_CLASS(IDBTransaction)
1016 :
1017 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBTransaction,
1018 : IDBWrapperCache)
1019 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDatabase)
1020 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError)
1021 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObjectStores)
1022 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDeletedObjectStores)
1023 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1024 :
1025 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBTransaction, IDBWrapperCache)
1026 : // Don't unlink mDatabase!
1027 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mError)
1028 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mObjectStores)
1029 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mDeletedObjectStores)
1030 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1031 :
1032 : JSObject*
1033 0 : IDBTransaction::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
1034 : {
1035 0 : AssertIsOnOwningThread();
1036 :
1037 0 : return IDBTransactionBinding::Wrap(aCx, this, aGivenProto);
1038 : }
1039 :
1040 : nsresult
1041 0 : IDBTransaction::GetEventTargetParent(EventChainPreVisitor& aVisitor)
1042 : {
1043 0 : AssertIsOnOwningThread();
1044 :
1045 0 : aVisitor.mCanHandle = true;
1046 0 : aVisitor.mParentTarget = mDatabase;
1047 0 : return NS_OK;
1048 : }
1049 :
1050 : NS_IMETHODIMP
1051 0 : IDBTransaction::Run()
1052 : {
1053 0 : AssertIsOnOwningThread();
1054 :
1055 : // We're back at the event loop, no longer newborn.
1056 0 : mCreating = false;
1057 :
1058 : // Maybe commit if there were no requests generated.
1059 0 : if (mReadyState == IDBTransaction::INITIAL) {
1060 0 : mReadyState = DONE;
1061 :
1062 0 : SendCommit();
1063 : }
1064 :
1065 0 : return NS_OK;
1066 : }
1067 :
1068 : bool
1069 0 : IDBTransaction::
1070 : WorkerHolder::Notify(Status aStatus)
1071 : {
1072 0 : MOZ_ASSERT(mWorkerPrivate);
1073 0 : mWorkerPrivate->AssertIsOnWorkerThread();
1074 0 : MOZ_ASSERT(aStatus > Running);
1075 :
1076 0 : if (mTransaction && aStatus > Terminating) {
1077 0 : mTransaction->AssertIsOnOwningThread();
1078 :
1079 0 : RefPtr<IDBTransaction> transaction = Move(mTransaction);
1080 :
1081 0 : if (!transaction->IsCommittingOrDone()) {
1082 0 : IDB_REPORT_INTERNAL_ERR();
1083 0 : transaction->AbortInternal(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR, nullptr);
1084 : }
1085 : }
1086 :
1087 0 : return true;
1088 : }
1089 :
1090 : } // namespace dom
1091 : } // namespace mozilla
|