diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 9247187afc1..538d63b9405 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -30,7 +30,6 @@ use embedder_traits::{ TouchEventType, UntrustedNodeAddress, ViewportDetails, WheelDelta, WheelEvent, WheelMode, }; use euclid::{Point2D, Rect, Scale, Size2D, Transform3D, Vector2D}; -use fnv::FnvHashMap; use ipc_channel::ipc::{self, IpcSharedMemory}; use libc::c_void; use log::{debug, info, trace, warn}; @@ -48,10 +47,10 @@ use webrender_api::units::{ }; use webrender_api::{ self, BuiltDisplayList, DirtyRect, DisplayListPayload, DocumentId, Epoch as WebRenderEpoch, - ExternalScrollId, FontInstanceFlags, FontInstanceKey, FontInstanceOptions, FontKey, - HitTestFlags, PipelineId as WebRenderPipelineId, PropertyBinding, ReferenceFrameKind, - RenderReasons, SampledScrollOffset, ScrollLocation, SpaceAndClipInfo, SpatialId, - SpatialTreeItemKey, TransformStyle, + FontInstanceFlags, FontInstanceKey, FontInstanceOptions, FontKey, HitTestFlags, + PipelineId as WebRenderPipelineId, PropertyBinding, ReferenceFrameKind, RenderReasons, + SampledScrollOffset, ScrollLocation, SpaceAndClipInfo, SpatialId, SpatialTreeItemKey, + TransformStyle, }; use crate::InitialCompositorState; @@ -260,26 +259,9 @@ impl PipelineDetails { } fn install_new_scroll_tree(&mut self, new_scroll_tree: ScrollTree) { - let old_scroll_offsets: FnvHashMap = self - .scroll_tree - .nodes - .drain(..) - .filter_map(|node| match (node.external_id(), node.offset()) { - (Some(external_id), Some(offset)) => Some((external_id, offset)), - _ => None, - }) - .collect(); - + let old_scroll_offsets = self.scroll_tree.scroll_offsets(); self.scroll_tree = new_scroll_tree; - for node in self.scroll_tree.nodes.iter_mut() { - match node.external_id() { - Some(external_id) => match old_scroll_offsets.get(&external_id) { - Some(new_offset) => node.set_offset(*new_offset), - None => continue, - }, - _ => continue, - }; - } + self.scroll_tree.set_all_scroll_offsets(&old_scroll_offsets); } } @@ -728,7 +710,7 @@ impl IOCompositor { self.global.borrow_mut().send_transaction(txn); }, - CompositorMsg::SendScrollNode(webview_id, pipeline_id, point, external_scroll_id) => { + CompositorMsg::SendScrollNode(webview_id, pipeline_id, offset, external_scroll_id) => { let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else { return; }; @@ -739,15 +721,17 @@ impl IOCompositor { return; }; - let offset = LayoutVector2D::new(point.x, point.y); let Some(offset) = pipeline_details .scroll_tree - .set_scroll_offsets_for_node_with_external_scroll_id( + .set_scroll_offset_for_node_with_external_scroll_id( external_scroll_id, - -offset, + offset, ScrollType::Script, ) else { + // The renderer should be fully up-to-date with script at this point and script + // should never try to scroll to an invalid location. + warn!("Could not scroll node with id: {external_scroll_id:?}"); return; }; diff --git a/components/compositing/webview_renderer.rs b/components/compositing/webview_renderer.rs index c1dd04ca011..448117ffc34 100644 --- a/components/compositing/webview_renderer.rs +++ b/components/compositing/webview_renderer.rs @@ -13,7 +13,7 @@ use compositing_traits::viewport_description::{ DEFAULT_ZOOM, MAX_ZOOM, MIN_ZOOM, ViewportDescription, }; use compositing_traits::{SendableFrameTree, WebViewTrait}; -use constellation_traits::{EmbedderToConstellationMessage, ScrollState, WindowSizeType}; +use constellation_traits::{EmbedderToConstellationMessage, WindowSizeType}; use embedder_traits::{ AnimationState, CompositorHitTestResult, InputEvent, MouseButton, MouseButtonAction, MouseButtonEvent, MouseMoveEvent, ShutdownState, TouchEvent, TouchEventResult, TouchEventType, @@ -204,18 +204,18 @@ impl WebViewRenderer { return; }; - let mut scroll_states = Vec::new(); - details.scroll_tree.nodes.iter().for_each(|node| { - if let (Some(scroll_id), Some(scroll_offset)) = (node.external_id(), node.offset()) { - scroll_states.push(ScrollState { - scroll_id, - scroll_offset, - }); - } - }); + let scroll_offsets = details.scroll_tree.scroll_offsets(); + + // This might be true if we have not received a display list from the layout + // associated with this pipeline yet. In that case, the layout is not ready to + // receive scroll offsets anyway, so just save time and prevent other issues by + // not sending them. + if scroll_offsets.is_empty() { + return; + } let _ = self.global.borrow().constellation_sender.send( - EmbedderToConstellationMessage::SetScrollStates(pipeline_id, scroll_states), + EmbedderToConstellationMessage::SetScrollStates(pipeline_id, scroll_offsets), ); } diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index d1a0f5c050d..8013c04d689 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -116,9 +116,8 @@ use constellation_traits::{ EmbedderToConstellationMessage, IFrameLoadInfo, IFrameLoadInfoWithData, IFrameSandboxState, IFrameSizeMsg, Job, LoadData, LoadOrigin, LogEntry, MessagePortMsg, NavigationHistoryBehavior, PaintMetricEvent, PortMessageTask, PortTransferInfo, SWManagerMsg, SWManagerSenders, - ScriptToConstellationChan, ScriptToConstellationMessage, ScrollState, - ServiceWorkerManagerFactory, ServiceWorkerMsg, StructuredSerializedData, TraversalDirection, - WindowSizeType, + ScriptToConstellationChan, ScriptToConstellationMessage, ServiceWorkerManagerFactory, + ServiceWorkerMsg, StructuredSerializedData, TraversalDirection, WindowSizeType, }; use crossbeam_channel::{Receiver, Select, Sender, unbounded}; use devtools_traits::{ @@ -167,7 +166,8 @@ use webgpu_traits::{WebGPU, WebGPURequest}; #[cfg(feature = "webgpu")] use webrender::RenderApi; use webrender::RenderApiSender; -use webrender_api::{DocumentId, ImageKey}; +use webrender_api::units::LayoutVector2D; +use webrender_api::{DocumentId, ExternalScrollId, ImageKey}; use crate::browsingcontext::{ AllBrowsingContextsIterator, BrowsingContext, FullyActiveBrowsingContextsIterator, @@ -6051,7 +6051,11 @@ where feature = "tracing", tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace") )] - fn handle_set_scroll_states(&self, pipeline_id: PipelineId, scroll_states: Vec) { + fn handle_set_scroll_states( + &self, + pipeline_id: PipelineId, + scroll_states: HashMap, + ) { let Some(pipeline) = self.pipelines.get(&pipeline_id) else { warn!("Discarding scroll offset update for unknown pipeline"); return; diff --git a/components/layout/layout_impl.rs b/components/layout/layout_impl.rs index 5eb88a218e8..77f5a70582e 100644 --- a/components/layout/layout_impl.rs +++ b/components/layout/layout_impl.rs @@ -15,16 +15,16 @@ use app_units::Au; use base::Epoch; use base::id::{PipelineId, WebViewId}; use compositing_traits::CrossProcessCompositorApi; -use constellation_traits::ScrollState; +use compositing_traits::display_list::ScrollType; use embedder_traits::{Theme, UntrustedNodeAddress, ViewportDetails}; use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect}; -use euclid::{Point2D, Scale, Size2D, Vector2D}; +use euclid::{Point2D, Scale, Size2D}; use fnv::FnvHashMap; use fonts::{FontContext, FontContextWebFontMethods}; use fonts_traits::StylesheetWebFontLoadFinishedCallback; use fxhash::FxHashMap; use ipc_channel::ipc::IpcSender; -use log::{debug, error}; +use log::{debug, error, warn}; use malloc_size_of::{MallocConditionalSizeOf, MallocSizeOf, MallocSizeOfOps}; use net_traits::image_cache::{ImageCache, UsePlaceholder}; use parking_lot::{Mutex, RwLock}; @@ -74,7 +74,7 @@ use style::{Zero, driver}; use style_traits::{CSSPixel, SpeculativePainter}; use stylo_atoms::Atom; use url::Url; -use webrender_api::units::{DevicePixel, DevicePoint, LayoutPixel, LayoutPoint, LayoutSize}; +use webrender_api::units::{DevicePixel, DevicePoint, LayoutSize, LayoutVector2D}; use webrender_api::{ExternalScrollId, HitTestFlags}; use crate::context::{CachedImageOrError, LayoutContext}; @@ -149,6 +149,12 @@ pub struct LayoutThread { /// layout trees remain the same. need_new_display_list: Cell, + /// Whether or not the existing stacking context tree is dirty and needs to be + /// rebuilt. This happens after a relayout or overflow update. The reason that we + /// don't simply clear the stacking context tree when it becomes dirty is that we need + /// to preserve scroll offsets from the old tree to the new one. + need_new_stacking_context_tree: Cell, + /// The box tree. box_tree: RefCell>>, @@ -161,9 +167,6 @@ pub struct LayoutThread { /// A counter for epoch messages epoch: Cell, - /// Scroll offsets of nodes that scroll. - scroll_offsets: RefCell>>, - // A cache that maps image resources specified in CSS (e.g as the `url()` value // for `background-image` or `content` properties) to either the final resolved // image data, or an error if the image cache failed to load/decode the image. @@ -485,11 +488,20 @@ impl Layout for LayoutThread { ) { } - fn set_scroll_offsets(&mut self, scroll_states: &[ScrollState]) { - *self.scroll_offsets.borrow_mut() = scroll_states - .iter() - .map(|scroll_state| (scroll_state.scroll_id, scroll_state.scroll_offset)) - .collect(); + fn set_scroll_offsets_from_renderer( + &mut self, + scroll_states: &HashMap, + ) { + let mut stacking_context_tree = self.stacking_context_tree.borrow_mut(); + let Some(stacking_context_tree) = stacking_context_tree.as_mut() else { + warn!("Received scroll offsets before finishing layout."); + return; + }; + + stacking_context_tree + .compositor_info + .scroll_tree + .set_all_scroll_offsets(scroll_states); } } @@ -533,13 +545,13 @@ impl LayoutThread { have_added_user_agent_stylesheets: false, have_ever_generated_display_list: Cell::new(false), need_new_display_list: Cell::new(false), + need_new_stacking_context_tree: Cell::new(false), box_tree: Default::default(), fragment_tree: Default::default(), stacking_context_tree: Default::default(), // Epoch starts at 1 because of the initial display list for epoch 0 that we send to WR epoch: Cell::new(Epoch(1)), compositor_api: config.compositor_api, - scroll_offsets: Default::default(), stylist: Stylist::new(device, QuirksMode::NoQuirks), resolved_images_cache: Default::default(), debug: opts::get().debug.clone(), @@ -674,8 +686,9 @@ impl LayoutThread { let built_display_list = self.build_display_list(&reflow_request, damage, &mut layout_context); - if let ReflowGoal::UpdateScrollNode(scroll_state) = reflow_request.reflow_goal { - self.update_scroll_node_state(&scroll_state); + if let ReflowGoal::UpdateScrollNode(external_scroll_id, offset) = reflow_request.reflow_goal + { + self.set_scroll_offset_from_script(external_scroll_id, offset); } let pending_images = std::mem::take(&mut *layout_context.pending_images.lock()); @@ -828,12 +841,10 @@ impl LayoutThread { *self.fragment_tree.borrow_mut() = Some(fragment_tree); - // The FragmentTree has been updated, so any existing StackingContext tree that layout - // had is now out of date and should be rebuilt. - *self.stacking_context_tree.borrow_mut() = None; - - // Force display list generation as layout has changed. + // Changes to layout require us to generate a new stacking context tree and display + // list the next time one is requested. self.need_new_display_list.set(true); + self.need_new_stacking_context_tree.set(true); if self.debug.dump_style_tree { println!( @@ -866,6 +877,11 @@ impl LayoutThread { fragment_tree.print(); } } + + // Changes to overflow require us to generate a new stacking context tree and + // display list the next time one is requested. + self.need_new_display_list.set(true); + self.need_new_stacking_context_tree.set(true); } fn build_stacking_context_tree(&self, reflow_request: &ReflowRequest, damage: RestyleDamage) { @@ -878,7 +894,7 @@ impl LayoutThread { return; }; if !damage.contains(RestyleDamage::REBUILD_STACKING_CONTEXT) && - self.stacking_context_tree.borrow().is_some() + !self.need_new_stacking_context_tree.get() { return; } @@ -889,19 +905,39 @@ impl LayoutThread { viewport_size.height.to_f32_px(), ); + let mut stacking_context_tree = self.stacking_context_tree.borrow_mut(); + let old_scroll_offsets = stacking_context_tree + .as_ref() + .map(|tree| tree.compositor_info.scroll_tree.scroll_offsets()); + // Build the StackingContextTree. This turns the `FragmentTree` into a // tree of fragments in CSS painting order and also creates all // applicable spatial and clip nodes. - *self.stacking_context_tree.borrow_mut() = Some(StackingContextTree::new( + let mut new_stacking_context_tree = StackingContextTree::new( fragment_tree, viewport_size, self.id.into(), !self.have_ever_generated_display_list.get(), &self.debug, - )); + ); + + // When a new StackingContextTree is built, it contains a freshly built + // ScrollTree. We want to preserve any existing scroll offsets in that tree, + // adjusted by any new scroll constraints. + if let Some(old_scroll_offsets) = old_scroll_offsets { + new_stacking_context_tree + .compositor_info + .scroll_tree + .set_all_scroll_offsets(&old_scroll_offsets); + } + + *stacking_context_tree = Some(new_stacking_context_tree); // Force display list generation as layout has changed. self.need_new_display_list.set(true); + + // The stacking context tree is up-to-date again. + self.need_new_stacking_context_tree.set(false); } /// Build the display list for the current layout and send it to the renderer. If no display @@ -959,17 +995,32 @@ impl LayoutThread { true } - fn update_scroll_node_state(&self, state: &ScrollState) { - self.scroll_offsets - .borrow_mut() - .insert(state.scroll_id, state.scroll_offset); - let point = Point2D::new(-state.scroll_offset.x, -state.scroll_offset.y); - self.compositor_api.send_scroll_node( - self.webview_id, - self.id.into(), - LayoutPoint::from_untyped(point), - state.scroll_id, - ); + fn set_scroll_offset_from_script( + &self, + external_scroll_id: ExternalScrollId, + offset: LayoutVector2D, + ) { + let mut stacking_context_tree = self.stacking_context_tree.borrow_mut(); + let Some(stacking_context_tree) = stacking_context_tree.as_mut() else { + return; + }; + + if let Some(offset) = stacking_context_tree + .compositor_info + .scroll_tree + .set_scroll_offset_for_node_with_external_scroll_id( + external_scroll_id, + offset, + ScrollType::Script, + ) + { + self.compositor_api.send_scroll_node( + self.webview_id, + self.id.into(), + offset, + external_scroll_id, + ); + } } /// Returns profiling information which is passed to the time profiler. diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index b34da3f06d2..ebf0891c0e1 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -24,7 +24,7 @@ use canvas_traits::webgl::WebGLChan; use compositing_traits::CrossProcessCompositorApi; use constellation_traits::{ DocumentState, LoadData, LoadOrigin, NavigationHistoryBehavior, ScriptToConstellationChan, - ScriptToConstellationMessage, ScrollState, StructuredSerializedData, WindowSizeType, + ScriptToConstellationMessage, StructuredSerializedData, WindowSizeType, }; use crossbeam_channel::{Sender, unbounded}; use cssparser::SourceLocation; @@ -2097,10 +2097,7 @@ impl Window { // TODO(mrobinson, #18709): Add smooth scrolling support to WebRender so that we can // properly process ScrollBehavior here. self.reflow( - ReflowGoal::UpdateScrollNode(ScrollState { - scroll_id, - scroll_offset: Vector2D::new(-x, -y), - }), + ReflowGoal::UpdateScrollNode(scroll_id, Vector2D::new(-x, -y)), can_gc, ); } @@ -3234,7 +3231,7 @@ fn should_move_clip_rect(clip_rect: UntypedRect, new_viewport: UntypedRect "\tFull", - ReflowGoal::UpdateScrollNode(_) => "\tUpdateScrollNode", + ReflowGoal::UpdateScrollNode(..) => "\tUpdateScrollNode", ReflowGoal::LayoutQuery(ref query_msg) => match *query_msg { QueryMsg::ContentBox => "\tContentBoxQuery", QueryMsg::ContentBoxes => "\tContentBoxesQuery", diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index d2a0dea2c6a..580c37954e0 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -39,7 +39,7 @@ use chrono::{DateTime, Local}; use compositing_traits::CrossProcessCompositorApi; use constellation_traits::{ JsEvalResult, LoadData, LoadOrigin, NavigationHistoryBehavior, ScriptToConstellationChan, - ScriptToConstellationMessage, ScrollState, StructuredSerializedData, WindowSizeType, + ScriptToConstellationMessage, StructuredSerializedData, WindowSizeType, }; use content_security_policy::{self as csp}; use crossbeam_channel::unbounded; @@ -99,7 +99,8 @@ use timers::{TimerEventRequest, TimerId, TimerScheduler}; use url::Position; #[cfg(feature = "webgpu")] use webgpu_traits::{WebGPUDevice, WebGPUMsg}; -use webrender_api::units::DevicePixel; +use webrender_api::ExternalScrollId; +use webrender_api::units::{DevicePixel, LayoutVector2D}; use crate::document_collection::DocumentCollection; use crate::document_loader::DocumentLoader; @@ -2010,7 +2011,11 @@ impl ScriptThread { } } - fn handle_set_scroll_states(&self, pipeline_id: PipelineId, scroll_states: Vec) { + fn handle_set_scroll_states( + &self, + pipeline_id: PipelineId, + scroll_states: HashMap, + ) { let Some(window) = self.documents.borrow().find_window(pipeline_id) else { warn!("Received scroll states for closed pipeline {pipeline_id}"); return; @@ -2020,16 +2025,15 @@ impl ScriptThread { ScriptThreadEventCategory::SetScrollState, Some(pipeline_id), || { - window.layout_mut().set_scroll_offsets(&scroll_states); + window + .layout_mut() + .set_scroll_offsets_from_renderer(&scroll_states); let mut scroll_offsets = HashMap::new(); - for scroll_state in scroll_states.into_iter() { - let scroll_offset = scroll_state.scroll_offset; - if scroll_state.scroll_id.is_root() { + for (scroll_id, scroll_offset) in scroll_states.into_iter() { + if scroll_id.is_root() { window.update_viewport_for_scroll(-scroll_offset.x, -scroll_offset.y); - } else if let Some(node_id) = - node_id_from_scroll_id(scroll_state.scroll_id.0 as usize) - { + } else if let Some(node_id) = node_id_from_scroll_id(scroll_id.0 as usize) { scroll_offsets.insert(OpaqueNode(node_id), -scroll_offset); } } diff --git a/components/shared/compositing/display_list.rs b/components/shared/compositing/display_list.rs index f716bf0a7a8..37986b6e91d 100644 --- a/components/shared/compositing/display_list.rs +++ b/components/shared/compositing/display_list.rs @@ -4,6 +4,8 @@ //! Defines data structures which are consumed by the Compositor. +use std::collections::HashMap; + use base::id::ScrollTreeNodeId; use bitflags::bitflags; use embedder_traits::Cursor; @@ -338,7 +340,7 @@ impl ScrollTree { /// Given an [`ExternalScrollId`] and an offset, update the scroll offset of the scroll node /// with the given id. - pub fn set_scroll_offsets_for_node_with_external_scroll_id( + pub fn set_scroll_offset_for_node_with_external_scroll_id( &mut self, external_scroll_id: ExternalScrollId, offset: LayoutVector2D, @@ -353,6 +355,29 @@ impl ScrollTree { _ => None, }) } + + /// Given a set of all scroll offsets coming from the Servo renderer, update all of the offsets + /// for nodes that actually exist in this tree. + pub fn set_all_scroll_offsets(&mut self, offsets: &HashMap) { + for node in self.nodes.iter_mut() { + if let SpatialTreeNodeInfo::Scroll(ref mut scroll_info) = node.info { + if let Some(offset) = offsets.get(&scroll_info.external_id) { + scroll_info.offset = *offset; + } + } + } + } + + /// Collect all of the scroll offsets of the scrolling nodes of this tree into a + /// [`HashMap`] which can be applied to another tree. + pub fn scroll_offsets(&self) -> HashMap { + HashMap::from_iter(self.nodes.iter().filter_map(|node| match node.info { + SpatialTreeNodeInfo::Scroll(ref scroll_info) => { + Some((scroll_info.external_id, scroll_info.offset)) + }, + _ => None, + })) + } } /// A data structure which stores compositor-side information about diff --git a/components/shared/compositing/lib.rs b/components/shared/compositing/lib.rs index c2acb83f240..6685ecbdbf9 100644 --- a/components/shared/compositing/lib.rs +++ b/components/shared/compositing/lib.rs @@ -35,7 +35,7 @@ use ipc_channel::ipc::{self, IpcSharedMemory}; use profile_traits::mem::{OpaqueSender, ReportsChan}; use serde::{Deserialize, Serialize}; use servo_geometry::{DeviceIndependentIntRect, DeviceIndependentIntSize}; -use webrender_api::units::{DevicePoint, LayoutPoint, TexelRect}; +use webrender_api::units::{DevicePoint, LayoutVector2D, TexelRect}; use webrender_api::{ BuiltDisplayList, BuiltDisplayListDescriptor, ExternalImage, ExternalImageData, ExternalImageHandler, ExternalImageId, ExternalImageSource, ExternalScrollId, @@ -124,7 +124,7 @@ pub enum CompositorMsg { SendScrollNode( WebViewId, WebRenderPipelineId, - LayoutPoint, + LayoutVector2D, ExternalScrollId, ), /// Inform WebRender of a new display list for the given pipeline. @@ -232,7 +232,7 @@ impl CrossProcessCompositorApi { &self, webview_id: WebViewId, pipeline_id: WebRenderPipelineId, - point: LayoutPoint, + point: LayoutVector2D, scroll_id: ExternalScrollId, ) { if let Err(e) = self.0.send(CompositorMsg::SendScrollNode( diff --git a/components/shared/constellation/lib.rs b/components/shared/constellation/lib.rs index 53e924c5ad5..98c8b6b81bb 100644 --- a/components/shared/constellation/lib.rs +++ b/components/shared/constellation/lib.rs @@ -22,7 +22,6 @@ use embedder_traits::{ CompositorHitTestResult, Cursor, InputEvent, JavaScriptEvaluationId, MediaSessionActionType, Theme, ViewportDetails, WebDriverCommandMsg, }; -use euclid::Vector2D; pub use from_script_message::*; use ipc_channel::ipc::IpcSender; use malloc_size_of_derive::MallocSizeOf; @@ -32,7 +31,7 @@ use servo_url::{ImmutableOrigin, ServoUrl}; pub use structured_data::*; use strum_macros::IntoStaticStr; use webrender_api::ExternalScrollId; -use webrender_api::units::LayoutPixel; +use webrender_api::units::LayoutVector2D; /// Messages to the Constellation from the embedding layer, whether from `ServoRenderer` or /// from `libservo` itself. @@ -90,7 +89,7 @@ pub enum EmbedderToConstellationMessage { SetWebViewThrottled(WebViewId, bool), /// The Servo renderer scrolled and is updating the scroll states of the nodes in the /// given pipeline via the constellation. - SetScrollStates(PipelineId, Vec), + SetScrollStates(PipelineId, HashMap), /// Notify the constellation that a particular paint metric event has happened for the given pipeline. PaintMetric(PipelineId, PaintMetricEvent), /// Evaluate a JavaScript string in the context of a `WebView`. When execution is complete or an @@ -136,15 +135,6 @@ pub enum WindowSizeType { Resize, } -/// The scroll state of a stacking context. -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] -pub struct ScrollState { - /// The ID of the scroll root. - pub scroll_id: ExternalScrollId, - /// The scrolling offset of this stacking context. - pub scroll_offset: Vector2D, -} - /// The direction of a history traversal #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub enum TraversalDirection { diff --git a/components/shared/script/lib.rs b/components/shared/script/lib.rs index a8188d16ccd..069ca2f6a30 100644 --- a/components/shared/script/lib.rs +++ b/components/shared/script/lib.rs @@ -9,6 +9,7 @@ #![deny(missing_docs)] #![deny(unsafe_code)] +use std::collections::HashMap; use std::fmt; use std::sync::Arc; @@ -20,8 +21,8 @@ use bluetooth_traits::BluetoothRequest; use canvas_traits::webgl::WebGLPipeline; use compositing_traits::CrossProcessCompositorApi; use constellation_traits::{ - LoadData, NavigationHistoryBehavior, ScriptToConstellationChan, ScrollState, - StructuredSerializedData, WindowSizeType, + LoadData, NavigationHistoryBehavior, ScriptToConstellationChan, StructuredSerializedData, + WindowSizeType, }; use crossbeam_channel::{RecvTimeoutError, Sender}; use devtools_traits::ScriptToDevtoolsControlMsg; @@ -47,8 +48,8 @@ use style_traits::{CSSPixel, SpeculativePainter}; use stylo_atoms::Atom; #[cfg(feature = "webgpu")] use webgpu_traits::WebGPUMsg; -use webrender_api::ImageKey; -use webrender_api::units::DevicePixel; +use webrender_api::units::{DevicePixel, LayoutVector2D}; +use webrender_api::{ExternalScrollId, ImageKey}; /// The initial data required to create a new layout attached to an existing script thread. #[derive(Debug, Deserialize, Serialize)] @@ -246,7 +247,7 @@ pub enum ScriptThreadMessage { SetWebGPUPort(IpcReceiver), /// The compositor scrolled and is updating the scroll states of the nodes in the given /// pipeline via the Constellation. - SetScrollStates(PipelineId, Vec), + SetScrollStates(PipelineId, HashMap), /// Evaluate the given JavaScript and return a result via a corresponding message /// to the Constellation. EvaluateJavaScript(PipelineId, JavaScriptEvaluationId, String), diff --git a/components/shared/script_layout/lib.rs b/components/shared/script_layout/lib.rs index d8dc00bf80f..feee178478d 100644 --- a/components/shared/script_layout/lib.rs +++ b/components/shared/script_layout/lib.rs @@ -11,6 +11,7 @@ pub mod wrapper_traits; use std::any::Any; +use std::collections::HashMap; use std::sync::Arc; use std::sync::atomic::{AtomicIsize, AtomicU64, Ordering}; @@ -19,7 +20,7 @@ use atomic_refcell::AtomicRefCell; use base::Epoch; use base::id::{BrowsingContextId, PipelineId, WebViewId}; use compositing_traits::CrossProcessCompositorApi; -use constellation_traits::{LoadData, ScrollState}; +use constellation_traits::LoadData; use embedder_traits::{Theme, UntrustedNodeAddress, ViewportDetails}; use euclid::default::{Point2D, Rect}; use fnv::FnvHashMap; @@ -48,8 +49,8 @@ use style::properties::PropertyId; use style::properties::style_structs::Font; use style::selector_parser::{PseudoElement, RestyleDamage, Snapshot}; use style::stylesheets::Stylesheet; -use webrender_api::ImageKey; -use webrender_api::units::DeviceIntSize; +use webrender_api::units::{DeviceIntSize, LayoutVector2D}; +use webrender_api::{ExternalScrollId, ImageKey}; pub trait GenericLayoutDataTrait: Any + MallocSizeOfTrait { fn as_any(&self) -> &dyn Any; @@ -245,7 +246,10 @@ pub trait Layout { ); /// Set the scroll states of this layout after a compositor scroll. - fn set_scroll_offsets(&mut self, scroll_states: &[ScrollState]); + fn set_scroll_offsets_from_renderer( + &mut self, + scroll_states: &HashMap, + ); fn query_content_box(&self, node: TrustedNodeAddress) -> Option>; fn query_content_boxes(&self, node: TrustedNodeAddress) -> Vec>; @@ -333,7 +337,7 @@ pub enum ReflowGoal { /// Tells layout about a single new scrolling offset from the script. The rest will /// remain untouched and layout won't forward this back to script. - UpdateScrollNode(ScrollState), + UpdateScrollNode(ExternalScrollId, LayoutVector2D), } impl ReflowGoal { @@ -341,7 +345,7 @@ impl ReflowGoal { /// be present or false if it only needs stacking-relative positions. pub fn needs_display_list(&self) -> bool { match *self { - ReflowGoal::UpdateTheRendering | ReflowGoal::UpdateScrollNode(_) => true, + ReflowGoal::UpdateTheRendering | ReflowGoal::UpdateScrollNode(..) => true, ReflowGoal::LayoutQuery(ref querymsg) => match *querymsg { QueryMsg::ElementInnerOuterTextQuery | QueryMsg::InnerWindowDimensionsQuery | @@ -363,7 +367,7 @@ impl ReflowGoal { /// false if a layout_thread display list is sufficient. pub fn needs_display(&self) -> bool { match *self { - ReflowGoal::UpdateTheRendering | ReflowGoal::UpdateScrollNode(_) => true, + ReflowGoal::UpdateTheRendering | ReflowGoal::UpdateScrollNode(..) => true, ReflowGoal::LayoutQuery(ref querymsg) => match *querymsg { QueryMsg::NodesFromPointQuery | QueryMsg::TextIndexQuery |