1 /* -*- Mode: C++; tab-width: 2; 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 file,
4  * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "nsCodeCoverage.h"
7 #include "mozilla/CodeCoverageHandler.h"
8 #include "mozilla/Unused.h"
9 #include "mozilla/dom/ContentParent.h"
10 #include "mozilla/dom/Promise.h"
11 
12 using mozilla::dom::ContentParent;
13 using mozilla::dom::Promise;
14 
NS_IMPL_ISUPPORTS(nsCodeCoverage,nsICodeCoverage)15 NS_IMPL_ISUPPORTS(nsCodeCoverage, nsICodeCoverage)
16 
17 nsCodeCoverage::nsCodeCoverage() {}
18 
~nsCodeCoverage()19 nsCodeCoverage::~nsCodeCoverage() {}
20 
21 enum RequestType { Flush };
22 
23 class ProcessCount final {
24   NS_INLINE_DECL_REFCOUNTING(ProcessCount);
25 
26  public:
ProcessCount(uint32_t c)27   explicit ProcessCount(uint32_t c) : mCount(c) {}
operator uint32_t() const28   operator uint32_t() const { return mCount; }
operator --()29   ProcessCount& operator--() {
30     mCount--;
31     return *this;
32   }
33 
34  private:
~ProcessCount()35   ~ProcessCount() {}
36   uint32_t mCount;
37 };
38 
39 namespace {
40 
Request(JSContext * cx,Promise ** aPromise,RequestType requestType)41 nsresult Request(JSContext* cx, Promise** aPromise, RequestType requestType) {
42   MOZ_ASSERT(XRE_IsParentProcess());
43   MOZ_ASSERT(NS_IsMainThread());
44 
45   nsIGlobalObject* global = xpc::CurrentNativeGlobal(cx);
46   if (NS_WARN_IF(!global)) {
47     return NS_ERROR_FAILURE;
48   }
49 
50   mozilla::ErrorResult result;
51   RefPtr<Promise> promise = Promise::Create(global, result);
52   if (NS_WARN_IF(result.Failed())) {
53     return result.StealNSResult();
54   }
55 
56   uint32_t processCount = 0;
57   for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
58     mozilla::Unused << cp;
59     ++processCount;
60   }
61 
62   if (requestType == RequestType::Flush) {
63     mozilla::CodeCoverageHandler::FlushCounters();
64   }
65 
66   if (processCount == 0) {
67     promise->MaybeResolveWithUndefined();
68   } else {
69     RefPtr<ProcessCount> processCountHolder(new ProcessCount(processCount));
70 
71     auto resolve = [processCountHolder, promise](bool unused) {
72       if (--(*processCountHolder) == 0) {
73         promise->MaybeResolveWithUndefined();
74       }
75     };
76 
77     auto reject = [promise](mozilla::ipc::ResponseRejectReason&& aReason) {
78       promise->MaybeReject(NS_ERROR_FAILURE);
79     };
80 
81     for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
82       if (requestType == RequestType::Flush) {
83         cp->SendFlushCodeCoverageCounters(resolve, reject);
84       }
85     }
86   }
87 
88   promise.forget(aPromise);
89   return NS_OK;
90 }
91 
92 }  // anonymous namespace
93 
FlushCounters(JSContext * cx,Promise ** aPromise)94 NS_IMETHODIMP nsCodeCoverage::FlushCounters(JSContext* cx, Promise** aPromise) {
95   return Request(cx, aPromise, RequestType::Flush);
96 }
97