tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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