aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorMartin Robinson <mrobinson@igalia.com>2023-04-30 20:21:58 +0200
committerMartin Robinson <mrobinson@igalia.com>2023-05-04 10:46:27 +0200
commit72302e2dae6c4726ae657f7b5b8b048f9f64ccf2 (patch)
tree307e1972dd46de70388bd9cba0f8589f05250226 /components
parent77a184a0e7379a63a0d9d8bf442d8dd5c3b5e307 (diff)
downloadservo-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')
-rw-r--r--components/layout/flow.rs15
-rw-r--r--components/layout/fragment.rs35
-rw-r--r--components/layout/query.rs10
-rw-r--r--components/layout_2020/Cargo.toml1
-rw-r--r--components/layout_2020/display_list/mod.rs80
-rw-r--r--components/layout_2020/display_list/stacking_context.rs8
-rw-r--r--components/layout_2020/dom_traversal.rs26
-rw-r--r--components/layout_2020/flexbox/construct.rs3
-rw-r--r--components/layout_2020/flexbox/layout.rs2
-rw-r--r--components/layout_2020/flow/construct.rs9
-rw-r--r--components/layout_2020/flow/inline.rs21
-rw-r--r--components/layout_2020/flow/mod.rs19
-rw-r--r--components/layout_2020/flow/root.rs32
-rw-r--r--components/layout_2020/formatting_contexts.rs33
-rw-r--r--components/layout_2020/fragment_tree/base.rs117
-rw-r--r--components/layout_2020/fragment_tree/mod.rs7
-rw-r--r--components/layout_2020/fragments.rs118
-rw-r--r--components/layout_2020/layout_debug.rs38
-rw-r--r--components/layout_2020/lib.rs1
-rw-r--r--components/layout_2020/positioned.rs2
-rw-r--r--components/layout_2020/query.rs38
-rw-r--r--components/layout_2020/replaced.rs18
-rw-r--r--components/layout_thread/dom_wrapper.rs62
-rw-r--r--components/layout_thread_2020/dom_wrapper.rs58
-rw-r--r--components/script_layout_interface/wrapper_traits.rs10
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;
}