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 #ifndef mozilla_dom_idbrequest_h__
8 #define mozilla_dom_idbrequest_h__
9 
10 #include "js/RootingAPI.h"
11 #include "mozilla/Attributes.h"
12 #include "mozilla/EventForwards.h"
13 #include "mozilla/dom/IDBRequestBinding.h"
14 #include "mozilla/dom/ScriptSettings.h"
15 #include "mozilla/DOMEventTargetHelper.h"
16 #include "mozilla/HoldDropJSObjects.h"
17 #include "nsCycleCollectionParticipant.h"
18 #include "ReportInternalError.h"
19 #include "SafeRefPtr.h"
20 
21 #define PRIVATE_IDBREQUEST_IID                       \
22   {                                                  \
23     0xe68901e5, 0x1d50, 0x4ee9, {                    \
24       0xaf, 0x49, 0x90, 0x99, 0x4a, 0xff, 0xc8, 0x39 \
25     }                                                \
26   }
27 
28 class nsIGlobalObject;
29 
30 namespace mozilla {
31 
32 class ErrorResult;
33 
34 namespace dom {
35 
36 class DOMException;
37 class IDBCursor;
38 class IDBDatabase;
39 class IDBFactory;
40 class IDBIndex;
41 class IDBObjectStore;
42 class IDBTransaction;
43 template <typename>
44 struct Nullable;
45 class OwningIDBObjectStoreOrIDBIndexOrIDBCursor;
46 class StrongWorkerRef;
47 
48 class IDBRequest : public DOMEventTargetHelper {
49  protected:
50   // mSourceAsObjectStore and mSourceAsIndex are exclusive and one must always
51   // be set. mSourceAsCursor is sometimes set also.
52   RefPtr<IDBObjectStore> mSourceAsObjectStore;
53   RefPtr<IDBIndex> mSourceAsIndex;
54   RefPtr<IDBCursor> mSourceAsCursor;
55 
56   SafeRefPtr<IDBTransaction> mTransaction;
57 
58   JS::Heap<JS::Value> mResultVal;
59   RefPtr<DOMException> mError;
60 
61   nsString mFilename;
62   uint64_t mLoggingSerialNumber;
63   nsresult mErrorCode;
64   uint32_t mLineNo;
65   uint32_t mColumn;
66   bool mHaveResultOrErrorCode;
67 
68  public:
69   [[nodiscard]] static MovingNotNull<RefPtr<IDBRequest>> Create(
70       JSContext* aCx, IDBDatabase* aDatabase,
71       SafeRefPtr<IDBTransaction> aTransaction);
72 
73   [[nodiscard]] static MovingNotNull<RefPtr<IDBRequest>> Create(
74       JSContext* aCx, IDBObjectStore* aSource, IDBDatabase* aDatabase,
75       SafeRefPtr<IDBTransaction> aTransaction);
76 
77   [[nodiscard]] static MovingNotNull<RefPtr<IDBRequest>> Create(
78       JSContext* aCx, IDBIndex* aSource, IDBDatabase* aDatabase,
79       SafeRefPtr<IDBTransaction> aTransaction);
80 
81   static void CaptureCaller(JSContext* aCx, nsAString& aFilename,
82                             uint32_t* aLineNo, uint32_t* aColumn);
83 
84   static uint64_t NextSerialNumber();
85 
86   // EventTarget
87   void GetEventTargetParent(EventChainPreVisitor& aVisitor) override;
88 
89   void GetSource(
90       Nullable<OwningIDBObjectStoreOrIDBIndexOrIDBCursor>& aSource) const;
91 
92   void Reset();
93 
94   template <typename ResultCallback>
SetResult(const ResultCallback & aCallback)95   void SetResult(const ResultCallback& aCallback) {
96     AssertIsOnOwningThread();
97     MOZ_ASSERT(!mHaveResultOrErrorCode);
98     MOZ_ASSERT(mResultVal.isUndefined());
99     MOZ_ASSERT(!mError);
100 
101     // Already disconnected from the owner.
102     if (!GetOwnerGlobal()) {
103       SetError(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
104       return;
105     }
106 
107     // See this global is still valid.
108     if (NS_WARN_IF(NS_FAILED(CheckCurrentGlobalCorrectness()))) {
109       SetError(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
110       return;
111     }
112 
113     AutoJSAPI autoJS;
114     if (!autoJS.Init(GetOwnerGlobal())) {
115       IDB_WARNING("Failed to initialize AutoJSAPI!");
116       SetError(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
117       return;
118     }
119 
120     JSContext* cx = autoJS.cx();
121 
122     JS::Rooted<JS::Value> result(cx);
123     nsresult rv = aCallback(cx, &result);
124     if (NS_WARN_IF(NS_FAILED(rv))) {
125       // This can only fail if the structured clone contains a mutable file
126       // and the child is not in the main thread and main process.
127       // In that case CreateAndWrapMutableFile() returns false which shows up
128       // as NS_ERROR_DOM_DATA_CLONE_ERR here.
129       MOZ_ASSERT(rv == NS_ERROR_DOM_DATA_CLONE_ERR);
130 
131       // We are not setting a result or an error object here since we want to
132       // throw an exception when the 'result' property is being touched.
133       return;
134     }
135 
136     mError = nullptr;
137 
138     mResultVal = result;
139     mozilla::HoldJSObjects(this);
140 
141     mHaveResultOrErrorCode = true;
142   }
143 
144   void SetError(nsresult aRv);
145 
146   nsresult GetErrorCode() const
147 #ifdef DEBUG
148       ;
149 #else
150   {
151     return mErrorCode;
152   }
153 #endif
154 
155   DOMException* GetErrorAfterResult() const
156 #ifdef DEBUG
157       ;
158 #else
159   {
160     return mError;
161   }
162 #endif
163 
164   DOMException* GetError(ErrorResult& aRv);
165 
166   void GetCallerLocation(nsAString& aFilename, uint32_t* aLineNo,
167                          uint32_t* aColumn) const;
168 
IsPending()169   bool IsPending() const { return !mHaveResultOrErrorCode; }
170 
LoggingSerialNumber()171   uint64_t LoggingSerialNumber() const {
172     AssertIsOnOwningThread();
173 
174     return mLoggingSerialNumber;
175   }
176 
177   void SetLoggingSerialNumber(uint64_t aLoggingSerialNumber);
178 
GetParentObject()179   nsIGlobalObject* GetParentObject() const { return GetOwnerGlobal(); }
180 
181   void GetResult(JS::MutableHandle<JS::Value> aResult, ErrorResult& aRv) const;
182 
GetResult(JSContext * aCx,JS::MutableHandle<JS::Value> aResult,ErrorResult & aRv)183   void GetResult(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
184                  ErrorResult& aRv) const {
185     GetResult(aResult, aRv);
186   }
187 
MaybeTransactionRef()188   Maybe<IDBTransaction&> MaybeTransactionRef() const {
189     AssertIsOnOwningThread();
190 
191     return mTransaction.maybeDeref();
192   }
193 
MutableTransactionRef()194   IDBTransaction& MutableTransactionRef() const {
195     AssertIsOnOwningThread();
196 
197     return *mTransaction;
198   }
199 
AcquireTransaction()200   SafeRefPtr<IDBTransaction> AcquireTransaction() const {
201     AssertIsOnOwningThread();
202 
203     return mTransaction.clonePtr();
204   }
205 
206   // For WebIDL binding.
GetTransaction()207   RefPtr<IDBTransaction> GetTransaction() const {
208     AssertIsOnOwningThread();
209 
210     return AsRefPtr(mTransaction.clonePtr());
211   }
212 
213   IDBRequestReadyState ReadyState() const;
214 
215   void SetSource(IDBCursor* aSource);
216 
217   IMPL_EVENT_HANDLER(success);
218   IMPL_EVENT_HANDLER(error);
219 
AssertIsOnOwningThread()220   void AssertIsOnOwningThread() const { NS_ASSERT_OWNINGTHREAD(IDBRequest); }
221 
222   NS_DECL_ISUPPORTS_INHERITED
223   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(IDBRequest,
224                                                          DOMEventTargetHelper)
225 
226   // nsWrapperCache
227   virtual JSObject* WrapObject(JSContext* aCx,
228                                JS::Handle<JSObject*> aGivenProto) override;
229 
230  protected:
231   explicit IDBRequest(IDBDatabase* aDatabase);
232   explicit IDBRequest(nsIGlobalObject* aGlobal);
233   ~IDBRequest();
234 
235   void InitMembers();
236 
237   void ConstructResult();
238 };
239 
240 class IDBOpenDBRequest final : public IDBRequest {
241   // Only touched on the owning thread.
242   SafeRefPtr<IDBFactory> mFactory;
243 
244   RefPtr<StrongWorkerRef> mWorkerRef;
245 
246   const bool mFileHandleDisabled;
247   bool mIncreasedActiveDatabaseCount;
248 
249  public:
250   [[nodiscard]] static RefPtr<IDBOpenDBRequest> Create(
251       JSContext* aCx, SafeRefPtr<IDBFactory> aFactory,
252       nsIGlobalObject* aGlobal);
253 
IsFileHandleDisabled()254   bool IsFileHandleDisabled() const { return mFileHandleDisabled; }
255 
256   void SetTransaction(SafeRefPtr<IDBTransaction> aTransaction);
257 
258   void DispatchNonTransactionError(nsresult aErrorCode);
259 
260   void NoteComplete();
261 
262   // EventTarget
263   virtual nsresult PostHandleEvent(EventChainPostVisitor& aVisitor) override;
264 
265   IMPL_EVENT_HANDLER(blocked);
266   IMPL_EVENT_HANDLER(upgradeneeded);
267 
268   NS_DECL_ISUPPORTS_INHERITED
269   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBOpenDBRequest, IDBRequest)
270 
271   // nsWrapperCache
272   virtual JSObject* WrapObject(JSContext* aCx,
273                                JS::Handle<JSObject*> aGivenProto) override;
274 
275  private:
276   IDBOpenDBRequest(SafeRefPtr<IDBFactory> aFactory, nsIGlobalObject* aGlobal,
277                    bool aFileHandleDisabled);
278 
279   ~IDBOpenDBRequest();
280 
281   void IncreaseActiveDatabaseCount();
282 
283   void MaybeDecreaseActiveDatabaseCount();
284 };
285 
286 }  // namespace dom
287 }  // namespace mozilla
288 
289 #endif  // mozilla_dom_idbrequest_h__
290