Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/hal/HalWakeLock.cpp
Line
Count
Source (jump to first uncovered line)
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/. */
6
7
#include "Hal.h"
8
#include "mozilla/HalWakeLock.h"
9
#include "mozilla/Services.h"
10
#include "mozilla/StaticPtr.h"
11
#include "mozilla/dom/ContentParent.h"
12
#include "nsAutoPtr.h"
13
#include "nsClassHashtable.h"
14
#include "nsDataHashtable.h"
15
#include "nsHashKeys.h"
16
#include "nsIPropertyBag2.h"
17
#include "nsIObserverService.h"
18
19
using namespace mozilla;
20
using namespace mozilla::hal;
21
22
namespace {
23
24
struct LockCount {
25
  LockCount()
26
    : numLocks(0)
27
    , numHidden(0)
28
0
  {}
29
  uint32_t numLocks;
30
  uint32_t numHidden;
31
  nsTArray<uint64_t> processes;
32
};
33
34
typedef nsDataHashtable<nsUint64HashKey, LockCount> ProcessLockTable;
35
typedef nsClassHashtable<nsStringHashKey, ProcessLockTable> LockTable;
36
37
int sActiveListeners = 0;
38
StaticAutoPtr<LockTable> sLockTable;
39
bool sIsShuttingDown = false;
40
41
WakeLockInformation
42
WakeLockInfoFromLockCount(const nsAString& aTopic, const LockCount& aLockCount)
43
0
{
44
0
  nsString topic(aTopic);
45
0
  WakeLockInformation info(topic, aLockCount.numLocks, aLockCount.numHidden,
46
0
                           aLockCount.processes);
47
0
48
0
  return info;
49
0
}
50
51
static void
52
CountWakeLocks(ProcessLockTable* aTable, LockCount* aTotalCount)
53
0
{
54
0
  for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) {
55
0
    const uint64_t& key = iter.Key();
56
0
    LockCount count = iter.UserData();
57
0
58
0
    aTotalCount->numLocks += count.numLocks;
59
0
    aTotalCount->numHidden += count.numHidden;
60
0
61
0
    // This is linear in the number of processes, but that should be small.
62
0
    if (!aTotalCount->processes.Contains(key)) {
63
0
      aTotalCount->processes.AppendElement(key);
64
0
    }
65
0
  }
66
0
}
67
68
class ClearHashtableOnShutdown final : public nsIObserver {
69
0
  ~ClearHashtableOnShutdown() {}
70
public:
71
  NS_DECL_ISUPPORTS
72
  NS_DECL_NSIOBSERVER
73
};
74
75
NS_IMPL_ISUPPORTS(ClearHashtableOnShutdown, nsIObserver)
76
77
NS_IMETHODIMP
78
ClearHashtableOnShutdown::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* data)
79
0
{
80
0
  MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
81
0
82
0
  sIsShuttingDown = true;
83
0
  sLockTable = nullptr;
84
0
85
0
  return NS_OK;
86
0
}
87
88
class CleanupOnContentShutdown final : public nsIObserver {
89
0
  ~CleanupOnContentShutdown() {}
90
public:
91
  NS_DECL_ISUPPORTS
92
  NS_DECL_NSIOBSERVER
93
};
94
95
NS_IMPL_ISUPPORTS(CleanupOnContentShutdown, nsIObserver)
96
97
NS_IMETHODIMP
98
CleanupOnContentShutdown::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* data)
99
0
{
100
0
  MOZ_ASSERT(!strcmp(aTopic, "ipc:content-shutdown"));
101
0
102
0
  if (sIsShuttingDown) {
103
0
    return NS_OK;
104
0
  }
105
0
106
0
  nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
107
0
  if (!props) {
108
0
    NS_WARNING("ipc:content-shutdown message without property bag as subject");
109
0
    return NS_OK;
110
0
  }
111
0
112
0
  uint64_t childID = 0;
113
0
  nsresult rv = props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"),
114
0
                                           &childID);
115
0
  if (NS_SUCCEEDED(rv)) {
116
0
    for (auto iter = sLockTable->Iter(); !iter.Done(); iter.Next()) {
117
0
      nsAutoPtr<ProcessLockTable>& table = iter.Data();
118
0
119
0
      if (table->Get(childID, nullptr)) {
120
0
        table->Remove(childID);
121
0
122
0
        LockCount totalCount;
123
0
        CountWakeLocks(table, &totalCount);
124
0
125
0
        if (sActiveListeners) {
126
0
          NotifyWakeLockChange(WakeLockInfoFromLockCount(iter.Key(),
127
0
                                                         totalCount));
128
0
        }
129
0
130
0
        if (totalCount.numLocks == 0) {
131
0
          iter.Remove();
132
0
        }
133
0
      }
134
0
    }
135
0
  } else {
136
0
    NS_WARNING("ipc:content-shutdown message without childID property");
137
0
  }
138
0
  return NS_OK;
139
0
}
140
141
} // namespace
142
143
namespace mozilla {
144
145
namespace hal {
146
147
void
148
WakeLockInit()
149
0
{
150
0
  sLockTable = new LockTable();
151
0
152
0
  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
153
0
  if (obs) {
154
0
    obs->AddObserver(new ClearHashtableOnShutdown(), "xpcom-shutdown", false);
155
0
    obs->AddObserver(new CleanupOnContentShutdown(), "ipc:content-shutdown", false);
156
0
  }
157
0
}
158
159
160
WakeLockState
161
ComputeWakeLockState(int aNumLocks, int aNumHidden)
162
0
{
163
0
  if (aNumLocks == 0) {
164
0
    return WAKE_LOCK_STATE_UNLOCKED;
165
0
  } else if (aNumLocks == aNumHidden) {
166
0
    return WAKE_LOCK_STATE_HIDDEN;
167
0
  } else {
168
0
    return WAKE_LOCK_STATE_VISIBLE;
169
0
  }
170
0
}
171
172
} // namespace hal
173
174
namespace hal_impl {
175
176
void
177
EnableWakeLockNotifications()
178
0
{
179
0
  sActiveListeners++;
180
0
}
181
182
void
183
DisableWakeLockNotifications()
184
0
{
185
0
  sActiveListeners--;
186
0
}
187
188
void
189
ModifyWakeLock(const nsAString& aTopic,
190
               hal::WakeLockControl aLockAdjust,
191
               hal::WakeLockControl aHiddenAdjust,
192
               uint64_t aProcessID)
193
0
{
194
0
  MOZ_ASSERT(NS_IsMainThread());
195
0
  MOZ_ASSERT(aProcessID != CONTENT_PROCESS_ID_UNKNOWN);
196
0
197
0
  if (sIsShuttingDown) {
198
0
    return;
199
0
  }
200
0
201
0
  ProcessLockTable* table = sLockTable->Get(aTopic);
202
0
  LockCount processCount;
203
0
  LockCount totalCount;
204
0
  if (!table) {
205
0
    table = new ProcessLockTable();
206
0
    sLockTable->Put(aTopic, table);
207
0
  } else {
208
0
    table->Get(aProcessID, &processCount);
209
0
    CountWakeLocks(table, &totalCount);
210
0
  }
211
0
212
0
  MOZ_ASSERT(processCount.numLocks >= processCount.numHidden);
213
0
  MOZ_ASSERT(aLockAdjust >= 0 || processCount.numLocks > 0);
214
0
  MOZ_ASSERT(aHiddenAdjust >= 0 || processCount.numHidden > 0);
215
0
  MOZ_ASSERT(totalCount.numLocks >= totalCount.numHidden);
216
0
  MOZ_ASSERT(aLockAdjust >= 0 || totalCount.numLocks > 0);
217
0
  MOZ_ASSERT(aHiddenAdjust >= 0 || totalCount.numHidden > 0);
218
0
219
0
  WakeLockState oldState = ComputeWakeLockState(totalCount.numLocks, totalCount.numHidden);
220
0
  bool processWasLocked = processCount.numLocks > 0;
221
0
222
0
  processCount.numLocks += aLockAdjust;
223
0
  processCount.numHidden += aHiddenAdjust;
224
0
225
0
  totalCount.numLocks += aLockAdjust;
226
0
  totalCount.numHidden += aHiddenAdjust;
227
0
228
0
  if (processCount.numLocks) {
229
0
    table->Put(aProcessID, processCount);
230
0
  } else {
231
0
    table->Remove(aProcessID);
232
0
  }
233
0
  if (!totalCount.numLocks) {
234
0
    sLockTable->Remove(aTopic);
235
0
  }
236
0
237
0
  if (sActiveListeners &&
238
0
      (oldState != ComputeWakeLockState(totalCount.numLocks,
239
0
                                        totalCount.numHidden) ||
240
0
       processWasLocked != (processCount.numLocks > 0))) {
241
0
242
0
    WakeLockInformation info;
243
0
    hal::GetWakeLockInfo(aTopic, &info);
244
0
    NotifyWakeLockChange(info);
245
0
  }
246
0
}
247
248
void
249
GetWakeLockInfo(const nsAString& aTopic, WakeLockInformation* aWakeLockInfo)
250
0
{
251
0
  if (sIsShuttingDown) {
252
0
    NS_WARNING("You don't want to get wake lock information during xpcom-shutdown!");
253
0
    *aWakeLockInfo = WakeLockInformation();
254
0
    return;
255
0
  }
256
0
257
0
  ProcessLockTable* table = sLockTable->Get(aTopic);
258
0
  if (!table) {
259
0
    *aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, LockCount());
260
0
    return;
261
0
  }
262
0
  LockCount totalCount;
263
0
  CountWakeLocks(table, &totalCount);
264
0
  *aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, totalCount);
265
0
}
266
267
} // namespace hal_impl
268
} // namespace mozilla