From 17948f3b3936b58528b1e101481cee5df52374f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Jim=C3=A9nez=20Moreno?= Date: Wed, 11 Mar 2020 17:11:05 +0100 Subject: [PATCH] Propagate text decoration where needed --- components/layout_2020/display_list/mod.rs | 6 +-- components/layout_2020/flow/construct.rs | 42 +++++++++++++++---- components/layout_2020/flow/float.rs | 3 ++ components/layout_2020/flow/inline.rs | 31 ++++++++------ components/layout_2020/flow/root.rs | 2 + components/layout_2020/formatting_contexts.rs | 3 ++ components/layout_2020/fragments.rs | 19 +-------- components/layout_2020/positioned.rs | 3 ++ components/style/values/specified/text.rs | 6 +++ 9 files changed, 75 insertions(+), 40 deletions(-) diff --git a/components/layout_2020/display_list/mod.rs b/components/layout_2020/display_list/mod.rs index ebdfd07a4fa..46c31934831 100644 --- a/components/layout_2020/display_list/mod.rs +++ b/components/layout_2020/display_list/mod.rs @@ -133,7 +133,7 @@ impl Fragment { // Underline. if fragment - .text_decorations_in_effect + .text_decoration_line .contains(TextDecorationLine::UNDERLINE) { let mut rect = rect; @@ -144,7 +144,7 @@ impl Fragment { // Overline. if fragment - .text_decorations_in_effect + .text_decoration_line .contains(TextDecorationLine::OVERLINE) { let mut rect = rect; @@ -164,7 +164,7 @@ impl Fragment { // Line-through. if fragment - .text_decorations_in_effect + .text_decoration_line .contains(TextDecorationLine::LINE_THROUGH) { let mut rect = rect; diff --git a/components/layout_2020/flow/construct.rs b/components/layout_2020/flow/construct.rs index 0bf0b5c83ad..fe9465f775e 100644 --- a/components/layout_2020/flow/construct.rs +++ b/components/layout_2020/flow/construct.rs @@ -19,6 +19,7 @@ use servo_arc::Arc; use std::convert::{TryFrom, TryInto}; use style::properties::ComputedValues; use style::selector_parser::PseudoElement; +use style::values::specified::text::TextDecorationLine; impl BlockFormattingContext { pub fn construct<'dom>( @@ -27,9 +28,16 @@ impl BlockFormattingContext { style: &Arc, contents: NonReplacedContents, content_sizes: ContentSizesRequest, + propagated_text_decoration_line: TextDecorationLine, ) -> (Self, BoxContentSizes) { - let (contents, contains_floats, inline_content_sizes) = - BlockContainer::construct(context, node, style, contents, content_sizes); + let (contents, contains_floats, inline_content_sizes) = BlockContainer::construct( + context, + node, + style, + contents, + content_sizes, + propagated_text_decoration_line, + ); // FIXME: add contribution to `inline_content_sizes` of floats in this formatting context // https://dbaron.org/css/intrinsic/#intrinsic let bfc = Self { @@ -52,6 +60,7 @@ enum BlockLevelCreator { Independent { display_inside: DisplayInside, contents: Contents, + propagated_text_decoration_line: TextDecorationLine, }, OutOfFlowAbsolutelyPositionedBox { display_inside: DisplayInside, @@ -72,7 +81,7 @@ enum BlockLevelCreator { /// Deferring allows using rayon’s `into_par_iter`. enum IntermediateBlockContainer { InlineFormattingContext(InlineFormattingContext), - Deferred(NonReplacedContents), + Deferred(NonReplacedContents, TextDecorationLine), } /// A builder for a block container. @@ -140,13 +149,16 @@ impl BlockContainer { block_container_style: &Arc, contents: NonReplacedContents, content_sizes: ContentSizesRequest, + propagated_text_decoration_line: TextDecorationLine, ) -> (BlockContainer, ContainsFloats, BoxContentSizes) { + let text_decoration_line = + propagated_text_decoration_line | block_container_style.clone_text_decoration_line(); let mut builder = BlockContainerBuilder { context, root, block_container_style, block_level_boxes: Vec::new(), - ongoing_inline_formatting_context: InlineFormattingContext::default(), + ongoing_inline_formatting_context: InlineFormattingContext::new(text_decoration_line), ongoing_inline_boxes_stack: Vec::new(), anonymous_style: None, contains_floats: ContainsFloats::No, @@ -439,6 +451,8 @@ where display_inside, contents, ContentSizesRequest::inline_if(!style.inline_size_is_length()), + // Text decorations are not propagated to atomic inline-level descendants. + TextDecorationLine::NONE, ), )) }; @@ -494,6 +508,9 @@ where .push(ArcRefCell::new(fragmented_inline)); } + let propagated_text_decoration_line = + self.ongoing_inline_formatting_context.text_decoration_line; + // We found a block level element, so the ongoing inline formatting // context needs to be ended. self.end_ongoing_inline_formatting_context(); @@ -501,11 +518,12 @@ where let kind = match contents.try_into() { Ok(contents) => match display_inside { DisplayInside::Flow => BlockLevelCreator::SameFormattingContextBlock( - IntermediateBlockContainer::Deferred(contents), + IntermediateBlockContainer::Deferred(contents, propagated_text_decoration_line), ), _ => BlockLevelCreator::Independent { display_inside, contents: contents.into(), + propagated_text_decoration_line, }, }, Err(contents) => { @@ -513,6 +531,7 @@ where BlockLevelCreator::Independent { display_inside, contents, + propagated_text_decoration_line, } }, }; @@ -680,6 +699,7 @@ where BlockLevelCreator::Independent { display_inside, contents, + propagated_text_decoration_line, } => { let content_sizes = ContentSizesRequest::inline_if( max_assign_in_flow_outer_content_sizes_to.is_some() && @@ -692,6 +712,7 @@ where display_inside, contents, content_sizes, + propagated_text_decoration_line, ); if let Some(to) = max_assign_in_flow_outer_content_sizes_to { to.max_assign(&contents.content_sizes.outer_inline(&contents.style)) @@ -742,8 +763,15 @@ impl IntermediateBlockContainer { content_sizes: ContentSizesRequest, ) -> (BlockContainer, ContainsFloats, BoxContentSizes) { match self { - IntermediateBlockContainer::Deferred(contents) => { - BlockContainer::construct(context, node, style, contents, content_sizes) + IntermediateBlockContainer::Deferred(contents, propagated_text_decoration_line) => { + BlockContainer::construct( + context, + node, + style, + contents, + content_sizes, + propagated_text_decoration_line, + ) }, IntermediateBlockContainer::InlineFormattingContext(ifc) => { let content_sizes = content_sizes.compute(|| ifc.inline_content_sizes(context)); diff --git a/components/layout_2020/flow/float.rs b/components/layout_2020/flow/float.rs index 5b34392ac8c..4a647f23071 100644 --- a/components/layout_2020/flow/float.rs +++ b/components/layout_2020/flow/float.rs @@ -9,6 +9,7 @@ use crate::sizing::ContentSizesRequest; use crate::style_ext::{ComputedValuesExt, DisplayInside}; use servo_arc::Arc; use style::properties::ComputedValues; +use style::values::specified::text::TextDecorationLine; #[derive(Debug, Serialize)] pub(crate) struct FloatBox { @@ -43,6 +44,8 @@ impl FloatBox { display_inside, contents, content_sizes, + // Text decorations are not propagated to any out-of-flow descendants + TextDecorationLine::NONE, ), } } diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index acd6795d639..6b4faceea75 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -33,6 +33,7 @@ use webrender_api::FontInstanceKey; #[derive(Debug, Default, Serialize)] pub(crate) struct InlineFormattingContext { pub(super) inline_level_boxes: Vec>, + pub(super) text_decoration_line: TextDecorationLine, } #[derive(Debug, Serialize)] @@ -69,6 +70,11 @@ struct InlineNestingLevelState<'box_tree> { inline_start: Length, max_block_size_of_fragments_so_far: Length, positioning_context: Option, + /// Indicates whether this nesting level have text decorations in effect. + /// From https://drafts.csswg.org/css-text-decor/#line-decoration + // "When specified on or propagated to a block container that establishes + // an IFC..." + text_decoration_line: TextDecorationLine, } struct PartialInlineBoxFragment<'box_tree> { @@ -123,6 +129,13 @@ struct Lines { } impl InlineFormattingContext { + pub(super) fn new(text_decoration_line: TextDecorationLine) -> InlineFormattingContext { + InlineFormattingContext { + inline_level_boxes: Default::default(), + text_decoration_line, + } + } + // This works on an already-constructed `InlineFormattingContext`, // Which would have to change if/when // `BlockContainer::construct` parallelize their construction. @@ -257,8 +270,10 @@ impl InlineFormattingContext { inline_start: Length::zero(), max_block_size_of_fragments_so_far: Length::zero(), positioning_context: None, + text_decoration_line: self.text_decoration_line, }, }; + loop { if let Some(child) = ifc.current_nesting_level.remaining_boxes.next() { match &*child.borrow() { @@ -387,17 +402,6 @@ impl Lines { }; self.next_line_block_position += size.block; - // From https://drafts.csswg.org/css-text-decor/#line-decoration - // "When specified on or propagated to an inline box, - // that box becomes a decorating box for that decoration, - // applying the decoration to all its fragments..." - let text_decoration_line = containing_block.style.clone_text_decoration_line(); - if text_decoration_line != TextDecorationLine::NONE { - for fragment in &mut line_contents { - fragment.set_text_decorations_in_effect(text_decoration_line); - } - } - self.fragments .push(Fragment::Anonymous(AnonymousFragment::new( Rect { start_corner, size }, @@ -436,6 +440,8 @@ impl InlineBox { start_corner += &relative_adjustement(&style, ifc.containing_block) } let positioning_context = PositioningContext::new_for_style(&style); + let text_decoration_line = + ifc.current_nesting_level.text_decoration_line | style.clone_text_decoration_line(); PartialInlineBoxFragment { tag: self.tag, style, @@ -454,6 +460,7 @@ impl InlineBox { inline_start: ifc.inline_position, max_block_size_of_fragments_so_far: Length::zero(), positioning_context, + text_decoration_line: text_decoration_line, }, ), } @@ -788,7 +795,7 @@ impl TextRun { font_metrics, font_key, glyphs, - text_decorations_in_effect: TextDecorationLine::NONE, + text_decoration_line: ifc.current_nesting_level.text_decoration_line, })); if runs.is_empty() { break; diff --git a/components/layout_2020/flow/root.rs b/components/layout_2020/flow/root.rs index 6a8be578630..23f63647977 100644 --- a/components/layout_2020/flow/root.rs +++ b/components/layout_2020/flow/root.rs @@ -104,6 +104,7 @@ fn construct_for_root_element<'dom>( ))], ) } else { + let propagated_text_decoration_line = style.clone_text_decoration_line(); ( ContainsFloats::No, vec![ArcRefCell::new(BlockLevelBox::Independent( @@ -114,6 +115,7 @@ fn construct_for_root_element<'dom>( display_inside, contents, ContentSizesRequest::None, + propagated_text_decoration_line, ), ))], ) diff --git a/components/layout_2020/formatting_contexts.rs b/components/layout_2020/formatting_contexts.rs index 100ef4a7c4b..9e73a17891e 100644 --- a/components/layout_2020/formatting_contexts.rs +++ b/components/layout_2020/formatting_contexts.rs @@ -16,6 +16,7 @@ use std::convert::TryInto; use style::dom::OpaqueNode; use style::properties::ComputedValues; use style::values::computed::Length; +use style::values::specified::text::TextDecorationLine; /// https://drafts.csswg.org/css-display/#independent-formatting-context #[derive(Debug, Serialize)] @@ -62,6 +63,7 @@ impl IndependentFormattingContext { display_inside: DisplayInside, contents: Contents, content_sizes: ContentSizesRequest, + propagated_text_decoration_line: TextDecorationLine, ) -> Self { match contents.try_into() { Ok(non_replaced) => match display_inside { @@ -72,6 +74,7 @@ impl IndependentFormattingContext { &style, non_replaced, content_sizes, + propagated_text_decoration_line, ); Self { tag: node.as_opaque(), diff --git a/components/layout_2020/fragments.rs b/components/layout_2020/fragments.rs index cfe2a913847..a42aa5c0c17 100644 --- a/components/layout_2020/fragments.rs +++ b/components/layout_2020/fragments.rs @@ -123,7 +123,7 @@ pub(crate) struct TextFragment { pub font_key: FontInstanceKey, pub glyphs: Vec>, /// A flag that represents the _used_ value of the text-decoration property. - pub text_decorations_in_effect: TextDecorationLine, + pub text_decoration_line: TextDecorationLine, } #[derive(Serialize)] @@ -195,23 +195,6 @@ impl AbsoluteOrFixedPositionedFragment { pub fn print(&self, tree: &mut PrintTree) { tree.add_item(format!("AbsoluteOrFixedPositionedFragment({:?})", self.0)); } - - pub fn set_text_decorations_in_effect(&mut self, text_decorations: TextDecorationLine) { - match self { - Fragment::Text(fragment) => fragment.text_decorations_in_effect = text_decorations, - Fragment::Box(fragment) => { - for child in &mut fragment.children { - child.set_text_decorations_in_effect(text_decorations); - } - }, - Fragment::Anonymous(fragment) => { - for child in &mut fragment.children { - child.set_text_decorations_in_effect(text_decorations); - } - }, - _ => (), - } - } } impl AnonymousFragment { diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index a0216ec296c..4bedd12925d 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -17,6 +17,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use style::computed_values::position::T as Position; use style::properties::ComputedValues; use style::values::computed::{Length, LengthOrAuto, LengthPercentage, LengthPercentageOrAuto}; +use style::values::specified::text::TextDecorationLine; use style::Zero; static HOISTED_FRAGMENT_ID_COUNTER: AtomicUsize = AtomicUsize::new(0); @@ -104,6 +105,8 @@ impl AbsolutelyPositionedBox { display_inside, contents, content_sizes, + // Text decorations are not propagated to any out-of-flow descendants. + TextDecorationLine::NONE, ), } } diff --git a/components/style/values/specified/text.rs b/components/style/values/specified/text.rs index e54fbeeb2fb..0228d5123ce 100644 --- a/components/style/values/specified/text.rs +++ b/components/style/values/specified/text.rs @@ -244,6 +244,12 @@ bitflags! { } } +impl Default for TextDecorationLine { + fn default() -> Self { + TextDecorationLine::NONE + } +} + impl Parse for TextDecorationLine { /// none | [ underline || overline || line-through || blink ] fn parse<'i, 't>(