mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
layout: Add support for table rows, columns, rowgroups and colgroups (#31341)
This adds support for table rows, columns, rowgroups and colgroups. There are few additions here: 1. The createion of fragments, which allows script queries and hit testing to work properly. These fragments are empty as all cells are still direct descendants of the table fragment. 2. Properly handling size information from tracks and track groups as well as frustrating rules about reordering rowgroups. 3. Painting a background seemlessly across track groups and groups. This is a thing that isn't done in legacy layout (nor WebKit)! Co-authored-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
parent
74c07db56c
commit
02ae1f448e
57 changed files with 4274 additions and 21000 deletions
|
@ -2,6 +2,7 @@
|
|||
* 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 app_units::Au;
|
||||
use gfx_traits::print_tree::PrintTree;
|
||||
use serde::Serialize;
|
||||
use servo_arc::Arc as ServoArc;
|
||||
|
@ -20,6 +21,11 @@ use crate::geom::{
|
|||
};
|
||||
use crate::style_ext::ComputedValuesExt;
|
||||
|
||||
pub(crate) struct ExtraBackground {
|
||||
pub style: ServoArc<ComputedValues>,
|
||||
pub rect: LogicalRect<Au>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub(crate) struct BoxFragment {
|
||||
pub base: BaseFragment,
|
||||
|
@ -62,6 +68,9 @@ pub(crate) struct BoxFragment {
|
|||
/// during stacking context tree construction because they rely on the size of the
|
||||
/// scroll container.
|
||||
pub(crate) resolved_sticky_insets: Option<PhysicalSides<LengthOrAuto>>,
|
||||
|
||||
#[serde(skip_serializing)]
|
||||
pub extra_backgrounds: Vec<ExtraBackground>,
|
||||
}
|
||||
|
||||
impl BoxFragment {
|
||||
|
@ -149,6 +158,7 @@ impl BoxFragment {
|
|||
scrollable_overflow_from_children,
|
||||
overconstrained,
|
||||
resolved_sticky_insets: None,
|
||||
extra_backgrounds: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,6 +175,10 @@ impl BoxFragment {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn add_extra_background(&mut self, extra_background: ExtraBackground) {
|
||||
self.extra_backgrounds.push(extra_background);
|
||||
}
|
||||
|
||||
pub fn scrollable_overflow(
|
||||
&self,
|
||||
containing_block: &PhysicalRect<Length>,
|
||||
|
|
|
@ -10,14 +10,16 @@ use gfx_traits::print_tree::PrintTree;
|
|||
use msg::constellation_msg::{BrowsingContextId, PipelineId};
|
||||
use serde::Serialize;
|
||||
use servo_arc::Arc as ServoArc;
|
||||
use style::logical_geometry::WritingMode;
|
||||
use style::properties::ComputedValues;
|
||||
use style::values::computed::Length;
|
||||
use style::values::specified::text::TextDecorationLine;
|
||||
use style::Zero;
|
||||
use webrender_api::{FontInstanceKey, ImageKey};
|
||||
|
||||
use super::{BaseFragment, BoxFragment, ContainingBlockManager, HoistedSharedFragment, Tag};
|
||||
use super::{
|
||||
BaseFragment, BoxFragment, ContainingBlockManager, HoistedSharedFragment, PositioningFragment,
|
||||
Tag,
|
||||
};
|
||||
use crate::cell::ArcRefCell;
|
||||
use crate::geom::{LogicalRect, LogicalSides, PhysicalRect};
|
||||
use crate::style_ext::ComputedValuesExt;
|
||||
|
@ -31,7 +33,7 @@ pub(crate) enum Fragment {
|
|||
/// the [SequentialLayoutState] of their float containing block formatting
|
||||
/// context.
|
||||
Float(BoxFragment),
|
||||
Anonymous(AnonymousFragment),
|
||||
Positioning(PositioningFragment),
|
||||
/// Absolute and fixed position fragments are hoisted up so that they
|
||||
/// are children of the BoxFragment that establishes their containing
|
||||
/// blocks, so that they can be laid out properly. When this happens
|
||||
|
@ -63,18 +65,6 @@ pub(crate) struct CollapsedMargin {
|
|||
min_negative: Length,
|
||||
}
|
||||
|
||||
/// Can contain child fragments with relative coordinates, but does not contribute to painting itself.
|
||||
#[derive(Serialize)]
|
||||
pub(crate) struct AnonymousFragment {
|
||||
pub base: BaseFragment,
|
||||
pub rect: LogicalRect<Length>,
|
||||
pub children: Vec<ArcRefCell<Fragment>>,
|
||||
pub mode: WritingMode,
|
||||
|
||||
/// The scrollable overflow of this anonymous fragment's children.
|
||||
pub scrollable_overflow: PhysicalRect<Length>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub(crate) struct TextFragment {
|
||||
pub base: BaseFragment,
|
||||
|
@ -119,7 +109,7 @@ impl Fragment {
|
|||
Fragment::Box(fragment) => &fragment.base,
|
||||
Fragment::Text(fragment) => &fragment.base,
|
||||
Fragment::AbsoluteOrFixedPositioned(_) => return None,
|
||||
Fragment::Anonymous(fragment) => &fragment.base,
|
||||
Fragment::Positioning(fragment) => &fragment.base,
|
||||
Fragment::Image(fragment) => &fragment.base,
|
||||
Fragment::IFrame(fragment) => &fragment.base,
|
||||
Fragment::Float(fragment) => &fragment.base,
|
||||
|
@ -141,7 +131,7 @@ impl Fragment {
|
|||
Fragment::AbsoluteOrFixedPositioned(_) => {
|
||||
tree.add_item("AbsoluteOrFixedPositioned".to_string());
|
||||
},
|
||||
Fragment::Anonymous(fragment) => fragment.print(tree),
|
||||
Fragment::Positioning(fragment) => fragment.print(tree),
|
||||
Fragment::Text(fragment) => fragment.print(tree),
|
||||
Fragment::Image(fragment) => fragment.print(tree),
|
||||
Fragment::IFrame(fragment) => fragment.print(tree),
|
||||
|
@ -166,7 +156,7 @@ impl Fragment {
|
|||
fragment.scrollable_overflow_for_parent(containing_block)
|
||||
},
|
||||
Fragment::AbsoluteOrFixedPositioned(_) => PhysicalRect::zero(),
|
||||
Fragment::Anonymous(fragment) => fragment.scrollable_overflow,
|
||||
Fragment::Positioning(fragment) => fragment.scrollable_overflow,
|
||||
Fragment::Text(fragment) => fragment
|
||||
.rect
|
||||
.to_physical(fragment.parent_style.writing_mode, containing_block),
|
||||
|
@ -219,10 +209,10 @@ impl Fragment {
|
|||
.iter()
|
||||
.find_map(|child| child.borrow().find(&new_manager, level + 1, process_func))
|
||||
},
|
||||
Fragment::Anonymous(fragment) => {
|
||||
Fragment::Positioning(fragment) => {
|
||||
let content_rect = fragment
|
||||
.rect
|
||||
.to_physical(fragment.mode, containing_block)
|
||||
.to_physical(fragment.writing_mode, containing_block)
|
||||
.translate(containing_block.origin.to_vector());
|
||||
let new_manager = manager.new_for_non_absolute_descendants(&content_rect);
|
||||
fragment
|
||||
|
@ -235,43 +225,6 @@ impl Fragment {
|
|||
}
|
||||
}
|
||||
|
||||
impl AnonymousFragment {
|
||||
pub fn new(rect: LogicalRect<Length>, children: Vec<Fragment>, mode: WritingMode) -> Self {
|
||||
// FIXME(mrobinson, bug 25564): We should be using the containing block
|
||||
// here to properly convert scrollable overflow to physical geometry.
|
||||
let containing_block = PhysicalRect::zero();
|
||||
let content_origin = rect.start_corner.to_physical(mode);
|
||||
let scrollable_overflow = children.iter().fold(PhysicalRect::zero(), |acc, child| {
|
||||
acc.union(
|
||||
&child
|
||||
.scrollable_overflow(&containing_block)
|
||||
.translate(content_origin.to_vector()),
|
||||
)
|
||||
});
|
||||
AnonymousFragment {
|
||||
base: BaseFragment::anonymous(),
|
||||
rect,
|
||||
children: children.into_iter().map(ArcRefCell::new).collect(),
|
||||
mode,
|
||||
scrollable_overflow,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print(&self, tree: &mut PrintTree) {
|
||||
tree.new_level(format!(
|
||||
"Anonymous\
|
||||
\nrect={:?}\
|
||||
\nscrollable_overflow={:?}",
|
||||
self.rect, self.scrollable_overflow
|
||||
));
|
||||
|
||||
for child in &self.children {
|
||||
child.borrow().print(tree);
|
||||
}
|
||||
tree.end_level();
|
||||
}
|
||||
}
|
||||
|
||||
impl TextFragment {
|
||||
pub fn print(&self, tree: &mut PrintTree) {
|
||||
tree.add_item(format!(
|
||||
|
|
|
@ -106,13 +106,15 @@ impl FragmentTree {
|
|||
Fragment::Box(fragment) | Fragment::Float(fragment) => fragment
|
||||
.border_rect()
|
||||
.to_physical(fragment.style.writing_mode, containing_block),
|
||||
Fragment::Positioning(fragment) => fragment
|
||||
.rect
|
||||
.to_physical(fragment.writing_mode, containing_block),
|
||||
Fragment::Text(fragment) => fragment
|
||||
.rect
|
||||
.to_physical(fragment.parent_style.writing_mode, containing_block),
|
||||
Fragment::AbsoluteOrFixedPositioned(_) |
|
||||
Fragment::Image(_) |
|
||||
Fragment::IFrame(_) |
|
||||
Fragment::Anonymous(_) => return None,
|
||||
Fragment::IFrame(_) => return None,
|
||||
};
|
||||
|
||||
found_any_nodes = true;
|
||||
|
@ -145,37 +147,41 @@ impl FragmentTree {
|
|||
return None;
|
||||
}
|
||||
|
||||
let (style, padding_rect) = match fragment {
|
||||
Fragment::Box(fragment) => (&fragment.style, fragment.padding_rect()),
|
||||
let rect = match fragment {
|
||||
Fragment::Box(fragment) => {
|
||||
// https://drafts.csswg.org/cssom-view/#dom-element-clienttop
|
||||
// " If the element has no associated CSS layout box or if the
|
||||
// CSS layout box is inline, return zero." For this check we
|
||||
// also explicitly ignore the list item portion of the display
|
||||
// style.
|
||||
if fragment.style.get_box().display.is_inline_flow() {
|
||||
return Some(Rect::zero());
|
||||
}
|
||||
|
||||
let border = fragment.style.get_border();
|
||||
let padding_rect = fragment
|
||||
.padding_rect()
|
||||
.to_physical(fragment.style.writing_mode, containing_block);
|
||||
Rect::new(
|
||||
Point2D::new(
|
||||
border.border_left_width.into(),
|
||||
border.border_top_width.into(),
|
||||
),
|
||||
Size2D::new(padding_rect.size.width, padding_rect.size.height),
|
||||
)
|
||||
},
|
||||
Fragment::Positioning(fragment) => fragment
|
||||
.rect
|
||||
.to_physical(fragment.writing_mode, containing_block)
|
||||
.cast_unit(),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
// https://drafts.csswg.org/cssom-view/#dom-element-clienttop
|
||||
// " If the element has no associated CSS layout box or if the
|
||||
// CSS layout box is inline, return zero." For this check we
|
||||
// also explicitly ignore the list item portion of the display
|
||||
// style.
|
||||
let display = &style.get_box().display;
|
||||
if display.inside() == style::values::specified::box_::DisplayInside::Flow &&
|
||||
display.outside() == style::values::specified::box_::DisplayOutside::Inline
|
||||
{
|
||||
return Some(Rect::zero());
|
||||
}
|
||||
|
||||
let border = style.get_border();
|
||||
let padding_rect = padding_rect.to_physical(style.writing_mode, containing_block);
|
||||
Some(
|
||||
Rect::new(
|
||||
Point2D::new(
|
||||
border.border_left_width.to_f32_px(),
|
||||
border.border_top_width.to_f32_px(),
|
||||
),
|
||||
Size2D::new(padding_rect.size.width.px(), padding_rect.size.height.px()),
|
||||
)
|
||||
.round()
|
||||
.to_i32()
|
||||
.to_untyped(),
|
||||
)
|
||||
let rect = Rect::new(
|
||||
Point2D::new(rect.origin.x.px(), rect.origin.y.px()),
|
||||
Size2D::new(rect.size.width.px(), rect.size.height.px()),
|
||||
);
|
||||
Some(rect.round().to_i32().to_untyped())
|
||||
})
|
||||
.unwrap_or_else(Rect::zero)
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ mod containing_block;
|
|||
mod fragment;
|
||||
mod fragment_tree;
|
||||
mod hoisted_shared_fragment;
|
||||
mod positioning_fragment;
|
||||
|
||||
pub(crate) use base_fragment::*;
|
||||
pub(crate) use box_fragment::*;
|
||||
|
@ -15,3 +16,4 @@ pub(crate) use containing_block::*;
|
|||
pub(crate) use fragment::*;
|
||||
pub use fragment_tree::*;
|
||||
pub(crate) use hoisted_shared_fragment::*;
|
||||
pub(crate) use positioning_fragment::*;
|
||||
|
|
100
components/layout_2020/fragment_tree/positioning_fragment.rs
Normal file
100
components/layout_2020/fragment_tree/positioning_fragment.rs
Normal file
|
@ -0,0 +1,100 @@
|
|||
/* 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 gfx_traits::print_tree::PrintTree;
|
||||
use serde::Serialize;
|
||||
use servo_arc::Arc as ServoArc;
|
||||
use style::logical_geometry::WritingMode;
|
||||
use style::properties::ComputedValues;
|
||||
use style::values::computed::Length;
|
||||
|
||||
use super::{BaseFragment, BaseFragmentInfo, Fragment};
|
||||
use crate::cell::ArcRefCell;
|
||||
use crate::geom::{LogicalRect, PhysicalRect};
|
||||
|
||||
/// Can contain child fragments with relative coordinates, but does not contribute to painting
|
||||
/// itself. [`PositioningFragments`] may be completely anonymous, or just non-painting Fragments
|
||||
/// generated by boxes.
|
||||
#[derive(Serialize)]
|
||||
pub(crate) struct PositioningFragment {
|
||||
pub base: BaseFragment,
|
||||
pub rect: LogicalRect<Length>,
|
||||
pub children: Vec<ArcRefCell<Fragment>>,
|
||||
pub writing_mode: WritingMode,
|
||||
|
||||
/// The scrollable overflow of this anonymous fragment's children.
|
||||
pub scrollable_overflow: PhysicalRect<Length>,
|
||||
|
||||
/// If this fragment was created with a style, the style of the fragment.
|
||||
#[serde(skip_serializing)]
|
||||
pub style: Option<ServoArc<ComputedValues>>,
|
||||
}
|
||||
|
||||
impl PositioningFragment {
|
||||
pub fn new_anonymous(
|
||||
rect: LogicalRect<Length>,
|
||||
children: Vec<Fragment>,
|
||||
mode: WritingMode,
|
||||
) -> Self {
|
||||
Self::new_with_base_fragment(BaseFragment::anonymous(), None, rect, children, mode)
|
||||
}
|
||||
|
||||
pub fn new_empty(
|
||||
base_fragment_info: BaseFragmentInfo,
|
||||
rect: LogicalRect<Length>,
|
||||
style: ServoArc<ComputedValues>,
|
||||
) -> Self {
|
||||
let writing_mode = style.writing_mode;
|
||||
Self::new_with_base_fragment(
|
||||
base_fragment_info.into(),
|
||||
Some(style),
|
||||
rect,
|
||||
Vec::new(),
|
||||
writing_mode,
|
||||
)
|
||||
}
|
||||
|
||||
fn new_with_base_fragment(
|
||||
base: BaseFragment,
|
||||
style: Option<ServoArc<ComputedValues>>,
|
||||
rect: LogicalRect<Length>,
|
||||
children: Vec<Fragment>,
|
||||
mode: WritingMode,
|
||||
) -> Self {
|
||||
// FIXME(mrobinson, bug 25564): We should be using the containing block
|
||||
// here to properly convert scrollable overflow to physical geometry.
|
||||
let containing_block = PhysicalRect::zero();
|
||||
let content_origin = rect.start_corner.to_physical(mode);
|
||||
let scrollable_overflow = children.iter().fold(PhysicalRect::zero(), |acc, child| {
|
||||
acc.union(
|
||||
&child
|
||||
.scrollable_overflow(&containing_block)
|
||||
.translate(content_origin.to_vector()),
|
||||
)
|
||||
});
|
||||
PositioningFragment {
|
||||
base,
|
||||
style,
|
||||
rect,
|
||||
children: children.into_iter().map(ArcRefCell::new).collect(),
|
||||
writing_mode: mode,
|
||||
scrollable_overflow,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print(&self, tree: &mut PrintTree) {
|
||||
tree.new_level(format!(
|
||||
"PositioningFragment\
|
||||
\nbase={:?}\
|
||||
\nrect={:?}\
|
||||
\nscrollable_overflow={:?}",
|
||||
self.base, self.rect, self.scrollable_overflow
|
||||
));
|
||||
|
||||
for child in &self.children {
|
||||
child.borrow().print(tree);
|
||||
}
|
||||
tree.end_level();
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue