mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
layout: Add line height from preserved segment breaks in quirks mode (#31419)
In quirks mode, preserved segment breaks should add line height to lines. This matches the behavior of WebKit and Blink, but not Gecko. This also handles the special-case of `<br>` elements, which are implemented with preserved segment breaks via `white-space: pre-line`. This is an implementation detail though because `<br>` has a special behavior if the line isn't empty -- it doesn't add any line height in this case.
This commit is contained in:
parent
a9a7e8a5cf
commit
e5598590ba
12 changed files with 158 additions and 10 deletions
|
@ -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(),
|
||||
};
|
||||
|
||||
|
|
|
@ -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 `<br>` elements. They are implemented with `pre-line` in Servo, but
|
||||
// this is an implementation detail. The "magic" behavior of `<br>` 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 `<body>` 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 `<br>` element.
|
||||
const IS_BR_ELEMENT = 0b00000010;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
/// <https://dom.spec.whatwg.org/#concept-element-local-name>.
|
||||
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>;
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
[line-height-preserved-segment-break.html]
|
||||
expected: FAIL
|
|
@ -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",
|
||||
[
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
[quirks-mode-br-line-height.html]
|
||||
expected: FAIL
|
|
@ -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",
|
||||
[]
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<link rel="author" href="mrobinson@igalia.com">
|
||||
<style>
|
||||
span {
|
||||
display: inline-block;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background: green;
|
||||
color: transparent;
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<div style="width: 100px; border: solid; line-height: 50px;">
|
||||
<span></span><br>
|
||||
<span style="background: transparent; color: transparent;">A</span>
|
||||
<span></span>
|
||||
</div>
|
||||
</body>
|
35
tests/wpt/mozilla/tests/css/quirks-mode-br-line-height.html
Normal file
35
tests/wpt/mozilla/tests/css/quirks-mode-br-line-height.html
Normal file
|
@ -0,0 +1,35 @@
|
|||
<!-- quirks mode -->
|
||||
<link rel="match" href="quirks-mode-br-line-height-ref.html">
|
||||
<link rel="author" href="mrobinson@igalia.com">
|
||||
<!--
|
||||
Normally in quirks, only text and uncollapsible white space in inline boxes
|
||||
adds the inline box line height to the line. A forced line break is a kind of
|
||||
uncollapsible white space, so it should add line height to the line box.
|
||||
|
||||
We implement <br> in Servo with `white-space: pre-line`, but <br> doesn't act
|
||||
like a preserved line break. It only adds its line height to the line if its
|
||||
on an empty line.
|
||||
|
||||
This behavior doesn't seem to be specified, so is part of the "magic" behavior
|
||||
of <br> elements.
|
||||
-->
|
||||
<style>
|
||||
span {
|
||||
display: inline-block;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background: green;
|
||||
color: transparent;
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<div style="width: 100px; border: solid; line-height: 50px;">
|
||||
<span>A</span>
|
||||
<!-- The first <br> ends the line and the second <br> adds 50px
|
||||
of block size, because it inherits `line-height` from the
|
||||
parent <div> -->
|
||||
<br>
|
||||
<br>
|
||||
<span>A</span>
|
||||
</div>
|
||||
</body>
|
|
@ -0,0 +1,24 @@
|
|||
<!-- quirks mode -->
|
||||
<link rel="author" title="Martin Robinson" href="mrobinson@igalia.com">
|
||||
<link rel="help" href="https://quirks.spec.whatwg.org/#the-line-height-calculation-quirk">
|
||||
<link rel="help" href="https://drafts.csswg.org/css-text/#valdef-white-space-pre-line">
|
||||
<link rel="match" href="../css/reference/ref-filled-green-100px-square.xht">
|
||||
|
||||
<style>
|
||||
.container {
|
||||
width: 100px;
|
||||
background: green;
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
.inline-box {
|
||||
line-height: 100px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
|
||||
<div style="width: 100px; height: 100px; background: red;">
|
||||
<!-- The preserved segment break in the span should mean that "The line height calculation
|
||||
quirk" does not apply. -->
|
||||
<div class="container"><span class="inline-box">
</span></div>
|
||||
</div>
|
Loading…
Add table
Add a link
Reference in a new issue