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
;
19 namespace mozilla::dom
{
21 NS_INTERFACE_MAP_BEGIN(WakeLock
)
22 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIDOMEventListener
)
23 NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener
)
24 NS_INTERFACE_MAP_ENTRY(nsIObserver
)
25 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
26 NS_INTERFACE_MAP_ENTRY(nsIWakeLock
)
29 NS_IMPL_ADDREF(WakeLock
)
30 NS_IMPL_RELEASE(WakeLock
)
35 mContentParentID(CONTENT_PROCESS_ID_UNKNOWN
) {}
37 WakeLock::~WakeLock() {
39 DetachEventListener();
42 nsresult
WakeLock::Init(const nsAString
& aTopic
, nsPIDOMWindowInner
* aWindow
) {
43 // Don't Init() a WakeLock twice.
44 MOZ_ASSERT(mTopic
.IsEmpty());
46 if (aTopic
.IsEmpty()) {
47 return NS_ERROR_INVALID_ARG
;
50 mTopic
.Assign(aTopic
);
52 mWindow
= do_GetWeakReference(aWindow
);
55 * Null windows are allowed. A wake lock without associated window
56 * is always considered invisible.
59 nsCOMPtr
<Document
> doc
= aWindow
->GetExtantDoc();
61 mHidden
= IsDocumentInvisible(*doc
);
64 AttachEventListener();
71 WakeLock::Observe(nsISupports
* aSubject
, const char* aTopic
,
72 const char16_t
* data
) {
73 // If this wake lock was acquired on behalf of another process, unlock it
74 // when that process dies.
76 // Note that we do /not/ call DoUnlock() here! The wake lock back-end is
77 // already listening for ipc:content-shutdown messages and will clear out its
78 // tally for the process when it dies. All we need to do here is ensure that
79 // unlock() becomes a nop.
81 MOZ_ASSERT(!strcmp(aTopic
, "ipc:content-shutdown"));
83 nsCOMPtr
<nsIPropertyBag2
> props
= do_QueryInterface(aSubject
);
85 NS_WARNING("ipc:content-shutdown message without property bag as subject");
90 nsresult rv
= props
->GetPropertyAsUint64(u
"childID"_ns
, &childID
);
91 if (NS_SUCCEEDED(rv
)) {
92 if (childID
== mContentParentID
) {
96 NS_WARNING("ipc:content-shutdown message without childID property");
101 void WakeLock::DoLock() {
103 // Change the flag immediately to prevent recursive reentering
107 mTopic
, hal::WAKE_LOCK_ADD_ONE
,
108 mHidden
? hal::WAKE_LOCK_ADD_ONE
: hal::WAKE_LOCK_NO_CHANGE
,
113 void WakeLock::DoUnlock() {
115 // Change the flag immediately to prevent recursive reentering
119 mTopic
, hal::WAKE_LOCK_REMOVE_ONE
,
120 mHidden
? hal::WAKE_LOCK_REMOVE_ONE
: hal::WAKE_LOCK_NO_CHANGE
,
125 void WakeLock::AttachEventListener() {
126 if (nsCOMPtr
<nsPIDOMWindowInner
> window
= do_QueryReferent(mWindow
)) {
127 nsCOMPtr
<Document
> doc
= window
->GetExtantDoc();
129 doc
->AddSystemEventListener(u
"visibilitychange"_ns
, this,
130 /* useCapture = */ true,
131 /* wantsUntrusted = */ false);
133 nsCOMPtr
<EventTarget
> target
= do_QueryInterface(window
);
134 target
->AddSystemEventListener(u
"pagehide"_ns
, this,
135 /* useCapture = */ true,
136 /* wantsUntrusted = */ false);
137 target
->AddSystemEventListener(u
"pageshow"_ns
, this,
138 /* useCapture = */ true,
139 /* wantsUntrusted = */ false);
144 void WakeLock::DetachEventListener() {
145 if (nsCOMPtr
<nsPIDOMWindowInner
> window
= do_QueryReferent(mWindow
)) {
146 nsCOMPtr
<Document
> doc
= window
->GetExtantDoc();
148 doc
->RemoveSystemEventListener(u
"visibilitychange"_ns
, this,
149 /* useCapture = */ true);
150 nsCOMPtr
<EventTarget
> target
= do_QueryInterface(window
);
151 target
->RemoveSystemEventListener(u
"pagehide"_ns
, this,
152 /* useCapture = */ true);
153 target
->RemoveSystemEventListener(u
"pageshow"_ns
, this,
154 /* useCapture = */ true);
159 void WakeLock::Unlock(ErrorResult
& aRv
) {
161 * We throw NS_ERROR_DOM_INVALID_STATE_ERR on double unlock.
164 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
169 DetachEventListener();
172 void WakeLock::GetTopic(nsAString
& aTopic
) { aTopic
.Assign(mTopic
); }
174 bool WakeLock::IsDocumentInvisible(const Document
& aDocument
) const {
175 // If document has a child element being used in the picture in picture
176 // mode, which is always visible to users, then we would consider the
177 // document as visible as well.
178 return aDocument
.Hidden() && !aDocument
.HasPictureInPictureChildElement();
182 WakeLock::HandleEvent(Event
* aEvent
) {
184 aEvent
->GetType(type
);
186 if (type
.EqualsLiteral("visibilitychange")) {
187 nsCOMPtr
<Document
> doc
= do_QueryInterface(aEvent
->GetTarget());
188 NS_ENSURE_STATE(doc
);
190 bool oldHidden
= mHidden
;
191 mHidden
= IsDocumentInvisible(*doc
);
193 if (mLocked
&& oldHidden
!= mHidden
) {
195 mTopic
, hal::WAKE_LOCK_NO_CHANGE
,
196 mHidden
? hal::WAKE_LOCK_ADD_ONE
: hal::WAKE_LOCK_REMOVE_ONE
,
203 if (type
.EqualsLiteral("pagehide")) {
208 if (type
.EqualsLiteral("pageshow")) {
220 return error
.StealNSResult();
223 nsPIDOMWindowInner
* WakeLock::GetParentObject() const {
224 nsCOMPtr
<nsPIDOMWindowInner
> window
= do_QueryInterface(mWindow
);
228 } // namespace mozilla::dom