Source code

Revision control

Other Tools

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 "IDBRequest.h"
8
9
#include "BackgroundChildImpl.h"
10
#include "IDBCursor.h"
11
#include "IDBDatabase.h"
12
#include "IDBEvents.h"
13
#include "IDBFactory.h"
14
#include "IDBIndex.h"
15
#include "IDBObjectStore.h"
16
#include "IDBTransaction.h"
17
#include "IndexedDatabaseManager.h"
18
#include "mozilla/ContentEvents.h"
19
#include "mozilla/ErrorResult.h"
20
#include "mozilla/EventDispatcher.h"
21
#include "mozilla/HoldDropJSObjects.h"
22
#include "mozilla/Move.h"
23
#include "mozilla/dom/DOMException.h"
24
#include "mozilla/dom/ErrorEventBinding.h"
25
#include "mozilla/dom/IDBOpenDBRequestBinding.h"
26
#include "mozilla/dom/ScriptSettings.h"
27
#include "mozilla/dom/WorkerPrivate.h"
28
#include "mozilla/dom/WorkerRef.h"
29
#include "nsCOMPtr.h"
30
#include "nsContentUtils.h"
31
#include "nsIScriptContext.h"
32
#include "nsJSUtils.h"
33
#include "nsIGlobalObject.h"
34
#include "nsString.h"
35
#include "ReportInternalError.h"
36
37
// Include this last to avoid path problems on Windows.
38
#include "ActorsChild.h"
39
40
namespace mozilla {
41
namespace dom {
42
43
using namespace mozilla::dom::indexedDB;
44
using namespace mozilla::ipc;
45
46
namespace {
47
48
NS_DEFINE_IID(kIDBRequestIID, PRIVATE_IDBREQUEST_IID);
49
50
} // namespace
51
52
IDBRequest::IDBRequest(IDBDatabase* aDatabase)
53
: DOMEventTargetHelper(aDatabase),
54
mLoggingSerialNumber(0),
55
mLineNo(0),
56
mColumn(0),
57
mHaveResultOrErrorCode(false) {
58
MOZ_ASSERT(aDatabase);
59
aDatabase->AssertIsOnOwningThread();
60
61
InitMembers();
62
}
63
64
IDBRequest::IDBRequest(nsIGlobalObject* aGlobal)
65
: DOMEventTargetHelper(aGlobal),
66
mLoggingSerialNumber(0),
67
mLineNo(0),
68
mColumn(0),
69
mHaveResultOrErrorCode(false) {
70
InitMembers();
71
}
72
73
IDBRequest::~IDBRequest() {
74
AssertIsOnOwningThread();
75
mozilla::DropJSObjects(this);
76
}
77
78
void IDBRequest::InitMembers() {
79
AssertIsOnOwningThread();
80
81
mResultVal.setUndefined();
82
mLoggingSerialNumber = NextSerialNumber();
83
mErrorCode = NS_OK;
84
mLineNo = 0;
85
mColumn = 0;
86
mHaveResultOrErrorCode = false;
87
}
88
89
// static
90
already_AddRefed<IDBRequest> IDBRequest::Create(JSContext* aCx,
91
IDBDatabase* aDatabase,
92
IDBTransaction* aTransaction) {
93
MOZ_ASSERT(aCx);
94
MOZ_ASSERT(aDatabase);
95
aDatabase->AssertIsOnOwningThread();
96
97
RefPtr<IDBRequest> request = new IDBRequest(aDatabase);
98
CaptureCaller(aCx, request->mFilename, &request->mLineNo, &request->mColumn);
99
100
request->mTransaction = aTransaction;
101
102
return request.forget();
103
}
104
105
// static
106
already_AddRefed<IDBRequest> IDBRequest::Create(
107
JSContext* aCx, IDBObjectStore* aSourceAsObjectStore,
108
IDBDatabase* aDatabase, IDBTransaction* aTransaction) {
109
MOZ_ASSERT(aSourceAsObjectStore);
110
aSourceAsObjectStore->AssertIsOnOwningThread();
111
112
RefPtr<IDBRequest> request = Create(aCx, aDatabase, aTransaction);
113
114
request->mSourceAsObjectStore = aSourceAsObjectStore;
115
116
return request.forget();
117
}
118
119
// static
120
already_AddRefed<IDBRequest> IDBRequest::Create(JSContext* aCx,
121
IDBIndex* aSourceAsIndex,
122
IDBDatabase* aDatabase,
123
IDBTransaction* aTransaction) {
124
MOZ_ASSERT(aSourceAsIndex);
125
aSourceAsIndex->AssertIsOnOwningThread();
126
127
RefPtr<IDBRequest> request = Create(aCx, aDatabase, aTransaction);
128
129
request->mSourceAsIndex = aSourceAsIndex;
130
131
return request.forget();
132
}
133
134
// static
135
uint64_t IDBRequest::NextSerialNumber() {
136
BackgroundChildImpl::ThreadLocal* threadLocal =
137
BackgroundChildImpl::GetThreadLocalForCurrentThread();
138
MOZ_ASSERT(threadLocal);
139
140
ThreadLocal* idbThreadLocal = threadLocal->mIndexedDBThreadLocal;
141
MOZ_ASSERT(idbThreadLocal);
142
143
return idbThreadLocal->NextRequestSN();
144
}
145
146
void IDBRequest::SetLoggingSerialNumber(uint64_t aLoggingSerialNumber) {
147
AssertIsOnOwningThread();
148
MOZ_ASSERT(aLoggingSerialNumber > mLoggingSerialNumber);
149
150
mLoggingSerialNumber = aLoggingSerialNumber;
151
}
152
153
void IDBRequest::CaptureCaller(JSContext* aCx, nsAString& aFilename,
154
uint32_t* aLineNo, uint32_t* aColumn) {
155
MOZ_ASSERT(aFilename.IsEmpty());
156
MOZ_ASSERT(aLineNo);
157
MOZ_ASSERT(aColumn);
158
159
nsJSUtils::GetCallingLocation(aCx, aFilename, aLineNo, aColumn);
160
}
161
162
void IDBRequest::GetSource(
163
Nullable<OwningIDBObjectStoreOrIDBIndexOrIDBCursor>& aSource) const {
164
AssertIsOnOwningThread();
165
166
MOZ_ASSERT_IF(mSourceAsObjectStore, !mSourceAsIndex);
167
MOZ_ASSERT_IF(mSourceAsIndex, !mSourceAsObjectStore);
168
MOZ_ASSERT_IF(mSourceAsCursor, mSourceAsObjectStore || mSourceAsIndex);
169
170
// Always check cursor first since cursor requests hold both the cursor and
171
// the objectStore or index the cursor came from.
172
if (mSourceAsCursor) {
173
aSource.SetValue().SetAsIDBCursor() = mSourceAsCursor;
174
} else if (mSourceAsObjectStore) {
175
aSource.SetValue().SetAsIDBObjectStore() = mSourceAsObjectStore;
176
} else if (mSourceAsIndex) {
177
aSource.SetValue().SetAsIDBIndex() = mSourceAsIndex;
178
} else {
179
aSource.SetNull();
180
}
181
}
182
183
void IDBRequest::Reset() {
184
AssertIsOnOwningThread();
185
186
mResultVal.setUndefined();
187
188
mHaveResultOrErrorCode = false;
189
mError = nullptr;
190
}
191
192
void IDBRequest::SetError(nsresult aRv) {
193
AssertIsOnOwningThread();
194
MOZ_ASSERT(NS_FAILED(aRv));
195
MOZ_ASSERT(NS_ERROR_GET_MODULE(aRv) == NS_ERROR_MODULE_DOM_INDEXEDDB);
196
MOZ_ASSERT(!mError);
197
198
mHaveResultOrErrorCode = true;
199
mError = DOMException::Create(aRv);
200
mErrorCode = aRv;
201
202
mResultVal.setUndefined();
203
}
204
205
#ifdef DEBUG
206
207
nsresult IDBRequest::GetErrorCode() const {
208
AssertIsOnOwningThread();
209
MOZ_ASSERT(mHaveResultOrErrorCode);
210
211
return mErrorCode;
212
}
213
214
DOMException* IDBRequest::GetErrorAfterResult() const {
215
AssertIsOnOwningThread();
216
MOZ_ASSERT(mHaveResultOrErrorCode);
217
218
return mError;
219
}
220
221
#endif // DEBUG
222
223
void IDBRequest::GetCallerLocation(nsAString& aFilename, uint32_t* aLineNo,
224
uint32_t* aColumn) const {
225
AssertIsOnOwningThread();
226
MOZ_ASSERT(aLineNo);
227
MOZ_ASSERT(aColumn);
228
229
aFilename = mFilename;
230
*aLineNo = mLineNo;
231
*aColumn = mColumn;
232
}
233
234
IDBRequestReadyState IDBRequest::ReadyState() const {
235
AssertIsOnOwningThread();
236
237
return IsPending() ? IDBRequestReadyState::Pending
238
: IDBRequestReadyState::Done;
239
}
240
241
void IDBRequest::SetSource(IDBCursor* aSource) {
242
AssertIsOnOwningThread();
243
MOZ_ASSERT(aSource);
244
MOZ_ASSERT(mSourceAsObjectStore || mSourceAsIndex);
245
MOZ_ASSERT(!mSourceAsCursor);
246
247
mSourceAsCursor = aSource;
248
}
249
250
JSObject* IDBRequest::WrapObject(JSContext* aCx,
251
JS::Handle<JSObject*> aGivenProto) {
252
return IDBRequest_Binding::Wrap(aCx, this, aGivenProto);
253
}
254
255
void IDBRequest::GetResult(JS::MutableHandle<JS::Value> aResult,
256
ErrorResult& aRv) const {
257
AssertIsOnOwningThread();
258
259
if (!mHaveResultOrErrorCode) {
260
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
261
return;
262
}
263
264
aResult.set(mResultVal);
265
}
266
267
void IDBRequest::SetResultCallback(ResultCallback* aCallback) {
268
AssertIsOnOwningThread();
269
MOZ_ASSERT(aCallback);
270
MOZ_ASSERT(!mHaveResultOrErrorCode);
271
MOZ_ASSERT(mResultVal.isUndefined());
272
MOZ_ASSERT(!mError);
273
274
// Already disconnected from the owner.
275
if (!GetOwnerGlobal()) {
276
SetError(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
277
return;
278
}
279
280
// See this global is still valid.
281
if (NS_WARN_IF(NS_FAILED(CheckCurrentGlobalCorrectness()))) {
282
SetError(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
283
return;
284
}
285
286
AutoJSAPI autoJS;
287
if (!autoJS.Init(GetOwnerGlobal())) {
288
IDB_WARNING("Failed to initialize AutoJSAPI!");
289
SetError(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
290
return;
291
}
292
293
JSContext* cx = autoJS.cx();
294
295
JS::Rooted<JS::Value> result(cx);
296
nsresult rv = aCallback->GetResult(cx, &result);
297
if (NS_WARN_IF(NS_FAILED(rv))) {
298
// This can only fail if the structured clone contains a mutable file
299
// and the child is not in the main thread and main process.
300
// In that case CreateAndWrapMutableFile() returns false which shows up
301
// as NS_ERROR_DOM_DATA_CLONE_ERR here.
302
MOZ_ASSERT(rv == NS_ERROR_DOM_DATA_CLONE_ERR);
303
304
// We are not setting a result or an error object here since we want to
305
// throw an exception when the 'result' property is being touched.
306
return;
307
}
308
309
mError = nullptr;
310
311
mResultVal = result;
312
mozilla::HoldJSObjects(this);
313
314
mHaveResultOrErrorCode = true;
315
}
316
317
DOMException* IDBRequest::GetError(ErrorResult& aRv) {
318
AssertIsOnOwningThread();
319
320
if (!mHaveResultOrErrorCode) {
321
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
322
return nullptr;
323
}
324
325
return mError;
326
}
327
328
NS_IMPL_CYCLE_COLLECTION_CLASS(IDBRequest)
329
330
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBRequest,
331
DOMEventTargetHelper)
332
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceAsObjectStore)
333
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceAsIndex)
334
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceAsCursor)
335
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction)
336
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError)
337
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
338
339
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBRequest,
340
DOMEventTargetHelper)
341
tmp->mResultVal.setUndefined();
342
mozilla::DropJSObjects(tmp);
343
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceAsObjectStore)
344
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceAsIndex)
345
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceAsCursor)
346
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTransaction)
347
NS_IMPL_CYCLE_COLLECTION_UNLINK(mError)
348
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
349
350
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(IDBRequest, DOMEventTargetHelper)
351
// Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because
352
// DOMEventTargetHelper does it for us.
353
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultVal)
354
NS_IMPL_CYCLE_COLLECTION_TRACE_END
355
356
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBRequest)
357
if (aIID.Equals(kIDBRequestIID)) {
358
foundInterface = this;
359
} else
360
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
361
362
NS_IMPL_ADDREF_INHERITED(IDBRequest, DOMEventTargetHelper)
363
NS_IMPL_RELEASE_INHERITED(IDBRequest, DOMEventTargetHelper)
364
365
void IDBRequest::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
366
AssertIsOnOwningThread();
367
368
aVisitor.mCanHandle = true;
369
aVisitor.SetParentTarget(mTransaction, false);
370
}
371
372
IDBOpenDBRequest::IDBOpenDBRequest(IDBFactory* aFactory,
373
nsIGlobalObject* aGlobal,
374
bool aFileHandleDisabled)
375
: IDBRequest(aGlobal),
376
mFactory(aFactory),
377
mFileHandleDisabled(aFileHandleDisabled),
378
mIncreasedActiveDatabaseCount(false) {
379
AssertIsOnOwningThread();
380
MOZ_ASSERT(aFactory);
381
MOZ_ASSERT(aGlobal);
382
}
383
384
IDBOpenDBRequest::~IDBOpenDBRequest() {
385
AssertIsOnOwningThread();
386
MOZ_ASSERT(!mIncreasedActiveDatabaseCount);
387
}
388
389
// static
390
already_AddRefed<IDBOpenDBRequest> IDBOpenDBRequest::Create(
391
JSContext* aCx, IDBFactory* aFactory, nsIGlobalObject* aGlobal) {
392
MOZ_ASSERT(aFactory);
393
aFactory->AssertIsOnOwningThread();
394
MOZ_ASSERT(aGlobal);
395
396
bool fileHandleDisabled = !IndexedDatabaseManager::IsFileHandleEnabled();
397
398
RefPtr<IDBOpenDBRequest> request =
399
new IDBOpenDBRequest(aFactory, aGlobal, fileHandleDisabled);
400
CaptureCaller(aCx, request->mFilename, &request->mLineNo, &request->mColumn);
401
402
if (!NS_IsMainThread()) {
403
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
404
MOZ_ASSERT(workerPrivate);
405
406
workerPrivate->AssertIsOnWorkerThread();
407
408
request->mWorkerRef =
409
StrongWorkerRef::Create(workerPrivate, "IDBOpenDBRequest");
410
if (NS_WARN_IF(!request->mWorkerRef)) {
411
return nullptr;
412
}
413
}
414
415
request->IncreaseActiveDatabaseCount();
416
417
return request.forget();
418
}
419
420
void IDBOpenDBRequest::SetTransaction(IDBTransaction* aTransaction) {
421
AssertIsOnOwningThread();
422
423
MOZ_ASSERT(!aTransaction || !mTransaction);
424
425
mTransaction = aTransaction;
426
}
427
428
void IDBOpenDBRequest::DispatchNonTransactionError(nsresult aErrorCode) {
429
AssertIsOnOwningThread();
430
MOZ_ASSERT(NS_FAILED(aErrorCode));
431
MOZ_ASSERT(NS_ERROR_GET_MODULE(aErrorCode) == NS_ERROR_MODULE_DOM_INDEXEDDB);
432
433
// The actor failed to initiate, decrease the number of active IDBOpenRequests
434
// here since NoteComplete won't be called.
435
MaybeDecreaseActiveDatabaseCount();
436
437
SetError(aErrorCode);
438
439
// Make an error event and fire it at the target.
440
RefPtr<Event> event = CreateGenericEvent(
441
this, nsDependentString(kErrorEventType), eDoesBubble, eCancelable);
442
MOZ_ASSERT(event);
443
444
IgnoredErrorResult rv;
445
DispatchEvent(*event, rv);
446
if (rv.Failed()) {
447
NS_WARNING("Failed to dispatch event!");
448
}
449
}
450
451
void IDBOpenDBRequest::NoteComplete() {
452
AssertIsOnOwningThread();
453
MOZ_ASSERT_IF(!NS_IsMainThread(), mWorkerRef);
454
455
// Normally, we decrease the number of active IDBOpenRequests here.
456
MaybeDecreaseActiveDatabaseCount();
457
458
// If we have a WorkerRef, then nulling this out will release the worker.
459
mWorkerRef = nullptr;
460
}
461
462
void IDBOpenDBRequest::IncreaseActiveDatabaseCount() {
463
AssertIsOnOwningThread();
464
MOZ_ASSERT(!mIncreasedActiveDatabaseCount);
465
466
// Increase the number of active IDBOpenRequests.
467
// Note: We count here instead of the actor's ctor because the preemption
468
// could happen at next JS interrupt but its BackgroundFactoryRequestChild
469
// could be created asynchronously from IDBFactory::BackgroundCreateCallback
470
// ::ActorCreated() if its PBackgroundChild is not created yet on this thread.
471
mFactory->UpdateActiveDatabaseCount(1);
472
mIncreasedActiveDatabaseCount = true;
473
}
474
475
void IDBOpenDBRequest::MaybeDecreaseActiveDatabaseCount() {
476
AssertIsOnOwningThread();
477
478
if (mIncreasedActiveDatabaseCount) {
479
// Decrease the number of active IDBOpenRequests.
480
mFactory->UpdateActiveDatabaseCount(-1);
481
mIncreasedActiveDatabaseCount = false;
482
}
483
}
484
485
NS_IMPL_CYCLE_COLLECTION_CLASS(IDBOpenDBRequest)
486
487
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBOpenDBRequest, IDBRequest)
488
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFactory)
489
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
490
491
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBOpenDBRequest, IDBRequest)
492
// Don't unlink mFactory!
493
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
494
495
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBOpenDBRequest)
496
NS_INTERFACE_MAP_END_INHERITING(IDBRequest)
497
498
NS_IMPL_ADDREF_INHERITED(IDBOpenDBRequest, IDBRequest)
499
NS_IMPL_RELEASE_INHERITED(IDBOpenDBRequest, IDBRequest)
500
501
nsresult IDBOpenDBRequest::PostHandleEvent(EventChainPostVisitor& aVisitor) {
502
nsresult rv =
503
IndexedDatabaseManager::CommonPostHandleEvent(aVisitor, mFactory);
504
if (NS_WARN_IF(NS_FAILED(rv))) {
505
return rv;
506
}
507
508
return NS_OK;
509
}
510
511
JSObject* IDBOpenDBRequest::WrapObject(JSContext* aCx,
512
JS::Handle<JSObject*> aGivenProto) {
513
AssertIsOnOwningThread();
514
515
return IDBOpenDBRequest_Binding::Wrap(aCx, this, aGivenProto);
516
}
517
518
} // namespace dom
519
} // namespace mozilla