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