summaryrefslogtreecommitdiffstats
path: root/dom/indexedDB
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 05:35:37 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 05:35:37 +0000
commita90a5cba08fdf6c0ceb95101c275108a152a3aed (patch)
tree532507288f3defd7f4dcf1af49698bcb76034855 /dom/indexedDB
parentAdding debian version 126.0.1-1. (diff)
downloadfirefox-a90a5cba08fdf6c0ceb95101c275108a152a3aed.tar.xz
firefox-a90a5cba08fdf6c0ceb95101c275108a152a3aed.zip
Merging upstream version 127.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/indexedDB')
-rw-r--r--dom/indexedDB/IDBFactory.cpp171
-rw-r--r--dom/indexedDB/IDBFactory.h32
-rw-r--r--dom/indexedDB/IDBObjectStore.cpp22
-rw-r--r--dom/indexedDB/Key.cpp125
-rw-r--r--dom/indexedDB/Key.h8
-rw-r--r--dom/indexedDB/crashtests/1499854-1.html2
-rw-r--r--dom/indexedDB/crashtests/1857979-1.html4
-rw-r--r--dom/indexedDB/test/perfdocs/index.rst2
-rw-r--r--dom/indexedDB/test/unit/test_invalid_version.js16
-rw-r--r--dom/indexedDB/test/unit/test_metadata2Restore.js40
-rw-r--r--dom/indexedDB/test/unit/test_metadataRestore.js24
11 files changed, 257 insertions, 189 deletions
diff --git a/dom/indexedDB/IDBFactory.cpp b/dom/indexedDB/IDBFactory.cpp
index c0dc5aeab2..b59efd9124 100644
--- a/dom/indexedDB/IDBFactory.cpp
+++ b/dom/indexedDB/IDBFactory.cpp
@@ -35,6 +35,8 @@
#include "nsIURI.h"
#include "nsIUUIDGenerator.h"
#include "nsIWebNavigation.h"
+#include "nsLiteralString.h"
+#include "nsStringFwd.h"
#include "nsNetUtil.h"
#include "nsSandboxFlags.h"
#include "nsServiceManagerUtils.h"
@@ -53,50 +55,7 @@ using namespace mozilla::ipc;
namespace {
-Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT IdentifyPrincipalType(
- const mozilla::ipc::PrincipalInfo& aPrincipalInfo) {
- switch (aPrincipalInfo.type()) {
- case PrincipalInfo::TSystemPrincipalInfo:
- return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT::system;
- case PrincipalInfo::TContentPrincipalInfo: {
- const ContentPrincipalInfo& info =
- aPrincipalInfo.get_ContentPrincipalInfo();
-
- nsCOMPtr<nsIURI> uri;
-
- if (NS_WARN_IF(NS_FAILED(NS_NewURI(getter_AddRefs(uri), info.spec())))) {
- // This could be discriminated as an extra error value, but this is
- // extremely unlikely to fail, so we just misuse ContentOther
- return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT::
- content_other;
- }
-
- // TODO Are there constants defined for the schemes somewhere?
- if (uri->SchemeIs("file")) {
- return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT::
- content_file;
- }
- if (uri->SchemeIs("http") || uri->SchemeIs("https")) {
- return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT::
- content_http_https;
- }
- if (uri->SchemeIs("moz-extension")) {
- return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT::
- content_moz_ext;
- }
- if (uri->SchemeIs("about")) {
- return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT::
- content_about;
- }
- return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT::
- content_other;
- }
- case PrincipalInfo::TExpandedPrincipalInfo:
- return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT::expanded;
- default:
- return Telemetry::LABELS_IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT::other;
- }
-}
+constexpr nsLiteralCString kAccessError("The operation is insecure");
} // namespace
@@ -112,11 +71,12 @@ struct IDBFactory::PendingRequestInfo {
}
};
-IDBFactory::IDBFactory(const IDBFactoryGuard&)
+IDBFactory::IDBFactory(const IDBFactoryGuard&, bool aAllowed)
: mBackgroundActor(nullptr),
mInnerWindowID(0),
mActiveTransactionCount(0),
mActiveDatabaseCount(0),
+ mAllowed(aAllowed),
mBackgroundActorFailed(false),
mPrivateBrowsingMode(false) {
AssertIsOnOwningThread();
@@ -140,16 +100,21 @@ Result<RefPtr<IDBFactory>, nsresult> IDBFactory::CreateForWindow(
nsCOMPtr<nsIPrincipal> principal;
nsresult rv = AllowedForWindowInternal(aWindow, &principal);
- if (rv == NS_ERROR_DOM_NOT_SUPPORTED_ERR) {
- NS_WARNING("IndexedDB is not permitted in a third-party window.");
- return RefPtr<IDBFactory>{};
- }
-
if (NS_WARN_IF(NS_FAILED(rv))) {
+ if (rv == NS_ERROR_DOM_NOT_SUPPORTED_ERR) {
+ NS_WARNING("IndexedDB is not permitted in a third-party window.");
+ }
+
if (rv == NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR) {
IDB_REPORT_INTERNAL_ERR();
}
- return Err(rv);
+
+ auto factory =
+ MakeRefPtr<IDBFactory>(IDBFactoryGuard{}, /* aAllowed */ false);
+ factory->BindToOwner(aWindow->AsGlobal());
+ factory->mInnerWindowID = aWindow->WindowID();
+
+ return factory;
}
MOZ_ASSERT(principal);
@@ -172,7 +137,7 @@ Result<RefPtr<IDBFactory>, nsresult> IDBFactory::CreateForWindow(
nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(aWindow);
nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(webNav);
- auto factory = MakeRefPtr<IDBFactory>(IDBFactoryGuard{});
+ auto factory = MakeRefPtr<IDBFactory>(IDBFactoryGuard{}, /* aAllowed */ true);
factory->mPrincipalInfo = std::move(principalInfo);
factory->BindToOwner(aWindow->AsGlobal());
@@ -203,7 +168,11 @@ Result<RefPtr<IDBFactory>, nsresult> IDBFactory::CreateForMainThreadJS(
MOZ_ASSERT(principal);
bool isSystem;
if (!AllowedForPrincipal(principal, &isSystem)) {
- return Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+ auto factory =
+ MakeRefPtr<IDBFactory>(IDBFactoryGuard{}, /* aAllowed */ false);
+ factory->BindToOwner(aGlobal);
+
+ return factory;
}
nsresult rv = PrincipalToPrincipalInfo(principal, principalInfo.get());
@@ -220,14 +189,23 @@ Result<RefPtr<IDBFactory>, nsresult> IDBFactory::CreateForMainThreadJS(
// static
Result<RefPtr<IDBFactory>, nsresult> IDBFactory::CreateForWorker(
- nsIGlobalObject* aGlobal, const PrincipalInfo& aPrincipalInfo,
+ nsIGlobalObject* aGlobal, UniquePtr<PrincipalInfo>&& aPrincipalInfo,
uint64_t aInnerWindowID) {
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aGlobal);
- MOZ_ASSERT(aPrincipalInfo.type() != PrincipalInfo::T__None);
- return CreateInternal(aGlobal, MakeUnique<PrincipalInfo>(aPrincipalInfo),
- aInnerWindowID);
+ if (!aPrincipalInfo) {
+ auto factory =
+ MakeRefPtr<IDBFactory>(IDBFactoryGuard{}, /* aAllowed */ false);
+ factory->BindToOwner(aGlobal);
+ factory->mInnerWindowID = aInnerWindowID;
+
+ return factory;
+ }
+
+ MOZ_ASSERT(aPrincipalInfo->type() != PrincipalInfo::T__None);
+
+ return CreateInternal(aGlobal, std::move(aPrincipalInfo), aInnerWindowID);
}
// static
@@ -264,10 +242,16 @@ Result<RefPtr<IDBFactory>, nsresult> IDBFactory::CreateInternal(
if (aPrincipalInfo->type() != PrincipalInfo::TContentPrincipalInfo &&
aPrincipalInfo->type() != PrincipalInfo::TSystemPrincipalInfo) {
NS_WARNING("IndexedDB not allowed for this principal!");
- return RefPtr<IDBFactory>{};
+
+ auto factory =
+ MakeRefPtr<IDBFactory>(IDBFactoryGuard{}, /* aAllowed */ false);
+ factory->BindToOwner(aGlobal);
+ factory->mInnerWindowID = aInnerWindowID;
+
+ return factory;
}
- auto factory = MakeRefPtr<IDBFactory>(IDBFactoryGuard{});
+ auto factory = MakeRefPtr<IDBFactory>(IDBFactoryGuard{}, /* aAllowed */ true);
factory->mPrincipalInfo = std::move(aPrincipalInfo);
factory->BindToOwner(aGlobal);
factory->mEventTarget = GetCurrentSerialEventTarget();
@@ -407,6 +391,7 @@ PersistenceType IDBFactory::GetPersistenceType(
void IDBFactory::UpdateActiveTransactionCount(int32_t aDelta) {
AssertIsOnOwningThread();
+ MOZ_ASSERT(mAllowed);
MOZ_DIAGNOSTIC_ASSERT(aDelta > 0 || (mActiveTransactionCount + aDelta) <
mActiveTransactionCount);
mActiveTransactionCount += aDelta;
@@ -414,6 +399,7 @@ void IDBFactory::UpdateActiveTransactionCount(int32_t aDelta) {
void IDBFactory::UpdateActiveDatabaseCount(int32_t aDelta) {
AssertIsOnOwningThread();
+ MOZ_ASSERT(mAllowed);
MOZ_DIAGNOSTIC_ASSERT(aDelta > 0 ||
(mActiveDatabaseCount + aDelta) < mActiveDatabaseCount);
mActiveDatabaseCount += aDelta;
@@ -424,6 +410,10 @@ void IDBFactory::UpdateActiveDatabaseCount(int32_t aDelta) {
}
bool IDBFactory::IsChrome() const {
+ if (!mAllowed) {
+ return false; // Minimal privileges
+ }
+
AssertIsOnOwningThread();
MOZ_ASSERT(mPrincipalInfo);
@@ -432,39 +422,28 @@ bool IDBFactory::IsChrome() const {
RefPtr<IDBOpenDBRequest> IDBFactory::Open(JSContext* aCx,
const nsAString& aName,
- uint64_t aVersion,
+ const Optional<uint64_t>& aVersion,
CallerType aCallerType,
ErrorResult& aRv) {
- return OpenInternal(aCx,
- /* aPrincipal */ nullptr, aName,
- Optional<uint64_t>(aVersion),
- /* aDeleting */ false, aCallerType, aRv);
-}
-
-RefPtr<IDBOpenDBRequest> IDBFactory::Open(JSContext* aCx,
- const nsAString& aName,
- const IDBOpenDBOptions& aOptions,
- CallerType aCallerType,
- ErrorResult& aRv) {
- // This overload is nonstandard, see bug 1275496.
- // Ignore calls with empty options for telemetry of usage count.
- // Unfortunately, we cannot distinguish between the use of the method with
- // only a single argument (which actually is a standard overload we don't want
- // to count) an empty dictionary passed explicitly (which is the custom
- // overload we would like to count). However, we assume that the latter is so
- // rare that it can be neglected.
- if (aOptions.IsAnyMemberPresent()) {
- Telemetry::AccumulateCategorical(IdentifyPrincipalType(*mPrincipalInfo));
+ if (!mAllowed) {
+ aRv.ThrowSecurityError(kAccessError);
+ return nullptr;
}
return OpenInternal(aCx,
- /* aPrincipal */ nullptr, aName, aOptions.mVersion,
+ /* aPrincipal */ nullptr, aName, aVersion,
/* aDeleting */ false, aCallerType, aRv);
}
-RefPtr<IDBOpenDBRequest> IDBFactory::DeleteDatabase(
- JSContext* aCx, const nsAString& aName, const IDBOpenDBOptions& aOptions,
- CallerType aCallerType, ErrorResult& aRv) {
+RefPtr<IDBOpenDBRequest> IDBFactory::DeleteDatabase(JSContext* aCx,
+ const nsAString& aName,
+ CallerType aCallerType,
+ ErrorResult& aRv) {
+ if (!mAllowed) {
+ aRv.ThrowSecurityError(kAccessError);
+ return nullptr;
+ }
+
return OpenInternal(aCx,
/* aPrincipal */ nullptr, aName, Optional<uint64_t>(),
/* aDeleting */ true, aCallerType, aRv);
@@ -478,6 +457,10 @@ already_AddRefed<Promise> IDBFactory::Databases(JSContext* const aCx,
}
RefPtr<Promise> promise = Promise::CreateInfallible(GetOwnerGlobal());
+ if (!mAllowed) {
+ promise->MaybeRejectWithSecurityError(kAccessError);
+ return promise.forget();
+ }
// Nothing can be done here if we have previously failed to create a
// background actor.
@@ -569,6 +552,11 @@ int16_t IDBFactory::Cmp(JSContext* aCx, JS::Handle<JS::Value> aFirst,
RefPtr<IDBOpenDBRequest> IDBFactory::OpenForPrincipal(
JSContext* aCx, nsIPrincipal* aPrincipal, const nsAString& aName,
uint64_t aVersion, SystemCallerGuarantee aGuarantee, ErrorResult& aRv) {
+ if (!mAllowed) {
+ aRv.ThrowSecurityError(kAccessError);
+ return nullptr;
+ }
+
MOZ_ASSERT(aPrincipal);
if (!NS_IsMainThread()) {
MOZ_CRASH(
@@ -584,6 +572,11 @@ RefPtr<IDBOpenDBRequest> IDBFactory::OpenForPrincipal(
JSContext* aCx, nsIPrincipal* aPrincipal, const nsAString& aName,
const IDBOpenDBOptions& aOptions, SystemCallerGuarantee aGuarantee,
ErrorResult& aRv) {
+ if (!mAllowed) {
+ aRv.ThrowSecurityError(kAccessError);
+ return nullptr;
+ }
+
MOZ_ASSERT(aPrincipal);
if (!NS_IsMainThread()) {
MOZ_CRASH(
@@ -599,6 +592,11 @@ RefPtr<IDBOpenDBRequest> IDBFactory::DeleteForPrincipal(
JSContext* aCx, nsIPrincipal* aPrincipal, const nsAString& aName,
const IDBOpenDBOptions& aOptions, SystemCallerGuarantee aGuarantee,
ErrorResult& aRv) {
+ if (!mAllowed) {
+ aRv.ThrowSecurityError(kAccessError);
+ return nullptr;
+ }
+
MOZ_ASSERT(aPrincipal);
if (!NS_IsMainThread()) {
MOZ_CRASH(
@@ -611,6 +609,8 @@ RefPtr<IDBOpenDBRequest> IDBFactory::DeleteForPrincipal(
}
nsresult IDBFactory::EnsureBackgroundActor() {
+ MOZ_ASSERT(mAllowed);
+
if (mBackgroundActor) {
return NS_OK;
}
@@ -671,6 +671,8 @@ RefPtr<IDBOpenDBRequest> IDBFactory::OpenInternal(
JSContext* aCx, nsIPrincipal* aPrincipal, const nsAString& aName,
const Optional<uint64_t>& aVersion, bool aDeleting, CallerType aCallerType,
ErrorResult& aRv) {
+ MOZ_ASSERT(mAllowed);
+
if (NS_WARN_IF(!GetOwnerGlobal())) {
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
return nullptr;
@@ -801,6 +803,7 @@ RefPtr<IDBOpenDBRequest> IDBFactory::OpenInternal(
nsresult IDBFactory::InitiateRequest(
const NotNull<RefPtr<IDBOpenDBRequest>>& aRequest,
const FactoryRequestParams& aParams) {
+ MOZ_ASSERT(mAllowed);
MOZ_ASSERT(mBackgroundActor);
MOZ_ASSERT(!mBackgroundActorFailed);
diff --git a/dom/indexedDB/IDBFactory.h b/dom/indexedDB/IDBFactory.h
index d64d571a05..08663eaec4 100644
--- a/dom/indexedDB/IDBFactory.h
+++ b/dom/indexedDB/IDBFactory.h
@@ -77,11 +77,16 @@ class IDBFactory final : public GlobalTeardownObserver, public nsWrapperCache {
uint32_t mActiveTransactionCount;
uint32_t mActiveDatabaseCount;
+ // When mAllowed is false we throw security errors on all operations. This is
+ // because although we make storage access decisions when we create the
+ // IDBFactory, the spec (and content) expects us to only throw if an attempt
+ // is made to use the resulting IDBFactory.
+ bool mAllowed;
bool mBackgroundActorFailed;
bool mPrivateBrowsingMode;
public:
- explicit IDBFactory(const IDBFactoryGuard&);
+ IDBFactory(const IDBFactoryGuard&, bool aAllowed);
static Result<RefPtr<IDBFactory>, nsresult> CreateForWindow(
nsPIDOMWindowInner* aWindow);
@@ -89,8 +94,9 @@ class IDBFactory final : public GlobalTeardownObserver, public nsWrapperCache {
static Result<RefPtr<IDBFactory>, nsresult> CreateForMainThreadJS(
nsIGlobalObject* aGlobal);
+ // mAllowed shall be false for null aPrincipalInfo.
static Result<RefPtr<IDBFactory>, nsresult> CreateForWorker(
- nsIGlobalObject* aGlobal, const PrincipalInfo& aPrincipalInfo,
+ nsIGlobalObject* aGlobal, UniquePtr<PrincipalInfo>&& aPrincipalInfo,
uint64_t aInnerWindowID);
static bool AllowedForWindow(nsPIDOMWindowInner* aWindow);
@@ -146,21 +152,15 @@ class IDBFactory final : public GlobalTeardownObserver, public nsWrapperCache {
bool IsChrome() const;
- [[nodiscard]] RefPtr<IDBOpenDBRequest> Open(JSContext* aCx,
- const nsAString& aName,
- uint64_t aVersion,
- CallerType aCallerType,
- ErrorResult& aRv);
-
- [[nodiscard]] RefPtr<IDBOpenDBRequest> Open(JSContext* aCx,
- const nsAString& aName,
- const IDBOpenDBOptions& aOptions,
- CallerType aCallerType,
- ErrorResult& aRv);
+ [[nodiscard]] RefPtr<IDBOpenDBRequest> Open(
+ JSContext* aCx, const nsAString& aName,
+ const Optional<uint64_t>& aVersion, CallerType aCallerType,
+ ErrorResult& aRv);
- [[nodiscard]] RefPtr<IDBOpenDBRequest> DeleteDatabase(
- JSContext* aCx, const nsAString& aName, const IDBOpenDBOptions& aOptions,
- CallerType aCallerType, ErrorResult& aRv);
+ [[nodiscard]] RefPtr<IDBOpenDBRequest> DeleteDatabase(JSContext* aCx,
+ const nsAString& aName,
+ CallerType aCallerType,
+ ErrorResult& aRv);
already_AddRefed<Promise> Databases(JSContext* aCx, ErrorResult& aRv);
diff --git a/dom/indexedDB/IDBObjectStore.cpp b/dom/indexedDB/IDBObjectStore.cpp
index 728f10b105..401c9defb2 100644
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -276,12 +276,22 @@ bool CopyingStructuredCloneWriteCallback(JSContext* aCx,
aObj);
}
+void StructuredCloneErrorCallback(JSContext* aCx, uint32_t aErrorId,
+ void* aClosure, const char* aErrorMessage) {
+ // This callback is only used to prevent the default cloning TypeErrors
+ // from being thrown, as we will throw DataCloneErrors instead per spec.
+}
+
nsresult GetAddInfoCallback(JSContext* aCx, void* aClosure) {
static const JSStructuredCloneCallbacks kStructuredCloneCallbacks = {
- nullptr /* read */, StructuredCloneWriteCallback /* write */,
- nullptr /* reportError */, nullptr /* readTransfer */,
- nullptr /* writeTransfer */, nullptr /* freeTransfer */,
- nullptr /* canTransfer */, nullptr /* sabCloned */
+ nullptr /* read */,
+ StructuredCloneWriteCallback /* write */,
+ StructuredCloneErrorCallback /* reportError */,
+ nullptr /* readTransfer */,
+ nullptr /* writeTransfer */,
+ nullptr /* freeTransfer */,
+ nullptr /* canTransfer */,
+ nullptr /* sabCloned */
};
MOZ_ASSERT(aCx);
@@ -555,7 +565,7 @@ bool IDBObjectStore::DeserializeValue(
static const JSStructuredCloneCallbacks callbacks = {
StructuredCloneReadCallback<StructuredCloneReadInfoChild>,
nullptr,
- nullptr,
+ StructuredCloneErrorCallback,
nullptr,
nullptr,
nullptr,
@@ -1751,7 +1761,7 @@ bool IDBObjectStore::ValueWrapper::Clone(JSContext* aCx) {
static const JSStructuredCloneCallbacks callbacks = {
CopyingStructuredCloneReadCallback /* read */,
CopyingStructuredCloneWriteCallback /* write */,
- nullptr /* reportError */,
+ StructuredCloneErrorCallback /* reportError */,
nullptr /* readTransfer */,
nullptr /* writeTransfer */,
nullptr /* freeTransfer */,
diff --git a/dom/indexedDB/Key.cpp b/dom/indexedDB/Key.cpp
index 7e0c607617..a6223bfc95 100644
--- a/dom/indexedDB/Key.cpp
+++ b/dom/indexedDB/Key.cpp
@@ -16,6 +16,7 @@
#include "js/MemoryFunctions.h"
#include "js/Object.h" // JS::GetBuiltinClass
#include "js/PropertyAndElement.h" // JS_DefineElement, JS_GetProperty, JS_GetPropertyById, JS_HasOwnProperty, JS_HasOwnPropertyById
+#include "js/SharedArrayBuffer.h" // IsSharedArrayBufferObject
#include "js/Value.h"
#include "jsfriendapi.h"
#include "mozilla/Casting.h"
@@ -102,6 +103,84 @@ IDBResult<Ok, IDBSpecialValue::Invalid> ConvertArrayValueToKey(
aPolicy.EndSubkeyList();
return Ok();
}
+
+bool IsDetachedBuffer(JSContext* aCx, JS::Handle<JSObject*> aJsBufferSource) {
+ if (JS_IsArrayBufferViewObject(aJsBufferSource)) {
+ bool unused = false;
+ JS::Rooted<JSObject*> viewedArrayBuffer(
+ aCx, JS_GetArrayBufferViewBuffer(aCx, aJsBufferSource, &unused));
+ return JS::IsDetachedArrayBufferObject(viewedArrayBuffer);
+ }
+
+ return JS::IsDetachedArrayBufferObject(aJsBufferSource);
+}
+
+// To get a copy of the bytes held by the buffer source given a buffer source
+// type instance bufferSource:
+IDBResult<Span<const uint8_t>, IDBSpecialValue::Invalid>
+GetByteSpanFromJSBufferSource(JSContext* aCx,
+ JS::Handle<JSObject*> aJsBufferSource) {
+ // 1. Let jsBufferSource be the result of converting bufferSource to a
+ // JavaScript value.
+
+ // 2. Let jsArrayBuffer be jsBufferSource.
+ JS::Handle<JSObject*>& jsArrayBuffer = aJsBufferSource;
+
+ // 3. Let offset be 0.
+ size_t offset = 0u;
+
+ // 4. Let length be 0.
+ size_t length = 0u;
+
+ // 8. Let bytes be a new byte sequence of length equal to length.
+ uint8_t* bytes = nullptr; // Note: Copy is deferred, no allocation here
+
+ // 5. If jsBufferSource has a [[ViewedArrayBuffer]] internal slot, then:
+ if (JS_IsArrayBufferViewObject(aJsBufferSource)) {
+ // 5.1 Set jsArrayBuffer to jsBufferSource.[[ViewedArrayBuffer]].
+ // 5.2 Set offset to jsBufferSource.[[ByteOffset]].
+ // 5.3 Set length to jsBufferSource.[[ByteLength]].
+
+ // 9. For i in the range offset to offset + length − 1, inclusive, set
+ // bytes[i − offset] to GetValueFromBuffer(jsArrayBuffer, i, Uint8, true,
+ // Unordered).
+ (void)offset;
+ bool unused = false;
+ if (!JS_GetObjectAsArrayBufferView(jsArrayBuffer, &length, &unused,
+ &bytes)) {
+ return Err(IDBError(SpecialValues::Invalid));
+ }
+
+ // 6. Otherwise:
+ } else {
+ // 6.1 Assert: jsBufferSource is an ArrayBuffer or SharedArrayBuffer object.
+ MOZ_RELEASE_ASSERT(JS::IsArrayBufferObject(aJsBufferSource) ||
+ JS::IsSharedArrayBufferObject(aJsBufferSource));
+
+ // 6.2 Set length to jsBufferSource.[[ArrayBufferByteLength]].
+
+ // 9. For i in the range offset to offset + length − 1, inclusive, set
+ // bytes[i − offset] to GetValueFromBuffer(jsArrayBuffer, i, Uint8, true,
+ // Unordered).
+ (void)offset;
+ if (!JS::GetObjectAsArrayBuffer(jsArrayBuffer, &length, &bytes)) {
+ return Err(IDBError(SpecialValues::Invalid));
+ }
+ }
+
+ // 7. If IsDetachedBuffer(jsArrayBuffer) is true, then return the empty byte
+ // sequence.
+ if (IsDetachedBuffer(aCx, aJsBufferSource)) {
+ // Note: As the web platform tests assume, and as has been discussed at
+ // https://github.com/w3c/IndexedDB/issues/417 - we are better off by
+ // throwing a DataCloneError. The spec language is about to be revised.
+ return Err(IDBError(SpecialValues::Invalid));
+ }
+
+ // 10. Return bytes.
+ return Span<uint8_t>{bytes, length}.AsConst();
+}
+
} // namespace
/*
@@ -435,8 +514,19 @@ IDBResult<Ok, IDBSpecialValue::Invalid> Key::EncodeJSValInternal(
// If `input` is a buffer source type
if (JS::IsArrayBufferObject(object) || JS_IsArrayBufferViewObject(object)) {
- const bool isViewObject = JS_IsArrayBufferViewObject(object);
- return EncodeBinary(object, isViewObject, aTypeOffset);
+ // 1. Let bytes be the result of getting a copy of the bytes held by the
+ // buffer source input.
+
+ auto res = GetByteSpanFromJSBufferSource(aCx, object);
+
+ // Rethrow any exceptions.
+ if (res.isErr()) {
+ return res.propagateErr();
+ }
+
+ // 2. Return a new key with type binary and value bytes.
+ // Note: The copy takes place here.
+ return EncodeAsString(res.inspect(), eBinary + aTypeOffset);
}
// If IsArray(`input`)
@@ -518,13 +608,14 @@ nsresult Key::DecodeJSValInternal(const EncodedDataType*& aPos,
} else if (*aPos - aTypeOffset == eFloat) {
aVal.setDouble(DecodeNumber(aPos, aEnd));
} else if (*aPos - aTypeOffset == eBinary) {
- JSObject* binary = DecodeBinary(aPos, aEnd, aCx);
- if (!binary) {
+ JSObject* arrayBufferObject =
+ GetArrayBufferObjectFromDataRange(aPos, aEnd, aCx);
+ if (!arrayBufferObject) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
- aVal.setObject(*binary);
+ aVal.setObject(*arrayBufferObject);
} else {
MOZ_ASSERT_UNREACHABLE("Unknown key type!");
}
@@ -869,28 +960,10 @@ double Key::DecodeNumber(const EncodedDataType*& aPos,
return BitwiseCast<double>(bits);
}
-Result<Ok, nsresult> Key::EncodeBinary(JSObject* aObject, bool aIsViewObject,
- uint8_t aTypeOffset) {
- uint8_t* bufferData;
- size_t bufferLength;
-
- // We must use JS::GetObjectAsArrayBuffer()/JS_GetObjectAsArrayBufferView()
- // instead of js::GetArrayBufferLengthAndData(). The object might be wrapped,
- // the former will handle the wrapped case, the later won't.
- if (aIsViewObject) {
- bool unused;
- JS_GetObjectAsArrayBufferView(aObject, &bufferLength, &unused, &bufferData);
- } else {
- JS::GetObjectAsArrayBuffer(aObject, &bufferLength, &bufferData);
- }
-
- return EncodeAsString(Span{bufferData, bufferLength}.AsConst(),
- eBinary + aTypeOffset);
-}
-
// static
-JSObject* Key::DecodeBinary(const EncodedDataType*& aPos,
- const EncodedDataType* aEnd, JSContext* aCx) {
+JSObject* Key::GetArrayBufferObjectFromDataRange(const EncodedDataType*& aPos,
+ const EncodedDataType* aEnd,
+ JSContext* aCx) {
JS::Rooted<JSObject*> rv(aCx);
DecodeStringy<eBinary, uint8_t>(
aPos, aEnd,
diff --git a/dom/indexedDB/Key.h b/dom/indexedDB/Key.h
index 25ddd3e0b1..f4e6570a96 100644
--- a/dom/indexedDB/Key.h
+++ b/dom/indexedDB/Key.h
@@ -226,9 +226,6 @@ class Key {
Result<Ok, nsresult> EncodeNumber(double aFloat, uint8_t aType);
- Result<Ok, nsresult> EncodeBinary(JSObject* aObject, bool aIsViewObject,
- uint8_t aTypeOffset);
-
// Decoding functions. aPos points into mBuffer and is adjusted to point
// past the consumed value. (Note: this may be beyond aEnd).
static nsresult DecodeJSVal(const EncodedDataType*& aPos,
@@ -241,8 +238,9 @@ class Key {
static double DecodeNumber(const EncodedDataType*& aPos,
const EncodedDataType* aEnd);
- static JSObject* DecodeBinary(const EncodedDataType*& aPos,
- const EncodedDataType* aEnd, JSContext* aCx);
+ static JSObject* GetArrayBufferObjectFromDataRange(
+ const EncodedDataType*& aPos, const EncodedDataType* aEnd,
+ JSContext* aCx);
// Returns the size of the decoded data for stringy (string or binary),
// excluding a null terminator.
diff --git a/dom/indexedDB/crashtests/1499854-1.html b/dom/indexedDB/crashtests/1499854-1.html
index 0e10601134..7d3a86a62f 100644
--- a/dom/indexedDB/crashtests/1499854-1.html
+++ b/dom/indexedDB/crashtests/1499854-1.html
@@ -6,7 +6,7 @@
o1 = new Int32Array(51488)
o2 = new ArrayBuffer(13964)
for (let i = 0; i < 51488; i++) o1[i] = 0x41
- const dbRequest = window.indexedDB.open('', {})
+ const dbRequest = window.indexedDB.open('')
dbRequest.onupgradeneeded = function(event) {
const store = event.target.result.createObjectStore('IDBStore_0', {})
store.add({}, 'ObjectKey_0')
diff --git a/dom/indexedDB/crashtests/1857979-1.html b/dom/indexedDB/crashtests/1857979-1.html
index d97aa24562..be5a3c947c 100644
--- a/dom/indexedDB/crashtests/1857979-1.html
+++ b/dom/indexedDB/crashtests/1857979-1.html
@@ -1,10 +1,10 @@
<!DOCTYPE html>
<script>
window.addEventListener('load', async () => {
- const db1 = indexedDB.open('DB_1696052013002', {})
+ const db1 = indexedDB.open('DB_1696052013002')
db1.onsuccess = () => window.close()
const blob = new Blob(['0'], {})
- db2 = indexedDB.open('DB_1696052013003', {})
+ db2 = indexedDB.open('DB_1696052013003')
db2.onupgradeneeded = (e) => {
const store = e.target.result.createObjectStore('IDBStore_0', {
'autoIncrement': true,
diff --git a/dom/indexedDB/test/perfdocs/index.rst b/dom/indexedDB/test/perfdocs/index.rst
index ec54211038..5d7d98e3de 100644
--- a/dom/indexedDB/test/perfdocs/index.rst
+++ b/dom/indexedDB/test/perfdocs/index.rst
@@ -51,7 +51,7 @@ How to add more tests?
* Under the ``[test_name]`` section, specity the test parameters as a sequence of ``--browsertime.key=value`` arguments as a value of ``browsertime_args =``
* Under the ``[test_name]`` section, override any other values as needed
-* Add test as a subtest to run for Desktop ``taskcluster/ci/test/browsertime-desktop.yml`` (maybe also for mobile)
+* Add test as a subtest to run for Desktop ``taskcluster/kinds/test/browsertime-desktop.yml`` (maybe also for mobile)
* Add test documentation to ``testing/raptor/raptor/perfdocs/config.yml``
* Generated files:
diff --git a/dom/indexedDB/test/unit/test_invalid_version.js b/dom/indexedDB/test/unit/test_invalid_version.js
index ea5e2953d0..bf745cb93a 100644
--- a/dom/indexedDB/test/unit/test_invalid_version.js
+++ b/dom/indexedDB/test/unit/test_invalid_version.js
@@ -26,21 +26,5 @@ function* testSteps() {
is(e.name, "TypeError", "Good error name.");
}
- try {
- indexedDB.open(name, { version: 0 });
- ok(false, "Should have thrown!");
- } catch (e) {
- ok(e instanceof TypeError, "Got TypeError.");
- is(e.name, "TypeError", "Good error name.");
- }
-
- try {
- indexedDB.open(name, { version: -1 });
- ok(false, "Should have thrown!");
- } catch (e) {
- ok(e instanceof TypeError, "Got TypeError.");
- is(e.name, "TypeError", "Good error name.");
- }
-
finishTest();
}
diff --git a/dom/indexedDB/test/unit/test_metadata2Restore.js b/dom/indexedDB/test/unit/test_metadata2Restore.js
index da31eecc86..643e734e70 100644
--- a/dom/indexedDB/test/unit/test_metadata2Restore.js
+++ b/dom/indexedDB/test/unit/test_metadata2Restore.js
@@ -15,7 +15,7 @@ function* testSteps() {
attrs: { userContextId: 1 },
url: "http://localhost:81",
dbName: "dbC",
- dbOptions: { version: 1, storage: "default" },
+ dbVersion: 1,
},
// This one lives in storage/default/http+++localhost+82^userContextId=1
@@ -25,7 +25,7 @@ function* testSteps() {
attrs: { userContextId: 1 },
url: "http://localhost:82",
dbName: "dbD",
- dbOptions: { version: 1, storage: "default" },
+ dbVersion: 1,
},
// This one lives in storage/default/http+++localhost+83^userContextId=1
@@ -36,7 +36,7 @@ function* testSteps() {
attrs: { userContextId: 1 },
url: "http://localhost:83",
dbName: "dbE",
- dbOptions: { version: 1, storage: "default" },
+ dbVersion: 1,
},
// This one lives in storage/default/http+++localhost+84^userContextId=1
@@ -47,7 +47,7 @@ function* testSteps() {
attrs: { userContextId: 1 },
url: "http://localhost:84",
dbName: "dbF",
- dbOptions: { version: 1, storage: "default" },
+ dbVersion: 1,
},
// This one lives in storage/default/http+++localhost+85^userContextId=1
@@ -59,7 +59,7 @@ function* testSteps() {
attrs: { userContextId: 1 },
url: "http://localhost:85",
dbName: "dbG",
- dbOptions: { version: 1, storage: "default" },
+ dbVersion: 1,
},
// This one lives in storage/default/http+++localhost+86^userContextId=1
@@ -72,7 +72,7 @@ function* testSteps() {
attrs: { userContextId: 1 },
url: "http://localhost:86",
dbName: "dbH",
- dbOptions: { version: 1, storage: "default" },
+ dbVersion: 1,
},
// This one lives in storage/default/http+++localhost+87^userContextId=1
@@ -85,7 +85,7 @@ function* testSteps() {
attrs: { userContextId: 1 },
url: "http://localhost:87",
dbName: "dbI",
- dbOptions: { version: 1, storage: "default" },
+ dbVersion: 1,
},
// This one lives in storage/default/http+++localhost+88^userContextId=1
@@ -99,7 +99,7 @@ function* testSteps() {
attrs: { userContextId: 1 },
url: "http://localhost:88",
dbName: "dbJ",
- dbOptions: { version: 1, storage: "default" },
+ dbVersion: 1,
},
// This one lives in storage/default/http+++localhost+89^userContextId=1
@@ -113,7 +113,7 @@ function* testSteps() {
attrs: { userContextId: 1 },
url: "http://localhost:89",
dbName: "dbK",
- dbOptions: { version: 1, storage: "default" },
+ dbVersion: 1,
},
// This one lives in storage/default/http+++localhost+90^userContextId=1
@@ -128,7 +128,7 @@ function* testSteps() {
attrs: { userContextId: 1 },
url: "http://localhost:90",
dbName: "dbL",
- dbOptions: { version: 1, storage: "default" },
+ dbVersion: 1,
},
// This one lives in storage/default/http+++localhost+91^userContextId=1
@@ -144,7 +144,7 @@ function* testSteps() {
attrs: { userContextId: 1 },
url: "http://localhost:91",
dbName: "dbM",
- dbOptions: { version: 1, storage: "default" },
+ dbVersion: 1,
},
// This one lives in storage/default/http+++localhost+92^userContextId=1
@@ -160,7 +160,7 @@ function* testSteps() {
attrs: { userContextId: 1 },
url: "http://localhost:92",
dbName: "dbN",
- dbOptions: { version: 1, storage: "default" },
+ dbVersion: 1,
},
// This one lives in storage/default/http+++localhost+93^userContextId=1
@@ -177,7 +177,7 @@ function* testSteps() {
attrs: { userContextId: 1 },
url: "http://localhost:93",
dbName: "dbO",
- dbOptions: { version: 1, storage: "default" },
+ dbVersion: 1,
},
// This one lives in storage/default/http+++localhost+94^userContextId=1
@@ -195,7 +195,7 @@ function* testSteps() {
attrs: { userContextId: 1 },
url: "http://localhost:94",
dbName: "dbP",
- dbOptions: { version: 1, storage: "default" },
+ dbVersion: 1,
},
// This one lives in storage/default/http+++localhost+95^userContextId=1
@@ -213,7 +213,7 @@ function* testSteps() {
attrs: { userContextId: 1 },
url: "http://localhost:95",
dbName: "dbQ",
- dbOptions: { version: 1, storage: "default" },
+ dbVersion: 1,
},
// This one lives in storage/default/http+++localhost+96^userContextId=1
@@ -232,7 +232,7 @@ function* testSteps() {
attrs: { userContextId: 1 },
url: "http://localhost:96",
dbName: "dbR",
- dbOptions: { version: 1, storage: "default" },
+ dbVersion: 1,
},
// This one lives in storage/default/http+++localhost+97^userContextId=1
@@ -252,7 +252,7 @@ function* testSteps() {
attrs: { userContextId: 1 },
url: "http://localhost:97",
dbName: "dbS",
- dbOptions: { version: 1, storage: "default" },
+ dbVersion: 1,
},
// This one lives in storage/default/http+++localhost+98^userContextId=1
@@ -272,7 +272,7 @@ function* testSteps() {
attrs: { userContextId: 1 },
url: "http://localhost:98",
dbName: "dbT",
- dbOptions: { version: 1, storage: "default" },
+ dbVersion: 1,
},
];
@@ -287,10 +287,10 @@ function* testSteps() {
request = indexedDB.openForPrincipal(
principal,
params.dbName,
- params.dbOptions
+ params.dbVersion
);
} else {
- request = indexedDB.open(params.dbName, params.dbOptions);
+ request = indexedDB.open(params.dbName, params.dbVersion);
}
return request;
}
diff --git a/dom/indexedDB/test/unit/test_metadataRestore.js b/dom/indexedDB/test/unit/test_metadataRestore.js
index 001d4da65b..302b1614d0 100644
--- a/dom/indexedDB/test/unit/test_metadataRestore.js
+++ b/dom/indexedDB/test/unit/test_metadataRestore.js
@@ -12,70 +12,70 @@ function* testSteps() {
{
url: "http://localhost:81",
dbName: "dbC",
- dbOptions: { version: 1, storage: "default" },
+ dbVersion: 1,
},
// This one lives in storage/default/http+++localhost+82
{
url: "http://localhost:82",
dbName: "dbD",
- dbOptions: { version: 1, storage: "default" },
+ dbVersion: 1,
},
// This one lives in storage/default/http+++localhost+83
{
url: "http://localhost:83",
dbName: "dbE",
- dbOptions: { version: 1, storage: "default" },
+ dbVersion: 1,
},
// This one lives in storage/default/http+++localhost+84
{
url: "http://localhost:84",
dbName: "dbF",
- dbOptions: { version: 1, storage: "default" },
+ dbVersion: 1,
},
// This one lives in storage/default/http+++localhost+85
{
url: "http://localhost:85",
dbName: "dbG",
- dbOptions: { version: 1, storage: "default" },
+ dbVersion: 1,
},
// This one lives in storage/default/http+++localhost+86
{
url: "http://localhost:86",
dbName: "dbH",
- dbOptions: { version: 1, storage: "default" },
+ dbVersion: 1,
},
// This one lives in storage/default/http+++localhost+87
{
url: "http://localhost:87",
dbName: "dbI",
- dbOptions: { version: 1, storage: "default" },
+ dbVersion: 1,
},
// This one lives in storage/default/http+++localhost+88
{
url: "http://localhost:88",
dbName: "dbJ",
- dbOptions: { version: 1, storage: "default" },
+ dbVersion: 1,
},
// This one lives in storage/default/http+++localhost+89
{
url: "http://localhost:89",
dbName: "dbK",
- dbOptions: { version: 1, storage: "default" },
+ dbVersion: 1,
},
// This one lives in storage/default/http+++localhost+90
{
url: "http://localhost:90",
dbName: "dbL",
- dbOptions: { version: 1, storage: "default" },
+ dbVersion: 1,
},
];
@@ -90,10 +90,10 @@ function* testSteps() {
request = indexedDB.openForPrincipal(
principal,
params.dbName,
- params.dbOptions
+ params.dbVersion
);
} else {
- request = indexedDB.open(params.dbName, params.dbOptions);
+ request = indexedDB.open(params.dbName, params.dbVersion);
}
return request;
}