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.
This commit is contained in:
Martin Robinson 2023-04-30 20:21:58 +02:00
parent 77a184a0e7
commit 72302e2dae
26 changed files with 487 additions and 277 deletions

1
Cargo.lock generated
View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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, dont paint it again here.
return;
}
@ -494,26 +508,32 @@ impl<'a> BuilderForBoxFragment<'a> {
}
},
Image::Url(ref image_url) => {
// FIXME: images wont 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 wont 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;

View file

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

View file

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

View file

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

View file

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

View file

@ -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),
});

View file

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

View file

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

View file

@ -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::<()>;

View file

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

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/. */
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))
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -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(),

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> {
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

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> {
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

View file

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