/* -*- 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 #include // 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 void ResolveOrReject(dom::Promise& aPromise, T&, Result& aResult) { aPromise.MaybeResolve(std::forward(aResult)); } template using PrintBackgroundTask = Result (T::*)(Args...) const; template void SpawnPrintBackgroundTask( T& aReceiver, dom::Promise& aPromise, const nsCString& aTelemetryKey, PrintBackgroundTask aBackgroundTask, Args... aArgs) { auto promiseHolder = MakeRefPtr>( "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>( "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(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 nsresult PrintBackgroundTaskPromise( T& aReceiver, JSContext* aCx, dom::Promise** aResultPromise, const nsCString& aTelemetryKey, PrintBackgroundTask aTask, Args... aArgs) { ErrorResult rv; RefPtr promise = dom::Promise::Create(xpc::CurrentNativeGlobal(aCx), rv); if (MOZ_UNLIKELY(rv.Failed())) { return rv.StealNSResult(); } SpawnPrintBackgroundTask(aReceiver, *promise, aTelemetryKey, aTask, std::forward(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 nsresult AsyncPromiseAttributeGetter( T& aReceiver, RefPtr& aPromiseSlot, JSContext* aCx, dom::Promise** aResultPromise, const nsCString& aTelemetryKey, PrintBackgroundTask aTask, Args... aArgs) { if (RefPtr existing = aPromiseSlot) { existing.forget(aResultPromise); return NS_OK; } nsresult rv = PrintBackgroundTaskPromise(aReceiver, aCx, aResultPromise, aTelemetryKey, aTask, std::forward(aArgs)...); NS_ENSURE_SUCCESS(rv, rv); aPromiseSlot = *aResultPromise; return NS_OK; } } // namespace mozilla #endif