Auto merge of #29699 - mrobinson:add-html-body-tag, r=mukilan

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.

<!-- Please describe your changes on the following line: -->

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: -->
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [x] These changes do not require tests because it does not change behavior.

<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
This commit is contained in:
bors-servo 2023-05-04 11:35:25 +02:00 committed by GitHub
commit 4e37d07ea4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 487 additions and 277 deletions

1
Cargo.lock generated
View file

@ -3147,6 +3147,7 @@ version = "0.0.1"
dependencies = [ dependencies = [
"app_units", "app_units",
"atomic_refcell", "atomic_refcell",
"bitflags",
"canvas_traits", "canvas_traits",
"cssparser", "cssparser",
"embedder_traits", "embedder_traits",

View file

@ -967,13 +967,23 @@ impl fmt::Debug for BaseFlow {
"".to_owned() "".to_owned()
}; };
let flags_string = if !self.flags.is_empty() {
format!("\nflags={:?}", self.flags)
} else {
"".to_owned()
};
write!( write!(
f, f,
"\nsc={:?}\ "\nsc={:?}\
\npos={:?}{}{}\ \npos={:?}{}{}\
\nfloatspec-in={:?}\ \nfloatspec-in={:?}\
\nfloatspec-out={:?}\ \nfloatspec-out={:?}\
\noverflow={:?}{}{}{}", \noverflow={:?}\
{child_count_string}\
{absolute_descendants_string}\
{damage_string}\
{flags_string}",
self.stacking_context_id, self.stacking_context_id,
self.position, self.position,
if self.flags.contains(FlowFlags::FLOATS_LEFT) { 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_in,
self.speculated_float_placement_out, self.speculated_float_placement_out,
self.overflow, self.overflow,
child_count_string,
absolute_descendants_string,
damage_string
) )
} }
} }

View file

@ -677,18 +677,27 @@ impl Fragment {
let mut restyle_damage = node.restyle_damage(); let mut restyle_damage = node.restyle_damage();
restyle_damage.remove(ServoRestyleDamage::RECONSTRUCT_FLOW); 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 { Fragment {
node: node.opaque(), node: node.opaque(),
style: style, style,
selected_style: node.selected_style(), selected_style: node.selected_style(),
restyle_damage: restyle_damage, restyle_damage,
border_box: LogicalRect::zero(writing_mode), border_box: LogicalRect::zero(writing_mode),
border_padding: LogicalMargin::zero(writing_mode), border_padding: LogicalMargin::zero(writing_mode),
margin: LogicalMargin::zero(writing_mode), margin: LogicalMargin::zero(writing_mode),
specific: specific, specific,
inline_context: None, inline_context: None,
pseudo: node.get_pseudo_element_type(), pseudo: node.get_pseudo_element_type(),
flags: FragmentFlags::empty(), flags,
debug_id: DebugId::new(), debug_id: DebugId::new(),
stacking_context_id: StackingContextId::root(), stacking_context_id: StackingContextId::root(),
established_reference_frame: None, established_reference_frame: None,
@ -3277,16 +3286,24 @@ impl fmt::Debug for Fragment {
"".to_owned() "".to_owned()
}; };
let flags_string = if !self.flags.is_empty() {
format!("\nflags={:?}", self.flags)
} else {
"".to_owned()
};
write!( write!(
f, f,
"\n{}({}) [{:?}]\nborder_box={:?}{}{}{}", "\n{}({}) [{:?}]\
\nborder_box={:?}\
{border_padding_string}\
{margin_string}\
{damage_string}\
{flags_string}",
self.specific.get_type(), self.specific.get_type(),
self.debug_id, self.debug_id,
self.specific, self.specific,
self.border_box, self.border_box,
border_padding_string,
margin_string,
damage_string
) )
} }
} }
@ -3430,6 +3447,8 @@ bitflags! {
const IS_BLOCK_FLEX_ITEM = 0b0000_0010; const IS_BLOCK_FLEX_ITEM = 0b0000_0010;
/// Whether this fragment represents the generated text from a text-overflow clip. /// Whether this fragment represents the generated text from a text-overflow clip.
const IS_ELLIPSIS = 0b0000_0100; 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;
} }
} }

View file

@ -9,7 +9,7 @@ use crate::context::LayoutContext;
use crate::display_list::items::{DisplayList, OpaqueNode, ScrollOffsetMap}; use crate::display_list::items::{DisplayList, OpaqueNode, ScrollOffsetMap};
use crate::display_list::IndexableText; use crate::display_list::IndexableText;
use crate::flow::{Flow, GetBaseFlow}; use crate::flow::{Flow, GetBaseFlow};
use crate::fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo}; use crate::fragment::{Fragment, FragmentBorderBoxIterator, FragmentFlags, SpecificFragmentInfo};
use crate::inline::InlineFragmentNodeFlags; use crate::inline::InlineFragmentNodeFlags;
use crate::opaque_node::OpaqueNodeMethods; use crate::opaque_node::OpaqueNodeMethods;
use crate::sequential; use crate::sequential;
@ -666,11 +666,9 @@ impl FragmentBorderBoxIterator for ParentOffsetBorderBoxIterator {
self.has_processed_node = true; self.has_processed_node = true;
} }
} else if self.node_offset_box.is_none() { } else if self.node_offset_box.is_none() {
// TODO(gw): Is there a less fragile way of checking whether this let is_body_element = fragment
// fragment is the body element, rather than just checking that .flags
// it's at level 1 (below the root node)? .contains(FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT);
let is_body_element = level == 1;
let is_valid_parent = match ( let is_valid_parent = match (
is_body_element, is_body_element,
fragment.style.get_box().position, fragment.style.get_box().position,

View file

@ -16,6 +16,7 @@ doctest = false
app_units = "0.7" app_units = "0.7"
atomic_refcell = "0.1.6" atomic_refcell = "0.1.6"
canvas_traits = { path = "../canvas_traits" } canvas_traits = { path = "../canvas_traits" }
bitflags = "1.0"
cssparser = "0.29" cssparser = "0.29"
embedder_traits = { path = "../embedder_traits" } embedder_traits = { path = "../embedder_traits" }
euclid = "0.22" euclid = "0.22"

View file

@ -5,7 +5,8 @@
use crate::context::LayoutContext; use crate::context::LayoutContext;
use crate::display_list::conversions::ToWebRender; use crate::display_list::conversions::ToWebRender;
use crate::display_list::stacking_context::StackingContextSection; 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::geom::{PhysicalPoint, PhysicalRect};
use crate::replaced::IntrinsicSizes; use crate::replaced::IntrinsicSizes;
use crate::style_ext::ComputedValuesExt; 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; use style::computed_values::pointer_events::T as PointerEvents;
let inherited_ui = style.get_inherited_ui(); let inherited_ui = style.get_inherited_ui();
if inherited_ui.pointer_events == PointerEvents::None { if inherited_ui.pointer_events == PointerEvents::None {
None return 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))
} }
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); 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 color = fragment.parent_style.clone_color();
let font_metrics = &fragment.font_metrics; let font_metrics = &fragment.font_metrics;
@ -431,7 +437,11 @@ impl<'a> BuilderForBoxFragment<'a> {
} }
fn build_hit_test(&self, builder: &mut DisplayListBuilder) { 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() { if hit_info.is_some() {
let mut common = builder.common_properties(self.border_rect, &self.fragment.style); let mut common = builder.common_properties(self.border_rect, &self.fragment.style);
common.hit_info = hit_info; common.hit_info = hit_info;
@ -443,7 +453,11 @@ impl<'a> BuilderForBoxFragment<'a> {
} }
fn build_background(&mut self, builder: &mut DisplayListBuilder) { 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, dont paint it again here. // This background is already painted for the canvas, dont paint it again here.
return; return;
} }
@ -494,26 +508,32 @@ impl<'a> BuilderForBoxFragment<'a> {
} }
}, },
Image::Url(ref image_url) => { Image::Url(ref image_url) => {
// FIXME: images wont always have in intrinsic width or height // FIXME: images wont always have in intrinsic width or
// when support for SVG is added. // height when support for SVG is added, or a WebRender
// Or a WebRender `ImageKey`, for that matter. // `ImageKey`, for that matter.
let (width, height, key) = match image_url.url() { //
Some(url) => { // FIXME: It feels like this should take into account the pseudo
match builder.context.get_webrender_image_for_url( // element and not just the node.
self.fragment.tag.node(), let node = match self.fragment.base.tag {
url.clone(), Some(tag) => tag.node,
UsePlaceholder::No,
) {
Some(WebRenderImageInfo {
width,
height,
key: Some(key),
}) => (width, height, key),
_ => continue,
}
},
None => continue, 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 // FIXME: https://drafts.csswg.org/css-images-4/#the-image-resolution
let dppx = 1.0; let dppx = 1.0;

View file

@ -384,8 +384,8 @@ impl StackingContext {
// The `StackingContextFragment` we found is for the root DOM element: // The `StackingContextFragment` we found is for the root DOM element:
debug_assert_eq!( debug_assert_eq!(
box_fragment.tag.node(), fragment.tag().map(|tag| tag.node),
fragment_tree.canvas_background.root_element Some(fragment_tree.canvas_background.root_element),
); );
// The root element may have a CSS transform, // The root element may have a CSS transform,
@ -868,8 +868,8 @@ impl BoxFragment {
return None; return None;
} }
let external_id = let tag = self.base.tag?;
wr::ExternalScrollId(self.tag.to_display_list_fragment_id(), wr.pipeline_id); let external_id = wr::ExternalScrollId(tag.to_display_list_fragment_id(), wr.pipeline_id);
let sensitivity = let sensitivity =
if ComputedOverflow::Hidden == overflow_x && ComputedOverflow::Hidden == overflow_y { if ComputedOverflow::Hidden == overflow_x && ComputedOverflow::Hidden == overflow_y {

View file

@ -5,6 +5,7 @@
use crate::cell::ArcRefCell; use crate::cell::ArcRefCell;
use crate::context::LayoutContext; use crate::context::LayoutContext;
use crate::element_data::{LayoutBox, LayoutDataForElement}; use crate::element_data::{LayoutBox, LayoutDataForElement};
use crate::fragment_tree::{BaseFragmentInfo, FragmentFlags, Tag};
use crate::geom::PhysicalSize; use crate::geom::PhysicalSize;
use crate::replaced::{CanvasInfo, CanvasSource, ReplacedContent}; use crate::replaced::{CanvasInfo, CanvasSource, ReplacedContent};
use crate::style_ext::{Display, DisplayGeneratingBox, DisplayInside, DisplayOutside}; 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 { pub(super) enum Contents {
/// Refers to a DOM subtree, plus `::before` and `::after` pseudo-elements. /// Refers to a DOM subtree, plus `::before` and `::after` pseudo-elements.
OfElement, OfElement,

View file

@ -10,7 +10,6 @@ use crate::dom_traversal::{
}; };
use crate::element_data::LayoutBox; use crate::element_data::LayoutBox;
use crate::formatting_contexts::IndependentFormattingContext; use crate::formatting_contexts::IndependentFormattingContext;
use crate::fragments::Tag;
use crate::positioned::AbsolutelyPositionedBox; use crate::positioned::AbsolutelyPositionedBox;
use crate::style_ext::DisplayGeneratingBox; use crate::style_ext::DisplayGeneratingBox;
use rayon::iter::{IntoParallelIterator, ParallelIterator}; use rayon::iter::{IntoParallelIterator, ParallelIterator};
@ -150,7 +149,7 @@ where
.info .info
.new_replacing_style(anonymous_style.clone().unwrap()), .new_replacing_style(anonymous_style.clone().unwrap()),
runs.into_iter().map(|run| crate::flow::inline::TextRun { 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(), text: run.text.into(),
parent_style: run.info.style, parent_style: run.info.style,
}), }),

View file

@ -732,7 +732,7 @@ impl FlexLine<'_> {
let margin = flex_context.sides_to_flow_relative(*margin); let margin = flex_context.sides_to_flow_relative(*margin);
let collapsed_margin = CollapsedBlockMargins::from_margin(&margin); let collapsed_margin = CollapsedBlockMargins::from_margin(&margin);
BoxFragment::new( BoxFragment::new(
item.box_.tag(), item.box_.base_fragment_info(),
item.box_.style().clone(), item.box_.style().clone(),
fragments, fragments,
content_rect, content_rect,

View file

@ -12,7 +12,6 @@ use crate::flow::float::FloatBox;
use crate::flow::inline::{InlineBox, InlineFormattingContext, InlineLevelBox, TextRun}; use crate::flow::inline::{InlineBox, InlineFormattingContext, InlineLevelBox, TextRun};
use crate::flow::{BlockContainer, BlockFormattingContext, BlockLevelBox}; use crate::flow::{BlockContainer, BlockFormattingContext, BlockLevelBox};
use crate::formatting_contexts::IndependentFormattingContext; use crate::formatting_contexts::IndependentFormattingContext;
use crate::fragments::Tag;
use crate::positioned::AbsolutelyPositionedBox; use crate::positioned::AbsolutelyPositionedBox;
use crate::style_ext::{DisplayGeneratingBox, DisplayInside, DisplayOutside}; use crate::style_ext::{DisplayGeneratingBox, DisplayInside, DisplayOutside};
use rayon::iter::{IntoParallelIterator, ParallelIterator}; use rayon::iter::{IntoParallelIterator, ParallelIterator};
@ -384,7 +383,7 @@ where
if let Some(text) = new_text_run_contents { if let Some(text) = new_text_run_contents {
inlines.push(ArcRefCell::new(InlineLevelBox::TextRun(TextRun { 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), parent_style: Arc::clone(&info.style),
text, text,
}))) })))
@ -495,7 +494,7 @@ where
// Whatever happened before, all we need to do before recurring // Whatever happened before, all we need to do before recurring
// is to remember this ongoing inline level box. // is to remember this ongoing inline level box.
self.ongoing_inline_boxes_stack.push(InlineBox { self.ongoing_inline_boxes_stack.push(InlineBox {
tag: Tag::from_node_and_style_info(info), base_fragment_info: info.into(),
style: info.style.clone(), style: info.style.clone(),
first_fragment: true, first_fragment: true,
last_fragment: false, last_fragment: false,
@ -556,7 +555,7 @@ where
.rev() .rev()
.map(|ongoing| { .map(|ongoing| {
let fragmented = InlineBox { let fragmented = InlineBox {
tag: ongoing.tag, base_fragment_info: ongoing.base_fragment_info,
style: ongoing.style.clone(), style: ongoing.style.clone(),
first_fragment: ongoing.first_fragment, first_fragment: ongoing.first_fragment,
// The fragmented boxes before the block level element // The fragmented boxes before the block level element
@ -755,7 +754,7 @@ where
BlockLevelCreator::SameFormattingContextBlock(contents) => { BlockLevelCreator::SameFormattingContextBlock(contents) => {
let (contents, contains_floats) = contents.finish(context, info); let (contents, contains_floats) = contents.finish(context, info);
let block_level_box = ArcRefCell::new(BlockLevelBox::SameFormattingContextBlock { let block_level_box = ArcRefCell::new(BlockLevelBox::SameFormattingContextBlock {
tag: Tag::from_node_and_style_info(info), base_fragment_info: info.into(),
contents, contents,
style: Arc::clone(&info.style), style: Arc::clone(&info.style),
}); });

View file

@ -7,9 +7,9 @@ use crate::context::LayoutContext;
use crate::flow::float::FloatBox; use crate::flow::float::FloatBox;
use crate::flow::FlowLayout; use crate::flow::FlowLayout;
use crate::formatting_contexts::IndependentFormattingContext; use crate::formatting_contexts::IndependentFormattingContext;
use crate::fragment_tree::BaseFragmentInfo;
use crate::fragments::{ use crate::fragments::{
AnonymousFragment, BoxFragment, CollapsedBlockMargins, DebugId, FontMetrics, Fragment, Tag, AnonymousFragment, BoxFragment, CollapsedBlockMargins, FontMetrics, Fragment, TextFragment,
TextFragment,
}; };
use crate::geom::flow_relative::{Rect, Sides, Vec2}; use crate::geom::flow_relative::{Rect, Sides, Vec2};
use crate::positioned::{ use crate::positioned::{
@ -51,7 +51,7 @@ pub(crate) enum InlineLevelBox {
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub(crate) struct InlineBox { pub(crate) struct InlineBox {
pub tag: Tag, pub base_fragment_info: BaseFragmentInfo,
#[serde(skip_serializing)] #[serde(skip_serializing)]
pub style: Arc<ComputedValues>, pub style: Arc<ComputedValues>,
pub first_fragment: bool, pub first_fragment: bool,
@ -62,7 +62,7 @@ pub(crate) struct InlineBox {
/// https://www.w3.org/TR/css-display-3/#css-text-run /// https://www.w3.org/TR/css-display-3/#css-text-run
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub(crate) struct TextRun { pub(crate) struct TextRun {
pub tag: Tag, pub base_fragment_info: BaseFragmentInfo,
#[serde(skip_serializing)] #[serde(skip_serializing)]
pub parent_style: Arc<ComputedValues>, pub parent_style: Arc<ComputedValues>,
pub text: String, pub text: String,
@ -82,7 +82,7 @@ struct InlineNestingLevelState<'box_tree> {
} }
struct PartialInlineBoxFragment<'box_tree> { struct PartialInlineBoxFragment<'box_tree> {
tag: Tag, base_fragment_info: BaseFragmentInfo,
style: Arc<ComputedValues>, style: Arc<ComputedValues>,
start_corner: Vec2<Length>, start_corner: Vec2<Length>,
padding: Sides<Length>, padding: Sides<Length>,
@ -471,7 +471,7 @@ impl InlineBox {
let text_decoration_line = let text_decoration_line =
ifc.current_nesting_level.text_decoration_line | style.clone_text_decoration_line(); ifc.current_nesting_level.text_decoration_line | style.clone_text_decoration_line();
PartialInlineBoxFragment { PartialInlineBoxFragment {
tag: self.tag, base_fragment_info: self.base_fragment_info,
style, style,
start_corner, start_corner,
padding, padding,
@ -512,7 +512,7 @@ impl<'box_tree> PartialInlineBoxFragment<'box_tree> {
}; };
let mut fragment = BoxFragment::new( let mut fragment = BoxFragment::new(
self.tag, self.base_fragment_info,
self.style.clone(), self.style.clone(),
std::mem::take(&mut nesting_level.fragments_so_far), std::mem::take(&mut nesting_level.fragments_so_far),
content_rect, content_rect,
@ -580,7 +580,7 @@ fn layout_atomic(
.make_fragments(&replaced.style, size.clone()); .make_fragments(&replaced.style, size.clone());
let content_rect = Rect { start_corner, size }; let content_rect = Rect { start_corner, size };
BoxFragment::new( BoxFragment::new(
replaced.tag, replaced.base_fragment_info,
replaced.style.clone(), replaced.style.clone(),
fragments, fragments,
content_rect, content_rect,
@ -655,7 +655,7 @@ fn layout_atomic(
}, },
}; };
BoxFragment::new( BoxFragment::new(
non_replaced.tag, non_replaced.base_fragment_info,
non_replaced.style.clone(), non_replaced.style.clone(),
independent_layout.fragments, independent_layout.fragments,
content_rect, content_rect,
@ -836,8 +836,7 @@ impl TextRun {
ifc.current_nesting_level ifc.current_nesting_level
.fragments_so_far .fragments_so_far
.push(Fragment::Text(TextFragment { .push(Fragment::Text(TextFragment {
tag: self.tag, base: self.base_fragment_info.into(),
debug_id: DebugId::new(),
parent_style: self.parent_style.clone(), parent_style: self.parent_style.clone(),
rect, rect,
font_metrics, font_metrics,

View file

@ -11,8 +11,9 @@ use crate::flow::inline::InlineFormattingContext;
use crate::formatting_contexts::{ use crate::formatting_contexts::{
IndependentFormattingContext, IndependentLayout, NonReplacedFormattingContext, IndependentFormattingContext, IndependentLayout, NonReplacedFormattingContext,
}; };
use crate::fragment_tree::BaseFragmentInfo;
use crate::fragments::{ use crate::fragments::{
AnonymousFragment, BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment, Tag, AnonymousFragment, BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment,
}; };
use crate::geom::flow_relative::{Rect, Sides, Vec2}; use crate::geom::flow_relative::{Rect, Sides, Vec2};
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext}; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
@ -50,7 +51,7 @@ pub(crate) enum BlockContainer {
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub(crate) enum BlockLevelBox { pub(crate) enum BlockLevelBox {
SameFormattingContextBlock { SameFormattingContextBlock {
tag: Tag, base_fragment_info: BaseFragmentInfo,
#[serde(skip_serializing)] #[serde(skip_serializing)]
style: Arc<ComputedValues>, style: Arc<ComputedValues>,
contents: BlockContainer, contents: BlockContainer,
@ -306,7 +307,7 @@ impl BlockLevelBox {
) -> Fragment { ) -> Fragment {
match self { match self {
BlockLevelBox::SameFormattingContextBlock { BlockLevelBox::SameFormattingContextBlock {
tag, base_fragment_info: tag,
style, style,
contents, contents,
} => Fragment::Box(positioning_context.layout_maybe_position_relative_fragment( } => Fragment::Box(positioning_context.layout_maybe_position_relative_fragment(
@ -335,7 +336,7 @@ impl BlockLevelBox {
|_positioning_context| { |_positioning_context| {
layout_in_flow_replaced_block_level( layout_in_flow_replaced_block_level(
containing_block, containing_block,
replaced.tag, replaced.base_fragment_info,
&replaced.style, &replaced.style,
&replaced.contents, &replaced.contents,
) )
@ -352,7 +353,7 @@ impl BlockLevelBox {
layout_context, layout_context,
positioning_context, positioning_context,
containing_block, containing_block,
non_replaced.tag, non_replaced.base_fragment_info,
&non_replaced.style, &non_replaced.style,
NonReplacedContents::EstablishesAnIndependentFormattingContext( NonReplacedContents::EstablishesAnIndependentFormattingContext(
non_replaced, non_replaced,
@ -420,7 +421,7 @@ fn layout_in_flow_non_replaced_block_level(
layout_context: &LayoutContext, layout_context: &LayoutContext,
positioning_context: &mut PositioningContext, positioning_context: &mut PositioningContext,
containing_block: &ContainingBlock, containing_block: &ContainingBlock,
tag: Tag, base_fragment_info: BaseFragmentInfo,
style: &Arc<ComputedValues>, style: &Arc<ComputedValues>,
block_level_kind: NonReplacedContents, block_level_kind: NonReplacedContents,
tree_rank: usize, tree_rank: usize,
@ -559,7 +560,7 @@ fn layout_in_flow_non_replaced_block_level(
}, },
}; };
BoxFragment::new( BoxFragment::new(
tag, base_fragment_info,
style.clone(), style.clone(),
fragments, fragments,
content_rect, content_rect,
@ -575,7 +576,7 @@ fn layout_in_flow_non_replaced_block_level(
/// https://drafts.csswg.org/css2/visudet.html#inline-replaced-height /// https://drafts.csswg.org/css2/visudet.html#inline-replaced-height
fn layout_in_flow_replaced_block_level<'a>( fn layout_in_flow_replaced_block_level<'a>(
containing_block: &ContainingBlock, containing_block: &ContainingBlock,
tag: Tag, base_fragment_info: BaseFragmentInfo,
style: &Arc<ComputedValues>, style: &Arc<ComputedValues>,
replaced: &ReplacedContent, replaced: &ReplacedContent,
) -> BoxFragment { ) -> BoxFragment {
@ -600,7 +601,7 @@ fn layout_in_flow_replaced_block_level<'a>(
}; };
let block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin); let block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin);
BoxFragment::new( BoxFragment::new(
tag, base_fragment_info,
style.clone(), style.clone(),
fragments, fragments,
content_rect, content_rect,

View file

@ -15,7 +15,8 @@ use crate::flow::float::FloatBox;
use crate::flow::inline::InlineLevelBox; use crate::flow::inline::InlineLevelBox;
use crate::flow::{BlockContainer, BlockFormattingContext, BlockLevelBox}; use crate::flow::{BlockContainer, BlockFormattingContext, BlockLevelBox};
use crate::formatting_contexts::IndependentFormattingContext; 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::flow_relative::Vec2;
use crate::geom::{PhysicalPoint, PhysicalRect, PhysicalSize}; use crate::geom::{PhysicalPoint, PhysicalRect, PhysicalSize};
use crate::positioned::AbsolutelyPositionedBox; use crate::positioned::AbsolutelyPositionedBox;
@ -36,7 +37,6 @@ use servo_arc::Arc;
use style::animation::AnimationSetKey; use style::animation::AnimationSetKey;
use style::dom::OpaqueNode; use style::dom::OpaqueNode;
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style::selector_parser::PseudoElement;
use style::values::computed::Length; use style::values::computed::Length;
use style_traits::CSSPixel; use style_traits::CSSPixel;
use webrender_api::{ClipId, SpaceAndClipInfo, SpatialId}; 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>) { pub fn remove_nodes_in_fragment_tree_from_set(&self, set: &mut FxHashSet<AnimationSetKey>) {
self.find(|fragment, _, _| { self.find(|fragment, _, _| {
let (node, pseudo) = match fragment.tag()? { let tag = fragment.tag()?;
Tag::Node(node) => (node, None), set.remove(&AnimationSetKey::new(tag.node, tag.pseudo));
Tag::BeforePseudo(node) => (node, Some(PseudoElement::Before)),
Tag::AfterPseudo(node) => (node, Some(PseudoElement::After)),
};
set.remove(&AnimationSetKey::new(node, pseudo));
None::<()> None::<()>
}); });
} }
pub fn get_content_box_for_node(&self, requested_node: OpaqueNode) -> Rect<Au> { pub fn get_content_box_for_node(&self, requested_node: OpaqueNode) -> Rect<Au> {
let mut bounding_box = PhysicalRect::zero(); 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| { self.find(|fragment, _, containing_block| {
if fragment.tag() != Some(tag_to_find) { if fragment.tag() != Some(tag_to_find) {
return None::<()>; return None::<()>;
@ -516,17 +512,15 @@ impl FragmentTree {
} }
pub fn get_border_dimensions_for_node(&self, requested_node: OpaqueNode) -> Rect<i32> { 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| { self.find(|fragment, _, containing_block| {
if fragment.tag() != Some(tag_to_find) {
return None;
}
let (style, padding_rect) = match fragment { let (style, padding_rect) = match fragment {
Fragment::Box(fragment) if fragment.tag.node() == requested_node => { Fragment::Box(fragment) => (&fragment.style, fragment.padding_rect()),
(&fragment.style, fragment.padding_rect()) _ => return None,
},
Fragment::AbsoluteOrFixedPositioned(_) |
Fragment::Box(_) |
Fragment::Text(_) |
Fragment::Image(_) |
Fragment::IFrame(_) |
Fragment::Anonymous(_) => return None,
}; };
// https://drafts.csswg.org/cssom-view/#dom-element-clienttop // 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> { pub fn get_scroll_area_for_node(&self, requested_node: OpaqueNode) -> Rect<i32> {
let mut scroll_area: PhysicalRect<Length> = PhysicalRect::zero(); 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| { self.find(|fragment, _, containing_block| {
if fragment.tag() != Some(tag_to_find) { if fragment.tag() != Some(tag_to_find) {
return None::<()>; return None::<()>;

View file

@ -6,7 +6,8 @@ use crate::context::LayoutContext;
use crate::dom_traversal::{Contents, NodeAndStyleInfo, NodeExt}; use crate::dom_traversal::{Contents, NodeAndStyleInfo, NodeExt};
use crate::flexbox::FlexContainer; use crate::flexbox::FlexContainer;
use crate::flow::BlockFormattingContext; use crate::flow::BlockFormattingContext;
use crate::fragments::{Fragment, Tag}; use crate::fragment_tree::BaseFragmentInfo;
use crate::fragments::Fragment;
use crate::positioned::PositioningContext; use crate::positioned::PositioningContext;
use crate::replaced::ReplacedContent; use crate::replaced::ReplacedContent;
use crate::sizing::{self, ContentSizes}; use crate::sizing::{self, ContentSizes};
@ -28,7 +29,7 @@ pub(crate) enum IndependentFormattingContext {
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub(crate) struct NonReplacedFormattingContext { pub(crate) struct NonReplacedFormattingContext {
pub tag: Tag, pub base_fragment_info: BaseFragmentInfo,
#[serde(skip_serializing)] #[serde(skip_serializing)]
pub style: Arc<ComputedValues>, pub style: Arc<ComputedValues>,
/// If it was requested during construction /// If it was requested during construction
@ -38,7 +39,7 @@ pub(crate) struct NonReplacedFormattingContext {
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub(crate) struct ReplacedFormattingContext { pub(crate) struct ReplacedFormattingContext {
pub tag: Tag, pub base_fragment_info: BaseFragmentInfo,
#[serde(skip_serializing)] #[serde(skip_serializing)]
pub style: Arc<ComputedValues>, pub style: Arc<ComputedValues>,
pub contents: ReplacedContent, pub contents: ReplacedContent,
@ -63,7 +64,7 @@ pub(crate) struct IndependentLayout {
impl IndependentFormattingContext { impl IndependentFormattingContext {
pub fn construct<'dom>( pub fn construct<'dom>(
context: &LayoutContext, context: &LayoutContext,
info: &NodeAndStyleInfo<impl NodeExt<'dom>>, node_and_style_info: &NodeAndStyleInfo<impl NodeExt<'dom>>,
display_inside: DisplayInside, display_inside: DisplayInside,
contents: Contents, contents: Contents,
propagated_text_decoration_line: TextDecorationLine, propagated_text_decoration_line: TextDecorationLine,
@ -76,7 +77,7 @@ impl IndependentFormattingContext {
NonReplacedFormattingContextContents::Flow( NonReplacedFormattingContextContents::Flow(
BlockFormattingContext::construct( BlockFormattingContext::construct(
context, context,
info, node_and_style_info,
non_replaced, non_replaced,
propagated_text_decoration_line, propagated_text_decoration_line,
is_list_item, is_list_item,
@ -86,37 +87,37 @@ impl IndependentFormattingContext {
DisplayInside::Flex => { DisplayInside::Flex => {
NonReplacedFormattingContextContents::Flex(FlexContainer::construct( NonReplacedFormattingContextContents::Flex(FlexContainer::construct(
context, context,
info, node_and_style_info,
non_replaced, non_replaced,
propagated_text_decoration_line, propagated_text_decoration_line,
)) ))
}, },
}; };
Self::NonReplaced(NonReplacedFormattingContext { Self::NonReplaced(NonReplacedFormattingContext {
tag: Tag::from_node_and_style_info(info), base_fragment_info: node_and_style_info.into(),
style: Arc::clone(&info.style), style: Arc::clone(&node_and_style_info.style),
content_sizes: None, content_sizes: None,
contents, contents,
}) })
}, },
Err(contents) => Self::Replaced(ReplacedFormattingContext { Err(contents) => Self::Replaced(ReplacedFormattingContext {
tag: Tag::from_node_and_style_info(info), base_fragment_info: node_and_style_info.into(),
style: Arc::clone(&info.style), style: Arc::clone(&node_and_style_info.style),
contents, contents,
}), }),
} }
} }
pub fn construct_for_text_runs<'dom>( 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>, runs: impl Iterator<Item = crate::flow::inline::TextRun>,
propagated_text_decoration_line: TextDecorationLine, propagated_text_decoration_line: TextDecorationLine,
) -> Self { ) -> Self {
let bfc = let bfc =
BlockFormattingContext::construct_for_text_runs(runs, propagated_text_decoration_line); BlockFormattingContext::construct_for_text_runs(runs, propagated_text_decoration_line);
Self::NonReplaced(NonReplacedFormattingContext { Self::NonReplaced(NonReplacedFormattingContext {
tag: Tag::from_node_and_style_info(info), base_fragment_info: node_and_style_info.into(),
style: Arc::clone(&info.style), style: Arc::clone(&node_and_style_info.style),
content_sizes: None, content_sizes: None,
contents: NonReplacedFormattingContextContents::Flow(bfc), contents: NonReplacedFormattingContextContents::Flow(bfc),
}) })
@ -129,10 +130,10 @@ impl IndependentFormattingContext {
} }
} }
pub fn tag(&self) -> Tag { pub fn base_fragment_info(&self) -> BaseFragmentInfo {
match self { match self {
Self::NonReplaced(inner) => inner.tag, Self::NonReplaced(inner) => inner.base_fragment_info,
Self::Replaced(inner) => inner.tag, Self::Replaced(inner) => inner.base_fragment_info,
} }
} }

View file

@ -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
}
}

View file

@ -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::*;

View file

@ -3,23 +3,17 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::cell::ArcRefCell; 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::flow_relative::{Rect, Sides};
use crate::geom::{PhysicalPoint, PhysicalRect}; use crate::geom::{PhysicalPoint, PhysicalRect};
#[cfg(debug_assertions)]
use crate::layout_debug;
use crate::positioned::HoistedSharedFragment; use crate::positioned::HoistedSharedFragment;
use gfx::font::FontMetrics as GfxFontMetrics; use gfx::font::FontMetrics as GfxFontMetrics;
use gfx::text::glyph::GlyphStore; use gfx::text::glyph::GlyphStore;
use gfx_traits::print_tree::PrintTree; use gfx_traits::print_tree::PrintTree;
use gfx_traits::{combine_id_with_fragment_type, FragmentType};
use msg::constellation_msg::{BrowsingContextId, PipelineId}; use msg::constellation_msg::{BrowsingContextId, PipelineId};
#[cfg(not(debug_assertions))]
use serde::ser::{Serialize, Serializer};
use servo_arc::Arc as ServoArc; use servo_arc::Arc as ServoArc;
use std::sync::Arc; use std::sync::Arc;
use style::computed_values::overflow_x::T as ComputedOverflow; use style::computed_values::overflow_x::T as ComputedOverflow;
use style::dom::OpaqueNode;
use style::logical_geometry::WritingMode; use style::logical_geometry::WritingMode;
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style::values::computed::Length; use style::values::computed::Length;
@ -27,42 +21,6 @@ use style::values::specified::text::TextDecorationLine;
use style::Zero; use style::Zero;
use webrender_api::{FontInstanceKey, ImageKey}; 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)] #[derive(Serialize)]
pub(crate) enum Fragment { pub(crate) enum Fragment {
Box(BoxFragment), Box(BoxFragment),
@ -82,8 +40,8 @@ pub(crate) enum Fragment {
#[derive(Serialize)] #[derive(Serialize)]
pub(crate) struct BoxFragment { pub(crate) struct BoxFragment {
pub tag: Tag, pub base: BaseFragment,
pub debug_id: DebugId,
#[serde(skip_serializing)] #[serde(skip_serializing)]
pub style: ServoArc<ComputedValues>, pub style: ServoArc<ComputedValues>,
pub children: Vec<ArcRefCell<Fragment>>, 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. /// Can contain child fragments with relative coordinates, but does not contribute to painting itself.
#[derive(Serialize)] #[derive(Serialize)]
pub(crate) struct AnonymousFragment { pub(crate) struct AnonymousFragment {
pub debug_id: DebugId, pub base: BaseFragment,
pub rect: Rect<Length>, pub rect: Rect<Length>,
pub children: Vec<ArcRefCell<Fragment>>, pub children: Vec<ArcRefCell<Fragment>>,
pub mode: WritingMode, pub mode: WritingMode,
@ -153,8 +111,7 @@ impl From<&GfxFontMetrics> for FontMetrics {
#[derive(Serialize)] #[derive(Serialize)]
pub(crate) struct TextFragment { pub(crate) struct TextFragment {
pub debug_id: DebugId, pub base: BaseFragment,
pub tag: Tag,
#[serde(skip_serializing)] #[serde(skip_serializing)]
pub parent_style: ServoArc<ComputedValues>, pub parent_style: ServoArc<ComputedValues>,
pub rect: Rect<Length>, pub rect: Rect<Length>,
@ -168,7 +125,7 @@ pub(crate) struct TextFragment {
#[derive(Serialize)] #[derive(Serialize)]
pub(crate) struct ImageFragment { pub(crate) struct ImageFragment {
pub debug_id: DebugId, pub base: BaseFragment,
#[serde(skip_serializing)] #[serde(skip_serializing)]
pub style: ServoArc<ComputedValues>, pub style: ServoArc<ComputedValues>,
pub rect: Rect<Length>, pub rect: Rect<Length>,
@ -178,7 +135,7 @@ pub(crate) struct ImageFragment {
#[derive(Serialize)] #[derive(Serialize)]
pub(crate) struct IFrameFragment { pub(crate) struct IFrameFragment {
pub debug_id: DebugId, pub base: BaseFragment,
pub pipeline_id: PipelineId, pub pipeline_id: PipelineId,
pub browsing_context_id: BrowsingContextId, pub browsing_context_id: BrowsingContextId,
pub rect: Rect<Length>, pub rect: Rect<Length>,
@ -200,15 +157,19 @@ impl Fragment {
position.inline += *offset; 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> { pub fn tag(&self) -> Option<Tag> {
match self { self.base().and_then(|base| base.tag)
Fragment::Box(fragment) => Some(fragment.tag),
Fragment::Text(fragment) => Some(fragment.tag),
Fragment::AbsoluteOrFixedPositioned(_) |
Fragment::Anonymous(_) |
Fragment::Image(_) |
Fragment::IFrame(_) => None,
}
} }
pub fn print(&self, tree: &mut PrintTree) { pub fn print(&self, tree: &mut PrintTree) {
@ -285,7 +246,7 @@ impl Fragment {
impl AnonymousFragment { impl AnonymousFragment {
pub fn no_op(mode: WritingMode) -> Self { pub fn no_op(mode: WritingMode) -> Self {
Self { Self {
debug_id: DebugId::new(), base: BaseFragment::anonymous(),
children: vec![], children: vec![],
rect: Rect::zero(), rect: Rect::zero(),
mode, mode,
@ -306,7 +267,7 @@ impl AnonymousFragment {
) )
}); });
AnonymousFragment { AnonymousFragment {
debug_id: DebugId::new(), base: BaseFragment::anonymous(),
rect, rect,
children: children children: children
.into_iter() .into_iter()
@ -334,7 +295,7 @@ impl AnonymousFragment {
impl BoxFragment { impl BoxFragment {
pub fn new( pub fn new(
tag: Tag, base_fragment_info: BaseFragmentInfo,
style: ServoArc<ComputedValues>, style: ServoArc<ComputedValues>,
children: Vec<Fragment>, children: Vec<Fragment>,
content_rect: Rect<Length>, content_rect: Rect<Length>,
@ -351,8 +312,7 @@ impl BoxFragment {
acc.union(&child.scrollable_overflow(&containing_block)) acc.union(&child.scrollable_overflow(&containing_block))
}); });
BoxFragment { BoxFragment {
tag, base: base_fragment_info.into(),
debug_id: DebugId::new(),
style, style,
children: children children: children
.into_iter() .into_iter()
@ -397,12 +357,14 @@ impl BoxFragment {
pub fn print(&self, tree: &mut PrintTree) { pub fn print(&self, tree: &mut PrintTree) {
tree.new_level(format!( tree.new_level(format!(
"Box\ "Box\
\nbase={:?}\
\ncontent={:?}\ \ncontent={:?}\
\npadding rect={:?}\ \npadding rect={:?}\
\nborder rect={:?}\ \nborder rect={:?}\
\nscrollable_overflow={:?}\ \nscrollable_overflow={:?}\
\noverflow={:?} / {:?}\ \noverflow={:?} / {:?}\
\nstyle={:p}", \nstyle={:p}",
self.base,
self.content_rect, self.content_rect,
self.padding_rect(), self.padding_rect(),
self.border_rect(), self.border_rect(),
@ -534,33 +496,3 @@ impl CollapsedMargin {
self.max_positive + self.min_negative 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))
}
}

View file

@ -6,6 +6,8 @@
//! that can be viewed by an external tool to make layout debugging easier. //! that can be viewed by an external tool to make layout debugging easier.
use crate::flow::{BoxTree, FragmentTree}; use crate::flow::{BoxTree, FragmentTree};
#[cfg(not(debug_assertions))]
use serde::ser::{Serialize, Serializer};
use serde_json::{to_string, to_value, Value}; use serde_json::{to_string, to_value, Value};
use std::cell::RefCell; use std::cell::RefCell;
use std::fs; 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, /// Begin a layout debug trace. If this has not been called,
/// creating debug scopes has no effect. /// creating debug scopes has no effect.
pub fn begin_trace(box_tree: Arc<BoxTree>, fragment_tree: Arc<FragmentTree>) { pub fn begin_trace(box_tree: Arc<BoxTree>, fragment_tree: Arc<FragmentTree>) {
@ -146,3 +142,33 @@ pub fn end_trace(generation: u32) {
) )
.unwrap(); .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))
}
}

View file

@ -18,6 +18,7 @@ pub mod element_data;
mod flexbox; mod flexbox;
pub mod flow; pub mod flow;
mod formatting_contexts; mod formatting_contexts;
mod fragment_tree;
mod fragments; mod fragments;
pub mod geom; pub mod geom;
#[macro_use] #[macro_use]

View file

@ -632,7 +632,7 @@ impl HoistedAbsolutelyPositionedBox {
}; };
BoxFragment::new( BoxFragment::new(
absolutely_positioned_box.context.tag(), absolutely_positioned_box.context.base_fragment_info(),
absolutely_positioned_box.context.style().clone(), absolutely_positioned_box.context.style().clone(),
fragments, fragments,
content_rect, content_rect,

View file

@ -5,7 +5,8 @@
//! Utilities for querying the layout, as needed by the layout thread. //! Utilities for querying the layout, as needed by the layout thread.
use crate::context::LayoutContext; use crate::context::LayoutContext;
use crate::flow::FragmentTree; use crate::flow::FragmentTree;
use crate::fragments::{Fragment, Tag}; use crate::fragment_tree::{FragmentFlags, Tag};
use crate::fragments::Fragment;
use app_units::Au; use app_units::Au;
use euclid::default::{Point2D, Rect}; use euclid::default::{Point2D, Rect};
use euclid::Size2D; use euclid::Size2D;
@ -263,13 +264,7 @@ pub fn process_resolved_style_request<'dom>(
let computed_style = let computed_style =
|| style.computed_value_to_string(PropertyDeclarationId::Longhand(longhand_id)); || style.computed_value_to_string(PropertyDeclarationId::Longhand(longhand_id));
let opaque = node.opaque(); let tag_to_find = Tag::new_pseudo(node.opaque(), *pseudo);
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."),
};
// https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle // https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle
// Here we are trying to conform to the specification that says that 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 fragment_tree
.find(|fragment, _, containing_block| { .find(|fragment, _, containing_block| {
if Some(tag_to_find) != fragment.tag() {
return None;
}
let box_fragment = match fragment { 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, _ => 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 // https://www.w3.org/TR/2016/WD-cssom-view-1-20160317/#extensions-to-the-htmlelement-interface
let mut parent_node_addresses = Vec::new(); 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| { let node_offset_box = fragment_tree.find(|fragment, level, containing_block| {
// FIXME: Is there a less fragile way of checking whether this let base = fragment.base()?;
// fragment is the body element, rather than just checking that let is_body_element = base
// it's at level 1 (below the root node)? .flags
let is_body_element = level == 1; .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 // Only consider the first fragment of the node found as per a
// possible interpretation of the specification: "[...] return the // possible interpretation of the specification: "[...] return the
// y-coordinate of the top border edge of the first CSS layout box // 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, (false, Position::Static) => false,
}; };
if let Tag::Node(node_address) = fragment.tag { match base.tag {
is_eligible_parent.then(|| node_address) Some(tag) if is_eligible_parent && !tag.is_pseudo() => Some(tag.node),
} else { _ => None,
None
} }
}, },
Fragment::AbsoluteOrFixedPositioned(_) | Fragment::AbsoluteOrFixedPositioned(_) |
@ -533,11 +532,12 @@ fn process_offset_parent_query_inner(
// //
// Since we saw `offset_parent_node_address` once, we should be able // Since we saw `offset_parent_node_address` once, we should be able
// to find it again. // to find it again.
let offset_parent_node_tag = Tag::new(offset_parent_node_address);
fragment_tree fragment_tree
.find(|fragment, _, containing_block| { .find(|fragment, _, containing_block| {
match fragment { match fragment {
Fragment::Box(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. // Again, take the *first* associated CSS layout box.
let padding_box_corner = fragment let padding_box_corner = fragment

View file

@ -4,7 +4,8 @@
use crate::context::LayoutContext; use crate::context::LayoutContext;
use crate::dom_traversal::NodeExt; 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::flow_relative::{Rect, Vec2};
use crate::geom::PhysicalSize; use crate::geom::PhysicalSize;
use crate::sizing::ContentSizes; use crate::sizing::ContentSizes;
@ -29,6 +30,7 @@ use webrender_api::ImageKey;
pub(crate) struct ReplacedContent { pub(crate) struct ReplacedContent {
pub kind: ReplacedContentKind, pub kind: ReplacedContentKind,
intrinsic: IntrinsicSizes, intrinsic: IntrinsicSizes,
base_fragment_info: BaseFragmentInfo,
} }
/// * Raster images always have an intrinsic width and height, with 1 image pixel = 1px. /// * 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>( pub fn from_image_url<'dom>(
@ -171,6 +178,7 @@ impl ReplacedContent {
// FIXME https://github.com/w3c/csswg-drafts/issues/4572 // FIXME https://github.com/w3c/csswg-drafts/issues/4572
ratio: Some(width / height), ratio: Some(width / height),
}, },
base_fragment_info: BaseFragmentInfo::new_for_node(element.as_opaque()),
}); });
} }
None None
@ -219,7 +227,7 @@ impl ReplacedContent {
.and_then(|image| image.id) .and_then(|image| image.id)
.map(|image_key| { .map(|image_key| {
Fragment::Image(ImageFragment { Fragment::Image(ImageFragment {
debug_id: DebugId::new(), base: self.base_fragment_info.into(),
style: style.clone(), style: style.clone(),
rect: Rect { rect: Rect {
start_corner: Vec2::zero(), start_corner: Vec2::zero(),
@ -232,7 +240,7 @@ impl ReplacedContent {
.collect(), .collect(),
ReplacedContentKind::IFrame(iframe) => { ReplacedContentKind::IFrame(iframe) => {
vec![Fragment::IFrame(IFrameFragment { vec![Fragment::IFrame(IFrameFragment {
debug_id: DebugId::new(), base: self.base_fragment_info.into(),
style: style.clone(), style: style.clone(),
pipeline_id: iframe.pipeline_id, pipeline_id: iframe.pipeline_id,
browsing_context_id: iframe.browsing_context_id, browsing_context_id: iframe.browsing_context_id,
@ -268,7 +276,7 @@ impl ReplacedContent {
}, },
}; };
vec![Fragment::Image(ImageFragment { vec![Fragment::Image(ImageFragment {
debug_id: DebugId::new(), base: self.base_fragment_info.into(),
style: style.clone(), style: style.clone(),
rect: Rect { rect: Rect {
start_corner: Vec2::zero(), start_corner: Vec2::zero(),

View file

@ -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> { impl<'le> TElement for ServoLayoutElement<'le> {
type ConcreteNode = ServoLayoutNode<'le>; type ConcreteNode = ServoLayoutNode<'le>;
type TraversalChildrenIterator = DomChildren<Self::ConcreteNode>; type TraversalChildrenIterator = DomChildren<Self::ConcreteNode>;
@ -671,11 +704,7 @@ impl<'le> TElement for ServoLayoutElement<'le> {
} }
fn is_html_document_body_element(&self) -> bool { fn is_html_document_body_element(&self) -> bool {
// This is only used for the "tables inherit from body" quirk, which we self.is_body_element_of_html_element_root()
// don't implement.
//
// FIXME(emilio): We should be able to give the right answer though!
false
} }
fn synthesize_presentational_hints_for_legacy_attributes<V>( 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>> { fn parent_element(&self) -> Option<ServoLayoutElement<'le>> {
self.element ServoLayoutElement::parent_element(self)
.upcast()
.composed_parent_node_ref()
.and_then(as_element)
} }
fn parent_node_is_shadow_root(&self) -> bool { fn parent_node_is_shadow_root(&self) -> bool {
@ -831,13 +857,7 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
} }
fn is_root(&self) -> bool { fn is_root(&self) -> bool {
match self.as_node().parent_node() { ServoLayoutElement::is_root(self)
None => false,
Some(node) => match node.script_type_id() {
NodeTypeId::Document(_) => true,
_ => false,
},
}
} }
fn is_empty(&self) -> bool { 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 { fn is_html_element_in_html_document(&self) -> bool {
if !self.element.is_html_element() { self.element.is_html_element() && self.as_node().owner_doc().is_html_document()
return false;
}
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 { fn is_shadow_host(&self) -> bool {
self.element.shadow_root().is_some() 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 /// This implementation of `::selectors::Element` is used for implementing lazy

View file

@ -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> { impl<'le> TElement for ServoLayoutElement<'le> {
type ConcreteNode = ServoLayoutNode<'le>; type ConcreteNode = ServoLayoutNode<'le>;
type TraversalChildrenIterator = DomChildren<Self::ConcreteNode>; type TraversalChildrenIterator = DomChildren<Self::ConcreteNode>;
@ -679,11 +713,7 @@ impl<'le> TElement for ServoLayoutElement<'le> {
} }
fn is_html_document_body_element(&self) -> bool { fn is_html_document_body_element(&self) -> bool {
// This is only used for the "tables inherit from body" quirk, which we self.is_body_element_of_html_element_root()
// don't implement.
//
// FIXME(emilio): We should be able to give the right answer though!
false
} }
fn synthesize_presentational_hints_for_legacy_attributes<V>( fn synthesize_presentational_hints_for_legacy_attributes<V>(
@ -839,13 +869,7 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
} }
fn is_root(&self) -> bool { fn is_root(&self) -> bool {
match self.as_node().parent_node() { ServoLayoutElement::is_root(self)
None => false,
Some(node) => match node.script_type_id() {
NodeTypeId::Document(_) => true,
_ => false,
},
}
} }
fn is_empty(&self) -> bool { 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 { fn is_html_element_in_html_document(&self) -> bool {
if !self.element.is_html_element() { self.element.is_html_element() && self.as_node().owner_doc().is_html_document()
return false;
}
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 { fn is_shadow_host(&self) -> bool {
self.element.shadow_root().is_some() 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 /// This implementation of `::selectors::Element` is used for implementing lazy

View file

@ -491,4 +491,14 @@ pub trait ThreadSafeLayoutElement<'dom>:
} }
fn is_shadow_host(&self) -> bool; 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;
} }