mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
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:
parent
85393c6931
commit
27276c0305
24 changed files with 1505 additions and 1641 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
||||
|
|
|
@ -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>) {
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue