layout: grid template getComputedStyle resolved value (#34885)

* Store taffy detailed info into fragment

Signed-off-by: stevennovaryo <steven.novaryo@gmail.com>

* Fix info propagation and resolved grid track query

Signed-off-by: stevennovaryo <steven.novaryo@gmail.com>

* Fix import

Signed-off-by: stevennovaryo <steven.novaryo@gmail.com>

* Fix tracklist matching logic and type optimization

Signed-off-by: stevennovaryo <steven.novaryo@gmail.com>

* Run fmt

Signed-off-by: stevennovaryo <steven.novaryo@gmail.com>

* Update wpt expectations

Signed-off-by: stevennovaryo <steven.novaryo@gmail.com>

* Optimizing info propagation and minor qol

Signed-off-by: stevennovaryo <steven.novaryo@gmail.com>

* Run fmt

Signed-off-by: stevennovaryo <steven.novaryo@gmail.com>

---------

Signed-off-by: stevennovaryo <steven.novaryo@gmail.com>
This commit is contained in:
Steven Novaryo 2025-01-09 18:49:27 +08:00 committed by GitHub
parent 040e29415b
commit 76fa456a9a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 297 additions and 778 deletions

View file

@ -976,6 +976,7 @@ impl FlexContainer {
content_inline_size_for_table: None,
baselines,
depends_on_block_constraints,
detailed_layout_info: None,
}
}

View file

@ -386,6 +386,7 @@ impl BlockFormattingContext {
content_inline_size_for_table: None,
baselines: flow_layout.baselines,
depends_on_block_constraints: flow_layout.depends_on_block_constraints,
detailed_layout_info: None,
}
}
}
@ -1146,6 +1147,7 @@ impl IndependentNonReplacedContents {
block_margins_collapsed_with_children,
)
.with_baselines(layout.baselines)
.with_detailed_layout_info(layout.detailed_layout_info)
}
/// Lay out a normal in flow non-replaced block that establishes an independent
@ -1455,6 +1457,7 @@ impl IndependentNonReplacedContents {
CollapsedBlockMargins::from_margin(&margin),
)
.with_baselines(layout.baselines)
.with_detailed_layout_info(layout.detailed_layout_info)
}
}

View file

@ -14,7 +14,9 @@ use crate::dom::NodeExt;
use crate::dom_traversal::{Contents, NodeAndStyleInfo};
use crate::flexbox::FlexContainer;
use crate::flow::BlockFormattingContext;
use crate::fragment_tree::{BaseFragmentInfo, BoxFragment, Fragment, FragmentFlags};
use crate::fragment_tree::{
BaseFragmentInfo, BoxFragment, DetailedLayoutInfo, Fragment, FragmentFlags,
};
use crate::geom::LogicalSides;
use crate::layout_box_base::LayoutBoxBase;
use crate::positioned::PositioningContext;
@ -84,6 +86,9 @@ pub(crate) struct IndependentLayout {
/// Whether or not this layout depends on the containing block size.
pub depends_on_block_constraints: bool,
/// Additional information of this layout that could be used by Javascripts and devtools.
pub detailed_layout_info: Option<DetailedLayoutInfo>,
}
pub(crate) struct IndependentLayoutResult {

View file

@ -20,6 +20,7 @@ use crate::geom::{
AuOrAuto, LengthPercentageOrAuto, PhysicalPoint, PhysicalRect, PhysicalSides, ToLogical,
};
use crate::style_ext::ComputedValuesExt;
use crate::taffy::DetailedTaffyGridInfo;
/// Describes how a [`BoxFragment`] paints its background.
pub(crate) enum BackgroundMode {
@ -39,6 +40,11 @@ pub(crate) struct ExtraBackground {
pub rect: PhysicalRect<Au>,
}
#[derive(Clone, Debug)]
pub(crate) enum DetailedLayoutInfo {
Grid(Box<DetailedTaffyGridInfo>),
}
#[derive(Serialize)]
pub(crate) struct BoxFragment {
pub base: BaseFragment,
@ -80,6 +86,10 @@ pub(crate) struct BoxFragment {
#[serde(skip_serializing)]
pub background_mode: BackgroundMode,
/// Additional information of from layout that could be used by Javascripts and devtools.
#[serde(skip_serializing)]
pub detailed_layout_info: Option<DetailedLayoutInfo>,
}
impl BoxFragment {
@ -114,6 +124,7 @@ impl BoxFragment {
scrollable_overflow_from_children,
resolved_sticky_insets: None,
background_mode: BackgroundMode::Normal,
detailed_layout_info: None,
}
}
@ -166,6 +177,11 @@ impl BoxFragment {
self.background_mode = BackgroundMode::None;
}
pub fn with_detailed_layout_info(mut self, info: Option<DetailedLayoutInfo>) -> Self {
self.detailed_layout_info = info;
self
}
pub fn scrollable_overflow(&self) -> PhysicalRect<Au> {
let physical_padding_rect = self.padding_rect();
let content_origin = self.content_rect.origin.to_vector();

View file

@ -23,7 +23,8 @@ use crate::formatting_contexts::{
IndependentFormattingContext, IndependentFormattingContextContents,
};
use crate::fragment_tree::{
BoxFragment, CollapsedBlockMargins, Fragment, FragmentFlags, HoistedSharedFragment,
BoxFragment, CollapsedBlockMargins, DetailedLayoutInfo, Fragment, FragmentFlags,
HoistedSharedFragment,
};
use crate::geom::{
AuOrAuto, LengthPercentageOrAuto, LogicalRect, LogicalSides, LogicalVec2, PhysicalPoint,
@ -561,6 +562,7 @@ impl HoistedAbsolutelyPositionedBox {
let mut new_fragment = {
let content_size: LogicalVec2<Au>;
let fragments;
let mut detailed_layout_info: Option<DetailedLayoutInfo> = None;
match &context.contents {
IndependentFormattingContextContents::Replaced(replaced) => {
// https://drafts.csswg.org/css2/visudet.html#abs-replaced-width
@ -629,6 +631,7 @@ impl HoistedAbsolutelyPositionedBox {
block: block_size,
};
fragments = independent_layout.fragments;
detailed_layout_info = independent_layout.detailed_layout_info;
},
};
@ -676,6 +679,7 @@ impl HoistedAbsolutelyPositionedBox {
// elements are not inflow.
CollapsedBlockMargins::zero(),
)
.with_detailed_layout_info(detailed_layout_info)
};
positioning_context.layout_collected_children(layout_context, &mut new_fragment);

View file

@ -8,6 +8,7 @@ use std::sync::Arc;
use app_units::Au;
use euclid::default::{Point2D, Rect};
use euclid::{SideOffsets2D, Size2D, Vector2D};
use itertools::Itertools;
use log::warn;
use script_layout_interface::wrapper_traits::{
LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode,
@ -33,11 +34,16 @@ use style::stylist::RuleInclusion;
use style::traversal::resolve_style;
use style::values::computed::Float;
use style::values::generics::font::LineHeight;
use style::values::specified::box_::DisplayInside;
use style::values::specified::GenericGridTemplateComponent;
use style_traits::{ParsingMode, ToCss};
use crate::flow::inline::construct::{TextTransformation, WhitespaceCollapse};
use crate::fragment_tree::{BoxFragment, Fragment, FragmentFlags, FragmentTree, Tag};
use crate::fragment_tree::{
BoxFragment, DetailedLayoutInfo, Fragment, FragmentFlags, FragmentTree, Tag,
};
use crate::geom::{PhysicalRect, PhysicalVec};
use crate::taffy::DetailedTaffyGridInfo;
pub fn process_content_box_request(
requested_node: OpaqueNode,
@ -184,7 +190,7 @@ pub fn process_resolved_style_request<'dom>(
return None;
}
let (content_rect, margins, padding) = match fragment {
let (content_rect, margins, padding, detailed_layout_info) = match fragment {
Fragment::Box(ref box_fragment) | Fragment::Float(ref box_fragment) => {
if style.get_box().position != Position::Static {
let resolved_insets = || {
@ -207,15 +213,34 @@ pub fn process_resolved_style_request<'dom>(
let content_rect = box_fragment.content_rect;
let margins = box_fragment.margin;
let padding = box_fragment.padding;
(content_rect, margins, padding)
let detailed_layout_info = &box_fragment.detailed_layout_info;
(content_rect, margins, padding, detailed_layout_info)
},
Fragment::Positioning(positioning_fragment) => {
let content_rect = positioning_fragment.rect;
(content_rect, SideOffsets2D::zero(), SideOffsets2D::zero())
(
content_rect,
SideOffsets2D::zero(),
SideOffsets2D::zero(),
&None,
)
},
_ => return None,
};
// https://drafts.csswg.org/css-grid/#resolved-track-list
// > The grid-template-rows and grid-template-columns properties are
// > resolved value special case properties.
//
// > When an element generates a grid container box...
if display.inside() == DisplayInside::Grid {
if let Some(DetailedLayoutInfo::Grid(info)) = detailed_layout_info {
if let Some(value) = resolve_grid_template(info, style, longhand_id) {
return Some(value);
}
}
}
// https://drafts.csswg.org/cssom/#resolved-value-special-case-property-like-height
// > If the property applies to the element or pseudo-element and the resolved value of the
// > display property is not none or contents, then the resolved value is the used value.
@ -259,6 +284,63 @@ fn resolved_size_should_be_used_value(fragment: &Fragment) -> bool {
}
}
fn resolve_grid_template(
grid_info: &DetailedTaffyGridInfo,
style: &ComputedValues,
longhand_id: LonghandId,
) -> Option<String> {
// https://drafts.csswg.org/css-grid/#resolved-track-list-standalone
fn serialize_standalone_non_subgrid_track_list(track_sizes: &[Au]) -> Option<String> {
match track_sizes.is_empty() {
// Standalone non subgrid grids with empty track lists should compute to `none`.
// As of current standard, this behaviour should only invoked by `none` computed value,
// therefore we can fallback into computed value resolving.
true => None,
// <https://drafts.csswg.org/css-grid/#resolved-track-list-standalone>
// > - Every track listed individually, whether implicitly or explicitly created,
// without using the repeat() notation.
// > - Every track size given as a length in pixels, regardless of sizing function.
// > - Adjacent line names collapsed into a single bracketed set.
// TODO: implement line names
false => Some(
track_sizes
.iter()
.map(|size| size.to_css_string())
.join(" "),
),
}
}
let (track_info, computed_value) = match longhand_id {
LonghandId::GridTemplateRows => (&grid_info.rows, &style.get_position().grid_template_rows),
LonghandId::GridTemplateColumns => (
&grid_info.columns,
&style.get_position().grid_template_columns,
),
_ => return None,
};
match computed_value {
// <https://drafts.csswg.org/css-grid/#resolved-track-list-standalone>
// > When an element generates a grid container box, the resolved value of its grid-template-rows or
// > grid-template-columns property in a standalone axis is the used value, serialized with:
GenericGridTemplateComponent::None |
GenericGridTemplateComponent::TrackList(_) |
GenericGridTemplateComponent::Masonry => {
serialize_standalone_non_subgrid_track_list(&track_info.sizes)
},
// <https://drafts.csswg.org/css-grid/#resolved-track-list-subgrid>
// > When an element generates a grid container box that is a subgrid, the resolved value of the
// > grid-template-rows and grid-template-columns properties represents the used number of columns,
// > serialized as the subgrid keyword followed by a list representing each of its lines as a
// > line name set of all the lines names explicitly defined on the subgrid (not including those
// > adopted from the parent grid), without using the repeat() notation.
// TODO: implement subgrid
GenericGridTemplateComponent::Subgrid(_) => None,
}
}
pub fn process_resolved_style_request_for_unstyled_node<'dom>(
context: &SharedStyleContext,
node: impl LayoutNode<'dom>,

View file

@ -1683,6 +1683,7 @@ impl<'a> TableLayout<'a> {
content_inline_size_for_table: None,
baselines: Baselines::default(),
depends_on_block_constraints,
detailed_layout_info: None,
};
table_layout

View file

@ -12,14 +12,16 @@ use style::Zero;
use taffy::style_helpers::{TaffyMaxContent, TaffyMinContent};
use taffy::{AvailableSpace, MaybeMath, RequestedAxis, RunMode};
use super::{TaffyContainer, TaffyItemBox, TaffyItemBoxInner, TaffyStyloStyle};
use super::{
DetailedTaffyGridInfo, TaffyContainer, TaffyItemBox, TaffyItemBoxInner, TaffyStyloStyle,
};
use crate::cell::ArcRefCell;
use crate::context::LayoutContext;
use crate::formatting_contexts::{
Baselines, IndependentFormattingContext, IndependentFormattingContextContents,
IndependentLayout,
};
use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, Fragment};
use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, DetailedLayoutInfo, Fragment};
use crate::geom::{
LogicalSides, LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalSize, Size,
SizeConstraint, Sizes,
@ -64,6 +66,10 @@ struct TaffyContainerContext<'a> {
positioning_context: &'a mut PositioningContext,
content_box_size_override: &'a ContainingBlock<'a>,
style: &'a ComputedValues,
detailed_layout_info: Option<DetailedLayoutInfo>,
/// Temporary location for children detailed info, which will be moved into child fragments
child_detailed_layout_infos: Vec<Option<DetailedLayoutInfo>>,
}
struct ChildIter(std::ops::Range<usize>);
@ -264,6 +270,8 @@ impl taffy::LayoutPartialTree for TaffyContainerContext<'_> {
};
child.child_fragments = layout.fragments;
self.child_detailed_layout_infos[usize::from(node_id)] =
layout.detailed_layout_info;
let block_size = layout.content_block_size.to_f32_px();
@ -314,6 +322,16 @@ impl taffy::LayoutGridContainer for TaffyContainerContext<'_> {
let child = (*self.source_child_nodes[id]).borrow();
TaffyStyloStyle(AtomicRef::map(child, |c| &*c.style))
}
fn set_detailed_grid_info(
&mut self,
_node_id: taffy::NodeId,
detailed_layout_info: taffy::DetailedGridInfo,
) {
self.detailed_layout_info = Some(DetailedLayoutInfo::Grid(Box::new(
DetailedTaffyGridInfo::from_detailed_grid_layout(detailed_layout_info),
)));
}
}
impl ComputeInlineContentSizes for TaffyContainer {
@ -355,6 +373,8 @@ impl ComputeInlineContentSizes for TaffyContainer {
content_box_size_override: containing_block,
style,
source_child_nodes: &self.children,
detailed_layout_info: None,
child_detailed_layout_infos: vec![None; self.children.len()],
};
let (max_content_output, min_content_output) = match style.clone_display().inside() {
@ -408,6 +428,8 @@ impl TaffyContainer {
content_box_size_override,
style: content_box_size_override.style,
source_child_nodes: &self.children,
detailed_layout_info: None,
child_detailed_layout_infos: vec![None; self.children.len()],
};
fn auto_or_to_option<T>(input: GenericLengthPercentageOrAuto<T>) -> Option<T> {
@ -456,11 +478,13 @@ impl TaffyContainer {
};
// Convert `taffy::Layout` into Servo `Fragment`s
// with container_ctx.child_detailed_layout_infos will also moved to the corresponding `Fragment`s
let fragments: Vec<Fragment> = self
.children
.iter()
.map(|child| (**child).borrow_mut())
.map(|mut child| {
.enumerate()
.map(|(child_id, mut child)| {
fn rect_to_logical_sides<T>(rect: taffy::Rect<T>) -> LogicalSides<T> {
LogicalSides {
inline_start: rect.left,
@ -522,6 +546,9 @@ impl TaffyContainer {
.map(Au::from_f32_px),
);
let child_detailed_layout_info: Option<DetailedLayoutInfo> =
std::mem::take(&mut container_ctx.child_detailed_layout_infos[child_id]);
match &mut child.taffy_level_box {
TaffyItemBoxInner::InFlowBox(independent_box) => {
let fragment = Fragment::Box(
@ -539,7 +566,8 @@ impl TaffyContainer {
.with_baselines(Baselines {
first: output.first_baselines.y.map(Au::from_f32_px),
last: None,
}),
})
.with_detailed_layout_info(child_detailed_layout_info),
);
child
@ -604,6 +632,8 @@ impl TaffyContainer {
// "true" is a safe default as it will prevent Servo from performing optimizations based
// on the assumption that the node's size does not depend on block constraints.
depends_on_block_constraints: true,
detailed_layout_info: container_ctx.detailed_layout_info,
}
}
}

View file

@ -5,6 +5,7 @@ mod layout;
mod stylo_taffy;
use std::fmt;
use app_units::Au;
use serde::Serialize;
use servo_arc::Arc;
use style::properties::ComputedValues;
@ -117,3 +118,38 @@ impl TaffyItemBox {
}
}
}
/// Details from Taffy grid layout that will be stored
#[derive(Clone, Debug)]
pub(crate) struct DetailedTaffyGridInfo {
pub rows: DetailedTaffyGridTrackInfo,
pub columns: DetailedTaffyGridTrackInfo,
}
impl DetailedTaffyGridInfo {
fn from_detailed_grid_layout(grid_info: taffy::DetailedGridInfo) -> Self {
Self {
rows: DetailedTaffyGridTrackInfo {
sizes: grid_info
.rows
.sizes
.iter()
.map(|size| Au::from_f32_px(*size))
.collect(),
},
columns: DetailedTaffyGridTrackInfo {
sizes: grid_info
.columns
.sizes
.iter()
.map(|size| Au::from_f32_px(*size))
.collect(),
},
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct DetailedTaffyGridTrackInfo {
pub sizes: Box<[Au]>,
}