Bug 1716846 [wpt PR 29402] - Update wpt metadata, a=testonly
[gecko.git] / hal / HalWakeLock.cpp
blob225b1ee03a908985e8ee833bd58a60615b28bc7f
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "Hal.h"
8 #include "base/process_util.h"
9 #include "mozilla/HalWakeLock.h"
10 #include "mozilla/Services.h"
11 #include "mozilla/StaticPtr.h"
12 #include "nsClassHashtable.h"
13 #include "nsTHashMap.h"
14 #include "nsHashKeys.h"
15 #include "nsIPropertyBag2.h"
16 #include "nsIObserver.h"
17 #include "nsIObserverService.h"
19 using namespace mozilla;
20 using namespace mozilla::hal;
22 namespace {
24 struct LockCount {
25 LockCount() : numLocks(0), numHidden(0) {}
26 uint32_t numLocks;
27 uint32_t numHidden;
28 CopyableTArray<uint64_t> processes;
31 typedef nsTHashMap<nsUint64HashKey, LockCount> ProcessLockTable;
32 typedef nsClassHashtable<nsStringHashKey, ProcessLockTable> LockTable;
34 int sActiveListeners = 0;
35 StaticAutoPtr<LockTable> sLockTable;
36 bool sIsShuttingDown = false;
38 WakeLockInformation WakeLockInfoFromLockCount(const nsAString& aTopic,
39 const LockCount& aLockCount) {
40 nsString topic(aTopic);
41 WakeLockInformation info(topic, aLockCount.numLocks, aLockCount.numHidden,
42 aLockCount.processes);
44 return info;
47 static void CountWakeLocks(ProcessLockTable* aTable, LockCount* aTotalCount) {
48 for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) {
49 const uint64_t& key = iter.Key();
50 LockCount count = iter.UserData();
52 aTotalCount->numLocks += count.numLocks;
53 aTotalCount->numHidden += count.numHidden;
55 // This is linear in the number of processes, but that should be small.
56 if (!aTotalCount->processes.Contains(key)) {
57 aTotalCount->processes.AppendElement(key);
62 class ClearHashtableOnShutdown final : public nsIObserver {
63 ~ClearHashtableOnShutdown() {}
65 public:
66 NS_DECL_ISUPPORTS
67 NS_DECL_NSIOBSERVER
70 NS_IMPL_ISUPPORTS(ClearHashtableOnShutdown, nsIObserver)
72 NS_IMETHODIMP
73 ClearHashtableOnShutdown::Observe(nsISupports* aSubject, const char* aTopic,
74 const char16_t* data) {
75 MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
77 sIsShuttingDown = true;
78 sLockTable = nullptr;
80 return NS_OK;
83 class CleanupOnContentShutdown final : public nsIObserver {
84 ~CleanupOnContentShutdown() {}
86 public:
87 NS_DECL_ISUPPORTS
88 NS_DECL_NSIOBSERVER
91 NS_IMPL_ISUPPORTS(CleanupOnContentShutdown, nsIObserver)
93 NS_IMETHODIMP
94 CleanupOnContentShutdown::Observe(nsISupports* aSubject, const char* aTopic,
95 const char16_t* data) {
96 MOZ_ASSERT(!strcmp(aTopic, "ipc:content-shutdown"));
98 if (sIsShuttingDown) {
99 return NS_OK;
102 nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
103 if (!props) {
104 NS_WARNING("ipc:content-shutdown message without property bag as subject");
105 return NS_OK;
108 uint64_t childID = 0;
109 nsresult rv = props->GetPropertyAsUint64(u"childID"_ns, &childID);
110 if (NS_SUCCEEDED(rv)) {
111 for (auto iter = sLockTable->Iter(); !iter.Done(); iter.Next()) {
112 auto table = iter.UserData();
114 if (table->Get(childID, nullptr)) {
115 table->Remove(childID);
117 LockCount totalCount;
118 CountWakeLocks(table, &totalCount);
120 if (sActiveListeners) {
121 NotifyWakeLockChange(
122 WakeLockInfoFromLockCount(iter.Key(), totalCount));
125 if (totalCount.numLocks == 0) {
126 iter.Remove();
130 } else {
131 NS_WARNING("ipc:content-shutdown message without childID property");
133 return NS_OK;
136 } // namespace
138 namespace mozilla {
140 namespace hal {
142 void WakeLockInit() {
143 sLockTable = new LockTable();
145 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
146 if (obs) {
147 obs->AddObserver(new ClearHashtableOnShutdown(), "xpcom-shutdown", false);
148 obs->AddObserver(new CleanupOnContentShutdown(), "ipc:content-shutdown",
149 false);
153 WakeLockState ComputeWakeLockState(int aNumLocks, int aNumHidden) {
154 if (aNumLocks == 0) {
155 return WAKE_LOCK_STATE_UNLOCKED;
156 } else if (aNumLocks == aNumHidden) {
157 return WAKE_LOCK_STATE_HIDDEN;
158 } else {
159 return WAKE_LOCK_STATE_VISIBLE;
163 } // namespace hal
165 namespace hal_impl {
167 void EnableWakeLockNotifications() { sActiveListeners++; }
169 void DisableWakeLockNotifications() { sActiveListeners--; }
171 void ModifyWakeLock(const nsAString& aTopic, hal::WakeLockControl aLockAdjust,
172 hal::WakeLockControl aHiddenAdjust, uint64_t aProcessID) {
173 MOZ_ASSERT(NS_IsMainThread());
174 MOZ_ASSERT(aProcessID != CONTENT_PROCESS_ID_UNKNOWN);
176 if (sIsShuttingDown) {
177 return;
180 LockCount processCount;
181 LockCount totalCount;
182 ProcessLockTable* const table =
183 sLockTable->WithEntryHandle(aTopic, [&](auto&& entry) {
184 if (!entry) {
185 entry.Insert(MakeUnique<ProcessLockTable>());
186 } else {
187 Unused << entry.Data()->Get(aProcessID, &processCount);
188 CountWakeLocks(entry->get(), &totalCount);
190 return entry->get();
193 MOZ_ASSERT(processCount.numLocks >= processCount.numHidden);
194 MOZ_ASSERT(aLockAdjust >= 0 || processCount.numLocks > 0);
195 MOZ_ASSERT(aHiddenAdjust >= 0 || processCount.numHidden > 0);
196 MOZ_ASSERT(totalCount.numLocks >= totalCount.numHidden);
197 MOZ_ASSERT(aLockAdjust >= 0 || totalCount.numLocks > 0);
198 MOZ_ASSERT(aHiddenAdjust >= 0 || totalCount.numHidden > 0);
200 WakeLockState oldState =
201 ComputeWakeLockState(totalCount.numLocks, totalCount.numHidden);
202 bool processWasLocked = processCount.numLocks > 0;
204 processCount.numLocks += aLockAdjust;
205 processCount.numHidden += aHiddenAdjust;
207 totalCount.numLocks += aLockAdjust;
208 totalCount.numHidden += aHiddenAdjust;
210 if (processCount.numLocks) {
211 table->InsertOrUpdate(aProcessID, processCount);
212 } else {
213 table->Remove(aProcessID);
215 if (!totalCount.numLocks) {
216 sLockTable->Remove(aTopic);
219 if (sActiveListeners &&
220 (oldState !=
221 ComputeWakeLockState(totalCount.numLocks, totalCount.numHidden) ||
222 processWasLocked != (processCount.numLocks > 0))) {
223 WakeLockInformation info;
224 hal::GetWakeLockInfo(aTopic, &info);
225 NotifyWakeLockChange(info);
229 void GetWakeLockInfo(const nsAString& aTopic,
230 WakeLockInformation* aWakeLockInfo) {
231 if (sIsShuttingDown) {
232 NS_WARNING(
233 "You don't want to get wake lock information during xpcom-shutdown!");
234 *aWakeLockInfo = WakeLockInformation();
235 return;
238 ProcessLockTable* table = sLockTable->Get(aTopic);
239 if (!table) {
240 *aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, LockCount());
241 return;
243 LockCount totalCount;
244 CountWakeLocks(table, &totalCount);
245 *aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, totalCount);
248 } // namespace hal_impl
249 } // namespace mozilla