summaryrefslogtreecommitdiffstats
path: root/widget/PrintBackgroundTask.h
diff options
context:
space:
mode:
Diffstat (limited to 'widget/PrintBackgroundTask.h')
-rw-r--r--widget/PrintBackgroundTask.h121
1 files changed, 121 insertions, 0 deletions
diff --git a/widget/PrintBackgroundTask.h b/widget/PrintBackgroundTask.h
new file mode 100644
index 0000000000..8b18c8a879
--- /dev/null
+++ b/widget/PrintBackgroundTask.h
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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_PrintBackgroundTask_h_
+#define mozilla_PrintBackgroundTask_h_
+
+#include "mozilla/dom/Promise.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/Telemetry.h"
+
+#include <tuple>
+#include <utility>
+
+// A helper to resolve a DOM Promise with the result of a const method, executed
+// in another thread.
+//
+// Once in the main thread, the caller can turn the result of the method into a
+// JSValue by specializing ResolveOrReject.
+namespace mozilla {
+
+template <typename T, typename Result>
+void ResolveOrReject(dom::Promise& aPromise, T&, Result& aResult) {
+ aPromise.MaybeResolve(std::forward<Result>(aResult));
+}
+
+template <typename T, typename Result, typename... Args>
+using PrintBackgroundTask = Result (T::*)(Args...) const;
+
+template <typename T, typename Result, typename... Args>
+void SpawnPrintBackgroundTask(
+ T& aReceiver, dom::Promise& aPromise, const nsCString& aTelemetryKey,
+ PrintBackgroundTask<T, Result, Args...> aBackgroundTask, Args... aArgs) {
+ auto promiseHolder = MakeRefPtr<nsMainThreadPtrHolder<dom::Promise>>(
+ "nsPrinterBase::SpawnBackgroundTaskPromise", &aPromise);
+ // We actually want to allow to access the printer data from the callback, so
+ // disable strict checking. They should of course only access immutable
+ // members.
+ auto holder = MakeRefPtr<nsMainThreadPtrHolder<T>>(
+ "nsPrinterBase::SpawnBackgroundTaskPrinter", &aReceiver,
+ /* strict = */ false);
+ // See
+ // https://stackoverflow.com/questions/47496358/c-lambdas-how-to-capture-variadic-parameter-pack-from-the-upper-scope
+ // about the tuple shenanigans. It could be improved with C++20
+ NS_DispatchBackgroundTask(
+ NS_NewRunnableFunction(
+ "SpawnPrintBackgroundTask",
+ [holder = std::move(holder), promiseHolder = std::move(promiseHolder),
+ aTelemetryKey, startRoundTrip = TimeStamp::Now(),
+ backgroundTask = aBackgroundTask,
+ aArgs = std::make_tuple(std::forward<Args>(aArgs)...)] {
+ auto start = TimeStamp::Now();
+ Result result = std::apply(
+ [&](auto&&... args) {
+ return (holder->get()->*backgroundTask)(args...);
+ },
+ std::move(aArgs));
+ Telemetry::AccumulateTimeDelta(
+ Telemetry::PRINT_BACKGROUND_TASK_TIME_MS, aTelemetryKey, start);
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "SpawnPrintBackgroundTaskResolution",
+ [holder = std::move(holder),
+ promiseHolder = std::move(promiseHolder),
+ telemetryKey = std::move(aTelemetryKey), startRoundTrip,
+ result = std::move(result)] {
+ Telemetry::AccumulateTimeDelta(
+ Telemetry::PRINT_BACKGROUND_TASK_ROUND_TRIP_TIME_MS,
+ telemetryKey, startRoundTrip);
+ ResolveOrReject(*promiseHolder->get(), *holder->get(),
+ result);
+ }));
+ }),
+ NS_DISPATCH_EVENT_MAY_BLOCK);
+}
+
+// Gets a fresh promise into aResultPromise, that resolves whenever the print
+// background task finishes.
+template <typename T, typename Result, typename... Args>
+nsresult PrintBackgroundTaskPromise(
+ T& aReceiver, JSContext* aCx, dom::Promise** aResultPromise,
+ const nsCString& aTelemetryKey,
+ PrintBackgroundTask<T, Result, Args...> aTask, Args... aArgs) {
+ ErrorResult rv;
+ RefPtr<dom::Promise> promise =
+ dom::Promise::Create(xpc::CurrentNativeGlobal(aCx), rv);
+ if (MOZ_UNLIKELY(rv.Failed())) {
+ return rv.StealNSResult();
+ }
+
+ SpawnPrintBackgroundTask(aReceiver, *promise, aTelemetryKey, aTask,
+ std::forward<Args>(aArgs)...);
+
+ promise.forget(aResultPromise);
+ return NS_OK;
+}
+
+// Resolves an async attribute via a background task, creating and storing a
+// promise as needed in aPromiseSlot.
+template <typename T, typename Result, typename... Args>
+nsresult AsyncPromiseAttributeGetter(
+ T& aReceiver, RefPtr<dom::Promise>& aPromiseSlot, JSContext* aCx,
+ dom::Promise** aResultPromise, const nsCString& aTelemetryKey,
+ PrintBackgroundTask<T, Result, Args...> aTask, Args... aArgs) {
+ if (RefPtr<dom::Promise> existing = aPromiseSlot) {
+ existing.forget(aResultPromise);
+ return NS_OK;
+ }
+
+ nsresult rv =
+ PrintBackgroundTaskPromise(aReceiver, aCx, aResultPromise, aTelemetryKey,
+ aTask, std::forward<Args>(aArgs)...);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aPromiseSlot = *aResultPromise;
+ return NS_OK;
+}
+
+} // namespace mozilla
+
+#endif