|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set ts=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 "nsIScriptContext.h" |
|
10 |
|
11 #include "mozilla/ContentEvents.h" |
|
12 #include "mozilla/EventDispatcher.h" |
|
13 #include "mozilla/dom/ErrorEventBinding.h" |
|
14 #include "mozilla/dom/IDBOpenDBRequestBinding.h" |
|
15 #include "mozilla/dom/UnionTypes.h" |
|
16 #include "nsComponentManagerUtils.h" |
|
17 #include "nsDOMClassInfoID.h" |
|
18 #include "nsDOMJSUtils.h" |
|
19 #include "nsContentUtils.h" |
|
20 #include "nsCxPusher.h" |
|
21 #include "nsJSUtils.h" |
|
22 #include "nsPIDOMWindow.h" |
|
23 #include "nsString.h" |
|
24 #include "nsThreadUtils.h" |
|
25 #include "nsWrapperCacheInlines.h" |
|
26 |
|
27 #include "AsyncConnectionHelper.h" |
|
28 #include "IDBCursor.h" |
|
29 #include "IDBEvents.h" |
|
30 #include "IDBFactory.h" |
|
31 #include "IDBIndex.h" |
|
32 #include "IDBObjectStore.h" |
|
33 #include "IDBTransaction.h" |
|
34 #include "ReportInternalError.h" |
|
35 |
|
36 namespace { |
|
37 |
|
38 #ifdef MOZ_ENABLE_PROFILER_SPS |
|
39 uint64_t gNextRequestSerialNumber = 1; |
|
40 #endif |
|
41 |
|
42 } // anonymous namespace |
|
43 |
|
44 USING_INDEXEDDB_NAMESPACE |
|
45 using mozilla::dom::OwningIDBObjectStoreOrIDBIndexOrIDBCursor; |
|
46 using mozilla::dom::ErrorEventInit; |
|
47 using namespace mozilla; |
|
48 |
|
49 IDBRequest::IDBRequest(IDBDatabase* aDatabase) |
|
50 : IDBWrapperCache(aDatabase), |
|
51 mResultVal(JSVAL_VOID), |
|
52 mActorParent(nullptr), |
|
53 #ifdef MOZ_ENABLE_PROFILER_SPS |
|
54 mSerialNumber(gNextRequestSerialNumber++), |
|
55 #endif |
|
56 mErrorCode(NS_OK), |
|
57 mLineNo(0), |
|
58 mHaveResultOrErrorCode(false) |
|
59 { |
|
60 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
61 } |
|
62 |
|
63 IDBRequest::IDBRequest(nsPIDOMWindow* aOwner) |
|
64 : IDBWrapperCache(aOwner), |
|
65 mResultVal(JSVAL_VOID), |
|
66 mActorParent(nullptr), |
|
67 #ifdef MOZ_ENABLE_PROFILER_SPS |
|
68 mSerialNumber(gNextRequestSerialNumber++), |
|
69 #endif |
|
70 mErrorCode(NS_OK), |
|
71 mLineNo(0), |
|
72 mHaveResultOrErrorCode(false) |
|
73 { |
|
74 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
75 } |
|
76 |
|
77 IDBRequest::~IDBRequest() |
|
78 { |
|
79 mResultVal = JSVAL_VOID; |
|
80 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
81 } |
|
82 |
|
83 // static |
|
84 already_AddRefed<IDBRequest> |
|
85 IDBRequest::Create(IDBDatabase* aDatabase, |
|
86 IDBTransaction* aTransaction) |
|
87 { |
|
88 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
89 nsRefPtr<IDBRequest> request(new IDBRequest(aDatabase)); |
|
90 |
|
91 request->mTransaction = aTransaction; |
|
92 request->SetScriptOwner(aDatabase->GetScriptOwner()); |
|
93 |
|
94 if (!aDatabase->Factory()->FromIPC()) { |
|
95 request->CaptureCaller(); |
|
96 } |
|
97 |
|
98 |
|
99 return request.forget(); |
|
100 } |
|
101 |
|
102 // static |
|
103 already_AddRefed<IDBRequest> |
|
104 IDBRequest::Create(IDBObjectStore* aSourceAsObjectStore, |
|
105 IDBDatabase* aDatabase, |
|
106 IDBTransaction* aTransaction) |
|
107 { |
|
108 nsRefPtr<IDBRequest> request = Create(aDatabase, aTransaction); |
|
109 |
|
110 request->mSourceAsObjectStore = aSourceAsObjectStore; |
|
111 |
|
112 return request.forget(); |
|
113 } |
|
114 |
|
115 // static |
|
116 already_AddRefed<IDBRequest> |
|
117 IDBRequest::Create(IDBIndex* aSourceAsIndex, |
|
118 IDBDatabase* aDatabase, |
|
119 IDBTransaction* aTransaction) |
|
120 { |
|
121 nsRefPtr<IDBRequest> request = Create(aDatabase, aTransaction); |
|
122 |
|
123 request->mSourceAsIndex = aSourceAsIndex; |
|
124 |
|
125 return request.forget(); |
|
126 } |
|
127 |
|
128 #ifdef DEBUG |
|
129 void |
|
130 IDBRequest::AssertSourceIsCorrect() const |
|
131 { |
|
132 // At most one of mSourceAs* is allowed to be non-null. Check that by |
|
133 // summing the double negation of each one and asserting the sum is at most |
|
134 // 1. |
|
135 |
|
136 MOZ_ASSERT(!!mSourceAsObjectStore + !!mSourceAsIndex + !!mSourceAsCursor <= 1); |
|
137 } |
|
138 #endif |
|
139 |
|
140 void |
|
141 IDBRequest::GetSource(Nullable<OwningIDBObjectStoreOrIDBIndexOrIDBCursor>& aSource) const |
|
142 { |
|
143 MOZ_ASSERT(NS_IsMainThread()); |
|
144 |
|
145 AssertSourceIsCorrect(); |
|
146 |
|
147 if (mSourceAsObjectStore) { |
|
148 aSource.SetValue().SetAsIDBObjectStore() = mSourceAsObjectStore; |
|
149 } else if (mSourceAsIndex) { |
|
150 aSource.SetValue().SetAsIDBIndex() = mSourceAsIndex; |
|
151 } else if (mSourceAsCursor) { |
|
152 aSource.SetValue().SetAsIDBCursor() = mSourceAsCursor; |
|
153 } else { |
|
154 aSource.SetNull(); |
|
155 } |
|
156 } |
|
157 |
|
158 void |
|
159 IDBRequest::Reset() |
|
160 { |
|
161 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
162 mResultVal = JSVAL_VOID; |
|
163 mHaveResultOrErrorCode = false; |
|
164 mError = nullptr; |
|
165 } |
|
166 |
|
167 nsresult |
|
168 IDBRequest::NotifyHelperCompleted(HelperBase* aHelper) |
|
169 { |
|
170 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
171 NS_ASSERTION(!mHaveResultOrErrorCode, "Already called!"); |
|
172 NS_ASSERTION(JSVAL_IS_VOID(mResultVal), "Should be undefined!"); |
|
173 |
|
174 mHaveResultOrErrorCode = true; |
|
175 |
|
176 nsresult rv = aHelper->GetResultCode(); |
|
177 |
|
178 // If the request failed then set the error code and return. |
|
179 if (NS_FAILED(rv)) { |
|
180 SetError(rv); |
|
181 return NS_OK; |
|
182 } |
|
183 |
|
184 // See if our window is still valid. If not then we're going to pretend that |
|
185 // we never completed. |
|
186 if (NS_FAILED(CheckInnerWindowCorrectness())) { |
|
187 return NS_OK; |
|
188 } |
|
189 |
|
190 // Otherwise we need to get the result from the helper. |
|
191 AutoPushJSContext cx(GetJSContext()); |
|
192 if (!cx) { |
|
193 IDB_WARNING("Failed to get safe JSContext!"); |
|
194 rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; |
|
195 SetError(rv); |
|
196 return rv; |
|
197 } |
|
198 |
|
199 JS::Rooted<JSObject*> global(cx, IDBWrapperCache::GetParentObject()); |
|
200 NS_ASSERTION(global, "This should never be null!"); |
|
201 |
|
202 JSAutoCompartment ac(cx, global); |
|
203 AssertIsRooted(); |
|
204 |
|
205 JS::Rooted<JS::Value> value(cx); |
|
206 rv = aHelper->GetSuccessResult(cx, &value); |
|
207 if (NS_FAILED(rv)) { |
|
208 NS_WARNING("GetSuccessResult failed!"); |
|
209 } |
|
210 |
|
211 if (NS_SUCCEEDED(rv)) { |
|
212 mError = nullptr; |
|
213 mResultVal = value; |
|
214 } |
|
215 else { |
|
216 SetError(rv); |
|
217 mResultVal = JSVAL_VOID; |
|
218 } |
|
219 |
|
220 return rv; |
|
221 } |
|
222 |
|
223 void |
|
224 IDBRequest::NotifyHelperSentResultsToChildProcess(nsresult aRv) |
|
225 { |
|
226 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
227 NS_ASSERTION(!mHaveResultOrErrorCode, "Already called!"); |
|
228 NS_ASSERTION(JSVAL_IS_VOID(mResultVal), "Should be undefined!"); |
|
229 |
|
230 // See if our window is still valid. If not then we're going to pretend that |
|
231 // we never completed. |
|
232 if (NS_FAILED(CheckInnerWindowCorrectness())) { |
|
233 return; |
|
234 } |
|
235 |
|
236 mHaveResultOrErrorCode = true; |
|
237 |
|
238 if (NS_FAILED(aRv)) { |
|
239 SetError(aRv); |
|
240 } |
|
241 } |
|
242 |
|
243 void |
|
244 IDBRequest::SetError(nsresult aRv) |
|
245 { |
|
246 NS_ASSERTION(NS_FAILED(aRv), "Er, what?"); |
|
247 NS_ASSERTION(!mError, "Already have an error?"); |
|
248 |
|
249 mHaveResultOrErrorCode = true; |
|
250 mError = new mozilla::dom::DOMError(GetOwner(), aRv); |
|
251 mErrorCode = aRv; |
|
252 |
|
253 mResultVal = JSVAL_VOID; |
|
254 } |
|
255 |
|
256 #ifdef DEBUG |
|
257 nsresult |
|
258 IDBRequest::GetErrorCode() const |
|
259 { |
|
260 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
261 NS_ASSERTION(mHaveResultOrErrorCode, "Don't call me yet!"); |
|
262 return mErrorCode; |
|
263 } |
|
264 #endif |
|
265 |
|
266 JSContext* |
|
267 IDBRequest::GetJSContext() |
|
268 { |
|
269 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
270 |
|
271 JSContext* cx; |
|
272 |
|
273 if (GetScriptOwner()) { |
|
274 return nsContentUtils::GetSafeJSContext(); |
|
275 } |
|
276 |
|
277 nsresult rv; |
|
278 nsIScriptContext* sc = GetContextForEventHandlers(&rv); |
|
279 NS_ENSURE_SUCCESS(rv, nullptr); |
|
280 NS_ENSURE_TRUE(sc, nullptr); |
|
281 |
|
282 cx = sc->GetNativeContext(); |
|
283 NS_ASSERTION(cx, "Failed to get a context!"); |
|
284 |
|
285 return cx; |
|
286 } |
|
287 |
|
288 void |
|
289 IDBRequest::CaptureCaller() |
|
290 { |
|
291 AutoJSContext cx; |
|
292 |
|
293 const char* filename = nullptr; |
|
294 uint32_t lineNo = 0; |
|
295 if (!nsJSUtils::GetCallingLocation(cx, &filename, &lineNo)) { |
|
296 NS_WARNING("Failed to get caller."); |
|
297 return; |
|
298 } |
|
299 |
|
300 mFilename.Assign(NS_ConvertUTF8toUTF16(filename)); |
|
301 mLineNo = lineNo; |
|
302 } |
|
303 |
|
304 void |
|
305 IDBRequest::FillScriptErrorEvent(ErrorEventInit& aEventInit) const |
|
306 { |
|
307 aEventInit.mLineno = mLineNo; |
|
308 aEventInit.mFilename = mFilename; |
|
309 } |
|
310 |
|
311 mozilla::dom::IDBRequestReadyState |
|
312 IDBRequest::ReadyState() const |
|
313 { |
|
314 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
315 |
|
316 if (IsPending()) { |
|
317 return IDBRequestReadyState::Pending; |
|
318 } |
|
319 |
|
320 return IDBRequestReadyState::Done; |
|
321 } |
|
322 |
|
323 JSObject* |
|
324 IDBRequest::WrapObject(JSContext* aCx) |
|
325 { |
|
326 return IDBRequestBinding::Wrap(aCx, this); |
|
327 } |
|
328 |
|
329 void |
|
330 IDBRequest::GetResult(JSContext* aCx, JS::MutableHandle<JS::Value> aResult, |
|
331 ErrorResult& aRv) const |
|
332 { |
|
333 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
334 |
|
335 if (!mHaveResultOrErrorCode) { |
|
336 // XXX Need a real error code here. |
|
337 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); |
|
338 } |
|
339 |
|
340 JS::ExposeValueToActiveJS(mResultVal); |
|
341 aResult.set(mResultVal); |
|
342 } |
|
343 |
|
344 mozilla::dom::DOMError* |
|
345 IDBRequest::GetError(mozilla::ErrorResult& aRv) |
|
346 { |
|
347 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
348 |
|
349 if (!mHaveResultOrErrorCode) { |
|
350 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); |
|
351 return nullptr; |
|
352 } |
|
353 |
|
354 return mError; |
|
355 } |
|
356 |
|
357 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBRequest) |
|
358 |
|
359 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBRequest, IDBWrapperCache) |
|
360 // Don't need NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS because |
|
361 // DOMEventTargetHelper does it for us. |
|
362 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceAsObjectStore) |
|
363 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceAsIndex) |
|
364 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceAsCursor) |
|
365 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction) |
|
366 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError) |
|
367 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
368 |
|
369 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBRequest, IDBWrapperCache) |
|
370 tmp->mResultVal = JSVAL_VOID; |
|
371 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceAsObjectStore) |
|
372 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceAsIndex) |
|
373 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceAsCursor) |
|
374 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTransaction) |
|
375 NS_IMPL_CYCLE_COLLECTION_UNLINK(mError) |
|
376 NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
|
377 |
|
378 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(IDBRequest, IDBWrapperCache) |
|
379 // Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because |
|
380 // DOMEventTargetHelper does it for us. |
|
381 NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mResultVal) |
|
382 NS_IMPL_CYCLE_COLLECTION_TRACE_END |
|
383 |
|
384 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBRequest) |
|
385 NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache) |
|
386 |
|
387 NS_IMPL_ADDREF_INHERITED(IDBRequest, IDBWrapperCache) |
|
388 NS_IMPL_RELEASE_INHERITED(IDBRequest, IDBWrapperCache) |
|
389 |
|
390 nsresult |
|
391 IDBRequest::PreHandleEvent(EventChainPreVisitor& aVisitor) |
|
392 { |
|
393 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
394 |
|
395 aVisitor.mCanHandle = true; |
|
396 aVisitor.mParentTarget = mTransaction; |
|
397 return NS_OK; |
|
398 } |
|
399 |
|
400 IDBOpenDBRequest::IDBOpenDBRequest(nsPIDOMWindow* aOwner) |
|
401 : IDBRequest(aOwner) |
|
402 { |
|
403 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
404 } |
|
405 |
|
406 IDBOpenDBRequest::~IDBOpenDBRequest() |
|
407 { |
|
408 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
409 } |
|
410 |
|
411 // static |
|
412 already_AddRefed<IDBOpenDBRequest> |
|
413 IDBOpenDBRequest::Create(IDBFactory* aFactory, |
|
414 nsPIDOMWindow* aOwner, |
|
415 JS::Handle<JSObject*> aScriptOwner) |
|
416 { |
|
417 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
418 NS_ASSERTION(aFactory, "Null pointer!"); |
|
419 |
|
420 nsRefPtr<IDBOpenDBRequest> request = new IDBOpenDBRequest(aOwner); |
|
421 |
|
422 request->SetScriptOwner(aScriptOwner); |
|
423 request->mFactory = aFactory; |
|
424 |
|
425 if (!aFactory->FromIPC()) { |
|
426 request->CaptureCaller(); |
|
427 } |
|
428 |
|
429 return request.forget(); |
|
430 } |
|
431 |
|
432 void |
|
433 IDBOpenDBRequest::SetTransaction(IDBTransaction* aTransaction) |
|
434 { |
|
435 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
436 |
|
437 NS_ASSERTION(!aTransaction || !mTransaction, |
|
438 "Shouldn't have a transaction here!"); |
|
439 |
|
440 mTransaction = aTransaction; |
|
441 } |
|
442 |
|
443 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBOpenDBRequest) |
|
444 |
|
445 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBOpenDBRequest, |
|
446 IDBRequest) |
|
447 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFactory) |
|
448 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
449 |
|
450 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBOpenDBRequest, |
|
451 IDBRequest) |
|
452 // Don't unlink mFactory! |
|
453 NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
|
454 |
|
455 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBOpenDBRequest) |
|
456 NS_INTERFACE_MAP_END_INHERITING(IDBRequest) |
|
457 |
|
458 NS_IMPL_ADDREF_INHERITED(IDBOpenDBRequest, IDBRequest) |
|
459 NS_IMPL_RELEASE_INHERITED(IDBOpenDBRequest, IDBRequest) |
|
460 |
|
461 nsresult |
|
462 IDBOpenDBRequest::PostHandleEvent(EventChainPostVisitor& aVisitor) |
|
463 { |
|
464 return IndexedDatabaseManager::FireWindowOnError(GetOwner(), aVisitor); |
|
465 } |
|
466 |
|
467 JSObject* |
|
468 IDBOpenDBRequest::WrapObject(JSContext* aCx) |
|
469 { |
|
470 return IDBOpenDBRequestBinding::Wrap(aCx, this); |
|
471 } |