Backed out changeset f594e6f00208 (bug 1940883) for causing crashes in bug 1941164.
[gecko.git] / dom / quota / QuotaCommon.h
blobaadf8936cbd7f21cb77a49e24b6d0f5f1fe69631
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/StaticString.h"
26 #include "mozilla/Try.h"
27 #if defined(QM_LOG_ERROR_ENABLED) && defined(QM_ERROR_STACKS_ENABLED)
28 # include "mozilla/Variant.h"
29 #endif
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"
35 #include "nsCOMPtr.h"
36 #include "nsDebug.h"
37 #include "nsError.h"
38 #include "nsIDirectoryEnumerator.h"
39 #include "nsIEventTarget.h"
40 #include "nsIFile.h"
41 #include "nsLiteralString.h"
42 #include "nsPrintfCString.h"
43 #include "nsReadableUtils.h"
44 #include "nsString.h"
45 #include "nsTArray.h"
46 #include "nsTLiteralString.h"
48 namespace mozilla {
49 template <typename T>
50 class NotNull;
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(...) \
76 do { \
77 nsPrintfCString str(__VA_ARGS__); \
78 mozilla::dom::quota::ReportInternalError(__FILE__, __LINE__, str.get()); \
79 NS_WARNING(str.get()); \
80 } while (0)
82 #define QM_LOG_TEST() MOZ_LOG_TEST(GetQuotaManagerLogger(), LogLevel::Info)
83 #define QM_LOG(_args) MOZ_LOG(GetQuotaManagerLogger(), LogLevel::Info, _args)
85 #ifdef DEBUG
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()) \
90 .get())
91 #else
92 # define UNKNOWN_FILE_WARNING(_leafName) (void)(_leafName);
93 #endif
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.
100 #ifdef DEBUG
101 # define WARN_IF_FILE_IS_UNKNOWN(_file) \
102 mozilla::dom::quota::WarnIfFileIsUnknown(_file, __FILE__, __LINE__)
103 #else
104 # define WARN_IF_FILE_IS_UNKNOWN(_file) Result<bool, nsresult>(false)
105 #endif
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
111 * new code):
113 * 1. Using QM_TRY/QM_TRY_UNWRAP/QM_TRY_INSPECT/QM_TRY_RETURN/QM_FAIL macros
114 * (Quota manager specific, defined below)
116 * Typical use cases:
118 * nsresult MyFunc1(nsIFile& aFile) {
119 * bool exists;
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.
125 * return NS_OK;
128 * nsresult MyFunc2(nsIFile& aFile) {
129 * bool exists;
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.
135 * return NS_OK;
138 * void MyFunc3(nsIFile& aFile) {
139 * bool exists;
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) {
147 * bool exists;
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.
155 * return NS_OK;
158 * nsresult MyFunc5(nsIFile& aFile) {
159 * bool exists;
160 * QM_TRY(aFile.Exists(&exists));
161 * if (exists) {
162 * // The file exists, and data could be read from it here.
163 * } else {
164 * QM_FAIL(NS_ERROR_FAILURE);
167 * return NS_OK;
170 * nsresult MyFunc6(nsIFile& aFile) {
171 * bool exists;
172 * QM_TRY(aFile.Exists(&exists));
173 * if (exists) {
174 * // The file exists, and data could be read from it here.
175 * } else {
176 * QM_FAIL(NS_ERROR_FAILURE,
177 * []() { NS_WARNING("The file doesn't exist!"); });
180 * return NS_OK;
183 * 2. Using MOZ_TRY/MOZ_TRY_VAR macros
185 * Typical use cases:
187 * nsresult MyFunc1(nsIFile& aFile) {
188 * // MOZ_TRY can't return a custom return value
190 * return NS_OK;
193 * nsresult MyFunc2(nsIFile& aFile) {
194 * // MOZ_TRY can't return a custom return value
196 * return NS_OK;
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
207 * return NS_OK;
210 * nsresult MyFunc5(nsIFile& aFile) {
211 * // There's no MOZ_FAIL, MOZ_TRY can't return a custom return value
213 * return NS_OK;
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
220 * return NS_OK;
223 * 3. Using NS_WARN_IF and NS_WARNING macro with own control flow handling
225 * Typical use cases:
227 * nsresult MyFunc1(nsIFile& aFile) {
228 * bool exists;
229 * nsresult rv = aFile.Exists(&exists);
230 * if (NS_WARN_IF(NS_FAILED(rv)) {
231 * return rv;
233 * if (NS_WARN_IF(!exists) {
234 * return NS_ERROR_FAILURE;
237 * // The file exists, and data could be read from it here.
239 * return NS_OK;
242 * nsresult MyFunc2(nsIFile& aFile) {
243 * bool exists;
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.
254 * return NS_OK;
257 * void MyFunc3(nsIFile& aFile) {
258 * bool exists;
259 * nsresult rv = aFile.Exists(&exists);
260 * if (NS_WARN_IF(NS_FAILED(rv)) {
261 * return;
263 * if (NS_WARN_IF(!exists) {
264 * return;
267 * // The file exists, and data could be read from it here.
270 * nsresult MyFunc4(nsIFile& aFile) {
271 * bool exists;
272 * nsresult rv = aFile.Exists(&exists);
273 * if (NS_WARN_IF(NS_FAILED(rv)) {
274 * NS_WARNING("The Exists call failed!");
275 * return rv;
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.
284 * return NS_OK;
287 * nsresult MyFunc5(nsIFile& aFile) {
288 * bool exists;
289 * nsresult rv = aFile.Exists(&exists);
290 * if (NS_WARN_IF(NS_FAILED(rv)) {
291 * return rv;
293 * if (exists) {
294 * // The file exists, and data could be read from it here.
295 * } else {
296 * return NS_ERROR_FAILURE;
299 * return NS_OK;
302 * nsresult MyFunc6(nsIFile& aFile) {
303 * bool exists;
304 * nsresult rv = aFile.Exists(&exists);
305 * if (NS_WARN_IF(NS_FAILED(rv)) {
306 * return rv;
308 * if (exists) {
309 * // The file exists, and data could be read from it here.
310 * } else {
311 * NS_WARNING("The file doesn't exist!");
312 * return NS_ERROR_FAILURE;
315 * return NS_OK;
318 * 4. Using NS_ENSURE_* macros
320 * Mainly:
321 * - NS_ENSURE_SUCCESS
322 * - NS_ENSURE_SUCCESS_VOID
323 * - NS_ENSURE_TRUE
324 * - NS_ENSURE_TRUE_VOID
326 * Typical use cases:
328 * nsresult MyFunc1(nsIFile& aFile) {
329 * bool exists;
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.
336 * return NS_OK;
339 * nsresult MyFunc2(nsIFile& aFile) {
340 * bool exists;
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.
347 * return NS_OK;
350 * void MyFunc3(nsIFile& aFile) {
351 * bool exists;
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
361 * // function
363 * return NS_OK;
366 * nsresult MyFunc5(nsIFile& aFile) {
367 * bool exists;
368 * nsresult rv = aFile.Exists(&exists);
369 * NS_ENSURE_SUCCESS(rv, rv);
370 * if (exists) {
371 * // The file exists, and data could be read from it here.
372 * } else {
373 * NS_ENSURE_TRUE(false, NS_ERROR_FAILURE);
376 * return NS_OK;
379 * nsresult MyFunc6(nsIFile& aFile) {
380 * // NS_ENSURE_TRUE can't run an additional cleanup function
382 * return NS_OK;
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);
432 #define QM_VOID
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)
441 : mActor(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))
457 #ifdef DEBUG
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."); }
465 #endif
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."); }
475 #endif
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(...) \
483 do { \
484 static_assert(false, "Did you forget arguments?"); \
485 } while (0)
487 #ifdef DEBUG
488 # define QM_HANDLE_ERROR(expr, error, severity) \
489 HandleError(#expr, error, __FILE__, __LINE__, severity)
490 #else
491 # define QM_HANDLE_ERROR(expr, error, severity) \
492 HandleError("Unavailable", error, __FILE__, __LINE__, severity)
493 #endif
495 #ifdef DEBUG
496 # define QM_HANDLE_ERROR_RETURN_NOTHING(expr, error, severity) \
497 HandleErrorReturnNothing(#expr, error, __FILE__, __LINE__, severity)
498 #else
499 # define QM_HANDLE_ERROR_RETURN_NOTHING(expr, error, severity) \
500 HandleErrorReturnNothing("Unavailable", error, __FILE__, __LINE__, severity)
501 #endif
503 #ifdef DEBUG
504 # define QM_HANDLE_ERROR_WITH_CLEANUP_RETURN_NOTHING(expr, error, severity, \
505 cleanup) \
506 HandleErrorWithCleanupReturnNothing(#expr, error, __FILE__, __LINE__, \
507 severity, cleanup)
508 #else
509 # define QM_HANDLE_ERROR_WITH_CLEANUP_RETURN_NOTHING(expr, error, severity, \
510 cleanup) \
511 HandleErrorWithCleanupReturnNothing("Unavailable", error, __FILE__, \
512 __LINE__, severity, cleanup)
513 #endif
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
545 // returned
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, \
560 cleanup) \
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(); \
578 if (predicate()) { \
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.
590 // See also
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
618 // directly.
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
631 // returned
632 #define QM_TRY_ASSIGN_CUSTOM_RET_VAL(tryResult, accessFunction, target, expr, \
633 customRetVal) \
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|
681 * must be an lvalue.
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); \
714 return tryResult;
716 // Handles the three arguments case when a custom return value needs to be
717 // returned
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); \
774 return retVal;
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); \
781 cleanup(); \
782 return retVal;
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
855 // directly.
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, \
869 expr, cleanup) \
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, \
891 ##__VA_ARGS__)
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
921 // directly.
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
955 * macro uses.
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()
968 : aResult.unwrap();
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( \
979 (expr), \
980 [&](const auto& firstRes) { \
981 bool res = predicate(firstRes); \
982 mozilla::dom::quota::QM_HANDLE_ERROR( \
983 #expr, firstRes, \
984 res ? mozilla::dom::quota::Severity::severity \
985 : mozilla::dom::quota::Severity::Error); \
986 return res; \
987 }, \
988 fallback)
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) \
1026 do { \
1027 if (NS_SUCCEEDED(_recorder)) { \
1028 _recorder = _status; \
1030 } while (0)
1032 # define OK_IN_NIGHTLY_PROPAGATE_IN_OTHERS \
1033 Ok {}
1035 # define RETURN_STATUS_OR_RESULT(_status, _rv) \
1036 return Err(NS_FAILED(_status) ? (_status) : (_rv))
1037 #else
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)
1044 #endif
1046 class mozIStorageConnection;
1047 class mozIStorageStatement;
1048 class nsIFile;
1050 namespace mozilla {
1052 class LogModule;
1054 struct CreateIfNonExistent {};
1056 struct NotOk {};
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) {
1061 if (aValue) {
1062 return Ok();
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};
1079 return Err(aValue);
1082 template <nsresult ErrorValue, typename V = mozilla::Ok>
1083 auto ErrToDefaultOkOrErr(nsresult aValue) -> Result<V, nsresult> {
1084 if (aValue == ErrorValue) {
1085 return V{};
1087 return Err(aValue);
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;
1103 #endif
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> {
1127 return V{};
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,
1141 nsresult aRv);
1143 RefPtr<Int64Promise> CreateAndRejectInt64Promise(StaticString aFunc,
1144 nsresult aRv);
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;
1164 static_assert(
1165 std::is_empty_v<
1166 typename std::invoke_result_t<Body, StepResultType&&>::ok_type>);
1168 while (true) {
1169 StepResultType element;
1170 MOZ_TRY_VAR(element, aStep());
1172 if (!static_cast<bool>(element)) {
1173 break;
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)
1196 -> Result<Ok,
1197 typename std::invoke_result_t<InputGenerator>::err_type> {
1198 MOZ_TRY_VAR(res, aBinaryOp(std::move(res), element));
1200 return Ok{};
1201 }));
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) {
1211 using std::begin;
1212 using std::end;
1213 return ReduceEach(
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>(
1218 res);
1220 aInit, aBinaryOp);
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(); });
1249 template <>
1250 class [[nodiscard]] GenericErrorResult<mozilla::ipc::IPCResult> {
1251 mozilla::ipc::IPCResult mErrorValue;
1253 template <typename V, typename E2>
1254 friend class Result;
1256 public:
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;
1277 #else
1278 // No need for these when we're not collecting telemetry.
1279 # define kQuotaInternalError
1280 # define kQuotaExternalError
1281 #endif
1283 MOZ_COLD void ReportInternalError(const char* aFile, uint32_t aLine,
1284 const char* aStr);
1286 LogModule* GetQuotaManagerLogger();
1288 void AnonymizeCString(nsACString& aCString);
1290 inline auto AnonymizedCString(const nsACString& aCString) {
1291 nsAutoCString result{aCString};
1292 AnonymizeCString(result);
1293 return result;
1296 void AnonymizeOriginString(nsACString& aOriginString);
1298 inline auto AnonymizedOriginString(const nsACString& aOriginString) {
1299 nsAutoCString result{aOriginString};
1300 AnonymizeOriginString(result);
1301 return result;
1304 #ifdef XP_WIN
1305 void CacheUseDOSDevicePathSyntaxPrefValue();
1306 #endif
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 {
1316 ExistsAsDirectory,
1317 ExistsAsFile,
1318 DoesNotExist,
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);
1355 namespace detail {
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
1367 // tree.
1368 nsDependentCSubstring GetObjdirDistIncludeTreeBase(
1369 const nsLiteralCString& aQuotaCommonHPath = nsLiteralCString(__FILE__));
1371 nsDependentCSubstring MakeSourceFileRelativePath(
1372 const nsACString& aSourceFilePath);
1374 } // namespace detail
1376 enum class Severity {
1377 Error,
1378 Warning,
1379 Info,
1380 Verbose,
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,
1389 Severity aSeverity)
1390 # else
1391 void LogError(const nsACString& aExpr, Maybe<nsresult> aMaybeRv,
1392 const nsACString& aSourceFilePath, int32_t aSourceFileLine,
1393 Severity aSeverity)
1394 # endif
1396 #endif
1398 #ifdef DEBUG
1399 Result<bool, nsresult> WarnIfFileIsUnknown(nsIFile& aFile,
1400 const char* aSourceFilePath,
1401 int32_t aSourceFileLine);
1402 #endif
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
1406 // not empty.
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
1411 // error handling.
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);
1426 } else {
1427 mozilla::dom::quota::LogError(
1428 nsDependentCString(aExpr), ResultType(Nothing{}),
1429 nsDependentCString(aSourceFilePath), aSourceFileLine, aSeverity);
1431 # else
1432 if constexpr (std::is_same_v<T, nsresult>) {
1433 mozilla::dom::quota::LogError(nsDependentCString(aExpr), Some(aRv),
1434 nsDependentCString(aSourceFilePath),
1435 aSourceFileLine, aSeverity);
1436 } else {
1437 mozilla::dom::quota::LogError(nsDependentCString(aExpr), Nothing{},
1438 nsDependentCString(aSourceFilePath),
1439 aSourceFileLine, aSeverity);
1441 # endif
1443 #else
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) {}
1449 #endif
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);
1457 return Nothing();
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);
1468 return Nothing();
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};
1493 // entry point
1494 template <typename T>
1495 auto invokabilize(T t) {
1496 return invokabilize_impl<T>{}(std::forward<T>(t));
1498 } // namespace gcc_detail
1499 #endif
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);
1510 #else
1511 using CustomRetVal = CustomRetVal_;
1512 CustomRetVal& aCustomRetVal = aCustomRetVal_;
1513 #endif
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],
1518 const T&>::value) {
1519 return aCustomRetVal(aFunc, aRv);
1520 } else if constexpr (std::is_invocable<CustomRetVal, const T&>::value) {
1521 return aCustomRetVal(aRv);
1522 } else {
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(
1544 [&aStmt] {
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> {
1556 ArrayType res;
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));
1562 return Ok{};
1563 }));
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));
1575 namespace detail {
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));
1584 return CollectEach(
1585 [&entries, &aCancel]() -> Result<nsCOMPtr<nsIFile>, nsresult> {
1586 if (aCancel()) {
1587 return nsCOMPtr<nsIFile>{};
1590 QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsCOMPtr<nsIFile>,
1591 entries, GetNextFile));
1593 aBody);
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));
1618 return ReduceEach(
1619 [&entries, &aCanceled]() -> Result<nsCOMPtr<nsIFile>, nsresult> {
1620 if (aCanceled) {
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,
1636 uint32_t aDelayMs)
1637 -> Result<typename std::invoke_result_t<Func>::ok_type, nsresult> {
1638 uint32_t retries = 0;
1640 while (true) {
1641 auto result = std::forward<Func>(aFunc)();
1643 if (result.isOk()) {
1644 return result;
1647 if (result.inspectErr() != NS_ERROR_FILE_IS_LOCKED &&
1648 result.inspectErr() != NS_ERROR_FILE_ACCESS_DENIED) {
1649 return result;
1652 if (retries++ >= aMaxRetries) {
1653 return result;
1656 PR_Sleep(PR_MillisecondsToInterval(aDelayMs));
1660 namespace detail {
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 {
1681 using RetType =
1682 std::invoke_result_t<Func, const FirstInitializationAttempt<
1683 Initialization, StringGenerator>&>;
1685 if constexpr (std::is_same_v<RetType, nsresult>) {
1686 return res;
1687 } else if constexpr (mozilla::detail::IsResult<RetType>::value &&
1688 std::is_same_v<typename RetType::err_type,
1689 nsresult>) {
1690 return res.isOk() ? NS_OK : res.inspectErr();
1691 } else {
1692 detail::UnsupportedReturnType();
1694 }();
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) {
1703 return res;
1706 if (!firstInitializationAttempt.Recorded()) {
1707 firstInitializationAttempt.Record(rv);
1710 return res;
1714 template <typename Initialization, typename StringGenerator, typename Func>
1715 auto ExecuteInitialization(
1716 FirstInitializationAttempts<Initialization, StringGenerator>&
1717 aFirstInitializationAttempts,
1718 const Initialization aInitialization, const nsACString& aContext,
1719 Func&& aFunc)
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()
1728 ? Nothing{}
1729 : Some(ScopedLogExtraInfo{
1730 ScopedLogExtraInfo::kTagContextTainted, aContext});
1731 #endif
1733 return std::forward<Func>(aFunc)(firstInitializationAttempt);
1737 } // namespace dom::quota
1738 } // namespace mozilla
1740 #endif // mozilla_dom_quota_quotacommon_h__