diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs index 417162c54a1..532b965d63e 100644 --- a/components/gfx/display_list/mod.rs +++ b/components/gfx/display_list/mod.rs @@ -75,6 +75,8 @@ pub struct DisplayList { pub floats: DList, /// All other content. pub content: DList, + /// Outlines: step 10. + pub outlines: DList, /// Child stacking contexts. pub children: DList>, } @@ -88,6 +90,7 @@ impl DisplayList { block_backgrounds_and_borders: DList::new(), floats: DList::new(), content: DList::new(), + outlines: DList::new(), children: DList::new(), } } @@ -102,12 +105,14 @@ impl DisplayList { &mut other.block_backgrounds_and_borders); servo_dlist::append_from(&mut self.floats, &mut other.floats); servo_dlist::append_from(&mut self.content, &mut other.content); + servo_dlist::append_from(&mut self.outlines, &mut other.outlines); servo_dlist::append_from(&mut self.children, &mut other.children); } /// Merges all display items from all non-float stacking levels to the `float` stacking level. #[inline] pub fn form_float_pseudo_stacking_context(&mut self) { + servo_dlist::prepend_from(&mut self.floats, &mut self.outlines); servo_dlist::prepend_from(&mut self.floats, &mut self.content); servo_dlist::prepend_from(&mut self.floats, &mut self.block_backgrounds_and_borders); servo_dlist::prepend_from(&mut self.floats, &mut self.background_and_borders); @@ -129,6 +134,9 @@ impl DisplayList { for display_item in self.content.iter() { result.push((*display_item).clone()) } + for display_item in self.outlines.iter() { + result.push((*display_item).clone()) + } result } } @@ -282,7 +290,10 @@ impl StackingContext { } } - // TODO(pcwalton): Step 10: Outlines. + // Step 10: Outlines. + for display_item in display_list.outlines.iter() { + display_item.draw_into_context(&mut paint_subcontext) + } // Undo our clipping and transform. if paint_subcontext.transient_clip_rect.is_some() { @@ -349,6 +360,12 @@ impl StackingContext { // Iterate through display items in reverse stacking order. Steps here refer to the // painting steps in CSS 2.1 Appendix E. // + // Step 10: Outlines. + hit_test_in_list(point, result, topmost_only, self.display_list.outlines.iter().rev()); + if topmost_only && !result.is_empty() { + return + } + // Steps 9 and 8: Positioned descendants with nonnegative z-indices. for kid in self.display_list.children.iter().rev() { if kid.z_index < 0 { diff --git a/components/gfx/display_list/optimizer.rs b/components/gfx/display_list/optimizer.rs index cb5703b6c3e..1397797d22a 100644 --- a/components/gfx/display_list/optimizer.rs +++ b/components/gfx/display_list/optimizer.rs @@ -35,6 +35,7 @@ impl DisplayListOptimizer { display_list.block_backgrounds_and_borders.iter()); self.add_in_bounds_display_items(&mut result.floats, display_list.floats.iter()); self.add_in_bounds_display_items(&mut result.content, display_list.content.iter()); + self.add_in_bounds_display_items(&mut result.outlines, display_list.outlines.iter()); self.add_in_bounds_stacking_contexts(&mut result.children, display_list.children.iter()); result } diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index b2296c721fc..db0a39605eb 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -112,6 +112,12 @@ pub trait FragmentDisplayListBuilding { level: StackingLevel, clip_rect: &Rect); + fn build_display_list_for_outline_if_applicable(&self, + style: &ComputedValues, + display_list: &mut DisplayList, + bounds: &Rect, + clip_rect: &Rect); + fn build_debug_borders_around_text_fragments(&self, display_list: &mut DisplayList, flow_origin: Point2D, @@ -439,6 +445,40 @@ impl FragmentDisplayListBuilding for Fragment { }), level); } + fn build_display_list_for_outline_if_applicable(&self, + style: &ComputedValues, + display_list: &mut DisplayList, + bounds: &Rect, + clip_rect: &Rect) { + let width = style.get_outline().outline_width; + if width == Au(0) { + return + } + + let outline_style = style.get_outline().outline_style; + if outline_style == border_style::none { + return + } + + // Outlines are not accounted for in the dimensions of the border box, so adjust the + // absolute bounds. + let mut bounds = *bounds; + bounds.origin.x = bounds.origin.x - width; + bounds.origin.y = bounds.origin.y - width; + bounds.size.width = bounds.size.width + width + width; + bounds.size.height = bounds.size.height + width + width; + + // Append the outline to the display list. + let color = style.resolve_color(style.get_outline().outline_color).to_gfx_color(); + display_list.outlines.push_back(BorderDisplayItemClass(box BorderDisplayItem { + base: BaseDisplayItem::new(bounds, self.node, *clip_rect), + border_widths: SideOffsets2D::new_all_same(width), + color: SideOffsets2D::new_all_same(color), + style: SideOffsets2D::new_all_same(outline_style), + radius: Default::default(), + })) + } + fn build_debug_borders_around_text_fragments(&self, display_list: &mut DisplayList, flow_origin: Point2D, @@ -578,9 +618,7 @@ impl FragmentDisplayListBuilding for Fragment { } } - // Add a border, if applicable. - // - // TODO: Outlines. + // Add a border and outlines, if applicable. match self.inline_context { Some(ref inline_context) => { for style in inline_context.styles.iter().rev() { @@ -590,6 +628,11 @@ impl FragmentDisplayListBuilding for Fragment { &absolute_fragment_bounds, level, clip_rect); + self.build_display_list_for_outline_if_applicable( + &**style, + display_list, + &absolute_fragment_bounds, + clip_rect); } } None => {} @@ -603,6 +646,11 @@ impl FragmentDisplayListBuilding for Fragment { &absolute_fragment_bounds, level, clip_rect); + self.build_display_list_for_outline_if_applicable( + &*self.style, + display_list, + &absolute_fragment_bounds, + clip_rect); } } } @@ -870,8 +918,8 @@ impl BlockFlowDisplayListBuilding for BlockFlow { let stacking_context = self.create_stacking_context(display_list, Some(Arc::new(PaintLayer::new(self.layer_id(0), - transparent, - scroll_policy)))); + transparent, + scroll_policy)))); self.base.display_list_building_result = StackingContextResult(stacking_context) } diff --git a/components/layout/flow.rs b/components/layout/flow.rs index 598a8717cdb..832ca187f91 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -1177,6 +1177,11 @@ impl<'a> MutableFlowUtils for &'a mut Flow + 'a { /// Assumption: Absolute descendants have had their overflow calculated. fn store_overflow(self, _: &LayoutContext) { let my_position = mut_base(self).position; + + // FIXME(pcwalton): We should calculate overflow on a per-fragment basis, because their + // styles can affect overflow regions. Consider `box-shadow`, `outline`, etc.--anything + // that can draw outside the border box. For now we assume overflow is the border box, but + // that is wrong. let mut overflow = my_position; if self.is_block_container() { diff --git a/components/style/properties/mod.rs.mako b/components/style/properties/mod.rs.mako index 5dc02d89a9d..0c00c772871 100644 --- a/components/style/properties/mod.rs.mako +++ b/components/style/properties/mod.rs.mako @@ -355,6 +355,38 @@ pub mod longhands { % endfor + ${new_style_struct("Outline", is_inherited=False)} + + // TODO(pcwalton): `invert` + ${predefined_type("outline-color", "CSSColor", "CurrentColor")} + + <%self:single_component_value name="outline-style"> + pub use super::border_top_style::{get_initial_value, to_computed_value}; + pub type SpecifiedValue = super::border_top_style::SpecifiedValue; + pub mod computed_value { + pub type T = super::super::border_top_style::computed_value::T; + } + pub fn from_component_value(value: &ComponentValue, base_url: &Url) + -> Result { + match value { + &Ident(ref ident) if ident.eq_ignore_ascii_case("hidden") => { + // `hidden` is not a valid value. + Err(()) + } + _ => super::border_top_style::from_component_value(value, base_url) + } + } + + + <%self:longhand name="outline-width"> + pub use super::border_top_width::{get_initial_value, parse}; + pub use computed::compute_Au as to_computed_value; + pub type SpecifiedValue = super::border_top_width::SpecifiedValue; + pub mod computed_value { + pub type T = super::super::border_top_width::computed_value::T; + } + + ${new_style_struct("PositionOffsets", is_inherited=False)} % for side in ["top", "right", "bottom", "left"]: @@ -1544,6 +1576,52 @@ pub mod shorthands { }) + <%self:shorthand name="outline" sub_properties="outline-color outline-style outline-width"> + let (mut color, mut style, mut width, mut any) = (None, None, None, false); + for component_value in input.skip_whitespace() { + if color.is_none() { + match specified::CSSColor::parse(component_value) { + Ok(c) => { + color = Some(c); + any = true; + continue + } + Err(()) => {} + } + } + if style.is_none() { + match border_top_style::from_component_value(component_value, base_url) { + Ok(s) => { + style = Some(s); + any = true; + continue + } + Err(()) => {} + } + } + if width.is_none() { + match parse_border_width(component_value, base_url) { + Ok(w) => { + width = Some(w); + any = true; + continue + } + Err(()) => {} + } + } + return Err(()) + } + if any { + Ok(Longhands { + outline_color: color, + outline_style: style, + outline_width: width, + }) + } else { + Err(()) + } + + <%self:shorthand name="font" sub_properties="font-style font-variant font-weight font-size line-height font-family"> let mut iter = input.skip_whitespace(); diff --git a/tests/ref/basic.list b/tests/ref/basic.list index 52a115fa547..f06e562d94b 100644 --- a/tests/ref/basic.list +++ b/tests/ref/basic.list @@ -191,3 +191,5 @@ fragment=top != ../html/acid2.html acid2_ref.html == text_transform_uppercase_a.html text_transform_uppercase_ref.html == text_transform_lowercase_a.html text_transform_lowercase_ref.html == text_transform_capitalize_a.html text_transform_capitalize_ref.html +== outlines_simple_a.html outlines_simple_ref.html +== outlines_wrap_a.html outlines_wrap_ref.html diff --git a/tests/ref/outlines_simple_a.html b/tests/ref/outlines_simple_a.html new file mode 100644 index 00000000000..2dfc2e9a632 --- /dev/null +++ b/tests/ref/outlines_simple_a.html @@ -0,0 +1,21 @@ + + + + +
+ + + diff --git a/tests/ref/outlines_simple_ref.html b/tests/ref/outlines_simple_ref.html new file mode 100644 index 00000000000..994661d7851 --- /dev/null +++ b/tests/ref/outlines_simple_ref.html @@ -0,0 +1,27 @@ + + + + +
+ + + + diff --git a/tests/ref/outlines_wrap_a.html b/tests/ref/outlines_wrap_a.html new file mode 100644 index 00000000000..d34a797d456 --- /dev/null +++ b/tests/ref/outlines_wrap_a.html @@ -0,0 +1,18 @@ + + + + + +
I like truffles
+ + diff --git a/tests/ref/outlines_wrap_ref.html b/tests/ref/outlines_wrap_ref.html new file mode 100644 index 00000000000..db5c08893b5 --- /dev/null +++ b/tests/ref/outlines_wrap_ref.html @@ -0,0 +1,19 @@ + + + + + +
I like truffles
+ + +