diff --git a/src/components/gfx/display_list.rs b/src/components/gfx/display_list.rs index a477bc71a5d..5a5300aabf3 100644 --- a/src/components/gfx/display_list.rs +++ b/src/components/gfx/display_list.rs @@ -92,12 +92,38 @@ pub struct SolidColorDisplayItem { /// Renders text. pub struct TextDisplayItem { + /// Fields common to all display items. base: BaseDisplayItem, + + /// The text run. text_run: Arc<~TextRun>, + + /// The range of text within the text run. range: Range, + + /// The color of the text. color: Color, + + /// A bitfield of flags for text display items. + flags: TextDisplayItemFlags, } +/// Flags for text display items. +pub struct TextDisplayItemFlags(u8); + +impl TextDisplayItemFlags { + pub fn new() -> TextDisplayItemFlags { + TextDisplayItemFlags(0) + } +} + +// Whether underlining is forced on. +bitfield!(TextDisplayItemFlags, override_underline, set_override_underline, 0x01) +// Whether overlining is forced on. +bitfield!(TextDisplayItemFlags, override_overline, set_override_overline, 0x02) +// Whether line-through is forced on. +bitfield!(TextDisplayItemFlags, override_line_through, set_override_line_through, 0x04) + /// Renders an image. pub struct ImageDisplayItem { base: BaseDisplayItem, @@ -184,18 +210,18 @@ impl DisplayItem { let strikeout_size = font_metrics.strikeout_size; let strikeout_offset = font_metrics.strikeout_offset; - if text_run.decoration.underline { + if text_run.decoration.underline || text.flags.override_underline() { let underline_y = baseline_origin.y - underline_offset; let underline_bounds = Rect(Point2D(baseline_origin.x, underline_y), Size2D(width, underline_size)); render_context.draw_solid_color(&underline_bounds, text.color); } - if text_run.decoration.overline { + if text_run.decoration.overline || text.flags.override_overline() { let overline_bounds = Rect(Point2D(baseline_origin.x, origin.y), Size2D(width, underline_size)); render_context.draw_solid_color(&overline_bounds, text.color); } - if text_run.decoration.line_through { + if text_run.decoration.line_through || text.flags.override_line_through() { let strikeout_y = baseline_origin.y - strikeout_offset; let strikeout_bounds = Rect(Point2D(baseline_origin.x, strikeout_y), Size2D(width, strikeout_size)); diff --git a/src/components/gfx/gfx.rc b/src/components/gfx/gfx.rc index b38fe166ca6..e8b9420461e 100644 --- a/src/components/gfx/gfx.rc +++ b/src/components/gfx/gfx.rc @@ -40,6 +40,10 @@ pub use gfx_font_list = font_list; pub use servo_gfx_font = font; pub use servo_gfx_font_list = font_list; +// Macros +mod macros; + +// Private rendering modules mod render_context; // Rendering diff --git a/src/components/gfx/macros.rs b/src/components/gfx/macros.rs new file mode 100644 index 00000000000..f8eaa07cc05 --- /dev/null +++ b/src/components/gfx/macros.rs @@ -0,0 +1,22 @@ +/* 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/. */ + +#[macro_escape]; + +macro_rules! bitfield( + ($bitfieldname:ident, $getter:ident, $setter:ident, $value:expr) => ( + impl $bitfieldname { + #[inline] + pub fn $getter(self) -> bool { + (*self & $value) != 0 + } + + #[inline] + pub fn $setter(&mut self, value: bool) { + *self = $bitfieldname((**self & !$value) | (if value { $value } else { 0 })) + } + } + ) +) + diff --git a/src/components/main/layout/block.rs b/src/components/main/layout/block.rs index 61cfcfac313..6da6e8fcfb0 100644 --- a/src/components/main/layout/block.rs +++ b/src/components/main/layout/block.rs @@ -501,7 +501,7 @@ impl BlockFlow { // add box that starts block context for box in self.box.iter() { - box.build_display_list(builder, dirty, &self.base.abs_position, list) + box.build_display_list(builder, dirty, self.base.abs_position, (&*self) as &Flow, list) } // TODO: handle any out-of-flow elements @@ -532,7 +532,7 @@ impl BlockFlow { let offset = self.base.abs_position + self.float.get_ref().rel_pos; // add box that starts block context for box in self.box.iter() { - box.build_display_list(builder, dirty, &offset, list) + box.build_display_list(builder, dirty, offset, (&*self) as &Flow, list) } @@ -621,10 +621,10 @@ impl Flow for BlockFlow { self.base.position.origin = Au::zero_point(); self.base.position.size.width = ctx.screen_size.size.width; self.base.floats_in = FloatContext::new(self.base.num_floats); - self.base.is_inorder = false; + self.base.flags.set_inorder(false); } - //position was set to the containing block by the flow's parent + // The position was set to the containing block by the flow's parent. let mut remaining_width = self.base.position.size.width; let mut x_offset = Au::new(0); @@ -632,7 +632,7 @@ impl Flow for BlockFlow { self.float.get_mut_ref().containing_width = remaining_width; // Parent usually sets this, but floats are never inorder - self.base.is_inorder = false; + self.base.flags.set_inorder(false); } for box in self.box.iter() { @@ -677,10 +677,10 @@ impl Flow for BlockFlow { } let has_inorder_children = if self.is_float() { - self.base.num_floats > 0 - } else { - self.base.is_inorder || self.base.num_floats > 0 - }; + self.base.num_floats > 0 + } else { + self.base.flags.inorder() || self.base.num_floats > 0 + }; for kid in self.base.child_iter() { assert!(kid.starts_block_flow() || kid.starts_inline_flow()); @@ -688,11 +688,16 @@ impl Flow for BlockFlow { let child_base = flow::mut_base(*kid); child_base.position.origin.x = x_offset; child_base.position.size.width = remaining_width; - child_base.is_inorder = has_inorder_children; + child_base.flags.set_inorder(has_inorder_children); - if !child_base.is_inorder { + if !child_base.flags.inorder() { child_base.floats_in = FloatContext::new(0); } + + // Per CSS 2.1 § 16.3.1, text decoration propagates to all children in flow. + // + // TODO(pcwalton): When we have out-of-flow children, don't unconditionally propagate. + child_base.flags.propagate_text_decoration_from_parent(self.base.flags) } } diff --git a/src/components/main/layout/box.rs b/src/components/main/layout/box.rs index 6bf80fb8be6..0c51d0690a0 100644 --- a/src/components/main/layout/box.rs +++ b/src/components/main/layout/box.rs @@ -11,7 +11,8 @@ use gfx::color::rgb; use gfx::display_list::{BaseDisplayItem, BorderDisplayItem, BorderDisplayItemClass}; use gfx::display_list::{DisplayList, ImageDisplayItem, ImageDisplayItemClass}; use gfx::display_list::{SolidColorDisplayItem, SolidColorDisplayItemClass, TextDisplayItem}; -use gfx::display_list::{TextDisplayItemClass, ClipDisplayItem, ClipDisplayItemClass}; +use gfx::display_list::{TextDisplayItemClass, TextDisplayItemFlags, ClipDisplayItem}; +use gfx::display_list::{ClipDisplayItemClass}; use gfx::font::{FontStyle, FontWeight300}; use gfx::text::text_run::TextRun; use script::dom::node::{AbstractNode, LayoutView}; @@ -27,12 +28,14 @@ use std::cmp::ApproxEq; use std::num::Zero; use style::ComputedValues; use style::computed_values::{LengthOrPercentage, overflow}; -use style::computed_values::{border_style, clear, float, font_family, font_style, line_height}; -use style::computed_values::{position, text_align, text_decoration, vertical_align, visibility}; +use style::computed_values::{border_style, clear, font_family, font_style, line_height}; +use style::computed_values::{text_align, text_decoration, vertical_align, visibility}; use css::node_style::StyledNode; use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData, ToGfxColor}; use layout::float_context::{ClearType, ClearLeft, ClearRight, ClearBoth}; +use layout::flow::Flow; +use layout::flow; use layout::model::{MaybeAuto, specified}; /// Boxes (`struct Box`) are the leaves of the layout tree. They cannot position themselves. In @@ -460,41 +463,15 @@ impl Box { self.style().Box.vertical_align } - /// Returns the text decoration of the computed style of the nearest `Element` node + /// Returns the text decoration of this box, according to the style of the nearest ancestor + /// element. + /// + /// NB: This may not be the actual text decoration, because of the override rules specified in + /// CSS 2.1 § 16.3.1. Unfortunately, computing this properly doesn't really fit into Servo's + /// model. Therefore, this is a best lower bound approximation, but the end result may actually + /// have the various decoration flags turned on afterward. pub fn text_decoration(&self) -> text_decoration::T { - /// Computes the propagated value of text-decoration, as specified in CSS 2.1 § 16.3.1 - /// TODO: make sure this works with anonymous box generation. - fn get_propagated_text_decoration(element: AbstractNode) - -> text_decoration::T { - //Skip over non-element nodes in the DOM - if !element.is_element() { - return match element.parent_node() { - None => text_decoration::none, - Some(parent) => get_propagated_text_decoration(parent), - }; - } - - // FIXME: Implement correctly. - let display_in_flow = true; - - let position = element.style().get().Box.position; - let float = element.style().get().Box.float; - - let in_flow = (position == position::static_) && (float == float::none) && - display_in_flow; - - let text_decoration = element.style().get().Text.text_decoration; - - if text_decoration == text_decoration::none && in_flow { - match element.parent_node() { - None => text_decoration::none, - Some(parent) => get_propagated_text_decoration(parent), - } - } else { - text_decoration - } - } - get_propagated_text_decoration(self.nearest_ancestor_element()) + self.style().Text.text_decoration } /// Returns the sum of margin, border, and padding on the left. @@ -622,13 +599,14 @@ impl Box { &self, _: &DisplayListBuilder, dirty: &Rect, - offset: &Point2D, + offset: Point2D, + flow: &Flow, list: &Cell>) { let box_bounds = self.position.get(); - let absolute_box_bounds = box_bounds.translate(offset); + let absolute_box_bounds = box_bounds.translate(&offset); debug!("Box::build_display_list at rel={}, abs={}: {:s}", box_bounds, absolute_box_bounds, self.debug_str()); - debug!("Box::build_display_list: dirty={}, offset={}", *dirty, *offset); + debug!("Box::build_display_list: dirty={}, offset={}", *dirty, offset); if self.style().Box.visibility != visibility::visible { return; @@ -661,6 +639,13 @@ impl Box { let color = self.style().Color.color.to_gfx_color(); + // Set the various text display item flags. + let flow_flags = flow::base(flow).flags; + let mut text_flags = TextDisplayItemFlags::new(); + text_flags.set_override_underline(flow_flags.override_underline()); + text_flags.set_override_overline(flow_flags.override_overline()); + text_flags.set_override_line_through(flow_flags.override_line_through()); + // Create the text box. do list.with_mut_ref |list| { let text_display_item = ~TextDisplayItem { @@ -671,6 +656,7 @@ impl Box { text_run: text_box.run.clone(), range: text_box.range, color: color, + flags: text_flags, }; list.append_item(TextDisplayItemClass(text_display_item)) diff --git a/src/components/main/layout/display_list_builder.rs b/src/components/main/layout/display_list_builder.rs index ae583e72334..d87e3c5d70d 100644 --- a/src/components/main/layout/display_list_builder.rs +++ b/src/components/main/layout/display_list_builder.rs @@ -42,7 +42,7 @@ impl ExtraDisplayListData for Nothing { /// Right now, the builder isn't used for much, but it establishes the pattern we'll need once we /// support display-list-based hit testing and so forth. pub struct DisplayListBuilder<'self> { - ctx: &'self LayoutContext, + ctx: &'self LayoutContext, } // diff --git a/src/components/main/layout/flow.rs b/src/components/main/layout/flow.rs index 2bb8071b51d..25d9d08e522 100644 --- a/src/components/main/layout/flow.rs +++ b/src/components/main/layout/flow.rs @@ -33,17 +33,17 @@ use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData}; use layout::float_context::{FloatContext, Invalid}; use layout::incremental::RestyleDamage; use layout::inline::InlineFlow; -use gfx::display_list::{ClipDisplayItemClass}; use extra::dlist::{DList, DListIterator, MutDListIterator}; use extra::container::Deque; use geom::point::Point2D; use geom::rect::Rect; -use gfx::display_list::DisplayList; -use servo_util::geometry::Au; +use gfx::display_list::{ClipDisplayItemClass, DisplayList}; use script::dom::node::{AbstractNode, LayoutView}; +use servo_util::geometry::Au; use std::cast; use std::cell::Cell; +use style::ComputedValues; /// Virtual methods that make up a float context. /// @@ -305,6 +305,49 @@ pub trait PostorderFlowTraversal { } } +/// Flags used in flows, tightly packed to save space. +pub struct FlowFlags(u8); + +/// The bitmask of flags that represent text decoration fields that get propagated downward. +/// +/// NB: If you update this field, you must update the bitfields below. +static TEXT_DECORATION_OVERRIDE_BITMASK: u8 = 0b00001110; + +impl FlowFlags { + /// Creates a new set of flow flags from the given style. + fn new(style: &ComputedValues) -> FlowFlags { + let text_decoration = style.Text.text_decoration; + let mut flags = FlowFlags(0); + flags.set_override_underline(text_decoration.underline); + flags.set_override_overline(text_decoration.overline); + flags.set_override_line_through(text_decoration.line_through); + flags + } + + /// Propagates text decoration flags from an appropriate parent flow per CSS 2.1 § 16.3.1. + pub fn propagate_text_decoration_from_parent(&mut self, parent: FlowFlags) { + *self = FlowFlags(**self | (*parent & TEXT_DECORATION_OVERRIDE_BITMASK)) + } +} + +// Whether we need an in-order traversal. +bitfield!(FlowFlags, inorder, set_inorder, 0x01) + +// Whether this flow forces `text-decoration: underline` on. +// +// NB: If you update this, you need to update TEXT_DECORATION_OVERRIDE_BITMASK. +bitfield!(FlowFlags, override_underline, set_override_underline, 0x02) + +// Whether this flow forces `text-decoration: overline` on. +// +// NB: If you update this, you need to update TEXT_DECORATION_OVERRIDE_BITMASK. +bitfield!(FlowFlags, override_overline, set_override_overline, 0x04) + +// Whether this flow forces `text-decoration: line-through` on. +// +// NB: If you update this, you need to update TEXT_DECORATION_OVERRIDE_BITMASK. +bitfield!(FlowFlags, override_line_through, set_override_line_through, 0x08) + /// Data common to all flows. /// /// FIXME: We need a naming convention for pseudo-inheritance like this. How about @@ -313,6 +356,7 @@ pub struct FlowData { node: AbstractNode, restyle_damage: RestyleDamage, + /// The children of this flow. children: DList<~Flow:>, /* TODO (Issue #87): debug only */ @@ -336,7 +380,9 @@ pub struct FlowData { floats_out: FloatContext, num_floats: uint, abs_position: Point2D, - is_inorder: bool, + + /// Various flags for flows, tightly packed to save space. + flags: FlowFlags, } pub struct BoxIterator { @@ -359,6 +405,7 @@ impl Iterator<@Box> for BoxIterator { impl FlowData { #[inline] pub fn new(id: int, node: AbstractNode) -> FlowData { + let style = node.style(); FlowData { node: node, restyle_damage: node.restyle_damage(), @@ -375,7 +422,8 @@ impl FlowData { floats_out: Invalid, num_floats: 0, abs_position: Point2D(Au::new(0), Au::new(0)), - is_inorder: false + + flags: FlowFlags::new(style.get()), } } diff --git a/src/components/main/layout/inline.rs b/src/components/main/layout/inline.rs index a69c1ec1d33..16225700da1 100644 --- a/src/components/main/layout/inline.rs +++ b/src/components/main/layout/inline.rs @@ -497,7 +497,7 @@ impl InlineFlow { self.boxes.len()); for box in self.boxes.iter() { - box.build_display_list(builder, dirty, &self.base.abs_position, list) + box.build_display_list(builder, dirty, self.base.abs_position, (&*self) as &Flow, list) } // TODO(#225): Should `inline-block` elements have flows as children of the inline flow or @@ -672,7 +672,7 @@ impl Flow for InlineFlow { for kid in self.base.child_iter() { let child_base = flow::mut_base(*kid); child_base.position.size.width = self.base.position.size.width; - child_base.is_inorder = self.base.is_inorder; + child_base.flags.set_inorder(self.base.flags.inorder()); } // There are no child contexts, so stop here. diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs index ae499fff8ca..3519a782607 100644 --- a/src/components/main/layout/layout_task.rs +++ b/src/components/main/layout/layout_task.rs @@ -178,7 +178,7 @@ impl<'self> PostorderFlowTraversal for AssignHeightsAndStoreOverflowTraversal<'s #[inline] fn should_process(&mut self, flow: &mut Flow) -> bool { - !flow::base(flow).is_inorder + !flow::base(flow).flags.inorder() } } diff --git a/src/components/main/macros.rs b/src/components/main/macros.rs index 144d57d40c4..34b0d2ac869 100644 --- a/src/components/main/macros.rs +++ b/src/components/main/macros.rs @@ -22,3 +22,20 @@ macro_rules! spawn_with( do ($task).spawn_with(( $($var),+ , () )) |( $($var),+ , () )| $body ) ) + +macro_rules! bitfield( + ($bitfieldname:ident, $getter:ident, $setter:ident, $value:expr) => ( + impl $bitfieldname { + #[inline] + pub fn $getter(self) -> bool { + (*self & $value) != 0 + } + + #[inline] + pub fn $setter(&mut self, value: bool) { + *self = $bitfieldname((**self & !$value) | (if value { $value } else { 0 })) + } + } + ) +) + diff --git a/src/test/ref/basic.list b/src/test/ref/basic.list index 0a6c56d0e66..f4d2b5aaacc 100644 --- a/src/test/ref/basic.list +++ b/src/test/ref/basic.list @@ -17,3 +17,4 @@ == png_rgba_colorspace_a.html png_rgba_colorspace_b.html == border_style_none_a.html border_style_none_b.html == acid1_a.html acid1_b.html +== text_decoration_propagation_a.html text_decoration_propagation_b.html diff --git a/src/test/ref/text_decoration_propagation_a.html b/src/test/ref/text_decoration_propagation_a.html new file mode 100644 index 00000000000..4811fbb2a2a --- /dev/null +++ b/src/test/ref/text_decoration_propagation_a.html @@ -0,0 +1,20 @@ + + + +I CAN'T LIVE ANOTHER DAY WITHOUT AIR CONDITIONING! + + + +
+ hi +
+ there +
+
+ + + diff --git a/src/test/ref/text_decoration_propagation_b.html b/src/test/ref/text_decoration_propagation_b.html new file mode 100644 index 00000000000..ff7473ec78e --- /dev/null +++ b/src/test/ref/text_decoration_propagation_b.html @@ -0,0 +1,23 @@ + + + +I CAN'T LIVE ANOTHER DAY WITHOUT AIR CONDITIONING! + + + +
+ hi +
+ there +
+
+ + +