зеркало из https://github.com/mozilla/gecko-dev.git
servo: Merge #13602 - layout: Rewrite Servo's `vertical-align` support to match CSS 2.1 § 10.8, and implement `vertical-align: middle` per CSS 2.1 § 10.8.1 (from pcwalton:vertical-align-middle); r=mbrubeck
`InlineMetrics` has been split into `InlineMetrics` for fragments and `LineMetrics` for lines. Both structures' fields have been renamed in order to more clearly delineate the difference between *space* and *content*. Vertical positioning of fragments has been reworked to take margins and borders into account only for replaced content. This patch fixes the `vertical_align_super_a.html` reftest. Servo now matches the rendering that Gecko and WebKit produce. Additionally, this includes a test for the popular inline-block centering technique described here: https://s.codepen.io/shshaw/fullpage/gEiDt?#Inline-Block r? @mbrubeck Source-Repo: https://github.com/servo/servo Source-Revision: 768a699fa5d822173b2247d6348413d09951a8bb
This commit is contained in:
Родитель
73f07d709c
Коммит
e87bbe3700
|
@ -306,7 +306,7 @@ impl FontHandleMethods for FontHandle {
|
||||||
.map(Au::from_f64_px)
|
.map(Au::from_f64_px)
|
||||||
.unwrap_or(max_advance_width);
|
.unwrap_or(max_advance_width);
|
||||||
|
|
||||||
let metrics = FontMetrics {
|
let metrics = FontMetrics {
|
||||||
underline_size: au_from_pt(self.ctfont.underline_thickness() as f64),
|
underline_size: au_from_pt(self.ctfont.underline_thickness() as f64),
|
||||||
// TODO(Issue #201): underline metrics are not reliable. Have to pull out of font table
|
// TODO(Issue #201): underline metrics are not reliable. Have to pull out of font table
|
||||||
// directly.
|
// directly.
|
||||||
|
@ -317,7 +317,7 @@ impl FontHandleMethods for FontHandle {
|
||||||
strikeout_size: Au(0), // FIXME(Issue #942)
|
strikeout_size: Au(0), // FIXME(Issue #942)
|
||||||
strikeout_offset: Au(0), // FIXME(Issue #942)
|
strikeout_offset: Au(0), // FIXME(Issue #942)
|
||||||
leading: au_from_pt(leading),
|
leading: au_from_pt(leading),
|
||||||
x_height: au_from_pt(self.ctfont.x_height() as f64),
|
x_height: au_from_pt((self.ctfont.x_height() as f64) * scale),
|
||||||
em_size: em_size,
|
em_size: em_size,
|
||||||
ascent: au_from_pt(ascent * scale),
|
ascent: au_from_pt(ascent * scale),
|
||||||
descent: au_from_pt(descent * scale),
|
descent: au_from_pt(descent * scale),
|
||||||
|
|
|
@ -476,14 +476,9 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
||||||
{
|
{
|
||||||
// FIXME(#6503): Use Arc::get_mut().unwrap() here.
|
// FIXME(#6503): Use Arc::get_mut().unwrap() here.
|
||||||
let inline_flow = flow_ref::deref_mut(&mut inline_flow_ref).as_mut_inline();
|
let inline_flow = flow_ref::deref_mut(&mut inline_flow_ref).as_mut_inline();
|
||||||
|
inline_flow.minimum_line_metrics =
|
||||||
|
inline_flow.minimum_line_metrics(&mut self.layout_context.font_context(),
|
||||||
let (ascent, descent) =
|
&node.style(self.style_context()))
|
||||||
inline_flow.compute_minimum_ascent_and_descent(&mut self.layout_context
|
|
||||||
.font_context(),
|
|
||||||
&node.style(self.style_context()));
|
|
||||||
inline_flow.minimum_block_size_above_baseline = ascent;
|
|
||||||
inline_flow.minimum_depth_below_baseline = descent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline_flow_ref.finish();
|
inline_flow_ref.finish();
|
||||||
|
|
|
@ -1401,7 +1401,7 @@ impl<'a> ImmutableFlowUtils for &'a Flow {
|
||||||
for kid in base(self).children.iter().rev() {
|
for kid in base(self).children.iter().rev() {
|
||||||
if kid.is_inline_flow() {
|
if kid.is_inline_flow() {
|
||||||
if let Some(baseline_offset) = kid.as_inline().baseline_offset_of_last_line() {
|
if let Some(baseline_offset) = kid.as_inline().baseline_offset_of_last_line() {
|
||||||
return Some(baseline_offset)
|
return Some(base(kid).position.start.b + baseline_offset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if kid.is_block_like() &&
|
if kid.is_block_like() &&
|
||||||
|
@ -1576,7 +1576,7 @@ impl ContainingBlockLink {
|
||||||
if flow.is_block_like() {
|
if flow.is_block_like() {
|
||||||
flow.as_block().explicit_block_containing_size(shared_context)
|
flow.as_block().explicit_block_containing_size(shared_context)
|
||||||
} else if flow.is_inline_flow() {
|
} else if flow.is_inline_flow() {
|
||||||
Some(flow.as_inline().minimum_block_size_above_baseline)
|
Some(flow.as_inline().minimum_line_metrics.space_above_baseline)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ use gfx::text::glyph::ByteIndex;
|
||||||
use gfx::text::text_run::{TextRun, TextRunSlice};
|
use gfx::text::text_run::{TextRun, TextRunSlice};
|
||||||
use gfx_traits::{FragmentType, LayerId, LayerType, StackingContextId};
|
use gfx_traits::{FragmentType, LayerId, LayerType, StackingContextId};
|
||||||
use inline::{FIRST_FRAGMENT_OF_ELEMENT, InlineFragmentContext, InlineFragmentNodeInfo};
|
use inline::{FIRST_FRAGMENT_OF_ELEMENT, InlineFragmentContext, InlineFragmentNodeInfo};
|
||||||
use inline::{InlineMetrics, LAST_FRAGMENT_OF_ELEMENT};
|
use inline::{InlineMetrics, LAST_FRAGMENT_OF_ELEMENT, LineMetrics};
|
||||||
use ipc_channel::ipc::IpcSender;
|
use ipc_channel::ipc::IpcSender;
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
use layout_debug;
|
use layout_debug;
|
||||||
|
@ -54,6 +54,10 @@ use text;
|
||||||
use text::TextRunScanner;
|
use text::TextRunScanner;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
// From gfxFontConstants.h in Firefox.
|
||||||
|
static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20;
|
||||||
|
static FONT_SUPERSCRIPT_OFFSET_RATIO: f32 = 0.34;
|
||||||
|
|
||||||
/// Fragments (`struct Fragment`) are the leaves of the layout tree. They cannot position
|
/// Fragments (`struct Fragment`) are the leaves of the layout tree. They cannot position
|
||||||
/// themselves. In general, fragments do not have a simple correspondence with CSS fragments in the
|
/// themselves. In general, fragments do not have a simple correspondence with CSS fragments in the
|
||||||
/// specification:
|
/// specification:
|
||||||
|
@ -643,6 +647,7 @@ impl ReplacedImageFragmentInfo {
|
||||||
inline_size + noncontent_inline_size
|
inline_size + noncontent_inline_size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Here, `noncontent_block_size` represents the sum of border and padding, but not margin.
|
||||||
pub fn calculate_replaced_block_size(&mut self,
|
pub fn calculate_replaced_block_size(&mut self,
|
||||||
style: &ServoComputedValues,
|
style: &ServoComputedValues,
|
||||||
noncontent_block_size: Au,
|
noncontent_block_size: Au,
|
||||||
|
@ -650,7 +655,6 @@ impl ReplacedImageFragmentInfo {
|
||||||
fragment_inline_size: Au,
|
fragment_inline_size: Au,
|
||||||
fragment_block_size: Au)
|
fragment_block_size: Au)
|
||||||
-> Au {
|
-> Au {
|
||||||
// TODO(ksh8281): compute border,margin,padding
|
|
||||||
let style_block_size = style.content_block_size();
|
let style_block_size = style.content_block_size();
|
||||||
let style_min_block_size = style.min_block_size();
|
let style_min_block_size = style.min_block_size();
|
||||||
let style_max_block_size = style.max_block_size();
|
let style_max_block_size = style.max_block_size();
|
||||||
|
@ -1178,13 +1182,6 @@ impl Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn calculate_line_height(&self, layout_context: &LayoutContext) -> Au {
|
|
||||||
let font_style = self.style.get_font_arc();
|
|
||||||
let font_metrics = text::font_metrics_for_style(&mut layout_context.font_context(),
|
|
||||||
font_style);
|
|
||||||
text::line_height_from_style(&*self.style, &font_metrics)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the sum of the inline-sizes of all the borders of this fragment. Note that this
|
/// Returns the sum of the inline-sizes of all the borders of this fragment. Note that this
|
||||||
/// can be expensive to compute, so if possible use the `border_padding` field instead.
|
/// can be expensive to compute, so if possible use the `border_padding` field instead.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -2132,38 +2129,52 @@ impl Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if this fragment is replaced content or an inline-block or false otherwise.
|
||||||
|
pub fn is_replaced_or_inline_block(&self) -> bool {
|
||||||
|
match self.specific {
|
||||||
|
SpecificFragmentInfo::Canvas(_) |
|
||||||
|
SpecificFragmentInfo::Iframe(_) |
|
||||||
|
SpecificFragmentInfo::Image(_) |
|
||||||
|
SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
|
||||||
|
SpecificFragmentInfo::InlineBlock(_) |
|
||||||
|
SpecificFragmentInfo::Svg(_) => true,
|
||||||
|
SpecificFragmentInfo::Generic |
|
||||||
|
SpecificFragmentInfo::GeneratedContent(_) |
|
||||||
|
SpecificFragmentInfo::InlineAbsolute(_) |
|
||||||
|
SpecificFragmentInfo::Table |
|
||||||
|
SpecificFragmentInfo::TableCell |
|
||||||
|
SpecificFragmentInfo::TableColumn(_) |
|
||||||
|
SpecificFragmentInfo::TableRow |
|
||||||
|
SpecificFragmentInfo::TableWrapper |
|
||||||
|
SpecificFragmentInfo::Multicol |
|
||||||
|
SpecificFragmentInfo::MulticolColumn |
|
||||||
|
SpecificFragmentInfo::ScannedText(_) |
|
||||||
|
SpecificFragmentInfo::UnscannedText(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Calculates block-size above baseline, depth below baseline, and ascent for this fragment
|
/// Calculates block-size above baseline, depth below baseline, and ascent for this fragment
|
||||||
/// when used in an inline formatting context. See CSS 2.1 § 10.8.1.
|
/// when used in an inline formatting context. See CSS 2.1 § 10.8.1.
|
||||||
pub fn inline_metrics(&self, layout_context: &LayoutContext) -> InlineMetrics {
|
///
|
||||||
return match self.specific {
|
/// This does not take `vertical-align` into account. For that, use `aligned_inline_metrics()`.
|
||||||
SpecificFragmentInfo::Image(ref image_fragment_info) => {
|
fn content_inline_metrics(&self, layout_context: &LayoutContext) -> InlineMetrics {
|
||||||
let computed_block_size = image_fragment_info.replaced_image_fragment_info
|
// CSS 2.1 § 10.8: "The height of each inline-level box in the line box is
|
||||||
.computed_block_size();
|
// calculated. For replaced elements, inline-block elements, and inline-table
|
||||||
|
// elements, this is the height of their margin box."
|
||||||
|
//
|
||||||
|
// FIXME(pcwalton): We have to handle `Generic` and `GeneratedContent` here to avoid
|
||||||
|
// crashing in a couple of `css21_dev/html4/content-` WPTs, but I don't see how those two
|
||||||
|
// fragment types should end up inside inlines. (In the case of `GeneratedContent`, those
|
||||||
|
// fragment types should have been resolved by now…)
|
||||||
|
let inline_metrics = match self.specific {
|
||||||
|
SpecificFragmentInfo::Canvas(_) | SpecificFragmentInfo::Iframe(_) |
|
||||||
|
SpecificFragmentInfo::Image(_) | SpecificFragmentInfo::Svg(_) |
|
||||||
|
SpecificFragmentInfo::Generic | SpecificFragmentInfo::GeneratedContent(_) => {
|
||||||
|
let ascent = self.border_box.size.block + self.margin.block_start;
|
||||||
InlineMetrics {
|
InlineMetrics {
|
||||||
block_size_above_baseline: computed_block_size +
|
space_above_baseline: ascent,
|
||||||
self.border_padding.block_start,
|
space_below_baseline: self.margin.block_end,
|
||||||
depth_below_baseline: self.border_padding.block_end,
|
ascent: ascent,
|
||||||
ascent: computed_block_size + self.border_padding.block_start,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SpecificFragmentInfo::Canvas(ref canvas_fragment_info) => {
|
|
||||||
let computed_block_size = canvas_fragment_info.replaced_image_fragment_info
|
|
||||||
.computed_block_size();
|
|
||||||
InlineMetrics {
|
|
||||||
block_size_above_baseline: computed_block_size +
|
|
||||||
self.border_padding.block_start,
|
|
||||||
depth_below_baseline: self.border_padding.block_end,
|
|
||||||
ascent: computed_block_size + self.border_padding.block_start,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SpecificFragmentInfo::Svg(ref svg_fragment_info) => {
|
|
||||||
let computed_block_size = svg_fragment_info.replaced_image_fragment_info
|
|
||||||
.computed_block_size();
|
|
||||||
InlineMetrics {
|
|
||||||
block_size_above_baseline: computed_block_size +
|
|
||||||
self.border_padding.block_start,
|
|
||||||
depth_below_baseline: self.border_padding.block_end,
|
|
||||||
ascent: computed_block_size + self.border_padding.block_start,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SpecificFragmentInfo::ScannedText(ref info) => {
|
SpecificFragmentInfo::ScannedText(ref info) => {
|
||||||
|
@ -2173,14 +2184,10 @@ impl Fragment {
|
||||||
return InlineMetrics::new(Au(0), Au(0), Au(0));
|
return InlineMetrics::new(Au(0), Au(0), Au(0));
|
||||||
}
|
}
|
||||||
// See CSS 2.1 § 10.8.1.
|
// See CSS 2.1 § 10.8.1.
|
||||||
let line_height = self.calculate_line_height(layout_context);
|
let font_metrics = text::font_metrics_for_style(&mut layout_context.font_context(),
|
||||||
let font_derived_metrics =
|
self.style.get_font_arc());
|
||||||
InlineMetrics::from_font_metrics(&info.run.font_metrics, line_height);
|
let line_height = text::line_height_from_style(&*self.style, &font_metrics);
|
||||||
InlineMetrics {
|
InlineMetrics::from_font_metrics(&info.run.font_metrics, line_height)
|
||||||
block_size_above_baseline: font_derived_metrics.block_size_above_baseline,
|
|
||||||
depth_below_baseline: font_derived_metrics.depth_below_baseline,
|
|
||||||
ascent: font_derived_metrics.ascent + self.border_padding.block_start,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SpecificFragmentInfo::InlineBlock(ref info) => {
|
SpecificFragmentInfo::InlineBlock(ref info) => {
|
||||||
inline_metrics_of_block(&info.flow_ref, &*self.style)
|
inline_metrics_of_block(&info.flow_ref, &*self.style)
|
||||||
|
@ -2191,21 +2198,26 @@ impl Fragment {
|
||||||
SpecificFragmentInfo::InlineAbsolute(_) => {
|
SpecificFragmentInfo::InlineAbsolute(_) => {
|
||||||
InlineMetrics::new(Au(0), Au(0), Au(0))
|
InlineMetrics::new(Au(0), Au(0), Au(0))
|
||||||
}
|
}
|
||||||
_ => {
|
SpecificFragmentInfo::Table |
|
||||||
InlineMetrics {
|
SpecificFragmentInfo::TableCell |
|
||||||
block_size_above_baseline: self.border_box.size.block,
|
SpecificFragmentInfo::TableColumn(_) |
|
||||||
depth_below_baseline: Au(0),
|
SpecificFragmentInfo::TableRow |
|
||||||
ascent: self.border_box.size.block,
|
SpecificFragmentInfo::TableWrapper |
|
||||||
}
|
SpecificFragmentInfo::Multicol |
|
||||||
|
SpecificFragmentInfo::MulticolColumn |
|
||||||
|
SpecificFragmentInfo::UnscannedText(_) => {
|
||||||
|
unreachable!("Shouldn't see fragments of this type here!")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
return inline_metrics;
|
||||||
|
|
||||||
fn inline_metrics_of_block(flow: &FlowRef, style: &ServoComputedValues) -> InlineMetrics {
|
fn inline_metrics_of_block(flow: &FlowRef, style: &ServoComputedValues) -> InlineMetrics {
|
||||||
// See CSS 2.1 § 10.8.1.
|
// CSS 2.1 § 10.8: "The height of each inline-level box in the line box is calculated.
|
||||||
|
// For replaced elements, inline-block elements, and inline-table elements, this is the
|
||||||
|
// height of their margin box."
|
||||||
let block_flow = flow.as_block();
|
let block_flow = flow.as_block();
|
||||||
let is_auto = style.get_position().height == LengthOrPercentageOrAuto::Auto;
|
let is_auto = style.get_position().height == LengthOrPercentageOrAuto::Auto;
|
||||||
let baseline_offset = flow.baseline_offset_of_last_line_box_in_flow();
|
let baseline_offset = match flow.baseline_offset_of_last_line_box_in_flow() {
|
||||||
let baseline_offset = match baseline_offset {
|
|
||||||
Some(baseline_offset) if is_auto => baseline_offset,
|
Some(baseline_offset) if is_auto => baseline_offset,
|
||||||
_ => block_flow.fragment.border_box.size.block,
|
_ => block_flow.fragment.border_box.size.block,
|
||||||
};
|
};
|
||||||
|
@ -2218,6 +2230,107 @@ impl Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Calculates the offset from the baseline that applies to this fragment due to
|
||||||
|
/// `vertical-align`. Positive values represent downward displacement.
|
||||||
|
///
|
||||||
|
/// If `actual_line_metrics` is supplied, then these metrics are used to determine the
|
||||||
|
/// displacement of the fragment when `top` or `bottom` `vertical-align` values are
|
||||||
|
/// encountered. If this is not supplied, then `top` and `bottom` values are ignored.
|
||||||
|
fn vertical_alignment_offset(&self,
|
||||||
|
layout_context: &LayoutContext,
|
||||||
|
content_inline_metrics: &InlineMetrics,
|
||||||
|
minimum_line_metrics: &LineMetrics,
|
||||||
|
actual_line_metrics: Option<&LineMetrics>)
|
||||||
|
-> Au {
|
||||||
|
let mut offset = Au(0);
|
||||||
|
for style in self.inline_styles() {
|
||||||
|
// If any of the inline styles say `top` or `bottom`, adjust the vertical align
|
||||||
|
// appropriately.
|
||||||
|
//
|
||||||
|
// FIXME(#5624, pcwalton): This passes our current reftests but isn't the right thing
|
||||||
|
// to do.
|
||||||
|
match style.get_box().vertical_align {
|
||||||
|
vertical_align::T::baseline => {}
|
||||||
|
vertical_align::T::middle => {
|
||||||
|
let font_metrics =
|
||||||
|
text::font_metrics_for_style(&mut layout_context.font_context(),
|
||||||
|
style.get_font_arc());
|
||||||
|
offset += (content_inline_metrics.ascent -
|
||||||
|
content_inline_metrics.space_below_baseline -
|
||||||
|
font_metrics.x_height).scale_by(0.5)
|
||||||
|
}
|
||||||
|
vertical_align::T::sub => {
|
||||||
|
offset += minimum_line_metrics.space_needed()
|
||||||
|
.scale_by(FONT_SUBSCRIPT_OFFSET_RATIO)
|
||||||
|
}
|
||||||
|
vertical_align::T::super_ => {
|
||||||
|
offset -= minimum_line_metrics.space_needed()
|
||||||
|
.scale_by(FONT_SUPERSCRIPT_OFFSET_RATIO)
|
||||||
|
}
|
||||||
|
vertical_align::T::text_top => {
|
||||||
|
offset = self.content_inline_metrics(layout_context).ascent -
|
||||||
|
minimum_line_metrics.space_above_baseline
|
||||||
|
}
|
||||||
|
vertical_align::T::text_bottom => {
|
||||||
|
offset = minimum_line_metrics.space_below_baseline -
|
||||||
|
self.content_inline_metrics(layout_context).space_below_baseline
|
||||||
|
}
|
||||||
|
vertical_align::T::top => {
|
||||||
|
if let Some(actual_line_metrics) = actual_line_metrics {
|
||||||
|
offset = content_inline_metrics.ascent -
|
||||||
|
actual_line_metrics.space_above_baseline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vertical_align::T::bottom => {
|
||||||
|
if let Some(actual_line_metrics) = actual_line_metrics {
|
||||||
|
offset = actual_line_metrics.space_below_baseline -
|
||||||
|
content_inline_metrics.space_below_baseline
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vertical_align::T::LengthOrPercentage(LengthOrPercentage::Length(length)) => {
|
||||||
|
offset -= length
|
||||||
|
}
|
||||||
|
vertical_align::T::LengthOrPercentage(LengthOrPercentage::Percentage(
|
||||||
|
percentage)) => {
|
||||||
|
offset -= minimum_line_metrics.space_needed().scale_by(percentage)
|
||||||
|
}
|
||||||
|
vertical_align::T::LengthOrPercentage(LengthOrPercentage::Calc(formula)) => {
|
||||||
|
offset -= minimum_line_metrics.space_needed().scale_by(formula.percentage()) +
|
||||||
|
formula.length()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offset
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculates block-size above baseline, depth below baseline, and ascent for this fragment
|
||||||
|
/// when used in an inline formatting context, taking `vertical-align` (other than `top` or
|
||||||
|
/// `bottom`) into account. See CSS 2.1 § 10.8.1.
|
||||||
|
///
|
||||||
|
/// If `actual_line_metrics` is supplied, then these metrics are used to determine the
|
||||||
|
/// displacement of the fragment when `top` or `bottom` `vertical-align` values are
|
||||||
|
/// encountered. If this is not supplied, then `top` and `bottom` values are ignored.
|
||||||
|
pub fn aligned_inline_metrics(&self,
|
||||||
|
layout_context: &LayoutContext,
|
||||||
|
minimum_line_metrics: &LineMetrics,
|
||||||
|
actual_line_metrics: Option<&LineMetrics>)
|
||||||
|
-> InlineMetrics {
|
||||||
|
let content_inline_metrics = self.content_inline_metrics(layout_context);
|
||||||
|
let vertical_alignment_offset = self.vertical_alignment_offset(layout_context,
|
||||||
|
&content_inline_metrics,
|
||||||
|
minimum_line_metrics,
|
||||||
|
actual_line_metrics);
|
||||||
|
let mut space_above_baseline = match actual_line_metrics {
|
||||||
|
None => content_inline_metrics.space_above_baseline,
|
||||||
|
Some(actual_line_metrics) => actual_line_metrics.space_above_baseline,
|
||||||
|
};
|
||||||
|
space_above_baseline = space_above_baseline - vertical_alignment_offset;
|
||||||
|
let space_below_baseline = content_inline_metrics.space_below_baseline +
|
||||||
|
vertical_alignment_offset;
|
||||||
|
let ascent = content_inline_metrics.ascent - vertical_alignment_offset;
|
||||||
|
InlineMetrics::new(space_above_baseline, space_below_baseline, ascent)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns true if this fragment is a hypothetical box. See CSS 2.1 § 10.3.7.
|
/// Returns true if this fragment is a hypothetical box. See CSS 2.1 § 10.3.7.
|
||||||
pub fn is_hypothetical(&self) -> bool {
|
pub fn is_hypothetical(&self) -> bool {
|
||||||
match self.specific {
|
match self.specific {
|
||||||
|
|
|
@ -37,14 +37,9 @@ use style::computed_values::{text_overflow, vertical_align, white_space};
|
||||||
use style::context::{SharedStyleContext, StyleContext};
|
use style::context::{SharedStyleContext, StyleContext};
|
||||||
use style::logical_geometry::{LogicalRect, LogicalSize, WritingMode};
|
use style::logical_geometry::{LogicalRect, LogicalSize, WritingMode};
|
||||||
use style::properties::ServoComputedValues;
|
use style::properties::ServoComputedValues;
|
||||||
use style::values::computed::LengthOrPercentage;
|
|
||||||
use text;
|
use text;
|
||||||
use unicode_bidi;
|
use unicode_bidi;
|
||||||
|
|
||||||
// From gfxFontConstants.h in Firefox
|
|
||||||
static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20;
|
|
||||||
static FONT_SUPERSCRIPT_OFFSET_RATIO: f32 = 0.34;
|
|
||||||
|
|
||||||
/// `Line`s are represented as offsets into the child list, rather than
|
/// `Line`s are represented as offsets into the child list, rather than
|
||||||
/// as an object that "owns" fragments. Choosing a different set of line
|
/// as an object that "owns" fragments. Choosing a different set of line
|
||||||
/// breaks requires a new list of offsets, and possibly some splitting and
|
/// breaks requires a new list of offsets, and possibly some splitting and
|
||||||
|
@ -158,31 +153,22 @@ pub struct Line {
|
||||||
/// ~~~
|
/// ~~~
|
||||||
pub green_zone: LogicalSize<Au>,
|
pub green_zone: LogicalSize<Au>,
|
||||||
|
|
||||||
/// The minimum block size above the baseline for this line, as specified by the style.
|
/// The minimum metrics for this line, as specified by the style.
|
||||||
pub minimum_block_size_above_baseline: Au,
|
pub minimum_metrics: LineMetrics,
|
||||||
|
|
||||||
/// The minimum depth below the baseline for this line, as specified by the style.
|
/// The actual metrics for this line.
|
||||||
pub minimum_depth_below_baseline: Au,
|
pub metrics: LineMetrics,
|
||||||
|
|
||||||
/// The inline metrics for this line.
|
|
||||||
pub inline_metrics: InlineMetrics,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Line {
|
impl Line {
|
||||||
fn new(writing_mode: WritingMode,
|
fn new(writing_mode: WritingMode, minimum_metrics: &LineMetrics) -> Line {
|
||||||
minimum_block_size_above_baseline: Au,
|
|
||||||
minimum_depth_below_baseline: Au)
|
|
||||||
-> Line {
|
|
||||||
Line {
|
Line {
|
||||||
range: Range::empty(),
|
range: Range::empty(),
|
||||||
visual_runs: None,
|
visual_runs: None,
|
||||||
bounds: LogicalRect::zero(writing_mode),
|
bounds: LogicalRect::zero(writing_mode),
|
||||||
green_zone: LogicalSize::zero(writing_mode),
|
green_zone: LogicalSize::zero(writing_mode),
|
||||||
minimum_block_size_above_baseline: minimum_block_size_above_baseline,
|
minimum_metrics: *minimum_metrics,
|
||||||
minimum_depth_below_baseline: minimum_depth_below_baseline,
|
metrics: *minimum_metrics,
|
||||||
inline_metrics: InlineMetrics::new(minimum_block_size_above_baseline,
|
|
||||||
minimum_depth_below_baseline,
|
|
||||||
minimum_block_size_above_baseline),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,29 +176,31 @@ impl Line {
|
||||||
///
|
///
|
||||||
/// FIXME(pcwalton): this assumes that the tallest fragment in the line determines the line
|
/// FIXME(pcwalton): this assumes that the tallest fragment in the line determines the line
|
||||||
/// block-size. This might not be the case with some weird text fonts.
|
/// block-size. This might not be the case with some weird text fonts.
|
||||||
fn new_inline_metrics(&self, new_fragment: &Fragment, layout_context: &LayoutContext)
|
fn new_metrics_for_fragment(&self, new_fragment: &Fragment, layout_context: &LayoutContext)
|
||||||
-> InlineMetrics {
|
-> LineMetrics {
|
||||||
if !new_fragment.is_vertically_aligned_to_top_or_bottom() {
|
if !new_fragment.is_vertically_aligned_to_top_or_bottom() {
|
||||||
let fragment_inline_metrics = new_fragment.inline_metrics(layout_context);
|
let fragment_inline_metrics =
|
||||||
self.inline_metrics.max(&fragment_inline_metrics)
|
new_fragment.aligned_inline_metrics(layout_context, &self.minimum_metrics, None);
|
||||||
|
self.metrics.new_metrics_for_fragment(&fragment_inline_metrics)
|
||||||
} else {
|
} else {
|
||||||
self.inline_metrics
|
self.metrics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the new block size that this line would have if `new_fragment` were added to it.
|
/// Returns the new block size that this line would have if `new_fragment` were added to it.
|
||||||
/// `new_inline_metrics` represents the new inline metrics that this line would have; it can
|
/// `new_inline_metrics` represents the new inline metrics that this line would have; it can
|
||||||
/// be computed with `new_inline_metrics()`.
|
/// be computed with `new_inline_metrics()`.
|
||||||
fn new_block_size(&self,
|
fn new_block_size_for_fragment(&self,
|
||||||
new_fragment: &Fragment,
|
new_fragment: &Fragment,
|
||||||
new_inline_metrics: &InlineMetrics,
|
new_line_metrics: &LineMetrics,
|
||||||
layout_context: &LayoutContext)
|
layout_context: &LayoutContext)
|
||||||
-> Au {
|
-> Au {
|
||||||
let new_block_size = if new_fragment.is_vertically_aligned_to_top_or_bottom() {
|
let new_block_size = if new_fragment.is_vertically_aligned_to_top_or_bottom() {
|
||||||
max(new_fragment.inline_metrics(layout_context).block_size(),
|
max(new_fragment.aligned_inline_metrics(layout_context, &self.minimum_metrics, None)
|
||||||
self.minimum_block_size_above_baseline + self.minimum_depth_below_baseline)
|
.space_needed(),
|
||||||
|
self.minimum_metrics.space_needed())
|
||||||
} else {
|
} else {
|
||||||
new_inline_metrics.block_size()
|
new_line_metrics.space_needed()
|
||||||
};
|
};
|
||||||
max(self.bounds.size.block, new_block_size)
|
max(self.bounds.size.block, new_block_size)
|
||||||
}
|
}
|
||||||
|
@ -243,34 +231,24 @@ struct LineBreaker {
|
||||||
cur_b: Au,
|
cur_b: Au,
|
||||||
/// The computed value of the indentation for the first line (`text-indent`, CSS 2.1 § 16.1).
|
/// The computed value of the indentation for the first line (`text-indent`, CSS 2.1 § 16.1).
|
||||||
first_line_indentation: Au,
|
first_line_indentation: Au,
|
||||||
/// The minimum block-size above the baseline for each line, as specified by the line height
|
/// The minimum metrics for each line, as specified by the line height and font style.
|
||||||
/// and font style.
|
minimum_metrics: LineMetrics,
|
||||||
minimum_block_size_above_baseline: Au,
|
|
||||||
/// The minimum depth below the baseline for each line, as specified by the line height and
|
|
||||||
/// font style.
|
|
||||||
minimum_depth_below_baseline: Au,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LineBreaker {
|
impl LineBreaker {
|
||||||
/// Creates a new `LineBreaker` with a set of floats and the indentation of the first line.
|
/// Creates a new `LineBreaker` with a set of floats and the indentation of the first line.
|
||||||
fn new(float_context: Floats,
|
fn new(float_context: Floats, first_line_indentation: Au, minimum_line_metrics: &LineMetrics)
|
||||||
first_line_indentation: Au,
|
|
||||||
minimum_block_size_above_baseline: Au,
|
|
||||||
minimum_depth_below_baseline: Au)
|
|
||||||
-> LineBreaker {
|
-> LineBreaker {
|
||||||
LineBreaker {
|
LineBreaker {
|
||||||
new_fragments: Vec::new(),
|
new_fragments: Vec::new(),
|
||||||
work_list: VecDeque::new(),
|
work_list: VecDeque::new(),
|
||||||
pending_line: Line::new(float_context.writing_mode,
|
pending_line: Line::new(float_context.writing_mode, minimum_line_metrics),
|
||||||
minimum_block_size_above_baseline,
|
|
||||||
minimum_depth_below_baseline),
|
|
||||||
floats: float_context,
|
floats: float_context,
|
||||||
lines: Vec::new(),
|
lines: Vec::new(),
|
||||||
cur_b: Au(0),
|
cur_b: Au(0),
|
||||||
last_known_line_breaking_opportunity: None,
|
last_known_line_breaking_opportunity: None,
|
||||||
first_line_indentation: first_line_indentation,
|
first_line_indentation: first_line_indentation,
|
||||||
minimum_block_size_above_baseline: minimum_block_size_above_baseline,
|
minimum_metrics: *minimum_line_metrics,
|
||||||
minimum_depth_below_baseline: minimum_depth_below_baseline,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,9 +263,8 @@ impl LineBreaker {
|
||||||
/// Reinitializes the pending line to blank data.
|
/// Reinitializes the pending line to blank data.
|
||||||
fn reset_line(&mut self) -> Line {
|
fn reset_line(&mut self) -> Line {
|
||||||
self.last_known_line_breaking_opportunity = None;
|
self.last_known_line_breaking_opportunity = None;
|
||||||
mem::replace(&mut self.pending_line, Line::new(self.floats.writing_mode,
|
mem::replace(&mut self.pending_line,
|
||||||
self.minimum_block_size_above_baseline,
|
Line::new(self.floats.writing_mode, &self.minimum_metrics))
|
||||||
self.minimum_depth_below_baseline))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reflows fragments for the given inline flow.
|
/// Reflows fragments for the given inline flow.
|
||||||
|
@ -574,10 +551,11 @@ impl LineBreaker {
|
||||||
// `green_zone.block < self.pending_line.bounds.size.block`, then we committed a line that
|
// `green_zone.block < self.pending_line.bounds.size.block`, then we committed a line that
|
||||||
// overlaps with floats.
|
// overlaps with floats.
|
||||||
let green_zone = self.pending_line.green_zone;
|
let green_zone = self.pending_line.green_zone;
|
||||||
let new_inline_metrics = self.pending_line.new_inline_metrics(&fragment, layout_context);
|
let new_line_metrics = self.pending_line.new_metrics_for_fragment(&fragment,
|
||||||
let new_block_size = self.pending_line.new_block_size(&fragment,
|
layout_context);
|
||||||
&new_inline_metrics,
|
let new_block_size = self.pending_line.new_block_size_for_fragment(&fragment,
|
||||||
layout_context);
|
&new_line_metrics,
|
||||||
|
layout_context);
|
||||||
if new_block_size > green_zone.block {
|
if new_block_size > green_zone.block {
|
||||||
// Uh-oh. Float collision imminent. Enter the float collision avoider!
|
// Uh-oh. Float collision imminent. Enter the float collision avoider!
|
||||||
if !self.avoid_floats(flow, fragment, new_block_size) {
|
if !self.avoid_floats(flow, fragment, new_block_size) {
|
||||||
|
@ -747,14 +725,13 @@ impl LineBreaker {
|
||||||
|
|
||||||
if !fragment.is_inline_absolute() && !fragment.is_hypothetical() {
|
if !fragment.is_inline_absolute() && !fragment.is_hypothetical() {
|
||||||
self.pending_line.bounds.size.inline = self.pending_line.bounds.size.inline +
|
self.pending_line.bounds.size.inline = self.pending_line.bounds.size.inline +
|
||||||
fragment.margin_box_inline_size() +
|
fragment.margin_box_inline_size() + indentation;
|
||||||
indentation;
|
self.pending_line.metrics = self.pending_line.new_metrics_for_fragment(&fragment,
|
||||||
self.pending_line.inline_metrics =
|
layout_context);
|
||||||
self.pending_line.new_inline_metrics(&fragment, layout_context);
|
|
||||||
self.pending_line.bounds.size.block =
|
self.pending_line.bounds.size.block =
|
||||||
self.pending_line.new_block_size(&fragment,
|
self.pending_line.new_block_size_for_fragment(&fragment,
|
||||||
&self.pending_line.inline_metrics,
|
&self.pending_line.metrics,
|
||||||
layout_context);
|
layout_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.new_fragments.push(fragment);
|
self.new_fragments.push(fragment);
|
||||||
|
@ -864,13 +841,8 @@ pub struct InlineFlow {
|
||||||
/// lines.
|
/// lines.
|
||||||
pub lines: Vec<Line>,
|
pub lines: Vec<Line>,
|
||||||
|
|
||||||
/// The minimum block-size above the baseline for each line, as specified by the line height
|
/// The minimum metrics for each line, as specified by the line height and font style.
|
||||||
/// and font style.
|
pub minimum_line_metrics: LineMetrics,
|
||||||
pub minimum_block_size_above_baseline: Au,
|
|
||||||
|
|
||||||
/// The minimum depth below the baseline for each line, as specified by the line height and
|
|
||||||
/// font style.
|
|
||||||
pub minimum_depth_below_baseline: Au,
|
|
||||||
|
|
||||||
/// The amount of indentation to use on the first line. This is determined by our block parent
|
/// The amount of indentation to use on the first line. This is determined by our block parent
|
||||||
/// (because percentages are relative to the containing block, and we aren't in a position to
|
/// (because percentages are relative to the containing block, and we aren't in a position to
|
||||||
|
@ -884,8 +856,7 @@ impl InlineFlow {
|
||||||
base: BaseFlow::new(None, writing_mode, ForceNonfloatedFlag::ForceNonfloated),
|
base: BaseFlow::new(None, writing_mode, ForceNonfloatedFlag::ForceNonfloated),
|
||||||
fragments: fragments,
|
fragments: fragments,
|
||||||
lines: Vec::new(),
|
lines: Vec::new(),
|
||||||
minimum_block_size_above_baseline: Au(0),
|
minimum_line_metrics: LineMetrics::new(Au(0), Au(0)),
|
||||||
minimum_depth_below_baseline: Au(0),
|
|
||||||
first_line_indentation: Au(0),
|
first_line_indentation: Au(0),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1050,90 +1021,62 @@ impl InlineFlow {
|
||||||
/// Sets final fragment positions in the block direction for one line.
|
/// Sets final fragment positions in the block direction for one line.
|
||||||
fn set_block_fragment_positions(fragments: &mut InlineFragments,
|
fn set_block_fragment_positions(fragments: &mut InlineFragments,
|
||||||
line: &Line,
|
line: &Line,
|
||||||
minimum_block_size_above_baseline: Au,
|
minimum_line_metrics: &LineMetrics,
|
||||||
minimum_depth_below_baseline: Au,
|
|
||||||
layout_context: &LayoutContext) {
|
layout_context: &LayoutContext) {
|
||||||
for fragment_index in line.range.each_index() {
|
for fragment_index in line.range.each_index() {
|
||||||
// If any of the inline styles say `top` or `bottom`, adjust the vertical align
|
|
||||||
// appropriately.
|
|
||||||
//
|
|
||||||
// FIXME(#5624, pcwalton): This passes our current reftests but isn't the right thing
|
|
||||||
// to do.
|
|
||||||
let fragment = fragments.get_mut(fragment_index.to_usize());
|
let fragment = fragments.get_mut(fragment_index.to_usize());
|
||||||
let fragment_inline_metrics = fragment.inline_metrics(layout_context);
|
let line_metrics = LineMetrics::for_line_and_fragment(line, fragment, layout_context);
|
||||||
let line_block_metrics = LineBlockMetrics::new(line, fragment, layout_context);
|
let inline_metrics = fragment.aligned_inline_metrics(layout_context,
|
||||||
let mut block_start = line_block_metrics.start +
|
minimum_line_metrics,
|
||||||
line_block_metrics.size_above_baseline -
|
Some(&line_metrics));
|
||||||
fragment_inline_metrics.ascent;
|
|
||||||
|
|
||||||
for style in fragment.inline_styles() {
|
// Align the top of the fragment's border box with its ascent above the baseline.
|
||||||
match style.get_box().vertical_align {
|
fragment.border_box.start.b = line.bounds.start.b + line_metrics.space_above_baseline -
|
||||||
vertical_align::T::baseline => {}
|
inline_metrics.ascent;
|
||||||
vertical_align::T::middle => {}
|
|
||||||
vertical_align::T::sub => {
|
// CSS 2.1 § 10.8: "The height of each inline-level box in the line box is
|
||||||
let sub_offset =
|
// calculated. For replaced elements, inline-block elements, and inline-table
|
||||||
(minimum_block_size_above_baseline +
|
// elements, this is the height of their margin box; for inline boxes, this is their
|
||||||
minimum_depth_below_baseline).scale_by(FONT_SUBSCRIPT_OFFSET_RATIO);
|
// 'line-height'."
|
||||||
block_start = block_start + sub_offset
|
//
|
||||||
}
|
// CSS 2.1 § 10.8.1: "Although margins, borders, and padding of non-replaced elements
|
||||||
vertical_align::T::super_ => {
|
// do not enter into the line box calculation, they are still rendered around inline
|
||||||
let super_offset =
|
// boxes."
|
||||||
(minimum_block_size_above_baseline +
|
//
|
||||||
minimum_depth_below_baseline).scale_by(FONT_SUPERSCRIPT_OFFSET_RATIO);
|
// Effectively, if the fragment is a non-replaced element (excluding inline-block), we
|
||||||
block_start = block_start - super_offset
|
// need to align its ascent above the baseline with the top of the *content box*, not
|
||||||
}
|
// the border box. Since the code above has already aligned it to the border box, we
|
||||||
vertical_align::T::text_top => {
|
// simply need to adjust it in this case.
|
||||||
block_start = line_block_metrics.start +
|
if !fragment.is_replaced_or_inline_block() {
|
||||||
line_block_metrics.size_above_baseline -
|
fragment.border_box.start.b -= fragment.border_padding.block_start
|
||||||
minimum_block_size_above_baseline
|
|
||||||
}
|
|
||||||
vertical_align::T::text_bottom => {
|
|
||||||
block_start = line_block_metrics.start +
|
|
||||||
line_block_metrics.size_above_baseline +
|
|
||||||
minimum_depth_below_baseline -
|
|
||||||
fragment.border_box.size.block
|
|
||||||
}
|
|
||||||
vertical_align::T::top => {
|
|
||||||
block_start = line_block_metrics.start
|
|
||||||
}
|
|
||||||
vertical_align::T::bottom => {
|
|
||||||
block_start = line_block_metrics.start + line_block_metrics.size -
|
|
||||||
fragment.border_box.size.block
|
|
||||||
}
|
|
||||||
vertical_align::T::LengthOrPercentage(LengthOrPercentage::Length(length)) => {
|
|
||||||
block_start = block_start - length
|
|
||||||
}
|
|
||||||
vertical_align::T::LengthOrPercentage(LengthOrPercentage::Percentage(
|
|
||||||
percentage)) => {
|
|
||||||
let line_height = fragment.calculate_line_height(layout_context);
|
|
||||||
let length = line_height.scale_by(percentage);
|
|
||||||
block_start = block_start - length
|
|
||||||
}
|
|
||||||
vertical_align::T::LengthOrPercentage(LengthOrPercentage::Calc(calc)) => {
|
|
||||||
let line_height = fragment.calculate_line_height(layout_context);
|
|
||||||
let percentage_length = line_height.scale_by(calc.percentage());
|
|
||||||
block_start = block_start - percentage_length - calc.length()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fragment.border_box.start.b = block_start;
|
|
||||||
fragment.update_late_computed_block_position_if_necessary();
|
fragment.update_late_computed_block_position_if_necessary();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes the minimum ascent and descent for each line. This is done during flow
|
/// Computes the minimum metrics for each line. This is done during flow construction.
|
||||||
/// construction.
|
|
||||||
///
|
///
|
||||||
/// `style` is the style of the block.
|
/// `style` is the style of the block.
|
||||||
pub fn compute_minimum_ascent_and_descent(&self,
|
pub fn minimum_line_metrics(&self, font_context: &mut FontContext, style: &ServoComputedValues)
|
||||||
|
-> LineMetrics {
|
||||||
|
InlineFlow::minimum_line_metrics_for_fragments(&self.fragments.fragments,
|
||||||
|
font_context,
|
||||||
|
style)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes the minimum line metrics for the given fragments. This is typically done during
|
||||||
|
/// flow construction.
|
||||||
|
///
|
||||||
|
/// `style` is the style of the block that these fragments belong to.
|
||||||
|
pub fn minimum_line_metrics_for_fragments(fragments: &[Fragment],
|
||||||
font_context: &mut FontContext,
|
font_context: &mut FontContext,
|
||||||
style: &ServoComputedValues)
|
style: &ServoComputedValues)
|
||||||
-> (Au, Au) {
|
-> LineMetrics {
|
||||||
// As a special case, if this flow contains only hypothetical fragments, then the entire
|
// As a special case, if this flow contains only hypothetical fragments, then the entire
|
||||||
// flow is hypothetical and takes up no space. See CSS 2.1 § 10.3.7.
|
// flow is hypothetical and takes up no space. See CSS 2.1 § 10.3.7.
|
||||||
if self.fragments.fragments.iter().all(|fragment| fragment.is_hypothetical()) {
|
if fragments.iter().all(Fragment::is_hypothetical) {
|
||||||
return (Au(0), Au(0))
|
return LineMetrics::new(Au(0), Au(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
let font_style = style.get_font_arc();
|
let font_style = style.get_font_arc();
|
||||||
|
@ -1141,86 +1084,73 @@ impl InlineFlow {
|
||||||
let line_height = text::line_height_from_style(style, &font_metrics);
|
let line_height = text::line_height_from_style(style, &font_metrics);
|
||||||
let inline_metrics = InlineMetrics::from_font_metrics(&font_metrics, line_height);
|
let inline_metrics = InlineMetrics::from_font_metrics(&font_metrics, line_height);
|
||||||
|
|
||||||
let mut block_size_above_baseline = Au(0);
|
let mut line_metrics = LineMetrics::new(Au(0), Au(i32::MIN));
|
||||||
let mut depth_below_baseline = Au(i32::MIN);
|
|
||||||
let mut largest_block_size_for_top_fragments = Au(0);
|
let mut largest_block_size_for_top_fragments = Au(0);
|
||||||
let mut largest_block_size_for_bottom_fragments = Au(0);
|
let mut largest_block_size_for_bottom_fragments = Au(0);
|
||||||
|
|
||||||
// We use `vertical_align::T::baseline` here because `vertical-align` must not apply to
|
// We use `vertical_align::T::baseline` here because `vertical-align` must not apply to
|
||||||
// the inside of inline blocks.
|
// the inside of inline blocks.
|
||||||
update_inline_metrics(&inline_metrics,
|
update_line_metrics_for_fragment(&mut line_metrics,
|
||||||
style.get_box().display,
|
&inline_metrics,
|
||||||
vertical_align::T::baseline,
|
style.get_box().display,
|
||||||
&mut block_size_above_baseline,
|
vertical_align::T::baseline,
|
||||||
&mut depth_below_baseline,
|
&mut largest_block_size_for_top_fragments,
|
||||||
&mut largest_block_size_for_top_fragments,
|
&mut largest_block_size_for_bottom_fragments);
|
||||||
&mut largest_block_size_for_bottom_fragments);
|
|
||||||
|
|
||||||
// According to CSS 2.1 § 10.8, `line-height` of any inline element specifies the minimal
|
// According to CSS 2.1 § 10.8, `line-height` of any inline element specifies the minimal
|
||||||
// height of line boxes within the element.
|
// height of line boxes within the element.
|
||||||
for frag in &self.fragments.fragments {
|
for inline_context in fragments.iter()
|
||||||
if let Some(ref inline_context) = frag.inline_context {
|
.filter_map(|fragment| fragment.inline_context.as_ref()) {
|
||||||
for node in &inline_context.nodes {
|
for node in &inline_context.nodes {
|
||||||
let font_style = node.style.get_font_arc();
|
let font_style = node.style.get_font_arc();
|
||||||
let font_metrics = text::font_metrics_for_style(font_context, font_style);
|
let font_metrics = text::font_metrics_for_style(font_context, font_style);
|
||||||
let line_height = text::line_height_from_style(&*node.style, &font_metrics);
|
let line_height = text::line_height_from_style(&*node.style, &font_metrics);
|
||||||
let inline_metrics = InlineMetrics::from_font_metrics(&font_metrics,
|
let inline_metrics = InlineMetrics::from_font_metrics(&font_metrics, line_height);
|
||||||
line_height);
|
|
||||||
|
|
||||||
update_inline_metrics(&inline_metrics,
|
update_line_metrics_for_fragment(&mut line_metrics,
|
||||||
node.style.get_box().display,
|
&inline_metrics,
|
||||||
node.style.get_box().vertical_align,
|
node.style.get_box().display,
|
||||||
&mut block_size_above_baseline,
|
node.style.get_box().vertical_align,
|
||||||
&mut depth_below_baseline,
|
&mut largest_block_size_for_top_fragments,
|
||||||
&mut largest_block_size_for_top_fragments,
|
&mut largest_block_size_for_bottom_fragments);
|
||||||
&mut largest_block_size_for_bottom_fragments);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
block_size_above_baseline =
|
line_metrics.space_above_baseline =
|
||||||
max(block_size_above_baseline,
|
max(line_metrics.space_above_baseline,
|
||||||
largest_block_size_for_bottom_fragments - max(depth_below_baseline, Au(0)));
|
largest_block_size_for_bottom_fragments - max(line_metrics.space_below_baseline,
|
||||||
depth_below_baseline =
|
Au(0)));
|
||||||
max(depth_below_baseline,
|
line_metrics.space_below_baseline =
|
||||||
largest_block_size_for_top_fragments - block_size_above_baseline);
|
max(line_metrics.space_below_baseline,
|
||||||
|
largest_block_size_for_top_fragments - line_metrics.space_above_baseline);
|
||||||
|
|
||||||
return (block_size_above_baseline, depth_below_baseline);
|
return line_metrics;
|
||||||
|
|
||||||
fn update_inline_metrics(inline_metrics: &InlineMetrics,
|
fn update_line_metrics_for_fragment(line_metrics: &mut LineMetrics,
|
||||||
display_value: display::T,
|
inline_metrics: &InlineMetrics,
|
||||||
vertical_align_value: vertical_align::T,
|
display_value: display::T,
|
||||||
block_size_above_baseline: &mut Au,
|
vertical_align_value: vertical_align::T,
|
||||||
depth_below_baseline: &mut Au,
|
largest_block_size_for_top_fragments: &mut Au,
|
||||||
largest_block_size_for_top_fragments: &mut Au,
|
largest_block_size_for_bottom_fragments: &mut Au) {
|
||||||
largest_block_size_for_bottom_fragments: &mut Au) {
|
|
||||||
match (display_value, vertical_align_value) {
|
match (display_value, vertical_align_value) {
|
||||||
(display::T::inline, vertical_align::T::top) |
|
(display::T::inline, vertical_align::T::top) |
|
||||||
(display::T::block, vertical_align::T::top) |
|
(display::T::block, vertical_align::T::top) |
|
||||||
(display::T::inline_block, vertical_align::T::top) if
|
(display::T::inline_block, vertical_align::T::top) if
|
||||||
inline_metrics.block_size_above_baseline >= Au(0) => {
|
inline_metrics.space_above_baseline >= Au(0) => {
|
||||||
*largest_block_size_for_top_fragments =
|
*largest_block_size_for_top_fragments = max(
|
||||||
max(*largest_block_size_for_top_fragments,
|
*largest_block_size_for_top_fragments,
|
||||||
inline_metrics.block_size_above_baseline +
|
inline_metrics.space_above_baseline + inline_metrics.space_below_baseline)
|
||||||
inline_metrics.depth_below_baseline)
|
|
||||||
}
|
}
|
||||||
(display::T::inline, vertical_align::T::bottom) |
|
(display::T::inline, vertical_align::T::bottom) |
|
||||||
(display::T::block, vertical_align::T::bottom) |
|
(display::T::block, vertical_align::T::bottom) |
|
||||||
(display::T::inline_block, vertical_align::T::bottom) if
|
(display::T::inline_block, vertical_align::T::bottom) if
|
||||||
inline_metrics.depth_below_baseline >= Au(0) => {
|
inline_metrics.space_below_baseline >= Au(0) => {
|
||||||
*largest_block_size_for_bottom_fragments =
|
*largest_block_size_for_bottom_fragments = max(
|
||||||
max(*largest_block_size_for_bottom_fragments,
|
*largest_block_size_for_bottom_fragments,
|
||||||
inline_metrics.block_size_above_baseline +
|
inline_metrics.space_above_baseline + inline_metrics.space_below_baseline)
|
||||||
inline_metrics.depth_below_baseline)
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
*block_size_above_baseline =
|
|
||||||
max(*block_size_above_baseline,
|
|
||||||
inline_metrics.block_size_above_baseline);
|
|
||||||
*depth_below_baseline = max(*depth_below_baseline,
|
|
||||||
inline_metrics.depth_below_baseline);
|
|
||||||
}
|
}
|
||||||
|
_ => *line_metrics = line_metrics.new_metrics_for_fragment(inline_metrics),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1284,7 +1214,7 @@ impl InlineFlow {
|
||||||
|
|
||||||
pub fn baseline_offset_of_last_line(&self) -> Option<Au> {
|
pub fn baseline_offset_of_last_line(&self) -> Option<Au> {
|
||||||
self.last_line_containing_real_fragments().map(|line| {
|
self.last_line_containing_real_fragments().map(|line| {
|
||||||
line.bounds.start.b + line.bounds.size.block - line.inline_metrics.depth_below_baseline
|
line.bounds.start.b + line.bounds.size.block - line.metrics.space_below_baseline
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1476,8 +1406,7 @@ impl Flow for InlineFlow {
|
||||||
// Perform line breaking.
|
// Perform line breaking.
|
||||||
let mut scanner = LineBreaker::new(self.base.floats.clone(),
|
let mut scanner = LineBreaker::new(self.base.floats.clone(),
|
||||||
indentation,
|
indentation,
|
||||||
self.minimum_block_size_above_baseline,
|
&self.minimum_line_metrics);
|
||||||
self.minimum_depth_below_baseline);
|
|
||||||
scanner.scan_for_lines(self, layout_context);
|
scanner.scan_for_lines(self, layout_context);
|
||||||
|
|
||||||
// Now, go through each line and lay out the fragments inside.
|
// Now, go through each line and lay out the fragments inside.
|
||||||
|
@ -1493,8 +1422,7 @@ impl Flow for InlineFlow {
|
||||||
// Compute the final positions in the block direction of each fragment.
|
// Compute the final positions in the block direction of each fragment.
|
||||||
InlineFlow::set_block_fragment_positions(&mut self.fragments,
|
InlineFlow::set_block_fragment_positions(&mut self.fragments,
|
||||||
line,
|
line,
|
||||||
self.minimum_block_size_above_baseline,
|
&self.minimum_line_metrics,
|
||||||
self.minimum_depth_below_baseline,
|
|
||||||
layout_context);
|
layout_context);
|
||||||
|
|
||||||
// This is used to set the block-start position of the next line in the next iteration
|
// This is used to set the block-start position of the next line in the next iteration
|
||||||
|
@ -1849,22 +1777,28 @@ fn inline_contexts_are_equal(inline_context_a: &Option<InlineFragmentContext>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Block-size above the baseline, depth below the baseline, and ascent for a fragment. See CSS 2.1
|
/// Ascent and space needed above and below the baseline for a fragment. See CSS 2.1 § 10.8.1.
|
||||||
/// § 10.8.1.
|
///
|
||||||
|
/// Descent is not included in this structure because it can be computed from the fragment's
|
||||||
|
/// border/content box and the ascent.
|
||||||
#[derive(Clone, Copy, Debug, RustcEncodable)]
|
#[derive(Clone, Copy, Debug, RustcEncodable)]
|
||||||
pub struct InlineMetrics {
|
pub struct InlineMetrics {
|
||||||
pub block_size_above_baseline: Au,
|
/// The amount of space above the baseline needed for this fragment.
|
||||||
pub depth_below_baseline: Au,
|
pub space_above_baseline: Au,
|
||||||
|
/// The amount of space below the baseline needed for this fragment.
|
||||||
|
pub space_below_baseline: Au,
|
||||||
|
/// The distance from the baseline to the top of this fragment. This can differ from
|
||||||
|
/// `block_size_above_baseline` if the fragment needs some empty space above it due to
|
||||||
|
/// line-height, etc.
|
||||||
pub ascent: Au,
|
pub ascent: Au,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InlineMetrics {
|
impl InlineMetrics {
|
||||||
/// Creates a new set of inline metrics.
|
/// Creates a new set of inline metrics.
|
||||||
pub fn new(block_size_above_baseline: Au, depth_below_baseline: Au, ascent: Au)
|
pub fn new(space_above_baseline: Au, space_below_baseline: Au, ascent: Au) -> InlineMetrics {
|
||||||
-> InlineMetrics {
|
|
||||||
InlineMetrics {
|
InlineMetrics {
|
||||||
block_size_above_baseline: block_size_above_baseline,
|
space_above_baseline: space_above_baseline,
|
||||||
depth_below_baseline: depth_below_baseline,
|
space_below_baseline: space_below_baseline,
|
||||||
ascent: ascent,
|
ascent: ascent,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1873,29 +1807,22 @@ impl InlineMetrics {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_font_metrics(font_metrics: &FontMetrics, line_height: Au) -> InlineMetrics {
|
pub fn from_font_metrics(font_metrics: &FontMetrics, line_height: Au) -> InlineMetrics {
|
||||||
let leading = line_height - (font_metrics.ascent + font_metrics.descent);
|
let leading = line_height - (font_metrics.ascent + font_metrics.descent);
|
||||||
|
|
||||||
// Calculating the half leading here and then using leading - half_leading
|
// Calculating the half leading here and then using leading - half_leading
|
||||||
// below ensure that we don't introduce any rounding accuracy issues here.
|
// below ensure that we don't introduce any rounding accuracy issues here.
|
||||||
// The invariant is that the resulting total line height must exactly
|
// The invariant is that the resulting total line height must exactly
|
||||||
// equal the requested line_height.
|
// equal the requested line_height.
|
||||||
let half_leading = leading.scale_by(0.5);
|
let half_leading = leading.scale_by(0.5);
|
||||||
InlineMetrics {
|
InlineMetrics {
|
||||||
block_size_above_baseline: font_metrics.ascent + half_leading,
|
space_above_baseline: font_metrics.ascent + half_leading,
|
||||||
depth_below_baseline: font_metrics.descent + leading - half_leading,
|
space_below_baseline: font_metrics.descent + leading - half_leading,
|
||||||
ascent: font_metrics.ascent,
|
ascent: font_metrics.ascent,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn block_size(&self) -> Au {
|
/// Returns the sum of the space needed above and below the baseline.
|
||||||
self.block_size_above_baseline + self.depth_below_baseline
|
fn space_needed(&self) -> Au {
|
||||||
}
|
self.space_above_baseline + self.space_below_baseline
|
||||||
|
|
||||||
pub fn max(&self, other: &InlineMetrics) -> InlineMetrics {
|
|
||||||
InlineMetrics {
|
|
||||||
block_size_above_baseline: max(self.block_size_above_baseline,
|
|
||||||
other.block_size_above_baseline),
|
|
||||||
depth_below_baseline: max(self.depth_below_baseline, other.depth_below_baseline),
|
|
||||||
ascent: max(self.ascent, other.ascent),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1905,31 +1832,55 @@ enum LineFlushMode {
|
||||||
Flush,
|
Flush,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LineBlockMetrics {
|
#[derive(Copy, Clone, Debug, RustcEncodable)]
|
||||||
start: Au,
|
pub struct LineMetrics {
|
||||||
size: Au,
|
pub space_above_baseline: Au,
|
||||||
size_above_baseline: Au,
|
pub space_below_baseline: Au,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LineBlockMetrics {
|
impl LineMetrics {
|
||||||
fn new(line: &Line, fragment: &Fragment, layout_context: &LayoutContext) -> LineBlockMetrics {
|
pub fn new(space_above_baseline: Au, space_below_baseline: Au) -> LineMetrics {
|
||||||
|
LineMetrics {
|
||||||
|
space_above_baseline: space_above_baseline,
|
||||||
|
space_below_baseline: space_below_baseline,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the line metrics that result from combining the line that these metrics represent
|
||||||
|
/// with a fragment with the given metrics.
|
||||||
|
fn new_metrics_for_fragment(&self, fragment_inline_metrics: &InlineMetrics) -> LineMetrics {
|
||||||
|
LineMetrics {
|
||||||
|
space_above_baseline: max(self.space_above_baseline,
|
||||||
|
fragment_inline_metrics.space_above_baseline),
|
||||||
|
space_below_baseline: max(self.space_below_baseline,
|
||||||
|
fragment_inline_metrics.space_below_baseline),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn for_line_and_fragment(line: &Line, fragment: &Fragment, layout_context: &LayoutContext)
|
||||||
|
-> LineMetrics {
|
||||||
if !fragment.is_hypothetical() {
|
if !fragment.is_hypothetical() {
|
||||||
return LineBlockMetrics {
|
let space_above_baseline = line.metrics.space_above_baseline;
|
||||||
start: line.bounds.start.b,
|
return LineMetrics {
|
||||||
size: line.bounds.size.block,
|
space_above_baseline: space_above_baseline,
|
||||||
size_above_baseline: line.inline_metrics.block_size_above_baseline,
|
space_below_baseline: line.bounds.size.block - space_above_baseline,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let hypothetical_inline_metrics = line.new_inline_metrics(fragment, layout_context);
|
let hypothetical_line_metrics = line.new_metrics_for_fragment(fragment, layout_context);
|
||||||
let hypothetical_block_size = line.new_block_size(fragment,
|
let hypothetical_block_size = line.new_block_size_for_fragment(fragment,
|
||||||
&hypothetical_inline_metrics,
|
&hypothetical_line_metrics,
|
||||||
layout_context);
|
layout_context);
|
||||||
LineBlockMetrics {
|
let hypothetical_space_above_baseline = hypothetical_line_metrics.space_above_baseline;
|
||||||
start: line.bounds.start.b,
|
LineMetrics {
|
||||||
size: hypothetical_block_size,
|
space_above_baseline: hypothetical_space_above_baseline,
|
||||||
size_above_baseline: hypothetical_inline_metrics.block_size_above_baseline,
|
space_below_baseline: hypothetical_block_size - hypothetical_space_above_baseline,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the sum of the space needed above and below the baseline.
|
||||||
|
pub fn space_needed(&self) -> Au {
|
||||||
|
self.space_above_baseline + self.space_below_baseline
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,14 +18,13 @@ use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, GeneratedC
|
||||||
use fragment::Overflow;
|
use fragment::Overflow;
|
||||||
use generated_content;
|
use generated_content;
|
||||||
use gfx::display_list::StackingContext;
|
use gfx::display_list::StackingContext;
|
||||||
use inline::InlineMetrics;
|
use inline::InlineFlow;
|
||||||
use script_layout_interface::restyle_damage::RESOLVE_GENERATED_CONTENT;
|
use script_layout_interface::restyle_damage::RESOLVE_GENERATED_CONTENT;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use style::computed_values::{list_style_type, position};
|
use style::computed_values::{list_style_type, position};
|
||||||
use style::context::SharedStyleContext;
|
use style::context::SharedStyleContext;
|
||||||
use style::logical_geometry::LogicalSize;
|
use style::logical_geometry::LogicalSize;
|
||||||
use style::properties::ServoComputedValues;
|
use style::properties::ServoComputedValues;
|
||||||
use text;
|
|
||||||
|
|
||||||
/// A block with the CSS `display` property equal to `list-item`.
|
/// A block with the CSS `display` property equal to `list-item`.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -105,21 +104,20 @@ impl Flow for ListItemFlow {
|
||||||
fn assign_block_size<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
|
fn assign_block_size<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
|
||||||
self.block_flow.assign_block_size(layout_context);
|
self.block_flow.assign_block_size(layout_context);
|
||||||
|
|
||||||
|
// FIXME(pcwalton): Do this during flow construction, like `InlineFlow` does?
|
||||||
|
let marker_line_metrics =
|
||||||
|
InlineFlow::minimum_line_metrics_for_fragments(&self.marker_fragments,
|
||||||
|
&mut layout_context.font_context(),
|
||||||
|
&*self.block_flow.fragment.style);
|
||||||
for marker in &mut self.marker_fragments {
|
for marker in &mut self.marker_fragments {
|
||||||
let containing_block_block_size =
|
let containing_block_block_size =
|
||||||
self.block_flow.base.block_container_explicit_block_size;
|
self.block_flow.base.block_container_explicit_block_size;
|
||||||
marker.assign_replaced_block_size_if_necessary(containing_block_block_size);
|
marker.assign_replaced_block_size_if_necessary(containing_block_block_size);
|
||||||
|
let marker_inline_metrics = marker.aligned_inline_metrics(layout_context,
|
||||||
let font_metrics =
|
&marker_line_metrics,
|
||||||
text::font_metrics_for_style(&mut layout_context.font_context(),
|
Some(&marker_line_metrics));
|
||||||
marker.style.get_font_arc());
|
marker.border_box.start.b = marker_line_metrics.space_above_baseline -
|
||||||
let line_height = text::line_height_from_style(&*marker.style, &font_metrics);
|
|
||||||
let item_inline_metrics = InlineMetrics::from_font_metrics(&font_metrics, line_height);
|
|
||||||
let marker_inline_metrics = marker.inline_metrics(layout_context);
|
|
||||||
marker.border_box.start.b = item_inline_metrics.block_size_above_baseline -
|
|
||||||
marker_inline_metrics.ascent;
|
marker_inline_metrics.ascent;
|
||||||
marker.border_box.size.block = marker_inline_metrics.ascent +
|
|
||||||
marker_inline_metrics.depth_below_baseline;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,6 @@ input[type="reset"] {
|
||||||
border-right: solid 1px #999999;
|
border-right: solid 1px #999999;
|
||||||
border-bottom: solid 1px #999999;
|
border-bottom: solid 1px #999999;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
vertical-align: middle;
|
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче