Bug 1879816: Make nsCocoaWindow::Destroy close the window and let destruction happen...
[gecko.git] / hal / HalWakeLock.cpp
blob80ef930567aefe5c43a1b30ced5e40c1e11b0600
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/FOGIPC.h"
10 #include "mozilla/HalWakeLock.h"
11 #include "mozilla/Services.h"
12 #include "mozilla/StaticPtr.h"
13 #include "nsClassHashtable.h"
14 #include "nsTHashMap.h"
15 #include "nsHashKeys.h"
16 #include "nsIPropertyBag2.h"
17 #include "nsIObserver.h"
18 #include "nsIObserverService.h"
20 using namespace mozilla;
21 using namespace mozilla::hal;
23 namespace {
25 struct LockCount {
26 LockCount() : numLocks(0), numHidden(0) {}
27 uint32_t numLocks;
28 uint32_t numHidden;
29 CopyableTArray<uint64_t> processes;
32 typedef nsTHashMap<nsUint64HashKey, LockCount> ProcessLockTable;
33 typedef nsClassHashtable<nsStringHashKey, ProcessLockTable> LockTable;
35 int sActiveListeners = 0;
36 StaticAutoPtr<LockTable> sLockTable;
37 bool sIsShuttingDown = false;
39 WakeLockInformation WakeLockInfoFromLockCount(const nsAString& aTopic,
40 const LockCount& aLockCount) {
41 nsString topic(aTopic);
42 WakeLockInformation info(topic, aLockCount.numLocks, aLockCount.numHidden,
43 aLockCount.processes);
45 return info;
48 static void CountWakeLocks(ProcessLockTable* aTable, LockCount* aTotalCount) {
49 for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) {
50 const uint64_t& key = iter.Key();
51 LockCount count = iter.UserData();
53 aTotalCount->numLocks += count.numLocks;
54 aTotalCount->numHidden += count.numHidden;
56 // This is linear in the number of processes, but that should be small.
57 if (!aTotalCount->processes.Contains(key)) {
58 aTotalCount->processes.AppendElement(key);
63 class ClearHashtableOnShutdown final : public nsIObserver {
64 ~ClearHashtableOnShutdown() {}
66 public:
67 NS_DECL_ISUPPORTS
68 NS_DECL_NSIOBSERVER
71 NS_IMPL_ISUPPORTS(ClearHashtableOnShutdown, nsIObserver)
73 NS_IMETHODIMP
74 ClearHashtableOnShutdown::Observe(nsISupports* aSubject, const char* aTopic,
75 const char16_t* data) {
76 MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
78 sIsShuttingDown = true;
79 sLockTable = nullptr;
81 return NS_OK;
84 class CleanupOnContentShutdown final : public nsIObserver {
85 ~CleanupOnContentShutdown() {}
87 public:
88 NS_DECL_ISUPPORTS
89 NS_DECL_NSIOBSERVER
92 NS_IMPL_ISUPPORTS(CleanupOnContentShutdown, nsIObserver)
94 NS_IMETHODIMP
95 CleanupOnContentShutdown::Observe(nsISupports* aSubject, const char* aTopic,
96 const char16_t* data) {
97 MOZ_ASSERT(!strcmp(aTopic, "ipc:content-shutdown"));
99 if (sIsShuttingDown) {
100 return NS_OK;
103 nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
104 if (!props) {
105 NS_WARNING("ipc:content-shutdown message without property bag as subject");
106 return NS_OK;
109 uint64_t childID = 0;
110 nsresult rv = props->GetPropertyAsUint64(u"childID"_ns, &childID);
111 if (NS_SUCCEEDED(rv)) {
112 for (auto iter = sLockTable->Iter(); !iter.Done(); iter.Next()) {
113 auto table = iter.UserData();
115 if (table->Get(childID, nullptr)) {
116 table->Remove(childID);
118 LockCount totalCount;
119 CountWakeLocks(table, &totalCount);
121 if (sActiveListeners) {
122 NotifyWakeLockChange(
123 WakeLockInfoFromLockCount(iter.Key(), totalCount));
126 if (totalCount.numLocks == 0) {
127 iter.Remove();
131 } else {
132 NS_WARNING("ipc:content-shutdown message without childID property");
134 return NS_OK;
137 } // namespace
139 namespace mozilla {
141 namespace hal {
143 void WakeLockInit() {
144 sLockTable = new LockTable();
146 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
147 if (obs) {
148 obs->AddObserver(new ClearHashtableOnShutdown(), "xpcom-shutdown", false);
149 obs->AddObserver(new CleanupOnContentShutdown(), "ipc:content-shutdown",
150 false);
154 WakeLockState ComputeWakeLockState(int aNumLocks, int aNumHidden) {
155 if (aNumLocks == 0) {
156 return WAKE_LOCK_STATE_UNLOCKED;
157 } else if (aNumLocks == aNumHidden) {
158 return WAKE_LOCK_STATE_HIDDEN;
159 } else {
160 return WAKE_LOCK_STATE_VISIBLE;
164 } // namespace hal
166 namespace hal_impl {
168 void EnableWakeLockNotifications() { sActiveListeners++; }
170 void DisableWakeLockNotifications() { sActiveListeners--; }
172 void ModifyWakeLockWithChildID(const nsAString& aTopic,
173 hal::WakeLockControl aLockAdjust,
174 hal::WakeLockControl aHiddenAdjust,
175 uint64_t aChildID) {
176 MOZ_ASSERT(NS_IsMainThread());
177 MOZ_ASSERT(aChildID != CONTENT_PROCESS_ID_UNKNOWN);
179 if (sIsShuttingDown) {
180 return;
183 LockCount processCount;
184 LockCount totalCount;
185 ProcessLockTable* const table =
186 sLockTable->WithEntryHandle(aTopic, [&](auto&& entry) {
187 if (!entry) {
188 entry.Insert(MakeUnique<ProcessLockTable>());
189 } else {
190 Unused << entry.Data()->Get(aChildID, &processCount);
191 CountWakeLocks(entry->get(), &totalCount);
193 return entry->get();
196 MOZ_ASSERT(processCount.numLocks >= processCount.numHidden);
197 MOZ_ASSERT(aLockAdjust >= 0 || processCount.numLocks > 0);
198 MOZ_ASSERT(aHiddenAdjust >= 0 || processCount.numHidden > 0);
199 MOZ_ASSERT(totalCount.numLocks >= totalCount.numHidden);
200 MOZ_ASSERT(aLockAdjust >= 0 || totalCount.numLocks > 0);
201 MOZ_ASSERT(aHiddenAdjust >= 0 || totalCount.numHidden > 0);
203 WakeLockState oldState =
204 ComputeWakeLockState(totalCount.numLocks, totalCount.numHidden);
206 if (ComputeWakeLockState(totalCount.numLocks + aLockAdjust,
207 totalCount.numHidden + aHiddenAdjust) != oldState &&
208 (aTopic.Equals(u"video-playing"_ns) ||
209 aTopic.Equals(u"audio-playing"_ns))) {
210 glean::RecordPowerMetrics();
213 bool processWasLocked = processCount.numLocks > 0;
215 processCount.numLocks += aLockAdjust;
216 processCount.numHidden += aHiddenAdjust;
218 totalCount.numLocks += aLockAdjust;
219 totalCount.numHidden += aHiddenAdjust;
221 if (processCount.numLocks) {
222 table->InsertOrUpdate(aChildID, processCount);
223 } else {
224 table->Remove(aChildID);
226 if (!totalCount.numLocks) {
227 sLockTable->Remove(aTopic);
230 if (sActiveListeners &&
231 (oldState !=
232 ComputeWakeLockState(totalCount.numLocks, totalCount.numHidden) ||
233 processWasLocked != (processCount.numLocks > 0))) {
234 WakeLockInformation info;
235 hal::GetWakeLockInfo(aTopic, &info);
236 NotifyWakeLockChange(info);
240 void ModifyWakeLock(const nsAString& aTopic, hal::WakeLockControl aLockAdjust,
241 hal::WakeLockControl aHiddenAdjust) {
242 ModifyWakeLockWithChildID(aTopic, aLockAdjust, aHiddenAdjust,
243 CONTENT_PROCESS_ID_MAIN);
246 void GetWakeLockInfo(const nsAString& aTopic,
247 WakeLockInformation* aWakeLockInfo) {
248 if (sIsShuttingDown) {
249 NS_WARNING(
250 "You don't want to get wake lock information during xpcom-shutdown!");
251 *aWakeLockInfo = WakeLockInformation();
252 return;
255 if (!sLockTable) {
256 // This can happen during some gtests.
257 NS_WARNING("Attempting to get wake lock information before initialization");
258 *aWakeLockInfo = WakeLockInformation();
259 return;
262 ProcessLockTable* table = sLockTable->Get(aTopic);
263 if (!table) {
264 *aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, LockCount());
265 return;
267 LockCount totalCount;
268 CountWakeLocks(table, &totalCount);
269 *aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, totalCount);
272 } // namespace hal_impl
273 } // namespace mozilla