diff --git a/components/layout_2020/dom_traversal.rs b/components/layout_2020/dom_traversal.rs index c4da1333a1c..c1226d7d1d8 100644 --- a/components/layout_2020/dom_traversal.rs +++ b/components/layout_2020/dom_traversal.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; -use html5ever::LocalName; +use html5ever::{local_name, LocalName}; use script_layout_interface::wrapper_traits::{ThreadSafeLayoutElement, ThreadSafeLayoutNode}; use servo_arc::Arc as ServoArc; use style::properties::ComputedValues; @@ -79,6 +79,9 @@ where Some(element) if element.is_body_element_of_html_element_root() => { FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT }, + Some(element) if element.get_local_name() == &local_name!("br") => { + FragmentFlags::IS_BR_ELEMENT + }, _ => FragmentFlags::empty(), }; diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index c5d6156afb6..41926944a91 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -35,7 +35,7 @@ use crate::flow::float::{FloatBox, SequentialLayoutState}; use crate::flow::FlowLayout; use crate::formatting_contexts::{Baselines, IndependentFormattingContext}; use crate::fragment_tree::{ - BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment, + BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment, FragmentFlags, PositioningFragment, }; use crate::geom::{LogicalRect, LogicalVec2}; @@ -624,6 +624,18 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> { self.white_space = style.get_inherited_text().white_space; } + fn processing_br_element(&self) -> bool { + self.inline_box_state_stack + .last() + .map(|state| { + state + .base_fragment_info + .flags + .contains(FragmentFlags::IS_BR_ELEMENT) + }) + .unwrap_or(false) + } + /// Start laying out a particular [`InlineBox`] into line items. This will push /// a new [`InlineBoxContainerState`] onto [`Self::inline_box_state_stack`]. fn start_inline_box(&mut self, inline_box: &InlineBox) { @@ -1114,12 +1126,22 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> { // Defer the actual line break until we've cleared all ending inline boxes. self.linebreak_before_new_content = true; - // We need to ensure that the appropriate space for a linebox is created even if there - // was no other content on this line. We mark the line as having content (needing a - // advance) and having at least the height associated with this nesting of inline boxes. - self.current_line - .max_block_size - .max_assign(&self.current_line_max_block_size_including_nested_containers()); + // In quirks mode, the line-height isn't automatically added to the line. If we consider a + // forced line break a kind of preserved white space, quirks mode requires that we add the + // line-height of the current element to the line box height. + // + // The exception here is `
` elements. They are implemented with `pre-line` in Servo, but + // this is an implementation detail. The "magic" behavior of `
` elements is that they + // add line-height to the line conditionally: only when they are on an otherwise empty line. + let line_is_empty = + !self.current_line_segment.has_content && !self.current_line.has_content; + if !self.processing_br_element() || line_is_empty { + let strut_size = self + .current_inline_container_state() + .strut_block_sizes + .clone(); + self.update_unbreakable_segment_for_new_content(&strut_size, Length::zero(), false); + } self.had_inflow_content = true; } @@ -1275,7 +1297,10 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> { /// Commit the current unbrekable segment to the current line. In addition, this will /// place all floats in the unbreakable segment and expand the line dimensions. fn commit_current_segment_to_line(&mut self) { - if self.current_line_segment.line_items.is_empty() { + // The line segments might have no items and have content after processing a forced + // linebreak on an empty line. + if self.current_line_segment.line_items.is_empty() && !self.current_line_segment.has_content + { return; } diff --git a/components/layout_2020/fragment_tree/base_fragment.rs b/components/layout_2020/fragment_tree/base_fragment.rs index 215d0cfaa9e..a43e375f26c 100644 --- a/components/layout_2020/fragment_tree/base_fragment.rs +++ b/components/layout_2020/fragment_tree/base_fragment.rs @@ -77,8 +77,10 @@ bitflags! { /// Flags used to track various information about a DOM node during layout. #[derive(Clone, Copy, Debug, Serialize)] pub(crate) struct FragmentFlags: u8 { - /// Whether or not this node is a body element on an HTML document. + /// Whether or not the node that created this fragment is a `` element on an HTML document. const IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT = 0b00000001; + /// Whether or not the node that created this Fragment is a `
` element. + const IS_BR_ELEMENT = 0b00000010; } } diff --git a/components/script/layout_dom/element.rs b/components/script/layout_dom/element.rs index 32bce3a95d1..40d7f0a8aa3 100644 --- a/components/script/layout_dom/element.rs +++ b/components/script/layout_dom/element.rs @@ -747,6 +747,10 @@ impl<'dom, LayoutDataType: LayoutDataTrait> ThreadSafeLayoutElement<'dom> self.element } + fn get_local_name(&self) -> &LocalName { + self.element.local_name() + } + fn get_attr_enum(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue> { self.element.get_attr_enum(namespace, name) } diff --git a/components/shared/script_layout/wrapper_traits.rs b/components/shared/script_layout/wrapper_traits.rs index 54cafc26bea..3274bdb641e 100644 --- a/components/shared/script_layout/wrapper_traits.rs +++ b/components/shared/script_layout/wrapper_traits.rs @@ -345,6 +345,10 @@ pub trait ThreadSafeLayoutElement<'dom>: /// lazily_compute_pseudo_element_style, which operates on TElement. unsafe fn unsafe_get(self) -> Self::ConcreteElement; + /// Get the local name of this element. See + /// . + fn get_local_name(&self) -> &LocalName; + fn get_attr(&self, namespace: &Namespace, name: &LocalName) -> Option<&str>; fn get_attr_enum(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue>; diff --git a/tests/wpt/meta-legacy-layout/quirks/line-height-preserved-segment-break.html.ini b/tests/wpt/meta-legacy-layout/quirks/line-height-preserved-segment-break.html.ini new file mode 100644 index 00000000000..7fa4d44d8f1 --- /dev/null +++ b/tests/wpt/meta-legacy-layout/quirks/line-height-preserved-segment-break.html.ini @@ -0,0 +1,2 @@ +[line-height-preserved-segment-break.html] + expected: FAIL diff --git a/tests/wpt/meta/MANIFEST.json b/tests/wpt/meta/MANIFEST.json index ac0748fd277..367808cd900 100644 --- a/tests/wpt/meta/MANIFEST.json +++ b/tests/wpt/meta/MANIFEST.json @@ -340751,6 +340751,19 @@ {} ] ], + "line-height-preserved-segment-break.html": [ + "8ad2651f4b999091996682be72745e31e0ce559a", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], "line-height-quirks-mode.html": [ "9e9baf62226dfccc15f81b6d22edb87a29e2d313", [ diff --git a/tests/wpt/mozilla/meta-legacy-layout/css/quirks-mode-br-line-height.html.ini b/tests/wpt/mozilla/meta-legacy-layout/css/quirks-mode-br-line-height.html.ini new file mode 100644 index 00000000000..2eb9c0e4d5f --- /dev/null +++ b/tests/wpt/mozilla/meta-legacy-layout/css/quirks-mode-br-line-height.html.ini @@ -0,0 +1,2 @@ +[quirks-mode-br-line-height.html] + expected: FAIL diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 73b7326bb53..1a8c9eda91c 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -5402,6 +5402,19 @@ {} ] ], + "quirks-mode-br-line-height.html": [ + "ecb49833f809e72cd57772e7a98085e787cb8baa", + [ + null, + [ + [ + "/_mozilla/css/quirks-mode-br-line-height-ref.html", + "==" + ] + ], + {} + ] + ], "quotes_none_a.html": [ "c37ff23e9084d7d198b3c97e14d2e00ab417dd6c", [ @@ -9673,6 +9686,10 @@ "0195f154cf3c4303e5aaf4fc9e7cfa358b8386d7", [] ], + "quirks-mode-br-line-height-ref.html": [ + "2108a4ae5e1f823cf2a3923070d54c42487fd8c6", + [] + ], "quotes_none_ref.html": [ "85f3cf14ca2da71a45efd75803e84e7b5dc23a85", [] diff --git a/tests/wpt/mozilla/tests/css/quirks-mode-br-line-height-ref.html b/tests/wpt/mozilla/tests/css/quirks-mode-br-line-height-ref.html new file mode 100644 index 00000000000..2108a4ae5e1 --- /dev/null +++ b/tests/wpt/mozilla/tests/css/quirks-mode-br-line-height-ref.html @@ -0,0 +1,17 @@ + + + +
+
+ A + +
+ diff --git a/tests/wpt/mozilla/tests/css/quirks-mode-br-line-height.html b/tests/wpt/mozilla/tests/css/quirks-mode-br-line-height.html new file mode 100644 index 00000000000..ecb49833f80 --- /dev/null +++ b/tests/wpt/mozilla/tests/css/quirks-mode-br-line-height.html @@ -0,0 +1,35 @@ + + + + + + +
+ A + +
+
+ A +
+ diff --git a/tests/wpt/tests/quirks/line-height-preserved-segment-break.html b/tests/wpt/tests/quirks/line-height-preserved-segment-break.html new file mode 100644 index 00000000000..8ad2651f4b9 --- /dev/null +++ b/tests/wpt/tests/quirks/line-height-preserved-segment-break.html @@ -0,0 +1,24 @@ + + + + + + + + +

Test passes if there is a filled green square and no red.

+
+ +
+