Bug 1886946: Remove incorrect assertion that buffer is not-pinned. r=sfink
[gecko.git] / dom / quota / QuotaCommon.h
blob74855dd16bef286fa78cf2e519989876b7471e69
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"
12 #include <algorithm>
13 #include <cstddef>
14 #include <cstdint>
15 #include <type_traits>
16 #include <utility>
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/Try.h"
26 #if defined(QM_LOG_ERROR_ENABLED) && defined(QM_ERROR_STACKS_ENABLED)
27 # include "mozilla/Variant.h"
28 #endif
29 #include "mozilla/dom/QMResult.h"
30 #include "mozilla/dom/quota/FirstInitializationAttemptsImpl.h"
31 #include "mozilla/dom/quota/RemoveParen.h"
32 #include "mozilla/dom/quota/ScopedLogExtraInfo.h"
33 #include "mozilla/ipc/ProtocolUtils.h"
34 #include "nsCOMPtr.h"
35 #include "nsDebug.h"
36 #include "nsError.h"
37 #include "nsIDirectoryEnumerator.h"
38 #include "nsIEventTarget.h"
39 #include "nsIFile.h"
40 #include "nsLiteralString.h"
41 #include "nsPrintfCString.h"
42 #include "nsReadableUtils.h"
43 #include "nsString.h"
44 #include "nsTArray.h"
45 #include "nsTLiteralString.h"
47 namespace mozilla {
48 template <typename T>
49 class NotNull;
52 #define MOZ_ARGS_AFTER_3(a1, a2, a3, ...) __VA_ARGS__
54 #define MOZ_ADD_ARGS2(...) , ##__VA_ARGS__
55 #define MOZ_ADD_ARGS(...) MOZ_ADD_ARGS2(__VA_ARGS__)
57 // Proper use of unique variable names can be tricky (especially if nesting of
58 // the final macro is required).
59 // See https://lifecs.likai.org/2016/07/c-preprocessor-hygienic-macros.html
60 #define MOZ_UNIQUE_VAR(base) MOZ_CONCAT(base, __COUNTER__)
62 // See https://florianjw.de/en/passing_overloaded_functions.html
63 // TODO: Add a test for this macro.
64 #define MOZ_SELECT_OVERLOAD(func) \
65 [](auto&&... aArgs) -> decltype(auto) { \
66 return func(std::forward<decltype(aArgs)>(aArgs)...); \
69 #define DSSTORE_FILE_NAME ".DS_Store"
70 #define DESKTOP_FILE_NAME ".desktop"
71 #define DESKTOP_INI_FILE_NAME "desktop.ini"
72 #define THUMBS_DB_FILE_NAME "thumbs.db"
74 #define QM_WARNING(...) \
75 do { \
76 nsPrintfCString str(__VA_ARGS__); \
77 mozilla::dom::quota::ReportInternalError(__FILE__, __LINE__, str.get()); \
78 NS_WARNING(str.get()); \
79 } while (0)
81 #define QM_LOG_TEST() MOZ_LOG_TEST(GetQuotaManagerLogger(), LogLevel::Info)
82 #define QM_LOG(_args) MOZ_LOG(GetQuotaManagerLogger(), LogLevel::Info, _args)
84 #ifdef DEBUG
85 # define UNKNOWN_FILE_WARNING(_leafName) \
86 NS_WARNING(nsPrintfCString( \
87 "Something (%s) in the directory that doesn't belong!", \
88 NS_ConvertUTF16toUTF8(_leafName).get()) \
89 .get())
90 #else
91 # define UNKNOWN_FILE_WARNING(_leafName) (void)(_leafName);
92 #endif
94 // This macro should be used in directory traversals for files or directories
95 // that are unknown for given directory traversal. It should only be called
96 // after all known (directory traversal specific) files or directories have
97 // been checked and handled.
98 // XXX Consider renaming the macro to QM_LOG_UNKNOWN_DIR_ENTRY.
99 #ifdef DEBUG
100 # define WARN_IF_FILE_IS_UNKNOWN(_file) \
101 mozilla::dom::quota::WarnIfFileIsUnknown(_file, __FILE__, __LINE__)
102 #else
103 # define WARN_IF_FILE_IS_UNKNOWN(_file) Result<bool, nsresult>(false)
104 #endif
107 * There are multiple ways to handle unrecoverable conditions (note that the
108 * patterns are put in reverse chronological order and only the first pattern
109 * QM_TRY/QM_TRY_UNWRAP/QM_TRY_INSPECT/QM_TRY_RETURN/QM_FAIL should be used in
110 * new code):
112 * 1. Using QM_TRY/QM_TRY_UNWRAP/QM_TRY_INSPECT/QM_TRY_RETURN/QM_FAIL macros
113 * (Quota manager specific, defined below)
115 * Typical use cases:
117 * nsresult MyFunc1(nsIFile& aFile) {
118 * bool exists;
119 * QM_TRY(aFile.Exists(&exists));
120 * QM_TRY(OkIf(exists), NS_ERROR_FAILURE);
122 * // The file exists, and data could be read from it here.
124 * return NS_OK;
127 * nsresult MyFunc2(nsIFile& aFile) {
128 * bool exists;
129 * QM_TRY(aFile.Exists(&exists), NS_ERROR_UNEXPECTED);
130 * QM_TRY(OkIf(exists), NS_ERROR_UNEXPECTED);
132 * // The file exists, and data could be read from it here.
134 * return NS_OK;
137 * void MyFunc3(nsIFile& aFile) {
138 * bool exists;
139 * QM_TRY(aFile.Exists(&exists), QM_VOID);
140 * QM_TRY(OkIf(exists), QM_VOID);
142 * // The file exists, and data could be read from it here.
145 * nsresult MyFunc4(nsIFile& aFile) {
146 * bool exists;
147 * QM_TRY(storageFile->Exists(&exists), QM_PROPAGATE,
148 * []() { NS_WARNING("The Exists call failed!"); });
149 * QM_TRY(OkIf(exists), NS_ERROR_FAILURE,
150 * []() { NS_WARNING("The file doesn't exist!"); });
152 * // The file exists, and data could be read from it here.
154 * return NS_OK;
157 * nsresult MyFunc5(nsIFile& aFile) {
158 * bool exists;
159 * QM_TRY(aFile.Exists(&exists));
160 * if (exists) {
161 * // The file exists, and data could be read from it here.
162 * } else {
163 * QM_FAIL(NS_ERROR_FAILURE);
166 * return NS_OK;
169 * nsresult MyFunc6(nsIFile& aFile) {
170 * bool exists;
171 * QM_TRY(aFile.Exists(&exists));
172 * if (exists) {
173 * // The file exists, and data could be read from it here.
174 * } else {
175 * QM_FAIL(NS_ERROR_FAILURE,
176 * []() { NS_WARNING("The file doesn't exist!"); });
179 * return NS_OK;
182 * 2. Using MOZ_TRY/MOZ_TRY_VAR macros
184 * Typical use cases:
186 * nsresult MyFunc1(nsIFile& aFile) {
187 * // MOZ_TRY can't return a custom return value
189 * return NS_OK;
192 * nsresult MyFunc2(nsIFile& aFile) {
193 * // MOZ_TRY can't return a custom return value
195 * return NS_OK;
198 * void MyFunc3(nsIFile& aFile) {
199 * // MOZ_TRY can't return a custom return value, "void" in this case
202 * nsresult MyFunc4(nsIFile& aFile) {
203 * // MOZ_TRY can't return a custom return value and run an additional
204 * // cleanup function
206 * return NS_OK;
209 * nsresult MyFunc5(nsIFile& aFile) {
210 * // There's no MOZ_FAIL, MOZ_TRY can't return a custom return value
212 * return NS_OK;
215 * nsresult MyFunc6(nsIFile& aFile) {
216 * // There's no MOZ_FAIL, MOZ_TRY can't return a custom return value and run
217 * // an additional cleanup function
219 * return NS_OK;
222 * 3. Using NS_WARN_IF and NS_WARNING macro with own control flow handling
224 * Typical use cases:
226 * nsresult MyFunc1(nsIFile& aFile) {
227 * bool exists;
228 * nsresult rv = aFile.Exists(&exists);
229 * if (NS_WARN_IF(NS_FAILED(rv)) {
230 * return rv;
232 * if (NS_WARN_IF(!exists) {
233 * return NS_ERROR_FAILURE;
236 * // The file exists, and data could be read from it here.
238 * return NS_OK;
241 * nsresult MyFunc2(nsIFile& aFile) {
242 * bool exists;
243 * nsresult rv = aFile.Exists(&exists);
244 * if (NS_WARN_IF(NS_FAILED(rv)) {
245 * return NS_ERROR_UNEXPECTED;
247 * if (NS_WARN_IF(!exists) {
248 * return NS_ERROR_UNEXPECTED;
251 * // The file exists, and data could be read from it here.
253 * return NS_OK;
256 * void MyFunc3(nsIFile& aFile) {
257 * bool exists;
258 * nsresult rv = aFile.Exists(&exists);
259 * if (NS_WARN_IF(NS_FAILED(rv)) {
260 * return;
262 * if (NS_WARN_IF(!exists) {
263 * return;
266 * // The file exists, and data could be read from it here.
269 * nsresult MyFunc4(nsIFile& aFile) {
270 * bool exists;
271 * nsresult rv = aFile.Exists(&exists);
272 * if (NS_WARN_IF(NS_FAILED(rv)) {
273 * NS_WARNING("The Exists call failed!");
274 * return rv;
276 * if (NS_WARN_IF(!exists) {
277 * NS_WARNING("The file doesn't exist!");
278 * return NS_ERROR_FAILURE;
281 * // The file exists, and data could be read from it here.
283 * return NS_OK;
286 * nsresult MyFunc5(nsIFile& aFile) {
287 * bool exists;
288 * nsresult rv = aFile.Exists(&exists);
289 * if (NS_WARN_IF(NS_FAILED(rv)) {
290 * return rv;
292 * if (exists) {
293 * // The file exists, and data could be read from it here.
294 * } else {
295 * return NS_ERROR_FAILURE;
298 * return NS_OK;
301 * nsresult MyFunc6(nsIFile& aFile) {
302 * bool exists;
303 * nsresult rv = aFile.Exists(&exists);
304 * if (NS_WARN_IF(NS_FAILED(rv)) {
305 * return rv;
307 * if (exists) {
308 * // The file exists, and data could be read from it here.
309 * } else {
310 * NS_WARNING("The file doesn't exist!");
311 * return NS_ERROR_FAILURE;
314 * return NS_OK;
317 * 4. Using NS_ENSURE_* macros
319 * Mainly:
320 * - NS_ENSURE_SUCCESS
321 * - NS_ENSURE_SUCCESS_VOID
322 * - NS_ENSURE_TRUE
323 * - NS_ENSURE_TRUE_VOID
325 * Typical use cases:
327 * nsresult MyFunc1(nsIFile& aFile) {
328 * bool exists;
329 * nsresult rv = aFile.Exists(&exists);
330 * NS_ENSURE_SUCCESS(rv, rv);
331 * NS_ENSURE_TRUE(exists, NS_ERROR_FAILURE);
333 * // The file exists, and data could be read from it here.
335 * return NS_OK;
338 * nsresult MyFunc2(nsIFile& aFile) {
339 * bool exists;
340 * nsresult rv = aFile.Exists(&exists);
341 * NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
342 * NS_ENSURE_TRUE(exists, NS_ERROR_UNEXPECTED);
344 * // The file exists, and data could be read from it here.
346 * return NS_OK;
349 * void MyFunc3(nsIFile& aFile) {
350 * bool exists;
351 * nsresult rv = aFile.Exists(&exists);
352 * NS_ENSURE_SUCCESS_VOID(rv);
353 * NS_ENSURE_TRUE_VOID(exists);
355 * // The file exists, and data could be read from it here.
358 * nsresult MyFunc4(nsIFile& aFile) {
359 * // NS_ENSURE_SUCCESS/NS_ENSURE_TRUE can't run an additional cleanup
360 * // function
362 * return NS_OK;
365 * nsresult MyFunc5(nsIFile& aFile) {
366 * bool exists;
367 * nsresult rv = aFile.Exists(&exists);
368 * NS_ENSURE_SUCCESS(rv, rv);
369 * if (exists) {
370 * // The file exists, and data could be read from it here.
371 * } else {
372 * NS_ENSURE_TRUE(false, NS_ERROR_FAILURE);
375 * return NS_OK;
378 * nsresult MyFunc6(nsIFile& aFile) {
379 * // NS_ENSURE_TRUE can't run an additional cleanup function
381 * return NS_OK;
384 * QM_TRY/QM_TRY_UNWRAP/QM_TRY_INSPECT is like MOZ_TRY/MOZ_TRY_VAR but if an
385 * error occurs it additionally calls a generic function HandleError to handle
386 * the error and it can be used to return custom return values as well and even
387 * call an additional cleanup function.
388 * HandleError currently only warns in debug builds, it will report to the
389 * browser console and telemetry in the future.
390 * The other advantage of QM_TRY/QM_TRY_UNWRAP/QM_TRY_INSPECT is that a local
391 * nsresult is not needed at all in all cases, all calls can be wrapped
392 * directly. If an error occurs, the warning contains a concrete call instead
393 * of the rv local variable. For example:
395 * 1. WARNING: NS_ENSURE_SUCCESS(rv, rv) failed with result 0x80004005
396 * (NS_ERROR_FAILURE): file XYZ, line N
398 * 2. WARNING: 'NS_FAILED(rv)', file XYZ, line N
400 * 3. Nothing (MOZ_TRY doesn't warn)
402 * 4. WARNING: Error: 'aFile.Exists(&exists)', file XYZ, line N
404 * QM_TRY_RETURN is a supplementary macro for cases when the result's success
405 * value can be directly returned (instead of assigning to a variable as in the
406 * QM_TRY_UNWRAP/QM_TRY_INSPECT case).
408 * QM_FAIL is a supplementary macro for cases when an error needs to be
409 * returned without evaluating an expression. It's possible to write
410 * QM_TRY(OkIf(false), NS_ERROR_FAILURE), but QM_FAIL(NS_ERROR_FAILURE) looks
411 * more straightforward.
413 * It's highly recommended to use
414 * QM_TRY/QM_TRY_UNWRAP/QM_TRY_INSPECT/QM_TRY_RETURN/QM_FAIL in new code for
415 * quota manager and quota clients. Existing code should be incrementally
416 * converted as needed.
418 * QM_TRY_VOID/QM_TRY_UNWRAP_VOID/QM_TRY_INSPECT_VOID/QM_FAIL_VOID is not
419 * defined on purpose since it's possible to use
420 * QM_TRY/QM_TRY_UNWRAP/QM_TRY_INSPECT/QM_FAIL even in void functions.
421 * However, QM_TRY(Task(), ) would look odd so it's recommended to use a dummy
422 * define QM_VOID that evaluates to nothing instead: QM_TRY(Task(), QM_VOID)
424 * Custom return values can be static or dynamically generated using functions
425 * with one of these signatures:
426 * auto(const char* aFunc, const char* aExpr);
427 * auto(const char* aFunc, const T& aRv);
428 * auto(const T& aRc);
431 #define QM_VOID
433 #define QM_PROPAGATE Err(tryTempError)
435 namespace mozilla::dom::quota::detail {
437 struct IpcFailCustomRetVal {
438 explicit IpcFailCustomRetVal(
439 mozilla::NotNull<mozilla::ipc::IProtocol*> aActor)
440 : mActor(aActor) {}
442 template <size_t NFunc, size_t NExpr>
443 auto operator()(const char (&aFunc)[NFunc],
444 const char (&aExpr)[NExpr]) const {
445 return mozilla::Err(mozilla::ipc::IPCResult::Fail(mActor, aFunc, aExpr));
448 mozilla::NotNull<mozilla::ipc::IProtocol*> mActor;
451 } // namespace mozilla::dom::quota::detail
453 #define QM_IPC_FAIL(actor) \
454 mozilla::dom::quota::detail::IpcFailCustomRetVal(mozilla::WrapNotNull(actor))
456 #ifdef DEBUG
457 # define QM_ASSERT_UNREACHABLE \
458 [](const char*, const char*) -> ::mozilla::GenericErrorResult<nsresult> { \
459 MOZ_CRASH("Should never be reached."); \
462 # define QM_ASSERT_UNREACHABLE_VOID \
463 [](const char*, const char*) { MOZ_CRASH("Should never be reached."); }
464 #endif
466 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
467 # define QM_DIAGNOSTIC_ASSERT_UNREACHABLE \
468 [](const char*, const char*) -> ::mozilla::GenericErrorResult<nsresult> { \
469 MOZ_CRASH("Should never be reached."); \
472 # define QM_DIAGNOSTIC_ASSERT_UNREACHABLE_VOID \
473 [](const char*, const char*) { MOZ_CRASH("Should never be reached."); }
474 #endif
476 #define QM_NO_CLEANUP [](const auto&) {}
478 // QM_MISSING_ARGS and QM_HANDLE_ERROR macros are implementation details of
479 // QM_TRY/QM_TRY_UNWRAP/QM_TRY_INSPECT/QM_FAIL and shouldn't be used directly.
481 #define QM_MISSING_ARGS(...) \
482 do { \
483 static_assert(false, "Did you forget arguments?"); \
484 } while (0)
486 #ifdef DEBUG
487 # define QM_HANDLE_ERROR(expr, error, severity) \
488 HandleError(#expr, error, __FILE__, __LINE__, severity)
489 #else
490 # define QM_HANDLE_ERROR(expr, error, severity) \
491 HandleError("Unavailable", error, __FILE__, __LINE__, severity)
492 #endif
494 #ifdef DEBUG
495 # define QM_HANDLE_ERROR_RETURN_NOTHING(expr, error, severity) \
496 HandleErrorReturnNothing(#expr, error, __FILE__, __LINE__, severity)
497 #else
498 # define QM_HANDLE_ERROR_RETURN_NOTHING(expr, error, severity) \
499 HandleErrorReturnNothing("Unavailable", error, __FILE__, __LINE__, severity)
500 #endif
502 #ifdef DEBUG
503 # define QM_HANDLE_ERROR_WITH_CLEANUP_RETURN_NOTHING(expr, error, severity, \
504 cleanup) \
505 HandleErrorWithCleanupReturnNothing(#expr, error, __FILE__, __LINE__, \
506 severity, cleanup)
507 #else
508 # define QM_HANDLE_ERROR_WITH_CLEANUP_RETURN_NOTHING(expr, error, severity, \
509 cleanup) \
510 HandleErrorWithCleanupReturnNothing("Unavailable", error, __FILE__, \
511 __LINE__, severity, cleanup)
512 #endif
514 // Handles the case when QM_VOID is passed as a custom return value.
515 #define QM_HANDLE_CUSTOM_RET_VAL_HELPER0(func, expr, error)
517 #define QM_HANDLE_CUSTOM_RET_VAL_HELPER1(func, expr, error, customRetVal) \
518 mozilla::dom::quota::HandleCustomRetVal(func, #expr, error, customRetVal)
520 #define QM_HANDLE_CUSTOM_RET_VAL_GLUE(a, b) a b
522 #define QM_HANDLE_CUSTOM_RET_VAL(...) \
523 QM_HANDLE_CUSTOM_RET_VAL_GLUE( \
524 MOZ_PASTE_PREFIX_AND_ARG_COUNT(QM_HANDLE_CUSTOM_RET_VAL_HELPER, \
525 MOZ_ARGS_AFTER_3(__VA_ARGS__)), \
526 (MOZ_ARG_1(__VA_ARGS__), MOZ_ARG_2(__VA_ARGS__), \
527 MOZ_ARG_3(__VA_ARGS__) MOZ_ADD_ARGS(MOZ_ARGS_AFTER_3(__VA_ARGS__))))
529 // QM_TRY_PROPAGATE_ERR, QM_TRY_CUSTOM_RET_VAL,
530 // QM_TRY_CUSTOM_RET_VAL_WITH_CLEANUP and QM_TRY_GLUE macros are implementation
531 // details of QM_TRY and shouldn't be used directly.
533 // Handles the two arguments case when the error is propagated.
534 #define QM_TRY_PROPAGATE_ERR(tryResult, expr) \
535 auto tryResult = (expr); \
536 static_assert(std::is_empty_v<typename decltype(tryResult)::ok_type>); \
537 if (MOZ_UNLIKELY(tryResult.isErr())) { \
538 mozilla::dom::quota::QM_HANDLE_ERROR( \
539 expr, tryResult.inspectErr(), mozilla::dom::quota::Severity::Error); \
540 return tryResult.propagateErr(); \
543 // Handles the three arguments case when a custom return value needs to be
544 // returned
545 #define QM_TRY_CUSTOM_RET_VAL(tryResult, expr, customRetVal) \
546 auto tryResult = (expr); \
547 static_assert(std::is_empty_v<typename decltype(tryResult)::ok_type>); \
548 if (MOZ_UNLIKELY(tryResult.isErr())) { \
549 auto tryTempError MOZ_MAYBE_UNUSED = tryResult.unwrapErr(); \
550 mozilla::dom::quota::QM_HANDLE_ERROR( \
551 expr, tryTempError, mozilla::dom::quota::Severity::Error); \
552 constexpr const auto& func MOZ_MAYBE_UNUSED = __func__; \
553 return QM_HANDLE_CUSTOM_RET_VAL(func, expr, tryTempError, customRetVal); \
556 // Handles the four arguments case when a cleanup function needs to be called
557 // before a custom return value is returned
558 #define QM_TRY_CUSTOM_RET_VAL_WITH_CLEANUP(tryResult, expr, customRetVal, \
559 cleanup) \
560 auto tryResult = (expr); \
561 static_assert(std::is_empty_v<typename decltype(tryResult)::ok_type>); \
562 if (MOZ_UNLIKELY(tryResult.isErr())) { \
563 auto tryTempError = tryResult.unwrapErr(); \
564 mozilla::dom::quota::QM_HANDLE_ERROR( \
565 expr, tryTempError, mozilla::dom::quota::Severity::Error); \
566 cleanup(tryTempError); \
567 constexpr const auto& func MOZ_MAYBE_UNUSED = __func__; \
568 return QM_HANDLE_CUSTOM_RET_VAL(func, expr, tryTempError, customRetVal); \
571 #define QM_TRY_CUSTOM_RET_VAL_WITH_CLEANUP_AND_PREDICATE( \
572 tryResult, expr, customRetVal, cleanup, predicate) \
573 auto tryResult = (expr); \
574 static_assert(std::is_empty_v<typename decltype(tryResult)::ok_type>); \
575 if (MOZ_UNLIKELY(tryResult.isErr())) { \
576 auto tryTempError = tryResult.unwrapErr(); \
577 if (predicate()) { \
578 mozilla::dom::quota::QM_HANDLE_ERROR( \
579 expr, tryTempError, mozilla::dom::quota::Severity::Error); \
581 cleanup(tryTempError); \
582 constexpr const auto& func MOZ_MAYBE_UNUSED = __func__; \
583 return QM_HANDLE_CUSTOM_RET_VAL(func, expr, tryTempError, customRetVal); \
586 // Chooses the final implementation macro for given argument count.
587 // This could use MOZ_PASTE_PREFIX_AND_ARG_COUNT, but explicit named suffxes
588 // read slightly better than plain numbers.
589 // See also
590 // https://stackoverflow.com/questions/3046889/optional-parameters-with-c-macros
591 #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__)) \
601 // Generates unique variable name. This extra internal macro (along with
602 // __COUNTER__) allows nesting of the final macro.
603 #define QM_TRY_GLUE(...) QM_TRY_META(MOZ_UNIQUE_VAR(tryResult), ##__VA_ARGS__)
606 * QM_TRY(expr[, customRetVal, cleanup, predicate]) is the C++ equivalent of
607 * Rust's `try!(expr);`. First, it evaluates expr, which must produce a Result
608 * value with empty ok_type. On Success, it does nothing else. On error, it
609 * calls HandleError (conditionally if the fourth argument was passed) and an
610 * additional cleanup function (if the third argument was passed) and finally
611 * returns an error Result from the enclosing function or a custom return value
612 * (if the second argument was passed).
614 #define QM_TRY(...) QM_TRY_GLUE(__VA_ARGS__)
616 // QM_TRY_ASSIGN_PROPAGATE_ERR, QM_TRY_ASSIGN_CUSTOM_RET_VAL,
617 // QM_TRY_ASSIGN_CUSTOM_RET_VAL_WITH_CLEANUP and QM_TRY_ASSIGN_GLUE macros are
618 // implementation details of QM_TRY_UNWRAP/QM_TRY_INSPECT and shouldn't be used
619 // directly.
621 // Handles the four arguments case when the error is propagated.
622 #define QM_TRY_ASSIGN_PROPAGATE_ERR(tryResult, accessFunction, target, expr) \
623 auto tryResult = (expr); \
624 if (MOZ_UNLIKELY(tryResult.isErr())) { \
625 mozilla::dom::quota::QM_HANDLE_ERROR( \
626 expr, tryResult.inspectErr(), mozilla::dom::quota::Severity::Error); \
627 return tryResult.propagateErr(); \
629 MOZ_REMOVE_PAREN(target) = tryResult.accessFunction();
631 // Handles the five arguments case when a custom return value needs to be
632 // returned
633 #define QM_TRY_ASSIGN_CUSTOM_RET_VAL(tryResult, accessFunction, target, expr, \
634 customRetVal) \
635 auto tryResult = (expr); \
636 if (MOZ_UNLIKELY(tryResult.isErr())) { \
637 auto tryTempError MOZ_MAYBE_UNUSED = tryResult.unwrapErr(); \
638 mozilla::dom::quota::QM_HANDLE_ERROR( \
639 expr, tryTempError, mozilla::dom::quota::Severity::Error); \
640 constexpr const auto& func MOZ_MAYBE_UNUSED = __func__; \
641 return QM_HANDLE_CUSTOM_RET_VAL(func, expr, tryTempError, customRetVal); \
643 MOZ_REMOVE_PAREN(target) = tryResult.accessFunction();
645 // Handles the six arguments case when a cleanup function needs to be called
646 // before a custom return value is returned
647 #define QM_TRY_ASSIGN_CUSTOM_RET_VAL_WITH_CLEANUP( \
648 tryResult, accessFunction, target, expr, customRetVal, cleanup) \
649 auto tryResult = (expr); \
650 if (MOZ_UNLIKELY(tryResult.isErr())) { \
651 auto tryTempError = tryResult.unwrapErr(); \
652 mozilla::dom::quota::QM_HANDLE_ERROR( \
653 expr, tryTempError, mozilla::dom::quota::Severity::Error); \
654 cleanup(tryTempError); \
655 constexpr const auto& func MOZ_MAYBE_UNUSED = __func__; \
656 return QM_HANDLE_CUSTOM_RET_VAL(func, expr, tryTempError, customRetVal); \
658 MOZ_REMOVE_PAREN(target) = tryResult.accessFunction();
660 // Chooses the final implementation macro for given argument count.
661 // See also the comment for QM_TRY_META.
662 #define QM_TRY_ASSIGN_META(...) \
663 MOZ_ARG_8(, ##__VA_ARGS__, \
664 QM_TRY_ASSIGN_CUSTOM_RET_VAL_WITH_CLEANUP(__VA_ARGS__), \
665 QM_TRY_ASSIGN_CUSTOM_RET_VAL(__VA_ARGS__), \
666 QM_TRY_ASSIGN_PROPAGATE_ERR(__VA_ARGS__), \
667 QM_MISSING_ARGS(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__), \
668 QM_MISSING_ARGS(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__))
670 // Generates unique variable name. This extra internal macro (along with
671 // __COUNTER__) allows nesting of the final macro.
672 #define QM_TRY_ASSIGN_GLUE(accessFunction, ...) \
673 QM_TRY_ASSIGN_META(MOZ_UNIQUE_VAR(tryResult), accessFunction, ##__VA_ARGS__)
676 * QM_TRY_UNWRAP(target, expr[, customRetVal, cleanup]) is the C++ equivalent of
677 * Rust's `target = try!(expr);`. First, it evaluates expr, which must produce
678 * a Result value. On success, the result's success value is unwrapped and
679 * assigned to target. On error, it calls HandleError and an additional cleanup
680 * function (if the fourth argument was passed) and finally returns the error
681 * result or a custom return value (if the third argument was passed). |target|
682 * must be an lvalue.
684 #define QM_TRY_UNWRAP(...) QM_TRY_ASSIGN_GLUE(unwrap, __VA_ARGS__)
687 * QM_TRY_INSPECT is similar to QM_TRY_UNWRAP, but it does not unwrap a success
688 * value, but inspects it and binds it to the target. It can therefore only be
689 * used when the target declares a const&. In general,
691 * QM_TRY_INSPECT(const auto &target, DoSomething())
693 * should be preferred over
695 * QM_TRY_UNWRAP(const auto target, DoSomething())
697 * as it avoids unnecessary moves/copies.
699 #define QM_TRY_INSPECT(...) QM_TRY_ASSIGN_GLUE(inspect, __VA_ARGS__)
701 // QM_TRY_RETURN_PROPAGATE_ERR, QM_TRY_RETURN_CUSTOM_RET_VAL,
702 // QM_TRY_RETURN_CUSTOM_RET_VAL_WITH_CLEANUP and QM_TRY_RETURN_GLUE macros are
703 // implementation details of QM_TRY_RETURN and shouldn't be used directly.
705 // Handles the two arguments case when the error is (also) propagated.
706 // Note that this deliberately uses a single return statement without going
707 // through unwrap/unwrapErr/propagateErr, so that this does not prevent NRVO or
708 // tail call optimizations when possible.
709 #define QM_TRY_RETURN_PROPAGATE_ERR(tryResult, expr) \
710 auto tryResult = (expr); \
711 if (MOZ_UNLIKELY(tryResult.isErr())) { \
712 mozilla::dom::quota::QM_HANDLE_ERROR( \
713 expr, tryResult.inspectErr(), mozilla::dom::quota::Severity::Error); \
715 return tryResult;
717 // Handles the three arguments case when a custom return value needs to be
718 // returned
719 #define QM_TRY_RETURN_CUSTOM_RET_VAL(tryResult, expr, customRetVal) \
720 auto tryResult = (expr); \
721 if (MOZ_UNLIKELY(tryResult.isErr())) { \
722 auto tryTempError MOZ_MAYBE_UNUSED = tryResult.unwrapErr(); \
723 mozilla::dom::quota::QM_HANDLE_ERROR( \
724 expr, tryResult.inspectErr(), mozilla::dom::quota::Severity::Error); \
725 constexpr const auto& func MOZ_MAYBE_UNUSED = __func__; \
726 return QM_HANDLE_CUSTOM_RET_VAL(func, expr, tryTempError, customRetVal); \
728 return tryResult.unwrap();
730 // Handles the four arguments case when a cleanup function needs to be called
731 // before a custom return value is returned
732 #define QM_TRY_RETURN_CUSTOM_RET_VAL_WITH_CLEANUP(tryResult, expr, \
733 customRetVal, cleanup) \
734 auto tryResult = (expr); \
735 if (MOZ_UNLIKELY(tryResult.isErr())) { \
736 auto tryTempError = tryResult.unwrapErr(); \
737 mozilla::dom::quota::QM_HANDLE_ERROR( \
738 expr, tryTempError, mozilla::dom::quota::Severity::Error); \
739 cleanup(tryTempError); \
740 constexpr const auto& func MOZ_MAYBE_UNUSED = __func__; \
741 return QM_HANDLE_CUSTOM_RET_VAL(func, expr, tryTempError, customRetVal); \
743 return tryResult.unwrap();
745 // Chooses the final implementation macro for given argument count.
746 // See also the comment for QM_TRY_META.
747 #define QM_TRY_RETURN_META(...) \
749 MOZ_ARG_6(, ##__VA_ARGS__, \
750 QM_TRY_RETURN_CUSTOM_RET_VAL_WITH_CLEANUP(__VA_ARGS__), \
751 QM_TRY_RETURN_CUSTOM_RET_VAL(__VA_ARGS__), \
752 QM_TRY_RETURN_PROPAGATE_ERR(__VA_ARGS__), \
753 QM_MISSING_ARGS(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__)) \
756 // Generates unique variable name. This extra internal macro (along with
757 // __COUNTER__) allows nesting of the final macro.
758 #define QM_TRY_RETURN_GLUE(...) \
759 QM_TRY_RETURN_META(MOZ_UNIQUE_VAR(tryResult), ##__VA_ARGS__)
762 * QM_TRY_RETURN(expr[, customRetVal, cleanup]) evaluates expr, which must
763 * produce a Result value. On success, the result's success value is returned.
764 * On error, it calls HandleError and an additional cleanup function (if the
765 * third argument was passed) and finally returns the error result or a custom
766 * return value (if the second argument was passed).
768 #define QM_TRY_RETURN(...) QM_TRY_RETURN_GLUE(__VA_ARGS__)
770 // QM_FAIL_RET_VAL and QM_FAIL_RET_VAL_WITH_CLEANUP macros are implementation
771 // details of QM_FAIL and shouldn't be used directly.
773 // Handles the one argument case when just an error is returned
774 #define QM_FAIL_RET_VAL(retVal) \
775 mozilla::dom::quota::QM_HANDLE_ERROR(Failure, 0, \
776 mozilla::dom::quota::Severity::Error); \
777 return retVal;
779 // Handles the two arguments case when a cleanup function needs to be called
780 // before a return value is returned
781 #define QM_FAIL_RET_VAL_WITH_CLEANUP(retVal, cleanup) \
782 mozilla::dom::quota::QM_HANDLE_ERROR(Failure, 0, \
783 mozilla::dom::quota::Severity::Error); \
784 cleanup(); \
785 return retVal;
787 // Chooses the final implementation macro for given argument count.
788 // See also the comment for QM_TRY_META.
789 #define QM_FAIL_META(...) \
790 MOZ_ARG_4(, ##__VA_ARGS__, QM_FAIL_RET_VAL_WITH_CLEANUP(__VA_ARGS__), \
791 QM_FAIL_RET_VAL(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__))
793 // This extra internal macro allows nesting of the final macro.
794 #define QM_FAIL_GLUE(...) QM_FAIL_META(__VA_ARGS__)
797 * QM_FAIL(retVal[, cleanup]) calls HandleError and an additional cleanup
798 * function (if the second argument was passed) and returns a return value.
800 #define QM_FAIL(...) QM_FAIL_GLUE(__VA_ARGS__)
802 // QM_REPORTONLY_TRY, QM_REPORTONLY_TRY_WITH_CLEANUP, QM_REPORTONLY_TRY_GLUE
803 // macros are implementation details of QM_WARNONLY_TRY/QM_INFOONLY_TRY and
804 // shouldn't be used directly.
806 // Handles the three arguments case when only a warning/info is reported.
807 #define QM_REPORTONLY_TRY(tryResult, severity, expr) \
808 auto tryResult = (expr); \
809 static_assert(std::is_empty_v<typename decltype(tryResult)::ok_type>); \
810 if (MOZ_UNLIKELY(tryResult.isErr())) { \
811 mozilla::dom::quota::QM_HANDLE_ERROR( \
812 expr, tryResult.unwrapErr(), mozilla::dom::quota::Severity::severity); \
815 // Handles the four arguments case when a cleanup function needs to be called
816 #define QM_REPORTONLY_TRY_WITH_CLEANUP(tryResult, severity, expr, cleanup) \
817 auto tryResult = (expr); \
818 static_assert(std::is_empty_v<typename decltype(tryResult)::ok_type>); \
819 if (MOZ_UNLIKELY(tryResult.isErr())) { \
820 auto tryTempError = tryResult.unwrapErr(); \
821 mozilla::dom::quota::QM_HANDLE_ERROR( \
822 expr, tryTempError, mozilla::dom::quota::Severity::severity); \
823 cleanup(tryTempError); \
826 // Chooses the final implementation macro for given argument count.
827 // See also the comment for QM_TRY_META.
828 #define QM_REPORTONLY_TRY_META(...) \
830 MOZ_ARG_6(, ##__VA_ARGS__, QM_REPORTONLY_TRY_WITH_CLEANUP(__VA_ARGS__), \
831 QM_REPORTONLY_TRY(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__), \
832 QM_MISSING_ARGS(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__)) \
835 // Generates unique variable name. This extra internal macro (along with
836 // __COUNTER__) allows nesting of the final macro.
837 #define QM_REPORTONLY_TRY_GLUE(severity, ...) \
838 QM_REPORTONLY_TRY_META(MOZ_UNIQUE_VAR(tryResult), severity, ##__VA_ARGS__)
841 * QM_WARNONLY_TRY(expr[, cleanup]) evaluates expr, which must produce a
842 * Result value with empty ok_type. On Success, it does nothing else. On error,
843 * it calls HandleError and an additional cleanup function (if the second
844 * argument was passed). This macro never returns and failures are always
845 * reported using a lower level of severity relative to failures reported by
846 * QM_TRY. The macro is intended for failures that should not be propagated.
848 #define QM_WARNONLY_TRY(...) QM_REPORTONLY_TRY_GLUE(Warning, __VA_ARGS__)
851 * QM_INFOONLY_TRY is like QM_WARNONLY_TRY. The only difference is that
852 * failures are reported using a lower level of severity relative to failures
853 * reported by QM_WARNONLY_TRY.
855 #define QM_INFOONLY_TRY(...) QM_REPORTONLY_TRY_GLUE(Info, __VA_ARGS__)
857 // QM_REPORTONLY_TRY_ASSIGN, QM_REPORTONLY_TRY_ASSIGN_WITH_CLEANUP,
858 // QM_REPORTONLY_TRY_ASSIGN_GLUE macros are implementation details of
859 // QM_WARNONLY_TRY_UNWRAP/QM_INFOONLY_TRY_UNWRAP and shouldn't be used
860 // directly.
862 // Handles the four arguments case when only a warning/info is reported.
863 #define QM_REPORTONLY_TRY_ASSIGN(tryResult, severity, target, expr) \
864 auto tryResult = (expr); \
865 MOZ_REMOVE_PAREN(target) = \
866 MOZ_LIKELY(tryResult.isOk()) \
867 ? Some(tryResult.unwrap()) \
868 : mozilla::dom::quota::QM_HANDLE_ERROR_RETURN_NOTHING( \
869 expr, tryResult.unwrapErr(), \
870 mozilla::dom::quota::Severity::severity);
872 // Handles the five arguments case when a cleanup function needs to be called
873 #define QM_REPORTONLY_TRY_ASSIGN_WITH_CLEANUP(tryResult, severity, target, \
874 expr, cleanup) \
875 auto tryResult = (expr); \
876 MOZ_REMOVE_PAREN(target) = \
877 MOZ_LIKELY(tryResult.isOk()) \
878 ? Some(tryResult.unwrap()) \
879 : mozilla::dom::quota::QM_HANDLE_ERROR_WITH_CLEANUP_RETURN_NOTHING( \
880 expr, tryResult.unwrapErr(), \
881 mozilla::dom::quota::Severity::severity, cleanup);
883 // Chooses the final implementation macro for given argument count.
884 // See also the comment for QM_TRY_META.
885 #define QM_REPORTONLY_TRY_ASSIGN_META(...) \
886 MOZ_ARG_7(, ##__VA_ARGS__, \
887 QM_REPORTONLY_TRY_ASSIGN_WITH_CLEANUP(__VA_ARGS__), \
888 QM_REPORTONLY_TRY_ASSIGN(__VA_ARGS__), \
889 QM_MISSING_ARGS(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__), \
890 QM_MISSING_ARGS(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__))
892 // Generates unique variable name. This extra internal macro (along with
893 // __COUNTER__) allows nesting of the final macro.
894 #define QM_REPORTONLY_TRY_ASSIGN_GLUE(severity, ...) \
895 QM_REPORTONLY_TRY_ASSIGN_META(MOZ_UNIQUE_VAR(tryResult), severity, \
896 ##__VA_ARGS__)
899 * QM_WARNONLY_TRY_UNWRAP(target, expr[, cleanup]) evaluates expr, which must
900 * produce a Result value. On success, the result's success value is first
901 * unwrapped from the Result, then wrapped in a Maybe and finally assigned to
902 * target. On error, it calls HandleError and an additional cleanup
903 * function (if the third argument was passed) and finally assigns Nothing to
904 * target. |target| must be an lvalue. This macro never returns and failures
905 * are always reported using a lower level of severity relative to failures
906 * reported by QM_TRY_UNWRAP/QM_TRY_INSPECT. The macro is intended for failures
907 * that should not be propagated.
909 #define QM_WARNONLY_TRY_UNWRAP(...) \
910 QM_REPORTONLY_TRY_ASSIGN_GLUE(Warning, __VA_ARGS__)
912 // QM_WARNONLY_TRY_INSPECT doesn't make sense.
915 * QM_INFOONLY_TRY_UNWRAP is like QM_WARNONLY_TRY_UNWRAP. The only difference is
916 * that failures are reported using a lower level of severity relative to
917 * failures reported by QM_WARNONLY_TRY_UNWRAP.
919 #define QM_INFOONLY_TRY_UNWRAP(...) \
920 QM_REPORTONLY_TRY_ASSIGN_GLUE(Info, __VA_ARGS__)
922 // QM_INFOONLY_TRY_INSPECT doesn't make sense.
924 // QM_OR_ELSE_REPORT macro is an implementation detail of
925 // QM_OR_ELSE_WARN/QM_OR_ELSE_INFO/QM_OR_ELSE_LOG_VERBOSE and shouldn't be used
926 // directly.
928 #define QM_OR_ELSE_REPORT(severity, expr, fallback) \
929 (expr).orElse([&](const auto& firstRes) { \
930 mozilla::dom::quota::QM_HANDLE_ERROR( \
931 #expr, firstRes, mozilla::dom::quota::Severity::severity); \
932 return fallback(firstRes); \
936 * QM_OR_ELSE_WARN(expr, fallback) evaluates expr, which must produce a Result
937 * value. On Success, it just moves the success over. On error, it calls
938 * HandleError (with the Warning severity) and a fallback function (passed as
939 * the second argument) which produces a new result. Failed expr is always
940 * reported as a warning (the macro essentially wraps the fallback function
941 * with a warning). QM_OR_ELSE_WARN is a sub macro and is intended to be used
942 * along with one of the main macros such as QM_TRY.
944 #define QM_OR_ELSE_WARN(...) QM_OR_ELSE_REPORT(Warning, __VA_ARGS__)
947 * QM_OR_ELSE_INFO is like QM_OR_ELSE_WARN. The only difference is that
948 * failures are reported using a lower level of severity relative to failures
949 * reported by QM_OR_ELSE_WARN.
951 #define QM_OR_ELSE_INFO(...) QM_OR_ELSE_REPORT(Info, __VA_ARGS__)
954 * QM_OR_ELSE_LOG_VERBOSE is like QM_OR_ELSE_WARN. The only difference is that
955 * failures are reported using the lowest severity which is currently ignored
956 * in LogError, so nothing goes to the console, browser console and telemetry.
957 * Since nothing goes to the telemetry, the macro can't signal the end of the
958 * underlying error stack or change the type of the error stack in the
959 * telemetry. For that reason, the expression shouldn't contain nested QM_TRY
960 * macro uses.
962 #define QM_OR_ELSE_LOG_VERBOSE(...) QM_OR_ELSE_REPORT(Verbose, __VA_ARGS__)
964 namespace mozilla::dom::quota {
966 // XXX Support orElseIf directly in mozilla::Result
967 template <typename V, typename E, typename P, typename F>
968 auto OrElseIf(Result<V, E>&& aResult, P&& aPred, F&& aFunc) -> Result<V, E> {
969 return MOZ_UNLIKELY(aResult.isErr())
970 ? (std::forward<P>(aPred)(aResult.inspectErr()))
971 ? std::forward<F>(aFunc)(aResult.unwrapErr())
972 : aResult.propagateErr()
973 : aResult.unwrap();
976 } // namespace mozilla::dom::quota
978 // QM_OR_ELSE_REPORT_IF macro is an implementation detail of
979 // QM_OR_ELSE_WARN_IF/QM_OR_ELSE_INFO_IF/QM_OR_ELSE_LOG_VERBOSE_IF and
980 // shouldn't be used directly.
982 #define QM_OR_ELSE_REPORT_IF(severity, expr, predicate, fallback) \
983 mozilla::dom::quota::OrElseIf( \
984 (expr), \
985 [&](const auto& firstRes) { \
986 bool res = predicate(firstRes); \
987 mozilla::dom::quota::QM_HANDLE_ERROR( \
988 #expr, firstRes, \
989 res ? mozilla::dom::quota::Severity::severity \
990 : mozilla::dom::quota::Severity::Error); \
991 return res; \
992 }, \
993 fallback)
996 * QM_OR_ELSE_WARN_IF(expr, predicate, fallback) evaluates expr first, which
997 * must produce a Result value. On Success, it just moves the success over.
998 * On error, it calls a predicate function (passed as the second argument) and
999 * then it either calls HandleError (with the Warning severity) and a fallback
1000 * function (passed as the third argument) which produces a new result if the
1001 * predicate returned true. Or it calls HandleError (with the Error severity)
1002 * and propagates the error result if the predicate returned false. So failed
1003 * expr can be reported as a warning or as an error depending on the predicate.
1004 * QM_OR_ELSE_WARN_IF is a sub macro and is intended to be used along with one
1005 * of the main macros such as QM_TRY.
1007 #define QM_OR_ELSE_WARN_IF(...) QM_OR_ELSE_REPORT_IF(Warning, __VA_ARGS__)
1010 * QM_OR_ELSE_INFO_IF is like QM_OR_ELSE_WARN_IF. The only difference is that
1011 * failures are reported using a lower level of severity relative to failures
1012 * reported by QM_OR_ELSE_WARN_IF.
1014 #define QM_OR_ELSE_INFO_IF(...) QM_OR_ELSE_REPORT_IF(Info, __VA_ARGS__)
1017 * QM_OR_ELSE_LOG_VERBOSE_IF is like QM_OR_ELSE_WARN_IF. The only difference is
1018 * that failures are reported using the lowest severity which is currently
1019 * ignored in LogError, so nothing goes to the console, browser console and
1020 * telemetry. Since nothing goes to the telemetry, the macro can't signal the
1021 * end of the underlying error stack or change the type of the error stack in
1022 * the telemetry. For that reason, the expression shouldn't contain nested
1023 * QM_TRY macro uses.
1025 #define QM_OR_ELSE_LOG_VERBOSE_IF(...) \
1026 QM_OR_ELSE_REPORT_IF(Verbose, __VA_ARGS__)
1028 // Telemetry probes to collect number of failure during the initialization.
1029 #ifdef NIGHTLY_BUILD
1030 # define RECORD_IN_NIGHTLY(_recorder, _status) \
1031 do { \
1032 if (NS_SUCCEEDED(_recorder)) { \
1033 _recorder = _status; \
1035 } while (0)
1037 # define OK_IN_NIGHTLY_PROPAGATE_IN_OTHERS \
1038 Ok {}
1040 # define RETURN_STATUS_OR_RESULT(_status, _rv) \
1041 return Err(NS_FAILED(_status) ? (_status) : (_rv))
1042 #else
1043 # define RECORD_IN_NIGHTLY(_dummy, _status) \
1046 # define OK_IN_NIGHTLY_PROPAGATE_IN_OTHERS QM_PROPAGATE
1048 # define RETURN_STATUS_OR_RESULT(_status, _rv) return Err(_rv)
1049 #endif
1051 class mozIStorageConnection;
1052 class mozIStorageStatement;
1053 class nsIFile;
1055 namespace mozilla {
1057 class LogModule;
1059 struct CreateIfNonExistent {};
1061 struct NotOk {};
1063 // Allow MOZ_TRY/QM_TRY to handle `bool` values by wrapping them with OkIf.
1064 // TODO: Maybe move this to mfbt/ResultExtensions.h
1065 inline Result<Ok, NotOk> OkIf(bool aValue) {
1066 if (aValue) {
1067 return Ok();
1069 return Err(NotOk());
1072 // TODO: Maybe move this to mfbt/ResultExtensions.h
1073 template <auto SuccessValue>
1074 auto OkToOk(Ok) -> Result<decltype(SuccessValue), nsresult> {
1075 return SuccessValue;
1078 template <nsresult ErrorValue, auto SuccessValue,
1079 typename V = decltype(SuccessValue)>
1080 auto ErrToOkOrErr(nsresult aValue) -> Result<V, nsresult> {
1081 if (aValue == ErrorValue) {
1082 return V{SuccessValue};
1084 return Err(aValue);
1087 template <nsresult ErrorValue, typename V = mozilla::Ok>
1088 auto ErrToDefaultOkOrErr(nsresult aValue) -> Result<V, nsresult> {
1089 if (aValue == ErrorValue) {
1090 return V{};
1092 return Err(aValue);
1095 // Helper template function so that QM_TRY predicates checking for a specific
1096 // error can be concisely written as IsSpecificError<NS_SOME_ERROR> instead of
1097 // as a more verbose lambda.
1098 template <nsresult ErrorValue>
1099 bool IsSpecificError(const nsresult aValue) {
1100 return aValue == ErrorValue;
1103 #ifdef QM_ERROR_STACKS_ENABLED
1104 template <nsresult ErrorValue>
1105 bool IsSpecificError(const QMResult& aValue) {
1106 return aValue.NSResult() == ErrorValue;
1108 #endif
1110 // Helper template function so that QM_TRY fallback functions that are
1111 // converting errors into specific in-band success values can be concisely
1112 // written as ErrToOk<SuccessValueToReturn> (with the return type inferred).
1113 // For example, many file-related APIs that access information about a file may
1114 // return an nsresult error code if the file does not exist. From an
1115 // application perspective, the file not existing is not actually exceptional
1116 // and can instead be handled by the success case.
1117 template <auto SuccessValue, typename V = decltype(SuccessValue)>
1118 auto ErrToOk(const nsresult aValue) -> Result<V, nsresult> {
1119 return V{SuccessValue};
1122 template <auto SuccessValue, typename V = decltype(SuccessValue)>
1123 auto ErrToOkFromQMResult(const QMResult& aValue) -> Result<V, QMResult> {
1124 return V{SuccessValue};
1127 // Helper template function so that QM_TRY fallback functions that are
1128 // suppressing errors by converting them into (generic) success can be
1129 // concisely written as ErrToDefaultOk<>.
1130 template <typename V = mozilla::Ok>
1131 auto ErrToDefaultOk(const nsresult aValue) -> Result<V, nsresult> {
1132 return V{};
1135 template <typename MozPromiseType, typename RejectValueT = nsresult>
1136 auto CreateAndRejectMozPromise(const char* aFunc, const RejectValueT& aRv)
1137 -> decltype(auto) {
1138 if constexpr (std::is_same_v<RejectValueT, nsresult>) {
1139 return MozPromiseType::CreateAndReject(aRv, aFunc);
1140 } else if constexpr (std::is_same_v<RejectValueT, QMResult>) {
1141 return MozPromiseType::CreateAndReject(aRv.NSResult(), aFunc);
1145 RefPtr<BoolPromise> CreateAndRejectBoolPromise(const char* aFunc, nsresult aRv);
1147 RefPtr<Int64Promise> CreateAndRejectInt64Promise(const char* aFunc,
1148 nsresult aRv);
1150 RefPtr<BoolPromise> CreateAndRejectBoolPromiseFromQMResult(const char* aFunc,
1151 const QMResult& aRv);
1153 // Like Rust's collect with a step function, not a generic iterator/range.
1155 // Cond must be a function type with a return type to Result<V, E>, where
1156 // V is convertible to bool
1157 // - success converts to true indicates that collection shall continue
1158 // - success converts to false indicates that collection is completed
1159 // - error indicates that collection shall stop, propagating the error
1161 // Body must a function type accepting a V xvalue with a return type convertible
1162 // to Result<empty, E>.
1163 template <typename Step, typename Body>
1164 auto CollectEach(Step aStep, const Body& aBody)
1165 -> Result<mozilla::Ok, typename std::invoke_result_t<Step>::err_type> {
1166 using StepResultType = typename std::invoke_result_t<Step>::ok_type;
1168 static_assert(
1169 std::is_empty_v<
1170 typename std::invoke_result_t<Body, StepResultType&&>::ok_type>);
1172 while (true) {
1173 StepResultType element;
1174 MOZ_TRY_VAR(element, aStep());
1176 if (!static_cast<bool>(element)) {
1177 break;
1180 MOZ_TRY(aBody(std::move(element)));
1183 return mozilla::Ok{};
1186 // This is like std::reduce with a to-be-defined execution policy (we don't want
1187 // to std::terminate on an error, but probably it's fine to just propagate any
1188 // error that occurred), operating not on a pair of iterators but rather a
1189 // generator function.
1190 template <typename InputGenerator, typename T, typename BinaryOp>
1191 auto ReduceEach(InputGenerator aInputGenerator, T aInit,
1192 const BinaryOp& aBinaryOp)
1193 -> Result<T, typename std::invoke_result_t<InputGenerator>::err_type> {
1194 T res = std::move(aInit);
1196 // XXX This can be done in parallel!
1197 MOZ_TRY(CollectEach(
1198 std::move(aInputGenerator),
1199 [&res, &aBinaryOp](const auto& element)
1200 -> Result<Ok,
1201 typename std::invoke_result_t<InputGenerator>::err_type> {
1202 MOZ_TRY_VAR(res, aBinaryOp(std::move(res), element));
1204 return Ok{};
1205 }));
1207 return std::move(res);
1210 // This is like std::reduce with a to-be-defined execution policy (we don't want
1211 // to std::terminate on an error, but probably it's fine to just propagate any
1212 // error that occurred).
1213 template <typename Range, typename T, typename BinaryOp>
1214 auto Reduce(Range&& aRange, T aInit, const BinaryOp& aBinaryOp) {
1215 using std::begin;
1216 using std::end;
1217 return ReduceEach(
1218 [it = begin(aRange), end = end(aRange)]() mutable {
1219 auto res = ToMaybeRef(it != end ? &*it++ : nullptr);
1220 return Result<decltype(res), typename std::invoke_result_t<
1221 BinaryOp, T, decltype(res)>::err_type>(
1222 res);
1224 aInit, aBinaryOp);
1227 template <typename Range, typename Body>
1228 auto CollectEachInRange(Range&& aRange, const Body& aBody)
1229 -> Result<mozilla::Ok, nsresult> {
1230 for (auto&& element : aRange) {
1231 MOZ_TRY(aBody(element));
1234 return mozilla::Ok{};
1237 // Like Rust's collect with a while loop, not a generic iterator/range.
1239 // Cond must be a function type accepting no parameters with a return type
1240 // convertible to Result<bool, E>, where
1241 // - success true indicates that collection shall continue
1242 // - success false indicates that collection is completed
1243 // - error indicates that collection shall stop, propagating the error
1245 // Body must a function type accepting no parameters with a return type
1246 // convertible to Result<empty, E>.
1247 template <typename Cond, typename Body>
1248 auto CollectWhile(const Cond& aCond, const Body& aBody)
1249 -> Result<mozilla::Ok, typename std::invoke_result_t<Cond>::err_type> {
1250 return CollectEach(aCond, [&aBody](bool) { return aBody(); });
1253 template <>
1254 class [[nodiscard]] GenericErrorResult<mozilla::ipc::IPCResult> {
1255 mozilla::ipc::IPCResult mErrorValue;
1257 template <typename V, typename E2>
1258 friend class Result;
1260 public:
1261 explicit GenericErrorResult(mozilla::ipc::IPCResult aErrorValue)
1262 : mErrorValue(aErrorValue) {
1263 MOZ_ASSERT(!aErrorValue);
1266 GenericErrorResult(mozilla::ipc::IPCResult aErrorValue,
1267 const ErrorPropagationTag&)
1268 : GenericErrorResult(aErrorValue) {}
1270 operator mozilla::ipc::IPCResult() const { return mErrorValue; }
1273 namespace dom::quota {
1275 extern const char kQuotaGenericDelimiter;
1277 // Telemetry keys to indicate types of errors.
1278 #ifdef NIGHTLY_BUILD
1279 extern const nsLiteralCString kQuotaInternalError;
1280 extern const nsLiteralCString kQuotaExternalError;
1281 #else
1282 // No need for these when we're not collecting telemetry.
1283 # define kQuotaInternalError
1284 # define kQuotaExternalError
1285 #endif
1287 class BackgroundThreadObject {
1288 protected:
1289 nsCOMPtr<nsISerialEventTarget> mOwningThread;
1291 public:
1292 void AssertIsOnOwningThread() const
1293 #ifdef DEBUG
1295 #else
1298 #endif
1300 nsISerialEventTarget* OwningThread() const;
1302 protected:
1303 BackgroundThreadObject();
1305 explicit BackgroundThreadObject(nsISerialEventTarget* aOwningThread);
1308 MOZ_COLD void ReportInternalError(const char* aFile, uint32_t aLine,
1309 const char* aStr);
1311 LogModule* GetQuotaManagerLogger();
1313 void AnonymizeCString(nsACString& aCString);
1315 inline auto AnonymizedCString(const nsACString& aCString) {
1316 nsAutoCString result{aCString};
1317 AnonymizeCString(result);
1318 return result;
1321 void AnonymizeOriginString(nsACString& aOriginString);
1323 inline auto AnonymizedOriginString(const nsACString& aOriginString) {
1324 nsAutoCString result{aOriginString};
1325 AnonymizeOriginString(result);
1326 return result;
1329 #ifdef XP_WIN
1330 void CacheUseDOSDevicePathSyntaxPrefValue();
1331 #endif
1333 Result<nsCOMPtr<nsIFile>, nsresult> QM_NewLocalFile(const nsAString& aPath);
1335 nsDependentCSubstring GetLeafName(const nsACString& aPath);
1337 Result<nsCOMPtr<nsIFile>, nsresult> CloneFileAndAppend(
1338 nsIFile& aDirectory, const nsAString& aPathElement);
1340 enum class nsIFileKind {
1341 ExistsAsDirectory,
1342 ExistsAsFile,
1343 DoesNotExist,
1346 // XXX We can use this outside of QM and its clients as well, probably. Maybe it
1347 // could be moved to xpcom/io?
1348 Result<nsIFileKind, nsresult> GetDirEntryKind(nsIFile& aFile);
1350 Result<nsCOMPtr<mozIStorageStatement>, nsresult> CreateStatement(
1351 mozIStorageConnection& aConnection, const nsACString& aStatementString);
1353 enum class SingleStepResult { AssertHasResult, ReturnNullIfNoResult };
1355 template <SingleStepResult ResultHandling>
1356 using SingleStepSuccessType =
1357 std::conditional_t<ResultHandling == SingleStepResult::AssertHasResult,
1358 NotNull<nsCOMPtr<mozIStorageStatement>>,
1359 nsCOMPtr<mozIStorageStatement>>;
1361 template <SingleStepResult ResultHandling>
1362 Result<SingleStepSuccessType<ResultHandling>, nsresult> ExecuteSingleStep(
1363 nsCOMPtr<mozIStorageStatement>&& aStatement);
1365 // Creates a statement with the specified aStatementString, executes a single
1366 // step, and returns the statement.
1367 // Depending on the value ResultHandling,
1368 // - it is asserted that there is a result (default resp.
1369 // SingleStepResult::AssertHasResult), and the success type is
1370 // MovingNotNull<nsCOMPtr<mozIStorageStatement>>
1371 // - it is asserted that there is no result, and the success type is Ok
1372 // - in case there is no result, nullptr is returned, and the success type is
1373 // nsCOMPtr<mozIStorageStatement>
1374 // Any other errors are always propagated.
1375 template <SingleStepResult ResultHandling = SingleStepResult::AssertHasResult>
1376 Result<SingleStepSuccessType<ResultHandling>, nsresult>
1377 CreateAndExecuteSingleStepStatement(mozIStorageConnection& aConnection,
1378 const nsACString& aStatementString);
1380 namespace detail {
1382 // Determine the absolute path of the root of our built source tree so we can
1383 // derive source-relative paths for non-exported header files in
1384 // MakeSourceFileRelativePath. Exported header files end up in the objdir and
1385 // we have GetObjdirDistIncludeTreeBase for that.
1386 nsDependentCSubstring GetSourceTreeBase();
1388 // Determine the absolute path of the root of our built OBJDIR/dist/include
1389 // directory. The aQuotaCommonHPath argument cleverly defaults to __FILE__
1390 // initialized in our exported header; no argument should ever be provided to
1391 // this method. GetSourceTreeBase handles identifying the root of the source
1392 // tree.
1393 nsDependentCSubstring GetObjdirDistIncludeTreeBase(
1394 const nsLiteralCString& aQuotaCommonHPath = nsLiteralCString(__FILE__));
1396 nsDependentCSubstring MakeSourceFileRelativePath(
1397 const nsACString& aSourceFilePath);
1399 } // namespace detail
1401 enum class Severity {
1402 Error,
1403 Warning,
1404 Info,
1405 Verbose,
1408 #ifdef QM_LOG_ERROR_ENABLED
1409 # ifdef QM_ERROR_STACKS_ENABLED
1410 using ResultType = Variant<QMResult, nsresult, Nothing>;
1412 void LogError(const nsACString& aExpr, const ResultType& aResult,
1413 const nsACString& aSourceFilePath, int32_t aSourceFileLine,
1414 Severity aSeverity)
1415 # else
1416 void LogError(const nsACString& aExpr, Maybe<nsresult> aMaybeRv,
1417 const nsACString& aSourceFilePath, int32_t aSourceFileLine,
1418 Severity aSeverity)
1419 # endif
1421 #endif
1423 #ifdef DEBUG
1424 Result<bool, nsresult> WarnIfFileIsUnknown(nsIFile& aFile,
1425 const char* aSourceFilePath,
1426 int32_t aSourceFileLine);
1427 #endif
1429 // As HandleError is a function that will only be called in error cases, it is
1430 // marked with MOZ_COLD to avoid bloating the code of calling functions, if it's
1431 // not empty.
1433 // For the same reason, the string-ish parameters are of type const char* rather
1434 // than any ns*String type, to minimize the code at each call site. This
1435 // deliberately de-optimizes runtime performance, which is uncritical during
1436 // error handling.
1438 // This functions are not intended to be called
1439 // directly, they should only be called from the QM_* macros.
1440 #ifdef QM_LOG_ERROR_ENABLED
1441 template <typename T>
1442 MOZ_COLD MOZ_NEVER_INLINE void HandleError(const char* aExpr, const T& aRv,
1443 const char* aSourceFilePath,
1444 int32_t aSourceFileLine,
1445 const Severity aSeverity) {
1446 # ifdef QM_ERROR_STACKS_ENABLED
1447 if constexpr (std::is_same_v<T, QMResult> || std::is_same_v<T, nsresult>) {
1448 mozilla::dom::quota::LogError(nsDependentCString(aExpr), ResultType(aRv),
1449 nsDependentCString(aSourceFilePath),
1450 aSourceFileLine, aSeverity);
1451 } else {
1452 mozilla::dom::quota::LogError(
1453 nsDependentCString(aExpr), ResultType(Nothing{}),
1454 nsDependentCString(aSourceFilePath), aSourceFileLine, aSeverity);
1456 # else
1457 if constexpr (std::is_same_v<T, nsresult>) {
1458 mozilla::dom::quota::LogError(nsDependentCString(aExpr), Some(aRv),
1459 nsDependentCString(aSourceFilePath),
1460 aSourceFileLine, aSeverity);
1461 } else {
1462 mozilla::dom::quota::LogError(nsDependentCString(aExpr), Nothing{},
1463 nsDependentCString(aSourceFilePath),
1464 aSourceFileLine, aSeverity);
1466 # endif
1468 #else
1469 template <typename T>
1470 MOZ_ALWAYS_INLINE constexpr void HandleError(const char* aExpr, const T& aRv,
1471 const char* aSourceFilePath,
1472 int32_t aSourceFileLine,
1473 const Severity aSeverity) {}
1474 #endif
1476 template <typename T>
1477 Nothing HandleErrorReturnNothing(const char* aExpr, const T& aRv,
1478 const char* aSourceFilePath,
1479 int32_t aSourceFileLine,
1480 const Severity aSeverity) {
1481 HandleError(aExpr, aRv, aSourceFilePath, aSourceFileLine, aSeverity);
1482 return Nothing();
1485 template <typename T, typename CleanupFunc>
1486 Nothing HandleErrorWithCleanupReturnNothing(const char* aExpr, const T& aRv,
1487 const char* aSourceFilePath,
1488 int32_t aSourceFileLine,
1489 const Severity aSeverity,
1490 CleanupFunc&& aCleanupFunc) {
1491 HandleError(aExpr, aRv, aSourceFilePath, aSourceFileLine, aSeverity);
1492 std::forward<CleanupFunc>(aCleanupFunc)(aRv);
1493 return Nothing();
1496 template <size_t NFunc, size_t NExpr, typename T, typename CustomRetVal>
1497 auto HandleCustomRetVal(const char (&aFunc)[NFunc], const char (&aExpr)[NExpr],
1498 const T& aRv, CustomRetVal&& aCustomRetVal) {
1499 if constexpr (std::is_invocable<CustomRetVal, const char[NFunc],
1500 const char[NExpr]>::value) {
1501 return std::forward<CustomRetVal>(aCustomRetVal)(aFunc, aExpr);
1502 } else if constexpr (std::is_invocable<CustomRetVal, const char[NFunc],
1503 const T&>::value) {
1504 return aCustomRetVal(aFunc, aRv);
1505 } else if constexpr (std::is_invocable<CustomRetVal, const T&>::value) {
1506 return aCustomRetVal(aRv);
1507 } else {
1508 return std::forward<CustomRetVal>(aCustomRetVal);
1512 template <SingleStepResult ResultHandling = SingleStepResult::AssertHasResult,
1513 typename BindFunctor>
1514 Result<SingleStepSuccessType<ResultHandling>, nsresult>
1515 CreateAndExecuteSingleStepStatement(mozIStorageConnection& aConnection,
1516 const nsACString& aStatementString,
1517 BindFunctor aBindFunctor) {
1518 QM_TRY_UNWRAP(auto stmt, CreateStatement(aConnection, aStatementString));
1520 QM_TRY(aBindFunctor(*stmt));
1522 return ExecuteSingleStep<ResultHandling>(std::move(stmt));
1525 template <typename StepFunc>
1526 Result<Ok, nsresult> CollectWhileHasResult(mozIStorageStatement& aStmt,
1527 StepFunc&& aStepFunc) {
1528 return CollectWhile(
1529 [&aStmt] {
1530 QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER(aStmt, ExecuteStep));
1532 [&aStmt, &aStepFunc] { return aStepFunc(aStmt); });
1535 template <typename StepFunc,
1536 typename ArrayType = nsTArray<typename std::invoke_result_t<
1537 StepFunc, mozIStorageStatement&>::ok_type>>
1538 auto CollectElementsWhileHasResult(mozIStorageStatement& aStmt,
1539 StepFunc&& aStepFunc)
1540 -> Result<ArrayType, nsresult> {
1541 ArrayType res;
1543 QM_TRY(CollectWhileHasResult(
1544 aStmt, [&aStepFunc, &res](auto& stmt) -> Result<Ok, nsresult> {
1545 QM_TRY_UNWRAP(auto element, aStepFunc(stmt));
1546 res.AppendElement(std::move(element));
1547 return Ok{};
1548 }));
1550 return std::move(res);
1553 template <typename ArrayType, typename StepFunc>
1554 auto CollectElementsWhileHasResultTyped(mozIStorageStatement& aStmt,
1555 StepFunc&& aStepFunc) {
1556 return CollectElementsWhileHasResult<StepFunc, ArrayType>(
1557 aStmt, std::forward<StepFunc>(aStepFunc));
1560 namespace detail {
1561 template <typename Cancel, typename Body>
1562 Result<mozilla::Ok, nsresult> CollectEachFile(nsIFile& aDirectory,
1563 const Cancel& aCancel,
1564 const Body& aBody) {
1565 QM_TRY_INSPECT(const auto& entries, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
1566 nsCOMPtr<nsIDirectoryEnumerator>,
1567 aDirectory, GetDirectoryEntries));
1569 return CollectEach(
1570 [&entries, &aCancel]() -> Result<nsCOMPtr<nsIFile>, nsresult> {
1571 if (aCancel()) {
1572 return nsCOMPtr<nsIFile>{};
1575 QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsCOMPtr<nsIFile>,
1576 entries, GetNextFile));
1578 aBody);
1580 } // namespace detail
1582 template <typename Body>
1583 Result<mozilla::Ok, nsresult> CollectEachFile(nsIFile& aDirectory,
1584 const Body& aBody) {
1585 return detail::CollectEachFile(
1586 aDirectory, [] { return false; }, aBody);
1589 template <typename Body>
1590 Result<mozilla::Ok, nsresult> CollectEachFileAtomicCancelable(
1591 nsIFile& aDirectory, const Atomic<bool>& aCanceled, const Body& aBody) {
1592 return detail::CollectEachFile(
1593 aDirectory, [&aCanceled] { return static_cast<bool>(aCanceled); }, aBody);
1596 template <typename T, typename Body>
1597 auto ReduceEachFileAtomicCancelable(nsIFile& aDirectory,
1598 const Atomic<bool>& aCanceled, T aInit,
1599 const Body& aBody) -> Result<T, nsresult> {
1600 QM_TRY_INSPECT(const auto& entries, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
1601 nsCOMPtr<nsIDirectoryEnumerator>,
1602 aDirectory, GetDirectoryEntries));
1604 return ReduceEach(
1605 [&entries, &aCanceled]() -> Result<nsCOMPtr<nsIFile>, nsresult> {
1606 if (aCanceled) {
1607 return nsCOMPtr<nsIFile>{};
1610 QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsCOMPtr<nsIFile>,
1611 entries, GetNextFile));
1613 std::move(aInit), aBody);
1616 constexpr bool IsDatabaseCorruptionError(const nsresult aRv) {
1617 return aRv == NS_ERROR_FILE_CORRUPTED || aRv == NS_ERROR_STORAGE_IOERR;
1620 template <typename Func>
1621 auto CallWithDelayedRetriesIfAccessDenied(Func&& aFunc, uint32_t aMaxRetries,
1622 uint32_t aDelayMs)
1623 -> Result<typename std::invoke_result_t<Func>::ok_type, nsresult> {
1624 uint32_t retries = 0;
1626 while (true) {
1627 auto result = std::forward<Func>(aFunc)();
1629 if (result.isOk()) {
1630 return result;
1633 if (result.inspectErr() != NS_ERROR_FILE_IS_LOCKED &&
1634 result.inspectErr() != NS_ERROR_FILE_ACCESS_DENIED) {
1635 return result;
1638 if (retries++ >= aMaxRetries) {
1639 return result;
1642 PR_Sleep(PR_MillisecondsToInterval(aDelayMs));
1646 namespace detail {
1648 template <bool flag = false>
1649 void UnsupportedReturnType() {
1650 static_assert(flag, "Unsupported return type!");
1653 } // namespace detail
1655 template <typename Initialization, typename StringGenerator, typename Func>
1656 auto ExecuteInitialization(
1657 FirstInitializationAttempts<Initialization, StringGenerator>&
1658 aFirstInitializationAttempts,
1659 const Initialization aInitialization, Func&& aFunc)
1660 -> std::invoke_result_t<Func, const FirstInitializationAttempt<
1661 Initialization, StringGenerator>&> {
1662 return aFirstInitializationAttempts.WithFirstInitializationAttempt(
1663 aInitialization, [&aFunc](auto&& firstInitializationAttempt) {
1664 auto res = std::forward<Func>(aFunc)(firstInitializationAttempt);
1666 const auto rv = [&res]() -> nsresult {
1667 using RetType =
1668 std::invoke_result_t<Func, const FirstInitializationAttempt<
1669 Initialization, StringGenerator>&>;
1671 if constexpr (std::is_same_v<RetType, nsresult>) {
1672 return res;
1673 } else if constexpr (mozilla::detail::IsResult<RetType>::value &&
1674 std::is_same_v<typename RetType::err_type,
1675 nsresult>) {
1676 return res.isOk() ? NS_OK : res.inspectErr();
1677 } else {
1678 detail::UnsupportedReturnType();
1680 }();
1682 // NS_ERROR_ABORT signals a non-fatal, recoverable problem during
1683 // initialization. We do not want these kind of failures to count
1684 // against our overall first initialization attempt telemetry. Thus we
1685 // just ignore this kind of failure and keep
1686 // aFirstInitializationAttempts unflagged to stay ready to record a real
1687 // success or failure on the next attempt.
1688 if (rv == NS_ERROR_ABORT) {
1689 return res;
1692 if (!firstInitializationAttempt.Recorded()) {
1693 firstInitializationAttempt.Record(rv);
1696 return res;
1700 template <typename Initialization, typename StringGenerator, typename Func>
1701 auto ExecuteInitialization(
1702 FirstInitializationAttempts<Initialization, StringGenerator>&
1703 aFirstInitializationAttempts,
1704 const Initialization aInitialization, const nsACString& aContext,
1705 Func&& aFunc)
1706 -> std::invoke_result_t<Func, const FirstInitializationAttempt<
1707 Initialization, StringGenerator>&> {
1708 return ExecuteInitialization(
1709 aFirstInitializationAttempts, aInitialization,
1710 [&](const auto& firstInitializationAttempt) -> decltype(auto) {
1711 #ifdef QM_SCOPED_LOG_EXTRA_INFO_ENABLED
1712 const auto maybeScopedLogExtraInfo =
1713 firstInitializationAttempt.Recorded()
1714 ? Nothing{}
1715 : Some(ScopedLogExtraInfo{
1716 ScopedLogExtraInfo::kTagContextTainted, aContext});
1717 #endif
1719 return std::forward<Func>(aFunc)(firstInitializationAttempt);
1723 } // namespace dom::quota
1724 } // namespace mozilla
1726 #endif // mozilla_dom_quota_quotacommon_h__