Bug 1472338: part 2) Change `clipboard.readText()` to read from the clipboard asynchr...
[gecko.git] / dom / indexedDB / ActorsParentCommon.cpp
blobb421754e4923f1491b319ef7dbe40142240ef572
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 "IndexedDBCommon.h"
16 #include "ReportInternalError.h"
18 // global includes
19 #include <stdlib.h>
20 #include <string.h>
21 #include <algorithm>
22 #include <numeric>
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"
49 #include "nsCOMPtr.h"
50 #include "nsCharSeparatedTokenizer.h"
51 #include "nsContentUtils.h"
52 #include "nsDebug.h"
53 #include "nsError.h"
54 #include "nsIInputStream.h"
55 #include "nsIPrincipal.h"
56 #include "nsIXPConnect.h"
57 #include "nsNetUtil.h"
58 #include "nsString.h"
59 #include "nsStringFlags.h"
60 #include "nsXULAppAPI.h"
61 #include "snappy/snappy.h"
63 class nsIFile;
65 namespace mozilla::dom::indexedDB {
67 static_assert(SNAPPY_VERSION == 0x010108);
69 using mozilla::ipc::IsOnBackgroundThread;
71 const nsLiteralString kJournalDirectoryName = u"journals"_ns;
73 namespace {
75 constexpr StructuredCloneFileBase::FileType ToStructuredCloneFileType(
76 const char16_t aTag) {
77 switch (aTag) {
78 case char16_t('-'):
79 return StructuredCloneFileBase::eMutableFile;
81 case char16_t('.'):
82 return StructuredCloneFileBase::eStructuredClone;
84 case char16_t('/'):
85 return StructuredCloneFileBase::eWasmBytecode;
87 case char16_t('\\'):
88 return StructuredCloneFileBase::eWasmCompiled;
90 default:
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(
109 int32_t, ToInteger,
110 type == StructuredCloneFileBase::eBlob
111 ? aText
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.
119 if (!fileInfo) {
120 IDB_WARNING(
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 {
133 public:
134 NS_INLINE_DECL_REFCOUNTING(SandboxHolder)
136 private:
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;
146 if (!sHolder) {
147 sHolder = new SandboxHolder();
148 ClearOnShutdown(&sHolder);
150 return sHolder;
153 JSObject* GetSandboxInternal(JSContext* aCx) {
154 if (!mSandbox) {
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);
163 QM_TRY(
164 MOZ_TO_RESULT(xpc->CreateSandbox(aCx, principal, sandbox.address())),
165 nullptr);
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.
178 uint32_t count = 1;
179 while ((aNumber >>= 7)) {
180 count++;
183 return count;
186 uint32_t CompressedByteCountForIndexId(IndexOrObjectStoreId aIndexId) {
187 MOZ_ASSERT(aIndexId);
188 MOZ_ASSERT(UINT64_MAX - uint64_t(aIndexId) >= uint64_t(aIndexId),
189 "Overflow!");
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;
200 #ifdef DEBUG
201 const uint8_t* const bufferStart = buffer;
202 const uint64_t originalNumber = aNumber;
203 #endif
205 while (true) {
206 uint64_t shiftedNumber = aNumber >> 7;
207 if (shiftedNumber) {
208 *buffer++ = uint8_t(0x80 | (aNumber & 0x7f));
209 aNumber = shiftedNumber;
210 } else {
211 *buffer++ = uint8_t(aNumber);
212 break;
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),
225 "Overflow!");
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
246 // to the caller?
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.
259 QM_TRY_INSPECT(
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);
274 auto idv =
275 IndexDataValue{indexId, unique, Key{nsCString{AsChars(keyBuffer)}}};
277 // Read sort key buffer length.
278 QM_TRY_INSPECT(
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();
304 return NS_OK;
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());
317 QM_TRY_INSPECT(
318 const int32_t& columnType,
319 MOZ_TO_RESULT_INVOKE_MEMBER(aSource, GetTypeOfIndex, aColumnIndex));
321 switch (columnType) {
322 case mozIStorageStatement::VALUE_TYPE_NULL:
323 return NS_OK;
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)));
339 return NS_OK;
342 default:
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()) {
382 QM_TRY_UNWRAP(files,
383 DeserializeStructuredCloneFiles(aFileManager, aFileIds));
386 return StructuredCloneReadInfoParent{std::move(data), std::move(files),
387 false};
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()) {
400 QM_TRY_UNWRAP(files,
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));
427 QM_TRY_INSPECT(
428 const auto& fileInputStream,
429 NS_NewLocalFileInputStream(nativeFile)
430 .andThen([aMaybeKey](auto fileInputStream)
431 -> Result<nsCOMPtr<nsIInputStream>, nsresult> {
432 if (aMaybeKey) {
433 return nsCOMPtr<nsIInputStream>{MakeRefPtr<
434 quota::DecryptingInputStream<IndexedDBCipherStrategy>>(
435 WrapNotNull(std::move(fileInputStream)),
436 kEncryptedStreamBlockSize, *aMaybeKey)};
439 return fileInputStream;
440 }));
442 QM_TRY(MOZ_TO_RESULT(
443 SnappyUncompressStructuredCloneData(*fileInputStream, data)));
446 return StructuredCloneReadInfoParent{std::move(data), std::move(files),
447 false};
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());
457 MOZ_ASSERT(aSource);
459 QM_TRY_INSPECT(
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,
470 aFileIdsIndex);
471 }()));
473 switch (columnType) {
474 case mozIStorageStatement::VALUE_TYPE_INTEGER: {
475 QM_TRY_INSPECT(
476 const int64_t& intData,
477 MOZ_TO_RESULT_INVOKE_MEMBER(aSource, GetInt64, aDataIndex));
479 uint64_t uintData;
480 memcpy(&uintData, &intData, sizeof(uint64_t));
482 return GetStructuredCloneReadInfoFromExternalBlob(uintData, aFileManager,
483 fileIds, aMaybeKey);
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);
496 default:
497 return Err(NS_ERROR_FILE_CORRUPTED);
501 } // namespace
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);
517 #endif
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),
533 mUnique(aUnique) {
534 MOZ_ASSERT(!aPosition.IsUnset());
536 MOZ_COUNT_CTOR(IndexDataValue);
539 bool IndexDataValue::operator==(const IndexDataValue& aOther) const {
540 if (mIndexId != aOther.mIndexId) {
541 return false;
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();
573 if (!arrayLength) {
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,
632 &aOutIndexValues);
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&,
645 uint32_t);
647 template Result<IndexDataValuesAutoArray, nsresult>
648 ReadCompressedIndexDataValues<mozIStorageStatement>(mozIStorageStatement&,
649 uint32_t);
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,
659 remainder};
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;
665 uint64_t result = 0;
667 const auto end = aSpan.cend();
669 const auto newPos =
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);
674 shiftCounter += 7;
676 return !(byte & 0x80);
679 QM_TRY(OkIf(newPos != end), Err(NS_ERROR_FILE_CORRUPTED), [](const auto&) {
680 MOZ_ASSERT(false);
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,
698 uint32_t aDataIndex,
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, ' ')
714 .ToRange()) {
715 MOZ_ASSERT(!token.IsEmpty());
717 QM_TRY_UNWRAP(auto structuredCloneFile,
718 DeserializeStructuredCloneFile(aFileManager, token));
720 result.EmplaceBack(std::move(structuredCloneFile));
723 return result;
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)));
735 return NS_OK;
738 } // namespace mozilla::dom::indexedDB