aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorMartin Robinson <mrobinson@igalia.com>2024-12-16 16:05:33 +0100
committerGitHub <noreply@github.com>2024-12-16 15:05:33 +0000
commit3e052676efb351143ffc26bb166045f272e16074 (patch)
tree98a4590601b7fe65af2973ec1f0833b0516be611 /components
parenteb82161a8ae503f3019098c3230e5b33b89ccb58 (diff)
downloadservo-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.rs6
-rw-r--r--components/constellation/tracing.rs2
-rw-r--r--components/layout/display_list/builder.rs28
-rw-r--r--components/layout_2020/context.rs5
-rw-r--r--components/layout_2020/display_list/mod.rs20
-rw-r--r--components/layout_2020/flexbox/layout.rs7
-rw-r--r--components/layout_2020/flow/mod.rs12
-rw-r--r--components/layout_2020/positioned.rs1
-rw-r--r--components/layout_2020/replaced.rs12
-rw-r--r--components/layout_2020/taffy/layout.rs7
-rw-r--r--components/layout_thread/lib.rs81
-rw-r--r--components/layout_thread_2020/lib.rs83
-rw-r--r--components/script/dom/htmliframeelement.rs2
-rw-r--r--components/script/dom/window.rs73
-rw-r--r--components/shared/script/script_msg.rs6
-rw-r--r--components/shared/script_layout/Cargo.toml1
-rw-r--r--components/shared/script_layout/lib.rs18
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.