aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorMartin Robinson <mrobinson@igalia.com>2024-04-22 16:23:35 +0200
committerGitHub <noreply@github.com>2024-04-22 14:23:35 +0000
commit97376e6d96abcbdfd30f3a91ec5aee7ce2add178 (patch)
treefd9b0698c1ea22f344c4404c5fe9e002ad7f1627 /components
parent363651c7f756e7b47281bbe22fda21b7ccfda7e3 (diff)
downloadservo-97376e6d96abcbdfd30f3a91ec5aee7ce2add178.tar.gz
servo-97376e6d96abcbdfd30f3a91ec5aee7ce2add178.zip
layout: Add a basic support for `list-style-position: outside` (#32114)
This change adds very basic support for `list-style-position`. Currently, the marker does not do any kind of baseline alignment with the rest of the list item contents and it also doesn't force the list item to be at least as tall as the marker. This adds a few new failures: - Four failures because markers do not ensure that list-items have at least the same block size as they do: - FAIL [expected PASS] /css/CSS2/lists/list-style-applies-to-012.xht - FAIL [expected PASS] /css/CSS2/lists/list-style-applies-to-014.xht - FAIL [expected PASS] /css/CSS2/lists/list-style-type-applies-to-012.xht - FAIL [expected PASS] /css/CSS2/lists/list-style-type-applies-to-014.xht - One failure because we don't yet support the `::marker` pseudo-selector: - FAIL [expected PASS] /css/css-position/position-absolute-dynamic-list-marker.html - One failure because we don't support the list item exception for the line height quirk: - FAIL [expected PASS] /quirks/line-height-in-list-item.tentative.html Co-authored-by: Oriol Brufau <obrufau@igalia.com> <!-- Please describe your changes on the following line: --> Fixes #27383. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes fix #27383. - [ ] There are tests for these changes OR - [ ] These changes do not require tests because ___ <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Co-authored-by: Oriol Brufau <obrufau@igalia.com>
Diffstat (limited to 'components')
-rw-r--r--components/layout_2020/flow/construct.rs52
-rw-r--r--components/layout_2020/flow/mod.rs93
-rw-r--r--components/layout_2020/lists.rs13
3 files changed, 141 insertions, 17 deletions
diff --git a/components/layout_2020/flow/construct.rs b/components/layout_2020/flow/construct.rs
index d61804751b0..02595e2b3c6 100644
--- a/components/layout_2020/flow/construct.rs
+++ b/components/layout_2020/flow/construct.rs
@@ -13,10 +13,13 @@ use style::selector_parser::PseudoElement;
use style::str::char_is_whitespace;
use style::values::specified::text::TextDecorationLine;
+use super::OutsideMarker;
use crate::cell::ArcRefCell;
use crate::context::LayoutContext;
use crate::dom::{BoxSlot, LayoutBox, NodeExt};
-use crate::dom_traversal::{Contents, NodeAndStyleInfo, NonReplacedContents, TraversalHandler};
+use crate::dom_traversal::{
+ Contents, NodeAndStyleInfo, NonReplacedContents, PseudoElementContentItem, TraversalHandler,
+};
use crate::flow::float::FloatBox;
use crate::flow::inline::{InlineBox, InlineFormattingContext, InlineLevelBox};
use crate::flow::text_run::TextRun;
@@ -98,6 +101,9 @@ enum BlockLevelCreator {
display_inside: DisplayInside,
contents: Contents,
},
+ OutsideMarker {
+ contents: Vec<PseudoElementContentItem>,
+ },
AnonymousTable {
table_block: ArcRefCell<BlockLevelBox>,
},
@@ -195,17 +201,12 @@ impl BlockContainer {
if is_list_item {
if let Some(marker_contents) = crate::lists::make_marker(context, info) {
- let _position = info.style.clone_list_style_position();
- // FIXME: implement support for `outside` and remove this:
- let position = ListStylePosition::Inside;
- match position {
+ match info.style.clone_list_style_position() {
ListStylePosition::Inside => {
builder.handle_list_item_marker_inside(info, marker_contents)
},
ListStylePosition::Outside => {
- // FIXME: implement layout for this case
- // https://github.com/servo/servo/issues/27383
- // and enable `list-style-position` and the `list-style` shorthand in Stylo.
+ builder.handle_list_item_marker_outside(info, marker_contents)
},
}
}
@@ -452,6 +453,18 @@ where
);
}
+ fn handle_list_item_marker_outside(
+ &mut self,
+ info: &NodeAndStyleInfo<Node>,
+ contents: Vec<crate::dom_traversal::PseudoElementContentItem>,
+ ) {
+ self.block_level_boxes.push(BlockLevelJob {
+ info: info.clone(),
+ box_slot: BoxSlot::dummy(),
+ kind: BlockLevelCreator::OutsideMarker { contents },
+ });
+ }
+
fn handle_inline_level_element(
&mut self,
info: &NodeAndStyleInfo<Node>,
@@ -768,6 +781,29 @@ where
display_inside,
contents,
))),
+ BlockLevelCreator::OutsideMarker { contents } => {
+ let marker_style = context
+ .shared_context()
+ .stylist
+ .style_for_anonymous::<Node::ConcreteElement>(
+ &context.shared_context().guards,
+ &PseudoElement::ServoLegacyText, // FIMXE: use `PseudoElement::Marker` when we add it
+ &info.style,
+ );
+ let info = info.new_replacing_style(marker_style.clone());
+ let contents = NonReplacedContents::OfPseudoElement(contents);
+ let block_container = BlockContainer::construct(
+ context,
+ &info,
+ contents,
+ TextDecorationLine::empty(),
+ false, /* is_list_item */
+ );
+ ArcRefCell::new(BlockLevelBox::OutsideMarker(OutsideMarker {
+ style: marker_style,
+ block_container,
+ }))
+ },
BlockLevelCreator::AnonymousTable { table_block } => table_block,
};
self.box_slot
diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs
index 4067489219d..c04f85ae351 100644
--- a/components/layout_2020/flow/mod.rs
+++ b/components/layout_2020/flow/mod.rs
@@ -71,6 +71,10 @@ impl BlockContainer {
#[derive(Debug, Serialize)]
pub(crate) enum BlockLevelBox {
+ Independent(IndependentFormattingContext),
+ OutOfFlowAbsolutelyPositionedBox(ArcRefCell<AbsolutelyPositionedBox>),
+ OutOfFlowFloatBox(FloatBox),
+ OutsideMarker(OutsideMarker),
SameFormattingContextBlock {
base_fragment_info: BaseFragmentInfo,
#[serde(skip_serializing)]
@@ -78,9 +82,6 @@ pub(crate) enum BlockLevelBox {
contents: BlockContainer,
contains_floats: bool,
},
- OutOfFlowAbsolutelyPositionedBox(ArcRefCell<AbsolutelyPositionedBox>),
- OutOfFlowFloatBox(FloatBox),
- Independent(IndependentFormattingContext),
}
impl BlockLevelBox {
@@ -103,6 +104,7 @@ impl BlockLevelBox {
BlockLevelBox::SameFormattingContextBlock { ref style, .. } => style,
BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(_) |
BlockLevelBox::OutOfFlowFloatBox(_) => return true,
+ BlockLevelBox::OutsideMarker(_) => return false,
BlockLevelBox::Independent(ref context) => {
// FIXME: If the element doesn't fit next to floats, it will get clearance.
// In that case this should be returning false.
@@ -205,6 +207,81 @@ struct FlowLayout {
#[derive(Clone, Copy)]
struct CollapsibleWithParentStartMargin(bool);
+/// The contentes of a BlockContainer created to render a list marker
+/// for a list that has `list-style-position: outside`.
+#[derive(Debug, Serialize)]
+pub(crate) struct OutsideMarker {
+ #[serde(skip_serializing)]
+ pub style: Arc<ComputedValues>,
+ pub block_container: BlockContainer,
+}
+
+impl OutsideMarker {
+ fn layout(
+ &self,
+ layout_context: &LayoutContext<'_>,
+ containing_block: &ContainingBlock<'_>,
+ positioning_context: &mut PositioningContext,
+ sequential_layout_state: Option<&mut SequentialLayoutState>,
+ collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
+ ) -> Fragment {
+ let content_sizes = self
+ .block_container
+ .inline_content_sizes(layout_context, containing_block.style.writing_mode);
+ let containing_block = ContainingBlock {
+ inline_size: content_sizes.max_content,
+ block_size: AuOrAuto::auto(),
+ style: &self.style,
+ };
+ let flow_layout = self.block_container.layout(
+ layout_context,
+ positioning_context,
+ &containing_block,
+ sequential_layout_state,
+ collapsible_with_parent_start_margin.unwrap_or(CollapsibleWithParentStartMargin(false)),
+ );
+ let max_inline_size = flow_layout.fragments.iter().fold(
+ Length::zero(),
+ |current_max, fragment| match fragment {
+ Fragment::Text(text) => current_max.max(text.rect.max_inline_position()),
+ Fragment::Image(image) => current_max.max(image.rect.max_inline_position()),
+ Fragment::Positioning(positioning) => {
+ current_max.max(positioning.rect.max_inline_position())
+ },
+ Fragment::Box(_) |
+ Fragment::Float(_) |
+ Fragment::AbsoluteOrFixedPositioned(_) |
+ Fragment::IFrame(_) => {
+ unreachable!("Found unexpected fragment type in outside list marker!");
+ },
+ },
+ );
+
+ let content_rect = LogicalRect {
+ start_corner: LogicalVec2 {
+ inline: -max_inline_size,
+ block: Zero::zero(),
+ },
+ size: LogicalVec2 {
+ inline: max_inline_size,
+ block: Zero::zero(),
+ },
+ };
+
+ Fragment::Box(BoxFragment::new(
+ BaseFragmentInfo::anonymous(),
+ self.style.clone(),
+ flow_layout.fragments,
+ content_rect,
+ LogicalSides::zero(),
+ LogicalSides::zero(),
+ LogicalSides::zero(),
+ None,
+ CollapsedBlockMargins::zero(),
+ ))
+ }
+}
+
impl BlockFormattingContext {
pub(super) fn layout(
&self,
@@ -261,7 +338,8 @@ fn calculate_inline_content_size_for_block_level_boxes(
) -> ContentSizes {
let get_box_info = |box_: &ArcRefCell<BlockLevelBox>| {
match &mut *box_.borrow_mut() {
- BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(_) => None,
+ BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(_) |
+ BlockLevelBox::OutsideMarker { .. } => None,
BlockLevelBox::OutOfFlowFloatBox(ref mut float_box) => {
let size = float_box
.contents
@@ -603,6 +681,13 @@ impl BlockLevelBox {
positioning_context,
containing_block,
)),
+ BlockLevelBox::OutsideMarker(outside_marker) => outside_marker.layout(
+ layout_context,
+ containing_block,
+ positioning_context,
+ sequential_layout_state,
+ collapsible_with_parent_start_margin,
+ ),
}
}
}
diff --git a/components/layout_2020/lists.rs b/components/layout_2020/lists.rs
index 6c71b5f34d0..b0e97ab02e3 100644
--- a/components/layout_2020/lists.rs
+++ b/components/layout_2020/lists.rs
@@ -55,11 +55,14 @@ where
fn marker_string(style: &style_structs::List) -> Option<&'static str> {
match style.list_style_type {
ListStyleType::None => None,
- ListStyleType::Disc => Some("• "),
- ListStyleType::Circle => Some("◦ "),
- ListStyleType::Square => Some("▪ "),
- ListStyleType::DisclosureOpen => Some("▾ "),
- ListStyleType::DisclosureClosed => Some("‣ "),
+ // TODO: Using non-breaking space here is a bit of a hack to give a bit of margin to outside
+ // markers, but really we should be setting `white-space: pre` on them instead.
+ // See https://github.com/w3c/csswg-drafts/issues/4891.
+ ListStyleType::Disc => Some("•\u{00a0}"),
+ ListStyleType::Circle => Some("◦\u{00a0}"),
+ ListStyleType::Square => Some("▪\u{00a0}"),
+ ListStyleType::DisclosureOpen => Some("▾\u{00a0}"),
+ ListStyleType::DisclosureClosed => Some("‣\u{00a0}"),
ListStyleType::Decimal |
ListStyleType::LowerAlpha |
ListStyleType::UpperAlpha |