diff options
author | Martin Robinson <mrobinson@igalia.com> | 2023-04-30 20:21:58 +0200 |
---|---|---|
committer | Martin Robinson <mrobinson@igalia.com> | 2023-05-04 10:46:27 +0200 |
commit | 72302e2dae6c4726ae657f7b5b8b048f9f64ccf2 (patch) | |
tree | 307e1972dd46de70388bd9cba0f8589f05250226 /components | |
parent | 77a184a0e7379a63a0d9d8bf442d8dd5c3b5e307 (diff) | |
download | servo-72302e2dae6c4726ae657f7b5b8b048f9f64ccf2.tar.gz servo-72302e2dae6c4726ae657f7b5b8b048f9f64ccf2.zip |
Detect body elements during layout
During layout it is often useful, for various specification reasons, to
know if an element is the `<body>` element of an `<html>` element root. There
are a couple places where a brittle heuristic is used to detect `<body>`
elements. This information is going to be even more important to
properly handle `<html>` elements that inherit their overflow property from
their `<body>` children.
Implementing this properly requires updating the DOM wrapper interface.
This check does reach up to the parent of thread-safe nodes, but this is
essentially the same kind of operation that `parent_style()` does, so is
ostensibly safe.
This change should not change any behavior and is just a preparation
step for properly handle `<body>` overflow.
Diffstat (limited to 'components')
25 files changed, 486 insertions, 277 deletions
diff --git a/components/layout/flow.rs b/components/layout/flow.rs index 01aa77043f1..ef7cfe3d5a7 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -967,13 +967,23 @@ impl fmt::Debug for BaseFlow { "".to_owned() }; + let flags_string = if !self.flags.is_empty() { + format!("\nflags={:?}", self.flags) + } else { + "".to_owned() + }; + write!( f, "\nsc={:?}\ \npos={:?}{}{}\ \nfloatspec-in={:?}\ \nfloatspec-out={:?}\ - \noverflow={:?}{}{}{}", + \noverflow={:?}\ + {child_count_string}\ + {absolute_descendants_string}\ + {damage_string}\ + {flags_string}", self.stacking_context_id, self.position, if self.flags.contains(FlowFlags::FLOATS_LEFT) { @@ -989,9 +999,6 @@ impl fmt::Debug for BaseFlow { self.speculated_float_placement_in, self.speculated_float_placement_out, self.overflow, - child_count_string, - absolute_descendants_string, - damage_string ) } } diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 843a7caebbd..ed2cd58cdab 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -677,18 +677,27 @@ impl Fragment { let mut restyle_damage = node.restyle_damage(); restyle_damage.remove(ServoRestyleDamage::RECONSTRUCT_FLOW); + let mut flags = FragmentFlags::empty(); + let is_body = node + .as_element() + .map(|element| element.is_body_element_of_html_element_root()) + .unwrap_or(false); + if is_body { + flags |= FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT; + } + Fragment { node: node.opaque(), - style: style, + style, selected_style: node.selected_style(), - restyle_damage: restyle_damage, + restyle_damage, border_box: LogicalRect::zero(writing_mode), border_padding: LogicalMargin::zero(writing_mode), margin: LogicalMargin::zero(writing_mode), - specific: specific, + specific, inline_context: None, pseudo: node.get_pseudo_element_type(), - flags: FragmentFlags::empty(), + flags, debug_id: DebugId::new(), stacking_context_id: StackingContextId::root(), established_reference_frame: None, @@ -3277,16 +3286,24 @@ impl fmt::Debug for Fragment { "".to_owned() }; + let flags_string = if !self.flags.is_empty() { + format!("\nflags={:?}", self.flags) + } else { + "".to_owned() + }; + write!( f, - "\n{}({}) [{:?}]\nborder_box={:?}{}{}{}", + "\n{}({}) [{:?}]\ + \nborder_box={:?}\ + {border_padding_string}\ + {margin_string}\ + {damage_string}\ + {flags_string}", self.specific.get_type(), self.debug_id, self.specific, self.border_box, - border_padding_string, - margin_string, - damage_string ) } } @@ -3430,6 +3447,8 @@ bitflags! { const IS_BLOCK_FLEX_ITEM = 0b0000_0010; /// Whether this fragment represents the generated text from a text-overflow clip. const IS_ELLIPSIS = 0b0000_0100; + /// Whether this fragment is for the body element child of a html element root element. + const IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT = 0b0000_1000; } } diff --git a/components/layout/query.rs b/components/layout/query.rs index ec3dd555ffc..1d4e810c23a 100644 --- a/components/layout/query.rs +++ b/components/layout/query.rs @@ -9,7 +9,7 @@ use crate::context::LayoutContext; use crate::display_list::items::{DisplayList, OpaqueNode, ScrollOffsetMap}; use crate::display_list::IndexableText; use crate::flow::{Flow, GetBaseFlow}; -use crate::fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo}; +use crate::fragment::{Fragment, FragmentBorderBoxIterator, FragmentFlags, SpecificFragmentInfo}; use crate::inline::InlineFragmentNodeFlags; use crate::opaque_node::OpaqueNodeMethods; use crate::sequential; @@ -666,11 +666,9 @@ impl FragmentBorderBoxIterator for ParentOffsetBorderBoxIterator { self.has_processed_node = true; } } else if self.node_offset_box.is_none() { - // TODO(gw): Is there a less fragile way of checking whether this - // fragment is the body element, rather than just checking that - // it's at level 1 (below the root node)? - let is_body_element = level == 1; - + let is_body_element = fragment + .flags + .contains(FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT); let is_valid_parent = match ( is_body_element, fragment.style.get_box().position, diff --git a/components/layout_2020/Cargo.toml b/components/layout_2020/Cargo.toml index 57184c47ef4..22c6d9a5494 100644 --- a/components/layout_2020/Cargo.toml +++ b/components/layout_2020/Cargo.toml @@ -16,6 +16,7 @@ doctest = false app_units = "0.7" atomic_refcell = "0.1.6" canvas_traits = { path = "../canvas_traits" } +bitflags = "1.0" cssparser = "0.29" embedder_traits = { path = "../embedder_traits" } euclid = "0.22" diff --git a/components/layout_2020/display_list/mod.rs b/components/layout_2020/display_list/mod.rs index 97b2e25a82e..9713764eddc 100644 --- a/components/layout_2020/display_list/mod.rs +++ b/components/layout_2020/display_list/mod.rs @@ -5,7 +5,8 @@ use crate::context::LayoutContext; use crate::display_list::conversions::ToWebRender; use crate::display_list::stacking_context::StackingContextSection; -use crate::fragments::{BoxFragment, Fragment, Tag, TextFragment}; +use crate::fragment_tree::Tag; +use crate::fragments::{BoxFragment, Fragment, TextFragment}; use crate::geom::{PhysicalPoint, PhysicalRect}; use crate::replaced::IntrinsicSizes; use crate::style_ext::ComputedValuesExt; @@ -95,19 +96,24 @@ impl<'a> DisplayListBuilder<'a> { } } - fn hit_info(&mut self, style: &ComputedValues, tag: Tag, auto_cursor: Cursor) -> HitInfo { + fn hit_info( + &mut self, + style: &ComputedValues, + tag: Option<Tag>, + auto_cursor: Cursor, + ) -> HitInfo { use style::computed_values::pointer_events::T as PointerEvents; let inherited_ui = style.get_inherited_ui(); if inherited_ui.pointer_events == PointerEvents::None { - None - } else { - let hit_test_index = self.compositor_info.add_hit_test_info( - tag.node().0 as u64, - Some(cursor(inherited_ui.cursor.keyword, auto_cursor)), - ); - Some((hit_test_index as u64, 0u16)) + return None; } + + let hit_test_index = self.compositor_info.add_hit_test_info( + tag?.node.0 as u64, + Some(cursor(inherited_ui.cursor.keyword, auto_cursor)), + ); + Some((hit_test_index as u64, 0u16)) } } @@ -210,7 +216,7 @@ impl Fragment { } let mut common = builder.common_properties(rect.to_webrender(), &fragment.parent_style); - common.hit_info = builder.hit_info(&fragment.parent_style, fragment.tag, Cursor::Text); + common.hit_info = builder.hit_info(&fragment.parent_style, fragment.base.tag, Cursor::Text); let color = fragment.parent_style.clone_color(); let font_metrics = &fragment.font_metrics; @@ -431,7 +437,11 @@ impl<'a> BuilderForBoxFragment<'a> { } fn build_hit_test(&self, builder: &mut DisplayListBuilder) { - let hit_info = builder.hit_info(&self.fragment.style, self.fragment.tag, Cursor::Default); + let hit_info = builder.hit_info( + &self.fragment.style, + self.fragment.base.tag, + Cursor::Default, + ); if hit_info.is_some() { let mut common = builder.common_properties(self.border_rect, &self.fragment.style); common.hit_info = hit_info; @@ -443,7 +453,11 @@ impl<'a> BuilderForBoxFragment<'a> { } fn build_background(&mut self, builder: &mut DisplayListBuilder) { - if self.fragment.tag.node() == builder.element_for_canvas_background { + if self + .fragment + .base + .is_for_node(builder.element_for_canvas_background) + { // This background is already painted for the canvas, don’t paint it again here. return; } @@ -494,26 +508,32 @@ impl<'a> BuilderForBoxFragment<'a> { } }, Image::Url(ref image_url) => { - // FIXME: images won’t always have in intrinsic width or height - // when support for SVG is added. - // Or a WebRender `ImageKey`, for that matter. - let (width, height, key) = match image_url.url() { - Some(url) => { - match builder.context.get_webrender_image_for_url( - self.fragment.tag.node(), - url.clone(), - UsePlaceholder::No, - ) { - Some(WebRenderImageInfo { - width, - height, - key: Some(key), - }) => (width, height, key), - _ => continue, - } - }, + // FIXME: images won’t always have in intrinsic width or + // height when support for SVG is added, or a WebRender + // `ImageKey`, for that matter. + // + // FIXME: It feels like this should take into account the pseudo + // element and not just the node. + let node = match self.fragment.base.tag { + Some(tag) => tag.node, None => continue, }; + let image_url = match image_url.url() { + Some(url) => url.clone(), + None => continue, + }; + let (width, height, key) = match builder.context.get_webrender_image_for_url( + node, + image_url, + UsePlaceholder::No, + ) { + Some(WebRenderImageInfo { + width, + height, + key: Some(key), + }) => (width, height, key), + _ => continue, + }; // FIXME: https://drafts.csswg.org/css-images-4/#the-image-resolution let dppx = 1.0; diff --git a/components/layout_2020/display_list/stacking_context.rs b/components/layout_2020/display_list/stacking_context.rs index bbcf4f838d0..ec61f829374 100644 --- a/components/layout_2020/display_list/stacking_context.rs +++ b/components/layout_2020/display_list/stacking_context.rs @@ -384,8 +384,8 @@ impl StackingContext { // The `StackingContextFragment` we found is for the root DOM element: debug_assert_eq!( - box_fragment.tag.node(), - fragment_tree.canvas_background.root_element + fragment.tag().map(|tag| tag.node), + Some(fragment_tree.canvas_background.root_element), ); // The root element may have a CSS transform, @@ -868,8 +868,8 @@ impl BoxFragment { return None; } - let external_id = - wr::ExternalScrollId(self.tag.to_display_list_fragment_id(), wr.pipeline_id); + let tag = self.base.tag?; + let external_id = wr::ExternalScrollId(tag.to_display_list_fragment_id(), wr.pipeline_id); let sensitivity = if ComputedOverflow::Hidden == overflow_x && ComputedOverflow::Hidden == overflow_y { diff --git a/components/layout_2020/dom_traversal.rs b/components/layout_2020/dom_traversal.rs index 2e1d81ad8fb..a41bead9d0f 100644 --- a/components/layout_2020/dom_traversal.rs +++ b/components/layout_2020/dom_traversal.rs @@ -5,6 +5,7 @@ use crate::cell::ArcRefCell; use crate::context::LayoutContext; use crate::element_data::{LayoutBox, LayoutDataForElement}; +use crate::fragment_tree::{BaseFragmentInfo, FragmentFlags, Tag}; use crate::geom::PhysicalSize; use crate::replaced::{CanvasInfo, CanvasSource, ReplacedContent}; use crate::style_ext::{Display, DisplayGeneratingBox, DisplayInside, DisplayOutside}; @@ -74,6 +75,31 @@ impl<Node: Clone> NodeAndStyleInfo<Node> { } } +impl<'dom, Node> From<&NodeAndStyleInfo<Node>> for BaseFragmentInfo +where + Node: NodeExt<'dom>, +{ + fn from(info: &NodeAndStyleInfo<Node>) -> Self { + let pseudo = info.pseudo_element_type.map(|pseudo| match pseudo { + WhichPseudoElement::Before => PseudoElement::Before, + WhichPseudoElement::After => PseudoElement::After, + }); + + let threadsafe_node = info.node.to_threadsafe(); + let flags = match threadsafe_node.as_element() { + Some(element) if element.is_body_element_of_html_element_root() => { + FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT + }, + _ => FragmentFlags::empty(), + }; + + Self { + tag: Tag::new_pseudo(threadsafe_node.opaque(), pseudo), + flags, + } + } +} + pub(super) enum Contents { /// Refers to a DOM subtree, plus `::before` and `::after` pseudo-elements. OfElement, diff --git a/components/layout_2020/flexbox/construct.rs b/components/layout_2020/flexbox/construct.rs index 6c7c90635f2..e8848c1dc39 100644 --- a/components/layout_2020/flexbox/construct.rs +++ b/components/layout_2020/flexbox/construct.rs @@ -10,7 +10,6 @@ use crate::dom_traversal::{ }; use crate::element_data::LayoutBox; use crate::formatting_contexts::IndependentFormattingContext; -use crate::fragments::Tag; use crate::positioned::AbsolutelyPositionedBox; use crate::style_ext::DisplayGeneratingBox; use rayon::iter::{IntoParallelIterator, ParallelIterator}; @@ -150,7 +149,7 @@ where .info .new_replacing_style(anonymous_style.clone().unwrap()), runs.into_iter().map(|run| crate::flow::inline::TextRun { - tag: Tag::from_node_and_style_info(&run.info), + base_fragment_info: (&run.info).into(), text: run.text.into(), parent_style: run.info.style, }), diff --git a/components/layout_2020/flexbox/layout.rs b/components/layout_2020/flexbox/layout.rs index b6f450ac046..54f4da2669e 100644 --- a/components/layout_2020/flexbox/layout.rs +++ b/components/layout_2020/flexbox/layout.rs @@ -732,7 +732,7 @@ impl FlexLine<'_> { let margin = flex_context.sides_to_flow_relative(*margin); let collapsed_margin = CollapsedBlockMargins::from_margin(&margin); BoxFragment::new( - item.box_.tag(), + item.box_.base_fragment_info(), item.box_.style().clone(), fragments, content_rect, diff --git a/components/layout_2020/flow/construct.rs b/components/layout_2020/flow/construct.rs index 8241f2a0357..14b92cafa67 100644 --- a/components/layout_2020/flow/construct.rs +++ b/components/layout_2020/flow/construct.rs @@ -12,7 +12,6 @@ use crate::flow::float::FloatBox; use crate::flow::inline::{InlineBox, InlineFormattingContext, InlineLevelBox, TextRun}; use crate::flow::{BlockContainer, BlockFormattingContext, BlockLevelBox}; use crate::formatting_contexts::IndependentFormattingContext; -use crate::fragments::Tag; use crate::positioned::AbsolutelyPositionedBox; use crate::style_ext::{DisplayGeneratingBox, DisplayInside, DisplayOutside}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; @@ -384,7 +383,7 @@ where if let Some(text) = new_text_run_contents { inlines.push(ArcRefCell::new(InlineLevelBox::TextRun(TextRun { - tag: Tag::from_node_and_style_info(info), + base_fragment_info: info.into(), parent_style: Arc::clone(&info.style), text, }))) @@ -495,7 +494,7 @@ where // Whatever happened before, all we need to do before recurring // is to remember this ongoing inline level box. self.ongoing_inline_boxes_stack.push(InlineBox { - tag: Tag::from_node_and_style_info(info), + base_fragment_info: info.into(), style: info.style.clone(), first_fragment: true, last_fragment: false, @@ -556,7 +555,7 @@ where .rev() .map(|ongoing| { let fragmented = InlineBox { - tag: ongoing.tag, + base_fragment_info: ongoing.base_fragment_info, style: ongoing.style.clone(), first_fragment: ongoing.first_fragment, // The fragmented boxes before the block level element @@ -755,7 +754,7 @@ where BlockLevelCreator::SameFormattingContextBlock(contents) => { let (contents, contains_floats) = contents.finish(context, info); let block_level_box = ArcRefCell::new(BlockLevelBox::SameFormattingContextBlock { - tag: Tag::from_node_and_style_info(info), + base_fragment_info: info.into(), contents, style: Arc::clone(&info.style), }); diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index 498a63e88b0..740c34cf7c0 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -7,9 +7,9 @@ use crate::context::LayoutContext; use crate::flow::float::FloatBox; use crate::flow::FlowLayout; use crate::formatting_contexts::IndependentFormattingContext; +use crate::fragment_tree::BaseFragmentInfo; use crate::fragments::{ - AnonymousFragment, BoxFragment, CollapsedBlockMargins, DebugId, FontMetrics, Fragment, Tag, - TextFragment, + AnonymousFragment, BoxFragment, CollapsedBlockMargins, FontMetrics, Fragment, TextFragment, }; use crate::geom::flow_relative::{Rect, Sides, Vec2}; use crate::positioned::{ @@ -51,7 +51,7 @@ pub(crate) enum InlineLevelBox { #[derive(Debug, Serialize)] pub(crate) struct InlineBox { - pub tag: Tag, + pub base_fragment_info: BaseFragmentInfo, #[serde(skip_serializing)] pub style: Arc<ComputedValues>, pub first_fragment: bool, @@ -62,7 +62,7 @@ pub(crate) struct InlineBox { /// https://www.w3.org/TR/css-display-3/#css-text-run #[derive(Debug, Serialize)] pub(crate) struct TextRun { - pub tag: Tag, + pub base_fragment_info: BaseFragmentInfo, #[serde(skip_serializing)] pub parent_style: Arc<ComputedValues>, pub text: String, @@ -82,7 +82,7 @@ struct InlineNestingLevelState<'box_tree> { } struct PartialInlineBoxFragment<'box_tree> { - tag: Tag, + base_fragment_info: BaseFragmentInfo, style: Arc<ComputedValues>, start_corner: Vec2<Length>, padding: Sides<Length>, @@ -471,7 +471,7 @@ impl InlineBox { let text_decoration_line = ifc.current_nesting_level.text_decoration_line | style.clone_text_decoration_line(); PartialInlineBoxFragment { - tag: self.tag, + base_fragment_info: self.base_fragment_info, style, start_corner, padding, @@ -512,7 +512,7 @@ impl<'box_tree> PartialInlineBoxFragment<'box_tree> { }; let mut fragment = BoxFragment::new( - self.tag, + self.base_fragment_info, self.style.clone(), std::mem::take(&mut nesting_level.fragments_so_far), content_rect, @@ -580,7 +580,7 @@ fn layout_atomic( .make_fragments(&replaced.style, size.clone()); let content_rect = Rect { start_corner, size }; BoxFragment::new( - replaced.tag, + replaced.base_fragment_info, replaced.style.clone(), fragments, content_rect, @@ -655,7 +655,7 @@ fn layout_atomic( }, }; BoxFragment::new( - non_replaced.tag, + non_replaced.base_fragment_info, non_replaced.style.clone(), independent_layout.fragments, content_rect, @@ -836,8 +836,7 @@ impl TextRun { ifc.current_nesting_level .fragments_so_far .push(Fragment::Text(TextFragment { - tag: self.tag, - debug_id: DebugId::new(), + base: self.base_fragment_info.into(), parent_style: self.parent_style.clone(), rect, font_metrics, diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs index c2ce669c51d..11af4147ce0 100644 --- a/components/layout_2020/flow/mod.rs +++ b/components/layout_2020/flow/mod.rs @@ -11,8 +11,9 @@ use crate::flow::inline::InlineFormattingContext; use crate::formatting_contexts::{ IndependentFormattingContext, IndependentLayout, NonReplacedFormattingContext, }; +use crate::fragment_tree::BaseFragmentInfo; use crate::fragments::{ - AnonymousFragment, BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment, Tag, + AnonymousFragment, BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment, }; use crate::geom::flow_relative::{Rect, Sides, Vec2}; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext}; @@ -50,7 +51,7 @@ pub(crate) enum BlockContainer { #[derive(Debug, Serialize)] pub(crate) enum BlockLevelBox { SameFormattingContextBlock { - tag: Tag, + base_fragment_info: BaseFragmentInfo, #[serde(skip_serializing)] style: Arc<ComputedValues>, contents: BlockContainer, @@ -306,7 +307,7 @@ impl BlockLevelBox { ) -> Fragment { match self { BlockLevelBox::SameFormattingContextBlock { - tag, + base_fragment_info: tag, style, contents, } => Fragment::Box(positioning_context.layout_maybe_position_relative_fragment( @@ -335,7 +336,7 @@ impl BlockLevelBox { |_positioning_context| { layout_in_flow_replaced_block_level( containing_block, - replaced.tag, + replaced.base_fragment_info, &replaced.style, &replaced.contents, ) @@ -352,7 +353,7 @@ impl BlockLevelBox { layout_context, positioning_context, containing_block, - non_replaced.tag, + non_replaced.base_fragment_info, &non_replaced.style, NonReplacedContents::EstablishesAnIndependentFormattingContext( non_replaced, @@ -420,7 +421,7 @@ fn layout_in_flow_non_replaced_block_level( layout_context: &LayoutContext, positioning_context: &mut PositioningContext, containing_block: &ContainingBlock, - tag: Tag, + base_fragment_info: BaseFragmentInfo, style: &Arc<ComputedValues>, block_level_kind: NonReplacedContents, tree_rank: usize, @@ -559,7 +560,7 @@ fn layout_in_flow_non_replaced_block_level( }, }; BoxFragment::new( - tag, + base_fragment_info, style.clone(), fragments, content_rect, @@ -575,7 +576,7 @@ fn layout_in_flow_non_replaced_block_level( /// https://drafts.csswg.org/css2/visudet.html#inline-replaced-height fn layout_in_flow_replaced_block_level<'a>( containing_block: &ContainingBlock, - tag: Tag, + base_fragment_info: BaseFragmentInfo, style: &Arc<ComputedValues>, replaced: &ReplacedContent, ) -> BoxFragment { @@ -600,7 +601,7 @@ fn layout_in_flow_replaced_block_level<'a>( }; let block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin); BoxFragment::new( - tag, + base_fragment_info, style.clone(), fragments, content_rect, diff --git a/components/layout_2020/flow/root.rs b/components/layout_2020/flow/root.rs index e76f1b8da78..c0ca277167c 100644 --- a/components/layout_2020/flow/root.rs +++ b/components/layout_2020/flow/root.rs @@ -15,7 +15,8 @@ use crate::flow::float::FloatBox; use crate::flow::inline::InlineLevelBox; use crate::flow::{BlockContainer, BlockFormattingContext, BlockLevelBox}; use crate::formatting_contexts::IndependentFormattingContext; -use crate::fragments::{Fragment, Tag}; +use crate::fragment_tree::Tag; +use crate::fragments::Fragment; use crate::geom::flow_relative::Vec2; use crate::geom::{PhysicalPoint, PhysicalRect, PhysicalSize}; use crate::positioned::AbsolutelyPositionedBox; @@ -36,7 +37,6 @@ use servo_arc::Arc; use style::animation::AnimationSetKey; use style::dom::OpaqueNode; use style::properties::ComputedValues; -use style::selector_parser::PseudoElement; use style::values::computed::Length; use style_traits::CSSPixel; use webrender_api::{ClipId, SpaceAndClipInfo, SpatialId}; @@ -466,19 +466,15 @@ impl FragmentTree { pub fn remove_nodes_in_fragment_tree_from_set(&self, set: &mut FxHashSet<AnimationSetKey>) { self.find(|fragment, _, _| { - let (node, pseudo) = match fragment.tag()? { - Tag::Node(node) => (node, None), - Tag::BeforePseudo(node) => (node, Some(PseudoElement::Before)), - Tag::AfterPseudo(node) => (node, Some(PseudoElement::After)), - }; - set.remove(&AnimationSetKey::new(node, pseudo)); + let tag = fragment.tag()?; + set.remove(&AnimationSetKey::new(tag.node, tag.pseudo)); None::<()> }); } pub fn get_content_box_for_node(&self, requested_node: OpaqueNode) -> Rect<Au> { let mut bounding_box = PhysicalRect::zero(); - let tag_to_find = Tag::Node(requested_node); + let tag_to_find = Tag::new(requested_node); self.find(|fragment, _, containing_block| { if fragment.tag() != Some(tag_to_find) { return None::<()>; @@ -516,17 +512,15 @@ impl FragmentTree { } pub fn get_border_dimensions_for_node(&self, requested_node: OpaqueNode) -> Rect<i32> { + let tag_to_find = Tag::new(requested_node); self.find(|fragment, _, containing_block| { + if fragment.tag() != Some(tag_to_find) { + return None; + } + let (style, padding_rect) = match fragment { - Fragment::Box(fragment) if fragment.tag.node() == requested_node => { - (&fragment.style, fragment.padding_rect()) - }, - Fragment::AbsoluteOrFixedPositioned(_) | - Fragment::Box(_) | - Fragment::Text(_) | - Fragment::Image(_) | - Fragment::IFrame(_) | - Fragment::Anonymous(_) => return None, + Fragment::Box(fragment) => (&fragment.style, fragment.padding_rect()), + _ => return None, }; // https://drafts.csswg.org/cssom-view/#dom-element-clienttop @@ -559,7 +553,7 @@ impl FragmentTree { pub fn get_scroll_area_for_node(&self, requested_node: OpaqueNode) -> Rect<i32> { let mut scroll_area: PhysicalRect<Length> = PhysicalRect::zero(); - let tag_to_find = Tag::Node(requested_node); + let tag_to_find = Tag::new(requested_node); self.find(|fragment, _, containing_block| { if fragment.tag() != Some(tag_to_find) { return None::<()>; diff --git a/components/layout_2020/formatting_contexts.rs b/components/layout_2020/formatting_contexts.rs index a311eb8a3a2..98326692b27 100644 --- a/components/layout_2020/formatting_contexts.rs +++ b/components/layout_2020/formatting_contexts.rs @@ -6,7 +6,8 @@ use crate::context::LayoutContext; use crate::dom_traversal::{Contents, NodeAndStyleInfo, NodeExt}; use crate::flexbox::FlexContainer; use crate::flow::BlockFormattingContext; -use crate::fragments::{Fragment, Tag}; +use crate::fragment_tree::BaseFragmentInfo; +use crate::fragments::Fragment; use crate::positioned::PositioningContext; use crate::replaced::ReplacedContent; use crate::sizing::{self, ContentSizes}; @@ -28,7 +29,7 @@ pub(crate) enum IndependentFormattingContext { #[derive(Debug, Serialize)] pub(crate) struct NonReplacedFormattingContext { - pub tag: Tag, + pub base_fragment_info: BaseFragmentInfo, #[serde(skip_serializing)] pub style: Arc<ComputedValues>, /// If it was requested during construction @@ -38,7 +39,7 @@ pub(crate) struct NonReplacedFormattingContext { #[derive(Debug, Serialize)] pub(crate) struct ReplacedFormattingContext { - pub tag: Tag, + pub base_fragment_info: BaseFragmentInfo, #[serde(skip_serializing)] pub style: Arc<ComputedValues>, pub contents: ReplacedContent, @@ -63,7 +64,7 @@ pub(crate) struct IndependentLayout { impl IndependentFormattingContext { pub fn construct<'dom>( context: &LayoutContext, - info: &NodeAndStyleInfo<impl NodeExt<'dom>>, + node_and_style_info: &NodeAndStyleInfo<impl NodeExt<'dom>>, display_inside: DisplayInside, contents: Contents, propagated_text_decoration_line: TextDecorationLine, @@ -76,7 +77,7 @@ impl IndependentFormattingContext { NonReplacedFormattingContextContents::Flow( BlockFormattingContext::construct( context, - info, + node_and_style_info, non_replaced, propagated_text_decoration_line, is_list_item, @@ -86,37 +87,37 @@ impl IndependentFormattingContext { DisplayInside::Flex => { NonReplacedFormattingContextContents::Flex(FlexContainer::construct( context, - info, + node_and_style_info, non_replaced, propagated_text_decoration_line, )) }, }; Self::NonReplaced(NonReplacedFormattingContext { - tag: Tag::from_node_and_style_info(info), - style: Arc::clone(&info.style), + base_fragment_info: node_and_style_info.into(), + style: Arc::clone(&node_and_style_info.style), content_sizes: None, contents, }) }, Err(contents) => Self::Replaced(ReplacedFormattingContext { - tag: Tag::from_node_and_style_info(info), - style: Arc::clone(&info.style), + base_fragment_info: node_and_style_info.into(), + style: Arc::clone(&node_and_style_info.style), contents, }), } } pub fn construct_for_text_runs<'dom>( - info: &NodeAndStyleInfo<impl NodeExt<'dom>>, + node_and_style_info: &NodeAndStyleInfo<impl NodeExt<'dom>>, runs: impl Iterator<Item = crate::flow::inline::TextRun>, propagated_text_decoration_line: TextDecorationLine, ) -> Self { let bfc = BlockFormattingContext::construct_for_text_runs(runs, propagated_text_decoration_line); Self::NonReplaced(NonReplacedFormattingContext { - tag: Tag::from_node_and_style_info(info), - style: Arc::clone(&info.style), + base_fragment_info: node_and_style_info.into(), + style: Arc::clone(&node_and_style_info.style), content_sizes: None, contents: NonReplacedFormattingContextContents::Flow(bfc), }) @@ -129,10 +130,10 @@ impl IndependentFormattingContext { } } - pub fn tag(&self) -> Tag { + pub fn base_fragment_info(&self) -> BaseFragmentInfo { match self { - Self::NonReplaced(inner) => inner.tag, - Self::Replaced(inner) => inner.tag, + Self::NonReplaced(inner) => inner.base_fragment_info, + Self::Replaced(inner) => inner.base_fragment_info, } } diff --git a/components/layout_2020/fragment_tree/base.rs b/components/layout_2020/fragment_tree/base.rs new file mode 100644 index 00000000000..d9271a7a0f6 --- /dev/null +++ b/components/layout_2020/fragment_tree/base.rs @@ -0,0 +1,117 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::layout_debug::DebugId; +use bitflags::bitflags; +use gfx_traits::{combine_id_with_fragment_type, FragmentType}; +use style::dom::OpaqueNode; +use style::selector_parser::PseudoElement; + +/// This data structure stores fields that are common to all non-base +/// Fragment types and should generally be the first member of all +/// concrete fragments. +#[derive(Debug, Serialize)] +pub(crate) struct BaseFragment { + /// A tag which identifies the DOM node and pseudo element of this + /// Fragment's content. If this fragment isn't related to any DOM + /// node at all, the tag will be None. + pub tag: Option<Tag>, + + /// An id used to uniquely identify this Fragment in debug builds. + pub debug_id: DebugId, + + /// Flags which various information about this fragment used during + /// layout. + pub flags: FragmentFlags, +} + +impl BaseFragment { + pub(crate) fn anonymous() -> Self { + BaseFragment { + tag: None, + debug_id: DebugId::new(), + flags: FragmentFlags::empty(), + } + } + + /// Returns true if this fragment is non-anonymous and it is for the given + /// OpaqueNode, regardless of the pseudo element. + pub(crate) fn is_for_node(&self, node: OpaqueNode) -> bool { + self.tag.map(|tag| tag.node == node).unwrap_or(false) + } +} + +/// Information necessary to construct a new BaseFragment. +#[derive(Clone, Copy, Debug, Serialize)] +pub(crate) struct BaseFragmentInfo { + /// The tag to use for the new BaseFragment. + pub tag: Tag, + + /// The flags to use for the new BaseFragment. + pub flags: FragmentFlags, +} + +impl BaseFragmentInfo { + pub(crate) fn new_for_node(node: OpaqueNode) -> Self { + Self { + tag: Tag::new(node), + flags: FragmentFlags::empty(), + } + } +} + +impl From<BaseFragmentInfo> for BaseFragment { + fn from(info: BaseFragmentInfo) -> Self { + Self { + tag: Some(info.tag), + debug_id: DebugId::new(), + flags: info.flags, + } + } +} + +bitflags! { + #[doc = "Flags used to track various information about a DOM node during layout."] + #[derive(Serialize)] + pub(crate) struct FragmentFlags: u8 { + #[doc = "Whether or not this node is a body element on an HTML document."] + const IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT = 0b00000001; + } +} + +/// A data structure used to hold DOM and pseudo-element information about +/// a particular layout object. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)] +pub(crate) struct Tag { + pub(crate) node: OpaqueNode, + pub(crate) pseudo: Option<PseudoElement>, +} + +impl Tag { + /// Create a new Tag for a non-pseudo element. This is mainly used for + /// matching existing tags, since it does not accept an `info` argument. + pub(crate) fn new(node: OpaqueNode) -> Self { + Tag { node, pseudo: None } + } + + /// Create a new Tag for a pseudo element. This is mainly used for + /// matching existing tags, since it does not accept an `info` argument. + pub(crate) fn new_pseudo(node: OpaqueNode, pseudo: Option<PseudoElement>) -> Self { + Tag { node, pseudo } + } + + /// Returns true if this tag is for a pseudo element. + pub(crate) fn is_pseudo(&self) -> bool { + self.pseudo.is_some() + } + + pub(crate) fn to_display_list_fragment_id(&self) -> u64 { + let fragment_type = match self.pseudo { + Some(PseudoElement::Before) => FragmentType::BeforePseudoContent, + Some(PseudoElement::After) => FragmentType::AfterPseudoContent, + _ => FragmentType::FragmentBody, + }; + combine_id_with_fragment_type(self.node.id() as usize, fragment_type) as u64 + } +} diff --git a/components/layout_2020/fragment_tree/mod.rs b/components/layout_2020/fragment_tree/mod.rs new file mode 100644 index 00000000000..1d7425655cb --- /dev/null +++ b/components/layout_2020/fragment_tree/mod.rs @@ -0,0 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +mod base; + +pub(crate) use base::*; diff --git a/components/layout_2020/fragments.rs b/components/layout_2020/fragments.rs index 687bdbe083e..53e6d93cdc1 100644 --- a/components/layout_2020/fragments.rs +++ b/components/layout_2020/fragments.rs @@ -3,23 +3,17 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::cell::ArcRefCell; -use crate::dom_traversal::{NodeAndStyleInfo, NodeExt, WhichPseudoElement}; +use crate::fragment_tree::{BaseFragment, BaseFragmentInfo, Tag}; use crate::geom::flow_relative::{Rect, Sides}; use crate::geom::{PhysicalPoint, PhysicalRect}; -#[cfg(debug_assertions)] -use crate::layout_debug; use crate::positioned::HoistedSharedFragment; use gfx::font::FontMetrics as GfxFontMetrics; use gfx::text::glyph::GlyphStore; use gfx_traits::print_tree::PrintTree; -use gfx_traits::{combine_id_with_fragment_type, FragmentType}; use msg::constellation_msg::{BrowsingContextId, PipelineId}; -#[cfg(not(debug_assertions))] -use serde::ser::{Serialize, Serializer}; use servo_arc::Arc as ServoArc; use std::sync::Arc; use style::computed_values::overflow_x::T as ComputedOverflow; -use style::dom::OpaqueNode; use style::logical_geometry::WritingMode; use style::properties::ComputedValues; use style::values::computed::Length; @@ -27,42 +21,6 @@ use style::values::specified::text::TextDecorationLine; use style::Zero; use webrender_api::{FontInstanceKey, ImageKey}; -#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)] -pub(crate) enum Tag { - Node(OpaqueNode), - BeforePseudo(OpaqueNode), - AfterPseudo(OpaqueNode), -} - -impl Tag { - pub(crate) fn node(&self) -> OpaqueNode { - match self { - Self::Node(node) | Self::AfterPseudo(node) | Self::BeforePseudo(node) => *node, - } - } - - pub(crate) fn to_display_list_fragment_id(&self) -> u64 { - let (node, content_type) = match self { - Self::Node(node) => (node, FragmentType::FragmentBody), - Self::AfterPseudo(node) => (node, FragmentType::BeforePseudoContent), - Self::BeforePseudo(node) => (node, FragmentType::AfterPseudoContent), - }; - combine_id_with_fragment_type(node.id() as usize, content_type) as u64 - } - - pub(crate) fn from_node_and_style_info<'dom, Node>(info: &NodeAndStyleInfo<Node>) -> Self - where - Node: NodeExt<'dom>, - { - let opaque_node = info.node.as_opaque(); - match info.pseudo_element_type { - None => Self::Node(opaque_node), - Some(WhichPseudoElement::Before) => Self::BeforePseudo(opaque_node), - Some(WhichPseudoElement::After) => Self::AfterPseudo(opaque_node), - } - } -} - #[derive(Serialize)] pub(crate) enum Fragment { Box(BoxFragment), @@ -82,8 +40,8 @@ pub(crate) enum Fragment { #[derive(Serialize)] pub(crate) struct BoxFragment { - pub tag: Tag, - pub debug_id: DebugId, + pub base: BaseFragment, + #[serde(skip_serializing)] pub style: ServoArc<ComputedValues>, pub children: Vec<ArcRefCell<Fragment>>, @@ -119,7 +77,7 @@ pub(crate) struct CollapsedMargin { /// Can contain child fragments with relative coordinates, but does not contribute to painting itself. #[derive(Serialize)] pub(crate) struct AnonymousFragment { - pub debug_id: DebugId, + pub base: BaseFragment, pub rect: Rect<Length>, pub children: Vec<ArcRefCell<Fragment>>, pub mode: WritingMode, @@ -153,8 +111,7 @@ impl From<&GfxFontMetrics> for FontMetrics { #[derive(Serialize)] pub(crate) struct TextFragment { - pub debug_id: DebugId, - pub tag: Tag, + pub base: BaseFragment, #[serde(skip_serializing)] pub parent_style: ServoArc<ComputedValues>, pub rect: Rect<Length>, @@ -168,7 +125,7 @@ pub(crate) struct TextFragment { #[derive(Serialize)] pub(crate) struct ImageFragment { - pub debug_id: DebugId, + pub base: BaseFragment, #[serde(skip_serializing)] pub style: ServoArc<ComputedValues>, pub rect: Rect<Length>, @@ -178,7 +135,7 @@ pub(crate) struct ImageFragment { #[derive(Serialize)] pub(crate) struct IFrameFragment { - pub debug_id: DebugId, + pub base: BaseFragment, pub pipeline_id: PipelineId, pub browsing_context_id: BrowsingContextId, pub rect: Rect<Length>, @@ -200,15 +157,19 @@ impl Fragment { position.inline += *offset; } + pub fn base(&self) -> Option<&BaseFragment> { + Some(match self { + Fragment::Box(fragment) => &fragment.base, + Fragment::Text(fragment) => &fragment.base, + Fragment::AbsoluteOrFixedPositioned(_) => return None, + Fragment::Anonymous(fragment) => &fragment.base, + Fragment::Image(fragment) => &fragment.base, + Fragment::IFrame(fragment) => &fragment.base, + }) + } + pub fn tag(&self) -> Option<Tag> { - match self { - Fragment::Box(fragment) => Some(fragment.tag), - Fragment::Text(fragment) => Some(fragment.tag), - Fragment::AbsoluteOrFixedPositioned(_) | - Fragment::Anonymous(_) | - Fragment::Image(_) | - Fragment::IFrame(_) => None, - } + self.base().and_then(|base| base.tag) } pub fn print(&self, tree: &mut PrintTree) { @@ -285,7 +246,7 @@ impl Fragment { impl AnonymousFragment { pub fn no_op(mode: WritingMode) -> Self { Self { - debug_id: DebugId::new(), + base: BaseFragment::anonymous(), children: vec![], rect: Rect::zero(), mode, @@ -306,7 +267,7 @@ impl AnonymousFragment { ) }); AnonymousFragment { - debug_id: DebugId::new(), + base: BaseFragment::anonymous(), rect, children: children .into_iter() @@ -334,7 +295,7 @@ impl AnonymousFragment { impl BoxFragment { pub fn new( - tag: Tag, + base_fragment_info: BaseFragmentInfo, style: ServoArc<ComputedValues>, children: Vec<Fragment>, content_rect: Rect<Length>, @@ -351,8 +312,7 @@ impl BoxFragment { acc.union(&child.scrollable_overflow(&containing_block)) }); BoxFragment { - tag, - debug_id: DebugId::new(), + base: base_fragment_info.into(), style, children: children .into_iter() @@ -397,12 +357,14 @@ impl BoxFragment { pub fn print(&self, tree: &mut PrintTree) { tree.new_level(format!( "Box\ + \nbase={:?}\ \ncontent={:?}\ \npadding rect={:?}\ \nborder rect={:?}\ \nscrollable_overflow={:?}\ \noverflow={:?} / {:?}\ \nstyle={:p}", + self.base, self.content_rect, self.padding_rect(), self.border_rect(), @@ -534,33 +496,3 @@ impl CollapsedMargin { self.max_positive + self.min_negative } } - -#[cfg(not(debug_assertions))] -#[derive(Clone)] -pub struct DebugId; - -#[cfg(debug_assertions)] -#[derive(Clone, Serialize)] -#[serde(transparent)] -pub struct DebugId(u16); - -#[cfg(not(debug_assertions))] -impl DebugId { - pub fn new() -> DebugId { - DebugId - } -} - -#[cfg(debug_assertions)] -impl DebugId { - pub fn new() -> DebugId { - DebugId(layout_debug::generate_unique_debug_id()) - } -} - -#[cfg(not(debug_assertions))] -impl Serialize for DebugId { - fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { - serializer.serialize_str(&format!("{:p}", &self)) - } -} diff --git a/components/layout_2020/layout_debug.rs b/components/layout_2020/layout_debug.rs index 79762f8b757..21636f32a89 100644 --- a/components/layout_2020/layout_debug.rs +++ b/components/layout_2020/layout_debug.rs @@ -6,6 +6,8 @@ //! that can be viewed by an external tool to make layout debugging easier. use crate::flow::{BoxTree, FragmentTree}; +#[cfg(not(debug_assertions))] +use serde::ser::{Serialize, Serializer}; use serde_json::{to_string, to_value, Value}; use std::cell::RefCell; use std::fs; @@ -101,12 +103,6 @@ impl Drop for Scope { } } -/// Generate a unique ID for Fragments. -#[cfg(debug_assertions)] -pub fn generate_unique_debug_id() -> u16 { - DEBUG_ID_COUNTER.fetch_add(1, Ordering::SeqCst) as u16 -} - /// Begin a layout debug trace. If this has not been called, /// creating debug scopes has no effect. pub fn begin_trace(box_tree: Arc<BoxTree>, fragment_tree: Arc<FragmentTree>) { @@ -146,3 +142,33 @@ pub fn end_trace(generation: u32) { ) .unwrap(); } + +#[cfg(not(debug_assertions))] +#[derive(Clone, Debug)] +pub struct DebugId; + +#[cfg(debug_assertions)] +#[derive(Clone, Debug, Serialize)] +#[serde(transparent)] +pub struct DebugId(u16); + +#[cfg(not(debug_assertions))] +impl DebugId { + pub fn new() -> DebugId { + DebugId + } +} + +#[cfg(debug_assertions)] +impl DebugId { + pub fn new() -> DebugId { + DebugId(DEBUG_ID_COUNTER.fetch_add(1, Ordering::SeqCst) as u16) + } +} + +#[cfg(not(debug_assertions))] +impl Serialize for DebugId { + fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { + serializer.serialize_str(&format!("{:p}", &self)) + } +} diff --git a/components/layout_2020/lib.rs b/components/layout_2020/lib.rs index 059e7923196..a2ceea5fc36 100644 --- a/components/layout_2020/lib.rs +++ b/components/layout_2020/lib.rs @@ -18,6 +18,7 @@ pub mod element_data; mod flexbox; pub mod flow; mod formatting_contexts; +mod fragment_tree; mod fragments; pub mod geom; #[macro_use] diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index 883f783dfd8..744086ebc93 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -632,7 +632,7 @@ impl HoistedAbsolutelyPositionedBox { }; BoxFragment::new( - absolutely_positioned_box.context.tag(), + absolutely_positioned_box.context.base_fragment_info(), absolutely_positioned_box.context.style().clone(), fragments, content_rect, diff --git a/components/layout_2020/query.rs b/components/layout_2020/query.rs index f59163a3a7c..769bb9641d8 100644 --- a/components/layout_2020/query.rs +++ b/components/layout_2020/query.rs @@ -5,7 +5,8 @@ //! Utilities for querying the layout, as needed by the layout thread. use crate::context::LayoutContext; use crate::flow::FragmentTree; -use crate::fragments::{Fragment, Tag}; +use crate::fragment_tree::{FragmentFlags, Tag}; +use crate::fragments::Fragment; use app_units::Au; use euclid::default::{Point2D, Rect}; use euclid::Size2D; @@ -263,13 +264,7 @@ pub fn process_resolved_style_request<'dom>( let computed_style = || style.computed_value_to_string(PropertyDeclarationId::Longhand(longhand_id)); - let opaque = node.opaque(); - let tag_to_find = match *pseudo { - None => Tag::Node(opaque), - Some(PseudoElement::Before) => Tag::BeforePseudo(opaque), - Some(PseudoElement::After) => Tag::AfterPseudo(opaque), - Some(_) => unreachable!("Should have returned before this point."), - }; + let tag_to_find = Tag::new_pseudo(node.opaque(), *pseudo); // https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle // Here we are trying to conform to the specification that says that getComputedStyle @@ -302,8 +297,12 @@ pub fn process_resolved_style_request<'dom>( }; fragment_tree .find(|fragment, _, containing_block| { + if Some(tag_to_find) != fragment.tag() { + return None; + } + let box_fragment = match fragment { - Fragment::Box(ref box_fragment) if box_fragment.tag == tag_to_find => box_fragment, + Fragment::Box(ref box_fragment) => box_fragment, _ => return None, }; @@ -399,13 +398,14 @@ fn process_offset_parent_query_inner( // https://www.w3.org/TR/2016/WD-cssom-view-1-20160317/#extensions-to-the-htmlelement-interface let mut parent_node_addresses = Vec::new(); + let tag_to_find = Tag::new(node); let node_offset_box = fragment_tree.find(|fragment, level, containing_block| { - // FIXME: Is there a less fragile way of checking whether this - // fragment is the body element, rather than just checking that - // it's at level 1 (below the root node)? - let is_body_element = level == 1; + let base = fragment.base()?; + let is_body_element = base + .flags + .contains(FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT); - if fragment.tag() == Some(Tag::Node(node)) { + if fragment.tag() == Some(tag_to_find) { // Only consider the first fragment of the node found as per a // possible interpretation of the specification: "[...] return the // y-coordinate of the top border edge of the first CSS layout box @@ -497,10 +497,9 @@ fn process_offset_parent_query_inner( (false, Position::Static) => false, }; - if let Tag::Node(node_address) = fragment.tag { - is_eligible_parent.then(|| node_address) - } else { - None + match base.tag { + Some(tag) if is_eligible_parent && !tag.is_pseudo() => Some(tag.node), + _ => None, } }, Fragment::AbsoluteOrFixedPositioned(_) | @@ -533,11 +532,12 @@ fn process_offset_parent_query_inner( // // Since we saw `offset_parent_node_address` once, we should be able // to find it again. + let offset_parent_node_tag = Tag::new(offset_parent_node_address); fragment_tree .find(|fragment, _, containing_block| { match fragment { Fragment::Box(fragment) - if fragment.tag == Tag::Node(offset_parent_node_address) => + if fragment.base.tag == Some(offset_parent_node_tag) => { // Again, take the *first* associated CSS layout box. let padding_box_corner = fragment diff --git a/components/layout_2020/replaced.rs b/components/layout_2020/replaced.rs index 907af6e67e9..0bea12eaf2d 100644 --- a/components/layout_2020/replaced.rs +++ b/components/layout_2020/replaced.rs @@ -4,7 +4,8 @@ use crate::context::LayoutContext; use crate::dom_traversal::NodeExt; -use crate::fragments::{DebugId, Fragment, IFrameFragment, ImageFragment}; +use crate::fragment_tree::BaseFragmentInfo; +use crate::fragments::{Fragment, IFrameFragment, ImageFragment}; use crate::geom::flow_relative::{Rect, Vec2}; use crate::geom::PhysicalSize; use crate::sizing::ContentSizes; @@ -29,6 +30,7 @@ use webrender_api::ImageKey; pub(crate) struct ReplacedContent { pub kind: ReplacedContentKind, intrinsic: IntrinsicSizes, + base_fragment_info: BaseFragmentInfo, } /// * Raster images always have an intrinsic width and height, with 1 image pixel = 1px. @@ -140,7 +142,12 @@ impl ReplacedContent { }, ); - return Some(Self { kind, intrinsic }); + let base_fragment_info = BaseFragmentInfo::new_for_node(element.as_opaque()); + return Some(Self { + kind, + intrinsic, + base_fragment_info, + }); } pub fn from_image_url<'dom>( @@ -171,6 +178,7 @@ impl ReplacedContent { // FIXME https://github.com/w3c/csswg-drafts/issues/4572 ratio: Some(width / height), }, + base_fragment_info: BaseFragmentInfo::new_for_node(element.as_opaque()), }); } None @@ -219,7 +227,7 @@ impl ReplacedContent { .and_then(|image| image.id) .map(|image_key| { Fragment::Image(ImageFragment { - debug_id: DebugId::new(), + base: self.base_fragment_info.into(), style: style.clone(), rect: Rect { start_corner: Vec2::zero(), @@ -232,7 +240,7 @@ impl ReplacedContent { .collect(), ReplacedContentKind::IFrame(iframe) => { vec![Fragment::IFrame(IFrameFragment { - debug_id: DebugId::new(), + base: self.base_fragment_info.into(), style: style.clone(), pipeline_id: iframe.pipeline_id, browsing_context_id: iframe.browsing_context_id, @@ -268,7 +276,7 @@ impl ReplacedContent { }, }; vec![Fragment::Image(ImageFragment { - debug_id: DebugId::new(), + base: self.base_fragment_info.into(), style: style.clone(), rect: Rect { start_corner: Vec2::zero(), diff --git a/components/layout_thread/dom_wrapper.rs b/components/layout_thread/dom_wrapper.rs index 5c700333c1d..35e7fa3b8ec 100644 --- a/components/layout_thread/dom_wrapper.rs +++ b/components/layout_thread/dom_wrapper.rs @@ -420,6 +420,39 @@ impl<'le> fmt::Debug for ServoLayoutElement<'le> { } } +impl<'dom> ServoLayoutElement<'dom> { + /// Returns true if this element is the body child of an html element root element. + fn is_body_element_of_html_element_root(&self) -> bool { + if self.element.local_name() != &local_name!("body") { + return false; + } + + self.parent_element() + .map(|element| { + element.is_root() && element.element.local_name() == &local_name!("html") + }) + .unwrap_or(false) + } + + /// Returns the parent element of this element, if it has one. + fn parent_element(&self) -> Option<Self> { + self.element + .upcast() + .composed_parent_node_ref() + .and_then(as_element) + } + + fn is_root(&self) -> bool { + match self.as_node().parent_node() { + None => false, + Some(node) => match node.script_type_id() { + NodeTypeId::Document(_) => true, + _ => false, + }, + } + } +} + impl<'le> TElement for ServoLayoutElement<'le> { type ConcreteNode = ServoLayoutNode<'le>; type TraversalChildrenIterator = DomChildren<Self::ConcreteNode>; @@ -671,11 +704,7 @@ impl<'le> TElement for ServoLayoutElement<'le> { } fn is_html_document_body_element(&self) -> bool { - // This is only used for the "tables inherit from body" quirk, which we - // don't implement. - // - // FIXME(emilio): We should be able to give the right answer though! - false + self.is_body_element_of_html_element_root() } fn synthesize_presentational_hints_for_legacy_attributes<V>( @@ -770,10 +799,7 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> { } fn parent_element(&self) -> Option<ServoLayoutElement<'le>> { - self.element - .upcast() - .composed_parent_node_ref() - .and_then(as_element) + ServoLayoutElement::parent_element(self) } fn parent_node_is_shadow_root(&self) -> bool { @@ -831,13 +857,7 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> { } fn is_root(&self) -> bool { - match self.as_node().parent_node() { - None => false, - Some(node) => match node.script_type_id() { - NodeTypeId::Document(_) => true, - _ => false, - }, - } + ServoLayoutElement::is_root(self) } fn is_empty(&self) -> bool { @@ -973,11 +993,7 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> { } fn is_html_element_in_html_document(&self) -> bool { - if !self.element.is_html_element() { - return false; - } - - self.as_node().owner_doc().is_html_document() + self.element.is_html_element() && self.as_node().owner_doc().is_html_document() } } @@ -1350,6 +1366,10 @@ impl<'le> ThreadSafeLayoutElement<'le> for ServoThreadSafeLayoutElement<'le> { fn is_shadow_host(&self) -> bool { self.element.shadow_root().is_some() } + + fn is_body_element_of_html_element_root(&self) -> bool { + self.element.is_html_document_body_element() + } } /// This implementation of `::selectors::Element` is used for implementing lazy diff --git a/components/layout_thread_2020/dom_wrapper.rs b/components/layout_thread_2020/dom_wrapper.rs index e37bbd38274..2b5f30f3cd9 100644 --- a/components/layout_thread_2020/dom_wrapper.rs +++ b/components/layout_thread_2020/dom_wrapper.rs @@ -428,6 +428,40 @@ impl<'le> fmt::Debug for ServoLayoutElement<'le> { } } +impl<'dom> ServoLayoutElement<'dom> { + /// Returns true if this element is the body child of an html element root element. + fn is_body_element_of_html_element_root(&self) -> bool { + if self.element.local_name() != &local_name!("body") { + return false; + } + + self.parent_element() + .map(|element| { + element.is_root() && element.element.local_name() == &local_name!("html") + }) + .unwrap_or(false) + } + + /// Returns the parent element of this element, if it has one. + fn parent_element(&self) -> Option<Self> { + self.element + .upcast() + .composed_parent_node_ref() + .and_then(as_element) + } + + // Returns true is this is the root element. + fn is_root(&self) -> bool { + match self.as_node().parent_node() { + None => false, + Some(node) => match node.script_type_id() { + NodeTypeId::Document(_) => true, + _ => false, + }, + } + } +} + impl<'le> TElement for ServoLayoutElement<'le> { type ConcreteNode = ServoLayoutNode<'le>; type TraversalChildrenIterator = DomChildren<Self::ConcreteNode>; @@ -679,11 +713,7 @@ impl<'le> TElement for ServoLayoutElement<'le> { } fn is_html_document_body_element(&self) -> bool { - // This is only used for the "tables inherit from body" quirk, which we - // don't implement. - // - // FIXME(emilio): We should be able to give the right answer though! - false + self.is_body_element_of_html_element_root() } fn synthesize_presentational_hints_for_legacy_attributes<V>( @@ -839,13 +869,7 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> { } fn is_root(&self) -> bool { - match self.as_node().parent_node() { - None => false, - Some(node) => match node.script_type_id() { - NodeTypeId::Document(_) => true, - _ => false, - }, - } + ServoLayoutElement::is_root(self) } fn is_empty(&self) -> bool { @@ -981,11 +1005,7 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> { } fn is_html_element_in_html_document(&self) -> bool { - if !self.element.is_html_element() { - return false; - } - - self.as_node().owner_doc().is_html_document() + self.element.is_html_element() && self.as_node().owner_doc().is_html_document() } } @@ -1361,6 +1381,10 @@ impl<'le> ThreadSafeLayoutElement<'le> for ServoThreadSafeLayoutElement<'le> { fn is_shadow_host(&self) -> bool { self.element.shadow_root().is_some() } + + fn is_body_element_of_html_element_root(&self) -> bool { + self.element.is_body_element_of_html_element_root() + } } /// This implementation of `::selectors::Element` is used for implementing lazy diff --git a/components/script_layout_interface/wrapper_traits.rs b/components/script_layout_interface/wrapper_traits.rs index 2056129244b..10e6f826315 100644 --- a/components/script_layout_interface/wrapper_traits.rs +++ b/components/script_layout_interface/wrapper_traits.rs @@ -491,4 +491,14 @@ pub trait ThreadSafeLayoutElement<'dom>: } fn is_shadow_host(&self) -> bool; + + /// Returns whether this node is a body element of an html element root + /// in an HTML element document. + /// + /// Note that this does require accessing the parent, which this interface + /// technically forbids. But accessing the parent is only unsafe insofar as + /// it can be used to reach siblings and cousins. A simple immutable borrow + /// of the parent data is fine, since the bottom-up traversal will not process + /// the parent until all the children have been processed. + fn is_body_element_of_html_element_root(&self) -> bool; } |