diff --git a/CHANGELOG.md b/CHANGELOG.md index d8abaaea..2d64788e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,7 +76,7 @@ And please only add new entries to the top of this list, right below the `# Unre - On Web, use the correct canvas size when calculating the new size during scale factor change, instead of using the output bitmap size. - On Web, scale factor and dark mode detection are now more robust. -- On Web, fix the bfcache by not using the `beforeunload` event. +- On Web, fix the bfcache by not using the `beforeunload` event and map bfcache loading/unloading to `Suspended`/`Resumed` events. - On Web, fix scale factor resize suggestion always overwriting the canvas size. - On macOS, fix crash when dropping `Window`. - On Web, use `Window.requestIdleCallback()` for `ControlFlow::Poll` when available. diff --git a/Cargo.toml b/Cargo.toml index 92615add..fba184fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -148,6 +148,7 @@ features = [ 'KeyboardEvent', 'MediaQueryList', 'Node', + 'PageTransitionEvent', 'PointerEvent', 'ResizeObserver', 'ResizeObserverBoxOptions', diff --git a/src/event.rs b/src/event.rs index e2f94151..1a57bece 100644 --- a/src/event.rs +++ b/src/event.rs @@ -125,6 +125,18 @@ pub enum Event<'a, T: 'static> { /// [`applicationWillResignActive`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622950-applicationwillresignactive /// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle /// + /// ## Web + /// + /// On Web, the `Suspended` event is emitted in response to a [`pagehide`] event + /// with the property [`persisted`] being true, which means that the page is being + /// put in the [´bfcache`] (back/forward cache) - an in-memory cache that stores a + /// complete snapshot of a page (including the JavaScript heap) as the user is + /// navigating away. + /// + /// [`pagehide`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event + /// [`persisted`]: https://developer.mozilla.org/en-US/docs/Web/API/PageTransitionEvent/persisted + /// [`bfcache`]: https://web.dev/bfcache/ + /// /// [`Resumed`]: Self::Resumed Suspended, @@ -179,6 +191,18 @@ pub enum Event<'a, T: 'static> { /// [`applicationDidBecomeActive`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622956-applicationdidbecomeactive /// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle /// + /// ## Web + /// + /// On Web, the `Resumed` event is emitted in response to a [`pageshow`] event + /// with the property [`persisted`] being true, which means that the page is being + /// restored from the [´bfcache`] (back/forward cache) - an in-memory cache that + /// stores a complete snapshot of a page (including the JavaScript heap) as the + /// user is navigating away. + /// + /// [`pageshow`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pageshow_event + /// [`persisted`]: https://developer.mozilla.org/en-US/docs/Web/API/PageTransitionEvent/persisted + /// [`bfcache`]: https://web.dev/bfcache/ + /// /// [`Suspended`]: Self::Suspended Resumed, diff --git a/src/platform_impl/web/event_loop/runner.rs b/src/platform_impl/web/event_loop/runner.rs index df4c7fab..90a24f9a 100644 --- a/src/platform_impl/web/event_loop/runner.rs +++ b/src/platform_impl/web/event_loop/runner.rs @@ -18,7 +18,7 @@ use std::{ rc::{Rc, Weak}, }; use wasm_bindgen::prelude::Closure; -use web_sys::{KeyboardEvent, PointerEvent, WheelEvent}; +use web_sys::{KeyboardEvent, PageTransitionEvent, PointerEvent, WheelEvent}; use web_time::{Duration, Instant}; pub struct Shared(Rc>); @@ -41,7 +41,7 @@ pub struct Execution { all_canvases: RefCell>)>>, redraw_pending: RefCell>, destroy_pending: RefCell>, - unload_event_handle: RefCell>, + page_transition_event_handle: RefCell>, device_events: Cell, on_mouse_move: OnEventHandle, on_wheel: OnEventHandle, @@ -146,7 +146,7 @@ impl Shared { all_canvases: RefCell::new(Vec::new()), redraw_pending: RefCell::new(HashSet::new()), destroy_pending: RefCell::new(VecDeque::new()), - unload_event_handle: RefCell::new(None), + page_transition_event_handle: RefCell::new(None), device_events: Cell::default(), on_mouse_move: RefCell::new(None), on_wheel: RefCell::new(None), @@ -183,11 +183,27 @@ impl Shared { } self.init(); - let close_instance = self.clone(); - *self.0.unload_event_handle.borrow_mut() = - Some(backend::on_unload(self.window(), move || { - close_instance.handle_unload() - })); + *self.0.page_transition_event_handle.borrow_mut() = Some(backend::on_page_transition( + self.window(), + { + let runner = self.clone(); + move |event: PageTransitionEvent| { + if event.persisted() { + runner.send_event(Event::Resumed); + } + } + }, + { + let runner = self.clone(); + move |event: PageTransitionEvent| { + if event.persisted() { + runner.send_event(Event::Suspended); + } else { + runner.handle_unload(); + } + } + }, + )); let runner = self.clone(); let window = self.window().clone(); @@ -605,7 +621,7 @@ impl Shared { fn handle_loop_destroyed(&self, control: &mut ControlFlow) { self.handle_event(Event::LoopDestroyed, control); let all_canvases = std::mem::take(&mut *self.0.all_canvases.borrow_mut()); - *self.0.unload_event_handle.borrow_mut() = None; + *self.0.page_transition_event_handle.borrow_mut() = None; *self.0.on_mouse_move.borrow_mut() = None; *self.0.on_wheel.borrow_mut() = None; *self.0.on_mouse_press.borrow_mut() = None; diff --git a/src/platform_impl/web/web_sys/mod.rs b/src/platform_impl/web/web_sys/mod.rs index 367680d2..558bd1a2 100644 --- a/src/platform_impl/web/web_sys/mod.rs +++ b/src/platform_impl/web/web_sys/mod.rs @@ -16,7 +16,7 @@ use crate::dpi::LogicalSize; use crate::platform::web::WindowExtWebSys; use crate::window::Window; use wasm_bindgen::closure::Closure; -use web_sys::{CssStyleDeclaration, Element, HtmlCanvasElement}; +use web_sys::{CssStyleDeclaration, Element, HtmlCanvasElement, PageTransitionEvent}; pub fn throw(msg: &str) { wasm_bindgen::throw_str(msg); @@ -28,16 +28,24 @@ pub fn exit_fullscreen(window: &web_sys::Window) { document.exit_fullscreen(); } -pub struct UnloadEventHandle { - _listener: event_handle::EventListenerHandle, +pub struct PageTransitionEventHandle { + _show_listener: event_handle::EventListenerHandle, + _hide_listener: event_handle::EventListenerHandle, } -pub fn on_unload(window: &web_sys::Window, handler: impl FnMut() + 'static) -> UnloadEventHandle { - let closure = Closure::new(handler); +pub fn on_page_transition( + window: &web_sys::Window, + show_handler: impl FnMut(PageTransitionEvent) + 'static, + hide_handler: impl FnMut(PageTransitionEvent) + 'static, +) -> PageTransitionEventHandle { + let show_closure = Closure::new(show_handler); + let hide_closure = Closure::new(hide_handler); - let listener = event_handle::EventListenerHandle::new(window, "pagehide", closure); - UnloadEventHandle { - _listener: listener, + let show_listener = event_handle::EventListenerHandle::new(window, "pageshow", show_closure); + let hide_listener = event_handle::EventListenerHandle::new(window, "pagehide", hide_closure); + PageTransitionEventHandle { + _show_listener: show_listener, + _hide_listener: hide_listener, } }