Bug 1941128 - Turn off network.dns.native_https_query on Mac again
[gecko.git] / dom / indexedDB / ActorsParentCommon.cpp
blob5f25bd8ad68ae347dc5daa626239152d2ae31801
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"
9 // local includes
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"
19 // global includes
20 #include <stdlib.h>
21 #include <string.h>
22 #include <algorithm>
23 #include <numeric>
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"
51 #include "nsCOMPtr.h"
52 #include "nsCharSeparatedTokenizer.h"
53 #include "nsContentUtils.h"
54 #include "nsDebug.h"
55 #include "nsError.h"
56 #include "nsIInputStream.h"
57 #include "nsIPrincipal.h"
58 #include "nsIXPConnect.h"
59 #include "nsNetUtil.h"
60 #include "nsString.h"
61 #include "nsStringFlags.h"
62 #include "nsXULAppAPI.h"
63 #include "snappy/snappy.h"
65 class nsIFile;
67 namespace mozilla::dom::indexedDB {
69 static_assert(SNAPPY_VERSION == 0x010200);
71 using mozilla::ipc::IsOnBackgroundThread;
73 const nsLiteralString kJournalDirectoryName = u"journals"_ns;
75 namespace {
77 constexpr StructuredCloneFileBase::FileType ToStructuredCloneFileType(
78 const char16_t aTag) {
79 switch (aTag) {
80 case char16_t('-'):
81 return StructuredCloneFileBase::eMutableFile;
83 case char16_t('.'):
84 return StructuredCloneFileBase::eStructuredClone;
86 case char16_t('/'):
87 return StructuredCloneFileBase::eWasmBytecode;
89 case char16_t('\\'):
90 return StructuredCloneFileBase::eWasmCompiled;
92 default:
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(
111 int32_t, ToInteger,
112 type == StructuredCloneFileBase::eBlob
113 ? aText
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 {
128 public:
129 NS_INLINE_DECL_REFCOUNTING(SandboxHolder)
131 private:
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;
141 if (!sHolder) {
142 sHolder = new SandboxHolder();
143 ClearOnShutdown(&sHolder);
145 return sHolder;
148 JSObject* GetSandboxInternal(JSContext* aCx) {
149 if (!mSandbox) {
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);
158 QM_TRY(
159 MOZ_TO_RESULT(xpc->CreateSandbox(aCx, principal, sandbox.address())),
160 nullptr);
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.
173 uint32_t count = 1;
174 while ((aNumber >>= 7)) {
175 count++;
178 return count;
181 uint32_t CompressedByteCountForIndexId(IndexOrObjectStoreId aIndexId) {
182 MOZ_ASSERT(aIndexId);
183 MOZ_ASSERT(UINT64_MAX - uint64_t(aIndexId) >= uint64_t(aIndexId),
184 "Overflow!");
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;
195 #ifdef DEBUG
196 const uint8_t* const bufferStart = buffer;
197 const uint64_t originalNumber = aNumber;
198 #endif
200 while (true) {
201 uint64_t shiftedNumber = aNumber >> 7;
202 if (shiftedNumber) {
203 *buffer++ = uint8_t(0x80 | (aNumber & 0x7f));
204 aNumber = shiftedNumber;
205 } else {
206 *buffer++ = uint8_t(aNumber);
207 break;
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),
220 "Overflow!");
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
241 // to the caller?
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.
254 QM_TRY_INSPECT(
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);
269 auto idv =
270 IndexDataValue{indexId, unique, Key{nsCString{AsChars(keyBuffer)}}};
272 // Read sort key buffer length.
273 QM_TRY_INSPECT(
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();
299 return NS_OK;
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());
312 QM_TRY_INSPECT(
313 const int32_t& columnType,
314 MOZ_TO_RESULT_INVOKE_MEMBER(aSource, GetTypeOfIndex, aColumnIndex));
316 switch (columnType) {
317 case mozIStorageStatement::VALUE_TYPE_NULL:
318 return NS_OK;
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)));
334 return NS_OK;
337 default:
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()) {
378 QM_TRY_UNWRAP(files,
379 DeserializeStructuredCloneFiles(aFileManager, aFileIds));
382 return StructuredCloneReadInfoParent{std::move(data), std::move(files),
383 false};
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()) {
396 QM_TRY_UNWRAP(files,
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()) {
420 nsCString fileKeyId;
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));
432 QM_TRY_INSPECT(
433 const auto& fileInputStream,
434 NS_NewLocalFileInputStream(nativeFile)
435 .andThen([maybeKey](auto fileInputStream)
436 -> Result<nsCOMPtr<nsIInputStream>, nsresult> {
437 if (maybeKey) {
438 return nsCOMPtr<nsIInputStream>{MakeRefPtr<
439 quota::DecryptingInputStream<IndexedDBCipherStrategy>>(
440 WrapNotNull(std::move(fileInputStream)),
441 kEncryptedStreamBlockSize, *maybeKey)};
444 return fileInputStream;
445 }));
447 QM_TRY(MOZ_TO_RESULT(
448 SnappyUncompressStructuredCloneData(*fileInputStream, data)));
451 return StructuredCloneReadInfoParent{std::move(data), std::move(files),
452 false};
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());
461 MOZ_ASSERT(aSource);
463 QM_TRY_INSPECT(
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,
474 aFileIdsIndex);
475 }()));
477 switch (columnType) {
478 case mozIStorageStatement::VALUE_TYPE_INTEGER: {
479 QM_TRY_INSPECT(
480 const int64_t& intData,
481 MOZ_TO_RESULT_INVOKE_MEMBER(aSource, GetInt64, aDataIndex));
483 uint64_t uintData;
484 memcpy(&uintData, &intData, sizeof(uint64_t));
486 return GetStructuredCloneReadInfoFromExternalBlob(uintData, aFileManager,
487 fileIds);
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);
500 default:
501 return Err(NS_ERROR_FILE_CORRUPTED);
505 } // namespace
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);
521 #endif
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),
537 mUnique(aUnique) {
538 MOZ_ASSERT(!aPosition.IsUnset());
540 MOZ_COUNT_CTOR(IndexDataValue);
543 bool IndexDataValue::operator==(const IndexDataValue& aOther) const {
544 if (mIndexId != aOther.mIndexId) {
545 return false;
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();
577 if (!arrayLength) {
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,
636 &aOutIndexValues);
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&,
649 uint32_t);
651 template Result<IndexDataValuesAutoArray, nsresult>
652 ReadCompressedIndexDataValues<mozIStorageStatement>(mozIStorageStatement&,
653 uint32_t);
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,
663 remainder};
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;
669 uint64_t result = 0;
671 const auto end = aSpan.cend();
673 const auto newPos =
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);
678 shiftCounter += 7;
680 return !(byte & 0x80);
683 QM_TRY(OkIf(newPos != end), Err(NS_ERROR_FILE_CORRUPTED), [](const auto&) {
684 MOZ_ASSERT(false);
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, ' ')
715 .ToRange()) {
716 MOZ_ASSERT(!token.IsEmpty());
718 QM_TRY_UNWRAP(auto structuredCloneFile,
719 DeserializeStructuredCloneFile(aFileManager, token));
721 result.EmplaceBack(std::move(structuredCloneFile));
724 return result;
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)));
736 return NS_OK;
739 } // namespace mozilla::dom::indexedDB