Bug 1885489 - Part 5: Add SnapshotIterator::readInt32(). r=iain
[gecko.git] / dom / power / WakeLockJS.cpp
blob3aa748fbe65cfd30b850da718b6caf9f7621f3b2
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
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ErrorList.h"
8 #include "mozilla/AlreadyAddRefed.h"
9 #include "mozilla/Assertions.h"
10 #include "mozilla/Logging.h"
11 #include "mozilla/dom/Document.h"
12 #include "mozilla/dom/Event.h"
13 #include "mozilla/dom/EventTarget.h"
14 #include "mozilla/dom/FeaturePolicyUtils.h"
15 #include "mozilla/dom/Navigator.h"
16 #include "mozilla/dom/Promise.h"
17 #include "mozilla/dom/WakeLockBinding.h"
18 #include "mozilla/Hal.h"
19 #include "mozilla/StaticPrefs_dom.h"
20 #include "nsCOMPtr.h"
21 #include "nsCRT.h"
22 #include "nsError.h"
23 #include "nsIGlobalObject.h"
24 #include "nsISupports.h"
25 #include "nsPIDOMWindow.h"
26 #include "nsContentPermissionHelper.h"
27 #include "nsServiceManagerUtils.h"
28 #include "nscore.h"
29 #include "WakeLock.h"
30 #include "WakeLockJS.h"
31 #include "WakeLockSentinel.h"
33 namespace mozilla::dom {
35 static mozilla::LazyLogModule sLogger("ScreenWakeLock");
37 #define MIN_BATTERY_LEVEL 0.05
39 nsLiteralCString WakeLockJS::GetRequestErrorMessage(RequestError aRv) {
40 switch (aRv) {
41 case RequestError::DocInactive:
42 return "The requesting document is inactive."_ns;
43 case RequestError::DocHidden:
44 return "The requesting document is hidden."_ns;
45 case RequestError::PolicyDisallowed:
46 return "A permissions policy does not allow screen-wake-lock for the requesting document."_ns;
47 case RequestError::PrefDisabled:
48 return "The pref dom.screenwakelock.enabled is disabled."_ns;
49 case RequestError::InternalFailure:
50 return "A browser-internal error occured."_ns;
51 case RequestError::PermissionDenied:
52 return "Permission to request screen-wake-lock was denied."_ns;
53 default:
54 MOZ_ASSERT_UNREACHABLE("Unknown error reason");
55 return "Unknown error"_ns;
59 // https://w3c.github.io/screen-wake-lock/#the-request-method steps 2-5
60 WakeLockJS::RequestError WakeLockJS::WakeLockAllowedForDocument(
61 Document* aDoc) {
62 if (!aDoc) {
63 return RequestError::InternalFailure;
66 // Step 2. check policy-controlled feature screen-wake-lock
67 if (!FeaturePolicyUtils::IsFeatureAllowed(aDoc, u"screen-wake-lock"_ns)) {
68 return RequestError::PolicyDisallowed;
71 // Step 3. Deny wake lock for user agent specific reasons
72 if (!StaticPrefs::dom_screenwakelock_enabled()) {
73 return RequestError::PrefDisabled;
76 // Step 4 check doc active
77 if (!aDoc->IsActive()) {
78 return RequestError::DocInactive;
81 // Step 5. check doc visible
82 if (aDoc->Hidden()) {
83 return RequestError::DocHidden;
86 return RequestError::Success;
89 // https://w3c.github.io/screen-wake-lock/#dfn-applicable-wake-lock
90 static bool IsWakeLockApplicable(WakeLockType aType) {
91 hal::BatteryInformation batteryInfo;
92 hal::GetCurrentBatteryInformation(&batteryInfo);
93 if (batteryInfo.level() <= MIN_BATTERY_LEVEL && !batteryInfo.charging()) {
94 return false;
97 // only currently supported wake lock type
98 return aType == WakeLockType::Screen;
101 // https://w3c.github.io/screen-wake-lock/#dfn-release-a-wake-lock
102 void ReleaseWakeLock(Document* aDoc, WakeLockSentinel* aLock,
103 WakeLockType aType) {
104 MOZ_ASSERT(aLock);
105 MOZ_ASSERT(aDoc);
107 RefPtr<WakeLockSentinel> kungFuDeathGrip = aLock;
108 aDoc->ActiveWakeLocks(aType).Remove(aLock);
109 aLock->NotifyLockReleased();
110 MOZ_LOG(sLogger, LogLevel::Debug, ("Released wake lock sentinel"));
113 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(WakeLockJS)
115 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WakeLockJS)
116 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
117 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
119 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WakeLockJS)
120 tmp->DetachListeners();
121 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
122 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
123 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
125 NS_IMPL_CYCLE_COLLECTING_ADDREF(WakeLockJS)
126 NS_IMPL_CYCLE_COLLECTING_RELEASE(WakeLockJS)
128 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WakeLockJS)
129 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
130 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
131 NS_INTERFACE_MAP_ENTRY(nsIObserver)
132 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
133 NS_INTERFACE_MAP_END
135 WakeLockJS::WakeLockJS(nsPIDOMWindowInner* aWindow) : mWindow(aWindow) {
136 AttachListeners();
139 WakeLockJS::~WakeLockJS() { DetachListeners(); }
141 nsISupports* WakeLockJS::GetParentObject() const { return mWindow; }
143 JSObject* WakeLockJS::WrapObject(JSContext* aCx,
144 JS::Handle<JSObject*> aGivenProto) {
145 return WakeLock_Binding::Wrap(aCx, this, aGivenProto);
148 // https://w3c.github.io/screen-wake-lock/#the-request-method Step 7.3
149 Result<already_AddRefed<WakeLockSentinel>, WakeLockJS::RequestError>
150 WakeLockJS::Obtain(WakeLockType aType, Document* aDoc) {
151 // Step 7.3.1. check visibility again
152 // Out of spec, but also check everything else
153 RequestError rv = WakeLockAllowedForDocument(aDoc);
154 if (rv != RequestError::Success) {
155 return Err(rv);
157 // Step 7.3.3. let lock be a new WakeLockSentinel
158 RefPtr<WakeLockSentinel> lock =
159 MakeRefPtr<WakeLockSentinel>(mWindow->AsGlobal(), aType);
160 // Step 7.3.2. acquire a wake lock
161 if (IsWakeLockApplicable(aType)) {
162 lock->AcquireActualLock();
165 // Steps 7.3.4. append lock to locks
166 aDoc->ActiveWakeLocks(aType).Insert(lock);
168 return lock.forget();
171 // https://w3c.github.io/screen-wake-lock/#the-request-method
172 already_AddRefed<Promise> WakeLockJS::Request(WakeLockType aType,
173 ErrorResult& aRv) {
174 MOZ_LOG(sLogger, LogLevel::Debug, ("Received request for wake lock"));
175 nsCOMPtr<nsIGlobalObject> global = mWindow->AsGlobal();
177 RefPtr<Promise> promise = Promise::Create(global, aRv);
178 NS_ENSURE_FALSE(aRv.Failed(), nullptr);
180 // Steps 1-5
181 nsCOMPtr<Document> doc = mWindow->GetExtantDoc();
182 RequestError rv = WakeLockAllowedForDocument(doc);
183 if (rv != RequestError::Success) {
184 promise->MaybeRejectWithNotAllowedError(GetRequestErrorMessage(rv));
185 return promise.forget();
188 // For now, we don't check the permission as we always grant the lock
189 // Step 7.3. Queue a task
190 NS_DispatchToMainThread(NS_NewRunnableFunction(
191 "ObtainWakeLock",
192 [aType, promise, doc, self = RefPtr<WakeLockJS>(this)]() {
193 auto lockOrErr = self->Obtain(aType, doc);
194 if (lockOrErr.isOk()) {
195 RefPtr<WakeLockSentinel> lock = lockOrErr.unwrap();
196 promise->MaybeResolve(lock);
197 MOZ_LOG(sLogger, LogLevel::Debug,
198 ("Resolved promise with wake lock sentinel"));
199 } else {
200 promise->MaybeRejectWithNotAllowedError(
201 GetRequestErrorMessage(lockOrErr.unwrapErr()));
203 }));
205 return promise.forget();
208 void WakeLockJS::AttachListeners() {
209 hal::RegisterBatteryObserver(this);
211 nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID);
212 MOZ_ASSERT(prefBranch);
213 DebugOnly<nsresult> rv =
214 prefBranch->AddObserver("dom.screenwakelock.enabled", this, true);
215 MOZ_ASSERT(NS_SUCCEEDED(rv));
218 void WakeLockJS::DetachListeners() {
219 hal::UnregisterBatteryObserver(this);
221 if (nsCOMPtr<nsIPrefBranch> prefBranch =
222 do_GetService(NS_PREFSERVICE_CONTRACTID)) {
223 prefBranch->RemoveObserver("dom.screenwakelock.enabled", this);
227 NS_IMETHODIMP WakeLockJS::Observe(nsISupports* aSubject, const char* aTopic,
228 const char16_t* aData) {
229 if (nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
230 if (!StaticPrefs::dom_screenwakelock_enabled()) {
231 nsCOMPtr<Document> doc = mWindow->GetExtantDoc();
232 MOZ_ASSERT(doc);
233 doc->UnlockAllWakeLocks(WakeLockType::Screen);
236 return NS_OK;
239 void WakeLockJS::Notify(const hal::BatteryInformation& aBatteryInfo) {
240 if (aBatteryInfo.level() > MIN_BATTERY_LEVEL || aBatteryInfo.charging()) {
241 return;
243 nsCOMPtr<Document> doc = mWindow->GetExtantDoc();
244 MOZ_ASSERT(doc);
245 doc->UnlockAllWakeLocks(WakeLockType::Screen);
248 } // namespace mozilla::dom