diff options
-rw-r--r-- | components/compositing/compositor.rs | 52 | ||||
-rw-r--r-- | components/compositing/webview.rs | 48 | ||||
-rw-r--r-- | components/compositing/windowing.rs | 14 | ||||
-rw-r--r-- | components/servo/examples/winit_minimal.rs | 14 | ||||
-rw-r--r-- | components/servo/lib.rs | 26 | ||||
-rw-r--r-- | components/servo/servo_delegate.rs | 5 | ||||
-rw-r--r-- | components/servo/webview.rs | 28 | ||||
-rw-r--r-- | components/servo/webview_delegate.rs | 5 | ||||
-rw-r--r-- | components/shared/compositing/lib.rs | 1 | ||||
-rw-r--r-- | ports/servoshell/desktop/app.rs | 23 | ||||
-rw-r--r-- | ports/servoshell/desktop/events_loop.rs | 2 | ||||
-rw-r--r-- | ports/servoshell/desktop/headed_window.rs | 12 | ||||
-rw-r--r-- | ports/servoshell/desktop/headless_window.rs | 12 | ||||
-rw-r--r-- | ports/servoshell/desktop/window_trait.rs | 1 | ||||
-rw-r--r-- | ports/servoshell/egl/app_state.rs | 35 |
15 files changed, 153 insertions, 125 deletions
diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index d4c522aa05e..ccb28c5df56 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -58,7 +58,7 @@ use webrender_api::{ use crate::InitialCompositorState; use crate::webview::{UnknownWebView, WebView}; use crate::webview_manager::WebViewManager; -use crate::windowing::{self, WebRenderDebugOption, WindowMethods}; +use crate::windowing::{WebRenderDebugOption, WindowMethods}; #[derive(Debug, PartialEq)] enum UnableToComposite { @@ -258,22 +258,20 @@ impl PipelineDetails { self.animation_callbacks_running } - pub(crate) fn tick_animations(&self, compositor: &IOCompositor) -> bool { - let animation_callbacks_running = self.animation_callbacks_running; - let animations_running = self.animations_running; - if !animation_callbacks_running && !animations_running { - return false; - } + pub(crate) fn animating(&self) -> bool { + !self.throttled && (self.animation_callbacks_running || self.animations_running) + } - if self.throttled { - return false; + pub(crate) fn tick_animations(&self, compositor: &IOCompositor) { + if !self.animating() { + return; } let mut tick_type = AnimationTickType::empty(); - if animations_running { + if self.animations_running { tick_type.insert(AnimationTickType::CSS_ANIMATIONS_AND_TRANSITIONS); } - if animation_callbacks_running { + if self.animation_callbacks_running { tick_type.insert(AnimationTickType::REQUEST_ANIMATION_FRAME); } @@ -281,7 +279,6 @@ impl PipelineDetails { if let Err(e) = compositor.global.borrow().constellation_sender.send(msg) { warn!("Sending tick to constellation failed ({:?}).", e); } - true } } @@ -490,6 +487,17 @@ impl IOCompositor { } } + pub fn webxr_running(&self) -> bool { + #[cfg(feature = "webxr")] + { + self.global.borrow().webxr_main_thread.running() + } + #[cfg(not(feature = "webxr"))] + { + false + } + } + fn set_needs_repaint(&self, reason: RepaintReason) { let mut needs_repaint = self.needs_repaint.get(); needs_repaint.insert(reason); @@ -1313,23 +1321,9 @@ impl IOCompositor { } self.last_animation_tick = Instant::now(); - #[cfg(feature = "webxr")] - let webxr_running = self.global.borrow().webxr_main_thread.running(); - #[cfg(not(feature = "webxr"))] - let webxr_running = false; - - let any_webviews_animating = !self - .webviews - .iter() - .all(|webview| !webview.tick_all_animations(self)); - - let animation_state = if !any_webviews_animating && !webxr_running { - windowing::AnimationState::Idle - } else { - windowing::AnimationState::Animating - }; - - self.window.set_animation_state(animation_state); + for webview in self.webviews.iter() { + webview.tick_all_animations(self); + } } pub(crate) fn device_pixels_per_page_pixel(&self) -> Scale<f32, CSSPixel, DevicePixel> { diff --git a/components/compositing/webview.rs b/components/compositing/webview.rs index 9e8136f28ad..5d365f3b89b 100644 --- a/components/compositing/webview.rs +++ b/components/compositing/webview.rs @@ -217,37 +217,41 @@ impl WebView { }) } - /// Sets or unsets the animations-running flag for the given pipeline, and schedules a - /// recomposite if necessary. Returns true if the pipeline is throttled. + /// Sets or unsets the animations-running flag for the given pipeline. Returns true if + /// the pipeline is throttled. pub(crate) fn change_running_animations_state( &mut self, pipeline_id: PipelineId, animation_state: AnimationState, ) -> bool { - let pipeline_details = self.ensure_pipeline_details(pipeline_id); - match animation_state { - AnimationState::AnimationsPresent => { - pipeline_details.animations_running = true; - }, - AnimationState::AnimationCallbacksPresent => { - pipeline_details.animation_callbacks_running = true; - }, - AnimationState::NoAnimationsPresent => { - pipeline_details.animations_running = false; - }, - AnimationState::NoAnimationCallbacksPresent => { - pipeline_details.animation_callbacks_running = false; - }, - } - pipeline_details.throttled + let throttled = { + let pipeline_details = self.ensure_pipeline_details(pipeline_id); + match animation_state { + AnimationState::AnimationsPresent => { + pipeline_details.animations_running = true; + }, + AnimationState::AnimationCallbacksPresent => { + pipeline_details.animation_callbacks_running = true; + }, + AnimationState::NoAnimationsPresent => { + pipeline_details.animations_running = false; + }, + AnimationState::NoAnimationCallbacksPresent => { + pipeline_details.animation_callbacks_running = false; + }, + } + pipeline_details.throttled + }; + + let animating = self.pipelines.values().any(PipelineDetails::animating); + self.renderer_webview.set_animating(animating); + throttled } - pub(crate) fn tick_all_animations(&self, compositor: &IOCompositor) -> bool { - let mut ticked_any = false; + pub(crate) fn tick_all_animations(&self, compositor: &IOCompositor) { for pipeline_details in self.pipelines.values() { - ticked_any = pipeline_details.tick_animations(compositor) || ticked_any; + pipeline_details.tick_animations(compositor) } - ticked_any } pub(crate) fn tick_animations_for_pipeline( diff --git a/components/compositing/windowing.rs b/components/compositing/windowing.rs index a73e65f11d1..1ab30e73154 100644 --- a/components/compositing/windowing.rs +++ b/components/compositing/windowing.rs @@ -4,8 +4,6 @@ //! Abstract windowing methods. The concrete implementations of these can be found in `platform/`. -use std::fmt::Debug; - use embedder_traits::{EventLoopWaker, MouseButton}; use euclid::Scale; use net::protocols::ProtocolRegistry; @@ -27,12 +25,6 @@ pub enum WebRenderDebugOption { RenderTargetDebug, } -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum AnimationState { - Idle, - Animating, -} - // TODO: this trait assumes that the window is responsible // for creating the GL context, making it current, buffer // swapping, etc. Really that should all be done by surfman. @@ -40,12 +32,6 @@ pub trait WindowMethods { /// Get the HighDPI factor of the native window, the screen and the framebuffer. /// TODO(martin): Move this to `RendererWebView` when possible. fn hidpi_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel>; - /// Set whether the application is currently animating. - /// Typically, when animations are active, the window - /// will want to avoid blocking on UI events, and just - /// run the event loop at the vsync interval. - /// TODO(martin): Move this to `RendererWebView` when possible. - fn set_animation_state(&self, _state: AnimationState); } pub trait EmbedderMethods { diff --git a/components/servo/examples/winit_minimal.rs b/components/servo/examples/winit_minimal.rs index f19fd9763fe..1f6dbbe5c12 100644 --- a/components/servo/examples/winit_minimal.rs +++ b/components/servo/examples/winit_minimal.rs @@ -1,11 +1,11 @@ /* 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, RefCell}; +use std::cell::RefCell; use std::error::Error; use std::rc::Rc; -use compositing::windowing::{AnimationState, EmbedderMethods, WindowMethods}; +use compositing::windowing::{EmbedderMethods, WindowMethods}; use euclid::{Point2D, Scale, Size2D}; use servo::{RenderingContext, Servo, TouchEventType, WebView, WindowRenderingContext}; use servo_geometry::DeviceIndependentPixel; @@ -236,15 +236,11 @@ impl embedder_traits::EventLoopWaker for Waker { struct WindowDelegate { window: Window, - animation_state: Cell<AnimationState>, } impl WindowDelegate { fn new(window: Window) -> Self { - Self { - window, - animation_state: Cell::new(AnimationState::Idle), - } + Self { window } } } @@ -252,10 +248,6 @@ impl WindowMethods for WindowDelegate { fn hidpi_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel> { Scale::new(self.window.scale_factor() as f32) } - - fn set_animation_state(&self, state: compositing::windowing::AnimationState) { - self.animation_state.set(state); - } } pub fn winit_size_to_euclid_size<T>(size: PhysicalSize<T>) -> Size2D<T, DevicePixel> { diff --git a/components/servo/lib.rs b/components/servo/lib.rs index 3ef27875d3a..eea6ea01392 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -210,6 +210,8 @@ pub struct Servo { /// and deinitialization of the JS Engine. Multiprocess Servo instances have their /// own instance that exists in the content process instead. _js_engine_setup: Option<JSEngineSetup>, + /// Whether or not any WebView in this instance is animating or WebXR is enabled. + animating: Cell<bool>, } #[derive(Clone)] @@ -507,6 +509,7 @@ impl Servo { webviews: Default::default(), servo_errors: ServoErrorChannel::default(), _js_engine_setup: js_engine_setup, + animating: Cell::new(false), } } @@ -518,6 +521,15 @@ impl Servo { *self.delegate.borrow_mut() = delegate; } + /// Whether or not any [`WebView`] of this Servo instance has animating content, such as a CSS + /// animation or transition or is running `requestAnimationFrame` callbacks. In addition, this + /// returns true if WebXR content is running. This indicates that the embedding application + /// should be spinning the Servo event loop on regular intervals in order to trigger animation + /// updates. + pub fn animating(&self) -> bool { + self.animating.get() + } + /// **EXPERIMENTAL:** Intialize GL accelerated media playback. This currently only works on a limited number /// of platforms. This should be run *before* calling [`Servo::new`] and creating the first [`WebView`]. pub fn initialize_gl_accelerated_media(display: NativeDisplay, api: GlApi, context: GlContext) { @@ -555,6 +567,7 @@ impl Servo { self.compositor.borrow_mut().perform_updates(); self.send_new_frame_ready_messages(); + self.send_animating_changed_messages(); self.handle_delegate_errors(); self.clean_up_destroyed_webview_handles(); @@ -580,6 +593,19 @@ impl Servo { } } + fn send_animating_changed_messages(&self) { + let animating = self.compositor.borrow().webxr_running() || + self.webviews + .borrow() + .values() + .filter_map(WebView::from_weak_handle) + .any(|webview| webview.animating()); + if animating != self.animating.get() { + self.animating.set(animating); + self.delegate().notify_animating_changed(animating); + } + } + fn handle_delegate_errors(&self) { while let Some(error) = self.servo_errors.try_recv() { self.delegate().notify_error(self, error); diff --git a/components/servo/servo_delegate.rs b/components/servo/servo_delegate.rs index 8ec53a4790b..b119a962029 100644 --- a/components/servo/servo_delegate.rs +++ b/components/servo/servo_delegate.rs @@ -28,6 +28,11 @@ pub trait ServoDelegate { /// Request a DevTools connection from a DevTools client. Typically an embedder application /// will show a permissions prompt when this happens to confirm a connection is allowed. fn request_devtools_connection(&self, _servo: &Servo, _request: AllowOrDenyRequest) {} + /// Any [`WebView`] in this Servo instance has either started to animate or WebXR is + /// running. When a [`WebView`] is animating, it is up to the embedding application + /// ensure that `Servo::spin_event_loop` is called at regular intervals in order to + /// update the painted contents of the [`WebView`]. + fn notify_animating_changed(&self, _animating: bool) {} /// Triggered when Servo will load a web (HTTP/HTTPS) resource. The load may be /// intercepted and alternate contents can be loaded by the client by calling /// [`WebResourceLoad::intercept`]. If not handled, the load will continue as normal. diff --git a/components/servo/webview.rs b/components/servo/webview.rs index ed243d56421..92564753168 100644 --- a/components/servo/webview.rs +++ b/components/servo/webview.rs @@ -81,6 +81,7 @@ pub(crate) struct WebViewInner { page_title: Option<String>, favicon_url: Option<Url>, focused: bool, + animating: bool, cursor: Cursor, } @@ -110,6 +111,7 @@ impl WebView { page_title: None, favicon_url: None, focused: false, + animating: false, cursor: Cursor::Pointer, }))); @@ -265,6 +267,22 @@ impl WebView { .send(EmbedderToConstellationMessage::BlurWebView); } + /// Whether or not this [`WebView`] has animating content, such as a CSS animation or + /// transition or is running `requestAnimationFrame` callbacks. This indicates that the + /// embedding application should be spinning the Servo event loop on regular intervals + /// in order to trigger animation updates. + pub fn animating(self) -> bool { + self.inner().animating + } + + pub(crate) fn set_animating(self, new_value: bool) { + if self.inner().animating == new_value { + return; + } + self.inner_mut().animating = new_value; + self.delegate().notify_animating_changed(self, new_value); + } + pub fn rect(&self) -> DeviceRect { self.inner().rect } @@ -475,12 +493,18 @@ struct ServoRendererWebView { } impl RendererWebView for ServoRendererWebView { + fn id(&self) -> WebViewId { + self.id + } + fn screen_geometry(&self) -> Option<ScreenGeometry> { let webview = WebView::from_weak_handle(&self.weak_handle)?; webview.delegate().screen_geometry(webview) } - fn id(&self) -> WebViewId { - self.id + fn set_animating(&self, new_value: bool) { + if let Some(webview) = WebView::from_weak_handle(&self.weak_handle) { + webview.set_animating(new_value); + } } } diff --git a/components/servo/webview_delegate.rs b/components/servo/webview_delegate.rs index b631eae7404..ac3dc7a5152 100644 --- a/components/servo/webview_delegate.rs +++ b/components/servo/webview_delegate.rs @@ -375,6 +375,11 @@ pub trait WebViewDelegate { /// This [`WebView`] has either become focused or lost focus. Whether or not the /// [`WebView`] is focused can be accessed via [`WebView::focused`]. fn notify_focus_changed(&self, _webview: WebView, _focused: bool) {} + /// This [`WebView`] has either started to animate or stopped animating. When a + /// [`WebView`] is animating, it is up to the embedding application ensure that + /// `Servo::spin_event_loop` is called at regular intervals in order to update the + /// painted contents of the [`WebView`]. + fn notify_animating_changed(&self, _webview: WebView, _animating: bool) {} /// The `LoadStatus` of the currently loading or loaded page in this [`WebView`] has changed. The new /// status can accessed via [`WebView::load_status`]. fn notify_load_status_changed(&self, _webview: WebView, _status: LoadStatus) {} diff --git a/components/shared/compositing/lib.rs b/components/shared/compositing/lib.rs index c231960ef50..baf59e53118 100644 --- a/components/shared/compositing/lib.rs +++ b/components/shared/compositing/lib.rs @@ -590,4 +590,5 @@ impl From<SerializableImageData> for ImageData { pub trait RendererWebView { fn id(&self) -> WebViewId; fn screen_geometry(&self) -> Option<ScreenGeometry>; + fn set_animating(&self, new_value: bool); } diff --git a/ports/servoshell/desktop/app.rs b/ports/servoshell/desktop/app.rs index 047197a60a8..657228e9d44 100644 --- a/ports/servoshell/desktop/app.rs +++ b/ports/servoshell/desktop/app.rs @@ -13,7 +13,7 @@ use std::{env, fs}; use euclid::Scale; use log::{info, trace, warn}; -use servo::compositing::windowing::{AnimationState, WindowMethods}; +use servo::compositing::windowing::WindowMethods; use servo::config::opts::Opts; use servo::config::prefs::Preferences; use servo::servo_config::pref; @@ -146,9 +146,6 @@ impl App { fn hidpi_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel> { self.0.hidpi_factor() } - fn set_animation_state(&self, state: AnimationState) { - self.0.set_animation_state(state); - } } let mut user_content_manager = UserContentManager::new(); @@ -183,8 +180,12 @@ impl App { self.state = AppState::Running(running_state); } - pub fn is_animating(&self) -> bool { - self.windows.iter().any(|(_, window)| window.is_animating()) + pub(crate) fn animating(&self) -> bool { + match self.state { + AppState::Initializing => false, + AppState::Running(ref running_app_state) => running_app_state.servo().animating(), + AppState::ShuttingDown => false, + } } /// Handle events with winit contexts @@ -401,10 +402,8 @@ impl ApplicationHandler<WakerEvent> for App { window.handle_winit_event(state.clone(), event); } - let animating = self.is_animating(); - // Block until the window gets an event - if !animating || self.suspended.get() { + if !self.animating() || self.suspended.get() { event_loop.set_control_flow(ControlFlow::Wait); } else { event_loop.set_control_flow(ControlFlow::Poll); @@ -427,7 +426,7 @@ impl ApplicationHandler<WakerEvent> for App { ); self.t = now; - if !matches!(self.state, AppState::Running(_)) { + if !matches!(self.state, AppState::Running(..)) { return; }; let Some(window) = self.windows.values().next() else { @@ -435,10 +434,8 @@ impl ApplicationHandler<WakerEvent> for App { }; let window = window.clone(); - let animating = self.is_animating(); - // Block until the window gets an event - if !animating || self.suspended.get() { + if !self.animating() || self.suspended.get() { event_loop.set_control_flow(ControlFlow::Wait); } else { event_loop.set_control_flow(ControlFlow::Poll); diff --git a/ports/servoshell/desktop/events_loop.rs b/ports/servoshell/desktop/events_loop.rs index ffe40eb8cb9..e47257d4f23 100644 --- a/ports/servoshell/desktop/events_loop.rs +++ b/ports/servoshell/desktop/events_loop.rs @@ -96,7 +96,7 @@ impl EventsLoop { if !app.handle_events_with_headless() { break; } - if !app.is_animating() { + if !app.animating() { *flag.lock().unwrap() = false; } } diff --git a/ports/servoshell/desktop/headed_window.rs b/ports/servoshell/desktop/headed_window.rs index f7986001c4d..d028d95cb73 100644 --- a/ports/servoshell/desktop/headed_window.rs +++ b/ports/servoshell/desktop/headed_window.rs @@ -14,7 +14,7 @@ use euclid::{Angle, Length, Point2D, Rotation3D, Scale, Size2D, UnknownUnit, Vec use keyboard_types::{Modifiers, ShortcutMatcher}; use log::{debug, info}; use raw_window_handle::{HasDisplayHandle, HasWindowHandle, RawWindowHandle}; -use servo::compositing::windowing::{AnimationState, WebRenderDebugOption, WindowMethods}; +use servo::compositing::windowing::{WebRenderDebugOption, WindowMethods}; use servo::servo_config::pref; use servo::servo_geometry::DeviceIndependentPixel; use servo::webrender_api::ScrollLocation; @@ -62,7 +62,6 @@ pub struct Window { /// A map of winit's key codes to key values that are interpreted from /// winit's ReceivedChar events. keys_down: RefCell<HashMap<LogicalKey, Key>>, - animation_state: Cell<AnimationState>, fullscreen: Cell<bool>, device_pixel_ratio_override: Option<f32>, xr_window_poses: RefCell<Vec<Rc<XRWindowPose>>>, @@ -151,7 +150,6 @@ impl Window { webview_relative_mouse_point: Cell::new(Point2D::zero()), last_pressed: Cell::new(None), keys_down: RefCell::new(HashMap::new()), - animation_state: Cell::new(AnimationState::Idle), fullscreen: Cell::new(false), inner_size: Cell::new(inner_size), monitor, @@ -570,10 +568,6 @@ impl WindowPortsMethods for Window { self.winit_window.set_cursor_visible(true); } - fn is_animating(&self) -> bool { - self.animation_state.get() == AnimationState::Animating - } - fn id(&self) -> winit::window::WindowId { self.winit_window.id() } @@ -772,10 +766,6 @@ impl WindowMethods for Window { self.device_pixel_ratio_override() .unwrap_or_else(|| self.device_hidpi_factor()) } - - fn set_animation_state(&self, state: AnimationState) { - self.animation_state.set(state); - } } fn winit_phase_to_touch_event_type(phase: TouchPhase) -> TouchEventType { diff --git a/ports/servoshell/desktop/headless_window.rs b/ports/servoshell/desktop/headless_window.rs index 76b846b3ebf..4ab202f1a56 100644 --- a/ports/servoshell/desktop/headless_window.rs +++ b/ports/servoshell/desktop/headless_window.rs @@ -9,7 +9,7 @@ use std::rc::Rc; use euclid::num::Zero; use euclid::{Length, Scale, Size2D}; -use servo::compositing::windowing::{AnimationState, WindowMethods}; +use servo::compositing::windowing::WindowMethods; use servo::servo_geometry::DeviceIndependentPixel; use servo::webrender_api::units::{DeviceIntSize, DevicePixel}; use servo::{RenderingContext, ScreenGeometry, SoftwareRenderingContext}; @@ -20,7 +20,6 @@ use crate::desktop::window_trait::WindowPortsMethods; use crate::prefs::ServoShellPreferences; pub struct Window { - animation_state: Cell<AnimationState>, fullscreen: Cell<bool>, device_pixel_ratio_override: Option<Scale<f32, DeviceIndependentPixel, DevicePixel>>, inner_size: Cell<DeviceIntSize>, @@ -50,7 +49,6 @@ impl Window { }); let window = Window { - animation_state: Cell::new(AnimationState::Idle), fullscreen: Cell::new(false), device_pixel_ratio_override, inner_size: Cell::new(inner_size), @@ -120,10 +118,6 @@ impl WindowPortsMethods for Window { self.fullscreen.get() } - fn is_animating(&self) -> bool { - self.animation_state.get() == AnimationState::Animating - } - fn handle_winit_event(&self, _: Rc<RunningAppState>, _: winit::event::WindowEvent) { // Not expecting any winit events. } @@ -157,8 +151,4 @@ impl WindowMethods for Window { self.device_pixel_ratio_override() .unwrap_or_else(|| self.device_hidpi_factor()) } - - fn set_animation_state(&self, state: AnimationState) { - self.animation_state.set(state); - } } diff --git a/ports/servoshell/desktop/window_trait.rs b/ports/servoshell/desktop/window_trait.rs index 62e31c73045..824d50f9c9c 100644 --- a/ports/servoshell/desktop/window_trait.rs +++ b/ports/servoshell/desktop/window_trait.rs @@ -28,7 +28,6 @@ pub trait WindowPortsMethods: WindowMethods { fn page_height(&self) -> f32; fn get_fullscreen(&self) -> bool; fn handle_winit_event(&self, state: Rc<RunningAppState>, event: winit::event::WindowEvent); - fn is_animating(&self) -> bool; fn set_title(&self, _title: &str) {} /// Request a new inner size for the window, not including external decorations. fn request_resize(&self, webview: &WebView, inner_size: DeviceIntSize) diff --git a/ports/servoshell/egl/app_state.rs b/ports/servoshell/egl/app_state.rs index ac747c8cf0e..faa91b4f23a 100644 --- a/ports/servoshell/egl/app_state.rs +++ b/ports/servoshell/egl/app_state.rs @@ -1,7 +1,7 @@ /* 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::{Ref, RefCell, RefMut}; +use std::cell::{Cell, Ref, RefCell, RefMut}; use std::collections::HashMap; use std::rc::Rc; @@ -11,7 +11,7 @@ use keyboard_types::{CompositionEvent, CompositionState}; use log::{debug, error, info, warn}; use raw_window_handle::{RawWindowHandle, WindowHandle}; use servo::base::id::WebViewId; -use servo::compositing::windowing::{AnimationState, EmbedderMethods, WindowMethods}; +use servo::compositing::windowing::{EmbedderMethods, WindowMethods}; use servo::euclid::{Point2D, Rect, Scale, Size2D, Vector2D}; use servo::servo_geometry::DeviceIndependentPixel; use servo::webrender_api::ScrollLocation; @@ -86,9 +86,16 @@ struct RunningAppStateInner { focused_webview_id: Option<WebViewId>, context_menu_sender: Option<IpcSender<ContextMenuResult>>, + + /// Whether or not the animation state has changed. This is used to trigger + /// host callbacks indicating that animation state has changed. + animating_state_changed: Rc<Cell<bool>>, +} + +struct ServoShellServoDelegate { + animating_state_changed: Rc<Cell<bool>>, } -struct ServoShellServoDelegate; impl ServoDelegate for ServoShellServoDelegate { fn notify_devtools_server_started(&self, _servo: &Servo, port: u16, _token: String) { info!("Devtools Server running on port {port}"); @@ -101,6 +108,10 @@ impl ServoDelegate for ServoShellServoDelegate { fn notify_error(&self, _servo: &Servo, error: ServoError) { error!("Saw Servo error: {error:?}!"); } + + fn notify_animating_changed(&self, _animating: bool) { + self.animating_state_changed.set(true); + } } impl WebViewDelegate for RunningAppState { @@ -263,7 +274,10 @@ impl RunningAppState { .or_else(|| Url::parse("about:blank").ok()) .unwrap(); - servo.set_delegate(Rc::new(ServoShellServoDelegate)); + let animating_state_changed = Rc::new(Cell::new(false)); + servo.set_delegate(Rc::new(ServoShellServoDelegate { + animating_state_changed: animating_state_changed.clone(), + })); let app_state = Rc::new(Self { rendering_context, @@ -276,6 +290,7 @@ impl RunningAppState { webviews: Default::default(), creation_order: vec![], focused_webview_id: None, + animating_state_changed, }), }); @@ -345,6 +360,12 @@ impl RunningAppState { if !should_continue { self.callbacks.host_callbacks.on_shutdown_complete(); } + if self.inner().animating_state_changed.get() { + self.inner().animating_state_changed.set(false); + self.callbacks + .host_callbacks + .on_animating_changed(self.servo.animating()); + } } /// Load an URL. @@ -684,10 +705,4 @@ impl WindowMethods for ServoWindowCallbacks { fn hidpi_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel> { self.hidpi_factor } - - fn set_animation_state(&self, state: AnimationState) { - debug!("WindowMethods::set_animation_state: {:?}", state); - self.host_callbacks - .on_animating_changed(state == AnimationState::Animating); - } } |