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 "IndexedDBCommon.h"
16 #include "ReportInternalError.h"
23 #include <type_traits>
24 #include "MainThreadUtils.h"
25 #include "SafeRefPtr.h"
26 #include "js/RootingAPI.h"
27 #include "js/StructuredClone.h"
28 #include "mozIStorageConnection.h"
29 #include "mozIStorageStatement.h"
30 #include "mozIStorageValueArray.h"
31 #include "mozilla/Assertions.h"
32 #include "mozilla/CheckedInt.h"
33 #include "mozilla/ClearOnShutdown.h"
34 #include "mozilla/JSObjectHolder.h"
35 #include "mozilla/NullPrincipal.h"
36 #include "mozilla/ProfilerLabels.h"
37 #include "mozilla/RefPtr.h"
38 #include "mozilla/ResultExtensions.h"
39 #include "mozilla/StaticPtr.h"
40 #include "mozilla/Telemetry.h"
41 #include "mozilla/TelemetryScalarEnums.h"
42 #include "mozilla/dom/quota/DecryptingInputStream_impl.h"
43 #include "mozilla/dom/quota/QuotaCommon.h"
44 #include "mozilla/dom/quota/ResultExtensions.h"
45 #include "mozilla/dom/quota/ScopedLogExtraInfo.h"
46 #include "mozilla/fallible.h"
47 #include "mozilla/ipc/BackgroundParent.h"
48 #include "mozilla/mozalloc.h"
50 #include "nsCharSeparatedTokenizer.h"
51 #include "nsContentUtils.h"
54 #include "nsIInputStream.h"
55 #include "nsIPrincipal.h"
56 #include "nsIXPConnect.h"
57 #include "nsNetUtil.h"
59 #include "nsStringFlags.h"
60 #include "nsXULAppAPI.h"
61 #include "snappy/snappy.h"
65 namespace mozilla::dom::indexedDB
{
67 static_assert(SNAPPY_VERSION
== 0x010108);
69 using mozilla::ipc::IsOnBackgroundThread
;
71 const nsLiteralString kJournalDirectoryName
= u
"journals"_ns
;
75 constexpr StructuredCloneFileBase::FileType
ToStructuredCloneFileType(
76 const char16_t aTag
) {
79 return StructuredCloneFileBase::eMutableFile
;
82 return StructuredCloneFileBase::eStructuredClone
;
85 return StructuredCloneFileBase::eWasmBytecode
;
88 return StructuredCloneFileBase::eWasmCompiled
;
91 return StructuredCloneFileBase::eBlob
;
95 int32_t ToInteger(const nsAString
& aStr
, nsresult
* const aRv
) {
96 return aStr
.ToInteger(aRv
);
99 Result
<StructuredCloneFileParent
, nsresult
> DeserializeStructuredCloneFile(
100 const DatabaseFileManager
& aFileManager
,
101 const nsDependentSubstring
& aText
) {
102 MOZ_ASSERT(!aText
.IsEmpty());
104 const StructuredCloneFileBase::FileType type
=
105 ToStructuredCloneFileType(aText
.First());
107 QM_TRY_INSPECT(const auto& id
,
108 MOZ_TO_RESULT_GET_TYPED(
110 type
== StructuredCloneFileBase::eBlob
112 : static_cast<const nsAString
&>(Substring(aText
, 1))));
114 SafeRefPtr
<DatabaseFileInfo
> fileInfo
= aFileManager
.GetFileInfo(id
);
115 MOZ_ASSERT(fileInfo
);
116 // XXX In bug 1432133, for some reasons DatabaseFileInfo object cannot be
117 // got. This is just a short-term fix, and we are working on finding the real
118 // cause in bug 1519859.
121 "Corrupt structured clone data detected in IndexedDB. Failing the "
122 "database request. Bug 1519859 will address this problem.");
123 Telemetry::ScalarAdd(Telemetry::ScalarID::IDB_FAILURE_FILEINFO_ERROR
, 1);
125 return Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
);
128 return StructuredCloneFileParent
{type
, std::move(fileInfo
)};
131 // This class helps to create only 1 sandbox.
132 class SandboxHolder final
{
134 NS_INLINE_DECL_REFCOUNTING(SandboxHolder
)
137 friend JSObject
* mozilla::dom::indexedDB::GetSandbox(JSContext
* aCx
);
139 ~SandboxHolder() = default;
141 static SandboxHolder
* GetOrCreate() {
142 MOZ_ASSERT(XRE_IsParentProcess());
143 MOZ_ASSERT(NS_IsMainThread());
145 static StaticRefPtr
<SandboxHolder
> sHolder
;
147 sHolder
= new SandboxHolder();
148 ClearOnShutdown(&sHolder
);
153 JSObject
* GetSandboxInternal(JSContext
* aCx
) {
155 nsIXPConnect
* const xpc
= nsContentUtils::XPConnect();
156 MOZ_ASSERT(xpc
, "This should never be null!");
158 // Let's use a null principal.
159 const nsCOMPtr
<nsIPrincipal
> principal
=
160 NullPrincipal::CreateWithoutOriginAttributes();
162 JS::Rooted
<JSObject
*> sandbox(aCx
);
164 MOZ_TO_RESULT(xpc
->CreateSandbox(aCx
, principal
, sandbox
.address())),
167 mSandbox
= new JSObjectHolder(aCx
, sandbox
);
170 return mSandbox
->GetJSObject();
173 RefPtr
<JSObjectHolder
> mSandbox
;
176 uint32_t CompressedByteCountForNumber(uint64_t aNumber
) {
177 // All bytes have 7 bits available.
179 while ((aNumber
>>= 7)) {
186 uint32_t CompressedByteCountForIndexId(IndexOrObjectStoreId aIndexId
) {
187 MOZ_ASSERT(aIndexId
);
188 MOZ_ASSERT(UINT64_MAX
- uint64_t(aIndexId
) >= uint64_t(aIndexId
),
191 return CompressedByteCountForNumber(uint64_t(aIndexId
* 2));
194 void WriteCompressedNumber(uint64_t aNumber
, uint8_t** aIterator
) {
195 MOZ_ASSERT(aIterator
);
196 MOZ_ASSERT(*aIterator
);
198 uint8_t*& buffer
= *aIterator
;
201 const uint8_t* const bufferStart
= buffer
;
202 const uint64_t originalNumber
= aNumber
;
206 uint64_t shiftedNumber
= aNumber
>> 7;
208 *buffer
++ = uint8_t(0x80 | (aNumber
& 0x7f));
209 aNumber
= shiftedNumber
;
211 *buffer
++ = uint8_t(aNumber
);
216 MOZ_ASSERT(buffer
> bufferStart
);
217 MOZ_ASSERT(uint32_t(buffer
- bufferStart
) ==
218 CompressedByteCountForNumber(originalNumber
));
221 void WriteCompressedIndexId(IndexOrObjectStoreId aIndexId
, bool aUnique
,
222 uint8_t** aIterator
) {
223 MOZ_ASSERT(aIndexId
);
224 MOZ_ASSERT(UINT64_MAX
- uint64_t(aIndexId
) >= uint64_t(aIndexId
),
226 MOZ_ASSERT(aIterator
);
227 MOZ_ASSERT(*aIterator
);
229 const uint64_t indexId
= (uint64_t(aIndexId
* 2) | (aUnique
? 1 : 0));
230 WriteCompressedNumber(indexId
, aIterator
);
233 // aOutIndexValues is an output parameter, since its storage is reused.
234 nsresult
ReadCompressedIndexDataValuesFromBlob(
235 const Span
<const uint8_t> aBlobData
,
236 nsTArray
<IndexDataValue
>* aOutIndexValues
) {
237 MOZ_ASSERT(!NS_IsMainThread());
238 MOZ_ASSERT(!IsOnBackgroundThread());
239 MOZ_ASSERT(!aBlobData
.IsEmpty());
240 MOZ_ASSERT(aOutIndexValues
);
241 MOZ_ASSERT(aOutIndexValues
->IsEmpty());
243 AUTO_PROFILER_LABEL("ReadCompressedIndexDataValuesFromBlob", DOM
);
245 // XXX Is this check still necessary with a Span? Or should it rather be moved
247 QM_TRY(OkIf(uintptr_t(aBlobData
.Elements()) <=
248 UINTPTR_MAX
- aBlobData
.LengthBytes()),
249 NS_ERROR_FILE_CORRUPTED
, IDB_REPORT_INTERNAL_ERR_LAMBDA
);
251 for (auto remainder
= aBlobData
; !remainder
.IsEmpty();) {
252 QM_TRY_INSPECT((const auto& [indexId
, unique
, remainderAfterIndexId
]),
253 ReadCompressedIndexId(remainder
));
255 QM_TRY(OkIf(!remainderAfterIndexId
.IsEmpty()), NS_ERROR_FILE_CORRUPTED
,
256 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
258 // Read key buffer length.
260 (const auto& [keyBufferLength
, remainderAfterKeyBufferLength
]),
261 ReadCompressedNumber(remainderAfterIndexId
));
263 QM_TRY(OkIf(!remainderAfterKeyBufferLength
.IsEmpty()),
264 NS_ERROR_FILE_CORRUPTED
, IDB_REPORT_INTERNAL_ERR_LAMBDA
);
266 QM_TRY(OkIf(keyBufferLength
<= uint64_t(UINT32_MAX
)),
267 NS_ERROR_FILE_CORRUPTED
, IDB_REPORT_INTERNAL_ERR_LAMBDA
);
269 QM_TRY(OkIf(keyBufferLength
<= remainderAfterKeyBufferLength
.Length()),
270 NS_ERROR_FILE_CORRUPTED
, IDB_REPORT_INTERNAL_ERR_LAMBDA
);
272 const auto [keyBuffer
, remainderAfterKeyBuffer
] =
273 remainderAfterKeyBufferLength
.SplitAt(keyBufferLength
);
275 IndexDataValue
{indexId
, unique
, Key
{nsCString
{AsChars(keyBuffer
)}}};
277 // Read sort key buffer length.
279 (const auto& [sortKeyBufferLength
, remainderAfterSortKeyBufferLength
]),
280 ReadCompressedNumber(remainderAfterKeyBuffer
));
282 remainder
= remainderAfterSortKeyBufferLength
;
283 if (sortKeyBufferLength
> 0) {
284 QM_TRY(OkIf(!remainder
.IsEmpty()), NS_ERROR_FILE_CORRUPTED
,
285 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
287 QM_TRY(OkIf(sortKeyBufferLength
<= uint64_t(UINT32_MAX
)),
288 NS_ERROR_FILE_CORRUPTED
, IDB_REPORT_INTERNAL_ERR_LAMBDA
);
290 QM_TRY(OkIf(sortKeyBufferLength
<= remainder
.Length()),
291 NS_ERROR_FILE_CORRUPTED
, IDB_REPORT_INTERNAL_ERR_LAMBDA
);
293 const auto [sortKeyBuffer
, remainderAfterSortKeyBuffer
] =
294 remainder
.SplitAt(sortKeyBufferLength
);
295 idv
.mLocaleAwarePosition
= Key
{nsCString
{AsChars(sortKeyBuffer
)}};
296 remainder
= remainderAfterSortKeyBuffer
;
299 QM_TRY(OkIf(aOutIndexValues
->AppendElement(std::move(idv
), fallible
)),
300 NS_ERROR_OUT_OF_MEMORY
, IDB_REPORT_INTERNAL_ERR_LAMBDA
);
302 aOutIndexValues
->Sort();
307 // aOutIndexValues is an output parameter, since its storage is reused.
308 template <typename T
>
309 nsresult
ReadCompressedIndexDataValuesFromSource(
310 T
& aSource
, uint32_t aColumnIndex
,
311 nsTArray
<IndexDataValue
>* aOutIndexValues
) {
312 MOZ_ASSERT(!NS_IsMainThread());
313 MOZ_ASSERT(!IsOnBackgroundThread());
314 MOZ_ASSERT(aOutIndexValues
);
315 MOZ_ASSERT(aOutIndexValues
->IsEmpty());
318 const int32_t& columnType
,
319 MOZ_TO_RESULT_INVOKE_MEMBER(aSource
, GetTypeOfIndex
, aColumnIndex
));
321 switch (columnType
) {
322 case mozIStorageStatement::VALUE_TYPE_NULL
:
325 case mozIStorageStatement::VALUE_TYPE_BLOB
: {
326 // XXX ToResultInvoke does not support multiple output parameters yet, so
327 // we also can't use QM_TRY_UNWRAP/QM_TRY_INSPECT here.
328 const uint8_t* blobData
;
329 uint32_t blobDataLength
;
330 QM_TRY(MOZ_TO_RESULT(
331 aSource
.GetSharedBlob(aColumnIndex
, &blobDataLength
, &blobData
)));
333 QM_TRY(OkIf(blobDataLength
), NS_ERROR_FILE_CORRUPTED
,
334 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
336 QM_TRY(MOZ_TO_RESULT(ReadCompressedIndexDataValuesFromBlob(
337 Span(blobData
, blobDataLength
), aOutIndexValues
)));
343 return NS_ERROR_FILE_CORRUPTED
;
347 Result
<StructuredCloneReadInfoParent
, nsresult
>
348 GetStructuredCloneReadInfoFromBlob(const uint8_t* aBlobData
,
349 uint32_t aBlobDataLength
,
350 const DatabaseFileManager
& aFileManager
,
351 const nsAString
& aFileIds
,
352 const Maybe
<CipherKey
>& aMaybeKey
) {
353 MOZ_ASSERT(!IsOnBackgroundThread());
355 AUTO_PROFILER_LABEL("GetStructuredCloneReadInfoFromBlob", DOM
);
357 const char* const compressed
= reinterpret_cast<const char*>(aBlobData
);
358 const size_t compressedLength
= size_t(aBlobDataLength
);
360 size_t uncompressedLength
;
361 QM_TRY(OkIf(snappy::GetUncompressedLength(compressed
, compressedLength
,
362 &uncompressedLength
)),
363 Err(NS_ERROR_FILE_CORRUPTED
));
365 AutoTArray
<uint8_t, 512> uncompressed
;
366 QM_TRY(OkIf(uncompressed
.SetLength(uncompressedLength
, fallible
)),
367 Err(NS_ERROR_OUT_OF_MEMORY
));
369 char* const uncompressedBuffer
=
370 reinterpret_cast<char*>(uncompressed
.Elements());
372 QM_TRY(OkIf(snappy::RawUncompress(compressed
, compressedLength
,
373 uncompressedBuffer
)),
374 Err(NS_ERROR_FILE_CORRUPTED
));
376 JSStructuredCloneData
data(JS::StructuredCloneScope::DifferentProcess
);
377 QM_TRY(OkIf(data
.AppendBytes(uncompressedBuffer
, uncompressed
.Length())),
378 Err(NS_ERROR_OUT_OF_MEMORY
));
380 nsTArray
<StructuredCloneFileParent
> files
;
381 if (!aFileIds
.IsVoid()) {
383 DeserializeStructuredCloneFiles(aFileManager
, aFileIds
));
386 return StructuredCloneReadInfoParent
{std::move(data
), std::move(files
),
390 Result
<StructuredCloneReadInfoParent
, nsresult
>
391 GetStructuredCloneReadInfoFromExternalBlob(
392 uint64_t aIntData
, const DatabaseFileManager
& aFileManager
,
393 const nsAString
& aFileIds
, const Maybe
<CipherKey
>& aMaybeKey
) {
394 MOZ_ASSERT(!IsOnBackgroundThread());
396 AUTO_PROFILER_LABEL("GetStructuredCloneReadInfoFromExternalBlob", DOM
);
398 nsTArray
<StructuredCloneFileParent
> files
;
399 if (!aFileIds
.IsVoid()) {
401 DeserializeStructuredCloneFiles(aFileManager
, aFileIds
));
404 // Higher and lower 32 bits described
405 // in ObjectStoreAddOrPutRequestOp::DoDatabaseWork.
406 const uint32_t index
= uint32_t(aIntData
& UINT32_MAX
);
408 QM_TRY(OkIf(index
< files
.Length()), Err(NS_ERROR_UNEXPECTED
),
409 [](const auto&) { MOZ_ASSERT(false, "Bad index value!"); });
411 if (IndexedDatabaseManager::PreprocessingEnabled()) {
412 return StructuredCloneReadInfoParent
{
413 JSStructuredCloneData
{JS::StructuredCloneScope::DifferentProcess
},
414 std::move(files
), true};
417 // XXX Why can there be multiple files, but we use only a single one here?
418 const StructuredCloneFileParent
& file
= files
[index
];
419 MOZ_ASSERT(file
.Type() == StructuredCloneFileBase::eStructuredClone
);
421 auto data
= JSStructuredCloneData
{JS::StructuredCloneScope::DifferentProcess
};
424 const nsCOMPtr
<nsIFile
> nativeFile
= file
.FileInfo().GetFileForFileInfo();
425 QM_TRY(OkIf(nativeFile
), Err(NS_ERROR_FAILURE
));
428 const auto& fileInputStream
,
429 NS_NewLocalFileInputStream(nativeFile
)
430 .andThen([aMaybeKey
](auto fileInputStream
)
431 -> Result
<nsCOMPtr
<nsIInputStream
>, nsresult
> {
433 return nsCOMPtr
<nsIInputStream
>{MakeRefPtr
<
434 quota::DecryptingInputStream
<IndexedDBCipherStrategy
>>(
435 WrapNotNull(std::move(fileInputStream
)),
436 kEncryptedStreamBlockSize
, *aMaybeKey
)};
439 return fileInputStream
;
442 QM_TRY(MOZ_TO_RESULT(
443 SnappyUncompressStructuredCloneData(*fileInputStream
, data
)));
446 return StructuredCloneReadInfoParent
{std::move(data
), std::move(files
),
450 template <typename T
>
451 Result
<StructuredCloneReadInfoParent
, nsresult
>
452 GetStructuredCloneReadInfoFromSource(T
* aSource
, uint32_t aDataIndex
,
453 uint32_t aFileIdsIndex
,
454 const DatabaseFileManager
& aFileManager
,
455 const Maybe
<CipherKey
>& aMaybeKey
) {
456 MOZ_ASSERT(!IsOnBackgroundThread());
460 const int32_t& columnType
,
461 MOZ_TO_RESULT_INVOKE_MEMBER(aSource
, GetTypeOfIndex
, aDataIndex
));
463 QM_TRY_INSPECT(const bool& isNull
, MOZ_TO_RESULT_INVOKE_MEMBER(
464 aSource
, GetIsNull
, aFileIdsIndex
));
466 QM_TRY_INSPECT(const nsString
& fileIds
, ([aSource
, aFileIdsIndex
, isNull
] {
467 return isNull
? Result
<nsString
, nsresult
>{VoidString()}
468 : MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
469 nsString
, aSource
, GetString
,
473 switch (columnType
) {
474 case mozIStorageStatement::VALUE_TYPE_INTEGER
: {
476 const int64_t& intData
,
477 MOZ_TO_RESULT_INVOKE_MEMBER(aSource
, GetInt64
, aDataIndex
));
480 memcpy(&uintData
, &intData
, sizeof(uint64_t));
482 return GetStructuredCloneReadInfoFromExternalBlob(uintData
, aFileManager
,
486 case mozIStorageStatement::VALUE_TYPE_BLOB
: {
487 const uint8_t* blobData
;
488 uint32_t blobDataLength
;
489 QM_TRY(MOZ_TO_RESULT(
490 aSource
->GetSharedBlob(aDataIndex
, &blobDataLength
, &blobData
)));
492 return GetStructuredCloneReadInfoFromBlob(
493 blobData
, blobDataLength
, aFileManager
, fileIds
, aMaybeKey
);
497 return Err(NS_ERROR_FILE_CORRUPTED
);
503 IndexDataValue::IndexDataValue() : mIndexId(0), mUnique(false) {
504 MOZ_COUNT_CTOR(IndexDataValue
);
507 #ifdef NS_BUILD_REFCNT_LOGGING
508 IndexDataValue::IndexDataValue(IndexDataValue
&& aOther
)
509 : mIndexId(aOther
.mIndexId
),
510 mPosition(std::move(aOther
.mPosition
)),
511 mLocaleAwarePosition(std::move(aOther
.mLocaleAwarePosition
)),
512 mUnique(aOther
.mUnique
) {
513 MOZ_ASSERT(!aOther
.mPosition
.IsUnset());
515 MOZ_COUNT_CTOR(IndexDataValue
);
519 IndexDataValue::IndexDataValue(IndexOrObjectStoreId aIndexId
, bool aUnique
,
520 const Key
& aPosition
)
521 : mIndexId(aIndexId
), mPosition(aPosition
), mUnique(aUnique
) {
522 MOZ_ASSERT(!aPosition
.IsUnset());
524 MOZ_COUNT_CTOR(IndexDataValue
);
527 IndexDataValue::IndexDataValue(IndexOrObjectStoreId aIndexId
, bool aUnique
,
528 const Key
& aPosition
,
529 const Key
& aLocaleAwarePosition
)
530 : mIndexId(aIndexId
),
531 mPosition(aPosition
),
532 mLocaleAwarePosition(aLocaleAwarePosition
),
534 MOZ_ASSERT(!aPosition
.IsUnset());
536 MOZ_COUNT_CTOR(IndexDataValue
);
539 bool IndexDataValue::operator==(const IndexDataValue
& aOther
) const {
540 if (mIndexId
!= aOther
.mIndexId
) {
543 if (mLocaleAwarePosition
.IsUnset()) {
544 return mPosition
== aOther
.mPosition
;
546 return mLocaleAwarePosition
== aOther
.mLocaleAwarePosition
;
549 bool IndexDataValue::operator<(const IndexDataValue
& aOther
) const {
550 if (mIndexId
== aOther
.mIndexId
) {
551 if (mLocaleAwarePosition
.IsUnset()) {
552 return mPosition
< aOther
.mPosition
;
554 return mLocaleAwarePosition
< aOther
.mLocaleAwarePosition
;
557 return mIndexId
< aOther
.mIndexId
;
560 JSObject
* GetSandbox(JSContext
* aCx
) {
561 SandboxHolder
* holder
= SandboxHolder::GetOrCreate();
562 return holder
->GetSandboxInternal(aCx
);
565 Result
<std::pair
<UniqueFreePtr
<uint8_t>, uint32_t>, nsresult
>
566 MakeCompressedIndexDataValues(const nsTArray
<IndexDataValue
>& aIndexValues
) {
567 MOZ_ASSERT(!NS_IsMainThread());
568 MOZ_ASSERT(!IsOnBackgroundThread());
570 AUTO_PROFILER_LABEL("MakeCompressedIndexDataValues", DOM
);
572 const uint32_t arrayLength
= aIndexValues
.Length();
574 return std::pair
{UniqueFreePtr
<uint8_t>{}, 0u};
577 // First calculate the size of the final buffer.
578 const auto blobDataLength
= std::accumulate(
579 aIndexValues
.cbegin(), aIndexValues
.cend(), CheckedUint32(0),
580 [](CheckedUint32 sum
, const IndexDataValue
& info
) {
581 const nsCString
& keyBuffer
= info
.mPosition
.GetBuffer();
582 const nsCString
& sortKeyBuffer
= info
.mLocaleAwarePosition
.GetBuffer();
583 const uint32_t keyBufferLength
= keyBuffer
.Length();
584 const uint32_t sortKeyBufferLength
= sortKeyBuffer
.Length();
586 MOZ_ASSERT(!keyBuffer
.IsEmpty());
588 return sum
+ CompressedByteCountForIndexId(info
.mIndexId
) +
589 CompressedByteCountForNumber(keyBufferLength
) +
590 CompressedByteCountForNumber(sortKeyBufferLength
) +
591 keyBufferLength
+ sortKeyBufferLength
;
594 QM_TRY(OkIf(blobDataLength
.isValid()),
595 Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR
),
596 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
598 UniqueFreePtr
<uint8_t> blobData(
599 static_cast<uint8_t*>(malloc(blobDataLength
.value())));
600 QM_TRY(OkIf(static_cast<bool>(blobData
)), Err(NS_ERROR_OUT_OF_MEMORY
),
601 IDB_REPORT_INTERNAL_ERR_LAMBDA
);
603 uint8_t* blobDataIter
= blobData
.get();
605 for (const IndexDataValue
& info
: aIndexValues
) {
606 const nsCString
& keyBuffer
= info
.mPosition
.GetBuffer();
607 const nsCString
& sortKeyBuffer
= info
.mLocaleAwarePosition
.GetBuffer();
608 const uint32_t keyBufferLength
= keyBuffer
.Length();
609 const uint32_t sortKeyBufferLength
= sortKeyBuffer
.Length();
611 WriteCompressedIndexId(info
.mIndexId
, info
.mUnique
, &blobDataIter
);
612 WriteCompressedNumber(keyBufferLength
, &blobDataIter
);
614 memcpy(blobDataIter
, keyBuffer
.get(), keyBufferLength
);
615 blobDataIter
+= keyBufferLength
;
617 WriteCompressedNumber(sortKeyBufferLength
, &blobDataIter
);
619 memcpy(blobDataIter
, sortKeyBuffer
.get(), sortKeyBufferLength
);
620 blobDataIter
+= sortKeyBufferLength
;
623 MOZ_ASSERT(blobDataIter
== blobData
.get() + blobDataLength
.value());
625 return std::pair
{std::move(blobData
), blobDataLength
.value()};
628 nsresult
ReadCompressedIndexDataValues(
629 mozIStorageStatement
& aStatement
, uint32_t aColumnIndex
,
630 nsTArray
<IndexDataValue
>& aOutIndexValues
) {
631 return ReadCompressedIndexDataValuesFromSource(aStatement
, aColumnIndex
,
635 template <typename T
>
636 Result
<IndexDataValuesAutoArray
, nsresult
> ReadCompressedIndexDataValues(
637 T
& aValues
, uint32_t aColumnIndex
) {
638 return MOZ_TO_RESULT_INVOKE_TYPED(IndexDataValuesAutoArray
,
639 &ReadCompressedIndexDataValuesFromSource
<T
>,
640 aValues
, aColumnIndex
);
643 template Result
<IndexDataValuesAutoArray
, nsresult
>
644 ReadCompressedIndexDataValues
<mozIStorageValueArray
>(mozIStorageValueArray
&,
647 template Result
<IndexDataValuesAutoArray
, nsresult
>
648 ReadCompressedIndexDataValues
<mozIStorageStatement
>(mozIStorageStatement
&,
651 Result
<std::tuple
<IndexOrObjectStoreId
, bool, Span
<const uint8_t>>, nsresult
>
652 ReadCompressedIndexId(const Span
<const uint8_t> aData
) {
653 QM_TRY_INSPECT((const auto& [indexId
, remainder
]),
654 ReadCompressedNumber(aData
));
656 MOZ_ASSERT(UINT64_MAX
/ 2 >= uint64_t(indexId
), "Bad index id!");
658 return std::tuple
{IndexOrObjectStoreId(indexId
>> 1), indexId
% 2 == 1,
662 Result
<std::pair
<uint64_t, mozilla::Span
<const uint8_t>>, nsresult
>
663 ReadCompressedNumber(const Span
<const uint8_t> aSpan
) {
664 uint8_t shiftCounter
= 0;
667 const auto end
= aSpan
.cend();
670 std::find_if(aSpan
.cbegin(), end
, [&result
, &shiftCounter
](uint8_t byte
) {
671 MOZ_ASSERT(shiftCounter
<= 56, "Shifted too many bits!");
673 result
+= (uint64_t(byte
& 0x7f) << shiftCounter
);
676 return !(byte
& 0x80);
679 QM_TRY(OkIf(newPos
!= end
), Err(NS_ERROR_FILE_CORRUPTED
), [](const auto&) {
681 IDB_REPORT_INTERNAL_ERR();
684 return std::pair
{result
, Span
{newPos
+ 1, end
}};
687 Result
<StructuredCloneReadInfoParent
, nsresult
>
688 GetStructuredCloneReadInfoFromValueArray(
689 mozIStorageValueArray
* aValues
, uint32_t aDataIndex
, uint32_t aFileIdsIndex
,
690 const DatabaseFileManager
& aFileManager
,
691 const Maybe
<CipherKey
>& aMaybeKey
) {
692 return GetStructuredCloneReadInfoFromSource(
693 aValues
, aDataIndex
, aFileIdsIndex
, aFileManager
, aMaybeKey
);
696 Result
<StructuredCloneReadInfoParent
, nsresult
>
697 GetStructuredCloneReadInfoFromStatement(mozIStorageStatement
* aStatement
,
699 uint32_t aFileIdsIndex
,
700 const DatabaseFileManager
& aFileManager
,
701 const Maybe
<CipherKey
>& aMaybeKey
) {
702 return GetStructuredCloneReadInfoFromSource(
703 aStatement
, aDataIndex
, aFileIdsIndex
, aFileManager
, aMaybeKey
);
706 Result
<nsTArray
<StructuredCloneFileParent
>, nsresult
>
707 DeserializeStructuredCloneFiles(const DatabaseFileManager
& aFileManager
,
708 const nsAString
& aText
) {
709 MOZ_ASSERT(!IsOnBackgroundThread());
711 nsTArray
<StructuredCloneFileParent
> result
;
712 for (const auto& token
:
713 nsCharSeparatedTokenizerTemplate
<NS_TokenizerIgnoreNothing
>(aText
, ' ')
715 MOZ_ASSERT(!token
.IsEmpty());
717 QM_TRY_UNWRAP(auto structuredCloneFile
,
718 DeserializeStructuredCloneFile(aFileManager
, token
));
720 result
.EmplaceBack(std::move(structuredCloneFile
));
726 nsresult
ExecuteSimpleSQLSequence(mozIStorageConnection
& aConnection
,
727 Span
<const nsLiteralCString
> aSQLCommands
) {
728 for (const auto& aSQLCommand
: aSQLCommands
) {
729 const auto extraInfo
= quota::ScopedLogExtraInfo
{
730 quota::ScopedLogExtraInfo::kTagQuery
, aSQLCommand
};
732 QM_TRY(MOZ_TO_RESULT(aConnection
.ExecuteSimpleSQL(aSQLCommand
)));
738 } // namespace mozilla::dom::indexedDB