mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
Auto merge of #10691 - pcwalton:float-fixes, r=mbrubeck
layout: Fix float speculation with percentage inline sizes, rewrite vertical alignment, fix inline block ascent/descent computation, and fix absolute inline-block hypothetical boxes. <!-- Reviewable:start --> This change is [<img src="https://reviewable.io/review_button.svg" height="35" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/10691) <!-- Reviewable:end -->
This commit is contained in:
commit
35ba29355f
32 changed files with 540 additions and 405 deletions
|
@ -272,7 +272,14 @@ impl<'a> TextRun {
|
|||
})
|
||||
}
|
||||
|
||||
/// Returns the index of the first glyph run containing the given byte index.
|
||||
pub fn minimum_splittable_inline_size(&self, range: &Range<ByteIndex>) -> Au {
|
||||
match self.natural_word_slices_in_range(range).next() {
|
||||
None => Au(0),
|
||||
Some(slice) => self.advance_for_range(&slice.range),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the index of the first glyph run containing the given character index.
|
||||
fn index_of_first_glyph_run_containing(&self, index: ByteIndex) -> Option<usize> {
|
||||
let self_ptr = self as *const TextRun;
|
||||
INDEX_OF_FIRST_GLYPH_RUN_CACHE.with(|index_of_first_glyph_run_cache| {
|
||||
|
|
|
@ -33,7 +33,8 @@ use display_list_builder::BlockFlowDisplayListBuilding;
|
|||
use display_list_builder::{BorderPaintingMode, DisplayListBuildState, FragmentDisplayListBuilding};
|
||||
use euclid::{Point2D, Rect, Size2D};
|
||||
use floats::{ClearType, FloatKind, Floats, PlacementInfo};
|
||||
use flow::{BLOCK_POSITION_IS_STATIC, CLEARS_LEFT, CLEARS_RIGHT, INLINE_POSITION_IS_STATIC};
|
||||
use flow::{BLOCK_POSITION_IS_STATIC, CLEARS_LEFT, CLEARS_RIGHT};
|
||||
use flow::{CONTAINS_TEXT_OR_REPLACED_FRAGMENTS, INLINE_POSITION_IS_STATIC};
|
||||
use flow::{IS_ABSOLUTELY_POSITIONED};
|
||||
use flow::{ImmutableFlowUtils, LateAbsolutePositionInfo, MutableFlowUtils, OpaqueFlow};
|
||||
use flow::{NEEDS_LAYER, PostorderFlowTraversal, PreorderFlowTraversal, FragmentationContext};
|
||||
|
@ -1473,61 +1474,89 @@ impl BlockFlow {
|
|||
}
|
||||
}
|
||||
|
||||
/// Computes intrinsic widths for a block.
|
||||
/// Computes intrinsic inline sizes for a block.
|
||||
pub fn bubble_inline_sizes_for_block(&mut self, consult_children: bool) {
|
||||
let _scope = layout_debug_scope!("block::bubble_inline_sizes {:x}", self.base.debug_id());
|
||||
|
||||
let flags = self.base.flags;
|
||||
|
||||
// Find the maximum inline-size from children.
|
||||
let mut computation = self.fragment.compute_intrinsic_inline_sizes();
|
||||
let (mut left_float_width, mut right_float_width) = (Au(0), Au(0));
|
||||
let (mut left_float_width_accumulator, mut right_float_width_accumulator) = (Au(0), Au(0));
|
||||
for kid in self.base.child_iter_mut() {
|
||||
let is_absolutely_positioned =
|
||||
flow::base(kid).flags.contains(IS_ABSOLUTELY_POSITIONED);
|
||||
let child_base = flow::mut_base(kid);
|
||||
let float_kind = child_base.flags.float_kind();
|
||||
if !is_absolutely_positioned && consult_children {
|
||||
computation.content_intrinsic_sizes.minimum_inline_size =
|
||||
max(computation.content_intrinsic_sizes.minimum_inline_size,
|
||||
child_base.intrinsic_inline_sizes.minimum_inline_size);
|
||||
|
||||
if child_base.flags.contains(CLEARS_LEFT) {
|
||||
left_float_width = max(left_float_width, left_float_width_accumulator);
|
||||
left_float_width_accumulator = Au(0)
|
||||
}
|
||||
if child_base.flags.contains(CLEARS_RIGHT) {
|
||||
right_float_width = max(right_float_width, right_float_width_accumulator);
|
||||
right_float_width_accumulator = Au(0)
|
||||
}
|
||||
|
||||
match float_kind {
|
||||
float::T::none => {
|
||||
computation.content_intrinsic_sizes.preferred_inline_size =
|
||||
max(computation.content_intrinsic_sizes.preferred_inline_size,
|
||||
child_base.intrinsic_inline_sizes.preferred_inline_size);
|
||||
}
|
||||
float::T::left => {
|
||||
left_float_width_accumulator = left_float_width_accumulator +
|
||||
child_base.intrinsic_inline_sizes.preferred_inline_size;
|
||||
}
|
||||
float::T::right => {
|
||||
right_float_width_accumulator = right_float_width_accumulator +
|
||||
child_base.intrinsic_inline_sizes.preferred_inline_size;
|
||||
}
|
||||
let mut flags = self.base.flags;
|
||||
if self.definitely_has_zero_block_size() {
|
||||
// This is kind of a hack for Acid2. But it's a harmless one, because (a) this behavior
|
||||
// is unspecified; (b) it matches the behavior one would intuitively expect, since
|
||||
// floats don't flow around blocks that take up no space in the block direction.
|
||||
flags.remove(CONTAINS_TEXT_OR_REPLACED_FRAGMENTS);
|
||||
} else if self.fragment.is_text_or_replaced() {
|
||||
flags.insert(CONTAINS_TEXT_OR_REPLACED_FRAGMENTS);
|
||||
} else {
|
||||
flags.remove(CONTAINS_TEXT_OR_REPLACED_FRAGMENTS);
|
||||
for kid in self.base.children.iter() {
|
||||
if flow::base(kid).flags.contains(CONTAINS_TEXT_OR_REPLACED_FRAGMENTS) {
|
||||
flags.insert(CONTAINS_TEXT_OR_REPLACED_FRAGMENTS);
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the maximum inline-size from children.
|
||||
//
|
||||
// See: https://lists.w3.org/Archives/Public/www-style/2014Nov/0085.html
|
||||
//
|
||||
// FIXME(pcwalton): This doesn't exactly follow that algorithm at the moment.
|
||||
// FIXME(pcwalton): This should consider all float descendants, not just children.
|
||||
// FIXME(pcwalton): This is not well-spec'd; INTRINSIC specifies to do this, but CSS-SIZING
|
||||
// says not to. In practice, Gecko and WebKit both do this.
|
||||
let mut computation = self.fragment.compute_intrinsic_inline_sizes();
|
||||
let (mut left_float_width, mut right_float_width) = (Au(0), Au(0));
|
||||
let (mut left_float_width_accumulator, mut right_float_width_accumulator) = (Au(0), Au(0));
|
||||
let mut preferred_inline_size_of_children_without_text_or_replaced_fragments = Au(0);
|
||||
for kid in self.base.child_iter_mut() {
|
||||
if flow::base(kid).flags.contains(IS_ABSOLUTELY_POSITIONED) || !consult_children {
|
||||
continue
|
||||
}
|
||||
|
||||
let child_base = flow::mut_base(kid);
|
||||
let float_kind = child_base.flags.float_kind();
|
||||
computation.content_intrinsic_sizes.minimum_inline_size =
|
||||
max(computation.content_intrinsic_sizes.minimum_inline_size,
|
||||
child_base.intrinsic_inline_sizes.minimum_inline_size);
|
||||
|
||||
if child_base.flags.contains(CLEARS_LEFT) {
|
||||
left_float_width = max(left_float_width, left_float_width_accumulator);
|
||||
left_float_width_accumulator = Au(0)
|
||||
}
|
||||
if child_base.flags.contains(CLEARS_RIGHT) {
|
||||
right_float_width = max(right_float_width, right_float_width_accumulator);
|
||||
right_float_width_accumulator = Au(0)
|
||||
}
|
||||
|
||||
match (float_kind, child_base.flags.contains(CONTAINS_TEXT_OR_REPLACED_FRAGMENTS)) {
|
||||
(float::T::none, true) => {
|
||||
computation.content_intrinsic_sizes.preferred_inline_size =
|
||||
max(computation.content_intrinsic_sizes.preferred_inline_size,
|
||||
child_base.intrinsic_inline_sizes.preferred_inline_size);
|
||||
}
|
||||
(float::T::none, false) => {
|
||||
preferred_inline_size_of_children_without_text_or_replaced_fragments = max(
|
||||
preferred_inline_size_of_children_without_text_or_replaced_fragments,
|
||||
child_base.intrinsic_inline_sizes.preferred_inline_size)
|
||||
}
|
||||
(float::T::left, _) => {
|
||||
left_float_width_accumulator = left_float_width_accumulator +
|
||||
child_base.intrinsic_inline_sizes.preferred_inline_size;
|
||||
}
|
||||
(float::T::right, _) => {
|
||||
right_float_width_accumulator = right_float_width_accumulator +
|
||||
child_base.intrinsic_inline_sizes.preferred_inline_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
left_float_width = max(left_float_width, left_float_width_accumulator);
|
||||
right_float_width = max(right_float_width, right_float_width_accumulator);
|
||||
|
||||
computation.content_intrinsic_sizes.preferred_inline_size =
|
||||
computation.content_intrinsic_sizes.preferred_inline_size + left_float_width +
|
||||
right_float_width;
|
||||
computation.content_intrinsic_sizes.preferred_inline_size =
|
||||
max(computation.content_intrinsic_sizes.preferred_inline_size,
|
||||
left_float_width + right_float_width);
|
||||
preferred_inline_size_of_children_without_text_or_replaced_fragments);
|
||||
|
||||
self.base.intrinsic_inline_sizes = computation.finish();
|
||||
self.base.flags = flags
|
||||
|
@ -1630,6 +1659,18 @@ impl BlockFlow {
|
|||
FormattingContextType::None | FormattingContextType::Other => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn definitely_has_zero_block_size(&self) -> bool {
|
||||
if !self.fragment.style.content_block_size().is_definitely_zero() {
|
||||
return false
|
||||
}
|
||||
let border_width = self.fragment.border_width();
|
||||
if border_width.block_start != Au(0) || border_width.block_end != Au(0) {
|
||||
return false
|
||||
}
|
||||
let padding = self.fragment.style.logical_padding();
|
||||
padding.block_start.is_definitely_zero() && padding.block_end.is_definitely_zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl Flow for BlockFlow {
|
||||
|
|
|
@ -686,7 +686,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
|||
/// FIXME(pcwalton): It is not clear to me that there isn't a cleaner way to handle
|
||||
/// `<textarea>`.
|
||||
fn build_flow_for_block_like(&mut self, flow: FlowRef, node: &ConcreteThreadSafeLayoutNode)
|
||||
-> ConstructionResult {
|
||||
-> ConstructionResult {
|
||||
let mut initial_fragments = IntermediateInlineFragments::new();
|
||||
let node_is_input_or_text_area =
|
||||
node.type_id() == Some(NodeTypeId::Element(ElementTypeId::HTMLElement(
|
||||
|
@ -1023,7 +1023,8 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
|
|||
|
||||
/// This is an annoying case, because the computed `display` value is `block`, but the
|
||||
/// hypothetical box is inline.
|
||||
fn build_fragment_for_absolutely_positioned_inline(&mut self, node: &ConcreteThreadSafeLayoutNode)
|
||||
fn build_fragment_for_absolutely_positioned_inline(&mut self,
|
||||
node: &ConcreteThreadSafeLayoutNode)
|
||||
-> ConstructionResult {
|
||||
let block_flow_result = self.build_flow_for_block(node, None);
|
||||
let (block_flow, abs_descendants) = match block_flow_result {
|
||||
|
@ -1519,11 +1520,10 @@ impl<'a, ConcreteThreadSafeLayoutNode> PostorderNodeMutTraversal<ConcreteThreadS
|
|||
}
|
||||
Some(NodeTypeId::Element(_)) => {
|
||||
let style = node.style(self.style_context());
|
||||
let munged_display = if style.get_box()._servo_display_for_hypothetical_box ==
|
||||
display::T::inline {
|
||||
display::T::inline
|
||||
} else {
|
||||
style.get_box().display
|
||||
let original_display = style.get_box()._servo_display_for_hypothetical_box;
|
||||
let munged_display = match original_display {
|
||||
display::T::inline | display::T::inline_block => original_display,
|
||||
_ => style.get_box().display,
|
||||
};
|
||||
(munged_display, style.get_box().float, style.get_box().position)
|
||||
}
|
||||
|
@ -1577,7 +1577,8 @@ impl<'a, ConcreteThreadSafeLayoutNode> PostorderNodeMutTraversal<ConcreteThreadS
|
|||
|
||||
// Inline items that are absolutely-positioned contribute inline fragment construction
|
||||
// results with a hypothetical fragment.
|
||||
(display::T::inline, _, position::T::absolute) => {
|
||||
(display::T::inline, _, position::T::absolute) |
|
||||
(display::T::inline_block, _, position::T::absolute) => {
|
||||
let construction_result =
|
||||
self.build_fragment_for_absolutely_positioned_inline(node);
|
||||
self.set_flow_construction_result(node, construction_result)
|
||||
|
|
|
@ -11,6 +11,7 @@ use std::fmt;
|
|||
use std::i32;
|
||||
use style::computed_values::float;
|
||||
use style::logical_geometry::{LogicalRect, LogicalSize, WritingMode};
|
||||
use style::values::computed::LengthOrPercentageOrAuto;
|
||||
|
||||
/// The kind of float: left or right.
|
||||
#[derive(Clone, RustcEncodable, Debug, Copy)]
|
||||
|
@ -78,7 +79,11 @@ impl FloatList {
|
|||
|
||||
impl fmt::Debug for FloatList {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "max_block_start={:?} floats={}", self.max_block_start, self.floats.len())
|
||||
try!(write!(f, "max_block_start={:?} floats={}", self.max_block_start, self.floats.len()));
|
||||
for float in self.floats.iter() {
|
||||
try!(write!(f, " {:?}", float));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -490,14 +495,30 @@ impl SpeculatedFloatPlacement {
|
|||
}
|
||||
|
||||
let base_flow = flow::base(flow);
|
||||
if !base_flow.flags.is_float() {
|
||||
return
|
||||
}
|
||||
|
||||
let mut float_inline_size = base_flow.intrinsic_inline_sizes.preferred_inline_size;
|
||||
if float_inline_size == Au(0) {
|
||||
if flow.is_block_like() {
|
||||
// Hack: If the size of the float is a percentage, then there's no way we can guess
|
||||
// at its size now. So just pick an arbitrary nonzero value (in this case, 1px) so
|
||||
// that the layout traversal logic will know that objects later in the document
|
||||
// might flow around this float.
|
||||
if let LengthOrPercentageOrAuto::Percentage(percentage) =
|
||||
flow.as_block().fragment.style.content_inline_size() {
|
||||
if percentage > 0.0 {
|
||||
float_inline_size = Au::from_px(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match base_flow.flags.float_kind() {
|
||||
float::T::none => {}
|
||||
float::T::left => {
|
||||
self.left = self.left + base_flow.intrinsic_inline_sizes.preferred_inline_size
|
||||
}
|
||||
float::T::right => {
|
||||
self.right = self.right + base_flow.intrinsic_inline_sizes.preferred_inline_size
|
||||
}
|
||||
float::T::left => self.left = self.left + float_inline_size,
|
||||
float::T::right => self.right = self.right + float_inline_size,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -512,6 +512,8 @@ pub trait ImmutableFlowUtils {
|
|||
/// Returns true if floats might flow through this flow, as determined by the float placement
|
||||
/// speculation pass.
|
||||
fn floats_might_flow_through(self) -> bool;
|
||||
|
||||
fn baseline_offset_of_last_line_box_in_flow(self) -> Option<Au>;
|
||||
}
|
||||
|
||||
pub trait MutableFlowUtils {
|
||||
|
@ -634,45 +636,48 @@ bitflags! {
|
|||
#[doc = "Whether this flow must have its own layer. Even if this flag is not set, it might"]
|
||||
#[doc = "get its own layer if it's deemed to be likely to overlap flows with their own"]
|
||||
#[doc = "layer."]
|
||||
const NEEDS_LAYER = 0b0000_0000_0000_0010_0000,
|
||||
const NEEDS_LAYER = 0b0000_0000_0000_0000_0010_0000,
|
||||
#[doc = "Whether this flow is absolutely positioned. This is checked all over layout, so a"]
|
||||
#[doc = "virtual call is too expensive."]
|
||||
const IS_ABSOLUTELY_POSITIONED = 0b0000_0000_0000_0100_0000,
|
||||
const IS_ABSOLUTELY_POSITIONED = 0b0000_0000_0000_0000_0100_0000,
|
||||
#[doc = "Whether this flow clears to the left. This is checked all over layout, so a"]
|
||||
#[doc = "virtual call is too expensive."]
|
||||
const CLEARS_LEFT = 0b0000_0000_0000_1000_0000,
|
||||
const CLEARS_LEFT = 0b0000_0000_0000_0000_1000_0000,
|
||||
#[doc = "Whether this flow clears to the right. This is checked all over layout, so a"]
|
||||
#[doc = "virtual call is too expensive."]
|
||||
const CLEARS_RIGHT = 0b0000_0000_0001_0000_0000,
|
||||
const CLEARS_RIGHT = 0b0000_0000_0000_0001_0000_0000,
|
||||
#[doc = "Whether this flow is left-floated. This is checked all over layout, so a"]
|
||||
#[doc = "virtual call is too expensive."]
|
||||
const FLOATS_LEFT = 0b0000_0000_0010_0000_0000,
|
||||
const FLOATS_LEFT = 0b0000_0000_0000_0010_0000_0000,
|
||||
#[doc = "Whether this flow is right-floated. This is checked all over layout, so a"]
|
||||
#[doc = "virtual call is too expensive."]
|
||||
const FLOATS_RIGHT = 0b0000_0000_0100_0000_0000,
|
||||
const FLOATS_RIGHT = 0b0000_0000_0000_0100_0000_0000,
|
||||
#[doc = "Text alignment. \
|
||||
|
||||
NB: If you update this, update `TEXT_ALIGN_SHIFT` below."]
|
||||
const TEXT_ALIGN = 0b0000_0111_1000_0000_0000,
|
||||
const TEXT_ALIGN = 0b0000_0000_0111_1000_0000_0000,
|
||||
#[doc = "Whether this flow has a fragment with `counter-reset` or `counter-increment` \
|
||||
styles."]
|
||||
const AFFECTS_COUNTERS = 0b0000_1000_0000_0000_0000,
|
||||
const AFFECTS_COUNTERS = 0b0000_0000_1000_0000_0000_0000,
|
||||
#[doc = "Whether this flow's descendants have fragments that affect `counter-reset` or \
|
||||
`counter-increment` styles."]
|
||||
const HAS_COUNTER_AFFECTING_CHILDREN = 0b0001_0000_0000_0000_0000,
|
||||
const HAS_COUNTER_AFFECTING_CHILDREN = 0b0000_0001_0000_0000_0000_0000,
|
||||
#[doc = "Whether this flow behaves as though it had `position: static` for the purposes \
|
||||
of positioning in the inline direction. This is set for flows with `position: \
|
||||
static` and `position: relative` as well as absolutely-positioned flows with \
|
||||
unconstrained positions in the inline direction."]
|
||||
const INLINE_POSITION_IS_STATIC = 0b0010_0000_0000_0000_0000,
|
||||
const INLINE_POSITION_IS_STATIC = 0b0000_0010_0000_0000_0000_0000,
|
||||
#[doc = "Whether this flow behaves as though it had `position: static` for the purposes \
|
||||
of positioning in the block direction. This is set for flows with `position: \
|
||||
static` and `position: relative` as well as absolutely-positioned flows with \
|
||||
unconstrained positions in the block direction."]
|
||||
const BLOCK_POSITION_IS_STATIC = 0b0100_0000_0000_0000_0000,
|
||||
const BLOCK_POSITION_IS_STATIC = 0b0000_0100_0000_0000_0000_0000,
|
||||
|
||||
/// Whether any ancestor is a fragmentation container
|
||||
const CAN_BE_FRAGMENTED = 0b1000_0000_0000_0000_0000,
|
||||
const CAN_BE_FRAGMENTED = 0b0000_1000_0000_0000_0000_0000,
|
||||
|
||||
/// Whether this flow contains any text and/or replaced fragments.
|
||||
const CONTAINS_TEXT_OR_REPLACED_FRAGMENTS = 0b0001_0000_0000_0000_0000_0000,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -975,9 +980,11 @@ impl fmt::Debug for BaseFlow {
|
|||
};
|
||||
|
||||
write!(f,
|
||||
"sc={:?} pos={:?}, floatspec-in={:?}, floatspec-out={:?}, overflow={:?}{}{}{}",
|
||||
"sc={:?} pos={:?}, {}{} floatspec-in={:?}, floatspec-out={:?}, overflow={:?}{}{}{}",
|
||||
self.stacking_context_id,
|
||||
self.position,
|
||||
if self.flags.contains(FLOATS_LEFT) { "FL" } else { "" },
|
||||
if self.flags.contains(FLOATS_RIGHT) { "FR" } else { "" },
|
||||
self.speculated_float_placement_in,
|
||||
self.speculated_float_placement_out,
|
||||
self.overflow,
|
||||
|
@ -1387,6 +1394,21 @@ impl<'a> ImmutableFlowUtils for &'a Flow {
|
|||
}
|
||||
self.as_block().formatting_context_type() == FormattingContextType::None
|
||||
}
|
||||
|
||||
fn baseline_offset_of_last_line_box_in_flow(self) -> Option<Au> {
|
||||
for kid in base(self).children.iter().rev() {
|
||||
if kid.is_inline_flow() {
|
||||
return kid.as_inline().baseline_offset_of_last_line()
|
||||
}
|
||||
if kid.is_block_like() &&
|
||||
kid.as_block().formatting_context_type() == FormattingContextType::None {
|
||||
if let Some(baseline_offset) = kid.baseline_offset_of_last_line_box_in_flow() {
|
||||
return Some(base(kid).position.start.b + baseline_offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MutableFlowUtils for &'a mut Flow {
|
||||
|
|
|
@ -111,6 +111,12 @@ impl<'a> Iterator for FlowListIterator<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> DoubleEndedIterator for FlowListIterator<'a> {
|
||||
fn next_back(&mut self) -> Option<&'a Flow> {
|
||||
self.it.next_back().map(|x| &**x)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for MutFlowListIterator<'a> {
|
||||
type Item = &'a mut Flow;
|
||||
#[inline]
|
||||
|
|
|
@ -11,7 +11,7 @@ use canvas_traits::CanvasMsg;
|
|||
use context::LayoutContext;
|
||||
use euclid::{Point2D, Rect, Size2D};
|
||||
use floats::ClearType;
|
||||
use flow::{self, Flow};
|
||||
use flow::{self, Flow, ImmutableFlowUtils};
|
||||
use flow_ref::{self, FlowRef};
|
||||
use gfx;
|
||||
use gfx::display_list::{BLUR_INFLATION_FACTOR, FragmentType, OpaqueNode, StackingContextId};
|
||||
|
@ -38,7 +38,7 @@ use std::sync::{Arc, Mutex};
|
|||
use style::computed_values::content::ContentItem;
|
||||
use style::computed_values::{border_collapse, clear, display, mix_blend_mode, overflow_wrap};
|
||||
use style::computed_values::{overflow_x, position, text_decoration, transform_style};
|
||||
use style::computed_values::{white_space, word_break, z_index};
|
||||
use style::computed_values::{vertical_align, white_space, word_break, z_index};
|
||||
use style::dom::TRestyleDamage;
|
||||
use style::logical_geometry::{LogicalMargin, LogicalRect, LogicalSize, WritingMode};
|
||||
use style::properties::{ComputedValues, ServoComputedValues};
|
||||
|
@ -1031,7 +1031,8 @@ impl Fragment {
|
|||
let mut specified = Au(0);
|
||||
|
||||
if flags.contains(INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED) {
|
||||
specified = MaybeAuto::from_style(style.content_inline_size(), Au(0)).specified_or_zero();
|
||||
specified = MaybeAuto::from_style(style.content_inline_size(),
|
||||
Au(0)).specified_or_zero();
|
||||
specified = max(model::specified(style.min_inline_size(), Au(0)), specified);
|
||||
if let Some(max) = model::specified_or_none(style.max_inline_size(), Au(0)) {
|
||||
specified = min(specified, max)
|
||||
|
@ -1070,14 +1071,15 @@ 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);
|
||||
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
|
||||
/// can be expensive to compute, so if possible use the `border_padding` field instead.
|
||||
#[inline]
|
||||
fn border_width(&self) -> LogicalMargin<Au> {
|
||||
pub fn border_width(&self) -> LogicalMargin<Au> {
|
||||
let style_border_width = match self.specific {
|
||||
SpecificFragmentInfo::ScannedText(_) |
|
||||
SpecificFragmentInfo::InlineBlock(_) => LogicalMargin::zero(self.style.writing_mode),
|
||||
|
@ -1471,6 +1473,17 @@ impl Fragment {
|
|||
result
|
||||
}
|
||||
|
||||
/// Returns the narrowest inline-size that the first splittable part of this fragment could
|
||||
/// possibly be split to. (In most cases, this returns the inline-size of the first word in
|
||||
/// this fragment.)
|
||||
pub fn minimum_splittable_inline_size(&self) -> Au {
|
||||
match self.specific {
|
||||
SpecificFragmentInfo::ScannedText(ref text) => {
|
||||
text.run.minimum_splittable_inline_size(&text.range)
|
||||
}
|
||||
_ => Au(0),
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO: What exactly does this function return? Why is it Au(0) for
|
||||
/// `SpecificFragmentInfo::Generic`?
|
||||
|
@ -1972,14 +1985,19 @@ impl Fragment {
|
|||
}
|
||||
SpecificFragmentInfo::InlineBlock(ref info) => {
|
||||
// See CSS 2.1 § 10.8.1.
|
||||
let block_flow = info.flow_ref.as_block();
|
||||
let font_style = self.style.get_font_arc();
|
||||
let font_metrics = text::font_metrics_for_style(&mut layout_context.font_context(),
|
||||
font_style);
|
||||
InlineMetrics::from_block_height(&font_metrics,
|
||||
block_flow.base.position.size.block,
|
||||
block_flow.fragment.margin.block_start,
|
||||
block_flow.fragment.margin.block_end)
|
||||
let flow = &info.flow_ref;
|
||||
let block_flow = flow.as_block();
|
||||
let baseline_offset = match flow.baseline_offset_of_last_line_box_in_flow() {
|
||||
Some(baseline_offset) => baseline_offset,
|
||||
None => block_flow.fragment.border_box.size.block,
|
||||
};
|
||||
let start_margin = block_flow.fragment.margin.block_start;
|
||||
let end_margin = block_flow.fragment.margin.block_end;
|
||||
let depth_below_baseline = flow::base(&**flow).position.size.block -
|
||||
baseline_offset + end_margin;
|
||||
InlineMetrics::new(baseline_offset + start_margin,
|
||||
depth_below_baseline,
|
||||
baseline_offset)
|
||||
}
|
||||
SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
|
||||
SpecificFragmentInfo::InlineAbsolute(_) => {
|
||||
|
@ -2541,6 +2559,46 @@ impl Fragment {
|
|||
pub fn layer_id_for_overflow_scroll(&self) -> LayerId {
|
||||
LayerId::new_of_type(LayerType::OverflowScroll, self.node.id() as usize)
|
||||
}
|
||||
|
||||
/// Returns true if any of the inline styles associated with this fragment have
|
||||
/// `vertical-align` set to `top` or `bottom`.
|
||||
pub fn is_vertically_aligned_to_top_or_bottom(&self) -> bool {
|
||||
match self.style.get_box().vertical_align {
|
||||
vertical_align::T::top | vertical_align::T::bottom => return true,
|
||||
_ => {}
|
||||
}
|
||||
if let Some(ref inline_context) = self.inline_context {
|
||||
for node in &inline_context.nodes {
|
||||
match node.style.get_box().vertical_align {
|
||||
vertical_align::T::top | vertical_align::T::bottom => return true,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn is_text_or_replaced(&self) -> bool {
|
||||
match self.specific {
|
||||
SpecificFragmentInfo::Generic |
|
||||
SpecificFragmentInfo::InlineAbsolute(_) |
|
||||
SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
|
||||
SpecificFragmentInfo::InlineBlock(_) |
|
||||
SpecificFragmentInfo::Multicol |
|
||||
SpecificFragmentInfo::MulticolColumn |
|
||||
SpecificFragmentInfo::Table |
|
||||
SpecificFragmentInfo::TableCell |
|
||||
SpecificFragmentInfo::TableColumn(_) |
|
||||
SpecificFragmentInfo::TableRow |
|
||||
SpecificFragmentInfo::TableWrapper => false,
|
||||
SpecificFragmentInfo::Canvas(_) |
|
||||
SpecificFragmentInfo::GeneratedContent(_) |
|
||||
SpecificFragmentInfo::Iframe(_) |
|
||||
SpecificFragmentInfo::Image(_) |
|
||||
SpecificFragmentInfo::ScannedText(_) |
|
||||
SpecificFragmentInfo::UnscannedText(_) => true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Fragment {
|
||||
|
|
|
@ -11,7 +11,8 @@ use display_list_builder::DisplayListBuildState;
|
|||
use display_list_builder::{FragmentDisplayListBuilding, InlineFlowDisplayListBuilding};
|
||||
use euclid::{Point2D, Size2D};
|
||||
use floats::{FloatKind, Floats, PlacementInfo};
|
||||
use flow::{EarlyAbsolutePositionInfo, MutableFlowUtils, OpaqueFlow};
|
||||
use flow::{CONTAINS_TEXT_OR_REPLACED_FRAGMENTS, EarlyAbsolutePositionInfo, MutableFlowUtils};
|
||||
use flow::{OpaqueFlow};
|
||||
use flow::{self, BaseFlow, Flow, FlowClass, ForceNonfloatedFlag, IS_ABSOLUTELY_POSITIONED};
|
||||
use flow_ref;
|
||||
use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, Overflow};
|
||||
|
@ -26,7 +27,7 @@ use range::{Range, RangeIndex};
|
|||
use std::cmp::max;
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, isize, mem};
|
||||
use std::{fmt, i32, isize, mem};
|
||||
use style::computed_values::{display, overflow_x, position, text_align, text_justify};
|
||||
use style::computed_values::{text_overflow, vertical_align, white_space};
|
||||
use style::logical_geometry::{LogicalRect, LogicalSize, WritingMode};
|
||||
|
@ -418,13 +419,23 @@ impl LineBreaker {
|
|||
// block-size. This might not be the case with some weird text fonts.
|
||||
fn new_inline_metrics_for_line(&self, new_fragment: &Fragment, layout_context: &LayoutContext)
|
||||
-> InlineMetrics {
|
||||
self.pending_line.inline_metrics.max(&new_fragment.inline_metrics(layout_context))
|
||||
if !new_fragment.is_vertically_aligned_to_top_or_bottom() {
|
||||
let fragment_inline_metrics = new_fragment.inline_metrics(layout_context);
|
||||
self.pending_line.inline_metrics.max(&fragment_inline_metrics)
|
||||
} else {
|
||||
self.pending_line.inline_metrics
|
||||
}
|
||||
}
|
||||
|
||||
fn new_block_size_for_line(&self, new_fragment: &Fragment, layout_context: &LayoutContext)
|
||||
-> Au {
|
||||
max(self.pending_line.bounds.size.block,
|
||||
self.new_inline_metrics_for_line(new_fragment, layout_context).block_size())
|
||||
let new_block_size = if new_fragment.is_vertically_aligned_to_top_or_bottom() {
|
||||
max(new_fragment.inline_metrics(layout_context).block_size(),
|
||||
self.minimum_block_size_above_baseline + self.minimum_depth_below_baseline)
|
||||
} else {
|
||||
self.new_inline_metrics_for_line(new_fragment, layout_context).block_size()
|
||||
};
|
||||
max(self.pending_line.bounds.size.block, new_block_size)
|
||||
}
|
||||
|
||||
/// Computes the position of a line that has only the provided fragment. Returns the bounding
|
||||
|
@ -444,7 +455,7 @@ impl LineBreaker {
|
|||
// Initially, pretend a splittable fragment has zero inline-size. We will move it later if
|
||||
// it has nonzero inline-size and that causes problems.
|
||||
let placement_inline_size = if first_fragment.can_split() {
|
||||
Au(0)
|
||||
first_fragment.minimum_splittable_inline_size()
|
||||
} else {
|
||||
first_fragment.margin_box_inline_size() + self.indentation_for_pending_fragment()
|
||||
};
|
||||
|
@ -855,105 +866,6 @@ impl InlineFlow {
|
|||
flow
|
||||
}
|
||||
|
||||
/// Returns the distance from the baseline for the logical block-start inline-start corner of
|
||||
/// this fragment, taking into account the value of the CSS `vertical-align` property.
|
||||
/// Negative values mean "toward the logical block-start" and positive values mean "toward the
|
||||
/// logical block-end".
|
||||
///
|
||||
/// The extra boolean is set if and only if `largest_block_size_for_top_fragments` and/or
|
||||
/// `largest_block_size_for_bottom_fragments` were updated. That is, if the box has a `top` or
|
||||
/// `bottom` value for `vertical-align`, true is returned.
|
||||
fn distance_from_baseline(fragment: &Fragment,
|
||||
ascent: Au,
|
||||
parent_text_block_start: Au,
|
||||
parent_text_block_end: Au,
|
||||
block_size_above_baseline: &mut Au,
|
||||
depth_below_baseline: &mut Au,
|
||||
largest_block_size_for_top_fragments: &mut Au,
|
||||
largest_block_size_for_bottom_fragments: &mut Au,
|
||||
layout_context: &LayoutContext)
|
||||
-> (Au, bool) {
|
||||
let (mut offset_from_baseline, mut largest_size_updated) = (Au(0), false);
|
||||
for style in fragment.inline_styles() {
|
||||
// Ignore `vertical-align` values for table cells.
|
||||
let box_style = style.get_box();
|
||||
match box_style.display {
|
||||
display::T::inline | display::T::block | display::T::inline_block => {}
|
||||
_ => continue,
|
||||
}
|
||||
|
||||
match box_style.vertical_align {
|
||||
vertical_align::T::baseline => {}
|
||||
vertical_align::T::middle => {
|
||||
// TODO: x-height value should be used from font info.
|
||||
// TODO: Doing nothing here passes our current reftests but doesn't work in
|
||||
// all situations. Add vertical align reftests and fix this.
|
||||
},
|
||||
vertical_align::T::sub => {
|
||||
let sub_offset = (parent_text_block_start + parent_text_block_end)
|
||||
.scale_by(FONT_SUBSCRIPT_OFFSET_RATIO);
|
||||
offset_from_baseline = offset_from_baseline + sub_offset
|
||||
},
|
||||
vertical_align::T::super_ => {
|
||||
let super_offset = (parent_text_block_start + parent_text_block_end)
|
||||
.scale_by(FONT_SUPERSCRIPT_OFFSET_RATIO);
|
||||
offset_from_baseline = offset_from_baseline - super_offset
|
||||
},
|
||||
vertical_align::T::text_top => {
|
||||
let fragment_block_size = *block_size_above_baseline +
|
||||
*depth_below_baseline;
|
||||
let prev_depth_below_baseline = *depth_below_baseline;
|
||||
*block_size_above_baseline = parent_text_block_start;
|
||||
*depth_below_baseline = fragment_block_size - *block_size_above_baseline;
|
||||
offset_from_baseline = offset_from_baseline + *depth_below_baseline -
|
||||
prev_depth_below_baseline
|
||||
},
|
||||
vertical_align::T::text_bottom => {
|
||||
let fragment_block_size = *block_size_above_baseline +
|
||||
*depth_below_baseline;
|
||||
let prev_depth_below_baseline = *depth_below_baseline;
|
||||
*depth_below_baseline = parent_text_block_end;
|
||||
*block_size_above_baseline = fragment_block_size - *depth_below_baseline;
|
||||
offset_from_baseline = offset_from_baseline + *depth_below_baseline -
|
||||
prev_depth_below_baseline
|
||||
},
|
||||
vertical_align::T::top => {
|
||||
if !largest_size_updated {
|
||||
largest_size_updated = true;
|
||||
*largest_block_size_for_top_fragments =
|
||||
max(*largest_block_size_for_top_fragments,
|
||||
*block_size_above_baseline + *depth_below_baseline);
|
||||
offset_from_baseline = offset_from_baseline +
|
||||
*block_size_above_baseline
|
||||
}
|
||||
},
|
||||
vertical_align::T::bottom => {
|
||||
if !largest_size_updated {
|
||||
largest_size_updated = true;
|
||||
*largest_block_size_for_bottom_fragments =
|
||||
max(*largest_block_size_for_bottom_fragments,
|
||||
*block_size_above_baseline + *depth_below_baseline);
|
||||
offset_from_baseline = offset_from_baseline - *depth_below_baseline
|
||||
}
|
||||
},
|
||||
vertical_align::T::LengthOrPercentage(LengthOrPercentage::Length(length)) => {
|
||||
offset_from_baseline = offset_from_baseline - length
|
||||
}
|
||||
vertical_align::T::LengthOrPercentage(LengthOrPercentage::Percentage(p)) => {
|
||||
let line_height = fragment.calculate_line_height(layout_context);
|
||||
let percent_offset = line_height.scale_by(p);
|
||||
offset_from_baseline = offset_from_baseline - percent_offset
|
||||
}
|
||||
vertical_align::T::LengthOrPercentage(LengthOrPercentage::Calc(calc)) => {
|
||||
let line_height = fragment.calculate_line_height(layout_context);
|
||||
let percent_offset = line_height.scale_by(calc.percentage());
|
||||
offset_from_baseline = offset_from_baseline - percent_offset - calc.length()
|
||||
}
|
||||
}
|
||||
}
|
||||
(offset_from_baseline - ascent, largest_size_updated)
|
||||
}
|
||||
|
||||
/// Sets fragment positions in the inline direction based on alignment for one line. This
|
||||
/// performs text justification if mandated by the style.
|
||||
fn set_inline_fragment_positions(fragments: &mut InlineFragments,
|
||||
|
@ -1119,13 +1031,12 @@ impl InlineFlow {
|
|||
}
|
||||
}
|
||||
|
||||
/// Sets final fragment positions in the block direction for one line. Assumes that the
|
||||
/// fragment positions were initially set to the distance from the baseline first.
|
||||
/// Sets final fragment positions in the block direction for one line.
|
||||
fn set_block_fragment_positions(fragments: &mut InlineFragments,
|
||||
line: &Line,
|
||||
line_distance_from_flow_block_start: Au,
|
||||
baseline_distance_from_block_start: Au,
|
||||
largest_depth_below_baseline: Au) {
|
||||
minimum_block_size_above_baseline: Au,
|
||||
minimum_depth_below_baseline: Au,
|
||||
layout_context: &LayoutContext) {
|
||||
for fragment_index in line.range.each_index() {
|
||||
// If any of the inline styles say `top` or `bottom`, adjust the vertical align
|
||||
// appropriately.
|
||||
|
@ -1133,42 +1044,63 @@ impl InlineFlow {
|
|||
// 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 mut vertical_align = vertical_align::T::baseline;
|
||||
let fragment_inline_metrics = fragment.inline_metrics(layout_context);
|
||||
let mut block_start = line.bounds.start.b +
|
||||
line.inline_metrics.block_size_above_baseline -
|
||||
fragment_inline_metrics.ascent;
|
||||
|
||||
for style in fragment.inline_styles() {
|
||||
match (style.get_box().display, style.get_box().vertical_align) {
|
||||
(display::T::inline, vertical_align::T::top) |
|
||||
(display::T::block, vertical_align::T::top) |
|
||||
(display::T::inline_block, vertical_align::T::top) => {
|
||||
vertical_align = vertical_align::T::top;
|
||||
break
|
||||
match style.get_box().vertical_align {
|
||||
vertical_align::T::baseline => {}
|
||||
vertical_align::T::middle => {}
|
||||
vertical_align::T::sub => {
|
||||
let sub_offset =
|
||||
(minimum_block_size_above_baseline +
|
||||
minimum_depth_below_baseline).scale_by(FONT_SUBSCRIPT_OFFSET_RATIO);
|
||||
block_start = block_start + sub_offset
|
||||
}
|
||||
(display::T::inline, vertical_align::T::bottom) |
|
||||
(display::T::block, vertical_align::T::bottom) |
|
||||
(display::T::inline_block, vertical_align::T::bottom) => {
|
||||
vertical_align = vertical_align::T::bottom;
|
||||
break
|
||||
vertical_align::T::super_ => {
|
||||
let super_offset =
|
||||
(minimum_block_size_above_baseline +
|
||||
minimum_depth_below_baseline).scale_by(FONT_SUPERSCRIPT_OFFSET_RATIO);
|
||||
block_start = block_start - super_offset
|
||||
}
|
||||
vertical_align::T::text_top => {
|
||||
block_start = line.bounds.start.b +
|
||||
line.inline_metrics.block_size_above_baseline -
|
||||
minimum_block_size_above_baseline
|
||||
}
|
||||
vertical_align::T::text_bottom => {
|
||||
block_start = line.bounds.start.b +
|
||||
line.inline_metrics.block_size_above_baseline +
|
||||
minimum_depth_below_baseline -
|
||||
fragment.border_box.size.block
|
||||
}
|
||||
vertical_align::T::top => {
|
||||
block_start = line.bounds.start.b
|
||||
}
|
||||
vertical_align::T::bottom => {
|
||||
block_start = line.bounds.start.b + line.bounds.size.block -
|
||||
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()
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
match vertical_align {
|
||||
vertical_align::T::top => {
|
||||
fragment.border_box.start.b = fragment.border_box.start.b +
|
||||
line_distance_from_flow_block_start
|
||||
}
|
||||
vertical_align::T::bottom => {
|
||||
fragment.border_box.start.b = fragment.border_box.start.b +
|
||||
line_distance_from_flow_block_start +
|
||||
baseline_distance_from_block_start +
|
||||
largest_depth_below_baseline;
|
||||
}
|
||||
_ => {
|
||||
fragment.border_box.start.b = fragment.border_box.start.b +
|
||||
line_distance_from_flow_block_start + baseline_distance_from_block_start
|
||||
}
|
||||
}
|
||||
|
||||
fragment.border_box.start.b = block_start;
|
||||
fragment.update_late_computed_block_position_if_necessary();
|
||||
}
|
||||
}
|
||||
|
@ -1192,31 +1124,88 @@ impl InlineFlow {
|
|||
let line_height = text::line_height_from_style(style, &font_metrics);
|
||||
let inline_metrics = InlineMetrics::from_font_metrics(&font_metrics, line_height);
|
||||
|
||||
let mut block_size_above_baseline = inline_metrics.block_size_above_baseline;
|
||||
let mut depth_below_baseline = inline_metrics.depth_below_baseline;
|
||||
let mut block_size_above_baseline = Au(0);
|
||||
let mut depth_below_baseline = Au(i32::MIN);
|
||||
let mut largest_block_size_for_top_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
|
||||
// the inside of inline blocks.
|
||||
update_inline_metrics(&inline_metrics,
|
||||
style.get_box().display,
|
||||
vertical_align::T::baseline,
|
||||
&mut block_size_above_baseline,
|
||||
&mut depth_below_baseline,
|
||||
&mut largest_block_size_for_top_fragments,
|
||||
&mut largest_block_size_for_bottom_fragments);
|
||||
|
||||
// According to CSS 2.1 § 10.8, `line-height` of any inline element specifies the minimal
|
||||
// height of line boxes within the element.
|
||||
for frag in &self.fragments.fragments {
|
||||
match frag.inline_context {
|
||||
Some(ref inline_context) => {
|
||||
for node in &inline_context.nodes {
|
||||
let font_style = node.style.get_font_arc();
|
||||
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 inline_metrics = InlineMetrics::from_font_metrics(&font_metrics,
|
||||
line_height);
|
||||
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);
|
||||
}
|
||||
if let Some(ref inline_context) = frag.inline_context {
|
||||
for node in &inline_context.nodes {
|
||||
let font_style = node.style.get_font_arc();
|
||||
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 inline_metrics = InlineMetrics::from_font_metrics(&font_metrics,
|
||||
line_height);
|
||||
|
||||
update_inline_metrics(&inline_metrics,
|
||||
node.style.get_box().display,
|
||||
node.style.get_box().vertical_align,
|
||||
&mut block_size_above_baseline,
|
||||
&mut depth_below_baseline,
|
||||
&mut largest_block_size_for_top_fragments,
|
||||
&mut largest_block_size_for_bottom_fragments);
|
||||
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
(block_size_above_baseline, depth_below_baseline)
|
||||
block_size_above_baseline =
|
||||
max(block_size_above_baseline,
|
||||
largest_block_size_for_bottom_fragments - max(depth_below_baseline, Au(0)));
|
||||
depth_below_baseline =
|
||||
max(depth_below_baseline,
|
||||
largest_block_size_for_top_fragments - block_size_above_baseline);
|
||||
|
||||
return (block_size_above_baseline, depth_below_baseline);
|
||||
|
||||
fn update_inline_metrics(inline_metrics: &InlineMetrics,
|
||||
display_value: display::T,
|
||||
vertical_align_value: vertical_align::T,
|
||||
block_size_above_baseline: &mut Au,
|
||||
depth_below_baseline: &mut Au,
|
||||
largest_block_size_for_top_fragments: &mut Au,
|
||||
largest_block_size_for_bottom_fragments: &mut Au) {
|
||||
match (display_value, vertical_align_value) {
|
||||
(display::T::inline, vertical_align::T::top) |
|
||||
(display::T::block, vertical_align::T::top) |
|
||||
(display::T::inline_block, vertical_align::T::top) if
|
||||
inline_metrics.block_size_above_baseline >= Au(0) => {
|
||||
*largest_block_size_for_top_fragments =
|
||||
max(*largest_block_size_for_top_fragments,
|
||||
inline_metrics.block_size_above_baseline +
|
||||
inline_metrics.depth_below_baseline)
|
||||
}
|
||||
(display::T::inline, vertical_align::T::bottom) |
|
||||
(display::T::block, vertical_align::T::bottom) |
|
||||
(display::T::inline_block, vertical_align::T::bottom) if
|
||||
inline_metrics.depth_below_baseline >= Au(0) => {
|
||||
*largest_block_size_for_bottom_fragments =
|
||||
max(*largest_block_size_for_bottom_fragments,
|
||||
inline_metrics.block_size_above_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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_restyle_damage(&mut self) {
|
||||
|
@ -1275,6 +1264,16 @@ impl InlineFlow {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn baseline_offset_of_last_line(&self) -> Option<Au> {
|
||||
match self.lines.last() {
|
||||
None => None,
|
||||
Some(ref last_line) => {
|
||||
Some(last_line.bounds.start.b + last_line.bounds.size.block -
|
||||
last_line.inline_metrics.depth_below_baseline)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Flow for InlineFlow {
|
||||
|
@ -1300,6 +1299,8 @@ impl Flow for InlineFlow {
|
|||
flow::mut_base(kid).floats = Floats::new(writing_mode);
|
||||
}
|
||||
|
||||
self.base.flags.remove(CONTAINS_TEXT_OR_REPLACED_FRAGMENTS);
|
||||
|
||||
let mut intrinsic_sizes_for_flow = IntrinsicISizesContribution::new();
|
||||
let mut intrinsic_sizes_for_inline_run = IntrinsicISizesContribution::new();
|
||||
let mut intrinsic_sizes_for_nonbroken_run = IntrinsicISizesContribution::new();
|
||||
|
@ -1358,6 +1359,10 @@ impl Flow for InlineFlow {
|
|||
}
|
||||
|
||||
fragment.restyle_damage.remove(BUBBLE_ISIZES);
|
||||
|
||||
if fragment.is_text_or_replaced() {
|
||||
self.base.flags.insert(CONTAINS_TEXT_OR_REPLACED_FRAGMENTS);
|
||||
}
|
||||
}
|
||||
|
||||
// Flush any remaining nonbroken-run and inline-run intrinsic sizes.
|
||||
|
@ -1451,11 +1456,8 @@ impl Flow for InlineFlow {
|
|||
|
||||
|
||||
// Now, go through each line and lay out the fragments inside.
|
||||
let mut line_distance_from_flow_block_start = Au(0);
|
||||
let line_count = self.lines.len();
|
||||
for line_index in 0..line_count {
|
||||
let line = &mut self.lines[line_index];
|
||||
|
||||
for (line_index, line) in self.lines.iter_mut().enumerate() {
|
||||
// Lay out fragments in the inline direction, and justify them if necessary.
|
||||
InlineFlow::set_inline_fragment_positions(&mut self.fragments,
|
||||
line,
|
||||
|
@ -1463,108 +1465,17 @@ impl Flow for InlineFlow {
|
|||
indentation,
|
||||
line_index + 1 == line_count);
|
||||
|
||||
// Set the block-start position of the current line.
|
||||
// `line_height_offset` is updated at the end of the previous loop.
|
||||
line.bounds.start.b = line_distance_from_flow_block_start;
|
||||
|
||||
// Calculate the distance from the baseline to the block-start and block-end of the
|
||||
// line.
|
||||
let mut largest_block_size_above_baseline = self.minimum_block_size_above_baseline;
|
||||
let mut largest_depth_below_baseline = self.minimum_depth_below_baseline;
|
||||
|
||||
// Calculate the largest block-size among fragments with 'top' and 'bottom' values
|
||||
// respectively.
|
||||
let (mut largest_block_size_for_top_fragments,
|
||||
mut largest_block_size_for_bottom_fragments) = (Au(0), Au(0));
|
||||
|
||||
for fragment_index in line.range.each_index() {
|
||||
let fragment = &mut self.fragments.fragments[fragment_index.to_usize()];
|
||||
|
||||
let InlineMetrics {
|
||||
mut block_size_above_baseline,
|
||||
mut depth_below_baseline,
|
||||
ascent
|
||||
} = fragment.inline_metrics(layout_context);
|
||||
|
||||
// To calculate text-top and text-bottom value when `vertical-align` is involved,
|
||||
// we should find the top and bottom of the content area of the parent fragment.
|
||||
// "Content area" is defined in CSS 2.1 § 10.6.1.
|
||||
//
|
||||
// TODO: We should extract em-box info from the font size of the parent and
|
||||
// calculate the distances from the baseline to the block-start and the block-end
|
||||
// of the parent's content area.
|
||||
|
||||
// We should calculate the distance from baseline to the top of parent's content
|
||||
// area. But for now we assume it's the font size.
|
||||
//
|
||||
// CSS 2.1 does not state which font to use. This version of the code uses
|
||||
// the parent's font.
|
||||
|
||||
// Calculate the final block-size above the baseline for this fragment.
|
||||
//
|
||||
// The no-update flag decides whether `largest_block_size_for_top_fragments` and
|
||||
// `largest_block_size_for_bottom_fragments` are to be updated or not. This will be
|
||||
// set if and only if the fragment has `vertical-align` set to `top` or `bottom`.
|
||||
let (distance_from_baseline, no_update_flag) =
|
||||
InlineFlow::distance_from_baseline(
|
||||
fragment,
|
||||
ascent,
|
||||
self.minimum_block_size_above_baseline,
|
||||
self.minimum_depth_below_baseline,
|
||||
&mut block_size_above_baseline,
|
||||
&mut depth_below_baseline,
|
||||
&mut largest_block_size_for_top_fragments,
|
||||
&mut largest_block_size_for_bottom_fragments,
|
||||
layout_context);
|
||||
|
||||
// Unless the current fragment has `vertical-align` set to `top` or `bottom`,
|
||||
// `largest_block_size_above_baseline` and `largest_depth_below_baseline` are
|
||||
// updated.
|
||||
if !no_update_flag {
|
||||
largest_block_size_above_baseline = max(block_size_above_baseline,
|
||||
largest_block_size_above_baseline);
|
||||
largest_depth_below_baseline = max(depth_below_baseline,
|
||||
largest_depth_below_baseline);
|
||||
}
|
||||
|
||||
// Temporarily use `fragment.border_box.start.b` to mean "the distance from the
|
||||
// baseline". We will assign the real value later.
|
||||
fragment.border_box.start.b = distance_from_baseline
|
||||
}
|
||||
|
||||
// Calculate the distance from the baseline to the top of the largest fragment with a
|
||||
// value for `bottom`. Then, if necessary, update `largest_block-size_above_baseline`.
|
||||
largest_block_size_above_baseline =
|
||||
max(largest_block_size_above_baseline,
|
||||
largest_block_size_for_bottom_fragments - largest_depth_below_baseline);
|
||||
|
||||
// Calculate the distance from baseline to the bottom of the largest fragment with a
|
||||
// value for `top`. Then, if necessary, update `largest_depth_below_baseline`.
|
||||
largest_depth_below_baseline =
|
||||
max(largest_depth_below_baseline,
|
||||
largest_block_size_for_top_fragments - largest_block_size_above_baseline);
|
||||
|
||||
// Now, the distance from the logical block-start of the line to the baseline can be
|
||||
// computed as `largest_block-size_above_baseline`.
|
||||
let baseline_distance_from_block_start = largest_block_size_above_baseline;
|
||||
|
||||
// Compute the final positions in the block direction of each fragment. Recall that
|
||||
// `fragment.border_box.start.b` was set to the distance from the baseline above.
|
||||
// Compute the final positions in the block direction of each fragment.
|
||||
InlineFlow::set_block_fragment_positions(&mut self.fragments,
|
||||
line,
|
||||
line_distance_from_flow_block_start,
|
||||
baseline_distance_from_block_start,
|
||||
largest_depth_below_baseline);
|
||||
self.minimum_block_size_above_baseline,
|
||||
self.minimum_depth_below_baseline,
|
||||
layout_context);
|
||||
|
||||
// This is used to set the block-start position of the next line in the next loop.
|
||||
line.bounds.size.block = largest_block_size_above_baseline +
|
||||
largest_depth_below_baseline;
|
||||
line_distance_from_flow_block_start = line_distance_from_flow_block_start +
|
||||
line.bounds.size.block;
|
||||
|
||||
// We're no longer on the first line, so set indentation to zero.
|
||||
// This is used to set the block-start position of the next line in the next iteration
|
||||
// of the loop. We're no longer on the first line, so set indentation to zero.
|
||||
indentation = Au(0)
|
||||
} // End of `lines.iter_mut()` loop.
|
||||
}
|
||||
|
||||
// Assign block sizes for any inline-block descendants.
|
||||
let thread_id = self.base.thread_id;
|
||||
|
@ -1951,22 +1862,6 @@ impl InlineMetrics {
|
|||
}
|
||||
}
|
||||
|
||||
/// Calculates inline metrics from font metrics and line block-size per CSS 2.1 § 10.8.1.
|
||||
#[inline]
|
||||
pub fn from_block_height(font_metrics: &FontMetrics,
|
||||
block_height: Au,
|
||||
block_start_margin: Au,
|
||||
block_end_margin: Au)
|
||||
-> InlineMetrics {
|
||||
let leading = block_height + block_start_margin + block_end_margin -
|
||||
(font_metrics.ascent + font_metrics.descent);
|
||||
InlineMetrics {
|
||||
block_size_above_baseline: font_metrics.ascent + leading.scale_by(0.5),
|
||||
depth_below_baseline: font_metrics.descent + leading.scale_by(0.5),
|
||||
ascent: font_metrics.ascent + leading.scale_by(0.5) - block_start_margin,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn block_size(&self) -> Au {
|
||||
self.block_size_above_baseline + self.depth_below_baseline
|
||||
}
|
||||
|
|
|
@ -1837,6 +1837,21 @@ pub mod computed {
|
|||
Auto,
|
||||
Calc(CalcLengthOrPercentage),
|
||||
}
|
||||
|
||||
impl LengthOrPercentageOrAuto {
|
||||
/// Returns true if the computed value is absolute 0 or 0%.
|
||||
///
|
||||
/// (Returns false for calc() values, even if ones that may resolve to zero.)
|
||||
#[inline]
|
||||
pub fn is_definitely_zero(&self) -> bool {
|
||||
use self::LengthOrPercentageOrAuto::*;
|
||||
match *self {
|
||||
Length(Au(0)) | Percentage(0.0) => true,
|
||||
Length(_) | Percentage(_) | Calc(_) | Auto => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for LengthOrPercentageOrAuto {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
[flexbox_align-items-stretch-2.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +1,3 @@
|
|||
[c414-flt-fit-001.htm]
|
||||
[ttwf-reftest-flex-inline.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[c414-flt-wrap-001.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[c5525-fltmrgn-000.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[c5525-fltwidth-001.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[first-line-pseudo-014.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[first-line-pseudo-015.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[floats-132.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[floats-wrap-top-below-inline-002l.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[floats-wrap-top-below-inline-002r.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,4 +0,0 @@
|
|||
[inline-block-zorder-003.htm]
|
||||
type: reftest
|
||||
expected:
|
||||
if os == "mac": FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[inline-replaced-width-012.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[inline-replaced-width-013.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[inline-replaced-width-015.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[left-applies-to-012.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[right-applies-to-012.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1644,6 +1644,18 @@
|
|||
"url": "/_mozilla/css/floats_margin_collapse_with_clearance_a.html"
|
||||
}
|
||||
],
|
||||
"css/floats_percentage_width_a.html": [
|
||||
{
|
||||
"path": "css/floats_percentage_width_a.html",
|
||||
"references": [
|
||||
[
|
||||
"/_mozilla/css/floats_percentage_width_ref.html",
|
||||
"=="
|
||||
]
|
||||
],
|
||||
"url": "/_mozilla/css/floats_percentage_width_a.html"
|
||||
}
|
||||
],
|
||||
"css/focus_selector.html": [
|
||||
{
|
||||
"path": "css/focus_selector.html",
|
||||
|
@ -2280,6 +2292,18 @@
|
|||
"url": "/_mozilla/css/inline_background_a.html"
|
||||
}
|
||||
],
|
||||
"css/inline_block_absolute_hypothetical_a.html": [
|
||||
{
|
||||
"path": "css/inline_block_absolute_hypothetical_a.html",
|
||||
"references": [
|
||||
[
|
||||
"/_mozilla/css/inline_block_absolute_hypothetical_ref.html",
|
||||
"=="
|
||||
]
|
||||
],
|
||||
"url": "/_mozilla/css/inline_block_absolute_hypothetical_a.html"
|
||||
}
|
||||
],
|
||||
"css/inline_block_baseline_a.html": [
|
||||
{
|
||||
"path": "css/inline_block_baseline_a.html",
|
||||
|
@ -8408,6 +8432,18 @@
|
|||
"url": "/_mozilla/css/floats_margin_collapse_with_clearance_a.html"
|
||||
}
|
||||
],
|
||||
"css/floats_percentage_width_a.html": [
|
||||
{
|
||||
"path": "css/floats_percentage_width_a.html",
|
||||
"references": [
|
||||
[
|
||||
"/_mozilla/css/floats_percentage_width_ref.html",
|
||||
"=="
|
||||
]
|
||||
],
|
||||
"url": "/_mozilla/css/floats_percentage_width_a.html"
|
||||
}
|
||||
],
|
||||
"css/focus_selector.html": [
|
||||
{
|
||||
"path": "css/focus_selector.html",
|
||||
|
@ -9044,6 +9080,18 @@
|
|||
"url": "/_mozilla/css/inline_background_a.html"
|
||||
}
|
||||
],
|
||||
"css/inline_block_absolute_hypothetical_a.html": [
|
||||
{
|
||||
"path": "css/inline_block_absolute_hypothetical_a.html",
|
||||
"references": [
|
||||
[
|
||||
"/_mozilla/css/inline_block_absolute_hypothetical_ref.html",
|
||||
"=="
|
||||
]
|
||||
],
|
||||
"url": "/_mozilla/css/inline_block_absolute_hypothetical_a.html"
|
||||
}
|
||||
],
|
||||
"css/inline_block_baseline_a.html": [
|
||||
{
|
||||
"path": "css/inline_block_baseline_a.html",
|
||||
|
|
18
tests/wpt/mozilla/tests/css/floats_percentage_width_a.html
Normal file
18
tests/wpt/mozilla/tests/css/floats_percentage_width_a.html
Normal file
|
@ -0,0 +1,18 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title></title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: none;
|
||||
}
|
||||
div {
|
||||
float: left;
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
background: gold;
|
||||
}
|
||||
</style>
|
||||
<link rel="match" href="floats_percentage_width_ref.html">
|
||||
<div></div>X
|
||||
|
16
tests/wpt/mozilla/tests/css/floats_percentage_width_ref.html
Normal file
16
tests/wpt/mozilla/tests/css/floats_percentage_width_ref.html
Normal file
|
@ -0,0 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title></title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: none;
|
||||
}
|
||||
div {
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
background: gold;
|
||||
}
|
||||
</style>
|
||||
<div></div>X
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<link rel="match" href="inline_block_absolute_hypothetical_ref.html">
|
||||
<style>
|
||||
span {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: 32px;
|
||||
}
|
||||
</style>
|
||||
Hello <span>world</span>!
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
span {
|
||||
display: inline;
|
||||
position: absolute;
|
||||
top: 32px;
|
||||
}
|
||||
</style>
|
||||
Hello <span>world</span>!
|
||||
|
|
@ -7,6 +7,8 @@ body, html {
|
|||
input {
|
||||
margin-left: 64px;
|
||||
border: none;
|
||||
background: lightblue;
|
||||
color: transparent;
|
||||
vertical-align: top;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -7,6 +7,8 @@ input {
|
|||
position: absolute;
|
||||
left: 64px;
|
||||
border: none;
|
||||
background: lightblue;
|
||||
color: transparent;
|
||||
}
|
||||
</style>
|
||||
<input type=button value=Hello>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue