mirror of
https://github.com/servo/servo.git
synced 2025-08-07 06:25:32 +01:00
auto merge of #4328 : pcwalton/servo/text-indent, r=SimonSapin
I had to use a somewhat unconventional method of computing text indentation (propagating from blocks down to inlines) because of the way containing blocks are handled in Servo. (As a side note, neither Gecko nor WebKit correctly handles percentages in `text-align`, at least incrementally -- i.e. when the percentages are relative to the viewport and the viewport is resized.) r? @SimonSapin
This commit is contained in:
commit
be66121937
12 changed files with 373 additions and 227 deletions
|
@ -1256,6 +1256,7 @@ impl BlockFlow {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn propagate_assigned_inline_size_to_children(
|
pub fn propagate_assigned_inline_size_to_children(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
layout_context: &LayoutContext,
|
||||||
inline_start_content_edge: Au,
|
inline_start_content_edge: Au,
|
||||||
content_inline_size: Au,
|
content_inline_size: Au,
|
||||||
optional_column_computed_inline_sizes: Option<&[ColumnComputedInlineSize]>) {
|
optional_column_computed_inline_sizes: Option<&[ColumnComputedInlineSize]>) {
|
||||||
|
@ -1306,6 +1307,13 @@ impl BlockFlow {
|
||||||
(LPA_Length(length), _) => Some(length),
|
(LPA_Length(length), _) => Some(length),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Calculate containing block inline size.
|
||||||
|
let containing_block_size = if flags.contains(IS_ABSOLUTELY_POSITIONED) {
|
||||||
|
self.containing_block_size(layout_context.shared.screen_size).inline
|
||||||
|
} else {
|
||||||
|
content_inline_size
|
||||||
|
};
|
||||||
|
|
||||||
for (i, kid) in self.base.child_iter().enumerate() {
|
for (i, kid) in self.base.child_iter().enumerate() {
|
||||||
{
|
{
|
||||||
let kid_base = flow::mut_base(kid);
|
let kid_base = flow::mut_base(kid);
|
||||||
|
@ -1383,7 +1391,16 @@ impl BlockFlow {
|
||||||
// Per CSS 2.1 § 16.3.1, text alignment propagates to all children in flow.
|
// Per CSS 2.1 § 16.3.1, text alignment propagates to all children in flow.
|
||||||
//
|
//
|
||||||
// TODO(#2018, pcwalton): Do this in the cascade instead.
|
// TODO(#2018, pcwalton): Do this in the cascade instead.
|
||||||
flow::mut_base(kid).flags.propagate_text_alignment_from_parent(flags.clone())
|
flow::mut_base(kid).flags.propagate_text_alignment_from_parent(flags.clone());
|
||||||
|
|
||||||
|
// Handle `text-indent` on behalf of any inline children that we have. This is
|
||||||
|
// necessary because any percentages are relative to the containing block, which only
|
||||||
|
// we know.
|
||||||
|
if kid.is_inline_flow() {
|
||||||
|
kid.as_inline().first_line_indentation =
|
||||||
|
specified(self.fragment.style().get_inheritedtext().text_indent,
|
||||||
|
containing_block_size);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1609,7 +1626,8 @@ impl Flow for BlockFlow {
|
||||||
let padding_and_borders = self.fragment.border_padding.inline_start_end();
|
let padding_and_borders = self.fragment.border_padding.inline_start_end();
|
||||||
let content_inline_size = self.fragment.border_box.size.inline - padding_and_borders;
|
let content_inline_size = self.fragment.border_box.size.inline - padding_and_borders;
|
||||||
|
|
||||||
self.propagate_assigned_inline_size_to_children(inline_start_content_edge,
|
self.propagate_assigned_inline_size_to_children(layout_context,
|
||||||
|
inline_start_content_edge,
|
||||||
content_inline_size,
|
content_inline_size,
|
||||||
None);
|
None);
|
||||||
}
|
}
|
||||||
|
@ -2090,7 +2108,7 @@ pub trait ISizeAndMarginsComputer {
|
||||||
// recalculated, but this time using the value of 'min-inline-size' as the computed value
|
// recalculated, but this time using the value of 'min-inline-size' as the computed value
|
||||||
// for 'inline-size'.
|
// for 'inline-size'.
|
||||||
let computed_min_inline_size = specified(block.fragment().style().min_inline_size(),
|
let computed_min_inline_size = specified(block.fragment().style().min_inline_size(),
|
||||||
containing_block_inline_size);
|
containing_block_inline_size);
|
||||||
if computed_min_inline_size > solution.inline_size {
|
if computed_min_inline_size > solution.inline_size {
|
||||||
input.computed_inline_size = Specified(computed_min_inline_size);
|
input.computed_inline_size = Specified(computed_min_inline_size);
|
||||||
solution = self.solve_inline_size_constraints(block, &input);
|
solution = self.solve_inline_size_constraints(block, &input);
|
||||||
|
|
|
@ -288,6 +288,9 @@ pub trait Flow: fmt::Show + ToString + Sync {
|
||||||
|
|
||||||
/// Return the dimensions of the containing block generated by this flow for absolutely-
|
/// Return the dimensions of the containing block generated by this flow for absolutely-
|
||||||
/// positioned descendants. For block flows, this is the padding box.
|
/// positioned descendants. For block flows, this is the padding box.
|
||||||
|
///
|
||||||
|
/// NB: Do not change this `&self` to `&mut self` under any circumstances! It has security
|
||||||
|
/// implications because this can be called on parents concurrently from descendants!
|
||||||
fn generated_containing_block_rect(&self) -> LogicalRect<Au> {
|
fn generated_containing_block_rect(&self) -> LogicalRect<Au> {
|
||||||
panic!("generated_containing_block_position not yet implemented for this flow")
|
panic!("generated_containing_block_position not yet implemented for this flow")
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,27 +158,32 @@ int_range_index! {
|
||||||
struct FragmentIndex(int)
|
struct FragmentIndex(int)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The line wrapping mode, controlled by the `white-space` property as described in CSS 2.1 §
|
bitflags! {
|
||||||
/// 16.6.
|
flags InlineReflowFlags: u8 {
|
||||||
#[deriving(PartialEq, Eq)]
|
#[doc="The `white-space: nowrap` property from CSS 2.1 § 16.6 is in effect."]
|
||||||
enum WrapMode {
|
const NO_WRAP_INLINE_REFLOW_FLAG = 0x01
|
||||||
/// `normal`.
|
}
|
||||||
WrapNormally,
|
|
||||||
/// `nowrap`.
|
|
||||||
NoWrap,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Arranges fragments into lines, splitting them up as necessary.
|
||||||
struct LineBreaker {
|
struct LineBreaker {
|
||||||
|
/// The floats we need to flow around.
|
||||||
floats: Floats,
|
floats: Floats,
|
||||||
new_fragments: Vec<Fragment>,
|
new_fragments: Vec<Fragment>,
|
||||||
work_list: RingBuf<Fragment>,
|
work_list: RingBuf<Fragment>,
|
||||||
|
/// The line we're currently working on.
|
||||||
pending_line: Line,
|
pending_line: Line,
|
||||||
|
/// The lines we've already committed.
|
||||||
lines: Vec<Line>,
|
lines: Vec<Line>,
|
||||||
cur_b: Au, // Current position on the block direction
|
/// The current position in the block direction.
|
||||||
|
cur_b: Au,
|
||||||
|
/// The computed value of the indentation for the first line (`text-indent`, CSS 2.1 § 16.1).
|
||||||
|
first_line_indentation: Au,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LineBreaker {
|
impl LineBreaker {
|
||||||
fn new(float_context: Floats) -> LineBreaker {
|
/// Creates a new `LineBreaker` with a set of floats and the indentation of the first line.
|
||||||
|
fn new(float_context: Floats, first_line_indentation: Au) -> LineBreaker {
|
||||||
LineBreaker {
|
LineBreaker {
|
||||||
new_fragments: Vec::new(),
|
new_fragments: Vec::new(),
|
||||||
work_list: RingBuf::new(),
|
work_list: RingBuf::new(),
|
||||||
|
@ -189,22 +194,20 @@ impl LineBreaker {
|
||||||
},
|
},
|
||||||
floats: float_context,
|
floats: float_context,
|
||||||
lines: Vec::new(),
|
lines: Vec::new(),
|
||||||
cur_b: Au(0)
|
cur_b: Au(0),
|
||||||
|
first_line_indentation: first_line_indentation,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn floats(&mut self) -> Floats {
|
/// Resets the `LineBreaker` to the initial state it had after a call to `new`.
|
||||||
self.floats.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reset_scanner(&mut self) {
|
fn reset_scanner(&mut self) {
|
||||||
debug!("Resetting LineBreaker's state for flow.");
|
|
||||||
self.lines = Vec::new();
|
self.lines = Vec::new();
|
||||||
self.new_fragments = Vec::new();
|
self.new_fragments = Vec::new();
|
||||||
self.cur_b = Au(0);
|
self.cur_b = Au(0);
|
||||||
self.reset_line();
|
self.reset_line();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reinitializes the pending line to blank data.
|
||||||
fn reset_line(&mut self) {
|
fn reset_line(&mut self) {
|
||||||
self.pending_line.range.reset(num::zero(), num::zero());
|
self.pending_line.range.reset(num::zero(), num::zero());
|
||||||
self.pending_line.bounds = LogicalRect::new(self.floats.writing_mode,
|
self.pending_line.bounds = LogicalRect::new(self.floats.writing_mode,
|
||||||
|
@ -215,70 +218,80 @@ impl LineBreaker {
|
||||||
self.pending_line.green_zone = LogicalSize::zero(self.floats.writing_mode)
|
self.pending_line.green_zone = LogicalSize::zero(self.floats.writing_mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reflows fragments for the given inline flow.
|
||||||
fn scan_for_lines(&mut self, flow: &mut InlineFlow, layout_context: &LayoutContext) {
|
fn scan_for_lines(&mut self, flow: &mut InlineFlow, layout_context: &LayoutContext) {
|
||||||
self.reset_scanner();
|
self.reset_scanner();
|
||||||
|
|
||||||
|
debug!("LineBreaker: scanning for lines, {} fragments", flow.fragments.len());
|
||||||
let mut old_fragments = mem::replace(&mut flow.fragments, InlineFragments::new());
|
let mut old_fragments = mem::replace(&mut flow.fragments, InlineFragments::new());
|
||||||
|
self.reflow_fragments(old_fragments.fragments.iter(), flow, layout_context);
|
||||||
{
|
|
||||||
// Enter a new scope so that `old_fragment_iter`'s borrow is released.
|
|
||||||
debug!("Scanning for lines. {} fragments.", old_fragments.len());
|
|
||||||
let mut old_fragment_iter = old_fragments.fragments.iter();
|
|
||||||
loop {
|
|
||||||
// acquire the next fragment to lay out from work list or fragment list
|
|
||||||
let cur_fragment = if self.work_list.is_empty() {
|
|
||||||
match old_fragment_iter.next() {
|
|
||||||
None => break,
|
|
||||||
Some(fragment) => {
|
|
||||||
debug!("LineBreaker: Working with fragment from flow: b{}",
|
|
||||||
fragment.debug_id());
|
|
||||||
(*fragment).clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let fragment = self.work_list.pop_front().unwrap();
|
|
||||||
debug!("LineBreaker: Working with fragment from work list: b{}",
|
|
||||||
fragment.debug_id());
|
|
||||||
fragment
|
|
||||||
};
|
|
||||||
|
|
||||||
let fragment_was_appended = match cur_fragment.white_space() {
|
|
||||||
white_space::normal => {
|
|
||||||
self.try_append_to_line(cur_fragment, flow, layout_context, WrapNormally)
|
|
||||||
}
|
|
||||||
white_space::pre => self.try_append_to_line_by_new_line(cur_fragment),
|
|
||||||
white_space::nowrap => {
|
|
||||||
self.try_append_to_line(cur_fragment, flow, layout_context, NoWrap)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if !fragment_was_appended {
|
|
||||||
debug!("LineBreaker: Fragment wasn't appended, because line {:u} was full.",
|
|
||||||
self.lines.len());
|
|
||||||
self.flush_current_line();
|
|
||||||
} else {
|
|
||||||
debug!("LineBreaker: appended a fragment to line {:u}", self.lines.len());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.pending_line.range.length() > num::zero() {
|
|
||||||
debug!("LineBreaker: Partially full line {:u} inline_start at end of scanning.",
|
|
||||||
self.lines.len());
|
|
||||||
self.flush_current_line();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
old_fragments.fragments = mem::replace(&mut self.new_fragments, vec![]);
|
old_fragments.fragments = mem::replace(&mut self.new_fragments, vec![]);
|
||||||
flow.fragments = old_fragments;
|
flow.fragments = old_fragments;
|
||||||
flow.lines = mem::replace(&mut self.lines, Vec::new());
|
flow.lines = mem::replace(&mut self.lines, Vec::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush_current_line(&mut self) {
|
/// Reflows the given fragments, which have been plucked out of the inline flow.
|
||||||
debug!("LineBreaker: Flushing line {:u}: {}",
|
fn reflow_fragments<'a,I>(&mut self,
|
||||||
self.lines.len(), self.pending_line);
|
mut old_fragment_iter: I,
|
||||||
|
flow: &'a InlineFlow,
|
||||||
|
layout_context: &LayoutContext)
|
||||||
|
where I: Iterator<&'a Fragment> {
|
||||||
|
loop {
|
||||||
|
// Acquire the next fragment to lay out from the work list or fragment list, as
|
||||||
|
// appropriate.
|
||||||
|
let fragment = if self.work_list.is_empty() {
|
||||||
|
match old_fragment_iter.next() {
|
||||||
|
None => break,
|
||||||
|
Some(fragment) => {
|
||||||
|
debug!("LineBreaker: working with fragment from flow: {}", fragment);
|
||||||
|
(*fragment).clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
debug!("LineBreaker: working with fragment from work list: {}",
|
||||||
|
self.work_list.front());
|
||||||
|
self.work_list.pop_front().unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
// clear line and add line mapping
|
// Set up our reflow flags.
|
||||||
debug!("LineBreaker: Saving information for flushed line {:u}.", self.lines.len());
|
let flags = match fragment.style().get_inheritedtext().white_space {
|
||||||
|
white_space::normal => InlineReflowFlags::empty(),
|
||||||
|
white_space::pre | white_space::nowrap => NO_WRAP_INLINE_REFLOW_FLAG,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Try to append the fragment, and commit the line (so we can try again with the next
|
||||||
|
// line) if we couldn't.
|
||||||
|
match fragment.style().get_inheritedtext().white_space {
|
||||||
|
white_space::normal | white_space::nowrap => {
|
||||||
|
if !self.append_fragment_to_line_if_possible(fragment,
|
||||||
|
flow,
|
||||||
|
layout_context,
|
||||||
|
flags) {
|
||||||
|
self.flush_current_line()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
white_space::pre => {
|
||||||
|
// FIXME(pcwalton): Surely we can unify
|
||||||
|
// `append_fragment_to_line_if_possible` and
|
||||||
|
// `try_append_to_line_by_new_line` by adding another bit in the reflow
|
||||||
|
// flags.
|
||||||
|
if !self.try_append_to_line_by_new_line(fragment) {
|
||||||
|
self.flush_current_line()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.pending_line_is_empty() {
|
||||||
|
debug!("LineBreaker: partially full line {} at end of scanning; committing it",
|
||||||
|
self.lines.len());
|
||||||
|
self.flush_current_line()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Commits a line to the list.
|
||||||
|
fn flush_current_line(&mut self) {
|
||||||
|
debug!("LineBreaker: flushing line {}: {}", self.lines.len(), self.pending_line);
|
||||||
self.lines.push(self.pending_line);
|
self.lines.push(self.pending_line);
|
||||||
self.cur_b = self.pending_line.bounds.start.b + self.pending_line.bounds.size.block;
|
self.cur_b = self.pending_line.bounds.start.b + self.pending_line.bounds.size.block;
|
||||||
self.reset_line();
|
self.reset_line();
|
||||||
|
@ -299,50 +312,48 @@ impl LineBreaker {
|
||||||
/// Computes the position of a line that has only the provided fragment. Returns the bounding
|
/// Computes the position of a line that has only the provided fragment. Returns the bounding
|
||||||
/// rect of the line's green zone (whose origin coincides with the line's origin) and the
|
/// rect of the line's green zone (whose origin coincides with the line's origin) and the
|
||||||
/// actual inline-size of the first fragment after splitting.
|
/// actual inline-size of the first fragment after splitting.
|
||||||
fn initial_line_placement(&self, first_fragment: &Fragment, ceiling: Au, flow: &InlineFlow)
|
fn initial_line_placement(&self,
|
||||||
|
flow: &InlineFlow,
|
||||||
|
first_fragment: &Fragment,
|
||||||
|
ceiling: Au)
|
||||||
-> (LogicalRect<Au>, Au) {
|
-> (LogicalRect<Au>, Au) {
|
||||||
debug!("LineBreaker: Trying to place first fragment of line {}", self.lines.len());
|
debug!("LineBreaker: trying to place first fragment of line {}; fragment size: {}, \
|
||||||
|
splittable: {}",
|
||||||
let first_fragment_size = first_fragment.border_box.size;
|
self.lines.len(),
|
||||||
let splittable = first_fragment.can_split();
|
first_fragment.border_box.size,
|
||||||
debug!("LineBreaker: fragment size: {}, splittable: {}", first_fragment_size, splittable);
|
first_fragment.can_split());
|
||||||
|
|
||||||
// Initially, pretend a splittable fragment has zero inline-size. We will move it later if
|
// Initially, pretend a splittable fragment has zero inline-size. We will move it later if
|
||||||
// it has nonzero inline-size and that causes problems.
|
// it has nonzero inline-size and that causes problems.
|
||||||
let placement_inline_size = if splittable {
|
let placement_inline_size = if first_fragment.can_split() {
|
||||||
Au(0)
|
Au(0)
|
||||||
} else {
|
} else {
|
||||||
first_fragment_size.inline
|
first_fragment.border_box.size.inline + self.indentation_for_pending_fragment()
|
||||||
};
|
};
|
||||||
|
|
||||||
let info = PlacementInfo {
|
// Try to place the fragment between floats.
|
||||||
|
let line_bounds = self.floats.place_between_floats(&PlacementInfo {
|
||||||
size: LogicalSize::new(self.floats.writing_mode,
|
size: LogicalSize::new(self.floats.writing_mode,
|
||||||
placement_inline_size,
|
placement_inline_size,
|
||||||
first_fragment_size.block),
|
first_fragment.border_box.size.block),
|
||||||
ceiling: ceiling,
|
ceiling: ceiling,
|
||||||
max_inline_size: flow.base.position.size.inline,
|
max_inline_size: flow.base.position.size.inline,
|
||||||
kind: FloatLeft,
|
kind: FloatLeft,
|
||||||
};
|
});
|
||||||
|
|
||||||
let line_bounds = self.floats.place_between_floats(&info);
|
// Simple case: if the fragment fits, then we can stop here.
|
||||||
|
if line_bounds.size.inline > first_fragment.border_box.size.inline {
|
||||||
debug!("LineBreaker: found position for line: {} using placement_info: {}",
|
debug!("LineBreaker: fragment fits on line {}", self.lines.len());
|
||||||
line_bounds,
|
return (line_bounds, first_fragment.border_box.size.inline);
|
||||||
info);
|
|
||||||
|
|
||||||
// Simple case: if the fragment fits, then we can stop here
|
|
||||||
if line_bounds.size.inline > first_fragment_size.inline {
|
|
||||||
debug!("LineBreaker: case=fragment fits");
|
|
||||||
return (line_bounds, first_fragment_size.inline);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not, but we can't split the fragment, then we'll place
|
// If not, but we can't split the fragment, then we'll place the line here and it will
|
||||||
// the line here and it will overflow.
|
// overflow.
|
||||||
if !splittable {
|
if !first_fragment.can_split() {
|
||||||
debug!("LineBreaker: case=line doesn't fit, but is unsplittable");
|
debug!("LineBreaker: line doesn't fit, but is unsplittable");
|
||||||
}
|
}
|
||||||
|
|
||||||
(line_bounds, first_fragment_size.inline)
|
(line_bounds, first_fragment.border_box.size.inline)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs float collision avoidance. This is called when adding a fragment is going to
|
/// Performs float collision avoidance. This is called when adding a fragment is going to
|
||||||
|
@ -359,17 +370,17 @@ impl LineBreaker {
|
||||||
///
|
///
|
||||||
/// Returns false if and only if we should break the line.
|
/// Returns false if and only if we should break the line.
|
||||||
fn avoid_floats(&mut self,
|
fn avoid_floats(&mut self,
|
||||||
in_fragment: Fragment,
|
|
||||||
flow: &InlineFlow,
|
flow: &InlineFlow,
|
||||||
new_block_size: Au,
|
in_fragment: Fragment,
|
||||||
line_is_empty: bool)
|
new_block_size: Au)
|
||||||
-> bool {
|
-> bool {
|
||||||
debug!("LineBreaker: entering float collision avoider!");
|
debug!("LineBreaker: entering float collision avoider!");
|
||||||
|
|
||||||
// First predict where the next line is going to be.
|
// First predict where the next line is going to be.
|
||||||
let this_line_y = self.pending_line.bounds.start.b;
|
|
||||||
let (next_line, first_fragment_inline_size) =
|
let (next_line, first_fragment_inline_size) =
|
||||||
self.initial_line_placement(&in_fragment, this_line_y, flow);
|
self.initial_line_placement(flow,
|
||||||
|
&in_fragment,
|
||||||
|
self.pending_line.bounds.start.b);
|
||||||
let next_green_zone = next_line.size;
|
let next_green_zone = next_line.size;
|
||||||
|
|
||||||
let new_inline_size = self.pending_line.bounds.size.inline + first_fragment_inline_size;
|
let new_inline_size = self.pending_line.bounds.size.inline + first_fragment_inline_size;
|
||||||
|
@ -382,7 +393,7 @@ impl LineBreaker {
|
||||||
self.pending_line.bounds.start = next_line.start;
|
self.pending_line.bounds.start = next_line.start;
|
||||||
self.pending_line.green_zone = next_green_zone;
|
self.pending_line.green_zone = next_green_zone;
|
||||||
|
|
||||||
assert!(!line_is_empty, "Non-terminating line breaking");
|
debug_assert!(!self.pending_line_is_empty(), "Non-terminating line breaking");
|
||||||
self.work_list.push_front(in_fragment);
|
self.work_list.push_front(in_fragment);
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -392,14 +403,17 @@ impl LineBreaker {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tries to append the given fragment to the line for `pre`-formatted text, splitting it if
|
||||||
|
/// necessary. Returns true if we successfully pushed the fragment to the line or false if we
|
||||||
|
/// couldn't.
|
||||||
fn try_append_to_line_by_new_line(&mut self, in_fragment: Fragment) -> bool {
|
fn try_append_to_line_by_new_line(&mut self, in_fragment: Fragment) -> bool {
|
||||||
let no_newline_positions = match in_fragment.newline_positions() {
|
let should_push = match in_fragment.newline_positions() {
|
||||||
None => true,
|
None => true,
|
||||||
Some(ref positions) => positions.is_empty(),
|
Some(ref positions) => positions.is_empty(),
|
||||||
};
|
};
|
||||||
if no_newline_positions {
|
if should_push {
|
||||||
debug!("LineBreaker: Did not find a new-line character, so pushing the fragment to \
|
debug!("LineBreaker: did not find a newline character; pushing the fragment to \
|
||||||
the line without splitting.");
|
the line without splitting");
|
||||||
self.push_fragment_to_line(in_fragment);
|
self.push_fragment_to_line(in_fragment);
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -408,7 +422,7 @@ impl LineBreaker {
|
||||||
|
|
||||||
let (inline_start, inline_end, run) =
|
let (inline_start, inline_end, run) =
|
||||||
in_fragment.find_split_info_by_new_line()
|
in_fragment.find_split_info_by_new_line()
|
||||||
.expect("LineBreaker: This split case makes no sense!");
|
.expect("LineBreaker: this split case makes no sense!");
|
||||||
let writing_mode = self.floats.writing_mode;
|
let writing_mode = self.floats.writing_mode;
|
||||||
|
|
||||||
let split_fragment = |split: SplitInfo| {
|
let split_fragment = |split: SplitInfo| {
|
||||||
|
@ -441,137 +455,135 @@ impl LineBreaker {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to append the given fragment to the line, splitting it if necessary. Returns false if
|
/// Tries to append the given fragment to the line, splitting it if necessary. Returns true if
|
||||||
/// and only if we should break the line.
|
/// we successfully pushed the fragment to the line or false if we couldn't.
|
||||||
///
|
fn append_fragment_to_line_if_possible(&mut self,
|
||||||
/// `wrap_mode` controls whether wrapping happens.
|
fragment: Fragment,
|
||||||
fn try_append_to_line(&mut self,
|
flow: &InlineFlow,
|
||||||
in_fragment: Fragment,
|
layout_context: &LayoutContext,
|
||||||
flow: &InlineFlow,
|
flags: InlineReflowFlags)
|
||||||
layout_context: &LayoutContext,
|
-> bool {
|
||||||
wrap_mode: WrapMode)
|
// Determine initial placement for the fragment if we need to.
|
||||||
-> bool {
|
if self.pending_line_is_empty() {
|
||||||
let line_is_empty = self.pending_line.range.length() == num::zero();
|
let (line_bounds, _) = self.initial_line_placement(flow, &fragment, self.cur_b);
|
||||||
if line_is_empty {
|
|
||||||
let (line_bounds, _) = self.initial_line_placement(&in_fragment, self.cur_b, flow);
|
|
||||||
self.pending_line.bounds.start = line_bounds.start;
|
self.pending_line.bounds.start = line_bounds.start;
|
||||||
self.pending_line.green_zone = line_bounds.size;
|
self.pending_line.green_zone = line_bounds.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("LineBreaker: Trying to append fragment to line {:u} (fragment size: {}, green \
|
debug!("LineBreaker: trying to append to line {} (fragment size: {}, green zone: {}): {}",
|
||||||
zone: {}): {}",
|
|
||||||
self.lines.len(),
|
self.lines.len(),
|
||||||
in_fragment.border_box.size,
|
fragment.border_box.size,
|
||||||
self.pending_line.green_zone,
|
self.pending_line.green_zone,
|
||||||
in_fragment);
|
fragment);
|
||||||
|
|
||||||
let green_zone = self.pending_line.green_zone;
|
|
||||||
|
|
||||||
// NB: At this point, if `green_zone.inline < self.pending_line.bounds.size.inline` or
|
// NB: At this point, if `green_zone.inline < self.pending_line.bounds.size.inline` or
|
||||||
// `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 new_block_size = self.new_block_size_for_line(&in_fragment, layout_context);
|
let new_block_size = self.new_block_size_for_line(&fragment, 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!
|
||||||
return self.avoid_floats(in_fragment, flow, new_block_size, line_is_empty)
|
return self.avoid_floats(flow, fragment, new_block_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're not going to overflow the green zone vertically, we might still do so
|
// If we're not going to overflow the green zone vertically, we might still do so
|
||||||
// horizontally. We'll try to place the whole fragment on this line and break somewhere if
|
// horizontally. We'll try to place the whole fragment on this line and break somewhere if
|
||||||
// it doesn't fit.
|
// it doesn't fit.
|
||||||
|
let indentation = self.indentation_for_pending_fragment();
|
||||||
let new_inline_size = self.pending_line.bounds.size.inline +
|
let new_inline_size = self.pending_line.bounds.size.inline +
|
||||||
in_fragment.border_box.size.inline;
|
fragment.border_box.size.inline + indentation;
|
||||||
if new_inline_size <= green_zone.inline {
|
if new_inline_size <= green_zone.inline {
|
||||||
debug!("LineBreaker: case=fragment fits without splitting");
|
debug!("LineBreaker: fragment fits without splitting");
|
||||||
self.push_fragment_to_line(in_fragment);
|
self.push_fragment_to_line(fragment);
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!in_fragment.can_split() && line_is_empty) || wrap_mode == NoWrap {
|
// If we can't split the fragment or aren't allowed to because of the wrapping mode, then
|
||||||
// TODO(eatkinson, issue #224): Signal that horizontal overflow happened?
|
// just overflow.
|
||||||
debug!("LineBreaker: case=fragment can't split and line {:u} is empty, so \
|
if (!fragment.can_split() && self.pending_line_is_empty()) ||
|
||||||
overflowing.",
|
flags.contains(NO_WRAP_INLINE_REFLOW_FLAG) {
|
||||||
|
debug!("LineBreaker: fragment can't split and line {} is empty, so overflowing",
|
||||||
self.lines.len());
|
self.lines.len());
|
||||||
self.push_fragment_to_line(in_fragment);
|
self.push_fragment_to_line(fragment);
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
let available_inline_size = green_zone.inline - self.pending_line.bounds.size.inline;
|
// Split it up!
|
||||||
let split = in_fragment.find_split_info_for_inline_size(CharIndex(0),
|
let available_inline_size = green_zone.inline - self.pending_line.bounds.size.inline -
|
||||||
available_inline_size,
|
indentation;
|
||||||
line_is_empty);
|
let (inline_start_fragment, inline_end_fragment) =
|
||||||
match split.map(|(inline_start, inline_end, run)| {
|
match fragment.find_split_info_for_inline_size(CharIndex(0),
|
||||||
let split_fragment = |split: SplitInfo| {
|
available_inline_size,
|
||||||
let info = box ScannedTextFragmentInfo::new(run.clone(),
|
self.pending_line_is_empty()) {
|
||||||
split.range,
|
None => {
|
||||||
Vec::new(),
|
debug!("LineBreaker: fragment was unsplittable; deferring to next line: {}",
|
||||||
in_fragment.border_box.size);
|
fragment);
|
||||||
let size = LogicalSize::new(self.floats.writing_mode,
|
self.work_list.push_front(fragment);
|
||||||
split.inline_size,
|
return false
|
||||||
in_fragment.border_box.size.block);
|
}
|
||||||
in_fragment.transform(size, info)
|
Some((start_split_info, end_split_info, run)) => {
|
||||||
|
let split_fragment = |split: SplitInfo| {
|
||||||
|
let info = box ScannedTextFragmentInfo::new(run.clone(),
|
||||||
|
split.range,
|
||||||
|
Vec::new(),
|
||||||
|
fragment.border_box.size);
|
||||||
|
let size = LogicalSize::new(self.floats.writing_mode,
|
||||||
|
split.inline_size,
|
||||||
|
fragment.border_box.size.block);
|
||||||
|
fragment.transform(size, info)
|
||||||
|
};
|
||||||
|
(start_split_info.map(|x| split_fragment(x)),
|
||||||
|
end_split_info.map(|x| split_fragment(x)))
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
(inline_start.map(|x| {
|
// Push the first fragment onto the line we're working on and start off the next line with
|
||||||
debug!("LineBreaker: Left split {}", x);
|
// the second fragment. If there's no second fragment, the next line will start off empty.
|
||||||
split_fragment(x)
|
match (inline_start_fragment, inline_end_fragment) {
|
||||||
}),
|
(Some(inline_start_fragment), Some(inline_end_fragment)) => {
|
||||||
inline_end.map(|x| {
|
|
||||||
debug!("LineBreaker: Right split {}", x);
|
|
||||||
split_fragment(x)
|
|
||||||
}))
|
|
||||||
}) {
|
|
||||||
None => {
|
|
||||||
debug!("LineBreaker: Tried to split unsplittable render fragment! Deferring to \
|
|
||||||
next line. {}",
|
|
||||||
in_fragment);
|
|
||||||
self.work_list.push_front(in_fragment);
|
|
||||||
false
|
|
||||||
},
|
|
||||||
Some((Some(inline_start_fragment), Some(inline_end_fragment))) => {
|
|
||||||
debug!("LineBreaker: Line break found! Pushing inline_start fragment to line and \
|
|
||||||
deferring inline_end fragment to next line.");
|
|
||||||
self.push_fragment_to_line(inline_start_fragment);
|
self.push_fragment_to_line(inline_start_fragment);
|
||||||
self.work_list.push_front(inline_end_fragment);
|
self.work_list.push_front(inline_end_fragment)
|
||||||
true
|
|
||||||
},
|
|
||||||
Some((Some(inline_start_fragment), None)) => {
|
|
||||||
debug!("LineBreaker: Pushing inline_start fragment to line.");
|
|
||||||
self.push_fragment_to_line(inline_start_fragment);
|
|
||||||
true
|
|
||||||
},
|
|
||||||
Some((None, Some(inline_end_fragment))) => {
|
|
||||||
debug!("LineBreaker: Pushing inline_end fragment to line.");
|
|
||||||
self.push_fragment_to_line(inline_end_fragment);
|
|
||||||
true
|
|
||||||
},
|
|
||||||
Some((None, None)) => {
|
|
||||||
debug!("LineBreaker: Nothing to do.");
|
|
||||||
true
|
|
||||||
},
|
},
|
||||||
|
(Some(fragment), None) | (None, Some(fragment)) => {
|
||||||
|
self.push_fragment_to_line(fragment)
|
||||||
|
}
|
||||||
|
(None, None) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
// An unconditional push
|
/// Pushes a fragment to the current line unconditionally.
|
||||||
fn push_fragment_to_line(&mut self, fragment: Fragment) {
|
fn push_fragment_to_line(&mut self, fragment: Fragment) {
|
||||||
debug!("LineBreaker: Pushing fragment {} to line {:u}",
|
let indentation = self.indentation_for_pending_fragment();
|
||||||
fragment.debug_id(),
|
if self.pending_line_is_empty() {
|
||||||
self.lines.len());
|
|
||||||
|
|
||||||
if self.pending_line.range.length() == num::zero() {
|
|
||||||
assert!(self.new_fragments.len() <= (u16::MAX as uint));
|
assert!(self.new_fragments.len() <= (u16::MAX as uint));
|
||||||
self.pending_line.range.reset(FragmentIndex(self.new_fragments.len() as int),
|
self.pending_line.range.reset(FragmentIndex(self.new_fragments.len() as int),
|
||||||
num::zero());
|
num::zero());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.pending_line.range.extend_by(FragmentIndex(1));
|
self.pending_line.range.extend_by(FragmentIndex(1));
|
||||||
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.border_box.size.inline;
|
fragment.border_box.size.inline +
|
||||||
|
indentation;
|
||||||
self.pending_line.bounds.size.block = max(self.pending_line.bounds.size.block,
|
self.pending_line.bounds.size.block = max(self.pending_line.bounds.size.block,
|
||||||
fragment.border_box.size.block);
|
fragment.border_box.size.block);
|
||||||
self.new_fragments.push(fragment);
|
self.new_fragments.push(fragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the indentation that needs to be applied before the fragment we're reflowing.
|
||||||
|
fn indentation_for_pending_fragment(&self) -> Au {
|
||||||
|
if self.pending_line_is_empty() && self.lines.is_empty() {
|
||||||
|
self.first_line_indentation
|
||||||
|
} else {
|
||||||
|
Au(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the pending line is empty and false otherwise.
|
||||||
|
fn pending_line_is_empty(&self) -> bool {
|
||||||
|
self.pending_line.range.length() == num::zero()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a list of inline fragments, including element ranges.
|
/// Represents a list of inline fragments, including element ranges.
|
||||||
|
@ -701,6 +713,11 @@ pub struct InlineFlow {
|
||||||
/// The minimum depth below the baseline for each line, as specified by the line block-size and
|
/// The minimum depth below the baseline for each line, as specified by the line block-size and
|
||||||
/// font style.
|
/// font style.
|
||||||
pub minimum_depth_below_baseline: Au,
|
pub minimum_depth_below_baseline: Au,
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
/// compute things relative to our parent's containing block).
|
||||||
|
pub first_line_indentation: Au,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InlineFlow {
|
impl InlineFlow {
|
||||||
|
@ -711,6 +728,7 @@ impl InlineFlow {
|
||||||
lines: Vec::new(),
|
lines: Vec::new(),
|
||||||
minimum_block_size_above_baseline: Au(0),
|
minimum_block_size_above_baseline: Au(0),
|
||||||
minimum_depth_below_baseline: Au(0),
|
minimum_depth_below_baseline: Au(0),
|
||||||
|
first_line_indentation: Au(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -790,32 +808,33 @@ impl InlineFlow {
|
||||||
/// Sets fragment positions in the inline direction based on alignment for one line.
|
/// Sets fragment positions in the inline direction based on alignment for one line.
|
||||||
fn set_inline_fragment_positions(fragments: &mut InlineFragments,
|
fn set_inline_fragment_positions(fragments: &mut InlineFragments,
|
||||||
line: &Line,
|
line: &Line,
|
||||||
line_align: text_align::T) {
|
line_align: text_align::T,
|
||||||
|
indentation: Au) {
|
||||||
// Figure out how much inline-size we have.
|
// Figure out how much inline-size we have.
|
||||||
let slack_inline_size = max(Au(0), line.green_zone.inline - line.bounds.size.inline);
|
let slack_inline_size = max(Au(0), line.green_zone.inline - line.bounds.size.inline);
|
||||||
|
|
||||||
// Set the fragment inline positions based on that alignment.
|
// Set the fragment inline positions based on that alignment.
|
||||||
let mut offset = line.bounds.start.i;
|
let mut inline_start_position_for_fragment = line.bounds.start.i + indentation +
|
||||||
offset = offset + match line_align {
|
match line_align {
|
||||||
// So sorry, but justified text is more complicated than shuffling line
|
// So sorry, but justified text is more complicated than shuffling line
|
||||||
// coordinates.
|
// coordinates.
|
||||||
//
|
//
|
||||||
// TODO(burg, issue #213): Implement `text-align: justify`.
|
// TODO(burg, issue #213): Implement `text-align: justify`.
|
||||||
text_align::left | text_align::justify => Au(0),
|
text_align::left | text_align::justify => Au(0),
|
||||||
text_align::center => slack_inline_size.scale_by(0.5),
|
text_align::center => slack_inline_size.scale_by(0.5),
|
||||||
text_align::right => slack_inline_size,
|
text_align::right => slack_inline_size,
|
||||||
};
|
};
|
||||||
|
|
||||||
for fragment_index in range(line.range.begin(), line.range.end()) {
|
for fragment_index in range(line.range.begin(), line.range.end()) {
|
||||||
let fragment = fragments.get_mut(fragment_index.to_uint());
|
let fragment = fragments.get_mut(fragment_index.to_uint());
|
||||||
let size = fragment.border_box.size;
|
let size = fragment.border_box.size;
|
||||||
fragment.border_box = LogicalRect::new(fragment.style.writing_mode,
|
fragment.border_box = LogicalRect::new(fragment.style.writing_mode,
|
||||||
offset,
|
inline_start_position_for_fragment,
|
||||||
fragment.border_box.start.b,
|
fragment.border_box.start.b,
|
||||||
size.inline,
|
size.inline,
|
||||||
size.block);
|
size.block);
|
||||||
fragment.update_late_computed_inline_position_if_necessary();
|
fragment.update_late_computed_inline_position_if_necessary();
|
||||||
offset = offset + size.inline;
|
inline_start_position_for_fragment = inline_start_position_for_fragment + size.inline;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -987,7 +1006,7 @@ impl Flow for InlineFlow {
|
||||||
// TODO(pcwalton): Cache the line scanner?
|
// TODO(pcwalton): Cache the line scanner?
|
||||||
debug!("assign_block_size_inline: floats in: {}", self.base.floats);
|
debug!("assign_block_size_inline: floats in: {}", self.base.floats);
|
||||||
|
|
||||||
// assign block-size for inline fragments
|
// Assign the block-size for the inline fragments.
|
||||||
let containing_block_block_size =
|
let containing_block_block_size =
|
||||||
self.base.block_container_explicit_block_size.unwrap_or(Au(0));
|
self.base.block_container_explicit_block_size.unwrap_or(Au(0));
|
||||||
for fragment in self.fragments.fragments.iter_mut() {
|
for fragment in self.fragments.fragments.iter_mut() {
|
||||||
|
@ -995,24 +1014,32 @@ impl Flow for InlineFlow {
|
||||||
containing_block_block_size);
|
containing_block_block_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset our state, so that we handle incremental reflow correctly.
|
||||||
|
//
|
||||||
|
// TODO(pcwalton): Do something smarter, like Gecko and WebKit?
|
||||||
debug!("lines: {}", self.lines);
|
debug!("lines: {}", self.lines);
|
||||||
|
|
||||||
self.fragments.merge_broken_lines();
|
self.fragments.merge_broken_lines();
|
||||||
|
|
||||||
self.lines = Vec::new();
|
self.lines = Vec::new();
|
||||||
|
|
||||||
let scanner_floats = self.base.floats.clone();
|
// Determine how much indentation the first line wants.
|
||||||
let mut scanner = LineBreaker::new(scanner_floats);
|
let mut indentation = if self.fragments.is_empty() {
|
||||||
scanner.scan_for_lines(self, layout_context);
|
Au(0)
|
||||||
|
} else {
|
||||||
|
self.first_line_indentation
|
||||||
|
};
|
||||||
|
|
||||||
// All lines use text alignment of the flow.
|
// Perform line breaking.
|
||||||
let text_align = self.base.flags.text_align();
|
let mut scanner = LineBreaker::new(self.base.floats.clone(), indentation);
|
||||||
|
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.
|
||||||
let mut line_distance_from_flow_block_start = Au(0);
|
let mut line_distance_from_flow_block_start = Au(0);
|
||||||
for line in self.lines.iter_mut() {
|
for line in self.lines.iter_mut() {
|
||||||
// Lay out fragments in the inline direction.
|
// Lay out fragments in the inline direction.
|
||||||
InlineFlow::set_inline_fragment_positions(&mut self.fragments, line, text_align);
|
InlineFlow::set_inline_fragment_positions(&mut self.fragments,
|
||||||
|
line,
|
||||||
|
self.base.flags.text_align(),
|
||||||
|
indentation);
|
||||||
|
|
||||||
// Set the block-start position of the current line.
|
// Set the block-start position of the current line.
|
||||||
// `line_height_offset` is updated at the end of the previous loop.
|
// `line_height_offset` is updated at the end of the previous loop.
|
||||||
|
@ -1112,7 +1139,10 @@ impl Flow for InlineFlow {
|
||||||
largest_depth_below_baseline;
|
largest_depth_below_baseline;
|
||||||
line_distance_from_flow_block_start = line_distance_from_flow_block_start +
|
line_distance_from_flow_block_start = line_distance_from_flow_block_start +
|
||||||
line.bounds.size.block;
|
line.bounds.size.block;
|
||||||
} // End of `lines.each` 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.
|
// Assign block sizes for any inline-block descendants.
|
||||||
for kid in self.base.child_iter() {
|
for kid in self.base.child_iter() {
|
||||||
|
@ -1128,7 +1158,7 @@ impl Flow for InlineFlow {
|
||||||
None => Au(0),
|
None => Au(0),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.base.floats = scanner.floats();
|
self.base.floats = scanner.floats.clone();
|
||||||
self.base.floats.translate(LogicalSize::new(self.base.writing_mode,
|
self.base.floats.translate(LogicalSize::new(self.base.writing_mode,
|
||||||
Au(0),
|
Au(0),
|
||||||
-self.base.position.size.block));
|
-self.base.position.size.block));
|
||||||
|
|
|
@ -315,6 +315,7 @@ impl Flow for TableFlow {
|
||||||
self.block_flow.base.flags.remove(IMPACTED_BY_RIGHT_FLOATS);
|
self.block_flow.base.flags.remove(IMPACTED_BY_RIGHT_FLOATS);
|
||||||
|
|
||||||
self.block_flow.propagate_assigned_inline_size_to_children(
|
self.block_flow.propagate_assigned_inline_size_to_children(
|
||||||
|
layout_context,
|
||||||
inline_start_content_edge,
|
inline_start_content_edge,
|
||||||
content_inline_size,
|
content_inline_size,
|
||||||
Some(self.column_computed_inline_sizes.as_slice()));
|
Some(self.column_computed_inline_sizes.as_slice()));
|
||||||
|
|
|
@ -96,7 +96,7 @@ impl Flow for TableCellFlow {
|
||||||
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments.
|
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments.
|
||||||
/// When called on this context, the context has had its inline-size set by the parent table
|
/// When called on this context, the context has had its inline-size set by the parent table
|
||||||
/// row.
|
/// row.
|
||||||
fn assign_inline_sizes(&mut self, ctx: &LayoutContext) {
|
fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
|
||||||
let _scope = layout_debug_scope!("table_cell::assign_inline_sizes {:x}",
|
let _scope = layout_debug_scope!("table_cell::assign_inline_sizes {:x}",
|
||||||
self.block_flow.base.debug_id());
|
self.block_flow.base.debug_id());
|
||||||
debug!("assign_inline_sizes({}): assigning inline_size for flow", "table_cell");
|
debug!("assign_inline_sizes({}): assigning inline_size for flow", "table_cell");
|
||||||
|
@ -107,7 +107,7 @@ impl Flow for TableCellFlow {
|
||||||
let inline_size_computer = InternalTable;
|
let inline_size_computer = InternalTable;
|
||||||
|
|
||||||
inline_size_computer.compute_used_inline_size(&mut self.block_flow,
|
inline_size_computer.compute_used_inline_size(&mut self.block_flow,
|
||||||
ctx,
|
layout_context,
|
||||||
containing_block_inline_size);
|
containing_block_inline_size);
|
||||||
|
|
||||||
let inline_start_content_edge =
|
let inline_start_content_edge =
|
||||||
|
@ -117,7 +117,8 @@ impl Flow for TableCellFlow {
|
||||||
let content_inline_size =
|
let content_inline_size =
|
||||||
self.block_flow.fragment.border_box.size.inline - padding_and_borders;
|
self.block_flow.fragment.border_box.size.inline - padding_and_borders;
|
||||||
|
|
||||||
self.block_flow.propagate_assigned_inline_size_to_children(inline_start_content_edge,
|
self.block_flow.propagate_assigned_inline_size_to_children(layout_context,
|
||||||
|
inline_start_content_edge,
|
||||||
content_inline_size,
|
content_inline_size,
|
||||||
None);
|
None);
|
||||||
}
|
}
|
||||||
|
|
|
@ -234,6 +234,7 @@ impl Flow for TableRowFlow {
|
||||||
containing_block_inline_size);
|
containing_block_inline_size);
|
||||||
|
|
||||||
self.block_flow.propagate_assigned_inline_size_to_children(
|
self.block_flow.propagate_assigned_inline_size_to_children(
|
||||||
|
layout_context,
|
||||||
inline_start_content_edge,
|
inline_start_content_edge,
|
||||||
containing_block_inline_size,
|
containing_block_inline_size,
|
||||||
Some(self.column_computed_inline_sizes.as_slice()));
|
Some(self.column_computed_inline_sizes.as_slice()));
|
||||||
|
|
|
@ -143,7 +143,7 @@ impl Flow for TableRowGroupFlow {
|
||||||
|
|
||||||
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments.
|
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments.
|
||||||
/// When called on this context, the context has had its inline-size set by the parent context.
|
/// When called on this context, the context has had its inline-size set by the parent context.
|
||||||
fn assign_inline_sizes(&mut self, ctx: &LayoutContext) {
|
fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
|
||||||
let _scope = layout_debug_scope!("table_rowgroup::assign_inline_sizes {:x}",
|
let _scope = layout_debug_scope!("table_rowgroup::assign_inline_sizes {:x}",
|
||||||
self.block_flow.base.debug_id());
|
self.block_flow.base.debug_id());
|
||||||
debug!("assign_inline_sizes({}): assigning inline_size for flow", "table_rowgroup");
|
debug!("assign_inline_sizes({}): assigning inline_size for flow", "table_rowgroup");
|
||||||
|
@ -157,10 +157,11 @@ impl Flow for TableRowGroupFlow {
|
||||||
|
|
||||||
let inline_size_computer = InternalTable;
|
let inline_size_computer = InternalTable;
|
||||||
inline_size_computer.compute_used_inline_size(&mut self.block_flow,
|
inline_size_computer.compute_used_inline_size(&mut self.block_flow,
|
||||||
ctx,
|
layout_context,
|
||||||
containing_block_inline_size);
|
containing_block_inline_size);
|
||||||
|
|
||||||
self.block_flow.propagate_assigned_inline_size_to_children(
|
self.block_flow.propagate_assigned_inline_size_to_children(
|
||||||
|
layout_context,
|
||||||
inline_start_content_edge,
|
inline_start_content_edge,
|
||||||
content_inline_size,
|
content_inline_size,
|
||||||
Some(self.column_computed_inline_sizes.as_slice()));
|
Some(self.column_computed_inline_sizes.as_slice()));
|
||||||
|
|
|
@ -299,12 +299,14 @@ impl Flow for TableWrapperFlow {
|
||||||
match assigned_column_inline_sizes {
|
match assigned_column_inline_sizes {
|
||||||
None => {
|
None => {
|
||||||
self.block_flow.propagate_assigned_inline_size_to_children(
|
self.block_flow.propagate_assigned_inline_size_to_children(
|
||||||
|
layout_context,
|
||||||
inline_start_content_edge,
|
inline_start_content_edge,
|
||||||
content_inline_size,
|
content_inline_size,
|
||||||
None)
|
None)
|
||||||
}
|
}
|
||||||
Some(ref assigned_column_inline_sizes) => {
|
Some(ref assigned_column_inline_sizes) => {
|
||||||
self.block_flow.propagate_assigned_inline_size_to_children(
|
self.block_flow.propagate_assigned_inline_size_to_children(
|
||||||
|
layout_context,
|
||||||
inline_start_content_edge,
|
inline_start_content_edge,
|
||||||
content_inline_size,
|
content_inline_size,
|
||||||
Some(assigned_column_inline_sizes.as_slice()));
|
Some(assigned_column_inline_sizes.as_slice()));
|
||||||
|
|
|
@ -1111,6 +1111,8 @@ pub mod longhands {
|
||||||
}
|
}
|
||||||
</%self:single_component_value>
|
</%self:single_component_value>
|
||||||
|
|
||||||
|
${predefined_type("text-indent", "LengthOrPercentage", "computed::LP_Length(Au(0))")}
|
||||||
|
|
||||||
${new_style_struct("Text", is_inherited=False)}
|
${new_style_struct("Text", is_inherited=False)}
|
||||||
|
|
||||||
<%self:longhand name="text-decoration">
|
<%self:longhand name="text-decoration">
|
||||||
|
|
|
@ -197,3 +197,4 @@ fragment=top != ../html/acid2.html acid2_ref.html
|
||||||
!= border_black_groove.html border_black_solid.html
|
!= border_black_groove.html border_black_solid.html
|
||||||
!= border_black_ridge.html border_black_solid.html
|
!= border_black_ridge.html border_black_solid.html
|
||||||
!= border_black_ridge.html border_black_groove.html
|
!= border_black_ridge.html border_black_groove.html
|
||||||
|
== text_indent_a.html text_indent_ref.html
|
||||||
|
|
40
tests/ref/text_indent_a.html
Normal file
40
tests/ref/text_indent_a.html
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<!-- Tests that `text-indent` works, in particular when combined with `text-align`. -->
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" type="text/css" href="css/ahem.css">
|
||||||
|
<style>
|
||||||
|
section {
|
||||||
|
color: blue;
|
||||||
|
position: absolute;
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
#a {
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
text-indent: 100px;
|
||||||
|
}
|
||||||
|
#b {
|
||||||
|
left: 0;
|
||||||
|
top: 200px;
|
||||||
|
text-indent: 50%;
|
||||||
|
}
|
||||||
|
#b2 {
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
#c {
|
||||||
|
left: 200px;
|
||||||
|
top: 0;
|
||||||
|
text-align: right;
|
||||||
|
text-indent: 100px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<section id=a>X X</section>
|
||||||
|
<section id=b><section id=b2>X X</section></section>
|
||||||
|
<section id=c>X X</section>
|
||||||
|
</body>
|
||||||
|
</html>
|
46
tests/ref/text_indent_ref.html
Normal file
46
tests/ref/text_indent_ref.html
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<!-- Tests that `text-indent` works, in particular when combined with `text-align`. -->
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" type="text/css" href="css/ahem.css">
|
||||||
|
<style>
|
||||||
|
section {
|
||||||
|
background: blue;
|
||||||
|
position: absolute;
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
#a {
|
||||||
|
top: 0;
|
||||||
|
left: 100px;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
#b {
|
||||||
|
top: 100px;
|
||||||
|
left: 0;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
#c {
|
||||||
|
top: 200px;
|
||||||
|
left: 100px;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
#d {
|
||||||
|
top: 300px;
|
||||||
|
left: 0;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
#e {
|
||||||
|
top: 0;
|
||||||
|
left: 300px;
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<section id=a></section>
|
||||||
|
<section id=b></section>
|
||||||
|
<section id=c></section>
|
||||||
|
<section id=d></section>
|
||||||
|
<section id=e></section>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Add table
Add a link
Reference in a new issue