mirror of
https://github.com/servo/servo.git
synced 2025-06-21 15:49:04 +01:00
layout: Perform text decoration propagation per CSS 2.1 § 16.3.1 without
going to the DOM.
This commit is contained in:
parent
7b3c3542a3
commit
a2e91d242b
13 changed files with 215 additions and 63 deletions
|
@ -92,12 +92,38 @@ pub struct SolidColorDisplayItem<E> {
|
|||
|
||||
/// Renders text.
|
||||
pub struct TextDisplayItem<E> {
|
||||
/// Fields common to all display items.
|
||||
base: BaseDisplayItem<E>,
|
||||
|
||||
/// 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<E> {
|
||||
base: BaseDisplayItem<E>,
|
||||
|
@ -184,18 +210,18 @@ impl<E> DisplayItem<E> {
|
|||
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));
|
||||
|
|
|
@ -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
|
||||
|
|
22
src/components/gfx/macros.rs
Normal file
22
src/components/gfx/macros.rs
Normal file
|
@ -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 }))
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<LayoutView>)
|
||||
-> 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<Au>,
|
||||
offset: &Point2D<Au>,
|
||||
offset: Point2D<Au>,
|
||||
flow: &Flow,
|
||||
list: &Cell<DisplayList<E>>) {
|
||||
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))
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -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<LayoutView>,
|
||||
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<Au>,
|
||||
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<LayoutView>) -> 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()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 }))
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
20
src/test/ref/text_decoration_propagation_a.html
Normal file
20
src/test/ref/text_decoration_propagation_a.html
Normal file
|
@ -0,0 +1,20 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>I CAN'T LIVE ANOTHER DAY WITHOUT AIR CONDITIONING!</title>
|
||||
<style>
|
||||
#a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id=a>
|
||||
hi
|
||||
<div id=b>
|
||||
there
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
23
src/test/ref/text_decoration_propagation_b.html
Normal file
23
src/test/ref/text_decoration_propagation_b.html
Normal file
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>I CAN'T LIVE ANOTHER DAY WITHOUT AIR CONDITIONING!</title>
|
||||
<style>
|
||||
#a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
#b {
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id=a>
|
||||
hi
|
||||
<div id=b>
|
||||
there
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue