aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Robinson <mrobinson@igalia.com>2025-04-09 21:41:53 +0200
committerGitHub <noreply@github.com>2025-04-09 19:41:53 +0000
commit2fe57cc2a2e5e7b982f7a32349117e2516724682 (patch)
treed8f75104771bcc8e83ae3adcc81f55f0e00ffc82
parent935db71183c9744455cf8e85262c3bed9b212984 (diff)
downloadservo-2fe57cc2a2e5e7b982f7a32349117e2516724682.tar.gz
servo-2fe57cc2a2e5e7b982f7a32349117e2516724682.zip
libservo: Move animation tracking from `WindowMethods` to delegates (#36400)
This changes removes animation tracking from the `WindowMethods` trait and moves it to `ServoDelegate` and `WebViewDelegate`. - Animation changes per-`WebView` are now triggered in the compositor only when the value is updated there, rather than right after ticking animations. - Both `WebView` and `Servo` now expose an `animation()` method, so tracking animation state actually becomes unecessary in many cases, such as that of desktop servoshell, which can just read the value when the event loop spins. Testing: No tests necessary as the API layer is still untested. Later, tests will be added for the `WebView` API and this can be tested then. Signed-off-by: Martin Robinson <mrobinson@igalia.com> Signed-off-by: Martin Robinson <mrobinson@igalia.com>
-rw-r--r--components/compositing/compositor.rs52
-rw-r--r--components/compositing/webview.rs48
-rw-r--r--components/compositing/windowing.rs14
-rw-r--r--components/servo/examples/winit_minimal.rs14
-rw-r--r--components/servo/lib.rs26
-rw-r--r--components/servo/servo_delegate.rs5
-rw-r--r--components/servo/webview.rs28
-rw-r--r--components/servo/webview_delegate.rs5
-rw-r--r--components/shared/compositing/lib.rs1
-rw-r--r--ports/servoshell/desktop/app.rs23
-rw-r--r--ports/servoshell/desktop/events_loop.rs2
-rw-r--r--ports/servoshell/desktop/headed_window.rs12
-rw-r--r--ports/servoshell/desktop/headless_window.rs12
-rw-r--r--ports/servoshell/desktop/window_trait.rs1
-rw-r--r--ports/servoshell/egl/app_state.rs35
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);
- }
}