Bug 1642744 [wpt PR 23920] - [ScrollTimeline] Update compositor timeline from blink...
[gecko.git] / hal / HalWakeLock.cpp
blob96c1cf3640580c64ce71541bb53429dc14bb8ac1
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 "mozilla/HalWakeLock.h"
9 #include "mozilla/Services.h"
10 #include "mozilla/StaticPtr.h"
11 #include "nsClassHashtable.h"
12 #include "nsDataHashtable.h"
13 #include "nsHashKeys.h"
14 #include "nsIPropertyBag2.h"
15 #include "nsIObserverService.h"
17 using namespace mozilla;
18 using namespace mozilla::hal;
20 namespace {
22 struct LockCount {
23 LockCount() : numLocks(0), numHidden(0) {}
24 uint32_t numLocks;
25 uint32_t numHidden;
26 CopyableTArray<uint64_t> processes;
29 typedef nsDataHashtable<nsUint64HashKey, LockCount> ProcessLockTable;
30 typedef nsClassHashtable<nsStringHashKey, ProcessLockTable> LockTable;
32 int sActiveListeners = 0;
33 StaticAutoPtr<LockTable> sLockTable;
34 bool sIsShuttingDown = false;
36 WakeLockInformation WakeLockInfoFromLockCount(const nsAString& aTopic,
37 const LockCount& aLockCount) {
38 nsString topic(aTopic);
39 WakeLockInformation info(topic, aLockCount.numLocks, aLockCount.numHidden,
40 aLockCount.processes);
42 return info;
45 static void CountWakeLocks(ProcessLockTable* aTable, LockCount* aTotalCount) {
46 for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) {
47 const uint64_t& key = iter.Key();
48 LockCount count = iter.UserData();
50 aTotalCount->numLocks += count.numLocks;
51 aTotalCount->numHidden += count.numHidden;
53 // This is linear in the number of processes, but that should be small.
54 if (!aTotalCount->processes.Contains(key)) {
55 aTotalCount->processes.AppendElement(key);
60 class ClearHashtableOnShutdown final : public nsIObserver {
61 ~ClearHashtableOnShutdown() {}
63 public:
64 NS_DECL_ISUPPORTS
65 NS_DECL_NSIOBSERVER
68 NS_IMPL_ISUPPORTS(ClearHashtableOnShutdown, nsIObserver)
70 NS_IMETHODIMP
71 ClearHashtableOnShutdown::Observe(nsISupports* aSubject, const char* aTopic,
72 const char16_t* data) {
73 MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
75 sIsShuttingDown = true;
76 sLockTable = nullptr;
78 return NS_OK;
81 class CleanupOnContentShutdown final : public nsIObserver {
82 ~CleanupOnContentShutdown() {}
84 public:
85 NS_DECL_ISUPPORTS
86 NS_DECL_NSIOBSERVER
89 NS_IMPL_ISUPPORTS(CleanupOnContentShutdown, nsIObserver)
91 NS_IMETHODIMP
92 CleanupOnContentShutdown::Observe(nsISupports* aSubject, const char* aTopic,
93 const char16_t* data) {
94 MOZ_ASSERT(!strcmp(aTopic, "ipc:content-shutdown"));
96 if (sIsShuttingDown) {
97 return NS_OK;
100 nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
101 if (!props) {
102 NS_WARNING("ipc:content-shutdown message without property bag as subject");
103 return NS_OK;
106 uint64_t childID = 0;
107 nsresult rv =
108 props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID);
109 if (NS_SUCCEEDED(rv)) {
110 for (auto iter = sLockTable->Iter(); !iter.Done(); iter.Next()) {
111 auto table = iter.UserData();
113 if (table->Get(childID, nullptr)) {
114 table->Remove(childID);
116 LockCount totalCount;
117 CountWakeLocks(table, &totalCount);
119 if (sActiveListeners) {
120 NotifyWakeLockChange(
121 WakeLockInfoFromLockCount(iter.Key(), totalCount));
124 if (totalCount.numLocks == 0) {
125 iter.Remove();
129 } else {
130 NS_WARNING("ipc:content-shutdown message without childID property");
132 return NS_OK;
135 } // namespace
137 namespace mozilla {
139 namespace hal {
141 void WakeLockInit() {
142 sLockTable = new LockTable();
144 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
145 if (obs) {
146 obs->AddObserver(new ClearHashtableOnShutdown(), "xpcom-shutdown", false);
147 obs->AddObserver(new CleanupOnContentShutdown(), "ipc:content-shutdown",
148 false);
152 WakeLockState ComputeWakeLockState(int aNumLocks, int aNumHidden) {
153 if (aNumLocks == 0) {
154 return WAKE_LOCK_STATE_UNLOCKED;
155 } else if (aNumLocks == aNumHidden) {
156 return WAKE_LOCK_STATE_HIDDEN;
157 } else {
158 return WAKE_LOCK_STATE_VISIBLE;
162 } // namespace hal
164 namespace hal_impl {
166 void EnableWakeLockNotifications() { sActiveListeners++; }
168 void DisableWakeLockNotifications() { sActiveListeners--; }
170 void ModifyWakeLock(const nsAString& aTopic, hal::WakeLockControl aLockAdjust,
171 hal::WakeLockControl aHiddenAdjust, uint64_t aProcessID) {
172 MOZ_ASSERT(NS_IsMainThread());
173 MOZ_ASSERT(aProcessID != CONTENT_PROCESS_ID_UNKNOWN);
175 if (sIsShuttingDown) {
176 return;
179 ProcessLockTable* table = sLockTable->Get(aTopic);
180 LockCount processCount;
181 LockCount totalCount;
182 if (!table) {
183 table = new ProcessLockTable();
184 sLockTable->Put(aTopic, table);
185 } else {
186 table->Get(aProcessID, &processCount);
187 CountWakeLocks(table, &totalCount);
190 MOZ_ASSERT(processCount.numLocks >= processCount.numHidden);
191 MOZ_ASSERT(aLockAdjust >= 0 || processCount.numLocks > 0);
192 MOZ_ASSERT(aHiddenAdjust >= 0 || processCount.numHidden > 0);
193 MOZ_ASSERT(totalCount.numLocks >= totalCount.numHidden);
194 MOZ_ASSERT(aLockAdjust >= 0 || totalCount.numLocks > 0);
195 MOZ_ASSERT(aHiddenAdjust >= 0 || totalCount.numHidden > 0);
197 WakeLockState oldState =
198 ComputeWakeLockState(totalCount.numLocks, totalCount.numHidden);
199 bool processWasLocked = processCount.numLocks > 0;
201 processCount.numLocks += aLockAdjust;
202 processCount.numHidden += aHiddenAdjust;
204 totalCount.numLocks += aLockAdjust;
205 totalCount.numHidden += aHiddenAdjust;
207 if (processCount.numLocks) {
208 table->Put(aProcessID, processCount);
209 } else {
210 table->Remove(aProcessID);
212 if (!totalCount.numLocks) {
213 sLockTable->Remove(aTopic);
216 if (sActiveListeners &&
217 (oldState !=
218 ComputeWakeLockState(totalCount.numLocks, totalCount.numHidden) ||
219 processWasLocked != (processCount.numLocks > 0))) {
220 WakeLockInformation info;
221 hal::GetWakeLockInfo(aTopic, &info);
222 NotifyWakeLockChange(info);
226 void GetWakeLockInfo(const nsAString& aTopic,
227 WakeLockInformation* aWakeLockInfo) {
228 if (sIsShuttingDown) {
229 NS_WARNING(
230 "You don't want to get wake lock information during xpcom-shutdown!");
231 *aWakeLockInfo = WakeLockInformation();
232 return;
235 ProcessLockTable* table = sLockTable->Get(aTopic);
236 if (!table) {
237 *aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, LockCount());
238 return;
240 LockCount totalCount;
241 CountWakeLocks(table, &totalCount);
242 *aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, totalCount);
245 } // namespace hal_impl
246 } // namespace mozilla