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/. */
8 #include "mozilla/dom/ContentParent.h"
9 #include "mozilla/dom/Event.h" // for Event
10 #include "mozilla/Hal.h"
11 #include "mozilla/HalWakeLock.h"
13 #include "mozilla/dom/Document.h"
14 #include "nsPIDOMWindow.h"
15 #include "nsIPropertyBag2.h"
17 using namespace mozilla::hal
;
22 NS_INTERFACE_MAP_BEGIN(WakeLock
)
23 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIDOMEventListener
)
24 NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener
)
25 NS_INTERFACE_MAP_ENTRY(nsIObserver
)
26 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
27 NS_INTERFACE_MAP_ENTRY(nsIWakeLock
)
30 NS_IMPL_ADDREF(WakeLock
)
31 NS_IMPL_RELEASE(WakeLock
)
36 mContentParentID(CONTENT_PROCESS_ID_UNKNOWN
) {}
38 WakeLock::~WakeLock() {
40 DetachEventListener();
43 nsresult
WakeLock::Init(const nsAString
& aTopic
, nsPIDOMWindowInner
* aWindow
) {
44 // Don't Init() a WakeLock twice.
45 MOZ_ASSERT(mTopic
.IsEmpty());
47 if (aTopic
.IsEmpty()) {
48 return NS_ERROR_INVALID_ARG
;
51 mTopic
.Assign(aTopic
);
53 mWindow
= do_GetWeakReference(aWindow
);
56 * Null windows are allowed. A wake lock without associated window
57 * is always considered invisible.
60 nsCOMPtr
<Document
> doc
= aWindow
->GetExtantDoc();
62 mHidden
= doc
->Hidden();
65 AttachEventListener();
71 nsresult
WakeLock::Init(const nsAString
& aTopic
,
72 ContentParent
* aContentParent
) {
73 // Don't Init() a WakeLock twice.
74 MOZ_ASSERT(mTopic
.IsEmpty());
75 MOZ_ASSERT(aContentParent
);
77 if (aTopic
.IsEmpty()) {
78 return NS_ERROR_INVALID_ARG
;
81 mTopic
.Assign(aTopic
);
82 mContentParentID
= aContentParent
->ChildID();
85 nsCOMPtr
<nsIObserverService
> obs
= services::GetObserverService();
87 obs
->AddObserver(this, "ipc:content-shutdown", /* ownsWeak */ true);
95 WakeLock::Observe(nsISupports
* aSubject
, const char* aTopic
,
96 const char16_t
* data
) {
97 // If this wake lock was acquired on behalf of another process, unlock it
98 // when that process dies.
100 // Note that we do /not/ call DoUnlock() here! The wake lock back-end is
101 // already listening for ipc:content-shutdown messages and will clear out its
102 // tally for the process when it dies. All we need to do here is ensure that
103 // unlock() becomes a nop.
105 MOZ_ASSERT(!strcmp(aTopic
, "ipc:content-shutdown"));
107 nsCOMPtr
<nsIPropertyBag2
> props
= do_QueryInterface(aSubject
);
109 NS_WARNING("ipc:content-shutdown message without property bag as subject");
113 uint64_t childID
= 0;
115 props
->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID
);
116 if (NS_SUCCEEDED(rv
)) {
117 if (childID
== mContentParentID
) {
121 NS_WARNING("ipc:content-shutdown message without childID property");
126 void WakeLock::DoLock() {
128 // Change the flag immediately to prevent recursive reentering
132 mTopic
, hal::WAKE_LOCK_ADD_ONE
,
133 mHidden
? hal::WAKE_LOCK_ADD_ONE
: hal::WAKE_LOCK_NO_CHANGE
,
138 void WakeLock::DoUnlock() {
140 // Change the flag immediately to prevent recursive reentering
144 mTopic
, hal::WAKE_LOCK_REMOVE_ONE
,
145 mHidden
? hal::WAKE_LOCK_REMOVE_ONE
: hal::WAKE_LOCK_NO_CHANGE
,
150 void WakeLock::AttachEventListener() {
151 if (nsCOMPtr
<nsPIDOMWindowInner
> window
= do_QueryReferent(mWindow
)) {
152 nsCOMPtr
<Document
> doc
= window
->GetExtantDoc();
154 doc
->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"), this,
155 /* useCapture = */ true,
156 /* wantsUntrusted = */ false);
158 nsCOMPtr
<EventTarget
> target
= do_QueryInterface(window
);
159 target
->AddSystemEventListener(NS_LITERAL_STRING("pagehide"), this,
160 /* useCapture = */ true,
161 /* wantsUntrusted = */ false);
162 target
->AddSystemEventListener(NS_LITERAL_STRING("pageshow"), this,
163 /* useCapture = */ true,
164 /* wantsUntrusted = */ false);
169 void WakeLock::DetachEventListener() {
170 if (nsCOMPtr
<nsPIDOMWindowInner
> window
= do_QueryReferent(mWindow
)) {
171 nsCOMPtr
<Document
> doc
= window
->GetExtantDoc();
173 doc
->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
175 /* useCapture = */ true);
176 nsCOMPtr
<EventTarget
> target
= do_QueryInterface(window
);
177 target
->RemoveSystemEventListener(NS_LITERAL_STRING("pagehide"), this,
178 /* useCapture = */ true);
179 target
->RemoveSystemEventListener(NS_LITERAL_STRING("pageshow"), this,
180 /* useCapture = */ true);
185 void WakeLock::Unlock(ErrorResult
& aRv
) {
187 * We throw NS_ERROR_DOM_INVALID_STATE_ERR on double unlock.
190 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
195 DetachEventListener();
198 void WakeLock::GetTopic(nsAString
& aTopic
) { aTopic
.Assign(mTopic
); }
201 WakeLock::HandleEvent(Event
* aEvent
) {
203 aEvent
->GetType(type
);
205 if (type
.EqualsLiteral("visibilitychange")) {
206 nsCOMPtr
<Document
> doc
= do_QueryInterface(aEvent
->GetTarget());
207 NS_ENSURE_STATE(doc
);
209 bool oldHidden
= mHidden
;
210 mHidden
= doc
->Hidden();
212 if (mLocked
&& oldHidden
!= mHidden
) {
214 mTopic
, hal::WAKE_LOCK_NO_CHANGE
,
215 mHidden
? hal::WAKE_LOCK_ADD_ONE
: hal::WAKE_LOCK_REMOVE_ONE
,
222 if (type
.EqualsLiteral("pagehide")) {
227 if (type
.EqualsLiteral("pageshow")) {
239 return error
.StealNSResult();
242 nsPIDOMWindowInner
* WakeLock::GetParentObject() const {
243 nsCOMPtr
<nsPIDOMWindowInner
> window
= do_QueryInterface(mWindow
);
248 } // namespace mozilla