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__