mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
e4c3e62beb
- Bug 1216751 part 1. Restrict value iterators to interfaces that have indexed properties and pair iterators to interfaces that do not have indexed properties. r=qdot (6519f3f8c5) - Bug 1216751 part 2. For value iterators, "entries", "keys", and "values" must just come from Array.prototype. r=qdot (c0859f945c) - Bug 1216751 part 3. For pair iterators, @@iterator should be an alias for "entries". Similarly for maplikes and "entries" and setlikes and "values". r=qdot (bbe7c04782) - Bug 1216751 part 4. Implement forEach for iterable interfaces. r=qdot (8fdba677a4) - Bug 1216751 part 5. Remove the now-unnecessary value iterator infrastructure, since it's entirely handled via the %ArrayPrototype% methods now. r=qdot (88d3911694) - Bug 1231333 - part 1, JS engine: only allow futexWait in workers. r=luke (28e16fd2f9) - Bug 1231333 - part 2, DOM: only allow futexWait in workers. r=khuey (6c4dc98037) - Bug 1148990 - Don't ship bagheeraclient.js or tokenserverclient.js on Android. r=gps (aa9b22699a) - Bug 1216749 - Land the Firefox Kinto.js client (r=rnewman) (ea8c74e2ea) - Bug 1230221 - Convert JS callsites to use asyncOpen2 within services/ (r=sicking) (07ac8751f1) - Bug 1242965 - Make services/common eslintable. r=rnewman (0c84562750) - Bug 1055616 - Skip addons addons without a sourceURI or from a non-secure domain rather than treating them as errors. r=rnewman (7b8b738be0) - Bug 1229986 - get Sync tps tests starting again. r=whimboo (8cd0bf4f7f) - Bug 1003204: Removed CommonUtils.exceptionStr() in services/sync r=makh r=gfritzsche (830c106a29) - Bug 1003204: Removed CommonUtils.exceptionStr() in services/common/ r=gfritzsche (2c7bd4f8b5) - Bug 1234734 - Replace CommonUtils.stackTrace() with Log.stackTrace(). r=markh (3f0e88f192) - Bug 1241715 - get Sync TPS tests working locally by tweaking observers listened for and the authentication setup. r=whimboo (529b2f3d44) - Bug 1203736 - Convert H264::DecodeSPS assert to error return. r=jya (41c8c34c42) - Bug 1186716: Error if SPS NAL parsing failed. r=rillian (6c158be51e) - Bug 1187076 - Warn at end of SPS buffers. r=jya (2a49671261) - fix broken files (a090aad200) - Bug 1218217: avoid buffersize overflow even if codec is unbounded in dimensions r=pkerr (356140c947) - Bug 1218217: bustage fix for static assert r=bustage (e86dc5bf3a) - Bug 1041882 - Remove Froyo-specific support from libcubeb. r=snorp, r=padenot (e1f2d5283f) - Bug 1073319 - Enable AVX2 for libvpx on linux (update.py). r=rillian (934fd0a896) - Bug 1245027 - Move LOCAL_INCLUDES to moz.build in media/libvpx. r=mshal (7e56797d0e) - parts of Bug 1151175 - Update libvpx update.py for 1.4.0. (0e3f4a470f) - bits of 1178215 (bab7592703) - Bug 1218124 - Add vpx_once patch to update script. r=gerald (7b72a43382) - Bug 1225221 - vpx: Allow 8k video in update.sh. r=kinetik (9ec59f7737) - Bug 1224363 - Upstream update patch - r=rillian (4772921a5f) - Bug 1224361 - Upstream update patch - r=rillian (36ad6f1de4) - Bug 1233983 - Make libvpx build with clang-cl; r=rillian (5d98a8d888) - Bug 1224371 - Upstream update patch. r=jya (25164ba856) - Bug 1237848 - Updated update.py patch - r=rillian (69646eb6dc) - Bug 1184226 - Suppressing received packets when disabled, r=ekr (c8dfdb1a56) - Bug 1184226 - Disabling write on shutdown, r=ekr (d5a810dbe5) - Bug 1184226 - Updating transportlayerdtls logging levels, r=ekr (f3bc4a9889) - Bug 1137932: Unwind the stack before starting the DTLS handshake. r=mt (69dce8243a) - Bug 1214269 - read multiple DTLS packets from NSS if present. r=mt rjesup (e57b1628f5) - Bug 1235235 - Fix -Wimplicit-fallthrough warning in media/mtransport/. r=ekr (d56c9d1244) - Bug 1115483 - Accept a match on any a=fingerprint value. r=ekr (4a58378c09) - Bug 1167274 - Do the right thing when accessing the proxyinfo fails for some reason. r=mt (3ea23173ea) - Bug 1125292 - Sending ALPN header field for WebRTC calls, r=bwc (16fda60c39) - Bug 1167443 - Fix verification of end-of-candidates in mochitests. r=mt (8d74546e68) - Bug 1192813 - update the default candidate as new candidates arrive. r=bwc (490ac80af2) - Bug 1206981 - prevent ICE TCP from being turned off under e10s. r=jesup (a38afd56b8) - Bug 1234578 - Assert if PCM is destroyed improperly. r=rjesup (f1aa0d7cbc) - Bug 1164564 - WorkerDebugger.initialize should not return failure when called more than once;r=khuey (c316c83af7) - Bug 1211903 - WorkerDebugger should live on the main thread;r=khuey (5586888e77) - Bug 1164581 - Adding an overload for NS_ProxyRelease that accepts already_AddRefed, and removing all the others. r=bobbyholley (bc70230689) - Bug 1186750 part 1 - Inlinize trivial constructors and destructors of events in DeviceStorageRequestParent. r=dhylands (0fc6b594b1) - Bug 1186750 part 2 - Remove some unused member fields in events in DeviceStorageRequestParent. r=dhylands (d4be7e7031) - Bug 1186750 part 3 - Abstract CancelableFileEvent in DeviceStorageReqeustParent and use already_AddRefed&& for passing DeviceStorageFile parameter. r=dhylands (cea4df4465) - Bug 1186750 part 4 - Clear runnable list in DeviceStorageRequestParent when being destroyed. r=dhylands (a4d6018ce6) - Bug 1196315 - Ensure MIME service is only accessed on the main thread. r=dhylands (20c07f4baf) - Bug 1186750 part 5 - Convert nsDOMDeviceStorage::CheckPermission to take already_AddRefed&&. r=dhylands (7b2d0b415e) - Bug 1186750 part 6 - Remove unused and unimplemented method nsDOMDeviceStorage::StorePermission. r=dhylands (e6772e7b51) - Bug 1186750 part 7 - Convert DispatchToOwningThread and DispatchOrAbandon to take already_AddRefed&&. r=dhylands (5925568a22) - Bug 1186750 part 8 - Convert DeviceStorageUsedSpaceCache::Dispatch to use already_AddRef&&. r=dhylands (660b44eec7) - Bug 1186750 part 9 - Use already_AddRefed&& to initialize mFile of device storage requests. r=dhylands (c94464f412) - Bug 1186750 part 10 - Simplify code in DeviceStorageRequestParent::Dispatch. r=dhylands (debcc219ca) - Bug 1186750 part 11 - Convert all usage of Dispatch/NS_DispatchToMainThread in dom/devicestorage to pass in either already_AddRefed or raw pointer. r=dhylands (753694d0b5) - Bug 1059469: Part 1 - Add a log module for dump() calls. r=bent (d94c677e49) - Bug 1059469: Part 2 - When rescheduling the interval timer, cancel it first, and refactor things so that actually does something. r=bent (1edc485b0f) - Bug 1243881 - patch 1 - unship performance.translateTime, r=bz (5a4afeea67) - Bug 1243881 - patch 2 - unship performance.translateTime, r=bz (5bf9557cd4) - Bug 1165722 - Replace JS_GetPropertyDescriptor usage in Xray code. r=bholley (e277cbcc78) - Bug 1243824. Add support for static functions and attributes on JSXrays. r=bholley (498d6c6034) - Bug 1228456 - SharedWorker should close the MessagePort in case the connecting runnable is not dispatched, r=smaug (c14a3e212f) - Bug 779707 - Add crashtest. (e86caca48e) - Bug 1228456 - add 'override' to the Cancel() method of a nsICancelableRunnable, rs=me (48db3b97e9) - Bug 1131323 - Enable SharedWorker loads to be intercepted through service workers; r=nsm (b2d972c5e3) - Bug 1173002 - Set worker system principal flag correctly when created from chrome, r=bz, a=kwierso. (ac9fc2980d) - bits of 1113429 backout (a862f16bb7) - bug 1206312 - add IndexedDatabaseManager include to IDBKeyRange. r=bz (bd6663f976) - Bug 1247117: De-namespace much of IndexedDB. r=baku (a996e3b443) - Bug 1196841: Update getAll/getAllKeys to match the spec and expose them. r=baku (7365769e04) - Bug 1196840: Make IDBTransaction::ObjectStoreNames const. r=baku (e7af2b0510) - Bug 1176165 - Fix the exception codes returned from functions that modify the IndexedDB schema, r=janv. (efa4e818d0) - Bug 935753 - Firefox displays the "This is a secure Firefox page" indicator on pages served by addons. r=MattN (77dced27ad) - Bug 925681 - Show identity block and reload icon in awesomebar in Australis' customization mode. ui-r=shorlander, r=Gijs (ffd1b2f6a4) - Bug 970382 - Add about:accounts to the list of chrome UIs with a special identity mode r=gavin (6d2817d087) - Bug 1051847 - Add trusted identity block to about:license and about:rights. r=dao (aa8dfe4d1d) - Bug 1094947 - The trusted identity block is not displayed for the about:downloads page. r=jaws (1c51faa077) - Bug 686281 - Implement CSS mask style; r=dbaron. (2f823c4a49) - Bug 686281 - Mask CSS parsing and Mask DOM API. r=dbaron (f9cc291131) - Bug 686281 - Mask CSS rendering; r=mstange (b26ba7ba7e) - Bug 686281 - Mask CSS animation; r=dbaron. (4ce1ba671e) - Bug 686281 - Mask CSS webkit-alias; r=dbaron. (c27f4023d6) - Bug 686281 - Mask mochitest; r=dbaron. (010fcdfd04) - Bug 686281 - Expands will-change of a shorthand prop to longhand ones; r=dbaron. (f8e4a6dcfd) - Bug 686281 - A static assertion to keep value correctness of NS_RULE_NODE_IS_ANIMATION_RULE; r=dbaron. (5ae87b576b) - Bug 686281 - Remove nsStyleSVGReset::mMask; r=dbaron (1e7a0dfb45) - Bug 686281 - mask-composite reftests; r=dbaron (7f769e196a) - Bug 686281 - Rename nsStyleSVGReset::mLayers to nsStyleSVGReset::mMask; Rename nsStyleBackground::mLayers to nsStyleBackground::mImage. r=dbaron (3bd4fc6e3b) - Bug 1241275 - Change the way -moz-window-dragging works. r=heycam,roc (5691f2dbf5) - Bug 1246892 - pass aCTF as a reference instead of value. r=roc (98b0e45063) - Bug 1234800 - Reinstate code that adjusts dirty rects for fixed-position frames in display ports. r=tn (44e55ebacb) - Bug 1234800 - Move this line to the right place. r=tn (1a86a7fc72) - Bug 1216832 - Handle preserve-3d visible regions during display list building by always transforming from the preserve-3d root each time. r=roc (1887af1172) - Bug 1231243 - In nsDisplayBackgroundImage::GetBoundsInternal(), take the union of the image bounds and the viewport bounds if APZ is enabled. r=mstange (87a1fa0ab4) - Bug 1246622 - Handle nested preserve-3d contexts when hit testing. r=roc (6eed51c734) - Bug 1235945 - Fix assertion error in some cases when running szip when debug flags are enabled for host tools. r=froydnj (3a0aa4f728) - Bug 1224798: Do not produce a clip mask if our context is entirely clipped out anyway. r=jrmuizel (3926a4ef7d) - Bug 1223604 - Disentangle nsSVGClipPathFrame::ApplyClipOrPaintClipMask and make the code easier to understand. r=Bas (c8c19a1b0d) - Bug 1204405: Don't access prefs off main thread in testing ProcessLink::Open(). r=khuey (301aa7259d) - Bug 1248896 - don't conditional compile on config ENABLE_TESTS in Nuwa. r=khuey (4f2fd275fd) - Bug 1232458 - use UniquePtr<T[]> instead of nsAutoArrayPtr<T> in WindowsDllBlocklist.cpp; r=aklotz (292071bdb5) - Bug 1247741 - Additional checks for pointer validity in LdrLoadDLL detour. r=aklotz (8ee48e8cf3) - Bug 1113930 - Move __libc_stack_end related code block from StackWalk.cpp in a non-OSX section. r=froydnj (4f0f9e2e66) - Bug 1113930 - Use the actual stack end address on x86 OSX and Android for the stack walker. r=froydnj (7371d9a508) - missing bit of Bug 1216681 (fdf69e362f) - Bug 1193593 - Test fingerprinting resistance for media queries in picture elements. r=heycam (6155b73c26) - Bug 1232829 - Detach obsolete DocumentTimeline from refresh driver when the document is reset; r=smaug (564680e2a0) - Bug 1075457, part 1 - Implement rendering for |clip-path:polygon()|. r=mstange, r=jwatt (76056caacd) - Bug 1075457, part 2 - Implement circle() and ellipse() for the |clip-path| property. r=mstange, r=jwatt (4b8b39c682) - Bug 1094571 - add unicode-range load tests. r=heycam (3358555411) - Bug 1216695 - Remove the Request.context specific bits from fetch-request-resources.https.html; r=bkelly (2315e50b97) - Bug 1193133 - Disable broken service worker wpt tests. r=bkelly (8f0205d5e7) - Bug 1199831: Fix a bunch of mixed-content violations in imported ServiceWorker WPTs. r=jdm (33f261ce91) - bit of Bug 603201 (325170577f) - Bug 1184798 - same origin, cors and no-cors load tests. r=bkelly (f8549dd0bb) - Bug 1210581: Test controlled worker loads (XHR, fetch, importScripts). r=ehsan (41a436df47) - Bug 1215196 - Fix web-platform-tests iframe scripts to avoid pulling in testharness.js in them; r=bkelly (a2edb0784c) - Bug 1242798 - Don't OSR into Ion on debuggee frames. (r=jandem) (21e17bdd9d) - Bug 1238658 - Allow setElem-accessor optimizations only for native baseHolder objects; r=efaust (12c9766a53) - Bug 1144630 - Follup: Fix review nit. (rs=evilpie) (67b5cc2c7f) - Bug 1182866 - Fix Baseline GETNAME stubs to check for uninitialized lexicals. (r=jandem) (dd47d2025a) - Bug 1189536 - Make fetch-request-xhr.https.html pass; r=bkelly (ce177226bf) - Bug 1188822 - Make service-workers/service-worker/fetch-request-resources.https.html pass. r=bkelly (3a5f3a6660)
2301 lines
64 KiB
C++
2301 lines
64 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "IDBObjectStore.h"
|
|
|
|
#include "FileInfo.h"
|
|
#include "IDBCursor.h"
|
|
#include "IDBDatabase.h"
|
|
#include "IDBEvents.h"
|
|
#include "IDBFactory.h"
|
|
#include "IDBIndex.h"
|
|
#include "IDBKeyRange.h"
|
|
#include "IDBMutableFile.h"
|
|
#include "IDBRequest.h"
|
|
#include "IDBTransaction.h"
|
|
#include "IndexedDatabase.h"
|
|
#include "IndexedDatabaseInlines.h"
|
|
#include "IndexedDatabaseManager.h"
|
|
#include "js/Class.h"
|
|
#include "js/Date.h"
|
|
#include "js/StructuredClone.h"
|
|
#include "KeyPath.h"
|
|
#include "mozilla/Endian.h"
|
|
#include "mozilla/ErrorResult.h"
|
|
#include "mozilla/Move.h"
|
|
#include "mozilla/dom/BindingUtils.h"
|
|
#include "mozilla/dom/ContentChild.h"
|
|
#include "mozilla/dom/ContentParent.h"
|
|
#include "mozilla/dom/DOMStringList.h"
|
|
#include "mozilla/dom/File.h"
|
|
#include "mozilla/dom/IDBMutableFileBinding.h"
|
|
#include "mozilla/dom/BlobBinding.h"
|
|
#include "mozilla/dom/IDBObjectStoreBinding.h"
|
|
#include "mozilla/dom/StructuredCloneHolder.h"
|
|
#include "mozilla/dom/StructuredCloneTags.h"
|
|
#include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h"
|
|
#include "mozilla/dom/ipc/BlobChild.h"
|
|
#include "mozilla/dom/ipc/BlobParent.h"
|
|
#include "mozilla/dom/ipc/nsIRemoteBlob.h"
|
|
#include "mozilla/ipc/BackgroundChild.h"
|
|
#include "mozilla/ipc/PBackgroundSharedTypes.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsQueryObject.h"
|
|
#include "ProfilerHelpers.h"
|
|
#include "ReportInternalError.h"
|
|
#include "WorkerPrivate.h"
|
|
#include "WorkerScope.h"
|
|
|
|
// Include this last to avoid path problems on Windows.
|
|
#include "ActorsChild.h"
|
|
|
|
namespace mozilla {
|
|
namespace dom {
|
|
|
|
using namespace mozilla::dom::quota;
|
|
using namespace mozilla::dom::workers;
|
|
using namespace mozilla::ipc;
|
|
|
|
struct IDBObjectStore::StructuredCloneWriteInfo
|
|
{
|
|
struct BlobOrMutableFile
|
|
{
|
|
RefPtr<Blob> mBlob;
|
|
RefPtr<IDBMutableFile> mMutableFile;
|
|
|
|
bool
|
|
operator==(const BlobOrMutableFile& aOther) const
|
|
{
|
|
return this->mBlob == aOther.mBlob &&
|
|
this->mMutableFile == aOther.mMutableFile;
|
|
}
|
|
};
|
|
|
|
JSAutoStructuredCloneBuffer mCloneBuffer;
|
|
nsTArray<BlobOrMutableFile> mBlobOrMutableFiles;
|
|
IDBDatabase* mDatabase;
|
|
uint64_t mOffsetToKeyProp;
|
|
|
|
explicit StructuredCloneWriteInfo(IDBDatabase* aDatabase)
|
|
: mDatabase(aDatabase)
|
|
, mOffsetToKeyProp(0)
|
|
{
|
|
MOZ_ASSERT(aDatabase);
|
|
|
|
MOZ_COUNT_CTOR(StructuredCloneWriteInfo);
|
|
}
|
|
|
|
StructuredCloneWriteInfo(StructuredCloneWriteInfo&& aCloneWriteInfo)
|
|
: mCloneBuffer(Move(aCloneWriteInfo.mCloneBuffer))
|
|
, mDatabase(aCloneWriteInfo.mDatabase)
|
|
, mOffsetToKeyProp(aCloneWriteInfo.mOffsetToKeyProp)
|
|
{
|
|
MOZ_ASSERT(mDatabase);
|
|
|
|
MOZ_COUNT_CTOR(StructuredCloneWriteInfo);
|
|
|
|
mBlobOrMutableFiles.SwapElements(aCloneWriteInfo.mBlobOrMutableFiles);
|
|
aCloneWriteInfo.mOffsetToKeyProp = 0;
|
|
}
|
|
|
|
~StructuredCloneWriteInfo()
|
|
{
|
|
MOZ_COUNT_DTOR(StructuredCloneWriteInfo);
|
|
}
|
|
|
|
bool
|
|
operator==(const StructuredCloneWriteInfo& aOther) const
|
|
{
|
|
return this->mCloneBuffer.nbytes() == aOther.mCloneBuffer.nbytes() &&
|
|
this->mCloneBuffer.data() == aOther.mCloneBuffer.data() &&
|
|
this->mBlobOrMutableFiles == aOther.mBlobOrMutableFiles &&
|
|
this->mDatabase == aOther.mDatabase &&
|
|
this->mOffsetToKeyProp == aOther.mOffsetToKeyProp;
|
|
}
|
|
|
|
bool
|
|
SetFromSerialized(const SerializedStructuredCloneWriteInfo& aOther)
|
|
{
|
|
if (aOther.data().IsEmpty()) {
|
|
mCloneBuffer.clear();
|
|
} else {
|
|
auto* aOtherBuffer =
|
|
reinterpret_cast<uint64_t*>(
|
|
const_cast<uint8_t*>(aOther.data().Elements()));
|
|
if (!mCloneBuffer.copy(aOtherBuffer, aOther.data().Length())) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
mBlobOrMutableFiles.Clear();
|
|
|
|
mOffsetToKeyProp = aOther.offsetToKeyProp();
|
|
return true;
|
|
}
|
|
};
|
|
|
|
namespace {
|
|
|
|
struct MOZ_STACK_CLASS MutableFileData final
|
|
{
|
|
nsString type;
|
|
nsString name;
|
|
|
|
MutableFileData()
|
|
{
|
|
MOZ_COUNT_CTOR(MutableFileData);
|
|
}
|
|
|
|
~MutableFileData()
|
|
{
|
|
MOZ_COUNT_DTOR(MutableFileData);
|
|
}
|
|
};
|
|
|
|
struct MOZ_STACK_CLASS BlobOrFileData final
|
|
{
|
|
uint32_t tag;
|
|
uint64_t size;
|
|
nsString type;
|
|
nsString name;
|
|
int64_t lastModifiedDate;
|
|
|
|
BlobOrFileData()
|
|
: tag(0)
|
|
, size(0)
|
|
, lastModifiedDate(INT64_MAX)
|
|
{
|
|
MOZ_COUNT_CTOR(BlobOrFileData);
|
|
}
|
|
|
|
~BlobOrFileData()
|
|
{
|
|
MOZ_COUNT_DTOR(BlobOrFileData);
|
|
}
|
|
};
|
|
|
|
struct MOZ_STACK_CLASS GetAddInfoClosure final
|
|
{
|
|
IDBObjectStore::StructuredCloneWriteInfo& mCloneWriteInfo;
|
|
JS::Handle<JS::Value> mValue;
|
|
|
|
GetAddInfoClosure(IDBObjectStore::StructuredCloneWriteInfo& aCloneWriteInfo,
|
|
JS::Handle<JS::Value> aValue)
|
|
: mCloneWriteInfo(aCloneWriteInfo)
|
|
, mValue(aValue)
|
|
{
|
|
MOZ_COUNT_CTOR(GetAddInfoClosure);
|
|
}
|
|
|
|
~GetAddInfoClosure()
|
|
{
|
|
MOZ_COUNT_DTOR(GetAddInfoClosure);
|
|
}
|
|
};
|
|
|
|
already_AddRefed<IDBRequest>
|
|
GenerateRequest(IDBObjectStore* aObjectStore)
|
|
{
|
|
MOZ_ASSERT(aObjectStore);
|
|
aObjectStore->AssertIsOnOwningThread();
|
|
|
|
IDBTransaction* transaction = aObjectStore->Transaction();
|
|
|
|
RefPtr<IDBRequest> request =
|
|
IDBRequest::Create(aObjectStore, transaction->Database(), transaction);
|
|
MOZ_ASSERT(request);
|
|
|
|
return request.forget();
|
|
}
|
|
|
|
bool
|
|
StructuredCloneWriteCallback(JSContext* aCx,
|
|
JSStructuredCloneWriter* aWriter,
|
|
JS::Handle<JSObject*> aObj,
|
|
void* aClosure)
|
|
{
|
|
MOZ_ASSERT(aCx);
|
|
MOZ_ASSERT(aWriter);
|
|
MOZ_ASSERT(aClosure);
|
|
|
|
auto* cloneWriteInfo =
|
|
static_cast<IDBObjectStore::StructuredCloneWriteInfo*>(aClosure);
|
|
|
|
if (JS_GetClass(aObj) == IDBObjectStore::DummyPropClass()) {
|
|
MOZ_ASSERT(!cloneWriteInfo->mOffsetToKeyProp);
|
|
cloneWriteInfo->mOffsetToKeyProp = js::GetSCOffset(aWriter);
|
|
|
|
uint64_t value = 0;
|
|
// Omit endian swap
|
|
return JS_WriteBytes(aWriter, &value, sizeof(value));
|
|
}
|
|
|
|
IDBMutableFile* mutableFile;
|
|
if (NS_SUCCEEDED(UNWRAP_OBJECT(IDBMutableFile, aObj, mutableFile))) {
|
|
if (cloneWriteInfo->mDatabase->IsFileHandleDisabled()) {
|
|
return false;
|
|
}
|
|
|
|
IDBDatabase* database = mutableFile->Database();
|
|
MOZ_ASSERT(database);
|
|
|
|
// Throw when trying to store IDBMutableFile objects that live in a
|
|
// different database.
|
|
if (database != cloneWriteInfo->mDatabase) {
|
|
MOZ_ASSERT(!SameCOMIdentity(database, cloneWriteInfo->mDatabase));
|
|
|
|
if (database->Name() != cloneWriteInfo->mDatabase->Name()) {
|
|
return false;
|
|
}
|
|
|
|
nsCString fileOrigin, databaseOrigin;
|
|
PersistenceType filePersistenceType, databasePersistenceType;
|
|
|
|
if (NS_WARN_IF(NS_FAILED(database->GetQuotaInfo(fileOrigin,
|
|
&filePersistenceType)))) {
|
|
return false;
|
|
}
|
|
|
|
if (NS_WARN_IF(NS_FAILED(cloneWriteInfo->mDatabase->GetQuotaInfo(
|
|
databaseOrigin,
|
|
&databasePersistenceType)))) {
|
|
return false;
|
|
}
|
|
|
|
if (filePersistenceType != databasePersistenceType ||
|
|
fileOrigin != databaseOrigin) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (cloneWriteInfo->mBlobOrMutableFiles.Length() > size_t(UINT32_MAX)) {
|
|
MOZ_ASSERT(false, "Fix the structured clone data to use a bigger type!");
|
|
return false;
|
|
}
|
|
|
|
const uint32_t index = cloneWriteInfo->mBlobOrMutableFiles.Length();
|
|
|
|
NS_ConvertUTF16toUTF8 convType(mutableFile->Type());
|
|
uint32_t convTypeLength =
|
|
NativeEndian::swapToLittleEndian(convType.Length());
|
|
|
|
NS_ConvertUTF16toUTF8 convName(mutableFile->Name());
|
|
uint32_t convNameLength =
|
|
NativeEndian::swapToLittleEndian(convName.Length());
|
|
|
|
if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_MUTABLEFILE, uint32_t(index)) ||
|
|
!JS_WriteBytes(aWriter, &convTypeLength, sizeof(uint32_t)) ||
|
|
!JS_WriteBytes(aWriter, convType.get(), convType.Length()) ||
|
|
!JS_WriteBytes(aWriter, &convNameLength, sizeof(uint32_t)) ||
|
|
!JS_WriteBytes(aWriter, convName.get(), convName.Length())) {
|
|
return false;
|
|
}
|
|
|
|
IDBObjectStore::StructuredCloneWriteInfo::BlobOrMutableFile*
|
|
newBlobOrMutableFile =
|
|
cloneWriteInfo->mBlobOrMutableFiles.AppendElement();
|
|
newBlobOrMutableFile->mMutableFile = mutableFile;
|
|
|
|
return true;
|
|
}
|
|
|
|
{
|
|
Blob* blob = nullptr;
|
|
if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob))) {
|
|
ErrorResult rv;
|
|
uint64_t size = blob->GetSize(rv);
|
|
MOZ_ASSERT(!rv.Failed());
|
|
|
|
size = NativeEndian::swapToLittleEndian(size);
|
|
|
|
nsString type;
|
|
blob->GetType(type);
|
|
|
|
NS_ConvertUTF16toUTF8 convType(type);
|
|
uint32_t convTypeLength =
|
|
NativeEndian::swapToLittleEndian(convType.Length());
|
|
|
|
if (cloneWriteInfo->mBlobOrMutableFiles.Length() > size_t(UINT32_MAX)) {
|
|
MOZ_ASSERT(false,
|
|
"Fix the structured clone data to use a bigger type!");
|
|
return false;
|
|
}
|
|
|
|
const uint32_t index = cloneWriteInfo->mBlobOrMutableFiles.Length();
|
|
|
|
if (!JS_WriteUint32Pair(aWriter,
|
|
blob->IsFile() ? SCTAG_DOM_FILE : SCTAG_DOM_BLOB,
|
|
index) ||
|
|
!JS_WriteBytes(aWriter, &size, sizeof(size)) ||
|
|
!JS_WriteBytes(aWriter, &convTypeLength, sizeof(convTypeLength)) ||
|
|
!JS_WriteBytes(aWriter, convType.get(), convType.Length())) {
|
|
return false;
|
|
}
|
|
|
|
RefPtr<File> file = blob->ToFile();
|
|
if (file) {
|
|
ErrorResult rv;
|
|
int64_t lastModifiedDate = file->GetLastModified(rv);
|
|
MOZ_ALWAYS_TRUE(!rv.Failed());
|
|
|
|
lastModifiedDate = NativeEndian::swapToLittleEndian(lastModifiedDate);
|
|
|
|
nsString name;
|
|
file->GetName(name);
|
|
|
|
NS_ConvertUTF16toUTF8 convName(name);
|
|
uint32_t convNameLength =
|
|
NativeEndian::swapToLittleEndian(convName.Length());
|
|
|
|
if (!JS_WriteBytes(aWriter, &lastModifiedDate, sizeof(lastModifiedDate)) ||
|
|
!JS_WriteBytes(aWriter, &convNameLength, sizeof(convNameLength)) ||
|
|
!JS_WriteBytes(aWriter, convName.get(), convName.Length())) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
IDBObjectStore::StructuredCloneWriteInfo::BlobOrMutableFile*
|
|
newBlobOrMutableFile =
|
|
cloneWriteInfo->mBlobOrMutableFiles.AppendElement();
|
|
newBlobOrMutableFile->mBlob = blob;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return StructuredCloneHolder::WriteFullySerializableObjects(aCx, aWriter, aObj);
|
|
}
|
|
|
|
nsresult
|
|
GetAddInfoCallback(JSContext* aCx, void* aClosure)
|
|
{
|
|
static const JSStructuredCloneCallbacks kStructuredCloneCallbacks = {
|
|
nullptr /* read */,
|
|
StructuredCloneWriteCallback /* write */,
|
|
nullptr /* reportError */,
|
|
nullptr /* readTransfer */,
|
|
nullptr /* writeTransfer */,
|
|
nullptr /* freeTransfer */
|
|
};
|
|
|
|
MOZ_ASSERT(aCx);
|
|
|
|
auto* data = static_cast<GetAddInfoClosure*>(aClosure);
|
|
MOZ_ASSERT(data);
|
|
|
|
data->mCloneWriteInfo.mOffsetToKeyProp = 0;
|
|
|
|
if (!data->mCloneWriteInfo.mCloneBuffer.write(aCx,
|
|
data->mValue,
|
|
&kStructuredCloneCallbacks,
|
|
&data->mCloneWriteInfo)) {
|
|
return NS_ERROR_DOM_DATA_CLONE_ERR;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
BlobChild*
|
|
ActorFromRemoteBlobImpl(BlobImpl* aImpl)
|
|
{
|
|
MOZ_ASSERT(aImpl);
|
|
|
|
nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(aImpl);
|
|
if (remoteBlob) {
|
|
BlobChild* actor = remoteBlob->GetBlobChild();
|
|
MOZ_ASSERT(actor);
|
|
|
|
if (actor->GetContentManager()) {
|
|
return nullptr;
|
|
}
|
|
|
|
MOZ_ASSERT(actor->GetBackgroundManager());
|
|
MOZ_ASSERT(BackgroundChild::GetForCurrentThread());
|
|
MOZ_ASSERT(actor->GetBackgroundManager() ==
|
|
BackgroundChild::GetForCurrentThread(),
|
|
"Blob actor is not bound to this thread!");
|
|
|
|
return actor;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
bool
|
|
ResolveMysteryMutableFile(IDBMutableFile* aMutableFile,
|
|
const nsString& aName,
|
|
const nsString& aType)
|
|
{
|
|
MOZ_ASSERT(aMutableFile);
|
|
aMutableFile->SetLazyData(aName, aType);
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
ResolveMysteryFile(BlobImpl* aImpl,
|
|
const nsString& aName,
|
|
const nsString& aContentType,
|
|
uint64_t aSize,
|
|
uint64_t aLastModifiedDate)
|
|
{
|
|
BlobChild* actor = ActorFromRemoteBlobImpl(aImpl);
|
|
if (actor) {
|
|
return actor->SetMysteryBlobInfo(aName, aContentType,
|
|
aSize, aLastModifiedDate,
|
|
BlobDirState::eUnknownIfDir);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
ResolveMysteryBlob(BlobImpl* aImpl,
|
|
const nsString& aContentType,
|
|
uint64_t aSize)
|
|
{
|
|
BlobChild* actor = ActorFromRemoteBlobImpl(aImpl);
|
|
if (actor) {
|
|
return actor->SetMysteryBlobInfo(aContentType, aSize);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
StructuredCloneReadString(JSStructuredCloneReader* aReader,
|
|
nsCString& aString)
|
|
{
|
|
uint32_t length;
|
|
if (!JS_ReadBytes(aReader, &length, sizeof(uint32_t))) {
|
|
NS_WARNING("Failed to read length!");
|
|
return false;
|
|
}
|
|
length = NativeEndian::swapFromLittleEndian(length);
|
|
|
|
if (!aString.SetLength(length, fallible)) {
|
|
NS_WARNING("Out of memory?");
|
|
return false;
|
|
}
|
|
char* buffer = aString.BeginWriting();
|
|
|
|
if (!JS_ReadBytes(aReader, buffer, length)) {
|
|
NS_WARNING("Failed to read type!");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
ReadFileHandle(JSStructuredCloneReader* aReader,
|
|
MutableFileData* aRetval)
|
|
{
|
|
static_assert(SCTAG_DOM_MUTABLEFILE == 0xFFFF8004, "Update me!");
|
|
MOZ_ASSERT(aReader && aRetval);
|
|
|
|
nsCString type;
|
|
if (!StructuredCloneReadString(aReader, type)) {
|
|
return false;
|
|
}
|
|
CopyUTF8toUTF16(type, aRetval->type);
|
|
|
|
nsCString name;
|
|
if (!StructuredCloneReadString(aReader, name)) {
|
|
return false;
|
|
}
|
|
CopyUTF8toUTF16(name, aRetval->name);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
ReadBlobOrFile(JSStructuredCloneReader* aReader,
|
|
uint32_t aTag,
|
|
BlobOrFileData* aRetval)
|
|
{
|
|
static_assert(SCTAG_DOM_BLOB == 0xffff8001 &&
|
|
SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE == 0xffff8002 &&
|
|
SCTAG_DOM_FILE == 0xffff8005,
|
|
"Update me!");
|
|
|
|
MOZ_ASSERT(aReader);
|
|
MOZ_ASSERT(aTag == SCTAG_DOM_FILE ||
|
|
aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
|
|
aTag == SCTAG_DOM_BLOB);
|
|
MOZ_ASSERT(aRetval);
|
|
|
|
aRetval->tag = aTag;
|
|
|
|
uint64_t size;
|
|
if (NS_WARN_IF(!JS_ReadBytes(aReader, &size, sizeof(uint64_t)))) {
|
|
return false;
|
|
}
|
|
|
|
aRetval->size = NativeEndian::swapFromLittleEndian(size);
|
|
|
|
nsCString type;
|
|
if (NS_WARN_IF(!StructuredCloneReadString(aReader, type))) {
|
|
return false;
|
|
}
|
|
|
|
CopyUTF8toUTF16(type, aRetval->type);
|
|
|
|
// Blobs are done.
|
|
if (aTag == SCTAG_DOM_BLOB) {
|
|
return true;
|
|
}
|
|
|
|
MOZ_ASSERT(aTag == SCTAG_DOM_FILE ||
|
|
aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE);
|
|
|
|
int64_t lastModifiedDate;
|
|
if (aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE) {
|
|
lastModifiedDate = INT64_MAX;
|
|
} else {
|
|
if (NS_WARN_IF(!JS_ReadBytes(aReader, &lastModifiedDate,
|
|
sizeof(lastModifiedDate)))) {
|
|
return false;
|
|
}
|
|
lastModifiedDate = NativeEndian::swapFromLittleEndian(lastModifiedDate);
|
|
}
|
|
|
|
aRetval->lastModifiedDate = lastModifiedDate;
|
|
|
|
nsCString name;
|
|
if (NS_WARN_IF(!StructuredCloneReadString(aReader, name))) {
|
|
return false;
|
|
}
|
|
|
|
CopyUTF8toUTF16(name, aRetval->name);
|
|
|
|
return true;
|
|
}
|
|
|
|
class ValueDeserializationHelper
|
|
{
|
|
public:
|
|
static bool
|
|
CreateAndWrapMutableFile(JSContext* aCx,
|
|
StructuredCloneFile& aFile,
|
|
const MutableFileData& aData,
|
|
JS::MutableHandle<JSObject*> aResult)
|
|
{
|
|
MOZ_ASSERT(aCx);
|
|
MOZ_ASSERT(aFile.mMutable);
|
|
|
|
if (!aFile.mMutableFile || !NS_IsMainThread()) {
|
|
return false;
|
|
}
|
|
|
|
if (NS_WARN_IF(!ResolveMysteryMutableFile(aFile.mMutableFile,
|
|
aData.name,
|
|
aData.type))) {
|
|
return false;
|
|
}
|
|
|
|
JS::Rooted<JS::Value> wrappedMutableFile(aCx);
|
|
if (!ToJSValue(aCx, aFile.mMutableFile, &wrappedMutableFile)) {
|
|
return false;
|
|
}
|
|
|
|
aResult.set(&wrappedMutableFile.toObject());
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
CreateAndWrapBlobOrFile(JSContext* aCx,
|
|
IDBDatabase* aDatabase,
|
|
StructuredCloneFile& aFile,
|
|
const BlobOrFileData& aData,
|
|
JS::MutableHandle<JSObject*> aResult)
|
|
{
|
|
MOZ_ASSERT(aCx);
|
|
MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE ||
|
|
aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
|
|
aData.tag == SCTAG_DOM_BLOB);
|
|
MOZ_ASSERT(!aFile.mMutable);
|
|
MOZ_ASSERT(aFile.mBlob);
|
|
|
|
// It can happen that this IDB is chrome code, so there is no parent, but
|
|
// still we want to set a correct parent for the new File object.
|
|
nsCOMPtr<nsISupports> parent;
|
|
if (NS_IsMainThread()) {
|
|
if (aDatabase && aDatabase->GetParentObject()) {
|
|
parent = aDatabase->GetParentObject();
|
|
} else {
|
|
parent = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
|
|
}
|
|
} else {
|
|
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
|
MOZ_ASSERT(workerPrivate);
|
|
|
|
WorkerGlobalScope* globalScope = workerPrivate->GlobalScope();
|
|
MOZ_ASSERT(globalScope);
|
|
|
|
parent = do_QueryObject(globalScope);
|
|
}
|
|
|
|
MOZ_ASSERT(parent);
|
|
|
|
if (aData.tag == SCTAG_DOM_BLOB) {
|
|
if (NS_WARN_IF(!ResolveMysteryBlob(aFile.mBlob->Impl(),
|
|
aData.type,
|
|
aData.size))) {
|
|
return false;
|
|
}
|
|
|
|
MOZ_ASSERT(!aFile.mBlob->IsFile());
|
|
|
|
JS::Rooted<JS::Value> wrappedBlob(aCx);
|
|
if (!ToJSValue(aCx, aFile.mBlob, &wrappedBlob)) {
|
|
return false;
|
|
}
|
|
|
|
aResult.set(&wrappedBlob.toObject());
|
|
return true;
|
|
}
|
|
|
|
if (NS_WARN_IF(!ResolveMysteryFile(aFile.mBlob->Impl(),
|
|
aData.name,
|
|
aData.type,
|
|
aData.size,
|
|
aData.lastModifiedDate))) {
|
|
return false;
|
|
}
|
|
|
|
MOZ_ASSERT(aFile.mBlob->IsFile());
|
|
RefPtr<File> file = aFile.mBlob->ToFile();
|
|
MOZ_ASSERT(file);
|
|
|
|
JS::Rooted<JS::Value> wrappedFile(aCx);
|
|
if (!ToJSValue(aCx, file, &wrappedFile)) {
|
|
return false;
|
|
}
|
|
|
|
aResult.set(&wrappedFile.toObject());
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class IndexDeserializationHelper
|
|
{
|
|
public:
|
|
static bool
|
|
CreateAndWrapMutableFile(JSContext* aCx,
|
|
StructuredCloneFile& aFile,
|
|
const MutableFileData& aData,
|
|
JS::MutableHandle<JSObject*> aResult)
|
|
{
|
|
// MutableFile can't be used in index creation, so just make a dummy object.
|
|
JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
|
|
if (NS_WARN_IF(!obj)) {
|
|
return false;
|
|
}
|
|
|
|
aResult.set(obj);
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
CreateAndWrapBlobOrFile(JSContext* aCx,
|
|
IDBDatabase* aDatabase,
|
|
StructuredCloneFile& aFile,
|
|
const BlobOrFileData& aData,
|
|
JS::MutableHandle<JSObject*> aResult)
|
|
{
|
|
MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE ||
|
|
aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
|
|
aData.tag == SCTAG_DOM_BLOB);
|
|
|
|
// The following properties are available for use in index creation
|
|
// Blob.size
|
|
// Blob.type
|
|
// File.name
|
|
// File.lastModifiedDate
|
|
|
|
JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
|
|
if (NS_WARN_IF(!obj)) {
|
|
return false;
|
|
}
|
|
|
|
// Technically these props go on the proto, but this detail won't change
|
|
// the results of index creation.
|
|
|
|
JS::Rooted<JSString*> type(aCx,
|
|
JS_NewUCStringCopyN(aCx, aData.type.get(), aData.type.Length()));
|
|
if (NS_WARN_IF(!type)) {
|
|
return false;
|
|
}
|
|
|
|
if (NS_WARN_IF(!JS_DefineProperty(aCx,
|
|
obj,
|
|
"size",
|
|
double(aData.size),
|
|
0))) {
|
|
return false;
|
|
}
|
|
|
|
if (NS_WARN_IF(!JS_DefineProperty(aCx, obj, "type", type, 0))) {
|
|
return false;
|
|
}
|
|
|
|
if (aData.tag == SCTAG_DOM_BLOB) {
|
|
aResult.set(obj);
|
|
return true;
|
|
}
|
|
|
|
JS::Rooted<JSString*> name(aCx,
|
|
JS_NewUCStringCopyN(aCx, aData.name.get(), aData.name.Length()));
|
|
if (NS_WARN_IF(!name)) {
|
|
return false;
|
|
}
|
|
|
|
JS::ClippedTime time = JS::TimeClip(aData.lastModifiedDate);
|
|
JS::Rooted<JSObject*> date(aCx, JS::NewDateObject(aCx, time));
|
|
if (NS_WARN_IF(!date)) {
|
|
return false;
|
|
}
|
|
|
|
if (NS_WARN_IF(!JS_DefineProperty(aCx, obj, "name", name, 0))) {
|
|
return false;
|
|
}
|
|
|
|
if (NS_WARN_IF(!JS_DefineProperty(aCx, obj, "lastModifiedDate", date, 0))) {
|
|
return false;
|
|
}
|
|
|
|
aResult.set(obj);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
// We don't need to upgrade database on B2G. See the comment in ActorsParent.cpp,
|
|
// UpgradeSchemaFrom18_0To19_0()
|
|
#if !defined(MOZ_B2G)
|
|
|
|
class UpgradeDeserializationHelper
|
|
{
|
|
public:
|
|
static bool
|
|
CreateAndWrapMutableFile(JSContext* aCx,
|
|
StructuredCloneFile& aFile,
|
|
const MutableFileData& aData,
|
|
JS::MutableHandle<JSObject*> aResult)
|
|
{
|
|
MOZ_ASSERT(aCx);
|
|
MOZ_ASSERT(!aFile.mMutable);
|
|
|
|
aFile.mMutable = true;
|
|
|
|
// Just make a dummy object. The file_ids upgrade function is only
|
|
// interested in the |mMutable| flag.
|
|
JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
|
|
|
|
if (NS_WARN_IF(!obj)) {
|
|
return false;
|
|
}
|
|
|
|
aResult.set(obj);
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
CreateAndWrapBlobOrFile(JSContext* aCx,
|
|
IDBDatabase* aDatabase,
|
|
StructuredCloneFile& aFile,
|
|
const BlobOrFileData& aData,
|
|
JS::MutableHandle<JSObject*> aResult)
|
|
{
|
|
MOZ_ASSERT(aCx);
|
|
MOZ_ASSERT(!aFile.mMutable);
|
|
MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE ||
|
|
aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
|
|
aData.tag == SCTAG_DOM_BLOB);
|
|
|
|
// Just make a dummy object. The file_ids upgrade function is only interested
|
|
// in the |mMutable| flag.
|
|
JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
|
|
|
|
if (NS_WARN_IF(!obj)) {
|
|
return false;
|
|
}
|
|
|
|
aResult.set(obj);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
#endif // MOZ_B2G
|
|
|
|
template <class Traits>
|
|
JSObject*
|
|
CommonStructuredCloneReadCallback(JSContext* aCx,
|
|
JSStructuredCloneReader* aReader,
|
|
uint32_t aTag,
|
|
uint32_t aData,
|
|
void* aClosure)
|
|
{
|
|
// We need to statically assert that our tag values are what we expect
|
|
// so that if people accidentally change them they notice.
|
|
static_assert(SCTAG_DOM_BLOB == 0xffff8001 &&
|
|
SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE == 0xffff8002 &&
|
|
SCTAG_DOM_MUTABLEFILE == 0xffff8004 &&
|
|
SCTAG_DOM_FILE == 0xffff8005,
|
|
"You changed our structured clone tag values and just ate "
|
|
"everyone's IndexedDB data. I hope you are happy.");
|
|
|
|
if (aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
|
|
aTag == SCTAG_DOM_BLOB ||
|
|
aTag == SCTAG_DOM_FILE ||
|
|
aTag == SCTAG_DOM_MUTABLEFILE) {
|
|
auto* cloneReadInfo = static_cast<StructuredCloneReadInfo*>(aClosure);
|
|
|
|
if (aData >= cloneReadInfo->mFiles.Length()) {
|
|
MOZ_ASSERT(false, "Bad index value!");
|
|
return nullptr;
|
|
}
|
|
|
|
StructuredCloneFile& file = cloneReadInfo->mFiles[aData];
|
|
|
|
JS::Rooted<JSObject*> result(aCx);
|
|
|
|
if (aTag == SCTAG_DOM_MUTABLEFILE) {
|
|
MutableFileData data;
|
|
if (NS_WARN_IF(!ReadFileHandle(aReader, &data))) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (NS_WARN_IF(!Traits::CreateAndWrapMutableFile(aCx,
|
|
file,
|
|
data,
|
|
&result))) {
|
|
return nullptr;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
BlobOrFileData data;
|
|
if (NS_WARN_IF(!ReadBlobOrFile(aReader, aTag, &data))) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (NS_WARN_IF(!Traits::CreateAndWrapBlobOrFile(aCx,
|
|
cloneReadInfo->mDatabase,
|
|
file,
|
|
data,
|
|
&result))) {
|
|
return nullptr;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
return StructuredCloneHolder::ReadFullySerializableObjects(aCx, aReader,
|
|
aTag);
|
|
}
|
|
|
|
// static
|
|
void
|
|
ClearStructuredCloneBuffer(JSAutoStructuredCloneBuffer& aBuffer)
|
|
{
|
|
if (aBuffer.data()) {
|
|
aBuffer.clear();
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
const JSClass IDBObjectStore::sDummyPropJSClass = {
|
|
"IDBObjectStore Dummy",
|
|
0 /* flags */
|
|
};
|
|
|
|
IDBObjectStore::IDBObjectStore(IDBTransaction* aTransaction,
|
|
const ObjectStoreSpec* aSpec)
|
|
: mTransaction(aTransaction)
|
|
, mCachedKeyPath(JS::UndefinedValue())
|
|
, mSpec(aSpec)
|
|
, mId(aSpec->metadata().id())
|
|
, mRooted(false)
|
|
{
|
|
MOZ_ASSERT(aTransaction);
|
|
aTransaction->AssertIsOnOwningThread();
|
|
MOZ_ASSERT(aSpec);
|
|
}
|
|
|
|
IDBObjectStore::~IDBObjectStore()
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
if (mRooted) {
|
|
mCachedKeyPath.setUndefined();
|
|
mozilla::DropJSObjects(this);
|
|
}
|
|
}
|
|
|
|
// static
|
|
already_AddRefed<IDBObjectStore>
|
|
IDBObjectStore::Create(IDBTransaction* aTransaction,
|
|
const ObjectStoreSpec& aSpec)
|
|
{
|
|
MOZ_ASSERT(aTransaction);
|
|
aTransaction->AssertIsOnOwningThread();
|
|
|
|
RefPtr<IDBObjectStore> objectStore =
|
|
new IDBObjectStore(aTransaction, &aSpec);
|
|
|
|
return objectStore.forget();
|
|
}
|
|
|
|
// static
|
|
nsresult
|
|
IDBObjectStore::AppendIndexUpdateInfo(
|
|
int64_t aIndexID,
|
|
const KeyPath& aKeyPath,
|
|
bool aUnique,
|
|
bool aMultiEntry,
|
|
const nsCString& aLocale,
|
|
JSContext* aCx,
|
|
JS::Handle<JS::Value> aVal,
|
|
nsTArray<IndexUpdateInfo>& aUpdateInfoArray)
|
|
{
|
|
nsresult rv;
|
|
|
|
#ifdef ENABLE_INTL_API
|
|
const bool localeAware = !aLocale.IsEmpty();
|
|
#endif
|
|
|
|
if (!aMultiEntry) {
|
|
Key key;
|
|
rv = aKeyPath.ExtractKey(aCx, aVal, key);
|
|
|
|
// If an index's keyPath doesn't match an object, we ignore that object.
|
|
if (rv == NS_ERROR_DOM_INDEXEDDB_DATA_ERR || key.IsUnset()) {
|
|
return NS_OK;
|
|
}
|
|
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement();
|
|
updateInfo->indexId() = aIndexID;
|
|
updateInfo->value() = key;
|
|
#ifdef ENABLE_INTL_API
|
|
if (localeAware) {
|
|
rv = key.ToLocaleBasedKey(updateInfo->localizedValue(), aLocale);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
JS::Rooted<JS::Value> val(aCx);
|
|
if (NS_FAILED(aKeyPath.ExtractKeyAsJSVal(aCx, aVal, val.address()))) {
|
|
return NS_OK;
|
|
}
|
|
|
|
bool isArray;
|
|
if (!JS_IsArrayObject(aCx, val, &isArray)) {
|
|
IDB_REPORT_INTERNAL_ERR();
|
|
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
|
}
|
|
if (isArray) {
|
|
JS::Rooted<JSObject*> array(aCx, &val.toObject());
|
|
uint32_t arrayLength;
|
|
if (NS_WARN_IF(!JS_GetArrayLength(aCx, array, &arrayLength))) {
|
|
IDB_REPORT_INTERNAL_ERR();
|
|
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
|
}
|
|
|
|
for (uint32_t arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) {
|
|
JS::Rooted<JS::Value> arrayItem(aCx);
|
|
if (NS_WARN_IF(!JS_GetElement(aCx, array, arrayIndex, &arrayItem))) {
|
|
IDB_REPORT_INTERNAL_ERR();
|
|
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
|
}
|
|
|
|
Key value;
|
|
if (NS_FAILED(value.SetFromJSVal(aCx, arrayItem)) ||
|
|
value.IsUnset()) {
|
|
// Not a value we can do anything with, ignore it.
|
|
continue;
|
|
}
|
|
|
|
IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement();
|
|
updateInfo->indexId() = aIndexID;
|
|
updateInfo->value() = value;
|
|
#ifdef ENABLE_INTL_API
|
|
if (localeAware) {
|
|
rv = value.ToLocaleBasedKey(updateInfo->localizedValue(), aLocale);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
else {
|
|
Key value;
|
|
if (NS_FAILED(value.SetFromJSVal(aCx, val)) ||
|
|
value.IsUnset()) {
|
|
// Not a value we can do anything with, ignore it.
|
|
return NS_OK;
|
|
}
|
|
|
|
IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement();
|
|
updateInfo->indexId() = aIndexID;
|
|
updateInfo->value() = value;
|
|
#ifdef ENABLE_INTL_API
|
|
if (localeAware) {
|
|
rv = value.ToLocaleBasedKey(updateInfo->localizedValue(), aLocale);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// static
|
|
void
|
|
IDBObjectStore::ClearCloneReadInfo(StructuredCloneReadInfo& aReadInfo)
|
|
{
|
|
// This is kind of tricky, we only want to release stuff on the main thread,
|
|
// but we can end up being called on other threads if we have already been
|
|
// cleared on the main thread.
|
|
if (!aReadInfo.mCloneBuffer.data() && !aReadInfo.mFiles.Length()) {
|
|
return;
|
|
}
|
|
|
|
ClearStructuredCloneBuffer(aReadInfo.mCloneBuffer);
|
|
aReadInfo.mFiles.Clear();
|
|
}
|
|
|
|
// static
|
|
bool
|
|
IDBObjectStore::DeserializeValue(JSContext* aCx,
|
|
StructuredCloneReadInfo& aCloneReadInfo,
|
|
JS::MutableHandle<JS::Value> aValue)
|
|
{
|
|
MOZ_ASSERT(aCx);
|
|
|
|
if (aCloneReadInfo.mData.IsEmpty()) {
|
|
aValue.setUndefined();
|
|
return true;
|
|
}
|
|
|
|
auto* data = reinterpret_cast<uint64_t*>(aCloneReadInfo.mData.Elements());
|
|
size_t dataLen = aCloneReadInfo.mData.Length();
|
|
|
|
MOZ_ASSERT(!(dataLen % sizeof(*data)));
|
|
|
|
JSAutoRequest ar(aCx);
|
|
|
|
static const JSStructuredCloneCallbacks callbacks = {
|
|
CommonStructuredCloneReadCallback<ValueDeserializationHelper>,
|
|
nullptr,
|
|
nullptr,
|
|
nullptr,
|
|
nullptr,
|
|
nullptr
|
|
};
|
|
|
|
// FIXME: Consider to use StructuredCloneHolder here and in other
|
|
// deserializing methods.
|
|
if (!JS_ReadStructuredClone(aCx, data, dataLen, JS_STRUCTURED_CLONE_VERSION,
|
|
aValue, &callbacks, &aCloneReadInfo)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// static
|
|
bool
|
|
IDBObjectStore::DeserializeIndexValue(JSContext* aCx,
|
|
StructuredCloneReadInfo& aCloneReadInfo,
|
|
JS::MutableHandle<JS::Value> aValue)
|
|
{
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
MOZ_ASSERT(aCx);
|
|
|
|
if (aCloneReadInfo.mData.IsEmpty()) {
|
|
aValue.setUndefined();
|
|
return true;
|
|
}
|
|
|
|
size_t dataLen = aCloneReadInfo.mData.Length();
|
|
|
|
uint64_t* data =
|
|
const_cast<uint64_t*>(reinterpret_cast<uint64_t*>(
|
|
aCloneReadInfo.mData.Elements()));
|
|
|
|
MOZ_ASSERT(!(dataLen % sizeof(*data)));
|
|
|
|
JSAutoRequest ar(aCx);
|
|
|
|
static const JSStructuredCloneCallbacks callbacks = {
|
|
CommonStructuredCloneReadCallback<IndexDeserializationHelper>,
|
|
nullptr,
|
|
nullptr
|
|
};
|
|
|
|
if (!JS_ReadStructuredClone(aCx, data, dataLen, JS_STRUCTURED_CLONE_VERSION,
|
|
aValue, &callbacks, &aCloneReadInfo)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#if !defined(MOZ_B2G)
|
|
|
|
// static
|
|
bool
|
|
IDBObjectStore::DeserializeUpgradeValue(JSContext* aCx,
|
|
StructuredCloneReadInfo& aCloneReadInfo,
|
|
JS::MutableHandle<JS::Value> aValue)
|
|
{
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
MOZ_ASSERT(aCx);
|
|
|
|
if (aCloneReadInfo.mData.IsEmpty()) {
|
|
aValue.setUndefined();
|
|
return true;
|
|
}
|
|
|
|
size_t dataLen = aCloneReadInfo.mData.Length();
|
|
|
|
uint64_t* data =
|
|
const_cast<uint64_t*>(reinterpret_cast<uint64_t*>(
|
|
aCloneReadInfo.mData.Elements()));
|
|
|
|
MOZ_ASSERT(!(dataLen % sizeof(*data)));
|
|
|
|
JSAutoRequest ar(aCx);
|
|
|
|
static JSStructuredCloneCallbacks callbacks = {
|
|
CommonStructuredCloneReadCallback<UpgradeDeserializationHelper>,
|
|
nullptr,
|
|
nullptr,
|
|
nullptr,
|
|
nullptr,
|
|
nullptr
|
|
};
|
|
|
|
if (!JS_ReadStructuredClone(aCx, data, dataLen, JS_STRUCTURED_CLONE_VERSION,
|
|
aValue, &callbacks, &aCloneReadInfo)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#endif // MOZ_B2G
|
|
|
|
#ifdef DEBUG
|
|
|
|
void
|
|
IDBObjectStore::AssertIsOnOwningThread() const
|
|
{
|
|
MOZ_ASSERT(mTransaction);
|
|
mTransaction->AssertIsOnOwningThread();
|
|
}
|
|
|
|
#endif // DEBUG
|
|
|
|
nsresult
|
|
IDBObjectStore::GetAddInfo(JSContext* aCx,
|
|
JS::Handle<JS::Value> aValue,
|
|
JS::Handle<JS::Value> aKeyVal,
|
|
StructuredCloneWriteInfo& aCloneWriteInfo,
|
|
Key& aKey,
|
|
nsTArray<IndexUpdateInfo>& aUpdateInfoArray)
|
|
{
|
|
// Return DATA_ERR if a key was passed in and this objectStore uses inline
|
|
// keys.
|
|
if (!aKeyVal.isUndefined() && HasValidKeyPath()) {
|
|
return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
|
|
}
|
|
|
|
bool isAutoIncrement = AutoIncrement();
|
|
|
|
nsresult rv;
|
|
|
|
if (!HasValidKeyPath()) {
|
|
// Out-of-line keys must be passed in.
|
|
rv = aKey.SetFromJSVal(aCx, aKeyVal);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
} else if (!isAutoIncrement) {
|
|
rv = GetKeyPath().ExtractKey(aCx, aValue, aKey);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
// Return DATA_ERR if no key was specified this isn't an autoIncrement
|
|
// objectStore.
|
|
if (aKey.IsUnset() && !isAutoIncrement) {
|
|
return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
|
|
}
|
|
|
|
// Figure out indexes and the index values to update here.
|
|
const nsTArray<IndexMetadata>& indexes = mSpec->indexes();
|
|
|
|
const uint32_t idxCount = indexes.Length();
|
|
aUpdateInfoArray.SetCapacity(idxCount); // Pretty good estimate
|
|
|
|
for (uint32_t idxIndex = 0; idxIndex < idxCount; idxIndex++) {
|
|
const IndexMetadata& metadata = indexes[idxIndex];
|
|
|
|
rv = AppendIndexUpdateInfo(metadata.id(), metadata.keyPath(),
|
|
metadata.unique(), metadata.multiEntry(),
|
|
metadata.locale(), aCx, aValue,
|
|
aUpdateInfoArray);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
GetAddInfoClosure data(aCloneWriteInfo, aValue);
|
|
|
|
if (isAutoIncrement && HasValidKeyPath()) {
|
|
MOZ_ASSERT(aKey.IsUnset());
|
|
|
|
rv = GetKeyPath().ExtractOrCreateKey(aCx,
|
|
aValue,
|
|
aKey,
|
|
&GetAddInfoCallback,
|
|
&data);
|
|
} else {
|
|
rv = GetAddInfoCallback(aCx, &data);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
already_AddRefed<IDBRequest>
|
|
IDBObjectStore::AddOrPut(JSContext* aCx,
|
|
JS::Handle<JS::Value> aValue,
|
|
JS::Handle<JS::Value> aKey,
|
|
bool aOverwrite,
|
|
bool aFromCursor,
|
|
ErrorResult& aRv)
|
|
{
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(aCx);
|
|
MOZ_ASSERT_IF(aFromCursor, aOverwrite);
|
|
|
|
if (mDeletedSpec) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
if (!mTransaction->IsOpen()) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
if (!mTransaction->IsWriteAllowed()) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
JS::Rooted<JS::Value> value(aCx, aValue);
|
|
Key key;
|
|
StructuredCloneWriteInfo cloneWriteInfo(mTransaction->Database());
|
|
nsTArray<IndexUpdateInfo> updateInfo;
|
|
|
|
aRv = GetAddInfo(aCx, value, aKey, cloneWriteInfo, key, updateInfo);
|
|
if (aRv.Failed()) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (!mTransaction->IsOpen()) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
FallibleTArray<uint8_t> cloneData;
|
|
if (NS_WARN_IF(!cloneData.SetLength(cloneWriteInfo.mCloneBuffer.nbytes(),
|
|
fallible))) {
|
|
aRv = NS_ERROR_OUT_OF_MEMORY;
|
|
return nullptr;
|
|
}
|
|
|
|
// XXX Remove this
|
|
memcpy(cloneData.Elements(), cloneWriteInfo.mCloneBuffer.data(),
|
|
cloneWriteInfo.mCloneBuffer.nbytes());
|
|
|
|
cloneWriteInfo.mCloneBuffer.clear();
|
|
|
|
ObjectStoreAddPutParams commonParams;
|
|
commonParams.objectStoreId() = Id();
|
|
commonParams.cloneInfo().data().SwapElements(cloneData);
|
|
commonParams.cloneInfo().offsetToKeyProp() = cloneWriteInfo.mOffsetToKeyProp;
|
|
commonParams.key() = key;
|
|
commonParams.indexUpdateInfos().SwapElements(updateInfo);
|
|
|
|
// Convert any blobs or mutable files into DatabaseOrMutableFile.
|
|
nsTArray<StructuredCloneWriteInfo::BlobOrMutableFile>& blobOrMutableFiles =
|
|
cloneWriteInfo.mBlobOrMutableFiles;
|
|
|
|
if (!blobOrMutableFiles.IsEmpty()) {
|
|
const uint32_t count = blobOrMutableFiles.Length();
|
|
|
|
FallibleTArray<DatabaseOrMutableFile> fileOrMutableFileActors;
|
|
if (NS_WARN_IF(!fileOrMutableFileActors.SetCapacity(count, fallible))) {
|
|
aRv = NS_ERROR_OUT_OF_MEMORY;
|
|
return nullptr;
|
|
}
|
|
|
|
IDBDatabase* database = mTransaction->Database();
|
|
|
|
for (uint32_t index = 0; index < count; index++) {
|
|
StructuredCloneWriteInfo::BlobOrMutableFile& blobOrMutableFile =
|
|
blobOrMutableFiles[index];
|
|
MOZ_ASSERT((blobOrMutableFile.mBlob && !blobOrMutableFile.mMutableFile) ||
|
|
(!blobOrMutableFile.mBlob && blobOrMutableFile.mMutableFile));
|
|
|
|
if (blobOrMutableFile.mBlob) {
|
|
PBackgroundIDBDatabaseFileChild* fileActor =
|
|
database->GetOrCreateFileActorForBlob(blobOrMutableFile.mBlob);
|
|
if (NS_WARN_IF(!fileActor)) {
|
|
IDB_REPORT_INTERNAL_ERR();
|
|
aRv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
|
return nullptr;
|
|
}
|
|
|
|
MOZ_ALWAYS_TRUE(fileOrMutableFileActors.AppendElement(fileActor,
|
|
fallible));
|
|
} else {
|
|
PBackgroundMutableFileChild* mutableFileActor =
|
|
blobOrMutableFile.mMutableFile->GetBackgroundActor();
|
|
if (NS_WARN_IF(!mutableFileActor)) {
|
|
IDB_REPORT_INTERNAL_ERR();
|
|
aRv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
|
return nullptr;
|
|
}
|
|
|
|
MOZ_ALWAYS_TRUE(fileOrMutableFileActors.AppendElement(mutableFileActor,
|
|
fallible));
|
|
}
|
|
}
|
|
|
|
commonParams.files().SwapElements(fileOrMutableFileActors);
|
|
}
|
|
|
|
RequestParams params;
|
|
if (aOverwrite) {
|
|
params = ObjectStorePutParams(commonParams);
|
|
} else {
|
|
params = ObjectStoreAddParams(commonParams);
|
|
}
|
|
|
|
RefPtr<IDBRequest> request = GenerateRequest(this);
|
|
MOZ_ASSERT(request);
|
|
|
|
if (!aFromCursor) {
|
|
if (aOverwrite) {
|
|
IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: "
|
|
"database(%s).transaction(%s).objectStore(%s).put(%s)",
|
|
"IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.put()",
|
|
IDB_LOG_ID_STRING(),
|
|
mTransaction->LoggingSerialNumber(),
|
|
request->LoggingSerialNumber(),
|
|
IDB_LOG_STRINGIFY(mTransaction->Database()),
|
|
IDB_LOG_STRINGIFY(mTransaction),
|
|
IDB_LOG_STRINGIFY(this),
|
|
IDB_LOG_STRINGIFY(key));
|
|
} else {
|
|
IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: "
|
|
"database(%s).transaction(%s).objectStore(%s).add(%s)",
|
|
"IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.add()",
|
|
IDB_LOG_ID_STRING(),
|
|
mTransaction->LoggingSerialNumber(),
|
|
request->LoggingSerialNumber(),
|
|
IDB_LOG_STRINGIFY(mTransaction->Database()),
|
|
IDB_LOG_STRINGIFY(mTransaction),
|
|
IDB_LOG_STRINGIFY(this),
|
|
IDB_LOG_STRINGIFY(key));
|
|
}
|
|
}
|
|
|
|
mTransaction->StartRequest(request, params);
|
|
|
|
return request.forget();
|
|
}
|
|
|
|
already_AddRefed<IDBRequest>
|
|
IDBObjectStore::GetAllInternal(bool aKeysOnly,
|
|
JSContext* aCx,
|
|
JS::Handle<JS::Value> aKey,
|
|
const Optional<uint32_t>& aLimit,
|
|
ErrorResult& aRv)
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
if (mDeletedSpec) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
if (!mTransaction->IsOpen()) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<IDBKeyRange> keyRange;
|
|
aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
const int64_t id = Id();
|
|
|
|
OptionalKeyRange optionalKeyRange;
|
|
if (keyRange) {
|
|
SerializedKeyRange serializedKeyRange;
|
|
keyRange->ToSerialized(serializedKeyRange);
|
|
optionalKeyRange = serializedKeyRange;
|
|
} else {
|
|
optionalKeyRange = void_t();
|
|
}
|
|
|
|
const uint32_t limit = aLimit.WasPassed() ? aLimit.Value() : 0;
|
|
|
|
RequestParams params;
|
|
if (aKeysOnly) {
|
|
params = ObjectStoreGetAllKeysParams(id, optionalKeyRange, limit);
|
|
} else {
|
|
params = ObjectStoreGetAllParams(id, optionalKeyRange, limit);
|
|
}
|
|
|
|
RefPtr<IDBRequest> request = GenerateRequest(this);
|
|
MOZ_ASSERT(request);
|
|
|
|
if (aKeysOnly) {
|
|
IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: "
|
|
"database(%s).transaction(%s).objectStore(%s)."
|
|
"getAllKeys(%s, %s)",
|
|
"IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.getAllKeys()",
|
|
IDB_LOG_ID_STRING(),
|
|
mTransaction->LoggingSerialNumber(),
|
|
request->LoggingSerialNumber(),
|
|
IDB_LOG_STRINGIFY(mTransaction->Database()),
|
|
IDB_LOG_STRINGIFY(mTransaction),
|
|
IDB_LOG_STRINGIFY(this),
|
|
IDB_LOG_STRINGIFY(keyRange),
|
|
IDB_LOG_STRINGIFY(aLimit));
|
|
} else {
|
|
IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: "
|
|
"database(%s).transaction(%s).objectStore(%s)."
|
|
"getAll(%s, %s)",
|
|
"IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.getAll()",
|
|
IDB_LOG_ID_STRING(),
|
|
mTransaction->LoggingSerialNumber(),
|
|
request->LoggingSerialNumber(),
|
|
IDB_LOG_STRINGIFY(mTransaction->Database()),
|
|
IDB_LOG_STRINGIFY(mTransaction),
|
|
IDB_LOG_STRINGIFY(this),
|
|
IDB_LOG_STRINGIFY(keyRange),
|
|
IDB_LOG_STRINGIFY(aLimit));
|
|
}
|
|
|
|
mTransaction->StartRequest(request, params);
|
|
|
|
return request.forget();
|
|
}
|
|
|
|
already_AddRefed<IDBRequest>
|
|
IDBObjectStore::Clear(ErrorResult& aRv)
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
if (mDeletedSpec) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
if (!mTransaction->IsOpen()) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
if (!mTransaction->IsWriteAllowed()) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
ObjectStoreClearParams params;
|
|
params.objectStoreId() = Id();
|
|
|
|
RefPtr<IDBRequest> request = GenerateRequest(this);
|
|
MOZ_ASSERT(request);
|
|
|
|
IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: "
|
|
"database(%s).transaction(%s).objectStore(%s).clear()",
|
|
"IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.clear()",
|
|
IDB_LOG_ID_STRING(),
|
|
mTransaction->LoggingSerialNumber(),
|
|
request->LoggingSerialNumber(),
|
|
IDB_LOG_STRINGIFY(mTransaction->Database()),
|
|
IDB_LOG_STRINGIFY(mTransaction),
|
|
IDB_LOG_STRINGIFY(this));
|
|
|
|
mTransaction->StartRequest(request, params);
|
|
|
|
return request.forget();
|
|
}
|
|
|
|
already_AddRefed<IDBIndex>
|
|
IDBObjectStore::Index(const nsAString& aName, ErrorResult &aRv)
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
if (mTransaction->IsCommittingOrDone() || mDeletedSpec) {
|
|
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
const nsTArray<IndexMetadata>& indexes = mSpec->indexes();
|
|
|
|
const IndexMetadata* metadata = nullptr;
|
|
|
|
for (uint32_t idxCount = indexes.Length(), idxIndex = 0;
|
|
idxIndex < idxCount;
|
|
idxIndex++) {
|
|
const IndexMetadata& index = indexes[idxIndex];
|
|
if (index.name() == aName) {
|
|
metadata = &index;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!metadata) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
const int64_t desiredId = metadata->id();
|
|
|
|
RefPtr<IDBIndex> index;
|
|
|
|
for (uint32_t idxCount = mIndexes.Length(), idxIndex = 0;
|
|
idxIndex < idxCount;
|
|
idxIndex++) {
|
|
RefPtr<IDBIndex>& existingIndex = mIndexes[idxIndex];
|
|
|
|
if (existingIndex->Id() == desiredId) {
|
|
index = existingIndex;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!index) {
|
|
index = IDBIndex::Create(this, *metadata);
|
|
MOZ_ASSERT(index);
|
|
|
|
mIndexes.AppendElement(index);
|
|
}
|
|
|
|
return index.forget();
|
|
}
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(IDBObjectStore)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBObjectStore)
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedKeyPath)
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBObjectStore)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexes);
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBObjectStore)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
|
|
|
// Don't unlink mTransaction!
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexes);
|
|
|
|
tmp->mCachedKeyPath.setUndefined();
|
|
|
|
if (tmp->mRooted) {
|
|
mozilla::DropJSObjects(tmp);
|
|
tmp->mRooted = false;
|
|
}
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBObjectStore)
|
|
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
|
NS_INTERFACE_MAP_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBObjectStore)
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBObjectStore)
|
|
|
|
JSObject*
|
|
IDBObjectStore::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
|
{
|
|
return IDBObjectStoreBinding::Wrap(aCx, this, aGivenProto);
|
|
}
|
|
|
|
nsPIDOMWindow*
|
|
IDBObjectStore::GetParentObject() const
|
|
{
|
|
return mTransaction->GetParentObject();
|
|
}
|
|
|
|
void
|
|
IDBObjectStore::GetKeyPath(JSContext* aCx,
|
|
JS::MutableHandle<JS::Value> aResult,
|
|
ErrorResult& aRv)
|
|
{
|
|
if (!mCachedKeyPath.isUndefined()) {
|
|
JS::ExposeValueToActiveJS(mCachedKeyPath);
|
|
aResult.set(mCachedKeyPath);
|
|
return;
|
|
}
|
|
|
|
aRv = GetKeyPath().ToJSVal(aCx, mCachedKeyPath);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return;
|
|
}
|
|
|
|
if (mCachedKeyPath.isGCThing()) {
|
|
mozilla::HoldJSObjects(this);
|
|
mRooted = true;
|
|
}
|
|
|
|
JS::ExposeValueToActiveJS(mCachedKeyPath);
|
|
aResult.set(mCachedKeyPath);
|
|
}
|
|
|
|
already_AddRefed<DOMStringList>
|
|
IDBObjectStore::IndexNames()
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
const nsTArray<IndexMetadata>& indexes = mSpec->indexes();
|
|
|
|
RefPtr<DOMStringList> list = new DOMStringList();
|
|
|
|
if (!indexes.IsEmpty()) {
|
|
nsTArray<nsString>& listNames = list->StringArray();
|
|
listNames.SetCapacity(indexes.Length());
|
|
|
|
for (uint32_t index = 0; index < indexes.Length(); index++) {
|
|
listNames.InsertElementSorted(indexes[index].name());
|
|
}
|
|
}
|
|
|
|
return list.forget();
|
|
}
|
|
|
|
already_AddRefed<IDBRequest>
|
|
IDBObjectStore::Get(JSContext* aCx,
|
|
JS::Handle<JS::Value> aKey,
|
|
ErrorResult& aRv)
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
if (mDeletedSpec) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
if (!mTransaction->IsOpen()) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<IDBKeyRange> keyRange;
|
|
aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
|
|
if (aRv.Failed()) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (!keyRange) {
|
|
// Must specify a key or keyRange for get().
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
ObjectStoreGetParams params;
|
|
params.objectStoreId() = Id();
|
|
keyRange->ToSerialized(params.keyRange());
|
|
|
|
RefPtr<IDBRequest> request = GenerateRequest(this);
|
|
MOZ_ASSERT(request);
|
|
|
|
IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: "
|
|
"database(%s).transaction(%s).objectStore(%s).get(%s)",
|
|
"IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.get()",
|
|
IDB_LOG_ID_STRING(),
|
|
mTransaction->LoggingSerialNumber(),
|
|
request->LoggingSerialNumber(),
|
|
IDB_LOG_STRINGIFY(mTransaction->Database()),
|
|
IDB_LOG_STRINGIFY(mTransaction),
|
|
IDB_LOG_STRINGIFY(this),
|
|
IDB_LOG_STRINGIFY(keyRange));
|
|
|
|
mTransaction->StartRequest(request, params);
|
|
|
|
return request.forget();
|
|
}
|
|
|
|
already_AddRefed<IDBRequest>
|
|
IDBObjectStore::DeleteInternal(JSContext* aCx,
|
|
JS::Handle<JS::Value> aKey,
|
|
bool aFromCursor,
|
|
ErrorResult& aRv)
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
if (mDeletedSpec) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
if (!mTransaction->IsOpen()) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
if (!mTransaction->IsWriteAllowed()) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<IDBKeyRange> keyRange;
|
|
aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
|
|
if (NS_WARN_IF((aRv.Failed()))) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (!keyRange) {
|
|
// Must specify a key or keyRange for delete().
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
ObjectStoreDeleteParams params;
|
|
params.objectStoreId() = Id();
|
|
keyRange->ToSerialized(params.keyRange());
|
|
|
|
RefPtr<IDBRequest> request = GenerateRequest(this);
|
|
MOZ_ASSERT(request);
|
|
|
|
if (!aFromCursor) {
|
|
IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: "
|
|
"database(%s).transaction(%s).objectStore(%s).delete(%s)",
|
|
"IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.delete()",
|
|
IDB_LOG_ID_STRING(),
|
|
mTransaction->LoggingSerialNumber(),
|
|
request->LoggingSerialNumber(),
|
|
IDB_LOG_STRINGIFY(mTransaction->Database()),
|
|
IDB_LOG_STRINGIFY(mTransaction),
|
|
IDB_LOG_STRINGIFY(this),
|
|
IDB_LOG_STRINGIFY(keyRange));
|
|
}
|
|
|
|
mTransaction->StartRequest(request, params);
|
|
|
|
return request.forget();
|
|
}
|
|
|
|
already_AddRefed<IDBIndex>
|
|
IDBObjectStore::CreateIndex(const nsAString& aName,
|
|
const nsAString& aKeyPath,
|
|
const IDBIndexParameters& aOptionalParameters,
|
|
ErrorResult& aRv)
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
KeyPath keyPath(0);
|
|
if (NS_FAILED(KeyPath::Parse(aKeyPath, &keyPath)) ||
|
|
!keyPath.IsValid()) {
|
|
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
return CreateIndexInternal(aName, keyPath, aOptionalParameters, aRv);
|
|
}
|
|
|
|
already_AddRefed<IDBIndex>
|
|
IDBObjectStore::CreateIndex(const nsAString& aName,
|
|
const Sequence<nsString >& aKeyPath,
|
|
const IDBIndexParameters& aOptionalParameters,
|
|
ErrorResult& aRv)
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
KeyPath keyPath(0);
|
|
if (aKeyPath.IsEmpty() ||
|
|
NS_FAILED(KeyPath::Parse(aKeyPath, &keyPath)) ||
|
|
!keyPath.IsValid()) {
|
|
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
return CreateIndexInternal(aName, keyPath, aOptionalParameters, aRv);
|
|
}
|
|
|
|
already_AddRefed<IDBIndex>
|
|
IDBObjectStore::CreateIndexInternal(
|
|
const nsAString& aName,
|
|
const KeyPath& aKeyPath,
|
|
const IDBIndexParameters& aOptionalParameters,
|
|
ErrorResult& aRv)
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
if (mTransaction->GetMode() != IDBTransaction::VERSION_CHANGE ||
|
|
mDeletedSpec) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
IDBTransaction* transaction = IDBTransaction::GetCurrent();
|
|
if (!transaction || transaction != mTransaction) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
MOZ_ASSERT(transaction->IsOpen());
|
|
|
|
auto& indexes = const_cast<nsTArray<IndexMetadata>&>(mSpec->indexes());
|
|
for (uint32_t count = indexes.Length(), index = 0;
|
|
index < count;
|
|
index++) {
|
|
if (aName == indexes[index].name()) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
if (aOptionalParameters.mMultiEntry && aKeyPath.IsArray()) {
|
|
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
for (uint32_t count = mIndexes.Length(), index = 0;
|
|
index < count;
|
|
index++) {
|
|
MOZ_ASSERT(mIndexes[index]->Name() != aName);
|
|
}
|
|
#endif
|
|
|
|
const IndexMetadata* oldMetadataElements =
|
|
indexes.IsEmpty() ? nullptr : indexes.Elements();
|
|
|
|
// With this setup we only validate the passed in locale name by the time we
|
|
// get to encoding Keys. Maybe we should do it here right away and error out.
|
|
|
|
// Valid locale names are always ASCII as per BCP-47.
|
|
nsCString locale = NS_LossyConvertUTF16toASCII(aOptionalParameters.mLocale);
|
|
bool autoLocale = locale.EqualsASCII("auto");
|
|
#ifdef ENABLE_INTL_API
|
|
if (autoLocale) {
|
|
locale = IndexedDatabaseManager::GetLocale();
|
|
}
|
|
#endif
|
|
|
|
IndexMetadata* metadata = indexes.AppendElement(
|
|
IndexMetadata(transaction->NextIndexId(), nsString(aName), aKeyPath,
|
|
locale,
|
|
aOptionalParameters.mUnique,
|
|
aOptionalParameters.mMultiEntry,
|
|
autoLocale));
|
|
|
|
if (oldMetadataElements &&
|
|
oldMetadataElements != indexes.Elements()) {
|
|
MOZ_ASSERT(indexes.Length() > 1);
|
|
|
|
// Array got moved, update the spec pointers for all live indexes.
|
|
RefreshSpec(/* aMayDelete */ false);
|
|
}
|
|
|
|
transaction->CreateIndex(this, *metadata);
|
|
|
|
RefPtr<IDBIndex> index = IDBIndex::Create(this, *metadata);
|
|
MOZ_ASSERT(index);
|
|
|
|
mIndexes.AppendElement(index);
|
|
|
|
// Don't do this in the macro because we always need to increment the serial
|
|
// number to keep in sync with the parent.
|
|
const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
|
|
|
|
IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: "
|
|
"database(%s).transaction(%s).objectStore(%s).createIndex(%s)",
|
|
"IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.createIndex()",
|
|
IDB_LOG_ID_STRING(),
|
|
mTransaction->LoggingSerialNumber(),
|
|
requestSerialNumber,
|
|
IDB_LOG_STRINGIFY(mTransaction->Database()),
|
|
IDB_LOG_STRINGIFY(mTransaction),
|
|
IDB_LOG_STRINGIFY(this),
|
|
IDB_LOG_STRINGIFY(index));
|
|
|
|
return index.forget();
|
|
}
|
|
|
|
void
|
|
IDBObjectStore::DeleteIndex(const nsAString& aName, ErrorResult& aRv)
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
if (mTransaction->GetMode() != IDBTransaction::VERSION_CHANGE ||
|
|
mDeletedSpec) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
|
|
return;
|
|
}
|
|
|
|
IDBTransaction* transaction = IDBTransaction::GetCurrent();
|
|
if (!transaction || transaction != mTransaction) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
|
|
return;
|
|
}
|
|
|
|
MOZ_ASSERT(transaction->IsOpen());
|
|
|
|
auto& metadataArray = const_cast<nsTArray<IndexMetadata>&>(mSpec->indexes());
|
|
|
|
int64_t foundId = 0;
|
|
|
|
for (uint32_t metadataCount = metadataArray.Length(), metadataIndex = 0;
|
|
metadataIndex < metadataCount;
|
|
metadataIndex++) {
|
|
const IndexMetadata& metadata = metadataArray[metadataIndex];
|
|
MOZ_ASSERT(metadata.id());
|
|
|
|
if (aName == metadata.name()) {
|
|
foundId = metadata.id();
|
|
|
|
// Must do this before altering the metadata array!
|
|
for (uint32_t indexCount = mIndexes.Length(), indexIndex = 0;
|
|
indexIndex < indexCount;
|
|
indexIndex++) {
|
|
RefPtr<IDBIndex>& index = mIndexes[indexIndex];
|
|
|
|
if (index->Id() == foundId) {
|
|
index->NoteDeletion();
|
|
mIndexes.RemoveElementAt(indexIndex);
|
|
break;
|
|
}
|
|
}
|
|
|
|
metadataArray.RemoveElementAt(metadataIndex);
|
|
|
|
RefreshSpec(/* aMayDelete */ false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!foundId) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR);
|
|
return;
|
|
}
|
|
|
|
// Don't do this in the macro because we always need to increment the serial
|
|
// number to keep in sync with the parent.
|
|
const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
|
|
|
|
IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: "
|
|
"database(%s).transaction(%s).objectStore(%s)."
|
|
"deleteIndex(\"%s\")",
|
|
"IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.deleteIndex()",
|
|
IDB_LOG_ID_STRING(),
|
|
mTransaction->LoggingSerialNumber(),
|
|
requestSerialNumber,
|
|
IDB_LOG_STRINGIFY(mTransaction->Database()),
|
|
IDB_LOG_STRINGIFY(mTransaction),
|
|
IDB_LOG_STRINGIFY(this),
|
|
NS_ConvertUTF16toUTF8(aName).get());
|
|
|
|
transaction->DeleteIndex(this, foundId);
|
|
}
|
|
|
|
already_AddRefed<IDBRequest>
|
|
IDBObjectStore::Count(JSContext* aCx,
|
|
JS::Handle<JS::Value> aKey,
|
|
ErrorResult& aRv)
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
if (mDeletedSpec) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
if (!mTransaction->IsOpen()) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<IDBKeyRange> keyRange;
|
|
aRv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
|
|
if (aRv.Failed()) {
|
|
return nullptr;
|
|
}
|
|
|
|
ObjectStoreCountParams params;
|
|
params.objectStoreId() = Id();
|
|
|
|
if (keyRange) {
|
|
SerializedKeyRange serializedKeyRange;
|
|
keyRange->ToSerialized(serializedKeyRange);
|
|
params.optionalKeyRange() = serializedKeyRange;
|
|
} else {
|
|
params.optionalKeyRange() = void_t();
|
|
}
|
|
|
|
RefPtr<IDBRequest> request = GenerateRequest(this);
|
|
MOZ_ASSERT(request);
|
|
|
|
IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: "
|
|
"database(%s).transaction(%s).objectStore(%s).count(%s)",
|
|
"IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.count()",
|
|
IDB_LOG_ID_STRING(),
|
|
mTransaction->LoggingSerialNumber(),
|
|
request->LoggingSerialNumber(),
|
|
IDB_LOG_STRINGIFY(mTransaction->Database()),
|
|
IDB_LOG_STRINGIFY(mTransaction),
|
|
IDB_LOG_STRINGIFY(this),
|
|
IDB_LOG_STRINGIFY(keyRange));
|
|
|
|
mTransaction->StartRequest(request, params);
|
|
|
|
return request.forget();
|
|
}
|
|
|
|
already_AddRefed<IDBRequest>
|
|
IDBObjectStore::OpenCursorInternal(bool aKeysOnly,
|
|
JSContext* aCx,
|
|
JS::Handle<JS::Value> aRange,
|
|
IDBCursorDirection aDirection,
|
|
ErrorResult& aRv)
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
if (mDeletedSpec) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
if (!mTransaction->IsOpen()) {
|
|
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<IDBKeyRange> keyRange;
|
|
aRv = IDBKeyRange::FromJSVal(aCx, aRange, getter_AddRefs(keyRange));
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
int64_t objectStoreId = Id();
|
|
|
|
OptionalKeyRange optionalKeyRange;
|
|
|
|
if (keyRange) {
|
|
SerializedKeyRange serializedKeyRange;
|
|
keyRange->ToSerialized(serializedKeyRange);
|
|
|
|
optionalKeyRange = Move(serializedKeyRange);
|
|
} else {
|
|
optionalKeyRange = void_t();
|
|
}
|
|
|
|
IDBCursor::Direction direction = IDBCursor::ConvertDirection(aDirection);
|
|
|
|
OpenCursorParams params;
|
|
if (aKeysOnly) {
|
|
ObjectStoreOpenKeyCursorParams openParams;
|
|
openParams.objectStoreId() = objectStoreId;
|
|
openParams.optionalKeyRange() = Move(optionalKeyRange);
|
|
openParams.direction() = direction;
|
|
|
|
params = Move(openParams);
|
|
} else {
|
|
ObjectStoreOpenCursorParams openParams;
|
|
openParams.objectStoreId() = objectStoreId;
|
|
openParams.optionalKeyRange() = Move(optionalKeyRange);
|
|
openParams.direction() = direction;
|
|
|
|
params = Move(openParams);
|
|
}
|
|
|
|
RefPtr<IDBRequest> request = GenerateRequest(this);
|
|
MOZ_ASSERT(request);
|
|
|
|
if (aKeysOnly) {
|
|
IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: "
|
|
"database(%s).transaction(%s).objectStore(%s)."
|
|
"openKeyCursor(%s, %s)",
|
|
"IndexedDB %s: C T[%lld] R[%llu]: "
|
|
"IDBObjectStore.openKeyCursor()",
|
|
IDB_LOG_ID_STRING(),
|
|
mTransaction->LoggingSerialNumber(),
|
|
request->LoggingSerialNumber(),
|
|
IDB_LOG_STRINGIFY(mTransaction->Database()),
|
|
IDB_LOG_STRINGIFY(mTransaction),
|
|
IDB_LOG_STRINGIFY(this),
|
|
IDB_LOG_STRINGIFY(keyRange),
|
|
IDB_LOG_STRINGIFY(direction));
|
|
} else {
|
|
IDB_LOG_MARK("IndexedDB %s: Child Transaction[%lld] Request[%llu]: "
|
|
"database(%s).transaction(%s).objectStore(%s)."
|
|
"openCursor(%s, %s)",
|
|
"IndexedDB %s: C T[%lld] R[%llu]: IDBObjectStore.openCursor()",
|
|
IDB_LOG_ID_STRING(),
|
|
mTransaction->LoggingSerialNumber(),
|
|
request->LoggingSerialNumber(),
|
|
IDB_LOG_STRINGIFY(mTransaction->Database()),
|
|
IDB_LOG_STRINGIFY(mTransaction),
|
|
IDB_LOG_STRINGIFY(this),
|
|
IDB_LOG_STRINGIFY(keyRange),
|
|
IDB_LOG_STRINGIFY(direction));
|
|
}
|
|
|
|
BackgroundCursorChild* actor =
|
|
new BackgroundCursorChild(request, this, direction);
|
|
|
|
mTransaction->OpenCursor(actor, params);
|
|
|
|
return request.forget();
|
|
}
|
|
|
|
void
|
|
IDBObjectStore::RefreshSpec(bool aMayDelete)
|
|
{
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT_IF(mDeletedSpec, mSpec == mDeletedSpec);
|
|
|
|
const DatabaseSpec* dbSpec = mTransaction->Database()->Spec();
|
|
MOZ_ASSERT(dbSpec);
|
|
|
|
const nsTArray<ObjectStoreSpec>& objectStores = dbSpec->objectStores();
|
|
|
|
bool found = false;
|
|
|
|
for (uint32_t objCount = objectStores.Length(), objIndex = 0;
|
|
objIndex < objCount;
|
|
objIndex++) {
|
|
const ObjectStoreSpec& objSpec = objectStores[objIndex];
|
|
|
|
if (objSpec.metadata().id() == Id()) {
|
|
mSpec = &objSpec;
|
|
|
|
for (uint32_t idxCount = mIndexes.Length(), idxIndex = 0;
|
|
idxIndex < idxCount;
|
|
idxIndex++) {
|
|
mIndexes[idxIndex]->RefreshMetadata(aMayDelete);
|
|
}
|
|
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
MOZ_ASSERT_IF(!aMayDelete && !mDeletedSpec, found);
|
|
|
|
if (found) {
|
|
MOZ_ASSERT(mSpec != mDeletedSpec);
|
|
mDeletedSpec = nullptr;
|
|
} else {
|
|
NoteDeletion();
|
|
}
|
|
}
|
|
|
|
const ObjectStoreSpec&
|
|
IDBObjectStore::Spec() const
|
|
{
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(mSpec);
|
|
|
|
return *mSpec;
|
|
}
|
|
|
|
void
|
|
IDBObjectStore::NoteDeletion()
|
|
{
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(mSpec);
|
|
MOZ_ASSERT(Id() == mSpec->metadata().id());
|
|
|
|
if (mDeletedSpec) {
|
|
MOZ_ASSERT(mDeletedSpec == mSpec);
|
|
return;
|
|
}
|
|
|
|
// Copy the spec here.
|
|
mDeletedSpec = new ObjectStoreSpec(*mSpec);
|
|
mDeletedSpec->indexes().Clear();
|
|
|
|
mSpec = mDeletedSpec;
|
|
|
|
if (!mIndexes.IsEmpty()) {
|
|
for (uint32_t count = mIndexes.Length(), index = 0;
|
|
index < count;
|
|
index++) {
|
|
mIndexes[index]->NoteDeletion();
|
|
}
|
|
}
|
|
}
|
|
|
|
const nsString&
|
|
IDBObjectStore::Name() const
|
|
{
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(mSpec);
|
|
|
|
return mSpec->metadata().name();
|
|
}
|
|
|
|
bool
|
|
IDBObjectStore::AutoIncrement() const
|
|
{
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(mSpec);
|
|
|
|
return mSpec->metadata().autoIncrement();
|
|
}
|
|
|
|
const indexedDB::KeyPath&
|
|
IDBObjectStore::GetKeyPath() const
|
|
{
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(mSpec);
|
|
|
|
return mSpec->metadata().keyPath();
|
|
}
|
|
|
|
bool
|
|
IDBObjectStore::HasValidKeyPath() const
|
|
{
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(mSpec);
|
|
|
|
return GetKeyPath().IsValid();
|
|
}
|
|
|
|
} // namespace dom
|
|
} // namespace mozilla
|