layout: Re-enable parallel layout by removing all RefCell instances

from `Flow`s; in the process, remove `InlineInfo` in favor of the
range-based design that was originally planned and halfway implemented.

Now, the DOM tree structure for inline flows is reflected not by a
series of arrays but instead by a flat list of ranges into the list of
boxes. As part of this, the `border` and `padding` fields, which were
incorrect in the case of inlines and necessitated separate
`noncontent_inline_foo` methods, have been merged into a single
`border_padding` field that is always guaranteed to be correct after
width assignment, even for inlines.
This commit is contained in:
Patrick Walton 2014-05-02 11:47:08 -07:00
parent 85393c6931
commit 27276c0305
24 changed files with 1505 additions and 1641 deletions

View file

@ -228,6 +228,16 @@ pub struct SolidColorDisplayItem {
pub color: Color,
}
/// Text decoration information.
pub struct TextDecorations {
/// The color to use for underlining, if any.
pub underline: Option<Color>,
/// The color to use for overlining, if any.
pub overline: Option<Color>,
/// The color to use for line-through, if any.
pub line_through: Option<Color>,
}
/// Renders text.
pub struct TextDisplayItem {
/// Fields common to all display items.
@ -242,31 +252,10 @@ pub struct TextDisplayItem {
/// The color of the text.
pub text_color: Color,
/// A bitfield of flags for text display items.
pub flags: TextDisplayItemFlags,
/// The color of text-decorations
pub underline_color: Color,
pub overline_color: Color,
pub line_through_color: Color,
/// Text decorations in effect.
pub text_decorations: TextDecorations,
}
/// Flags for text display items.
pub struct TextDisplayItemFlags(pub u8);
impl TextDisplayItemFlags {
pub fn new() -> TextDisplayItemFlags {
TextDisplayItemFlags(0)
}
}
// Whether underlining is forced on.
bitfield!(TextDisplayItemFlags, override_underline, set_override_underline, 0x01)
// Whether overlining is forced on.
bitfield!(TextDisplayItemFlags, override_overline, set_override_overline, 0x02)
// Whether line-through is forced on.
bitfield!(TextDisplayItemFlags, override_line_through, set_override_line_through, 0x04)
/// Renders an image.
pub struct ImageDisplayItem {
pub base: BaseDisplayItem,
@ -369,22 +358,24 @@ impl DisplayItem {
let strikeout_size = font_metrics.strikeout_size;
let strikeout_offset = font_metrics.strikeout_offset;
if text_run.decoration.underline || text.flags.override_underline() {
for underline_color in text.text_decorations.underline.iter() {
let underline_y = baseline_origin.y - underline_offset;
let underline_bounds = Rect(Point2D(baseline_origin.x, underline_y),
Size2D(width, underline_size));
render_context.draw_solid_color(&underline_bounds, text.underline_color);
render_context.draw_solid_color(&underline_bounds, *underline_color);
}
if text_run.decoration.overline || text.flags.override_overline() {
for overline_color in text.text_decorations.overline.iter() {
let overline_bounds = Rect(Point2D(baseline_origin.x, origin.y),
Size2D(width, underline_size));
render_context.draw_solid_color(&overline_bounds, text.overline_color);
render_context.draw_solid_color(&overline_bounds, *overline_color);
}
if text_run.decoration.line_through || text.flags.override_line_through() {
for line_through_color in text.text_decorations.line_through.iter() {
let strikeout_y = baseline_origin.y - strikeout_offset;
let strikeout_bounds = Rect(Point2D(baseline_origin.x, strikeout_y),
Size2D(width, strikeout_size));
render_context.draw_solid_color(&strikeout_bounds, text.line_through_color);
render_context.draw_solid_color(&strikeout_bounds, *line_through_color);
}
}

View file

@ -23,6 +23,7 @@ use layout::flow;
use layout::model::{Auto, IntrinsicWidths, MarginCollapseInfo, MarginsCollapse};
use layout::model::{MarginsCollapseThrough, MaybeAuto, NoCollapsibleMargins, Specified, specified};
use layout::model::{specified_or_none};
use layout::model;
use layout::wrapper::ThreadSafeLayoutNode;
use style::ComputedValues;
use style::computed_values::{clear, position};
@ -468,12 +469,12 @@ pub enum MarginsMayCollapseFlag {
fn propagate_layer_flag_from_child(layers_needed_for_descendants: &mut bool, kid: &mut Flow) {
if kid.is_absolute_containing_block() {
let kid_base = flow::mut_base(kid);
if kid_base.flags_info.flags.needs_layer() {
if kid_base.flags.needs_layer() {
*layers_needed_for_descendants = true
}
} else {
let kid_base = flow::mut_base(kid);
if kid_base.flags_info.flags.layers_needed_for_descendants() {
if kid_base.flags.layers_needed_for_descendants() {
*layers_needed_for_descendants = true
}
}
@ -609,14 +610,14 @@ impl BlockFlow {
/// positioned elements.
/// Note: Assume this is called in a top-down traversal, so it is ok to
/// reference the CB.
#[inline]
pub fn containing_block_size(&mut self, viewport_size: Size2D<Au>) -> Size2D<Au> {
assert!(self.is_absolutely_positioned());
if self.is_fixed() {
// Initial containing block is the CB for the root
viewport_size
} else {
let cb = self.base.absolute_cb.resolve().unwrap();
cb.generated_cb_size()
self.base.absolute_cb.generated_containing_block_rect().size
}
}
@ -640,7 +641,7 @@ impl BlockFlow {
return false
}
let cb_top_edge_offset = flow.generated_cb_position().y;
let cb_top_edge_offset = flow.generated_containing_block_rect().origin.y;
let mut descendant_offset_iter = mut_base(flow).abs_descendants.iter_with_offset();
// Pass in the respective static y offset for each descendant.
for (ref mut descendant_link, ref y_offset) in descendant_offset_iter {
@ -780,10 +781,8 @@ impl BlockFlow {
self.base.position.size.height = self.base.position.size.height + top_margin_value +
bottom_margin_value;
let mut position = *self.box_.border_box.borrow();
position.size.height = position.size.height + top_margin_value + bottom_margin_value;
*self.box_.border_box.borrow_mut() = position;
self.box_.border_box.size.height = self.box_.border_box.size.height + top_margin_value +
bottom_margin_value;
}
/// Assign height for current flow.
@ -820,14 +819,13 @@ impl BlockFlow {
self.base.floats.translate(Point2D(-self.box_.left_offset(), Au(0)));
// The sum of our top border and top padding.
let top_offset = self.box_.border.borrow().top + self.box_.padding.borrow().top;
let top_offset = self.box_.border_padding.top;
translate_including_floats(&mut cur_y, top_offset, inorder, &mut self.base.floats);
let can_collapse_top_margin_with_kids =
margins_may_collapse == MarginsMayCollapse &&
!self.is_absolutely_positioned() &&
self.box_.border.borrow().top == Au(0) &&
self.box_.padding.borrow().top == Au(0);
self.box_.border_padding.top == Au(0);
margin_collapse_info.initialize_top_margin(&self.box_,
can_collapse_top_margin_with_kids);
@ -930,10 +928,7 @@ impl BlockFlow {
translate_including_floats(&mut cur_y, delta, inorder, &mut floats);
}
self.base
.flags_info
.flags
.set_layers_needed_for_descendants(layers_needed_for_descendants);
self.base.flags.set_layers_needed_for_descendants(layers_needed_for_descendants);
self.collect_static_y_offsets_from_kids();
@ -941,8 +936,7 @@ impl BlockFlow {
let can_collapse_bottom_margin_with_kids =
margins_may_collapse == MarginsMayCollapse &&
!self.is_absolutely_positioned() &&
self.box_.border.borrow().bottom == Au(0) &&
self.box_.padding.borrow().bottom == Au(0);
self.box_.border_padding.bottom == Au(0);
let (collapsible_margins, delta) =
margin_collapse_info.finish_and_compute_collapsible_margins(
&self.box_,
@ -966,14 +960,12 @@ impl BlockFlow {
// Fixed position layers get layers.
if self.is_fixed() {
self.base.flags_info.flags.set_needs_layer(true)
self.base.flags.set_needs_layer(true)
}
// Store the content height for use in calculating the absolute flow's dimensions
// later.
let mut temp_position = *self.box_.border_box.borrow();
temp_position.size.height = height;
*self.box_.border_box.borrow_mut() = temp_position;
self.box_.border_box.size.height = height;
return
}
@ -992,15 +984,13 @@ impl BlockFlow {
translate_including_floats(&mut cur_y, delta, inorder, &mut floats);
// Compute content height and noncontent height.
let bottom_offset = self.box_.border.borrow().bottom + self.box_.padding.borrow().bottom;
let bottom_offset = self.box_.border_padding.bottom;
translate_including_floats(&mut cur_y, bottom_offset, inorder, &mut floats);
// Now that `cur_y` is at the bottom of the border box, compute the final border box
// position.
let mut border_box = *self.box_.border_box.borrow();
border_box.size.height = cur_y;
border_box.origin.y = Au(0);
*self.box_.border_box.borrow_mut() = border_box;
self.box_.border_box.size.height = cur_y;
self.box_.border_box.origin.y = Au(0);
self.base.position.size.height = cur_y;
self.base.floats = floats.clone();
@ -1032,21 +1022,16 @@ impl BlockFlow {
/// Therefore, assign_height_float was already called on this kid flow by
/// the traversal function. So, the values used are well-defined.
pub fn assign_height_float_inorder(&mut self) {
let height = self.box_.border_box.borrow().size.height;
let height = self.box_.border_box.size.height;
let clearance = match self.box_.clear() {
None => Au(0),
Some(clear) => self.base.floats.clearance(clear),
};
let noncontent_width = self.box_.padding.borrow().left + self.box_.padding.borrow().right +
self.box_.border.borrow().left + self.box_.border.borrow().right;
let full_noncontent_width = noncontent_width + self.box_.margin.borrow().left +
self.box_.margin.borrow().right;
let margin_height = self.box_.margin.borrow().top + self.box_.margin.borrow().bottom;
let margin_height = self.box_.margin.vertical();
let info = PlacementInfo {
size: Size2D(self.base.position.size.width + full_noncontent_width,
size: Size2D(self.base.position.size.width + self.box_.margin.horizontal() +
self.box_.border_padding.horizontal(),
height + margin_height),
ceiling: clearance + self.base.position.origin.y,
max_width: self.float.get_ref().containing_width,
@ -1082,10 +1067,9 @@ impl BlockFlow {
// Floats establish a block formatting context, so we discard the output floats here.
drop(floats);
}
let mut cur_y = Au(0);
let top_offset = self.box_.margin.borrow().top + self.box_.border.borrow().top + self.box_.padding.borrow().top;
cur_y = cur_y + top_offset;
let top_offset = self.box_.margin.top + self.box_.border_padding.top;
let mut cur_y = top_offset;
// cur_y is now at the top content edge
@ -1098,17 +1082,10 @@ impl BlockFlow {
let content_height = cur_y - top_offset;
let mut noncontent_height;
let mut position = *self.box_.border_box.borrow();
// The associated box is the border box of this flow.
position.origin.y = self.box_.margin.borrow().top;
noncontent_height = self.box_.padding.borrow().top + self.box_.padding.borrow().bottom +
self.box_.border.borrow().top + self.box_.border.borrow().bottom;
self.box_.border_box.origin.y = self.box_.margin.top;
// Calculate content height, taking `min-height` and `max-height` into account.
let mut candidate_height_iterator = CandidateHeightIterator::new(self.box_.style(), None);
for (candidate_height, new_candidate_height) in candidate_height_iterator {
*new_candidate_height = match candidate_height {
@ -1118,11 +1095,9 @@ impl BlockFlow {
}
let content_height = candidate_height_iterator.candidate_value;
let noncontent_height = self.box_.border_padding.vertical();
debug!("assign_height_float -- height: {}", content_height + noncontent_height);
position.size.height = content_height + noncontent_height;
*self.box_.border_box.borrow_mut() = position;
self.box_.border_box.size.height = content_height + noncontent_height;
}
fn build_display_list_block_common(&mut self,
@ -1132,25 +1107,25 @@ impl BlockFlow {
offset: Point2D<Au>,
background_border_level: BackgroundAndBorderLevel) {
let mut info = *info;
let rel_offset = self.box_.relative_position(&info.relative_containing_block_size);
let rel_offset = self.box_.relative_position(&info.relative_containing_block_size, None);
// Add the box that starts the block context.
self.box_.build_display_list(stacking_context,
builder,
&info,
self.base.abs_position + rel_offset + offset,
(&*self) as &Flow,
background_border_level);
background_border_level,
None);
// For relatively-positioned descendants, the containing block formed by a block is
// just the content box. The containing block for absolutely-positioned descendants,
// on the other hand, only established if we are positioned.
info.relative_containing_block_size = self.box_.content_box_size();
info.relative_containing_block_size = self.box_.content_box().size;
if self.is_positioned() {
info.absolute_containing_block_position =
self.base.abs_position +
self.generated_cb_position() +
self.box_.relative_position(&info.relative_containing_block_size)
self.generated_containing_block_rect().origin +
self.box_.relative_position(&info.relative_containing_block_size, None)
}
let this_position = self.base.abs_position;
@ -1172,7 +1147,7 @@ impl BlockFlow {
// Process absolute descendant links.
let mut absolute_info = info;
absolute_info.layers_needed_for_positioned_flows =
self.base.flags_info.flags.layers_needed_for_descendants();
self.base.flags.layers_needed_for_descendants();
for abs_descendant_link in self.base.abs_descendants.iter() {
match abs_descendant_link.resolve() {
Some(flow) => {
@ -1233,53 +1208,40 @@ impl BlockFlow {
let static_y_offset = self.static_y_offset;
// This is the stored content height value from assign-height
let content_height = self.box_.border_box.borrow().size.height - self.box_.noncontent_height();
let style = self.box_.style();
// Non-auto margin-top and margin-bottom values have already been
// calculated during assign-width.
let mut margin = self.box_.margin.borrow_mut();
let margin_top = match MaybeAuto::from_style(style.Margin.get().margin_top, Au(0)) {
Auto => Auto,
_ => Specified(margin.top)
};
let margin_bottom = match MaybeAuto::from_style(style.Margin.get().margin_bottom, Au(0)) {
Auto => Auto,
_ => Specified(margin.bottom)
};
let (top, bottom) =
(MaybeAuto::from_style(style.PositionOffsets.get().top, containing_block_height),
MaybeAuto::from_style(style.PositionOffsets.get().bottom, containing_block_height));
let available_height = containing_block_height - self.box_.noncontent_height();
let content_height = self.box_.content_box().size.height;
let mut solution = None;
if self.is_replaced_content() {
// Calculate used value of height just like we do for inline replaced elements.
// TODO: Pass in the containing block height when Box's
// assign-height can handle it correctly.
self.box_.assign_replaced_height_if_necessary();
// TODO: Right now, this content height value includes the
// margin because of erroneous height calculation in Box_.
// Check this when that has been fixed.
let height_used_val = self.box_.border_box.borrow().size.height;
solution = Some(HeightConstraintSolution::solve_vertical_constraints_abs_replaced(
height_used_val,
margin_top,
margin_bottom,
top,
bottom,
content_height,
available_height,
static_y_offset));
} else {
let mut candidate_height_iterator =
CandidateHeightIterator::new(style, Some(containing_block_height));
{
// Non-auto margin-top and margin-bottom values have already been
// calculated during assign-width.
let margin_top = match self.box_.style().Margin.get().margin_top {
LPA_Auto => Auto,
_ => Specified(self.box_.margin.top)
};
let margin_bottom = match self.box_.style().Margin.get().margin_bottom {
LPA_Auto => Auto,
_ => Specified(self.box_.margin.bottom)
};
for (height_used_val, new_candidate_height) in candidate_height_iterator {
solution =
Some(HeightConstraintSolution::solve_vertical_constraints_abs_nonreplaced(
let top;
let bottom;
{
let position_style = self.box_.style().PositionOffsets.get();
top = MaybeAuto::from_style(position_style.top, containing_block_height);
bottom = MaybeAuto::from_style(position_style.bottom, containing_block_height);
}
let available_height = containing_block_height - self.box_.border_padding.vertical();
if self.is_replaced_content() {
// Calculate used value of height just like we do for inline replaced elements.
// TODO: Pass in the containing block height when Box's
// assign-height can handle it correctly.
self.box_.assign_replaced_height_if_necessary();
// TODO: Right now, this content height value includes the
// margin because of erroneous height calculation in Box_.
// Check this when that has been fixed.
let height_used_val = self.box_.border_box.size.height;
solution = Some(HeightConstraintSolution::solve_vertical_constraints_abs_replaced(
height_used_val,
margin_top,
margin_bottom,
@ -1288,24 +1250,36 @@ impl BlockFlow {
content_height,
available_height,
static_y_offset));
} else {
let style = self.box_.style();
let mut candidate_height_iterator =
CandidateHeightIterator::new(style, Some(containing_block_height));
*new_candidate_height = solution.unwrap().height
for (height_used_val, new_candidate_height) in candidate_height_iterator {
solution =
Some(HeightConstraintSolution::solve_vertical_constraints_abs_nonreplaced(
height_used_val,
margin_top,
margin_bottom,
top,
bottom,
content_height,
available_height,
static_y_offset));
*new_candidate_height = solution.unwrap().height
}
}
}
let solution = solution.unwrap();
self.box_.margin.top = solution.margin_top;
self.box_.margin.bottom = solution.margin_bottom;
self.box_.border_box.origin.y = Au(0);
self.box_.border_box.size.height = solution.height + self.box_.border_padding.vertical();
margin.top = solution.margin_top;
margin.bottom = solution.margin_bottom;
let mut position = self.box_.border_box.borrow_mut();
position.origin.y = Au(0);
// Border box height
let border_and_padding = self.box_.noncontent_height();
position.size.height = solution.height + border_and_padding;
self.base.position.origin.y = solution.top + margin.top;
self.base.position.size.height = solution.height + border_and_padding;
self.base.position.origin.y = solution.top + self.box_.margin.top;
self.base.position.size.height = solution.height + self.box_.border_padding.vertical();
}
/// Add display items for Absolutely Positioned flow.
@ -1335,7 +1309,7 @@ impl BlockFlow {
Point2D(Au(0), Au(0)),
RootOfStackingContextLevel);
if !info.layers_needed_for_positioned_flows && !self.base.flags_info.flags.needs_layer() {
if !info.layers_needed_for_positioned_flows && !self.base.flags.needs_layer() {
// We didn't need a layer.
//
// TODO(#781, pcwalton): `z-index`.
@ -1381,7 +1355,7 @@ impl BlockFlow {
self.float.get_mut_ref().containing_width = containing_block_width;
// Parent usually sets this, but floats are never inorder
self.base.flags_info.flags.set_inorder(false);
self.base.flags.set_inorder(false)
}
}
@ -1393,7 +1367,7 @@ impl BlockFlow {
let has_inorder_children = if self.is_float() {
self.base.num_floats > 0
} else {
self.base.flags_info.flags.inorder() || self.base.num_floats > 0
self.base.flags.inorder() || self.base.num_floats > 0
};
let kid_abs_cb_x_offset;
@ -1401,7 +1375,8 @@ impl BlockFlow {
// Pass yourself as a new Containing Block
// The static x offset for any immediate kid flows will be the
// left padding
kid_abs_cb_x_offset = self.box_.padding.borrow().left;
kid_abs_cb_x_offset = self.box_.border_padding.left -
model::border_from_style(self.box_.style()).left;
} else {
// For kids, the left margin edge will be at our left content edge.
// The current static offset is at our left margin
@ -1413,7 +1388,7 @@ impl BlockFlow {
// This value is used only for table cells.
let mut kid_left_margin_edge = left_content_edge;
let flags_info = self.base.flags_info.clone();
let flags = self.base.flags.clone();
for (i, kid) in self.base.child_iter().enumerate() {
if kid.is_block_flow() {
let kid_block = kid.as_block();
@ -1427,9 +1402,9 @@ impl BlockFlow {
child_base.position.origin.x = left_content_edge;
// Width of kid flow is our content width
child_base.position.size.width = content_width;
child_base.flags_info.flags.set_inorder(has_inorder_children);
child_base.flags.set_inorder(has_inorder_children);
if !child_base.flags_info.flags.inorder() {
if !child_base.flags.inorder() {
child_base.floats = Floats::new();
}
}
@ -1467,12 +1442,10 @@ impl BlockFlow {
None => {}
}
// Per CSS 2.1 § 16.3.1, text decoration 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.
let child_base = flow::mut_base(kid);
child_base.flags_info.propagate_text_decoration_from_parent(&flags_info);
child_base.flags_info.propagate_text_alignment_from_parent(&flags_info)
flow::mut_base(kid).flags.propagate_text_alignment_from_parent(flags.clone())
}
}
}
@ -1518,13 +1491,7 @@ impl Flow for BlockFlow {
self.base.num_floats = num_floats;
}
// Add in borders, padding, and margins.
{
// Can compute border width here since it doesn't depend on anything.
self.box_.compute_borders(self.box_.style())
}
let box_intrinsic_widths = self.box_.intrinsic_widths();
let box_intrinsic_widths = self.box_.intrinsic_widths(None);
intrinsic_widths.minimum_width = geometry::max(intrinsic_widths.minimum_width,
box_intrinsic_widths.minimum_width);
intrinsic_widths.preferred_width = geometry::max(intrinsic_widths.preferred_width,
@ -1553,7 +1520,7 @@ impl Flow for BlockFlow {
self.base.position.size.width = ctx.screen_size.width;
self.base.floats = Floats::new();
// Root element is not floated
self.base.flags_info.flags.set_inorder(false);
self.base.flags.set_inorder(false);
}
// The position was set to the containing block by the flow's parent.
@ -1568,11 +1535,9 @@ impl Flow for BlockFlow {
self.base.clear = self.box_.style().Box.get().clear;
// Move in from the left border edge
let left_content_edge = self.box_.border_box.borrow().origin.x
+ self.box_.padding.borrow().left + self.box_.border.borrow().left;
let padding_and_borders = self.box_.padding.borrow().left + self.box_.padding.borrow().right +
self.box_.border.borrow().left + self.box_.border.borrow().right;
let content_width = self.box_.border_box.borrow().size.width - padding_and_borders;
let left_content_edge = self.box_.border_box.origin.x + self.box_.border_padding.left;
let padding_and_borders = self.box_.border_padding.horizontal();
let content_width = self.box_.border_box.size.width - padding_and_borders;
if self.is_float() {
self.base.position.size.width = content_width;
@ -1645,17 +1610,13 @@ impl Flow for BlockFlow {
self.is_relatively_positioned() || self.is_root()
}
/// Return the dimensions of the CB generated _by_ this flow for absolute descendants.
///
/// For Blocks, this will be the padding box.
fn generated_cb_size(&self) -> Size2D<Au> {
self.box_.padding_box_size()
}
/// Return position of the CB generated by this flow from the start of this flow.
fn generated_cb_position(&self) -> Point2D<Au> {
// Border box y coordinate + border top
self.box_.border_box.borrow().origin + Point2D(self.box_.border.borrow().left, self.box_.border.borrow().top)
/// Return the dimensions of the containing block generated by this flow for absolutely-
/// positioned descendants. For block flows, this is the padding box.
fn generated_containing_block_rect(&self) -> Rect<Au> {
let border = model::border_from_style(self.box_.style());
Rect(self.box_.border_box.origin + Point2D(border.left, border.top),
Size2D(self.box_.border_box.size.width - border.horizontal(),
self.box_.border_box.size.height - border.vertical()))
}
fn layer_id(&self, fragment_index: uint) -> LayerId {
@ -1762,19 +1723,16 @@ pub trait WidthAndMarginsComputer {
block: &mut BlockFlow,
parent_flow_width: Au,
ctx: &mut LayoutContext)
-> WidthConstraintInput {
-> WidthConstraintInput {
let containing_block_width = self.containing_block_width(block, parent_flow_width, ctx);
let computed_width = self.initial_computed_width(block, parent_flow_width, ctx);
block.box_.compute_border_padding_margins(containing_block_width, None);
let style = block.box_.style();
// The text alignment of a block flow is the text alignment of its box's style.
block.base.flags_info.flags.set_text_align(style.InheritedText.get().text_align);
block.box_.compute_padding(style, containing_block_width);
// We calculate and set margin-top and margin-bottom here
// because CSS 2.1 defines % on this wrt CB *width*.
block.box_.compute_margin_top_bottom(containing_block_width);
block.base.flags.set_text_align(style.InheritedText.get().text_align);
let (margin_left, margin_right) =
(MaybeAuto::from_style(style.Margin.get().margin_left, containing_block_width),
@ -1783,7 +1741,7 @@ pub trait WidthAndMarginsComputer {
let (left, right) =
(MaybeAuto::from_style(style.PositionOffsets.get().left, containing_block_width),
MaybeAuto::from_style(style.PositionOffsets.get().right, containing_block_width));
let available_width = containing_block_width - block.box_.noncontent_width();
let available_width = containing_block_width - block.box_.border_padding.horizontal();
return WidthConstraintInput::new(computed_width,
margin_left,
margin_right,
@ -1805,17 +1763,14 @@ pub trait WidthAndMarginsComputer {
block: &mut BlockFlow,
solution: WidthConstraintSolution) {
let box_ = block.box_();
let mut margin = box_.margin.borrow_mut();
margin.left = solution.margin_left;
margin.right = solution.margin_right;
box_.margin.left = solution.margin_left;
box_.margin.right = solution.margin_right;
// The associated box is the border box of this flow.
let mut position_ref = box_.border_box.borrow_mut();
// Left border edge.
position_ref.origin.x = margin.left;
// Border box width
position_ref.size.width = solution.width + box_.noncontent_width();
box_.border_box.origin.x = box_.margin.left;
// Border box width.
box_.border_box.size.width = solution.width + box_.border_padding.horizontal();
}
/// Set the x coordinate of the given flow if it is absolutely positioned.
@ -1824,7 +1779,7 @@ pub trait WidthAndMarginsComputer {
/// Solve the width and margins constraints for this block flow.
fn solve_width_constraints(&self,
block: &mut BlockFlow,
input: WidthConstraintInput)
input: &WidthConstraintInput)
-> WidthConstraintSolution;
fn initial_computed_width(&self,
@ -1855,14 +1810,14 @@ pub trait WidthAndMarginsComputer {
let containing_block_width = self.containing_block_width(block, parent_flow_width, ctx);
let mut solution = self.solve_width_constraints(block, input);
let mut solution = self.solve_width_constraints(block, &input);
// If the tentative used width is greater than 'max-width', width should be recalculated,
// but this time using the computed value of 'max-width' as the computed value for 'width'.
match specified_or_none(block.box_().style().Box.get().max_width, containing_block_width) {
Some(max_width) if max_width < solution.width => {
input.computed_width = Specified(max_width);
solution = self.solve_width_constraints(block, input);
solution = self.solve_width_constraints(block, &input);
}
_ => {}
}
@ -1873,7 +1828,7 @@ pub trait WidthAndMarginsComputer {
containing_block_width);
if computed_min_width > solution.width {
input.computed_width = Specified(computed_min_width);
solution = self.solve_width_constraints(block, input);
solution = self.solve_width_constraints(block, &input);
}
self.set_width_constraint_solutions(block, solution);
@ -1889,7 +1844,7 @@ pub trait WidthAndMarginsComputer {
/// where available_width = CB width - (horizontal border + padding)
fn solve_block_width_constraints(&self,
_: &mut BlockFlow,
input: WidthConstraintInput)
input: &WidthConstraintInput)
-> WidthConstraintSolution {
let (computed_width, left_margin, right_margin, available_width) = (input.computed_width,
input.left_margin,
@ -1973,9 +1928,9 @@ impl WidthAndMarginsComputer for AbsoluteNonReplaced {
/// Return the solution for the equation.
fn solve_width_constraints(&self,
block: &mut BlockFlow,
input: WidthConstraintInput)
input: &WidthConstraintInput)
-> WidthConstraintSolution {
let WidthConstraintInput {
let &WidthConstraintInput {
computed_width,
left_margin,
right_margin,
@ -2119,11 +2074,9 @@ impl WidthAndMarginsComputer for AbsoluteReplaced {
/// [aka available_width]
///
/// Return the solution for the equation.
fn solve_width_constraints(&self,
_: &mut BlockFlow,
input: WidthConstraintInput)
fn solve_width_constraints(&self, _: &mut BlockFlow, input: &WidthConstraintInput)
-> WidthConstraintSolution {
let WidthConstraintInput {
let &WidthConstraintInput {
computed_width,
left_margin,
right_margin,
@ -2213,7 +2166,7 @@ impl WidthAndMarginsComputer for AbsoluteReplaced {
-> MaybeAuto {
let containing_block_width = block.containing_block_size(ctx.screen_size).width;
let box_ = block.box_();
box_.assign_replaced_width_if_necessary(containing_block_width);
box_.assign_replaced_width_if_necessary(containing_block_width, None);
// For replaced absolute flow, the rest of the constraint solving will
// take width to be specified as the value computed here.
Specified(box_.content_width())
@ -2233,7 +2186,7 @@ impl WidthAndMarginsComputer for BlockNonReplaced {
/// Compute left and right margins and width.
fn solve_width_constraints(&self,
block: &mut BlockFlow,
input: WidthConstraintInput)
input: &WidthConstraintInput)
-> WidthConstraintSolution {
self.solve_block_width_constraints(block, input)
}
@ -2246,7 +2199,7 @@ impl WidthAndMarginsComputer for BlockReplaced {
/// like for non-replaced blocks.
fn solve_width_constraints(&self,
block: &mut BlockFlow,
input: WidthConstraintInput)
input: &WidthConstraintInput)
-> WidthConstraintSolution {
match input.computed_width {
Specified(_) => {},
@ -2262,7 +2215,7 @@ impl WidthAndMarginsComputer for BlockReplaced {
_: &mut LayoutContext)
-> MaybeAuto {
let box_ = block.box_();
box_.assign_replaced_width_if_necessary(parent_flow_width);
box_.assign_replaced_width_if_necessary(parent_flow_width, None);
// For replaced block flow, the rest of the constraint solving will
// take width to be specified as the value computed here.
Specified(box_.content_width())
@ -2276,7 +2229,7 @@ impl WidthAndMarginsComputer for FloatNonReplaced {
/// If width is computed as 'auto', the used value is the 'shrink-to-fit' width.
fn solve_width_constraints(&self,
block: &mut BlockFlow,
input: WidthConstraintInput)
input: &WidthConstraintInput)
-> WidthConstraintSolution {
let (computed_width, left_margin, right_margin, available_width) = (input.computed_width,
input.left_margin,
@ -2296,9 +2249,7 @@ impl WidthAndMarginsComputer for FloatReplaced {
/// CSS Section 10.3.5
///
/// If width is computed as 'auto', the used value is the 'shrink-to-fit' width.
fn solve_width_constraints(&self,
_: &mut BlockFlow,
input: WidthConstraintInput)
fn solve_width_constraints(&self, _: &mut BlockFlow, input: &WidthConstraintInput)
-> WidthConstraintSolution {
let (computed_width, left_margin, right_margin) = (input.computed_width,
input.left_margin,
@ -2320,7 +2271,7 @@ impl WidthAndMarginsComputer for FloatReplaced {
_: &mut LayoutContext)
-> MaybeAuto {
let box_ = block.box_();
box_.assign_replaced_width_if_necessary(parent_flow_width);
box_.assign_replaced_width_if_necessary(parent_flow_width, None);
// For replaced block flow, the rest of the constraint solving will
// take width to be specified as the value computed here.
Specified(box_.content_width())

File diff suppressed because it is too large Load diff

View file

@ -23,15 +23,14 @@
use css::node_style::StyledNode;
use layout::block::BlockFlow;
use layout::box_::{Box, GenericBox, IframeBox, IframeBoxInfo, ImageBox, ImageBoxInfo};
use layout::box_::{InlineInfo, InlineParentInfo, SpecificBoxInfo};
use layout::box_::{TableBox, TableCellBox, TableColumnBox, TableColumnBoxInfo, TableRowBox};
use layout::box_::{TableWrapperBox, UnscannedTextBox, UnscannedTextBoxInfo};
use layout::box_::{SpecificBoxInfo, TableBox, TableCellBox, TableColumnBox, TableColumnBoxInfo};
use layout::box_::{TableRowBox, TableWrapperBox, UnscannedTextBox, UnscannedTextBoxInfo};
use layout::context::LayoutContext;
use layout::floats::FloatKind;
use layout::flow::{Flow, ImmutableFlowUtils, MutableOwnedFlowUtils};
use layout::flow::{Descendants, AbsDescendants};
use layout::flow_list::{Rawlink};
use layout::inline::InlineFlow;
use layout::inline::{InlineBoxes, InlineFlow};
use layout::table_wrapper::TableWrapperFlow;
use layout::table::TableFlow;
use layout::table_caption::TableCaptionFlow;
@ -57,13 +56,12 @@ use script::dom::node::{CommentNodeTypeId, DoctypeNodeTypeId, DocumentFragmentNo
use script::dom::node::{DocumentNodeTypeId, ElementNodeTypeId, ProcessingInstructionNodeTypeId};
use script::dom::node::{TextNodeTypeId};
use script::dom::text::Text;
use servo_util::geometry::Au;
use servo_util::namespace;
use servo_util::smallvec::SmallVec;
use servo_util::range::Range;
use servo_util::smallvec::{SmallVec, SmallVec0};
use servo_util::str::is_whitespace;
use servo_util::url::{is_image_data, parse_url};
use std::mem;
use std::num::Zero;
use style::ComputedValues;
use style::computed_values::{display, position, float, white_space};
use sync::Arc;
@ -78,7 +76,7 @@ pub enum ConstructionResult {
/// This node contributed a flow at the proper position in the tree.
/// Nothing more needs to be done for this node. It has bubbled up fixed
/// and absolute descendant flows that have a CB above it.
FlowConstructionResult(~Flow, AbsDescendants),
FlowConstructionResult(~Flow:Share, AbsDescendants),
/// This node contributed some object or objects that will be needed to construct a proper flow
/// later up the tree, but these objects have not yet found their home.
@ -131,7 +129,7 @@ pub struct InlineBoxesConstructionResult {
pub splits: Option<~[InlineBlockSplit]>,
/// Any boxes that succeed the {ib} splits.
pub boxes: ~[Box],
pub boxes: InlineBoxes,
/// Any absolute descendants that we're bubbling up.
pub abs_descendants: AbsDescendants,
@ -161,12 +159,10 @@ pub struct InlineBoxesConstructionResult {
/// ])
pub struct InlineBlockSplit {
/// The inline boxes that precede the flow.
///
/// TODO(pcwalton): Small vector optimization.
pub predecessor_boxes: ~[Box],
pub predecessors: InlineBoxes,
/// The flow that caused this {ib} split.
pub flow: ~Flow,
pub flow: ~Flow:Share,
}
impl InlineBlockSplit {
@ -238,6 +234,53 @@ impl<T> OptVector<T> for Option<~[T]> {
}
}
/// Holds inline boxes that we're gathering for children of an inline node.
struct InlineBoxAccumulator {
/// The list of boxes.
boxes: InlineBoxes,
/// Whether we've created a range to enclose all the boxes. This will be true if the outer node
/// is an inline and false otherwise.
has_enclosing_range: bool,
}
impl InlineBoxAccumulator {
fn new() -> InlineBoxAccumulator {
InlineBoxAccumulator {
boxes: InlineBoxes::new(),
has_enclosing_range: false,
}
}
fn from_inline_node(node: &ThreadSafeLayoutNode) -> InlineBoxAccumulator {
let mut boxes = InlineBoxes::new();
boxes.map.push(node.style().clone(), Range::new(0, 0));
InlineBoxAccumulator {
boxes: boxes,
has_enclosing_range: true,
}
}
fn finish(self) -> InlineBoxes {
let InlineBoxAccumulator {
boxes: mut boxes,
has_enclosing_range
} = self;
if has_enclosing_range {
let len = boxes.len();
boxes.map.get_mut(0).range.extend_to(len);
}
boxes
}
}
enum WhitespaceStrippingMode {
NoWhitespaceStripping,
StripWhitespaceFromStart,
StripWhitespaceFromEnd,
}
/// An object that knows how to create flows.
pub struct FlowConstructor<'a> {
/// The layout context.
@ -327,15 +370,33 @@ impl<'a> FlowConstructor<'a> {
/// otherwise.
#[inline(always)]
fn flush_inline_boxes_to_flow_or_list(&mut self,
boxes: ~[Box],
flow: &mut ~Flow,
flow_list: &mut ~[~Flow],
box_accumulator: InlineBoxAccumulator,
flow: &mut ~Flow:Share,
flow_list: &mut ~[~Flow:Share],
whitespace_stripping: WhitespaceStrippingMode,
node: &ThreadSafeLayoutNode) {
let mut boxes = box_accumulator.finish();
if boxes.len() == 0 {
return
}
let mut inline_flow = ~InlineFlow::from_boxes((*node).clone(), boxes) as ~Flow;
match whitespace_stripping {
NoWhitespaceStripping => {}
StripWhitespaceFromStart => {
strip_ignorable_whitespace_from_start(&mut boxes);
if boxes.len() == 0 {
return
}
}
StripWhitespaceFromEnd => {
strip_ignorable_whitespace_from_end(&mut boxes);
if boxes.len() == 0 {
return
}
}
}
let mut inline_flow = ~InlineFlow::from_boxes((*node).clone(), boxes) as ~Flow:Share;
TextRunScanner::new().scan_for_runs(self.font_context(), inline_flow);
inline_flow.finish(self.layout_context);
@ -346,25 +407,14 @@ impl<'a> FlowConstructor<'a> {
}
}
/// Creates an inline flow from a set of inline boxes, if present, and adds it as a child of
/// the given flow or pushes it onto the given flow list.
fn flush_inline_boxes_to_flow_or_list_if_necessary(&mut self,
opt_boxes: &mut Option<~[Box]>,
flow: &mut ~Flow,
flow_list: &mut ~[~Flow],
node: &ThreadSafeLayoutNode) {
let opt_boxes = mem::replace(opt_boxes, None);
if opt_boxes.len() > 0 {
self.flush_inline_boxes_to_flow_or_list(opt_boxes.to_vec(), flow, flow_list, node)
}
}
fn build_block_flow_using_children_construction_result(&mut self,
flow: &mut ~Flow,
consecutive_siblings: &mut ~[~Flow],
flow: &mut ~Flow:Share,
consecutive_siblings:
&mut ~[~Flow:Share],
node: &ThreadSafeLayoutNode,
kid: ThreadSafeLayoutNode,
opt_boxes_for_inline_flow: &mut Option<~[Box]>,
inline_box_accumulator:
&mut InlineBoxAccumulator,
abs_descendants: &mut Descendants,
first_box: &mut bool) {
match kid.swap_out_construction_result() {
@ -381,20 +431,22 @@ impl<'a> FlowConstructor<'a> {
} else {
// Strip ignorable whitespace from the start of this flow per CSS 2.1 §
// 9.2.1.1.
if flow.is_table_kind() || *first_box {
strip_ignorable_whitespace_from_start(opt_boxes_for_inline_flow);
*first_box = false
}
let whitespace_stripping = if flow.is_table_kind() || *first_box {
*first_box = false;
StripWhitespaceFromStart
} else {
NoWhitespaceStripping
};
// Flush any inline boxes that we were gathering up. This allows us to handle
// {ib} splits.
debug!("flushing {} inline box(es) to flow A",
opt_boxes_for_inline_flow.as_ref()
.map_or(0, |boxes| boxes.len()));
self.flush_inline_boxes_to_flow_or_list_if_necessary(
opt_boxes_for_inline_flow,
inline_box_accumulator.boxes.len());
self.flush_inline_boxes_to_flow_or_list(
mem::replace(inline_box_accumulator, InlineBoxAccumulator::new()),
flow,
consecutive_siblings,
whitespace_stripping,
node);
if !consecutive_siblings.is_empty() {
let consecutive_siblings = mem::replace(consecutive_siblings, ~[]);
@ -409,7 +461,7 @@ impl<'a> FlowConstructor<'a> {
ConstructionItemConstructionResult(InlineBoxesConstructionItem(
InlineBoxesConstructionResult {
splits: opt_splits,
boxes: boxes,
boxes: successor_boxes,
abs_descendants: kid_abs_descendants,
})) => {
// Add any {ib} splits.
@ -420,27 +472,29 @@ impl<'a> FlowConstructor<'a> {
// Pull apart the {ib} split object and push its predecessor boxes
// onto the list.
let InlineBlockSplit {
predecessor_boxes: predecessor_boxes,
predecessors: predecessors,
flow: kid_flow
} = split;
opt_boxes_for_inline_flow.push_all_move(predecessor_boxes);
inline_box_accumulator.boxes.push_all(predecessors);
// If this is the first box in flow, then strip ignorable
// whitespace per CSS 2.1 § 9.2.1.1.
if *first_box {
strip_ignorable_whitespace_from_start(
opt_boxes_for_inline_flow);
*first_box = false
}
let whitespace_stripping = if *first_box {
*first_box = false;
StripWhitespaceFromStart
} else {
NoWhitespaceStripping
};
// Flush any inline boxes that we were gathering up.
debug!("flushing {} inline box(es) to flow A",
opt_boxes_for_inline_flow.as_ref()
.map_or(0, |boxes| boxes.len()));
self.flush_inline_boxes_to_flow_or_list_if_necessary(
opt_boxes_for_inline_flow,
inline_box_accumulator.boxes.len());
self.flush_inline_boxes_to_flow_or_list(
mem::replace(inline_box_accumulator,
InlineBoxAccumulator::new()),
flow,
consecutive_siblings,
whitespace_stripping,
node);
// Push the flow generated by the {ib} split onto our list of
@ -455,7 +509,7 @@ impl<'a> FlowConstructor<'a> {
}
// Add the boxes to the list we're maintaining.
opt_boxes_for_inline_flow.push_all_move(boxes);
inline_box_accumulator.boxes.push_all(successor_boxes);
abs_descendants.push_descendants(kid_abs_descendants);
}
ConstructionItemConstructionResult(WhitespaceConstructionItem(..)) => {
@ -476,11 +530,11 @@ impl<'a> FlowConstructor<'a> {
/// Also, deal with the absolute and fixed descendants bubbled up by
/// children nodes.
fn build_flow_using_children(&mut self,
mut flow: ~Flow,
mut flow: ~Flow:Share,
node: &ThreadSafeLayoutNode)
-> ConstructionResult {
// Gather up boxes for the inline flows we might need to create.
let mut opt_boxes_for_inline_flow = None;
let mut inline_box_accumulator = InlineBoxAccumulator::new();
let mut consecutive_siblings = ~[];
let mut first_box = true;
@ -491,23 +545,22 @@ impl<'a> FlowConstructor<'a> {
self.process(&kid);
}
self.build_block_flow_using_children_construction_result(
&mut flow,
&mut consecutive_siblings,
node,
kid,
&mut opt_boxes_for_inline_flow,
&mut abs_descendants,
&mut first_box);
self.build_block_flow_using_children_construction_result(&mut flow,
&mut consecutive_siblings,
node,
kid,
&mut inline_box_accumulator,
&mut abs_descendants,
&mut first_box);
}
// Perform a final flush of any inline boxes that we were gathering up to handle {ib}
// splits, after stripping ignorable whitespace.
strip_ignorable_whitespace_from_end(&mut opt_boxes_for_inline_flow);
self.flush_inline_boxes_to_flow_or_list_if_necessary(&mut opt_boxes_for_inline_flow,
&mut flow,
&mut consecutive_siblings,
node);
self.flush_inline_boxes_to_flow_or_list(inline_box_accumulator,
&mut flow,
&mut consecutive_siblings,
StripWhitespaceFromEnd,
node);
if !consecutive_siblings.is_empty() {
self.generate_anonymous_missing_child(consecutive_siblings, &mut flow, node);
}
@ -535,7 +588,7 @@ impl<'a> FlowConstructor<'a> {
/// other `BlockFlow`s or `InlineFlow`s underneath it, depending on whether {ib} splits needed
/// to happen.
fn build_flow_for_block(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult {
let flow = ~BlockFlow::from_node(self, node) as ~Flow;
let flow = ~BlockFlow::from_node(self, node) as ~Flow:Share;
self.build_flow_using_children(flow, node)
}
@ -543,7 +596,7 @@ impl<'a> FlowConstructor<'a> {
/// a `BlockFlow` underneath it.
fn build_flow_for_floated_block(&mut self, node: &ThreadSafeLayoutNode, float_kind: FloatKind)
-> ConstructionResult {
let flow = ~BlockFlow::float_from_node(self, node, float_kind) as ~Flow;
let flow = ~BlockFlow::float_from_node(self, node, float_kind) as ~Flow:Share;
self.build_flow_using_children(flow, node)
}
@ -553,7 +606,7 @@ impl<'a> FlowConstructor<'a> {
fn build_boxes_for_nonreplaced_inline_content(&mut self, node: &ThreadSafeLayoutNode)
-> ConstructionResult {
let mut opt_inline_block_splits = None;
let mut opt_box_accumulator = None;
let mut box_accumulator = InlineBoxAccumulator::from_inline_node(node);
let mut abs_descendants = Descendants::new();
// Concatenate all the boxes of our kids, creating {ib} splits as necessary.
@ -567,7 +620,9 @@ impl<'a> FlowConstructor<'a> {
// {ib} split. Flush the accumulator to our new split and make a new
// accumulator to hold any subsequent boxes we come across.
let split = InlineBlockSplit {
predecessor_boxes: mem::replace(&mut opt_box_accumulator, None).to_vec(),
predecessors:
mem::replace(&mut box_accumulator,
InlineBoxAccumulator::from_inline_node(node)).finish(),
flow: flow,
};
opt_inline_block_splits.push(split);
@ -576,7 +631,7 @@ impl<'a> FlowConstructor<'a> {
ConstructionItemConstructionResult(InlineBoxesConstructionItem(
InlineBoxesConstructionResult {
splits: opt_splits,
boxes: boxes,
boxes: successors,
abs_descendants: kid_abs_descendants,
})) => {
@ -586,14 +641,16 @@ impl<'a> FlowConstructor<'a> {
Some(splits) => {
for split in splits.move_iter() {
let InlineBlockSplit {
predecessor_boxes: boxes,
predecessors: predecessors,
flow: kid_flow
} = split;
opt_box_accumulator.push_all_move(boxes);
box_accumulator.boxes.push_all(predecessors);
let split = InlineBlockSplit {
predecessor_boxes: mem::replace(&mut opt_box_accumulator,
None).to_vec(),
predecessors:
mem::replace(&mut box_accumulator,
InlineBoxAccumulator::from_inline_node(node))
.finish(),
flow: kid_flow,
};
opt_inline_block_splits.push(split)
@ -602,17 +659,18 @@ impl<'a> FlowConstructor<'a> {
}
// Push residual boxes.
opt_box_accumulator.push_all_move(boxes);
box_accumulator.boxes.push_all(successors);
abs_descendants.push_descendants(kid_abs_descendants);
}
ConstructionItemConstructionResult(WhitespaceConstructionItem(whitespace_node,
whitespace_style))
=> {
// Instantiate the whitespace box.
opt_box_accumulator.push(Box::from_opaque_node_and_style(
whitespace_node,
whitespace_style,
UnscannedTextBox(UnscannedTextBoxInfo::from_text(~" "))))
let box_info = UnscannedTextBox(UnscannedTextBoxInfo::from_text(~" "));
let fragment = Box::from_opaque_node_and_style(whitespace_node,
whitespace_style.clone(),
box_info);
box_accumulator.boxes.push(fragment, whitespace_style)
}
ConstructionItemConstructionResult(TableColumnBoxConstructionItem(_)) => {
// TODO: Implement anonymous table objects for missing parents
@ -621,56 +679,12 @@ impl<'a> FlowConstructor<'a> {
}
}
// fill inline info
match opt_inline_block_splits {
Some(ref splits) => {
match opt_box_accumulator {
Some(ref boxes) => {
// Both
let mut total: ~[&Box] = ~[];
for split in splits.iter() {
for box_ in split.predecessor_boxes.iter() {
total.push(box_);
}
}
for box_ in boxes.iter() {
total.push(box_);
}
self.set_inline_info_for_inline_child(total, node);
},
None => {
let mut total: ~[&Box] = ~[];
for split in splits.iter() {
for box_ in split.predecessor_boxes.iter() {
total.push(box_);
}
}
self.set_inline_info_for_inline_child(total, node);
}
}
},
None => {
match opt_box_accumulator {
Some(ref boxes) => {
let mut total: ~[&Box] = ~[];
for box_ in boxes.iter() {
total.push(box_);
}
self.set_inline_info_for_inline_child(total, node);
},
None => {}
}
}
}
// Finally, make a new construction result.
if opt_inline_block_splits.len() > 0 || opt_box_accumulator.len() > 0
|| abs_descendants.len() > 0 {
if opt_inline_block_splits.len() > 0 || box_accumulator.boxes.len() > 0
|| abs_descendants.len() > 0 {
let construction_item = InlineBoxesConstructionItem(InlineBoxesConstructionResult {
splits: opt_inline_block_splits,
boxes: opt_box_accumulator.to_vec(),
boxes: box_accumulator.finish(),
abs_descendants: abs_descendants,
});
ConstructionItemConstructionResult(construction_item)
@ -679,60 +693,6 @@ impl<'a> FlowConstructor<'a> {
}
}
// FIXME(#1999, pcwalton): Why does this function create a box only to throw it away???
fn set_inline_info_for_inline_child(&mut self,
boxes: &[&Box],
parent_node: &ThreadSafeLayoutNode) {
let parent_box = Box::new(self, parent_node);
let font_style = parent_box.font_style();
let font_group = self.font_context().get_resolved_font_for_style(&font_style);
let (font_ascent,font_descent) = {
let fg = font_group.borrow();
let font = fg.fonts[0].borrow();
(font.metrics.ascent,font.metrics.descent)
};
let boxes_len = boxes.len();
parent_box.compute_borders(parent_box.style());
// FIXME(#2000, pcwalton): I suspect that `Au(0)` is not correct for the containing block
// width.
parent_box.compute_padding(parent_box.style(), Au(0));
for (i, box_) in boxes.iter().enumerate() {
let mut info = box_.inline_info.borrow_mut();
if info.is_none() {
*info = Some(InlineInfo::new());
}
let mut border = *parent_box.border.borrow();
let mut padding = *parent_box.padding.borrow();
if i != 0 {
border.left = Zero::zero();
padding.left = Zero::zero()
}
if i != (boxes_len - 1) {
border.right = Zero::zero();
padding.right = Zero::zero()
}
match &mut *info {
&Some(ref mut info) => {
// TODO(ksh8281): Compute margins.
info.parent_info.push(InlineParentInfo {
padding: padding,
border: border,
margin: Zero::zero(),
style: parent_box.style.clone(),
font_ascent: font_ascent,
font_descent: font_descent,
node: OpaqueNodeMethods::from_thread_safe_layout_node(parent_node),
})
},
&None => {}
}
}
}
/// Creates an `InlineBoxesConstructionResult` for replaced content. Replaced content doesn't
/// render its children, so this just nukes a child's boxes and creates a `Box`.
fn build_boxes_for_replaced_inline_content(&mut self, node: &ThreadSafeLayoutNode)
@ -751,12 +711,12 @@ impl<'a> FlowConstructor<'a> {
node.style().clone()))
}
let mut opt_box_accumulator = None;
opt_box_accumulator.push(Box::new(self, node));
let mut boxes = InlineBoxes::new();
boxes.push(Box::new(self, node), node.style().clone());
let construction_item = InlineBoxesConstructionItem(InlineBoxesConstructionResult {
splits: None,
boxes: opt_box_accumulator.to_vec(),
boxes: boxes,
abs_descendants: Descendants::new(),
});
ConstructionItemConstructionResult(construction_item)
@ -777,7 +737,7 @@ impl<'a> FlowConstructor<'a> {
/// TableCaptionFlow is populated underneath TableWrapperFlow
fn place_table_caption_under_table_wrapper(&mut self,
table_wrapper_flow: &mut ~Flow,
table_wrapper_flow: &mut ~Flow:Share,
node: &ThreadSafeLayoutNode) {
for kid in node.children() {
match kid.swap_out_construction_result() {
@ -794,8 +754,8 @@ impl<'a> FlowConstructor<'a> {
/// Generates an anonymous table flow according to CSS 2.1 § 17.2.1, step 2.
/// If necessary, generate recursively another anonymous table flow.
fn generate_anonymous_missing_child(&mut self,
child_flows: ~[~Flow],
flow: &mut ~Flow,
child_flows: ~[~Flow:Share],
flow: &mut ~Flow:Share,
node: &ThreadSafeLayoutNode) {
let mut anonymous_flow = flow.generate_missing_child_flow(node);
let mut consecutive_siblings = ~[];
@ -822,10 +782,10 @@ impl<'a> FlowConstructor<'a> {
/// other `TableCaptionFlow`s or `TableFlow`s underneath it.
fn build_flow_for_table_wrapper(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult {
let box_ = Box::new_from_specific_info(node, TableWrapperBox);
let mut wrapper_flow = ~TableWrapperFlow::from_node_and_box(node, box_) as ~Flow;
let mut wrapper_flow = ~TableWrapperFlow::from_node_and_box(node, box_) as ~Flow:Share;
let table_box_ = Box::new_from_specific_info(node, TableBox);
let table_flow = ~TableFlow::from_node_and_box(node, table_box_) as ~Flow;
let table_flow = ~TableFlow::from_node_and_box(node, table_box_) as ~Flow:Share;
// We first populate the TableFlow with other flows than TableCaptionFlow.
// We then populate the TableWrapperFlow with TableCaptionFlow, and attach
@ -871,7 +831,7 @@ impl<'a> FlowConstructor<'a> {
/// Builds a flow for a node with `display: table-caption`. This yields a `TableCaptionFlow`
/// with possibly other `BlockFlow`s or `InlineFlow`s underneath it.
fn build_flow_for_table_caption(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult {
let flow = ~TableCaptionFlow::from_node(self, node) as ~Flow;
let flow = ~TableCaptionFlow::from_node(self, node) as ~Flow:Share;
self.build_flow_using_children(flow, node)
}
@ -879,7 +839,7 @@ impl<'a> FlowConstructor<'a> {
/// with possibly other `TableRowFlow`s underneath it.
fn build_flow_for_table_rowgroup(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult {
let box_ = Box::new_from_specific_info(node, TableRowBox);
let flow = ~TableRowGroupFlow::from_node_and_box(node, box_) as ~Flow;
let flow = ~TableRowGroupFlow::from_node_and_box(node, box_) as ~Flow:Share;
self.build_flow_using_children(flow, node)
}
@ -887,7 +847,7 @@ impl<'a> FlowConstructor<'a> {
/// possibly other `TableCellFlow`s underneath it.
fn build_flow_for_table_row(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult {
let box_ = Box::new_from_specific_info(node, TableRowBox);
let flow = ~TableRowFlow::from_node_and_box(node, box_) as ~Flow;
let flow = ~TableRowFlow::from_node_and_box(node, box_) as ~Flow:Share;
self.build_flow_using_children(flow, node)
}
@ -895,7 +855,7 @@ impl<'a> FlowConstructor<'a> {
/// possibly other `BlockFlow`s or `InlineFlow`s underneath it.
fn build_flow_for_table_cell(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult {
let box_ = Box::new_from_specific_info(node, TableCellBox);
let flow = ~TableCellFlow::from_node_and_box(node, box_) as ~Flow;
let flow = ~TableCellFlow::from_node_and_box(node, box_) as ~Flow:Share;
self.build_flow_using_children(flow, node)
}
@ -934,7 +894,8 @@ impl<'a> FlowConstructor<'a> {
let specific = TableColumnBox(TableColumnBoxInfo::new(node));
col_boxes.push( Box::new_from_specific_info(node, specific) );
}
let mut flow = ~TableColGroupFlow::from_node_and_boxes(node, box_, col_boxes) as ~Flow;
let mut flow = ~TableColGroupFlow::from_node_and_boxes(node, box_, col_boxes) as
~Flow:Share;
flow.finish(self.layout_context);
FlowConstructionResult(flow, Descendants::new())
@ -1200,52 +1161,56 @@ impl<'ln> ObjectElement for ThreadSafeLayoutNode<'ln> {
}
/// Strips ignorable whitespace from the start of a list of boxes.
fn strip_ignorable_whitespace_from_start(opt_boxes: &mut Option<~[Box]>) {
match mem::replace(opt_boxes, None) {
None => return,
Some(boxes) => {
// FIXME(pcwalton): This is slow because vector shift is broken. :(
let mut found_nonwhitespace = false;
let mut result = ~[];
let mut last_removed_box: Option<Box> = None;
for box_ in boxes.move_iter() {
if !found_nonwhitespace && box_.is_whitespace_only() {
debug!("stripping ignorable whitespace from start");
last_removed_box = Some(box_);
continue
}
fn strip_ignorable_whitespace_from_start(boxes: &mut InlineBoxes) {
if boxes.len() == 0 {
return
}
found_nonwhitespace = true;
match last_removed_box {
Some(ref last_removed_box) => {
box_.merge_noncontent_inline_left(last_removed_box);
},
None => {}
}
last_removed_box = None;
result.push(box_)
}
let InlineBoxes {
boxes: old_boxes,
map: mut map
} = mem::replace(boxes, InlineBoxes::new());
*opt_boxes = Some(result)
// FIXME(#2264, pcwalton): This is slow because vector shift is broken. :(
let mut found_nonwhitespace = false;
let mut new_boxes = SmallVec0::new();
for fragment in old_boxes.iter() {
if !found_nonwhitespace && fragment.is_whitespace_only() {
debug!("stripping ignorable whitespace from start");
continue
}
found_nonwhitespace = true;
new_boxes.push(fragment.clone())
}
map.fixup(old_boxes.as_slice(), new_boxes.as_slice());
*boxes = InlineBoxes {
boxes: new_boxes,
map: map,
}
}
/// Strips ignorable whitespace from the end of a list of boxes.
fn strip_ignorable_whitespace_from_end(opt_boxes: &mut Option<~[Box]>) {
match *opt_boxes {
None => {}
Some(ref mut boxes) => {
while boxes.len() > 0 && boxes.last().get_ref().is_whitespace_only() {
debug!("stripping ignorable whitespace from end");
let box_ = boxes.pop().unwrap();
if boxes.len() > 0 {
boxes[boxes.len() - 1].merge_noncontent_inline_right(&box_);
}
}
}
fn strip_ignorable_whitespace_from_end(boxes: &mut InlineBoxes) {
if boxes.len() == 0 {
return
}
if opt_boxes.len() == 0 {
*opt_boxes = None
let InlineBoxes {
boxes: old_boxes,
map: mut map
} = mem::replace(boxes, InlineBoxes::new());
let mut new_boxes = old_boxes.clone();
while new_boxes.len() > 0 && new_boxes.as_slice().last().get_ref().is_whitespace_only() {
debug!("stripping ignorable whitespace from end");
drop(new_boxes.pop());
}
map.fixup(old_boxes.as_slice(), new_boxes.as_slice());
*boxes = InlineBoxes {
boxes: new_boxes,
map: map,
}
}

View file

@ -13,6 +13,7 @@ use gfx::font_context::{FontContext, FontContextInfo};
use green::task::GreenTask;
use script::layout_interface::LayoutChan;
use servo_msg::constellation_msg::ConstellationChan;
use servo_net::image::holder::LocalImageCacheHandle;
use servo_util::geometry::Au;
use servo_util::opts::Opts;
use std::cast;
@ -23,7 +24,7 @@ use std::rt::local::Local;
#[cfg(not(target_os="android"))]
use std::rt::task::Task;
use style::{ComputedValues, Stylist};
use sync::{Arc, Mutex};
use sync::Arc;
use url::Url;
#[cfg(target_os="android")]
@ -56,8 +57,7 @@ local_data_key!(style_sharing_candidate_cache: *mut StyleSharingCandidateCache)
#[deriving(Clone)]
pub struct LayoutContext {
/// The local image cache.
// FIXME(rust#13125): Remove the *() for the real type.
pub image_cache: Arc<Mutex<*()>>,
pub image_cache: LocalImageCacheHandle,
/// The current screen size.
pub screen_size: Size2D<Au>,

View file

@ -30,7 +30,7 @@ use layout::block::BlockFlow;
use layout::box_::{Box, TableRowBox, TableCellBox};
use layout::construct::OptVector;
use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo, ToGfxColor};
use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
use layout::floats::Floats;
use layout::flow_list::{FlowList, Link, Rawlink, FlowListIterator, MutFlowListIterator};
use layout::incremental::RestyleDamage;
@ -48,10 +48,8 @@ use layout::table_cell::TableCellFlow;
use layout::wrapper::ThreadSafeLayoutNode;
use collections::Deque;
use geom::Size2D;
use geom::point::Point2D;
use geom::rect::Rect;
use gfx::color::Color;
use gfx::display_list::StackingContext;
use servo_msg::compositor_msg::LayerId;
use servo_util::geometry::Au;
@ -60,7 +58,6 @@ use std::cast;
use std::iter::Zip;
use std::sync::atomics::Relaxed;
use std::slice::MutItems;
use style::ComputedValues;
use style::computed_values::{clear, position, text_align};
/// Virtual methods that make up a float context.
@ -227,14 +224,10 @@ pub trait Flow {
false
}
/// Return the dimensions of the CB generated _by_ this flow for absolute descendants.
fn generated_cb_size(&self) -> Size2D<Au> {
fail!("generated_cb_size not yet implemented")
}
/// Return position of the CB generated by this flow from the start of this flow.
fn generated_cb_position(&self) -> Point2D<Au> {
fail!("this is not the CB-generating flow you're looking for")
/// Return the dimensions of the containing block generated by this flow for absolutely-
/// positioned descendants. For block flows, this is the padding box.
fn generated_containing_block_rect(&self) -> Rect<Au> {
fail!("generated_containing_block_position not yet implemented for this flow")
}
/// Returns a layer ID for the given fragment.
@ -318,7 +311,7 @@ pub trait ImmutableFlowUtils {
fn need_anonymous_flow(self, child: &Flow) -> bool;
/// Generates missing child flow of this flow.
fn generate_missing_child_flow(self, node: &ThreadSafeLayoutNode) -> ~Flow;
fn generate_missing_child_flow(self, node: &ThreadSafeLayoutNode) -> ~Flow:Share;
/// Returns true if this flow has no children.
fn is_leaf(self) -> bool;
@ -375,7 +368,7 @@ pub trait MutableFlowUtils {
pub trait MutableOwnedFlowUtils {
/// Adds a new flow as a child of this flow. Removes the flow from the given leaf set if
/// it's present.
fn add_new_child(&mut self, new_child: ~Flow);
fn add_new_child(&mut self, new_child: ~Flow:Share);
/// Finishes a flow. Once a flow is finished, no more child flows or boxes may be added to it.
/// This will normally run the bubble-widths (minimum and preferred -- i.e. intrinsic -- width)
@ -439,30 +432,10 @@ pub trait PostorderFlowTraversal {
}
}
#[deriving(Clone)]
pub struct FlowFlagsInfo {
pub flags: FlowFlags,
/// text-decoration colors
pub rare_flow_flags: Option<~RareFlowFlags>,
}
#[deriving(Clone)]
pub struct RareFlowFlags {
pub underline_color: Color,
pub overline_color: Color,
pub line_through_color: Color,
}
/// Flags used in flows, tightly packed to save space.
#[deriving(Clone)]
pub struct FlowFlags(pub u8);
/// The bitmask of flags that represent text decoration fields that get propagated downward.
///
/// NB: If you update this field, you must update the bitfields below.
static TEXT_DECORATION_OVERRIDE_BITMASK: u8 = 0b0000_1110;
/// The bitmask of flags that represent the text alignment field.
///
/// NB: If you update this field, you must update the bitfields below.
@ -473,155 +446,9 @@ static TEXT_ALIGN_BITMASK: u8 = 0b0011_0000;
/// NB: If you update this field, you must update the bitfields below.
static TEXT_ALIGN_SHIFT: u8 = 4;
impl FlowFlagsInfo {
/// Creates a new set of flow flags from the given style.
pub fn new(style: &ComputedValues) -> FlowFlagsInfo {
let text_decoration = style.Text.get().text_decoration;
let mut flags = FlowFlags(0);
flags.set_override_underline(text_decoration.underline);
flags.set_override_overline(text_decoration.overline);
flags.set_override_line_through(text_decoration.line_through);
// TODO(ksh8281) compute text-decoration-color,style,line
let rare_flow_flags = if flags.is_text_decoration_enabled() {
Some(~RareFlowFlags {
underline_color: style.Color.get().color.to_gfx_color(),
overline_color: style.Color.get().color.to_gfx_color(),
line_through_color: style.Color.get().color.to_gfx_color(),
})
} else {
None
};
FlowFlagsInfo {
flags: flags,
rare_flow_flags: rare_flow_flags,
}
}
pub fn underline_color(&self, default_color: Color) -> Color {
match self.rare_flow_flags {
Some(ref data) => {
data.underline_color
},
None => {
default_color
}
}
}
pub fn overline_color(&self, default_color: Color) -> Color {
match self.rare_flow_flags {
Some(ref data) => {
data.overline_color
},
None => {
default_color
}
}
}
pub fn line_through_color(&self, default_color: Color) -> Color {
match self.rare_flow_flags {
Some(ref data) => {
data.line_through_color
},
None => {
default_color
}
}
}
/// Propagates text decoration flags from an appropriate parent flow per CSS 2.1 § 16.3.1.
pub fn propagate_text_decoration_from_parent(&mut self, parent: &FlowFlagsInfo) {
if !parent.flags.is_text_decoration_enabled() {
return ;
}
if !self.flags.is_text_decoration_enabled() && parent.flags.is_text_decoration_enabled() {
self.rare_flow_flags = parent.rare_flow_flags.clone();
self.flags.set_text_decoration_override(parent.flags);
return ;
}
if !self.flags.override_underline() && parent.flags.override_underline() {
match parent.rare_flow_flags {
Some(ref parent_data) => {
match self.rare_flow_flags {
Some(ref mut data) => {
data.underline_color = parent_data.underline_color;
},
None => {
fail!("if flow has text-decoration, it must have rare_flow_flags");
}
}
},
None => {
fail!("if flow has text-decoration, it must have rare_flow_flags");
}
}
}
if !self.flags.override_overline() && parent.flags.override_overline() {
match parent.rare_flow_flags {
Some(ref parent_data) => {
match self.rare_flow_flags {
Some(ref mut data) => {
data.overline_color = parent_data.overline_color;
},
None => {
fail!("if flow has text-decoration, it must have rare_flow_flags");
}
}
},
None => {
fail!("if flow has text-decoration, it must have rare_flow_flags");
}
}
}
if !self.flags.override_line_through() && parent.flags.override_line_through() {
match parent.rare_flow_flags {
Some(ref parent_data) => {
match self.rare_flow_flags {
Some(ref mut data) => {
data.line_through_color = parent_data.line_through_color;
},
None => {
fail!("if flow has text-decoration, it must have rare_flow_flags");
}
}
},
None => {
fail!("if flow has text-decoration, it must have rare_flow_flags");
}
}
}
self.flags.set_text_decoration_override(parent.flags);
}
/// Propagates text alignment flags from an appropriate parent flow per CSS 2.1.
pub fn propagate_text_alignment_from_parent(&mut self, parent: &FlowFlagsInfo) {
self.flags.set_text_align_override(parent.flags);
}
}
// Whether we need an in-order traversal.
bitfield!(FlowFlags, inorder, set_inorder, 0b0000_0001)
// Whether this flow forces `text-decoration: underline` on.
//
// NB: If you update this, you need to update TEXT_DECORATION_OVERRIDE_BITMASK.
bitfield!(FlowFlags, override_underline, set_override_underline, 0b0000_0010)
// Whether this flow forces `text-decoration: overline` on.
//
// NB: If you update this, you need to update TEXT_DECORATION_OVERRIDE_BITMASK.
bitfield!(FlowFlags, override_overline, set_override_overline, 0b0000_0100)
// Whether this flow forces `text-decoration: line-through` on.
//
// NB: If you update this, you need to update TEXT_DECORATION_OVERRIDE_BITMASK.
bitfield!(FlowFlags, override_line_through, set_override_line_through, 0b0000_1000)
// Whether this flow contains a flow that has its own layer within the same absolute containing
// block.
bitfield!(FlowFlags,
@ -633,8 +460,20 @@ bitfield!(FlowFlags,
// layer if it's deemed to be likely to overlap flows with their own layer.
bitfield!(FlowFlags, needs_layer, set_needs_layer, 0b1000_0000)
// The text alignment for this flow.
impl FlowFlags {
/// Creates a new set of flow flags.
pub fn new() -> FlowFlags {
FlowFlags(0)
}
/// Propagates text alignment flags from an appropriate parent flow per CSS 2.1.
///
/// FIXME(#2265, pcwalton): It would be cleaner and faster to make this a derived CSS property
/// `-servo-text-align-in-effect`.
pub fn propagate_text_alignment_from_parent(&mut self, parent_flags: FlowFlags) {
self.set_text_align_override(parent_flags);
}
#[inline]
pub fn text_align(self) -> text_align::T {
let FlowFlags(ff) = self;
@ -653,19 +492,6 @@ impl FlowFlags {
let FlowFlags(pff) = parent;
*self = FlowFlags(ff | (pff & TEXT_ALIGN_BITMASK))
}
#[inline]
pub fn set_text_decoration_override(&mut self, parent: FlowFlags) {
let FlowFlags(ff) = *self;
let FlowFlags(pff) = parent;
*self = FlowFlags(ff | (pff & TEXT_DECORATION_OVERRIDE_BITMASK));
}
#[inline]
pub fn is_text_decoration_enabled(&self) -> bool {
let FlowFlags(ref ff) = *self;
(*ff & TEXT_DECORATION_OVERRIDE_BITMASK) != 0
}
}
/// The Descendants of a flow.
@ -781,11 +607,12 @@ pub struct BaseFlow {
/// Offset wrt the nearest positioned ancestor - aka the Containing Block
/// for any absolutely positioned elements.
pub absolute_static_x_offset: Au,
/// Offset wrt the Initial Containing Block.
pub fixed_static_x_offset: Au,
/// Reference to the Containing Block, if this flow is absolutely positioned.
pub absolute_cb: Rawlink,
pub absolute_cb: ContainingBlockLink,
/// Whether this flow has been destroyed.
///
@ -793,8 +620,8 @@ pub struct BaseFlow {
/// flag can have memory safety implications.
destroyed: bool,
/// Various flags for flows and some info
pub flags_info: FlowFlagsInfo,
/// Various flags for flows, tightly packed to save space.
pub flags: FlowFlags,
}
#[unsafe_destructor]
@ -809,7 +636,6 @@ impl Drop for BaseFlow {
impl BaseFlow {
#[inline]
pub fn new(node: ThreadSafeLayoutNode) -> BaseFlow {
let style = node.style();
BaseFlow {
restyle_damage: node.restyle_damage(),
@ -831,11 +657,11 @@ impl BaseFlow {
abs_descendants: Descendants::new(),
absolute_static_x_offset: Au::new(0),
fixed_static_x_offset: Au::new(0),
absolute_cb: Rawlink::none(),
absolute_cb: ContainingBlockLink::new(),
destroyed: false,
flags_info: FlowFlagsInfo::new(&**style),
flags: FlowFlags::new(),
}
}
@ -934,15 +760,15 @@ impl<'a> ImmutableFlowUtils for &'a Flow {
}
/// Generates missing child flow of this flow.
fn generate_missing_child_flow(self, node: &ThreadSafeLayoutNode) -> ~Flow {
fn generate_missing_child_flow(self, node: &ThreadSafeLayoutNode) -> ~Flow:Share {
match self.class() {
TableFlowClass | TableRowGroupFlowClass => {
let box_ = Box::new_anonymous_table_box(node, TableRowBox);
~TableRowFlow::from_node_and_box(node, box_) as ~Flow
~TableRowFlow::from_node_and_box(node, box_) as ~Flow:Share
},
TableRowFlowClass => {
let box_ = Box::new_anonymous_table_box(node, TableCellBox);
~TableCellFlow::from_node_and_box(node, box_) as ~Flow
~TableCellFlow::from_node_and_box(node, box_) as ~Flow:Share
},
_ => {
fail!("no need to generate a missing child")
@ -1164,9 +990,9 @@ impl<'a> MutableFlowUtils for &'a mut Flow {
}
}
impl MutableOwnedFlowUtils for ~Flow {
impl MutableOwnedFlowUtils for ~Flow:Share {
/// Adds a new flow as a child of this flow. Fails if this flow is marked as a leaf.
fn add_new_child(&mut self, mut new_child: ~Flow) {
fn add_new_child(&mut self, mut new_child: ~Flow:Share) {
{
let kid_base = mut_base(new_child);
kid_base.parallel.parent = parallel::mut_owned_flow_to_unsafe_flow(self);
@ -1204,7 +1030,7 @@ impl MutableOwnedFlowUtils for ~Flow {
match descendant_link.resolve() {
Some(flow) => {
let base = mut_base(flow);
base.absolute_cb = self_link.clone();
base.absolute_cb.set(self_link.clone());
}
None => fail!("empty Rawlink to a descendant")
}
@ -1217,3 +1043,32 @@ impl MutableOwnedFlowUtils for ~Flow {
self_borrowed.destroy();
}
}
/// A link to a flow's containing block.
///
/// This cannot safely be a `Flow` pointer because this is a pointer *up* the tree, not *down* the
/// tree. A pointer up the tree is unsafe during layout because it can be used to access a node
/// with an immutable reference while that same node is being laid out, causing possible iterator
/// invalidation and use-after-free.
pub struct ContainingBlockLink {
/// TODO(pcwalton): Reference count.
link: Rawlink,
}
impl ContainingBlockLink {
fn new() -> ContainingBlockLink {
ContainingBlockLink {
link: Rawlink::none(),
}
}
fn set(&mut self, link: Rawlink) {
self.link = link
}
#[inline]
pub fn generated_containing_block_rect(&mut self) -> Rect<Au> {
self.link.resolve().unwrap().generated_containing_block_rect()
}
}

View file

@ -11,7 +11,7 @@ use std::ptr;
use layout::flow::{Flow, base, mut_base};
pub type Link = Option<~Flow>;
pub type Link = Option<~Flow:Share>;
#[deriving(Clone)]
pub struct Rawlink {
@ -88,7 +88,7 @@ impl Rawlink {
}
/// Set the .prev field on `next`, then return `Some(next)`
fn link_with_prev(mut next: ~Flow, prev: Rawlink) -> Link {
fn link_with_prev(mut next: ~Flow:Share, prev: Rawlink) -> Link {
mut_base(next).prev_sibling = prev;
Some(next)
}
@ -146,7 +146,7 @@ impl FlowList {
/// Add an element first in the list
///
/// O(1)
pub fn push_front(&mut self, mut new_head: ~Flow) {
pub fn push_front(&mut self, mut new_head: ~Flow:Share) {
match self.list_head {
None => {
self.list_tail = Rawlink::some(new_head);
@ -165,7 +165,7 @@ impl FlowList {
/// Remove the first element and return it, or None if the list is empty
///
/// O(1)
pub fn pop_front(&mut self) -> Option<~Flow> {
pub fn pop_front(&mut self) -> Option<~Flow:Share> {
self.list_head.take().map(|mut front_node| {
self.length -= 1;
match mut_base(front_node).next_sibling.take() {
@ -179,7 +179,7 @@ impl FlowList {
/// Add an element last in the list
///
/// O(1)
pub fn push_back(&mut self, mut new_tail: ~Flow) {
pub fn push_back(&mut self, mut new_tail: ~Flow:Share) {
if self.list_tail.is_none() {
return self.push_front(new_tail);
} else {
@ -194,7 +194,7 @@ impl FlowList {
/// Remove the last element and return it, or None if the list is empty
///
/// O(1)
pub fn pop_back(&mut self) -> Option<~Flow> {
pub fn pop_back(&mut self) -> Option<~Flow:Share> {
if self.list_tail.is_none() {
None
} else {

View file

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use css::node_style::StyledNode;
use layout::box_::{Box, CannotSplit, GenericBox, IframeBox, ImageBox, InlineInfo, ScannedTextBox};
use layout::box_::{Box, CannotSplit, GenericBox, IframeBox, ImageBox, ScannedTextBox};
use layout::box_::{SplitDidFit, SplitDidNotFit, TableBox, TableCellBox, TableColumnBox};
use layout::box_::{TableRowBox, TableWrapperBox, UnscannedTextBox};
use layout::context::LayoutContext;
@ -12,18 +12,23 @@ use layout::floats::{FloatLeft, Floats, PlacementInfo};
use layout::flow::{BaseFlow, FlowClass, Flow, InlineFlowClass};
use layout::flow;
use layout::model::IntrinsicWidths;
use layout::util::ElementMapping;
use layout::model;
use layout::wrapper::ThreadSafeLayoutNode;
use collections::{Deque, RingBuf};
use geom::{Point2D, Rect, Size2D};
use geom::{Point2D, Rect, SideOffsets2D, Size2D};
use gfx::display_list::{ContentLevel, StackingContext};
use servo_util::geometry::Au;
use servo_util::geometry;
use servo_util::range::Range;
use servo_util::smallvec::{SmallVec, SmallVec0};
use std::iter::Enumerate;
use std::mem;
use std::slice::{Items, MutItems};
use std::u16;
use style::computed_values::{text_align, vertical_align, white_space};
use style::ComputedValues;
use sync::Arc;
/// Lineboxes are represented as offsets into the child list, rather than
/// as an object that "owns" boxes. Choosing a different set of line
@ -58,10 +63,10 @@ pub struct LineBox {
struct LineboxScanner {
pub floats: Floats,
pub new_boxes: ~[Box],
pub new_boxes: SmallVec0<Box>,
pub work_list: RingBuf<Box>,
pub pending_line: LineBox,
pub lines: ~[LineBox],
pub lines: SmallVec0<LineBox>,
pub cur_y: Au,
}
@ -69,14 +74,14 @@ impl LineboxScanner {
pub fn new(float_ctx: Floats) -> LineboxScanner {
LineboxScanner {
floats: float_ctx,
new_boxes: ~[],
new_boxes: SmallVec0::new(),
work_list: RingBuf::new(),
pending_line: LineBox {
range: Range::empty(),
bounds: Rect(Point2D(Au::new(0), Au::new(0)), Size2D(Au::new(0), Au::new(0))),
green_zone: Size2D(Au::new(0), Au::new(0))
},
lines: ~[],
lines: SmallVec0::new(),
cur_y: Au::new(0)
}
}
@ -87,9 +92,9 @@ impl LineboxScanner {
fn reset_scanner(&mut self) {
debug!("Resetting line box scanner's state for flow.");
self.lines = ~[];
self.new_boxes = ~[];
self.cur_y = Au::new(0);
self.lines = SmallVec0::new();
self.new_boxes = SmallVec0::new();
self.cur_y = Au(0);
self.reset_linebox();
}
@ -102,19 +107,29 @@ impl LineboxScanner {
pub fn scan_for_lines(&mut self, flow: &mut InlineFlow) {
self.reset_scanner();
// Swap out temporarily.
let InlineBoxes {
boxes: old_boxes,
map: mut map
} = mem::replace(&mut flow.boxes, InlineBoxes::new());
let mut old_box_iter = old_boxes.iter();
loop {
// acquire the next box to lay out from work list or box list
let cur_box = if self.work_list.is_empty() {
if flow.boxes.is_empty() {
break;
match old_box_iter.next() {
None => break,
Some(fragment) => {
debug!("LineboxScanner: Working with fragment from flow: b{}",
fragment.debug_id());
(*fragment).clone()
}
}
let box_ = flow.boxes.remove(0).unwrap(); // FIXME: use a linkedlist
debug!("LineboxScanner: Working with box from box list: b{}", box_.debug_id());
box_
} else {
let box_ = self.work_list.pop_front().unwrap();
debug!("LineboxScanner: Working with box from work list: b{}", box_.debug_id());
box_
let fragment = self.work_list.pop_front().unwrap();
debug!("LineboxScanner: Working with box from work list: b{}",
fragment.debug_id());
fragment
};
let box_was_appended = match cur_box.white_space() {
@ -137,17 +152,13 @@ impl LineboxScanner {
self.flush_current_line();
}
flow.elems.repair_for_box_changes(flow.boxes, self.new_boxes);
map.fixup(old_boxes.as_slice(), self.new_boxes.as_slice());
flow.boxes = InlineBoxes {
boxes: mem::replace(&mut self.new_boxes, SmallVec0::new()),
map: map,
};
self.swap_out_results(flow);
}
fn swap_out_results(&mut self, flow: &mut InlineFlow) {
debug!("LineboxScanner: Propagating scanned lines[n={:u}] to inline flow",
self.lines.len());
mem::swap(&mut flow.boxes, &mut self.new_boxes);
mem::swap(&mut flow.lines, &mut self.lines);
flow.lines = mem::replace(&mut self.lines, SmallVec0::new());
}
fn flush_current_line(&mut self) {
@ -179,7 +190,7 @@ impl LineboxScanner {
-> (Rect<Au>, Au) {
debug!("LineboxScanner: Trying to place first box of line {}", self.lines.len());
let first_box_size = first_box.border_box.borrow().size;
let first_box_size = first_box.border_box.size;
let splittable = first_box.can_split();
debug!("LineboxScanner: box size: {}, splittable: {}", first_box_size, splittable);
let line_is_empty: bool = self.pending_line.range.length() == 0;
@ -233,9 +244,9 @@ impl LineboxScanner {
debug!("LineboxScanner: case=box split and fit");
let actual_box_width = match (left, right) {
(Some(l_box), Some(_)) => l_box.border_box.borrow().size.width,
(Some(l_box), None) => l_box.border_box.borrow().size.width,
(None, Some(r_box)) => r_box.border_box.borrow().size.width,
(Some(l_box), Some(_)) => l_box.border_box.size.width,
(Some(l_box), None) => l_box.border_box.size.width,
(None, Some(r_box)) => r_box.border_box.size.width,
(None, None) => fail!("This case makes no sense.")
};
return (line_bounds, actual_box_width);
@ -247,9 +258,9 @@ impl LineboxScanner {
debug!("LineboxScanner: case=box split and fit didn't fit; trying to push it down");
let actual_box_width = match (left, right) {
(Some(l_box), Some(_)) => l_box.border_box.borrow().size.width,
(Some(l_box), None) => l_box.border_box.borrow().size.width,
(None, Some(r_box)) => r_box.border_box.borrow().size.width,
(Some(l_box), Some(_)) => l_box.border_box.size.width,
(Some(l_box), None) => l_box.border_box.size.width,
(None, Some(r_box)) => r_box.border_box.size.width,
(None, None) => fail!("This case makes no sense.")
};
@ -348,7 +359,7 @@ impl LineboxScanner {
debug!("LineboxScanner: Trying to append box to line {:u} (box size: {}, green zone: \
{}): {:s}",
self.lines.len(),
in_box.border_box.borrow().size,
in_box.border_box.size,
self.pending_line.green_zone,
in_box.debug_str());
@ -368,7 +379,7 @@ impl LineboxScanner {
// horizontally. We'll try to place the whole box on this line and break somewhere if it
// doesn't fit.
let new_width = self.pending_line.bounds.size.width + in_box.border_box.borrow().size.width;
let new_width = self.pending_line.bounds.size.width + in_box.border_box.size.width;
if new_width <= green_zone.width {
debug!("LineboxScanner: case=box fits without splitting");
self.push_box_to_line(in_box);
@ -439,49 +450,148 @@ impl LineboxScanner {
}
self.pending_line.range.extend_by(1);
self.pending_line.bounds.size.width = self.pending_line.bounds.size.width +
box_.border_box.borrow().size.width;
box_.border_box.size.width;
self.pending_line.bounds.size.height = Au::max(self.pending_line.bounds.size.height,
box_.border_box.borrow().size.height);
box_.border_box.size.height);
self.new_boxes.push(box_);
}
}
/// Iterator over boxes.
pub struct BoxIterator<'a> {
iter: Enumerate<Items<'a,Box>>,
map: &'a FragmentMap,
}
impl<'a> Iterator<(&'a Box, InlineFragmentContext<'a>)> for BoxIterator<'a> {
#[inline]
fn next(&mut self) -> Option<(&'a Box, InlineFragmentContext<'a>)> {
match self.iter.next() {
None => None,
Some((i, fragment)) => Some((fragment, InlineFragmentContext::new(self.map, i))),
}
}
}
/// Mutable iterator over boxes.
pub struct MutBoxIterator<'a> {
iter: Enumerate<MutItems<'a,Box>>,
map: &'a FragmentMap,
}
impl<'a> Iterator<(&'a mut Box, InlineFragmentContext<'a>)> for MutBoxIterator<'a> {
#[inline]
fn next(&mut self) -> Option<(&'a mut Box, InlineFragmentContext<'a>)> {
match self.iter.next() {
None => None,
Some((i, fragment)) => Some((fragment, InlineFragmentContext::new(self.map, i))),
}
}
}
/// Represents a list of inline boxes, including element ranges.
pub struct InlineBoxes {
/// The boxes themselves.
pub boxes: SmallVec0<Box>,
/// Tracks the elements that made up the boxes above.
pub map: FragmentMap,
}
impl InlineBoxes {
/// Creates an empty set of inline boxes.
pub fn new() -> InlineBoxes {
InlineBoxes {
boxes: SmallVec0::new(),
map: FragmentMap::new(),
}
}
/// Returns the number of inline boxes.
pub fn len(&self) -> uint {
self.boxes.len()
}
/// Returns true if this list contains no boxes and false if it contains at least one box.
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Pushes a new inline box.
pub fn push(&mut self, fragment: Box, style: Arc<ComputedValues>) {
self.map.push(style, Range::new(self.boxes.len(), 1));
self.boxes.push(fragment)
}
/// Merges another set of inline boxes with this one.
pub fn push_all(&mut self, other: InlineBoxes) {
let InlineBoxes {
boxes: other_boxes,
map: other_map
} = other;
let adjustment = self.boxes.len();
self.map.push_all(other_map, adjustment);
self.boxes.push_all_move(other_boxes);
}
/// Returns an iterator that iterates over all boxes along with the appropriate context.
pub fn iter<'a>(&'a self) -> BoxIterator<'a> {
BoxIterator {
iter: self.boxes.as_slice().iter().enumerate(),
map: &self.map,
}
}
/// Returns an iterator that iterates over all boxes along with the appropriate context and
/// allows those boxes to be mutated.
pub fn mut_iter<'a>(&'a mut self) -> MutBoxIterator<'a> {
MutBoxIterator {
iter: self.boxes.as_mut_slice().mut_iter().enumerate(),
map: &self.map,
}
}
/// A convenience function to return the box at a given index.
pub fn get<'a>(&'a self, index: uint) -> &'a Box {
self.boxes.get(index)
}
/// A convenience function to return a mutable reference to the box at a given index.
pub fn get_mut<'a>(&'a mut self, index: uint) -> &'a mut Box {
self.boxes.get_mut(index)
}
}
/// Flows for inline layout.
pub struct InlineFlow {
/// Data common to all flows.
pub base: BaseFlow,
/// A vector of all inline render boxes. Several boxes may correspond to one node/element.
pub boxes: ~[Box],
/// A vector of all inline fragments. Several fragments may correspond to one node/element.
pub boxes: InlineBoxes,
// vec of ranges into boxes that represents line positions.
// these ranges are disjoint, and are the result of inline layout.
// also some metadata used for positioning lines
pub lines: ~[LineBox],
// vec of ranges into boxes that represent elements. These ranges
// must be well-nested, and are only related to the content of
// boxes (not lines). Ranges are only kept for non-leaf elements.
pub elems: ElementMapping,
/// A vector of ranges into boxes that represents line positions. These ranges are disjoint and
/// are the result of inline layout. This also includes some metadata used for positioning
/// lines.
pub lines: SmallVec0<LineBox>,
}
impl InlineFlow {
pub fn from_boxes(node: ThreadSafeLayoutNode, boxes: ~[Box]) -> InlineFlow {
pub fn from_boxes(node: ThreadSafeLayoutNode, boxes: InlineBoxes) -> InlineFlow {
InlineFlow {
base: BaseFlow::new(node),
boxes: boxes,
lines: ~[],
elems: ElementMapping::new(),
lines: SmallVec0::new(),
}
}
pub fn teardown(&mut self) {
for box_ in self.boxes.iter() {
box_.teardown();
for (fragment, _) in self.boxes.iter() {
fragment.teardown();
}
self.boxes = ~[];
self.boxes = InlineBoxes::new();
}
pub fn build_display_list_inline(&self,
pub fn build_display_list_inline(&mut self,
stacking_context: &mut StackingContext,
builder: &DisplayListBuilder,
info: &DisplayListBuildingInfo) {
@ -494,14 +604,15 @@ impl InlineFlow {
// not recurse on a line if nothing in it can intersect the dirty region.
debug!("Flow: building display list for {:u} inline boxes", self.boxes.len());
for box_ in self.boxes.iter() {
let rel_offset = box_.relative_position(&info.relative_containing_block_size);
box_.build_display_list(stacking_context,
builder,
info,
self.base.abs_position + rel_offset,
(&*self) as &Flow,
ContentLevel);
for (fragment, context) in self.boxes.mut_iter() {
let rel_offset = fragment.relative_position(&info.relative_containing_block_size,
Some(context));
fragment.build_display_list(stacking_context,
builder,
info,
self.base.abs_position + rel_offset,
ContentLevel,
Some(context));
}
// TODO(#225): Should `inline-block` elements have flows as children of the inline flow or
@ -582,7 +693,9 @@ impl InlineFlow {
}
/// Sets box X positions based on alignment for one line.
fn set_horizontal_box_positions(boxes: &[Box], line: &LineBox, linebox_align: text_align::T) {
fn set_horizontal_box_positions(boxes: &mut InlineBoxes,
line: &LineBox,
linebox_align: text_align::T) {
// Figure out how much width we have.
let slack_width = Au::max(Au(0), line.green_zone.width - line.bounds.size.width);
@ -599,10 +712,9 @@ impl InlineFlow {
};
for i in line.range.eachi() {
let box_ = &boxes[i];
let mut border_box = box_.border_box.borrow_mut();
let size = border_box.size;
*border_box = Rect(Point2D(offset_x, border_box.origin.y), size);
let box_ = boxes.get_mut(i);
let size = box_.border_box.size;
box_.border_box = Rect(Point2D(offset_x, box_.border_box.origin.y), size);
offset_x = offset_x + size.width;
}
}
@ -631,11 +743,10 @@ impl Flow for InlineFlow {
}
let mut intrinsic_widths = IntrinsicWidths::new();
for box_ in self.boxes.iter() {
debug!("Flow: measuring {:s}", box_.debug_str());
box_.compute_borders(box_.style());
for (fragment, context) in self.boxes.mut_iter() {
debug!("Flow: measuring {:s}", fragment.debug_str());
let box_intrinsic_widths = box_.intrinsic_widths();
let box_intrinsic_widths = fragment.intrinsic_widths(Some(context));
intrinsic_widths.minimum_width = geometry::max(intrinsic_widths.minimum_width,
box_intrinsic_widths.minimum_width);
intrinsic_widths.preferred_width = geometry::max(intrinsic_widths.preferred_width,
@ -657,8 +768,9 @@ impl Flow for InlineFlow {
{
let this = &mut *self;
for box_ in this.boxes.iter() {
box_.assign_replaced_width_if_necessary(self.base.position.size.width);
for (fragment, context) in this.boxes.mut_iter() {
fragment.assign_replaced_width_if_necessary(self.base.position.size.width,
Some(context))
}
}
@ -699,9 +811,10 @@ impl Flow for InlineFlow {
debug!("assign_height_inline: floats in: {:?}", self.base.floats);
// assign height for inline boxes
for box_ in self.boxes.iter() {
box_.assign_replaced_height_if_necessary();
for (fragment, _) in self.boxes.mut_iter() {
fragment.assign_replaced_height_if_necessary();
}
let scanner_floats = self.base.floats.clone();
let mut scanner = LineboxScanner::new(scanner_floats);
@ -710,12 +823,12 @@ impl Flow for InlineFlow {
let mut line_height_offset = Au::new(0);
// All lines use text alignment of the flow.
let text_align = self.base.flags_info.flags.text_align();
let text_align = self.base.flags.text_align();
// Now, go through each line and lay out the boxes inside.
for line in self.lines.mut_iter() {
// Lay out boxes horizontally.
InlineFlow::set_horizontal_box_positions(self.boxes, line, text_align);
InlineFlow::set_horizontal_box_positions(&mut self.boxes, line, text_align);
// Set the top y position of the current linebox.
// `line_height_offset` is updated at the end of the previous loop.
@ -728,8 +841,9 @@ impl Flow for InlineFlow {
let (mut biggest_top, mut biggest_bottom) = (Au(0), Au(0));
for box_i in line.range.eachi() {
let cur_box = &self.boxes[box_i];
let top = cur_box.noncontent_top();
let cur_box = self.boxes.boxes.get_mut(box_i);
let top = cur_box.border_padding.top;
// FIXME(pcwalton): Move into `box.rs` like the rest of box-specific layout code?
let (top_from_base, bottom_from_base, ascent) = match cur_box.specific {
@ -738,7 +852,7 @@ impl Flow for InlineFlow {
// TODO: margin, border, padding's top and bottom should be calculated in
// advance, since baseline of image is bottom margin edge.
let bottom = cur_box.noncontent_bottom();
let bottom = cur_box.border_padding.bottom;
let noncontent_height = top + bottom;
height = height + noncontent_height;
@ -760,13 +874,13 @@ impl Flow for InlineFlow {
// Offset from the top of the box is 1/2 of the leading + ascent
let text_offset = text_ascent + (line_height - em_size).scale_by(0.5);
text_bounds.translate(&Point2D(cur_box.border_box.borrow().origin.x, Au(0)));
text_bounds.translate(&Point2D(cur_box.border_box.origin.x, Au(0)));
(text_offset, line_height - text_offset, text_ascent)
},
GenericBox | IframeBox(_) | TableBox | TableCellBox | TableRowBox |
TableWrapperBox => {
let height = cur_box.border_box.borrow().size.height;
let height = cur_box.border_box.size.height;
(height, Au::new(0), height)
},
TableColumnBox(_) => fail!("Table column boxes do not have height"),
@ -822,7 +936,7 @@ impl Flow for InlineFlow {
bottommost = bottom_from_base;
}
cur_box.border_box.borrow_mut().origin.y = line.bounds.origin.y + offset + top;
cur_box.border_box.origin.y = line.bounds.origin.y + offset + top;
}
// Calculate the distance from baseline to the top of the biggest box with 'bottom'
@ -844,27 +958,14 @@ impl Flow for InlineFlow {
// All boxes' y position is updated following the new baseline offset.
for box_i in line.range.eachi() {
let cur_box = &self.boxes[box_i];
let cur_box = self.boxes.get_mut(box_i);
let adjust_offset = match cur_box.vertical_align() {
vertical_align::top => Au::new(0),
vertical_align::bottom => baseline_offset + bottommost,
_ => baseline_offset,
};
let mut border_box = cur_box.border_box.borrow_mut();
border_box.origin.y = border_box.origin.y + adjust_offset;
let mut info = cur_box.inline_info.borrow_mut();
if info.is_none() {
*info = Some(InlineInfo::new());
}
match &mut *info {
&Some(ref mut info) => {
// TODO (ksh8281) compute vertical-align, line-height
info.baseline = line.bounds.origin.y + baseline_offset;
},
&None => {}
}
cur_box.border_box.origin.y = cur_box.border_box.origin.y + adjust_offset;
}
// This is used to set the top y position of the next linebox in the next loop.
@ -875,7 +976,8 @@ impl Flow for InlineFlow {
self.base.position.size.height =
if self.lines.len() > 0 {
self.lines.last().get_ref().bounds.origin.y + self.lines.last().get_ref().bounds.size.height
self.lines.as_slice().last().get_ref().bounds.origin.y +
self.lines.as_slice().last().get_ref().bounds.size.height
} else {
Au::new(0)
};
@ -885,8 +987,253 @@ impl Flow for InlineFlow {
}
fn debug_str(&self) -> ~str {
let val: ~[~str] = self.boxes.iter().map(|s| s.debug_str()).collect();
let toprint = val.connect(",");
format!("InlineFlow: {}", toprint)
let mut string = "InlineFlow: ".to_str();
for (i, (fragment, _)) in self.boxes.iter().enumerate() {
if i != 0 {
string.push_str(", ")
}
string.push_str(fragment.debug_str())
}
string
}
}
/// Information that inline flows keep about a single nested element. This is used to recover the
/// DOM structure from the flat box list when it's needed.
pub struct FragmentRange {
/// The style of the DOM node that this range refers to.
pub style: Arc<ComputedValues>,
/// The range, in indices into the fragment list.
pub range: Range,
}
impl FragmentRange {
/// Creates a new fragment range from the given values.
fn new(style: Arc<ComputedValues>, range: Range) -> FragmentRange {
FragmentRange {
style: style,
range: range,
}
}
/// Returns the dimensions of the border in this fragment range.
pub fn border(&self) -> SideOffsets2D<Au> {
model::border_from_style(&*self.style)
}
/// Returns the dimensions of the padding in this fragment range.
pub fn padding(&self) -> SideOffsets2D<Au> {
// FIXME(#2266, pcwalton): Is Au(0) right here for the containing block?
model::padding_from_style(&*self.style, Au(0))
}
}
struct FragmentFixupWorkItem {
style: Arc<ComputedValues>,
new_start_index: uint,
old_end_index: uint,
}
/// The type of an iterator over fragment ranges in the fragment map.
pub struct RangeIterator<'a> {
iter: Items<'a,FragmentRange>,
index: uint,
seen_first: bool,
}
impl<'a> Iterator<&'a FragmentRange> for RangeIterator<'a> {
fn next(&mut self) -> Option<&'a FragmentRange> {
if self.seen_first {
match self.iter.next() {
Some(fragment_range) if fragment_range.range.contains(self.index) => {
return Some(fragment_range)
}
Some(_) | None => return None
}
}
loop {
match self.iter.next() {
None => return None,
Some(fragment_range) if fragment_range.range.contains(self.index) => {
self.seen_first = true;
return Some(fragment_range)
}
Some(_) => {}
}
}
}
}
/// Information that inline flows keep about nested elements. This is used to recover the DOM
/// structure from the flat box list when it's needed.
pub struct FragmentMap {
list: SmallVec0<FragmentRange>,
}
impl FragmentMap {
/// Creates a new fragment map.
pub fn new() -> FragmentMap {
FragmentMap {
list: SmallVec0::new(),
}
}
/// Adds the given node to the fragment map.
pub fn push(&mut self, style: Arc<ComputedValues>, range: Range) {
self.list.push(FragmentRange::new(style, range))
}
/// Pushes the ranges in another fragment map onto the end of this one, adjusting indices as
/// necessary.
fn push_all(&mut self, other: FragmentMap, adjustment: uint) {
let FragmentMap {
list: mut other_list
} = other;
for other_range in other_list.move_iter() {
let FragmentRange {
style: other_style,
range: mut other_range
} = other_range;
other_range.shift_by(adjustment as int);
self.push(other_style, other_range)
}
}
/// Returns the range with the given index.
pub fn get_mut<'a>(&'a mut self, index: uint) -> &'a mut FragmentRange {
&mut self.list.as_mut_slice()[index]
}
/// Iterates over all ranges that contain the box with the given index, outermost first.
#[inline(always)]
fn ranges_for_index<'a>(&'a self, index: uint) -> RangeIterator<'a> {
RangeIterator {
iter: self.list.as_slice().iter(),
index: index,
seen_first: false,
}
}
/// Rebuilds the list after the fragments have been split or deleted (for example, for line
/// breaking). This assumes that the overall structure of the DOM has not changed; if the
/// DOM has changed, then the flow constructor will need to do more complicated surgery than
/// this function can provide.
///
/// FIXME(#2267, pcwalton): It would be more efficient to not have to clone boxes all the time;
/// i.e. if `old_boxes` contained less info than the entire range of boxes. See
/// `layout::construct::strip_ignorable_whitespace_from_start` for an example of some code that
/// needlessly has to clone boxes.
pub fn fixup(&mut self, old_fragments: &[Box], new_fragments: &[Box]) {
// TODO(pcwalton): Post Rust upgrade, use `with_capacity` here.
let mut old_list = mem::replace(&mut self.list, SmallVec0::new());
let mut worklist = SmallVec0::new(); // FIXME(#2269, pcwalton): was smallvec4
let mut old_list_iter = old_list.move_iter().peekable();
let mut new_fragments_iter = new_fragments.iter().enumerate().peekable();
// FIXME(#2270, pcwalton): I don't think this will work if multiple old fragments
// correspond to the same node.
for (old_fragment_index, old_fragment) in old_fragments.iter().enumerate() {
// Find the start of the corresponding new fragment.
let new_fragment_start = match new_fragments_iter.peek() {
Some(&(index, new_fragment)) if new_fragment.node == old_fragment.node => {
// We found the start of the corresponding new fragment.
index
}
Some(_) | None => {
// The old fragment got deleted entirely.
continue
}
};
drop(new_fragments_iter.next());
// Eat any additional fragments that the old fragment got split into.
loop {
match new_fragments_iter.peek() {
Some(&(_, new_fragment)) if new_fragment.node == old_fragment.node => {}
Some(_) | None => break,
}
drop(new_fragments_iter.next());
}
// Find all ranges that started at this old fragment and add them onto the worklist.
loop {
match old_list_iter.peek() {
None => break,
Some(fragment_range) => {
if fragment_range.range.begin() > old_fragment_index {
// We haven't gotten to the appropriate old fragment yet, so stop.
break
}
// Note that it can be the case that `fragment_range.range.begin() < i`.
// This is OK, as it corresponds to the case in which a fragment got
// deleted entirely (e.g. ignorable whitespace got nuked). In that case we
// want to keep the range, but shorten it.
}
};
let FragmentRange {
style: style,
range: old_range,
} = old_list_iter.next().unwrap();
worklist.push(FragmentFixupWorkItem {
style: style,
new_start_index: new_fragment_start,
old_end_index: old_range.end(),
});
}
// Pop off any ranges that ended at this fragment.
loop {
match worklist.as_slice().last() {
None => break,
Some(last_work_item) => {
if last_work_item.old_end_index > old_fragment_index + 1 {
// Haven't gotten to it yet.
break
}
}
}
let new_last_index = match new_fragments_iter.peek() {
None => {
// At the end.
new_fragments.len()
}
Some(&(index, _)) => index,
};
let FragmentFixupWorkItem {
style,
new_start_index,
..
} = worklist.pop().unwrap();
let range = Range::new(new_start_index, new_last_index - new_start_index);
self.list.push(FragmentRange::new(style, range))
}
}
}
}
/// The context that an inline fragment appears in. This allows the fragment map to be passed in
/// conveniently to various fragment functions.
pub struct InlineFragmentContext<'a> {
map: &'a FragmentMap,
index: uint,
}
impl<'a> InlineFragmentContext<'a> {
pub fn new<'a>(map: &'a FragmentMap, index: uint) -> InlineFragmentContext<'a> {
InlineFragmentContext {
map: map,
index: index,
}
}
pub fn ranges(&self) -> RangeIterator<'a> {
self.map.ranges_for_index(self.index)
}
}

View file

@ -42,6 +42,7 @@ use script::layout_interface::{ReflowForDisplay, ReflowMsg};
use script::script_task::{ReflowCompleteMsg, ScriptChan, SendEventMsg};
use servo_msg::compositor_msg::Scrollable;
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, Failure, FailureMsg};
use servo_net::image::holder::LocalImageCacheHandle;
use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg};
use servo_net::local_image_cache::{ImageResponder, LocalImageCache};
use servo_util::geometry::Au;
@ -87,8 +88,7 @@ pub struct LayoutTask {
pub image_cache_task: ImageCacheTask,
/// The local image cache.
// FIXME(rust#13125): Remove the *() for the real type.
pub local_image_cache: Arc<Mutex<*()>>,
pub local_image_cache: LocalImageCacheHandle,
/// The size of the viewport.
pub screen_size: Size2D<Au>,
@ -160,7 +160,7 @@ impl PreorderFlowTraversal for FlowTreeVerificationTraversal {
#[inline]
fn process(&mut self, flow: &mut Flow) -> bool {
let base = flow::base(flow);
if !base.flags_info.flags.is_leaf() && !base.flags_info.flags.is_nonleaf() {
if !base.flags.is_leaf() && !base.flags.is_nonleaf() {
println("flow tree verification failed: flow wasn't a leaf or a nonleaf!");
flow.dump();
fail!("flow tree verification failed")
@ -225,7 +225,7 @@ impl<'a> PostorderFlowTraversal for AssignHeightsAndStoreOverflowTraversal<'a> {
#[inline]
fn should_process(&mut self, flow: &mut Flow) -> bool {
!flow::base(flow).flags_info.flags.inorder()
!flow::base(flow).flags.inorder()
}
}
@ -291,9 +291,11 @@ impl LayoutTask {
profiler_chan: ProfilerChan)
-> LayoutTask {
let local_image_cache = ~LocalImageCache(image_cache_task.clone());
let local_image_cache = Arc::new(Mutex::new(unsafe {
cast::transmute::<~LocalImageCache, *()>(local_image_cache)
}));
let local_image_cache = unsafe {
let cache = Arc::new(Mutex::new(cast::transmute::<~LocalImageCache,
*()>(local_image_cache)));
LocalImageCacheHandle::new(cast::transmute::<Arc<Mutex<*()>>,Arc<*()>>(cache))
};
let screen_size = Size2D(Au(0), Au(0));
let parallel_traversal = if opts.layout_threads != 1 {
Some(WorkQueue::new("LayoutWorker", opts.layout_threads, ptr::mut_null()))
@ -429,7 +431,7 @@ impl LayoutTask {
}
/// Retrieves the flow tree root from the root node.
fn get_layout_root(&self, node: LayoutNode) -> ~Flow {
fn get_layout_root(&self, node: LayoutNode) -> ~Flow:Share {
let mut layout_data_ref = node.mutate_layout_data();
let result = match &mut *layout_data_ref {
&Some(ref mut layout_data) => {
@ -495,7 +497,7 @@ impl LayoutTask {
/// benchmarked against those two. It is marked `#[inline(never)]` to aid profiling.
#[inline(never)]
fn solve_constraints_parallel(&mut self,
layout_root: &mut ~Flow,
layout_root: &mut ~Flow:Share,
layout_context: &mut LayoutContext) {
if layout_context.opts.bubble_widths_separately {
let mut traversal = BubbleWidthsTraversal {
@ -521,13 +523,13 @@ impl LayoutTask {
/// This is only on in debug builds.
#[inline(never)]
#[cfg(debug)]
fn verify_flow_tree(&mut self, layout_root: &mut ~Flow) {
fn verify_flow_tree(&mut self, layout_root: &mut ~Flow:Share) {
let mut traversal = FlowTreeVerificationTraversal;
layout_root.traverse_preorder(&mut traversal);
}
#[cfg(not(debug))]
fn verify_flow_tree(&mut self, _: &mut ~Flow) {
fn verify_flow_tree(&mut self, _: &mut ~Flow:Share) {
}
/// The high-level routine that performs layout tasks.
@ -545,10 +547,7 @@ impl LayoutTask {
{
// Reset the image cache.
let val = self.local_image_cache.lock();
let local_image_cache = unsafe {
cast::transmute::<*(), &mut LocalImageCache>(*val)
};
let mut local_image_cache = self.local_image_cache.get().lock();
local_image_cache.next_round(self.make_on_image_available_cb());
}

View file

@ -7,7 +7,10 @@
use layout::box_::Box;
use computed = style::computed_values;
use geom::SideOffsets2D;
use style::computed_values::{LPA_Auto, LPA_Length, LPA_Percentage, LP_Length, LP_Percentage};
use style::computed_values::{border_style};
use style::ComputedValues;
use servo_util::geometry::Au;
use servo_util::geometry;
@ -101,7 +104,7 @@ impl MarginCollapseInfo {
self.state = AccumulatingMarginIn
}
self.top_margin = AdjoiningMargins::from_margin(fragment.margin.borrow().top)
self.top_margin = AdjoiningMargins::from_margin(fragment.margin.top)
}
pub fn finish_and_compute_collapsible_margins(mut self,
@ -135,7 +138,7 @@ impl MarginCollapseInfo {
// Different logic is needed here depending on whether this flow can collapse its bottom
// margin with its children.
let bottom_margin = fragment.margin.borrow().bottom;
let bottom_margin = fragment.margin.bottom;
if !can_collapse_bottom_margin_with_kids {
match state {
MarginsCollapseThroughFinalMarginState => {
@ -310,3 +313,32 @@ pub fn specified(length: computed::LengthOrPercentage, containing_length: Au) ->
computed::LP_Percentage(p) => containing_length.scale_by(p)
}
}
#[inline]
pub fn border_from_style(style: &ComputedValues) -> SideOffsets2D<Au> {
#[inline]
fn width(width: Au, style: border_style::T) -> Au {
if style == border_style::none {
Au(0)
} else {
width
}
}
let border_style = style.Border.get();
SideOffsets2D::new(width(border_style.border_top_width, border_style.border_top_style),
width(border_style.border_right_width, border_style.border_right_style),
width(border_style.border_bottom_width, border_style.border_bottom_style),
width(border_style.border_left_width, border_style.border_left_style))
}
#[inline]
pub fn padding_from_style(style: &ComputedValues, containing_block_width: Au)
-> SideOffsets2D<Au> {
let padding_style = style.Padding.get();
SideOffsets2D::new(specified(padding_style.padding_top, containing_block_width),
specified(padding_style.padding_right, containing_block_width),
specified(padding_style.padding_bottom, containing_block_width),
specified(padding_style.padding_left, containing_block_width))
}

View file

@ -63,13 +63,13 @@ fn null_unsafe_flow() -> UnsafeFlow {
(0, 0)
}
pub fn owned_flow_to_unsafe_flow(flow: *~Flow) -> UnsafeFlow {
pub fn owned_flow_to_unsafe_flow(flow: *~Flow:Share) -> UnsafeFlow {
unsafe {
cast::transmute_copy(&*flow)
}
}
pub fn mut_owned_flow_to_unsafe_flow(flow: *mut ~Flow) -> UnsafeFlow {
pub fn mut_owned_flow_to_unsafe_flow(flow: *mut ~Flow:Share) -> UnsafeFlow {
unsafe {
cast::transmute_copy(&*flow)
}
@ -138,7 +138,7 @@ trait ParallelPostorderFlowTraversal : PostorderFlowTraversal {
loop {
unsafe {
// Get a real flow.
let flow: &mut ~Flow = cast::transmute(&unsafe_flow);
let flow: &mut ~Flow:Share = cast::transmute(&unsafe_flow);
// Perform the appropriate traversal.
if self.should_process(*flow) {
@ -160,7 +160,7 @@ trait ParallelPostorderFlowTraversal : PostorderFlowTraversal {
// No, we're not at the root yet. Then are we the last child
// of our parent to finish processing? If so, we can continue
// on with our parent; otherwise, we've gotta wait.
let parent: &mut ~Flow = cast::transmute(&unsafe_parent);
let parent: &mut ~Flow:Share = cast::transmute(&unsafe_parent);
let parent_base = flow::mut_base(*parent);
if parent_base.parallel.children_count.fetch_sub(1, SeqCst) == 1 {
// We were the last child of our parent. Reflow our parent.
@ -192,7 +192,7 @@ trait ParallelPreorderFlowTraversal : PreorderFlowTraversal {
let mut had_children = false;
unsafe {
// Get a real flow.
let flow: &mut ~Flow = cast::transmute(&unsafe_flow);
let flow: &mut ~Flow:Share = cast::transmute(&unsafe_flow);
// Perform the appropriate traversal.
self.process(*flow);
@ -423,7 +423,7 @@ pub fn recalc_style_for_subtree(root_node: &LayoutNode,
queue.data = ptr::mut_null()
}
pub fn traverse_flow_tree_preorder(root: &mut ~Flow,
pub fn traverse_flow_tree_preorder(root: &mut ~Flow:Share,
profiler_chan: ProfilerChan,
layout_context: &mut LayoutContext,
queue: &mut WorkQueue<*mut LayoutContext,PaddedUnsafeFlow>) {

View file

@ -241,7 +241,6 @@ impl Flow for TableFlow {
let child_base = flow::mut_base(kid);
num_floats = num_floats + child_base.num_floats;
}
self.block_flow.box_.compute_borders(self.block_flow.box_.style());
self.block_flow.base.num_floats = num_floats;
self.block_flow.base.intrinsic_widths.minimum_width = min_width;
self.block_flow.base.intrinsic_widths.preferred_width = geometry::max(min_width, pref_width);
@ -268,10 +267,9 @@ impl Flow for TableFlow {
let width_computer = InternalTable;
width_computer.compute_used_width(&mut self.block_flow, ctx, containing_block_width);
let left_content_edge = self.block_flow.box_.padding.borrow().left + self.block_flow.box_.border.borrow().left;
let padding_and_borders = self.block_flow.box_.padding.borrow().left + self.block_flow.box_.padding.borrow().right +
self.block_flow.box_.border.borrow().left + self.block_flow.box_.border.borrow().right;
let content_width = self.block_flow.box_.border_box.borrow().size.width - padding_and_borders;
let left_content_edge = self.block_flow.box_.border_padding.left;
let padding_and_borders = self.block_flow.box_.border_padding.horizontal();
let content_width = self.block_flow.box_.border_box.size.width - padding_and_borders;
match self.table_layout {
FixedLayout => {
@ -320,8 +318,8 @@ impl Flow for TableFlow {
/// Table, TableRowGroup, TableRow, TableCell types.
/// Their widths are calculated in the same way and do not have margins.
pub struct InternalTable;
impl WidthAndMarginsComputer for InternalTable {
impl WidthAndMarginsComputer for InternalTable {
/// Compute the used value of width, taking care of min-width and max-width.
///
/// CSS Section 10.4: Minimum and Maximum widths
@ -330,16 +328,12 @@ impl WidthAndMarginsComputer for InternalTable {
ctx: &mut LayoutContext,
parent_flow_width: Au) {
let input = self.compute_width_constraint_inputs(block, parent_flow_width, ctx);
let solution = self.solve_width_constraints(block, input);
let solution = self.solve_width_constraints(block, &input);
self.set_width_constraint_solutions(block, solution);
}
/// Solve the width and margins constraints for this block flow.
fn solve_width_constraints(&self,
_: &mut BlockFlow,
input: WidthConstraintInput)
fn solve_width_constraints(&self, _: &mut BlockFlow, input: &WidthConstraintInput)
-> WidthConstraintSolution {
WidthConstraintSolution::new(input.available_width, Au::new(0), Au::new(0))
}

View file

@ -39,6 +39,10 @@ impl TableCellFlow {
&self.block_flow.box_
}
pub fn mut_box<'a>(&'a mut self) -> &'a mut Box {
&mut self.block_flow.box_
}
/// Assign height for table-cell flow.
///
/// TODO(#2015, pcwalton): This doesn't handle floats right.
@ -100,12 +104,14 @@ impl Flow for TableCellFlow {
let width_computer = InternalTable;
width_computer.compute_used_width(&mut self.block_flow, ctx, containing_block_width);
let left_content_edge = self.block_flow.box_.border_box.borrow().origin.x + self.block_flow.box_.padding.borrow().left + self.block_flow.box_.border.borrow().left;
let padding_and_borders = self.block_flow.box_.padding.borrow().left + self.block_flow.box_.padding.borrow().right +
self.block_flow.box_.border.borrow().left + self.block_flow.box_.border.borrow().right;
let content_width = self.block_flow.box_.border_box.borrow().size.width - padding_and_borders;
let left_content_edge = self.block_flow.box_.border_box.origin.x +
self.block_flow.box_.border_padding.left;
let padding_and_borders = self.block_flow.box_.border_padding.horizontal();
let content_width = self.block_flow.box_.border_box.size.width - padding_and_borders;
self.block_flow.propagate_assigned_width_to_children(left_content_edge, content_width, None);
self.block_flow.propagate_assigned_width_to_children(left_content_edge,
content_width,
None);
}
/// This is called on kid flows by a parent.

View file

@ -98,7 +98,9 @@ impl TableRowFlow {
// TODO: Percentage height
let child_specified_height = MaybeAuto::from_style(child_box.style().Box.get().height,
Au::new(0)).specified_or_zero();
max_y = geometry::max(max_y, child_specified_height + child_box.noncontent_height());
max_y =
geometry::max(max_y,
child_specified_height + child_box.border_padding.vertical());
}
let child_node = flow::mut_base(kid);
child_node.position.origin.y = cur_y;
@ -116,18 +118,18 @@ impl TableRowFlow {
// Assign the height of own box
//
// FIXME(pcwalton): Take `cur_y` into account.
let mut position = *self.block_flow.box_.border_box.borrow();
let mut position = self.block_flow.box_.border_box;
position.size.height = height;
*self.block_flow.box_.border_box.borrow_mut() = position;
self.block_flow.box_.border_box = position;
self.block_flow.base.position.size.height = height;
// Assign the height of kid boxes, which is the same value as own height.
for kid in self.block_flow.base.child_iter() {
{
let kid_box_ = kid.as_table_cell().box_();
let mut position = *kid_box_.border_box.borrow();
let kid_box_ = kid.as_table_cell().mut_box();
let mut position = kid_box_.border_box;
position.size.height = height;
*kid_box_.border_box.borrow_mut() = position;
kid_box_.border_box = position;
}
let child_node = flow::mut_base(kid);
child_node.position.size.height = height;

View file

@ -93,9 +93,9 @@ impl TableRowGroupFlow {
let height = cur_y - top_offset;
let mut position = *self.block_flow.box_.border_box.borrow();
let mut position = self.block_flow.box_.border_box;
position.size.height = height;
*self.block_flow.box_.border_box.borrow_mut() = position;
self.block_flow.box_.border_box = position;
self.block_flow.base.position.size.height = height;
}

View file

@ -170,8 +170,8 @@ impl Flow for TableWrapperFlow {
let width_computer = TableWrapper;
width_computer.compute_used_width_table_wrapper(self, ctx, containing_block_width);
let left_content_edge = self.block_flow.box_.border_box.borrow().origin.x;
let content_width = self.block_flow.box_.border_box.borrow().size.width;
let left_content_edge = self.block_flow.box_.border_box.origin.x;
let content_width = self.block_flow.box_.border_box.size.width;
match self.table_layout {
FixedLayout | _ if self.is_float() =>
@ -222,16 +222,17 @@ impl Flow for TableWrapperFlow {
}
struct TableWrapper;
impl TableWrapper {
fn compute_used_width_table_wrapper(&self,
table_wrapper: &mut TableWrapperFlow,
ctx: &mut LayoutContext,
parent_flow_width: Au) {
let input = self.compute_width_constraint_inputs_table_wrapper(table_wrapper,
parent_flow_width,
ctx);
parent_flow_width,
ctx);
let solution = self.solve_width_constraints(&mut table_wrapper.block_flow, input);
let solution = self.solve_width_constraints(&mut table_wrapper.block_flow, &input);
self.set_width_constraint_solutions(&mut table_wrapper.block_flow, solution);
self.set_flow_x_coord_if_necessary(&mut table_wrapper.block_flow, solution);
@ -334,9 +335,7 @@ impl TableWrapper {
impl WidthAndMarginsComputer for TableWrapper {
/// Solve the width and margins constraints for this block flow.
fn solve_width_constraints(&self,
block: &mut BlockFlow,
input: WidthConstraintInput)
fn solve_width_constraints(&self, block: &mut BlockFlow, input: &WidthConstraintInput)
-> WidthConstraintSolution {
self.solve_block_width_constraints(block, input)
}

View file

@ -6,15 +6,28 @@
use layout::box_::{Box, ScannedTextBox, ScannedTextBoxInfo, UnscannedTextBox};
use layout::flow::Flow;
use layout::inline::InlineBoxes;
use gfx::font_context::FontContext;
use gfx::text::text_run::TextRun;
use gfx::text::util::{CompressWhitespaceNewline, transform_text, CompressNone};
use servo_util::range::Range;
use servo_util::smallvec::{SmallVec, SmallVec0};
use std::mem;
use std::slice;
use style::computed_values::white_space;
use sync::Arc;
struct NewLinePositions {
new_line_pos: ~[uint],
}
// A helper function.
fn can_coalesce_text_nodes(boxes: &[Box], left_i: uint, right_i: uint) -> bool {
assert!(left_i != right_i);
boxes[left_i].can_merge_with_box(&boxes[right_i])
}
/// A stack-allocated object for scanning an inline flow into `TextRun`-containing `TextBox`es.
pub struct TextRunScanner {
pub clump: Range,
@ -33,36 +46,40 @@ impl TextRunScanner {
debug!("TextRunScanner: scanning {:u} boxes for text runs...", inline.boxes.len());
}
let InlineBoxes {
boxes: old_boxes,
map: mut map
} = mem::replace(&mut flow.as_inline().boxes, InlineBoxes::new());
let mut last_whitespace = true;
let mut out_boxes = ~[];
for box_i in range(0, flow.as_immutable_inline().boxes.len()) {
let mut new_boxes = SmallVec0::new();
for box_i in range(0, old_boxes.len()) {
debug!("TextRunScanner: considering box: {:u}", box_i);
if box_i > 0 && !can_coalesce_text_nodes(flow.as_immutable_inline().boxes,
box_i - 1,
box_i) {
if box_i > 0 && !can_coalesce_text_nodes(old_boxes.as_slice(), box_i - 1, box_i) {
last_whitespace = self.flush_clump_to_list(font_context,
flow,
last_whitespace,
&mut out_boxes);
old_boxes.as_slice(),
&mut new_boxes,
last_whitespace);
}
self.clump.extend_by(1);
}
// handle remaining clumps
// Handle remaining clumps.
if self.clump.length() > 0 {
self.flush_clump_to_list(font_context, flow, last_whitespace, &mut out_boxes);
drop(self.flush_clump_to_list(font_context,
old_boxes.as_slice(),
&mut new_boxes,
last_whitespace))
}
debug!("TextRunScanner: swapping out boxes.");
// Swap out the old and new box list of the flow.
flow.as_inline().boxes = out_boxes;
// A helper function.
fn can_coalesce_text_nodes(boxes: &[Box], left_i: uint, right_i: uint) -> bool {
assert!(left_i < boxes.len());
assert!(right_i > 0 && right_i < boxes.len());
assert!(left_i != right_i);
boxes[left_i].can_merge_with_box(&boxes[right_i])
map.fixup(old_boxes.as_slice(), new_boxes.as_slice());
flow.as_inline().boxes = InlineBoxes {
boxes: new_boxes,
map: map,
}
}
@ -73,17 +90,14 @@ impl TextRunScanner {
/// for correct painting order. Since we compress several leaf boxes here, the mapping must be
/// adjusted.
///
/// FIXME(pcwalton): Stop cloning boxes. Instead we will need to consume the `in_box`es as we
/// iterate over them.
/// FIXME(#2267, pcwalton): Stop cloning boxes. Instead we will need to replace each `in_box`
/// with some smaller stub.
pub fn flush_clump_to_list(&mut self,
font_context: &mut FontContext,
flow: &mut Flow,
last_whitespace: bool,
out_boxes: &mut ~[Box])
in_boxes: &[Box],
out_boxes: &mut SmallVec0<Box>,
last_whitespace: bool)
-> bool {
let inline = flow.as_inline();
let in_boxes = &mut inline.boxes;
assert!(self.clump.length() > 0);
debug!("TextRunScanner: flushing boxes in range={}", self.clump);
@ -122,7 +136,6 @@ impl TextRunScanner {
};
let mut new_line_pos = ~[];
let (transformed_text, whitespace) = transform_text(*text,
compression,
last_whitespace,
@ -135,7 +148,8 @@ impl TextRunScanner {
// font group fonts. This is probably achieved by creating the font group above
// and then letting `FontGroup` decide which `Font` to stick into the text run.
let fontgroup = font_context.get_resolved_font_for_style(&font_style);
let run = ~fontgroup.borrow().create_textrun(transformed_text.clone(), decoration);
let run = ~fontgroup.borrow().create_textrun(transformed_text.clone(),
decoration);
debug!("TextRunScanner: pushing single text box in range: {} ({})",
self.clump,
@ -147,14 +161,6 @@ impl TextRunScanner {
ScannedTextBox(new_text_box_info));
new_box.new_line_pos = new_line_pos;
out_boxes.push(new_box)
} else {
if self.clump.begin() + 1 < in_boxes.len() {
// if the this box has border,margin,padding of inline,
// we should copy that stuff to next box.
in_boxes[self.clump.begin() + 1]
.merge_noncontent_inline_left(&in_boxes[self.clump.begin()]);
}
}
},
(false, true) => {
@ -172,10 +178,6 @@ impl TextRunScanner {
white_space::pre => CompressNone,
};
struct NewLinePositions {
new_line_pos: ~[uint],
}
let mut new_line_positions: ~[NewLinePositions] = ~[];
// First, transform/compress text of all the nodes.
@ -235,42 +237,19 @@ impl TextRunScanner {
debug!("Elided an `UnscannedTextbox` because it was zero-length after \
compression; {:s}",
in_boxes[i].debug_str());
// in this case, in_boxes[i] is elided
// so, we should merge inline info with next index of in_boxes
if i + 1 < in_boxes.len() {
in_boxes[i + 1].merge_noncontent_inline_left(&in_boxes[i]);
}
continue
}
let new_text_box_info = ScannedTextBoxInfo::new(run.get_ref().clone(), range);
let new_metrics = new_text_box_info.run.metrics_for_range(&range);
let mut new_box = in_boxes[i].transform(new_metrics.bounding_box.size,
ScannedTextBox(new_text_box_info));
ScannedTextBox(new_text_box_info));
new_box.new_line_pos = new_line_positions[logical_offset].new_line_pos.clone();
out_boxes.push(new_box)
}
}
} // End of match.
debug!("--- In boxes: ---");
for (i, box_) in in_boxes.iter().enumerate() {
debug!("{:u} --> {:s}", i, box_.debug_str());
}
debug!("------------------");
debug!("--- Out boxes: ---");
for (i, box_) in out_boxes.iter().enumerate() {
debug!("{:u} --> {:s}", i, box_.debug_str());
}
debug!("------------------");
debug!("--- Elem ranges: ---");
for (i, nr) in inline.elems.eachi() {
debug!("{:u}: {} --> {:?}", i, nr.range, nr.node.id()); ()
}
debug!("--------------------");
let end = self.clump.end(); // FIXME: borrow checker workaround
self.clump.reset(end, 0);

View file

@ -2,7 +2,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use layout::box_::Box;
use layout::construct::{ConstructionResult, NoConstructionResult};
use layout::parallel::DomParallelInfo;
use layout::wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode};
@ -13,122 +12,11 @@ use script::dom::bindings::js::JS;
use script::dom::bindings::utils::Reflectable;
use script::dom::node::Node;
use script::layout_interface::{LayoutChan, UntrustedNodeAddress, TrustedNodeAddress};
use servo_util::range::Range;
use std::cast;
use std::cell::{Ref, RefMut};
use std::iter::Enumerate;
use std::slice::Items;
use style::ComputedValues;
use sync::Arc;
/// A range of nodes.
pub struct NodeRange {
pub node: OpaqueNode,
pub range: Range,
}
impl NodeRange {
pub fn new(node: OpaqueNode, range: &Range) -> NodeRange {
NodeRange {
node: node,
range: (*range).clone()
}
}
}
pub struct ElementMapping {
entries: ~[NodeRange],
}
impl ElementMapping {
pub fn new() -> ElementMapping {
ElementMapping {
entries: ~[],
}
}
pub fn add_mapping(&mut self, node: OpaqueNode, range: &Range) {
self.entries.push(NodeRange::new(node, range))
}
pub fn each(&self, callback: |nr: &NodeRange| -> bool) -> bool {
for nr in self.entries.iter() {
if !callback(nr) {
break
}
}
true
}
pub fn eachi<'a>(&'a self) -> Enumerate<Items<'a, NodeRange>> {
self.entries.iter().enumerate()
}
pub fn repair_for_box_changes(&mut self, old_boxes: &[Box], new_boxes: &[Box]) {
let entries = &mut self.entries;
debug!("--- Old boxes: ---");
for (i, box_) in old_boxes.iter().enumerate() {
debug!("{:u} --> {:s}", i, box_.debug_str());
}
debug!("------------------");
debug!("--- New boxes: ---");
for (i, box_) in new_boxes.iter().enumerate() {
debug!("{:u} --> {:s}", i, box_.debug_str());
}
debug!("------------------");
debug!("--- Elem ranges before repair: ---");
for (i, nr) in entries.iter().enumerate() {
debug!("{:u}: {} --> {:?}", i, nr.range, nr.node.id());
}
debug!("----------------------------------");
let mut old_i = 0;
let mut new_j = 0;
struct WorkItem {
begin_idx: uint,
entry_idx: uint,
};
let mut repair_stack : ~[WorkItem] = ~[];
// index into entries
let mut entries_k = 0;
while old_i < old_boxes.len() {
debug!("repair_for_box_changes: Considering old box {:u}", old_i);
// possibly push several items
while entries_k < entries.len() && old_i == entries[entries_k].range.begin() {
let item = WorkItem {begin_idx: new_j, entry_idx: entries_k};
debug!("repair_for_box_changes: Push work item for elem {:u}: {:?}", entries_k, item);
repair_stack.push(item);
entries_k += 1;
}
while new_j < new_boxes.len() && old_boxes[old_i].node != new_boxes[new_j].node {
debug!("repair_for_box_changes: Slide through new box {:u}", new_j);
new_j += 1;
}
old_i += 1;
// possibly pop several items
while repair_stack.len() > 0 && old_i == entries[repair_stack.last().get_ref().entry_idx].range.end() {
let item = repair_stack.pop().unwrap();
debug!("repair_for_box_changes: Set range for {:u} to {}",
item.entry_idx, Range::new(item.begin_idx, new_j - item.begin_idx));
entries[item.entry_idx].range = Range::new(item.begin_idx, new_j - item.begin_idx);
}
}
debug!("--- Elem ranges after repair: ---");
for (i, nr) in entries.iter().enumerate() {
debug!("{:u}: {} --> {:?}", i, nr.range, nr.node.id());
}
debug!("----------------------------------");
}
}
/// Data that layout associates with a node.
pub struct PrivateLayoutData {
/// The results of CSS styling for this node.

View file

@ -6,15 +6,55 @@ use image::base::Image;
use image_cache_task::{ImageReady, ImageNotReady, ImageFailed};
use local_image_cache::LocalImageCache;
use sync::{Arc, Mutex};
use geom::size::Size2D;
use std::{cast, mem};
use std::cast;
use std::mem;
use std::ptr;
use sync::{Arc, Mutex};
use url::Url;
// FIXME: Nasty coupling here This will be a problem if we want to factor out image handling from
// the network stack. This should probably be factored out into an interface and use dependency
// injection.
/// An unfortunate hack to make this `Arc<Mutex>` `Share`.
pub struct LocalImageCacheHandle {
data: *uint,
}
impl Drop for LocalImageCacheHandle {
fn drop(&mut self) {
unsafe {
let _: ~Arc<Mutex<~LocalImageCache>> =
cast::transmute(mem::replace(&mut self.data, ptr::null()));
}
}
}
impl Clone for LocalImageCacheHandle {
fn clone(&self) -> LocalImageCacheHandle {
unsafe {
let handle = cast::transmute::<&Arc<Mutex<~LocalImageCache>>,&Arc<*()>>(self.get());
let new_handle = (*handle).clone();
LocalImageCacheHandle::new(new_handle)
}
}
}
impl LocalImageCacheHandle {
pub unsafe fn new(cache: Arc<*()>) -> LocalImageCacheHandle {
LocalImageCacheHandle {
data: cast::transmute(~cache),
}
}
pub fn get<'a>(&'a self) -> &'a Arc<Mutex<~LocalImageCache>> {
unsafe {
cast::transmute::<*uint,&'a Arc<Mutex<~LocalImageCache>>>(self.data)
}
}
}
/// A struct to store image data. The image will be loaded once the first time it is requested,
/// and an Arc will be stored. Clones of this Arc are given out on demand.
#[deriving(Clone)]
@ -22,12 +62,11 @@ pub struct ImageHolder {
url: Url,
image: Option<Arc<~Image>>,
cached_size: Size2D<int>,
// FIXME(rust#13125): Remove the *() for the real type.
local_image_cache: Arc<Mutex<*()>>,
local_image_cache: LocalImageCacheHandle,
}
impl ImageHolder {
pub fn new(url: Url, local_image_cache: Arc<Mutex<*()>>) -> ImageHolder {
pub fn new(url: Url, local_image_cache: LocalImageCacheHandle) -> ImageHolder {
debug!("ImageHolder::new() {}", url.to_str());
let holder = ImageHolder {
url: url,
@ -42,10 +81,8 @@ impl ImageHolder {
// should be done as early as possible and decode only once we
// are sure that the image will be used.
{
let val = holder.local_image_cache.lock();
let mut local_image_cache = unsafe {
cast::transmute::<*(), &mut LocalImageCache>(*val)
};
let val = holder.local_image_cache.get().lock();
let mut local_image_cache = val;
local_image_cache.prefetch(&holder.url);
local_image_cache.decode(&holder.url);
}
@ -79,10 +116,8 @@ impl ImageHolder {
// the image and store it for the future
if self.image.is_none() {
let port = {
let val = self.local_image_cache.lock();
let mut local_image_cache = unsafe {
cast::transmute::<*(), &mut LocalImageCache>(*val)
};
let val = self.local_image_cache.get().lock();
let mut local_image_cache = val;
local_image_cache.get_image(&self.url)
};
match port.recv() {

View file

@ -170,11 +170,13 @@ pub mod computed {
pub use servo_util::geometry::Au;
pub struct Context {
pub color: longhands::color::computed_value::T,
pub inherited_font_weight: longhands::font_weight::computed_value::T,
pub inherited_font_size: longhands::font_size::computed_value::T,
pub inherited_minimum_line_height: longhands::_servo_minimum_line_height::T,
pub inherited_text_decorations_in_effect: longhands::_servo_text_decorations_in_effect::T,
pub inherited_height: longhands::height::T,
pub color: longhands::color::computed_value::T,
pub text_decoration: longhands::text_decoration::computed_value::T,
pub font_size: longhands::font_size::computed_value::T,
pub display: longhands::display::computed_value::T,
pub positioned: bool,

View file

@ -38,7 +38,10 @@ class Longhand(object):
self.name = name
self.ident = to_rust_ident(name)
self.style_struct = THIS_STYLE_STRUCT
self.derived_from = None if derived_from is None else to_rust_ident(derived_from)
if derived_from is None:
self.derived_from = None
else:
self.derived_from = [ to_rust_ident(name) for name in derived_from ]
class Shorthand(object):
def __init__(self, name, sub_properties):
@ -87,11 +90,17 @@ pub mod longhands {
<%def name="raw_longhand(name, no_super=False, derived_from=None)">
<%
if derived_from is not None:
derived_from = derived_from.split()
property = Longhand(name, derived_from=derived_from)
THIS_STYLE_STRUCT.longhands.append(property)
LONGHANDS.append(property)
LONGHANDS_BY_NAME[name] = property
DERIVED_LONGHANDS.setdefault(derived_from, []).append(property)
if derived_from is not None:
for name in derived_from:
DERIVED_LONGHANDS.setdefault(name, []).append(property)
%>
pub mod ${property.ident} {
% if not no_super:
@ -419,8 +428,9 @@ pub mod longhands {
}
#[inline]
pub fn derive(value: line_height::computed_value::T, context: &computed::Context)
-> Au {
pub fn derive_from_line_height(value: line_height::computed_value::T,
context: &computed::Context)
-> Au {
if context.display != display::computed_value::inline {
match value {
line_height::Normal => context.font_size.scale_by(DEFAULT_LINE_HEIGHT),
@ -936,6 +946,82 @@ pub mod longhands {
${switch_to_style_struct("InheritedText")}
<%self:longhand name="-servo-text-decorations-in-effect"
derived_from="display text-decoration">
use super::RGBA;
use super::super::longhands::display;
pub use to_computed_value = super::computed_as_specified;
#[deriving(Clone, Eq)]
pub struct SpecifiedValue {
pub underline: Option<RGBA>,
pub overline: Option<RGBA>,
pub line_through: Option<RGBA>,
}
pub mod computed_value {
pub type T = super::SpecifiedValue;
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
SpecifiedValue {
underline: None,
overline: None,
line_through: None,
}
}
fn maybe(flag: bool, context: &computed::Context) -> Option<RGBA> {
if flag {
Some(context.color)
} else {
None
}
}
fn derive(context: &computed::Context) -> computed_value::T {
// Start with no declarations if this is a block; otherwise, start with the
// declarations in effect and add in the text decorations that this inline specifies.
let mut result = match context.display {
display::computed_value::inline => context.inherited_text_decorations_in_effect,
_ => {
SpecifiedValue {
underline: None,
overline: None,
line_through: None,
}
}
};
if result.underline.is_none() {
result.underline = maybe(context.text_decoration.underline, context)
}
if result.overline.is_none() {
result.overline = maybe(context.text_decoration.overline, context)
}
if result.line_through.is_none() {
result.line_through = maybe(context.text_decoration.line_through, context)
}
result
}
#[inline]
pub fn derive_from_text_decoration(_: text_decoration::computed_value::T,
context: &computed::Context)
-> computed_value::T {
derive(context)
}
#[inline]
pub fn derive_from_display(_: display::computed_value::T, context: &computed::Context)
-> computed_value::T {
derive(context)
}
</%self:longhand>
${single_keyword("white-space", "normal pre")}
// CSS 2.1, Section 17 - Tables
@ -1486,9 +1572,11 @@ fn cascade_with_cached_declarations(applicable_declarations: &[MatchedProperty],
% if property.name in DERIVED_LONGHANDS:
% for derived in DERIVED_LONGHANDS[property.name]:
style_${derived.style_struct.name}.get_mut()
.${derived.ident} =
longhands::${derived.ident}::derive(computed_value,
context);
.${derived.ident} =
longhands::${derived.ident}
::derive_from_${property.ident}(
computed_value,
context);
% endfor
% endif
}
@ -1552,10 +1640,13 @@ pub fn cascade(applicable_declarations: &[MatchedProperty],
inherited_minimum_line_height: inherited_style.InheritedBox
.get()
._servo_minimum_line_height,
inherited_text_decorations_in_effect:
inherited_style.InheritedText.get()._servo_text_decorations_in_effect,
// To be overridden by applicable declarations:
font_size: inherited_font_style.font_size,
display: longhands::display::get_initial_value(),
color: inherited_style.Color.get().color,
text_decoration: longhands::text_decoration::get_initial_value(),
positioned: false,
floated: false,
border_top_present: false,
@ -1603,6 +1694,9 @@ pub fn cascade(applicable_declarations: &[MatchedProperty],
float_declaration(ref value) => {
context.floated = get_specified!(Box, float, value) != longhands::float::none;
}
text_decoration_declaration(ref value) => {
context.text_decoration = get_specified!(Text, text_decoration, value);
}
% for side in ["top", "right", "bottom", "left"]:
border_${side}_style_declaration(ref value) => {
context.border_${side}_present =
@ -1673,9 +1767,11 @@ pub fn cascade(applicable_declarations: &[MatchedProperty],
% if property.name in DERIVED_LONGHANDS:
% for derived in DERIVED_LONGHANDS[property.name]:
style_${derived.style_struct.name}.get_mut()
.${derived.ident} =
longhands::${derived.ident}::derive(computed_value,
&context);
.${derived.ident} =
longhands::${derived.ident}
::derive_from_${property.ident}(
computed_value,
&context);
% endfor
% endif
}

View file

@ -5,6 +5,7 @@
use geom::point::Point2D;
use geom::rect::Rect;
use std::default::Default;
use std::num::{NumCast, One, Zero};
use std::fmt;
@ -14,6 +15,13 @@ use std::fmt;
#[deriving(Clone, Eq, Ord, Zero)]
pub struct Au(pub i32);
impl Default for Au {
#[inline]
fn default() -> Au {
Au(0)
}
}
impl fmt::Show for Au {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Au(n) = *self;

View file

@ -8,8 +8,10 @@
use azure::azure_hl::{BackendType, CairoBackend, CoreGraphicsBackend};
use azure::azure_hl::{CoreGraphicsAcceleratedBackend, Direct2DBackend, SkiaBackend};
use getopts;
use std::cmp;
use std::io;
use std::os;
use std::rt;
/// Global flags for Servo, currently set on the command line.
#[deriving(Clone)]
@ -142,9 +144,7 @@ pub fn from_cmdline_args(args: &[~str]) -> Option<Opts> {
let layout_threads: uint = match opt_match.opt_str("y") {
Some(layout_threads_str) => from_str(layout_threads_str).unwrap(),
// FIXME: Re-enable the below computation, once a fix for #1926 lands.
// cmp::max(rt::default_sched_threads() * 3 / 4, 1),
None => 1,
None => cmp::max(rt::default_sched_threads() * 3 / 4, 1),
};
Some(Opts {

View file

@ -110,6 +110,26 @@ pub trait SmallVec<T> : SmallVecPrivate<T> {
}
}
fn pop(&mut self) -> Option<T> {
if self.len() == 0 {
return None
}
unsafe {
let mut value: T = mem::uninit();
let last_index = self.len() - 1;
if (last_index as int) < 0 {
fail!("overflow")
}
let end_ptr = self.begin().offset(last_index as int);
mem::swap(&mut value, cast::transmute::<*T,&mut T>(end_ptr));
self.set_len(last_index);
Some(value)
}
}
fn grow(&mut self, new_cap: uint) {
unsafe {
let new_alloc: *mut T = cast::transmute(global_heap::malloc_raw(mem::size_of::<T>() *
@ -165,6 +185,11 @@ pub trait SmallVec<T> : SmallVecPrivate<T> {
self.slice(0, self.len())
}
fn as_mut_slice<'a>(&'a mut self) -> &'a mut [T] {
let len = self.len();
self.mut_slice(0, len)
}
fn mut_slice<'a>(&'a mut self, start: uint, end: uint) -> &'a mut [T] {
assert!(start <= end);
assert!(end <= self.len());