style: Implement 'inline list-item' and 'inline flow-root list-item' values for the 'display' property.

Differential Revision: https://phabricator.services.mozilla.com/D39832
This commit is contained in:
Mats Palmgren 2019-08-14 14:37:03 +00:00 committed by Emilio Cobos Álvarez
parent 07dad28e49
commit 2d29e6edd4
No known key found for this signature in database
GPG key ID: E1152D0994E4BF8A
2 changed files with 84 additions and 90 deletions

View file

@ -8,6 +8,8 @@
use crate::dom::TElement; use crate::dom::TElement;
use crate::properties::computed_value_flags::ComputedValueFlags; use crate::properties::computed_value_flags::ComputedValueFlags;
use crate::properties::longhands::display::computed_value::T as Display; use crate::properties::longhands::display::computed_value::T as Display;
#[cfg(feature = "gecko")]
use crate::values::specified::box_::DisplayInside;
use crate::properties::longhands::float::computed_value::T as Float; use crate::properties::longhands::float::computed_value::T as Float;
use crate::properties::longhands::overflow_x::computed_value::T as Overflow; use crate::properties::longhands::overflow_x::computed_value::T as Overflow;
use crate::properties::longhands::position::computed_value::T as Position; use crate::properties::longhands::position::computed_value::T as Position;
@ -175,7 +177,9 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
/// Apply the blockification rules based on the table in CSS 2.2 section 9.7. /// Apply the blockification rules based on the table in CSS 2.2 section 9.7.
/// <https://drafts.csswg.org/css2/visuren.html#dis-pos-flo> /// <https://drafts.csswg.org/css2/visuren.html#dis-pos-flo>
/// A ::marker pseudo-element with 'list-style-position:outside' needs to /// A ::marker pseudo-element with 'list-style-position:outside' needs to
/// have its 'display' blockified. /// have its 'display' blockified, unless the ::marker is for an inline
/// list-item (for which 'list-style-position:outside' behaves as 'inside').
/// https://drafts.csswg.org/css-lists-3/#list-style-position-property
fn blockify_if_necessary<E>(&mut self, layout_parent_style: &ComputedValues, element: Option<E>) fn blockify_if_necessary<E>(&mut self, layout_parent_style: &ComputedValues, element: Option<E>)
where where
E: TElement, E: TElement,
@ -194,21 +198,19 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
let is_root = self.style.pseudo.is_none() && element.map_or(false, |e| e.is_root()); let is_root = self.style.pseudo.is_none() && element.map_or(false, |e| e.is_root());
blockify_if!(is_root); blockify_if!(is_root);
if !self.skip_item_display_fixup(element) { if !self.skip_item_display_fixup(element) {
blockify_if!(layout_parent_style let parent_display = layout_parent_style.get_box().clone_display();
.get_box() blockify_if!(parent_display.is_item_container());
.clone_display()
.is_item_container());
} }
let is_item_or_root = blockify; let is_item_or_root = blockify;
blockify_if!(self.style.floated()); blockify_if!(self.style.floated());
blockify_if!(self.style.out_of_flow_positioned()); blockify_if!(self.style.out_of_flow_positioned());
#[cfg(feature = "gecko")]
blockify_if!( blockify_if!(
self.style.pseudo.map_or(false, |p| p.is_marker()) && self.style.pseudo.map_or(false, |p| p.is_marker()) &&
self.style.get_parent_list().clone_list_style_position() == self.style.get_parent_list().clone_list_style_position() == ListStylePosition::Outside &&
ListStylePosition::Outside layout_parent_style.get_box().clone_display().inside() != DisplayInside::Inline);
);
if !blockify { if !blockify {
return; return;

View file

@ -393,55 +393,66 @@ impl Display {
/// Convert this display into an equivalent block display. /// Convert this display into an equivalent block display.
/// ///
/// Also used for style adjustments. /// Also used for :root style adjustments.
pub fn equivalent_block_display(&self, _is_root_element: bool) -> Self { pub fn equivalent_block_display(&self, _is_root_element: bool) -> Self {
#[cfg(feature = "gecko")]
{
// Special handling for `contents` and `list-item`s on the root element.
if _is_root_element && (*self == Display::Contents || self.is_list_item()) {
return Display::Block;
}
match self.outside() {
DisplayOutside::Inline => {
let inside = match self.inside() {
DisplayInside::Inline | DisplayInside::FlowRoot => DisplayInside::Block,
// FIXME: we don't handle `block ruby` in layout yet, remove this when we do:
DisplayInside::Ruby => DisplayInside::Block,
inside => inside,
};
Display::from3(DisplayOutside::Block, inside, self.is_list_item())
},
DisplayOutside::Block | DisplayOutside::None => *self,
_ => Display::Block,
}
}
#[cfg(not(feature = "gecko"))]
match *self { match *self {
// Values that have a corresponding block-outside version. // Values that have a corresponding block-outside version.
#[cfg(any(feature = "gecko", feature = "servo-layout-2013"))] #[cfg(feature = "servo-layout-2013")]
Display::InlineTable => Display::Table, Display::InlineTable => Display::Table,
#[cfg(any(feature = "gecko", feature = "servo-layout-2013"))] #[cfg(feature = "servo-layout-2013")]
Display::InlineFlex => Display::Flex, Display::InlineFlex => Display::Flex,
#[cfg(feature = "gecko")]
Display::InlineGrid => Display::Grid,
#[cfg(feature = "gecko")]
Display::WebkitInlineBox => Display::WebkitBox,
// Special handling for contents and list-item on the root
// element for Gecko.
#[cfg(feature = "gecko")]
Display::Contents | Display::ListItem if _is_root_element => Display::Block,
// These are not changed by blockification. // These are not changed by blockification.
Display::None | Display::Block => *self, Display::None | Display::Block => *self,
#[cfg(any(feature = "gecko", feature = "servo-layout-2013"))] #[cfg(feature = "servo-layout-2013")]
Display::Flex | Display::ListItem | Display::Table => *self, Display::Flex | Display::ListItem | Display::Table => *self,
#[cfg(feature = "gecko")]
Display::Contents | Display::FlowRoot | Display::Grid | Display::WebkitBox => *self,
// Everything else becomes block. // Everything else becomes block.
_ => Display::Block, _ => Display::Block,
} }
} }
/// Convert this display into an inline-outside display. /// Convert this display into an equivalent inline-outside display.
/// /// https://drafts.csswg.org/css-display/#inlinify
/// Ideally it should implement spec: https://drafts.csswg.org/css-display/#inlinify
/// but the spec isn't stable enough, so we copy what Gecko does for now.
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub fn inlinify(&self) -> Self { pub fn inlinify(&self) -> Self {
match *self { match self.outside() {
Display::Block | Display::FlowRoot => Display::InlineBlock, DisplayOutside::Block => {
Display::Table => Display::InlineTable, let inside = match self.inside() {
Display::Flex => Display::InlineFlex, DisplayInside::Block => DisplayInside::FlowRoot,
Display::Grid => Display::InlineGrid, inside => inside,
// XXX bug 1105868 this should probably be InlineListItem: };
Display::ListItem => Display::Inline, Display::from3(DisplayOutside::Inline, inside, self.is_list_item())
Display::MozBox => Display::MozInlineBox, },
Display::MozStack => Display::MozInlineStack, DisplayOutside::XUL => {
Display::WebkitBox => Display::WebkitInlineBox, match self.inside() {
other => other, DisplayInside::MozBox => Display::MozInlineBox,
DisplayInside::MozStack => Display::MozInlineStack,
_ => *self,
}
},
_ => *self,
} }
} }
@ -496,8 +507,15 @@ impl ToCss for Display {
} }
(_, inside) => { (_, inside) => {
if self.is_list_item() { if self.is_list_item() {
debug_assert_eq!(inside, DisplayInside::FlowRoot); if outside != DisplayOutside::Block {
dest.write_str("flow-root list-item") outside.to_css(dest)?;
dest.write_str(" ")?;
}
if inside != DisplayInside::Flow {
inside.to_css(dest)?;
dest.write_str(" ")?;
}
dest.write_str("list-item")
} else { } else {
inside.to_css(dest) inside.to_css(dest)
} }
@ -547,35 +565,10 @@ fn parse_display_outside<'i, 't>(
Ok(try_match_ident_ignore_ascii_case! { input, Ok(try_match_ident_ignore_ascii_case! { input,
"block" => DisplayOutside::Block, "block" => DisplayOutside::Block,
"inline" => DisplayOutside::Inline, "inline" => DisplayOutside::Inline,
// FIXME: not supported in layout yet:
//"run-in" => DisplayOutside::RunIn,
})
}
/// FIXME: this can be replaced with parse_display_outside once we
/// support all its values for list items.
#[cfg(feature = "gecko")]
fn parse_display_outside_for_list_item<'i, 't>(
input: &mut Parser<'i, 't>,
) -> Result<DisplayOutside, ParseError<'i>> {
Ok(try_match_ident_ignore_ascii_case! { input,
"block" => DisplayOutside::Block,
// FIXME(bug 1105868): not supported in layout yet:
//"inline" => DisplayOutside::Inline,
// FIXME(bug 2056): not supported in layout yet: // FIXME(bug 2056): not supported in layout yet:
//"run-in" => DisplayOutside::RunIn, //"run-in" => DisplayOutside::RunIn,
}) })
} }
/// Test a <display-outside> Result for same values as above.
#[cfg(feature = "gecko")]
fn is_valid_outside_for_list_item<'i>(
outside: &Result<DisplayOutside, ParseError<'i>>,
) -> bool {
match outside {
Ok(DisplayOutside::Block) => true,
_ => false,
}
}
/// FIXME: this can be replaced with parse_display_outside once we /// FIXME: this can be replaced with parse_display_outside once we
/// support all its values for `ruby`. /// support all its values for `ruby`.
@ -637,32 +630,29 @@ impl Parse for Display {
if !got_list_item && is_valid_inside_for_list_item(&inside) { if !got_list_item && is_valid_inside_for_list_item(&inside) {
got_list_item = input.try(parse_list_item).is_ok(); got_list_item = input.try(parse_list_item).is_ok();
} }
let outside = if got_list_item { let outside = match inside {
input.try(parse_display_outside_for_list_item) // FIXME we don't handle `block ruby` in layout yet.
} else { Ok(DisplayInside::Ruby) => input.try(parse_display_outside_for_ruby),
match inside { _ => input.try(parse_display_outside),
Ok(DisplayInside::Ruby) => input.try(parse_display_outside_for_ruby),
_ => input.try(parse_display_outside),
}
}; };
if !got_list_item && is_valid_outside_for_list_item(&outside) { if outside.is_ok() {
got_list_item = input.try(parse_list_item).is_ok(); if !got_list_item && (inside.is_err() || is_valid_inside_for_list_item(&inside)) {
}
if outside.is_ok() && inside.is_err(){
inside = if got_list_item {
input.try(parse_display_inside_for_list_item)
} else {
match outside {
// FIXME we don't handle `block ruby` in layout yet.
Ok(DisplayOutside::Block) => input.try(parse_display_inside_for_block),
_ => input.try(parse_display_inside),
}
};
if !got_list_item &&
is_valid_outside_for_list_item(&outside) &&
is_valid_inside_for_list_item(&inside) {
got_list_item = input.try(parse_list_item).is_ok(); got_list_item = input.try(parse_list_item).is_ok();
} }
if inside.is_err() {
inside = if got_list_item {
input.try(parse_display_inside_for_list_item)
} else {
match outside {
// FIXME we don't handle `block ruby` in layout yet.
Ok(DisplayOutside::Block) => input.try(parse_display_inside_for_block),
_ => input.try(parse_display_inside),
}
};
if !got_list_item && is_valid_inside_for_list_item(&inside) {
got_list_item = input.try(parse_list_item).is_ok();
}
}
} }
if got_list_item || inside.is_ok() || outside.is_ok() { if got_list_item || inside.is_ok() || outside.is_ok() {
let inside = inside.unwrap_or(DisplayInside::Flow); let inside = inside.unwrap_or(DisplayInside::Flow);
@ -730,6 +720,8 @@ impl SpecifiedValueInfo for Display {
"inline-flex", "inline-flex",
"inline-grid", "inline-grid",
"inline-table", "inline-table",
"inline list-item",
"inline flow-root list-item",
"list-item", "list-item",
"none", "none",
"ruby", "ruby",