mirror of
https://github.com/servo/servo
synced 2026-04-25 17:15:48 +02:00
script: Implement wake lock api (#43617)
Implement the WakeLock API Fixes: #41493 --------- Signed-off-by: Kelechi Ebiri <ebiritg@gmail.com>
This commit is contained in:
11
Cargo.lock
generated
11
Cargo.lock
generated
@@ -7589,6 +7589,7 @@ dependencies = [
|
||||
"servo-storage-traits",
|
||||
"servo-tracing",
|
||||
"servo-url",
|
||||
"servo-wakelock",
|
||||
"servo-webgl",
|
||||
"servo-webgpu",
|
||||
"servo-webxr",
|
||||
@@ -7801,6 +7802,7 @@ dependencies = [
|
||||
"servo-storage-traits",
|
||||
"servo-tracing",
|
||||
"servo-url",
|
||||
"servo-wakelock",
|
||||
"servo-webgpu",
|
||||
"servo-webgpu-traits",
|
||||
"servo-webxr-api",
|
||||
@@ -7837,6 +7839,7 @@ dependencies = [
|
||||
"servo-profile-traits",
|
||||
"servo-storage-traits",
|
||||
"servo-url",
|
||||
"servo-wakelock",
|
||||
"servo-webgpu-traits",
|
||||
"strum",
|
||||
"uuid",
|
||||
@@ -8770,6 +8773,7 @@ dependencies = [
|
||||
"servo-storage-traits",
|
||||
"servo-timers",
|
||||
"servo-url",
|
||||
"servo-wakelock",
|
||||
"servo-webgpu-traits",
|
||||
"servo-webxr-api",
|
||||
"servo-xpath",
|
||||
@@ -8948,6 +8952,13 @@ dependencies = [
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "servo-wakelock"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "servo-webdriver-server"
|
||||
version = "0.1.0"
|
||||
|
||||
@@ -285,6 +285,7 @@ servo-media-traits = { version = "0.1.0", path = "components/media/traits" }
|
||||
servo-media-webrtc = { version = "0.1.0", path = "components/media/webrtc" }
|
||||
servo-tracing = { version = "0.1.0", path = "components/servo_tracing" }
|
||||
servo-url = { version = "0.1.0", path = "components/url" }
|
||||
servo-wakelock = { version = "0.1.0", path = "components/wakelock" }
|
||||
storage = { package = "servo-storage", version = "0.1.0", path = "components/storage" }
|
||||
storage_traits = { package = "servo-storage-traits", version = "0.1.0", path = "components/shared/storage" }
|
||||
timers = { package = "servo-timers", version = "0.1.0", path = "components/timers" }
|
||||
|
||||
0
FETCH_HEAD
Normal file
0
FETCH_HEAD
Normal file
@@ -142,6 +142,8 @@ pub struct Preferences {
|
||||
pub dom_gamepad_enabled: bool,
|
||||
// feature: Geolocation API | #38903 | Web/API/Geolocation_API
|
||||
pub dom_geolocation_enabled: bool,
|
||||
// feature: Screen Wake Lock API | #43615 | Web/API/Screen_Wake_Lock_API
|
||||
pub dom_wakelock_enabled: bool,
|
||||
// feature: IndexedDB | #6963 | Web/API/IndexedDB_API
|
||||
pub dom_indexeddb_enabled: bool,
|
||||
// feature: IntersectionObserver | #35767 | Web/API/Intersection_Observer_API
|
||||
@@ -364,6 +366,7 @@ impl Preferences {
|
||||
dom_fullscreen_test: false,
|
||||
dom_gamepad_enabled: true,
|
||||
dom_geolocation_enabled: false,
|
||||
dom_wakelock_enabled: false,
|
||||
dom_indexeddb_enabled: false,
|
||||
dom_intersection_observer_enabled: false,
|
||||
dom_microdata_testing_enabled: false,
|
||||
|
||||
@@ -59,6 +59,7 @@ servo-config = { workspace = true }
|
||||
servo-constellation-traits = { workspace = true }
|
||||
servo-tracing = { workspace = true }
|
||||
servo-url = { workspace = true }
|
||||
servo-wakelock = { workspace = true }
|
||||
storage_traits = { workspace = true }
|
||||
stylo = { workspace = true }
|
||||
stylo_traits = { workspace = true }
|
||||
|
||||
@@ -168,6 +168,7 @@ use servo_constellation_traits::{
|
||||
WindowSizeType,
|
||||
};
|
||||
use servo_url::{Host, ImmutableOrigin, ServoUrl};
|
||||
use servo_wakelock::{WakeLockProvider, WakeLockType};
|
||||
use storage_traits::StorageThreads;
|
||||
use storage_traits::client_storage::ClientStorageThreadMessage;
|
||||
use storage_traits::indexeddb::{IndexedDBThreadMsg, SyncOperation};
|
||||
@@ -484,6 +485,13 @@ pub struct Constellation<STF, SWF> {
|
||||
/// Pipeline ID of the active media session.
|
||||
active_media_session: Option<PipelineId>,
|
||||
|
||||
/// Aggregate screen wake lock count across all webviews. The provider is notified
|
||||
/// only when this transitions 0→1 (acquire) or N→0 (release).
|
||||
screen_wake_lock_count: u32,
|
||||
|
||||
/// Provider for OS-level screen wake lock acquisition and release.
|
||||
wake_lock_provider: Box<dyn WakeLockProvider>,
|
||||
|
||||
/// The image bytes associated with the BrokenImageIcon embedder resource.
|
||||
/// Read during startup and provided to image caches that are created
|
||||
/// on an as-needed basis, rather than retrieving it every time.
|
||||
@@ -581,6 +589,9 @@ pub struct InitialConstellationState {
|
||||
|
||||
/// The async runtime.
|
||||
pub async_runtime: Box<dyn AsyncRuntime>,
|
||||
|
||||
/// The wake lock provider for acquiring and releasing OS-level screen wake locks.
|
||||
pub wake_lock_provider: Box<dyn WakeLockProvider>,
|
||||
}
|
||||
|
||||
/// When we are exiting a pipeline, we can either force exiting or not. A normal exit
|
||||
@@ -731,6 +742,8 @@ where
|
||||
active_keyboard_modifiers: Modifiers::empty(),
|
||||
hard_fail,
|
||||
active_media_session: None,
|
||||
screen_wake_lock_count: 0,
|
||||
wake_lock_provider: state.wake_lock_provider,
|
||||
broken_image_icon_data: broken_image_icon_data.clone(),
|
||||
process_manager: ProcessManager::new(state.mem_profiler_chan),
|
||||
async_runtime: state.async_runtime,
|
||||
@@ -2094,6 +2107,26 @@ where
|
||||
let _ = event_loop.send(ScriptThreadMessage::TriggerGarbageCollection);
|
||||
}
|
||||
},
|
||||
ScriptToConstellationMessage::AcquireWakeLock(type_) => match type_ {
|
||||
WakeLockType::Screen => {
|
||||
self.screen_wake_lock_count += 1;
|
||||
if self.screen_wake_lock_count == 1 {
|
||||
if let Err(e) = self.wake_lock_provider.acquire(type_) {
|
||||
warn!("Failed to acquire screen wake lock: {e}");
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
ScriptToConstellationMessage::ReleaseWakeLock(type_) => match type_ {
|
||||
WakeLockType::Screen => {
|
||||
self.screen_wake_lock_count = self.screen_wake_lock_count.saturating_sub(1);
|
||||
if self.screen_wake_lock_count == 0 {
|
||||
if let Err(e) = self.wake_lock_provider.release(type_) {
|
||||
warn!("Failed to release screen wake lock: {e}");
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -189,6 +189,8 @@ mod from_script {
|
||||
target!("RespondToScreenshotReadinessRequest")
|
||||
},
|
||||
Self::TriggerGarbageCollection => target!("TriggerGarbageCollection"),
|
||||
Self::AcquireWakeLock(..) => target!("AcquireWakeLock"),
|
||||
Self::ReleaseWakeLock(..) => target!("ReleaseWakeLock"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,6 +140,7 @@ servo-constellation-traits = { workspace = true }
|
||||
servo-geometry = { workspace = true }
|
||||
servo-media = { workspace = true }
|
||||
servo-url = { workspace = true }
|
||||
servo-wakelock = { workspace = true }
|
||||
servo_arc = { workspace = true }
|
||||
sha1 = { workspace = true }
|
||||
sha2 = { workspace = true }
|
||||
|
||||
@@ -376,6 +376,8 @@ pub(crate) mod values;
|
||||
pub(crate) mod virtualmethods;
|
||||
pub(crate) mod visibilitystateentry;
|
||||
pub(crate) mod visualviewport;
|
||||
pub(crate) mod wakelock;
|
||||
pub(crate) use self::wakelock::*;
|
||||
pub(crate) mod webgl;
|
||||
pub(crate) use self::webgl::extensions::ext::*;
|
||||
pub(crate) use self::webgl::*;
|
||||
|
||||
@@ -57,6 +57,7 @@ use crate::dom::pluginarray::PluginArray;
|
||||
use crate::dom::serviceworkercontainer::ServiceWorkerContainer;
|
||||
use crate::dom::servointernals::ServoInternals;
|
||||
use crate::dom::types::UserActivation;
|
||||
use crate::dom::wakelock::WakeLock;
|
||||
#[cfg(feature = "webgpu")]
|
||||
use crate::dom::webgpu::gpu::GPU;
|
||||
use crate::dom::window::Window;
|
||||
@@ -133,6 +134,7 @@ pub(crate) struct Navigator {
|
||||
has_gamepad_gesture: Cell<bool>,
|
||||
servo_internals: MutNullableDom<ServoInternals>,
|
||||
user_activation: MutNullableDom<UserActivation>,
|
||||
wake_lock: MutNullableDom<WakeLock>,
|
||||
}
|
||||
|
||||
impl Navigator {
|
||||
@@ -159,6 +161,7 @@ impl Navigator {
|
||||
has_gamepad_gesture: Cell::new(false),
|
||||
servo_internals: Default::default(),
|
||||
user_activation: Default::default(),
|
||||
wake_lock: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -610,6 +613,11 @@ impl NavigatorMethods<crate::DomTypeHolder> for Navigator {
|
||||
self.user_activation
|
||||
.or_init(|| UserActivation::new(&self.global(), can_gc))
|
||||
}
|
||||
|
||||
/// <https://w3c.github.io/screen-wake-lock/#dom-navigator-wakelock>
|
||||
fn WakeLock(&self, cx: &mut js::context::JSContext) -> DomRoot<WakeLock> {
|
||||
self.wake_lock.or_init(|| WakeLock::new(cx, &self.global()))
|
||||
}
|
||||
}
|
||||
|
||||
struct BeaconFetchListener {
|
||||
|
||||
@@ -393,6 +393,7 @@ impl Convert<PermissionFeature> for PermissionName {
|
||||
PermissionName::Background_sync => PermissionFeature::BackgroundSync,
|
||||
PermissionName::Bluetooth => PermissionFeature::Bluetooth,
|
||||
PermissionName::Persistent_storage => PermissionFeature::PersistentStorage,
|
||||
PermissionName::Screen_wake_lock => PermissionFeature::ScreenWakeLock,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
8
components/script/dom/wakelock/mod.rs
Normal file
8
components/script/dom/wakelock/mod.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#[expect(clippy::module_inception, reason = "The interface name is WakeLock")]
|
||||
pub(crate) mod wakelock;
|
||||
pub(crate) use wakelock::WakeLock;
|
||||
pub(crate) mod wakelocksentinel;
|
||||
104
components/script/dom/wakelock/wakelock.rs
Normal file
104
components/script/dom/wakelock/wakelock.rs
Normal file
@@ -0,0 +1,104 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
use dom_struct::dom_struct;
|
||||
use embedder_traits::{AllowOrDeny, EmbedderMsg};
|
||||
use js::context::JSContext;
|
||||
use js::realm::CurrentRealm;
|
||||
use servo_constellation_traits::ScriptToConstellationMessage;
|
||||
|
||||
use crate::dom::bindings::codegen::Bindings::DocumentBinding::{
|
||||
DocumentMethods, DocumentVisibilityState,
|
||||
};
|
||||
use crate::dom::bindings::codegen::Bindings::WakeLockBinding::{WakeLockMethods, WakeLockType};
|
||||
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
||||
use crate::dom::bindings::error::Error;
|
||||
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_cx};
|
||||
use crate::dom::bindings::root::DomRoot;
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::dom::promise::Promise;
|
||||
use crate::dom::wakelock::wakelocksentinel::WakeLockSentinel;
|
||||
use crate::routed_promise::{RoutedPromiseListener, callback_promise};
|
||||
use crate::script_runtime::CanGc;
|
||||
|
||||
/// <https://w3c.github.io/screen-wake-lock/#the-wakelock-interface>
|
||||
#[dom_struct]
|
||||
pub(crate) struct WakeLock {
|
||||
reflector_: Reflector,
|
||||
}
|
||||
|
||||
impl WakeLock {
|
||||
pub(crate) fn new_inherited() -> Self {
|
||||
Self {
|
||||
reflector_: Reflector::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new(cx: &mut js::context::JSContext, global: &GlobalScope) -> DomRoot<Self> {
|
||||
reflect_dom_object_with_cx(Box::new(Self::new_inherited()), global, cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl WakeLockMethods<crate::DomTypeHolder> for WakeLock {
|
||||
/// <https://w3c.github.io/screen-wake-lock/#the-request-method>
|
||||
fn Request(&self, cx: &mut CurrentRealm, _type_: WakeLockType) -> Rc<Promise> {
|
||||
let global = GlobalScope::from_current_realm(cx);
|
||||
let promise = Promise::new_in_realm(cx);
|
||||
|
||||
// Step 1. Let document be this's relevant global object's associated Document.
|
||||
let document = global.as_window().Document();
|
||||
|
||||
// Step 2. If document is not fully active, reject with NotAllowedError.
|
||||
if !document.is_fully_active() {
|
||||
promise.reject_error(Error::NotAllowed(None), CanGc::from_cx(cx));
|
||||
return promise;
|
||||
}
|
||||
|
||||
// Step 3. If document's visibility state is "hidden", reject with NotAllowedError.
|
||||
if document.VisibilityState() == DocumentVisibilityState::Hidden {
|
||||
promise.reject_error(Error::NotAllowed(None), CanGc::from_cx(cx));
|
||||
return promise;
|
||||
}
|
||||
|
||||
// Step 4. Obtain permission for "screen-wake-lock".
|
||||
// <https://w3c.github.io/screen-wake-lock/#dfn-obtain-permission>
|
||||
let Some(webview_id) = global.webview_id() else {
|
||||
promise.reject_error(Error::NotAllowed(None), CanGc::from_cx(cx));
|
||||
return promise;
|
||||
};
|
||||
|
||||
let task_source = global.task_manager().dom_manipulation_task_source();
|
||||
let callback = callback_promise(&promise, self, task_source);
|
||||
global.send_to_embedder(EmbedderMsg::RequestWakeLockPermission(webview_id, callback));
|
||||
|
||||
promise
|
||||
}
|
||||
}
|
||||
|
||||
impl RoutedPromiseListener<AllowOrDeny> for WakeLock {
|
||||
/// <https://w3c.github.io/screen-wake-lock/#the-request-method>
|
||||
fn handle_response(&self, cx: &mut JSContext, response: AllowOrDeny, promise: &Rc<Promise>) {
|
||||
let can_gc = CanGc::from_cx(cx);
|
||||
match response {
|
||||
// Step 7a. If permission is denied, reject with NotAllowedError.
|
||||
AllowOrDeny::Deny => {
|
||||
promise.reject_error(Error::NotAllowed(None), can_gc);
|
||||
},
|
||||
// Step 7b-7c. Acquire the lock and resolve with a WakeLockSentinel.
|
||||
AllowOrDeny::Allow => {
|
||||
let global = self.global();
|
||||
global.as_window().send_to_constellation(
|
||||
ScriptToConstellationMessage::AcquireWakeLock(
|
||||
servo_wakelock::WakeLockType::Screen,
|
||||
),
|
||||
);
|
||||
|
||||
let sentinel = WakeLockSentinel::new(cx, &global, WakeLockType::Screen);
|
||||
promise.resolve_native(&sentinel, can_gc);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
53
components/script/dom/wakelock/wakelocksentinel.rs
Normal file
53
components/script/dom/wakelock/wakelocksentinel.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
use dom_struct::dom_struct;
|
||||
|
||||
use crate::dom::bindings::codegen::Bindings::WakeLockBinding::{
|
||||
WakeLockSentinelMethods, WakeLockType,
|
||||
};
|
||||
use crate::dom::bindings::reflector::reflect_dom_object_with_cx;
|
||||
use crate::dom::bindings::root::DomRoot;
|
||||
use crate::dom::eventtarget::EventTarget;
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
|
||||
/// <https://w3c.github.io/screen-wake-lock/#the-wakelocksentinel-interface>
|
||||
#[dom_struct]
|
||||
pub(crate) struct WakeLockSentinel {
|
||||
eventtarget: EventTarget,
|
||||
released: Cell<bool>,
|
||||
type_: WakeLockType,
|
||||
}
|
||||
|
||||
impl WakeLockSentinel {
|
||||
pub(crate) fn new_inherited(type_: WakeLockType) -> Self {
|
||||
Self {
|
||||
eventtarget: EventTarget::new_inherited(),
|
||||
released: Cell::new(false),
|
||||
type_,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new(
|
||||
cx: &mut js::context::JSContext,
|
||||
global: &GlobalScope,
|
||||
type_: WakeLockType,
|
||||
) -> DomRoot<Self> {
|
||||
reflect_dom_object_with_cx(Box::new(Self::new_inherited(type_)), global, cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl WakeLockSentinelMethods<crate::DomTypeHolder> for WakeLockSentinel {
|
||||
/// <https://w3c.github.io/screen-wake-lock/#dom-wakelocksentinel-released>
|
||||
fn Released(&self) -> bool {
|
||||
self.released.get()
|
||||
}
|
||||
|
||||
/// <https://w3c.github.io/screen-wake-lock/#dom-wakelocksentinel-type>
|
||||
fn Type(&self) -> WakeLockType {
|
||||
self.type_
|
||||
}
|
||||
}
|
||||
@@ -700,7 +700,11 @@ DOMInterfaces = {
|
||||
'Navigator': {
|
||||
'inRealms': ['GetVRDisplays'],
|
||||
'canGc': ['Languages', 'SendBeacon', 'UserActivation'],
|
||||
'cx': ['Clipboard']
|
||||
'cx': ['Clipboard', 'WakeLock'],
|
||||
},
|
||||
|
||||
'WakeLock': {
|
||||
'realm': ['Request'],
|
||||
},
|
||||
|
||||
'Node': {
|
||||
|
||||
@@ -26,6 +26,7 @@ enum PermissionName {
|
||||
"background-sync",
|
||||
"bluetooth",
|
||||
"persistent-storage",
|
||||
"screen-wake-lock",
|
||||
};
|
||||
|
||||
[Pref="dom_permissions_enabled", Exposed=(Window,Worker)]
|
||||
|
||||
26
components/script_bindings/webidls/WakeLock.webidl
Normal file
26
components/script_bindings/webidls/WakeLock.webidl
Normal file
@@ -0,0 +1,26 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// https://w3c.github.io/screen-wake-lock/
|
||||
|
||||
[SecureContext]
|
||||
partial interface Navigator {
|
||||
[SameObject, Pref="dom_wakelock_enabled"] readonly attribute WakeLock wakeLock;
|
||||
};
|
||||
|
||||
enum WakeLockType { "screen" };
|
||||
|
||||
[SecureContext, Exposed=(Window), Pref="dom_wakelock_enabled"]
|
||||
interface WakeLock {
|
||||
Promise<WakeLockSentinel> request(optional WakeLockType type = "screen");
|
||||
};
|
||||
|
||||
[SecureContext, Exposed=(Window), Pref="dom_wakelock_enabled"]
|
||||
interface WakeLockSentinel : EventTarget {
|
||||
readonly attribute boolean released;
|
||||
readonly attribute WakeLockType type;
|
||||
|
||||
// Promise<void> release();
|
||||
// attribute EventHandler onrelease;
|
||||
};
|
||||
@@ -126,6 +126,7 @@ servo-media-dummy = { workspace = true }
|
||||
servo-media-gstreamer = { workspace = true, optional = true }
|
||||
servo-tracing = { workspace = true }
|
||||
servo-url = { workspace = true }
|
||||
servo-wakelock = { workspace = true }
|
||||
storage = { workspace = true }
|
||||
storage_traits = { workspace = true }
|
||||
stylo = { workspace = true }
|
||||
|
||||
@@ -6,8 +6,8 @@ use std::cell::RefCell;
|
||||
|
||||
use crossbeam_channel::{Receiver, Sender, TryRecvError, unbounded};
|
||||
use log::warn;
|
||||
use serde::Serialize;
|
||||
use servo_base::generic_channel::{GenericSender, SendError, SendResult};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use servo_base::generic_channel::{GenericCallback, GenericSender, SendError, SendResult};
|
||||
use tokio::sync::mpsc::UnboundedSender as TokioSender;
|
||||
use tokio::sync::oneshot::Sender as TokioOneshotSender;
|
||||
|
||||
@@ -73,6 +73,15 @@ impl<T: Serialize> AbstractSender for GenericSender<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: for<'de> Deserialize<'de> + Serialize + Send + 'static> AbstractSender
|
||||
for GenericCallback<T>
|
||||
{
|
||||
type Message = T;
|
||||
fn send(&self, value: T) -> SendResult {
|
||||
GenericCallback::send(self, value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AbstractSender for TokioSender<T> {
|
||||
type Message = T;
|
||||
fn send(&self, value: T) -> SendResult {
|
||||
|
||||
@@ -69,6 +69,7 @@ use servo_geometry::{
|
||||
};
|
||||
use servo_media::ServoMedia;
|
||||
use servo_media::player::context::GlContext;
|
||||
use servo_wakelock::NoOpWakeLockProvider;
|
||||
use storage::new_storage_threads;
|
||||
use storage_traits::StorageThreads;
|
||||
use style::global_style_data::StyleThreadPool;
|
||||
@@ -543,6 +544,21 @@ impl ServoInner {
|
||||
.request_permission(webview, permission_request);
|
||||
}
|
||||
},
|
||||
EmbedderMsg::RequestWakeLockPermission(webview_id, callback) => {
|
||||
if let Some(webview) = self.get_webview_handle(webview_id) {
|
||||
let permission_request = PermissionRequest {
|
||||
requested_feature: PermissionFeature::ScreenWakeLock,
|
||||
allow_deny_request: AllowOrDenyRequest::new_from_callback(
|
||||
callback,
|
||||
AllowOrDeny::Deny,
|
||||
self.servo_errors.sender(),
|
||||
),
|
||||
};
|
||||
webview
|
||||
.delegate()
|
||||
.request_permission(webview, permission_request);
|
||||
}
|
||||
},
|
||||
EmbedderMsg::OnDevtoolsStarted(port, token) => match port {
|
||||
Ok(port) => self
|
||||
.delegate
|
||||
@@ -1161,6 +1177,7 @@ fn create_constellation(
|
||||
wgpu_image_map: paint.webgpu_image_map(),
|
||||
async_runtime,
|
||||
privileged_urls,
|
||||
wake_lock_provider: Box::new(NoOpWakeLockProvider),
|
||||
};
|
||||
|
||||
let layout_factory = Arc::new(LayoutFactoryImpl());
|
||||
|
||||
@@ -15,7 +15,7 @@ use embedder_traits::{
|
||||
WebResourceRequest, WebResourceResponse, WebResourceResponseMsg,
|
||||
};
|
||||
use paint_api::rendering_context::RenderingContext;
|
||||
use servo_base::generic_channel::{GenericSender, SendError};
|
||||
use servo_base::generic_channel::{GenericCallback, GenericSender, SendError};
|
||||
use servo_base::id::PipelineId;
|
||||
use servo_constellation_traits::EmbedderToConstellationMessage;
|
||||
use tokio::sync::mpsc::UnboundedSender as TokioSender;
|
||||
@@ -104,6 +104,17 @@ impl AllowOrDenyRequest {
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn new_from_callback(
|
||||
callback: GenericCallback<AllowOrDeny>,
|
||||
default_response: AllowOrDeny,
|
||||
error_sender: ServoErrorSender,
|
||||
) -> Self {
|
||||
Self(
|
||||
IpcResponder::new_same_process(Box::new(callback), default_response),
|
||||
error_sender,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn allow(mut self) {
|
||||
if let Err(error) = self.0.send(AllowOrDeny::Allow) {
|
||||
self.1.raise_response_send_error(error);
|
||||
|
||||
@@ -41,6 +41,7 @@ servo-base = { workspace = true }
|
||||
servo-canvas-traits = { workspace = true }
|
||||
servo-config = { workspace = true }
|
||||
servo-url = { workspace = true }
|
||||
servo-wakelock = { workspace = true }
|
||||
storage_traits = { workspace = true }
|
||||
strum = { workspace = true }
|
||||
uuid = { workspace = true }
|
||||
|
||||
@@ -37,6 +37,7 @@ use servo_base::id::{
|
||||
use servo_canvas_traits::canvas::{CanvasId, CanvasMsg};
|
||||
use servo_canvas_traits::webgl::WebGLChan;
|
||||
use servo_url::{ImmutableOrigin, OriginSnapshot, ServoUrl};
|
||||
use servo_wakelock::WakeLockType;
|
||||
use storage_traits::StorageThreads;
|
||||
use storage_traits::webstorage_thread::WebStorageType;
|
||||
use strum::IntoStaticStr;
|
||||
@@ -729,6 +730,14 @@ pub enum ScriptToConstellationMessage {
|
||||
RespondToScreenshotReadinessRequest(ScreenshotReadinessResponse),
|
||||
/// Request the constellation to force garbage collection in all `ScriptThread`'s.
|
||||
TriggerGarbageCollection,
|
||||
/// Request to acquire a wake lock of the given type. The constellation will track the
|
||||
/// aggregate lock count and notify the provider only when the count transitions from 0 to 1.
|
||||
/// <https://w3c.github.io/screen-wake-lock/#dfn-acquire-wake-lock>
|
||||
AcquireWakeLock(WakeLockType),
|
||||
/// Request to release a wake lock of the given type. The constellation will track the
|
||||
/// aggregate lock count and notify the provider only when the count transitions from N to 0.
|
||||
/// <https://w3c.github.io/screen-wake-lock/#dfn-release-wake-lock>
|
||||
ReleaseWakeLock(WakeLockType),
|
||||
}
|
||||
|
||||
impl fmt::Debug for ScriptToConstellationMessage {
|
||||
|
||||
@@ -481,6 +481,10 @@ pub enum EmbedderMsg {
|
||||
),
|
||||
/// Open interface to request permission specified by prompt.
|
||||
PromptPermission(WebViewId, PermissionFeature, GenericSender<AllowOrDeny>),
|
||||
/// Async permission request for screen wake lock. The callback is invoked
|
||||
/// with the user's decision, which resolves or rejects the pending promise
|
||||
/// without blocking the script thread.
|
||||
RequestWakeLockPermission(WebViewId, GenericCallback<AllowOrDeny>),
|
||||
/// Report the status of Devtools Server with a token that can be used to bypass the permission prompt.
|
||||
OnDevtoolsStarted(Result<u16, ()>, String),
|
||||
/// Ask the user to allow a devtools client to connect.
|
||||
@@ -595,6 +599,7 @@ pub enum PermissionFeature {
|
||||
BackgroundSync,
|
||||
Bluetooth,
|
||||
PersistentStorage,
|
||||
ScreenWakeLock,
|
||||
}
|
||||
|
||||
/// Used to specify the kind of input method editor appropriate to edit a field.
|
||||
|
||||
17
components/wakelock/Cargo.toml
Normal file
17
components/wakelock/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "servo-wakelock"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
publish.workspace = true
|
||||
rust-version.workspace = true
|
||||
repository.workspace = true
|
||||
description.workspace = true
|
||||
|
||||
[lib]
|
||||
name = "servo_wakelock"
|
||||
path = "lib.rs"
|
||||
|
||||
[dependencies]
|
||||
serde = { workspace = true }
|
||||
51
components/wakelock/lib.rs
Normal file
51
components/wakelock/lib.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! Platform abstraction for the Screen Wake Lock API.
|
||||
//!
|
||||
//! Defines [`WakeLockProvider`], a trait for acquiring and releasing OS-level
|
||||
//! wake locks. Platform-specific implementations will be added in follow-up
|
||||
//! work. For now, [`NoOpWakeLockProvider`] is the only implementation and
|
||||
//! does nothing.
|
||||
//!
|
||||
//! <https://w3c.github.io/screen-wake-lock/>
|
||||
use std::error::Error;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// The type of wake lock to acquire or release.
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub enum WakeLockType {
|
||||
Screen,
|
||||
}
|
||||
|
||||
/// Trait for platform-specific wake lock support.
|
||||
///
|
||||
/// Implementations are responsible for interacting with the OS to prevent
|
||||
/// the screen (or other resources) from sleeping while a wake lock is held.
|
||||
pub trait WakeLockProvider: Send + Sync {
|
||||
/// Acquire a wake lock of the given type, preventing the associated
|
||||
/// resource from sleeping. Called when the aggregate lock count transitions
|
||||
/// from 0 to 1. Returns an error if the OS fails to grant the lock.
|
||||
fn acquire(&self, type_: WakeLockType) -> Result<(), Box<dyn Error>>;
|
||||
|
||||
/// Release a previously acquired wake lock of the given type, allowing
|
||||
/// the resource to sleep. Called when the aggregate lock count transitions
|
||||
/// from N to 0.
|
||||
fn release(&self, type_: WakeLockType) -> Result<(), Box<dyn Error>>;
|
||||
}
|
||||
|
||||
/// A no-op [`WakeLockProvider`] used when no platform implementation is
|
||||
/// available. All operations succeed silently.
|
||||
pub struct NoOpWakeLockProvider;
|
||||
|
||||
impl WakeLockProvider for NoOpWakeLockProvider {
|
||||
fn acquire(&self, _type_: WakeLockType) -> Result<(), Box<dyn Error>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn release(&self, _type_: WakeLockType) -> Result<(), Box<dyn Error>> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
2
tests/wpt/include.ini
vendored
2
tests/wpt/include.ini
vendored
@@ -254,6 +254,8 @@ skip: true
|
||||
skip: false
|
||||
[secure-contexts]
|
||||
skip: false
|
||||
[screen-wake-lock]
|
||||
skip: false
|
||||
[selection]
|
||||
skip: false
|
||||
[service-workers]
|
||||
|
||||
@@ -17,9 +17,6 @@
|
||||
[Query "nfc" permission]
|
||||
expected: FAIL
|
||||
|
||||
[Query "screen-wake-lock" permission]
|
||||
expected: FAIL
|
||||
|
||||
[Query "display-capture" permission]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
1
tests/wpt/meta/screen-wake-lock/__dir__.ini
vendored
Normal file
1
tests/wpt/meta/screen-wake-lock/__dir__.ini
vendored
Normal file
@@ -0,0 +1 @@
|
||||
prefs: [dom_wakelock_enabled: true]
|
||||
27
tests/wpt/meta/screen-wake-lock/idlharness.https.window.js.ini
vendored
Normal file
27
tests/wpt/meta/screen-wake-lock/idlharness.https.window.js.ini
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
[idlharness.https.window.html]
|
||||
[idl_test setup]
|
||||
expected: FAIL
|
||||
|
||||
[WakeLockSentinel interface: operation release()]
|
||||
expected: FAIL
|
||||
|
||||
[WakeLockSentinel interface: attribute onrelease]
|
||||
expected: FAIL
|
||||
|
||||
[WakeLockSentinel must be primary interface of sentinel]
|
||||
expected: FAIL
|
||||
|
||||
[Stringification of sentinel]
|
||||
expected: FAIL
|
||||
|
||||
[WakeLockSentinel interface: sentinel must inherit property "released" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[WakeLockSentinel interface: sentinel must inherit property "type" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[WakeLockSentinel interface: sentinel must inherit property "release()" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[WakeLockSentinel interface: sentinel must inherit property "onrelease" with the proper type]
|
||||
expected: FAIL
|
||||
@@ -1,7 +1,4 @@
|
||||
[wakelock-disabled-by-permissions-policy.https.html]
|
||||
[Permissions-Policy header "screen-wake-lock=()" disallows the top-level document.]
|
||||
expected: FAIL
|
||||
|
||||
[Permissions-Policy header "screen-wake-lock=()" disallows same-origin iframes.]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
3
tests/wpt/meta/screen-wake-lock/wakelock-screen-type-on-worker.https.worker.js.ini
vendored
Normal file
3
tests/wpt/meta/screen-wake-lock/wakelock-screen-type-on-worker.https.worker.js.ini
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[wakelock-screen-type-on-worker.https.worker.html]
|
||||
[Screen wake lock should not be allowed in dedicated worker]
|
||||
expected: FAIL
|
||||
3
tests/wpt/meta/screen-wake-lock/wakelock-supported-by-permissions-policy.html.ini
vendored
Normal file
3
tests/wpt/meta/screen-wake-lock/wakelock-supported-by-permissions-policy.html.ini
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[wakelock-supported-by-permissions-policy.html]
|
||||
[document.permissionsPolicy.features should advertise screen-wake-lock.]
|
||||
expected: FAIL
|
||||
3
tests/wpt/meta/screen-wake-lock/wakelock-type.https.window.js.ini
vendored
Normal file
3
tests/wpt/meta/screen-wake-lock/wakelock-type.https.window.js.ini
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[wakelock-type.https.window.html]
|
||||
['type' parameter in WakeLock.request() defaults to 'screen']
|
||||
expected: FAIL
|
||||
Reference in New Issue
Block a user