dom/indexedDB/IDBCursor.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "base/basictypes.h"
michael@0 8
michael@0 9 #include "IDBCursor.h"
michael@0 10
michael@0 11 #include "mozilla/storage.h"
michael@0 12 #include "nsComponentManagerUtils.h"
michael@0 13 #include "nsJSUtils.h"
michael@0 14 #include "nsThreadUtils.h"
michael@0 15
michael@0 16 #include "AsyncConnectionHelper.h"
michael@0 17 #include "IDBEvents.h"
michael@0 18 #include "IDBIndex.h"
michael@0 19 #include "IDBObjectStore.h"
michael@0 20 #include "IDBTransaction.h"
michael@0 21 #include "ProfilerHelpers.h"
michael@0 22 #include "ReportInternalError.h"
michael@0 23 #include "TransactionThreadPool.h"
michael@0 24
michael@0 25 #include "ipc/IndexedDBChild.h"
michael@0 26 #include "ipc/IndexedDBParent.h"
michael@0 27
michael@0 28 #include "IndexedDatabaseInlines.h"
michael@0 29 #include "mozilla/dom/BindingDeclarations.h"
michael@0 30 #include "mozilla/dom/UnionTypes.h"
michael@0 31
michael@0 32 USING_INDEXEDDB_NAMESPACE
michael@0 33 using namespace mozilla::dom::indexedDB::ipc;
michael@0 34 using mozilla::dom::Optional;
michael@0 35 using mozilla::dom::OwningIDBObjectStoreOrIDBIndex;
michael@0 36 using mozilla::ErrorResult;
michael@0 37
michael@0 38 static_assert(sizeof(size_t) >= sizeof(IDBCursor::Direction),
michael@0 39 "Relying on conversion between size_t and IDBCursor::Direction");
michael@0 40
michael@0 41 namespace {
michael@0 42
michael@0 43 class CursorHelper : public AsyncConnectionHelper
michael@0 44 {
michael@0 45 public:
michael@0 46 CursorHelper(IDBCursor* aCursor)
michael@0 47 : AsyncConnectionHelper(aCursor->Transaction(), aCursor->Request()),
michael@0 48 mCursor(aCursor), mActor(nullptr)
michael@0 49 {
michael@0 50 NS_ASSERTION(aCursor, "Null cursor!");
michael@0 51 }
michael@0 52
michael@0 53 virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE;
michael@0 54
michael@0 55 virtual nsresult Dispatch(nsIEventTarget* aDatabaseThread) MOZ_OVERRIDE;
michael@0 56
michael@0 57 virtual nsresult
michael@0 58 PackArgumentsForParentProcess(CursorRequestParams& aParams) = 0;
michael@0 59
michael@0 60 virtual nsresult
michael@0 61 UnpackResponseFromParentProcess(const ResponseValue& aResponseValue) = 0;
michael@0 62
michael@0 63 protected:
michael@0 64 virtual ~CursorHelper()
michael@0 65 { }
michael@0 66
michael@0 67 nsRefPtr<IDBCursor> mCursor;
michael@0 68
michael@0 69 private:
michael@0 70 IndexedDBCursorRequestChild* mActor;
michael@0 71 };
michael@0 72
michael@0 73 } // anonymous namespace
michael@0 74
michael@0 75 BEGIN_INDEXEDDB_NAMESPACE
michael@0 76
michael@0 77 class ContinueHelper : public CursorHelper
michael@0 78 {
michael@0 79 public:
michael@0 80 ContinueHelper(IDBCursor* aCursor,
michael@0 81 int32_t aCount)
michael@0 82 : CursorHelper(aCursor), mCount(aCount)
michael@0 83 {
michael@0 84 MOZ_ASSERT(NS_IsMainThread());
michael@0 85 MOZ_ASSERT(aCursor);
michael@0 86 MOZ_ASSERT(aCount > 0);
michael@0 87 }
michael@0 88
michael@0 89 virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection)
michael@0 90 MOZ_OVERRIDE;
michael@0 91
michael@0 92 virtual nsresult GetSuccessResult(JSContext* aCx,
michael@0 93 JS::MutableHandle<JS::Value> aVal)
michael@0 94 MOZ_OVERRIDE;
michael@0 95
michael@0 96 virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE;
michael@0 97
michael@0 98 virtual nsresult
michael@0 99 PackArgumentsForParentProcess(CursorRequestParams& aParams) MOZ_OVERRIDE;
michael@0 100
michael@0 101 virtual ChildProcessSendResult
michael@0 102 SendResponseToChildProcess(nsresult aResultCode) MOZ_OVERRIDE;
michael@0 103
michael@0 104 virtual nsresult
michael@0 105 UnpackResponseFromParentProcess(const ResponseValue& aResponseValue)
michael@0 106 MOZ_OVERRIDE;
michael@0 107
michael@0 108 protected:
michael@0 109 virtual ~ContinueHelper()
michael@0 110 {
michael@0 111 IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo);
michael@0 112 }
michael@0 113
michael@0 114 virtual nsresult
michael@0 115 BindArgumentsToStatement(mozIStorageStatement* aStatement) = 0;
michael@0 116
michael@0 117 virtual nsresult
michael@0 118 GatherResultsFromStatement(mozIStorageStatement* aStatement) = 0;
michael@0 119
michael@0 120 void UpdateCursorState()
michael@0 121 {
michael@0 122 mCursor->mCachedKey = JSVAL_VOID;
michael@0 123 mCursor->mCachedPrimaryKey = JSVAL_VOID;
michael@0 124 mCursor->mCachedValue = JSVAL_VOID;
michael@0 125 mCursor->mHaveCachedKey = false;
michael@0 126 mCursor->mHaveCachedPrimaryKey = false;
michael@0 127 mCursor->mHaveCachedValue = false;
michael@0 128 mCursor->mContinueCalled = false;
michael@0 129
michael@0 130 if (mKey.IsUnset()) {
michael@0 131 mCursor->mHaveValue = false;
michael@0 132 } else {
michael@0 133 MOZ_ASSERT(mCursor->mType == IDBCursor::OBJECTSTORE ||
michael@0 134 mCursor->mType == IDBCursor::OBJECTSTOREKEY ||
michael@0 135 !mObjectKey.IsUnset());
michael@0 136
michael@0 137 // Set new values.
michael@0 138 mCursor->mKey = mKey;
michael@0 139 mCursor->mObjectKey = mObjectKey;
michael@0 140 mCursor->mContinueToKey.Unset();
michael@0 141
michael@0 142 mCursor->mCloneReadInfo = Move(mCloneReadInfo);
michael@0 143 mCloneReadInfo.mCloneBuffer.clear();
michael@0 144 }
michael@0 145 }
michael@0 146
michael@0 147 int32_t mCount;
michael@0 148 Key mKey;
michael@0 149 Key mObjectKey;
michael@0 150 StructuredCloneReadInfo mCloneReadInfo;
michael@0 151 };
michael@0 152
michael@0 153 class ContinueObjectStoreHelper : public ContinueHelper
michael@0 154 {
michael@0 155 public:
michael@0 156 ContinueObjectStoreHelper(IDBCursor* aCursor,
michael@0 157 uint32_t aCount)
michael@0 158 : ContinueHelper(aCursor, aCount)
michael@0 159 { }
michael@0 160
michael@0 161 protected:
michael@0 162 virtual ~ContinueObjectStoreHelper()
michael@0 163 { }
michael@0 164
michael@0 165 private:
michael@0 166 nsresult BindArgumentsToStatement(mozIStorageStatement* aStatement);
michael@0 167 nsresult GatherResultsFromStatement(mozIStorageStatement* aStatement);
michael@0 168 };
michael@0 169
michael@0 170 class ContinueObjectStoreKeyHelper : public ContinueObjectStoreHelper
michael@0 171 {
michael@0 172 public:
michael@0 173 ContinueObjectStoreKeyHelper(IDBCursor* aCursor,
michael@0 174 uint32_t aCount)
michael@0 175 : ContinueObjectStoreHelper(aCursor, aCount)
michael@0 176 { }
michael@0 177
michael@0 178 private:
michael@0 179 virtual ~ContinueObjectStoreKeyHelper()
michael@0 180 { }
michael@0 181
michael@0 182 virtual nsresult
michael@0 183 GatherResultsFromStatement(mozIStorageStatement* aStatement) MOZ_OVERRIDE;
michael@0 184 };
michael@0 185
michael@0 186 class ContinueIndexHelper : public ContinueHelper
michael@0 187 {
michael@0 188 public:
michael@0 189 ContinueIndexHelper(IDBCursor* aCursor,
michael@0 190 uint32_t aCount)
michael@0 191 : ContinueHelper(aCursor, aCount)
michael@0 192 { }
michael@0 193
michael@0 194 protected:
michael@0 195 virtual ~ContinueIndexHelper()
michael@0 196 { }
michael@0 197
michael@0 198 private:
michael@0 199 nsresult BindArgumentsToStatement(mozIStorageStatement* aStatement);
michael@0 200 nsresult GatherResultsFromStatement(mozIStorageStatement* aStatement);
michael@0 201 };
michael@0 202
michael@0 203 class ContinueIndexObjectHelper : public ContinueIndexHelper
michael@0 204 {
michael@0 205 public:
michael@0 206 ContinueIndexObjectHelper(IDBCursor* aCursor,
michael@0 207 uint32_t aCount)
michael@0 208 : ContinueIndexHelper(aCursor, aCount)
michael@0 209 { }
michael@0 210
michael@0 211 private:
michael@0 212 virtual ~ContinueIndexObjectHelper()
michael@0 213 { }
michael@0 214
michael@0 215 nsresult GatherResultsFromStatement(mozIStorageStatement* aStatement);
michael@0 216 };
michael@0 217
michael@0 218 END_INDEXEDDB_NAMESPACE
michael@0 219
michael@0 220 // static
michael@0 221 already_AddRefed<IDBCursor>
michael@0 222 IDBCursor::Create(IDBRequest* aRequest,
michael@0 223 IDBTransaction* aTransaction,
michael@0 224 IDBObjectStore* aObjectStore,
michael@0 225 Direction aDirection,
michael@0 226 const Key& aRangeKey,
michael@0 227 const nsACString& aContinueQuery,
michael@0 228 const nsACString& aContinueToQuery,
michael@0 229 const Key& aKey,
michael@0 230 StructuredCloneReadInfo&& aCloneReadInfo)
michael@0 231 {
michael@0 232 NS_ASSERTION(aObjectStore, "Null pointer!");
michael@0 233 NS_ASSERTION(!aKey.IsUnset(), "Bad key!");
michael@0 234
michael@0 235 nsRefPtr<IDBCursor> cursor =
michael@0 236 IDBCursor::CreateCommon(aRequest, aTransaction, aObjectStore, aDirection,
michael@0 237 aRangeKey, aContinueQuery, aContinueToQuery);
michael@0 238 NS_ASSERTION(cursor, "This shouldn't fail!");
michael@0 239
michael@0 240 cursor->mObjectStore = aObjectStore;
michael@0 241 cursor->mType = OBJECTSTORE;
michael@0 242 cursor->mKey = aKey;
michael@0 243 cursor->mCloneReadInfo = Move(aCloneReadInfo);
michael@0 244
michael@0 245 return cursor.forget();
michael@0 246 }
michael@0 247
michael@0 248 // static
michael@0 249 already_AddRefed<IDBCursor>
michael@0 250 IDBCursor::Create(IDBRequest* aRequest,
michael@0 251 IDBTransaction* aTransaction,
michael@0 252 IDBObjectStore* aObjectStore,
michael@0 253 Direction aDirection,
michael@0 254 const Key& aRangeKey,
michael@0 255 const nsACString& aContinueQuery,
michael@0 256 const nsACString& aContinueToQuery,
michael@0 257 const Key& aKey)
michael@0 258 {
michael@0 259 MOZ_ASSERT(aObjectStore);
michael@0 260 MOZ_ASSERT(!aKey.IsUnset());
michael@0 261
michael@0 262 nsRefPtr<IDBCursor> cursor =
michael@0 263 IDBCursor::CreateCommon(aRequest, aTransaction, aObjectStore, aDirection,
michael@0 264 aRangeKey, aContinueQuery, aContinueToQuery);
michael@0 265 NS_ASSERTION(cursor, "This shouldn't fail!");
michael@0 266
michael@0 267 cursor->mObjectStore = aObjectStore;
michael@0 268 cursor->mType = OBJECTSTOREKEY;
michael@0 269 cursor->mKey = aKey;
michael@0 270
michael@0 271 return cursor.forget();
michael@0 272 }
michael@0 273
michael@0 274 // static
michael@0 275 already_AddRefed<IDBCursor>
michael@0 276 IDBCursor::Create(IDBRequest* aRequest,
michael@0 277 IDBTransaction* aTransaction,
michael@0 278 IDBIndex* aIndex,
michael@0 279 Direction aDirection,
michael@0 280 const Key& aRangeKey,
michael@0 281 const nsACString& aContinueQuery,
michael@0 282 const nsACString& aContinueToQuery,
michael@0 283 const Key& aKey,
michael@0 284 const Key& aObjectKey)
michael@0 285 {
michael@0 286 NS_ASSERTION(aIndex, "Null pointer!");
michael@0 287 NS_ASSERTION(!aKey.IsUnset(), "Bad key!");
michael@0 288 NS_ASSERTION(!aObjectKey.IsUnset(), "Bad key!");
michael@0 289
michael@0 290 nsRefPtr<IDBCursor> cursor =
michael@0 291 IDBCursor::CreateCommon(aRequest, aTransaction, aIndex->ObjectStore(),
michael@0 292 aDirection, aRangeKey, aContinueQuery,
michael@0 293 aContinueToQuery);
michael@0 294 NS_ASSERTION(cursor, "This shouldn't fail!");
michael@0 295
michael@0 296 cursor->mIndex = aIndex;
michael@0 297 cursor->mType = INDEXKEY;
michael@0 298 cursor->mKey = aKey,
michael@0 299 cursor->mObjectKey = aObjectKey;
michael@0 300
michael@0 301 return cursor.forget();
michael@0 302 }
michael@0 303
michael@0 304 // static
michael@0 305 already_AddRefed<IDBCursor>
michael@0 306 IDBCursor::Create(IDBRequest* aRequest,
michael@0 307 IDBTransaction* aTransaction,
michael@0 308 IDBIndex* aIndex,
michael@0 309 Direction aDirection,
michael@0 310 const Key& aRangeKey,
michael@0 311 const nsACString& aContinueQuery,
michael@0 312 const nsACString& aContinueToQuery,
michael@0 313 const Key& aKey,
michael@0 314 const Key& aObjectKey,
michael@0 315 StructuredCloneReadInfo&& aCloneReadInfo)
michael@0 316 {
michael@0 317 NS_ASSERTION(aIndex, "Null pointer!");
michael@0 318 NS_ASSERTION(!aKey.IsUnset(), "Bad key!");
michael@0 319
michael@0 320 nsRefPtr<IDBCursor> cursor =
michael@0 321 IDBCursor::CreateCommon(aRequest, aTransaction, aIndex->ObjectStore(),
michael@0 322 aDirection, aRangeKey, aContinueQuery,
michael@0 323 aContinueToQuery);
michael@0 324 NS_ASSERTION(cursor, "This shouldn't fail!");
michael@0 325
michael@0 326 cursor->mObjectStore = aIndex->ObjectStore();
michael@0 327 cursor->mIndex = aIndex;
michael@0 328 cursor->mType = INDEXOBJECT;
michael@0 329 cursor->mKey = aKey;
michael@0 330 cursor->mObjectKey = aObjectKey;
michael@0 331 cursor->mCloneReadInfo = Move(aCloneReadInfo);
michael@0 332
michael@0 333 return cursor.forget();
michael@0 334 }
michael@0 335
michael@0 336 // static
michael@0 337 IDBCursor::Direction
michael@0 338 IDBCursor::ConvertDirection(mozilla::dom::IDBCursorDirection aDirection)
michael@0 339 {
michael@0 340 switch (aDirection) {
michael@0 341 case mozilla::dom::IDBCursorDirection::Next:
michael@0 342 return NEXT;
michael@0 343
michael@0 344 case mozilla::dom::IDBCursorDirection::Nextunique:
michael@0 345 return NEXT_UNIQUE;
michael@0 346
michael@0 347 case mozilla::dom::IDBCursorDirection::Prev:
michael@0 348 return PREV;
michael@0 349
michael@0 350 case mozilla::dom::IDBCursorDirection::Prevunique:
michael@0 351 return PREV_UNIQUE;
michael@0 352
michael@0 353 default:
michael@0 354 MOZ_ASSUME_UNREACHABLE("Unknown direction!");
michael@0 355 }
michael@0 356 }
michael@0 357
michael@0 358 // static
michael@0 359 already_AddRefed<IDBCursor>
michael@0 360 IDBCursor::CreateCommon(IDBRequest* aRequest,
michael@0 361 IDBTransaction* aTransaction,
michael@0 362 IDBObjectStore* aObjectStore,
michael@0 363 Direction aDirection,
michael@0 364 const Key& aRangeKey,
michael@0 365 const nsACString& aContinueQuery,
michael@0 366 const nsACString& aContinueToQuery)
michael@0 367 {
michael@0 368 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 369 NS_ASSERTION(aRequest, "Null pointer!");
michael@0 370 NS_ASSERTION(aTransaction, "Null pointer!");
michael@0 371 NS_ASSERTION(aObjectStore, "Null pointer!");
michael@0 372 NS_ASSERTION(!aContinueQuery.IsEmpty() ||
michael@0 373 !IndexedDatabaseManager::IsMainProcess(),
michael@0 374 "Empty query!");
michael@0 375 NS_ASSERTION(!aContinueToQuery.IsEmpty() ||
michael@0 376 !IndexedDatabaseManager::IsMainProcess(),
michael@0 377 "Empty query!");
michael@0 378
michael@0 379 nsRefPtr<IDBCursor> cursor = new IDBCursor();
michael@0 380
michael@0 381 IDBDatabase* database = aTransaction->Database();
michael@0 382 cursor->mScriptOwner = database->GetScriptOwner();
michael@0 383
michael@0 384 if (cursor->mScriptOwner) {
michael@0 385 mozilla::HoldJSObjects(cursor.get());
michael@0 386 cursor->mRooted = true;
michael@0 387 }
michael@0 388
michael@0 389 cursor->mRequest = aRequest;
michael@0 390 cursor->mTransaction = aTransaction;
michael@0 391 cursor->mObjectStore = aObjectStore;
michael@0 392 cursor->mDirection = aDirection;
michael@0 393 cursor->mContinueQuery = aContinueQuery;
michael@0 394 cursor->mContinueToQuery = aContinueToQuery;
michael@0 395 cursor->mRangeKey = aRangeKey;
michael@0 396
michael@0 397 return cursor.forget();
michael@0 398 }
michael@0 399
michael@0 400 IDBCursor::IDBCursor()
michael@0 401 : mScriptOwner(nullptr),
michael@0 402 mType(OBJECTSTORE),
michael@0 403 mDirection(IDBCursor::NEXT),
michael@0 404 mCachedKey(JSVAL_VOID),
michael@0 405 mCachedPrimaryKey(JSVAL_VOID),
michael@0 406 mCachedValue(JSVAL_VOID),
michael@0 407 mActorChild(nullptr),
michael@0 408 mActorParent(nullptr),
michael@0 409 mHaveCachedKey(false),
michael@0 410 mHaveCachedPrimaryKey(false),
michael@0 411 mHaveCachedValue(false),
michael@0 412 mRooted(false),
michael@0 413 mContinueCalled(false),
michael@0 414 mHaveValue(true)
michael@0 415 {
michael@0 416 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 417
michael@0 418 SetIsDOMBinding();
michael@0 419 }
michael@0 420
michael@0 421 IDBCursor::~IDBCursor()
michael@0 422 {
michael@0 423 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 424
michael@0 425 NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!");
michael@0 426 if (mActorChild) {
michael@0 427 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
michael@0 428 mActorChild->Send__delete__(mActorChild);
michael@0 429 NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!");
michael@0 430 }
michael@0 431
michael@0 432 DropJSObjects();
michael@0 433 IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo);
michael@0 434 }
michael@0 435
michael@0 436 void
michael@0 437 IDBCursor::DropJSObjects()
michael@0 438 {
michael@0 439 if (!mRooted) {
michael@0 440 return;
michael@0 441 }
michael@0 442 mScriptOwner = nullptr;
michael@0 443 mCachedKey = JSVAL_VOID;
michael@0 444 mCachedPrimaryKey = JSVAL_VOID;
michael@0 445 mCachedValue = JSVAL_VOID;
michael@0 446 mHaveCachedKey = false;
michael@0 447 mHaveCachedPrimaryKey = false;
michael@0 448 mHaveCachedValue = false;
michael@0 449 mRooted = false;
michael@0 450 mHaveValue = false;
michael@0 451 mozilla::DropJSObjects(this);
michael@0 452 }
michael@0 453
michael@0 454 void
michael@0 455 IDBCursor::ContinueInternal(const Key& aKey, int32_t aCount, ErrorResult& aRv)
michael@0 456 {
michael@0 457 MOZ_ASSERT(NS_IsMainThread());
michael@0 458 MOZ_ASSERT(aCount > 0);
michael@0 459
michael@0 460 if (!mTransaction->IsOpen()) {
michael@0 461 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
michael@0 462 return;
michael@0 463 }
michael@0 464
michael@0 465 if (!mHaveValue || mContinueCalled) {
michael@0 466 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
michael@0 467 return;
michael@0 468 }
michael@0 469
michael@0 470 mContinueToKey = aKey;
michael@0 471
michael@0 472 MOZ_ASSERT(mRequest->ReadyState() == IDBRequestReadyState::Done);
michael@0 473
michael@0 474 mRequest->Reset();
michael@0 475
michael@0 476 nsRefPtr<ContinueHelper> helper;
michael@0 477 switch (mType) {
michael@0 478 case OBJECTSTORE:
michael@0 479 helper = new ContinueObjectStoreHelper(this, aCount);
michael@0 480 break;
michael@0 481
michael@0 482 case OBJECTSTOREKEY:
michael@0 483 helper = new ContinueObjectStoreKeyHelper(this, aCount);
michael@0 484 break;
michael@0 485
michael@0 486 case INDEXKEY:
michael@0 487 helper = new ContinueIndexHelper(this, aCount);
michael@0 488 break;
michael@0 489
michael@0 490 case INDEXOBJECT:
michael@0 491 helper = new ContinueIndexObjectHelper(this, aCount);
michael@0 492 break;
michael@0 493
michael@0 494 default:
michael@0 495 MOZ_ASSUME_UNREACHABLE("Unknown cursor type!");
michael@0 496 }
michael@0 497
michael@0 498 nsresult rv = helper->DispatchToTransactionPool();
michael@0 499 if (NS_FAILED(rv)) {
michael@0 500 IDB_WARNING("Failed to dispatch!");
michael@0 501 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 502 return;
michael@0 503 }
michael@0 504
michael@0 505 mContinueCalled = true;
michael@0 506 }
michael@0 507
michael@0 508 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBCursor)
michael@0 509
michael@0 510 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBCursor)
michael@0 511 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
michael@0 512 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRequest)
michael@0 513 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction)
michael@0 514 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObjectStore)
michael@0 515 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndex)
michael@0 516 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
michael@0 517
michael@0 518 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBCursor)
michael@0 519 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
michael@0 520 NS_ASSERTION(tmp->mHaveCachedKey || JSVAL_IS_VOID(tmp->mCachedKey),
michael@0 521 "Should have a cached key");
michael@0 522 NS_ASSERTION(tmp->mHaveCachedPrimaryKey ||
michael@0 523 JSVAL_IS_VOID(tmp->mCachedPrimaryKey),
michael@0 524 "Should have a cached primary key");
michael@0 525 NS_ASSERTION(tmp->mHaveCachedValue || JSVAL_IS_VOID(tmp->mCachedValue),
michael@0 526 "Should have a cached value");
michael@0 527 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mScriptOwner)
michael@0 528 NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedKey)
michael@0 529 NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedPrimaryKey)
michael@0 530 NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedValue)
michael@0 531 NS_IMPL_CYCLE_COLLECTION_TRACE_END
michael@0 532
michael@0 533 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBCursor)
michael@0 534 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
michael@0 535 // Don't unlink mObjectStore, mIndex, or mTransaction!
michael@0 536 tmp->DropJSObjects();
michael@0 537 NS_IMPL_CYCLE_COLLECTION_UNLINK(mRequest)
michael@0 538 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
michael@0 539
michael@0 540 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBCursor)
michael@0 541 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
michael@0 542 NS_INTERFACE_MAP_ENTRY(nsISupports)
michael@0 543 NS_INTERFACE_MAP_END
michael@0 544
michael@0 545 NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBCursor)
michael@0 546 NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBCursor)
michael@0 547
michael@0 548 JSObject*
michael@0 549 IDBCursor::WrapObject(JSContext* aCx)
michael@0 550 {
michael@0 551 MOZ_ASSERT(NS_IsMainThread());
michael@0 552
michael@0 553 switch (mType) {
michael@0 554 case OBJECTSTORE:
michael@0 555 case INDEXOBJECT:
michael@0 556 return IDBCursorWithValueBinding::Wrap(aCx, this);
michael@0 557
michael@0 558 case OBJECTSTOREKEY:
michael@0 559 case INDEXKEY:
michael@0 560 return IDBCursorBinding::Wrap(aCx, this);
michael@0 561
michael@0 562 default:
michael@0 563 MOZ_ASSUME_UNREACHABLE("Bad type!");
michael@0 564 }
michael@0 565 }
michael@0 566
michael@0 567 mozilla::dom::IDBCursorDirection
michael@0 568 IDBCursor::GetDirection() const
michael@0 569 {
michael@0 570 MOZ_ASSERT(NS_IsMainThread());
michael@0 571
michael@0 572 switch (mDirection) {
michael@0 573 case NEXT:
michael@0 574 return mozilla::dom::IDBCursorDirection::Next;
michael@0 575
michael@0 576 case NEXT_UNIQUE:
michael@0 577 return mozilla::dom::IDBCursorDirection::Nextunique;
michael@0 578
michael@0 579 case PREV:
michael@0 580 return mozilla::dom::IDBCursorDirection::Prev;
michael@0 581
michael@0 582 case PREV_UNIQUE:
michael@0 583 return mozilla::dom::IDBCursorDirection::Prevunique;
michael@0 584
michael@0 585 default:
michael@0 586 MOZ_ASSUME_UNREACHABLE("Bad direction!");
michael@0 587 }
michael@0 588 }
michael@0 589
michael@0 590 void
michael@0 591 IDBCursor::GetSource(OwningIDBObjectStoreOrIDBIndex& aSource) const
michael@0 592 {
michael@0 593 MOZ_ASSERT(NS_IsMainThread());
michael@0 594
michael@0 595 switch (mType) {
michael@0 596 case OBJECTSTORE:
michael@0 597 case OBJECTSTOREKEY:
michael@0 598 MOZ_ASSERT(mObjectStore);
michael@0 599 aSource.SetAsIDBObjectStore() = mObjectStore;
michael@0 600 break;
michael@0 601
michael@0 602 case INDEXKEY:
michael@0 603 case INDEXOBJECT:
michael@0 604 MOZ_ASSERT(mIndex);
michael@0 605 aSource.SetAsIDBIndex() = mIndex;
michael@0 606 break;
michael@0 607
michael@0 608 default:
michael@0 609 MOZ_ASSUME_UNREACHABLE("Bad type!");
michael@0 610 }
michael@0 611 }
michael@0 612
michael@0 613 void
michael@0 614 IDBCursor::GetKey(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
michael@0 615 ErrorResult& aRv)
michael@0 616 {
michael@0 617 MOZ_ASSERT(NS_IsMainThread());
michael@0 618 MOZ_ASSERT(!mKey.IsUnset() || !mHaveValue);
michael@0 619
michael@0 620 if (!mHaveValue) {
michael@0 621 aResult.setUndefined();
michael@0 622 return;
michael@0 623 }
michael@0 624
michael@0 625 if (!mHaveCachedKey) {
michael@0 626 if (!mRooted) {
michael@0 627 mozilla::HoldJSObjects(this);
michael@0 628 mRooted = true;
michael@0 629 }
michael@0 630
michael@0 631 aRv = mKey.ToJSVal(aCx, mCachedKey);
michael@0 632 if (NS_WARN_IF(aRv.Failed())) {
michael@0 633 return;
michael@0 634 }
michael@0 635
michael@0 636 mHaveCachedKey = true;
michael@0 637 }
michael@0 638
michael@0 639 JS::ExposeValueToActiveJS(mCachedKey);
michael@0 640 aResult.set(mCachedKey);
michael@0 641 }
michael@0 642
michael@0 643 void
michael@0 644 IDBCursor::GetPrimaryKey(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
michael@0 645 ErrorResult& aRv)
michael@0 646 {
michael@0 647 MOZ_ASSERT(NS_IsMainThread());
michael@0 648
michael@0 649 if (!mHaveValue) {
michael@0 650 aResult.setUndefined();
michael@0 651 return;
michael@0 652 }
michael@0 653
michael@0 654 if (!mHaveCachedPrimaryKey) {
michael@0 655 if (!mRooted) {
michael@0 656 mozilla::HoldJSObjects(this);
michael@0 657 mRooted = true;
michael@0 658 }
michael@0 659
michael@0 660 const Key& key =
michael@0 661 (mType == OBJECTSTORE || mType == OBJECTSTOREKEY) ? mKey : mObjectKey;
michael@0 662 MOZ_ASSERT(!key.IsUnset());
michael@0 663
michael@0 664 aRv = key.ToJSVal(aCx, mCachedPrimaryKey);
michael@0 665 if (NS_WARN_IF(aRv.Failed())) {
michael@0 666 return;
michael@0 667 }
michael@0 668
michael@0 669 mHaveCachedPrimaryKey = true;
michael@0 670 }
michael@0 671
michael@0 672 JS::ExposeValueToActiveJS(mCachedPrimaryKey);
michael@0 673 aResult.set(mCachedPrimaryKey);
michael@0 674 }
michael@0 675
michael@0 676 void
michael@0 677 IDBCursor::GetValue(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
michael@0 678 ErrorResult& aRv)
michael@0 679 {
michael@0 680 MOZ_ASSERT(NS_IsMainThread());
michael@0 681 MOZ_ASSERT(mType == OBJECTSTORE || mType == INDEXOBJECT);
michael@0 682
michael@0 683 if (!mHaveValue) {
michael@0 684 aResult.setUndefined();
michael@0 685 return;
michael@0 686 }
michael@0 687
michael@0 688 if (!mHaveCachedValue) {
michael@0 689 if (!mRooted) {
michael@0 690 mozilla::HoldJSObjects(this);
michael@0 691 mRooted = true;
michael@0 692 }
michael@0 693
michael@0 694 JS::Rooted<JS::Value> val(aCx);
michael@0 695 if (!IDBObjectStore::DeserializeValue(aCx, mCloneReadInfo, &val)) {
michael@0 696 aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
michael@0 697 return;
michael@0 698 }
michael@0 699
michael@0 700 mCloneReadInfo.mCloneBuffer.clear();
michael@0 701
michael@0 702 mCachedValue = val;
michael@0 703 mHaveCachedValue = true;
michael@0 704 }
michael@0 705
michael@0 706 JS::ExposeValueToActiveJS(mCachedValue);
michael@0 707 aResult.set(mCachedValue);
michael@0 708 }
michael@0 709
michael@0 710 void
michael@0 711 IDBCursor::Continue(JSContext* aCx,
michael@0 712 JS::Handle<JS::Value> aKey,
michael@0 713 ErrorResult &aRv)
michael@0 714 {
michael@0 715 MOZ_ASSERT(NS_IsMainThread());
michael@0 716
michael@0 717 Key key;
michael@0 718 aRv = key.SetFromJSVal(aCx, aKey);
michael@0 719 ENSURE_SUCCESS_VOID(aRv);
michael@0 720
michael@0 721 if (!key.IsUnset()) {
michael@0 722 switch (mDirection) {
michael@0 723 case IDBCursor::NEXT:
michael@0 724 case IDBCursor::NEXT_UNIQUE:
michael@0 725 if (key <= mKey) {
michael@0 726 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
michael@0 727 return;
michael@0 728 }
michael@0 729 break;
michael@0 730
michael@0 731 case IDBCursor::PREV:
michael@0 732 case IDBCursor::PREV_UNIQUE:
michael@0 733 if (key >= mKey) {
michael@0 734 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
michael@0 735 return;
michael@0 736 }
michael@0 737 break;
michael@0 738
michael@0 739 default:
michael@0 740 MOZ_ASSUME_UNREACHABLE("Unknown direction type!");
michael@0 741 }
michael@0 742 }
michael@0 743
michael@0 744 ContinueInternal(key, 1, aRv);
michael@0 745 if (aRv.Failed()) {
michael@0 746 return;
michael@0 747 }
michael@0 748
michael@0 749 #ifdef IDB_PROFILER_USE_MARKS
michael@0 750 if (mType == OBJECTSTORE || mType == OBJECTSTOREKEY) {
michael@0 751 IDB_PROFILER_MARK("IndexedDB Request %llu: "
michael@0 752 "database(%s).transaction(%s).objectStore(%s).cursor(%s)."
michael@0 753 "continue(%s)",
michael@0 754 "IDBRequest[%llu] MT IDBCursor.continue()",
michael@0 755 Request()->GetSerialNumber(),
michael@0 756 IDB_PROFILER_STRING(Transaction()->Database()),
michael@0 757 IDB_PROFILER_STRING(Transaction()),
michael@0 758 IDB_PROFILER_STRING(mObjectStore),
michael@0 759 IDB_PROFILER_STRING(mDirection),
michael@0 760 key.IsUnset() ? "" : IDB_PROFILER_STRING(key));
michael@0 761 }
michael@0 762 else {
michael@0 763 IDB_PROFILER_MARK("IndexedDB Request %llu: "
michael@0 764 "database(%s).transaction(%s).objectStore(%s).index(%s)."
michael@0 765 "cursor(%s).continue(%s)",
michael@0 766 "IDBRequest[%llu] MT IDBCursor.continue()",
michael@0 767 Request()->GetSerialNumber(),
michael@0 768 IDB_PROFILER_STRING(Transaction()->Database()),
michael@0 769 IDB_PROFILER_STRING(Transaction()),
michael@0 770 IDB_PROFILER_STRING(mObjectStore),
michael@0 771 IDB_PROFILER_STRING(mIndex),
michael@0 772 IDB_PROFILER_STRING(mDirection),
michael@0 773 key.IsUnset() ? "" : IDB_PROFILER_STRING(key));
michael@0 774 }
michael@0 775 #endif
michael@0 776 }
michael@0 777
michael@0 778 already_AddRefed<IDBRequest>
michael@0 779 IDBCursor::Update(JSContext* aCx, JS::Handle<JS::Value> aValue,
michael@0 780 ErrorResult& aRv)
michael@0 781 {
michael@0 782 MOZ_ASSERT(NS_IsMainThread());
michael@0 783
michael@0 784 if (!mTransaction->IsOpen()) {
michael@0 785 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
michael@0 786 return nullptr;
michael@0 787 }
michael@0 788
michael@0 789 if (!mTransaction->IsWriteAllowed()) {
michael@0 790 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
michael@0 791 return nullptr;
michael@0 792 }
michael@0 793
michael@0 794 if (!mHaveValue || mType == OBJECTSTOREKEY || mType == INDEXKEY) {
michael@0 795 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
michael@0 796 return nullptr;
michael@0 797 }
michael@0 798
michael@0 799 MOZ_ASSERT(mObjectStore);
michael@0 800 MOZ_ASSERT(!mKey.IsUnset());
michael@0 801 MOZ_ASSERT(mType == OBJECTSTORE || mType == INDEXOBJECT);
michael@0 802 MOZ_ASSERT_IF(mType == INDEXOBJECT, !mObjectKey.IsUnset());
michael@0 803
michael@0 804 const Key& objectKey = (mType == OBJECTSTORE) ? mKey : mObjectKey;
michael@0 805
michael@0 806 nsRefPtr<IDBRequest> request;
michael@0 807 if (mObjectStore->HasValidKeyPath()) {
michael@0 808 // Make sure the object given has the correct keyPath value set on it.
michael@0 809 const KeyPath& keyPath = mObjectStore->GetKeyPath();
michael@0 810 Key key;
michael@0 811
michael@0 812 aRv = keyPath.ExtractKey(aCx, aValue, key);
michael@0 813 if (aRv.Failed()) {
michael@0 814 return nullptr;
michael@0 815 }
michael@0 816
michael@0 817 if (key != objectKey) {
michael@0 818 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
michael@0 819 return nullptr;
michael@0 820 }
michael@0 821
michael@0 822 request = mObjectStore->Put(aCx, aValue, JS::UndefinedHandleValue, aRv);
michael@0 823 if (aRv.Failed()) {
michael@0 824 return nullptr;
michael@0 825 }
michael@0 826 }
michael@0 827 else {
michael@0 828 JS::Rooted<JS::Value> keyVal(aCx);
michael@0 829 aRv = objectKey.ToJSVal(aCx, &keyVal);
michael@0 830 ENSURE_SUCCESS(aRv, nullptr);
michael@0 831
michael@0 832 request = mObjectStore->Put(aCx, aValue, keyVal, aRv);
michael@0 833 if (aRv.Failed()) {
michael@0 834 return nullptr;
michael@0 835 }
michael@0 836 }
michael@0 837
michael@0 838 #ifdef IDB_PROFILER_USE_MARKS
michael@0 839 {
michael@0 840 uint64_t requestSerial =
michael@0 841 static_cast<IDBRequest*>(request.get())->GetSerialNumber();
michael@0 842 if (mType == OBJECTSTORE) {
michael@0 843 IDB_PROFILER_MARK("IndexedDB Request %llu: "
michael@0 844 "database(%s).transaction(%s).objectStore(%s)."
michael@0 845 "cursor(%s).update(%s)",
michael@0 846 "IDBRequest[%llu] MT IDBCursor.update()",
michael@0 847 requestSerial,
michael@0 848 IDB_PROFILER_STRING(mTransaction->Database()),
michael@0 849 IDB_PROFILER_STRING(mTransaction),
michael@0 850 IDB_PROFILER_STRING(mObjectStore),
michael@0 851 IDB_PROFILER_STRING(mDirection),
michael@0 852 mObjectStore->HasValidKeyPath() ? "" :
michael@0 853 IDB_PROFILER_STRING(objectKey));
michael@0 854 }
michael@0 855 else {
michael@0 856 IDB_PROFILER_MARK("IndexedDB Request %llu: "
michael@0 857 "database(%s).transaction(%s).objectStore(%s)."
michael@0 858 "index(%s).cursor(%s).update(%s)",
michael@0 859 "IDBRequest[%llu] MT IDBCursor.update()",
michael@0 860 requestSerial,
michael@0 861 IDB_PROFILER_STRING(mTransaction->Database()),
michael@0 862 IDB_PROFILER_STRING(mTransaction),
michael@0 863 IDB_PROFILER_STRING(mObjectStore),
michael@0 864 IDB_PROFILER_STRING(mIndex),
michael@0 865 IDB_PROFILER_STRING(mDirection),
michael@0 866 mObjectStore->HasValidKeyPath() ? "" :
michael@0 867 IDB_PROFILER_STRING(objectKey));
michael@0 868 }
michael@0 869 }
michael@0 870 #endif
michael@0 871
michael@0 872 return request.forget();
michael@0 873 }
michael@0 874
michael@0 875 already_AddRefed<IDBRequest>
michael@0 876 IDBCursor::Delete(JSContext* aCx, ErrorResult& aRv)
michael@0 877 {
michael@0 878 MOZ_ASSERT(NS_IsMainThread());
michael@0 879
michael@0 880 if (!mTransaction->IsOpen()) {
michael@0 881 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
michael@0 882 return nullptr;
michael@0 883 }
michael@0 884
michael@0 885 if (!mTransaction->IsWriteAllowed()) {
michael@0 886 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
michael@0 887 return nullptr;
michael@0 888 }
michael@0 889
michael@0 890 if (!mHaveValue || mType == OBJECTSTOREKEY || mType == INDEXKEY) {
michael@0 891 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
michael@0 892 return nullptr;
michael@0 893 }
michael@0 894
michael@0 895 MOZ_ASSERT(mObjectStore);
michael@0 896 MOZ_ASSERT(mType == OBJECTSTORE || mType == INDEXOBJECT);
michael@0 897 MOZ_ASSERT(!mKey.IsUnset());
michael@0 898
michael@0 899 const Key& objectKey = (mType == OBJECTSTORE) ? mKey : mObjectKey;
michael@0 900
michael@0 901 JS::Rooted<JS::Value> key(aCx);
michael@0 902 aRv = objectKey.ToJSVal(aCx, &key);
michael@0 903 ENSURE_SUCCESS(aRv, nullptr);
michael@0 904
michael@0 905 nsRefPtr<IDBRequest> request = mObjectStore->Delete(aCx, key, aRv);
michael@0 906 ENSURE_SUCCESS(aRv, nullptr);
michael@0 907
michael@0 908 #ifdef IDB_PROFILER_USE_MARKS
michael@0 909 {
michael@0 910 uint64_t requestSerial = request->GetSerialNumber();
michael@0 911 if (mType == OBJECTSTORE) {
michael@0 912 IDB_PROFILER_MARK("IndexedDB Request %llu: "
michael@0 913 "database(%s).transaction(%s).objectStore(%s)."
michael@0 914 "cursor(%s).delete(%s)",
michael@0 915 "IDBRequest[%llu] MT IDBCursor.delete()",
michael@0 916 requestSerial,
michael@0 917 IDB_PROFILER_STRING(mTransaction->Database()),
michael@0 918 IDB_PROFILER_STRING(mTransaction),
michael@0 919 IDB_PROFILER_STRING(mObjectStore),
michael@0 920 IDB_PROFILER_STRING(mDirection),
michael@0 921 mObjectStore->HasValidKeyPath() ? "" :
michael@0 922 IDB_PROFILER_STRING(objectKey));
michael@0 923 }
michael@0 924 else {
michael@0 925 IDB_PROFILER_MARK("IndexedDB Request %llu: "
michael@0 926 "database(%s).transaction(%s).objectStore(%s)."
michael@0 927 "index(%s).cursor(%s).delete(%s)",
michael@0 928 "IDBRequest[%llu] MT IDBCursor.delete()",
michael@0 929 requestSerial,
michael@0 930 IDB_PROFILER_STRING(mTransaction->Database()),
michael@0 931 IDB_PROFILER_STRING(mTransaction),
michael@0 932 IDB_PROFILER_STRING(mObjectStore),
michael@0 933 IDB_PROFILER_STRING(mIndex),
michael@0 934 IDB_PROFILER_STRING(mDirection),
michael@0 935 mObjectStore->HasValidKeyPath() ? "" :
michael@0 936 IDB_PROFILER_STRING(objectKey));
michael@0 937 }
michael@0 938 }
michael@0 939 #endif
michael@0 940
michael@0 941 return request.forget();
michael@0 942 }
michael@0 943
michael@0 944 void
michael@0 945 IDBCursor::Advance(uint32_t aCount, ErrorResult &aRv)
michael@0 946 {
michael@0 947 MOZ_ASSERT(NS_IsMainThread());
michael@0 948
michael@0 949 if (aCount < 1) {
michael@0 950 aRv.ThrowTypeError(MSG_INVALID_ADVANCE_COUNT);
michael@0 951 return;
michael@0 952 }
michael@0 953
michael@0 954 Key key;
michael@0 955 ContinueInternal(key, int32_t(aCount), aRv);
michael@0 956 ENSURE_SUCCESS_VOID(aRv);
michael@0 957
michael@0 958 #ifdef IDB_PROFILER_USE_MARKS
michael@0 959 {
michael@0 960 if (mType == OBJECTSTORE || mType == OBJECTSTOREKEY) {
michael@0 961 IDB_PROFILER_MARK("IndexedDB Request %llu: "
michael@0 962 "database(%s).transaction(%s).objectStore(%s)."
michael@0 963 "cursor(%s).advance(%ld)",
michael@0 964 "IDBRequest[%llu] MT IDBCursor.advance()",
michael@0 965 Request()->GetSerialNumber(),
michael@0 966 IDB_PROFILER_STRING(Transaction()->Database()),
michael@0 967 IDB_PROFILER_STRING(Transaction()),
michael@0 968 IDB_PROFILER_STRING(mObjectStore),
michael@0 969 IDB_PROFILER_STRING(mDirection), aCount);
michael@0 970 }
michael@0 971 else {
michael@0 972 IDB_PROFILER_MARK("IndexedDB Request %llu: "
michael@0 973 "database(%s).transaction(%s).objectStore(%s)."
michael@0 974 "index(%s).cursor(%s).advance(%ld)",
michael@0 975 "IDBRequest[%llu] MT IDBCursor.advance()",
michael@0 976 Request()->GetSerialNumber(),
michael@0 977 IDB_PROFILER_STRING(Transaction()->Database()),
michael@0 978 IDB_PROFILER_STRING(Transaction()),
michael@0 979 IDB_PROFILER_STRING(mObjectStore),
michael@0 980 IDB_PROFILER_STRING(mIndex),
michael@0 981 IDB_PROFILER_STRING(mDirection), aCount);
michael@0 982 }
michael@0 983 }
michael@0 984 #endif
michael@0 985 }
michael@0 986
michael@0 987 void
michael@0 988 CursorHelper::ReleaseMainThreadObjects()
michael@0 989 {
michael@0 990 mCursor = nullptr;
michael@0 991 AsyncConnectionHelper::ReleaseMainThreadObjects();
michael@0 992 }
michael@0 993
michael@0 994 nsresult
michael@0 995 CursorHelper::Dispatch(nsIEventTarget* aDatabaseThread)
michael@0 996 {
michael@0 997 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 998
michael@0 999 PROFILER_MAIN_THREAD_LABEL("IndexedDB", "CursorHelper::Dispatch");
michael@0 1000
michael@0 1001 if (IndexedDatabaseManager::IsMainProcess()) {
michael@0 1002 return AsyncConnectionHelper::Dispatch(aDatabaseThread);
michael@0 1003 }
michael@0 1004
michael@0 1005 // If we've been invalidated then there's no point sending anything to the
michael@0 1006 // parent process.
michael@0 1007 if (mCursor->Transaction()->Database()->IsInvalidated()) {
michael@0 1008 IDB_REPORT_INTERNAL_ERR();
michael@0 1009 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
michael@0 1010 }
michael@0 1011
michael@0 1012 IndexedDBCursorChild* cursorActor = mCursor->GetActorChild();
michael@0 1013 NS_ASSERTION(cursorActor, "Must have an actor here!");
michael@0 1014
michael@0 1015 CursorRequestParams params;
michael@0 1016 nsresult rv = PackArgumentsForParentProcess(params);
michael@0 1017 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 1018
michael@0 1019 NoDispatchEventTarget target;
michael@0 1020 rv = AsyncConnectionHelper::Dispatch(&target);
michael@0 1021 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 1022
michael@0 1023 mActor = new IndexedDBCursorRequestChild(this, mCursor, params.type());
michael@0 1024 cursorActor->SendPIndexedDBRequestConstructor(mActor, params);
michael@0 1025
michael@0 1026 return NS_OK;
michael@0 1027 }
michael@0 1028
michael@0 1029 nsresult
michael@0 1030 ContinueHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
michael@0 1031 {
michael@0 1032 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
michael@0 1033 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
michael@0 1034
michael@0 1035 PROFILER_LABEL("IndexedDB", "ContinueHelper::DoDatabaseWork");
michael@0 1036
michael@0 1037 // We need to pick a query based on whether or not the cursor's mContinueToKey
michael@0 1038 // is set. If it is unset then othing was passed to continue so we'll grab the
michael@0 1039 // next item in the database that is greater than (less than, if we're running
michael@0 1040 // a PREV cursor) the current key. If it is set then a key was passed to
michael@0 1041 // continue so we'll grab the next item in the database that is greater than
michael@0 1042 // (less than, if we're running a PREV cursor) or equal to the key that was
michael@0 1043 // specified.
michael@0 1044
michael@0 1045 nsAutoCString query;
michael@0 1046 if (mCursor->mContinueToKey.IsUnset()) {
michael@0 1047 query.Assign(mCursor->mContinueQuery);
michael@0 1048 }
michael@0 1049 else {
michael@0 1050 query.Assign(mCursor->mContinueToQuery);
michael@0 1051 }
michael@0 1052 NS_ASSERTION(!query.IsEmpty(), "Bad query!");
michael@0 1053
michael@0 1054 query.AppendInt(mCount);
michael@0 1055
michael@0 1056 nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
michael@0 1057 IDB_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 1058
michael@0 1059 mozStorageStatementScoper scoper(stmt);
michael@0 1060
michael@0 1061 nsresult rv = BindArgumentsToStatement(stmt);
michael@0 1062 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 1063
michael@0 1064 NS_ASSERTION(mCount > 0, "Not ok!");
michael@0 1065
michael@0 1066 bool hasResult;
michael@0 1067 for (int32_t index = 0; index < mCount; index++) {
michael@0 1068 rv = stmt->ExecuteStep(&hasResult);
michael@0 1069 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 1070
michael@0 1071 if (!hasResult) {
michael@0 1072 break;
michael@0 1073 }
michael@0 1074 }
michael@0 1075
michael@0 1076 if (hasResult) {
michael@0 1077 rv = GatherResultsFromStatement(stmt);
michael@0 1078 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 1079 }
michael@0 1080 else {
michael@0 1081 mKey.Unset();
michael@0 1082 }
michael@0 1083
michael@0 1084 return NS_OK;
michael@0 1085 }
michael@0 1086
michael@0 1087 nsresult
michael@0 1088 ContinueHelper::GetSuccessResult(JSContext* aCx,
michael@0 1089 JS::MutableHandle<JS::Value> aVal)
michael@0 1090 {
michael@0 1091 UpdateCursorState();
michael@0 1092
michael@0 1093 if (mKey.IsUnset()) {
michael@0 1094 aVal.setNull();
michael@0 1095 }
michael@0 1096 else {
michael@0 1097 nsresult rv = WrapNative(aCx, mCursor, aVal);
michael@0 1098 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1099 }
michael@0 1100
michael@0 1101 return NS_OK;
michael@0 1102 }
michael@0 1103
michael@0 1104 void
michael@0 1105 ContinueHelper::ReleaseMainThreadObjects()
michael@0 1106 {
michael@0 1107 IDBObjectStore::ClearCloneReadInfo(mCloneReadInfo);
michael@0 1108 CursorHelper::ReleaseMainThreadObjects();
michael@0 1109 }
michael@0 1110
michael@0 1111 nsresult
michael@0 1112 ContinueHelper::PackArgumentsForParentProcess(CursorRequestParams& aParams)
michael@0 1113 {
michael@0 1114 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 1115 NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
michael@0 1116
michael@0 1117 PROFILER_MAIN_THREAD_LABEL("IndexedDB",
michael@0 1118 "ContinueHelper::PackArgumentsForParentProcess");
michael@0 1119
michael@0 1120 ContinueParams params;
michael@0 1121
michael@0 1122 params.key() = mCursor->mContinueToKey;
michael@0 1123 params.count() = uint32_t(mCount);
michael@0 1124
michael@0 1125 aParams = params;
michael@0 1126 return NS_OK;
michael@0 1127 }
michael@0 1128
michael@0 1129 AsyncConnectionHelper::ChildProcessSendResult
michael@0 1130 ContinueHelper::SendResponseToChildProcess(nsresult aResultCode)
michael@0 1131 {
michael@0 1132 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
michael@0 1133 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
michael@0 1134
michael@0 1135 PROFILER_MAIN_THREAD_LABEL("IndexedDB",
michael@0 1136 "ContinueHelper::SendResponseToChildProcess");
michael@0 1137
michael@0 1138 IndexedDBRequestParentBase* actor = mRequest->GetActorParent();
michael@0 1139 NS_ASSERTION(actor, "How did we get this far without an actor?");
michael@0 1140
michael@0 1141 InfallibleTArray<PBlobParent*> blobsParent;
michael@0 1142
michael@0 1143 if (NS_SUCCEEDED(aResultCode)) {
michael@0 1144 IDBDatabase* database = mTransaction->Database();
michael@0 1145 NS_ASSERTION(database, "This should never be null!");
michael@0 1146
michael@0 1147 ContentParent* contentParent = database->GetContentParent();
michael@0 1148 NS_ASSERTION(contentParent, "This should never be null!");
michael@0 1149
michael@0 1150 FileManager* fileManager = database->Manager();
michael@0 1151 NS_ASSERTION(fileManager, "This should never be null!");
michael@0 1152
michael@0 1153 const nsTArray<StructuredCloneFile>& files = mCloneReadInfo.mFiles;
michael@0 1154
michael@0 1155 aResultCode =
michael@0 1156 IDBObjectStore::ConvertBlobsToActors(contentParent, fileManager, files,
michael@0 1157 blobsParent);
michael@0 1158 if (NS_FAILED(aResultCode)) {
michael@0 1159 NS_WARNING("ConvertBlobsToActors failed!");
michael@0 1160 }
michael@0 1161 }
michael@0 1162
michael@0 1163 ResponseValue response;
michael@0 1164 if (NS_FAILED(aResultCode)) {
michael@0 1165 response = aResultCode;
michael@0 1166 }
michael@0 1167 else {
michael@0 1168 ContinueResponse continueResponse;
michael@0 1169 continueResponse.key() = mKey;
michael@0 1170 continueResponse.objectKey() = mObjectKey;
michael@0 1171 continueResponse.cloneInfo() = mCloneReadInfo;
michael@0 1172 continueResponse.blobsParent().SwapElements(blobsParent);
michael@0 1173 response = continueResponse;
michael@0 1174 }
michael@0 1175
michael@0 1176 if (!actor->SendResponse(response)) {
michael@0 1177 return Error;
michael@0 1178 }
michael@0 1179
michael@0 1180 UpdateCursorState();
michael@0 1181
michael@0 1182 return Success_Sent;
michael@0 1183 }
michael@0 1184
michael@0 1185 nsresult
michael@0 1186 ContinueHelper::UnpackResponseFromParentProcess(
michael@0 1187 const ResponseValue& aResponseValue)
michael@0 1188 {
michael@0 1189 NS_ASSERTION(aResponseValue.type() == ResponseValue::TContinueResponse,
michael@0 1190 "Bad response type!");
michael@0 1191
michael@0 1192 const ContinueResponse& response = aResponseValue.get_ContinueResponse();
michael@0 1193
michael@0 1194 mKey = response.key();
michael@0 1195 mObjectKey = response.objectKey();
michael@0 1196
michael@0 1197 const SerializedStructuredCloneReadInfo& cloneInfo = response.cloneInfo();
michael@0 1198
michael@0 1199 NS_ASSERTION((!cloneInfo.dataLength && !cloneInfo.data) ||
michael@0 1200 (cloneInfo.dataLength && cloneInfo.data),
michael@0 1201 "Inconsistent clone info!");
michael@0 1202
michael@0 1203 if (!mCloneReadInfo.SetFromSerialized(cloneInfo)) {
michael@0 1204 IDB_WARNING("Failed to copy clone buffer!");
michael@0 1205 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
michael@0 1206 }
michael@0 1207
michael@0 1208 IDBObjectStore::ConvertActorsToBlobs(response.blobsChild(),
michael@0 1209 mCloneReadInfo.mFiles);
michael@0 1210 return NS_OK;
michael@0 1211 }
michael@0 1212
michael@0 1213 nsresult
michael@0 1214 ContinueObjectStoreHelper::BindArgumentsToStatement(
michael@0 1215 mozIStorageStatement* aStatement)
michael@0 1216 {
michael@0 1217 MOZ_ASSERT(!NS_IsMainThread());
michael@0 1218 MOZ_ASSERT(aStatement);
michael@0 1219
michael@0 1220 // Bind object store id.
michael@0 1221 nsresult rv = aStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"),
michael@0 1222 mCursor->mObjectStore->Id());
michael@0 1223 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 1224
michael@0 1225 NS_NAMED_LITERAL_CSTRING(currentKeyName, "current_key");
michael@0 1226 NS_NAMED_LITERAL_CSTRING(rangeKeyName, "range_key");
michael@0 1227
michael@0 1228 // Bind current key.
michael@0 1229 const Key& currentKey = mCursor->mContinueToKey.IsUnset() ?
michael@0 1230 mCursor->mKey :
michael@0 1231 mCursor->mContinueToKey;
michael@0 1232
michael@0 1233 rv = currentKey.BindToStatement(aStatement, currentKeyName);
michael@0 1234 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1235
michael@0 1236 // Bind range key if it is specified.
michael@0 1237 const Key& rangeKey = mCursor->mRangeKey;
michael@0 1238
michael@0 1239 if (!rangeKey.IsUnset()) {
michael@0 1240 rv = rangeKey.BindToStatement(aStatement, rangeKeyName);
michael@0 1241 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1242 }
michael@0 1243
michael@0 1244 return NS_OK;
michael@0 1245 }
michael@0 1246
michael@0 1247 nsresult
michael@0 1248 ContinueObjectStoreHelper::GatherResultsFromStatement(
michael@0 1249 mozIStorageStatement* aStatement)
michael@0 1250 {
michael@0 1251 MOZ_ASSERT(!NS_IsMainThread());
michael@0 1252 MOZ_ASSERT(aStatement);
michael@0 1253
michael@0 1254 // Figure out what kind of key we have next.
michael@0 1255 nsresult rv = mKey.SetFromStatement(aStatement, 0);
michael@0 1256 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1257
michael@0 1258 rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(aStatement, 1, 2,
michael@0 1259 mDatabase,
michael@0 1260 mCloneReadInfo);
michael@0 1261 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1262
michael@0 1263 return NS_OK;
michael@0 1264 }
michael@0 1265
michael@0 1266 nsresult
michael@0 1267 ContinueObjectStoreKeyHelper::GatherResultsFromStatement(
michael@0 1268 mozIStorageStatement* aStatement)
michael@0 1269 {
michael@0 1270 MOZ_ASSERT(!NS_IsMainThread());
michael@0 1271 MOZ_ASSERT(aStatement);
michael@0 1272
michael@0 1273 nsresult rv = mKey.SetFromStatement(aStatement, 0);
michael@0 1274 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1275
michael@0 1276 return NS_OK;
michael@0 1277 }
michael@0 1278
michael@0 1279 nsresult
michael@0 1280 ContinueIndexHelper::BindArgumentsToStatement(mozIStorageStatement* aStatement)
michael@0 1281 {
michael@0 1282 // Bind index id.
michael@0 1283 nsresult rv = aStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"),
michael@0 1284 mCursor->mIndex->Id());
michael@0 1285 IDB_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
michael@0 1286
michael@0 1287 NS_NAMED_LITERAL_CSTRING(currentKeyName, "current_key");
michael@0 1288
michael@0 1289 // Bind current key.
michael@0 1290 const Key& currentKey = mCursor->mContinueToKey.IsUnset() ?
michael@0 1291 mCursor->mKey :
michael@0 1292 mCursor->mContinueToKey;
michael@0 1293
michael@0 1294 rv = currentKey.BindToStatement(aStatement, currentKeyName);
michael@0 1295 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1296
michael@0 1297 // Bind range key if it is specified.
michael@0 1298 if (!mCursor->mRangeKey.IsUnset()) {
michael@0 1299 NS_NAMED_LITERAL_CSTRING(rangeKeyName, "range_key");
michael@0 1300 rv = mCursor->mRangeKey.BindToStatement(aStatement, rangeKeyName);
michael@0 1301 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1302 }
michael@0 1303
michael@0 1304 // Bind object key if duplicates are allowed and we're not continuing to a
michael@0 1305 // specific key.
michael@0 1306 if ((mCursor->mDirection == IDBCursor::NEXT ||
michael@0 1307 mCursor->mDirection == IDBCursor::PREV) &&
michael@0 1308 mCursor->mContinueToKey.IsUnset()) {
michael@0 1309 NS_ASSERTION(!mCursor->mObjectKey.IsUnset(), "Bad key!");
michael@0 1310
michael@0 1311 NS_NAMED_LITERAL_CSTRING(objectKeyName, "object_key");
michael@0 1312 rv = mCursor->mObjectKey.BindToStatement(aStatement, objectKeyName);
michael@0 1313 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1314 }
michael@0 1315
michael@0 1316 return NS_OK;
michael@0 1317 }
michael@0 1318
michael@0 1319 nsresult
michael@0 1320 ContinueIndexHelper::GatherResultsFromStatement(
michael@0 1321 mozIStorageStatement* aStatement)
michael@0 1322 {
michael@0 1323 nsresult rv = mKey.SetFromStatement(aStatement, 0);
michael@0 1324 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1325
michael@0 1326 rv = mObjectKey.SetFromStatement(aStatement, 1);
michael@0 1327 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1328
michael@0 1329 return NS_OK;
michael@0 1330 }
michael@0 1331
michael@0 1332 nsresult
michael@0 1333 ContinueIndexObjectHelper::GatherResultsFromStatement(
michael@0 1334 mozIStorageStatement* aStatement)
michael@0 1335 {
michael@0 1336 nsresult rv = mKey.SetFromStatement(aStatement, 0);
michael@0 1337 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1338
michael@0 1339 rv = mObjectKey.SetFromStatement(aStatement, 1);
michael@0 1340 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1341
michael@0 1342 rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(aStatement, 2, 3,
michael@0 1343 mDatabase, mCloneReadInfo);
michael@0 1344 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1345
michael@0 1346 return NS_OK;
michael@0 1347 }

mercurial