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