From f2c5127f27037b1194e48c5a55e83f3e6df38c12 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Sun, 17 Dec 2023 13:31:48 +0100 Subject: [PATCH] Web: remove queuing fullscreen request (#3242) --- CHANGELOG.md | 1 + src/platform_impl/web/event_loop/runner.rs | 28 --- .../web/event_loop/window_target.rs | 2 - src/platform_impl/web/web_sys/canvas.rs | 49 +---- src/platform_impl/web/web_sys/fullscreen.rs | 191 +++++++----------- src/platform_impl/web/web_sys/pointer.rs | 4 +- src/window.rs | 3 +- 7 files changed, 79 insertions(+), 199 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 964f360f..9bbde75e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ Unreleased` header. - On Wayland, disable Client Side Decorations when `wl_subcompositor` is not supported. - On X11, fix `Xft.dpi` detection from Xresources. - On Windows, fix consecutive calls to `window.set_fullscreen(Some(Fullscreen::Borderless(None)))` resulting in losing previous window state when eventually exiting fullscreen using `window.set_fullscreen(None)`. +- On Web, remove queuing fullscreen request in absence of transient activation. # 0.29.4 diff --git a/src/platform_impl/web/event_loop/runner.rs b/src/platform_impl/web/event_loop/runner.rs index 068d2893..b029ab51 100644 --- a/src/platform_impl/web/event_loop/runner.rs +++ b/src/platform_impl/web/event_loop/runner.rs @@ -67,7 +67,6 @@ pub struct Execution { on_key_press: OnEventHandle, on_key_release: OnEventHandle, on_visibility_change: OnEventHandle, - on_touch_end: OnEventHandle, } enum RunnerEnum { @@ -181,7 +180,6 @@ impl Shared { on_key_press: RefCell::new(None), on_key_release: RefCell::new(None), on_visibility_change: RefCell::new(None), - on_touch_end: RefCell::new(None), } })) } @@ -342,8 +340,6 @@ impl Shared { self.window().clone(), "pointerdown", Closure::new(move |event: PointerEvent| { - runner.transient_activation(); - if !runner.device_events() { return; } @@ -367,8 +363,6 @@ impl Shared { self.window().clone(), "pointerup", Closure::new(move |event: PointerEvent| { - runner.transient_activation(); - if !runner.device_events() { return; } @@ -392,8 +386,6 @@ impl Shared { self.window().clone(), "keydown", Closure::new(move |event: KeyboardEvent| { - runner.transient_activation(); - if !runner.device_events() { return; } @@ -452,14 +444,6 @@ impl Shared { } }), )); - let runner = self.clone(); - *self.0.on_touch_end.borrow_mut() = Some(EventListenerHandle::new( - self.window().clone(), - "touchend", - Closure::new(move |_| { - runner.transient_activation(); - }), - )); } // Generate a strictly increasing ID @@ -788,18 +772,6 @@ impl Shared { } } - fn transient_activation(&self) { - self.0 - .all_canvases - .borrow() - .iter() - .for_each(|(_, canvas, _)| { - if let Some(canvas) = canvas.upgrade() { - canvas.borrow().transient_activation(); - } - }); - } - pub fn event_loop_recreation(&self, allow: bool) { self.0.event_loop_recreation.set(allow) } diff --git a/src/platform_impl/web/event_loop/window_target.rs b/src/platform_impl/web/event_loop/window_target.rs index 09a5cc8b..16cd5093 100644 --- a/src/platform_impl/web/event_loop/window_target.rs +++ b/src/platform_impl/web/event_loop/window_target.rs @@ -647,8 +647,6 @@ impl EventLoopWindowTarget { let runner = self.runner.clone(); canvas.on_animation_frame(move || runner.request_redraw(RootWindowId(id))); - - canvas.on_touch_end(); } pub fn available_monitors(&self) -> VecDequeIter { diff --git a/src/platform_impl/web/web_sys/canvas.rs b/src/platform_impl/web/web_sys/canvas.rs index ada3cc5f..f2cffd63 100644 --- a/src/platform_impl/web/web_sys/canvas.rs +++ b/src/platform_impl/web/web_sys/canvas.rs @@ -1,5 +1,5 @@ use std::cell::Cell; -use std::rc::{Rc, Weak}; +use std::rc::Rc; use std::sync::{Arc, Mutex}; use smol_str::SmolStr; @@ -18,11 +18,10 @@ use crate::window::{WindowAttributes, WindowId as RootWindowId}; use super::super::WindowId; use super::animation_frame::AnimationFrameHandler; use super::event_handle::EventListenerHandle; -use super::fullscreen::FullscreenHandler; use super::intersection_handle::IntersectionObserverHandle; use super::media_query_handle::MediaQueryListHandle; use super::pointer::PointerHandler; -use super::{event, ButtonsState, ResizeScaleHandle}; +use super::{event, fullscreen, ButtonsState, ResizeScaleHandle}; #[allow(dead_code)] pub struct Canvas { @@ -52,7 +51,6 @@ pub struct Common { style: Style, old_size: Rc>>, current_size: Rc>>, - fullscreen_handler: Rc, } #[derive(Clone, Debug)] @@ -105,7 +103,6 @@ impl Canvas { style, old_size: Rc::default(), current_size: Rc::default(), - fullscreen_handler: Rc::new(FullscreenHandler::new(document.clone(), canvas.clone())), }; if let Some(size) = attr.inner_size { @@ -129,7 +126,7 @@ impl Canvas { } if attr.fullscreen.0.is_some() { - common.fullscreen_handler.request_fullscreen(); + fullscreen::request_fullscreen(&document, &canvas); } if attr.active { @@ -281,7 +278,7 @@ impl Canvas { where F: 'static + FnMut(PhysicalKey, Key, Option, KeyLocation, bool, ModifiersState), { - self.on_keyboard_press = Some(self.common.add_transient_event( + self.on_keyboard_press = Some(self.common.add_event( "keydown", move |event: KeyboardEvent| { if prevent_default { @@ -441,20 +438,16 @@ impl Canvas { self.animation_frame_handler.on_animation_frame(f) } - pub(crate) fn on_touch_end(&mut self) { - self.on_touch_end = Some(self.common.add_transient_event("touchend", |_| {})); - } - pub fn request_fullscreen(&self) { - self.common.fullscreen_handler.request_fullscreen() + fullscreen::request_fullscreen(self.document(), self.raw()); } pub fn exit_fullscreen(&self) { - self.common.fullscreen_handler.exit_fullscreen() + fullscreen::exit_fullscreen(self.document(), self.raw()); } pub fn is_fullscreen(&self) -> bool { - self.common.fullscreen_handler.is_fullscreen() + fullscreen::is_fullscreen(self.document(), self.raw()) } pub fn request_animation_frame(&self) { @@ -505,10 +498,6 @@ impl Canvas { } } - pub(crate) fn transient_activation(&self) { - self.common.fullscreen_handler.transient_activation() - } - pub fn remove_listeners(&mut self) { self.on_touch_start = None; self.on_focus = None; @@ -522,7 +511,6 @@ impl Canvas { self.on_intersect = None; self.animation_frame_handler.cancel(); self.on_touch_end = None; - self.common.fullscreen_handler.cancel(); } } @@ -538,29 +526,6 @@ impl Common { { EventListenerHandle::new(self.raw.clone(), event_name, Closure::new(handler)) } - - // The difference between add_event and add_user_event is that the latter has a special meaning - // for browser security. A user event is a deliberate action by the user (like a mouse or key - // press) and is the only time things like a fullscreen request may be successfully completed.) - pub fn add_transient_event( - &self, - event_name: &'static str, - mut handler: F, - ) -> EventListenerHandle - where - E: 'static + AsRef + wasm_bindgen::convert::FromWasmAbi, - F: 'static + FnMut(E), - { - let fullscreen_handler = Rc::downgrade(&self.fullscreen_handler); - - self.add_event(event_name, move |event: E| { - handler(event); - - if let Some(fullscreen_handler) = Weak::upgrade(&fullscreen_handler) { - fullscreen_handler.transient_activation() - } - }) - } } impl Style { diff --git a/src/platform_impl/web/web_sys/fullscreen.rs b/src/platform_impl/web/web_sys/fullscreen.rs index 6ad1ae3e..70089879 100644 --- a/src/platform_impl/web/web_sys/fullscreen.rs +++ b/src/platform_impl/web/web_sys/fullscreen.rs @@ -1,6 +1,3 @@ -use std::cell::Cell; -use std::rc::Rc; - use js_sys::Promise; use once_cell::unsync::OnceCell; use wasm_bindgen::closure::Closure; @@ -8,138 +5,86 @@ use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::{JsCast, JsValue}; use web_sys::{Document, Element, HtmlCanvasElement}; -use super::EventListenerHandle; +pub fn request_fullscreen(document: &Document, canvas: &HtmlCanvasElement) { + if is_fullscreen(document, canvas) { + return; + } -thread_local! { - static FULLSCREEN_API_SUPPORT: OnceCell = OnceCell::new(); + #[wasm_bindgen] + extern "C" { + #[wasm_bindgen(extends = HtmlCanvasElement)] + type RequestFullscreen; + + #[wasm_bindgen(method, js_name = requestFullscreen)] + fn request_fullscreen(this: &RequestFullscreen) -> Promise; + + #[wasm_bindgen(method, js_name = webkitRequestFullscreen)] + fn webkit_request_fullscreen(this: &RequestFullscreen); + } + + let canvas: &RequestFullscreen = canvas.unchecked_ref(); + + if has_fullscreen_api_support(canvas) { + thread_local! { + static REJECT_HANDLER: Closure = Closure::new(|_| ()); + } + REJECT_HANDLER.with(|handler| { + let _ = canvas.request_fullscreen().catch(handler); + }); + } else { + canvas.webkit_request_fullscreen(); + } } -pub struct FullscreenHandler { - document: Document, - canvas: HtmlCanvasElement, - fullscreen_requested: Rc>, - _fullscreen_change: EventListenerHandle, +pub fn is_fullscreen(document: &Document, canvas: &HtmlCanvasElement) -> bool { + #[wasm_bindgen] + extern "C" { + type FullscreenElement; + + #[wasm_bindgen(method, getter, js_name = webkitFullscreenElement)] + fn webkit_fullscreen_element(this: &FullscreenElement) -> Option; + } + + let element = if has_fullscreen_api_support(canvas) { + #[allow(clippy::disallowed_methods)] + document.fullscreen_element() + } else { + let document: &FullscreenElement = document.unchecked_ref(); + document.webkit_fullscreen_element() + }; + + match element { + Some(element) => { + let canvas: &Element = canvas; + canvas == &element + } + None => false, + } } -impl FullscreenHandler { - pub fn new(document: Document, canvas: HtmlCanvasElement) -> Self { - let fullscreen_requested = Rc::new(Cell::new(false)); - let fullscreen_change = EventListenerHandle::new( - canvas.clone(), - if has_fullscreen_api_support(&canvas) { - "fullscreenchange" - } else { - "webkitfullscreenchange" - }, - Closure::new({ - let fullscreen_requested = fullscreen_requested.clone(); - move || { - // It doesn't matter if the canvas entered or exitted fullscreen mode, - // we don't want to request it again later. - fullscreen_requested.set(false); - } - }), - ); +pub fn exit_fullscreen(document: &Document, canvas: &HtmlCanvasElement) { + #[wasm_bindgen] + extern "C" { + type ExitFullscreen; - Self { - document, - canvas, - fullscreen_requested, - _fullscreen_change: fullscreen_change, - } + #[wasm_bindgen(method, js_name = webkitExitFullscreen)] + fn webkit_exit_fullscreen(this: &ExitFullscreen); } - fn internal_request_fullscreen(&self) { - #[wasm_bindgen] - extern "C" { - type RequestFullscreen; - - #[wasm_bindgen(method, js_name = requestFullscreen)] - fn request_fullscreen(this: &RequestFullscreen) -> Promise; - - #[wasm_bindgen(method, js_name = webkitRequestFullscreen)] - fn webkit_request_fullscreen(this: &RequestFullscreen); - } - - let canvas: &RequestFullscreen = self.canvas.unchecked_ref(); - - if has_fullscreen_api_support(&self.canvas) { - thread_local! { - static REJECT_HANDLER: Closure = Closure::new(|_| ()); - } - REJECT_HANDLER.with(|handler| { - let _ = canvas.request_fullscreen().catch(handler); - }); - } else { - canvas.webkit_request_fullscreen(); - } - } - - pub fn request_fullscreen(&self) { - if !self.is_fullscreen() { - self.internal_request_fullscreen(); - self.fullscreen_requested.set(true); - } - } - - pub fn transient_activation(&self) { - if self.fullscreen_requested.get() { - self.internal_request_fullscreen() - } - } - - pub fn is_fullscreen(&self) -> bool { - #[wasm_bindgen] - extern "C" { - type FullscreenElement; - - #[wasm_bindgen(method, getter, js_name = webkitFullscreenElement)] - fn webkit_fullscreen_element(this: &FullscreenElement) -> Option; - } - - let element = if has_fullscreen_api_support(&self.canvas) { - #[allow(clippy::disallowed_methods)] - self.document.fullscreen_element() - } else { - let document: &FullscreenElement = self.document.unchecked_ref(); - document.webkit_fullscreen_element() - }; - - match element { - Some(element) => { - let canvas: &Element = &self.canvas; - canvas == &element - } - None => false, - } - } - - pub fn exit_fullscreen(&self) { - #[wasm_bindgen] - extern "C" { - type ExitFullscreen; - - #[wasm_bindgen(method, js_name = webkitExitFullscreen)] - fn webkit_exit_fullscreen(this: &ExitFullscreen); - } - - if has_fullscreen_api_support(&self.canvas) { - #[allow(clippy::disallowed_methods)] - self.document.exit_fullscreen() - } else { - let document: &ExitFullscreen = self.document.unchecked_ref(); - document.webkit_exit_fullscreen() - } - - self.fullscreen_requested.set(false); - } - - pub fn cancel(&self) { - self.fullscreen_requested.set(false); + if has_fullscreen_api_support(canvas) { + #[allow(clippy::disallowed_methods)] + document.exit_fullscreen() + } else { + let document: &ExitFullscreen = document.unchecked_ref(); + document.webkit_exit_fullscreen() } } fn has_fullscreen_api_support(canvas: &HtmlCanvasElement) -> bool { + thread_local! { + static FULLSCREEN_API_SUPPORT: OnceCell = OnceCell::new(); + } + FULLSCREEN_API_SUPPORT.with(|support| { *support.get_or_init(|| { #[wasm_bindgen] diff --git a/src/platform_impl/web/web_sys/pointer.rs b/src/platform_impl/web/web_sys/pointer.rs index 4605d0ac..71f128a9 100644 --- a/src/platform_impl/web/web_sys/pointer.rs +++ b/src/platform_impl/web/web_sys/pointer.rs @@ -80,7 +80,7 @@ impl PointerHandler { T: 'static + FnMut(ModifiersState, i32, PhysicalPosition, Force), { let window = canvas_common.window.clone(); - self.on_pointer_release = Some(canvas_common.add_transient_event( + self.on_pointer_release = Some(canvas_common.add_event( "pointerup", move |event: PointerEvent| { let modifiers = event::mouse_modifiers(&event); @@ -118,7 +118,7 @@ impl PointerHandler { { let window = canvas_common.window.clone(); let canvas = canvas_common.raw.clone(); - self.on_pointer_press = Some(canvas_common.add_transient_event( + self.on_pointer_press = Some(canvas_common.add_event( "pointerdown", move |event: PointerEvent| { if prevent_default { diff --git a/src/window.rs b/src/window.rs index 5b2e66ed..7a697489 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1078,8 +1078,7 @@ impl Window { /// - **Wayland:** Does not support exclusive fullscreen mode and will no-op a request. /// - **Windows:** Screen saver is disabled in fullscreen mode. /// - **Android / Orbital:** Unsupported. - /// - **Web:** Does nothing without a [transient activation], but queues the request - /// for the next activation. + /// - **Web:** Does nothing without a [transient activation]. /// /// [transient activation]: https://developer.mozilla.org/en-US/docs/Glossary/Transient_activation #[inline]