diff options
Diffstat (limited to 'src')
20 files changed, 1485 insertions, 1038 deletions
diff --git a/src/components/gfx/display_list.rs b/src/components/gfx/display_list.rs index 585ad7650aa..6d47d76fc08 100644 --- a/src/components/gfx/display_list.rs +++ b/src/components/gfx/display_list.rs @@ -18,12 +18,15 @@ use color::Color; use render_context::RenderContext; use text::TextRun; +use collections::deque::Deque; +use collections::dlist::DList; +use collections::dlist; use geom::{Point2D, Rect, SideOffsets2D, Size2D}; use libc::uintptr_t; use servo_net::image::base::Image; use servo_util::geometry::Au; use servo_util::range::Range; -use servo_util::smallvec::{SmallVec, SmallVec0, SmallVecIterator}; +use servo_util::smallvec::{SmallVec, SmallVec0}; use std::mem; use std::slice::Items; use style::computed_values::border_style; @@ -46,11 +49,35 @@ impl OpaqueNode { } } -/// A stacking context. See CSS 2.1 § E.2. "Steps" below refer to steps in that section of the -/// specification. -/// -/// TODO(pcwalton): Outlines. -pub struct StackingContext { +/// "Steps" as defined by CSS 2.1 § E.2. +#[deriving(Eq)] +pub enum StackingLevel { + /// The border and backgrounds for the root of this stacking context: steps 1 and 2. + BackgroundAndBordersStackingLevel, + /// Borders and backgrounds for block-level descendants: step 4. + BlockBackgroundsAndBordersStackingLevel, + /// Floats: step 5. These are treated as pseudo-stacking contexts. + FloatStackingLevel, + /// All other content. + ContentStackingLevel, + /// Positioned descendant stacking contexts, along with their `z-index` levels. + /// + /// TODO(pcwalton): `z-index` should be the actual CSS property value in order to handle + /// `auto`, not just an integer. + PositionedDescendantStackingLevel(i32) +} + +impl StackingLevel { + pub fn from_background_and_border_level(level: BackgroundAndBorderLevel) -> StackingLevel { + match level { + RootOfStackingContextLevel => BackgroundAndBordersStackingLevel, + BlockLevel => BlockBackgroundsAndBordersStackingLevel, + ContentLevel => ContentStackingLevel, + } + } +} + +struct StackingContext { /// The border and backgrounds for the root of this stacking context: steps 1 and 2. pub background_and_borders: DisplayList, /// Borders and backgrounds for block-level descendants: step 4. @@ -59,78 +86,115 @@ pub struct StackingContext { pub floats: DisplayList, /// All other content. pub content: DisplayList, - /// Positioned descendant stacking contexts, along with their `z-index` levels. /// /// TODO(pcwalton): `z-index` should be the actual CSS property value in order to handle - /// `auto`, not just an integer. In this case we should store an actual stacking context, not - /// a flattened display list. - pub positioned_descendants: SmallVec0<(int, DisplayList)>, + /// `auto`, not just an integer. + pub positioned_descendants: SmallVec0<(i32, DisplayList)>, } impl StackingContext { - pub fn new() -> StackingContext { - StackingContext { + /// Creates a stacking context from a display list. + fn new(list: DisplayList) -> StackingContext { + let DisplayList { + list: mut list + } = list; + + let mut stacking_context = StackingContext { background_and_borders: DisplayList::new(), block_backgrounds_and_borders: DisplayList::new(), floats: DisplayList::new(), content: DisplayList::new(), positioned_descendants: SmallVec0::new(), - } - } + }; - pub fn list_for_background_and_border_level<'a>( - &'a mut self, - level: BackgroundAndBorderLevel) - -> &'a mut DisplayList { - match level { - RootOfStackingContextLevel => &mut self.background_and_borders, - BlockLevel => &mut self.block_backgrounds_and_borders, - ContentLevel => &mut self.content, + for item in list.move_iter() { + match item { + ClipDisplayItemClass(~ClipDisplayItem { + base: base, + children: sublist + }) => { + let sub_stacking_context = StackingContext::new(sublist); + stacking_context.merge_with_clip(sub_stacking_context, &base.bounds, base.node) + } + item => { + match item.base().level { + BackgroundAndBordersStackingLevel => { + stacking_context.background_and_borders.push(item) + } + BlockBackgroundsAndBordersStackingLevel => { + stacking_context.block_backgrounds_and_borders.push(item) + } + FloatStackingLevel => stacking_context.floats.push(item), + ContentStackingLevel => stacking_context.content.push(item), + PositionedDescendantStackingLevel(z_index) => { + match stacking_context.positioned_descendants + .mut_iter() + .find(|& &(z, _)| z_index == z) { + Some(&(_, ref mut my_list)) => { + my_list.push(item); + continue + } + None => {} + } + + let mut new_list = DisplayList::new(); + new_list.list.push_back(item); + stacking_context.positioned_descendants.push((z_index, new_list)) + } + } + } + } } + + stacking_context } - /// Flattens a stacking context into a display list according to the steps in CSS 2.1 § E.2. - pub fn flatten(self) -> DisplayList { - // Steps 1 and 2: Borders and background for the root. + /// Merges another stacking context into this one, with the given clipping rectangle and DOM + /// node that supplies it. + fn merge_with_clip(&mut self, + other: StackingContext, + clip_rect: &Rect<Au>, + clipping_dom_node: OpaqueNode) { let StackingContext { - background_and_borders: mut result, + background_and_borders, block_backgrounds_and_borders, floats, content, positioned_descendants: mut positioned_descendants - } = self; - - // TODO(pcwalton): Sort positioned children according to z-index. + } = other; - // Step 3: Positioned descendants with negative z-indices. - for &(ref mut z_index, ref mut list) in positioned_descendants.mut_iter() { - if *z_index < 0 { - result.push_all_move(mem::replace(list, DisplayList::new())) + let push = |destination: &mut DisplayList, source: DisplayList, level| { + if !source.is_empty() { + let base = BaseDisplayItem::new(*clip_rect, clipping_dom_node, level); + destination.push(ClipDisplayItemClass(~ClipDisplayItem::new(base, source))) } - } - - // Step 4: Block backgrounds and borders. - result.push_all_move(block_backgrounds_and_borders); - - // Step 5: Floats. - result.push_all_move(floats); - - // TODO(pcwalton): Step 6: Inlines that generate stacking contexts. - - // Step 7: Content. - result.push_all_move(content); + }; - // Steps 8 and 9: Positioned descendants with nonnegative z-indices. - for &(ref mut z_index, ref mut list) in positioned_descendants.mut_iter() { - if *z_index >= 0 { - result.push_all_move(mem::replace(list, DisplayList::new())) + push(&mut self.background_and_borders, + background_and_borders, + BackgroundAndBordersStackingLevel); + push(&mut self.block_backgrounds_and_borders, + block_backgrounds_and_borders, + BlockBackgroundsAndBordersStackingLevel); + push(&mut self.floats, floats, FloatStackingLevel); + push(&mut self.content, content, ContentStackingLevel); + + for (z_index, list) in positioned_descendants.move_iter() { + match self.positioned_descendants + .mut_iter() + .find(|& &(existing_z_index, _)| z_index == existing_z_index) { + Some(&(_, ref mut existing_list)) => { + push(existing_list, list, PositionedDescendantStackingLevel(z_index)); + continue + } + None => {} } - } - - // TODO(pcwalton): Step 10: Outlines. - result + let mut new_list = DisplayList::new(); + push(&mut new_list, list, PositionedDescendantStackingLevel(z_index)); + self.positioned_descendants.push((z_index, new_list)); + } } } @@ -143,7 +207,7 @@ pub enum BackgroundAndBorderLevel { /// A list of rendering operations to be performed. pub struct DisplayList { - pub list: SmallVec0<DisplayItem>, + pub list: DList<DisplayItem>, } pub enum DisplayListIterator<'a> { @@ -165,7 +229,7 @@ impl DisplayList { /// Creates a new display list. pub fn new() -> DisplayList { DisplayList { - list: SmallVec0::new(), + list: DList::new(), } } @@ -177,16 +241,17 @@ impl DisplayList { /// Appends the given item to the display list. pub fn push(&mut self, item: DisplayItem) { - self.list.push(item) + self.list.push_back(item) } /// Appends the given display list to this display list, consuming the other display list in /// the process. pub fn push_all_move(&mut self, other: DisplayList) { - self.list.push_all_move(other.list) + self.list.append(other.list) } - /// Draws the display list into the given render context. + /// Draws the display list into the given render context. The display list must be flattened + /// first for correct painting. pub fn draw_into_context(&self, render_context: &mut RenderContext) { debug!("Beginning display list."); for item in self.list.iter() { @@ -199,6 +264,74 @@ impl DisplayList { pub fn iter<'a>(&'a self) -> DisplayItemIterator<'a> { ParentDisplayItemIterator(self.list.iter()) } + + /// Returns true if this list is empty and false otherwise. + fn is_empty(&self) -> bool { + self.list.len() == 0 + } + + /// Flattens a display list into a display list with a single stacking level according to the + /// steps in CSS 2.1 § E.2. + /// + /// This must be called before `draw_into_context()` is for correct results. + pub fn flatten(self, resulting_level: StackingLevel) -> DisplayList { + // TODO(pcwalton): Sort positioned children according to z-index. + + let mut result = DisplayList::new(); + let StackingContext { + background_and_borders, + block_backgrounds_and_borders, + floats, + content, + positioned_descendants: mut positioned_descendants + } = StackingContext::new(self); + + // Steps 1 and 2: Borders and background for the root. + result.push_all_move(background_and_borders); + + // TODO(pcwalton): Sort positioned children according to z-index. + + // Step 3: Positioned descendants with negative z-indices. + for &(ref mut z_index, ref mut list) in positioned_descendants.mut_iter() { + if *z_index < 0 { + result.push_all_move(mem::replace(list, DisplayList::new())) + } + } + + // Step 4: Block backgrounds and borders. + result.push_all_move(block_backgrounds_and_borders); + + // Step 5: Floats. + result.push_all_move(floats); + + // TODO(pcwalton): Step 6: Inlines that generate stacking contexts. + + // Step 7: Content. + result.push_all_move(content); + + // Steps 8 and 9: Positioned descendants with nonnegative z-indices. + for &(ref mut z_index, ref mut list) in positioned_descendants.mut_iter() { + if *z_index >= 0 { + result.push_all_move(mem::replace(list, DisplayList::new())) + } + } + + // TODO(pcwalton): Step 10: Outlines. + + result.set_stacking_level(resulting_level); + result + } + + /// Sets the stacking level for this display list and all its subitems. + fn set_stacking_level(&mut self, new_level: StackingLevel) { + for item in self.list.mut_iter() { + item.mut_base().level = new_level; + match item.mut_sublist() { + None => {} + Some(sublist) => sublist.set_stacking_level(new_level), + } + } + } } /// One drawing command in the list. @@ -208,7 +341,14 @@ pub enum DisplayItem { ImageDisplayItemClass(~ImageDisplayItem), BorderDisplayItemClass(~BorderDisplayItem), LineDisplayItemClass(~LineDisplayItem), - ClipDisplayItemClass(~ClipDisplayItem) + ClipDisplayItemClass(~ClipDisplayItem), + + /// A pseudo-display item that exists only so that queries like `ContentBoxQuery` and + /// `ContentBoxesQuery` can be answered. + /// + /// FIXME(pcwalton): This is really bogus. Those queries should not consult the display list + /// but should instead consult the flow/box tree. + PseudoDisplayItemClass(~BaseDisplayItem), } /// Information common to all display items. @@ -220,6 +360,19 @@ pub struct BaseDisplayItem { /// The originating DOM node. pub node: OpaqueNode, + + /// The stacking level in which this display item lives. + pub level: StackingLevel, +} + +impl BaseDisplayItem { + pub fn new(bounds: Rect<Au>, node: OpaqueNode, level: StackingLevel) -> BaseDisplayItem { + BaseDisplayItem { + bounds: bounds, + node: node, + level: level, + } + } } /// Renders a solid color. @@ -292,15 +445,27 @@ pub struct LineDisplayItem { pub style: border_style::T } +/// Clips a list of child display items to this display item's boundaries. pub struct ClipDisplayItem { + /// The base information. pub base: BaseDisplayItem, - pub child_list: SmallVec0<DisplayItem>, - pub need_clip: bool + + /// The child nodes. + pub children: DisplayList, +} + +impl ClipDisplayItem { + pub fn new(base: BaseDisplayItem, children: DisplayList) -> ClipDisplayItem { + ClipDisplayItem { + base: base, + children: children, + } + } } pub enum DisplayItemIterator<'a> { EmptyDisplayItemIterator, - ParentDisplayItemIterator(SmallVecIterator<'a,DisplayItem>), + ParentDisplayItemIterator(dlist::Items<'a,DisplayItem>), } impl<'a> Iterator<&'a DisplayItem> for DisplayItemIterator<'a> { @@ -316,21 +481,20 @@ impl<'a> Iterator<&'a DisplayItem> for DisplayItemIterator<'a> { impl DisplayItem { /// Renders this display item into the given render context. fn draw_into_context(&self, render_context: &mut RenderContext) { + // This should have been flattened to the content stacking level first. + assert!(self.base().level == ContentStackingLevel); + match *self { SolidColorDisplayItemClass(ref solid_color) => { render_context.draw_solid_color(&solid_color.base.bounds, solid_color.color) } ClipDisplayItemClass(ref clip) => { - if clip.need_clip { - render_context.draw_push_clip(&clip.base.bounds); - } - for item in clip.child_list.iter() { + render_context.draw_push_clip(&clip.base.bounds); + for item in clip.children.iter() { (*item).draw_into_context(render_context); } - if clip.need_clip { - render_context.draw_pop_clip(); - } + render_context.draw_pop_clip(); } TextDisplayItemClass(ref text) => { @@ -412,6 +576,8 @@ impl DisplayItem { line.color, line.style) } + + PseudoDisplayItemClass(_) => {} } } @@ -423,6 +589,19 @@ impl DisplayItem { BorderDisplayItemClass(ref border) => &border.base, LineDisplayItemClass(ref line) => &line.base, ClipDisplayItemClass(ref clip) => &clip.base, + PseudoDisplayItemClass(ref base) => &**base, + } + } + + pub fn mut_base<'a>(&'a mut self) -> &'a mut BaseDisplayItem { + match *self { + SolidColorDisplayItemClass(ref mut solid_color) => &mut solid_color.base, + TextDisplayItemClass(ref mut text) => &mut text.base, + ImageDisplayItemClass(ref mut image_item) => &mut image_item.base, + BorderDisplayItemClass(ref mut border) => &mut border.base, + LineDisplayItemClass(ref mut line) => &mut line.base, + ClipDisplayItemClass(ref mut clip) => &mut clip.base, + PseudoDisplayItemClass(ref mut base) => &mut **base, } } @@ -432,12 +611,26 @@ impl DisplayItem { pub fn children<'a>(&'a self) -> DisplayItemIterator<'a> { match *self { - ClipDisplayItemClass(ref clip) => ParentDisplayItemIterator(clip.child_list.iter()), + ClipDisplayItemClass(ref clip) => ParentDisplayItemIterator(clip.children.list.iter()), + SolidColorDisplayItemClass(..) | + TextDisplayItemClass(..) | + ImageDisplayItemClass(..) | + BorderDisplayItemClass(..) | + LineDisplayItemClass(..) | + PseudoDisplayItemClass(..) => EmptyDisplayItemIterator, + } + } + + /// Returns a mutable reference to the sublist contained within this display list item, if any. + fn mut_sublist<'a>(&'a mut self) -> Option<&'a mut DisplayList> { + match *self { + ClipDisplayItemClass(ref mut clip) => Some(&mut clip.children), SolidColorDisplayItemClass(..) | TextDisplayItemClass(..) | ImageDisplayItemClass(..) | BorderDisplayItemClass(..) | - LineDisplayItemClass(..) => EmptyDisplayItemIterator, + LineDisplayItemClass(..) | + PseudoDisplayItemClass(..) => None, } } @@ -460,8 +653,9 @@ impl DisplayItem { BorderDisplayItemClass(_) => "Border", LineDisplayItemClass(_) => "Line", ClipDisplayItemClass(_) => "Clip", + PseudoDisplayItemClass(_) => "Pseudo", }; - format!("{} @ {:?}", class, self.base().bounds) + format!("{} @ {} ({:x})", class, self.base().bounds, self.base().node.id()) } } diff --git a/src/components/main/layout/block.rs b/src/components/main/layout/block.rs index 32f0aee6197..60c37eca7ed 100644 --- a/src/components/main/layout/block.rs +++ b/src/components/main/layout/block.rs @@ -15,7 +15,6 @@ use layout::box_::{Box, ImageBox, ScannedTextBox}; use layout::construct::FlowConstructor; use layout::context::LayoutContext; -use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo}; use layout::floats::{ClearBoth, ClearLeft, ClearRight, FloatKind, Floats, PlacementInfo}; use layout::flow::{BaseFlow, BlockFlowClass, FlowClass, Flow, ImmutableFlowUtils}; use layout::flow::{MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal, mut_base}; @@ -28,18 +27,22 @@ use layout::wrapper::ThreadSafeLayoutNode; use style::ComputedValues; use style::computed_values::{clear, position}; +use collections::Deque; +use collections::dlist::DList; use geom::{Point2D, Rect, Size2D}; use gfx::color; -use gfx::display_list::{BackgroundAndBorderLevel, BlockLevel, RootOfStackingContextLevel}; -use gfx::display_list::{StackingContext}; +use gfx::display_list::{BackgroundAndBorderLevel, BlockLevel, ContentStackingLevel, DisplayList}; +use gfx::display_list::{FloatStackingLevel, PositionedDescendantStackingLevel}; +use gfx::display_list::{RootOfStackingContextLevel}; use gfx::render_task::RenderLayer; use servo_msg::compositor_msg::{FixedPosition, LayerId, Scrollable}; use servo_util::geometry::Au; use servo_util::geometry; use servo_util::smallvec::{SmallVec, SmallVec0}; +use std::mem; use std::num::Zero; use style::computed_values::{LPA_Auto, LPA_Length, LPA_Percentage, LPN_Length, LPN_None}; -use style::computed_values::{LPN_Percentage, LP_Length, LP_Percentage}; +use style::computed_values::{LPN_Percentage, LP_Length, LP_Percentage, display, float, overflow}; use sync::Arc; /// Information specific to floated blocks. @@ -52,9 +55,6 @@ pub struct FloatedBlockInfo { /// Index into the box list for inline floats pub index: Option<uint>, - /// Number of floated children - pub floated_children: uint, - /// Left or right? pub float_kind: FloatKind, } @@ -65,7 +65,6 @@ impl FloatedBlockInfo { containing_width: Au(0), rel_pos: Point2D(Au(0), Au(0)), index: None, - floated_children: 0, float_kind: float_kind, } } @@ -377,12 +376,9 @@ enum CandidateHeightIteratorStatus { } // A helper function used in height calculation. -fn translate_including_floats(cur_y: &mut Au, delta: Au, inorder: bool, floats: &mut Floats) { +fn translate_including_floats(cur_y: &mut Au, delta: Au, floats: &mut Floats) { *cur_y = *cur_y + delta; - - if inorder { - floats.translate(Point2D(Au(0), -delta)); - } + floats.translate(Point2D(Au(0), -delta)); } /// The real assign-heights traversal for flows with position 'absolute'. @@ -801,14 +797,13 @@ impl BlockFlow { #[inline(always)] pub fn assign_height_block_base(&mut self, layout_context: &mut LayoutContext, - inorder: bool, margins_may_collapse: MarginsMayCollapseFlag) { // Our current border-box position. let mut cur_y = Au(0); // Absolute positioning establishes a block formatting context. Don't propagate floats // in or out. (But do propagate them between kids.) - if inorder && self.is_absolutely_positioned() { + if self.is_absolutely_positioned() { self.base.floats = Floats::new(); } if margins_may_collapse != MarginsMayCollapse { @@ -820,7 +815,7 @@ impl BlockFlow { // The sum of our top border and top padding. let top_offset = self.box_.border_padding.top; - translate_including_floats(&mut cur_y, top_offset, inorder, &mut self.base.floats); + translate_including_floats(&mut cur_y, top_offset, &mut self.base.floats); let can_collapse_top_margin_with_kids = margins_may_collapse == MarginsMayCollapse && @@ -829,19 +824,15 @@ impl BlockFlow { margin_collapse_info.initialize_top_margin(&self.box_, can_collapse_top_margin_with_kids); - // At this point, cur_y is at the content edge of the flow's box. + // At this point, `cur_y` is at the content edge of our box. Now iterate over children. let mut floats = self.base.floats.clone(); let mut layers_needed_for_descendants = false; for kid in self.base.child_iter() { if kid.is_absolutely_positioned() { // Assume that the *hypothetical box* for an absolute flow starts immediately after // the bottom border edge of the previous flow. - kid.as_block().base.position.origin.y = cur_y; - - if inorder { - kid.assign_height_inorder(layout_context) - } - + flow::mut_base(kid).position.origin.y = cur_y; + kid.assign_height_for_inorder_child_if_necessary(layout_context); propagate_layer_flag_from_child(&mut layers_needed_for_descendants, kid); // Skip the collapsing and float processing for absolute flow kids and continue @@ -850,86 +841,82 @@ impl BlockFlow { } // Assign height now for the child if it was impacted by floats and we couldn't before. - let mut floats_out = None; - if inorder { - if !kid.is_float() { - let kid_base = flow::mut_base(kid); - if kid_base.clear != clear::none { - // We have clearance, so assume there are no floats in and perform layout. - // - // FIXME(#2008, pcwalton): This could be wrong if we have `clear: left` or - // `clear: right` and there are still floats to impact, of course. But this - // gets complicated with margin collapse. Possibly the right thing to do is - // to lay out the block again in this rare case. (Note that WebKit can lay - // blocks out twice; this may be related, although I haven't looked into it - // closely.) - kid_base.floats = Floats::new() - } else { - kid_base.floats = floats.clone() - } - } else { - let kid_base = flow::mut_base(kid); - kid_base.position.origin.y = margin_collapse_info.current_float_ceiling(); - kid_base.floats = floats.clone() - } - - kid.assign_height_inorder(layout_context); + flow::mut_base(kid).floats = floats.clone(); + if kid.is_float() { + // FIXME(pcwalton): Using `position.origin.y` to mean the float ceiling is a + // bit of a hack. + flow::mut_base(kid).position.origin.y = + margin_collapse_info.current_float_ceiling(); + propagate_layer_flag_from_child(&mut layers_needed_for_descendants, kid); - floats_out = Some(flow::mut_base(kid).floats.clone()); + let kid_was_impacted_by_floats = + kid.assign_height_for_inorder_child_if_necessary(layout_context); + assert!(kid_was_impacted_by_floats); // As it was a float itself... - // A horrible hack that has to do with the fact that `origin.y` is used for - // something else later (containing block for float). - if kid.is_float() { - flow::mut_base(kid).position.origin.y = cur_y; - } + let kid_base = flow::mut_base(kid); + kid_base.position.origin.y = cur_y; + floats = kid_base.floats.clone(); + continue } - propagate_layer_flag_from_child(&mut layers_needed_for_descendants, kid); - // If the child was a float, stop here. - if kid.is_float() { - if inorder { - floats = floats_out.take_unwrap(); - } - continue + // If we have clearance, assume there are no floats in. + // + // FIXME(#2008, pcwalton): This could be wrong if we have `clear: left` or `clear: + // right` and there are still floats to impact, of course. But this gets complicated + // with margin collapse. Possibly the right thing to do is to lay out the block again + // in this rare case. (Note that WebKit can lay blocks out twice; this may be related, + // although I haven't looked into it closely.) + if kid.float_clearance() != clear::none { + flow::mut_base(kid).floats = Floats::new() } - // Handle any (possibly collapsed) top margin. - let kid_base = flow::mut_base(kid); - let delta = margin_collapse_info.advance_top_margin(&kid_base.collapsible_margins); - translate_including_floats(&mut cur_y, delta, inorder, &mut floats); + // Lay the child out if this was an in-order traversal. + let kid_was_impacted_by_floats = + kid.assign_height_for_inorder_child_if_necessary(layout_context); - // Clear past floats, if necessary. - if inorder { - let clearance = match kid_base.clear { - clear::none => Au(0), - clear::left => floats.clearance(ClearLeft), - clear::right => floats.clearance(ClearRight), - clear::both => floats.clearance(ClearBoth), - }; - cur_y = cur_y + clearance - } + // Mark flows for layerization if necessary to handle painting order correctly. + propagate_layer_flag_from_child(&mut layers_needed_for_descendants, kid); + + // Handle any (possibly collapsed) top margin. + let delta = + margin_collapse_info.advance_top_margin(&flow::base(kid).collapsible_margins); + translate_including_floats(&mut cur_y, delta, &mut floats); + + // Clear past the floats that came in, if necessary. + let clearance = match kid.float_clearance() { + clear::none => Au(0), + clear::left => floats.clearance(ClearLeft), + clear::right => floats.clearance(ClearRight), + clear::both => floats.clearance(ClearBoth), + }; + cur_y = cur_y + clearance; // At this point, `cur_y` is at the border edge of the child. - assert!(kid_base.position.origin.y == Au(0)); - kid_base.position.origin.y = cur_y; + flow::mut_base(kid).position.origin.y = cur_y; - // If this was an inorder traversal, grab the child's floats now. - if inorder { - floats = floats_out.take_unwrap() + // Now pull out the child's outgoing floats. We didn't do this immediately after the + // `assign_height_for_inorder_child_if_necessary` call because clearance on a block + // operates on the floats that come *in*, not the floats that go *out*. + if kid_was_impacted_by_floats { + floats = flow::mut_base(kid).floats.clone() } // Move past the child's border box. Do not use the `translate_including_floats` // function here because the child has already translated floats past its border box. + let kid_base = flow::mut_base(kid); cur_y = cur_y + kid_base.position.size.height; // Handle any (possibly collapsed) bottom margin. let delta = margin_collapse_info.advance_bottom_margin(&kid_base.collapsible_margins); - translate_including_floats(&mut cur_y, delta, inorder, &mut floats); + translate_including_floats(&mut cur_y, delta, &mut floats); } + // Mark ourselves for layerization if that will be necessary to paint in the proper order + // (CSS 2.1, Appendix E). self.base.flags.set_layers_needed_for_descendants(layers_needed_for_descendants); + // Collect various offsets needed by absolutely positioned descendants. self.collect_static_y_offsets_from_kids(); // Add in our bottom margin and compute our collapsible margins. @@ -942,7 +929,7 @@ impl BlockFlow { &self.box_, can_collapse_bottom_margin_with_kids); self.base.collapsible_margins = collapsible_margins; - translate_including_floats(&mut cur_y, delta, inorder, &mut floats); + translate_including_floats(&mut cur_y, delta, &mut floats); // FIXME(#2003, pcwalton): The max is taken here so that you can scroll the page, but this // is not correct behavior according to CSS 2.1 § 10.5. Instead I think we should treat the @@ -981,11 +968,11 @@ impl BlockFlow { // Adjust `cur_y` as necessary to account for the explicitly-specified height. height = candidate_height_iterator.candidate_value; let delta = height - (cur_y - top_offset); - translate_including_floats(&mut cur_y, delta, inorder, &mut floats); + translate_including_floats(&mut cur_y, delta, &mut floats); // Compute content height and noncontent height. let bottom_offset = self.box_.border_padding.bottom; - translate_including_floats(&mut cur_y, bottom_offset, inorder, &mut floats); + translate_including_floats(&mut cur_y, bottom_offset, &mut floats); // Now that `cur_y` is at the bottom of the border box, compute the final border box // position. @@ -1011,17 +998,15 @@ impl BlockFlow { /// Add placement information about current float flow for use by the parent. /// - /// Also, use information given by parent about other floats to find out - /// our relative position. + /// Also, use information given by parent about other floats to find out our relative position. /// - /// This does not give any information about any float descendants because - /// they do not affect elements outside of the subtree rooted at this - /// float. + /// This does not give any information about any float descendants because they do not affect + /// elements outside of the subtree rooted at this float. /// - /// This function is called on a kid flow by a parent. - /// 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) { + /// This function is called on a kid flow by a parent. Therefore, `assign_height_float` was + /// already called on this kid flow by the traversal function. So, the values used are + /// well-defined. + pub fn place_float(&mut self) { let height = self.box_.border_box.size.height; let clearance = match self.box_.clear() { None => Au(0), @@ -1054,20 +1039,16 @@ impl BlockFlow { /// /// It does not calculate the height of the flow itself. pub fn assign_height_float(&mut self, ctx: &mut LayoutContext) { - // Now that we've determined our height, propagate that out. - let has_inorder_children = self.base.num_floats > 0; - if has_inorder_children { - let mut floats = Floats::new(); - for kid in self.base.child_iter() { - flow::mut_base(kid).floats = floats; - kid.assign_height_inorder(ctx); - floats = flow::mut_base(kid).floats.clone(); - } - - // Floats establish a block formatting context, so we discard the output floats here. - drop(floats); + let mut floats = Floats::new(); + for kid in self.base.child_iter() { + flow::mut_base(kid).floats = floats.clone(); + kid.assign_height_for_inorder_child_if_necessary(ctx); + floats = flow::mut_base(kid).floats.clone(); } + // Floats establish a block formatting context, so we discard the output floats here. + drop(floats); + let top_offset = self.box_.margin.top + self.box_.border_padding.top; let mut cur_y = top_offset; @@ -1101,99 +1082,75 @@ impl BlockFlow { } fn build_display_list_block_common(&mut self, - stacking_context: &mut StackingContext, - builder: &mut DisplayListBuilder, - info: &DisplayListBuildingInfo, + layout_context: &LayoutContext, offset: Point2D<Au>, background_border_level: BackgroundAndBorderLevel) { - let mut info = *info; - let rel_offset = self.box_.relative_position(&info.relative_containing_block_size, None); + let rel_offset = + self.box_.relative_position(&self.base + .absolute_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, - 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; - if self.is_positioned() { - info.absolute_containing_block_position = - self.base.abs_position + - self.generated_containing_block_rect().origin + - self.box_.relative_position(&info.relative_containing_block_size, None) - } - - let this_position = self.base.abs_position; + let mut display_list = DisplayList::new(); + let mut accumulator = + self.box_.build_display_list(&mut display_list, + layout_context, + self.base.abs_position + rel_offset + offset, + background_border_level, + None); + + let mut child_layers = DList::new(); for kid in self.base.child_iter() { - { - let kid_base = flow::mut_base(kid); - kid_base.abs_position = this_position + kid_base.position.origin + rel_offset + - offset; - } - if kid.is_absolutely_positioned() { // All absolute flows will be handled by their containing block. continue } - kid.build_display_list(stacking_context, builder, &info); + accumulator.push_child(&mut display_list, kid); + child_layers.append(mem::replace(&mut flow::mut_base(kid).layers, DList::new())) } // Process absolute descendant links. - let mut absolute_info = info; - absolute_info.layers_needed_for_positioned_flows = - self.base.flags.layers_needed_for_descendants(); for abs_descendant_link in self.base.abs_descendants.iter() { match abs_descendant_link.resolve() { - Some(flow) => { + Some(kid) => { // TODO(pradeep): Send in our absolute position directly. - flow.build_display_list(stacking_context, builder, &absolute_info) + accumulator.push_child(&mut display_list, kid); + child_layers.append(mem::replace(&mut flow::mut_base(kid).layers, + DList::new())); } None => fail!("empty Rawlink to a descendant") } } + + accumulator.finish(&mut *self, display_list); + self.base.layers = child_layers } /// Add display items for current block. /// /// Set the absolute position for children after doing any offsetting for /// position: relative. - pub fn build_display_list_block(&mut self, - stacking_context: &mut StackingContext, - builder: &mut DisplayListBuilder, - info: &DisplayListBuildingInfo) { + pub fn build_display_list_block(&mut self, layout_context: &LayoutContext) { if self.is_float() { // TODO(#2009, pcwalton): This is a pseudo-stacking context. We need to merge `z-index: // auto` kids into the parent stacking context, when that is supported. - self.build_display_list_float(stacking_context, builder, info) + self.build_display_list_float(layout_context) } else if self.is_absolutely_positioned() { - self.build_display_list_abs(stacking_context, builder, info) + self.build_display_list_abs(layout_context) } else { - self.build_display_list_block_common(stacking_context, - builder, - info, - Point2D(Au(0), Au(0)), - BlockLevel) + self.build_display_list_block_common(layout_context, Zero::zero(), BlockLevel) } } - pub fn build_display_list_float(&mut self, - parent_stacking_context: &mut StackingContext, - builder: &mut DisplayListBuilder, - info: &DisplayListBuildingInfo) { - let mut stacking_context = StackingContext::new(); + pub fn build_display_list_float(&mut self, layout_context: &LayoutContext) { let float_offset = self.float.get_ref().rel_pos; - self.build_display_list_block_common(&mut stacking_context, - builder, - info, + self.build_display_list_block_common(layout_context, float_offset, RootOfStackingContextLevel); - parent_stacking_context.floats.push_all_move(stacking_context.flatten()) + self.base.display_list = mem::replace(&mut self.base.display_list, + DisplayList::new()).flatten(FloatStackingLevel) } /// Calculate and set the height, offsets, etc. for absolutely positioned flow. @@ -1283,58 +1240,41 @@ impl BlockFlow { } /// Add display items for Absolutely Positioned flow. - pub fn build_display_list_abs(&mut self, - parent_stacking_context: &mut StackingContext, - builder: &mut DisplayListBuilder, - info: &DisplayListBuildingInfo) { - let mut stacking_context = StackingContext::new(); - let mut info = *info; - - info.absolute_containing_block_position = if self.is_fixed() { - // The viewport is initially at (0, 0). - self.base.position.origin - } else { - // Absolute position of Containing Block + position of absolute flow - // wrt Containing Block - info.absolute_containing_block_position + self.base.position.origin - }; - - // Set the absolute position, which will be passed down later as part - // of containing block details for absolute descendants. - self.base.abs_position = info.absolute_containing_block_position; - - self.build_display_list_block_common(&mut stacking_context, - builder, - &info, - Point2D(Au(0), Au(0)), + fn build_display_list_abs(&mut self, layout_context: &LayoutContext) { + self.build_display_list_block_common(layout_context, + Zero::zero(), RootOfStackingContextLevel); - if !info.layers_needed_for_positioned_flows && !self.base.flags.needs_layer() { + if !self.base.absolute_position_info.layers_needed_for_positioned_flows && + !self.base.flags.needs_layer() { // We didn't need a layer. // // TODO(#781, pcwalton): `z-index`. - parent_stacking_context.positioned_descendants.push((0, stacking_context.flatten())); + self.base.display_list = + mem::replace(&mut self.base.display_list, + DisplayList::new()).flatten(PositionedDescendantStackingLevel(0)); return } // If we got here, then we need a new layer. let size = Size2D(self.base.position.size.width.to_nearest_px() as uint, self.base.position.size.height.to_nearest_px() as uint); - let origin = Point2D(info.absolute_containing_block_position.x.to_nearest_px() as uint, - info.absolute_containing_block_position.y.to_nearest_px() as uint); + let origin = Point2D(self.base.abs_position.x.to_nearest_px() as uint, + self.base.abs_position.y.to_nearest_px() as uint); let scroll_policy = if self.is_fixed() { FixedPosition } else { Scrollable }; + let display_list = mem::replace(&mut self.base.display_list, DisplayList::new()); let new_layer = RenderLayer { id: self.layer_id(0), - display_list: Arc::new(stacking_context.flatten()), + display_list: Arc::new(display_list.flatten(ContentStackingLevel)), position: Rect(origin, size), background_color: color::rgba(255.0, 255.0, 255.0, 0.0), scroll_policy: scroll_policy, }; - builder.layers.push(new_layer) + self.base.layers.push_back(new_layer) } /// Return the top outer edge of the Hypothetical Box for an absolute flow. @@ -1348,27 +1288,15 @@ impl BlockFlow { self.base.position.origin.y } - /// Initializes the containing width if this block flow is a float. This is done at the start - /// of `assign_widths`. - fn set_containing_width_if_float(&mut self, containing_block_width: Au) { - if self.is_float() { - self.float.get_mut_ref().containing_width = containing_block_width; - - // Parent usually sets this, but floats are never inorder - self.base.flags.set_inorder(false) - } - } - /// Assigns the computed left content edge and width to all the children of this block flow. + /// Also computes whether each child will be impacted by floats. pub fn propagate_assigned_width_to_children(&mut self, left_content_edge: Au, content_width: Au, opt_col_widths: Option<~[Au]>) { - let has_inorder_children = if self.is_float() { - self.base.num_floats > 0 - } else { - self.base.flags.inorder() || self.base.num_floats > 0 - }; + // Keep track of whether floats could impact each child. + let mut left_floats_impact_child = self.base.flags.impacted_by_left_floats(); + let mut right_floats_impact_child = self.base.flags.impacted_by_right_floats(); let kid_abs_cb_x_offset; if self.is_positioned() { @@ -1398,16 +1326,31 @@ impl BlockFlow { { let child_base = flow::mut_base(kid); - // Left margin edge of kid flow is at our left content edge + // The left margin edge of the child flow is at our left content edge. child_base.position.origin.x = left_content_edge; - // Width of kid flow is our content width + // The width of the child flow is our content width. child_base.position.size.width = content_width; - child_base.flags.set_inorder(has_inorder_children); + } - if !child_base.flags.inorder() { - child_base.floats = Floats::new(); + // Determine float impaction. + match kid.float_clearance() { + clear::none => {} + clear::left => left_floats_impact_child = false, + clear::right => right_floats_impact_child = false, + clear::both => { + left_floats_impact_child = false; + right_floats_impact_child = false; } } + { + let kid_base = flow::mut_base(kid); + left_floats_impact_child = left_floats_impact_child || + kid_base.flags.has_left_floated_descendants(); + right_floats_impact_child = right_floats_impact_child || + kid_base.flags.has_right_floated_descendants(); + kid_base.flags.set_impacted_by_left_floats(left_floats_impact_child); + kid_base.flags.set_impacted_by_right_floats(right_floats_impact_child); + } // Handle tables. match opt_col_widths { @@ -1459,20 +1402,45 @@ impl Flow for BlockFlow { self } - /* Recursively (bottom-up) determine the context's preferred and - minimum widths. When called on this context, all child contexts - have had their min/pref widths set. This function must decide - min/pref widths based on child context widths and dimensions of - any boxes it is responsible for flowing. */ + /// Returns the direction that this flow clears floats in, if any. + fn float_clearance(&self) -> clear::T { + self.box_.style().Box.get().clear + } - /* TODO: inline-blocks */ + /// Returns true if this flow is a block formatting context and false otherwise. + fn is_block_formatting_context(&self, only_impactable_by_floats: bool) -> bool { + let style = self.box_.style(); + if style.Box.get().float != float::none { + return !only_impactable_by_floats + } + if style.Box.get().overflow != overflow::visible { + return true + } + match style.Box.get().display { + display::table_cell | display::table_caption | display::inline_block => true, + _ => false, + } + } + + /// Pass 1 of reflow: computes minimum and preferred widths. + /// + /// Recursively (bottom-up) determine the flow's minimum and preferred widths. When called on + /// this flow, all child flows have had their minimum and preferred widths set. This function + /// must decide minimum/preferred widths based on its children's widths and the dimensions of + /// any boxes it is responsible for flowing. + /// + /// TODO(pcwalton): Inline blocks. fn bubble_widths(&mut self, _: &mut LayoutContext) { - let mut num_floats = 0; + let mut flags = self.base.flags; + flags.set_has_left_floated_descendants(false); + flags.set_has_right_floated_descendants(false); // Find the maximum width from children. let mut intrinsic_widths = IntrinsicWidths::new(); for child_ctx in self.base.child_iter() { - assert!(child_ctx.is_block_flow() || child_ctx.is_inline_flow() || child_ctx.is_table_kind()); + assert!(child_ctx.is_block_flow() || + child_ctx.is_inline_flow() || + child_ctx.is_table_kind()); let child_base = flow::mut_base(child_ctx); intrinsic_widths.minimum_width = @@ -1481,14 +1449,8 @@ impl Flow for BlockFlow { intrinsic_widths.preferred_width = geometry::max(intrinsic_widths.preferred_width, child_base.intrinsic_widths.total_preferred_width()); - num_floats = num_floats + child_base.num_floats; - } - if self.is_float() { - self.base.num_floats = 1; - self.float.get_mut_ref().floated_children = num_floats; - } else { - self.base.num_floats = num_floats; + flags.union_floated_descendants_flags(child_base.flags); } let box_intrinsic_widths = self.box_.intrinsic_widths(None); @@ -1497,8 +1459,14 @@ impl Flow for BlockFlow { intrinsic_widths.preferred_width = geometry::max(intrinsic_widths.preferred_width, box_intrinsic_widths.preferred_width); intrinsic_widths.surround_width = box_intrinsic_widths.surround_width; + self.base.intrinsic_widths = intrinsic_widths; - self.base.intrinsic_widths = intrinsic_widths + match self.box_.style().Box.get().float { + float::none => {} + float::left => flags.set_has_left_floated_descendants(true), + float::right => flags.set_has_right_floated_descendants(true), + } + self.base.flags = flags } /// Recursively (top-down) determines the actual width of child contexts and boxes. When called @@ -1519,20 +1487,24 @@ impl Flow for BlockFlow { self.base.position.origin = Zero::zero(); self.base.position.size.width = ctx.screen_size.width; self.base.floats = Floats::new(); - // Root element is not floated - self.base.flags.set_inorder(false); + + // The root element is never impacted by floats. + self.base.flags.set_impacted_by_left_floats(false); + self.base.flags.set_impacted_by_right_floats(false); } // The position was set to the containing block by the flow's parent. let containing_block_width = self.base.position.size.width; - - self.set_containing_width_if_float(containing_block_width); - self.compute_used_width(ctx, containing_block_width); + if self.is_float() { + self.float.get_mut_ref().containing_width = containing_block_width; + } - // Assign `clear` now so that the assign-heights pass will have the correct value for - // it. - self.base.clear = self.box_.style().Box.get().clear; + // Block formatting contexts are never impacted by floats. + if self.is_block_formatting_context(false) { + self.base.flags.set_impacted_by_left_floats(false); + self.base.flags.set_impacted_by_right_floats(false); + } // Move in from the left border edge let left_content_edge = self.box_.border_box.origin.x + self.box_.border_padding.left; @@ -1546,18 +1518,24 @@ impl Flow for BlockFlow { self.propagate_assigned_width_to_children(left_content_edge, content_width, None); } - /// This is called on kid flows by a parent. + /// Assigns heights in-order; or, if this is a float, places the float. The default + /// implementation simply assigns heights if this flow is impacted by floats. Returns true if + /// this child was impacted by floats or false otherwise. /// - /// Hence, we can assume that assign_height has already been called on the - /// kid (because of the bottom-up traversal). - fn assign_height_inorder(&mut self, ctx: &mut LayoutContext) { + /// This is called on child flows by the parent. Hence, we can assume that `assign_height` has + /// already been called on the child (because of the bottom-up traversal). + fn assign_height_for_inorder_child_if_necessary(&mut self, layout_context: &mut LayoutContext) + -> bool { if self.is_float() { - debug!("assign_height_inorder_float: assigning height for float"); - self.assign_height_float_inorder(); - } else { - debug!("assign_height_inorder: assigning height for block"); - self.assign_height_block_base(ctx, true, MarginsMayCollapse); + self.place_float(); + return true } + + let impacted = self.base.flags.impacted_by_floats(); + if impacted { + self.assign_height(layout_context); + } + impacted } fn assign_height(&mut self, ctx: &mut LayoutContext) { @@ -1569,13 +1547,77 @@ impl Flow for BlockFlow { self.assign_height_float(ctx); } else { debug!("assign_height: assigning height for block"); - // This is the only case in which a block flow can start an inorder - // subtraversal. - if self.is_root() && self.base.num_floats > 0 { - self.assign_height_inorder(ctx); - return; + self.assign_height_block_base(ctx, MarginsMayCollapse); + } + } + + fn compute_absolute_position(&mut self) { + if self.is_absolutely_positioned() { + self.base + .absolute_position_info + .absolute_containing_block_position = if self.is_fixed() { + // The viewport is initially at (0, 0). + self.base.position.origin + } else { + // Absolute position of the containing block + position of absolute flow w/r/t the + // containing block. + self.base.absolute_position_info.absolute_containing_block_position + + self.base.position.origin + }; + + // Set the absolute position, which will be passed down later as part + // of containing block details for absolute descendants. + self.base.abs_position = + self.base.absolute_position_info.absolute_containing_block_position; + } + + // 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, is only established if we are positioned. + let relative_offset = + self.box_.relative_position(&self.base + .absolute_position_info + .relative_containing_block_size, + None); + if self.is_positioned() { + self.base.absolute_position_info.absolute_containing_block_position = + self.base.abs_position + + self.generated_containing_block_rect().origin + + relative_offset + }; + + let float_offset = if self.is_float() { + self.float.get_ref().rel_pos + } else { + Zero::zero() + }; + + // Compute absolute position info for children. + let mut absolute_position_info = self.base.absolute_position_info; + absolute_position_info.relative_containing_block_size = self.box_.content_box().size; + absolute_position_info.layers_needed_for_positioned_flows = + self.base.flags.layers_needed_for_descendants(); + + // Process children. + let this_position = self.base.abs_position; + for kid in self.base.child_iter() { + if !kid.is_absolutely_positioned() { + let kid_base = flow::mut_base(kid); + kid_base.abs_position = this_position + kid_base.position.origin + + relative_offset + float_offset; + kid_base.absolute_position_info = absolute_position_info + } + } + + // Process absolute descendant links. + for absolute_descendant_link in self.base.abs_descendants.iter() { + match absolute_descendant_link.resolve() { + Some(absolute_descendant) => { + flow::mut_base(absolute_descendant).absolute_position_info = + absolute_position_info + } + None => fail!("empty Rawlink to a descendant") } - self.assign_height_block_base(ctx, false, MarginsMayCollapse); } } diff --git a/src/components/main/layout/box_.rs b/src/components/main/layout/box_.rs index 6db04e95bee..41b133243e1 100644 --- a/src/components/main/layout/box_.rs +++ b/src/components/main/layout/box_.rs @@ -7,25 +7,26 @@ use css::node_style::StyledNode; 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; -use layout::inline::InlineFragmentContext; +use layout::flow; +use layout::inline::{InlineFragmentContext, InlineMetrics}; use layout::model::{Auto, IntrinsicWidths, MaybeAuto, Specified, specified}; use layout::model; -use layout::util::OpaqueNodeMethods; +use layout::text; +use layout::util::{OpaqueNodeMethods, ToGfxColor}; use layout::wrapper::{TLayoutNode, ThreadSafeLayoutNode}; -use sync::Arc; use geom::{Point2D, Rect, Size2D, SideOffsets2D}; use geom::approxeq::ApproxEq; use gfx::color::rgb; use gfx::display_list::{BackgroundAndBorderLevel, BaseDisplayItem, BorderDisplayItem}; 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, TextDecorations}; -use gfx::display_list::{TextDisplayItem, TextDisplayItemClass}; +use gfx::display_list::{ContentStackingLevel, DisplayItem, DisplayList, ImageDisplayItem}; +use gfx::display_list::{ImageDisplayItemClass, LineDisplayItem}; +use gfx::display_list::{LineDisplayItemClass, OpaqueNode, PseudoDisplayItemClass}; +use gfx::display_list::{SolidColorDisplayItem, SolidColorDisplayItemClass, StackingLevel}; +use gfx::display_list::{TextDecorations, TextDisplayItem, TextDisplayItemClass}; use gfx::font::FontStyle; use gfx::text::text_run::TextRun; use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg, PipelineId, SubpageId}; @@ -34,17 +35,18 @@ use servo_util::geometry::Au; use servo_util::geometry; use servo_util::range::*; use servo_util::namespace; -use servo_util::smallvec::{SmallVec, SmallVec0}; +use servo_util::smallvec::SmallVec; use servo_util::str::is_whitespace; use std::cast; use std::from_str::FromStr; use std::iter::AdditiveIterator; +use std::mem; use std::num::Zero; use style::{ComputedValues, TElement, TNode, cascade, initial_values}; 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 style::computed_values::{background_repeat, border_style, clear, position, text_align}; +use style::computed_values::{text_decoration, vertical_align, visibility, white_space}; +use sync::Arc; use url::Url; /// Boxes (`struct Box`) are the leaves of the layout tree. They cannot position themselves. In @@ -434,13 +436,7 @@ impl Box { } pub fn calculate_line_height(&self, font_size: Au) -> Au { - let from_inline = match self.style().InheritedBox.get().line_height { - line_height::Normal => font_size.scale_by(1.14), - line_height::Number(l) => font_size.scale_by(l), - line_height::Length(l) => l - }; - let minimum = self.style().InheritedBox.get()._servo_minimum_line_height; - Au::max(from_inline, minimum) + text::line_height_from_style(self.style(), font_size) } /// Returns the sum of the widths of all the borders of this fragment. This is private because @@ -575,32 +571,9 @@ impl Box { } } - /// Converts this node's computed style to a font style used for rendering. - /// - /// FIXME(pcwalton): This should not be necessary; just make the font part of style sharable - /// with the display list somehow. (Perhaps we should use an ARC.) + /// Converts this fragment's computed style to a font style used for rendering. pub fn font_style(&self) -> FontStyle { - let my_style = self.style(); - - debug!("(font style) start"); - - // FIXME: Too much allocation here. - let font_families = my_style.Font.get().font_family.iter().map(|family| { - match *family { - font_family::FamilyName(ref name) => (*name).clone(), - } - }).collect(); - debug!("(font style) font families: `{:?}`", font_families); - - let font_size = my_style.Font.get().font_size.to_f64().unwrap() / 60.0; - debug!("(font style) font size: `{:f}px`", font_size); - - FontStyle { - pt_size: font_size, - weight: my_style.Font.get().font_weight, - style: my_style.Font.get().font_style, - families: font_families, - } + text::computed_style_to_font_style(self.style()) } #[inline(always)] @@ -658,7 +631,8 @@ impl Box { /// necessary. pub fn build_display_list_for_background_if_applicable(&self, list: &mut DisplayList, - builder: &DisplayListBuilder, + layout_context: &LayoutContext, + level: StackingLevel, absolute_bounds: &Rect<Au>) { // 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 @@ -668,10 +642,7 @@ impl Box { let background_color = style.resolve_color(style.Background.get().background_color); if !background_color.alpha.approx_eq(&0.0) { let display_item = ~SolidColorDisplayItem { - base: BaseDisplayItem { - bounds: *absolute_bounds, - node: self.node, - }, + base: BaseDisplayItem::new(*absolute_bounds, self.node, level), color: background_color.to_gfx_color(), }; @@ -681,93 +652,84 @@ impl Box { // The background image is painted on top of the background color. // Implements background image, per spec: // http://www.w3.org/TR/CSS21/colors.html#background - match style.Background.get().background_image { - Some(ref image_url) => { - let mut holder = ImageHolder::new(image_url.clone(), - builder.ctx.image_cache.clone()); - match holder.get_image() { - Some(image) => { - debug!("(building display list) building background image"); - - // Adjust bounds for `background-position` and `background-attachment`. - let mut bounds = *absolute_bounds; - let horizontal_position = model::specified( - style.Background.get().background_position.horizontal, - bounds.size.width); - let vertical_position = model::specified( - style.Background.get().background_position.vertical, - bounds.size.height); - - let clip_display_item; - match style.Background.get().background_attachment { - background_attachment::scroll => { - clip_display_item = None; - bounds.origin.x = bounds.origin.x + horizontal_position; - bounds.origin.y = bounds.origin.y + vertical_position; - bounds.size.width = bounds.size.width - horizontal_position; - bounds.size.height = bounds.size.height - vertical_position; - } - background_attachment::fixed => { - clip_display_item = Some(~ClipDisplayItem { - base: BaseDisplayItem { - bounds: bounds, - node: self.node, - }, - child_list: SmallVec0::new(), - need_clip: true, - }); - - bounds = Rect { - origin: Point2D(horizontal_position, vertical_position), - size: Size2D(bounds.origin.x + bounds.size.width, - bounds.origin.y + bounds.size.height), - } - } - } - // Adjust sizes for `background-repeat`. - match style.Background.get().background_repeat { - background_repeat::no_repeat => { - bounds.size.width = Au::from_px(image.width as int); - bounds.size.height = Au::from_px(image.height as int) - } - background_repeat::repeat_x => { - bounds.size.height = Au::from_px(image.height as int) - } - background_repeat::repeat_y => { - bounds.size.width = Au::from_px(image.width as int) - } - background_repeat::repeat => {} - }; - - - // Create the image display item. - let image_display_item = ImageDisplayItemClass(~ImageDisplayItem { - base: BaseDisplayItem { - bounds: bounds, - node: self.node, - }, - image: image.clone(), - stretch_size: Size2D(Au::from_px(image.width as int), - Au::from_px(image.height as int)), - }); - - match clip_display_item { - None => list.push(image_display_item), - Some(mut clip_display_item) => { - clip_display_item.child_list.push(image_display_item); - list.push(ClipDisplayItemClass(clip_display_item)) - } - } - } - None => { - // No image data at all? Do nothing. - // - // TODO: Add some kind of placeholder background image. - debug!("(building display list) no background image :("); - } + let background = style.Background.get(); + let image_url = match background.background_image { + None => return, + Some(ref image_url) => image_url, + }; + + let mut holder = ImageHolder::new(image_url.clone(), layout_context.image_cache.clone()); + let image = match holder.get_image() { + None => { + // No image data at all? Do nothing. + // + // TODO: Add some kind of placeholder background image. + debug!("(building display list) no background image :("); + return + } + Some(image) => image, + }; + debug!("(building display list) building background image"); + + // Adjust bounds for `background-position` and `background-attachment`. + let mut bounds = *absolute_bounds; + let horizontal_position = model::specified(background.background_position.horizontal, + bounds.size.width); + let vertical_position = model::specified(background.background_position.vertical, + bounds.size.height); + + let clip_display_item; + match background.background_attachment { + background_attachment::scroll => { + clip_display_item = None; + bounds.origin.x = bounds.origin.x + horizontal_position; + bounds.origin.y = bounds.origin.y + vertical_position; + bounds.size.width = bounds.size.width - horizontal_position; + bounds.size.height = bounds.size.height - vertical_position; + } + background_attachment::fixed => { + clip_display_item = Some(~ClipDisplayItem { + base: BaseDisplayItem::new(bounds, self.node, level), + children: DisplayList::new(), + }); + + bounds = Rect { + origin: Point2D(horizontal_position, vertical_position), + size: Size2D(bounds.origin.x + bounds.size.width, + bounds.origin.y + bounds.size.height), } } - None => {} + } + + // Adjust sizes for `background-repeat`. + match background.background_repeat { + background_repeat::no_repeat => { + bounds.size.width = Au::from_px(image.width as int); + bounds.size.height = Au::from_px(image.height as int) + } + background_repeat::repeat_x => { + bounds.size.height = Au::from_px(image.height as int) + } + background_repeat::repeat_y => { + bounds.size.width = Au::from_px(image.width as int) + } + background_repeat::repeat => {} + }; + + // Create the image display item. + let image_display_item = ImageDisplayItemClass(~ImageDisplayItem { + base: BaseDisplayItem::new(bounds, self.node, level), + image: image.clone(), + stretch_size: Size2D(Au::from_px(image.width as int), + Au::from_px(image.height as int)), + }); + + match clip_display_item { + None => list.push(image_display_item), + Some(mut clip_display_item) => { + clip_display_item.children.push(image_display_item); + list.push(ClipDisplayItemClass(clip_display_item)) + } } } @@ -776,6 +738,7 @@ impl Box { pub fn build_display_list_for_borders_if_applicable(&self, list: &mut DisplayList, abs_bounds: &Rect<Au>, + level: StackingLevel, inline_fragment_context: Option<InlineFragmentContext>) { // Fast path. @@ -792,10 +755,7 @@ impl Box { // Append the border to the display list. let border_display_item = ~BorderDisplayItem { - base: BaseDisplayItem { - bounds: *abs_bounds, - node: self.node, - }, + base: BaseDisplayItem::new(*abs_bounds, self.node, level), border: border, color: SideOffsets2D::new(top_color.to_gfx_color(), right_color.to_gfx_color(), @@ -811,7 +771,7 @@ impl Box { } fn build_debug_borders_around_text_boxes(&self, - stacking_context: &mut StackingContext, + display_list: &mut DisplayList, flow_origin: Point2D<Au>, text_box: &ScannedTextBoxInfo) { let box_bounds = self.border_box; @@ -821,16 +781,12 @@ impl Box { let debug_border = SideOffsets2D::new_all_same(Au::from_px(1)); let border_display_item = ~BorderDisplayItem { - base: BaseDisplayItem { - bounds: absolute_box_bounds, - node: self.node, - }, + base: BaseDisplayItem::new(absolute_box_bounds, self.node, ContentStackingLevel), border: debug_border, color: SideOffsets2D::new_all_same(rgb(0, 0, 200)), style: SideOffsets2D::new_all_same(border_style::solid) - }; - stacking_context.content.push(BorderDisplayItemClass(border_display_item)); + display_list.push(BorderDisplayItemClass(border_display_item)); // Draw a rectangle representing the baselines. let ascent = text_box.run.metrics_for_range(&text_box.range).ascent; @@ -838,19 +794,15 @@ impl Box { Size2D(absolute_box_bounds.size.width, Au(0))); let line_display_item = ~LineDisplayItem { - base: BaseDisplayItem { - bounds: baseline, - node: self.node, - }, + base: BaseDisplayItem::new(baseline, self.node, ContentStackingLevel), color: rgb(0, 200, 0), style: border_style::dashed, - }; - stacking_context.content.push(LineDisplayItemClass(line_display_item)) + display_list.push(LineDisplayItemClass(line_display_item)); } fn build_debug_borders_around_box(&self, - stacking_context: &mut StackingContext, + display_list: &mut DisplayList, flow_origin: Point2D<Au>) { let box_bounds = self.border_box; let absolute_box_bounds = box_bounds.translate(&flow_origin); @@ -859,34 +811,30 @@ impl Box { let debug_border = SideOffsets2D::new_all_same(Au::from_px(1)); let border_display_item = ~BorderDisplayItem { - base: BaseDisplayItem { - bounds: absolute_box_bounds, - node: self.node, - }, + base: BaseDisplayItem::new(absolute_box_bounds, self.node, ContentStackingLevel), border: debug_border, color: SideOffsets2D::new_all_same(rgb(0, 0, 200)), style: SideOffsets2D::new_all_same(border_style::solid) - }; - stacking_context.content.push(BorderDisplayItemClass(border_display_item)) + display_list.push(BorderDisplayItemClass(border_display_item)) } /// Adds the display items for this box to the given stacking context. /// /// Arguments: /// - /// * `stacking_context`: The stacking context to add display items to. - /// * `builder`: The display list builder, which manages the coordinate system and options. + /// * `display_list`: The unflattened display list to add display items to. + /// * `layout_context`: The layout context. /// * `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. - pub fn build_display_list(&mut self, - stacking_context: &mut StackingContext, - builder: &DisplayListBuilder, - _: &DisplayListBuildingInfo, + pub fn build_display_list(&self, + display_list: &mut DisplayList, + layout_context: &LayoutContext, flow_origin: Point2D<Au>, background_and_border_level: BackgroundAndBorderLevel, - inline_fragment_context: Option<InlineFragmentContext>) { + inline_fragment_context: Option<InlineFragmentContext>) + -> ChildDisplayListAccumulator { // Box position wrt to the owning flow. let box_bounds = self.border_box; let absolute_box_bounds = box_bounds.translate(&flow_origin); @@ -894,36 +842,49 @@ impl Box { box_bounds, absolute_box_bounds, self.debug_str()); - debug!("Box::build_display_list: dirty={}, flow_origin={}", builder.dirty, flow_origin); - + debug!("Box::build_display_list: dirty={}, flow_origin={}", + layout_context.dirty, + flow_origin); + + let mut accumulator = ChildDisplayListAccumulator::new(self.style(), + absolute_box_bounds, + self.node, + ContentStackingLevel); if self.style().InheritedBox.get().visibility != visibility::visible { - return + return accumulator } - if !absolute_box_bounds.intersects(&builder.dirty) { + if !absolute_box_bounds.intersects(&layout_context.dirty) { debug!("Box::build_display_list: Did not intersect..."); - return + return accumulator } debug!("Box::build_display_list: intersected. Adding display item..."); { - let list = - stacking_context.list_for_background_and_border_level(background_and_border_level); + let level = + StackingLevel::from_background_and_border_level(background_and_border_level); + + // Add a pseudo-display item for content box queries. This is a very bogus thing to do. + let base_display_item = ~BaseDisplayItem::new(absolute_box_bounds, self.node, level); + display_list.push(PseudoDisplayItemClass(base_display_item)); // Add the background to the list, if applicable. - self.build_display_list_for_background_if_applicable(list, - builder, + self.build_display_list_for_background_if_applicable(display_list, + layout_context, + level, &absolute_box_bounds); // Add a border, if applicable. // // TODO: Outlines. - self.build_display_list_for_borders_if_applicable(list, + self.build_display_list_for_borders_if_applicable(display_list, &absolute_box_bounds, + level, inline_fragment_context); } + // Add a clip, if applicable. match self.specific { UnscannedTextBox(_) => fail!("Shouldn't see unscanned boxes here."), TableColumnBox(_) => fail!("Shouldn't see table column boxes here."), @@ -949,41 +910,27 @@ impl Box { // Create the text box. let text_display_item = ~TextDisplayItem { - base: BaseDisplayItem { - bounds: bounds, - node: self.node, - }, + base: BaseDisplayItem::new(bounds, self.node, ContentStackingLevel), text_run: text_box.run.clone(), range: text_box.range, text_color: text_color, text_decorations: text_decorations, }; - - stacking_context.content.push(TextDisplayItemClass(text_display_item)); + accumulator.push(display_list, TextDisplayItemClass(text_display_item)); // Draw debug frames for text bounds. // // 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, + debug!("{:?}", self.build_debug_borders_around_text_boxes(display_list, flow_origin, text_box)) }, GenericBox | IframeBox(..) | TableBox | TableCellBox | TableRowBox | TableWrapperBox => { - let item = ~ClipDisplayItem { - base: BaseDisplayItem { - bounds: absolute_box_bounds, - node: self.node, - }, - child_list: SmallVec0::new(), - need_clip: self.needs_clip() - }; - stacking_context.content.push(ClipDisplayItemClass(item)); - // 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)) + debug!("{:?}", self.build_debug_borders_around_box(display_list, flow_origin)) }, ImageBox(_) => { let mut bounds = absolute_box_bounds.clone(); @@ -993,23 +940,22 @@ impl Box { bounds.size.height = bounds.size.height - self.border_padding.vertical(); match self.specific { - ImageBox(ref mut image_box) => { - let image_ref = &mut image_box.image; - match image_ref.get_image() { + ImageBox(ref image_box) => { + let image_ref = &image_box.image; + match image_ref.get_image_if_present() { 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, - }, + base: BaseDisplayItem::new(bounds, + self.node, + ContentStackingLevel), image: image.clone(), stretch_size: bounds.size, }; - stacking_context.content - .push(ImageDisplayItemClass(image_display_item)) + accumulator.push(display_list, + ImageDisplayItemClass(image_display_item)) } None => { // No image data at all? Do nothing. @@ -1024,7 +970,7 @@ impl Box { // 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)) + debug!("{:?}", self.build_debug_borders_around_box(display_list, flow_origin)) } } @@ -1040,10 +986,12 @@ impl Box { // iframe is actually going to be displayed. match self.specific { IframeBox(ref iframe_box) => { - self.finalize_position_and_size_of_iframe(iframe_box, flow_origin, builder.ctx) + self.finalize_position_and_size_of_iframe(iframe_box, flow_origin, layout_context) } _ => {} } + + accumulator } /// Returns the intrinsic widths of this fragment. @@ -1120,6 +1068,9 @@ impl Box { } ScannedTextBox(ref text_box_info) => { // Compute the height based on the line-height and font size. + // + // FIXME(pcwalton): Shouldn't we use the value of the `font-size` property below + // instead of the bounding box of the text run? let (range, run) = (&text_box_info.range, &text_box_info.run); let text_bounds = run.metrics_for_range(range).bounding_box; let em_size = text_bounds.size.height; @@ -1401,6 +1352,34 @@ impl Box { } } + /// Calculates height above baseline, depth below baseline, and ascent for this fragment when + /// used in an inline formatting context. See CSS 2.1 § 10.8.1. + pub fn inline_metrics(&self) -> InlineMetrics { + match self.specific { + ImageBox(ref image_box_info) => { + let computed_height = image_box_info.computed_height(); + InlineMetrics { + height_above_baseline: computed_height + self.border_padding.vertical(), + depth_below_baseline: Au(0), + ascent: computed_height + self.border_padding.bottom, + } + } + ScannedTextBox(ref text_box) => { + // See CSS 2.1 § 10.8.1. + let font_size = self.style().Font.get().font_size; + let line_height = self.calculate_line_height(font_size); + InlineMetrics::from_font_metrics(&text_box.run.font_metrics, line_height) + } + _ => { + InlineMetrics { + height_above_baseline: self.border_box.size.height, + depth_below_baseline: Au(0), + ascent: self.border_box.size.height, + } + } + } + } + /// Returns true if this box can merge with another adjacent box or false otherwise. pub fn can_merge_with_box(&self, other: &Box) -> bool { match (&self.specific, &other.specific) { @@ -1484,3 +1463,60 @@ impl Box { chan.send(msg) } } + +/// An object that accumulates display lists of child flows, applying a clipping rect if necessary. +pub struct ChildDisplayListAccumulator { + clip_display_item: Option<~ClipDisplayItem>, +} + +impl ChildDisplayListAccumulator { + /// Creates a `ChildDisplayListAccumulator` from the `overflow` property in the given style. + fn new(style: &ComputedValues, bounds: Rect<Au>, node: OpaqueNode, level: StackingLevel) + -> ChildDisplayListAccumulator { + ChildDisplayListAccumulator { + clip_display_item: match style.Box.get().overflow { + overflow::hidden => { + Some(~ClipDisplayItem { + base: BaseDisplayItem::new(bounds, node, level), + children: DisplayList::new(), + }) + } + _ => None, + } + } + } + + /// Pushes the given display item onto this display list. + pub fn push(&mut self, parent_display_list: &mut DisplayList, item: DisplayItem) { + match self.clip_display_item { + None => parent_display_list.push(item), + Some(ref mut clip_display_item) => clip_display_item.children.push(item), + } + } + + /// Pushes the display items from the given child onto this display list. + pub fn push_child(&mut self, parent_display_list: &mut DisplayList, child: &mut Flow) { + let kid_display_list = mem::replace(&mut flow::mut_base(child).display_list, + DisplayList::new()); + match self.clip_display_item { + None => parent_display_list.push_all_move(kid_display_list), + Some(ref mut clip_display_item) => { + clip_display_item.children.push_all_move(kid_display_list) + } + } + } + + /// Consumes this accumulator and pushes the clipping item, if any, onto the display list + /// associated with the given flow, along with the items in the given display list. + pub fn finish(self, parent: &mut Flow, mut display_list: DisplayList) { + let ChildDisplayListAccumulator { + clip_display_item + } = self; + match clip_display_item { + None => {} + Some(clip_display_item) => display_list.push(ClipDisplayItemClass(clip_display_item)), + } + flow::mut_base(parent).display_list = display_list + } +} + diff --git a/src/components/main/layout/construct.rs b/src/components/main/layout/construct.rs index 92059e44d18..72ce8e09b97 100644 --- a/src/components/main/layout/construct.rs +++ b/src/components/main/layout/construct.rs @@ -396,7 +396,9 @@ impl<'a> FlowConstructor<'a> { } } - let mut inline_flow = ~InlineFlow::from_boxes((*node).clone(), boxes) as ~Flow:Share; + let mut inline_flow = ~InlineFlow::from_boxes((*node).clone(), boxes); + inline_flow.compute_minimum_ascent_and_descent(self.font_context(), &**node.style()); + let mut inline_flow = inline_flow as ~Flow:Share; TextRunScanner::new().scan_for_runs(self.font_context(), inline_flow); inline_flow.finish(self.layout_context); diff --git a/src/components/main/layout/context.rs b/src/components/main/layout/context.rs index 8b7db32538a..3910de1a3ac 100644 --- a/src/components/main/layout/context.rs +++ b/src/components/main/layout/context.rs @@ -6,6 +6,7 @@ use css::matching::{ApplicableDeclarationsCache, StyleSharingCandidateCache}; +use geom::rect::Rect; use geom::size::Size2D; use gfx::display_list::OpaqueNode; use gfx::font_context::{FontContext, FontContextInfo}; @@ -87,6 +88,9 @@ pub struct LayoutContext { /// The command line options. pub opts: Opts, + + /// The dirty rectangle, used during display list building. + pub dirty: Rect<Au>, } #[cfg(not(target_os="android"))] diff --git a/src/components/main/layout/display_list_builder.rs b/src/components/main/layout/display_list_builder.rs deleted file mode 100644 index 2a508977351..00000000000 --- a/src/components/main/layout/display_list_builder.rs +++ /dev/null @@ -1,52 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -//! Constructs display lists from boxes. - -use layout::context::LayoutContext; - -use geom::{Point2D, Rect, Size2D}; -use gfx::render_task::RenderLayer; -use gfx; -use servo_util::geometry::Au; -use servo_util::smallvec::SmallVec0; -use style; - -/// Manages the information needed to construct the display list. -pub struct DisplayListBuilder<'a> { - pub ctx: &'a LayoutContext, - - /// A list of render layers that we've built up, root layer not included. - pub layers: SmallVec0<RenderLayer>, - - /// The dirty rect. - pub dirty: Rect<Au>, -} - -/// Information needed at each step of the display list building traversal. -pub struct DisplayListBuildingInfo { - /// The size of the containing block for relatively-positioned descendants. - pub relative_containing_block_size: Size2D<Au>, - /// The position and size of the absolute containing block. - pub absolute_containing_block_position: Point2D<Au>, - /// Whether the absolute containing block forces positioned descendants to be layerized. - pub layers_needed_for_positioned_flows: bool, -} - -// -// Miscellaneous useful routines -// - -/// Allows a CSS color to be converted into a graphics color. -pub trait ToGfxColor { - /// Converts a CSS color to a graphics color. - fn to_gfx_color(&self) -> gfx::color::Color; -} - -impl ToGfxColor for style::computed_values::RGBA { - fn to_gfx_color(&self) -> gfx::color::Color { - gfx::color::rgba(self.red, self.green, self.blue, self.alpha) - } -} - diff --git a/src/components/main/layout/flow.rs b/src/components/main/layout/flow.rs index 2b1563dcf09..cda5b87e6a5 100644 --- a/src/components/main/layout/flow.rs +++ b/src/components/main/layout/flow.rs @@ -30,7 +30,6 @@ 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}; use layout::floats::Floats; use layout::flow_list::{FlowList, Link, Rawlink, FlowListIterator, MutFlowListIterator}; use layout::incremental::RestyleDamage; @@ -48,14 +47,18 @@ use layout::table_cell::TableCellFlow; use layout::wrapper::ThreadSafeLayoutNode; use collections::Deque; +use collections::dlist::DList; use geom::point::Point2D; use geom::rect::Rect; -use gfx::display_list::StackingContext; +use geom::size::Size2D; +use gfx::display_list::DisplayList; +use gfx::render_task::RenderLayer; use servo_msg::compositor_msg::LayerId; use servo_util::geometry::Au; use servo_util::smallvec::{SmallVec, SmallVec0}; use std::cast; use std::iter::Zip; +use std::num::Zero; use std::sync::atomics::Relaxed; use std::slice::MutItems; use style::computed_values::{clear, position, text_align}; @@ -74,6 +77,7 @@ pub trait Flow { /// If this is a block flow, returns the underlying object. Fails otherwise. fn as_block<'a>(&'a mut self) -> &'a mut BlockFlow { + debug!("called as_block() on a flow of type {}", self.class()); fail!("called as_block() on a non-block flow") } @@ -144,6 +148,11 @@ pub trait Flow { // Main methods /// Pass 1 of reflow: computes minimum and preferred widths. + /// + /// Recursively (bottom-up) determine the flow's minimum and preferred widths. When called on + /// this flow, all child flows have had their minimum and preferred widths set. This function + /// must decide minimum/preferred widths based on its children's widths and the dimensions of + /// any boxes it is responsible for flowing. fn bubble_widths(&mut self, _ctx: &mut LayoutContext) { fail!("bubble_widths not yet implemented") } @@ -158,9 +167,32 @@ pub trait Flow { fail!("assign_height not yet implemented") } - /// In-order version of pass 3a of reflow: computes heights with floats present. - fn assign_height_inorder(&mut self, _ctx: &mut LayoutContext) { - fail!("assign_height_inorder not yet implemented") + /// Assigns heights in-order; or, if this is a float, places the float. The default + /// implementation simply assigns heights if this flow is impacted by floats. Returns true if + /// this child was impacted by floats or false otherwise. + fn assign_height_for_inorder_child_if_necessary(&mut self, layout_context: &mut LayoutContext) + -> bool { + let impacted = base(self).flags.impacted_by_floats(); + if impacted { + self.assign_height(layout_context); + } + impacted + } + + /// Phase 4 of reflow: computes absolute positions. + fn compute_absolute_position(&mut self) { + // The default implementation is a no-op. + } + + /// Returns the direction that this flow clears floats in, if any. + fn float_clearance(&self) -> clear::T { + clear::none + } + + /// Returns true if this float is a block formatting context and false otherwise. The default + /// implementation returns false. + fn is_block_formatting_context(&self, _only_impactable_by_floats: bool) -> bool { + false } fn compute_collapsible_top_margin(&mut self, @@ -355,11 +387,8 @@ pub trait MutableFlowUtils { /// Computes the overflow region for this flow. fn store_overflow(self, _: &mut LayoutContext); - /// Builds the display lists for this flow and its descendants. - fn build_display_list(self, - stacking_context: &mut StackingContext, - builder: &mut DisplayListBuilder, - info: &DisplayListBuildingInfo); + /// Builds the display lists for this flow. + fn build_display_list(self, layout_context: &LayoutContext); /// Destroys the flow. fn destroy(self); @@ -387,6 +416,7 @@ pub trait MutableOwnedFlowUtils { fn destroy(&mut self); } +#[deriving(Eq, Show)] pub enum FlowClass { BlockFlowClass, InlineFlowClass, @@ -436,6 +466,26 @@ pub trait PostorderFlowTraversal { #[deriving(Clone)] pub struct FlowFlags(pub u8); +/// The bitmask of flags that represent the `has_left_floated_descendants` and +/// `has_right_floated_descendants` fields. +/// +/// NB: If you update this field, you must update the bitfields below. +static HAS_FLOATED_DESCENDANTS_BITMASK: u8 = 0b0000_0011; + +// Whether this flow has descendants that float left in the same block formatting context. +bitfield!(FlowFlags, has_left_floated_descendants, set_has_left_floated_descendants, 0b0000_0001) + +// Whether this flow has descendants that float right in the same block formatting context. +bitfield!(FlowFlags, has_right_floated_descendants, set_has_right_floated_descendants, 0b0000_0010) + +// Whether this flow is impacted by floats to the left in the same block formatting context (i.e. +// its height depends on some prior flows with `float: left`). +bitfield!(FlowFlags, impacted_by_left_floats, set_impacted_by_left_floats, 0b0000_0100) + +// Whether this flow is impacted by floats to the right in the same block formatting context (i.e. +// its height depends on some prior flows with `float: right`). +bitfield!(FlowFlags, impacted_by_right_floats, set_impacted_by_right_floats, 0b0000_1000) + /// The bitmask of flags that represent the text alignment field. /// /// NB: If you update this field, you must update the bitfields below. @@ -446,9 +496,6 @@ 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; -// Whether we need an in-order traversal. -bitfield!(FlowFlags, inorder, set_inorder, 0b0000_0001) - // Whether this flow contains a flow that has its own layer within the same absolute containing // block. bitfield!(FlowFlags, @@ -492,6 +539,18 @@ impl FlowFlags { let FlowFlags(pff) = parent; *self = FlowFlags(ff | (pff & TEXT_ALIGN_BITMASK)) } + + #[inline] + pub fn union_floated_descendants_flags(&mut self, other: FlowFlags) { + let FlowFlags(my_flags) = *self; + let FlowFlags(other_flags) = other; + *self = FlowFlags(my_flags | (other_flags & HAS_FLOATED_DESCENDANTS_BITMASK)) + } + + #[inline] + pub fn impacted_by_floats(&self) -> bool { + self.impacted_by_left_floats() || self.impacted_by_right_floats() + } } /// The Descendants of a flow. @@ -548,6 +607,31 @@ pub type DescendantIter<'a> = MutItems<'a, Rawlink>; pub type DescendantOffsetIter<'a> = Zip<MutItems<'a, Rawlink>, MutItems<'a, Au>>; +/// Information needed to compute absolute (i.e. viewport-relative) flow positions (not to be +/// confused with absolutely-positioned flows). +pub struct AbsolutePositionInfo { + /// The size of the containing block for relatively-positioned descendants. + pub relative_containing_block_size: Size2D<Au>, + /// The position of the absolute containing block. + pub absolute_containing_block_position: Point2D<Au>, + /// Whether the absolute containing block forces positioned descendants to be layerized. + /// + /// FIXME(pcwalton): Move into `FlowFlags`. + pub layers_needed_for_positioned_flows: bool, +} + +impl AbsolutePositionInfo { + pub fn new() -> AbsolutePositionInfo { + // FIXME(pcwalton): The initial relative containing block size should be equal to the size + // of the root layer. + AbsolutePositionInfo { + relative_containing_block_size: Size2D::zero(), + absolute_containing_block_position: Zero::zero(), + layers_needed_for_positioned_flows: false, + } + } +} + /// Data common to all flows. pub struct BaseFlow { pub restyle_damage: RestyleDamage, @@ -584,16 +668,6 @@ pub struct BaseFlow { /// The floats next to this flow. pub floats: Floats, - /// The value of this flow's `clear` property, if any. - pub clear: clear::T, - - /// For normal flows, this is the number of floated descendants that are - /// not contained within any other floated descendant of this flow. For - /// floats, it is 1. - /// It is used to allocate float data if necessary and to - /// decide whether to do an in-order traversal for assign_height. - pub num_floats: uint, - /// The collapsible margins for this flow, if any. pub collapsible_margins: CollapsibleMargins, @@ -614,6 +688,18 @@ pub struct BaseFlow { /// Reference to the Containing Block, if this flow is absolutely positioned. pub absolute_cb: ContainingBlockLink, + /// Information needed to compute absolute (i.e. viewport-relative) flow positions (not to be + /// confused with absolutely-positioned flows). + /// + /// FIXME(pcwalton): Merge with `absolute_static_x_offset` and `fixed_static_x_offset` above? + pub absolute_position_info: AbsolutePositionInfo, + + /// The unflattened display items for this flow. + pub display_list: DisplayList, + + /// Any layers that we're bubbling up, in a linked list. + pub layers: DList<RenderLayer>, + /// Whether this flow has been destroyed. /// /// TODO(pcwalton): Pack this into the flags? Need to be careful because manipulation of this @@ -650,14 +736,15 @@ impl BaseFlow { parallel: FlowParallelInfo::new(), floats: Floats::new(), - num_floats: 0, collapsible_margins: CollapsibleMargins::new(), - clear: clear::none, abs_position: Point2D(Au::new(0), Au::new(0)), abs_descendants: Descendants::new(), absolute_static_x_offset: Au::new(0), fixed_static_x_offset: Au::new(0), absolute_cb: ContainingBlockLink::new(), + display_list: DisplayList::new(), + layers: DList::new(), + absolute_position_info: AbsolutePositionInfo::new(), destroyed: false, @@ -931,48 +1018,28 @@ impl<'a> MutableFlowUtils for &'a mut Flow { /// /// Arguments: /// - /// * `stacking_context`: The parent stacking context that this flow belongs to and to which - /// display items will be added. - /// /// * `builder`: The display list builder, which contains information used during the entire /// display list building pass. /// /// * `info`: Per-flow display list building information. - fn build_display_list(self, - stacking_context: &mut StackingContext, - builder: &mut DisplayListBuilder, - info: &DisplayListBuildingInfo) { + fn build_display_list(self, layout_context: &LayoutContext) { debug!("Flow: building display list"); match self.class() { - BlockFlowClass => { - self.as_block().build_display_list_block(stacking_context, builder, info) - } - InlineFlowClass => { - self.as_inline().build_display_list_inline(stacking_context, builder, info) - } + BlockFlowClass => self.as_block().build_display_list_block(layout_context), + InlineFlowClass => self.as_inline().build_display_list_inline(layout_context), TableWrapperFlowClass => { - self.as_table_wrapper().build_display_list_table_wrapper(stacking_context, - builder, - info) - } - TableFlowClass => { - self.as_table().build_display_list_table(stacking_context, builder, info) + self.as_table_wrapper().build_display_list_table_wrapper(layout_context) } + TableFlowClass => self.as_table().build_display_list_table(layout_context), TableRowGroupFlowClass => { - self.as_table_rowgroup().build_display_list_table_rowgroup(stacking_context, - builder, - info) - } - TableRowFlowClass => { - self.as_table_row().build_display_list_table_row(stacking_context, builder, info) + self.as_table_rowgroup().build_display_list_table_rowgroup(layout_context) } + TableRowFlowClass => self.as_table_row().build_display_list_table_row(layout_context), TableCaptionFlowClass => { - self.as_table_caption().build_display_list_table_caption(stacking_context, - builder, - info) + self.as_table_caption().build_display_list_table_caption(layout_context) } TableCellFlowClass => { - self.as_table_cell().build_display_list_table_cell(stacking_context, builder, info) + self.as_table_cell().build_display_list_table_cell(layout_context) } TableColGroupFlowClass => { // Nothing to do here, as column groups don't render. @@ -1001,6 +1068,7 @@ impl MutableOwnedFlowUtils for ~Flow:Share { let base = mut_base(*self); base.children.push_back(new_child); let _ = base.parallel.children_count.fetch_add(1, Relaxed); + let _ = base.parallel.children_and_absolute_descendant_count.fetch_add(1, Relaxed); } /// Finishes a flow. Once a flow is finished, no more child flows or boxes may be added to it. @@ -1025,6 +1093,10 @@ impl MutableOwnedFlowUtils for ~Flow:Share { let self_link = Rawlink::some(*self); let block = self.as_block(); block.base.abs_descendants = abs_descendants; + block.base + .parallel + .children_and_absolute_descendant_count + .fetch_add(block.base.abs_descendants.len() as int, Relaxed); for descendant_link in block.base.abs_descendants.iter() { match descendant_link.resolve() { @@ -1066,6 +1138,10 @@ impl ContainingBlockLink { self.link = link } + pub unsafe fn resolve(&mut self) -> Option<&mut Flow> { + self.link.resolve() + } + #[inline] pub fn generated_containing_block_rect(&mut self) -> Rect<Au> { self.link.resolve().unwrap().generated_containing_block_rect() diff --git a/src/components/main/layout/inline.rs b/src/components/main/layout/inline.rs index ab4e8e43208..6d446da5125 100644 --- a/src/components/main/layout/inline.rs +++ b/src/components/main/layout/inline.rs @@ -3,21 +3,21 @@ * 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, ScannedTextBox}; -use layout::box_::{SplitDidFit, SplitDidNotFit, TableBox, TableCellBox, TableColumnBox}; -use layout::box_::{TableRowBox, TableWrapperBox, UnscannedTextBox}; +use layout::box_::{Box, CannotSplit, SplitDidFit, SplitDidNotFit}; use layout::context::LayoutContext; -use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo}; use layout::floats::{FloatLeft, Floats, PlacementInfo}; use layout::flow::{BaseFlow, FlowClass, Flow, InlineFlowClass}; use layout::flow; use layout::model::IntrinsicWidths; use layout::model; +use layout::text; use layout::wrapper::ThreadSafeLayoutNode; use collections::{Deque, RingBuf}; use geom::{Point2D, Rect, SideOffsets2D, Size2D}; -use gfx::display_list::{ContentLevel, StackingContext}; +use gfx::display_list::ContentLevel; +use gfx::font::FontMetrics; +use gfx::font_context::FontContext; use servo_util::geometry::Au; use servo_util::geometry; use servo_util::range::Range; @@ -573,6 +573,14 @@ pub struct InlineFlow { /// are the result of inline layout. This also includes some metadata used for positioning /// lines. pub lines: SmallVec0<LineBox>, + + /// The minimum height above the baseline for each line, as specified by the line height and + /// font style. + pub minimum_height_above_baseline: Au, + + /// The minimum depth below the baseline for each line, as specified by the line height and + /// font style. + pub minimum_depth_below_baseline: Au, } impl InlineFlow { @@ -581,6 +589,8 @@ impl InlineFlow { base: BaseFlow::new(node), boxes: boxes, lines: SmallVec0::new(), + minimum_height_above_baseline: Au(0), + minimum_depth_below_baseline: Au(0), } } @@ -591,12 +601,9 @@ impl InlineFlow { self.boxes = InlineBoxes::new(); } - pub fn build_display_list_inline(&mut self, - stacking_context: &mut StackingContext, - builder: &DisplayListBuilder, - info: &DisplayListBuildingInfo) { + pub fn build_display_list_inline(&mut self, layout_context: &LayoutContext) { let abs_rect = Rect(self.base.abs_position, self.base.position.size); - if !abs_rect.intersects(&builder.dirty) { + if !abs_rect.intersects(&layout_context.dirty) { return } @@ -605,14 +612,15 @@ impl InlineFlow { debug!("Flow: building display list for {:u} inline boxes", self.boxes.len()); for (fragment, context) in self.boxes.mut_iter() { - let rel_offset = fragment.relative_position(&info.relative_containing_block_size, + let rel_offset = fragment.relative_position(&self.base + .absolute_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)); + drop(fragment.build_display_list(&mut self.base.display_list, + layout_context, + self.base.abs_position + rel_offset, + ContentLevel, + Some(context))); } // TODO(#225): Should `inline-block` elements have flows as children of the inline flow or @@ -621,71 +629,72 @@ impl InlineFlow { // For now, don't traverse the subtree rooted here. } - /// Returns the relative offset from the baseline for this box, taking into account the value - /// of the CSS `vertical-align` property. + /// Returns the distance from the baseline for the logical top left corner of this fragment, + /// taking into account the value of the CSS `vertical-align` property. Negative values mean + /// "toward the logical top" and positive values mean "toward the logical bottom". /// /// The extra boolean is set if and only if `biggest_top` and/or `biggest_bottom` were updated. /// That is, if the box has a `top` or `bottom` value, true is returned. - fn relative_offset_from_baseline(cur_box: &Box, - ascent: Au, - parent_text_top: Au, - parent_text_bottom: Au, - top_from_base: &mut Au, - bottom_from_base: &mut Au, - biggest_top: &mut Au, - biggest_bottom: &mut Au) - -> (Au, bool) { - match cur_box.vertical_align() { + fn distance_from_baseline(fragment: &Box, + ascent: Au, + parent_text_top: Au, + parent_text_bottom: Au, + height_above_baseline: &mut Au, + depth_below_baseline: &mut Au, + largest_height_for_top_fragments: &mut Au, + largest_height_for_bottom_fragments: &mut Au) + -> (Au, bool) { + match fragment.vertical_align() { vertical_align::baseline => (-ascent, false), vertical_align::middle => { // TODO: x-height value should be used from font info. - let xheight = Au::new(0); - (-(xheight + cur_box.content_height()).scale_by(0.5), false) + let xheight = Au(0); + (-(xheight + fragment.content_height()).scale_by(0.5), false) }, vertical_align::sub => { - // TODO: The proper position for subscripts should be used. - // Lower the baseline to the proper position for subscripts - let sub_offset = Au::new(0); + // TODO: The proper position for subscripts should be used. Lower the baseline to + // the proper position for subscripts. + let sub_offset = Au(0); (sub_offset - ascent, false) }, vertical_align::super_ => { - // TODO: The proper position for superscripts should be used. - // Raise the baseline to the proper position for superscripts - let super_offset = Au::new(0); + // TODO: The proper position for superscripts should be used. Raise the baseline to + // the proper position for superscripts. + let super_offset = Au(0); (-super_offset - ascent, false) }, vertical_align::text_top => { - let box_height = *top_from_base + *bottom_from_base; - let prev_bottom_from_base = *bottom_from_base; - *top_from_base = parent_text_top; - *bottom_from_base = box_height - *top_from_base; - (*bottom_from_base - prev_bottom_from_base - ascent, false) + let box_height = *height_above_baseline + *depth_below_baseline; + let prev_depth_below_baseline = *depth_below_baseline; + *height_above_baseline = parent_text_top; + *depth_below_baseline = box_height - *height_above_baseline; + (*depth_below_baseline - prev_depth_below_baseline - ascent, false) }, vertical_align::text_bottom => { - let box_height = *top_from_base + *bottom_from_base; - let prev_bottom_from_base = *bottom_from_base; - *bottom_from_base = parent_text_bottom; - *top_from_base = box_height - *bottom_from_base; - (*bottom_from_base - prev_bottom_from_base - ascent, false) + let box_height = *height_above_baseline + *depth_below_baseline; + let prev_depth_below_baseline = *depth_below_baseline; + *depth_below_baseline = parent_text_bottom; + *height_above_baseline = box_height - *depth_below_baseline; + (*depth_below_baseline - prev_depth_below_baseline - ascent, false) }, vertical_align::top => { - if *biggest_top < (*top_from_base + *bottom_from_base) { - *biggest_top = *top_from_base + *bottom_from_base; - } - let offset_top = *top_from_base - ascent; + *largest_height_for_top_fragments = + Au::max(*largest_height_for_top_fragments, + *height_above_baseline + *depth_below_baseline); + let offset_top = *height_above_baseline - ascent; (offset_top, true) }, vertical_align::bottom => { - if *biggest_bottom < (*top_from_base + *bottom_from_base) { - *biggest_bottom = *top_from_base + *bottom_from_base; - } - let offset_bottom = -(*bottom_from_base + ascent); + *largest_height_for_bottom_fragments = + Au::max(*largest_height_for_bottom_fragments, + *height_above_baseline + *depth_below_baseline); + let offset_bottom = -(*depth_below_baseline + ascent); (offset_bottom, true) }, vertical_align::Length(length) => (-(length + ascent), false), vertical_align::Percentage(p) => { - let pt_size = cur_box.font_style().pt_size; - let line_height = cur_box.calculate_line_height(Au::from_pt(pt_size)); + let pt_size = fragment.font_style().pt_size; + let line_height = fragment.calculate_line_height(Au::from_pt(pt_size)); let percent_offset = line_height.scale_by(p); (-(percent_offset + ascent), false) } @@ -718,6 +727,21 @@ impl InlineFlow { offset_x = offset_x + size.width; } } + + /// Computes the minimum ascent and descent for each line. This is done during flow + /// construction. + /// + /// `style` is the style of the block. + pub fn compute_minimum_ascent_and_descent(&mut self, + font_context: &mut FontContext, + style: &ComputedValues) { + let font_style = text::computed_style_to_font_style(style); + let font_metrics = text::font_metrics_for_style(font_context, &font_style); + let line_height = text::line_height_from_style(style, style.Font.get().font_size); + let inline_metrics = InlineMetrics::from_font_metrics(&font_metrics, line_height); + self.minimum_height_above_baseline = inline_metrics.height_above_baseline; + self.minimum_depth_below_baseline = inline_metrics.depth_below_baseline; + } } impl Flow for InlineFlow { @@ -734,12 +758,8 @@ impl Flow for InlineFlow { } fn bubble_widths(&mut self, _: &mut LayoutContext) { - let mut num_floats = 0; - for kid in self.base.child_iter() { - let child_base = flow::mut_base(kid); - num_floats += child_base.num_floats; - child_base.floats = Floats::new(); + flow::mut_base(kid).floats = Floats::new(); } let mut intrinsic_widths = IntrinsicWidths::new(); @@ -754,7 +774,6 @@ impl Flow for InlineFlow { } self.base.intrinsic_widths = intrinsic_widths; - self.base.num_floats = num_floats; } /// Recursively (top-down) determines the actual width of child contexts and boxes. When called @@ -786,16 +805,7 @@ impl Flow for InlineFlow { // 'inline-block' box that created this flow before recursing. } - fn assign_height_inorder(&mut self, ctx: &mut LayoutContext) { - for kid in self.base.child_iter() { - kid.assign_height_inorder(ctx); - } - self.assign_height(ctx); - } - - /// Calculate and set the height of this Flow. - /// - /// CSS Section 10.6.1 + /// Calculate and set the height of this flow. See CSS 2.1 § 10.6.1. fn assign_height(&mut self, _: &mut LayoutContext) { debug!("assign_height_inline: assigning height for flow"); @@ -817,85 +827,42 @@ impl Flow for InlineFlow { let scanner_floats = self.base.floats.clone(); let mut scanner = LineboxScanner::new(scanner_floats); - - // Access the linebox scanner. scanner.scan_for_lines(self); - let mut line_height_offset = Au::new(0); // All lines use text alignment of the flow. let text_align = self.base.flags.text_align(); // Now, go through each line and lay out the boxes inside. + let mut line_distance_from_flow_top = Au(0); for line in self.lines.mut_iter() { // Lay out boxes horizontally. InlineFlow::set_horizontal_box_positions(&mut self.boxes, line, text_align); - // Set the top y position of the current linebox. + // Set the top y position of the current line box. // `line_height_offset` is updated at the end of the previous loop. - line.bounds.origin.y = line.bounds.origin.y + line_height_offset; + line.bounds.origin.y = line_distance_from_flow_top; + + // Calculate the distance from the baseline to the top and bottom of the line box. + let mut largest_height_above_baseline = self.minimum_height_above_baseline; + let mut largest_depth_below_baseline = self.minimum_depth_below_baseline; - // Calculate the distance from baseline to the top and bottom of the linebox. - let (mut topmost, mut bottommost) = (Au(0), Au(0)); - // Calculate the biggest height among boxes with 'top' and 'bottom' values + // Calculate the largest height among boxes with 'top' and 'bottom' values // respectively. - let (mut biggest_top, mut biggest_bottom) = (Au(0), Au(0)); + let (mut largest_height_for_top_fragments, mut largest_height_for_bottom_fragments) = + (Au(0), Au(0)); for box_i in line.range.eachi() { - 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 { - ImageBox(_) => { - let mut height = cur_box.content_height(); - - // 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.border_padding.bottom; - let noncontent_height = top + bottom; - height = height + noncontent_height; - - let ascent = height + bottom; - (height, Au::new(0), ascent) - }, - ScannedTextBox(ref text_box) => { - let range = &text_box.range; - let run = &text_box.run; - - // Compute the height based on the line-height and font size - let text_bounds = run.metrics_for_range(range).bounding_box; - let em_size = text_bounds.size.height; - let line_height = cur_box.calculate_line_height(em_size); - - // Find the top and bottom of the content area. - // Those are used in text-top and text-bottom value of 'vertical-align' - let text_ascent = text_box.run.font_metrics.ascent; - - // 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.origin.x, Au(0))); - - (text_offset, line_height - text_offset, text_ascent) - }, - GenericBox | IframeBox(_) | TableBox | TableCellBox | TableRowBox | - TableWrapperBox => { - let height = cur_box.border_box.size.height; - (height, Au::new(0), height) - }, - TableColumnBox(_) => fail!("Table column boxes do not have height"), - UnscannedTextBox(_) => { - fail!("Unscanned text boxes should have been scanned by now.") - } - }; + let fragment = self.boxes.boxes.get_mut(box_i); - let mut top_from_base = top_from_base; - let mut bottom_from_base = bottom_from_base; + let InlineMetrics { + height_above_baseline: mut height_above_baseline, + depth_below_baseline: mut depth_below_baseline, + ascent + } = fragment.inline_metrics(); - // To calculate text-top and text-bottom value of 'vertical-align', - // we should find the top and bottom of the content area of parent box. - // The content area is defined in: - // http://www.w3.org/TR/CSS2/visudet.html#inline-non-replaced + // To calculate text-top and text-bottom value when `vertical-align` is involved, + // we should find the top and bottom of the content area of the parent box. + // "Content area" is defined in CSS 2.1 § 10.6.1. // // TODO: We should extract em-box info from the font size of the parent and // calculate the distances from the baseline to the top and the bottom of the @@ -904,74 +871,84 @@ impl Flow for InlineFlow { // We should calculate the distance from baseline to the top of parent's content // area. But for now we assume it's the font size. // - // The spec does not state which font to use. Previous versions of the code used + // CSS 2.1 does not state which font to use. Previous versions of the code used // the parent's font; this code uses the current font. - let parent_text_top = cur_box.style().Font.get().font_size; + let parent_text_top = fragment.style().Font.get().font_size; // We should calculate the distance from baseline to the bottom of the parent's // content area. But for now we assume it's zero. - let parent_text_bottom = Au::new(0); + let parent_text_bottom = Au(0); - // Calculate a relative offset from the baseline. + // Calculate the final height above the baseline for this box. // - // The no-update flag decides whether `biggest_top` and `biggest_bottom` are - // updated or not. That is, if the box has a `top` or `bottom` value, - // `no_update_flag` becomes true. - let (offset, no_update_flag) = - InlineFlow::relative_offset_from_baseline(cur_box, - ascent, - parent_text_top, - parent_text_bottom, - &mut top_from_base, - &mut bottom_from_base, - &mut biggest_top, - &mut biggest_bottom); - - // If the current box has 'top' or 'bottom' value, no_update_flag is true. - // Otherwise, topmost and bottomost are updated. - if !no_update_flag && top_from_base > topmost { - topmost = top_from_base; - } - if !no_update_flag && bottom_from_base > bottommost { - bottommost = bottom_from_base; + // The no-update flag decides whether `largest_height_for_top_fragments` and + // `largest_height_for_bottom_fragments` are to be updated or not. This will be set + // if and only if the fragment has `vertical-align` set to `top` or `bottom`. + let (distance_from_baseline, no_update_flag) = + InlineFlow::distance_from_baseline( + fragment, + ascent, + parent_text_top, + parent_text_bottom, + &mut height_above_baseline, + &mut depth_below_baseline, + &mut largest_height_for_top_fragments, + &mut largest_height_for_bottom_fragments); + + // Unless the current fragment has `vertical-align` set to `top` or `bottom`, + // `largest_height_above_baseline` and `largest_depth_below_baseline` are updated. + if !no_update_flag { + largest_height_above_baseline = Au::max(height_above_baseline, + largest_height_above_baseline); + largest_depth_below_baseline = Au::max(depth_below_baseline, + largest_depth_below_baseline); } - cur_box.border_box.origin.y = line.bounds.origin.y + offset + top; + // Temporarily use `fragment.border_box.origin.y` to mean "the distance from the + // baseline". We will assign the real value later. + fragment.border_box.origin.y = distance_from_baseline } - // Calculate the distance from baseline to the top of the biggest box with 'bottom' - // value. Then, if necessary, update the topmost. - let topmost_of_bottom = biggest_bottom - bottommost; - if topmost_of_bottom > topmost { - topmost = topmost_of_bottom; - } + // Calculate the distance from the baseline to the top of the largest box with a + // value for `bottom`. Then, if necessary, update `largest_height_above_baseline`. + largest_height_above_baseline = + Au::max(largest_height_above_baseline, + largest_height_for_bottom_fragments - largest_depth_below_baseline); - // Calculate the distance from baseline to the bottom of the biggest box with 'top' - // value. Then, if necessary, update the bottommost. - let bottommost_of_top = biggest_top - topmost; - if bottommost_of_top > bottommost { - bottommost = bottommost_of_top; - } + // Calculate the distance from baseline to the bottom of the largest box with a value + // for `top`. Then, if necessary, update `largest_depth_below_baseline`. + largest_depth_below_baseline = + Au::max(largest_depth_below_baseline, + largest_height_for_top_fragments - largest_height_above_baseline); - // Now, the baseline offset from the top of linebox is set as topmost. - let baseline_offset = topmost; + // Now, the distance from the logical top of the line box to the baseline can be + // computed as `largest_height_above_baseline`. + let baseline_distance_from_top = largest_height_above_baseline; - // All boxes' y position is updated following the new baseline offset. + // Compute the final positions in the block direction of each fragment. Recall that + // `fragment.border_box.origin.y` was set to the distance from the baseline above. for box_i in line.range.eachi() { - 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, - }; - - cur_box.border_box.origin.y = cur_box.border_box.origin.y + adjust_offset; + let fragment = self.boxes.get_mut(box_i); + match fragment.vertical_align() { + vertical_align::top => { + fragment.border_box.origin.y = fragment.border_box.origin.y + + line_distance_from_flow_top + } + vertical_align::bottom => { + fragment.border_box.origin.y = fragment.border_box.origin.y + + line_distance_from_flow_top + baseline_distance_from_top + + largest_depth_below_baseline + } + _ => { + fragment.border_box.origin.y = fragment.border_box.origin.y + + line_distance_from_flow_top + baseline_distance_from_top + } + } } - // This is used to set the top y position of the next linebox in the next loop. - line_height_offset = line_height_offset + topmost + bottommost - - line.bounds.size.height; - line.bounds.size.height = topmost + bottommost; + // This is used to set the top y position of the next line box in the next loop. + line.bounds.size.height = largest_height_above_baseline + largest_depth_below_baseline; + line_distance_from_flow_top = line_distance_from_flow_top + line.bounds.size.height; } // End of `lines.each` loop. self.base.position.size.height = @@ -998,6 +975,12 @@ impl Flow for InlineFlow { } } +struct FragmentFixupWorkItem { + style: Arc<ComputedValues>, + new_start_index: uint, + old_end_index: uint, +} + /// 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 { @@ -1028,12 +1011,6 @@ impl FragmentRange { } } -struct FragmentFixupWorkItem { - style: Arc<ComputedValues>, - new_start_index: uint, - old_end_index: uint, -} - /// The type of an iterator over fragment ranges in the fragment map. pub struct RangeIterator<'a> { iter: Items<'a,FragmentRange>, @@ -1237,3 +1214,24 @@ impl<'a> InlineFragmentContext<'a> { } } +/// Height above the baseline, depth below the baseline, and ascent for a fragment. See CSS 2.1 § +/// 10.8.1. +pub struct InlineMetrics { + pub height_above_baseline: Au, + pub depth_below_baseline: Au, + pub ascent: Au, +} + +impl InlineMetrics { + /// Calculates inline metrics from font metrics and line height per CSS 2.1 § 10.8.1. + #[inline] + pub fn from_font_metrics(font_metrics: &FontMetrics, line_height: Au) -> InlineMetrics { + let leading = line_height - (font_metrics.ascent + font_metrics.descent); + InlineMetrics { + height_above_baseline: font_metrics.ascent + leading.scale_by(0.5), + depth_below_baseline: font_metrics.descent + leading.scale_by(0.5), + ascent: font_metrics.ascent, + } + } +} + diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs index 64b833fc55b..e21cd7a52c8 100644 --- a/src/components/main/layout/layout_task.rs +++ b/src/components/main/layout/layout_task.rs @@ -11,21 +11,21 @@ use css::select::new_stylist; use css::node_style::StyledNode; use layout::construct::{FlowConstructionResult, NoConstructionResult}; use layout::context::LayoutContext; -use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo, ToGfxColor}; use layout::flow::{Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils}; use layout::flow::{PreorderFlowTraversal, PostorderFlowTraversal}; use layout::flow; use layout::incremental::RestyleDamage; use layout::parallel::PaddedUnsafeFlow; use layout::parallel; -use layout::util::{LayoutDataAccess, LayoutDataWrapper, OpaqueNodeMethods}; +use layout::util::{LayoutDataAccess, LayoutDataWrapper, OpaqueNodeMethods, ToGfxColor}; use layout::wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode}; +use collections::dlist::DList; use geom::point::Point2D; use geom::rect::Rect; use geom::size::Size2D; -use gfx::display_list::{ClipDisplayItemClass, DisplayItem, DisplayItemIterator, DisplayList}; -use gfx::display_list::{OpaqueNode, StackingContext}; +use gfx::display_list::{ClipDisplayItemClass, ContentStackingLevel, DisplayItem}; +use gfx::display_list::{DisplayItemIterator, DisplayList, OpaqueNode}; use gfx::font_context::{FontContext, FontContextInfo}; use gfx::render_task::{RenderMsg, RenderChan, RenderLayer}; use gfx::{render_task, color}; @@ -48,7 +48,7 @@ use servo_net::local_image_cache::{ImageResponder, LocalImageCache}; use servo_util::geometry::Au; use servo_util::geometry; use servo_util::opts::Opts; -use servo_util::smallvec::{SmallVec, SmallVec0, SmallVec1}; +use servo_util::smallvec::{SmallVec, SmallVec1}; use servo_util::time::{ProfilerChan, profile}; use servo_util::time; use servo_util::task::send_on_failure; @@ -107,7 +107,11 @@ pub struct LayoutTask { /// The channel on which messages can be sent to the profiler. pub profiler_chan: ProfilerChan, - pub opts: Opts + /// The command-line options. + pub opts: Opts, + + /// The dirty rect. Used during display list construction. + pub dirty: Rect<Au>, } /// The damage computation traversal. @@ -225,7 +229,31 @@ impl<'a> PostorderFlowTraversal for AssignHeightsAndStoreOverflowTraversal<'a> { #[inline] fn should_process(&mut self, flow: &mut Flow) -> bool { - !flow::base(flow).flags.inorder() + !flow::base(flow).flags.impacted_by_floats() + } +} + +/// The display list construction traversal. +pub struct BuildDisplayListTraversal<'a> { + layout_context: &'a LayoutContext, +} + +impl<'a> BuildDisplayListTraversal<'a> { + #[inline] + fn process(&mut self, flow: &mut Flow) { + flow.compute_absolute_position(); + + for kid in flow::mut_base(flow).child_iter() { + if !kid.is_absolutely_positioned() { + self.process(kid) + } + } + + for absolute_descendant_link in flow::mut_base(flow).abs_descendants.iter() { + self.process(absolute_descendant_link.resolve().unwrap()) + } + + flow.build_display_list(self.layout_context) } } @@ -319,7 +347,8 @@ impl LayoutTask { initial_css_values: Arc::new(style::initial_values()), parallel_traversal: parallel_traversal, profiler_chan: profiler_chan, - opts: opts.clone() + opts: opts.clone(), + dirty: Rect::zero(), } } @@ -349,6 +378,7 @@ impl LayoutTask { url: (*url).clone(), reflow_root: OpaqueNodeMethods::from_layout_node(reflow_root), opts: self.opts.clone(), + dirty: Rect::zero(), } } @@ -634,23 +664,26 @@ impl LayoutTask { // Build the display list if necessary, and send it to the renderer. if data.goal == ReflowForDisplay { profile(time::LayoutDispListBuildCategory, self.profiler_chan.clone(), || { - let mut root_stacking_context = StackingContext::new(); - let mut display_list_builder = DisplayListBuilder { - ctx: &layout_ctx, - layers: SmallVec0::new(), - dirty: flow::base(layout_root).position.clone(), - }; - let display_list_building_info = DisplayListBuildingInfo { - relative_containing_block_size: flow::base(layout_root).position.size, - absolute_containing_block_position: Point2D(Au(0), Au(0)), - layers_needed_for_positioned_flows: false, - }; + layout_ctx.dirty = flow::base(layout_root).position.clone(); - layout_root.build_display_list(&mut root_stacking_context, - &mut display_list_builder, - &display_list_building_info); + match self.parallel_traversal { + None => { + let mut traversal = BuildDisplayListTraversal { + layout_context: &layout_ctx, + }; + traversal.process(layout_root); + } + Some(ref mut traversal) => { + parallel::build_display_list_for_subtree(&mut layout_root, + self.profiler_chan.clone(), + &mut layout_ctx, + traversal); + } + } - let display_list = Arc::new(root_stacking_context.flatten()); + let root_display_list = mem::replace(&mut flow::mut_base(layout_root).display_list, + DisplayList::new()); + let display_list = Arc::new(root_display_list.flatten(ContentStackingLevel)); // FIXME(pcwalton): This is really ugly and can't handle overflow: scroll. Refactor // it with extreme prejudice. @@ -677,12 +710,9 @@ impl LayoutTask { } } - let root_size = Size2D(display_list_building_info.relative_containing_block_size - .width - .to_nearest_px() as uint, - display_list_building_info.relative_containing_block_size - .height - .to_nearest_px() as uint); + let root_size = flow::base(layout_root).position.size; + let root_size = Size2D(root_size.width.to_nearest_px() as uint, + root_size.height.to_nearest_px() as uint); let render_layer = RenderLayer { id: layout_root.layer_id(0), display_list: display_list.clone(), @@ -693,13 +723,15 @@ impl LayoutTask { self.display_list = Some(display_list.clone()); + // TODO(pcwalton): Eventually, when we have incremental reflow, this will have to + // be smarter in order to handle retained layer contents properly from reflow to + // reflow. let mut layers = SmallVec1::new(); layers.push(render_layer); - let DisplayListBuilder { - layers: sublayers, - .. - } = display_list_builder; - layers.push_all_move(sublayers); + for layer in mem::replace(&mut flow::mut_base(layout_root).layers, + DList::new()).move_iter() { + layers.push(layer) + } debug!("Layout done!"); @@ -726,7 +758,6 @@ impl LayoutTask { // need to compare nodes for equality. Thus we can safely work only with `OpaqueNode`. ContentBoxQuery(node, reply_chan) => { let node: OpaqueNode = OpaqueNodeMethods::from_script_node(node); - fn union_boxes_for_node(accumulator: &mut Option<Rect<Au>>, mut iter: DisplayItemIterator, node: OpaqueNode) { @@ -774,28 +805,22 @@ impl LayoutTask { reply_chan.send(ContentBoxesResponse(boxes)) } HitTestQuery(_, point, reply_chan) => { - fn hit_test(x: Au, y: Au, list: &[DisplayItem]) + fn hit_test<'a,I:Iterator<&'a DisplayItem>>(x: Au, y: Au, mut iterator: I) -> Option<HitTestResponse> { - for item in list.rev_iter() { + for item in iterator { match *item { ClipDisplayItemClass(ref cc) => { - if !cc.need_clip || geometry::rect_contains_point(cc.base.bounds, - Point2D(x, y)) { - let ret = hit_test(x, y, cc.child_list.as_slice()); + if geometry::rect_contains_point(cc.base.bounds, Point2D(x, y)) { + let ret = hit_test(x, y, cc.children.list.rev_iter()); if !ret.is_none() { return ret } } + continue } _ => {} } - } - for item in list.rev_iter() { - match *item { - ClipDisplayItemClass(_) => continue, - _ => {} - } let bounds = item.bounds(); // TODO(tikue): This check should really be performed by a method of @@ -816,7 +841,7 @@ impl LayoutTask { Au::from_frac_px(point.y as f64)); let resp = match self.display_list { None => fail!("no display list!"), - Some(ref display_list) => hit_test(x, y, display_list.list.as_slice()), + Some(ref display_list) => hit_test(x, y, display_list.list.rev_iter()), }; if resp.is_some() { reply_chan.send(Ok(resp.unwrap())); @@ -826,44 +851,43 @@ impl LayoutTask { } MouseOverQuery(_, point, reply_chan) => { - fn mouse_over_test(x: Au, + fn mouse_over_test<'a, + I:Iterator<&'a DisplayItem>>( + x: Au, y: Au, - list: &[DisplayItem], + mut iterator: I, result: &mut Vec<UntrustedNodeAddress>) { - for item in list.rev_iter() { + for item in iterator { match *item { ClipDisplayItemClass(ref cc) => { - mouse_over_test(x, y, cc.child_list.as_slice(), result); + mouse_over_test(x, y, cc.children.list.rev_iter(), result); + } + _ => { + let bounds = item.bounds(); + + // TODO(tikue): This check should really be performed by a method + // of DisplayItem. + if x < bounds.origin.x + bounds.size.width && + bounds.origin.x <= x && + y < bounds.origin.y + bounds.size.height && + bounds.origin.y <= y { + result.push(item.base() + .node + .to_untrusted_node_address()); + } } - _ => {} - } - } - - for item in list.rev_iter() { - let bounds = item.bounds(); - - // TODO(tikue): This check should really be performed by a method of - // DisplayItem. - if x < bounds.origin.x + bounds.size.width && - bounds.origin.x <= x && - y < bounds.origin.y + bounds.size.height && - bounds.origin.y <= y { - result.push(item.base() - .node - .to_untrusted_node_address()); } } } let mut mouse_over_list: Vec<UntrustedNodeAddress> = vec!(); - let (x, y) = (Au::from_frac_px(point.x as f64), - Au::from_frac_px(point.y as f64)); + let (x, y) = (Au::from_frac_px(point.x as f64), Au::from_frac_px(point.y as f64)); match self.display_list { None => fail!("no display list!"), Some(ref display_list) => { mouse_over_test(x, y, - display_list.list.as_slice(), + display_list.list.rev_iter(), &mut mouse_over_list); } }; diff --git a/src/components/main/layout/parallel.rs b/src/components/main/layout/parallel.rs index b3d8ae08594..983ba09d4d5 100644 --- a/src/components/main/layout/parallel.rs +++ b/src/components/main/layout/parallel.rs @@ -10,7 +10,7 @@ use css::matching::{ApplicableDeclarations, CannotShare, MatchMethods, StyleWasS use layout::construct::FlowConstructor; use layout::context::LayoutContext; use layout::extra::LayoutAuxMethods; -use layout::flow::{Flow, PreorderFlowTraversal, PostorderFlowTraversal}; +use layout::flow::{Flow, MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal}; use layout::flow; use layout::layout_task::{AssignHeightsAndStoreOverflowTraversal, AssignWidthsTraversal}; use layout::layout_task::{BubbleWidthsTraversal}; @@ -105,6 +105,8 @@ impl DomParallelInfo { pub struct FlowParallelInfo { /// The number of children that still need work done. pub children_count: AtomicInt, + /// The number of children and absolute descendants that still need work done. + pub children_and_absolute_descendant_count: AtomicInt, /// The address of the parent flow. pub parent: UnsafeFlow, } @@ -113,6 +115,7 @@ impl FlowParallelInfo { pub fn new() -> FlowParallelInfo { FlowParallelInfo { children_count: AtomicInt::new(0), + children_and_absolute_descendant_count: AtomicInt::new(0), parent: null_unsafe_flow(), } } @@ -180,6 +183,7 @@ trait ParallelPreorderFlowTraversal : PreorderFlowTraversal { unsafe_flow: UnsafeFlow, proxy: &mut WorkerProxy<*mut LayoutContext,PaddedUnsafeFlow>); + #[inline(always)] fn run_parallel_helper(&mut self, unsafe_flow: UnsafeFlow, proxy: &mut WorkerProxy<*mut LayoutContext,PaddedUnsafeFlow>, @@ -405,6 +409,117 @@ fn assign_heights_and_store_overflow(unsafe_flow: PaddedUnsafeFlow, assign_heights_traversal.run_parallel(unsafe_flow.to_flow(), proxy) } +fn compute_absolute_position(unsafe_flow: PaddedUnsafeFlow, + proxy: &mut WorkerProxy<*mut LayoutContext,PaddedUnsafeFlow>) { + let mut had_descendants = false; + unsafe { + // Get a real flow. + let flow: &mut ~Flow:Share = cast::transmute(&unsafe_flow); + + // Compute the absolute position for the flow. + flow.compute_absolute_position(); + + // Count the number of absolutely-positioned children, so that we can subtract it from + // from `children_and_absolute_descendant_count` to get the number of real children. + let mut absolutely_positioned_child_count = 0; + for kid in flow::child_iter(*flow) { + if kid.is_absolutely_positioned() { + absolutely_positioned_child_count += 1; + } + } + + // Don't enqueue absolutely positioned children. + drop(flow::mut_base(*flow).parallel + .children_and_absolute_descendant_count + .fetch_sub(absolutely_positioned_child_count as int, SeqCst)); + + // Possibly enqueue the children. + for kid in flow::child_iter(*flow) { + if !kid.is_absolutely_positioned() { + had_descendants = true; + proxy.push(WorkUnit { + fun: compute_absolute_position, + data: UnsafeFlowConversions::from_flow(&borrowed_flow_to_unsafe_flow(kid)), + }); + } + } + + // Possibly enqueue absolute descendants. + for absolute_descendant_link in flow::mut_base(*flow).abs_descendants.iter() { + had_descendants = true; + let descendant = absolute_descendant_link.resolve().unwrap(); + proxy.push(WorkUnit { + fun: compute_absolute_position, + data: UnsafeFlowConversions::from_flow(&borrowed_flow_to_unsafe_flow(descendant)), + }); + } + + // If there were no more descendants, start building the display list. + if !had_descendants { + build_display_list(UnsafeFlowConversions::from_flow( + &mut_owned_flow_to_unsafe_flow(flow)), + proxy) + } + } +} + +fn build_display_list(mut unsafe_flow: PaddedUnsafeFlow, + proxy: &mut WorkerProxy<*mut LayoutContext,PaddedUnsafeFlow>) { + let layout_context: &mut LayoutContext = unsafe { + cast::transmute(*proxy.user_data()) + }; + + loop { + unsafe { + // Get a real flow. + let flow: &mut ~Flow:Share = cast::transmute(&unsafe_flow); + + // Build display lists. + flow.build_display_list(layout_context); + + { + let base = flow::mut_base(*flow); + + // Reset the count of children and absolute descendants for the next layout + // traversal. + let children_and_absolute_descendant_count = base.children.len() + + base.abs_descendants.len(); + base.parallel + .children_and_absolute_descendant_count + .store(children_and_absolute_descendant_count as int, Relaxed); + } + + // Possibly enqueue the parent. + let unsafe_parent = if flow.is_absolutely_positioned() { + mut_borrowed_flow_to_unsafe_flow(flow::mut_base(*flow).absolute_cb + .resolve() + .unwrap()) + } else { + flow::mut_base(*flow).parallel.parent + }; + if unsafe_parent == null_unsafe_flow() { + // We're done! + break + } + + // 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:Share = cast::transmute(&unsafe_parent); + let parent_base = flow::mut_base(*parent); + if parent_base.parallel + .children_and_absolute_descendant_count + .fetch_sub(1, SeqCst) == 1 { + // We were the last child of our parent. Build display lists for our parent. + unsafe_flow = UnsafeFlowConversions::from_flow(&unsafe_parent) + } else { + // Stop. + break + } + } + } +} + pub fn recalc_style_for_subtree(root_node: &LayoutNode, layout_context: &mut LayoutContext, queue: &mut WorkQueue<*mut LayoutContext,UnsafeLayoutNode>) { @@ -442,3 +557,24 @@ pub fn traverse_flow_tree_preorder(root: &mut ~Flow:Share, queue.data = ptr::mut_null() } + +pub fn build_display_list_for_subtree(root: &mut ~Flow:Share, + profiler_chan: ProfilerChan, + layout_context: &mut LayoutContext, + queue: &mut WorkQueue<*mut LayoutContext,PaddedUnsafeFlow>) { + unsafe { + queue.data = cast::transmute(layout_context) + } + + profile(time::LayoutParallelWarmupCategory, profiler_chan, || { + queue.push(WorkUnit { + fun: compute_absolute_position, + data: UnsafeFlowConversions::from_flow(&mut_owned_flow_to_unsafe_flow(root)), + }) + }); + + queue.run(); + + queue.data = ptr::mut_null() +} + diff --git a/src/components/main/layout/table.rs b/src/components/main/layout/table.rs index 1a558374e51..81305216cd4 100644 --- a/src/components/main/layout/table.rs +++ b/src/components/main/layout/table.rs @@ -9,14 +9,11 @@ use layout::block::{BlockFlow, MarginsMayNotCollapse, WidthAndMarginsComputer}; use layout::block::{WidthConstraintInput, WidthConstraintSolution}; use layout::construct::FlowConstructor; use layout::context::LayoutContext; -use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo}; -use layout::floats::{FloatKind}; +use layout::floats::FloatKind; use layout::flow::{TableFlowClass, FlowClass, Flow, ImmutableFlowUtils}; -use layout::flow; use layout::table_wrapper::{TableLayout, FixedLayout, AutoLayout}; use layout::wrapper::ThreadSafeLayoutNode; -use gfx::display_list::StackingContext; use servo_util::geometry::Au; use servo_util::geometry; use style::computed_values::table_layout; @@ -132,16 +129,13 @@ impl TableFlow { /// inline(always) because this is only ever called by in-order or non-in-order top-level /// methods #[inline(always)] - fn assign_height_table_base(&mut self, layout_context: &mut LayoutContext, inorder: bool) { - self.block_flow.assign_height_block_base(layout_context, inorder, MarginsMayNotCollapse); + fn assign_height_table_base(&mut self, layout_context: &mut LayoutContext) { + self.block_flow.assign_height_block_base(layout_context, MarginsMayNotCollapse); } - pub fn build_display_list_table(&mut self, - stacking_context: &mut StackingContext, - builder: &mut DisplayListBuilder, - info: &DisplayListBuildingInfo) { + pub fn build_display_list_table(&mut self, layout_context: &LayoutContext) { debug!("build_display_list_table: same process as block flow"); - self.block_flow.build_display_list_block(stacking_context, builder, info); + self.block_flow.build_display_list_block(layout_context); } } @@ -178,7 +172,6 @@ impl Flow for TableFlow { let mut min_width = Au(0); let mut pref_width = Au(0); let mut did_first_row = false; - let mut num_floats = 0; for kid in self.block_flow.base.child_iter() { assert!(kid.is_proper_table_child()); @@ -238,12 +231,10 @@ impl Flow for TableFlow { } } } - let child_base = flow::mut_base(kid); - num_floats = num_floats + child_base.num_floats; } - 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); + self.block_flow.base.intrinsic_widths.preferred_width = + geometry::max(min_width, pref_width); } /// Recursively (top-down) determines the actual width of child contexts and boxes. When called @@ -295,18 +286,13 @@ impl Flow for TableFlow { self.block_flow.propagate_assigned_width_to_children(left_content_edge, content_width, Some(self.col_widths.clone())); } - /// This is called on kid flows by a parent. - /// - /// Hence, we can assume that assign_height has already been called on the - /// kid (because of the bottom-up traversal). - fn assign_height_inorder(&mut self, ctx: &mut LayoutContext) { - debug!("assign_height_inorder: assigning height for table"); - self.assign_height_table_base(ctx, true); - } - fn assign_height(&mut self, ctx: &mut LayoutContext) { debug!("assign_height: assigning height for table"); - self.assign_height_table_base(ctx, false); + self.assign_height_table_base(ctx); + } + + fn compute_absolute_position(&mut self) { + self.block_flow.compute_absolute_position() } fn debug_str(&self) -> ~str { diff --git a/src/components/main/layout/table_caption.rs b/src/components/main/layout/table_caption.rs index 38d0260ac53..6be42bf3730 100644 --- a/src/components/main/layout/table_caption.rs +++ b/src/components/main/layout/table_caption.rs @@ -7,12 +7,9 @@ use layout::block::BlockFlow; use layout::construct::FlowConstructor; use layout::context::LayoutContext; -use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo}; use layout::flow::{TableCaptionFlowClass, FlowClass, Flow}; use layout::wrapper::ThreadSafeLayoutNode; -use gfx::display_list::StackingContext; - /// A table formatting context. pub struct TableCaptionFlow { pub block_flow: BlockFlow, @@ -31,12 +28,9 @@ impl TableCaptionFlow { self.block_flow.teardown(); } - pub fn build_display_list_table_caption(&mut self, - stacking_context: &mut StackingContext, - builder: &mut DisplayListBuilder, - info: &DisplayListBuildingInfo) { + pub fn build_display_list_table_caption(&mut self, layout_context: &LayoutContext) { debug!("build_display_list_table_caption: same process as block flow"); - self.block_flow.build_display_list_block(stacking_context, builder, info) + self.block_flow.build_display_list_block(layout_context) } } @@ -62,20 +56,15 @@ impl Flow for TableCaptionFlow { self.block_flow.assign_widths(ctx); } - /// This is called on kid flows by a parent. - /// - /// Hence, we can assume that assign_height has already been called on the - /// kid (because of the bottom-up traversal). - fn assign_height_inorder(&mut self, ctx: &mut LayoutContext) { - debug!("assign_height_inorder: assigning height for table_caption"); - self.block_flow.assign_height_inorder(ctx); - } - fn assign_height(&mut self, ctx: &mut LayoutContext) { debug!("assign_height: assigning height for table_caption"); self.block_flow.assign_height(ctx); } + fn compute_absolute_position(&mut self) { + self.block_flow.compute_absolute_position() + } + fn debug_str(&self) -> ~str { let txt = ~"TableCaptionFlow: "; txt.append(self.block_flow.box_.debug_str()) diff --git a/src/components/main/layout/table_cell.rs b/src/components/main/layout/table_cell.rs index 17dbbf45efb..3af8f434969 100644 --- a/src/components/main/layout/table_cell.rs +++ b/src/components/main/layout/table_cell.rs @@ -7,13 +7,11 @@ use layout::box_::Box; use layout::block::{BlockFlow, MarginsMayNotCollapse, WidthAndMarginsComputer}; use layout::context::LayoutContext; -use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo}; use layout::flow::{TableCellFlowClass, FlowClass, Flow}; use layout::model::{MaybeAuto}; use layout::table::InternalTable; use layout::wrapper::ThreadSafeLayoutNode; -use gfx::display_list::StackingContext; use servo_util::geometry::Au; /// A table formatting context. @@ -23,9 +21,7 @@ pub struct TableCellFlow { } impl TableCellFlow { - pub fn from_node_and_box(node: &ThreadSafeLayoutNode, - box_: Box) - -> TableCellFlow { + pub fn from_node_and_box(node: &ThreadSafeLayoutNode, box_: Box) -> TableCellFlow { TableCellFlow { block_flow: BlockFlow::from_node_and_box(node, box_) } @@ -50,18 +46,13 @@ impl TableCellFlow { /// inline(always) because this is only ever called by in-order or non-in-order top-level /// methods #[inline(always)] - fn assign_height_table_cell_base(&mut self, - layout_context: &mut LayoutContext, - inorder: bool) { - self.block_flow.assign_height_block_base(layout_context, inorder, MarginsMayNotCollapse) + fn assign_height_table_cell_base(&mut self, layout_context: &mut LayoutContext) { + self.block_flow.assign_height_block_base(layout_context, MarginsMayNotCollapse) } - pub fn build_display_list_table_cell(&mut self, - stacking_context: &mut StackingContext, - builder: &mut DisplayListBuilder, - info: &DisplayListBuildingInfo) { + pub fn build_display_list_table_cell(&mut self, layout_context: &LayoutContext) { debug!("build_display_list_table: same process as block flow"); - self.block_flow.build_display_list_block(stacking_context, builder, info) + self.block_flow.build_display_list_block(layout_context) } } @@ -114,18 +105,13 @@ impl Flow for TableCellFlow { None); } - /// This is called on kid flows by a parent. - /// - /// Hence, we can assume that assign_height has already been called on the - /// kid (because of the bottom-up traversal). - fn assign_height_inorder(&mut self, ctx: &mut LayoutContext) { - debug!("assign_height_inorder: assigning height for table_cell"); - self.assign_height_table_cell_base(ctx, true); - } - fn assign_height(&mut self, ctx: &mut LayoutContext) { debug!("assign_height: assigning height for table_cell"); - self.assign_height_table_cell_base(ctx, false); + self.assign_height_table_cell_base(ctx); + } + + fn compute_absolute_position(&mut self) { + self.block_flow.compute_absolute_position() } fn debug_str(&self) -> ~str { diff --git a/src/components/main/layout/table_row.rs b/src/components/main/layout/table_row.rs index 274df4d3bcd..4bc80b53fa5 100644 --- a/src/components/main/layout/table_row.rs +++ b/src/components/main/layout/table_row.rs @@ -9,14 +9,12 @@ use layout::block::BlockFlow; use layout::block::WidthAndMarginsComputer; use layout::construct::FlowConstructor; use layout::context::LayoutContext; -use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo}; use layout::flow::{TableRowFlowClass, FlowClass, Flow, ImmutableFlowUtils}; use layout::flow; use layout::table::InternalTable; use layout::model::{MaybeAuto, Specified, Auto}; use layout::wrapper::ThreadSafeLayoutNode; -use gfx::display_list::StackingContext; use servo_util::geometry::Au; use servo_util::geometry; @@ -81,7 +79,7 @@ impl TableRowFlow { /// inline(always) because this is only ever called by in-order or non-in-order top-level /// methods #[inline(always)] - fn assign_height_table_row_base(&mut self, layout_context: &mut LayoutContext, inorder: bool) { + fn assign_height_table_row_base(&mut self, layout_context: &mut LayoutContext) { let (top_offset, _, _) = self.initialize_offsets(); let /* mut */ cur_y = top_offset; @@ -89,9 +87,7 @@ impl TableRowFlow { // Per CSS 2.1 § 17.5.3, find max_y = max( computed `height`, minimum height of all cells ) let mut max_y = Au::new(0); for kid in self.block_flow.base.child_iter() { - if inorder { - kid.assign_height_inorder(layout_context) - } + kid.assign_height_for_inorder_child_if_necessary(layout_context); { let child_box = kid.as_table_cell().box_(); @@ -136,12 +132,9 @@ impl TableRowFlow { } } - pub fn build_display_list_table_row(&mut self, - stacking_context: &mut StackingContext, - builder: &mut DisplayListBuilder, - info: &DisplayListBuildingInfo) { + pub fn build_display_list_table_row(&mut self, layout_context: &LayoutContext) { debug!("build_display_list_table_row: same process as block flow"); - self.block_flow.build_display_list_block(stacking_context, builder, info) + self.block_flow.build_display_list_block(layout_context) } } @@ -179,7 +172,6 @@ impl Flow for TableRowFlow { fn bubble_widths(&mut self, _: &mut LayoutContext) { let mut min_width = Au(0); let mut pref_width = Au(0); - let mut num_floats = 0; /* find the specified widths from child table-cell contexts */ for kid in self.block_flow.base.child_iter() { assert!(kid.is_table_cell()); @@ -198,9 +190,7 @@ impl Flow for TableRowFlow { self.col_pref_widths.push(child_base.intrinsic_widths.preferred_width); min_width = min_width + child_base.intrinsic_widths.minimum_width; pref_width = pref_width + child_base.intrinsic_widths.preferred_width; - num_floats = num_floats + child_base.num_floats; } - 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); @@ -222,18 +212,13 @@ impl Flow for TableRowFlow { self.block_flow.propagate_assigned_width_to_children(left_content_edge, Au(0), Some(self.col_widths.clone())); } - /// This is called on kid flows by a parent. - /// - /// Hence, we can assume that assign_height has already been called on the - /// kid (because of the bottom-up traversal). - fn assign_height_inorder(&mut self, ctx: &mut LayoutContext) { - debug!("assign_height_inorder: assigning height for table_row"); - self.assign_height_table_row_base(ctx, true); - } - fn assign_height(&mut self, ctx: &mut LayoutContext) { debug!("assign_height: assigning height for table_row"); - self.assign_height_table_row_base(ctx, false); + self.assign_height_table_row_base(ctx); + } + + fn compute_absolute_position(&mut self) { + self.block_flow.compute_absolute_position() } fn debug_str(&self) -> ~str { diff --git a/src/components/main/layout/table_rowgroup.rs b/src/components/main/layout/table_rowgroup.rs index ece55cd8bdf..e8e11bd8a66 100644 --- a/src/components/main/layout/table_rowgroup.rs +++ b/src/components/main/layout/table_rowgroup.rs @@ -9,13 +9,11 @@ use layout::block::BlockFlow; use layout::block::WidthAndMarginsComputer; use layout::construct::FlowConstructor; use layout::context::LayoutContext; -use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo}; use layout::flow::{TableRowGroupFlowClass, FlowClass, Flow, ImmutableFlowUtils}; use layout::flow; use layout::table::{InternalTable, TableFlow}; use layout::wrapper::ThreadSafeLayoutNode; -use gfx::display_list::StackingContext; use servo_util::geometry::Au; use servo_util::geometry; @@ -80,12 +78,14 @@ impl TableRowGroupFlow { /// inline(always) because this is only ever called by in-order or non-in-order top-level /// methods #[inline(always)] - fn assign_height_table_rowgroup_base(&mut self, _: &mut LayoutContext, _: bool) { + fn assign_height_table_rowgroup_base(&mut self, layout_context: &mut LayoutContext) { let (top_offset, _, _) = self.initialize_offsets(); let mut cur_y = top_offset; for kid in self.block_flow.base.child_iter() { + kid.assign_height_for_inorder_child_if_necessary(layout_context); + let child_node = flow::mut_base(kid); child_node.position.origin.y = cur_y; cur_y = cur_y + child_node.position.size.height; @@ -99,12 +99,9 @@ impl TableRowGroupFlow { self.block_flow.base.position.size.height = height; } - pub fn build_display_list_table_rowgroup(&mut self, - stacking_context: &mut StackingContext, - builder: &mut DisplayListBuilder, - info: &DisplayListBuildingInfo) { + pub fn build_display_list_table_rowgroup(&mut self, layout_context: &LayoutContext) { debug!("build_display_list_table_rowgroup: same process as block flow"); - self.block_flow.build_display_list_block(stacking_context, builder, info) + self.block_flow.build_display_list_block(layout_context) } } @@ -143,7 +140,6 @@ impl Flow for TableRowGroupFlow { fn bubble_widths(&mut self, _: &mut LayoutContext) { let mut min_width = Au(0); let mut pref_width = Au(0); - let mut num_floats = 0; for kid in self.block_flow.base.child_iter() { assert!(kid.is_table_row()); @@ -174,11 +170,8 @@ impl Flow for TableRowGroupFlow { pref_width = pref_width + new_kid_pref; } } - let child_base = flow::mut_base(kid); - num_floats = num_floats + child_base.num_floats; } - 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); @@ -201,18 +194,13 @@ impl Flow for TableRowGroupFlow { self.block_flow.propagate_assigned_width_to_children(left_content_edge, content_width, Some(self.col_widths.clone())); } - /// This is called on kid flows by a parent. - /// - /// Hence, we can assume that assign_height has already been called on the - /// kid (because of the bottom-up traversal). - fn assign_height_inorder(&mut self, ctx: &mut LayoutContext) { - debug!("assign_height_inorder: assigning height for table_rowgroup"); - self.assign_height_table_rowgroup_base(ctx, true); - } - fn assign_height(&mut self, ctx: &mut LayoutContext) { debug!("assign_height: assigning height for table_rowgroup"); - self.assign_height_table_rowgroup_base(ctx, false); + self.assign_height_table_rowgroup_base(ctx); + } + + fn compute_absolute_position(&mut self) { + self.block_flow.compute_absolute_position() } fn debug_str(&self) -> ~str { diff --git a/src/components/main/layout/table_wrapper.rs b/src/components/main/layout/table_wrapper.rs index 060cbee6713..8cb064a7add 100644 --- a/src/components/main/layout/table_wrapper.rs +++ b/src/components/main/layout/table_wrapper.rs @@ -9,13 +9,11 @@ use layout::block::{BlockFlow, MarginsMayNotCollapse, WidthAndMarginsComputer}; use layout::block::{WidthConstraintInput, WidthConstraintSolution}; use layout::construct::FlowConstructor; use layout::context::LayoutContext; -use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo}; use layout::floats::FloatKind; use layout::flow::{TableWrapperFlowClass, FlowClass, Flow, ImmutableFlowUtils}; use layout::model::{Specified, Auto, specified}; use layout::wrapper::ThreadSafeLayoutNode; -use gfx::display_list::StackingContext; use servo_util::geometry::Au; use servo_util::geometry; use style::computed_values::table_layout; @@ -104,18 +102,13 @@ impl TableWrapperFlow { /// inline(always) because this is only ever called by in-order or non-in-order top-level /// methods #[inline(always)] - fn assign_height_table_wrapper_base(&mut self, - layout_context: &mut LayoutContext, - inorder: bool) { - self.block_flow.assign_height_block_base(layout_context, inorder, MarginsMayNotCollapse); + fn assign_height_table_wrapper_base(&mut self, layout_context: &mut LayoutContext) { + self.block_flow.assign_height_block_base(layout_context, MarginsMayNotCollapse); } - pub fn build_display_list_table_wrapper(&mut self, - stacking_context: &mut StackingContext, - builder: &mut DisplayListBuilder, - info: &DisplayListBuildingInfo) { + pub fn build_display_list_table_wrapper(&mut self, layout_context: &LayoutContext) { debug!("build_display_list_table_wrapper: same process as block flow"); - self.block_flow.build_display_list_block(stacking_context, builder, info); + self.block_flow.build_display_list_block(layout_context); } } @@ -187,30 +180,20 @@ impl Flow for TableWrapperFlow { self.block_flow.propagate_assigned_width_to_children(left_content_edge, content_width, assigned_col_widths); } - /// This is called on kid flows by a parent. - /// - /// Hence, we can assume that assign_height has already been called on the - /// kid (because of the bottom-up traversal). - fn assign_height_inorder(&mut self, ctx: &mut LayoutContext) { - if self.is_float() { - debug!("assign_height_inorder_float: assigning height for floated table_wrapper"); - self.block_flow.assign_height_float_inorder(); - } else { - debug!("assign_height_inorder: assigning height for table_wrapper"); - self.assign_height_table_wrapper_base(ctx, true); - } - } - fn assign_height(&mut self, ctx: &mut LayoutContext) { if self.is_float() { debug!("assign_height_float: assigning height for floated table_wrapper"); self.block_flow.assign_height_float(ctx); } else { debug!("assign_height: assigning height for table_wrapper"); - self.assign_height_table_wrapper_base(ctx, false); + self.assign_height_table_wrapper_base(ctx); } } + fn compute_absolute_position(&mut self) { + self.block_flow.compute_absolute_position() + } + fn debug_str(&self) -> ~str { let txt = if self.is_float() { ~"TableWrapperFlow(Float): " diff --git a/src/components/main/layout/text.rs b/src/components/main/layout/text.rs index f60fececdf5..63773e23f83 100644 --- a/src/components/main/layout/text.rs +++ b/src/components/main/layout/text.rs @@ -8,14 +8,17 @@ use layout::box_::{Box, ScannedTextBox, ScannedTextBoxInfo, UnscannedTextBox}; use layout::flow::Flow; use layout::inline::InlineBoxes; +use gfx::font::{FontMetrics, FontStyle}; use gfx::font_context::FontContext; use gfx::text::text_run::TextRun; use gfx::text::util::{CompressWhitespaceNewline, transform_text, CompressNone}; +use servo_util::geometry::Au; use servo_util::range::Range; use servo_util::smallvec::{SmallVec, SmallVec0}; use std::mem; use std::slice; -use style::computed_values::white_space; +use style::ComputedValues; +use style::computed_values::{font_family, line_height, white_space}; use sync::Arc; struct NewLinePositions { @@ -256,3 +259,53 @@ impl TextRunScanner { new_whitespace } // End of `flush_clump_to_list`. } + +/// Returns the metrics of the font represented by the given `FontStyle`, respectively. +/// +/// `#[inline]` because often the caller only needs a few fields from the font metrics. +#[inline] +pub fn font_metrics_for_style(font_context: &mut FontContext, font_style: &FontStyle) + -> FontMetrics { + let fontgroup = font_context.get_resolved_font_for_style(font_style); + fontgroup.borrow().fonts[0].borrow().metrics.clone() +} + +/// Converts a computed style to a font style used for rendering. +/// +/// FIXME(pcwalton): This should not be necessary; just make the font part of the style sharable +/// with the display list somehow. (Perhaps we should use an ARC.) +pub fn computed_style_to_font_style(style: &ComputedValues) -> FontStyle { + debug!("(font style) start"); + + // FIXME: Too much allocation here. + let mut font_families = style.Font.get().font_family.iter().map(|family| { + match *family { + font_family::FamilyName(ref name) => (*name).clone(), + } + }); + debug!("(font style) font families: `{:?}`", font_families); + + let font_size = style.Font.get().font_size.to_f64().unwrap() / 60.0; + debug!("(font style) font size: `{:f}px`", font_size); + + FontStyle { + pt_size: font_size, + weight: style.Font.get().font_weight, + style: style.Font.get().font_style, + families: font_families.collect(), + } +} + +/// Returns the line height needed by the given computed style and font size. +/// +/// FIXME(pcwalton): I believe this should not take a separate `font-size` parameter. +pub fn line_height_from_style(style: &ComputedValues, font_size: Au) -> Au { + let from_inline = match style.InheritedBox.get().line_height { + line_height::Normal => font_size.scale_by(1.14), + line_height::Number(l) => font_size.scale_by(l), + line_height::Length(l) => l + }; + let minimum = style.InheritedBox.get()._servo_minimum_line_height; + Au::max(from_inline, minimum) +} + diff --git a/src/components/main/layout/util.rs b/src/components/main/layout/util.rs index 3a64b4cd6a9..48c782d7f3a 100644 --- a/src/components/main/layout/util.rs +++ b/src/components/main/layout/util.rs @@ -7,6 +7,7 @@ use layout::parallel::DomParallelInfo; use layout::wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode}; use gfx::display_list::OpaqueNode; +use gfx; use libc::uintptr_t; use script::dom::bindings::js::JS; use script::dom::bindings::utils::Reflectable; @@ -15,6 +16,7 @@ use script::layout_interface::{LayoutChan, UntrustedNodeAddress, TrustedNodeAddr use std::cast; use std::cell::{Ref, RefMut}; use style::ComputedValues; +use style; use sync::Arc; /// Data that layout associates with a node. @@ -148,6 +150,17 @@ impl OpaqueNodeMethods for OpaqueNode { addr } } +} + +/// Allows a CSS color to be converted into a graphics color. +pub trait ToGfxColor { + /// Converts a CSS color to a graphics color. + fn to_gfx_color(&self) -> gfx::color::Color; +} +impl ToGfxColor for style::computed_values::RGBA { + fn to_gfx_color(&self) -> gfx::color::Color { + gfx::color::rgba(self.red, self.green, self.blue, self.alpha) + } } diff --git a/src/components/main/servo.rs b/src/components/main/servo.rs index 91e3b1c7c5b..37b9092a7c0 100755 --- a/src/components/main/servo.rs +++ b/src/components/main/servo.rs @@ -98,7 +98,6 @@ pub mod layout { pub mod box_; pub mod construct; pub mod context; - pub mod display_list_builder; pub mod floats; pub mod flow; pub mod flow_list; diff --git a/src/components/net/image/holder.rs b/src/components/net/image/holder.rs index cf9a65ef61f..40a9739fb37 100644 --- a/src/components/net/image/holder.rs +++ b/src/components/net/image/holder.rs @@ -109,6 +109,11 @@ impl ImageHolder { }) } + pub fn get_image_if_present(&self) -> Option<Arc<~Image>> { + debug!("get_image_if_present() {}", self.url.to_str()); + self.image.clone() + } + pub fn get_image(&mut self) -> Option<Arc<~Image>> { debug!("get_image() {}", self.url.to_str()); |