Propagate text decoration where needed

This commit is contained in:
Fernando Jiménez Moreno 2020-03-11 17:11:05 +01:00
parent 83fc9943d3
commit 17948f3b39
9 changed files with 75 additions and 40 deletions

View file

@ -133,7 +133,7 @@ impl Fragment {
// Underline. // Underline.
if fragment if fragment
.text_decorations_in_effect .text_decoration_line
.contains(TextDecorationLine::UNDERLINE) .contains(TextDecorationLine::UNDERLINE)
{ {
let mut rect = rect; let mut rect = rect;
@ -144,7 +144,7 @@ impl Fragment {
// Overline. // Overline.
if fragment if fragment
.text_decorations_in_effect .text_decoration_line
.contains(TextDecorationLine::OVERLINE) .contains(TextDecorationLine::OVERLINE)
{ {
let mut rect = rect; let mut rect = rect;
@ -164,7 +164,7 @@ impl Fragment {
// Line-through. // Line-through.
if fragment if fragment
.text_decorations_in_effect .text_decoration_line
.contains(TextDecorationLine::LINE_THROUGH) .contains(TextDecorationLine::LINE_THROUGH)
{ {
let mut rect = rect; let mut rect = rect;

View file

@ -19,6 +19,7 @@ use servo_arc::Arc;
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style::selector_parser::PseudoElement; use style::selector_parser::PseudoElement;
use style::values::specified::text::TextDecorationLine;
impl BlockFormattingContext { impl BlockFormattingContext {
pub fn construct<'dom>( pub fn construct<'dom>(
@ -27,9 +28,16 @@ impl BlockFormattingContext {
style: &Arc<ComputedValues>, style: &Arc<ComputedValues>,
contents: NonReplacedContents, contents: NonReplacedContents,
content_sizes: ContentSizesRequest, content_sizes: ContentSizesRequest,
propagated_text_decoration_line: TextDecorationLine,
) -> (Self, BoxContentSizes) { ) -> (Self, BoxContentSizes) {
let (contents, contains_floats, inline_content_sizes) = let (contents, contains_floats, inline_content_sizes) = BlockContainer::construct(
BlockContainer::construct(context, node, style, contents, content_sizes); context,
node,
style,
contents,
content_sizes,
propagated_text_decoration_line,
);
// FIXME: add contribution to `inline_content_sizes` of floats in this formatting context // FIXME: add contribution to `inline_content_sizes` of floats in this formatting context
// https://dbaron.org/css/intrinsic/#intrinsic // https://dbaron.org/css/intrinsic/#intrinsic
let bfc = Self { let bfc = Self {
@ -52,6 +60,7 @@ enum BlockLevelCreator {
Independent { Independent {
display_inside: DisplayInside, display_inside: DisplayInside,
contents: Contents, contents: Contents,
propagated_text_decoration_line: TextDecorationLine,
}, },
OutOfFlowAbsolutelyPositionedBox { OutOfFlowAbsolutelyPositionedBox {
display_inside: DisplayInside, display_inside: DisplayInside,
@ -72,7 +81,7 @@ enum BlockLevelCreator {
/// Deferring allows using rayons `into_par_iter`. /// Deferring allows using rayons `into_par_iter`.
enum IntermediateBlockContainer { enum IntermediateBlockContainer {
InlineFormattingContext(InlineFormattingContext), InlineFormattingContext(InlineFormattingContext),
Deferred(NonReplacedContents), Deferred(NonReplacedContents, TextDecorationLine),
} }
/// A builder for a block container. /// A builder for a block container.
@ -140,13 +149,16 @@ impl BlockContainer {
block_container_style: &Arc<ComputedValues>, block_container_style: &Arc<ComputedValues>,
contents: NonReplacedContents, contents: NonReplacedContents,
content_sizes: ContentSizesRequest, content_sizes: ContentSizesRequest,
propagated_text_decoration_line: TextDecorationLine,
) -> (BlockContainer, ContainsFloats, BoxContentSizes) { ) -> (BlockContainer, ContainsFloats, BoxContentSizes) {
let text_decoration_line =
propagated_text_decoration_line | block_container_style.clone_text_decoration_line();
let mut builder = BlockContainerBuilder { let mut builder = BlockContainerBuilder {
context, context,
root, root,
block_container_style, block_container_style,
block_level_boxes: Vec::new(), 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(), ongoing_inline_boxes_stack: Vec::new(),
anonymous_style: None, anonymous_style: None,
contains_floats: ContainsFloats::No, contains_floats: ContainsFloats::No,
@ -439,6 +451,8 @@ where
display_inside, display_inside,
contents, contents,
ContentSizesRequest::inline_if(!style.inline_size_is_length()), 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)); .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 // We found a block level element, so the ongoing inline formatting
// context needs to be ended. // context needs to be ended.
self.end_ongoing_inline_formatting_context(); self.end_ongoing_inline_formatting_context();
@ -501,11 +518,12 @@ where
let kind = match contents.try_into() { let kind = match contents.try_into() {
Ok(contents) => match display_inside { Ok(contents) => match display_inside {
DisplayInside::Flow => BlockLevelCreator::SameFormattingContextBlock( DisplayInside::Flow => BlockLevelCreator::SameFormattingContextBlock(
IntermediateBlockContainer::Deferred(contents), IntermediateBlockContainer::Deferred(contents, propagated_text_decoration_line),
), ),
_ => BlockLevelCreator::Independent { _ => BlockLevelCreator::Independent {
display_inside, display_inside,
contents: contents.into(), contents: contents.into(),
propagated_text_decoration_line,
}, },
}, },
Err(contents) => { Err(contents) => {
@ -513,6 +531,7 @@ where
BlockLevelCreator::Independent { BlockLevelCreator::Independent {
display_inside, display_inside,
contents, contents,
propagated_text_decoration_line,
} }
}, },
}; };
@ -680,6 +699,7 @@ where
BlockLevelCreator::Independent { BlockLevelCreator::Independent {
display_inside, display_inside,
contents, contents,
propagated_text_decoration_line,
} => { } => {
let content_sizes = ContentSizesRequest::inline_if( let content_sizes = ContentSizesRequest::inline_if(
max_assign_in_flow_outer_content_sizes_to.is_some() && max_assign_in_flow_outer_content_sizes_to.is_some() &&
@ -692,6 +712,7 @@ where
display_inside, display_inside,
contents, contents,
content_sizes, content_sizes,
propagated_text_decoration_line,
); );
if let Some(to) = max_assign_in_flow_outer_content_sizes_to { if let Some(to) = max_assign_in_flow_outer_content_sizes_to {
to.max_assign(&contents.content_sizes.outer_inline(&contents.style)) to.max_assign(&contents.content_sizes.outer_inline(&contents.style))
@ -742,8 +763,15 @@ impl IntermediateBlockContainer {
content_sizes: ContentSizesRequest, content_sizes: ContentSizesRequest,
) -> (BlockContainer, ContainsFloats, BoxContentSizes) { ) -> (BlockContainer, ContainsFloats, BoxContentSizes) {
match self { match self {
IntermediateBlockContainer::Deferred(contents) => { IntermediateBlockContainer::Deferred(contents, propagated_text_decoration_line) => {
BlockContainer::construct(context, node, style, contents, content_sizes) BlockContainer::construct(
context,
node,
style,
contents,
content_sizes,
propagated_text_decoration_line,
)
}, },
IntermediateBlockContainer::InlineFormattingContext(ifc) => { IntermediateBlockContainer::InlineFormattingContext(ifc) => {
let content_sizes = content_sizes.compute(|| ifc.inline_content_sizes(context)); let content_sizes = content_sizes.compute(|| ifc.inline_content_sizes(context));

View file

@ -9,6 +9,7 @@ use crate::sizing::ContentSizesRequest;
use crate::style_ext::{ComputedValuesExt, DisplayInside}; use crate::style_ext::{ComputedValuesExt, DisplayInside};
use servo_arc::Arc; use servo_arc::Arc;
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style::values::specified::text::TextDecorationLine;
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub(crate) struct FloatBox { pub(crate) struct FloatBox {
@ -43,6 +44,8 @@ impl FloatBox {
display_inside, display_inside,
contents, contents,
content_sizes, content_sizes,
// Text decorations are not propagated to any out-of-flow descendants
TextDecorationLine::NONE,
), ),
} }
} }

View file

@ -33,6 +33,7 @@ use webrender_api::FontInstanceKey;
#[derive(Debug, Default, Serialize)] #[derive(Debug, Default, Serialize)]
pub(crate) struct InlineFormattingContext { pub(crate) struct InlineFormattingContext {
pub(super) inline_level_boxes: Vec<ArcRefCell<InlineLevelBox>>, pub(super) inline_level_boxes: Vec<ArcRefCell<InlineLevelBox>>,
pub(super) text_decoration_line: TextDecorationLine,
} }
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
@ -69,6 +70,11 @@ struct InlineNestingLevelState<'box_tree> {
inline_start: Length, inline_start: Length,
max_block_size_of_fragments_so_far: Length, max_block_size_of_fragments_so_far: Length,
positioning_context: Option<PositioningContext>, positioning_context: Option<PositioningContext>,
/// 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> { struct PartialInlineBoxFragment<'box_tree> {
@ -123,6 +129,13 @@ struct Lines {
} }
impl InlineFormattingContext { 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`, // This works on an already-constructed `InlineFormattingContext`,
// Which would have to change if/when // Which would have to change if/when
// `BlockContainer::construct` parallelize their construction. // `BlockContainer::construct` parallelize their construction.
@ -257,8 +270,10 @@ impl InlineFormattingContext {
inline_start: Length::zero(), inline_start: Length::zero(),
max_block_size_of_fragments_so_far: Length::zero(), max_block_size_of_fragments_so_far: Length::zero(),
positioning_context: None, positioning_context: None,
text_decoration_line: self.text_decoration_line,
}, },
}; };
loop { loop {
if let Some(child) = ifc.current_nesting_level.remaining_boxes.next() { if let Some(child) = ifc.current_nesting_level.remaining_boxes.next() {
match &*child.borrow() { match &*child.borrow() {
@ -387,17 +402,6 @@ impl Lines {
}; };
self.next_line_block_position += size.block; 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 self.fragments
.push(Fragment::Anonymous(AnonymousFragment::new( .push(Fragment::Anonymous(AnonymousFragment::new(
Rect { start_corner, size }, Rect { start_corner, size },
@ -436,6 +440,8 @@ impl InlineBox {
start_corner += &relative_adjustement(&style, ifc.containing_block) start_corner += &relative_adjustement(&style, ifc.containing_block)
} }
let positioning_context = PositioningContext::new_for_style(&style); 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 { PartialInlineBoxFragment {
tag: self.tag, tag: self.tag,
style, style,
@ -454,6 +460,7 @@ impl InlineBox {
inline_start: ifc.inline_position, inline_start: ifc.inline_position,
max_block_size_of_fragments_so_far: Length::zero(), max_block_size_of_fragments_so_far: Length::zero(),
positioning_context, positioning_context,
text_decoration_line: text_decoration_line,
}, },
), ),
} }
@ -788,7 +795,7 @@ impl TextRun {
font_metrics, font_metrics,
font_key, font_key,
glyphs, glyphs,
text_decorations_in_effect: TextDecorationLine::NONE, text_decoration_line: ifc.current_nesting_level.text_decoration_line,
})); }));
if runs.is_empty() { if runs.is_empty() {
break; break;

View file

@ -104,6 +104,7 @@ fn construct_for_root_element<'dom>(
))], ))],
) )
} else { } else {
let propagated_text_decoration_line = style.clone_text_decoration_line();
( (
ContainsFloats::No, ContainsFloats::No,
vec![ArcRefCell::new(BlockLevelBox::Independent( vec![ArcRefCell::new(BlockLevelBox::Independent(
@ -114,6 +115,7 @@ fn construct_for_root_element<'dom>(
display_inside, display_inside,
contents, contents,
ContentSizesRequest::None, ContentSizesRequest::None,
propagated_text_decoration_line,
), ),
))], ))],
) )

View file

@ -16,6 +16,7 @@ use std::convert::TryInto;
use style::dom::OpaqueNode; use style::dom::OpaqueNode;
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style::values::computed::Length; use style::values::computed::Length;
use style::values::specified::text::TextDecorationLine;
/// https://drafts.csswg.org/css-display/#independent-formatting-context /// https://drafts.csswg.org/css-display/#independent-formatting-context
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
@ -62,6 +63,7 @@ impl IndependentFormattingContext {
display_inside: DisplayInside, display_inside: DisplayInside,
contents: Contents, contents: Contents,
content_sizes: ContentSizesRequest, content_sizes: ContentSizesRequest,
propagated_text_decoration_line: TextDecorationLine,
) -> Self { ) -> Self {
match contents.try_into() { match contents.try_into() {
Ok(non_replaced) => match display_inside { Ok(non_replaced) => match display_inside {
@ -72,6 +74,7 @@ impl IndependentFormattingContext {
&style, &style,
non_replaced, non_replaced,
content_sizes, content_sizes,
propagated_text_decoration_line,
); );
Self { Self {
tag: node.as_opaque(), tag: node.as_opaque(),

View file

@ -123,7 +123,7 @@ pub(crate) struct TextFragment {
pub font_key: FontInstanceKey, pub font_key: FontInstanceKey,
pub glyphs: Vec<Arc<GlyphStore>>, pub glyphs: Vec<Arc<GlyphStore>>,
/// A flag that represents the _used_ value of the text-decoration property. /// 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)] #[derive(Serialize)]
@ -195,23 +195,6 @@ impl AbsoluteOrFixedPositionedFragment {
pub fn print(&self, tree: &mut PrintTree) { pub fn print(&self, tree: &mut PrintTree) {
tree.add_item(format!("AbsoluteOrFixedPositionedFragment({:?})", self.0)); 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 { impl AnonymousFragment {

View file

@ -17,6 +17,7 @@ use std::sync::atomic::{AtomicUsize, Ordering};
use style::computed_values::position::T as Position; use style::computed_values::position::T as Position;
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style::values::computed::{Length, LengthOrAuto, LengthPercentage, LengthPercentageOrAuto}; use style::values::computed::{Length, LengthOrAuto, LengthPercentage, LengthPercentageOrAuto};
use style::values::specified::text::TextDecorationLine;
use style::Zero; use style::Zero;
static HOISTED_FRAGMENT_ID_COUNTER: AtomicUsize = AtomicUsize::new(0); static HOISTED_FRAGMENT_ID_COUNTER: AtomicUsize = AtomicUsize::new(0);
@ -104,6 +105,8 @@ impl AbsolutelyPositionedBox {
display_inside, display_inside,
contents, contents,
content_sizes, content_sizes,
// Text decorations are not propagated to any out-of-flow descendants.
TextDecorationLine::NONE,
), ),
} }
} }

View file

@ -244,6 +244,12 @@ bitflags! {
} }
} }
impl Default for TextDecorationLine {
fn default() -> Self {
TextDecorationLine::NONE
}
}
impl Parse for TextDecorationLine { impl Parse for TextDecorationLine {
/// none | [ underline || overline || line-through || blink ] /// none | [ underline || overline || line-through || blink ]
fn parse<'i, 't>( fn parse<'i, 't>(