diff options
author | Martin Robinson <mrobinson@igalia.com> | 2024-12-16 16:05:33 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-12-16 15:05:33 +0000 |
commit | 3e052676efb351143ffc26bb166045f272e16074 (patch) | |
tree | 98a4590601b7fe65af2973ec1f0833b0516be611 /components | |
parent | eb82161a8ae503f3019098c3230e5b33b89ccb58 (diff) | |
download | servo-3e052676efb351143ffc26bb166045f272e16074.tar.gz servo-3e052676efb351143ffc26bb166045f272e16074.zip |
script: Manage `<iframe>` sizes in `Window` (#34643)
Manage `<iframe>` size updates in `Window`. In addition to removing
duplicated code, this will allow setting `<iframe>` sizes synchronously
on child `Pipeline`s of the same origin in the script process in a
followup change. The goal is remove flakiness from `<iframe>` sizing.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Diffstat (limited to 'components')
-rw-r--r-- | components/constellation/constellation.rs | 6 | ||||
-rw-r--r-- | components/constellation/tracing.rs | 2 | ||||
-rw-r--r-- | components/layout/display_list/builder.rs | 28 | ||||
-rw-r--r-- | components/layout_2020/context.rs | 5 | ||||
-rw-r--r-- | components/layout_2020/display_list/mod.rs | 20 | ||||
-rw-r--r-- | components/layout_2020/flexbox/layout.rs | 7 | ||||
-rw-r--r-- | components/layout_2020/flow/mod.rs | 12 | ||||
-rw-r--r-- | components/layout_2020/positioned.rs | 1 | ||||
-rw-r--r-- | components/layout_2020/replaced.rs | 12 | ||||
-rw-r--r-- | components/layout_2020/taffy/layout.rs | 7 | ||||
-rw-r--r-- | components/layout_thread/lib.rs | 81 | ||||
-rw-r--r-- | components/layout_thread_2020/lib.rs | 83 | ||||
-rw-r--r-- | components/script/dom/htmliframeelement.rs | 2 | ||||
-rw-r--r-- | components/script/dom/window.rs | 73 | ||||
-rw-r--r-- | components/shared/script/script_msg.rs | 6 | ||||
-rw-r--r-- | components/shared/script_layout/Cargo.toml | 1 | ||||
-rw-r--r-- | components/shared/script_layout/lib.rs | 18 |
17 files changed, 164 insertions, 200 deletions
diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index e7713b11674..f85f6c676e4 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -1865,6 +1865,7 @@ where pipeline.title = title; } }, + FromScriptMsg::IFrameSizes(iframe_sizes) => self.handle_iframe_size_msg(iframe_sizes), } } @@ -2138,11 +2139,6 @@ where fn handle_request_from_layout(&mut self, message: FromLayoutMsg) { trace_layout_msg!(message, "{message:?}"); match message { - // Layout sends new sizes for all subframes. This needs to be reflected by all - // frame trees in the navigation context containing the subframe. - FromLayoutMsg::IFrameSizes(iframe_sizes) => { - self.handle_iframe_size_msg(iframe_sizes); - }, FromLayoutMsg::PendingPaintMetric(pipeline_id, epoch) => { self.handle_pending_paint_metric(pipeline_id, epoch); }, diff --git a/components/constellation/tracing.rs b/components/constellation/tracing.rs index 06ee3ee44e3..c81da81fa89 100644 --- a/components/constellation/tracing.rs +++ b/components/constellation/tracing.rs @@ -189,6 +189,7 @@ mod from_script { #[cfg(feature = "webgpu")] Self::GetWebGPUChan(..) => target!("GetWebGPUChan"), Self::TitleChanged(..) => target!("TitleChanged"), + Self::IFrameSizes(..) => target!("IFrameSizes"), } } } @@ -257,7 +258,6 @@ mod from_layout { impl LogTarget for script_traits::LayoutMsg { fn log_target(&self) -> &'static str { match self { - Self::IFrameSizes(..) => target!("IFrameSizes"), Self::PendingPaintMetric(..) => target!("PendingPaintMetric"), } } diff --git a/components/layout/display_list/builder.rs b/components/layout/display_list/builder.rs index bece8e09557..4046f45f314 100644 --- a/components/layout/display_list/builder.rs +++ b/components/layout/display_list/builder.rs @@ -13,7 +13,7 @@ use std::sync::Arc; use std::{f32, mem}; use app_units::{Au, AU_PER_PX}; -use base::id::{BrowsingContextId, PipelineId}; +use base::id::PipelineId; use bitflags::bitflags; use canvas_traits::canvas::{CanvasMsg, FromLayoutMsg}; use embedder_traits::Cursor; @@ -25,7 +25,9 @@ use ipc_channel::ipc; use log::{debug, warn}; use net_traits::image_cache::UsePlaceholder; use range::Range; -use script_layout_interface::{combine_id_with_fragment_type, FragmentType}; +use script_layout_interface::{ + combine_id_with_fragment_type, FragmentType, IFrameSize, IFrameSizes, +}; use servo_config::opts; use servo_geometry::{self, MaxRect}; use style::color::AbsoluteColor; @@ -43,7 +45,7 @@ use style::values::computed::{ClipRectOrAuto, Gradient}; use style::values::generics::background::BackgroundSize; use style::values::generics::image::PaintWorklet; use style::values::specified::ui::CursorKind; -use style_traits::{CSSPixel, ToCss}; +use style_traits::ToCss; use webrender_api::units::{LayoutRect, LayoutTransform, LayoutVector2D}; use webrender_api::{ self, BorderDetails, BorderRadius, BorderSide, BoxShadowClipMode, ColorF, ColorU, @@ -327,7 +329,7 @@ pub struct DisplayListBuildState<'a> { /// Vector containing iframe sizes, used to inform the constellation about /// new iframe sizes - pub iframe_sizes: FnvHashMap<BrowsingContextId, euclid::Size2D<f32, CSSPixel>>, + pub iframe_sizes: IFrameSizes, /// Stores text runs to answer text queries used to place a cursor inside text. pub indexable_text: IndexableText, @@ -1872,19 +1874,25 @@ impl Fragment { }, SpecificFragmentInfo::Iframe(ref fragment_info) => { if !stacking_relative_content_box.is_empty() { - let browsing_context_id = match fragment_info.browsing_context_id { - Some(browsing_context_id) => browsing_context_id, - None => return warn!("No browsing context id for iframe."), + let Some(browsing_context_id) = fragment_info.browsing_context_id else { + return warn!("No browsing context id for iframe."); + }; + let Some(pipeline_id) = fragment_info.pipeline_id else { + return warn!("No pipeline id for iframe."); }; let base = create_base_display_item(state); let bounds = stacking_relative_content_box.to_layout(); - // XXXjdm: This sleight-of-hand to convert LayoutRect -> Size2D<CSSPixel> - // looks bogus. state.iframe_sizes.insert( browsing_context_id, - euclid::Size2D::new(bounds.size().width, bounds.size().height), + IFrameSize { + browsing_context_id, + pipeline_id, + // XXXjdm: This sleight-of-hand to convert LayoutRect -> Size2D<CSSPixel> + // looks bogus. + size: euclid::Size2D::new(bounds.size().width, bounds.size().height), + }, ); let pipeline_id = match fragment_info.pipeline_id { diff --git a/components/layout_2020/context.rs b/components/layout_2020/context.rs index f1542327422..871fce6cb0d 100644 --- a/components/layout_2020/context.rs +++ b/components/layout_2020/context.rs @@ -11,7 +11,7 @@ use net_traits::image_cache::{ ImageCache, ImageCacheResult, ImageOrMetadataAvailable, UsePlaceholder, }; use parking_lot::{Mutex, RwLock}; -use script_layout_interface::{PendingImage, PendingImageState}; +use script_layout_interface::{IFrameSizes, PendingImage, PendingImageState}; use servo_url::{ImmutableOrigin, ServoUrl}; use style::context::SharedStyleContext; use style::dom::OpaqueNode; @@ -35,6 +35,9 @@ pub struct LayoutContext<'a> { /// A list of in-progress image loads to be shared with the script thread. pub pending_images: Mutex<Vec<PendingImage>>, + /// A collection of `<iframe>` sizes to send back to script. + pub iframe_sizes: Mutex<IFrameSizes>, + pub webrender_image_cache: Arc<RwLock<FnvHashMap<(ServoUrl, UsePlaceholder), WebRenderImageInfo>>>, } diff --git a/components/layout_2020/display_list/mod.rs b/components/layout_2020/display_list/mod.rs index 0d848141874..2be4d005e30 100644 --- a/components/layout_2020/display_list/mod.rs +++ b/components/layout_2020/display_list/mod.rs @@ -6,11 +6,9 @@ use std::cell::{OnceCell, RefCell}; use std::sync::Arc; use app_units::Au; -use base::id::BrowsingContextId; use base::WebRenderEpochToU16; use embedder_traits::Cursor; use euclid::{Point2D, SideOffsets2D, Size2D, UnknownUnit}; -use fnv::FnvHashMap; use fonts::GlyphStore; use gradient::WebRenderGradient; use net_traits::image_cache::UsePlaceholder; @@ -32,7 +30,6 @@ use style::values::generics::NonNegative; use style::values::specified::text::TextDecorationLine; use style::values::specified::ui::CursorKind; use style::Zero; -use style_traits::CSSPixel; use webrender_api::units::{DevicePixel, LayoutPixel, LayoutRect, LayoutSize}; use webrender_api::{ self as wr, units, BorderDetails, BoxShadowClipMode, ClipChainId, CommonItemProperties, @@ -162,12 +159,6 @@ pub(crate) struct DisplayListBuilder<'a> { /// The [DisplayList] used to collect display list items and metadata. pub display_list: &'a mut DisplayList, - /// A recording of the sizes of iframes encountered when building this - /// display list. This information is forwarded to layout for the - /// iframe so that its layout knows how large the initial containing block / - /// viewport is. - iframe_sizes: FnvHashMap<BrowsingContextId, Size2D<f32, CSSPixel>>, - /// Contentful paint i.e. whether the display list contains items of type /// text, image, non-white canvas or SVG). Used by metrics. /// See <https://w3c.github.io/paint-timing/#first-contentful-paint>. @@ -175,12 +166,13 @@ pub(crate) struct DisplayListBuilder<'a> { } impl DisplayList { + /// Build the display list, returning true if it was contentful. pub fn build( &mut self, context: &LayoutContext, fragment_tree: &FragmentTree, root_stacking_context: &StackingContext, - ) -> (FnvHashMap<BrowsingContextId, Size2D<f32, CSSPixel>>, bool) { + ) -> bool { #[cfg(feature = "tracing")] let _span = tracing::trace_span!("display_list::build", servo_profiling = true).entered(); let mut builder = DisplayListBuilder { @@ -191,10 +183,9 @@ impl DisplayList { is_contentful: false, context, display_list: self, - iframe_sizes: FnvHashMap::default(), }; fragment_tree.build_display_list(&mut builder, root_stacking_context); - (builder.iframe_sizes, builder.is_contentful) + builder.is_contentful } } @@ -318,11 +309,6 @@ impl Fragment { builder.is_contentful = true; let rect = iframe.rect.translate(containing_block.origin.to_vector()); - builder.iframe_sizes.insert( - iframe.browsing_context_id, - Size2D::new(rect.size.width.to_f32_px(), rect.size.height.to_f32_px()), - ); - let common = builder.common_properties(rect.to_webrender(), &iframe.style); builder.wr().push_iframe( rect.to_webrender(), diff --git a/components/layout_2020/flexbox/layout.rs b/components/layout_2020/flexbox/layout.rs index 9e705e5e5d3..668477cbd32 100644 --- a/components/layout_2020/flexbox/layout.rs +++ b/components/layout_2020/flexbox/layout.rs @@ -1987,8 +1987,11 @@ impl FlexItem<'_> { } } - let fragments = replaced - .make_fragments(item_style, size.to_physical_size(container_writing_mode)); + let fragments = replaced.make_fragments( + flex_context.layout_context, + item_style, + size.to_physical_size(container_writing_mode), + ); Some(FlexItemLayoutResult { hypothetical_cross_size, diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs index e3a45b579b6..47deeb10767 100644 --- a/components/layout_2020/flow/mod.rs +++ b/components/layout_2020/flow/mod.rs @@ -1399,6 +1399,7 @@ impl ReplacedContents { fn layout_in_flow_block_level( &self, base: &LayoutBoxBase, + layout_context: &LayoutContext, containing_block: &ContainingBlock, mut sequential_layout_state: Option<&mut SequentialLayoutState>, ) -> BoxFragment { @@ -1420,7 +1421,7 @@ impl ReplacedContents { let containing_block_writing_mode = containing_block.style.writing_mode; let physical_content_size = content_size.to_physical_size(containing_block_writing_mode); - let fragments = self.make_fragments(&base.style, physical_content_size); + let fragments = self.make_fragments(layout_context, &base.style, physical_content_size); let clearance; if let Some(ref mut sequential_layout_state) = sequential_layout_state { @@ -2071,7 +2072,12 @@ impl IndependentFormattingContext { sequential_layout_state, ), IndependentFormattingContextContents::Replaced(contents) => contents - .layout_in_flow_block_level(&self.base, containing_block, sequential_layout_state), + .layout_in_flow_block_level( + &self.base, + layout_context, + containing_block, + sequential_layout_state, + ), } } pub(crate) fn layout_float_or_atomic_inline( @@ -2099,7 +2105,7 @@ impl IndependentFormattingContext { &content_box_sizes_and_pbm, ) .to_physical_size(container_writing_mode); - let fragments = replaced.make_fragments(style, content_size); + let fragments = replaced.make_fragments(layout_context, style, content_size); let content_rect = PhysicalRect::new(PhysicalPoint::zero(), content_size); (fragments, content_rect, None) diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index fe193878203..ecb65af97cc 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -572,6 +572,7 @@ impl HoistedAbsolutelyPositionedBox { block: block_axis.size.to_definite().unwrap(), }; fragments = replaced.make_fragments( + layout_context, &style, content_size.to_physical_size(containing_block_writing_mode), ); diff --git a/components/layout_2020/replaced.rs b/components/layout_2020/replaced.rs index 149b5fe119b..86345e0b8f5 100644 --- a/components/layout_2020/replaced.rs +++ b/components/layout_2020/replaced.rs @@ -10,9 +10,11 @@ use app_units::Au; use base::id::{BrowsingContextId, PipelineId}; use canvas_traits::canvas::{CanvasId, CanvasMsg, FromLayoutMsg}; use data_url::DataUrl; +use euclid::Size2D; use ipc_channel::ipc::{self, IpcSender}; use net_traits::image_cache::{ImageOrMetadataAvailable, UsePlaceholder}; use pixels::Image; +use script_layout_interface::IFrameSize; use serde::Serialize; use servo_arc::Arc as ServoArc; use style::computed_values::object_fit::T as ObjectFit; @@ -280,6 +282,7 @@ impl ReplacedContents { pub fn make_fragments( &self, + layout_context: &LayoutContext, style: &ServoArc<ComputedValues>, size: PhysicalSize<Au>, ) -> Vec<Fragment> { @@ -349,6 +352,15 @@ impl ReplacedContents { image_key: video.as_ref().map(|video| video.image_key), })], ReplacedContentKind::IFrame(iframe) => { + let size = Size2D::new(rect.size.width.to_f32_px(), rect.size.height.to_f32_px()); + layout_context.iframe_sizes.lock().insert( + iframe.browsing_context_id, + IFrameSize { + browsing_context_id: iframe.browsing_context_id, + pipeline_id: iframe.pipeline_id, + size, + }, + ); vec![Fragment::IFrame(IFrameFragment { base: self.base_fragment_info.into(), style: style.clone(), diff --git a/components/layout_2020/taffy/layout.rs b/components/layout_2020/taffy/layout.rs index 5d21914278c..4ed13aa008b 100644 --- a/components/layout_2020/taffy/layout.rs +++ b/components/layout_2020/taffy/layout.rs @@ -174,8 +174,11 @@ impl taffy::LayoutPartialTree for TaffyContainerContext<'_> { // Create fragments if the RunMode if PerformLayout // If the RunMode is ComputeSize then only the returned size will be used if inputs.run_mode == RunMode::PerformLayout { - child.child_fragments = - replaced.make_fragments(style, content_box_size); + child.child_fragments = replaced.make_fragments( + self.layout_context, + style, + content_box_size, + ); } let computed_size = taffy::Size { diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index f897b93d888..a01ae8c424d 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -15,7 +15,7 @@ use std::sync::{Arc, LazyLock, Mutex}; use app_units::Au; use base::cross_process_instant::CrossProcessInstant; -use base::id::{BrowsingContextId, PipelineId}; +use base::id::PipelineId; use base::Epoch; use embedder_traits::resources::{self, Resource}; use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect, Size2D as UntypedSize2D}; @@ -47,7 +47,7 @@ use layout::traversal::{ }; use layout::wrapper::ThreadSafeLayoutNodeHelpers; use layout::{layout_debug, layout_debug_scope, parallel, sequential}; -use log::{debug, error, trace, warn}; +use log::{debug, error, trace}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use metrics::{PaintTimeMetrics, ProfilerMetadataFactory}; use net_traits::image_cache::{ImageCache, UsePlaceholder}; @@ -61,12 +61,12 @@ use profile_traits::{path, time_profile}; use script::layout_dom::{ServoLayoutElement, ServoLayoutNode}; use script_layout_interface::wrapper_traits::LayoutNode; use script_layout_interface::{ - Layout, LayoutConfig, LayoutFactory, NodesFromPointQueryType, OffsetParentResponse, Reflow, - ReflowGoal, ReflowRequest, ReflowResult, TrustedNodeAddress, + IFrameSizes, Layout, LayoutConfig, LayoutFactory, NodesFromPointQueryType, + OffsetParentResponse, Reflow, ReflowGoal, ReflowRequest, ReflowResult, TrustedNodeAddress, }; use script_traits::{ - ConstellationControlMsg, DrawAPaintImageResult, IFrameSizeMsg, LayoutMsg as ConstellationMsg, - PaintWorkletError, Painter, ScrollState, UntrustedNodeAddress, WindowSizeData, WindowSizeType, + ConstellationControlMsg, DrawAPaintImageResult, PaintWorkletError, Painter, ScrollState, + UntrustedNodeAddress, WindowSizeData, }; use servo_arc::Arc as ServoArc; use servo_atoms::Atom; @@ -130,9 +130,6 @@ pub struct LayoutThread { /// Is the current reflow of an iframe, as opposed to a root window? is_iframe: bool, - /// The channel on which messages can be sent to the constellation. - constellation_chan: IpcSender<ConstellationMsg>, - /// The channel on which messages can be sent to the script thread. script_chan: IpcSender<ConstellationControlMsg>, @@ -186,7 +183,7 @@ pub struct LayoutThread { paint_time_metrics: PaintTimeMetrics, /// The sizes of all iframes encountered during the last layout operation. - last_iframe_sizes: RefCell<FnvHashMap<BrowsingContextId, Size2D<f32, CSSPixel>>>, + last_iframe_sizes: RefCell<IFrameSizes>, /// Debug options, copied from configuration to this `LayoutThread` in order /// to avoid having to constantly access the thread-safe global options. @@ -204,7 +201,6 @@ impl LayoutFactory for LayoutFactoryImpl { config.id, config.url, config.is_iframe, - config.constellation_chan, config.script_chan, config.image_cache, config.resource_threads, @@ -306,16 +302,6 @@ impl Layout for LayoutThread { get_the_text_steps(node, &self.indexable_text.borrow()) } - fn query_inner_window_dimension( - &self, - browsing_context_id: BrowsingContextId, - ) -> Option<Size2D<f32, CSSPixel>> { - self.last_iframe_sizes - .borrow() - .get(&browsing_context_id) - .cloned() - } - fn query_nodes_from_point( &self, point: UntypedPoint2D<f32>, @@ -529,7 +515,6 @@ impl LayoutThread { id: PipelineId, url: ServoUrl, is_iframe: bool, - constellation_chan: IpcSender<ConstellationMsg>, script_chan: IpcSender<ConstellationControlMsg>, image_cache: Arc<dyn ImageCache>, resource_threads: ResourceThreads, @@ -571,7 +556,6 @@ impl LayoutThread { url, is_iframe, script_chan, - constellation_chan, time_profiler_chan, registered_painters: RegisteredPaintersImpl(Default::default()), image_cache, @@ -755,48 +739,6 @@ impl LayoutThread { ); } - /// Update the recorded iframe sizes of the contents of layout and when these sizes changes, - /// send a message to the constellation informing it of the new sizes. - fn update_iframe_sizes( - &self, - new_iframe_sizes: FnvHashMap<BrowsingContextId, Size2D<f32, CSSPixel>>, - ) { - let old_iframe_sizes = - std::mem::replace(&mut *self.last_iframe_sizes.borrow_mut(), new_iframe_sizes); - - if self.last_iframe_sizes.borrow().is_empty() { - return; - } - - let size_messages: Vec<_> = self - .last_iframe_sizes - .borrow() - .iter() - .filter_map(|(browsing_context_id, size)| { - match old_iframe_sizes.get(browsing_context_id) { - Some(old_size) if old_size != size => Some(IFrameSizeMsg { - browsing_context_id: *browsing_context_id, - size: *size, - type_: WindowSizeType::Resize, - }), - None => Some(IFrameSizeMsg { - browsing_context_id: *browsing_context_id, - size: *size, - type_: WindowSizeType::Initial, - }), - _ => None, - } - }) - .collect(); - - if !size_messages.is_empty() { - let msg = ConstellationMsg::IFrameSizes(size_messages); - if let Err(e) = self.constellation_chan.send(msg) { - warn!("Layout resize to constellation failed ({}).", e); - } - } - } - /// Computes the stacking-relative positions of all flows and, if the painting is dirty and the /// reflow type need it, builds the display list. fn compute_abs_pos_and_build_display_list( @@ -850,8 +792,8 @@ impl LayoutThread { build_state.root_stacking_context.overflow = origin; // We will not use build_state.iframe_sizes again, so it's safe to move it. - let iframe_sizes = std::mem::take(&mut build_state.iframe_sizes); - self.update_iframe_sizes(iframe_sizes); + *self.last_iframe_sizes.borrow_mut() = + std::mem::take(&mut build_state.iframe_sizes); *self.indexable_text.borrow_mut() = std::mem::take(&mut build_state.indexable_text); @@ -1130,7 +1072,10 @@ impl LayoutThread { } let pending_images = std::mem::take(&mut *layout_context.pending_images.lock().unwrap()); - Some(ReflowResult { pending_images }) + Some(ReflowResult { + pending_images, + iframe_sizes: self.last_iframe_sizes.borrow().clone(), + }) } fn update_scroll_node_state(&self, state: &ScrollState) { diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs index 6dc5a204556..0010f68e022 100644 --- a/components/layout_thread_2020/lib.rs +++ b/components/layout_thread_2020/lib.rs @@ -16,7 +16,7 @@ use std::sync::{Arc, LazyLock}; use app_units::Au; use base::cross_process_instant::CrossProcessInstant; -use base::id::{BrowsingContextId, PipelineId}; +use base::id::PipelineId; use base::Epoch; use embedder_traits::resources::{self, Resource}; use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect, Size2D as UntypedSize2D}; @@ -35,7 +35,7 @@ use layout::query::{ }; use layout::traversal::RecalcStyle; use layout::{layout_debug, BoxTree, FragmentTree}; -use log::{debug, error, warn}; +use log::{debug, error}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use metrics::{PaintTimeMetrics, ProfilerMetadataFactory}; use net_traits::image_cache::{ImageCache, UsePlaceholder}; @@ -52,8 +52,8 @@ use script_layout_interface::{ ReflowRequest, ReflowResult, TrustedNodeAddress, }; use script_traits::{ - ConstellationControlMsg, DrawAPaintImageResult, IFrameSizeMsg, LayoutMsg as ConstellationMsg, - PaintWorkletError, Painter, ScrollState, UntrustedNodeAddress, WindowSizeData, WindowSizeType, + ConstellationControlMsg, DrawAPaintImageResult, PaintWorkletError, Painter, ScrollState, + UntrustedNodeAddress, WindowSizeData, }; use servo_arc::Arc as ServoArc; use servo_atoms::Atom; @@ -115,9 +115,6 @@ pub struct LayoutThread { /// Is the current reflow of an iframe, as opposed to a root window? is_iframe: bool, - /// The channel on which messages can be sent to the constellation. - constellation_chan: IpcSender<ConstellationMsg>, - /// The channel on which messages can be sent to the script thread. script_chan: IpcSender<ConstellationControlMsg>, @@ -164,9 +161,6 @@ pub struct LayoutThread { /// Paint time metrics. paint_time_metrics: PaintTimeMetrics, - /// The sizes of all iframes encountered during the last layout operation. - last_iframe_sizes: RefCell<FnvHashMap<BrowsingContextId, Size2D<f32, CSSPixel>>>, - /// Debug options, copied from configuration to this `LayoutThread` in order /// to avoid having to constantly access the thread-safe global options. debug: DebugOptions, @@ -180,7 +174,6 @@ impl LayoutFactory for LayoutFactoryImpl { config.id, config.url, config.is_iframe, - config.constellation_chan, config.script_chan, config.image_cache, config.resource_threads, @@ -295,15 +288,6 @@ impl Layout for LayoutThread { get_the_text_steps(node) } - fn query_inner_window_dimension( - &self, - _context: BrowsingContextId, - ) -> Option<Size2D<f32, CSSPixel>> { - // TODO(jdm): port the iframe sizing code from layout2013's display - // builder in order to support query iframe sizing. - None - } - #[cfg_attr( feature = "tracing", tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace") @@ -505,7 +489,6 @@ impl LayoutThread { id: PipelineId, url: ServoUrl, is_iframe: bool, - constellation_chan: IpcSender<ConstellationMsg>, script_chan: IpcSender<ConstellationControlMsg>, image_cache: Arc<dyn ImageCache>, resource_threads: ResourceThreads, @@ -548,7 +531,6 @@ impl LayoutThread { id, url, is_iframe, - constellation_chan, script_chan: script_chan.clone(), time_profiler_chan, registered_painters: RegisteredPaintersImpl(Default::default()), @@ -569,7 +551,6 @@ impl LayoutThread { stylist: Stylist::new(device, QuirksMode::NoQuirks), webrender_image_cache: Default::default(), paint_time_metrics, - last_iframe_sizes: Default::default(), debug: opts::get().debug.clone(), } } @@ -623,7 +604,8 @@ impl LayoutThread { image_cache: self.image_cache.clone(), font_context: self.font_context.clone(), webrender_image_cache: self.webrender_image_cache.clone(), - pending_images: Mutex::new(vec![]), + pending_images: Mutex::default(), + iframe_sizes: Mutex::default(), use_rayon, } } @@ -867,7 +849,11 @@ impl LayoutThread { } let pending_images = std::mem::take(&mut *layout_context.pending_images.lock()); - Some(ReflowResult { pending_images }) + let iframe_sizes = std::mem::take(&mut *layout_context.iframe_sizes.lock()); + Some(ReflowResult { + pending_images, + iframe_sizes, + }) } fn update_scroll_node_state(&self, state: &ScrollState) { @@ -935,8 +921,7 @@ impl LayoutThread { display_list.build_stacking_context_tree(&fragment_tree, &self.debug); // Build the rest of the display list which inclues all of the WebRender primitives. - let (iframe_sizes, is_contentful) = - display_list.build(context, &fragment_tree, &root_stacking_context); + let is_contentful = display_list.build(context, &fragment_tree, &root_stacking_context); if self.debug.dump_flow_tree { fragment_tree.print(); @@ -963,8 +948,6 @@ impl LayoutThread { .remove_unused_font_resources(keys, instance_keys) } - self.update_iframe_sizes(iframe_sizes); - if self.debug.trace_layout { layout_debug::end_trace(self.generation.get()); } @@ -1009,48 +992,6 @@ impl LayoutThread { } } - /// Update the recorded iframe sizes of the contents of layout and when these sizes changes, - /// send a message to the constellation informing it of the new sizes. - fn update_iframe_sizes( - &self, - new_iframe_sizes: FnvHashMap<BrowsingContextId, Size2D<f32, CSSPixel>>, - ) { - let old_iframe_sizes = - std::mem::replace(&mut *self.last_iframe_sizes.borrow_mut(), new_iframe_sizes); - - if self.last_iframe_sizes.borrow().is_empty() { - return; - } - - let size_messages: Vec<_> = self - .last_iframe_sizes - .borrow() - .iter() - .filter_map(|(browsing_context_id, size)| { - match old_iframe_sizes.get(browsing_context_id) { - Some(old_size) if old_size != size => Some(IFrameSizeMsg { - browsing_context_id: *browsing_context_id, - size: *size, - type_: WindowSizeType::Resize, - }), - None => Some(IFrameSizeMsg { - browsing_context_id: *browsing_context_id, - size: *size, - type_: WindowSizeType::Initial, - }), - _ => None, - } - }) - .collect(); - - if !size_messages.is_empty() { - let msg = ConstellationMsg::IFrameSizes(size_messages); - if let Err(e) = self.constellation_chan.send(msg) { - warn!("Layout resize to constellation failed ({}).", e); - } - } - } - fn viewport_did_change(&mut self, window_size_data: WindowSizeData) -> bool { let new_pixel_ratio = window_size_data.device_pixel_ratio.get(); let new_viewport_size = Size2D::new( diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index 0e57e4ad26c..f12f7daea40 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -196,7 +196,7 @@ impl HTMLIFrameElement { let window_size = WindowSizeData { initial_viewport: window - .inner_window_dimensions_query(browsing_context_id, can_gc) + .get_iframe_size_if_known(browsing_context_id, can_gc) .unwrap_or_default(), device_pixel_ratio: window.device_pixel_ratio(), }; diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 833ad837bf1..1c9f755c61a 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -51,14 +51,14 @@ use profile_traits::ipc as ProfiledIpc; use profile_traits::mem::ProfilerChan as MemProfilerChan; use profile_traits::time::ProfilerChan as TimeProfilerChan; use script_layout_interface::{ - combine_id_with_fragment_type, FragmentType, Layout, PendingImageState, QueryMsg, Reflow, - ReflowGoal, ReflowRequest, TrustedNodeAddress, + combine_id_with_fragment_type, FragmentType, IFrameSizes, Layout, PendingImageState, QueryMsg, + Reflow, ReflowGoal, ReflowRequest, TrustedNodeAddress, }; use script_traits::webdriver_msg::{WebDriverJSError, WebDriverJSResult}; use script_traits::{ - ConstellationControlMsg, DocumentState, HistoryEntryReplacement, LoadData, ScriptMsg, - ScriptToConstellationChan, ScrollState, StructuredSerializedData, Theme, TimerSchedulerMsg, - WindowSizeData, WindowSizeType, + ConstellationControlMsg, DocumentState, HistoryEntryReplacement, IFrameSizeMsg, LoadData, + ScriptMsg, ScriptToConstellationChan, ScrollState, StructuredSerializedData, Theme, + TimerSchedulerMsg, WindowSizeData, WindowSizeType, }; use selectors::attr::CaseSensitivity; use servo_arc::Arc as ServoArc; @@ -377,6 +377,15 @@ pub struct Window { /// <https://dom.spec.whatwg.org/#window-current-event> current_event: DomRefCell<Option<Dom<Event>>>, + + /// Sizes of the various `<iframes>` that we might have on this [`Window`]. + /// This is used to: + /// - Send the proper size for the `<iframe>` during new-Pipeline creation + /// when the `src` attribute changes. + /// - Let the `Constellation` know about `BrowsingContext` (one per `<iframe>`) + /// size changes when an `<iframe>` changes size during layout. + #[no_trace] + iframe_sizes: RefCell<IFrameSizes>, } impl Window { @@ -1958,12 +1967,48 @@ impl Window { } } + self.handle_new_iframe_sizes_after_layout(results.iframe_sizes); + document.update_animations_post_reflow(); self.update_constellation_epoch(); true } + /// Update the recorded iframe sizes of the contents of layout. When these sizes change, + /// send a message to the `Constellation` informing it of the new sizes. + fn handle_new_iframe_sizes_after_layout(&self, new_iframe_sizes: IFrameSizes) { + let old_iframe_sizes = self.iframe_sizes.replace(new_iframe_sizes); + let new_iframe_sizes = self.iframe_sizes.borrow(); + if new_iframe_sizes.is_empty() { + return; + } + + // Send asynchronous updates to `Constellation.` + let size_messages: Vec<_> = new_iframe_sizes + .iter() + .filter_map(|(browsing_context_id, size)| { + match old_iframe_sizes.get(browsing_context_id) { + Some(old_size) if old_size.size == size.size => None, + Some(..) => Some(IFrameSizeMsg { + browsing_context_id: *browsing_context_id, + size: size.size, + type_: WindowSizeType::Resize, + }), + None => Some(IFrameSizeMsg { + browsing_context_id: *browsing_context_id, + size: size.size, + type_: WindowSizeType::Initial, + }), + } + }) + .collect(); + + if !size_messages.is_empty() { + self.send_to_constellation(ScriptMsg::IFrameSizes(size_messages)); + } + } + /// Reflows the page if it's possible to do so and the page is dirty. Returns true if layout /// actually happened, false otherwise. /// @@ -2255,17 +2300,20 @@ impl Window { )) } - pub fn inner_window_dimensions_query( + /// If the given |browsing_context_id| refers to an `<iframe>` that is an element + /// in this [`Window`] and that `<iframe>` has been laid out, return its size. + /// Otherwise, return `None`. + pub(crate) fn get_iframe_size_if_known( &self, - browsing_context: BrowsingContextId, + browsing_context_id: BrowsingContextId, can_gc: CanGc, ) -> Option<Size2D<f32, CSSPixel>> { - if !self.layout_reflow(QueryMsg::InnerWindowDimensionsQuery, can_gc) { - return None; - } - self.layout + // Reflow might fail, but do a best effort to return the right size. + self.layout_reflow(QueryMsg::InnerWindowDimensionsQuery, can_gc); + self.iframe_sizes .borrow() - .query_inner_window_dimension(browsing_context) + .get(&browsing_context_id) + .map(|iframe_size| iframe_size.size) } #[allow(unsafe_code)] @@ -2800,6 +2848,7 @@ impl Window { layout_marker: DomRefCell::new(Rc::new(Cell::new(true))), current_event: DomRefCell::new(None), theme: Cell::new(PrefersColorScheme::Light), + iframe_sizes: RefCell::default(), }); unsafe { WindowBinding::Wrap(JSContext::from_ptr(runtime.cx()), win) } diff --git a/components/shared/script/script_msg.rs b/components/shared/script/script_msg.rs index 75da73fa167..9e51cf6d548 100644 --- a/components/shared/script/script_msg.rs +++ b/components/shared/script/script_msg.rs @@ -46,8 +46,6 @@ pub struct IFrameSizeMsg { /// Messages from the layout to the constellation. #[derive(Deserialize, Serialize)] pub enum LayoutMsg { - /// Inform the constellation of the size of the iframe's viewport. - IFrameSizes(Vec<IFrameSizeMsg>), /// Requests that the constellation inform the compositor that it needs to record /// the time when the frame with the given ID (epoch) is painted. PendingPaintMetric(PipelineId, Epoch), @@ -57,7 +55,6 @@ impl fmt::Debug for LayoutMsg { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { use self::LayoutMsg::*; let variant = match *self { - IFrameSizes(..) => "IFrameSizes", PendingPaintMetric(..) => "PendingPaintMetric", }; write!(formatter, "LayoutMsg::{}", variant) @@ -260,6 +257,8 @@ pub enum ScriptMsg { GetWebGPUChan(IpcSender<Option<WebGPU>>), /// Notify the constellation of a pipeline's document's title. TitleChanged(PipelineId, String), + /// Notify the constellation that the size of some `<iframe>`s has changed. + IFrameSizes(Vec<IFrameSizeMsg>), } impl fmt::Debug for ScriptMsg { @@ -320,6 +319,7 @@ impl fmt::Debug for ScriptMsg { #[cfg(feature = "webgpu")] GetWebGPUChan(..) => "GetWebGPUChan", TitleChanged(..) => "TitleChanged", + IFrameSizes(..) => "IFramSizes", }; write!(formatter, "ScriptMsg::{}", variant) } diff --git a/components/shared/script_layout/Cargo.toml b/components/shared/script_layout/Cargo.toml index 5de7c95f9c7..1dee24a3107 100644 --- a/components/shared/script_layout/Cargo.toml +++ b/components/shared/script_layout/Cargo.toml @@ -18,6 +18,7 @@ atomic_refcell = { workspace = true } canvas_traits = { workspace = true } crossbeam-channel = { workspace = true } euclid = { workspace = true } +fnv = { workspace = true } fonts = { path = "../../fonts" } fonts_traits = { workspace = true } html5ever = { workspace = true } diff --git a/components/shared/script_layout/lib.rs b/components/shared/script_layout/lib.rs index 591e356320c..f8643e866e7 100644 --- a/components/shared/script_layout/lib.rs +++ b/components/shared/script_layout/lib.rs @@ -23,6 +23,7 @@ use base::Epoch; use canvas_traits::canvas::{CanvasId, CanvasMsg}; use euclid::default::{Point2D, Rect}; use euclid::Size2D; +use fnv::FnvHashMap; use fonts::SystemFontServiceProxy; use ipc_channel::ipc::IpcSender; use libc::c_void; @@ -257,10 +258,6 @@ pub trait Layout { fn query_content_boxes(&self, node: OpaqueNode) -> Vec<Rect<Au>>; fn query_client_rect(&self, node: OpaqueNode) -> Rect<i32>; fn query_element_inner_outer_text(&self, node: TrustedNodeAddress) -> String; - fn query_inner_window_dimension( - &self, - context: BrowsingContextId, - ) -> Option<Size2D<f32, CSSPixel>>; fn query_nodes_from_point( &self, point: Point2D<f32>, @@ -400,11 +397,24 @@ pub struct Reflow { pub page_clip_rect: Rect<Au>, } +#[derive(Clone, Debug, MallocSizeOf)] +pub struct IFrameSize { + pub browsing_context_id: BrowsingContextId, + pub pipeline_id: PipelineId, + pub size: Size2D<f32, CSSPixel>, +} + +pub type IFrameSizes = FnvHashMap<BrowsingContextId, IFrameSize>; + /// Information derived from a layout pass that needs to be returned to the script thread. #[derive(Debug, Default)] pub struct ReflowResult { /// The list of images that were encountered that are in progress. pub pending_images: Vec<PendingImage>, + /// The list of iframes in this layout and their sizes, used in order + /// to communicate them with the Constellation and also the `Window` + /// element of their content pages. + pub iframe_sizes: IFrameSizes, } /// Information needed for a script-initiated reflow. |