forked from mirrors/gecko-dev
		
	 5770ce1b8f
			
		
	
	
		5770ce1b8f
		
	
	
	
	
		
			
			MOZ_NEVER_INLINE can't 100% guarantee that all compilers never inline such functions, but the current combination of MOZ_COLD and MOZ_NEVER_INLINE produces sane builds with only minor code size increase (max 0.19% increase). Differential Revision: https://phabricator.services.mozilla.com/D117142
		
			
				
	
	
		
			1599 lines
		
	
	
	
		
			61 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1599 lines
		
	
	
	
		
			61 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- 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 "mozilla/dom/quota/Config.h"
 | |
| 
 | |
| #include <algorithm>
 | |
| #include <cstddef>
 | |
| #include <cstdint>
 | |
| #include <map>
 | |
| #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/Result.h"
 | |
| #include "mozilla/ResultExtensions.h"
 | |
| #include "mozilla/ThreadLocal.h"
 | |
| #if defined(QM_LOG_ERROR_ENABLED) && defined(QM_ERROR_STACKS_ENABLED)
 | |
| #  include "mozilla/Variant.h"
 | |
| #endif
 | |
| #include "mozilla/dom/QMResult.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"
 | |
| #include "nsXULAppAPI.h"
 | |
| 
 | |
| namespace mozilla {
 | |
| template <typename T>
 | |
| class NotNull;
 | |
| }
 | |
| 
 | |
| // 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://stackoverflow.com/questions/24481810/how-to-remove-the-enclosing-parentheses-with-macro
 | |
| #define MOZ_REMOVE_PAREN(X) MOZ_REMOVE_PAREN_HELPER2(MOZ_REMOVE_PAREN_HELPER X)
 | |
| #define MOZ_REMOVE_PAREN_HELPER(...) MOZ_REMOVE_PAREN_HELPER __VA_ARGS__
 | |
| #define MOZ_REMOVE_PAREN_HELPER2(...) MOZ_REMOVE_PAREN_HELPER3(__VA_ARGS__)
 | |
| #define MOZ_REMOVE_PAREN_HELPER3(...) MOZ_REMOVE_PAREN_HELPER4_##__VA_ARGS__
 | |
| #define MOZ_REMOVE_PAREN_HELPER4_MOZ_REMOVE_PAREN_HELPER
 | |
| 
 | |
| // 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)
 | |
| 
 | |
| #define UNKNOWN_FILE_WARNING(_leafName)                                       \
 | |
|   NS_WARNING(                                                                 \
 | |
|       nsPrintfCString("Something (%s) in the directory that doesn't belong!", \
 | |
|                       NS_ConvertUTF16toUTF8(_leafName).get())                 \
 | |
|           .get())
 | |
| 
 | |
| // 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.
 | |
| #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)
 | |
|  */
 | |
| 
 | |
| #define QM_VOID
 | |
| 
 | |
| #define QM_PROPAGATE Err(tryTempError)
 | |
| 
 | |
| #ifdef DEBUG
 | |
| #  define QM_ASSERT_UNREACHABLE                       \
 | |
|     []() -> ::mozilla::GenericErrorResult<nsresult> { \
 | |
|       MOZ_CRASH("Should never be reached.");          \
 | |
|     }()
 | |
| 
 | |
| #  define QM_ASSERT_UNREACHABLE_VOID \
 | |
|     [] { MOZ_CRASH("Should never be reached."); }()
 | |
| #endif
 | |
| 
 | |
| #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
 | |
| #  define QM_DIAGNOSTIC_ASSERT_UNREACHABLE            \
 | |
|     []() -> ::mozilla::GenericErrorResult<nsresult> { \
 | |
|       MOZ_CRASH("Should never be reached.");          \
 | |
|     }()
 | |
| 
 | |
| #  define QM_DIAGNOSTIC_ASSERT_UNREACHABLE_VOID \
 | |
|     [] { MOZ_CRASH("Should never be reached."); }()
 | |
| #endif
 | |
| 
 | |
| // 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
 | |
| 
 | |
| // 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 = ::mozilla::ToResult(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 = ::mozilla::ToResult(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);       \
 | |
|     return 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 = ::mozilla::ToResult(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);                                                \
 | |
|     return 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_6(                                                                 \
 | |
|         , ##__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]) 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 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);            \
 | |
|     return 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);                                          \
 | |
|     return 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 = ::mozilla::ToResult(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 = ::mozilla::ToResult(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); \
 | |
|     return 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 = ::mozilla::ToResult(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);                                               \
 | |
|     return 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_NOTEONLY_TRY and
 | |
| // shouldn't be used directly.
 | |
| 
 | |
| // Handles the three arguments case when only a warning/note is reported.
 | |
| #define QM_REPORTONLY_TRY(tryResult, severity, expr)                           \
 | |
|   auto tryResult = ::mozilla::ToResult(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 = ::mozilla::ToResult(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_NOTEONLY_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_NOTEONLY_TRY(...) QM_REPORTONLY_TRY_GLUE(Note, __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_NOTEONLY_TRY_UNWRAP and shouldn't be used
 | |
| // directly.
 | |
| 
 | |
| // Handles the four arguments case when only a warning/note 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_NOTEONLY_TRY_UNWRAP is like QM_WARN_CHECK_UNWRAP. The only difference is
 | |
|  * that failures are reported using a lower level of severity relative to
 | |
|  * failures reported by QM_WARN_CHECK_UNWRAP.
 | |
|  */
 | |
| #define QM_NOTEONLY_TRY_UNWRAP(...) \
 | |
|   QM_REPORTONLY_TRY_ASSIGN_GLUE(Note, __VA_ARGS__)
 | |
| 
 | |
| // QM_NOTEONLY_TRY_INSPECT doesn't make sense.
 | |
| 
 | |
| // QM_OR_ELSE_REPORT macro is an implementation detail of
 | |
| // QM_OR_ELSE_WARN/QM_OR_ELSE_NOTE/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_NOTE 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_NOTE(...) QM_OR_ELSE_REPORT(Note, __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(Log, __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_NOTE_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_NOTE_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_NOTE_IF(...) QM_OR_ELSE_REPORT_IF(Note, __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 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;
 | |
| }
 | |
| 
 | |
| // 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};
 | |
| }
 | |
| 
 | |
| // 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{};
 | |
| }
 | |
| 
 | |
| // TODO: Maybe move this to mfbt/ResultExtensions.h
 | |
| template <typename R, typename Func, typename... Args>
 | |
| Result<R, nsresult> ToResultGet(const Func& aFunc, Args&&... aArgs) {
 | |
|   nsresult rv;
 | |
|   R res = aFunc(std::forward<Args>(aArgs)..., &rv);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     return Err(rv);
 | |
|   }
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| // 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::result_of_t<Step()>::err_type> {
 | |
|   using StepResultType = typename std::result_of_t<Step()>::ok_type;
 | |
| 
 | |
|   static_assert(std::is_empty_v<
 | |
|                 typename std::result_of_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::result_of_t<Cond()>::err_type> {
 | |
|   return CollectEach(aCond, [&aBody](bool) { return aBody(); });
 | |
| }
 | |
| 
 | |
| template <>
 | |
| class MOZ_MUST_USE_TYPE 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 {
 | |
| namespace 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
 | |
| 
 | |
| class BackgroundThreadObject {
 | |
|  protected:
 | |
|   nsCOMPtr<nsIEventTarget> mOwningThread;
 | |
| 
 | |
|  public:
 | |
|   void AssertIsOnOwningThread() const
 | |
| #ifdef DEBUG
 | |
|       ;
 | |
| #else
 | |
|   {
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   nsIEventTarget* OwningThread() const;
 | |
| 
 | |
|  protected:
 | |
|   BackgroundThreadObject();
 | |
| 
 | |
|   explicit BackgroundThreadObject(nsIEventTarget* aOwningThread);
 | |
| };
 | |
| 
 | |
| void AssertIsOnIOThread();
 | |
| 
 | |
| void AssertCurrentThreadOwnsQuotaMutex();
 | |
| 
 | |
| bool IsOnIOThread();
 | |
| 
 | |
| 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,
 | |
|   Note,
 | |
|   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
 | |
| 
 | |
| struct MOZ_STACK_CLASS ScopedLogExtraInfo {
 | |
|   static constexpr const char kTagQuery[] = "query";
 | |
|   static constexpr const char kTagContext[] = "context";
 | |
| 
 | |
| #ifdef QM_SCOPED_LOG_EXTRA_INFO_ENABLED
 | |
|  private:
 | |
|   static auto FindSlot(const char* aTag);
 | |
| 
 | |
|  public:
 | |
|   template <size_t N>
 | |
|   ScopedLogExtraInfo(const char (&aTag)[N], const nsACString& aExtraInfo)
 | |
|       : mTag{aTag}, mCurrentValue{aExtraInfo} {
 | |
|     // Initialize is currently only called in the parent process, we could call
 | |
|     // it directly from nsLayoutStatics::Initialize in the content process to
 | |
|     // allow use of ScopedLogExtraInfo in that too. The check in GetExtraInfoMap
 | |
|     // must be removed then.
 | |
|     MOZ_ASSERT(XRE_IsParentProcess());
 | |
| 
 | |
|     AddInfo();
 | |
|   }
 | |
| 
 | |
|   ~ScopedLogExtraInfo();
 | |
| 
 | |
|   ScopedLogExtraInfo(ScopedLogExtraInfo&& aOther);
 | |
|   ScopedLogExtraInfo& operator=(ScopedLogExtraInfo&& aOther) = delete;
 | |
| 
 | |
|   ScopedLogExtraInfo(const ScopedLogExtraInfo&) = delete;
 | |
|   ScopedLogExtraInfo& operator=(const ScopedLogExtraInfo&) = delete;
 | |
| 
 | |
|   using ScopedLogExtraInfoMap = std::map<const char*, const nsACString*>;
 | |
|   static ScopedLogExtraInfoMap GetExtraInfoMap();
 | |
| 
 | |
|   static void Initialize();
 | |
| 
 | |
|  private:
 | |
|   const char* mTag;
 | |
|   const nsACString* mPreviousValue;
 | |
|   nsCString mCurrentValue;
 | |
| 
 | |
|   static MOZ_THREAD_LOCAL(const nsACString*) sQueryValue;
 | |
|   static MOZ_THREAD_LOCAL(const nsACString*) sContextValue;
 | |
| 
 | |
|   void AddInfo();
 | |
| #else
 | |
|   template <size_t N>
 | |
|   ScopedLogExtraInfo(const char (&aTag)[N], const nsACString& aExtraInfo) {}
 | |
| 
 | |
|   // user-defined to silence unused variable warnings
 | |
|   ~ScopedLogExtraInfo() {}
 | |
| #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();
 | |
| }
 | |
| 
 | |
| 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(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_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_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_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_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::result_of_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 quota
 | |
| }  // namespace dom
 | |
| }  // namespace mozilla
 | |
| 
 | |
| #endif  // mozilla_dom_quota_quotacommon_h__
 |