diff --git a/src/components/gfx/display_list.rs b/src/components/gfx/display_list.rs index 9c96401c90b..585ad7650aa 100644 --- a/src/components/gfx/display_list.rs +++ b/src/components/gfx/display_list.rs @@ -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, + /// The color to use for overlining, if any. + pub overline: Option, + /// The color to use for line-through, if any. + pub line_through: Option, +} + /// 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); } } diff --git a/src/components/main/layout/block.rs b/src/components/main/layout/block.rs index b1579bee2a0..32f0aee6197 100644 --- a/src/components/main/layout/block.rs +++ b/src/components/main/layout/block.rs @@ -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) -> Size2D { 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, 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 { - 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 { - // 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 { + 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()) diff --git a/src/components/main/layout/box_.rs b/src/components/main/layout/box_.rs index 014fb63a97f..6db04e95bee 100644 --- a/src/components/main/layout/box_.rs +++ b/src/components/main/layout/box_.rs @@ -9,14 +9,14 @@ use layout::construct::FlowConstructor; use layout::context::LayoutContext; use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo, ToGfxColor}; use layout::floats::{ClearBoth, ClearLeft, ClearRight, ClearType}; -use layout::flow::{Flow, FlowFlagsInfo}; -use layout::flow; +use layout::flow::Flow; +use layout::inline::InlineFragmentContext; use layout::model::{Auto, IntrinsicWidths, MaybeAuto, Specified, specified}; use layout::model; use layout::util::OpaqueNodeMethods; use layout::wrapper::{TLayoutNode, ThreadSafeLayoutNode}; -use sync::{Arc, Mutex}; +use sync::Arc; use geom::{Point2D, Rect, Size2D, SideOffsets2D}; use geom::approxeq::ApproxEq; use gfx::color::rgb; @@ -24,12 +24,12 @@ use gfx::display_list::{BackgroundAndBorderLevel, BaseDisplayItem, BorderDisplay use gfx::display_list::{BorderDisplayItemClass, ClipDisplayItem, ClipDisplayItemClass}; use gfx::display_list::{DisplayList, ImageDisplayItem, ImageDisplayItemClass, LineDisplayItem}; use gfx::display_list::{LineDisplayItemClass, OpaqueNode, SolidColorDisplayItem}; -use gfx::display_list::{SolidColorDisplayItemClass, StackingContext, TextDisplayItem}; -use gfx::display_list::{TextDisplayItemClass, TextDisplayItemFlags}; +use gfx::display_list::{SolidColorDisplayItemClass, StackingContext, TextDecorations}; +use gfx::display_list::{TextDisplayItem, TextDisplayItemClass}; use gfx::font::FontStyle; use gfx::text::text_run::TextRun; use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg, PipelineId, SubpageId}; -use servo_net::image::holder::ImageHolder; +use servo_net::image::holder::{ImageHolder, LocalImageCacheHandle}; use servo_util::geometry::Au; use servo_util::geometry; use servo_util::range::*; @@ -37,14 +37,14 @@ use servo_util::namespace; use servo_util::smallvec::{SmallVec, SmallVec0}; use servo_util::str::is_whitespace; use std::cast; -use std::cell::RefCell; use std::from_str::FromStr; +use std::iter::AdditiveIterator; use std::num::Zero; use style::{ComputedValues, TElement, TNode, cascade, initial_values}; -use style::computed_values::{LengthOrPercentage, LengthOrPercentageOrAuto, overflow, LPA_Auto}; -use style::computed_values::{background_attachment, background_repeat, border_style, clear}; -use style::computed_values::{font_family, line_height, position, text_align, text_decoration}; -use style::computed_values::{vertical_align, visibility, white_space}; +use style::computed_values::{LengthOrPercentageOrAuto, overflow, LPA_Auto, background_attachment}; +use style::computed_values::{background_repeat, border_style, clear, font_family, line_height}; +use style::computed_values::{position, text_align, text_decoration, vertical_align, visibility}; +use style::computed_values::{white_space}; use url::Url; /// Boxes (`struct Box`) are the leaves of the layout tree. They cannot position themselves. In @@ -66,7 +66,7 @@ use url::Url; /// content such as images are resized differently from tables, text, or other content. Different /// types of boxes may also contain custom data; for example, text boxes contain text. /// -/// FIXME(pcwalton): This can be slimmed down quite a bit. +/// FIXME(#2260, pcwalton): This can be slimmed down some. #[deriving(Clone)] pub struct Box { /// An opaque reference to the DOM node that this `Box` originates from. @@ -77,29 +77,21 @@ pub struct Box { /// The position of this box relative to its owning flow. /// The size includes padding and border, but not margin. - pub border_box: RefCell>, + pub border_box: Rect, - /// The border of the content box. - /// - /// FIXME(pcwalton): This need not be stored in the box. - pub border: RefCell>, - - /// The padding of the content box. - pub padding: RefCell>, + /// The sum of border and padding; i.e. the distance from the edge of the border box to the + /// content edge of the box. + pub border_padding: SideOffsets2D, /// The margin of the content box. - pub margin: RefCell>, + pub margin: SideOffsets2D, /// Info specific to the kind of box. Keep this enum small. pub specific: SpecificBoxInfo, - /// positioned box offsets - pub position_offsets: RefCell>, - - /// Inline data - pub inline_info: RefCell>, - /// New-line chracter(\n)'s positions(relative, not absolute) + /// + /// FIXME(#2260, pcwalton): This is very inefficient; remove. pub new_line_pos: ~[uint], } @@ -122,9 +114,9 @@ pub enum SpecificBoxInfo { #[deriving(Clone)] pub struct ImageBoxInfo { /// The image held within this box. - pub image: RefCell, - pub computed_width: RefCell>, - pub computed_height: RefCell>, + pub image: ImageHolder, + pub computed_width: Option, + pub computed_height: Option, pub dom_width: Option, pub dom_height: Option, } @@ -136,7 +128,7 @@ impl ImageBoxInfo { /// me. pub fn new(node: &ThreadSafeLayoutNode, image_url: Url, - local_image_cache: Arc>) + local_image_cache: LocalImageCacheHandle) -> ImageBoxInfo { fn convert_length(node: &ThreadSafeLayoutNode, name: &str) -> Option { let element = node.as_element(); @@ -147,9 +139,9 @@ impl ImageBoxInfo { } ImageBoxInfo { - image: RefCell::new(ImageHolder::new(image_url, local_image_cache)), - computed_width: RefCell::new(None), - computed_height: RefCell::new(None), + image: ImageHolder::new(image_url, local_image_cache), + computed_width: None, + computed_height: None, dom_width: convert_length(node,"width"), dom_height: convert_length(node,"height"), } @@ -157,18 +149,12 @@ impl ImageBoxInfo { /// Returns the calculated width of the image, accounting for the width attribute. pub fn computed_width(&self) -> Au { - match &*self.computed_width.borrow() { - &Some(width) => { - width - }, - &None => { - fail!("image width is not computed yet!"); - } - } + self.computed_width.expect("image width is not computed yet!") } - /// Returns width of image(just original width) - pub fn image_width(&self) -> Au { - let mut image_ref = self.image.borrow_mut(); + + /// Returns the original width of the image. + pub fn image_width(&mut self) -> Au { + let image_ref = &mut self.image; Au::from_px(image_ref.get_size().unwrap_or(Size2D(0,0)).width) } @@ -193,19 +179,15 @@ impl ImageBoxInfo { } /// Returns the calculated height of the image, accounting for the height attribute. pub fn computed_height(&self) -> Au { - match &*self.computed_height.borrow() { - &Some(height) => { - height - }, - &None => { - fail!("image height is not computed yet!"); - } + match self.computed_height { + Some(height) => height, + None => fail!("image height is not computed yet!"), } } - /// Returns height of image(just original height) - pub fn image_height(&self) -> Au { - let mut image_ref = self.image.borrow_mut(); + /// Returns the original height of the image. + pub fn image_height(&mut self) -> Au { + let image_ref = &mut self.image; Au::from_px(image_ref.get_size().unwrap_or(Size2D(0,0)).height) } } @@ -290,36 +272,6 @@ pub enum SplitBoxResult { } -/// Data for inline boxes. -/// -/// FIXME(#2013, pcwalton): Copying `InlineParentInfo` vectors all the time is really inefficient. -/// Use atomic reference counting instead. -#[deriving(Clone)] -pub struct InlineInfo { - pub parent_info: SmallVec0, - pub baseline: Au, -} - -impl InlineInfo { - pub fn new() -> InlineInfo { - InlineInfo { - parent_info: SmallVec0::new(), - baseline: Au(0), - } - } -} - -#[deriving(Clone)] -pub struct InlineParentInfo { - pub padding: SideOffsets2D, - pub border: SideOffsets2D, - pub margin: SideOffsets2D, - pub style: Arc, - pub font_ascent: Au, - pub font_descent: Au, - pub node: OpaqueNode, -} - /// A box that represents a table column. #[deriving(Clone)] pub struct TableColumnBoxInfo { @@ -343,81 +295,6 @@ impl TableColumnBoxInfo { } } -// FIXME: Take just one parameter and use concat_ident! (mozilla/rust#12249) -macro_rules! def_noncontent( ($side:ident, $get:ident, $inline_get:ident) => ( - impl Box { - pub fn $get(&self) -> Au { - self.border.borrow().$side + self.padding.borrow().$side - } - - pub fn $inline_get(&self) -> Au { - let mut val = Au::new(0); - let info = self.inline_info.borrow(); - match &*info { - &Some(ref info) => { - for info in info.parent_info.iter() { - val = val + info.border.$side + info.padding.$side; - } - }, - &None => {} - } - val - } - } -)) - -macro_rules! def_noncontent_horiz( ($side:ident, $merge:ident, $clear:ident) => ( - impl Box { - pub fn $merge(&self, other_box: &Box) { - let mut info = self.inline_info.borrow_mut(); - let other_info = other_box.inline_info.borrow(); - - match &*other_info { - &Some(ref other_info) => { - match &mut *info { - &Some(ref mut info) => { - for other_item in other_info.parent_info.iter() { - for item in info.parent_info.mut_iter() { - if item.node == other_item.node { - item.border.$side = other_item.border.$side; - item.padding.$side = other_item.padding.$side; - item.margin.$side = other_item.margin.$side; - break; - } - } - } - }, - &None => {} - } - }, - &None => {} - } - } - - pub fn $clear(&self) { - let mut info = self.inline_info.borrow_mut(); - match &mut *info { - &Some(ref mut info) => { - for item in info.parent_info.mut_iter() { - item.border.$side = Au::new(0); - item.padding.$side = Au::new(0); - item.margin.$side = Au::new(0); - } - }, - &None => {} - } - } - } -)) - -def_noncontent!(left, noncontent_left, noncontent_inline_left) -def_noncontent!(right, noncontent_right, noncontent_inline_right) -def_noncontent!(top, noncontent_top, noncontent_inline_top) -def_noncontent!(bottom, noncontent_bottom, noncontent_inline_bottom) - -def_noncontent_horiz!(left, merge_noncontent_inline_left, clear_noncontent_inline_left) -def_noncontent_horiz!(right, merge_noncontent_inline_right, clear_noncontent_inline_right) - impl Box { /// Constructs a new `Box` instance for the given node. /// @@ -430,13 +307,10 @@ impl Box { Box { node: OpaqueNodeMethods::from_thread_safe_layout_node(node), style: node.style().clone(), - border_box: RefCell::new(Rect::zero()), - border: RefCell::new(Zero::zero()), - padding: RefCell::new(Zero::zero()), - margin: RefCell::new(Zero::zero()), + border_box: Rect::zero(), + border_padding: Zero::zero(), + margin: Zero::zero(), specific: constructor.build_specific_box_info_for_node(node), - position_offsets: RefCell::new(Zero::zero()), - inline_info: RefCell::new(None), new_line_pos: ~[], } } @@ -446,13 +320,10 @@ impl Box { Box { node: OpaqueNodeMethods::from_thread_safe_layout_node(node), style: node.style().clone(), - border_box: RefCell::new(Rect::zero()), - border: RefCell::new(Zero::zero()), - padding: RefCell::new(Zero::zero()), - margin: RefCell::new(Zero::zero()), + border_box: Rect::zero(), + border_padding: Zero::zero(), + margin: Zero::zero(), specific: specific, - position_offsets: RefCell::new(Zero::zero()), - inline_info: RefCell::new(None), new_line_pos: ~[], } } @@ -473,13 +344,10 @@ impl Box { Box { node: OpaqueNodeMethods::from_thread_safe_layout_node(node), style: Arc::new(node_style), - border_box: RefCell::new(Rect::zero()), - border: RefCell::new(Zero::zero()), - padding: RefCell::new(Zero::zero()), - margin: RefCell::new(Zero::zero()), + border_box: Rect::zero(), + border_padding: Zero::zero(), + margin: Zero::zero(), specific: specific, - position_offsets: RefCell::new(Zero::zero()), - inline_info: RefCell::new(None), new_line_pos: ~[], } } @@ -492,13 +360,10 @@ impl Box { Box { node: node, style: style, - border_box: RefCell::new(Rect::zero()), - border: RefCell::new(Zero::zero()), - padding: RefCell::new(Zero::zero()), - margin: RefCell::new(Zero::zero()), + border_box: Rect::zero(), + border_padding: Zero::zero(), + margin: Zero::zero(), specific: specific, - position_offsets: RefCell::new(Zero::zero()), - inline_info: RefCell::new(None), new_line_pos: ~[], } } @@ -517,13 +382,10 @@ impl Box { Box { node: self.node, style: self.style.clone(), - border_box: RefCell::new(Rect(self.border_box.borrow().origin, size)), - border: RefCell::new(*self.border.borrow()), - padding: RefCell::new(*self.padding.borrow()), - margin: RefCell::new(*self.margin.borrow()), + border_box: Rect(self.border_box.origin, size), + border_padding: self.border_padding, + margin: self.margin, specific: specific, - position_offsets: RefCell::new(Zero::zero()), - inline_info: self.inline_info.clone(), new_line_pos: self.new_line_pos.clone(), } } @@ -553,14 +415,16 @@ impl Box { }; let (padding_left, padding_right) = if use_padding { - (self.compute_padding_length(style.Padding.get().padding_left, Au(0)), - self.compute_padding_length(style.Padding.get().padding_right, Au(0))) + (model::specified(style.Padding.get().padding_left, Au(0)), + model::specified(style.Padding.get().padding_right, Au(0))) } else { (Au(0), Au(0)) }; + // FIXME(#2261, pcwalton): This won't work well for inlines: is this OK? + let border = self.border_width(None); let surround_width = margin_left + margin_right + padding_left + padding_right + - self.border.borrow().left + self.border.borrow().right; + border.horizontal(); IntrinsicWidths { minimum_width: width, @@ -579,123 +443,73 @@ impl Box { Au::max(from_inline, minimum) } - /// Populates the box model border parameters from the given computed style. - /// - /// FIXME(pcwalton): This should not be necessary. Just go to the style. - pub fn compute_borders(&self, style: &ComputedValues) { - let border = match self.specific { - TableWrapperBox => { - SideOffsets2D::new(Au(0), Au(0), Au(0), Au(0)) - }, - _ => { - #[inline] - fn width(width: Au, style: border_style::T) -> Au { - if style == border_style::none { - Au(0) - } else { - width - } - } - - SideOffsets2D::new(width(style.Border.get().border_top_width, - style.Border.get().border_top_style), - width(style.Border.get().border_right_width, - style.Border.get().border_right_style), - width(style.Border.get().border_bottom_width, - style.Border.get().border_bottom_style), - width(style.Border.get().border_left_width, - style.Border.get().border_left_style)) - } - }; - *self.border.borrow_mut() = border; - } - - pub fn compute_positioned_offsets(&self, style: &ComputedValues, containing_width: Au, containing_height: Au) { - *self.position_offsets.borrow_mut() = SideOffsets2D::new( - MaybeAuto::from_style(style.PositionOffsets.get().top, containing_height) - .specified_or_zero(), - MaybeAuto::from_style(style.PositionOffsets.get().right, containing_width) - .specified_or_zero(), - MaybeAuto::from_style(style.PositionOffsets.get().bottom, containing_height) - .specified_or_zero(), - MaybeAuto::from_style(style.PositionOffsets.get().left, containing_width) - .specified_or_zero()); - } - - /// Compute and set margin-top and margin-bottom values. - /// - /// If a value is specified or is a percentage, we calculate the right value here. - /// If it is auto, it is up to assign-height to ignore this value and - /// calculate the correct margin values. - pub fn compute_margin_top_bottom(&self, containing_block_width: Au) { - match self.specific { - TableBox | TableCellBox | TableRowBox | TableColumnBox(_) => { - *self.margin.borrow_mut() = SideOffsets2D::new(Au(0), Au(0), Au(0), Au(0)); - }, - _ => { - let style = self.style(); - // Note: CSS 2.1 defines margin % values wrt CB *width* (not height). - let margin_top = MaybeAuto::from_style(style.Margin.get().margin_top, - containing_block_width).specified_or_zero(); - let margin_bottom = MaybeAuto::from_style(style.Margin.get().margin_bottom, - containing_block_width).specified_or_zero(); - let mut margin = *self.margin.borrow(); - margin.top = margin_top; - margin.bottom = margin_bottom; - *self.margin.borrow_mut() = margin; + /// Returns the sum of the widths of all the borders of this fragment. This is private because + /// it should only be called during intrinsic width computation or computation of + /// `border_padding`. Other consumers of this information should simply consult that field. + #[inline] + fn border_width(&self, inline_fragment_context: Option) + -> SideOffsets2D { + match inline_fragment_context { + None => model::border_from_style(self.style()), + Some(inline_fragment_context) => { + inline_fragment_context.ranges().map(|range| range.border()).sum() } } } - /// Populates the box model padding parameters from the given computed style. - pub fn compute_padding(&self, style: &ComputedValues, containing_block_width: Au) { - let padding = match self.specific { - TableColumnBox(_) | TableRowBox | TableWrapperBox => { - SideOffsets2D::new(Au(0), Au(0), Au(0), Au(0)) - }, - GenericBox | IframeBox(_) | ImageBox(_) | TableBox | TableCellBox | - ScannedTextBox(_) | UnscannedTextBox(_) => { - SideOffsets2D::new(self.compute_padding_length(style.Padding - .get() - .padding_top, - containing_block_width), - self.compute_padding_length(style.Padding - .get() - .padding_right, - containing_block_width), - self.compute_padding_length(style.Padding - .get() - .padding_bottom, - containing_block_width), - self.compute_padding_length(style.Padding - .get() - .padding_left, - containing_block_width)) + /// Computes the border, padding, and vertical margins from the containing block width and the + /// style. After this call, the `border_padding` and the vertical direction of the `margin` + /// field will be correct. + pub fn compute_border_padding_margins(&mut self, + containing_block_width: Au, + inline_fragment_context: Option) { + // Compute vertical margins. Note that this value will be ignored by layout if the style + // specifies `auto`. + match self.specific { + TableBox | TableCellBox | TableRowBox | TableColumnBox(_) => { + self.margin.top = Au(0); + self.margin.bottom = Au(0) + } + _ => { + // NB: Percentages are relative to containing block width (not height) per CSS 2.1. + self.margin.top = + MaybeAuto::from_style(self.style().Margin.get().margin_top, + containing_block_width).specified_or_zero(); + self.margin.bottom = + MaybeAuto::from_style(self.style().Margin.get().margin_bottom, + containing_block_width).specified_or_zero() + } + } + + // Compute border. + let border = match inline_fragment_context { + None => model::border_from_style(self.style()), + Some(inline_fragment_context) => { + inline_fragment_context.ranges().map(|range| range.border()).sum() } }; - *self.padding.borrow_mut() = padding; - } - fn compute_padding_length(&self, padding: LengthOrPercentage, content_box_width: Au) -> Au { - specified(padding, content_box_width) - } + // Compute padding. + let padding = match self.specific { + TableColumnBox(_) | TableRowBox | TableWrapperBox => Zero::zero(), + _ => { + match inline_fragment_context { + None => model::padding_from_style(self.style(), containing_block_width), + Some(inline_fragment_context) => { + inline_fragment_context.ranges().map(|range| range.padding()).sum() + } + } + } + }; - pub fn padding_box_size(&self) -> Size2D { - let border_box_size = self.border_box.borrow().size; - Size2D(border_box_size.width - self.border.borrow().left - self.border.borrow().right, - border_box_size.height - self.border.borrow().top - self.border.borrow().bottom) - } - - pub fn noncontent_width(&self) -> Au { - self.noncontent_left() + self.noncontent_right() - } - - pub fn noncontent_height(&self) -> Au { - self.noncontent_top() + self.noncontent_bottom() + self.border_padding = border + padding } // Return offset from original position because of `position: relative`. - pub fn relative_position(&self, container_block_size: &Size2D) -> Point2D { + pub fn relative_position(&self, + container_block_size: &Size2D, + inline_fragment_context: Option) + -> Point2D { fn left_right(style: &ComputedValues, block_width: Au) -> Au { // TODO(ksh8281) : consider RTL(right-to-left) culture match (style.PositionOffsets.get().left, style.PositionOffsets.get().right) { @@ -723,31 +537,27 @@ impl Box { } } - let mut rel_pos: Point2D = Point2D { - x: Au::new(0), - y: Au::new(0), - }; - - if self.style().Box.get().position == position::relative { - rel_pos.x = rel_pos.x + left_right(self.style(), container_block_size.width); - rel_pos.y = rel_pos.y + top_bottom(self.style(), container_block_size.height); - } - // Go over the ancestor boxes and add all relative offsets (if any). - let info = self.inline_info.borrow(); - match &*info { - &Some(ref info) => { - for info in info.parent_info.iter() { - if info.style.Box.get().position == position::relative { - rel_pos.x = rel_pos.x + left_right(&*info.style, + let mut rel_pos: Point2D = Zero::zero(); + match inline_fragment_context { + None => { + if self.style().Box.get().position == position::relative { + rel_pos.x = rel_pos.x + left_right(self.style(), container_block_size.width); + rel_pos.y = rel_pos.y + top_bottom(self.style(), container_block_size.height); + } + } + Some(inline_fragment_context) => { + for range in inline_fragment_context.ranges() { + if range.style.Box.get().position == position::relative { + rel_pos.x = rel_pos.x + left_right(&*range.style, container_block_size.width); - rel_pos.y = rel_pos.y + top_bottom(&*info.style, + rel_pos.y = rel_pos.y + top_bottom(&*range.style, container_block_size.height); } } }, - &None => {} } + rel_pos } @@ -824,35 +634,15 @@ impl Box { } /// Returns the left offset from margin edge to content edge. + /// + /// FIXME(#2262, pcwalton): I think this method is pretty bogus, because it won't work for + /// inlines. pub fn left_offset(&self) -> Au { match self.specific { - TableWrapperBox => self.margin.borrow().left, - TableBox | TableCellBox => self.border.borrow().left + self.padding.borrow().left, - TableRowBox => self.border.borrow().left, + TableWrapperBox => self.margin.left, + TableBox | TableCellBox | TableRowBox => self.border_padding.left, TableColumnBox(_) => Au(0), - _ => self.margin.borrow().left + self.border.borrow().left + self.padding.borrow().left - } - } - - /// Returns the top offset from margin edge to content edge. - pub fn top_offset(&self) -> Au { - match self.specific { - TableWrapperBox => self.margin.borrow().top, - TableBox | TableCellBox => self.border.borrow().top + self.padding.borrow().top, - TableRowBox => self.border.borrow().top, - TableColumnBox(_) => Au(0), - _ => self.margin.borrow().top + self.border.borrow().top + self.padding.borrow().top - } - } - - /// Returns the bottom offset from margin edge to content edge. - pub fn bottom_offset(&self) -> Au { - match self.specific { - TableWrapperBox => self.margin.borrow().bottom, - TableBox | TableCellBox => self.border.borrow().bottom + self.padding.borrow().bottom, - TableRowBox => self.border.borrow().bottom, - TableColumnBox(_) => Au(0), - _ => self.margin.borrow().bottom + self.border.borrow().bottom + self.padding.borrow().bottom + _ => self.margin.left + self.border_padding.left, } } @@ -864,99 +654,12 @@ impl Box { } } - /// Returns the amount of left and right "fringe" used by this box. This is based on margins, - /// borders, padding, and width. - pub fn get_used_width(&self) -> (Au, Au) { - // TODO: This should actually do some computation! See CSS 2.1, Sections 10.3 and 10.4. - (Au::new(0), Au::new(0)) - } - - /// Returns the amount of left and right "fringe" used by this box. This should be based on - /// margins, borders, padding, and width. - pub fn get_used_height(&self) -> (Au, Au) { - // TODO: This should actually do some computation! See CSS 2.1, Sections 10.5 and 10.6. - (Au::new(0), Au::new(0)) - } - - pub fn paint_inline_background_border_if_applicable(&self, - list: &mut DisplayList, - absolute_bounds: &Rect, - offset: &Point2D) { - // FIXME: This causes a lot of background colors to be displayed when they are clearly not - // needed. We could use display list optimization to clean this up, but it still seems - // inefficient. What we really want is something like "nearest ancestor element that - // doesn't have a box". - let info = self.inline_info.borrow(); - match &*info { - &Some(ref box_info) => { - let mut bg_rect = absolute_bounds.clone(); - for info in box_info.parent_info.as_slice().rev_iter() { - // TODO (ksh8281) compute vertical-align, line-height - bg_rect.origin.y = box_info.baseline + offset.y - info.font_ascent; - bg_rect.size.height = info.font_ascent + info.font_descent; - let background_color = info.style.resolve_color( - info.style.Background.get().background_color); - - if !background_color.alpha.approx_eq(&0.0) { - let solid_color_display_item = ~SolidColorDisplayItem { - base: BaseDisplayItem { - bounds: bg_rect.clone(), - node: self.node, - }, - color: background_color.to_gfx_color(), - }; - - list.push(SolidColorDisplayItemClass(solid_color_display_item)) - } - - let border = &info.border; - - // Fast path. - if border.is_zero() { - continue - } - - bg_rect.origin.y = bg_rect.origin.y - border.top; - bg_rect.size.height = bg_rect.size.height + border.top + border.bottom; - - let style = &info.style; - let top_color = style.resolve_color(style.Border.get().border_top_color); - let right_color = style.resolve_color(style.Border.get().border_right_color); - let bottom_color = style.resolve_color(style.Border.get().border_bottom_color); - let left_color = style.resolve_color(style.Border.get().border_left_color); - let top_style = style.Border.get().border_top_style; - let right_style = style.Border.get().border_right_style; - let bottom_style = style.Border.get().border_bottom_style; - let left_style = style.Border.get().border_left_style; - - let border_display_item = ~BorderDisplayItem { - base: BaseDisplayItem { - bounds: bg_rect, - node: self.node, - }, - border: border.clone(), - color: SideOffsets2D::new(top_color.to_gfx_color(), - right_color.to_gfx_color(), - bottom_color.to_gfx_color(), - left_color.to_gfx_color()), - style: SideOffsets2D::new(top_style, right_style, bottom_style, left_style) - }; - - list.push(BorderDisplayItemClass(border_display_item)); - - bg_rect.origin.x = bg_rect.origin.x + border.left; - bg_rect.size.width = bg_rect.size.width - border.left - border.right; - } - }, - &None => {} - } - } /// Adds the display items necessary to paint the background of this box to the display list if /// necessary. - pub fn paint_background_if_applicable(&self, - list: &mut DisplayList, - builder: &DisplayListBuilder, - absolute_bounds: &Rect) { + pub fn build_display_list_for_background_if_applicable(&self, + list: &mut DisplayList, + builder: &DisplayListBuilder, + absolute_bounds: &Rect) { // FIXME: This causes a lot of background colors to be displayed when they are clearly not // needed. We could use display list optimization to clean this up, but it still seems // inefficient. What we really want is something like "nearest ancestor element that @@ -1070,10 +773,14 @@ impl Box { /// Adds the display items necessary to paint the borders of this box to a display list if /// necessary. - pub fn paint_borders_if_applicable(&self, list: &mut DisplayList, abs_bounds: &Rect) { + pub fn build_display_list_for_borders_if_applicable(&self, + list: &mut DisplayList, + abs_bounds: &Rect, + inline_fragment_context: + Option) { // Fast path. - let border = self.border.borrow(); - if border.is_zero() { + let border = self.border_width(inline_fragment_context); + if border == Zero::zero() { return } @@ -1082,31 +789,22 @@ impl Box { let right_color = style.resolve_color(style.Border.get().border_right_color); let bottom_color = style.resolve_color(style.Border.get().border_bottom_color); let left_color = style.resolve_color(style.Border.get().border_left_color); - let top_style = style.Border.get().border_top_style; - let right_style = style.Border.get().border_right_style; - let bottom_style = style.Border.get().border_bottom_style; - let left_style = style.Border.get().border_left_style; - - let mut abs_bounds = abs_bounds.clone(); - abs_bounds.origin.x = abs_bounds.origin.x + self.noncontent_inline_left(); - abs_bounds.size.width = abs_bounds.size.width - self.noncontent_inline_left() - - self.noncontent_inline_right(); // Append the border to the display list. let border_display_item = ~BorderDisplayItem { base: BaseDisplayItem { - bounds: abs_bounds, + bounds: *abs_bounds, node: self.node, }, - border: *border, + border: border, color: SideOffsets2D::new(top_color.to_gfx_color(), right_color.to_gfx_color(), bottom_color.to_gfx_color(), left_color.to_gfx_color()), - style: SideOffsets2D::new(top_style, - right_style, - bottom_style, - left_style) + style: SideOffsets2D::new(style.Border.get().border_top_style, + style.Border.get().border_right_style, + style.Border.get().border_bottom_style, + style.Border.get().border_left_style) }; list.push(BorderDisplayItemClass(border_display_item)) @@ -1116,7 +814,7 @@ impl Box { stacking_context: &mut StackingContext, flow_origin: Point2D, text_box: &ScannedTextBoxInfo) { - let box_bounds = self.border_box.borrow(); + let box_bounds = self.border_box; let absolute_box_bounds = box_bounds.translate(&flow_origin); // Compute the text box bounds and draw a border surrounding them. @@ -1154,7 +852,7 @@ impl Box { fn build_debug_borders_around_box(&self, stacking_context: &mut StackingContext, flow_origin: Point2D) { - let box_bounds = self.border_box.borrow(); + let box_bounds = self.border_box; let absolute_box_bounds = box_bounds.translate(&flow_origin); // This prints a debug border around the border of this box. @@ -1182,16 +880,15 @@ impl Box { /// * `dirty`: The dirty rectangle in the coordinate system of the owning flow. /// * `flow_origin`: Position of the origin of the owning flow wrt the display list root flow. /// box. - /// * `flow`: The flow that this box belongs to. - pub fn build_display_list(&self, + pub fn build_display_list(&mut self, stacking_context: &mut StackingContext, builder: &DisplayListBuilder, _: &DisplayListBuildingInfo, flow_origin: Point2D, - flow: &Flow, - background_and_border_level: BackgroundAndBorderLevel) { + background_and_border_level: BackgroundAndBorderLevel, + inline_fragment_context: Option) { // Box position wrt to the owning flow. - let box_bounds = *self.border_box.borrow(); + let box_bounds = self.border_box; let absolute_box_bounds = box_bounds.translate(&flow_origin); debug!("Box::build_display_list at rel={}, abs={}: {:s}", box_bounds, @@ -1214,52 +911,41 @@ impl Box { let list = stacking_context.list_for_background_and_border_level(background_and_border_level); - // Add a background to the list, if this is an inline. - // - // FIXME(pcwalton): This is kind of ugly; merge with the call below? - self.paint_inline_background_border_if_applicable(list, - &absolute_box_bounds, - &flow_origin); - // Add the background to the list, if applicable. - self.paint_background_if_applicable(list, builder, &absolute_box_bounds); + self.build_display_list_for_background_if_applicable(list, + builder, + &absolute_box_bounds); // Add a border, if applicable. // // TODO: Outlines. - self.paint_borders_if_applicable(list, &absolute_box_bounds); + self.build_display_list_for_borders_if_applicable(list, + &absolute_box_bounds, + inline_fragment_context); } match self.specific { UnscannedTextBox(_) => fail!("Shouldn't see unscanned boxes here."), TableColumnBox(_) => fail!("Shouldn't see table column boxes here."), ScannedTextBox(ref text_box) => { + // Compute text color. let text_color = self.style().Color.get().color.to_gfx_color(); - // Set the various text display item flags. - let mut flow_flags = flow::base(flow).flags_info.clone(); - - let inline_info = self.inline_info.borrow(); - match &*inline_info { - &Some(ref info) => { - for data in info.parent_info.as_slice().rev_iter() { - let parent_info = FlowFlagsInfo::new(&*data.style); - flow_flags.propagate_text_decoration_from_parent(&parent_info); - } - }, - &None => {} - } - let mut text_flags = TextDisplayItemFlags::new(); - text_flags.set_override_underline(flow_flags.flags.override_underline()); - text_flags.set_override_overline(flow_flags.flags.override_overline()); - text_flags.set_override_line_through(flow_flags.flags.override_line_through()); + // Compute text decorations. + let text_decorations_in_effect = self.style() + .InheritedText + .get() + ._servo_text_decorations_in_effect; + let text_decorations = TextDecorations { + underline: text_decorations_in_effect.underline.map(|c| c.to_gfx_color()), + overline: text_decorations_in_effect.overline.map(|c| c.to_gfx_color()), + line_through: text_decorations_in_effect.line_through + .map(|c| c.to_gfx_color()), + }; let mut bounds = absolute_box_bounds.clone(); - bounds.origin.x = bounds.origin.x + self.noncontent_left() - + self.noncontent_inline_left(); - bounds.size.width = bounds.size.width - self.noncontent_width() - - self.noncontent_inline_left() - - self.noncontent_inline_right(); + bounds.origin.x = bounds.origin.x + self.border_padding.left; + bounds.size.width = bounds.size.width - self.border_padding.horizontal(); // Create the text box. let text_display_item = ~TextDisplayItem { @@ -1270,18 +956,15 @@ impl Box { text_run: text_box.run.clone(), range: text_box.range, text_color: text_color, - overline_color: flow_flags.overline_color(text_color), - underline_color: flow_flags.underline_color(text_color), - line_through_color: flow_flags.line_through_color(text_color), - flags: text_flags, + text_decorations: text_decorations, }; stacking_context.content.push(TextDisplayItemClass(text_display_item)); // Draw debug frames for text bounds. // - // FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We - // should have a real `SERVO_DEBUG` system. + // FIXME(#2263, pcwalton): This is a bit of an abuse of the logging infrastructure. + // We should have a real `SERVO_DEBUG` system. debug!("{:?}", self.build_debug_borders_around_text_boxes(stacking_context, flow_origin, text_box)) @@ -1302,41 +985,45 @@ impl Box { // should have a real `SERVO_DEBUG` system. debug!("{:?}", self.build_debug_borders_around_box(stacking_context, flow_origin)) }, - ImageBox(ref image_box) => { - let mut image_ref = image_box.image.borrow_mut(); + ImageBox(_) => { let mut bounds = absolute_box_bounds.clone(); - bounds.origin.x = bounds.origin.x + self.noncontent_left() - + self.noncontent_inline_left(); - bounds.origin.y = bounds.origin.y + self.noncontent_top(); - bounds.size.width = bounds.size.width - - self.noncontent_width() - self.noncontent_inline_left() - - self.noncontent_inline_right(); - bounds.size.height = bounds.size.height - self.noncontent_height(); + bounds.origin.x = bounds.origin.x + self.border_padding.left; + bounds.origin.y = bounds.origin.y + self.border_padding.top; + bounds.size.width = bounds.size.width - self.border_padding.horizontal(); + bounds.size.height = bounds.size.height - self.border_padding.vertical(); - match image_ref.get_image() { - Some(image) => { - debug!("(building display list) building image box"); + match self.specific { + ImageBox(ref mut image_box) => { + let image_ref = &mut image_box.image; + match image_ref.get_image() { + Some(image) => { + debug!("(building display list) building image box"); - // Place the image into the display list. - let image_display_item = ~ImageDisplayItem { - base: BaseDisplayItem { - bounds: bounds, - node: self.node, - }, - image: image.clone(), - stretch_size: bounds.size, - }; - stacking_context.content.push(ImageDisplayItemClass(image_display_item)) - } - None => { - // No image data at all? Do nothing. - // - // TODO: Add some kind of placeholder image. - debug!("(building display list) no image :("); + // Place the image into the display list. + let image_display_item = ~ImageDisplayItem { + base: BaseDisplayItem { + bounds: bounds, + node: self.node, + }, + image: image.clone(), + stretch_size: bounds.size, + }; + stacking_context.content + .push(ImageDisplayItemClass(image_display_item)) + } + None => { + // No image data at all? Do nothing. + // + // TODO: Add some kind of placeholder image. + debug!("(building display list) no image :("); + } + } } + _ => fail!("shouldn't get here"), } - // FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We - // should have a real `SERVO_DEBUG` system. + + // FIXME(pcwalton): This is a bit of an abuse of the logging + // infrastructure. We should have a real `SERVO_DEBUG` system. debug!("{:?}", self.build_debug_borders_around_box(stacking_context, flow_origin)) } } @@ -1360,13 +1047,14 @@ impl Box { } /// Returns the intrinsic widths of this fragment. - pub fn intrinsic_widths(&self) -> IntrinsicWidths { + pub fn intrinsic_widths(&mut self, inline_fragment_context: Option) + -> IntrinsicWidths { let mut result = self.style_specified_intrinsic_width(); match self.specific { GenericBox | IframeBox(_) | TableBox | TableCellBox | TableColumnBox(_) | TableRowBox | TableWrapperBox => {} - ImageBox(ref image_box_info) => { + ImageBox(ref mut image_box_info) => { let image_width = image_box_info.image_width(); result.minimum_width = geometry::max(result.minimum_width, image_width); result.preferred_width = geometry::max(result.preferred_width, image_width); @@ -1387,15 +1075,13 @@ impl Box { UnscannedTextBox(..) => fail!("Unscanned text boxes should have been scanned by now!"), } - // Take borders and padding for parent inline boxes into account. - match *self.inline_info.borrow() { + // Take borders and padding for parent inline boxes into account, if necessary. + match inline_fragment_context { None => {} - Some(ref inline_info) => { - for inline_parent_info in inline_info.parent_info.iter() { - let border_width = inline_parent_info.border.left + - inline_parent_info.border.right; - let padding_width = inline_parent_info.padding.left + - inline_parent_info.padding.right; + Some(context) => { + for range in context.ranges() { + let border_width = range.border().horizontal(); + let padding_width = range.padding().horizontal(); result.minimum_width = result.minimum_width + border_width + padding_width; result.preferred_width = result.preferred_width + border_width + padding_width; } @@ -1444,11 +1130,18 @@ impl Box { } } - /// Return the size of the content box. - pub fn content_box_size(&self) -> Size2D { - let border_box_size = self.border_box.borrow().size; - Size2D(border_box_size.width - self.noncontent_width(), - border_box_size.height - self.noncontent_height()) + /// Returns the dimensions of the content box. + /// + /// This is marked `#[inline]` because it is frequently called when only one or two of the + /// values are needed and that will save computation. + #[inline] + pub fn content_box(&self) -> Rect { + Rect { + origin: Point2D(self.border_box.origin.x + self.border_padding.left, + self.border_box.origin.y + self.border_padding.top), + size: Size2D(self.border_box.size.width - self.border_padding.horizontal(), + self.border_box.size.height - self.border_padding.vertical()), + } } /// Split box which includes new-line character @@ -1570,7 +1263,7 @@ impl Box { let left_box = if left_range.length() > 0 { let new_text_box_info = ScannedTextBoxInfo::new(text_box_info.run.clone(), left_range); let mut new_metrics = new_text_box_info.run.metrics_for_range(&left_range); - new_metrics.bounding_box.size.height = self.border_box.borrow().size.height; + new_metrics.bounding_box.size.height = self.border_box.size.height; Some(self.transform(new_metrics.bounding_box.size, ScannedTextBox(new_text_box_info))) } else { @@ -1578,9 +1271,10 @@ impl Box { }; let right_box = right_range.map_or(None, |range: Range| { - let new_text_box_info = ScannedTextBoxInfo::new(text_box_info.run.clone(), range); + let new_text_box_info = ScannedTextBoxInfo::new(text_box_info.run.clone(), + range); let mut new_metrics = new_text_box_info.run.metrics_for_range(&range); - new_metrics.bounding_box.size.height = self.border_box.borrow().size.height; + new_metrics.bounding_box.size.height = self.border_box.size.height; Some(self.transform(new_metrics.bounding_box.size, ScannedTextBox(new_text_box_info))) }); @@ -1588,12 +1282,6 @@ impl Box { if pieces_processed_count == 1 || left_box.is_none() { SplitDidNotFit(left_box, right_box) } else { - if left_box.is_some() { - left_box.get_ref().clear_noncontent_inline_right(); - } - if right_box.is_some() { - right_box.get_ref().clear_noncontent_inline_left(); - } SplitDidFit(left_box, right_box) } } @@ -1610,74 +1298,84 @@ impl Box { /// Assigns replaced width, padding, and margins for this box only if it is replaced content /// per CSS 2.1 § 10.3.2. - pub fn assign_replaced_width_if_necessary(&self, container_width: Au) { + pub fn assign_replaced_width_if_necessary(&mut self, + container_width: Au, + inline_fragment_context: + Option) { match self.specific { GenericBox | IframeBox(_) | TableBox | TableCellBox | TableRowBox | - TableWrapperBox => {} - ImageBox(ref image_box_info) => { - self.compute_padding(self.style(), container_width); + TableWrapperBox => return, + TableColumnBox(_) => fail!("Table column boxes do not have width"), + UnscannedTextBox(_) => fail!("Unscanned text boxes should have been scanned by now!"), + ImageBox(_) | ScannedTextBox(_) => {} + }; + self.compute_border_padding_margins(container_width, inline_fragment_context); + + let style_width = self.style().Box.get().width; + let style_height = self.style().Box.get().height; + let noncontent_width = self.border_padding.horizontal(); + + match self.specific { + ScannedTextBox(_) => { + // Scanned text boxes will have already had their content widths assigned by this + // point. + self.border_box.size.width = self.border_box.size.width + noncontent_width + } + ImageBox(ref mut image_box_info) => { // TODO(ksh8281): compute border,margin - let width = ImageBoxInfo::style_length(self.style().Box.get().width, + let width = ImageBoxInfo::style_length(style_width, image_box_info.dom_width, container_width); - - // FIXME(ksh8281): we shouldn't figure height this way - // now, we don't know about size of parent's height - let height = ImageBoxInfo::style_length(self.style().Box.get().height, - image_box_info.dom_height, - Au::new(0)); + let height = ImageBoxInfo::style_length(style_height, + image_box_info.dom_height, + Au(0)); let width = match (width,height) { - (Auto,Auto) => { - image_box_info.image_width() - }, + (Auto, Auto) => image_box_info.image_width(), (Auto,Specified(h)) => { let scale = image_box_info. image_height().to_f32().unwrap() / h.to_f32().unwrap(); Au::new((image_box_info.image_width().to_f32().unwrap() / scale) as i32) }, - (Specified(w),_) => { - w - } + (Specified(w), _) => w, }; - let mut position = self.border_box.borrow_mut(); - position.size.width = width + self.noncontent_width() + - self.noncontent_inline_left() + self.noncontent_inline_right(); - *image_box_info.computed_width.borrow_mut() = Some(width); + self.border_box.size.width = width + noncontent_width; + image_box_info.computed_width = Some(width); } - ScannedTextBox(_) => { - // Scanned text boxes will have already had their - // content_widths assigned by this point. - let mut position = self.border_box.borrow_mut(); - position.size.width = position.size.width + self.noncontent_width() + - self.noncontent_inline_left() + self.noncontent_inline_right(); - } - TableColumnBox(_) => fail!("Table column boxes do not have width"), - UnscannedTextBox(_) => fail!("Unscanned text boxes should have been scanned by now!"), + _ => fail!("this case should have been handled above"), } } - /// Assign height for this box if it is replaced content. + /// Assign height for this box if it is replaced content. The width must have been assigned + /// first. /// - /// Ideally, this should follow CSS 2.1 § 10.6.2 - pub fn assign_replaced_height_if_necessary(&self) { + /// Ideally, this should follow CSS 2.1 § 10.6.2. + pub fn assign_replaced_height_if_necessary(&mut self) { match self.specific { GenericBox | IframeBox(_) | TableBox | TableCellBox | TableRowBox | - TableWrapperBox => {} - ImageBox(ref image_box_info) => { + TableWrapperBox => return, + TableColumnBox(_) => fail!("Table column boxes do not have height"), + UnscannedTextBox(_) => fail!("Unscanned text boxes should have been scanned by now!"), + ImageBox(_) | ScannedTextBox(_) => {} + } + + let style_width = self.style().Box.get().width; + let style_height = self.style().Box.get().height; + let noncontent_height = self.border_padding.vertical(); + + match self.specific { + ImageBox(ref mut image_box_info) => { // TODO(ksh8281): compute border,margin,padding let width = image_box_info.computed_width(); // FIXME(ksh8281): we shouldn't assign height this way // we don't know about size of parent's height - let height = ImageBoxInfo::style_length(self.style().Box.get().height, + let height = ImageBoxInfo::style_length(style_height, image_box_info.dom_height, - Au::new(0)); + Au(0)); - let height = match (self.style().Box.get().width, - image_box_info.dom_width, - height) { + let height = match (style_width, image_box_info.dom_width, height) { (LPA_Auto, None, Auto) => { image_box_info.image_height() }, @@ -1691,20 +1389,15 @@ impl Box { } }; - let mut position = self.border_box.borrow_mut(); - *image_box_info.computed_height.borrow_mut() = Some(height); - position.size.height = height + self.noncontent_height() + image_box_info.computed_height = Some(height); + self.border_box.size.height = height + noncontent_height } ScannedTextBox(_) => { - // Scanned text boxes will have already had their widths assigned by this point - let mut position = self.border_box.borrow_mut(); - // Scanned text boxes' content heights are calculated by the - // text run scanner during Flow construction. - position.size.height - = position.size.height + self.noncontent_height() + // Scanned text boxes' content heights are calculated by the text run scanner + // during flow construction. + self.border_box.size.height = self.border_box.size.height + noncontent_height } - TableColumnBox(_) => fail!("Table column boxes do not have height"), - UnscannedTextBox(_) => fail!("Unscanned text boxes should have been scanned by now!"), + _ => fail!("should have been handled above"), } } @@ -1747,11 +1440,10 @@ impl Box { UnscannedTextBox(_) => "UnscannedTextBox", }; - format!("({}{}{}{})", + format!("({}{}{})", class_name, - self.side_offsets_debug_string("b", *self.border.borrow()), - self.side_offsets_debug_string("p", *self.padding.borrow()), - self.side_offsets_debug_string("m", *self.margin.borrow())) + self.side_offsets_debug_string("bp", self.border_padding), + self.side_offsets_debug_string("m", self.margin)) } /// A helper function to return a debug string describing the side offsets for one of the rect @@ -1776,12 +1468,10 @@ impl Box { iframe_box: &IframeBoxInfo, offset: Point2D, layout_context: &LayoutContext) { - let left = offset.x + self.margin.borrow().left + self.border.borrow().left + - self.padding.borrow().left; - let top = offset.y + self.margin.borrow().top + self.border.borrow().top + - self.padding.borrow().top; - let width = self.border_box.borrow().size.width - self.noncontent_width(); - let height = self.border_box.borrow().size.height - self.noncontent_height(); + let left = offset.x + self.margin.left + self.border_padding.left; + let top = offset.y + self.margin.top + self.border_padding.top; + let width = self.content_box().size.width; + let height = self.content_box().size.height; let origin = Point2D(geometry::to_frac_px(left) as f32, geometry::to_frac_px(top) as f32); let size = Size2D(geometry::to_frac_px(width) as f32, geometry::to_frac_px(height) as f32); let rect = Rect(origin, size); diff --git a/src/components/main/layout/construct.rs b/src/components/main/layout/construct.rs index 77222b6b770..92059e44d18 100644 --- a/src/components/main/layout/construct.rs +++ b/src/components/main/layout/construct.rs @@ -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 OptVector 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 = 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, } } diff --git a/src/components/main/layout/context.rs b/src/components/main/layout/context.rs index b25d4e932b6..8b7db32538a 100644 --- a/src/components/main/layout/context.rs +++ b/src/components/main/layout/context.rs @@ -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>, + pub image_cache: LocalImageCacheHandle, /// The current screen size. pub screen_size: Size2D, diff --git a/src/components/main/layout/flow.rs b/src/components/main/layout/flow.rs index 1114f676ba7..2b1563dcf09 100644 --- a/src/components/main/layout/flow.rs +++ b/src/components/main/layout/flow.rs @@ -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 { - 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 { - 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 { + 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 { + self.link.resolve().unwrap().generated_containing_block_rect() + } +} + diff --git a/src/components/main/layout/flow_list.rs b/src/components/main/layout/flow_list.rs index f3eebde19dc..33079d7137a 100644 --- a/src/components/main/layout/flow_list.rs +++ b/src/components/main/layout/flow_list.rs @@ -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 { diff --git a/src/components/main/layout/inline.rs b/src/components/main/layout/inline.rs index 313217a96bc..ab4e8e43208 100644 --- a/src/components/main/layout/inline.rs +++ b/src/components/main/layout/inline.rs @@ -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, pub work_list: RingBuf, pub pending_line: LineBox, - pub lines: ~[LineBox], + pub lines: SmallVec0, 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) { 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>, + 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>, + 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, + /// 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) { + 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, } 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, + /// 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, range: Range) -> FragmentRange { + FragmentRange { + style: style, + range: range, + } + } + + /// Returns the dimensions of the border in this fragment range. + pub fn border(&self) -> SideOffsets2D { + model::border_from_style(&*self.style) + } + + /// Returns the dimensions of the padding in this fragment range. + pub fn padding(&self) -> SideOffsets2D { + // FIXME(#2266, pcwalton): Is Au(0) right here for the containing block? + model::padding_from_style(&*self.style, Au(0)) + } +} + +struct FragmentFixupWorkItem { + style: Arc, + 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, +} + +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, 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) + } +} + diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs index ec05eec152d..64b833fc55b 100644 --- a/src/components/main/layout/layout_task.rs +++ b/src/components/main/layout/layout_task.rs @@ -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>, + pub local_image_cache: LocalImageCacheHandle, /// The size of the viewport. pub screen_size: Size2D, @@ -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<*()>>(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()); } diff --git a/src/components/main/layout/model.rs b/src/components/main/layout/model.rs index 3a5f3be8f1c..14b312a0596 100644 --- a/src/components/main/layout/model.rs +++ b/src/components/main/layout/model.rs @@ -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 { + #[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 { + 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)) +} + diff --git a/src/components/main/layout/parallel.rs b/src/components/main/layout/parallel.rs index 51a50e392d7..b3d8ae08594 100644 --- a/src/components/main/layout/parallel.rs +++ b/src/components/main/layout/parallel.rs @@ -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>) { diff --git a/src/components/main/layout/table.rs b/src/components/main/layout/table.rs index cc5b5e3ae70..1a558374e51 100644 --- a/src/components/main/layout/table.rs +++ b/src/components/main/layout/table.rs @@ -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)) } diff --git a/src/components/main/layout/table_cell.rs b/src/components/main/layout/table_cell.rs index 03a70e16004..17dbbf45efb 100644 --- a/src/components/main/layout/table_cell.rs +++ b/src/components/main/layout/table_cell.rs @@ -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. diff --git a/src/components/main/layout/table_row.rs b/src/components/main/layout/table_row.rs index 500bae24585..274df4d3bcd 100644 --- a/src/components/main/layout/table_row.rs +++ b/src/components/main/layout/table_row.rs @@ -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; diff --git a/src/components/main/layout/table_rowgroup.rs b/src/components/main/layout/table_rowgroup.rs index 9c794e8ecaa..ece55cd8bdf 100644 --- a/src/components/main/layout/table_rowgroup.rs +++ b/src/components/main/layout/table_rowgroup.rs @@ -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; } diff --git a/src/components/main/layout/table_wrapper.rs b/src/components/main/layout/table_wrapper.rs index b04906234bc..060cbee6713 100644 --- a/src/components/main/layout/table_wrapper.rs +++ b/src/components/main/layout/table_wrapper.rs @@ -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) } diff --git a/src/components/main/layout/text.rs b/src/components/main/layout/text.rs index 32903bade0c..f60fececdf5 100644 --- a/src/components/main/layout/text.rs +++ b/src/components/main/layout/text.rs @@ -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, + 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); diff --git a/src/components/main/layout/util.rs b/src/components/main/layout/util.rs index caae0a99f06..3a64b4cd6a9 100644 --- a/src/components/main/layout/util.rs +++ b/src/components/main/layout/util.rs @@ -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> { - 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. diff --git a/src/components/net/image/holder.rs b/src/components/net/image/holder.rs index 8bf51fdee65..cf9a65ef61f 100644 --- a/src/components/net/image/holder.rs +++ b/src/components/net/image/holder.rs @@ -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` `Share`. +pub struct LocalImageCacheHandle { + data: *uint, +} + +impl Drop for LocalImageCacheHandle { + fn drop(&mut self) { + unsafe { + let _: ~Arc> = + cast::transmute(mem::replace(&mut self.data, ptr::null())); + } + } +} + +impl Clone for LocalImageCacheHandle { + fn clone(&self) -> LocalImageCacheHandle { + unsafe { + let handle = cast::transmute::<&Arc>,&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> { + unsafe { + cast::transmute::<*uint,&'a Arc>>(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>, cached_size: Size2D, - // FIXME(rust#13125): Remove the *() for the real type. - local_image_cache: Arc>, + local_image_cache: LocalImageCacheHandle, } impl ImageHolder { - pub fn new(url: Url, local_image_cache: Arc>) -> 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() { diff --git a/src/components/style/common_types.rs b/src/components/style/common_types.rs index 1b832f71c03..4050306a939 100644 --- a/src/components/style/common_types.rs +++ b/src/components/style/common_types.rs @@ -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, diff --git a/src/components/style/properties.rs.mako b/src/components/style/properties.rs.mako index da5ee291eb0..682c373c7ab 100644 --- a/src/components/style/properties.rs.mako +++ b/src/components/style/properties.rs.mako @@ -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, + pub overline: Option, + pub line_through: Option, + } + + 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 { + 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) + } + + ${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 } diff --git a/src/components/util/geometry.rs b/src/components/util/geometry.rs index 93a5adc832a..f1dd2d816ce 100644 --- a/src/components/util/geometry.rs +++ b/src/components/util/geometry.rs @@ -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; diff --git a/src/components/util/opts.rs b/src/components/util/opts.rs index 829a1214517..1a8f96aac60 100644 --- a/src/components/util/opts.rs +++ b/src/components/util/opts.rs @@ -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 { 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 { diff --git a/src/components/util/smallvec.rs b/src/components/util/smallvec.rs index 089401e2a18..9a15007f623 100644 --- a/src/components/util/smallvec.rs +++ b/src/components/util/smallvec.rs @@ -110,6 +110,26 @@ pub trait SmallVec : SmallVecPrivate { } } + fn pop(&mut self) -> Option { + 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::() * @@ -165,6 +185,11 @@ pub trait SmallVec : SmallVecPrivate { 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());