1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef mozilla_PrintBackgroundTask_h_
7 #define mozilla_PrintBackgroundTask_h_
9 #include "mozilla/dom/Promise.h"
10 #include "mozilla/ErrorResult.h"
11 #include "mozilla/Telemetry.h"
15 // A helper to resolve a DOM Promise with the result of a const method, executed
18 // Once in the main thread, the caller can turn the result of the method into a
19 // JSValue by specializing ResolveOrReject.
22 template <typename T
, typename Result
>
23 void ResolveOrReject(dom::Promise
& aPromise
, T
&, Result
& aResult
) {
24 aPromise
.MaybeResolve(std::forward
<Result
>(aResult
));
27 template <typename T
, typename Result
, typename
... Args
>
28 using PrintBackgroundTask
= Result (T::*)(Args
...) const;
30 template <typename T
, typename Result
, typename
... Args
>
31 void SpawnPrintBackgroundTask(
32 T
& aReceiver
, dom::Promise
& aPromise
, const nsCString
& aTelemetryKey
,
33 PrintBackgroundTask
<T
, Result
, Args
...> aBackgroundTask
, Args
... aArgs
) {
34 auto promiseHolder
= MakeRefPtr
<nsMainThreadPtrHolder
<dom::Promise
>>(
35 "nsPrinterBase::SpawnBackgroundTaskPromise", &aPromise
);
36 // We actually want to allow to access the printer data from the callback, so
37 // disable strict checking. They should of course only access immutable
39 auto holder
= MakeRefPtr
<nsMainThreadPtrHolder
<T
>>(
40 "nsPrinterBase::SpawnBackgroundTaskPrinter", &aReceiver
,
41 /* strict = */ false);
43 // https://stackoverflow.com/questions/47496358/c-lambdas-how-to-capture-variadic-parameter-pack-from-the-upper-scope
44 // about the tuple shenanigans. It could be improved with C++20
45 NS_DispatchBackgroundTask(
46 NS_NewRunnableFunction(
47 "SpawnPrintBackgroundTask",
48 [holder
= std::move(holder
), promiseHolder
= std::move(promiseHolder
),
49 aTelemetryKey
, startRoundTrip
= TimeStamp::Now(),
50 backgroundTask
= aBackgroundTask
,
51 aArgs
= std::make_tuple(std::forward
<Args
>(aArgs
)...)] {
52 auto start
= TimeStamp::Now();
53 Result result
= std::apply(
55 return (holder
->get()->*backgroundTask
)(args
...);
58 Telemetry::AccumulateTimeDelta(
59 Telemetry::PRINT_BACKGROUND_TASK_TIME_MS
, aTelemetryKey
, start
);
60 NS_DispatchToMainThread(NS_NewRunnableFunction(
61 "SpawnPrintBackgroundTaskResolution",
62 [holder
= std::move(holder
),
63 promiseHolder
= std::move(promiseHolder
),
64 telemetryKey
= std::move(aTelemetryKey
), startRoundTrip
,
65 result
= std::move(result
)] {
66 Telemetry::AccumulateTimeDelta(
67 Telemetry::PRINT_BACKGROUND_TASK_ROUND_TRIP_TIME_MS
,
68 telemetryKey
, startRoundTrip
);
69 ResolveOrReject(*promiseHolder
->get(), *holder
->get(),
73 NS_DISPATCH_EVENT_MAY_BLOCK
);
76 // Gets a fresh promise into aResultPromise, that resolves whenever the print
77 // background task finishes.
78 template <typename T
, typename Result
, typename
... Args
>
79 nsresult
PrintBackgroundTaskPromise(
80 T
& aReceiver
, JSContext
* aCx
, dom::Promise
** aResultPromise
,
81 const nsCString
& aTelemetryKey
,
82 PrintBackgroundTask
<T
, Result
, Args
...> aTask
, Args
... aArgs
) {
84 RefPtr
<dom::Promise
> promise
=
85 dom::Promise::Create(xpc::CurrentNativeGlobal(aCx
), rv
);
86 if (MOZ_UNLIKELY(rv
.Failed())) {
87 return rv
.StealNSResult();
90 SpawnPrintBackgroundTask(aReceiver
, *promise
, aTelemetryKey
, aTask
,
91 std::forward
<Args
>(aArgs
)...);
93 promise
.forget(aResultPromise
);
97 // Resolves an async attribute via a background task, creating and storing a
98 // promise as needed in aPromiseSlot.
99 template <typename T
, typename Result
, typename
... Args
>
100 nsresult
AsyncPromiseAttributeGetter(
101 T
& aReceiver
, RefPtr
<dom::Promise
>& aPromiseSlot
, JSContext
* aCx
,
102 dom::Promise
** aResultPromise
, const nsCString
& aTelemetryKey
,
103 PrintBackgroundTask
<T
, Result
, Args
...> aTask
, Args
... aArgs
) {
104 if (RefPtr
<dom::Promise
> existing
= aPromiseSlot
) {
105 existing
.forget(aResultPromise
);
110 PrintBackgroundTaskPromise(aReceiver
, aCx
, aResultPromise
, aTelemetryKey
,
111 aTask
, std::forward
<Args
>(aArgs
)...);
112 NS_ENSURE_SUCCESS(rv
, rv
);
114 aPromiseSlot
= *aResultPromise
;
118 } // namespace mozilla