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/. */
7 #ifndef mozilla_dom_quota_quotacommon_h__
8 #define mozilla_dom_quota_quotacommon_h__
10 #include "mozilla/dom/quota/Config.h"
15 #include <type_traits>
17 #include "mozIStorageStatement.h"
18 #include "mozilla/Assertions.h"
19 #include "mozilla/Atomics.h"
20 #include "mozilla/Attributes.h"
21 #include "mozilla/Likely.h"
22 #include "mozilla/MacroArgs.h"
23 #include "mozilla/Maybe.h"
24 #include "mozilla/ResultExtensions.h"
25 #include "mozilla/StaticString.h"
26 #include "mozilla/Try.h"
27 #if defined(QM_LOG_ERROR_ENABLED) && defined(QM_ERROR_STACKS_ENABLED)
28 # include "mozilla/Variant.h"
30 #include "mozilla/dom/QMResult.h"
31 #include "mozilla/dom/quota/FirstInitializationAttemptsImpl.h"
32 #include "mozilla/dom/quota/RemoveParen.h"
33 #include "mozilla/dom/quota/ScopedLogExtraInfo.h"
34 #include "mozilla/ipc/ProtocolUtils.h"
38 #include "nsIDirectoryEnumerator.h"
39 #include "nsIEventTarget.h"
41 #include "nsLiteralString.h"
42 #include "nsPrintfCString.h"
43 #include "nsReadableUtils.h"
46 #include "nsTLiteralString.h"
53 #define MOZ_ARGS_AFTER_3(a1, a2, a3, ...) __VA_ARGS__
55 #define MOZ_ADD_ARGS2(...) , ##__VA_ARGS__
56 #define MOZ_ADD_ARGS(...) MOZ_ADD_ARGS2(__VA_ARGS__)
58 // Proper use of unique variable names can be tricky (especially if nesting of
59 // the final macro is required).
60 // See https://lifecs.likai.org/2016/07/c-preprocessor-hygienic-macros.html
61 #define MOZ_UNIQUE_VAR(base) MOZ_CONCAT(base, __COUNTER__)
63 // See https://florianjw.de/en/passing_overloaded_functions.html
64 // TODO: Add a test for this macro.
65 #define MOZ_SELECT_OVERLOAD(func) \
66 [](auto&&... aArgs) -> decltype(auto) { \
67 return func(std::forward<decltype(aArgs)>(aArgs)...); \
70 #define DSSTORE_FILE_NAME ".DS_Store"
71 #define DESKTOP_FILE_NAME ".desktop"
72 #define DESKTOP_INI_FILE_NAME "desktop.ini"
73 #define THUMBS_DB_FILE_NAME "thumbs.db"
75 #define QM_WARNING(...) \
77 nsPrintfCString str(__VA_ARGS__); \
78 mozilla::dom::quota::ReportInternalError(__FILE__, __LINE__, str.get()); \
79 NS_WARNING(str.get()); \
82 #define QM_LOG_TEST() MOZ_LOG_TEST(GetQuotaManagerLogger(), LogLevel::Info)
83 #define QM_LOG(_args) MOZ_LOG(GetQuotaManagerLogger(), LogLevel::Info, _args)
86 # define UNKNOWN_FILE_WARNING(_leafName) \
87 NS_WARNING(nsPrintfCString( \
88 "Something (%s) in the directory that doesn't belong!", \
89 NS_ConvertUTF16toUTF8(_leafName).get()) \
92 # define UNKNOWN_FILE_WARNING(_leafName) (void)(_leafName);
95 // This macro should be used in directory traversals for files or directories
96 // that are unknown for given directory traversal. It should only be called
97 // after all known (directory traversal specific) files or directories have
98 // been checked and handled.
99 // XXX Consider renaming the macro to QM_LOG_UNKNOWN_DIR_ENTRY.
101 # define WARN_IF_FILE_IS_UNKNOWN(_file) \
102 mozilla::dom::quota::WarnIfFileIsUnknown(_file, __FILE__, __LINE__)
104 # define WARN_IF_FILE_IS_UNKNOWN(_file) Result<bool, nsresult>(false)
108 * There are multiple ways to handle unrecoverable conditions (note that the
109 * patterns are put in reverse chronological order and only the first pattern
110 * QM_TRY/QM_TRY_UNWRAP/QM_TRY_INSPECT/QM_TRY_RETURN/QM_FAIL should be used in
113 * 1. Using QM_TRY/QM_TRY_UNWRAP/QM_TRY_INSPECT/QM_TRY_RETURN/QM_FAIL macros
114 * (Quota manager specific, defined below)
118 * nsresult MyFunc1(nsIFile& aFile) {
120 * QM_TRY(aFile.Exists(&exists));
121 * QM_TRY(OkIf(exists), NS_ERROR_FAILURE);
123 * // The file exists, and data could be read from it here.
128 * nsresult MyFunc2(nsIFile& aFile) {
130 * QM_TRY(aFile.Exists(&exists), NS_ERROR_UNEXPECTED);
131 * QM_TRY(OkIf(exists), NS_ERROR_UNEXPECTED);
133 * // The file exists, and data could be read from it here.
138 * void MyFunc3(nsIFile& aFile) {
140 * QM_TRY(aFile.Exists(&exists), QM_VOID);
141 * QM_TRY(OkIf(exists), QM_VOID);
143 * // The file exists, and data could be read from it here.
146 * nsresult MyFunc4(nsIFile& aFile) {
148 * QM_TRY(storageFile->Exists(&exists), QM_PROPAGATE,
149 * []() { NS_WARNING("The Exists call failed!"); });
150 * QM_TRY(OkIf(exists), NS_ERROR_FAILURE,
151 * []() { NS_WARNING("The file doesn't exist!"); });
153 * // The file exists, and data could be read from it here.
158 * nsresult MyFunc5(nsIFile& aFile) {
160 * QM_TRY(aFile.Exists(&exists));
162 * // The file exists, and data could be read from it here.
164 * QM_FAIL(NS_ERROR_FAILURE);
170 * nsresult MyFunc6(nsIFile& aFile) {
172 * QM_TRY(aFile.Exists(&exists));
174 * // The file exists, and data could be read from it here.
176 * QM_FAIL(NS_ERROR_FAILURE,
177 * []() { NS_WARNING("The file doesn't exist!"); });
183 * 2. Using MOZ_TRY/MOZ_TRY_VAR macros
187 * nsresult MyFunc1(nsIFile& aFile) {
188 * // MOZ_TRY can't return a custom return value
193 * nsresult MyFunc2(nsIFile& aFile) {
194 * // MOZ_TRY can't return a custom return value
199 * void MyFunc3(nsIFile& aFile) {
200 * // MOZ_TRY can't return a custom return value, "void" in this case
203 * nsresult MyFunc4(nsIFile& aFile) {
204 * // MOZ_TRY can't return a custom return value and run an additional
205 * // cleanup function
210 * nsresult MyFunc5(nsIFile& aFile) {
211 * // There's no MOZ_FAIL, MOZ_TRY can't return a custom return value
216 * nsresult MyFunc6(nsIFile& aFile) {
217 * // There's no MOZ_FAIL, MOZ_TRY can't return a custom return value and run
218 * // an additional cleanup function
223 * 3. Using NS_WARN_IF and NS_WARNING macro with own control flow handling
227 * nsresult MyFunc1(nsIFile& aFile) {
229 * nsresult rv = aFile.Exists(&exists);
230 * if (NS_WARN_IF(NS_FAILED(rv)) {
233 * if (NS_WARN_IF(!exists) {
234 * return NS_ERROR_FAILURE;
237 * // The file exists, and data could be read from it here.
242 * nsresult MyFunc2(nsIFile& aFile) {
244 * nsresult rv = aFile.Exists(&exists);
245 * if (NS_WARN_IF(NS_FAILED(rv)) {
246 * return NS_ERROR_UNEXPECTED;
248 * if (NS_WARN_IF(!exists) {
249 * return NS_ERROR_UNEXPECTED;
252 * // The file exists, and data could be read from it here.
257 * void MyFunc3(nsIFile& aFile) {
259 * nsresult rv = aFile.Exists(&exists);
260 * if (NS_WARN_IF(NS_FAILED(rv)) {
263 * if (NS_WARN_IF(!exists) {
267 * // The file exists, and data could be read from it here.
270 * nsresult MyFunc4(nsIFile& aFile) {
272 * nsresult rv = aFile.Exists(&exists);
273 * if (NS_WARN_IF(NS_FAILED(rv)) {
274 * NS_WARNING("The Exists call failed!");
277 * if (NS_WARN_IF(!exists) {
278 * NS_WARNING("The file doesn't exist!");
279 * return NS_ERROR_FAILURE;
282 * // The file exists, and data could be read from it here.
287 * nsresult MyFunc5(nsIFile& aFile) {
289 * nsresult rv = aFile.Exists(&exists);
290 * if (NS_WARN_IF(NS_FAILED(rv)) {
294 * // The file exists, and data could be read from it here.
296 * return NS_ERROR_FAILURE;
302 * nsresult MyFunc6(nsIFile& aFile) {
304 * nsresult rv = aFile.Exists(&exists);
305 * if (NS_WARN_IF(NS_FAILED(rv)) {
309 * // The file exists, and data could be read from it here.
311 * NS_WARNING("The file doesn't exist!");
312 * return NS_ERROR_FAILURE;
318 * 4. Using NS_ENSURE_* macros
321 * - NS_ENSURE_SUCCESS
322 * - NS_ENSURE_SUCCESS_VOID
324 * - NS_ENSURE_TRUE_VOID
328 * nsresult MyFunc1(nsIFile& aFile) {
330 * nsresult rv = aFile.Exists(&exists);
331 * NS_ENSURE_SUCCESS(rv, rv);
332 * NS_ENSURE_TRUE(exists, NS_ERROR_FAILURE);
334 * // The file exists, and data could be read from it here.
339 * nsresult MyFunc2(nsIFile& aFile) {
341 * nsresult rv = aFile.Exists(&exists);
342 * NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
343 * NS_ENSURE_TRUE(exists, NS_ERROR_UNEXPECTED);
345 * // The file exists, and data could be read from it here.
350 * void MyFunc3(nsIFile& aFile) {
352 * nsresult rv = aFile.Exists(&exists);
353 * NS_ENSURE_SUCCESS_VOID(rv);
354 * NS_ENSURE_TRUE_VOID(exists);
356 * // The file exists, and data could be read from it here.
359 * nsresult MyFunc4(nsIFile& aFile) {
360 * // NS_ENSURE_SUCCESS/NS_ENSURE_TRUE can't run an additional cleanup
366 * nsresult MyFunc5(nsIFile& aFile) {
368 * nsresult rv = aFile.Exists(&exists);
369 * NS_ENSURE_SUCCESS(rv, rv);
371 * // The file exists, and data could be read from it here.
373 * NS_ENSURE_TRUE(false, NS_ERROR_FAILURE);
379 * nsresult MyFunc6(nsIFile& aFile) {
380 * // NS_ENSURE_TRUE can't run an additional cleanup function
385 * QM_TRY/QM_TRY_UNWRAP/QM_TRY_INSPECT is like MOZ_TRY/MOZ_TRY_VAR but if an
386 * error occurs it additionally calls a generic function HandleError to handle
387 * the error and it can be used to return custom return values as well and even
388 * call an additional cleanup function.
389 * HandleError currently only warns in debug builds, it will report to the
390 * browser console and telemetry in the future.
391 * The other advantage of QM_TRY/QM_TRY_UNWRAP/QM_TRY_INSPECT is that a local
392 * nsresult is not needed at all in all cases, all calls can be wrapped
393 * directly. If an error occurs, the warning contains a concrete call instead
394 * of the rv local variable. For example:
396 * 1. WARNING: NS_ENSURE_SUCCESS(rv, rv) failed with result 0x80004005
397 * (NS_ERROR_FAILURE): file XYZ, line N
399 * 2. WARNING: 'NS_FAILED(rv)', file XYZ, line N
401 * 3. Nothing (MOZ_TRY doesn't warn)
403 * 4. WARNING: Error: 'aFile.Exists(&exists)', file XYZ, line N
405 * QM_TRY_RETURN is a supplementary macro for cases when the result's success
406 * value can be directly returned (instead of assigning to a variable as in the
407 * QM_TRY_UNWRAP/QM_TRY_INSPECT case).
409 * QM_FAIL is a supplementary macro for cases when an error needs to be
410 * returned without evaluating an expression. It's possible to write
411 * QM_TRY(OkIf(false), NS_ERROR_FAILURE), but QM_FAIL(NS_ERROR_FAILURE) looks
412 * more straightforward.
414 * It's highly recommended to use
415 * QM_TRY/QM_TRY_UNWRAP/QM_TRY_INSPECT/QM_TRY_RETURN/QM_FAIL in new code for
416 * quota manager and quota clients. Existing code should be incrementally
417 * converted as needed.
419 * QM_TRY_VOID/QM_TRY_UNWRAP_VOID/QM_TRY_INSPECT_VOID/QM_FAIL_VOID is not
420 * defined on purpose since it's possible to use
421 * QM_TRY/QM_TRY_UNWRAP/QM_TRY_INSPECT/QM_FAIL even in void functions.
422 * However, QM_TRY(Task(), ) would look odd so it's recommended to use a dummy
423 * define QM_VOID that evaluates to nothing instead: QM_TRY(Task(), QM_VOID)
425 * Custom return values can be static or dynamically generated using functions
426 * with one of these signatures:
427 * auto(const char* aFunc, const char* aExpr);
428 * auto(const char* aFunc, const T& aRv);
429 * auto(const T& aRc);
434 #define QM_PROPAGATE Err(tryTempError)
436 namespace mozilla::dom::quota::detail
{
438 struct IpcFailCustomRetVal
{
439 explicit IpcFailCustomRetVal(
440 mozilla::NotNull
<mozilla::ipc::IProtocol
*> aActor
)
443 template <size_t NFunc
, size_t NExpr
>
444 auto operator()(const char (&aFunc
)[NFunc
],
445 const char (&aExpr
)[NExpr
]) const {
446 return mozilla::Err(mozilla::ipc::IPCResult::Fail(mActor
, aFunc
, aExpr
));
449 mozilla::NotNull
<mozilla::ipc::IProtocol
*> mActor
;
452 } // namespace mozilla::dom::quota::detail
454 #define QM_IPC_FAIL(actor) \
455 mozilla::dom::quota::detail::IpcFailCustomRetVal(mozilla::WrapNotNull(actor))
458 # define QM_ASSERT_UNREACHABLE \
459 [](const char*, const char*) -> ::mozilla::GenericErrorResult<nsresult> { \
460 MOZ_CRASH("Should never be reached."); \
463 # define QM_ASSERT_UNREACHABLE_VOID \
464 [](const char*, const char*) { MOZ_CRASH("Should never be reached."); }
467 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
468 # define QM_DIAGNOSTIC_ASSERT_UNREACHABLE \
469 [](const char*, const char*) -> ::mozilla::GenericErrorResult<nsresult> { \
470 MOZ_CRASH("Should never be reached."); \
473 # define QM_DIAGNOSTIC_ASSERT_UNREACHABLE_VOID \
474 [](const char*, const char*) { MOZ_CRASH("Should never be reached."); }
477 #define QM_NO_CLEANUP [](const auto&) {}
479 // QM_MISSING_ARGS and QM_HANDLE_ERROR macros are implementation details of
480 // QM_TRY/QM_TRY_UNWRAP/QM_TRY_INSPECT/QM_FAIL and shouldn't be used directly.
482 #define QM_MISSING_ARGS(...) \
484 static_assert(false, "Did you forget arguments?"); \
488 # define QM_HANDLE_ERROR(expr, error, severity) \
489 HandleError(#expr, error, __FILE__, __LINE__, severity)
491 # define QM_HANDLE_ERROR(expr, error, severity) \
492 HandleError("Unavailable", error, __FILE__, __LINE__, severity)
496 # define QM_HANDLE_ERROR_RETURN_NOTHING(expr, error, severity) \
497 HandleErrorReturnNothing(#expr, error, __FILE__, __LINE__, severity)
499 # define QM_HANDLE_ERROR_RETURN_NOTHING(expr, error, severity) \
500 HandleErrorReturnNothing("Unavailable", error, __FILE__, __LINE__, severity)
504 # define QM_HANDLE_ERROR_WITH_CLEANUP_RETURN_NOTHING(expr, error, severity, \
506 HandleErrorWithCleanupReturnNothing(#expr, error, __FILE__, __LINE__, \
509 # define QM_HANDLE_ERROR_WITH_CLEANUP_RETURN_NOTHING(expr, error, severity, \
511 HandleErrorWithCleanupReturnNothing("Unavailable", error, __FILE__, \
512 __LINE__, severity, cleanup)
515 // Handles the case when QM_VOID is passed as a custom return value.
516 #define QM_HANDLE_CUSTOM_RET_VAL_HELPER0(func, expr, error)
518 #define QM_HANDLE_CUSTOM_RET_VAL_HELPER1(func, expr, error, customRetVal) \
519 mozilla::dom::quota::HandleCustomRetVal(func, #expr, error, customRetVal)
521 #define QM_HANDLE_CUSTOM_RET_VAL_GLUE(a, b) a b
523 #define QM_HANDLE_CUSTOM_RET_VAL(...) \
524 QM_HANDLE_CUSTOM_RET_VAL_GLUE( \
525 MOZ_PASTE_PREFIX_AND_ARG_COUNT(QM_HANDLE_CUSTOM_RET_VAL_HELPER, \
526 MOZ_ARGS_AFTER_3(__VA_ARGS__)), \
527 (MOZ_ARG_1(__VA_ARGS__), MOZ_ARG_2(__VA_ARGS__), \
528 MOZ_ARG_3(__VA_ARGS__) MOZ_ADD_ARGS(MOZ_ARGS_AFTER_3(__VA_ARGS__))))
530 // QM_TRY_PROPAGATE_ERR, QM_TRY_CUSTOM_RET_VAL,
531 // QM_TRY_CUSTOM_RET_VAL_WITH_CLEANUP and QM_TRY_GLUE macros are implementation
532 // details of QM_TRY and shouldn't be used directly.
534 // Handles the two arguments case when the error is propagated.
535 #define QM_TRY_PROPAGATE_ERR(tryResult, expr) \
536 auto tryResult = (expr); \
537 static_assert(std::is_empty_v<typename decltype(tryResult)::ok_type>); \
538 if (MOZ_UNLIKELY(tryResult.isErr())) { \
539 mozilla::dom::quota::QM_HANDLE_ERROR( \
540 expr, tryResult.inspectErr(), mozilla::dom::quota::Severity::Error); \
541 return tryResult.propagateErr(); \
544 // Handles the three arguments case when a custom return value needs to be
546 #define QM_TRY_CUSTOM_RET_VAL(tryResult, expr, customRetVal) \
547 auto tryResult = (expr); \
548 static_assert(std::is_empty_v<typename decltype(tryResult)::ok_type>); \
549 if (MOZ_UNLIKELY(tryResult.isErr())) { \
550 auto tryTempError MOZ_MAYBE_UNUSED = tryResult.unwrapErr(); \
551 mozilla::dom::quota::QM_HANDLE_ERROR( \
552 expr, tryTempError, mozilla::dom::quota::Severity::Error); \
553 constexpr const auto& func MOZ_MAYBE_UNUSED = __func__; \
554 return QM_HANDLE_CUSTOM_RET_VAL(func, expr, tryTempError, customRetVal); \
557 // Handles the four arguments case when a cleanup function needs to be called
558 // before a custom return value is returned
559 #define QM_TRY_CUSTOM_RET_VAL_WITH_CLEANUP(tryResult, expr, customRetVal, \
561 auto tryResult = (expr); \
562 static_assert(std::is_empty_v<typename decltype(tryResult)::ok_type>); \
563 if (MOZ_UNLIKELY(tryResult.isErr())) { \
564 auto tryTempError = tryResult.unwrapErr(); \
565 mozilla::dom::quota::QM_HANDLE_ERROR( \
566 expr, tryTempError, mozilla::dom::quota::Severity::Error); \
567 cleanup(tryTempError); \
568 constexpr const auto& func MOZ_MAYBE_UNUSED = __func__; \
569 return QM_HANDLE_CUSTOM_RET_VAL(func, expr, tryTempError, customRetVal); \
572 #define QM_TRY_CUSTOM_RET_VAL_WITH_CLEANUP_AND_PREDICATE( \
573 tryResult, expr, customRetVal, cleanup, predicate) \
574 auto tryResult = (expr); \
575 static_assert(std::is_empty_v<typename decltype(tryResult)::ok_type>); \
576 if (MOZ_UNLIKELY(tryResult.isErr())) { \
577 auto tryTempError = tryResult.unwrapErr(); \
579 mozilla::dom::quota::QM_HANDLE_ERROR( \
580 expr, tryTempError, mozilla::dom::quota::Severity::Error); \
582 cleanup(tryTempError); \
583 constexpr const auto& func MOZ_MAYBE_UNUSED = __func__; \
584 return QM_HANDLE_CUSTOM_RET_VAL(func, expr, tryTempError, customRetVal); \
587 // Chooses the final implementation macro for given argument count.
588 // This could use MOZ_PASTE_PREFIX_AND_ARG_COUNT, but explicit named suffxes
589 // read slightly better than plain numbers.
591 // https://stackoverflow.com/questions/3046889/optional-parameters-with-c-macros
592 #define QM_TRY_META(...) \
593 {MOZ_ARG_7(, ##__VA_ARGS__, \
594 QM_TRY_CUSTOM_RET_VAL_WITH_CLEANUP_AND_PREDICATE(__VA_ARGS__), \
595 QM_TRY_CUSTOM_RET_VAL_WITH_CLEANUP(__VA_ARGS__), \
596 QM_TRY_CUSTOM_RET_VAL(__VA_ARGS__), \
597 QM_TRY_PROPAGATE_ERR(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__), \
598 QM_MISSING_ARGS(__VA_ARGS__))}
600 // Generates unique variable name. This extra internal macro (along with
601 // __COUNTER__) allows nesting of the final macro.
602 #define QM_TRY_GLUE(...) QM_TRY_META(MOZ_UNIQUE_VAR(tryResult), ##__VA_ARGS__)
605 * QM_TRY(expr[, customRetVal, cleanup, predicate]) is the C++ equivalent of
606 * Rust's `try!(expr);`. First, it evaluates expr, which must produce a Result
607 * value with empty ok_type. On Success, it does nothing else. On error, it
608 * calls HandleError (conditionally if the fourth argument was passed) and an
609 * additional cleanup function (if the third argument was passed) and finally
610 * returns an error Result from the enclosing function or a custom return value
611 * (if the second argument was passed).
613 #define QM_TRY(...) QM_TRY_GLUE(__VA_ARGS__)
615 // QM_TRY_ASSIGN_PROPAGATE_ERR, QM_TRY_ASSIGN_CUSTOM_RET_VAL,
616 // QM_TRY_ASSIGN_CUSTOM_RET_VAL_WITH_CLEANUP and QM_TRY_ASSIGN_GLUE macros are
617 // implementation details of QM_TRY_UNWRAP/QM_TRY_INSPECT and shouldn't be used
620 // Handles the four arguments case when the error is propagated.
621 #define QM_TRY_ASSIGN_PROPAGATE_ERR(tryResult, accessFunction, target, expr) \
622 auto tryResult = (expr); \
623 if (MOZ_UNLIKELY(tryResult.isErr())) { \
624 mozilla::dom::quota::QM_HANDLE_ERROR( \
625 expr, tryResult.inspectErr(), mozilla::dom::quota::Severity::Error); \
626 return tryResult.propagateErr(); \
628 MOZ_REMOVE_PAREN(target) = tryResult.accessFunction();
630 // Handles the five arguments case when a custom return value needs to be
632 #define QM_TRY_ASSIGN_CUSTOM_RET_VAL(tryResult, accessFunction, target, expr, \
634 auto tryResult = (expr); \
635 if (MOZ_UNLIKELY(tryResult.isErr())) { \
636 auto tryTempError MOZ_MAYBE_UNUSED = tryResult.unwrapErr(); \
637 mozilla::dom::quota::QM_HANDLE_ERROR( \
638 expr, tryTempError, mozilla::dom::quota::Severity::Error); \
639 constexpr const auto& func MOZ_MAYBE_UNUSED = __func__; \
640 return QM_HANDLE_CUSTOM_RET_VAL(func, expr, tryTempError, customRetVal); \
642 MOZ_REMOVE_PAREN(target) = tryResult.accessFunction();
644 // Handles the six arguments case when a cleanup function needs to be called
645 // before a custom return value is returned
646 #define QM_TRY_ASSIGN_CUSTOM_RET_VAL_WITH_CLEANUP( \
647 tryResult, accessFunction, target, expr, customRetVal, cleanup) \
648 auto tryResult = (expr); \
649 if (MOZ_UNLIKELY(tryResult.isErr())) { \
650 auto tryTempError = tryResult.unwrapErr(); \
651 mozilla::dom::quota::QM_HANDLE_ERROR( \
652 expr, tryTempError, mozilla::dom::quota::Severity::Error); \
653 cleanup(tryTempError); \
654 constexpr const auto& func MOZ_MAYBE_UNUSED = __func__; \
655 return QM_HANDLE_CUSTOM_RET_VAL(func, expr, tryTempError, customRetVal); \
657 MOZ_REMOVE_PAREN(target) = tryResult.accessFunction();
659 // Chooses the final implementation macro for given argument count.
660 // See also the comment for QM_TRY_META.
661 #define QM_TRY_ASSIGN_META(...) \
662 MOZ_ARG_8(, ##__VA_ARGS__, \
663 QM_TRY_ASSIGN_CUSTOM_RET_VAL_WITH_CLEANUP(__VA_ARGS__), \
664 QM_TRY_ASSIGN_CUSTOM_RET_VAL(__VA_ARGS__), \
665 QM_TRY_ASSIGN_PROPAGATE_ERR(__VA_ARGS__), \
666 QM_MISSING_ARGS(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__), \
667 QM_MISSING_ARGS(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__))
669 // Generates unique variable name. This extra internal macro (along with
670 // __COUNTER__) allows nesting of the final macro.
671 #define QM_TRY_ASSIGN_GLUE(accessFunction, ...) \
672 QM_TRY_ASSIGN_META(MOZ_UNIQUE_VAR(tryResult), accessFunction, ##__VA_ARGS__)
675 * QM_TRY_UNWRAP(target, expr[, customRetVal, cleanup]) is the C++ equivalent of
676 * Rust's `target = try!(expr);`. First, it evaluates expr, which must produce
677 * a Result value. On success, the result's success value is unwrapped and
678 * assigned to target. On error, it calls HandleError and an additional cleanup
679 * function (if the fourth argument was passed) and finally returns the error
680 * result or a custom return value (if the third argument was passed). |target|
683 #define QM_TRY_UNWRAP(...) QM_TRY_ASSIGN_GLUE(unwrap, __VA_ARGS__)
686 * QM_TRY_INSPECT is similar to QM_TRY_UNWRAP, but it does not unwrap a success
687 * value, but inspects it and binds it to the target. It can therefore only be
688 * used when the target declares a const&. In general,
690 * QM_TRY_INSPECT(const auto &target, DoSomething())
692 * should be preferred over
694 * QM_TRY_UNWRAP(const auto target, DoSomething())
696 * as it avoids unnecessary moves/copies.
698 #define QM_TRY_INSPECT(...) QM_TRY_ASSIGN_GLUE(inspect, __VA_ARGS__)
700 // QM_TRY_RETURN_PROPAGATE_ERR, QM_TRY_RETURN_CUSTOM_RET_VAL,
701 // QM_TRY_RETURN_CUSTOM_RET_VAL_WITH_CLEANUP and QM_TRY_RETURN_GLUE macros are
702 // implementation details of QM_TRY_RETURN and shouldn't be used directly.
704 // Handles the two arguments case when the error is (also) propagated.
705 // Note that this deliberately uses a single return statement without going
706 // through unwrap/unwrapErr/propagateErr, so that this does not prevent NRVO or
707 // tail call optimizations when possible.
708 #define QM_TRY_RETURN_PROPAGATE_ERR(tryResult, expr) \
709 auto tryResult = (expr); \
710 if (MOZ_UNLIKELY(tryResult.isErr())) { \
711 mozilla::dom::quota::QM_HANDLE_ERROR( \
712 expr, tryResult.inspectErr(), mozilla::dom::quota::Severity::Error); \
716 // Handles the three arguments case when a custom return value needs to be
718 #define QM_TRY_RETURN_CUSTOM_RET_VAL(tryResult, expr, customRetVal) \
719 auto tryResult = (expr); \
720 if (MOZ_UNLIKELY(tryResult.isErr())) { \
721 auto tryTempError MOZ_MAYBE_UNUSED = tryResult.unwrapErr(); \
722 mozilla::dom::quota::QM_HANDLE_ERROR( \
723 expr, tryResult.inspectErr(), mozilla::dom::quota::Severity::Error); \
724 constexpr const auto& func MOZ_MAYBE_UNUSED = __func__; \
725 return QM_HANDLE_CUSTOM_RET_VAL(func, expr, tryTempError, customRetVal); \
727 return tryResult.unwrap();
729 // Handles the four arguments case when a cleanup function needs to be called
730 // before a custom return value is returned
731 #define QM_TRY_RETURN_CUSTOM_RET_VAL_WITH_CLEANUP(tryResult, expr, \
732 customRetVal, cleanup) \
733 auto tryResult = (expr); \
734 if (MOZ_UNLIKELY(tryResult.isErr())) { \
735 auto tryTempError = tryResult.unwrapErr(); \
736 mozilla::dom::quota::QM_HANDLE_ERROR( \
737 expr, tryTempError, mozilla::dom::quota::Severity::Error); \
738 cleanup(tryTempError); \
739 constexpr const auto& func MOZ_MAYBE_UNUSED = __func__; \
740 return QM_HANDLE_CUSTOM_RET_VAL(func, expr, tryTempError, customRetVal); \
742 return tryResult.unwrap();
744 // Chooses the final implementation macro for given argument count.
745 // See also the comment for QM_TRY_META.
746 #define QM_TRY_RETURN_META(...) \
747 {MOZ_ARG_6(, ##__VA_ARGS__, \
748 QM_TRY_RETURN_CUSTOM_RET_VAL_WITH_CLEANUP(__VA_ARGS__), \
749 QM_TRY_RETURN_CUSTOM_RET_VAL(__VA_ARGS__), \
750 QM_TRY_RETURN_PROPAGATE_ERR(__VA_ARGS__), \
751 QM_MISSING_ARGS(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__))}
753 // Generates unique variable name. This extra internal macro (along with
754 // __COUNTER__) allows nesting of the final macro.
755 #define QM_TRY_RETURN_GLUE(...) \
756 QM_TRY_RETURN_META(MOZ_UNIQUE_VAR(tryResult), ##__VA_ARGS__)
759 * QM_TRY_RETURN(expr[, customRetVal, cleanup]) evaluates expr, which must
760 * produce a Result value. On success, the result's success value is returned.
761 * On error, it calls HandleError and an additional cleanup function (if the
762 * third argument was passed) and finally returns the error result or a custom
763 * return value (if the second argument was passed).
765 #define QM_TRY_RETURN(...) QM_TRY_RETURN_GLUE(__VA_ARGS__)
767 // QM_FAIL_RET_VAL and QM_FAIL_RET_VAL_WITH_CLEANUP macros are implementation
768 // details of QM_FAIL and shouldn't be used directly.
770 // Handles the one argument case when just an error is returned
771 #define QM_FAIL_RET_VAL(retVal) \
772 mozilla::dom::quota::QM_HANDLE_ERROR(Failure, 0, \
773 mozilla::dom::quota::Severity::Error); \
776 // Handles the two arguments case when a cleanup function needs to be called
777 // before a return value is returned
778 #define QM_FAIL_RET_VAL_WITH_CLEANUP(retVal, cleanup) \
779 mozilla::dom::quota::QM_HANDLE_ERROR(Failure, 0, \
780 mozilla::dom::quota::Severity::Error); \
784 // Chooses the final implementation macro for given argument count.
785 // See also the comment for QM_TRY_META.
786 #define QM_FAIL_META(...) \
787 MOZ_ARG_4(, ##__VA_ARGS__, QM_FAIL_RET_VAL_WITH_CLEANUP(__VA_ARGS__), \
788 QM_FAIL_RET_VAL(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__))
790 // This extra internal macro allows nesting of the final macro.
791 #define QM_FAIL_GLUE(...) QM_FAIL_META(__VA_ARGS__)
794 * QM_FAIL(retVal[, cleanup]) calls HandleError and an additional cleanup
795 * function (if the second argument was passed) and returns a return value.
797 #define QM_FAIL(...) QM_FAIL_GLUE(__VA_ARGS__)
799 // QM_REPORTONLY_TRY, QM_REPORTONLY_TRY_WITH_CLEANUP, QM_REPORTONLY_TRY_GLUE
800 // macros are implementation details of QM_WARNONLY_TRY/QM_INFOONLY_TRY and
801 // shouldn't be used directly.
803 // Handles the three arguments case when only a warning/info is reported.
804 #define QM_REPORTONLY_TRY(tryResult, severity, expr) \
805 auto tryResult = (expr); \
806 static_assert(std::is_empty_v<typename decltype(tryResult)::ok_type>); \
807 if (MOZ_UNLIKELY(tryResult.isErr())) { \
808 mozilla::dom::quota::QM_HANDLE_ERROR( \
809 expr, tryResult.unwrapErr(), mozilla::dom::quota::Severity::severity); \
812 // Handles the four arguments case when a cleanup function needs to be called
813 #define QM_REPORTONLY_TRY_WITH_CLEANUP(tryResult, severity, expr, cleanup) \
814 auto tryResult = (expr); \
815 static_assert(std::is_empty_v<typename decltype(tryResult)::ok_type>); \
816 if (MOZ_UNLIKELY(tryResult.isErr())) { \
817 auto tryTempError = tryResult.unwrapErr(); \
818 mozilla::dom::quota::QM_HANDLE_ERROR( \
819 expr, tryTempError, mozilla::dom::quota::Severity::severity); \
820 cleanup(tryTempError); \
823 // Chooses the final implementation macro for given argument count.
824 // See also the comment for QM_TRY_META.
825 #define QM_REPORTONLY_TRY_META(...) \
826 {MOZ_ARG_6(, ##__VA_ARGS__, QM_REPORTONLY_TRY_WITH_CLEANUP(__VA_ARGS__), \
827 QM_REPORTONLY_TRY(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__), \
828 QM_MISSING_ARGS(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__))}
830 // Generates unique variable name. This extra internal macro (along with
831 // __COUNTER__) allows nesting of the final macro.
832 #define QM_REPORTONLY_TRY_GLUE(severity, ...) \
833 QM_REPORTONLY_TRY_META(MOZ_UNIQUE_VAR(tryResult), severity, ##__VA_ARGS__)
836 * QM_WARNONLY_TRY(expr[, cleanup]) evaluates expr, which must produce a
837 * Result value with empty ok_type. On Success, it does nothing else. On error,
838 * it calls HandleError and an additional cleanup function (if the second
839 * argument was passed). This macro never returns and failures are always
840 * reported using a lower level of severity relative to failures reported by
841 * QM_TRY. The macro is intended for failures that should not be propagated.
843 #define QM_WARNONLY_TRY(...) QM_REPORTONLY_TRY_GLUE(Warning, __VA_ARGS__)
846 * QM_INFOONLY_TRY is like QM_WARNONLY_TRY. The only difference is that
847 * failures are reported using a lower level of severity relative to failures
848 * reported by QM_WARNONLY_TRY.
850 #define QM_INFOONLY_TRY(...) QM_REPORTONLY_TRY_GLUE(Info, __VA_ARGS__)
852 // QM_REPORTONLY_TRY_ASSIGN, QM_REPORTONLY_TRY_ASSIGN_WITH_CLEANUP,
853 // QM_REPORTONLY_TRY_ASSIGN_GLUE macros are implementation details of
854 // QM_WARNONLY_TRY_UNWRAP/QM_INFOONLY_TRY_UNWRAP and shouldn't be used
857 // Handles the four arguments case when only a warning/info is reported.
858 #define QM_REPORTONLY_TRY_ASSIGN(tryResult, severity, target, expr) \
859 auto tryResult = (expr); \
860 MOZ_REMOVE_PAREN(target) = \
861 MOZ_LIKELY(tryResult.isOk()) \
862 ? Some(tryResult.unwrap()) \
863 : mozilla::dom::quota::QM_HANDLE_ERROR_RETURN_NOTHING( \
864 expr, tryResult.unwrapErr(), \
865 mozilla::dom::quota::Severity::severity);
867 // Handles the five arguments case when a cleanup function needs to be called
868 #define QM_REPORTONLY_TRY_ASSIGN_WITH_CLEANUP(tryResult, severity, target, \
870 auto tryResult = (expr); \
871 MOZ_REMOVE_PAREN(target) = \
872 MOZ_LIKELY(tryResult.isOk()) \
873 ? Some(tryResult.unwrap()) \
874 : mozilla::dom::quota::QM_HANDLE_ERROR_WITH_CLEANUP_RETURN_NOTHING( \
875 expr, tryResult.unwrapErr(), \
876 mozilla::dom::quota::Severity::severity, cleanup);
878 // Chooses the final implementation macro for given argument count.
879 // See also the comment for QM_TRY_META.
880 #define QM_REPORTONLY_TRY_ASSIGN_META(...) \
881 MOZ_ARG_7(, ##__VA_ARGS__, \
882 QM_REPORTONLY_TRY_ASSIGN_WITH_CLEANUP(__VA_ARGS__), \
883 QM_REPORTONLY_TRY_ASSIGN(__VA_ARGS__), \
884 QM_MISSING_ARGS(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__), \
885 QM_MISSING_ARGS(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__))
887 // Generates unique variable name. This extra internal macro (along with
888 // __COUNTER__) allows nesting of the final macro.
889 #define QM_REPORTONLY_TRY_ASSIGN_GLUE(severity, ...) \
890 QM_REPORTONLY_TRY_ASSIGN_META(MOZ_UNIQUE_VAR(tryResult), severity, \
894 * QM_WARNONLY_TRY_UNWRAP(target, expr[, cleanup]) evaluates expr, which must
895 * produce a Result value. On success, the result's success value is first
896 * unwrapped from the Result, then wrapped in a Maybe and finally assigned to
897 * target. On error, it calls HandleError and an additional cleanup
898 * function (if the third argument was passed) and finally assigns Nothing to
899 * target. |target| must be an lvalue. This macro never returns and failures
900 * are always reported using a lower level of severity relative to failures
901 * reported by QM_TRY_UNWRAP/QM_TRY_INSPECT. The macro is intended for failures
902 * that should not be propagated.
904 #define QM_WARNONLY_TRY_UNWRAP(...) \
905 QM_REPORTONLY_TRY_ASSIGN_GLUE(Warning, __VA_ARGS__)
907 // QM_WARNONLY_TRY_INSPECT doesn't make sense.
910 * QM_INFOONLY_TRY_UNWRAP is like QM_WARNONLY_TRY_UNWRAP. The only difference is
911 * that failures are reported using a lower level of severity relative to
912 * failures reported by QM_WARNONLY_TRY_UNWRAP.
914 #define QM_INFOONLY_TRY_UNWRAP(...) \
915 QM_REPORTONLY_TRY_ASSIGN_GLUE(Info, __VA_ARGS__)
917 // QM_INFOONLY_TRY_INSPECT doesn't make sense.
919 // QM_OR_ELSE_REPORT macro is an implementation detail of
920 // QM_OR_ELSE_WARN/QM_OR_ELSE_INFO/QM_OR_ELSE_LOG_VERBOSE and shouldn't be used
923 #define QM_OR_ELSE_REPORT(severity, expr, fallback) \
924 (expr).orElse([&](const auto& firstRes) { \
925 mozilla::dom::quota::QM_HANDLE_ERROR( \
926 #expr, firstRes, mozilla::dom::quota::Severity::severity); \
927 return fallback(firstRes); \
931 * QM_OR_ELSE_WARN(expr, fallback) evaluates expr, which must produce a Result
932 * value. On Success, it just moves the success over. On error, it calls
933 * HandleError (with the Warning severity) and a fallback function (passed as
934 * the second argument) which produces a new result. Failed expr is always
935 * reported as a warning (the macro essentially wraps the fallback function
936 * with a warning). QM_OR_ELSE_WARN is a sub macro and is intended to be used
937 * along with one of the main macros such as QM_TRY.
939 #define QM_OR_ELSE_WARN(...) QM_OR_ELSE_REPORT(Warning, __VA_ARGS__)
942 * QM_OR_ELSE_INFO is like QM_OR_ELSE_WARN. The only difference is that
943 * failures are reported using a lower level of severity relative to failures
944 * reported by QM_OR_ELSE_WARN.
946 #define QM_OR_ELSE_INFO(...) QM_OR_ELSE_REPORT(Info, __VA_ARGS__)
949 * QM_OR_ELSE_LOG_VERBOSE is like QM_OR_ELSE_WARN. The only difference is that
950 * failures are reported using the lowest severity which is currently ignored
951 * in LogError, so nothing goes to the console, browser console and telemetry.
952 * Since nothing goes to the telemetry, the macro can't signal the end of the
953 * underlying error stack or change the type of the error stack in the
954 * telemetry. For that reason, the expression shouldn't contain nested QM_TRY
957 #define QM_OR_ELSE_LOG_VERBOSE(...) QM_OR_ELSE_REPORT(Verbose, __VA_ARGS__)
959 namespace mozilla::dom::quota
{
961 // XXX Support orElseIf directly in mozilla::Result
962 template <typename V
, typename E
, typename P
, typename F
>
963 auto OrElseIf(Result
<V
, E
>&& aResult
, P
&& aPred
, F
&& aFunc
) -> Result
<V
, E
> {
964 return MOZ_UNLIKELY(aResult
.isErr())
965 ? (std::forward
<P
>(aPred
)(aResult
.inspectErr()))
966 ? std::forward
<F
>(aFunc
)(aResult
.unwrapErr())
967 : aResult
.propagateErr()
971 } // namespace mozilla::dom::quota
973 // QM_OR_ELSE_REPORT_IF macro is an implementation detail of
974 // QM_OR_ELSE_WARN_IF/QM_OR_ELSE_INFO_IF/QM_OR_ELSE_LOG_VERBOSE_IF and
975 // shouldn't be used directly.
977 #define QM_OR_ELSE_REPORT_IF(severity, expr, predicate, fallback) \
978 mozilla::dom::quota::OrElseIf( \
980 [&](const auto& firstRes) { \
981 bool res = predicate(firstRes); \
982 mozilla::dom::quota::QM_HANDLE_ERROR( \
984 res ? mozilla::dom::quota::Severity::severity \
985 : mozilla::dom::quota::Severity::Error); \
991 * QM_OR_ELSE_WARN_IF(expr, predicate, fallback) evaluates expr first, which
992 * must produce a Result value. On Success, it just moves the success over.
993 * On error, it calls a predicate function (passed as the second argument) and
994 * then it either calls HandleError (with the Warning severity) and a fallback
995 * function (passed as the third argument) which produces a new result if the
996 * predicate returned true. Or it calls HandleError (with the Error severity)
997 * and propagates the error result if the predicate returned false. So failed
998 * expr can be reported as a warning or as an error depending on the predicate.
999 * QM_OR_ELSE_WARN_IF is a sub macro and is intended to be used along with one
1000 * of the main macros such as QM_TRY.
1002 #define QM_OR_ELSE_WARN_IF(...) QM_OR_ELSE_REPORT_IF(Warning, __VA_ARGS__)
1005 * QM_OR_ELSE_INFO_IF is like QM_OR_ELSE_WARN_IF. The only difference is that
1006 * failures are reported using a lower level of severity relative to failures
1007 * reported by QM_OR_ELSE_WARN_IF.
1009 #define QM_OR_ELSE_INFO_IF(...) QM_OR_ELSE_REPORT_IF(Info, __VA_ARGS__)
1012 * QM_OR_ELSE_LOG_VERBOSE_IF is like QM_OR_ELSE_WARN_IF. The only difference is
1013 * that failures are reported using the lowest severity which is currently
1014 * ignored in LogError, so nothing goes to the console, browser console and
1015 * telemetry. Since nothing goes to the telemetry, the macro can't signal the
1016 * end of the underlying error stack or change the type of the error stack in
1017 * the telemetry. For that reason, the expression shouldn't contain nested
1018 * QM_TRY macro uses.
1020 #define QM_OR_ELSE_LOG_VERBOSE_IF(...) \
1021 QM_OR_ELSE_REPORT_IF(Verbose, __VA_ARGS__)
1023 // Telemetry probes to collect number of failure during the initialization.
1024 #ifdef NIGHTLY_BUILD
1025 # define RECORD_IN_NIGHTLY(_recorder, _status) \
1027 if (NS_SUCCEEDED(_recorder)) { \
1028 _recorder = _status; \
1032 # define OK_IN_NIGHTLY_PROPAGATE_IN_OTHERS \
1035 # define RETURN_STATUS_OR_RESULT(_status, _rv) \
1036 return Err(NS_FAILED(_status) ? (_status) : (_rv))
1038 # define RECORD_IN_NIGHTLY(_dummy, _status) \
1041 # define OK_IN_NIGHTLY_PROPAGATE_IN_OTHERS QM_PROPAGATE
1043 # define RETURN_STATUS_OR_RESULT(_status, _rv) return Err(_rv)
1046 class mozIStorageConnection
;
1047 class mozIStorageStatement
;
1054 struct CreateIfNonExistent
{};
1058 // Allow MOZ_TRY/QM_TRY to handle `bool` values by wrapping them with OkIf.
1059 // TODO: Maybe move this to mfbt/ResultExtensions.h
1060 inline Result
<Ok
, NotOk
> OkIf(bool aValue
) {
1064 return Err(NotOk());
1067 // TODO: Maybe move this to mfbt/ResultExtensions.h
1068 template <auto SuccessValue
>
1069 auto OkToOk(Ok
) -> Result
<decltype(SuccessValue
), nsresult
> {
1070 return SuccessValue
;
1073 template <nsresult ErrorValue
, auto SuccessValue
,
1074 typename V
= decltype(SuccessValue
)>
1075 auto ErrToOkOrErr(nsresult aValue
) -> Result
<V
, nsresult
> {
1076 if (aValue
== ErrorValue
) {
1077 return V
{SuccessValue
};
1082 template <nsresult ErrorValue
, typename V
= mozilla::Ok
>
1083 auto ErrToDefaultOkOrErr(nsresult aValue
) -> Result
<V
, nsresult
> {
1084 if (aValue
== ErrorValue
) {
1090 // Helper template function so that QM_TRY predicates checking for a specific
1091 // error can be concisely written as IsSpecificError<NS_SOME_ERROR> instead of
1092 // as a more verbose lambda.
1093 template <nsresult ErrorValue
>
1094 bool IsSpecificError(const nsresult aValue
) {
1095 return aValue
== ErrorValue
;
1098 #ifdef QM_ERROR_STACKS_ENABLED
1099 template <nsresult ErrorValue
>
1100 bool IsSpecificError(const QMResult
& aValue
) {
1101 return aValue
.NSResult() == ErrorValue
;
1105 // Helper template function so that QM_TRY fallback functions that are
1106 // converting errors into specific in-band success values can be concisely
1107 // written as ErrToOk<SuccessValueToReturn> (with the return type inferred).
1108 // For example, many file-related APIs that access information about a file may
1109 // return an nsresult error code if the file does not exist. From an
1110 // application perspective, the file not existing is not actually exceptional
1111 // and can instead be handled by the success case.
1112 template <auto SuccessValue
, typename V
= decltype(SuccessValue
)>
1113 auto ErrToOk(const nsresult aValue
) -> Result
<V
, nsresult
> {
1114 return V
{SuccessValue
};
1117 template <auto SuccessValue
, typename V
= decltype(SuccessValue
)>
1118 auto ErrToOkFromQMResult(const QMResult
& aValue
) -> Result
<V
, QMResult
> {
1119 return V
{SuccessValue
};
1122 // Helper template function so that QM_TRY fallback functions that are
1123 // suppressing errors by converting them into (generic) success can be
1124 // concisely written as ErrToDefaultOk<>.
1125 template <typename V
= mozilla::Ok
>
1126 auto ErrToDefaultOk(const nsresult aValue
) -> Result
<V
, nsresult
> {
1130 template <typename MozPromiseType
, typename RejectValueT
= nsresult
>
1131 auto CreateAndRejectMozPromise(StaticString aFunc
,
1132 const RejectValueT
& aRv
) -> decltype(auto) {
1133 if constexpr (std::is_same_v
<RejectValueT
, nsresult
>) {
1134 return MozPromiseType::CreateAndReject(aRv
, aFunc
);
1135 } else if constexpr (std::is_same_v
<RejectValueT
, QMResult
>) {
1136 return MozPromiseType::CreateAndReject(aRv
.NSResult(), aFunc
);
1140 RefPtr
<BoolPromise
> CreateAndRejectBoolPromise(StaticString aFunc
,
1143 RefPtr
<Int64Promise
> CreateAndRejectInt64Promise(StaticString aFunc
,
1146 RefPtr
<BoolPromise
> CreateAndRejectBoolPromiseFromQMResult(StaticString aFunc
,
1147 const QMResult
& aRv
);
1149 // Like Rust's collect with a step function, not a generic iterator/range.
1151 // Cond must be a function type with a return type to Result<V, E>, where
1152 // V is convertible to bool
1153 // - success converts to true indicates that collection shall continue
1154 // - success converts to false indicates that collection is completed
1155 // - error indicates that collection shall stop, propagating the error
1157 // Body must a function type accepting a V xvalue with a return type convertible
1158 // to Result<empty, E>.
1159 template <typename Step
, typename Body
>
1160 auto CollectEach(Step aStep
, const Body
& aBody
)
1161 -> Result
<mozilla::Ok
, typename
std::invoke_result_t
<Step
>::err_type
> {
1162 using StepResultType
= typename
std::invoke_result_t
<Step
>::ok_type
;
1166 typename
std::invoke_result_t
<Body
, StepResultType
&&>::ok_type
>);
1169 StepResultType element
;
1170 MOZ_TRY_VAR(element
, aStep());
1172 if (!static_cast<bool>(element
)) {
1176 MOZ_TRY(aBody(std::move(element
)));
1179 return mozilla::Ok
{};
1182 // This is like std::reduce with a to-be-defined execution policy (we don't want
1183 // to std::terminate on an error, but probably it's fine to just propagate any
1184 // error that occurred), operating not on a pair of iterators but rather a
1185 // generator function.
1186 template <typename InputGenerator
, typename T
, typename BinaryOp
>
1187 auto ReduceEach(InputGenerator aInputGenerator
, T aInit
,
1188 const BinaryOp
& aBinaryOp
)
1189 -> Result
<T
, typename
std::invoke_result_t
<InputGenerator
>::err_type
> {
1190 T res
= std::move(aInit
);
1192 // XXX This can be done in parallel!
1193 MOZ_TRY(CollectEach(
1194 std::move(aInputGenerator
),
1195 [&res
, &aBinaryOp
](const auto& element
)
1197 typename
std::invoke_result_t
<InputGenerator
>::err_type
> {
1198 MOZ_TRY_VAR(res
, aBinaryOp(std::move(res
), element
));
1203 return std::move(res
);
1206 // This is like std::reduce with a to-be-defined execution policy (we don't want
1207 // to std::terminate on an error, but probably it's fine to just propagate any
1208 // error that occurred).
1209 template <typename Range
, typename T
, typename BinaryOp
>
1210 auto Reduce(Range
&& aRange
, T aInit
, const BinaryOp
& aBinaryOp
) {
1214 [it
= begin(aRange
), end
= end(aRange
)]() mutable {
1215 auto res
= ToMaybeRef(it
!= end
? &*it
++ : nullptr);
1216 return Result
<decltype(res
), typename
std::invoke_result_t
<
1217 BinaryOp
, T
, decltype(res
)>::err_type
>(
1223 template <typename Range
, typename Body
>
1224 auto CollectEachInRange(Range
&& aRange
,
1225 const Body
& aBody
) -> Result
<mozilla::Ok
, nsresult
> {
1226 for (auto&& element
: aRange
) {
1227 MOZ_TRY(aBody(element
));
1230 return mozilla::Ok
{};
1233 // Like Rust's collect with a while loop, not a generic iterator/range.
1235 // Cond must be a function type accepting no parameters with a return type
1236 // convertible to Result<bool, E>, where
1237 // - success true indicates that collection shall continue
1238 // - success false indicates that collection is completed
1239 // - error indicates that collection shall stop, propagating the error
1241 // Body must a function type accepting no parameters with a return type
1242 // convertible to Result<empty, E>.
1243 template <typename Cond
, typename Body
>
1244 auto CollectWhile(const Cond
& aCond
, const Body
& aBody
)
1245 -> Result
<mozilla::Ok
, typename
std::invoke_result_t
<Cond
>::err_type
> {
1246 return CollectEach(aCond
, [&aBody
](bool) { return aBody(); });
1250 class [[nodiscard
]] GenericErrorResult
<mozilla::ipc::IPCResult
> {
1251 mozilla::ipc::IPCResult mErrorValue
;
1253 template <typename V
, typename E2
>
1254 friend class Result
;
1257 explicit GenericErrorResult(mozilla::ipc::IPCResult aErrorValue
)
1258 : mErrorValue(aErrorValue
) {
1259 MOZ_ASSERT(!aErrorValue
);
1262 GenericErrorResult(mozilla::ipc::IPCResult aErrorValue
,
1263 const ErrorPropagationTag
&)
1264 : GenericErrorResult(aErrorValue
) {}
1266 operator mozilla::ipc::IPCResult() const { return mErrorValue
; }
1269 namespace dom::quota
{
1271 extern const char kQuotaGenericDelimiter
;
1273 // Telemetry keys to indicate types of errors.
1274 #ifdef NIGHTLY_BUILD
1275 extern const nsLiteralCString kQuotaInternalError
;
1276 extern const nsLiteralCString kQuotaExternalError
;
1278 // No need for these when we're not collecting telemetry.
1279 # define kQuotaInternalError
1280 # define kQuotaExternalError
1283 MOZ_COLD
void ReportInternalError(const char* aFile
, uint32_t aLine
,
1286 LogModule
* GetQuotaManagerLogger();
1288 void AnonymizeCString(nsACString
& aCString
);
1290 inline auto AnonymizedCString(const nsACString
& aCString
) {
1291 nsAutoCString result
{aCString
};
1292 AnonymizeCString(result
);
1296 void AnonymizeOriginString(nsACString
& aOriginString
);
1298 inline auto AnonymizedOriginString(const nsACString
& aOriginString
) {
1299 nsAutoCString result
{aOriginString
};
1300 AnonymizeOriginString(result
);
1305 void CacheUseDOSDevicePathSyntaxPrefValue();
1308 Result
<nsCOMPtr
<nsIFile
>, nsresult
> QM_NewLocalFile(const nsAString
& aPath
);
1310 nsDependentCSubstring
GetLeafName(const nsACString
& aPath
);
1312 Result
<nsCOMPtr
<nsIFile
>, nsresult
> CloneFileAndAppend(
1313 nsIFile
& aDirectory
, const nsAString
& aPathElement
);
1315 enum class nsIFileKind
{
1321 // XXX We can use this outside of QM and its clients as well, probably. Maybe it
1322 // could be moved to xpcom/io?
1323 Result
<nsIFileKind
, nsresult
> GetDirEntryKind(nsIFile
& aFile
);
1325 Result
<nsCOMPtr
<mozIStorageStatement
>, nsresult
> CreateStatement(
1326 mozIStorageConnection
& aConnection
, const nsACString
& aStatementString
);
1328 enum class SingleStepResult
{ AssertHasResult
, ReturnNullIfNoResult
};
1330 template <SingleStepResult ResultHandling
>
1331 using SingleStepSuccessType
=
1332 std::conditional_t
<ResultHandling
== SingleStepResult::AssertHasResult
,
1333 NotNull
<nsCOMPtr
<mozIStorageStatement
>>,
1334 nsCOMPtr
<mozIStorageStatement
>>;
1336 template <SingleStepResult ResultHandling
>
1337 Result
<SingleStepSuccessType
<ResultHandling
>, nsresult
> ExecuteSingleStep(
1338 nsCOMPtr
<mozIStorageStatement
>&& aStatement
);
1340 // Creates a statement with the specified aStatementString, executes a single
1341 // step, and returns the statement.
1342 // Depending on the value ResultHandling,
1343 // - it is asserted that there is a result (default resp.
1344 // SingleStepResult::AssertHasResult), and the success type is
1345 // MovingNotNull<nsCOMPtr<mozIStorageStatement>>
1346 // - it is asserted that there is no result, and the success type is Ok
1347 // - in case there is no result, nullptr is returned, and the success type is
1348 // nsCOMPtr<mozIStorageStatement>
1349 // Any other errors are always propagated.
1350 template <SingleStepResult ResultHandling
= SingleStepResult::AssertHasResult
>
1351 Result
<SingleStepSuccessType
<ResultHandling
>, nsresult
>
1352 CreateAndExecuteSingleStepStatement(mozIStorageConnection
& aConnection
,
1353 const nsACString
& aStatementString
);
1357 // Determine the absolute path of the root of our built source tree so we can
1358 // derive source-relative paths for non-exported header files in
1359 // MakeSourceFileRelativePath. Exported header files end up in the objdir and
1360 // we have GetObjdirDistIncludeTreeBase for that.
1361 nsDependentCSubstring
GetSourceTreeBase();
1363 // Determine the absolute path of the root of our built OBJDIR/dist/include
1364 // directory. The aQuotaCommonHPath argument cleverly defaults to __FILE__
1365 // initialized in our exported header; no argument should ever be provided to
1366 // this method. GetSourceTreeBase handles identifying the root of the source
1368 nsDependentCSubstring
GetObjdirDistIncludeTreeBase(
1369 const nsLiteralCString
& aQuotaCommonHPath
= nsLiteralCString(__FILE__
));
1371 nsDependentCSubstring
MakeSourceFileRelativePath(
1372 const nsACString
& aSourceFilePath
);
1374 } // namespace detail
1376 enum class Severity
{
1383 #ifdef QM_LOG_ERROR_ENABLED
1384 # ifdef QM_ERROR_STACKS_ENABLED
1385 using ResultType
= Variant
<QMResult
, nsresult
, Nothing
>;
1387 void LogError(const nsACString
& aExpr
, const ResultType
& aResult
,
1388 const nsACString
& aSourceFilePath
, int32_t aSourceFileLine
,
1391 void LogError(const nsACString
& aExpr
, Maybe
<nsresult
> aMaybeRv
,
1392 const nsACString
& aSourceFilePath
, int32_t aSourceFileLine
,
1399 Result
<bool, nsresult
> WarnIfFileIsUnknown(nsIFile
& aFile
,
1400 const char* aSourceFilePath
,
1401 int32_t aSourceFileLine
);
1404 // As HandleError is a function that will only be called in error cases, it is
1405 // marked with MOZ_COLD to avoid bloating the code of calling functions, if it's
1408 // For the same reason, the string-ish parameters are of type const char* rather
1409 // than any ns*String type, to minimize the code at each call site. This
1410 // deliberately de-optimizes runtime performance, which is uncritical during
1413 // This functions are not intended to be called
1414 // directly, they should only be called from the QM_* macros.
1415 #ifdef QM_LOG_ERROR_ENABLED
1416 template <typename T
>
1417 MOZ_COLD MOZ_NEVER_INLINE
void HandleError(const char* aExpr
, const T
& aRv
,
1418 const char* aSourceFilePath
,
1419 int32_t aSourceFileLine
,
1420 const Severity aSeverity
) {
1421 # ifdef QM_ERROR_STACKS_ENABLED
1422 if constexpr (std::is_same_v
<T
, QMResult
> || std::is_same_v
<T
, nsresult
>) {
1423 mozilla::dom::quota::LogError(nsDependentCString(aExpr
), ResultType(aRv
),
1424 nsDependentCString(aSourceFilePath
),
1425 aSourceFileLine
, aSeverity
);
1427 mozilla::dom::quota::LogError(
1428 nsDependentCString(aExpr
), ResultType(Nothing
{}),
1429 nsDependentCString(aSourceFilePath
), aSourceFileLine
, aSeverity
);
1432 if constexpr (std::is_same_v
<T
, nsresult
>) {
1433 mozilla::dom::quota::LogError(nsDependentCString(aExpr
), Some(aRv
),
1434 nsDependentCString(aSourceFilePath
),
1435 aSourceFileLine
, aSeverity
);
1437 mozilla::dom::quota::LogError(nsDependentCString(aExpr
), Nothing
{},
1438 nsDependentCString(aSourceFilePath
),
1439 aSourceFileLine
, aSeverity
);
1444 template <typename T
>
1445 MOZ_ALWAYS_INLINE
constexpr void HandleError(const char* aExpr
, const T
& aRv
,
1446 const char* aSourceFilePath
,
1447 int32_t aSourceFileLine
,
1448 const Severity aSeverity
) {}
1451 template <typename T
>
1452 Nothing
HandleErrorReturnNothing(const char* aExpr
, const T
& aRv
,
1453 const char* aSourceFilePath
,
1454 int32_t aSourceFileLine
,
1455 const Severity aSeverity
) {
1456 HandleError(aExpr
, aRv
, aSourceFilePath
, aSourceFileLine
, aSeverity
);
1460 template <typename T
, typename CleanupFunc
>
1461 Nothing
HandleErrorWithCleanupReturnNothing(const char* aExpr
, const T
& aRv
,
1462 const char* aSourceFilePath
,
1463 int32_t aSourceFileLine
,
1464 const Severity aSeverity
,
1465 CleanupFunc
&& aCleanupFunc
) {
1466 HandleError(aExpr
, aRv
, aSourceFilePath
, aSourceFileLine
, aSeverity
);
1467 std::forward
<CleanupFunc
>(aCleanupFunc
)(aRv
);
1471 // Implementation of workaround for GCC bug #114812.
1472 #if defined(__GNUC__) && !defined(__clang__)
1473 namespace gcc_detail
{
1474 // usual case: identity function
1475 template <typename T
>
1476 struct invokabilize_impl
{
1477 auto operator()(T t
) -> T
{ return t
; }
1479 // reference-to-function: wrap in std::function
1480 template <typename R
, typename
... Args
>
1481 struct invokabilize_impl
<R (&)(Args
...)> {
1482 auto operator()(R (&t
)(Args
...)) -> std::function
<R(Args
...)> {
1483 return std::function
{t
};
1486 // pointer-to-function: wrap in std::function
1487 template <typename R
, typename
... Args
>
1488 struct invokabilize_impl
<R (*)(Args
...)> {
1489 auto operator()(R (*t
)(Args
...)) -> std::function
<R(Args
...)> {
1490 return std::function
{t
};
1494 template <typename T
>
1495 auto invokabilize(T t
) {
1496 return invokabilize_impl
<T
>{}(std::forward
<T
>(t
));
1498 } // namespace gcc_detail
1501 template <size_t NFunc
, size_t NExpr
, typename T
, typename CustomRetVal_
>
1502 auto HandleCustomRetVal(const char (&aFunc
)[NFunc
], const char (&aExpr
)[NExpr
],
1503 const T
& aRv
, CustomRetVal_
&& aCustomRetVal_
) {
1504 #if defined(__GNUC__) && !defined(__clang__)
1505 // Workaround for gcc bug #114812. (See either that bug, or our bug 1891541,
1506 // for more details.)
1507 auto aCustomRetVal
=
1508 gcc_detail::invokabilize(std::forward
<CustomRetVal_
>(aCustomRetVal_
));
1509 using CustomRetVal
= decltype(aCustomRetVal
);
1511 using CustomRetVal
= CustomRetVal_
;
1512 CustomRetVal
& aCustomRetVal
= aCustomRetVal_
;
1514 if constexpr (std::is_invocable
<CustomRetVal
, const char[NFunc
],
1515 const char[NExpr
]>::value
) {
1516 return std::forward
<CustomRetVal
>(aCustomRetVal
)(aFunc
, aExpr
);
1517 } else if constexpr (std::is_invocable
<CustomRetVal
, const char[NFunc
],
1519 return aCustomRetVal(aFunc
, aRv
);
1520 } else if constexpr (std::is_invocable
<CustomRetVal
, const T
&>::value
) {
1521 return aCustomRetVal(aRv
);
1523 return std::forward
<CustomRetVal
>(aCustomRetVal
);
1527 template <SingleStepResult ResultHandling
= SingleStepResult::AssertHasResult
,
1528 typename BindFunctor
>
1529 Result
<SingleStepSuccessType
<ResultHandling
>, nsresult
>
1530 CreateAndExecuteSingleStepStatement(mozIStorageConnection
& aConnection
,
1531 const nsACString
& aStatementString
,
1532 BindFunctor aBindFunctor
) {
1533 QM_TRY_UNWRAP(auto stmt
, CreateStatement(aConnection
, aStatementString
));
1535 QM_TRY(aBindFunctor(*stmt
));
1537 return ExecuteSingleStep
<ResultHandling
>(std::move(stmt
));
1540 template <typename StepFunc
>
1541 Result
<Ok
, nsresult
> CollectWhileHasResult(mozIStorageStatement
& aStmt
,
1542 StepFunc
&& aStepFunc
) {
1543 return CollectWhile(
1545 QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER(aStmt
, ExecuteStep
));
1547 [&aStmt
, &aStepFunc
] { return aStepFunc(aStmt
); });
1550 template <typename StepFunc
,
1551 typename ArrayType
= nsTArray
<typename
std::invoke_result_t
<
1552 StepFunc
, mozIStorageStatement
&>::ok_type
>>
1553 auto CollectElementsWhileHasResult(mozIStorageStatement
& aStmt
,
1554 StepFunc
&& aStepFunc
)
1555 -> Result
<ArrayType
, nsresult
> {
1558 QM_TRY(CollectWhileHasResult(
1559 aStmt
, [&aStepFunc
, &res
](auto& stmt
) -> Result
<Ok
, nsresult
> {
1560 QM_TRY_UNWRAP(auto element
, aStepFunc(stmt
));
1561 res
.AppendElement(std::move(element
));
1565 return std::move(res
);
1568 template <typename ArrayType
, typename StepFunc
>
1569 auto CollectElementsWhileHasResultTyped(mozIStorageStatement
& aStmt
,
1570 StepFunc
&& aStepFunc
) {
1571 return CollectElementsWhileHasResult
<StepFunc
, ArrayType
>(
1572 aStmt
, std::forward
<StepFunc
>(aStepFunc
));
1576 template <typename Cancel
, typename Body
>
1577 Result
<mozilla::Ok
, nsresult
> CollectEachFile(nsIFile
& aDirectory
,
1578 const Cancel
& aCancel
,
1579 const Body
& aBody
) {
1580 QM_TRY_INSPECT(const auto& entries
, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
1581 nsCOMPtr
<nsIDirectoryEnumerator
>,
1582 aDirectory
, GetDirectoryEntries
));
1585 [&entries
, &aCancel
]() -> Result
<nsCOMPtr
<nsIFile
>, nsresult
> {
1587 return nsCOMPtr
<nsIFile
>{};
1590 QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsCOMPtr
<nsIFile
>,
1591 entries
, GetNextFile
));
1595 } // namespace detail
1597 template <typename Body
>
1598 Result
<mozilla::Ok
, nsresult
> CollectEachFile(nsIFile
& aDirectory
,
1599 const Body
& aBody
) {
1600 return detail::CollectEachFile(aDirectory
, [] { return false; }, aBody
);
1603 template <typename Body
>
1604 Result
<mozilla::Ok
, nsresult
> CollectEachFileAtomicCancelable(
1605 nsIFile
& aDirectory
, const Atomic
<bool>& aCanceled
, const Body
& aBody
) {
1606 return detail::CollectEachFile(
1607 aDirectory
, [&aCanceled
] { return static_cast<bool>(aCanceled
); }, aBody
);
1610 template <typename T
, typename Body
>
1611 auto ReduceEachFileAtomicCancelable(nsIFile
& aDirectory
,
1612 const Atomic
<bool>& aCanceled
, T aInit
,
1613 const Body
& aBody
) -> Result
<T
, nsresult
> {
1614 QM_TRY_INSPECT(const auto& entries
, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
1615 nsCOMPtr
<nsIDirectoryEnumerator
>,
1616 aDirectory
, GetDirectoryEntries
));
1619 [&entries
, &aCanceled
]() -> Result
<nsCOMPtr
<nsIFile
>, nsresult
> {
1621 return nsCOMPtr
<nsIFile
>{};
1624 QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsCOMPtr
<nsIFile
>,
1625 entries
, GetNextFile
));
1627 std::move(aInit
), aBody
);
1630 constexpr bool IsDatabaseCorruptionError(const nsresult aRv
) {
1631 return aRv
== NS_ERROR_FILE_CORRUPTED
|| aRv
== NS_ERROR_STORAGE_IOERR
;
1634 template <typename Func
>
1635 auto CallWithDelayedRetriesIfAccessDenied(Func
&& aFunc
, uint32_t aMaxRetries
,
1637 -> Result
<typename
std::invoke_result_t
<Func
>::ok_type
, nsresult
> {
1638 uint32_t retries
= 0;
1641 auto result
= std::forward
<Func
>(aFunc
)();
1643 if (result
.isOk()) {
1647 if (result
.inspectErr() != NS_ERROR_FILE_IS_LOCKED
&&
1648 result
.inspectErr() != NS_ERROR_FILE_ACCESS_DENIED
) {
1652 if (retries
++ >= aMaxRetries
) {
1656 PR_Sleep(PR_MillisecondsToInterval(aDelayMs
));
1662 template <bool flag
= false>
1663 void UnsupportedReturnType() {
1664 static_assert(flag
, "Unsupported return type!");
1667 } // namespace detail
1669 template <typename Initialization
, typename StringGenerator
, typename Func
>
1670 auto ExecuteInitialization(
1671 FirstInitializationAttempts
<Initialization
, StringGenerator
>&
1672 aFirstInitializationAttempts
,
1673 const Initialization aInitialization
, Func
&& aFunc
)
1674 -> std::invoke_result_t
<Func
, const FirstInitializationAttempt
<
1675 Initialization
, StringGenerator
>&> {
1676 return aFirstInitializationAttempts
.WithFirstInitializationAttempt(
1677 aInitialization
, [&aFunc
](auto&& firstInitializationAttempt
) {
1678 auto res
= std::forward
<Func
>(aFunc
)(firstInitializationAttempt
);
1680 const auto rv
= [&res
]() -> nsresult
{
1682 std::invoke_result_t
<Func
, const FirstInitializationAttempt
<
1683 Initialization
, StringGenerator
>&>;
1685 if constexpr (std::is_same_v
<RetType
, nsresult
>) {
1687 } else if constexpr (mozilla::detail::IsResult
<RetType
>::value
&&
1688 std::is_same_v
<typename
RetType::err_type
,
1690 return res
.isOk() ? NS_OK
: res
.inspectErr();
1692 detail::UnsupportedReturnType();
1696 // NS_ERROR_ABORT signals a non-fatal, recoverable problem during
1697 // initialization. We do not want these kind of failures to count
1698 // against our overall first initialization attempt telemetry. Thus we
1699 // just ignore this kind of failure and keep
1700 // aFirstInitializationAttempts unflagged to stay ready to record a real
1701 // success or failure on the next attempt.
1702 if (rv
== NS_ERROR_ABORT
) {
1706 if (!firstInitializationAttempt
.Recorded()) {
1707 firstInitializationAttempt
.Record(rv
);
1714 template <typename Initialization
, typename StringGenerator
, typename Func
>
1715 auto ExecuteInitialization(
1716 FirstInitializationAttempts
<Initialization
, StringGenerator
>&
1717 aFirstInitializationAttempts
,
1718 const Initialization aInitialization
, const nsACString
& aContext
,
1720 -> std::invoke_result_t
<Func
, const FirstInitializationAttempt
<
1721 Initialization
, StringGenerator
>&> {
1722 return ExecuteInitialization(
1723 aFirstInitializationAttempts
, aInitialization
,
1724 [&](const auto& firstInitializationAttempt
) -> decltype(auto) {
1725 #ifdef QM_SCOPED_LOG_EXTRA_INFO_ENABLED
1726 const auto maybeScopedLogExtraInfo
=
1727 firstInitializationAttempt
.Recorded()
1729 : Some(ScopedLogExtraInfo
{
1730 ScopedLogExtraInfo::kTagContextTainted
, aContext
});
1733 return std::forward
<Func
>(aFunc
)(firstInitializationAttempt
);
1737 } // namespace dom::quota
1738 } // namespace mozilla
1740 #endif // mozilla_dom_quota_quotacommon_h__