summaryrefslogtreecommitdiffstats
path: root/widget/PrintBackgroundTask.h
blob: d8b4d8a4e58f345eee486df9510295a00148a28d (plain)
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
/* -*- 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 <utility>
#include <tuple>

// 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