QuotaCommon.cpp (22710B)
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 "mozilla/dom/quota/QuotaCommon.h" 8 9 #ifdef QM_ERROR_STACKS_ENABLED 10 # include "base/process_util.h" 11 #endif 12 #include "mozIStorageConnection.h" 13 #include "mozIStorageStatement.h" 14 #include "mozilla/ErrorNames.h" 15 #include "mozilla/Logging.h" 16 #include "mozilla/MozPromise.h" 17 #include "mozilla/TextUtils.h" 18 #include "mozilla/dom/quota/ResultExtensions.h" 19 #include "mozilla/dom/quota/ScopedLogExtraInfo.h" 20 #include "mozilla/glean/DomQuotaMetrics.h" 21 #include "nsIConsoleService.h" 22 #include "nsIFile.h" 23 #include "nsServiceManagerUtils.h" 24 #include "nsStringFlags.h" 25 #include "nsTStringRepr.h" 26 #include "nsUnicharUtils.h" 27 #include "nsXPCOM.h" 28 #include "nsXULAppAPI.h" 29 30 #ifdef XP_WIN 31 # include "mozilla/Atomics.h" 32 # include "mozilla/StaticPrefs_dom.h" 33 # include "mozilla/ipc/BackgroundParent.h" 34 # include "nsILocalFileWin.h" 35 #endif 36 37 namespace mozilla { 38 39 RefPtr<BoolPromise> CreateAndRejectBoolPromise(StaticString aFunc, 40 nsresult aRv) { 41 return CreateAndRejectMozPromise<BoolPromise>(aFunc, aRv); 42 } 43 44 RefPtr<Int64Promise> CreateAndRejectInt64Promise(StaticString aFunc, 45 nsresult aRv) { 46 return CreateAndRejectMozPromise<Int64Promise>(aFunc, aRv); 47 } 48 49 RefPtr<BoolPromise> CreateAndRejectBoolPromiseFromQMResult( 50 StaticString aFunc, const QMResult& aRv) { 51 return CreateAndRejectMozPromise<BoolPromise>(aFunc, aRv); 52 } 53 54 namespace dom::quota { 55 56 namespace { 57 58 #ifdef DEBUG 59 constexpr auto kDSStoreFileName = u".DS_Store"_ns; 60 constexpr auto kDesktopFileName = u".desktop"_ns; 61 constexpr auto kDesktopIniFileName = u"desktop.ini"_ns; 62 constexpr auto kThumbsDbFileName = u"thumbs.db"_ns; 63 #endif 64 65 #ifdef XP_WIN 66 Atomic<int32_t> gUseDOSDevicePathSyntax(-1); 67 #endif 68 69 LazyLogModule gLogger("QuotaManager"); 70 71 void AnonymizeCString(nsACString& aCString, uint32_t aStart) { 72 MOZ_ASSERT(!aCString.IsEmpty()); 73 MOZ_ASSERT(aStart < aCString.Length()); 74 75 char* iter = aCString.BeginWriting() + aStart; 76 char* end = aCString.EndWriting(); 77 78 while (iter != end) { 79 char c = *iter; 80 81 if (IsAsciiAlpha(c)) { 82 *iter = 'a'; 83 } else if (IsAsciiDigit(c)) { 84 *iter = 'D'; 85 } 86 87 ++iter; 88 } 89 } 90 91 } // namespace 92 93 const char kQuotaGenericDelimiter = '|'; 94 95 #ifdef NIGHTLY_BUILD 96 const nsLiteralCString kQuotaInternalError = "internal"_ns; 97 const nsLiteralCString kQuotaExternalError = "external"_ns; 98 #endif 99 100 LogModule* GetQuotaManagerLogger() { return gLogger; } 101 102 void AnonymizeCString(nsACString& aCString) { 103 if (aCString.IsEmpty()) { 104 return; 105 } 106 AnonymizeCString(aCString, /* aStart */ 0); 107 } 108 109 void AnonymizeOriginString(nsACString& aOriginString) { 110 if (aOriginString.IsEmpty()) { 111 return; 112 } 113 114 int32_t start = aOriginString.FindChar(':'); 115 if (start < 0) { 116 start = 0; 117 } 118 119 AnonymizeCString(aOriginString, start); 120 } 121 122 #ifdef XP_WIN 123 void CacheUseDOSDevicePathSyntaxPrefValue() { 124 MOZ_ASSERT(XRE_IsParentProcess()); 125 ::mozilla::ipc::AssertIsOnBackgroundThread(); 126 127 if (gUseDOSDevicePathSyntax == -1) { 128 bool useDOSDevicePathSyntax = 129 StaticPrefs::dom_quotaManager_useDOSDevicePathSyntax_DoNotUseDirectly(); 130 gUseDOSDevicePathSyntax = useDOSDevicePathSyntax ? 1 : 0; 131 } 132 } 133 #endif 134 135 Result<nsCOMPtr<nsIFile>, nsresult> QM_NewLocalFile(const nsAString& aPath) { 136 QM_TRY_UNWRAP( 137 auto file, 138 MOZ_TO_RESULT_INVOKE_TYPED(nsCOMPtr<nsIFile>, NS_NewLocalFile, aPath), 139 QM_PROPAGATE, [&aPath](const nsresult rv) { 140 QM_WARNING("Failed to construct a file for path (%s)", 141 NS_ConvertUTF16toUTF8(aPath).get()); 142 }); 143 144 #ifdef XP_WIN 145 MOZ_ASSERT(gUseDOSDevicePathSyntax != -1); 146 147 if (gUseDOSDevicePathSyntax) { 148 QM_TRY_INSPECT( 149 const auto& winFile, 150 MOZ_TO_RESULT_GET_TYPED(nsCOMPtr<nsILocalFileWin>, 151 MOZ_SELECT_OVERLOAD(do_QueryInterface), file)); 152 153 MOZ_ASSERT(winFile); 154 winFile->SetUseDOSDevicePathSyntax(true); 155 } 156 #endif 157 158 return file; 159 } 160 161 nsDependentCSubstring GetLeafName(const nsACString& aPath) { 162 nsACString::const_iterator start, end; 163 aPath.BeginReading(start); 164 aPath.EndReading(end); 165 166 bool found = RFindInReadable("/"_ns, start, end); 167 if (found) { 168 start = end; 169 } 170 171 aPath.EndReading(end); 172 173 return nsDependentCSubstring(start.get(), end.get()); 174 } 175 176 Result<nsCOMPtr<nsIFile>, nsresult> CloneFileAndAppend( 177 nsIFile& aDirectory, const nsAString& aPathElement) { 178 QM_TRY_UNWRAP(auto resultFile, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED( 179 nsCOMPtr<nsIFile>, aDirectory, Clone)); 180 181 QM_TRY(MOZ_TO_RESULT(resultFile->Append(aPathElement))); 182 183 return resultFile; 184 } 185 186 Result<nsIFileKind, nsresult> GetDirEntryKind(nsIFile& aFile) { 187 // Callers call this function without checking if the directory already 188 // exists (idempotent usage). QM_OR_ELSE_WARN_IF is not used here since we 189 // just want to log NS_ERROR_FILE_NOT_FOUND and NS_ERROR_FILE_FS_CORRUPTED 190 // results and not spam the reports. 191 QM_TRY_RETURN(QM_OR_ELSE_LOG_VERBOSE_IF( 192 MOZ_TO_RESULT_INVOKE_MEMBER(aFile, IsDirectory) 193 .map([](const bool isDirectory) { 194 return isDirectory ? nsIFileKind::ExistsAsDirectory 195 : nsIFileKind::ExistsAsFile; 196 }), 197 ([](const nsresult rv) { 198 return rv == NS_ERROR_FILE_NOT_FOUND || 199 // We treat NS_ERROR_FILE_FS_CORRUPTED as if the file did not 200 // exist at all. 201 rv == NS_ERROR_FILE_FS_CORRUPTED; 202 }), 203 ErrToOk<nsIFileKind::DoesNotExist>)); 204 } 205 206 Result<nsCOMPtr<mozIStorageStatement>, nsresult> CreateStatement( 207 mozIStorageConnection& aConnection, const nsACString& aStatementString) { 208 QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER_TYPED( 209 nsCOMPtr<mozIStorageStatement>, aConnection, CreateStatement, 210 aStatementString)); 211 } 212 213 template <SingleStepResult ResultHandling> 214 Result<SingleStepSuccessType<ResultHandling>, nsresult> ExecuteSingleStep( 215 nsCOMPtr<mozIStorageStatement>&& aStatement) { 216 QM_TRY_INSPECT(const bool& hasResult, 217 MOZ_TO_RESULT_INVOKE_MEMBER(aStatement, ExecuteStep)); 218 219 if constexpr (ResultHandling == SingleStepResult::AssertHasResult) { 220 MOZ_ASSERT(hasResult); 221 (void)hasResult; 222 223 return WrapNotNullUnchecked(std::move(aStatement)); 224 } else { 225 return hasResult ? std::move(aStatement) : nullptr; 226 } 227 } 228 229 template Result<SingleStepSuccessType<SingleStepResult::AssertHasResult>, 230 nsresult> 231 ExecuteSingleStep<SingleStepResult::AssertHasResult>( 232 nsCOMPtr<mozIStorageStatement>&&); 233 234 template Result<SingleStepSuccessType<SingleStepResult::ReturnNullIfNoResult>, 235 nsresult> 236 ExecuteSingleStep<SingleStepResult::ReturnNullIfNoResult>( 237 nsCOMPtr<mozIStorageStatement>&&); 238 239 template <SingleStepResult ResultHandling> 240 Result<SingleStepSuccessType<ResultHandling>, nsresult> 241 CreateAndExecuteSingleStepStatement(mozIStorageConnection& aConnection, 242 const nsACString& aStatementString) { 243 QM_TRY_UNWRAP(auto stmt, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED( 244 nsCOMPtr<mozIStorageStatement>, aConnection, 245 CreateStatement, aStatementString)); 246 247 return ExecuteSingleStep<ResultHandling>(std::move(stmt)); 248 } 249 250 template Result<SingleStepSuccessType<SingleStepResult::AssertHasResult>, 251 nsresult> 252 CreateAndExecuteSingleStepStatement<SingleStepResult::AssertHasResult>( 253 mozIStorageConnection& aConnection, const nsACString& aStatementString); 254 255 template Result<SingleStepSuccessType<SingleStepResult::ReturnNullIfNoResult>, 256 nsresult> 257 CreateAndExecuteSingleStepStatement<SingleStepResult::ReturnNullIfNoResult>( 258 mozIStorageConnection& aConnection, const nsACString& aStatementString); 259 260 namespace detail { 261 262 // Given aPath of /foo/bar/baz and aRelativePath of /bar/baz, returns the 263 // absolute portion of aPath /foo by removing the common suffix from aPath. 264 nsDependentCSubstring GetTreeBase(const nsLiteralCString& aPath, 265 const nsLiteralCString& aRelativePath) { 266 MOZ_ASSERT(StringEndsWith(aPath, aRelativePath)); 267 return Substring(aPath, 0, aPath.Length() - aRelativePath.Length()); 268 } 269 270 nsDependentCSubstring GetSourceTreeBase() { 271 static constexpr auto thisSourceFileRelativePath = 272 "/dom/quota/QuotaCommon.cpp"_ns; 273 274 return GetTreeBase(nsLiteralCString(__FILE__), thisSourceFileRelativePath); 275 } 276 277 nsDependentCSubstring GetObjdirDistIncludeTreeBase( 278 const nsLiteralCString& aQuotaCommonHPath) { 279 static constexpr auto quotaCommonHSourceFileRelativePath = 280 "/mozilla/dom/quota/QuotaCommon.h"_ns; 281 282 return GetTreeBase(aQuotaCommonHPath, quotaCommonHSourceFileRelativePath); 283 } 284 285 static constexpr auto kSourceFileRelativePathMap = 286 std::array<std::pair<nsLiteralCString, nsLiteralCString>, 1>{ 287 {{"mozilla/dom/LocalStorageCommon.h"_ns, 288 "dom/localstorage/LocalStorageCommon.h"_ns}}}; 289 290 nsDependentCSubstring MakeSourceFileRelativePath( 291 const nsACString& aSourceFilePath) { 292 static constexpr auto error = "ERROR"_ns; 293 static constexpr auto mozillaRelativeBase = "mozilla/"_ns; 294 295 static const auto sourceTreeBase = GetSourceTreeBase(); 296 297 if (MOZ_LIKELY(StringBeginsWith(aSourceFilePath, sourceTreeBase))) { 298 return Substring(aSourceFilePath, sourceTreeBase.Length() + 1); 299 } 300 301 // The source file could have been exported to the OBJDIR/dist/include 302 // directory, so we need to check that case as well. 303 static const auto objdirDistIncludeTreeBase = GetObjdirDistIncludeTreeBase(); 304 305 if (MOZ_LIKELY( 306 StringBeginsWith(aSourceFilePath, objdirDistIncludeTreeBase))) { 307 const auto sourceFileRelativePath = 308 Substring(aSourceFilePath, objdirDistIncludeTreeBase.Length() + 1); 309 310 // Exported source files don't have to use the same directory structure as 311 // original source files. Check if we have a mapping for the exported 312 // source file. 313 const auto foundIt = std::find_if( 314 kSourceFileRelativePathMap.cbegin(), kSourceFileRelativePathMap.cend(), 315 [&sourceFileRelativePath](const auto& entry) { 316 return entry.first == sourceFileRelativePath; 317 }); 318 319 if (MOZ_UNLIKELY(foundIt != kSourceFileRelativePathMap.cend())) { 320 return Substring(foundIt->second, 0); 321 } 322 323 // If we don't have a mapping for it, just remove the mozilla/ prefix 324 // (if there's any). 325 if (MOZ_LIKELY( 326 StringBeginsWith(sourceFileRelativePath, mozillaRelativeBase))) { 327 return Substring(sourceFileRelativePath, mozillaRelativeBase.Length()); 328 } 329 330 // At this point, we don't know how to transform the relative path of the 331 // exported source file back to the relative path of the original source 332 // file. This can happen when QM_TRY is used in an exported nsIFoo.h file. 333 // If you really need to use QM_TRY there, consider adding a new mapping 334 // for the exported source file. 335 return sourceFileRelativePath; 336 } 337 338 nsCString::const_iterator begin, end; 339 if (RFindInReadable("/"_ns, aSourceFilePath.BeginReading(begin), 340 aSourceFilePath.EndReading(end))) { 341 // Use the basename as a fallback, to avoid exposing any user parts of the 342 // path. 343 ++begin; 344 return Substring(begin, aSourceFilePath.EndReading(end)); 345 } 346 347 return nsDependentCSubstring{static_cast<mozilla::Span<const char>>( 348 static_cast<const nsCString&>(error))}; 349 } 350 351 } // namespace detail 352 353 #ifdef QM_LOG_ERROR_ENABLED 354 # ifdef QM_ERROR_STACKS_ENABLED 355 void LogError(const nsACString& aExpr, const ResultType& aResult, 356 const nsACString& aSourceFilePath, const int32_t aSourceFileLine, 357 const Severity aSeverity) 358 # else 359 void LogError(const nsACString& aExpr, const Maybe<nsresult> aMaybeRv, 360 const nsACString& aSourceFilePath, const int32_t aSourceFileLine, 361 const Severity aSeverity) 362 # endif 363 { 364 // TODO: Add MOZ_LOG support, bug 1711661. 365 366 // We have to ignore failures with the Verbose severity until we have support 367 // for MOZ_LOG. 368 if (aSeverity == Severity::Verbose) { 369 return; 370 } 371 372 const Tainted<nsCString>* contextTaintedPtr = nullptr; 373 374 # ifdef QM_SCOPED_LOG_EXTRA_INFO_ENABLED 375 const auto& extraInfoMap = ScopedLogExtraInfo::GetExtraInfoMap(); 376 377 if (const auto contextIt = 378 extraInfoMap.find(ScopedLogExtraInfo::kTagContextTainted); 379 contextIt != extraInfoMap.cend()) { 380 contextTaintedPtr = contextIt->second; 381 } 382 # endif 383 384 const auto severityString = [&aSeverity]() -> nsLiteralCString { 385 switch (aSeverity) { 386 case Severity::Error: 387 return "ERROR"_ns; 388 case Severity::Warning: 389 return "WARNING"_ns; 390 case Severity::Info: 391 return "INFO"_ns; 392 case Severity::Verbose: 393 return "VERBOSE"_ns; 394 } 395 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Bad severity value!"); 396 }(); 397 398 Maybe<nsresult> maybeRv; 399 400 # ifdef QM_ERROR_STACKS_ENABLED 401 if (aResult.is<QMResult>()) { 402 maybeRv = Some(aResult.as<QMResult>().NSResult()); 403 } else if (aResult.is<nsresult>()) { 404 maybeRv = Some(aResult.as<nsresult>()); 405 } 406 # else 407 maybeRv = aMaybeRv; 408 # endif 409 410 nsAutoCString rvCode; 411 nsAutoCString rvName; 412 413 if (maybeRv) { 414 nsresult rv = *maybeRv; 415 416 // Ignore this special error code, as it's an expected failure in certain 417 // cases, especially preloading of datastores for LSNG. See the related 418 // comment in InitializeTemporaryClientOp::DoDirectoryWork. 419 // 420 // Note: For now, this simple check is sufficient. However, if more cases 421 // like this are added in the future, it may be worth introducing a more 422 // structured system for handling expected errors. 423 if (rv == NS_ERROR_DOM_QM_CLIENT_INIT_ORIGIN_UNINITIALIZED) { 424 return; 425 } 426 427 rvCode = nsPrintfCString("0x%" PRIX32, static_cast<uint32_t>(rv)); 428 429 // XXX NS_ERROR_MODULE_WIN32 should be handled in GetErrorName directly. 430 if (NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_WIN32) { 431 // XXX We could also try to get the Win32 error name here. 432 rvName = nsPrintfCString( 433 "NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_WIN32, 0x%" PRIX16 ")", 434 NS_ERROR_GET_CODE(rv)); 435 } else { 436 mozilla::GetErrorName(rv, rvName); 437 } 438 } 439 440 # ifdef QM_ERROR_STACKS_ENABLED 441 nsAutoCString frameIdString; 442 nsAutoCString stackIdString; 443 nsAutoCString processIdString; 444 445 if (aResult.is<QMResult>()) { 446 const QMResult& result = aResult.as<QMResult>(); 447 frameIdString = IntToCString(result.FrameId()); 448 stackIdString = IntToCString(result.StackId()); 449 processIdString = 450 IntToCString(static_cast<uint32_t>(base::GetCurrentProcId())); 451 } 452 # endif 453 454 auto extraInfosStringTainted = Tainted<nsAutoCString>([&] { 455 nsAutoCString extraInfosString; 456 457 if (!rvCode.IsEmpty()) { 458 extraInfosString.Append(" failed with resultCode "_ns + rvCode); 459 } 460 461 if (!rvName.IsEmpty()) { 462 extraInfosString.Append(", resultName "_ns + rvName); 463 } 464 465 # ifdef QM_ERROR_STACKS_ENABLED 466 if (!frameIdString.IsEmpty()) { 467 extraInfosString.Append(", frameId "_ns + frameIdString); 468 } 469 470 if (!stackIdString.IsEmpty()) { 471 extraInfosString.Append(", stackId "_ns + stackIdString); 472 } 473 474 if (!processIdString.IsEmpty()) { 475 extraInfosString.Append(", processId "_ns + processIdString); 476 } 477 # endif 478 479 # ifdef QM_SCOPED_LOG_EXTRA_INFO_ENABLED 480 for (const auto& item : extraInfoMap) { 481 const auto& valueTainted = *item.second; 482 483 extraInfosString.Append( 484 ", "_ns + nsDependentCString(item.first) + " "_ns + 485 MOZ_NO_VALIDATE(valueTainted, 486 "It's okay to append any `extraInfoMap` value to " 487 "`extraInfosString`.")); 488 } 489 # endif 490 491 return extraInfosString; 492 }()); 493 494 const auto sourceFileRelativePath = 495 detail::MakeSourceFileRelativePath(aSourceFilePath); 496 497 # ifdef QM_LOG_ERROR_TO_CONSOLE_ENABLED 498 NS_DebugBreak( 499 NS_DEBUG_WARNING, 500 nsAutoCString("QM_TRY failure ("_ns + severityString + ")"_ns).get(), 501 (MOZ_NO_VALIDATE(extraInfosStringTainted, 502 "It's okay to check if `extraInfosString` is empty.") 503 .IsEmpty() 504 ? nsPromiseFlatCString(aExpr) 505 : static_cast<const nsCString&>(nsAutoCString( 506 aExpr + MOZ_NO_VALIDATE(extraInfosStringTainted, 507 "It's okay to log `extraInfosString` " 508 "to stdout/console.")))) 509 .get(), 510 nsPromiseFlatCString(sourceFileRelativePath).get(), aSourceFileLine); 511 # endif 512 513 # ifdef QM_LOG_ERROR_TO_BROWSER_CONSOLE_ENABLED 514 // XXX We might want to allow reporting to the browsing console even when 515 // there's no context in future once we are sure that it can't spam the 516 // browser console or when we have special about:quotamanager for the 517 // reporting (instead of the browsing console). 518 // Another option is to keep the current check and rely on MOZ_LOG reporting 519 // in future once that's available. 520 if (contextTaintedPtr) { 521 nsCOMPtr<nsIConsoleService> console = 522 do_GetService(NS_CONSOLESERVICE_CONTRACTID); 523 if (console) { 524 NS_ConvertUTF8toUTF16 message( 525 "QM_TRY failure ("_ns + severityString + ")"_ns + ": '"_ns + aExpr + 526 MOZ_NO_VALIDATE( 527 extraInfosStringTainted, 528 "It's okay to log `extraInfosString` to the browser console.") + 529 "', file "_ns + sourceFileRelativePath + ":"_ns + 530 IntToCString(aSourceFileLine)); 531 532 // The concatenation above results in a message like: 533 // QM_TRY failure (ERROR): 'MaybeRemoveLocalStorageArchiveTmpFile() failed 534 // with resultCode 0x80004005, resultName NS_ERROR_FAILURE, frameId 1, 535 // stackId 1, processId 53978, context Initialization::Storage', file 536 // dom/quota/ActorsParent.cpp:6029 537 538 console->LogStringMessage(message.get()); 539 } 540 } 541 # endif 542 543 # ifdef QM_LOG_ERROR_TO_TELEMETRY_ENABLED 544 // The context tag is special because it's used to enable logging to 545 // telemetry (besides carrying information). Other tags (like query) don't 546 // enable logging to telemetry. 547 548 if (contextTaintedPtr) { 549 const auto& contextTainted = *contextTaintedPtr; 550 551 // Do NOT CHANGE this if you don't know what you're doing. 552 553 // `extraInfoString` is not included in the telemetry event on purpose 554 // since it can contain sensitive information. 555 556 // For now, we don't include aExpr in the telemetry event. It might help to 557 // match locations across versions, but they might be large. 558 559 // New extra entries (with potentially sensitive content) can't be easily 560 // (accidentally) added because they would have to be added to Events.yaml 561 // under "dom.quota.try" which would require a data review. 562 563 mozilla::glean::dom_quota_try::ErrorStepExtra extra; 564 extra.context = Some(MOZ_NO_VALIDATE( 565 contextTainted, 566 "Context has been data-reviewed for telemetry transmission.")); 567 568 mozilla::gecko_trace::events::DomQuotaTryEvent try_event; 569 570 # ifdef QM_ERROR_STACKS_ENABLED 571 if (!frameIdString.IsEmpty()) { 572 extra.frameId = Some(frameIdString); 573 try_event.WithFrameId(frameIdString); 574 } 575 576 if (!processIdString.IsEmpty()) { 577 extra.processId = Some(processIdString); 578 } 579 # endif 580 581 if (!rvName.IsEmpty()) { 582 extra.result = Some(rvName); 583 try_event.WithResult(rvName); 584 } 585 586 // Here, we are generating thread local sequence number and thread Id 587 // information which could be useful for summarizing and categorizing 588 // log statistics in QM_TRY stack propagation scripts. Since, this is 589 // a thread local object, we do not need to worry about data races. 590 static MOZ_THREAD_LOCAL(uint32_t) sSequenceNumber; 591 592 // This would be initialized once, all subsequent calls would be a no-op. 593 MOZ_ALWAYS_TRUE(sSequenceNumber.init()); 594 595 // sequence number should always starts at number 1. 596 // `sSequenceNumber` gets initialized to 0; so we have to increment here. 597 const auto newSeqNum = sSequenceNumber.get() + 1; 598 const auto threadId = baseprofiler::profiler_current_thread_id().ToNumber(); 599 600 const auto threadIdAndSequence = 601 (static_cast<uint64_t>(threadId) << 32) | (newSeqNum & 0xFFFFFFFF); 602 603 extra.seq = Some(threadIdAndSequence); 604 605 sSequenceNumber.set(newSeqNum); 606 607 extra.severity = Some(severityString); 608 try_event.WithSeverity(severityString); 609 610 extra.sourceFile = Some(sourceFileRelativePath); 611 try_event.WithSourceFile(sourceFileRelativePath); 612 613 extra.sourceLine = Some(aSourceFileLine); 614 try_event.WithSourceLine(aSourceFileLine); 615 616 # ifdef QM_ERROR_STACKS_ENABLED 617 if (!stackIdString.IsEmpty()) { 618 extra.stackId = Some(stackIdString); 619 } 620 # endif 621 622 glean::dom_quota_try::error_step.Record(Some(extra)); 623 try_event.Emit(); 624 } 625 # endif 626 } 627 #endif 628 629 #ifdef DEBUG 630 Result<bool, nsresult> WarnIfFileIsUnknown(nsIFile& aFile, 631 const char* aSourceFilePath, 632 const int32_t aSourceFileLine) { 633 nsString leafName; 634 nsresult rv = aFile.GetLeafName(leafName); 635 if (NS_WARN_IF(NS_FAILED(rv))) { 636 return Err(rv); 637 } 638 639 bool isDirectory; 640 rv = aFile.IsDirectory(&isDirectory); 641 if (NS_WARN_IF(NS_FAILED(rv))) { 642 return Err(rv); 643 } 644 645 if (!isDirectory) { 646 // Don't warn about OS metadata files. These files are only used in 647 // different platforms, but the profile can be shared across different 648 // operating systems, so we check it on all platforms. 649 if (leafName.Equals(kDSStoreFileName) || 650 leafName.Equals(kDesktopFileName) || 651 leafName.Equals(kDesktopIniFileName, 652 nsCaseInsensitiveStringComparator) || 653 leafName.Equals(kThumbsDbFileName, nsCaseInsensitiveStringComparator)) { 654 return false; 655 } 656 657 // Don't warn about files starting with ".". 658 if (leafName.First() == char16_t('.')) { 659 return false; 660 } 661 } 662 663 NS_DebugBreak( 664 NS_DEBUG_WARNING, 665 nsPrintfCString("Something (%s) in the directory that doesn't belong!", 666 NS_ConvertUTF16toUTF8(leafName).get()) 667 .get(), 668 nullptr, aSourceFilePath, aSourceFileLine); 669 670 return true; 671 } 672 #endif 673 674 } // namespace dom::quota 675 } // namespace mozilla