summaryrefslogtreecommitdiffstats
path: root/dom/quota/QuotaCommon.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/quota/QuotaCommon.cpp')
-rw-r--r--dom/quota/QuotaCommon.cpp639
1 files changed, 639 insertions, 0 deletions
diff --git a/dom/quota/QuotaCommon.cpp b/dom/quota/QuotaCommon.cpp
new file mode 100644
index 0000000000..e2df8a1082
--- /dev/null
+++ b/dom/quota/QuotaCommon.cpp
@@ -0,0 +1,639 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/quota/QuotaCommon.h"
+
+#ifdef QM_ERROR_STACKS_ENABLED
+# include "base/process_util.h"
+#endif
+#include "mozIStorageConnection.h"
+#include "mozIStorageStatement.h"
+#include "mozilla/ErrorNames.h"
+#include "mozilla/MozPromise.h"
+#include "mozilla/Logging.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/TelemetryComms.h"
+#include "mozilla/TelemetryEventEnums.h"
+#include "mozilla/TextUtils.h"
+#include "mozilla/dom/quota/ResultExtensions.h"
+#include "mozilla/dom/quota/ScopedLogExtraInfo.h"
+#include "nsIConsoleService.h"
+#include "nsIFile.h"
+#include "nsServiceManagerUtils.h"
+#include "nsStringFlags.h"
+#include "nsTStringRepr.h"
+#include "nsUnicharUtils.h"
+#include "nsXPCOM.h"
+#include "nsXULAppAPI.h"
+
+#ifdef XP_WIN
+# include "mozilla/Atomics.h"
+# include "mozilla/ipc/BackgroundParent.h"
+# include "mozilla/StaticPrefs_dom.h"
+# include "nsILocalFileWin.h"
+#endif
+
+namespace mozilla {
+
+RefPtr<BoolPromise> CreateAndRejectBoolPromise(const char* aFunc,
+ nsresult aRv) {
+ return CreateAndRejectMozPromise<BoolPromise>(aFunc, aRv);
+}
+
+RefPtr<Int64Promise> CreateAndRejectInt64Promise(const char* aFunc,
+ nsresult aRv) {
+ return CreateAndRejectMozPromise<Int64Promise>(aFunc, aRv);
+}
+
+RefPtr<BoolPromise> CreateAndRejectBoolPromiseFromQMResult(
+ const char* aFunc, const QMResult& aRv) {
+ return CreateAndRejectMozPromise<BoolPromise>(aFunc, aRv);
+}
+
+namespace dom::quota {
+
+using namespace mozilla::Telemetry;
+
+namespace {
+
+#ifdef DEBUG
+constexpr auto kDSStoreFileName = u".DS_Store"_ns;
+constexpr auto kDesktopFileName = u".desktop"_ns;
+constexpr auto kDesktopIniFileName = u"desktop.ini"_ns;
+constexpr auto kThumbsDbFileName = u"thumbs.db"_ns;
+#endif
+
+#ifdef XP_WIN
+Atomic<int32_t> gUseDOSDevicePathSyntax(-1);
+#endif
+
+LazyLogModule gLogger("QuotaManager");
+
+void AnonymizeCString(nsACString& aCString, uint32_t aStart) {
+ MOZ_ASSERT(!aCString.IsEmpty());
+ MOZ_ASSERT(aStart < aCString.Length());
+
+ char* iter = aCString.BeginWriting() + aStart;
+ char* end = aCString.EndWriting();
+
+ while (iter != end) {
+ char c = *iter;
+
+ if (IsAsciiAlpha(c)) {
+ *iter = 'a';
+ } else if (IsAsciiDigit(c)) {
+ *iter = 'D';
+ }
+
+ ++iter;
+ }
+}
+
+} // namespace
+
+const char kQuotaGenericDelimiter = '|';
+
+#ifdef NIGHTLY_BUILD
+const nsLiteralCString kQuotaInternalError = "internal"_ns;
+const nsLiteralCString kQuotaExternalError = "external"_ns;
+#endif
+
+LogModule* GetQuotaManagerLogger() { return gLogger; }
+
+void AnonymizeCString(nsACString& aCString) {
+ if (aCString.IsEmpty()) {
+ return;
+ }
+ AnonymizeCString(aCString, /* aStart */ 0);
+}
+
+void AnonymizeOriginString(nsACString& aOriginString) {
+ if (aOriginString.IsEmpty()) {
+ return;
+ }
+
+ int32_t start = aOriginString.FindChar(':');
+ if (start < 0) {
+ start = 0;
+ }
+
+ AnonymizeCString(aOriginString, start);
+}
+
+#ifdef XP_WIN
+void CacheUseDOSDevicePathSyntaxPrefValue() {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ ::mozilla::ipc::AssertIsOnBackgroundThread();
+
+ if (gUseDOSDevicePathSyntax == -1) {
+ bool useDOSDevicePathSyntax =
+ StaticPrefs::dom_quotaManager_useDOSDevicePathSyntax_DoNotUseDirectly();
+ gUseDOSDevicePathSyntax = useDOSDevicePathSyntax ? 1 : 0;
+ }
+}
+#endif
+
+Result<nsCOMPtr<nsIFile>, nsresult> QM_NewLocalFile(const nsAString& aPath) {
+ QM_TRY_UNWRAP(
+ auto file,
+ MOZ_TO_RESULT_INVOKE_TYPED(nsCOMPtr<nsIFile>, NS_NewLocalFile, aPath,
+ /* aFollowLinks */ false),
+ QM_PROPAGATE, [&aPath](const nsresult rv) {
+ QM_WARNING("Failed to construct a file for path (%s)",
+ NS_ConvertUTF16toUTF8(aPath).get());
+ });
+
+#ifdef XP_WIN
+ MOZ_ASSERT(gUseDOSDevicePathSyntax != -1);
+
+ if (gUseDOSDevicePathSyntax) {
+ QM_TRY_INSPECT(
+ const auto& winFile,
+ MOZ_TO_RESULT_GET_TYPED(nsCOMPtr<nsILocalFileWin>,
+ MOZ_SELECT_OVERLOAD(do_QueryInterface), file));
+
+ MOZ_ASSERT(winFile);
+ winFile->SetUseDOSDevicePathSyntax(true);
+ }
+#endif
+
+ return file;
+}
+
+nsDependentCSubstring GetLeafName(const nsACString& aPath) {
+ nsACString::const_iterator start, end;
+ aPath.BeginReading(start);
+ aPath.EndReading(end);
+
+ bool found = RFindInReadable("/"_ns, start, end);
+ if (found) {
+ start = end;
+ }
+
+ aPath.EndReading(end);
+
+ return nsDependentCSubstring(start.get(), end.get());
+}
+
+Result<nsCOMPtr<nsIFile>, nsresult> CloneFileAndAppend(
+ nsIFile& aDirectory, const nsAString& aPathElement) {
+ QM_TRY_UNWRAP(auto resultFile, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
+ nsCOMPtr<nsIFile>, aDirectory, Clone));
+
+ QM_TRY(MOZ_TO_RESULT(resultFile->Append(aPathElement)));
+
+ return resultFile;
+}
+
+Result<nsIFileKind, nsresult> GetDirEntryKind(nsIFile& aFile) {
+ // Callers call this function without checking if the directory already
+ // exists (idempotent usage). QM_OR_ELSE_WARN_IF is not used here since we
+ // just want to log NS_ERROR_FILE_NOT_FOUND and NS_ERROR_FILE_FS_CORRUPTED
+ // results and not spam the reports.
+ QM_TRY_RETURN(QM_OR_ELSE_LOG_VERBOSE_IF(
+ MOZ_TO_RESULT_INVOKE_MEMBER(aFile, IsDirectory)
+ .map([](const bool isDirectory) {
+ return isDirectory ? nsIFileKind::ExistsAsDirectory
+ : nsIFileKind::ExistsAsFile;
+ }),
+ ([](const nsresult rv) {
+ return rv == NS_ERROR_FILE_NOT_FOUND ||
+ // We treat NS_ERROR_FILE_FS_CORRUPTED as if the file did not
+ // exist at all.
+ rv == NS_ERROR_FILE_FS_CORRUPTED;
+ }),
+ ErrToOk<nsIFileKind::DoesNotExist>));
+}
+
+Result<nsCOMPtr<mozIStorageStatement>, nsresult> CreateStatement(
+ mozIStorageConnection& aConnection, const nsACString& aStatementString) {
+ QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
+ nsCOMPtr<mozIStorageStatement>, aConnection, CreateStatement,
+ aStatementString));
+}
+
+template <SingleStepResult ResultHandling>
+Result<SingleStepSuccessType<ResultHandling>, nsresult> ExecuteSingleStep(
+ nsCOMPtr<mozIStorageStatement>&& aStatement) {
+ QM_TRY_INSPECT(const bool& hasResult,
+ MOZ_TO_RESULT_INVOKE_MEMBER(aStatement, ExecuteStep));
+
+ if constexpr (ResultHandling == SingleStepResult::AssertHasResult) {
+ MOZ_ASSERT(hasResult);
+ (void)hasResult;
+
+ return WrapNotNullUnchecked(std::move(aStatement));
+ } else {
+ return hasResult ? std::move(aStatement) : nullptr;
+ }
+}
+
+template Result<SingleStepSuccessType<SingleStepResult::AssertHasResult>,
+ nsresult>
+ExecuteSingleStep<SingleStepResult::AssertHasResult>(
+ nsCOMPtr<mozIStorageStatement>&&);
+
+template Result<SingleStepSuccessType<SingleStepResult::ReturnNullIfNoResult>,
+ nsresult>
+ExecuteSingleStep<SingleStepResult::ReturnNullIfNoResult>(
+ nsCOMPtr<mozIStorageStatement>&&);
+
+template <SingleStepResult ResultHandling>
+Result<SingleStepSuccessType<ResultHandling>, nsresult>
+CreateAndExecuteSingleStepStatement(mozIStorageConnection& aConnection,
+ const nsACString& aStatementString) {
+ QM_TRY_UNWRAP(auto stmt, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
+ nsCOMPtr<mozIStorageStatement>, aConnection,
+ CreateStatement, aStatementString));
+
+ return ExecuteSingleStep<ResultHandling>(std::move(stmt));
+}
+
+template Result<SingleStepSuccessType<SingleStepResult::AssertHasResult>,
+ nsresult>
+CreateAndExecuteSingleStepStatement<SingleStepResult::AssertHasResult>(
+ mozIStorageConnection& aConnection, const nsACString& aStatementString);
+
+template Result<SingleStepSuccessType<SingleStepResult::ReturnNullIfNoResult>,
+ nsresult>
+CreateAndExecuteSingleStepStatement<SingleStepResult::ReturnNullIfNoResult>(
+ mozIStorageConnection& aConnection, const nsACString& aStatementString);
+
+namespace detail {
+
+// Given aPath of /foo/bar/baz and aRelativePath of /bar/baz, returns the
+// absolute portion of aPath /foo by removing the common suffix from aPath.
+nsDependentCSubstring GetTreeBase(const nsLiteralCString& aPath,
+ const nsLiteralCString& aRelativePath) {
+ MOZ_ASSERT(StringEndsWith(aPath, aRelativePath));
+ return Substring(aPath, 0, aPath.Length() - aRelativePath.Length());
+}
+
+nsDependentCSubstring GetSourceTreeBase() {
+ static constexpr auto thisSourceFileRelativePath =
+ "/dom/quota/QuotaCommon.cpp"_ns;
+
+ return GetTreeBase(nsLiteralCString(__FILE__), thisSourceFileRelativePath);
+}
+
+nsDependentCSubstring GetObjdirDistIncludeTreeBase(
+ const nsLiteralCString& aQuotaCommonHPath) {
+ static constexpr auto quotaCommonHSourceFileRelativePath =
+ "/mozilla/dom/quota/QuotaCommon.h"_ns;
+
+ return GetTreeBase(aQuotaCommonHPath, quotaCommonHSourceFileRelativePath);
+}
+
+static constexpr auto kSourceFileRelativePathMap =
+ std::array<std::pair<nsLiteralCString, nsLiteralCString>, 1>{
+ {{"mozilla/dom/LocalStorageCommon.h"_ns,
+ "dom/localstorage/LocalStorageCommon.h"_ns}}};
+
+nsDependentCSubstring MakeSourceFileRelativePath(
+ const nsACString& aSourceFilePath) {
+ static constexpr auto error = "ERROR"_ns;
+ static constexpr auto mozillaRelativeBase = "mozilla/"_ns;
+
+ static const auto sourceTreeBase = GetSourceTreeBase();
+
+ if (MOZ_LIKELY(StringBeginsWith(aSourceFilePath, sourceTreeBase))) {
+ return Substring(aSourceFilePath, sourceTreeBase.Length() + 1);
+ }
+
+ // The source file could have been exported to the OBJDIR/dist/include
+ // directory, so we need to check that case as well.
+ static const auto objdirDistIncludeTreeBase = GetObjdirDistIncludeTreeBase();
+
+ if (MOZ_LIKELY(
+ StringBeginsWith(aSourceFilePath, objdirDistIncludeTreeBase))) {
+ const auto sourceFileRelativePath =
+ Substring(aSourceFilePath, objdirDistIncludeTreeBase.Length() + 1);
+
+ // Exported source files don't have to use the same directory structure as
+ // original source files. Check if we have a mapping for the exported
+ // source file.
+ const auto foundIt = std::find_if(
+ kSourceFileRelativePathMap.cbegin(), kSourceFileRelativePathMap.cend(),
+ [&sourceFileRelativePath](const auto& entry) {
+ return entry.first == sourceFileRelativePath;
+ });
+
+ if (MOZ_UNLIKELY(foundIt != kSourceFileRelativePathMap.cend())) {
+ return Substring(foundIt->second, 0);
+ }
+
+ // If we don't have a mapping for it, just remove the mozilla/ prefix
+ // (if there's any).
+ if (MOZ_LIKELY(
+ StringBeginsWith(sourceFileRelativePath, mozillaRelativeBase))) {
+ return Substring(sourceFileRelativePath, mozillaRelativeBase.Length());
+ }
+
+ // At this point, we don't know how to transform the relative path of the
+ // exported source file back to the relative path of the original source
+ // file. This can happen when QM_TRY is used in an exported nsIFoo.h file.
+ // If you really need to use QM_TRY there, consider adding a new mapping
+ // for the exported source file.
+ return sourceFileRelativePath;
+ }
+
+ nsCString::const_iterator begin, end;
+ if (RFindInReadable("/"_ns, aSourceFilePath.BeginReading(begin),
+ aSourceFilePath.EndReading(end))) {
+ // Use the basename as a fallback, to avoid exposing any user parts of the
+ // path.
+ ++begin;
+ return Substring(begin, aSourceFilePath.EndReading(end));
+ }
+
+ return nsDependentCSubstring{static_cast<mozilla::Span<const char>>(
+ static_cast<const nsCString&>(error))};
+}
+
+} // namespace detail
+
+#ifdef QM_LOG_ERROR_ENABLED
+# ifdef QM_ERROR_STACKS_ENABLED
+void LogError(const nsACString& aExpr, const ResultType& aResult,
+ const nsACString& aSourceFilePath, const int32_t aSourceFileLine,
+ const Severity aSeverity)
+# else
+void LogError(const nsACString& aExpr, const Maybe<nsresult> aMaybeRv,
+ const nsACString& aSourceFilePath, const int32_t aSourceFileLine,
+ const Severity aSeverity)
+# endif
+{
+ // TODO: Add MOZ_LOG support, bug 1711661.
+
+ // We have to ignore failures with the Verbose severity until we have support
+ // for MOZ_LOG.
+ if (aSeverity == Severity::Verbose) {
+ return;
+ }
+
+ nsAutoCString context;
+
+# ifdef QM_SCOPED_LOG_EXTRA_INFO_ENABLED
+ const auto& extraInfoMap = ScopedLogExtraInfo::GetExtraInfoMap();
+
+ if (const auto contextIt = extraInfoMap.find(ScopedLogExtraInfo::kTagContext);
+ contextIt != extraInfoMap.cend()) {
+ context = *contextIt->second;
+ }
+# endif
+
+ const auto severityString = [&aSeverity]() -> nsLiteralCString {
+ switch (aSeverity) {
+ case Severity::Error:
+ return "ERROR"_ns;
+ case Severity::Warning:
+ return "WARNING"_ns;
+ case Severity::Info:
+ return "INFO"_ns;
+ case Severity::Verbose:
+ return "VERBOSE"_ns;
+ }
+ MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Bad severity value!");
+ }();
+
+ Maybe<nsresult> maybeRv;
+
+# ifdef QM_ERROR_STACKS_ENABLED
+ if (aResult.is<QMResult>()) {
+ maybeRv = Some(aResult.as<QMResult>().NSResult());
+ } else if (aResult.is<nsresult>()) {
+ maybeRv = Some(aResult.as<nsresult>());
+ }
+# else
+ maybeRv = aMaybeRv;
+# endif
+
+ nsAutoCString rvCode;
+ nsAutoCString rvName;
+
+ if (maybeRv) {
+ nsresult rv = *maybeRv;
+
+ rvCode = nsPrintfCString("0x%" PRIX32, static_cast<uint32_t>(rv));
+
+ // XXX NS_ERROR_MODULE_WIN32 should be handled in GetErrorName directly.
+ if (NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_WIN32) {
+ // XXX We could also try to get the Win32 error name here.
+ rvName = nsPrintfCString(
+ "NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_WIN32, 0x%" PRIX16 ")",
+ NS_ERROR_GET_CODE(rv));
+ } else {
+ mozilla::GetErrorName(rv, rvName);
+ }
+ }
+
+# ifdef QM_ERROR_STACKS_ENABLED
+ nsAutoCString frameIdString;
+ nsAutoCString stackIdString;
+ nsAutoCString processIdString;
+
+ if (aResult.is<QMResult>()) {
+ const QMResult& result = aResult.as<QMResult>();
+ frameIdString = IntToCString(result.FrameId());
+ stackIdString = IntToCString(result.StackId());
+ processIdString =
+ IntToCString(static_cast<uint32_t>(base::GetCurrentProcId()));
+ }
+# endif
+
+ nsAutoCString extraInfosString;
+
+ if (!rvCode.IsEmpty()) {
+ extraInfosString.Append(" failed with resultCode "_ns + rvCode);
+ }
+
+ if (!rvName.IsEmpty()) {
+ extraInfosString.Append(", resultName "_ns + rvName);
+ }
+
+# ifdef QM_ERROR_STACKS_ENABLED
+ if (!frameIdString.IsEmpty()) {
+ extraInfosString.Append(", frameId "_ns + frameIdString);
+ }
+
+ if (!stackIdString.IsEmpty()) {
+ extraInfosString.Append(", stackId "_ns + stackIdString);
+ }
+
+ if (!processIdString.IsEmpty()) {
+ extraInfosString.Append(", processId "_ns + processIdString);
+ }
+# endif
+
+# ifdef QM_SCOPED_LOG_EXTRA_INFO_ENABLED
+ for (const auto& item : extraInfoMap) {
+ extraInfosString.Append(", "_ns + nsDependentCString(item.first) + " "_ns +
+ *item.second);
+ }
+# endif
+
+ const auto sourceFileRelativePath =
+ detail::MakeSourceFileRelativePath(aSourceFilePath);
+
+# ifdef QM_LOG_ERROR_TO_CONSOLE_ENABLED
+ NS_DebugBreak(
+ NS_DEBUG_WARNING,
+ nsAutoCString("QM_TRY failure ("_ns + severityString + ")"_ns).get(),
+ (extraInfosString.IsEmpty() ? nsPromiseFlatCString(aExpr)
+ : static_cast<const nsCString&>(nsAutoCString(
+ aExpr + extraInfosString)))
+ .get(),
+ nsPromiseFlatCString(sourceFileRelativePath).get(), aSourceFileLine);
+# endif
+
+# ifdef QM_LOG_ERROR_TO_BROWSER_CONSOLE_ENABLED
+ // XXX We might want to allow reporting to the browsing console even when
+ // there's no context in future once we are sure that it can't spam the
+ // browser console or when we have special about:quotamanager for the
+ // reporting (instead of the browsing console).
+ // Another option is to keep the current check and rely on MOZ_LOG reporting
+ // in future once that's available.
+ if (!context.IsEmpty()) {
+ nsCOMPtr<nsIConsoleService> console =
+ do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+ if (console) {
+ NS_ConvertUTF8toUTF16 message(
+ "QM_TRY failure ("_ns + severityString + ")"_ns + ": '"_ns + aExpr +
+ extraInfosString + "', file "_ns + sourceFileRelativePath + ":"_ns +
+ IntToCString(aSourceFileLine));
+
+ // The concatenation above results in a message like:
+ // QM_TRY failure (ERROR): 'MaybeRemoveLocalStorageArchiveTmpFile() failed
+ // with resultCode 0x80004005, resultName NS_ERROR_FAILURE, frameId 1,
+ // stackId 1, processId 53978, context Initialization::Storage', file
+ // dom/quota/ActorsParent.cpp:6029
+
+ console->LogStringMessage(message.get());
+ }
+ }
+# endif
+
+# ifdef QM_LOG_ERROR_TO_TELEMETRY_ENABLED
+ if (!context.IsEmpty()) {
+ // For now, we don't include aExpr in the telemetry event. It might help to
+ // match locations across versions, but they might be large.
+ auto extra = Some([&] {
+ auto res = CopyableTArray<EventExtraEntry>{};
+ res.SetCapacity(9);
+
+ res.AppendElement(EventExtraEntry{"context"_ns, nsCString{context}});
+
+# ifdef QM_ERROR_STACKS_ENABLED
+ if (!frameIdString.IsEmpty()) {
+ res.AppendElement(
+ EventExtraEntry{"frame_id"_ns, nsCString{frameIdString}});
+ }
+
+ if (!processIdString.IsEmpty()) {
+ res.AppendElement(
+ EventExtraEntry{"process_id"_ns, nsCString{processIdString}});
+ }
+# endif
+
+ if (!rvName.IsEmpty()) {
+ res.AppendElement(EventExtraEntry{"result"_ns, nsCString{rvName}});
+ }
+
+ // Here, we are generating thread local sequence number and thread Id
+ // information which could be useful for summarizing and categorizing
+ // log statistics in QM_TRY stack propagation scripts. Since, this is
+ // a thread local object, we do not need to worry about data races.
+ static MOZ_THREAD_LOCAL(uint32_t) sSequenceNumber;
+
+ // This would be initialized once, all subsequent calls would be a no-op.
+ MOZ_ALWAYS_TRUE(sSequenceNumber.init());
+
+ // sequence number should always starts at number 1.
+ // `sSequenceNumber` gets initialized to 0; so we have to increment here.
+ const auto newSeqNum = sSequenceNumber.get() + 1;
+ const auto threadId =
+ mozilla::baseprofiler::profiler_current_thread_id().ToNumber();
+
+ const auto threadIdAndSequence =
+ (static_cast<uint64_t>(threadId) << 32) | (newSeqNum & 0xFFFFFFFF);
+
+ res.AppendElement(
+ EventExtraEntry{"seq"_ns, IntToCString(threadIdAndSequence)});
+
+ sSequenceNumber.set(newSeqNum);
+
+ res.AppendElement(EventExtraEntry{"severity"_ns, severityString});
+
+ res.AppendElement(
+ EventExtraEntry{"source_file"_ns, nsCString(sourceFileRelativePath)});
+
+ res.AppendElement(
+ EventExtraEntry{"source_line"_ns, IntToCString(aSourceFileLine)});
+
+# ifdef QM_ERROR_STACKS_ENABLED
+ if (!stackIdString.IsEmpty()) {
+ res.AppendElement(
+ EventExtraEntry{"stack_id"_ns, nsCString{stackIdString}});
+ }
+# endif
+
+ return res;
+ }());
+
+ Telemetry::RecordEvent(Telemetry::EventID::DomQuotaTry_Error_Step,
+ Nothing(), extra);
+ }
+# endif
+}
+#endif
+
+#ifdef DEBUG
+Result<bool, nsresult> WarnIfFileIsUnknown(nsIFile& aFile,
+ const char* aSourceFilePath,
+ const int32_t aSourceFileLine) {
+ nsString leafName;
+ nsresult rv = aFile.GetLeafName(leafName);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return Err(rv);
+ }
+
+ bool isDirectory;
+ rv = aFile.IsDirectory(&isDirectory);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return Err(rv);
+ }
+
+ if (!isDirectory) {
+ // Don't warn about OS metadata files. These files are only used in
+ // different platforms, but the profile can be shared across different
+ // operating systems, so we check it on all platforms.
+ if (leafName.Equals(kDSStoreFileName) ||
+ leafName.Equals(kDesktopFileName) ||
+ leafName.Equals(kDesktopIniFileName,
+ nsCaseInsensitiveStringComparator) ||
+ leafName.Equals(kThumbsDbFileName, nsCaseInsensitiveStringComparator)) {
+ return false;
+ }
+
+ // Don't warn about files starting with ".".
+ if (leafName.First() == char16_t('.')) {
+ return false;
+ }
+ }
+
+ NS_DebugBreak(
+ NS_DEBUG_WARNING,
+ nsPrintfCString("Something (%s) in the directory that doesn't belong!",
+ NS_ConvertUTF16toUTF8(leafName).get())
+ .get(),
+ nullptr, aSourceFilePath, aSourceFileLine);
+
+ return true;
+}
+#endif
+
+} // namespace dom::quota
+} // namespace mozilla