1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ActorsParentCommon.h"
10 #include "DatabaseFileInfo.h"
11 #include "DatabaseFileManager.h"
12 #include "IndexedDatabase.h" // for StructuredCloneFile...
13 #include "IndexedDatabaseInlines.h"
14 #include "IndexedDatabaseManager.h"
15 #include "IndexedDBCipherKeyManager.h"
16 #include "IndexedDBCommon.h"
17 #include "ReportInternalError.h"
24 #include <type_traits>
25 #include "MainThreadUtils.h"
26 #include "SafeRefPtr.h"
27 #include "js/RootingAPI.h"
28 #include "js/StructuredClone.h"
29 #include "mozIStorageConnection.h"
30 #include "mozIStorageStatement.h"
31 #include "mozIStorageValueArray.h"
32 #include "mozilla/Assertions.h"
33 #include "mozilla/CheckedInt.h"
34 #include "mozilla/ClearOnShutdown.h"
35 #include "mozilla/JSObjectHolder.h"
36 #include "mozilla/NullPrincipal.h"
37 #include "mozilla/ProfilerLabels.h"
38 #include "mozilla/RefPtr.h"
39 #include "mozilla/ResultExtensions.h"
40 #include "mozilla/StaticPrefs_dom.h"
41 #include "mozilla/StaticPtr.h"
42 #include "mozilla/Telemetry.h"
43 #include "mozilla/TelemetryScalarEnums.h"
44 #include "mozilla/dom/quota/DecryptingInputStream_impl.h"
45 #include "mozilla/dom/quota/QuotaCommon.h"
46 #include "mozilla/dom/quota/ResultExtensions.h"
47 #include "mozilla/dom/quota/ScopedLogExtraInfo.h"
48 #include "mozilla/fallible.h"
49 #include "mozilla/ipc/BackgroundParent.h"
50 #include "mozilla/mozalloc.h"
52 #include "nsCharSeparatedTokenizer.h"
53 #include "nsContentUtils.h"
56 #include "nsIInputStream.h"
57 #include "nsIPrincipal.h"
58 #include "nsIXPConnect.h"
59 #include "nsNetUtil.h"
61 #include "nsStringFlags.h"
62 #include "nsXULAppAPI.h"
63 #include "snappy/snappy.h"
67 namespace mozilla::dom::indexedDB
{
69 static_assert(SNAPPY_VERSION
== 0x010200);
71 using mozilla::ipc::IsOnBackgroundThread
;
73 const nsLiteralString kJournalDirectoryName
= u
"journals"_ns
;
77 constexpr StructuredCloneFileBase::FileType
ToStructuredCloneFileType(
78 const char16_t aTag
) {
81 return StructuredCloneFileBase::eMutableFile
;
84 return StructuredCloneFileBase::eStructuredClone
;
87 return StructuredCloneFileBase::eWasmBytecode
;
90 return StructuredCloneFileBase::eWasmCompiled
;
93 return StructuredCloneFileBase::eBlob
;
97 int32_t ToInteger(const nsAString
& aStr
, nsresult
* const aRv
) {
98 return aStr
.ToInteger(aRv
);
101 Result
<StructuredCloneFileParent
, nsresult
> DeserializeStructuredCloneFile(
102 const DatabaseFileManager
& aFileManager
,
103 const nsDependentSubstring
& aText
) {
104 MOZ_ASSERT(!aText
.IsEmpty());
106 const StructuredCloneFileBase::FileType type
=
107 ToStructuredCloneFileType(aText
.First());
109 QM_TRY_INSPECT(const auto& id
,
110 MOZ_TO_RESULT_GET_TYPED(
112 type
== StructuredCloneFileBase::eBlob
114 : static_cast<const nsAString
&>(Substring(aText
, 1))));
116 SafeRefPtr
<DatabaseFileInfo
> fileInfo
= aFileManager
.GetFileInfo(id
);
117 MOZ_ASSERT(fileInfo
);
118 // XXX In bug 1432133, for some reasons DatabaseFileInfo object cannot be
119 // got. This is just a short-term fix, and we are working on finding the real
120 // cause in bug 1519859.
121 QM_TRY(OkIf((bool)fileInfo
), Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
));
123 return StructuredCloneFileParent
{type
, std::move(fileInfo
)};
126 // This class helps to create only 1 sandbox.
127 class SandboxHolder final
{
129 NS_INLINE_DECL_REFCOUNTING(SandboxHolder
)
132 friend JSObject
* mozilla::dom::indexedDB::GetSandbox(JSContext
* aCx
);
134 ~SandboxHolder() = default;
136 static SandboxHolder
* GetOrCreate() {
137 MOZ_ASSERT(XRE_IsParentProcess());
138 MOZ_ASSERT(NS_IsMainThread());
140 static StaticRefPtr
<SandboxHolder
> sHolder
;
142 sHolder
= new SandboxHolder();
143 ClearOnShutdown(&sHolder
);
148 JSObject
* GetSandboxInternal(JSContext
* aCx
) {
150 nsIXPConnect
* const xpc
= nsContentUtils::XPConnect();
151 MOZ_ASSERT(xpc
, "This should never be null!");
153 // Let's use a null principal.
154 const nsCOMPtr
<nsIPrincipal
> principal
=
155 NullPrincipal::CreateWithoutOriginAttributes();
157 JS::Rooted
<JSObject
*> sandbox(aCx
);
159 MOZ_TO_RESULT(xpc
->CreateSandbox(aCx
, principal
, sandbox
.address())),
162 mSandbox
= new JSObjectHolder(aCx
, sandbox
);
165 return mSandbox
->GetJSObject();
168 RefPtr
<JSObjectHolder
> mSandbox
;
171 uint32_t CompressedByteCountForNumber(uint64_t aNumber
) {
172 // All bytes have 7 bits available.
174 while ((aNumber
>>= 7)) {
181 uint32_t CompressedByteCountForIndexId(IndexOrObjectStoreId aIndexId
) {
182 MOZ_ASSERT(aIndexId
);
183 MOZ_ASSERT(UINT64_MAX
- uint64_t(aIndexId
) >= uint64_t(aIndexId
),
186 return CompressedByteCountForNumber(uint64_t(aIndexId
* 2));
189 void WriteCompressedNumber(uint64_t aNumber
, uint8_t** aIterator
) {
190 MOZ_ASSERT(aIterator
);
191 MOZ_ASSERT(*aIterator
);
193 uint8_t*& buffer
= *aIterator
;
196 const uint8_t* const bufferStart
= buffer
;
197 const uint64_t originalNumber
= aNumber
;
201 uint64_t shiftedNumber
= aNumber
>> 7;
203 *buffer
++ = uint8_t(0x80 | (aNumber
& 0x7f));
204 aNumber
= shiftedNumber
;
206 *buffer
++ = uint8_t(aNumber
);
211 MOZ_ASSERT(buffer
> bufferStart
);
212 MOZ_ASSERT(uint32_t(buffer
- bufferStart
) ==
213 CompressedByteCountForNumber(originalNumber
));
216 void WriteCompressedIndexId(IndexOrObjectStoreId aIndexId
, bool aUnique
,
217 uint8_t** aIterator
) {
218 MOZ_ASSERT(aIndexId
);
219 MOZ_ASSERT(UINT64_MAX
- uint64_t(aIndexId
) >= uint64_t(aIndexId
),
221 MOZ_ASSERT(aIterator
);
222 MOZ_ASSERT(*aIterator
);
224 const uint64_t indexId
= (uint64_t(aIndexId
* 2) | (aUnique
? 1 : 0));
225 WriteCompressedNumber(indexId
, aIterator
);
228 // aOutIndexValues is an output parameter, since its storage is reused.
229 nsresult
ReadCompressedIndexDataValuesFromBlob(
230 const Span
<const uint8_t> aBlobData
,
231 nsTArray
<IndexDataValue
>* aOutIndexValues
) {
232 MOZ_ASSERT(!NS_IsMainThread());
233 MOZ_ASSERT(!IsOnBackgroundThread());
234 MOZ_ASSERT(!aBlobData
.IsEmpty());
235 MOZ_ASSERT(aOutIndexValues
);
236 MOZ_ASSERT(aOutIndexValues
->IsEmpty());
238 AUTO_PROFILER_LABEL("ReadCompressedIndexDataValuesFromBlob", DOM
);
240 // XXX Is this check still necessary with a Span? Or should it rather be moved
242 QM_TRY(OkIf(uintptr_t(aBlobData
.Elements()) <=
243 UINTPTR_MAX
- aBlobData
.LengthBytes()),
244 NS_ERROR_FILE_CORRUPTED
, IDB_REPORT_INTERNAL_ERR_LAMBDA
);
246 for (auto remainder
= aBlobData
; !remainder
.IsEmpty();) {
247 QM_TRY_INSPECT((const auto& [indexId
, unique
, remainderAfterIndexId
]),
248 ReadCompressedIndexId(remainder
));
250 QM_TRY(OkIf(!remainderAfterIndexId
.IsEmpty()), NS_ERROR_FILE_CORRUPTED
,
251 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
253 // Read key buffer length.
255 (const auto& [keyBufferLength
, remainderAfterKeyBufferLength
]),
256 ReadCompressedNumber(remainderAfterIndexId
));
258 QM_TRY(OkIf(!remainderAfterKeyBufferLength
.IsEmpty()),
259 NS_ERROR_FILE_CORRUPTED
, IDB_REPORT_INTERNAL_ERR_LAMBDA
);
261 QM_TRY(OkIf(keyBufferLength
<= uint64_t(UINT32_MAX
)),
262 NS_ERROR_FILE_CORRUPTED
, IDB_REPORT_INTERNAL_ERR_LAMBDA
);
264 QM_TRY(OkIf(keyBufferLength
<= remainderAfterKeyBufferLength
.Length()),
265 NS_ERROR_FILE_CORRUPTED
, IDB_REPORT_INTERNAL_ERR_LAMBDA
);
267 const auto [keyBuffer
, remainderAfterKeyBuffer
] =
268 remainderAfterKeyBufferLength
.SplitAt(keyBufferLength
);
270 IndexDataValue
{indexId
, unique
, Key
{nsCString
{AsChars(keyBuffer
)}}};
272 // Read sort key buffer length.
274 (const auto& [sortKeyBufferLength
, remainderAfterSortKeyBufferLength
]),
275 ReadCompressedNumber(remainderAfterKeyBuffer
));
277 remainder
= remainderAfterSortKeyBufferLength
;
278 if (sortKeyBufferLength
> 0) {
279 QM_TRY(OkIf(!remainder
.IsEmpty()), NS_ERROR_FILE_CORRUPTED
,
280 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
282 QM_TRY(OkIf(sortKeyBufferLength
<= uint64_t(UINT32_MAX
)),
283 NS_ERROR_FILE_CORRUPTED
, IDB_REPORT_INTERNAL_ERR_LAMBDA
);
285 QM_TRY(OkIf(sortKeyBufferLength
<= remainder
.Length()),
286 NS_ERROR_FILE_CORRUPTED
, IDB_REPORT_INTERNAL_ERR_LAMBDA
);
288 const auto [sortKeyBuffer
, remainderAfterSortKeyBuffer
] =
289 remainder
.SplitAt(sortKeyBufferLength
);
290 idv
.mLocaleAwarePosition
= Key
{nsCString
{AsChars(sortKeyBuffer
)}};
291 remainder
= remainderAfterSortKeyBuffer
;
294 QM_TRY(OkIf(aOutIndexValues
->AppendElement(std::move(idv
), fallible
)),
295 NS_ERROR_OUT_OF_MEMORY
, IDB_REPORT_INTERNAL_ERR_LAMBDA
);
297 aOutIndexValues
->Sort();
302 // aOutIndexValues is an output parameter, since its storage is reused.
303 template <typename T
>
304 nsresult
ReadCompressedIndexDataValuesFromSource(
305 T
& aSource
, uint32_t aColumnIndex
,
306 nsTArray
<IndexDataValue
>* aOutIndexValues
) {
307 MOZ_ASSERT(!NS_IsMainThread());
308 MOZ_ASSERT(!IsOnBackgroundThread());
309 MOZ_ASSERT(aOutIndexValues
);
310 MOZ_ASSERT(aOutIndexValues
->IsEmpty());
313 const int32_t& columnType
,
314 MOZ_TO_RESULT_INVOKE_MEMBER(aSource
, GetTypeOfIndex
, aColumnIndex
));
316 switch (columnType
) {
317 case mozIStorageStatement::VALUE_TYPE_NULL
:
320 case mozIStorageStatement::VALUE_TYPE_BLOB
: {
321 // XXX ToResultInvoke does not support multiple output parameters yet, so
322 // we also can't use QM_TRY_UNWRAP/QM_TRY_INSPECT here.
323 const uint8_t* blobData
;
324 uint32_t blobDataLength
;
325 QM_TRY(MOZ_TO_RESULT(
326 aSource
.GetSharedBlob(aColumnIndex
, &blobDataLength
, &blobData
)));
328 QM_TRY(OkIf(blobDataLength
), NS_ERROR_FILE_CORRUPTED
,
329 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
331 QM_TRY(MOZ_TO_RESULT(ReadCompressedIndexDataValuesFromBlob(
332 Span(blobData
, blobDataLength
), aOutIndexValues
)));
338 return NS_ERROR_FILE_CORRUPTED
;
342 Result
<StructuredCloneReadInfoParent
, nsresult
>
343 GetStructuredCloneReadInfoFromBlob(const uint8_t* aBlobData
,
344 uint32_t aBlobDataLength
,
345 const DatabaseFileManager
& aFileManager
,
346 const nsAString
& aFileIds
) {
347 MOZ_ASSERT(!IsOnBackgroundThread());
349 AUTO_PROFILER_LABEL("GetStructuredCloneReadInfoFromBlob", DOM
);
351 const char* const compressed
= reinterpret_cast<const char*>(aBlobData
);
352 const size_t compressedLength
= size_t(aBlobDataLength
);
354 size_t uncompressedLength
;
355 QM_TRY(OkIf(snappy::GetUncompressedLength(compressed
, compressedLength
,
356 &uncompressedLength
)),
357 Err(NS_ERROR_FILE_CORRUPTED
));
359 // `data` (JSStructuredCloneData) currently uses 4k buffer internally.
360 // For performance reasons, it's better to align `uncompressed` with that.
361 AutoTArray
<uint8_t, 4096> uncompressed
;
362 QM_TRY(OkIf(uncompressed
.SetLength(uncompressedLength
, fallible
)),
363 Err(NS_ERROR_OUT_OF_MEMORY
));
365 char* const uncompressedBuffer
=
366 reinterpret_cast<char*>(uncompressed
.Elements());
368 QM_TRY(OkIf(snappy::RawUncompress(compressed
, compressedLength
,
369 uncompressedBuffer
)),
370 Err(NS_ERROR_FILE_CORRUPTED
));
372 JSStructuredCloneData
data(JS::StructuredCloneScope::DifferentProcess
);
373 QM_TRY(OkIf(data
.AppendBytes(uncompressedBuffer
, uncompressed
.Length())),
374 Err(NS_ERROR_OUT_OF_MEMORY
));
376 nsTArray
<StructuredCloneFileParent
> files
;
377 if (!aFileIds
.IsVoid()) {
379 DeserializeStructuredCloneFiles(aFileManager
, aFileIds
));
382 return StructuredCloneReadInfoParent
{std::move(data
), std::move(files
),
386 Result
<StructuredCloneReadInfoParent
, nsresult
>
387 GetStructuredCloneReadInfoFromExternalBlob(
388 uint64_t aIntData
, const DatabaseFileManager
& aFileManager
,
389 const nsAString
& aFileIds
) {
390 MOZ_ASSERT(!IsOnBackgroundThread());
392 AUTO_PROFILER_LABEL("GetStructuredCloneReadInfoFromExternalBlob", DOM
);
394 nsTArray
<StructuredCloneFileParent
> files
;
395 if (!aFileIds
.IsVoid()) {
397 DeserializeStructuredCloneFiles(aFileManager
, aFileIds
));
400 // Higher and lower 32 bits described
401 // in ObjectStoreAddOrPutRequestOp::DoDatabaseWork.
402 const uint32_t index
= uint32_t(aIntData
& UINT32_MAX
);
404 QM_TRY(OkIf(index
< files
.Length()), Err(NS_ERROR_UNEXPECTED
),
405 [](const auto&) { MOZ_ASSERT(false, "Bad index value!"); });
407 if (StaticPrefs::dom_indexedDB_preprocessing()) {
408 return StructuredCloneReadInfoParent
{
409 JSStructuredCloneData
{JS::StructuredCloneScope::DifferentProcess
},
410 std::move(files
), true};
413 // XXX Why can there be multiple files, but we use only a single one here?
414 const StructuredCloneFileParent
& file
= files
[index
];
415 MOZ_ASSERT(file
.Type() == StructuredCloneFileBase::eStructuredClone
);
417 Maybe
<CipherKey
> maybeKey
;
419 if (aFileManager
.IsInPrivateBrowsingMode()) {
421 fileKeyId
.AppendInt(file
.FileInfo().Id());
423 maybeKey
= aFileManager
.MutableCipherKeyManagerRef().Get(fileKeyId
);
426 auto data
= JSStructuredCloneData
{JS::StructuredCloneScope::DifferentProcess
};
429 const nsCOMPtr
<nsIFile
> nativeFile
= file
.FileInfo().GetFileForFileInfo();
430 QM_TRY(OkIf(nativeFile
), Err(NS_ERROR_FAILURE
));
433 const auto& fileInputStream
,
434 NS_NewLocalFileInputStream(nativeFile
)
435 .andThen([maybeKey
](auto fileInputStream
)
436 -> Result
<nsCOMPtr
<nsIInputStream
>, nsresult
> {
438 return nsCOMPtr
<nsIInputStream
>{MakeRefPtr
<
439 quota::DecryptingInputStream
<IndexedDBCipherStrategy
>>(
440 WrapNotNull(std::move(fileInputStream
)),
441 kEncryptedStreamBlockSize
, *maybeKey
)};
444 return fileInputStream
;
447 QM_TRY(MOZ_TO_RESULT(
448 SnappyUncompressStructuredCloneData(*fileInputStream
, data
)));
451 return StructuredCloneReadInfoParent
{std::move(data
), std::move(files
),
455 template <typename T
>
456 Result
<StructuredCloneReadInfoParent
, nsresult
>
457 GetStructuredCloneReadInfoFromSource(T
* aSource
, uint32_t aDataIndex
,
458 uint32_t aFileIdsIndex
,
459 const DatabaseFileManager
& aFileManager
) {
460 MOZ_ASSERT(!IsOnBackgroundThread());
464 const int32_t& columnType
,
465 MOZ_TO_RESULT_INVOKE_MEMBER(aSource
, GetTypeOfIndex
, aDataIndex
));
467 QM_TRY_INSPECT(const bool& isNull
, MOZ_TO_RESULT_INVOKE_MEMBER(
468 aSource
, GetIsNull
, aFileIdsIndex
));
470 QM_TRY_INSPECT(const nsString
& fileIds
, ([aSource
, aFileIdsIndex
, isNull
] {
471 return isNull
? Result
<nsString
, nsresult
>{VoidString()}
472 : MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
473 nsString
, aSource
, GetString
,
477 switch (columnType
) {
478 case mozIStorageStatement::VALUE_TYPE_INTEGER
: {
480 const int64_t& intData
,
481 MOZ_TO_RESULT_INVOKE_MEMBER(aSource
, GetInt64
, aDataIndex
));
484 memcpy(&uintData
, &intData
, sizeof(uint64_t));
486 return GetStructuredCloneReadInfoFromExternalBlob(uintData
, aFileManager
,
490 case mozIStorageStatement::VALUE_TYPE_BLOB
: {
491 const uint8_t* blobData
;
492 uint32_t blobDataLength
;
493 QM_TRY(MOZ_TO_RESULT(
494 aSource
->GetSharedBlob(aDataIndex
, &blobDataLength
, &blobData
)));
496 return GetStructuredCloneReadInfoFromBlob(blobData
, blobDataLength
,
497 aFileManager
, fileIds
);
501 return Err(NS_ERROR_FILE_CORRUPTED
);
507 IndexDataValue::IndexDataValue() : mIndexId(0), mUnique(false) {
508 MOZ_COUNT_CTOR(IndexDataValue
);
511 #if defined(DEBUG) || defined(NS_BUILD_REFCNT_LOGGING)
512 IndexDataValue::IndexDataValue(IndexDataValue
&& aOther
) noexcept
513 : mIndexId(aOther
.mIndexId
),
514 mPosition(std::move(aOther
.mPosition
)),
515 mLocaleAwarePosition(std::move(aOther
.mLocaleAwarePosition
)),
516 mUnique(aOther
.mUnique
) {
517 MOZ_ASSERT(!aOther
.mPosition
.IsUnset());
519 MOZ_COUNT_CTOR(IndexDataValue
);
523 IndexDataValue::IndexDataValue(IndexOrObjectStoreId aIndexId
, bool aUnique
,
524 const Key
& aPosition
)
525 : mIndexId(aIndexId
), mPosition(aPosition
), mUnique(aUnique
) {
526 MOZ_ASSERT(!aPosition
.IsUnset());
528 MOZ_COUNT_CTOR(IndexDataValue
);
531 IndexDataValue::IndexDataValue(IndexOrObjectStoreId aIndexId
, bool aUnique
,
532 const Key
& aPosition
,
533 const Key
& aLocaleAwarePosition
)
534 : mIndexId(aIndexId
),
535 mPosition(aPosition
),
536 mLocaleAwarePosition(aLocaleAwarePosition
),
538 MOZ_ASSERT(!aPosition
.IsUnset());
540 MOZ_COUNT_CTOR(IndexDataValue
);
543 bool IndexDataValue::operator==(const IndexDataValue
& aOther
) const {
544 if (mIndexId
!= aOther
.mIndexId
) {
547 if (mLocaleAwarePosition
.IsUnset()) {
548 return mPosition
== aOther
.mPosition
;
550 return mLocaleAwarePosition
== aOther
.mLocaleAwarePosition
;
553 bool IndexDataValue::operator<(const IndexDataValue
& aOther
) const {
554 if (mIndexId
== aOther
.mIndexId
) {
555 if (mLocaleAwarePosition
.IsUnset()) {
556 return mPosition
< aOther
.mPosition
;
558 return mLocaleAwarePosition
< aOther
.mLocaleAwarePosition
;
561 return mIndexId
< aOther
.mIndexId
;
564 JSObject
* GetSandbox(JSContext
* aCx
) {
565 SandboxHolder
* holder
= SandboxHolder::GetOrCreate();
566 return holder
->GetSandboxInternal(aCx
);
569 Result
<std::pair
<UniqueFreePtr
<uint8_t>, uint32_t>, nsresult
>
570 MakeCompressedIndexDataValues(const nsTArray
<IndexDataValue
>& aIndexValues
) {
571 MOZ_ASSERT(!NS_IsMainThread());
572 MOZ_ASSERT(!IsOnBackgroundThread());
574 AUTO_PROFILER_LABEL("MakeCompressedIndexDataValues", DOM
);
576 const uint32_t arrayLength
= aIndexValues
.Length();
578 return std::pair
{UniqueFreePtr
<uint8_t>{}, 0u};
581 // First calculate the size of the final buffer.
582 const auto blobDataLength
= std::accumulate(
583 aIndexValues
.cbegin(), aIndexValues
.cend(), CheckedUint32(0),
584 [](CheckedUint32 sum
, const IndexDataValue
& info
) {
585 const nsCString
& keyBuffer
= info
.mPosition
.GetBuffer();
586 const nsCString
& sortKeyBuffer
= info
.mLocaleAwarePosition
.GetBuffer();
587 const uint32_t keyBufferLength
= keyBuffer
.Length();
588 const uint32_t sortKeyBufferLength
= sortKeyBuffer
.Length();
590 MOZ_ASSERT(!keyBuffer
.IsEmpty());
592 return sum
+ CompressedByteCountForIndexId(info
.mIndexId
) +
593 CompressedByteCountForNumber(keyBufferLength
) +
594 CompressedByteCountForNumber(sortKeyBufferLength
) +
595 keyBufferLength
+ sortKeyBufferLength
;
598 QM_TRY(OkIf(blobDataLength
.isValid()),
599 Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
),
600 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
602 UniqueFreePtr
<uint8_t> blobData(
603 static_cast<uint8_t*>(malloc(blobDataLength
.value())));
604 QM_TRY(OkIf(static_cast<bool>(blobData
)), Err(NS_ERROR_OUT_OF_MEMORY
),
605 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
607 uint8_t* blobDataIter
= blobData
.get();
609 for (const IndexDataValue
& info
: aIndexValues
) {
610 const nsCString
& keyBuffer
= info
.mPosition
.GetBuffer();
611 const nsCString
& sortKeyBuffer
= info
.mLocaleAwarePosition
.GetBuffer();
612 const uint32_t keyBufferLength
= keyBuffer
.Length();
613 const uint32_t sortKeyBufferLength
= sortKeyBuffer
.Length();
615 WriteCompressedIndexId(info
.mIndexId
, info
.mUnique
, &blobDataIter
);
616 WriteCompressedNumber(keyBufferLength
, &blobDataIter
);
618 memcpy(blobDataIter
, keyBuffer
.get(), keyBufferLength
);
619 blobDataIter
+= keyBufferLength
;
621 WriteCompressedNumber(sortKeyBufferLength
, &blobDataIter
);
623 memcpy(blobDataIter
, sortKeyBuffer
.get(), sortKeyBufferLength
);
624 blobDataIter
+= sortKeyBufferLength
;
627 MOZ_ASSERT(blobDataIter
== blobData
.get() + blobDataLength
.value());
629 return std::pair
{std::move(blobData
), blobDataLength
.value()};
632 nsresult
ReadCompressedIndexDataValues(
633 mozIStorageStatement
& aStatement
, uint32_t aColumnIndex
,
634 nsTArray
<IndexDataValue
>& aOutIndexValues
) {
635 return ReadCompressedIndexDataValuesFromSource(aStatement
, aColumnIndex
,
639 template <typename T
>
640 Result
<IndexDataValuesAutoArray
, nsresult
> ReadCompressedIndexDataValues(
641 T
& aValues
, uint32_t aColumnIndex
) {
642 return MOZ_TO_RESULT_INVOKE_TYPED(IndexDataValuesAutoArray
,
643 &ReadCompressedIndexDataValuesFromSource
<T
>,
644 aValues
, aColumnIndex
);
647 template Result
<IndexDataValuesAutoArray
, nsresult
>
648 ReadCompressedIndexDataValues
<mozIStorageValueArray
>(mozIStorageValueArray
&,
651 template Result
<IndexDataValuesAutoArray
, nsresult
>
652 ReadCompressedIndexDataValues
<mozIStorageStatement
>(mozIStorageStatement
&,
655 Result
<std::tuple
<IndexOrObjectStoreId
, bool, Span
<const uint8_t>>, nsresult
>
656 ReadCompressedIndexId(const Span
<const uint8_t> aData
) {
657 QM_TRY_INSPECT((const auto& [indexId
, remainder
]),
658 ReadCompressedNumber(aData
));
660 MOZ_ASSERT(UINT64_MAX
/ 2 >= uint64_t(indexId
), "Bad index id!");
662 return std::tuple
{IndexOrObjectStoreId(indexId
>> 1), indexId
% 2 == 1,
666 Result
<std::pair
<uint64_t, mozilla::Span
<const uint8_t>>, nsresult
>
667 ReadCompressedNumber(const Span
<const uint8_t> aSpan
) {
668 uint8_t shiftCounter
= 0;
671 const auto end
= aSpan
.cend();
674 std::find_if(aSpan
.cbegin(), end
, [&result
, &shiftCounter
](uint8_t byte
) {
675 MOZ_ASSERT(shiftCounter
<= 56, "Shifted too many bits!");
677 result
+= (uint64_t(byte
& 0x7f) << shiftCounter
);
680 return !(byte
& 0x80);
683 QM_TRY(OkIf(newPos
!= end
), Err(NS_ERROR_FILE_CORRUPTED
), [](const auto&) {
685 IDB_REPORT_INTERNAL_ERR();
688 return std::pair
{result
, Span
{newPos
+ 1, end
}};
691 Result
<StructuredCloneReadInfoParent
, nsresult
>
692 GetStructuredCloneReadInfoFromValueArray(
693 mozIStorageValueArray
* aValues
, uint32_t aDataIndex
, uint32_t aFileIdsIndex
,
694 const DatabaseFileManager
& aFileManager
) {
695 return GetStructuredCloneReadInfoFromSource(aValues
, aDataIndex
,
696 aFileIdsIndex
, aFileManager
);
699 Result
<StructuredCloneReadInfoParent
, nsresult
>
700 GetStructuredCloneReadInfoFromStatement(
701 mozIStorageStatement
* aStatement
, uint32_t aDataIndex
,
702 uint32_t aFileIdsIndex
, const DatabaseFileManager
& aFileManager
) {
703 return GetStructuredCloneReadInfoFromSource(aStatement
, aDataIndex
,
704 aFileIdsIndex
, aFileManager
);
707 Result
<nsTArray
<StructuredCloneFileParent
>, nsresult
>
708 DeserializeStructuredCloneFiles(const DatabaseFileManager
& aFileManager
,
709 const nsAString
& aText
) {
710 MOZ_ASSERT(!IsOnBackgroundThread());
712 nsTArray
<StructuredCloneFileParent
> result
;
713 for (const auto& token
:
714 nsCharSeparatedTokenizerTemplate
<NS_TokenizerIgnoreNothing
>(aText
, ' ')
716 MOZ_ASSERT(!token
.IsEmpty());
718 QM_TRY_UNWRAP(auto structuredCloneFile
,
719 DeserializeStructuredCloneFile(aFileManager
, token
));
721 result
.EmplaceBack(std::move(structuredCloneFile
));
727 nsresult
ExecuteSimpleSQLSequence(mozIStorageConnection
& aConnection
,
728 Span
<const nsLiteralCString
> aSQLCommands
) {
729 for (const auto& aSQLCommand
: aSQLCommands
) {
730 const auto extraInfo
= quota::ScopedLogExtraInfo
{
731 quota::ScopedLogExtraInfo::kTagQueryTainted
, aSQLCommand
};
733 QM_TRY(MOZ_TO_RESULT(aConnection
.ExecuteSimpleSQL(aSQLCommand
)));
739 } // namespace mozilla::dom::indexedDB