Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/power/WakeLock.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 "WakeLock.h"
8
#include "mozilla/dom/ContentParent.h"
9
#include "mozilla/dom/Event.h" // for Event
10
#include "mozilla/Hal.h"
11
#include "mozilla/HalWakeLock.h"
12
#include "nsError.h"
13
#include "nsIDocument.h"
14
#include "nsIDOMWindow.h"
15
#include "nsPIDOMWindow.h"
16
#include "nsIPropertyBag2.h"
17
18
using namespace mozilla::hal;
19
20
namespace mozilla {
21
namespace dom {
22
23
0
NS_INTERFACE_MAP_BEGIN(WakeLock)
24
0
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMEventListener)
25
0
  NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
26
0
  NS_INTERFACE_MAP_ENTRY(nsIObserver)
27
0
  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
28
0
NS_INTERFACE_MAP_END
29
30
NS_IMPL_ADDREF(WakeLock)
31
NS_IMPL_RELEASE(WakeLock)
32
33
WakeLock::WakeLock()
34
  : mLocked(false)
35
  , mHidden(true)
36
  , mContentParentID(CONTENT_PROCESS_ID_UNKNOWN)
37
0
{
38
0
}
39
40
WakeLock::~WakeLock()
41
0
{
42
0
  DoUnlock();
43
0
  DetachEventListener();
44
0
}
45
46
nsresult
47
WakeLock::Init(const nsAString &aTopic, nsPIDOMWindowInner* aWindow)
48
0
{
49
0
  // Don't Init() a WakeLock twice.
50
0
  MOZ_ASSERT(mTopic.IsEmpty());
51
0
52
0
  if (aTopic.IsEmpty()) {
53
0
    return NS_ERROR_INVALID_ARG;
54
0
  }
55
0
56
0
  mTopic.Assign(aTopic);
57
0
58
0
  mWindow = do_GetWeakReference(aWindow);
59
0
60
0
  /**
61
0
   * Null windows are allowed. A wake lock without associated window
62
0
   * is always considered invisible.
63
0
   */
64
0
  if (aWindow) {
65
0
    nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
66
0
    NS_ENSURE_STATE(doc);
67
0
    mHidden = doc->Hidden();
68
0
  }
69
0
70
0
  AttachEventListener();
71
0
  DoLock();
72
0
73
0
  return NS_OK;
74
0
}
75
76
nsresult
77
WakeLock::Init(const nsAString& aTopic, ContentParent* aContentParent)
78
0
{
79
0
  // Don't Init() a WakeLock twice.
80
0
  MOZ_ASSERT(mTopic.IsEmpty());
81
0
  MOZ_ASSERT(aContentParent);
82
0
83
0
  if (aTopic.IsEmpty()) {
84
0
    return NS_ERROR_INVALID_ARG;
85
0
  }
86
0
87
0
  mTopic.Assign(aTopic);
88
0
  mContentParentID = aContentParent->ChildID();
89
0
  mHidden = false;
90
0
91
0
  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
92
0
  if (obs) {
93
0
    obs->AddObserver(this, "ipc:content-shutdown", /* ownsWeak */ true);
94
0
  }
95
0
96
0
  DoLock();
97
0
  return NS_OK;
98
0
}
99
100
NS_IMETHODIMP
101
WakeLock::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* data)
102
0
{
103
0
  // If this wake lock was acquired on behalf of another process, unlock it
104
0
  // when that process dies.
105
0
  //
106
0
  // Note that we do /not/ call DoUnlock() here!  The wake lock back-end is
107
0
  // already listening for ipc:content-shutdown messages and will clear out its
108
0
  // tally for the process when it dies.  All we need to do here is ensure that
109
0
  // unlock() becomes a nop.
110
0
111
0
  MOZ_ASSERT(!strcmp(aTopic, "ipc:content-shutdown"));
112
0
113
0
  nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
114
0
  if (!props) {
115
0
    NS_WARNING("ipc:content-shutdown message without property bag as subject");
116
0
    return NS_OK;
117
0
  }
118
0
119
0
  uint64_t childID = 0;
120
0
  nsresult rv = props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"),
121
0
                                           &childID);
122
0
  if (NS_SUCCEEDED(rv)) {
123
0
    if (childID == mContentParentID) {
124
0
      mLocked = false;
125
0
    }
126
0
  } else {
127
0
    NS_WARNING("ipc:content-shutdown message without childID property");
128
0
  }
129
0
  return NS_OK;
130
0
}
131
132
void
133
WakeLock::DoLock()
134
0
{
135
0
  if (!mLocked) {
136
0
    // Change the flag immediately to prevent recursive reentering
137
0
    mLocked = true;
138
0
139
0
    hal::ModifyWakeLock(mTopic,
140
0
                        hal::WAKE_LOCK_ADD_ONE,
141
0
                        mHidden ? hal::WAKE_LOCK_ADD_ONE : hal::WAKE_LOCK_NO_CHANGE,
142
0
                        mContentParentID);
143
0
  }
144
0
}
145
146
void
147
WakeLock::DoUnlock()
148
0
{
149
0
  if (mLocked) {
150
0
    // Change the flag immediately to prevent recursive reentering
151
0
    mLocked = false;
152
0
153
0
    hal::ModifyWakeLock(mTopic,
154
0
                        hal::WAKE_LOCK_REMOVE_ONE,
155
0
                        mHidden ? hal::WAKE_LOCK_REMOVE_ONE : hal::WAKE_LOCK_NO_CHANGE,
156
0
                        mContentParentID);
157
0
  }
158
0
}
159
160
void
161
WakeLock::AttachEventListener()
162
0
{
163
0
  if (nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mWindow)) {
164
0
    nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
165
0
    if (doc) {
166
0
      doc->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
167
0
                                  this,
168
0
                                  /* useCapture = */ true,
169
0
                                  /* wantsUntrusted = */ false);
170
0
171
0
      nsCOMPtr<EventTarget> target = do_QueryInterface(window);
172
0
      target->AddSystemEventListener(NS_LITERAL_STRING("pagehide"),
173
0
                                     this,
174
0
                                     /* useCapture = */ true,
175
0
                                     /* wantsUntrusted = */ false);
176
0
      target->AddSystemEventListener(NS_LITERAL_STRING("pageshow"),
177
0
                                     this,
178
0
                                     /* useCapture = */ true,
179
0
                                     /* wantsUntrusted = */ false);
180
0
    }
181
0
  }
182
0
}
183
184
void
185
WakeLock::DetachEventListener()
186
0
{
187
0
  if (nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mWindow)) {
188
0
    nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
189
0
    if (doc) {
190
0
      doc->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
191
0
                                     this,
192
0
                                     /* useCapture = */ true);
193
0
      nsCOMPtr<EventTarget> target = do_QueryInterface(window);
194
0
      target->RemoveSystemEventListener(NS_LITERAL_STRING("pagehide"),
195
0
                                        this,
196
0
                                        /* useCapture = */ true);
197
0
      target->RemoveSystemEventListener(NS_LITERAL_STRING("pageshow"),
198
0
                                        this,
199
0
                                        /* useCapture = */ true);
200
0
    }
201
0
  }
202
0
}
203
204
void
205
WakeLock::Unlock(ErrorResult& aRv)
206
0
{
207
0
  /*
208
0
   * We throw NS_ERROR_DOM_INVALID_STATE_ERR on double unlock.
209
0
   */
210
0
  if (!mLocked) {
211
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
212
0
    return;
213
0
  }
214
0
215
0
  DoUnlock();
216
0
  DetachEventListener();
217
0
}
218
219
void
220
WakeLock::GetTopic(nsAString &aTopic)
221
0
{
222
0
  aTopic.Assign(mTopic);
223
0
}
224
225
NS_IMETHODIMP
226
WakeLock::HandleEvent(Event *aEvent)
227
0
{
228
0
  nsAutoString type;
229
0
  aEvent->GetType(type);
230
0
231
0
  if (type.EqualsLiteral("visibilitychange")) {
232
0
    nsCOMPtr<nsIDocument> doc = do_QueryInterface(aEvent->GetTarget());
233
0
    NS_ENSURE_STATE(doc);
234
0
235
0
    bool oldHidden = mHidden;
236
0
    mHidden = doc->Hidden();
237
0
238
0
    if (mLocked && oldHidden != mHidden) {
239
0
      hal::ModifyWakeLock(mTopic,
240
0
                          hal::WAKE_LOCK_NO_CHANGE,
241
0
                          mHidden ? hal::WAKE_LOCK_ADD_ONE : hal::WAKE_LOCK_REMOVE_ONE,
242
0
                          mContentParentID);
243
0
    }
244
0
245
0
    return NS_OK;
246
0
  }
247
0
248
0
  if (type.EqualsLiteral("pagehide")) {
249
0
    DoUnlock();
250
0
    return NS_OK;
251
0
  }
252
0
253
0
  if (type.EqualsLiteral("pageshow")) {
254
0
    DoLock();
255
0
    return NS_OK;
256
0
  }
257
0
258
0
  return NS_OK;
259
0
}
260
261
nsPIDOMWindowInner*
262
WakeLock::GetParentObject() const
263
0
{
264
0
  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mWindow);
265
0
  return window;
266
0
}
267
268
} // namespace dom
269
} // namespace mozilla