ActorsParentCommon.cpp (25097B)
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/. */ 6 7 #include "ActorsParentCommon.h" 8 9 // local includes 10 #include "DatabaseFileInfo.h" 11 #include "DatabaseFileManager.h" 12 #include "IndexedDBCipherKeyManager.h" 13 #include "IndexedDBCommon.h" 14 #include "IndexedDatabase.h" // for StructuredCloneFile... 15 #include "IndexedDatabaseInlines.h" 16 #include "IndexedDatabaseManager.h" 17 #include "ReportInternalError.h" 18 19 // global includes 20 #include <stdlib.h> 21 #include <string.h> 22 23 #include <algorithm> 24 #include <numeric> 25 26 #include "MainThreadUtils.h" 27 #include "SafeRefPtr.h" 28 #include "js/RootingAPI.h" 29 #include "js/StructuredClone.h" 30 #include "mozIStorageConnection.h" 31 #include "mozIStorageStatement.h" 32 #include "mozIStorageValueArray.h" 33 #include "mozilla/Assertions.h" 34 #include "mozilla/CheckedInt.h" 35 #include "mozilla/ClearOnShutdown.h" 36 #include "mozilla/JSObjectHolder.h" 37 #include "mozilla/NullPrincipal.h" 38 #include "mozilla/ProfilerLabels.h" 39 #include "mozilla/RefPtr.h" 40 #include "mozilla/ResultExtensions.h" 41 #include "mozilla/StaticPrefs_dom.h" 42 #include "mozilla/StaticPtr.h" 43 #include "mozilla/dom/quota/DecryptingInputStream_impl.h" 44 #include "mozilla/dom/quota/QuotaCommon.h" 45 #include "mozilla/dom/quota/ResultExtensions.h" 46 #include "mozilla/dom/quota/ScopedLogExtraInfo.h" 47 #include "mozilla/fallible.h" 48 #include "mozilla/ipc/BackgroundParent.h" 49 #include "mozilla/mozalloc.h" 50 #include "nsCOMPtr.h" 51 #include "nsCharSeparatedTokenizer.h" 52 #include "nsContentUtils.h" 53 #include "nsDebug.h" 54 #include "nsError.h" 55 #include "nsIInputStream.h" 56 #include "nsIPrincipal.h" 57 #include "nsIXPConnect.h" 58 #include "nsNetUtil.h" 59 #include "nsString.h" 60 #include "nsStringFlags.h" 61 #include "nsXULAppAPI.h" 62 #include "snappy/snappy.h" 63 64 class nsIFile; 65 66 namespace mozilla::dom::indexedDB { 67 68 static_assert(SNAPPY_VERSION == 0x010202); 69 70 using mozilla::ipc::IsOnBackgroundThread; 71 72 const nsLiteralString kJournalDirectoryName = u"journals"_ns; 73 74 namespace { 75 76 constexpr StructuredCloneFileBase::FileType ToStructuredCloneFileType( 77 const char16_t aTag) { 78 switch (aTag) { 79 case char16_t('-'): 80 return StructuredCloneFileBase::eMutableFile; 81 82 case char16_t('.'): 83 return StructuredCloneFileBase::eStructuredClone; 84 85 case char16_t('/'): 86 return StructuredCloneFileBase::eWasmBytecode; 87 88 case char16_t('\\'): 89 return StructuredCloneFileBase::eWasmCompiled; 90 91 default: 92 return StructuredCloneFileBase::eBlob; 93 } 94 } 95 96 int32_t ToInteger(const nsAString& aStr, nsresult* const aRv) { 97 return aStr.ToInteger(aRv); 98 } 99 100 Result<StructuredCloneFileParent, nsresult> DeserializeStructuredCloneFile( 101 const DatabaseFileManager& aFileManager, 102 const nsDependentSubstring& aText) { 103 MOZ_ASSERT(!aText.IsEmpty()); 104 105 const StructuredCloneFileBase::FileType type = 106 ToStructuredCloneFileType(aText.First()); 107 108 QM_TRY_INSPECT(const auto& id, 109 MOZ_TO_RESULT_GET_TYPED( 110 int32_t, ToInteger, 111 type == StructuredCloneFileBase::eBlob 112 ? aText 113 : static_cast<const nsAString&>(Substring(aText, 1)))); 114 115 SafeRefPtr<DatabaseFileInfo> fileInfo = aFileManager.GetFileInfo(id); 116 MOZ_ASSERT(fileInfo); 117 // XXX In bug 1432133, for some reasons DatabaseFileInfo object cannot be 118 // got. This is just a short-term fix, and we are working on finding the real 119 // cause in bug 1519859. 120 QM_TRY(OkIf((bool)fileInfo), Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR)); 121 122 return StructuredCloneFileParent{type, std::move(fileInfo)}; 123 } 124 125 // This class helps to create only 1 sandbox. 126 class SandboxHolder final { 127 public: 128 NS_INLINE_DECL_REFCOUNTING(SandboxHolder) 129 130 private: 131 friend JSObject* mozilla::dom::indexedDB::GetSandbox(JSContext* aCx); 132 133 ~SandboxHolder() = default; 134 135 static SandboxHolder* GetOrCreate() { 136 MOZ_ASSERT(XRE_IsParentProcess()); 137 MOZ_ASSERT(NS_IsMainThread()); 138 139 static StaticRefPtr<SandboxHolder> sHolder; 140 if (!sHolder) { 141 sHolder = new SandboxHolder(); 142 ClearOnShutdown(&sHolder); 143 } 144 return sHolder; 145 } 146 147 JSObject* GetSandboxInternal(JSContext* aCx) { 148 if (!mSandbox) { 149 nsIXPConnect* const xpc = nsContentUtils::XPConnect(); 150 MOZ_ASSERT(xpc, "This should never be null!"); 151 152 // Let's use a null principal. 153 const nsCOMPtr<nsIPrincipal> principal = 154 NullPrincipal::CreateWithoutOriginAttributes(); 155 156 JS::Rooted<JSObject*> sandbox(aCx); 157 QM_TRY( 158 MOZ_TO_RESULT(xpc->CreateSandbox(aCx, principal, sandbox.address())), 159 nullptr); 160 161 mSandbox = new JSObjectHolder(aCx, sandbox); 162 } 163 164 return mSandbox->GetJSObject(); 165 } 166 167 RefPtr<JSObjectHolder> mSandbox; 168 }; 169 170 uint32_t CompressedByteCountForNumber(uint64_t aNumber) { 171 // All bytes have 7 bits available. 172 uint32_t count = 1; 173 while ((aNumber >>= 7)) { 174 count++; 175 } 176 177 return count; 178 } 179 180 uint32_t CompressedByteCountForIndexId(IndexOrObjectStoreId aIndexId) { 181 MOZ_ASSERT(aIndexId); 182 MOZ_ASSERT(UINT64_MAX - uint64_t(aIndexId) >= uint64_t(aIndexId), 183 "Overflow!"); 184 185 return CompressedByteCountForNumber(uint64_t(aIndexId * 2)); 186 } 187 188 void WriteCompressedNumber(uint64_t aNumber, uint8_t** aIterator) { 189 MOZ_ASSERT(aIterator); 190 MOZ_ASSERT(*aIterator); 191 192 uint8_t*& buffer = *aIterator; 193 194 #ifdef DEBUG 195 const uint8_t* const bufferStart = buffer; 196 const uint64_t originalNumber = aNumber; 197 #endif 198 199 while (true) { 200 uint64_t shiftedNumber = aNumber >> 7; 201 if (shiftedNumber) { 202 *buffer++ = uint8_t(0x80 | (aNumber & 0x7f)); 203 aNumber = shiftedNumber; 204 } else { 205 *buffer++ = uint8_t(aNumber); 206 break; 207 } 208 } 209 210 MOZ_ASSERT(buffer > bufferStart); 211 MOZ_ASSERT(uint32_t(buffer - bufferStart) == 212 CompressedByteCountForNumber(originalNumber)); 213 } 214 215 void WriteCompressedIndexId(IndexOrObjectStoreId aIndexId, bool aUnique, 216 uint8_t** aIterator) { 217 MOZ_ASSERT(aIndexId); 218 MOZ_ASSERT(UINT64_MAX - uint64_t(aIndexId) >= uint64_t(aIndexId), 219 "Overflow!"); 220 MOZ_ASSERT(aIterator); 221 MOZ_ASSERT(*aIterator); 222 223 const uint64_t indexId = (uint64_t(aIndexId * 2) | (aUnique ? 1 : 0)); 224 WriteCompressedNumber(indexId, aIterator); 225 } 226 227 // aOutIndexValues is an output parameter, since its storage is reused. 228 nsresult ReadCompressedIndexDataValuesFromBlob( 229 const Span<const uint8_t> aBlobData, 230 nsTArray<IndexDataValue>* aOutIndexValues) { 231 MOZ_ASSERT(!NS_IsMainThread()); 232 MOZ_ASSERT(!IsOnBackgroundThread()); 233 MOZ_ASSERT(!aBlobData.IsEmpty()); 234 MOZ_ASSERT(aOutIndexValues); 235 MOZ_ASSERT(aOutIndexValues->IsEmpty()); 236 237 AUTO_PROFILER_LABEL("ReadCompressedIndexDataValuesFromBlob", DOM); 238 239 // XXX Is this check still necessary with a Span? Or should it rather be moved 240 // to the caller? 241 QM_TRY(OkIf(uintptr_t(aBlobData.Elements()) <= 242 UINTPTR_MAX - aBlobData.LengthBytes()), 243 NS_ERROR_FILE_CORRUPTED, IDB_REPORT_INTERNAL_ERR_LAMBDA); 244 245 for (auto remainder = aBlobData; !remainder.IsEmpty();) { 246 QM_TRY_INSPECT((const auto& [indexId, unique, remainderAfterIndexId]), 247 ReadCompressedIndexId(remainder)); 248 249 QM_TRY(OkIf(!remainderAfterIndexId.IsEmpty()), NS_ERROR_FILE_CORRUPTED, 250 IDB_REPORT_INTERNAL_ERR_LAMBDA); 251 252 // Read key buffer length. 253 QM_TRY_INSPECT( 254 (const auto& [keyBufferLength, remainderAfterKeyBufferLength]), 255 ReadCompressedNumber(remainderAfterIndexId)); 256 257 QM_TRY(OkIf(!remainderAfterKeyBufferLength.IsEmpty()), 258 NS_ERROR_FILE_CORRUPTED, IDB_REPORT_INTERNAL_ERR_LAMBDA); 259 260 QM_TRY(OkIf(keyBufferLength <= uint64_t(UINT32_MAX)), 261 NS_ERROR_FILE_CORRUPTED, IDB_REPORT_INTERNAL_ERR_LAMBDA); 262 263 QM_TRY(OkIf(keyBufferLength <= remainderAfterKeyBufferLength.Length()), 264 NS_ERROR_FILE_CORRUPTED, IDB_REPORT_INTERNAL_ERR_LAMBDA); 265 266 const auto [keyBuffer, remainderAfterKeyBuffer] = 267 remainderAfterKeyBufferLength.SplitAt(keyBufferLength); 268 auto idv = 269 IndexDataValue{indexId, unique, Key{nsCString{AsChars(keyBuffer)}}}; 270 271 // Read sort key buffer length. 272 QM_TRY_INSPECT( 273 (const auto& [sortKeyBufferLength, remainderAfterSortKeyBufferLength]), 274 ReadCompressedNumber(remainderAfterKeyBuffer)); 275 276 remainder = remainderAfterSortKeyBufferLength; 277 if (sortKeyBufferLength > 0) { 278 QM_TRY(OkIf(!remainder.IsEmpty()), NS_ERROR_FILE_CORRUPTED, 279 IDB_REPORT_INTERNAL_ERR_LAMBDA); 280 281 QM_TRY(OkIf(sortKeyBufferLength <= uint64_t(UINT32_MAX)), 282 NS_ERROR_FILE_CORRUPTED, IDB_REPORT_INTERNAL_ERR_LAMBDA); 283 284 QM_TRY(OkIf(sortKeyBufferLength <= remainder.Length()), 285 NS_ERROR_FILE_CORRUPTED, IDB_REPORT_INTERNAL_ERR_LAMBDA); 286 287 const auto [sortKeyBuffer, remainderAfterSortKeyBuffer] = 288 remainder.SplitAt(sortKeyBufferLength); 289 idv.mLocaleAwarePosition = Key{nsCString{AsChars(sortKeyBuffer)}}; 290 remainder = remainderAfterSortKeyBuffer; 291 } 292 293 QM_TRY(OkIf(aOutIndexValues->AppendElement(std::move(idv), fallible)), 294 NS_ERROR_OUT_OF_MEMORY, IDB_REPORT_INTERNAL_ERR_LAMBDA); 295 } 296 aOutIndexValues->Sort(); 297 298 return NS_OK; 299 } 300 301 // aOutIndexValues is an output parameter, since its storage is reused. 302 template <typename T> 303 nsresult ReadCompressedIndexDataValuesFromSource( 304 T& aSource, uint32_t aColumnIndex, 305 nsTArray<IndexDataValue>* aOutIndexValues) { 306 MOZ_ASSERT(!NS_IsMainThread()); 307 MOZ_ASSERT(!IsOnBackgroundThread()); 308 MOZ_ASSERT(aOutIndexValues); 309 MOZ_ASSERT(aOutIndexValues->IsEmpty()); 310 311 QM_TRY_INSPECT( 312 const int32_t& columnType, 313 MOZ_TO_RESULT_INVOKE_MEMBER(aSource, GetTypeOfIndex, aColumnIndex)); 314 315 switch (columnType) { 316 case mozIStorageStatement::VALUE_TYPE_NULL: 317 return NS_OK; 318 319 case mozIStorageStatement::VALUE_TYPE_BLOB: { 320 // XXX ToResultInvoke does not support multiple output parameters yet, so 321 // we also can't use QM_TRY_UNWRAP/QM_TRY_INSPECT here. 322 const uint8_t* blobData; 323 uint32_t blobDataLength; 324 QM_TRY(MOZ_TO_RESULT( 325 aSource.GetSharedBlob(aColumnIndex, &blobDataLength, &blobData))); 326 327 QM_TRY(OkIf(blobDataLength), NS_ERROR_FILE_CORRUPTED, 328 IDB_REPORT_INTERNAL_ERR_LAMBDA); 329 330 QM_TRY(MOZ_TO_RESULT(ReadCompressedIndexDataValuesFromBlob( 331 Span(blobData, blobDataLength), aOutIndexValues))); 332 333 return NS_OK; 334 } 335 336 default: 337 return NS_ERROR_FILE_CORRUPTED; 338 } 339 } 340 341 Result<StructuredCloneReadInfoParent, nsresult> 342 GetStructuredCloneReadInfoFromBlob(const uint8_t* aBlobData, 343 uint32_t aBlobDataLength, 344 const DatabaseFileManager& aFileManager, 345 const nsAString& aFileIds) { 346 MOZ_ASSERT(!IsOnBackgroundThread()); 347 348 AUTO_PROFILER_LABEL("GetStructuredCloneReadInfoFromBlob", DOM); 349 350 const char* const compressed = reinterpret_cast<const char*>(aBlobData); 351 const size_t compressedLength = size_t(aBlobDataLength); 352 353 size_t uncompressedLength; 354 QM_TRY(OkIf(snappy::GetUncompressedLength(compressed, compressedLength, 355 &uncompressedLength)), 356 Err(NS_ERROR_FILE_CORRUPTED)); 357 358 // `data` (JSStructuredCloneData) currently uses 4k buffer internally. 359 // For performance reasons, it's better to align `uncompressed` with that. 360 AutoTArray<uint8_t, 4096> uncompressed; 361 QM_TRY(OkIf(uncompressed.SetLength(uncompressedLength, fallible)), 362 Err(NS_ERROR_OUT_OF_MEMORY)); 363 364 char* const uncompressedBuffer = 365 reinterpret_cast<char*>(uncompressed.Elements()); 366 367 QM_TRY(OkIf(snappy::RawUncompress(compressed, compressedLength, 368 uncompressedBuffer)), 369 Err(NS_ERROR_FILE_CORRUPTED)); 370 371 JSStructuredCloneData data(JS::StructuredCloneScope::DifferentProcess); 372 QM_TRY(OkIf(data.AppendBytes(uncompressedBuffer, uncompressed.Length())), 373 Err(NS_ERROR_OUT_OF_MEMORY)); 374 375 nsTArray<StructuredCloneFileParent> files; 376 if (!aFileIds.IsVoid()) { 377 QM_TRY_UNWRAP(files, 378 DeserializeStructuredCloneFiles(aFileManager, aFileIds)); 379 } 380 381 return StructuredCloneReadInfoParent{std::move(data), std::move(files), 382 false}; 383 } 384 385 Result<StructuredCloneReadInfoParent, nsresult> 386 GetStructuredCloneReadInfoFromExternalBlob( 387 uint64_t aIntData, const DatabaseFileManager& aFileManager, 388 const nsAString& aFileIds) { 389 MOZ_ASSERT(!IsOnBackgroundThread()); 390 391 AUTO_PROFILER_LABEL("GetStructuredCloneReadInfoFromExternalBlob", DOM); 392 393 nsTArray<StructuredCloneFileParent> files; 394 if (!aFileIds.IsVoid()) { 395 QM_TRY_UNWRAP(files, 396 DeserializeStructuredCloneFiles(aFileManager, aFileIds)); 397 } 398 399 // Higher and lower 32 bits described 400 // in ObjectStoreAddOrPutRequestOp::DoDatabaseWork. 401 const uint32_t index = uint32_t(aIntData & UINT32_MAX); 402 403 QM_TRY(OkIf(index < files.Length()), Err(NS_ERROR_UNEXPECTED), 404 [](const auto&) { MOZ_ASSERT(false, "Bad index value!"); }); 405 406 if (StaticPrefs::dom_indexedDB_preprocessing()) { 407 return StructuredCloneReadInfoParent{ 408 JSStructuredCloneData{JS::StructuredCloneScope::DifferentProcess}, 409 std::move(files), true}; 410 } 411 412 // XXX Why can there be multiple files, but we use only a single one here? 413 const StructuredCloneFileParent& file = files[index]; 414 MOZ_ASSERT(file.Type() == StructuredCloneFileBase::eStructuredClone); 415 416 Maybe<CipherKey> maybeKey; 417 418 if (aFileManager.IsInPrivateBrowsingMode()) { 419 nsCString fileKeyId; 420 fileKeyId.AppendInt(file.FileInfo().Id()); 421 422 maybeKey = aFileManager.MutableCipherKeyManagerRef().Get(fileKeyId); 423 } 424 425 auto data = JSStructuredCloneData{JS::StructuredCloneScope::DifferentProcess}; 426 427 { 428 const nsCOMPtr<nsIFile> nativeFile = file.FileInfo().GetFileForFileInfo(); 429 QM_TRY(OkIf(nativeFile), Err(NS_ERROR_FAILURE)); 430 431 QM_TRY_INSPECT( 432 const auto& fileInputStream, 433 NS_NewLocalFileInputStream(nativeFile) 434 .andThen([maybeKey](auto fileInputStream) 435 -> Result<nsCOMPtr<nsIInputStream>, nsresult> { 436 if (maybeKey) { 437 return nsCOMPtr<nsIInputStream>{MakeRefPtr< 438 quota::DecryptingInputStream<IndexedDBCipherStrategy>>( 439 WrapNotNull(std::move(fileInputStream)), 440 kEncryptedStreamBlockSize, *maybeKey)}; 441 } 442 443 return fileInputStream; 444 })); 445 446 QM_TRY(MOZ_TO_RESULT( 447 SnappyUncompressStructuredCloneData(*fileInputStream, data))); 448 } 449 450 return StructuredCloneReadInfoParent{std::move(data), std::move(files), 451 false}; 452 } 453 454 template <typename T> 455 Result<StructuredCloneReadInfoParent, nsresult> 456 GetStructuredCloneReadInfoFromSource(T* aSource, uint32_t aDataIndex, 457 uint32_t aFileIdsIndex, 458 const DatabaseFileManager& aFileManager) { 459 MOZ_ASSERT(!IsOnBackgroundThread()); 460 MOZ_ASSERT(aSource); 461 462 QM_TRY_INSPECT( 463 const int32_t& columnType, 464 MOZ_TO_RESULT_INVOKE_MEMBER(aSource, GetTypeOfIndex, aDataIndex)); 465 466 QM_TRY_INSPECT(const bool& isNull, MOZ_TO_RESULT_INVOKE_MEMBER( 467 aSource, GetIsNull, aFileIdsIndex)); 468 469 QM_TRY_INSPECT(const nsString& fileIds, ([aSource, aFileIdsIndex, isNull] { 470 return isNull ? Result<nsString, nsresult>{VoidString()} 471 : MOZ_TO_RESULT_INVOKE_MEMBER_TYPED( 472 nsString, aSource, GetString, 473 aFileIdsIndex); 474 }())); 475 476 switch (columnType) { 477 case mozIStorageStatement::VALUE_TYPE_INTEGER: { 478 QM_TRY_INSPECT( 479 const int64_t& intData, 480 MOZ_TO_RESULT_INVOKE_MEMBER(aSource, GetInt64, aDataIndex)); 481 482 uint64_t uintData; 483 memcpy(&uintData, &intData, sizeof(uint64_t)); 484 485 return GetStructuredCloneReadInfoFromExternalBlob(uintData, aFileManager, 486 fileIds); 487 } 488 489 case mozIStorageStatement::VALUE_TYPE_BLOB: { 490 const uint8_t* blobData; 491 uint32_t blobDataLength; 492 QM_TRY(MOZ_TO_RESULT( 493 aSource->GetSharedBlob(aDataIndex, &blobDataLength, &blobData))); 494 495 return GetStructuredCloneReadInfoFromBlob(blobData, blobDataLength, 496 aFileManager, fileIds); 497 } 498 499 default: 500 return Err(NS_ERROR_FILE_CORRUPTED); 501 } 502 } 503 504 } // namespace 505 506 IndexDataValue::IndexDataValue() : mIndexId(0), mUnique(false) { 507 MOZ_COUNT_CTOR(IndexDataValue); 508 } 509 510 #if defined(DEBUG) || defined(NS_BUILD_REFCNT_LOGGING) 511 IndexDataValue::IndexDataValue(IndexDataValue&& aOther) noexcept 512 : mIndexId(aOther.mIndexId), 513 mPosition(std::move(aOther.mPosition)), 514 mLocaleAwarePosition(std::move(aOther.mLocaleAwarePosition)), 515 mUnique(aOther.mUnique) { 516 MOZ_ASSERT(!aOther.mPosition.IsUnset()); 517 518 MOZ_COUNT_CTOR(IndexDataValue); 519 } 520 #endif 521 522 IndexDataValue::IndexDataValue(IndexOrObjectStoreId aIndexId, bool aUnique, 523 const Key& aPosition) 524 : mIndexId(aIndexId), mPosition(aPosition), mUnique(aUnique) { 525 MOZ_ASSERT(!aPosition.IsUnset()); 526 527 MOZ_COUNT_CTOR(IndexDataValue); 528 } 529 530 IndexDataValue::IndexDataValue(IndexOrObjectStoreId aIndexId, bool aUnique, 531 const Key& aPosition, 532 const Key& aLocaleAwarePosition) 533 : mIndexId(aIndexId), 534 mPosition(aPosition), 535 mLocaleAwarePosition(aLocaleAwarePosition), 536 mUnique(aUnique) { 537 MOZ_ASSERT(!aPosition.IsUnset()); 538 539 MOZ_COUNT_CTOR(IndexDataValue); 540 } 541 542 bool IndexDataValue::operator==(const IndexDataValue& aOther) const { 543 if (mIndexId != aOther.mIndexId) { 544 return false; 545 } 546 if (mLocaleAwarePosition.IsUnset()) { 547 return mPosition == aOther.mPosition; 548 } 549 return mLocaleAwarePosition == aOther.mLocaleAwarePosition; 550 } 551 552 bool IndexDataValue::operator<(const IndexDataValue& aOther) const { 553 if (mIndexId == aOther.mIndexId) { 554 if (mLocaleAwarePosition.IsUnset()) { 555 return mPosition < aOther.mPosition; 556 } 557 return mLocaleAwarePosition < aOther.mLocaleAwarePosition; 558 } 559 560 return mIndexId < aOther.mIndexId; 561 } 562 563 JSObject* GetSandbox(JSContext* aCx) { 564 SandboxHolder* holder = SandboxHolder::GetOrCreate(); 565 return holder->GetSandboxInternal(aCx); 566 } 567 568 Result<std::pair<UniqueFreePtr<uint8_t>, uint32_t>, nsresult> 569 MakeCompressedIndexDataValues(const nsTArray<IndexDataValue>& aIndexValues) { 570 MOZ_ASSERT(!NS_IsMainThread()); 571 MOZ_ASSERT(!IsOnBackgroundThread()); 572 573 AUTO_PROFILER_LABEL("MakeCompressedIndexDataValues", DOM); 574 575 const uint32_t arrayLength = aIndexValues.Length(); 576 if (!arrayLength) { 577 return std::pair{UniqueFreePtr<uint8_t>{}, 0u}; 578 } 579 580 // First calculate the size of the final buffer. 581 const auto blobDataLength = std::accumulate( 582 aIndexValues.cbegin(), aIndexValues.cend(), CheckedUint32(0), 583 [](CheckedUint32 sum, const IndexDataValue& info) { 584 const nsCString& keyBuffer = info.mPosition.GetBuffer(); 585 const nsCString& sortKeyBuffer = info.mLocaleAwarePosition.GetBuffer(); 586 const uint32_t keyBufferLength = keyBuffer.Length(); 587 const uint32_t sortKeyBufferLength = sortKeyBuffer.Length(); 588 589 MOZ_ASSERT(!keyBuffer.IsEmpty()); 590 591 return sum + CompressedByteCountForIndexId(info.mIndexId) + 592 CompressedByteCountForNumber(keyBufferLength) + 593 CompressedByteCountForNumber(sortKeyBufferLength) + 594 keyBufferLength + sortKeyBufferLength; 595 }); 596 597 QM_TRY(OkIf(blobDataLength.isValid()), 598 Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR), 599 IDB_REPORT_INTERNAL_ERR_LAMBDA); 600 601 UniqueFreePtr<uint8_t> blobData( 602 static_cast<uint8_t*>(malloc(blobDataLength.value()))); 603 QM_TRY(OkIf(static_cast<bool>(blobData)), Err(NS_ERROR_OUT_OF_MEMORY), 604 IDB_REPORT_INTERNAL_ERR_LAMBDA); 605 606 uint8_t* blobDataIter = blobData.get(); 607 608 for (const IndexDataValue& info : aIndexValues) { 609 const nsCString& keyBuffer = info.mPosition.GetBuffer(); 610 const nsCString& sortKeyBuffer = info.mLocaleAwarePosition.GetBuffer(); 611 const uint32_t keyBufferLength = keyBuffer.Length(); 612 const uint32_t sortKeyBufferLength = sortKeyBuffer.Length(); 613 614 WriteCompressedIndexId(info.mIndexId, info.mUnique, &blobDataIter); 615 WriteCompressedNumber(keyBufferLength, &blobDataIter); 616 617 memcpy(blobDataIter, keyBuffer.get(), keyBufferLength); 618 blobDataIter += keyBufferLength; 619 620 WriteCompressedNumber(sortKeyBufferLength, &blobDataIter); 621 622 memcpy(blobDataIter, sortKeyBuffer.get(), sortKeyBufferLength); 623 blobDataIter += sortKeyBufferLength; 624 } 625 626 MOZ_ASSERT(blobDataIter == blobData.get() + blobDataLength.value()); 627 628 return std::pair{std::move(blobData), blobDataLength.value()}; 629 } 630 631 nsresult ReadCompressedIndexDataValues( 632 mozIStorageStatement& aStatement, uint32_t aColumnIndex, 633 nsTArray<IndexDataValue>& aOutIndexValues) { 634 return ReadCompressedIndexDataValuesFromSource(aStatement, aColumnIndex, 635 &aOutIndexValues); 636 } 637 638 template <typename T> 639 Result<IndexDataValuesAutoArray, nsresult> ReadCompressedIndexDataValues( 640 T& aValues, uint32_t aColumnIndex) { 641 return MOZ_TO_RESULT_INVOKE_TYPED(IndexDataValuesAutoArray, 642 &ReadCompressedIndexDataValuesFromSource<T>, 643 aValues, aColumnIndex); 644 } 645 646 template Result<IndexDataValuesAutoArray, nsresult> 647 ReadCompressedIndexDataValues<mozIStorageValueArray>(mozIStorageValueArray&, 648 uint32_t); 649 650 template Result<IndexDataValuesAutoArray, nsresult> 651 ReadCompressedIndexDataValues<mozIStorageStatement>(mozIStorageStatement&, 652 uint32_t); 653 654 Result<std::tuple<IndexOrObjectStoreId, bool, Span<const uint8_t>>, nsresult> 655 ReadCompressedIndexId(const Span<const uint8_t> aData) { 656 QM_TRY_INSPECT((const auto& [indexId, remainder]), 657 ReadCompressedNumber(aData)); 658 659 MOZ_ASSERT(UINT64_MAX / 2 >= uint64_t(indexId), "Bad index id!"); 660 661 return std::tuple{IndexOrObjectStoreId(indexId >> 1), indexId % 2 == 1, 662 remainder}; 663 } 664 665 Result<std::pair<uint64_t, mozilla::Span<const uint8_t>>, nsresult> 666 ReadCompressedNumber(const Span<const uint8_t> aSpan) { 667 uint8_t shiftCounter = 0; 668 uint64_t result = 0; 669 670 const auto end = aSpan.cend(); 671 672 const auto newPos = 673 std::find_if(aSpan.cbegin(), end, [&result, &shiftCounter](uint8_t byte) { 674 MOZ_ASSERT(shiftCounter <= 56, "Shifted too many bits!"); 675 676 result += (uint64_t(byte & 0x7f) << shiftCounter); 677 shiftCounter += 7; 678 679 return !(byte & 0x80); 680 }); 681 682 QM_TRY(OkIf(newPos != end), Err(NS_ERROR_FILE_CORRUPTED), [](const auto&) { 683 MOZ_ASSERT(false); 684 IDB_REPORT_INTERNAL_ERR(); 685 }); 686 687 return std::pair{result, Span{newPos + 1, end}}; 688 } 689 690 Result<StructuredCloneReadInfoParent, nsresult> 691 GetStructuredCloneReadInfoFromValueArray( 692 mozIStorageValueArray* aValues, uint32_t aDataIndex, uint32_t aFileIdsIndex, 693 const DatabaseFileManager& aFileManager) { 694 return GetStructuredCloneReadInfoFromSource(aValues, aDataIndex, 695 aFileIdsIndex, aFileManager); 696 } 697 698 Result<StructuredCloneReadInfoParent, nsresult> 699 GetStructuredCloneReadInfoFromStatement( 700 mozIStorageStatement* aStatement, uint32_t aDataIndex, 701 uint32_t aFileIdsIndex, const DatabaseFileManager& aFileManager) { 702 return GetStructuredCloneReadInfoFromSource(aStatement, aDataIndex, 703 aFileIdsIndex, aFileManager); 704 } 705 706 Result<nsTArray<StructuredCloneFileParent>, nsresult> 707 DeserializeStructuredCloneFiles(const DatabaseFileManager& aFileManager, 708 const nsAString& aText) { 709 MOZ_ASSERT(!IsOnBackgroundThread()); 710 711 nsTArray<StructuredCloneFileParent> result; 712 for (const auto& token : 713 nsCharSeparatedTokenizerTemplate<NS_TokenizerIgnoreNothing>(aText, ' ') 714 .ToRange()) { 715 MOZ_ASSERT(!token.IsEmpty()); 716 717 QM_TRY_UNWRAP(auto structuredCloneFile, 718 DeserializeStructuredCloneFile(aFileManager, token)); 719 720 result.EmplaceBack(std::move(structuredCloneFile)); 721 } 722 723 return result; 724 } 725 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::kTagQueryTainted, aSQLCommand}; 731 732 QM_TRY(MOZ_TO_RESULT(aConnection.ExecuteSimpleSQL(aSQLCommand))); 733 } 734 735 return NS_OK; 736 } 737 738 } // namespace mozilla::dom::indexedDB