fune/dom/quota/QuotaCommon.cpp
Ray Kraesig e2ee2f11df Bug 1891541 - [2/2] enforce that MozPromise only accepts static strings r=xpcom-reviewers,media-playback-reviewers,padenot,emilio
All present uses of the call-site arguments to MozPromise's methods
supply static strings. However, this is nowhere enforced. Do so.

Additionally, since this is the third or fourth time the present author
alone has personally implemented such an enforcement mechanism, create a
helper class to simplify doing so.

No functional changes.

Differential Revision: https://phabricator.services.mozilla.com/D207462
2024-04-24 17:02:49 +00:00

677 lines
22 KiB
C++

/* -*- 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(StaticString aFunc,
nsresult aRv) {
return CreateAndRejectMozPromise<BoolPromise>(aFunc, aRv);
}
RefPtr<Int64Promise> CreateAndRejectInt64Promise(StaticString aFunc,
nsresult aRv) {
return CreateAndRejectMozPromise<Int64Promise>(aFunc, aRv);
}
RefPtr<BoolPromise> CreateAndRejectBoolPromiseFromQMResult(
StaticString 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;
}
const Tainted<nsCString>* contextTaintedPtr = nullptr;
# ifdef QM_SCOPED_LOG_EXTRA_INFO_ENABLED
const auto& extraInfoMap = ScopedLogExtraInfo::GetExtraInfoMap();
if (const auto contextIt =
extraInfoMap.find(ScopedLogExtraInfo::kTagContextTainted);
contextIt != extraInfoMap.cend()) {
contextTaintedPtr = 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
auto extraInfosStringTainted = Tainted<nsAutoCString>([&] {
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) {
const auto& valueTainted = *item.second;
extraInfosString.Append(
", "_ns + nsDependentCString(item.first) + " "_ns +
MOZ_NO_VALIDATE(valueTainted,
"It's okay to append any `extraInfoMap` value to "
"`extraInfosString`."));
}
# endif
return extraInfosString;
}());
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(),
(MOZ_NO_VALIDATE(extraInfosStringTainted,
"It's okay to check if `extraInfosString` is empty.")
.IsEmpty()
? nsPromiseFlatCString(aExpr)
: static_cast<const nsCString&>(nsAutoCString(
aExpr + MOZ_NO_VALIDATE(extraInfosStringTainted,
"It's okay to log `extraInfosString` "
"to stdout/console."))))
.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 (contextTaintedPtr) {
nsCOMPtr<nsIConsoleService> console =
do_GetService(NS_CONSOLESERVICE_CONTRACTID);
if (console) {
NS_ConvertUTF8toUTF16 message(
"QM_TRY failure ("_ns + severityString + ")"_ns + ": '"_ns + aExpr +
MOZ_NO_VALIDATE(
extraInfosStringTainted,
"It's okay to log `extraInfosString` to the browser console.") +
"', 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
// The context tag is special because it's used to enable logging to
// telemetry (besides carrying information). Other tags (like query) don't
// enable logging to telemetry.
if (contextTaintedPtr) {
const auto& contextTainted = *contextTaintedPtr;
// Do NOT CHANGE this if you don't know what you're doing.
// `extraInfoString` is not included in the telemetry event on purpose
// since it can contain sensitive information.
// For now, we don't include aExpr in the telemetry event. It might help to
// match locations across versions, but they might be large.
// New extra entries (with potentially sensitive content) can't be easily
// (accidentally) added because they would have to be added to Events.yaml
// under "dom.quota.try" which would require a data review.
auto extra = Some([&] {
auto res = CopyableTArray<EventExtraEntry>{};
res.SetCapacity(9);
res.AppendElement(EventExtraEntry{
"context"_ns,
MOZ_NO_VALIDATE(
contextTainted,
"Context has been data-reviewed for telemetry transmission.")});
# 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