Skip to main content

script/dom/wakelock/
wakelock.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::cell::Cell;
6
7use dom_struct::dom_struct;
8use embedder_traits::{AllowOrDeny, EmbedderMsg};
9use js::context::JSContext;
10use js::realm::CurrentRealm;
11use script_bindings::reflector::{Reflector, reflect_dom_object_with_cx};
12use servo_constellation_traits::ScriptToConstellationMessage;
13
14use crate::conversions::Convert;
15use crate::dom::bindings::codegen::Bindings::DocumentBinding::{
16    DocumentMethods, DocumentVisibilityState,
17};
18use crate::dom::bindings::codegen::Bindings::WakeLockBinding::{WakeLockMethods, WakeLockType};
19use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
20use crate::dom::bindings::error::Error;
21use crate::dom::bindings::reflector::DomGlobal;
22use crate::dom::bindings::root::DomRoot;
23use crate::dom::globalscope::GlobalScope;
24use crate::dom::promise::{Promise, RootedPromise};
25use crate::dom::wakelock::wakelocksentinel::WakeLockSentinel;
26use crate::routed_promise::{RoutedPromiseListener, callback_promise};
27
28/// <https://w3c.github.io/screen-wake-lock/#the-wakelock-interface>
29#[dom_struct]
30pub(crate) struct WakeLock {
31    reflector_: Reflector,
32    type_: Cell<WakeLockType>,
33}
34
35impl WakeLock {
36    pub(crate) fn new_inherited() -> Self {
37        Self {
38            reflector_: Reflector::new(),
39            type_: Cell::new(WakeLockType::Screen),
40        }
41    }
42
43    pub(crate) fn new(cx: &mut js::context::JSContext, global: &GlobalScope) -> DomRoot<Self> {
44        reflect_dom_object_with_cx(Box::new(Self::new_inherited()), global, cx)
45    }
46}
47
48impl WakeLockMethods<crate::DomTypeHolder> for WakeLock {
49    /// <https://w3c.github.io/screen-wake-lock/#the-request-method>
50    fn Request(&self, cx: &mut CurrentRealm, type_: WakeLockType) -> RootedPromise {
51        let global = GlobalScope::from_current_realm(cx);
52        let promise = Promise::new_in_realm_rooted(cx);
53
54        // Step 1. Let document be this's relevant global object's associated Document.
55        let document = global.as_window().Document();
56
57        // Step 2. If document is not fully active, reject with NotAllowedError.
58        if !document.is_fully_active() {
59            promise.reject_error(cx, Error::NotAllowed(Some(
60                        "Failed to execute 'request' on 'WakeLock': The requesting page is not fully active."
61                        .to_string())));
62            return promise;
63        }
64
65        // Step 3. If document's visibility state is "hidden", reject with NotAllowedError.
66        if document.VisibilityState() == DocumentVisibilityState::Hidden {
67            promise.reject_error(cx, Error::NotAllowed(Some(
68                        "Failed to execute 'request' on 'WakeLock': The requesting page is not visible."
69                        .to_string()
70                        )));
71            return promise;
72        }
73
74        // Step 4. Obtain permission for "screen-wake-lock".
75        // <https://w3c.github.io/screen-wake-lock/#dfn-obtain-permission>
76        let Some(webview_id) = global.webview_id() else {
77            promise.reject_error(
78                cx,
79                Error::NotAllowed(Some("Unable to obtain WakeLock permission.".to_string())),
80            );
81            return promise;
82        };
83
84        self.type_.set(type_);
85        let task_manager = global.task_manager();
86        let task_source = task_manager.dom_manipulation_task_source();
87        let callback = callback_promise(&promise, self, task_source);
88        global.send_to_embedder(EmbedderMsg::RequestWakeLockPermission(
89            webview_id,
90            callback,
91            self.type_.get().convert(),
92        ));
93
94        promise
95    }
96}
97
98impl RoutedPromiseListener<AllowOrDeny> for WakeLock {
99    /// <https://w3c.github.io/screen-wake-lock/#the-request-method>
100    fn handle_response(&self, cx: &mut JSContext, response: AllowOrDeny, promise: &RootedPromise) {
101        match response {
102            // Step 7a. If permission is denied, reject with NotAllowedError.
103            AllowOrDeny::Deny => {
104                promise.reject_error(
105                    cx,
106                    Error::NotAllowed(Some(
107                        "Failed to execute 'request' on 'WakeLock': Permission denied.".to_string(),
108                    )),
109                );
110            },
111            // Step 7b-7c. Acquire the lock and resolve with a WakeLockSentinel.
112            AllowOrDeny::Allow => {
113                let global = self.global();
114                global.as_window().send_to_constellation(
115                    ScriptToConstellationMessage::AcquireWakeLock(self.type_.get().convert()),
116                );
117
118                let sentinel = WakeLockSentinel::new(cx, &global, self.type_.get());
119                promise.resolve_native(cx, &sentinel);
120            },
121        }
122    }
123}
124
125impl Convert<embedder_traits::WakeLockType> for WakeLockType {
126    fn convert(self) -> embedder_traits::WakeLockType {
127        match self {
128            WakeLockType::Screen => embedder_traits::WakeLockType::Screen,
129        }
130    }
131}