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