Bug 1806664 - Use python toolchain tasks to build firefox r=glandium,taskgraph-review...
[gecko.git] / dom / power / WakeLock.cpp
blobbdfcfeaccd26130fc4a08a772f4f31110d505ed8
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 "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 "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)
27 NS_INTERFACE_MAP_END
29 NS_IMPL_ADDREF(WakeLock)
30 NS_IMPL_RELEASE(WakeLock)
32 WakeLock::WakeLock()
33 : mLocked(false),
34 mHidden(true),
35 mContentParentID(CONTENT_PROCESS_ID_UNKNOWN) {}
37 WakeLock::~WakeLock() {
38 DoUnlock();
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);
54 /**
55 * Null windows are allowed. A wake lock without associated window
56 * is always considered invisible.
58 if (aWindow) {
59 nsCOMPtr<Document> doc = aWindow->GetExtantDoc();
60 NS_ENSURE_STATE(doc);
61 mHidden = IsDocumentInvisible(*doc);
64 AttachEventListener();
65 DoLock();
67 return NS_OK;
70 NS_IMETHODIMP
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);
84 if (!props) {
85 NS_WARNING("ipc:content-shutdown message without property bag as subject");
86 return NS_OK;
89 uint64_t childID = 0;
90 nsresult rv = props->GetPropertyAsUint64(u"childID"_ns, &childID);
91 if (NS_SUCCEEDED(rv)) {
92 if (childID == mContentParentID) {
93 mLocked = false;
95 } else {
96 NS_WARNING("ipc:content-shutdown message without childID property");
98 return NS_OK;
101 void WakeLock::DoLock() {
102 if (!mLocked) {
103 // Change the flag immediately to prevent recursive reentering
104 mLocked = true;
106 hal::ModifyWakeLock(
107 mTopic, hal::WAKE_LOCK_ADD_ONE,
108 mHidden ? hal::WAKE_LOCK_ADD_ONE : hal::WAKE_LOCK_NO_CHANGE,
109 mContentParentID);
113 void WakeLock::DoUnlock() {
114 if (mLocked) {
115 // Change the flag immediately to prevent recursive reentering
116 mLocked = false;
118 hal::ModifyWakeLock(
119 mTopic, hal::WAKE_LOCK_REMOVE_ONE,
120 mHidden ? hal::WAKE_LOCK_REMOVE_ONE : hal::WAKE_LOCK_NO_CHANGE,
121 mContentParentID);
125 void WakeLock::AttachEventListener() {
126 if (nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mWindow)) {
127 nsCOMPtr<Document> doc = window->GetExtantDoc();
128 if (doc) {
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();
147 if (doc) {
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.
163 if (!mLocked) {
164 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
165 return;
168 DoUnlock();
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();
181 NS_IMETHODIMP
182 WakeLock::HandleEvent(Event* aEvent) {
183 nsAutoString type;
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) {
194 hal::ModifyWakeLock(
195 mTopic, hal::WAKE_LOCK_NO_CHANGE,
196 mHidden ? hal::WAKE_LOCK_ADD_ONE : hal::WAKE_LOCK_REMOVE_ONE,
197 mContentParentID);
200 return NS_OK;
203 if (type.EqualsLiteral("pagehide")) {
204 DoUnlock();
205 return NS_OK;
208 if (type.EqualsLiteral("pageshow")) {
209 DoLock();
210 return NS_OK;
213 return NS_OK;
216 NS_IMETHODIMP
217 WakeLock::Unlock() {
218 ErrorResult error;
219 Unlock(error);
220 return error.StealNSResult();
223 nsPIDOMWindowInner* WakeLock::GetParentObject() const {
224 nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mWindow);
225 return window;
228 } // namespace mozilla::dom