mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
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:
parent
77a184a0e7
commit
72302e2dae
26 changed files with 487 additions and 277 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -3147,6 +3147,7 @@ version = "0.0.1"
|
|||
dependencies = [
|
||||
"app_units",
|
||||
"atomic_refcell",
|
||||
"bitflags",
|
||||
"canvas_traits",
|
||||
"cssparser",
|
||||
"embedder_traits",
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
use crate::context::LayoutContext;
|
||||
use crate::display_list::conversions::ToWebRender;
|
||||
use crate::display_list::stacking_context::StackingContextSection;
|
||||
use crate::fragments::{BoxFragment, Fragment, Tag, TextFragment};
|
||||
use crate::fragment_tree::Tag;
|
||||
use crate::fragments::{BoxFragment, Fragment, TextFragment};
|
||||
use crate::geom::{PhysicalPoint, PhysicalRect};
|
||||
use crate::replaced::IntrinsicSizes;
|
||||
use crate::style_ext::ComputedValuesExt;
|
||||
|
@ -95,19 +96,24 @@ impl<'a> DisplayListBuilder<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn hit_info(&mut self, style: &ComputedValues, tag: Tag, auto_cursor: Cursor) -> HitInfo {
|
||||
fn hit_info(
|
||||
&mut self,
|
||||
style: &ComputedValues,
|
||||
tag: Option<Tag>,
|
||||
auto_cursor: Cursor,
|
||||
) -> HitInfo {
|
||||
use style::computed_values::pointer_events::T as PointerEvents;
|
||||
|
||||
let inherited_ui = style.get_inherited_ui();
|
||||
if inherited_ui.pointer_events == PointerEvents::None {
|
||||
None
|
||||
} else {
|
||||
let hit_test_index = self.compositor_info.add_hit_test_info(
|
||||
tag.node().0 as u64,
|
||||
Some(cursor(inherited_ui.cursor.keyword, auto_cursor)),
|
||||
);
|
||||
Some((hit_test_index as u64, 0u16))
|
||||
return None;
|
||||
}
|
||||
|
||||
let hit_test_index = self.compositor_info.add_hit_test_info(
|
||||
tag?.node.0 as u64,
|
||||
Some(cursor(inherited_ui.cursor.keyword, auto_cursor)),
|
||||
);
|
||||
Some((hit_test_index as u64, 0u16))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,7 +216,7 @@ impl Fragment {
|
|||
}
|
||||
|
||||
let mut common = builder.common_properties(rect.to_webrender(), &fragment.parent_style);
|
||||
common.hit_info = builder.hit_info(&fragment.parent_style, fragment.tag, Cursor::Text);
|
||||
common.hit_info = builder.hit_info(&fragment.parent_style, fragment.base.tag, Cursor::Text);
|
||||
|
||||
let color = fragment.parent_style.clone_color();
|
||||
let font_metrics = &fragment.font_metrics;
|
||||
|
@ -431,7 +437,11 @@ impl<'a> BuilderForBoxFragment<'a> {
|
|||
}
|
||||
|
||||
fn build_hit_test(&self, builder: &mut DisplayListBuilder) {
|
||||
let hit_info = builder.hit_info(&self.fragment.style, self.fragment.tag, Cursor::Default);
|
||||
let hit_info = builder.hit_info(
|
||||
&self.fragment.style,
|
||||
self.fragment.base.tag,
|
||||
Cursor::Default,
|
||||
);
|
||||
if hit_info.is_some() {
|
||||
let mut common = builder.common_properties(self.border_rect, &self.fragment.style);
|
||||
common.hit_info = hit_info;
|
||||
|
@ -443,7 +453,11 @@ impl<'a> BuilderForBoxFragment<'a> {
|
|||
}
|
||||
|
||||
fn build_background(&mut self, builder: &mut DisplayListBuilder) {
|
||||
if self.fragment.tag.node() == builder.element_for_canvas_background {
|
||||
if self
|
||||
.fragment
|
||||
.base
|
||||
.is_for_node(builder.element_for_canvas_background)
|
||||
{
|
||||
// This background is already painted for the canvas, don’t paint it again here.
|
||||
return;
|
||||
}
|
||||
|
@ -494,26 +508,32 @@ impl<'a> BuilderForBoxFragment<'a> {
|
|||
}
|
||||
},
|
||||
Image::Url(ref image_url) => {
|
||||
// FIXME: images won’t always have in intrinsic width or height
|
||||
// when support for SVG is added.
|
||||
// Or a WebRender `ImageKey`, for that matter.
|
||||
let (width, height, key) = match image_url.url() {
|
||||
Some(url) => {
|
||||
match builder.context.get_webrender_image_for_url(
|
||||
self.fragment.tag.node(),
|
||||
url.clone(),
|
||||
UsePlaceholder::No,
|
||||
) {
|
||||
Some(WebRenderImageInfo {
|
||||
width,
|
||||
height,
|
||||
key: Some(key),
|
||||
}) => (width, height, key),
|
||||
_ => continue,
|
||||
}
|
||||
},
|
||||
// FIXME: images won’t always have in intrinsic width or
|
||||
// height when support for SVG is added, or a WebRender
|
||||
// `ImageKey`, for that matter.
|
||||
//
|
||||
// FIXME: It feels like this should take into account the pseudo
|
||||
// element and not just the node.
|
||||
let node = match self.fragment.base.tag {
|
||||
Some(tag) => tag.node,
|
||||
None => continue,
|
||||
};
|
||||
let image_url = match image_url.url() {
|
||||
Some(url) => url.clone(),
|
||||
None => continue,
|
||||
};
|
||||
let (width, height, key) = match builder.context.get_webrender_image_for_url(
|
||||
node,
|
||||
image_url,
|
||||
UsePlaceholder::No,
|
||||
) {
|
||||
Some(WebRenderImageInfo {
|
||||
width,
|
||||
height,
|
||||
key: Some(key),
|
||||
}) => (width, height, key),
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
// FIXME: https://drafts.csswg.org/css-images-4/#the-image-resolution
|
||||
let dppx = 1.0;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
}),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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),
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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::<()>;
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
117
components/layout_2020/fragment_tree/base.rs
Normal file
117
components/layout_2020/fragment_tree/base.rs
Normal 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
|
||||
}
|
||||
}
|
7
components/layout_2020/fragment_tree/mod.rs
Normal file
7
components/layout_2020/fragment_tree/mod.rs
Normal 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::*;
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue