tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

QuotaCommon.h (70812B)


      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/. */
      6 
      7 #ifndef mozilla_dom_quota_quotacommon_h__
      8 #define mozilla_dom_quota_quotacommon_h__
      9 
     10 #include <algorithm>
     11 #include <cstddef>
     12 #include <cstdint>
     13 #include <type_traits>
     14 #include <utility>
     15 
     16 #include "mozIStorageStatement.h"
     17 #include "mozilla/Assertions.h"
     18 #include "mozilla/Atomics.h"
     19 #include "mozilla/Attributes.h"
     20 #include "mozilla/GeckoTrace.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 #include "mozilla/dom/quota/Config.h"
     28 #if defined(QM_LOG_ERROR_ENABLED) && defined(QM_ERROR_STACKS_ENABLED)
     29 #  include "mozilla/Variant.h"
     30 #endif
     31 #include "mozilla/dom/QMResult.h"
     32 #include "mozilla/dom/quota/FirstInitializationAttemptsImpl.h"
     33 #include "mozilla/dom/quota/RemoveParen.h"
     34 #include "mozilla/dom/quota/ScopedLogExtraInfo.h"
     35 #include "mozilla/ipc/ProtocolUtils.h"
     36 #include "nsCOMPtr.h"
     37 #include "nsDebug.h"
     38 #include "nsError.h"
     39 #include "nsIDirectoryEnumerator.h"
     40 #include "nsIEventTarget.h"
     41 #include "nsIFile.h"
     42 #include "nsLiteralString.h"
     43 #include "nsPrintfCString.h"
     44 #include "nsReadableUtils.h"
     45 #include "nsString.h"
     46 #include "nsTArray.h"
     47 #include "nsTLiteralString.h"
     48 
     49 namespace mozilla {
     50 template <typename T>
     51 class NotNull;
     52 }
     53 
     54 #define MOZ_ARGS_AFTER_3(a1, a2, a3, ...) __VA_ARGS__
     55 
     56 #define MOZ_ADD_ARGS2(...) , ##__VA_ARGS__
     57 #define MOZ_ADD_ARGS(...) MOZ_ADD_ARGS2(__VA_ARGS__)
     58 
     59 // Proper use of unique variable names can be tricky (especially if nesting of
     60 // the final macro is required).
     61 // See https://lifecs.likai.org/2016/07/c-preprocessor-hygienic-macros.html
     62 #define MOZ_UNIQUE_VAR(base) MOZ_CONCAT(base, __COUNTER__)
     63 
     64 // See https://florianjw.de/en/passing_overloaded_functions.html
     65 // TODO: Add a test for this macro.
     66 #define MOZ_SELECT_OVERLOAD(func)                         \
     67  [](auto&&... aArgs) -> decltype(auto) {                 \
     68    return func(std::forward<decltype(aArgs)>(aArgs)...); \
     69  }
     70 
     71 #define DSSTORE_FILE_NAME ".DS_Store"
     72 #define DESKTOP_FILE_NAME ".desktop"
     73 #define DESKTOP_INI_FILE_NAME "desktop.ini"
     74 #define THUMBS_DB_FILE_NAME "thumbs.db"
     75 
     76 #define QM_WARNING(...)                                                      \
     77  do {                                                                       \
     78    nsPrintfCString str(__VA_ARGS__);                                        \
     79    mozilla::dom::quota::ReportInternalError(__FILE__, __LINE__, str.get()); \
     80    NS_WARNING(str.get());                                                   \
     81  } while (0)
     82 
     83 #define QM_LOG_TEST() MOZ_LOG_TEST(GetQuotaManagerLogger(), LogLevel::Info)
     84 #define QM_LOG(_args) MOZ_LOG(GetQuotaManagerLogger(), LogLevel::Info, _args)
     85 
     86 #ifdef DEBUG
     87 #  define UNKNOWN_FILE_WARNING(_leafName)                                  \
     88    NS_WARNING(nsPrintfCString(                                            \
     89                   "Something (%s) in the directory that doesn't belong!", \
     90                   NS_ConvertUTF16toUTF8(_leafName).get())                 \
     91                   .get())
     92 #else
     93 #  define UNKNOWN_FILE_WARNING(_leafName) (void)(_leafName);
     94 #endif
     95 
     96 // This macro should be used in directory traversals for files or directories
     97 // that are unknown for given directory traversal. It should only be called
     98 // after all known (directory traversal specific) files or directories have
     99 // been checked and handled.
    100 // XXX Consider renaming the macro to QM_LOG_UNKNOWN_DIR_ENTRY.
    101 #ifdef DEBUG
    102 #  define WARN_IF_FILE_IS_UNKNOWN(_file) \
    103    mozilla::dom::quota::WarnIfFileIsUnknown(_file, __FILE__, __LINE__)
    104 #else
    105 #  define WARN_IF_FILE_IS_UNKNOWN(_file) Result<bool, nsresult>(false)
    106 #endif
    107 
    108 /**
    109 * There are multiple ways to handle unrecoverable conditions (note that the
    110 * patterns are put in reverse chronological order and only the first pattern
    111 * QM_TRY/QM_TRY_UNWRAP/QM_TRY_INSPECT/QM_TRY_RETURN/QM_FAIL should be used in
    112 * new code):
    113 *
    114 * 1. Using QM_TRY/QM_TRY_UNWRAP/QM_TRY_INSPECT/QM_TRY_RETURN/QM_FAIL macros
    115 *    (Quota manager specific, defined below)
    116 *
    117 * Typical use cases:
    118 *
    119 * nsresult MyFunc1(nsIFile& aFile) {
    120 *   bool exists;
    121 *   QM_TRY(aFile.Exists(&exists));
    122 *   QM_TRY(OkIf(exists), NS_ERROR_FAILURE);
    123 *
    124 *   // The file exists, and data could be read from it here.
    125 *
    126 *   return NS_OK;
    127 * }
    128 *
    129 * nsresult MyFunc2(nsIFile& aFile) {
    130 *   bool exists;
    131 *   QM_TRY(aFile.Exists(&exists), NS_ERROR_UNEXPECTED);
    132 *   QM_TRY(OkIf(exists), NS_ERROR_UNEXPECTED);
    133 *
    134 *   // The file exists, and data could be read from it here.
    135 *
    136 *   return NS_OK;
    137 * }
    138 *
    139 * void MyFunc3(nsIFile& aFile) {
    140 *   bool exists;
    141 *   QM_TRY(aFile.Exists(&exists), QM_VOID);
    142 *   QM_TRY(OkIf(exists), QM_VOID);
    143 *
    144 *   // The file exists, and data could be read from it here.
    145 * }
    146 *
    147 * nsresult MyFunc4(nsIFile& aFile) {
    148 *   bool exists;
    149 *   QM_TRY(storageFile->Exists(&exists), QM_PROPAGATE,
    150 *          []() { NS_WARNING("The Exists call failed!"); });
    151 *   QM_TRY(OkIf(exists), NS_ERROR_FAILURE,
    152 *          []() { NS_WARNING("The file doesn't exist!"); });
    153 *
    154 *   // The file exists, and data could be read from it here.
    155 *
    156 *   return NS_OK;
    157 * }
    158 *
    159 * nsresult MyFunc5(nsIFile& aFile) {
    160 *   bool exists;
    161 *   QM_TRY(aFile.Exists(&exists));
    162 *   if (exists) {
    163 *     // The file exists, and data could be read from it here.
    164 *   } else {
    165 *     QM_FAIL(NS_ERROR_FAILURE);
    166 *   }
    167 *
    168 *   return NS_OK;
    169 * }
    170 *
    171 * nsresult MyFunc6(nsIFile& aFile) {
    172 *   bool exists;
    173 *   QM_TRY(aFile.Exists(&exists));
    174 *   if (exists) {
    175 *     // The file exists, and data could be read from it here.
    176 *   } else {
    177 *     QM_FAIL(NS_ERROR_FAILURE,
    178 *             []() { NS_WARNING("The file doesn't exist!"); });
    179 *   }
    180 *
    181 *   return NS_OK;
    182 * }
    183 *
    184 * 2. Using MOZ_TRY macro
    185 *
    186 * Typical use cases:
    187 *
    188 * nsresult MyFunc1(nsIFile& aFile) {
    189 *   // MOZ_TRY can't return a custom return value
    190 *
    191 *   return NS_OK;
    192 * }
    193 *
    194 * nsresult MyFunc2(nsIFile& aFile) {
    195 *   // MOZ_TRY can't return a custom return value
    196 *
    197 *   return NS_OK;
    198 * }
    199 *
    200 * void MyFunc3(nsIFile& aFile) {
    201 *   // MOZ_TRY can't return a custom return value, "void" in this case
    202 * }
    203 *
    204 * nsresult MyFunc4(nsIFile& aFile) {
    205 *   // MOZ_TRY can't return a custom return value and run an additional
    206 *   // cleanup function
    207 *
    208 *   return NS_OK;
    209 * }
    210 *
    211 * nsresult MyFunc5(nsIFile& aFile) {
    212 *   // There's no MOZ_FAIL, MOZ_TRY can't return a custom return value
    213 *
    214 *   return NS_OK;
    215 * }
    216 *
    217 * nsresult MyFunc6(nsIFile& aFile) {
    218 *   // There's no MOZ_FAIL, MOZ_TRY can't return a custom return value and run
    219 *   // an additional cleanup function
    220 *
    221 *   return NS_OK;
    222 * }
    223 *
    224 * 3. Using NS_WARN_IF and NS_WARNING macro with own control flow handling
    225 *
    226 * Typical use cases:
    227 *
    228 * nsresult MyFunc1(nsIFile& aFile) {
    229 *   bool exists;
    230 *   nsresult rv = aFile.Exists(&exists);
    231 *   if (NS_WARN_IF(NS_FAILED(rv)) {
    232 *     return rv;
    233 *   }
    234 *   if (NS_WARN_IF(!exists) {
    235 *     return NS_ERROR_FAILURE;
    236 *   }
    237 *
    238 *   // The file exists, and data could be read from it here.
    239 *
    240 *   return NS_OK;
    241 * }
    242 *
    243 * nsresult MyFunc2(nsIFile& aFile) {
    244 *   bool exists;
    245 *   nsresult rv = aFile.Exists(&exists);
    246 *   if (NS_WARN_IF(NS_FAILED(rv)) {
    247 *     return NS_ERROR_UNEXPECTED;
    248 *   }
    249 *   if (NS_WARN_IF(!exists) {
    250 *     return NS_ERROR_UNEXPECTED;
    251 *   }
    252 *
    253 *   // The file exists, and data could be read from it here.
    254 *
    255 *   return NS_OK;
    256 * }
    257 *
    258 * void MyFunc3(nsIFile& aFile) {
    259 *   bool exists;
    260 *   nsresult rv = aFile.Exists(&exists);
    261 *   if (NS_WARN_IF(NS_FAILED(rv)) {
    262 *     return;
    263 *   }
    264 *   if (NS_WARN_IF(!exists) {
    265 *     return;
    266 *   }
    267 *
    268 *   // The file exists, and data could be read from it here.
    269 * }
    270 *
    271 * nsresult MyFunc4(nsIFile& aFile) {
    272 *   bool exists;
    273 *   nsresult rv = aFile.Exists(&exists);
    274 *   if (NS_WARN_IF(NS_FAILED(rv)) {
    275 *     NS_WARNING("The Exists call failed!");
    276 *     return rv;
    277 *   }
    278 *   if (NS_WARN_IF(!exists) {
    279 *     NS_WARNING("The file doesn't exist!");
    280 *     return NS_ERROR_FAILURE;
    281 *   }
    282 *
    283 *   // The file exists, and data could be read from it here.
    284 *
    285 *   return NS_OK;
    286 * }
    287 *
    288 * nsresult MyFunc5(nsIFile& aFile) {
    289 *   bool exists;
    290 *   nsresult rv = aFile.Exists(&exists);
    291 *   if (NS_WARN_IF(NS_FAILED(rv)) {
    292 *     return rv;
    293 *   }
    294 *   if (exists) {
    295 *     // The file exists, and data could be read from it here.
    296 *   } else {
    297 *     return NS_ERROR_FAILURE;
    298 *   }
    299 *
    300 *   return NS_OK;
    301 * }
    302 *
    303 * nsresult MyFunc6(nsIFile& aFile) {
    304 *   bool exists;
    305 *   nsresult rv = aFile.Exists(&exists);
    306 *   if (NS_WARN_IF(NS_FAILED(rv)) {
    307 *     return rv;
    308 *   }
    309 *   if (exists) {
    310 *     // The file exists, and data could be read from it here.
    311 *   } else {
    312 *     NS_WARNING("The file doesn't exist!");
    313 *     return NS_ERROR_FAILURE;
    314 *   }
    315 *
    316 *   return NS_OK;
    317 * }
    318 *
    319 * 4. Using NS_ENSURE_* macros
    320 *
    321 * Mainly:
    322 * - NS_ENSURE_SUCCESS
    323 * - NS_ENSURE_SUCCESS_VOID
    324 * - NS_ENSURE_TRUE
    325 * - NS_ENSURE_TRUE_VOID
    326 *
    327 * Typical use cases:
    328 *
    329 * nsresult MyFunc1(nsIFile& aFile) {
    330 *   bool exists;
    331 *   nsresult rv = aFile.Exists(&exists);
    332 *   NS_ENSURE_SUCCESS(rv, rv);
    333 *   NS_ENSURE_TRUE(exists, NS_ERROR_FAILURE);
    334 *
    335 *   // The file exists, and data could be read from it here.
    336 *
    337 *   return NS_OK;
    338 * }
    339 *
    340 * nsresult MyFunc2(nsIFile& aFile) {
    341 *   bool exists;
    342 *   nsresult rv = aFile.Exists(&exists);
    343 *   NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
    344 *   NS_ENSURE_TRUE(exists, NS_ERROR_UNEXPECTED);
    345 *
    346 *   // The file exists, and data could be read from it here.
    347 *
    348 *   return NS_OK;
    349 * }
    350 *
    351 * void MyFunc3(nsIFile& aFile) {
    352 *   bool exists;
    353 *   nsresult rv = aFile.Exists(&exists);
    354 *   NS_ENSURE_SUCCESS_VOID(rv);
    355 *   NS_ENSURE_TRUE_VOID(exists);
    356 *
    357 *   // The file exists, and data could be read from it here.
    358 * }
    359 *
    360 * nsresult MyFunc4(nsIFile& aFile) {
    361 *   // NS_ENSURE_SUCCESS/NS_ENSURE_TRUE can't run an additional cleanup
    362 *   // function
    363 *
    364 *   return NS_OK;
    365 * }
    366 *
    367 * nsresult MyFunc5(nsIFile& aFile) {
    368 *   bool exists;
    369 *   nsresult rv = aFile.Exists(&exists);
    370 *   NS_ENSURE_SUCCESS(rv, rv);
    371 *   if (exists) {
    372 *     // The file exists, and data could be read from it here.
    373 *   } else {
    374 *     NS_ENSURE_TRUE(false, NS_ERROR_FAILURE);
    375 *   }
    376 *
    377 *   return NS_OK;
    378 * }
    379 *
    380 * nsresult MyFunc6(nsIFile& aFile) {
    381 *   // NS_ENSURE_TRUE can't run an additional cleanup function
    382 *
    383 *   return NS_OK;
    384 * }
    385 *
    386 * QM_TRY/QM_TRY_UNWRAP/QM_TRY_INSPECT is like MOZ_TRY but if an
    387 * error occurs it additionally calls a generic function HandleError to handle
    388 * the error and it can be used to return custom return values as well and even
    389 * call an additional cleanup function.
    390 * HandleError currently only warns in debug builds, it will report to the
    391 * browser console and telemetry in the future.
    392 * The other advantage of QM_TRY/QM_TRY_UNWRAP/QM_TRY_INSPECT is that a local
    393 * nsresult is not needed at all in all cases, all calls can be wrapped
    394 * directly. If an error occurs, the warning contains a concrete call instead
    395 * of the rv local variable. For example:
    396 *
    397 * 1. WARNING: NS_ENSURE_SUCCESS(rv, rv) failed with result 0x80004005
    398 * (NS_ERROR_FAILURE): file XYZ, line N
    399 *
    400 * 2. WARNING: 'NS_FAILED(rv)', file XYZ, line N
    401 *
    402 * 3. Nothing (MOZ_TRY doesn't warn)
    403 *
    404 * 4. WARNING: Error: 'aFile.Exists(&exists)', file XYZ, line N
    405 *
    406 * QM_TRY_RETURN is a supplementary macro for cases when the result's success
    407 * value can be directly returned (instead of assigning to a variable as in the
    408 * QM_TRY_UNWRAP/QM_TRY_INSPECT case).
    409 *
    410 * QM_FAIL is a supplementary macro for cases when an error needs to be
    411 * returned without evaluating an expression. It's possible to write
    412 * QM_TRY(OkIf(false), NS_ERROR_FAILURE), but QM_FAIL(NS_ERROR_FAILURE) looks
    413 * more straightforward.
    414 *
    415 * It's highly recommended to use
    416 * QM_TRY/QM_TRY_UNWRAP/QM_TRY_INSPECT/QM_TRY_RETURN/QM_FAIL in new code for
    417 * quota manager and quota clients. Existing code should be incrementally
    418 * converted as needed.
    419 *
    420 * QM_TRY_VOID/QM_TRY_UNWRAP_VOID/QM_TRY_INSPECT_VOID/QM_FAIL_VOID is not
    421 * defined on purpose since it's possible to use
    422 * QM_TRY/QM_TRY_UNWRAP/QM_TRY_INSPECT/QM_FAIL even in void functions.
    423 * However, QM_TRY(Task(), ) would look odd so it's recommended to use a dummy
    424 * define QM_VOID that evaluates to nothing instead: QM_TRY(Task(), QM_VOID)
    425 *
    426 * Custom return values can be static or dynamically generated using functions
    427 * with one of these signatures:
    428 *   auto(const char* aFunc, const char* aExpr);
    429 *   auto(const char* aFunc, const T& aRv);
    430 *   auto(const T& aRc);
    431 */
    432 
    433 #define QM_VOID
    434 
    435 #define QM_PROPAGATE Err(tryTempError)
    436 
    437 namespace mozilla::dom::quota::detail {
    438 
    439 struct IpcFailCustomRetVal {
    440  explicit IpcFailCustomRetVal(
    441      mozilla::NotNull<mozilla::ipc::IProtocol*> aActor)
    442      : mActor(aActor) {}
    443 
    444  template <size_t NFunc, size_t NExpr>
    445  auto operator()(const char (&aFunc)[NFunc],
    446                  const char (&aExpr)[NExpr]) const {
    447    return mozilla::Err(mozilla::ipc::IPCResult::Fail(mActor, aFunc, aExpr));
    448  }
    449 
    450  mozilla::NotNull<mozilla::ipc::IProtocol*> mActor;
    451 };
    452 
    453 }  // namespace mozilla::dom::quota::detail
    454 
    455 #define QM_IPC_FAIL(actor) \
    456  mozilla::dom::quota::detail::IpcFailCustomRetVal(mozilla::WrapNotNull(actor))
    457 
    458 #ifdef DEBUG
    459 #  define QM_ASSERT_UNREACHABLE                                               \
    460    [](const char*, const char*) -> ::mozilla::GenericErrorResult<nsresult> { \
    461      MOZ_CRASH("Should never be reached.");                                  \
    462    }
    463 
    464 #  define QM_ASSERT_UNREACHABLE_VOID \
    465    [](const char*, const char*) { MOZ_CRASH("Should never be reached."); }
    466 #endif
    467 
    468 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    469 #  define QM_DIAGNOSTIC_ASSERT_UNREACHABLE                                    \
    470    [](const char*, const char*) -> ::mozilla::GenericErrorResult<nsresult> { \
    471      MOZ_CRASH("Should never be reached.");                                  \
    472    }
    473 
    474 #  define QM_DIAGNOSTIC_ASSERT_UNREACHABLE_VOID \
    475    [](const char*, const char*) { MOZ_CRASH("Should never be reached."); }
    476 #endif
    477 
    478 #define QM_NO_CLEANUP [](const auto&) {}
    479 
    480 // QM_MISSING_ARGS and QM_HANDLE_ERROR macros are implementation details of
    481 // QM_TRY/QM_TRY_UNWRAP/QM_TRY_INSPECT/QM_FAIL and shouldn't be used directly.
    482 
    483 #define QM_MISSING_ARGS(...)                           \
    484  do {                                                 \
    485    static_assert(false, "Did you forget arguments?"); \
    486  } while (0)
    487 
    488 #ifdef DEBUG
    489 #  define QM_HANDLE_ERROR(expr, error, severity) \
    490    HandleError(#expr, error, __FILE__, __LINE__, severity)
    491 #else
    492 #  define QM_HANDLE_ERROR(expr, error, severity) \
    493    HandleError("Unavailable", error, __FILE__, __LINE__, severity)
    494 #endif
    495 
    496 #ifdef DEBUG
    497 #  define QM_HANDLE_ERROR_RETURN_NOTHING(expr, error, severity) \
    498    HandleErrorReturnNothing(#expr, error, __FILE__, __LINE__, severity)
    499 #else
    500 #  define QM_HANDLE_ERROR_RETURN_NOTHING(expr, error, severity) \
    501    HandleErrorReturnNothing("Unavailable", error, __FILE__, __LINE__, severity)
    502 #endif
    503 
    504 #ifdef DEBUG
    505 #  define QM_HANDLE_ERROR_WITH_CLEANUP_RETURN_NOTHING(expr, error, severity, \
    506                                                      cleanup)               \
    507    HandleErrorWithCleanupReturnNothing(#expr, error, __FILE__, __LINE__,    \
    508                                        severity, cleanup)
    509 #else
    510 #  define QM_HANDLE_ERROR_WITH_CLEANUP_RETURN_NOTHING(expr, error, severity, \
    511                                                      cleanup)               \
    512    HandleErrorWithCleanupReturnNothing("Unavailable", error, __FILE__,      \
    513                                        __LINE__, severity, cleanup)
    514 #endif
    515 
    516 // Handles the case when QM_VOID is passed as a custom return value.
    517 #define QM_HANDLE_CUSTOM_RET_VAL_HELPER0(func, expr, error)
    518 
    519 #define QM_HANDLE_CUSTOM_RET_VAL_HELPER1(func, expr, error, customRetVal) \
    520  mozilla::dom::quota::HandleCustomRetVal(func, #expr, error, customRetVal)
    521 
    522 #define QM_HANDLE_CUSTOM_RET_VAL_GLUE(a, b) a b
    523 
    524 #define QM_HANDLE_CUSTOM_RET_VAL(...)                                 \
    525  QM_HANDLE_CUSTOM_RET_VAL_GLUE(                                      \
    526      MOZ_PASTE_PREFIX_AND_ARG_COUNT(QM_HANDLE_CUSTOM_RET_VAL_HELPER, \
    527                                     MOZ_ARGS_AFTER_3(__VA_ARGS__)),  \
    528      (MOZ_ARG_1(__VA_ARGS__), MOZ_ARG_2(__VA_ARGS__),                \
    529       MOZ_ARG_3(__VA_ARGS__) MOZ_ADD_ARGS(MOZ_ARGS_AFTER_3(__VA_ARGS__))))
    530 
    531 // QM_TRY_PROPAGATE_ERR, QM_TRY_CUSTOM_RET_VAL,
    532 // QM_TRY_CUSTOM_RET_VAL_WITH_CLEANUP and QM_TRY_GLUE macros are implementation
    533 // details of QM_TRY and shouldn't be used directly.
    534 
    535 // Handles the two arguments case when the error is propagated.
    536 #define QM_TRY_PROPAGATE_ERR(tryResult, expr)                                \
    537  auto tryResult = (expr);                                                   \
    538  static_assert(std::is_empty_v<typename decltype(tryResult)::ok_type>);     \
    539  if (MOZ_UNLIKELY(tryResult.isErr())) {                                     \
    540    mozilla::dom::quota::QM_HANDLE_ERROR(                                    \
    541        expr, tryResult.inspectErr(), mozilla::dom::quota::Severity::Error); \
    542    return tryResult.propagateErr();                                         \
    543  }
    544 
    545 // Handles the three arguments case when a custom return value needs to be
    546 // returned
    547 #define QM_TRY_CUSTOM_RET_VAL(tryResult, expr, customRetVal)                 \
    548  auto tryResult = (expr);                                                   \
    549  static_assert(std::is_empty_v<typename decltype(tryResult)::ok_type>);     \
    550  if (MOZ_UNLIKELY(tryResult.isErr())) {                                     \
    551    [[maybe_unused]] auto tryTempError = tryResult.unwrapErr();              \
    552    mozilla::dom::quota::QM_HANDLE_ERROR(                                    \
    553        expr, tryTempError, mozilla::dom::quota::Severity::Error);           \
    554    [[maybe_unused]] constexpr const auto& func = __func__;                  \
    555    return QM_HANDLE_CUSTOM_RET_VAL(func, expr, tryTempError, customRetVal); \
    556  }
    557 
    558 // Handles the four arguments case when a cleanup function needs to be called
    559 // before a custom return value is returned
    560 #define QM_TRY_CUSTOM_RET_VAL_WITH_CLEANUP(tryResult, expr, customRetVal,    \
    561                                           cleanup)                          \
    562  auto tryResult = (expr);                                                   \
    563  static_assert(std::is_empty_v<typename decltype(tryResult)::ok_type>);     \
    564  if (MOZ_UNLIKELY(tryResult.isErr())) {                                     \
    565    auto tryTempError = tryResult.unwrapErr();                               \
    566    mozilla::dom::quota::QM_HANDLE_ERROR(                                    \
    567        expr, tryTempError, mozilla::dom::quota::Severity::Error);           \
    568    cleanup(tryTempError);                                                   \
    569    [[maybe_unused]] constexpr const auto& func = __func__;                  \
    570    return QM_HANDLE_CUSTOM_RET_VAL(func, expr, tryTempError, customRetVal); \
    571  }
    572 
    573 #define QM_TRY_CUSTOM_RET_VAL_WITH_CLEANUP_AND_PREDICATE(                    \
    574    tryResult, expr, customRetVal, cleanup, predicate)                       \
    575  auto tryResult = (expr);                                                   \
    576  static_assert(std::is_empty_v<typename decltype(tryResult)::ok_type>);     \
    577  if (MOZ_UNLIKELY(tryResult.isErr())) {                                     \
    578    auto tryTempError = tryResult.unwrapErr();                               \
    579    if (predicate()) {                                                       \
    580      mozilla::dom::quota::QM_HANDLE_ERROR(                                  \
    581          expr, tryTempError, mozilla::dom::quota::Severity::Error);         \
    582    }                                                                        \
    583    cleanup(tryTempError);                                                   \
    584    [[maybe_unused]] constexpr const auto& func = __func__;                  \
    585    return QM_HANDLE_CUSTOM_RET_VAL(func, expr, tryTempError, customRetVal); \
    586  }
    587 
    588 // Chooses the final implementation macro for given argument count.
    589 // This could use MOZ_PASTE_PREFIX_AND_ARG_COUNT, but explicit named suffxes
    590 // read slightly better than plain numbers.
    591 // See also
    592 // https://stackoverflow.com/questions/3046889/optional-parameters-with-c-macros
    593 #define QM_TRY_META(...)                                                      \
    594  {MOZ_ARG_7(, ##__VA_ARGS__,                                                 \
    595             QM_TRY_CUSTOM_RET_VAL_WITH_CLEANUP_AND_PREDICATE(__VA_ARGS__),   \
    596             QM_TRY_CUSTOM_RET_VAL_WITH_CLEANUP(__VA_ARGS__),                 \
    597             QM_TRY_CUSTOM_RET_VAL(__VA_ARGS__),                              \
    598             QM_TRY_PROPAGATE_ERR(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__), \
    599             QM_MISSING_ARGS(__VA_ARGS__))}
    600 
    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__)
    604 
    605 /**
    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).
    613 */
    614 #define QM_TRY(...) QM_TRY_GLUE(__VA_ARGS__)
    615 
    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.
    620 
    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();                                         \
    628  }                                                                          \
    629  MOZ_REMOVE_PAREN(target) = tryResult.accessFunction();
    630 
    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    [[maybe_unused]] auto tryTempError = tryResult.unwrapErr();               \
    638    mozilla::dom::quota::QM_HANDLE_ERROR(                                     \
    639        expr, tryTempError, mozilla::dom::quota::Severity::Error);            \
    640    [[maybe_unused]] constexpr const auto& func = __func__;                   \
    641    return QM_HANDLE_CUSTOM_RET_VAL(func, expr, tryTempError, customRetVal);  \
    642  }                                                                           \
    643  MOZ_REMOVE_PAREN(target) = tryResult.accessFunction();
    644 
    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    [[maybe_unused]] constexpr const auto& func = __func__;                  \
    656    return QM_HANDLE_CUSTOM_RET_VAL(func, expr, tryTempError, customRetVal); \
    657  }                                                                          \
    658  MOZ_REMOVE_PAREN(target) = tryResult.accessFunction();
    659 
    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__))
    669 
    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__)
    674 
    675 /**
    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.
    683 */
    684 #define QM_TRY_UNWRAP(...) QM_TRY_ASSIGN_GLUE(unwrap, __VA_ARGS__)
    685 
    686 /**
    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,
    690 *
    691 *   QM_TRY_INSPECT(const auto &target, DoSomething())
    692 *
    693 * should be preferred over
    694 *
    695 *   QM_TRY_UNWRAP(const auto target, DoSomething())
    696 *
    697 * as it avoids unnecessary moves/copies.
    698 */
    699 #define QM_TRY_INSPECT(...) QM_TRY_ASSIGN_GLUE(inspect, __VA_ARGS__)
    700 
    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.
    704 
    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); \
    714  }                                                                          \
    715  return tryResult;
    716 
    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    [[maybe_unused]] auto tryTempError = tryResult.unwrapErr();              \
    723    mozilla::dom::quota::QM_HANDLE_ERROR(                                    \
    724        expr, tryResult.inspectErr(), mozilla::dom::quota::Severity::Error); \
    725    [[maybe_unused]] constexpr const auto& func = __func__;                  \
    726    return QM_HANDLE_CUSTOM_RET_VAL(func, expr, tryTempError, customRetVal); \
    727  }                                                                          \
    728  return tryResult.unwrap();
    729 
    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    [[maybe_unused]] constexpr const auto& func = __func__;                  \
    741    return QM_HANDLE_CUSTOM_RET_VAL(func, expr, tryTempError, customRetVal); \
    742  }                                                                          \
    743  return tryResult.unwrap();
    744 
    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(...)                                      \
    748  {MOZ_ARG_6(, ##__VA_ARGS__,                                        \
    749             QM_TRY_RETURN_CUSTOM_RET_VAL_WITH_CLEANUP(__VA_ARGS__), \
    750             QM_TRY_RETURN_CUSTOM_RET_VAL(__VA_ARGS__),              \
    751             QM_TRY_RETURN_PROPAGATE_ERR(__VA_ARGS__),               \
    752             QM_MISSING_ARGS(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__))}
    753 
    754 // Generates unique variable name. This extra internal macro (along with
    755 // __COUNTER__) allows nesting of the final macro.
    756 #define QM_TRY_RETURN_GLUE(...) \
    757  QM_TRY_RETURN_META(MOZ_UNIQUE_VAR(tryResult), ##__VA_ARGS__)
    758 
    759 /**
    760 * QM_TRY_RETURN(expr[, customRetVal, cleanup]) evaluates expr, which must
    761 * produce a Result value. On success, the result's success value is returned.
    762 * On error, it calls HandleError and an additional cleanup function (if the
    763 * third argument was passed) and finally returns the error result or a custom
    764 * return value (if the second argument was passed).
    765 */
    766 #define QM_TRY_RETURN(...) QM_TRY_RETURN_GLUE(__VA_ARGS__)
    767 
    768 // QM_FAIL_RET_VAL and QM_FAIL_RET_VAL_WITH_CLEANUP macros are implementation
    769 // details of QM_FAIL and shouldn't be used directly.
    770 
    771 // Handles the one argument case when just an error is returned
    772 #define QM_FAIL_RET_VAL(retVal)                                               \
    773  mozilla::dom::quota::QM_HANDLE_ERROR(Failure, 0,                            \
    774                                       mozilla::dom::quota::Severity::Error); \
    775  return retVal;
    776 
    777 // Handles the two arguments case when a cleanup function needs to be called
    778 // before a return value is returned
    779 #define QM_FAIL_RET_VAL_WITH_CLEANUP(retVal, cleanup)                         \
    780  mozilla::dom::quota::QM_HANDLE_ERROR(Failure, 0,                            \
    781                                       mozilla::dom::quota::Severity::Error); \
    782  cleanup();                                                                  \
    783  return retVal;
    784 
    785 // Chooses the final implementation macro for given argument count.
    786 // See also the comment for QM_TRY_META.
    787 #define QM_FAIL_META(...)                                               \
    788  MOZ_ARG_4(, ##__VA_ARGS__, QM_FAIL_RET_VAL_WITH_CLEANUP(__VA_ARGS__), \
    789            QM_FAIL_RET_VAL(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__))
    790 
    791 // This extra internal macro allows nesting of the final macro.
    792 #define QM_FAIL_GLUE(...) QM_FAIL_META(__VA_ARGS__)
    793 
    794 /**
    795 * QM_FAIL(retVal[, cleanup]) calls HandleError and an additional cleanup
    796 * function (if the second argument was passed) and returns a return value.
    797 */
    798 #define QM_FAIL(...) QM_FAIL_GLUE(__VA_ARGS__)
    799 
    800 // QM_REPORTONLY_TRY, QM_REPORTONLY_TRY_WITH_CLEANUP, QM_REPORTONLY_TRY_GLUE
    801 // macros are implementation details of QM_WARNONLY_TRY/QM_INFOONLY_TRY and
    802 // shouldn't be used directly.
    803 
    804 // Handles the three arguments case when only a warning/info is reported.
    805 #define QM_REPORTONLY_TRY(tryResult, severity, expr)                           \
    806  auto tryResult = (expr);                                                     \
    807  static_assert(std::is_empty_v<typename decltype(tryResult)::ok_type>);       \
    808  if (MOZ_UNLIKELY(tryResult.isErr())) {                                       \
    809    mozilla::dom::quota::QM_HANDLE_ERROR(                                      \
    810        expr, tryResult.unwrapErr(), mozilla::dom::quota::Severity::severity); \
    811  }
    812 
    813 // Handles the four arguments case when a cleanup function needs to be called
    814 #define QM_REPORTONLY_TRY_WITH_CLEANUP(tryResult, severity, expr, cleanup) \
    815  auto tryResult = (expr);                                                 \
    816  static_assert(std::is_empty_v<typename decltype(tryResult)::ok_type>);   \
    817  if (MOZ_UNLIKELY(tryResult.isErr())) {                                   \
    818    auto tryTempError = tryResult.unwrapErr();                             \
    819    mozilla::dom::quota::QM_HANDLE_ERROR(                                  \
    820        expr, tryTempError, mozilla::dom::quota::Severity::severity);      \
    821    cleanup(tryTempError);                                                 \
    822  }
    823 
    824 // Chooses the final implementation macro for given argument count.
    825 // See also the comment for QM_TRY_META.
    826 #define QM_REPORTONLY_TRY_META(...)                                        \
    827  {MOZ_ARG_6(, ##__VA_ARGS__, QM_REPORTONLY_TRY_WITH_CLEANUP(__VA_ARGS__), \
    828             QM_REPORTONLY_TRY(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__), \
    829             QM_MISSING_ARGS(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__))}
    830 
    831 // Generates unique variable name. This extra internal macro (along with
    832 // __COUNTER__) allows nesting of the final macro.
    833 #define QM_REPORTONLY_TRY_GLUE(severity, ...) \
    834  QM_REPORTONLY_TRY_META(MOZ_UNIQUE_VAR(tryResult), severity, ##__VA_ARGS__)
    835 
    836 /**
    837 * QM_WARNONLY_TRY(expr[, cleanup]) evaluates expr, which must produce a
    838 * Result value with empty ok_type. On Success, it does nothing else. On error,
    839 * it calls HandleError and an additional cleanup function (if the second
    840 * argument was passed). This macro never returns and failures are always
    841 * reported using a lower level of severity relative to failures reported by
    842 * QM_TRY. The macro is intended for failures that should not be propagated.
    843 */
    844 #define QM_WARNONLY_TRY(...) QM_REPORTONLY_TRY_GLUE(Warning, __VA_ARGS__)
    845 
    846 /**
    847 * QM_INFOONLY_TRY is like QM_WARNONLY_TRY. The only difference is that
    848 * failures are reported using a lower level of severity relative to failures
    849 * reported by QM_WARNONLY_TRY.
    850 */
    851 #define QM_INFOONLY_TRY(...) QM_REPORTONLY_TRY_GLUE(Info, __VA_ARGS__)
    852 
    853 // QM_REPORTONLY_TRY_ASSIGN, QM_REPORTONLY_TRY_ASSIGN_WITH_CLEANUP,
    854 // QM_REPORTONLY_TRY_ASSIGN_GLUE macros are implementation details of
    855 // QM_WARNONLY_TRY_UNWRAP/QM_INFOONLY_TRY_UNWRAP and shouldn't be used
    856 // directly.
    857 
    858 // Handles the four arguments case when only a warning/info is reported.
    859 #define QM_REPORTONLY_TRY_ASSIGN(tryResult, severity, target, expr) \
    860  auto tryResult = (expr);                                          \
    861  MOZ_REMOVE_PAREN(target) =                                        \
    862      MOZ_LIKELY(tryResult.isOk())                                  \
    863          ? Some(tryResult.unwrap())                                \
    864          : mozilla::dom::quota::QM_HANDLE_ERROR_RETURN_NOTHING(    \
    865                expr, tryResult.unwrapErr(),                        \
    866                mozilla::dom::quota::Severity::severity);
    867 
    868 // Handles the five arguments case when a cleanup function needs to be called
    869 #define QM_REPORTONLY_TRY_ASSIGN_WITH_CLEANUP(tryResult, severity, target,    \
    870                                              expr, cleanup)                  \
    871  auto tryResult = (expr);                                                    \
    872  MOZ_REMOVE_PAREN(target) =                                                  \
    873      MOZ_LIKELY(tryResult.isOk())                                            \
    874          ? Some(tryResult.unwrap())                                          \
    875          : mozilla::dom::quota::QM_HANDLE_ERROR_WITH_CLEANUP_RETURN_NOTHING( \
    876                expr, tryResult.unwrapErr(),                                  \
    877                mozilla::dom::quota::Severity::severity, cleanup);
    878 
    879 // Chooses the final implementation macro for given argument count.
    880 // See also the comment for QM_TRY_META.
    881 #define QM_REPORTONLY_TRY_ASSIGN_META(...)                              \
    882  MOZ_ARG_7(, ##__VA_ARGS__,                                            \
    883            QM_REPORTONLY_TRY_ASSIGN_WITH_CLEANUP(__VA_ARGS__),         \
    884            QM_REPORTONLY_TRY_ASSIGN(__VA_ARGS__),                      \
    885            QM_MISSING_ARGS(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__), \
    886            QM_MISSING_ARGS(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__))
    887 
    888 // Generates unique variable name. This extra internal macro (along with
    889 // __COUNTER__) allows nesting of the final macro.
    890 #define QM_REPORTONLY_TRY_ASSIGN_GLUE(severity, ...)                 \
    891  QM_REPORTONLY_TRY_ASSIGN_META(MOZ_UNIQUE_VAR(tryResult), severity, \
    892                                ##__VA_ARGS__)
    893 
    894 /**
    895 * QM_WARNONLY_TRY_UNWRAP(target, expr[, cleanup]) evaluates expr, which must
    896 * produce a Result value. On success, the result's success value is first
    897 * unwrapped from the Result, then wrapped in a Maybe and finally assigned to
    898 * target. On error, it calls HandleError and an additional cleanup
    899 * function (if the third argument was passed) and finally assigns Nothing to
    900 * target. |target| must be an lvalue. This macro never returns and failures
    901 * are always reported using a lower level of severity relative to failures
    902 * reported by QM_TRY_UNWRAP/QM_TRY_INSPECT. The macro is intended for failures
    903 * that should not be propagated.
    904 */
    905 #define QM_WARNONLY_TRY_UNWRAP(...) \
    906  QM_REPORTONLY_TRY_ASSIGN_GLUE(Warning, __VA_ARGS__)
    907 
    908 // QM_WARNONLY_TRY_INSPECT doesn't make sense.
    909 
    910 /**
    911 * QM_INFOONLY_TRY_UNWRAP is like QM_WARNONLY_TRY_UNWRAP. The only difference is
    912 * that failures are reported using a lower level of severity relative to
    913 * failures reported by QM_WARNONLY_TRY_UNWRAP.
    914 */
    915 #define QM_INFOONLY_TRY_UNWRAP(...) \
    916  QM_REPORTONLY_TRY_ASSIGN_GLUE(Info, __VA_ARGS__)
    917 
    918 // QM_INFOONLY_TRY_INSPECT doesn't make sense.
    919 
    920 /**
    921 * QM_VERBOSEONLY_TRY_UNWRAP is like QM_WARNONLY_TRY_UNWRAP. The only
    922 * difference is that failures are reported using the lowest severity which is
    923 * currently ignored in LogError, so nothing goes to the console, browser
    924 * console and telemetry. Since nothing goes to the telemetry, the macro can't
    925 * signal the end of the underlying error stack or change the type of the error
    926 * stack in the telemetry. For that reason, the expression shouldn't contain
    927 * nested QM_TRY macro uses.
    928 */
    929 #define QM_VERBOSEONLY_TRY_UNWRAP(...) \
    930  QM_REPORTONLY_TRY_ASSIGN_GLUE(Verbose, __VA_ARGS__)
    931 
    932 // QM_VERBOSEONLY_TRY_INSPECT doesn't make sense.
    933 
    934 // QM_OR_ELSE_REPORT macro is an implementation detail of
    935 // QM_OR_ELSE_WARN/QM_OR_ELSE_INFO/QM_OR_ELSE_LOG_VERBOSE and shouldn't be used
    936 // directly.
    937 
    938 #define QM_OR_ELSE_REPORT(severity, expr, fallback)                \
    939  (expr).orElse([&](const auto& firstRes) {                        \
    940    mozilla::dom::quota::QM_HANDLE_ERROR(                          \
    941        #expr, firstRes, mozilla::dom::quota::Severity::severity); \
    942    return fallback(firstRes);                                     \
    943  })
    944 
    945 /*
    946 * QM_OR_ELSE_WARN(expr, fallback) evaluates expr, which must produce a Result
    947 * value. On Success, it just moves the success over. On error, it calls
    948 * HandleError (with the Warning severity) and a fallback function (passed as
    949 * the second argument) which produces a new result. Failed expr is always
    950 * reported as a warning (the macro essentially wraps the fallback function
    951 * with a warning). QM_OR_ELSE_WARN is a sub macro and is intended to be used
    952 * along with one of the main macros such as QM_TRY.
    953 */
    954 #define QM_OR_ELSE_WARN(...) QM_OR_ELSE_REPORT(Warning, __VA_ARGS__)
    955 
    956 /**
    957 * QM_OR_ELSE_INFO is like QM_OR_ELSE_WARN. The only difference is that
    958 * failures are reported using a lower level of severity relative to failures
    959 * reported by QM_OR_ELSE_WARN.
    960 */
    961 #define QM_OR_ELSE_INFO(...) QM_OR_ELSE_REPORT(Info, __VA_ARGS__)
    962 
    963 /**
    964 * QM_OR_ELSE_LOG_VERBOSE is like QM_OR_ELSE_WARN. The only difference is that
    965 * failures are reported using the lowest severity which is currently ignored
    966 * in LogError, so nothing goes to the console, browser console and telemetry.
    967 * Since nothing goes to the telemetry, the macro can't signal the end of the
    968 * underlying error stack or change the type of the error stack in the
    969 * telemetry. For that reason, the expression shouldn't contain nested QM_TRY
    970 * macro uses.
    971 */
    972 #define QM_OR_ELSE_LOG_VERBOSE(...) QM_OR_ELSE_REPORT(Verbose, __VA_ARGS__)
    973 
    974 namespace mozilla::dom::quota {
    975 
    976 // XXX Support orElseIf directly in mozilla::Result
    977 template <typename V, typename E, typename P, typename F>
    978 auto OrElseIf(Result<V, E>&& aResult, P&& aPred, F&& aFunc) -> Result<V, E> {
    979  return MOZ_UNLIKELY(aResult.isErr())
    980             ? (std::forward<P>(aPred)(aResult.inspectErr()))
    981                   ? std::forward<F>(aFunc)(aResult.unwrapErr())
    982                   : aResult.propagateErr()
    983             : aResult.unwrap();
    984 }
    985 
    986 }  // namespace mozilla::dom::quota
    987 
    988 // QM_OR_ELSE_REPORT_IF macro is an implementation detail of
    989 // QM_OR_ELSE_WARN_IF/QM_OR_ELSE_INFO_IF/QM_OR_ELSE_LOG_VERBOSE_IF and
    990 // shouldn't be used directly.
    991 
    992 #define QM_OR_ELSE_REPORT_IF(severity, expr, predicate, fallback) \
    993  mozilla::dom::quota::OrElseIf(                                  \
    994      (expr),                                                     \
    995      [&](const auto& firstRes) {                                 \
    996        bool res = predicate(firstRes);                           \
    997        mozilla::dom::quota::QM_HANDLE_ERROR(                     \
    998            #expr, firstRes,                                      \
    999            res ? mozilla::dom::quota::Severity::severity         \
   1000                : mozilla::dom::quota::Severity::Error);          \
   1001        return res;                                               \
   1002      },                                                          \
   1003      fallback)
   1004 
   1005 /*
   1006 * QM_OR_ELSE_WARN_IF(expr, predicate, fallback) evaluates expr first, which
   1007 * must produce a Result value. On Success, it just moves the success over.
   1008 * On error, it calls a predicate function (passed as the second argument) and
   1009 * then it either calls HandleError (with the Warning severity) and a fallback
   1010 * function (passed as the third argument) which produces a new result if the
   1011 * predicate returned true. Or it calls HandleError (with the Error severity)
   1012 * and propagates the error result if the predicate returned false. So failed
   1013 * expr can be reported as a warning or as an error depending on the predicate.
   1014 * QM_OR_ELSE_WARN_IF is a sub macro and is intended to be used along with one
   1015 * of the main macros such as QM_TRY.
   1016 */
   1017 #define QM_OR_ELSE_WARN_IF(...) QM_OR_ELSE_REPORT_IF(Warning, __VA_ARGS__)
   1018 
   1019 /**
   1020 * QM_OR_ELSE_INFO_IF is like QM_OR_ELSE_WARN_IF. The only difference is that
   1021 * failures are reported using a lower level of severity relative to failures
   1022 * reported by QM_OR_ELSE_WARN_IF.
   1023 */
   1024 #define QM_OR_ELSE_INFO_IF(...) QM_OR_ELSE_REPORT_IF(Info, __VA_ARGS__)
   1025 
   1026 /**
   1027 * QM_OR_ELSE_LOG_VERBOSE_IF is like QM_OR_ELSE_WARN_IF. The only difference is
   1028 * that failures are reported using the lowest severity which is currently
   1029 * ignored in LogError, so nothing goes to the console, browser console and
   1030 * telemetry. Since nothing goes to the telemetry, the macro can't signal the
   1031 * end of the underlying error stack or change the type of the error stack in
   1032 * the telemetry. For that reason, the expression shouldn't contain nested
   1033 * QM_TRY macro uses.
   1034 */
   1035 #define QM_OR_ELSE_LOG_VERBOSE_IF(...) \
   1036  QM_OR_ELSE_REPORT_IF(Verbose, __VA_ARGS__)
   1037 
   1038 // Telemetry probes to collect number of failure during the initialization.
   1039 #ifdef NIGHTLY_BUILD
   1040 #  define RECORD_IN_NIGHTLY(_recorder, _status) \
   1041    do {                                        \
   1042      if (NS_SUCCEEDED(_recorder)) {            \
   1043        _recorder = _status;                    \
   1044      }                                         \
   1045    } while (0)
   1046 
   1047 #  define OK_IN_NIGHTLY_PROPAGATE_IN_OTHERS \
   1048    Ok {}
   1049 
   1050 #  define RETURN_STATUS_OR_RESULT(_status, _rv) \
   1051    return Err(NS_FAILED(_status) ? (_status) : (_rv))
   1052 #else
   1053 #  define RECORD_IN_NIGHTLY(_dummy, _status) \
   1054    {                                        \
   1055    }
   1056 
   1057 #  define OK_IN_NIGHTLY_PROPAGATE_IN_OTHERS QM_PROPAGATE
   1058 
   1059 #  define RETURN_STATUS_OR_RESULT(_status, _rv) return Err(_rv)
   1060 #endif
   1061 
   1062 class mozIStorageConnection;
   1063 class mozIStorageStatement;
   1064 class nsIFile;
   1065 
   1066 namespace mozilla {
   1067 
   1068 class LogModule;
   1069 
   1070 struct CreateIfNonExistent {};
   1071 
   1072 struct NotOk {};
   1073 
   1074 // Allow MOZ_TRY/QM_TRY to handle `bool` values by wrapping them with OkIf.
   1075 // TODO: Maybe move this to mfbt/ResultExtensions.h
   1076 inline Result<Ok, NotOk> OkIf(bool aValue) {
   1077  if (aValue) {
   1078    return Ok();
   1079  }
   1080  return Err(NotOk());
   1081 }
   1082 
   1083 // TODO: Maybe move this to mfbt/ResultExtensions.h
   1084 template <auto SuccessValue>
   1085 auto OkToOk(Ok) -> Result<decltype(SuccessValue), nsresult> {
   1086  return SuccessValue;
   1087 }
   1088 
   1089 template <nsresult ErrorValue, auto SuccessValue,
   1090          typename V = decltype(SuccessValue)>
   1091 auto ErrToOkOrErr(nsresult aValue) -> Result<V, nsresult> {
   1092  if (aValue == ErrorValue) {
   1093    return V{SuccessValue};
   1094  }
   1095  return Err(aValue);
   1096 }
   1097 
   1098 template <nsresult ErrorValue, typename V = mozilla::Ok>
   1099 auto ErrToDefaultOkOrErr(nsresult aValue) -> Result<V, nsresult> {
   1100  if (aValue == ErrorValue) {
   1101    return V{};
   1102  }
   1103  return Err(aValue);
   1104 }
   1105 
   1106 // Helper template function so that QM_TRY predicates checking for a specific
   1107 // error can be concisely written as IsSpecificError<NS_SOME_ERROR> instead of
   1108 // as a more verbose lambda.
   1109 template <nsresult ErrorValue>
   1110 bool IsSpecificError(const nsresult aValue) {
   1111  return aValue == ErrorValue;
   1112 }
   1113 
   1114 #ifdef QM_ERROR_STACKS_ENABLED
   1115 template <nsresult ErrorValue>
   1116 bool IsSpecificError(const QMResult& aValue) {
   1117  return aValue.NSResult() == ErrorValue;
   1118 }
   1119 #endif
   1120 
   1121 // Helper template function so that QM_TRY fallback functions that are
   1122 // converting errors into specific in-band success values can be concisely
   1123 // written as ErrToOk<SuccessValueToReturn> (with the return type inferred).
   1124 // For example, many file-related APIs that access information about a file may
   1125 // return an nsresult error code if the file does not exist. From an
   1126 // application perspective, the file not existing is not actually exceptional
   1127 // and can instead be handled by the success case.
   1128 template <auto SuccessValue, typename V = decltype(SuccessValue)>
   1129 auto ErrToOk(const nsresult aValue) -> Result<V, nsresult> {
   1130  return V{SuccessValue};
   1131 }
   1132 
   1133 template <auto SuccessValue, typename V = decltype(SuccessValue)>
   1134 auto ErrToOkFromQMResult(const QMResult& aValue) -> Result<V, QMResult> {
   1135  return V{SuccessValue};
   1136 }
   1137 
   1138 // Helper template function so that QM_TRY fallback functions that are
   1139 // suppressing errors by converting them into (generic) success can be
   1140 // concisely written as ErrToDefaultOk<>.
   1141 template <typename V = mozilla::Ok>
   1142 auto ErrToDefaultOk(const nsresult aValue) -> Result<V, nsresult> {
   1143  return V{};
   1144 }
   1145 
   1146 template <typename MozPromiseType, typename RejectValueT = nsresult>
   1147 auto CreateAndRejectMozPromise(StaticString aFunc, const RejectValueT& aRv)
   1148    -> decltype(auto) {
   1149  if constexpr (std::is_same_v<RejectValueT, nsresult>) {
   1150    return MozPromiseType::CreateAndReject(aRv, aFunc);
   1151  } else if constexpr (std::is_same_v<RejectValueT, QMResult>) {
   1152    return MozPromiseType::CreateAndReject(aRv.NSResult(), aFunc);
   1153  }
   1154 }
   1155 
   1156 RefPtr<BoolPromise> CreateAndRejectBoolPromise(StaticString aFunc,
   1157                                               nsresult aRv);
   1158 
   1159 RefPtr<Int64Promise> CreateAndRejectInt64Promise(StaticString aFunc,
   1160                                                 nsresult aRv);
   1161 
   1162 RefPtr<BoolPromise> CreateAndRejectBoolPromiseFromQMResult(StaticString aFunc,
   1163                                                           const QMResult& aRv);
   1164 
   1165 // Like Rust's collect with a step function, not a generic iterator/range.
   1166 //
   1167 // Cond must be a function type with a return type to Result<V, E>, where
   1168 // V is convertible to bool
   1169 // - success converts to true indicates that collection shall continue
   1170 // - success converts to false indicates that collection is completed
   1171 // - error indicates that collection shall stop, propagating the error
   1172 //
   1173 // Body must a function type accepting a V xvalue with a return type convertible
   1174 // to Result<empty, E>.
   1175 template <typename Step, typename Body>
   1176 auto CollectEach(Step aStep, const Body& aBody)
   1177    -> Result<mozilla::Ok, typename std::invoke_result_t<Step>::err_type> {
   1178  using StepResultType = typename std::invoke_result_t<Step>::ok_type;
   1179 
   1180  static_assert(
   1181      std::is_empty_v<
   1182          typename std::invoke_result_t<Body, StepResultType&&>::ok_type>);
   1183 
   1184  while (true) {
   1185    StepResultType element = MOZ_TRY(aStep());
   1186 
   1187    if (!static_cast<bool>(element)) {
   1188      break;
   1189    }
   1190 
   1191    MOZ_TRY(aBody(std::move(element)));
   1192  }
   1193 
   1194  return mozilla::Ok{};
   1195 }
   1196 
   1197 // This is like std::reduce with a to-be-defined execution policy (we don't want
   1198 // to std::terminate on an error, but probably it's fine to just propagate any
   1199 // error that occurred), operating not on a pair of iterators but rather a
   1200 // generator function.
   1201 template <typename InputGenerator, typename T, typename BinaryOp>
   1202 auto ReduceEach(InputGenerator aInputGenerator, T aInit,
   1203                const BinaryOp& aBinaryOp)
   1204    -> Result<T, typename std::invoke_result_t<InputGenerator>::err_type> {
   1205  T res = std::move(aInit);
   1206 
   1207  // XXX This can be done in parallel!
   1208  MOZ_TRY(CollectEach(
   1209      std::move(aInputGenerator),
   1210      [&res, &aBinaryOp](const auto& element)
   1211          -> Result<Ok,
   1212                    typename std::invoke_result_t<InputGenerator>::err_type> {
   1213        res = MOZ_TRY(aBinaryOp(std::move(res), element));
   1214 
   1215        return Ok{};
   1216      }));
   1217 
   1218  return std::move(res);
   1219 }
   1220 
   1221 // This is like std::reduce with a to-be-defined execution policy (we don't want
   1222 // to std::terminate on an error, but probably it's fine to just propagate any
   1223 // error that occurred).
   1224 template <typename Range, typename T, typename BinaryOp>
   1225 auto Reduce(Range&& aRange, T aInit, const BinaryOp& aBinaryOp) {
   1226  using std::begin;
   1227  using std::end;
   1228  return ReduceEach(
   1229      [it = begin(aRange), end = end(aRange)]() mutable {
   1230        auto res = ToMaybeRef(it != end ? &*it++ : nullptr);
   1231        return Result<decltype(res), typename std::invoke_result_t<
   1232                                         BinaryOp, T, decltype(res)>::err_type>(
   1233            res);
   1234      },
   1235      aInit, aBinaryOp);
   1236 }
   1237 
   1238 template <typename Range, typename Body>
   1239 auto CollectEachInRange(Range&& aRange, const Body& aBody)
   1240    -> Result<mozilla::Ok, nsresult> {
   1241  for (auto&& element : aRange) {
   1242    MOZ_TRY(aBody(element));
   1243  }
   1244 
   1245  return mozilla::Ok{};
   1246 }
   1247 
   1248 // Like Rust's collect with a while loop, not a generic iterator/range.
   1249 //
   1250 // Cond must be a function type accepting no parameters with a return type
   1251 // convertible to Result<bool, E>, where
   1252 // - success true indicates that collection shall continue
   1253 // - success false indicates that collection is completed
   1254 // - error indicates that collection shall stop, propagating the error
   1255 //
   1256 // Body must a function type accepting no parameters with a return type
   1257 // convertible to Result<empty, E>.
   1258 template <typename Cond, typename Body>
   1259 auto CollectWhile(const Cond& aCond, const Body& aBody)
   1260    -> Result<mozilla::Ok, typename std::invoke_result_t<Cond>::err_type> {
   1261  return CollectEach(aCond, [&aBody](bool) { return aBody(); });
   1262 }
   1263 
   1264 template <>
   1265 class [[nodiscard]] GenericErrorResult<mozilla::ipc::IPCResult> {
   1266  mozilla::ipc::IPCResult mErrorValue;
   1267 
   1268  template <typename V, typename E2>
   1269  friend class Result;
   1270 
   1271 public:
   1272  explicit GenericErrorResult(mozilla::ipc::IPCResult aErrorValue)
   1273      : mErrorValue(aErrorValue) {
   1274    MOZ_ASSERT(!aErrorValue);
   1275  }
   1276 
   1277  GenericErrorResult(mozilla::ipc::IPCResult aErrorValue,
   1278                     const ErrorPropagationTag&)
   1279      : GenericErrorResult(aErrorValue) {}
   1280 
   1281  operator mozilla::ipc::IPCResult() const { return mErrorValue; }
   1282 };
   1283 
   1284 namespace dom::quota {
   1285 
   1286 extern const char kQuotaGenericDelimiter;
   1287 
   1288 // Telemetry keys to indicate types of errors.
   1289 #ifdef NIGHTLY_BUILD
   1290 extern const nsLiteralCString kQuotaInternalError;
   1291 extern const nsLiteralCString kQuotaExternalError;
   1292 #else
   1293 // No need for these when we're not collecting telemetry.
   1294 #  define kQuotaInternalError
   1295 #  define kQuotaExternalError
   1296 #endif
   1297 
   1298 MOZ_COLD void ReportInternalError(const char* aFile, uint32_t aLine,
   1299                                  const char* aStr);
   1300 
   1301 LogModule* GetQuotaManagerLogger();
   1302 
   1303 void AnonymizeCString(nsACString& aCString);
   1304 
   1305 inline auto AnonymizedCString(const nsACString& aCString) {
   1306  nsAutoCString result{aCString};
   1307  AnonymizeCString(result);
   1308  return result;
   1309 }
   1310 
   1311 void AnonymizeOriginString(nsACString& aOriginString);
   1312 
   1313 inline auto AnonymizedOriginString(const nsACString& aOriginString) {
   1314  nsAutoCString result{aOriginString};
   1315  AnonymizeOriginString(result);
   1316  return result;
   1317 }
   1318 
   1319 #ifdef XP_WIN
   1320 void CacheUseDOSDevicePathSyntaxPrefValue();
   1321 #endif
   1322 
   1323 Result<nsCOMPtr<nsIFile>, nsresult> QM_NewLocalFile(const nsAString& aPath);
   1324 
   1325 nsDependentCSubstring GetLeafName(const nsACString& aPath);
   1326 
   1327 Result<nsCOMPtr<nsIFile>, nsresult> CloneFileAndAppend(
   1328    nsIFile& aDirectory, const nsAString& aPathElement);
   1329 
   1330 enum class nsIFileKind {
   1331  ExistsAsDirectory,
   1332  ExistsAsFile,
   1333  DoesNotExist,
   1334 };
   1335 
   1336 // XXX We can use this outside of QM and its clients as well, probably. Maybe it
   1337 // could be moved to xpcom/io?
   1338 Result<nsIFileKind, nsresult> GetDirEntryKind(nsIFile& aFile);
   1339 
   1340 Result<nsCOMPtr<mozIStorageStatement>, nsresult> CreateStatement(
   1341    mozIStorageConnection& aConnection, const nsACString& aStatementString);
   1342 
   1343 enum class SingleStepResult { AssertHasResult, ReturnNullIfNoResult };
   1344 
   1345 template <SingleStepResult ResultHandling>
   1346 using SingleStepSuccessType =
   1347    std::conditional_t<ResultHandling == SingleStepResult::AssertHasResult,
   1348                       NotNull<nsCOMPtr<mozIStorageStatement>>,
   1349                       nsCOMPtr<mozIStorageStatement>>;
   1350 
   1351 template <SingleStepResult ResultHandling>
   1352 Result<SingleStepSuccessType<ResultHandling>, nsresult> ExecuteSingleStep(
   1353    nsCOMPtr<mozIStorageStatement>&& aStatement);
   1354 
   1355 // Creates a statement with the specified aStatementString, executes a single
   1356 // step, and returns the statement.
   1357 // Depending on the value ResultHandling,
   1358 // - it is asserted that there is a result (default resp.
   1359 //   SingleStepResult::AssertHasResult), and the success type is
   1360 //   MovingNotNull<nsCOMPtr<mozIStorageStatement>>
   1361 // - it is asserted that there is no result, and the success type is Ok
   1362 // - in case there is no result, nullptr is returned, and the success type is
   1363 //   nsCOMPtr<mozIStorageStatement>
   1364 // Any other errors are always propagated.
   1365 template <SingleStepResult ResultHandling = SingleStepResult::AssertHasResult>
   1366 Result<SingleStepSuccessType<ResultHandling>, nsresult>
   1367 CreateAndExecuteSingleStepStatement(mozIStorageConnection& aConnection,
   1368                                    const nsACString& aStatementString);
   1369 
   1370 namespace detail {
   1371 
   1372 // Determine the absolute path of the root of our built source tree so we can
   1373 // derive source-relative paths for non-exported header files in
   1374 // MakeSourceFileRelativePath. Exported header files end up in the objdir and
   1375 // we have GetObjdirDistIncludeTreeBase for that.
   1376 nsDependentCSubstring GetSourceTreeBase();
   1377 
   1378 // Determine the absolute path of the root of our built OBJDIR/dist/include
   1379 // directory. The aQuotaCommonHPath argument cleverly defaults to __FILE__
   1380 // initialized in our exported header; no argument should ever be provided to
   1381 // this method. GetSourceTreeBase handles identifying the root of the source
   1382 // tree.
   1383 nsDependentCSubstring GetObjdirDistIncludeTreeBase(
   1384    const nsLiteralCString& aQuotaCommonHPath = nsLiteralCString(__FILE__));
   1385 
   1386 nsDependentCSubstring MakeSourceFileRelativePath(
   1387    const nsACString& aSourceFilePath);
   1388 
   1389 }  // namespace detail
   1390 
   1391 enum class Severity {
   1392  Error,
   1393  Warning,
   1394  Info,
   1395  Verbose,
   1396 };
   1397 
   1398 #ifdef QM_LOG_ERROR_ENABLED
   1399 #  ifdef QM_ERROR_STACKS_ENABLED
   1400 using ResultType = Variant<QMResult, nsresult, Nothing>;
   1401 
   1402 void LogError(const nsACString& aExpr, const ResultType& aResult,
   1403              const nsACString& aSourceFilePath, int32_t aSourceFileLine,
   1404              Severity aSeverity)
   1405 #  else
   1406 void LogError(const nsACString& aExpr, Maybe<nsresult> aMaybeRv,
   1407              const nsACString& aSourceFilePath, int32_t aSourceFileLine,
   1408              Severity aSeverity)
   1409 #  endif
   1410    ;
   1411 #endif
   1412 
   1413 #ifdef DEBUG
   1414 Result<bool, nsresult> WarnIfFileIsUnknown(nsIFile& aFile,
   1415                                           const char* aSourceFilePath,
   1416                                           int32_t aSourceFileLine);
   1417 #endif
   1418 
   1419 // As HandleError is a function that will only be called in error cases, it is
   1420 // marked with MOZ_COLD to avoid bloating the code of calling functions, if it's
   1421 // not empty.
   1422 //
   1423 // For the same reason, the string-ish parameters are of type const char* rather
   1424 // than any ns*String type, to minimize the code at each call site. This
   1425 // deliberately de-optimizes runtime performance, which is uncritical during
   1426 // error handling.
   1427 //
   1428 // This functions are not intended to be called
   1429 // directly, they should only be called from the QM_* macros.
   1430 #ifdef QM_LOG_ERROR_ENABLED
   1431 template <typename T>
   1432 MOZ_COLD MOZ_NEVER_INLINE void HandleError(const char* aExpr, const T& aRv,
   1433                                           const char* aSourceFilePath,
   1434                                           int32_t aSourceFileLine,
   1435                                           const Severity aSeverity) {
   1436 #  ifdef QM_ERROR_STACKS_ENABLED
   1437  if constexpr (std::is_same_v<T, QMResult> || std::is_same_v<T, nsresult>) {
   1438    mozilla::dom::quota::LogError(nsDependentCString(aExpr), ResultType(aRv),
   1439                                  nsDependentCString(aSourceFilePath),
   1440                                  aSourceFileLine, aSeverity);
   1441  } else {
   1442    mozilla::dom::quota::LogError(
   1443        nsDependentCString(aExpr), ResultType(Nothing{}),
   1444        nsDependentCString(aSourceFilePath), aSourceFileLine, aSeverity);
   1445  }
   1446 #  else
   1447  if constexpr (std::is_same_v<T, nsresult>) {
   1448    mozilla::dom::quota::LogError(nsDependentCString(aExpr), Some(aRv),
   1449                                  nsDependentCString(aSourceFilePath),
   1450                                  aSourceFileLine, aSeverity);
   1451  } else {
   1452    mozilla::dom::quota::LogError(nsDependentCString(aExpr), Nothing{},
   1453                                  nsDependentCString(aSourceFilePath),
   1454                                  aSourceFileLine, aSeverity);
   1455  }
   1456 #  endif
   1457 }
   1458 #else
   1459 template <typename T>
   1460 MOZ_ALWAYS_INLINE constexpr void HandleError(const char* aExpr, const T& aRv,
   1461                                             const char* aSourceFilePath,
   1462                                             int32_t aSourceFileLine,
   1463                                             const Severity aSeverity) {}
   1464 #endif
   1465 
   1466 template <typename T>
   1467 Nothing HandleErrorReturnNothing(const char* aExpr, const T& aRv,
   1468                                 const char* aSourceFilePath,
   1469                                 int32_t aSourceFileLine,
   1470                                 const Severity aSeverity) {
   1471  HandleError(aExpr, aRv, aSourceFilePath, aSourceFileLine, aSeverity);
   1472  return Nothing();
   1473 }
   1474 
   1475 template <typename T, typename CleanupFunc>
   1476 Nothing HandleErrorWithCleanupReturnNothing(const char* aExpr, const T& aRv,
   1477                                            const char* aSourceFilePath,
   1478                                            int32_t aSourceFileLine,
   1479                                            const Severity aSeverity,
   1480                                            CleanupFunc&& aCleanupFunc) {
   1481  HandleError(aExpr, aRv, aSourceFilePath, aSourceFileLine, aSeverity);
   1482  std::forward<CleanupFunc>(aCleanupFunc)(aRv);
   1483  return Nothing();
   1484 }
   1485 
   1486 // Implementation of workaround for GCC bug #114812.
   1487 #if defined(__GNUC__) && !defined(__clang__)
   1488 namespace gcc_detail {
   1489 // usual case: identity function
   1490 template <typename T>
   1491 struct invokabilize_impl {
   1492  auto operator()(T t) -> T { return t; }
   1493 };
   1494 // reference-to-function: wrap in std::function
   1495 template <typename R, typename... Args>
   1496 struct invokabilize_impl<R (&)(Args...)> {
   1497  auto operator()(R (&t)(Args...)) -> std::function<R(Args...)> {
   1498    return std::function{t};
   1499  }
   1500 };
   1501 // pointer-to-function: wrap in std::function
   1502 template <typename R, typename... Args>
   1503 struct invokabilize_impl<R (*)(Args...)> {
   1504  auto operator()(R (*t)(Args...)) -> std::function<R(Args...)> {
   1505    return std::function{t};
   1506  }
   1507 };
   1508 // entry point
   1509 template <typename T>
   1510 auto invokabilize(T t) {
   1511  return invokabilize_impl<T>{}(std::forward<T>(t));
   1512 }
   1513 }  // namespace gcc_detail
   1514 #endif
   1515 
   1516 template <size_t NFunc, size_t NExpr, typename T, typename CustomRetVal_>
   1517 auto HandleCustomRetVal(const char (&aFunc)[NFunc], const char (&aExpr)[NExpr],
   1518                        const T& aRv, CustomRetVal_&& aCustomRetVal_) {
   1519 #if defined(__GNUC__) && !defined(__clang__)
   1520  // Workaround for gcc bug #114812. (See either that bug, or our bug 1891541,
   1521  // for more details.)
   1522  auto aCustomRetVal =
   1523      gcc_detail::invokabilize(std::forward<CustomRetVal_>(aCustomRetVal_));
   1524  using CustomRetVal = decltype(aCustomRetVal);
   1525 #else
   1526  using CustomRetVal = CustomRetVal_;
   1527  CustomRetVal& aCustomRetVal = aCustomRetVal_;
   1528 #endif
   1529  if constexpr (std::is_invocable<CustomRetVal, const char[NFunc],
   1530                                  const char[NExpr]>::value) {
   1531    return std::forward<CustomRetVal>(aCustomRetVal)(aFunc, aExpr);
   1532  } else if constexpr (std::is_invocable<CustomRetVal, const char[NFunc],
   1533                                         const T&>::value) {
   1534    return aCustomRetVal(aFunc, aRv);
   1535  } else if constexpr (std::is_invocable<CustomRetVal, const T&>::value) {
   1536    return aCustomRetVal(aRv);
   1537  } else {
   1538    return std::forward<CustomRetVal>(aCustomRetVal);
   1539  }
   1540 }
   1541 
   1542 template <SingleStepResult ResultHandling = SingleStepResult::AssertHasResult,
   1543          typename BindFunctor>
   1544 Result<SingleStepSuccessType<ResultHandling>, nsresult>
   1545 CreateAndExecuteSingleStepStatement(mozIStorageConnection& aConnection,
   1546                                    const nsACString& aStatementString,
   1547                                    BindFunctor aBindFunctor) {
   1548  QM_TRY_UNWRAP(auto stmt, CreateStatement(aConnection, aStatementString));
   1549 
   1550  QM_TRY(aBindFunctor(*stmt));
   1551 
   1552  return ExecuteSingleStep<ResultHandling>(std::move(stmt));
   1553 }
   1554 
   1555 template <typename StepFunc>
   1556 Result<Ok, nsresult> CollectWhileHasResult(mozIStorageStatement& aStmt,
   1557                                           StepFunc&& aStepFunc) {
   1558  return CollectWhile(
   1559      [&aStmt] {
   1560        QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER(aStmt, ExecuteStep));
   1561      },
   1562      [&aStmt, &aStepFunc] { return aStepFunc(aStmt); });
   1563 }
   1564 
   1565 template <typename StepFunc,
   1566          typename ArrayType = nsTArray<typename std::invoke_result_t<
   1567              StepFunc, mozIStorageStatement&>::ok_type>>
   1568 auto CollectElementsWhileHasResult(mozIStorageStatement& aStmt,
   1569                                   StepFunc&& aStepFunc)
   1570    -> Result<ArrayType, nsresult> {
   1571  ArrayType res;
   1572 
   1573  QM_TRY(CollectWhileHasResult(
   1574      aStmt, [&aStepFunc, &res](auto& stmt) -> Result<Ok, nsresult> {
   1575        QM_TRY_UNWRAP(auto element, aStepFunc(stmt));
   1576        res.AppendElement(std::move(element));
   1577        return Ok{};
   1578      }));
   1579 
   1580  return std::move(res);
   1581 }
   1582 
   1583 template <typename ArrayType, typename StepFunc>
   1584 auto CollectElementsWhileHasResultTyped(mozIStorageStatement& aStmt,
   1585                                        StepFunc&& aStepFunc) {
   1586  return CollectElementsWhileHasResult<StepFunc, ArrayType>(
   1587      aStmt, std::forward<StepFunc>(aStepFunc));
   1588 }
   1589 
   1590 namespace detail {
   1591 template <typename Cancel, typename Body>
   1592 Result<mozilla::Ok, nsresult> CollectEachFile(nsIFile& aDirectory,
   1593                                              const Cancel& aCancel,
   1594                                              const Body& aBody) {
   1595  GECKO_TRACE_SCOPE("dom::quota", "CollectEachFile");
   1596 
   1597  QM_TRY_INSPECT(const auto& entries, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
   1598                                          nsCOMPtr<nsIDirectoryEnumerator>,
   1599                                          aDirectory, GetDirectoryEntries));
   1600 
   1601  return CollectEach(
   1602      [&entries, &aCancel]() -> Result<nsCOMPtr<nsIFile>, nsresult> {
   1603        if (aCancel()) {
   1604          return nsCOMPtr<nsIFile>{};
   1605        }
   1606 
   1607        QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsCOMPtr<nsIFile>,
   1608                                                        entries, GetNextFile));
   1609      },
   1610      aBody);
   1611 }
   1612 }  // namespace detail
   1613 
   1614 template <typename Body>
   1615 Result<mozilla::Ok, nsresult> CollectEachFile(nsIFile& aDirectory,
   1616                                              const Body& aBody) {
   1617  return detail::CollectEachFile(aDirectory, [] { return false; }, aBody);
   1618 }
   1619 
   1620 template <typename Body>
   1621 Result<mozilla::Ok, nsresult> CollectEachFileAtomicCancelable(
   1622    nsIFile& aDirectory, const Atomic<bool>& aCanceled, const Body& aBody) {
   1623  return detail::CollectEachFile(
   1624      aDirectory, [&aCanceled] { return static_cast<bool>(aCanceled); }, aBody);
   1625 }
   1626 
   1627 template <typename T, typename Body>
   1628 auto ReduceEachFileAtomicCancelable(nsIFile& aDirectory,
   1629                                    const Atomic<bool>& aCanceled, T aInit,
   1630                                    const Body& aBody) -> Result<T, nsresult> {
   1631  QM_TRY_INSPECT(const auto& entries, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
   1632                                          nsCOMPtr<nsIDirectoryEnumerator>,
   1633                                          aDirectory, GetDirectoryEntries));
   1634 
   1635  return ReduceEach(
   1636      [&entries, &aCanceled]() -> Result<nsCOMPtr<nsIFile>, nsresult> {
   1637        if (aCanceled) {
   1638          return nsCOMPtr<nsIFile>{};
   1639        }
   1640 
   1641        QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsCOMPtr<nsIFile>,
   1642                                                        entries, GetNextFile));
   1643      },
   1644      std::move(aInit), aBody);
   1645 }
   1646 
   1647 constexpr bool IsDatabaseCorruptionError(const nsresult aRv) {
   1648  return aRv == NS_ERROR_FILE_CORRUPTED || aRv == NS_ERROR_STORAGE_IOERR;
   1649 }
   1650 
   1651 template <typename Func>
   1652 auto CallWithDelayedRetriesIfAccessDenied(Func&& aFunc, uint32_t aMaxRetries,
   1653                                          uint32_t aDelayMs)
   1654    -> Result<typename std::invoke_result_t<Func>::ok_type, nsresult> {
   1655  std::decay_t<Func> func = std::forward<Func>(aFunc);
   1656 
   1657  uint32_t retries = 0;
   1658 
   1659  while (true) {
   1660    auto result = std::invoke(func);
   1661 
   1662    if (result.isOk()) {
   1663      return result;
   1664    }
   1665 
   1666    if (result.inspectErr() != NS_ERROR_FILE_IS_LOCKED &&
   1667        result.inspectErr() != NS_ERROR_FILE_ACCESS_DENIED) {
   1668      return result;
   1669    }
   1670 
   1671    if (retries++ >= aMaxRetries) {
   1672      return result;
   1673    }
   1674 
   1675    PR_Sleep(PR_MillisecondsToInterval(aDelayMs));
   1676  }
   1677 }
   1678 
   1679 namespace detail {
   1680 
   1681 template <bool flag = false>
   1682 void UnsupportedReturnType() {
   1683  static_assert(flag, "Unsupported return type!");
   1684 }
   1685 
   1686 }  // namespace detail
   1687 
   1688 template <typename Initialization, typename StringGenerator, typename Func>
   1689 auto ExecuteInitialization(
   1690    FirstInitializationAttempts<Initialization, StringGenerator>&
   1691        aFirstInitializationAttempts,
   1692    const Initialization aInitialization, Func&& aFunc)
   1693    -> std::invoke_result_t<Func, const FirstInitializationAttempt<
   1694                                      Initialization, StringGenerator>&> {
   1695  return aFirstInitializationAttempts.WithFirstInitializationAttempt(
   1696      aInitialization, [&aFunc](auto&& firstInitializationAttempt) {
   1697        auto res = std::forward<Func>(aFunc)(firstInitializationAttempt);
   1698 
   1699        const auto rv = [&res]() -> nsresult {
   1700          using RetType =
   1701              std::invoke_result_t<Func, const FirstInitializationAttempt<
   1702                                             Initialization, StringGenerator>&>;
   1703 
   1704          if constexpr (std::is_same_v<RetType, nsresult>) {
   1705            return res;
   1706          } else if constexpr (mozilla::detail::IsResult<RetType>::value &&
   1707                               std::is_same_v<typename RetType::err_type,
   1708                                              nsresult>) {
   1709            return res.isOk() ? NS_OK : res.inspectErr();
   1710          } else {
   1711            detail::UnsupportedReturnType();
   1712          }
   1713        }();
   1714 
   1715        // NS_ERROR_ABORT signals a non-fatal, recoverable problem during
   1716        // initialization. We do not want these kind of failures to count
   1717        // against our overall first initialization attempt telemetry. Thus we
   1718        // just ignore this kind of failure and keep
   1719        // aFirstInitializationAttempts unflagged to stay ready to record a real
   1720        // success or failure on the next attempt.
   1721        if (rv == NS_ERROR_ABORT) {
   1722          return res;
   1723        }
   1724 
   1725        if (!firstInitializationAttempt.Recorded()) {
   1726          firstInitializationAttempt.Record(rv);
   1727        }
   1728 
   1729        return res;
   1730      });
   1731 }
   1732 
   1733 template <typename Initialization, typename StringGenerator, typename Func>
   1734 auto ExecuteInitialization(
   1735    FirstInitializationAttempts<Initialization, StringGenerator>&
   1736        aFirstInitializationAttempts,
   1737    const Initialization aInitialization, const nsACString& aContext,
   1738    Func&& aFunc)
   1739    -> std::invoke_result_t<Func, const FirstInitializationAttempt<
   1740                                      Initialization, StringGenerator>&> {
   1741  return ExecuteInitialization(
   1742      aFirstInitializationAttempts, aInitialization,
   1743      [&](const auto& firstInitializationAttempt) -> decltype(auto) {
   1744 #ifdef QM_SCOPED_LOG_EXTRA_INFO_ENABLED
   1745        const auto maybeScopedLogExtraInfo =
   1746            firstInitializationAttempt.Recorded()
   1747                ? Nothing{}
   1748                : Some(ScopedLogExtraInfo{
   1749                      ScopedLogExtraInfo::kTagContextTainted, aContext});
   1750 #endif
   1751        GECKO_TRACE_SCOPE("dom::quota", aContext);
   1752 
   1753        return std::forward<Func>(aFunc)(firstInitializationAttempt);
   1754      });
   1755 }
   1756 
   1757 }  // namespace dom::quota
   1758 }  // namespace mozilla
   1759 
   1760 #endif  // mozilla_dom_quota_quotacommon_h__