layout: Explicitly thread border box dimensions and relative offsets

through display list building.

The old `flow_origin` concept was ill-defined (sometimes the border box
plus the flow origin, sometimes including horizontal margins and
sometimes not, sometimes including relative position and sometimes not),
leading to brittleness and test failures. This commit reworks the logic
to always pass border box origins in during display list building.
This commit is contained in:
Patrick Walton 2014-12-24 21:40:06 -06:00
parent 5ea2c6dcfd
commit bf540d590a
20 changed files with 591 additions and 456 deletions

View file

@ -609,6 +609,20 @@ impl ClippingRegion {
});
self
}
/// Translates this clipping region by the given vector.
#[inline]
pub fn translate(&self, delta: &Point2D<Au>) -> ClippingRegion {
ClippingRegion {
main: self.main.translate(delta),
complex: self.complex.iter().map(|complex| {
ComplexClippingRegion {
rect: complex.rect.translate(delta),
radii: complex.radii,
}
}).collect(),
}
}
}
/// Metadata attached to each display item. This is useful for performing auxiliary tasks with

View file

@ -32,7 +32,7 @@ use context::LayoutContext;
use css::node_style::StyledNode;
use display_list_builder::{BlockFlowDisplayListBuilding, FragmentDisplayListBuilding};
use floats::{ClearType, FloatKind, Floats, PlacementInfo};
use flow::{AbsolutePositionInfo, BaseFlow, ForceNonfloatedFlag, FlowClass, Flow};
use flow::{mod, AbsolutePositionInfo, BaseFlow, ForceNonfloatedFlag, FlowClass, Flow};
use flow::{ImmutableFlowUtils, MutableFlowUtils, PreorderFlowTraversal};
use flow::{PostorderFlowTraversal, mut_base};
use flow::{HAS_LEFT_FLOATED_DESCENDANTS, HAS_RIGHT_FLOATED_DESCENDANTS};
@ -40,8 +40,7 @@ use flow::{IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS};
use flow::{LAYERS_NEEDED_FOR_DESCENDANTS, NEEDS_LAYER};
use flow::{IS_ABSOLUTELY_POSITIONED};
use flow::{CLEARS_LEFT, CLEARS_RIGHT};
use flow;
use fragment::{Fragment, FragmentOverflowIterator, SpecificFragmentInfo};
use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo};
use incremental::{REFLOW, REFLOW_OUT_OF_FLOW};
use layout_debug;
use model::{IntrinsicISizes, MarginCollapseInfo};
@ -49,11 +48,11 @@ use model::{MaybeAuto, CollapsibleMargins, specified, specified_or_none};
use table::ColumnComputedInlineSize;
use wrapper::ThreadSafeLayoutNode;
use geom::{Rect, Size2D};
use geom::{Point2D, Rect, Size2D};
use gfx::display_list::{ClippingRegion, DisplayList};
use serialize::{Encoder, Encodable};
use servo_msg::compositor_msg::LayerId;
use servo_util::geometry::{Au, MAX_AU, ZERO_POINT};
use servo_util::geometry::{Au, MAX_AU};
use servo_util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize};
use servo_util::opts;
use std::cmp::{max, min};
@ -1769,23 +1768,40 @@ impl Flow for BlockFlow {
};
// Compute the origin and clipping rectangle for children.
let origin_for_children = if self.fragment.establishes_stacking_context() {
ZERO_POINT
let relative_offset = relative_offset.to_physical(self.base.writing_mode);
let origin_for_children;
let clip_in_child_coordinate_system;
if self.fragment.establishes_stacking_context() {
// We establish a stacking context, so the position of our children is vertically
// correct, but has to be adjusted to accommodate horizontal margins. (Note the
// calculation involving `position` below and recall that inline-direction flow
// positions are relative to the edges of the margin box.)
//
// FIXME(pcwalton): Is this vertical-writing-direction-safe?
let margin = self.fragment.margin.to_physical(self.base.writing_mode);
origin_for_children = Point2D(-margin.left, Au(0)) + relative_offset;
clip_in_child_coordinate_system =
self.base.clip.translate(&-self.base.stacking_relative_position)
} else {
self.base.stacking_relative_position
};
let clip = self.fragment.clipping_region_for_children(&self.base.clip,
&origin_for_children);
origin_for_children = self.base.stacking_relative_position + relative_offset;
clip_in_child_coordinate_system = self.base.clip.clone()
}
let stacking_relative_border_box =
self.fragment
.stacking_relative_border_box(&self.base.stacking_relative_position,
&self.base
.absolute_position_info
.relative_containing_block_size,
CoordinateSystem::Self);
let clip = self.fragment.clipping_region_for_children(&clip_in_child_coordinate_system,
&stacking_relative_border_box);
// Process children.
let writing_mode = self.base.writing_mode;
for kid in self.base.child_iter() {
if !flow::base(kid).flags.contains(IS_ABSOLUTELY_POSITIONED) {
let kid_base = flow::mut_base(kid);
kid_base.stacking_relative_position =
origin_for_children
+ kid_base.position.start.to_physical(kid_base.writing_mode, container_size)
+ relative_offset.to_physical(writing_mode);
kid_base.stacking_relative_position = origin_for_children +
kid_base.position.start.to_physical(kid_base.writing_mode, container_size);
}
flow::mut_base(kid).absolute_position_info = absolute_position_info_for_children;
@ -1832,16 +1848,20 @@ impl Flow for BlockFlow {
fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
if self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) &&
self.fragment.style().logical_position().inline_start == LengthOrPercentageOrAuto::Auto &&
self.fragment.style().logical_position().inline_end == LengthOrPercentageOrAuto::Auto {
self.fragment.style().logical_position().inline_start ==
LengthOrPercentageOrAuto::Auto &&
self.fragment.style().logical_position().inline_end ==
LengthOrPercentageOrAuto::Auto {
self.base.position.start.i = inline_position
}
}
fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
if self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) &&
self.fragment.style().logical_position().block_start == LengthOrPercentageOrAuto::Auto &&
self.fragment.style().logical_position().block_end == LengthOrPercentageOrAuto::Auto {
self.fragment.style().logical_position().block_start ==
LengthOrPercentageOrAuto::Auto &&
self.fragment.style().logical_position().block_end ==
LengthOrPercentageOrAuto::Auto {
self.base.position.start.b = block_position
}
}
@ -1861,16 +1881,32 @@ impl Flow for BlockFlow {
self.fragment.compute_overflow()
}
fn iterate_through_fragment_overflow(&self, iterator: &mut FragmentOverflowIterator) {
if iterator.should_process(&self.fragment) {
iterator.process(&self.fragment, self.fragment.compute_overflow());
fn iterate_through_fragment_border_boxes(&self,
iterator: &mut FragmentBorderBoxIterator,
stacking_context_position: &Point2D<Au>) {
if !iterator.should_process(&self.fragment) {
return
}
iterator.process(&self.fragment,
&self.fragment
.stacking_relative_border_box(&self.base.stacking_relative_position,
&self.base
.absolute_position_info
.relative_containing_block_size,
CoordinateSystem::Parent)
.translate(stacking_context_position));
}
}
impl fmt::Show for BlockFlow {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} - {:x}: frag={} ({})", self.class(), self.base.debug_id(), self.fragment, self.base)
write!(f,
"{} - {:x}: frag={} ({})",
self.class(),
self.base.debug_id(),
self.fragment,
self.base)
}
}

View file

@ -52,9 +52,9 @@ use servo_util::opts;
use std::collections::DList;
use std::mem;
use std::sync::atomic::Relaxed;
use style::ComputedValues;
use style::computed_values::{caption_side, display, empty_cells, float, list_style_position};
use style::computed_values::{position};
use style::{mod, ComputedValues};
use sync::Arc;
use url::Url;
@ -697,20 +697,38 @@ impl<'a> FlowConstructor<'a> {
node.restyle_damage()))
}
// If the value of `display` property is not `inline`, then we have a situation like
// `<div style="position:absolute">foo bar baz</div>`. The fragments for `foo`, `bar`, and
// `baz` had better not be absolutely positioned!
let mut style = (*node.style()).clone();
if style.get_box().display != display::inline {
style = Arc::new(style::make_inline(&*style))
}
// If this is generated content, then we need to initialize the accumulator with the
// fragment corresponding to that content. Otherwise, just initialize with the ordinary
// fragment that needs to be generated for this inline node.
let fragment = if node.get_pseudo_element_type() != PseudoElementType::Normal {
let fragment_info = SpecificFragmentInfo::UnscannedText(UnscannedTextFragmentInfo::new(node));
Fragment::new_from_specific_info(node, fragment_info)
let fragment_info =
SpecificFragmentInfo::UnscannedText(UnscannedTextFragmentInfo::new(node));
Fragment::from_opaque_node_and_style(
OpaqueNodeMethods::from_thread_safe_layout_node(node),
style,
node.restyle_damage(),
fragment_info)
} else {
Fragment::new(self, node)
Fragment::from_opaque_node_and_style(
OpaqueNodeMethods::from_thread_safe_layout_node(node),
style,
node.restyle_damage(),
self.build_specific_fragment_info_for_node(node))
};
let mut fragments = DList::new();
fragments.push_back(fragment);
let construction_item = ConstructionItem::InlineFragments(InlineFragmentsConstructionResult {
let construction_item =
ConstructionItem::InlineFragments(InlineFragmentsConstructionResult {
splits: DList::new(),
fragments: fragments,
abs_descendants: Descendants::new(),
@ -726,13 +744,15 @@ impl<'a> FlowConstructor<'a> {
_ => unreachable!()
};
let fragment_info = SpecificFragmentInfo::InlineBlock(InlineBlockFragmentInfo::new(block_flow));
let fragment_info = SpecificFragmentInfo::InlineBlock(InlineBlockFragmentInfo::new(
block_flow));
let fragment = Fragment::new_from_specific_info(node, fragment_info);
let mut fragment_accumulator = InlineFragmentsAccumulator::from_inline_node(node);
fragment_accumulator.fragments.push_back(fragment);
let construction_item = ConstructionItem::InlineFragments(InlineFragmentsConstructionResult {
let construction_item =
ConstructionItem::InlineFragments(InlineFragmentsConstructionResult {
splits: DList::new(),
fragments: fragment_accumulator.to_dlist(),
abs_descendants: abs_descendants,
@ -757,7 +777,8 @@ impl<'a> FlowConstructor<'a> {
let mut fragment_accumulator = InlineFragmentsAccumulator::from_inline_node(node);
fragment_accumulator.fragments.push_back(fragment);
let construction_item = ConstructionItem::InlineFragments(InlineFragmentsConstructionResult {
let construction_item =
ConstructionItem::InlineFragments(InlineFragmentsConstructionResult {
splits: DList::new(),
fragments: fragment_accumulator.to_dlist(),
abs_descendants: abs_descendants,

View file

@ -13,8 +13,9 @@
use block::BlockFlow;
use context::LayoutContext;
use flow::{mod, Flow, IS_ABSOLUTELY_POSITIONED, NEEDS_LAYER};
use fragment::{Fragment, SpecificFragmentInfo, IframeFragmentInfo, ImageFragmentInfo};
use fragment::ScannedTextFragmentInfo;
use fragment::{CoordinateSystem, Fragment, IframeFragmentInfo, ImageFragmentInfo};
use fragment::{ScannedTextFragmentInfo, SpecificFragmentInfo};
use inline::InlineFlow;
use list_item::ListItemFlow;
use model;
use util::{OpaqueNodeMethods, ToGfxColor};
@ -36,8 +37,8 @@ use servo_msg::constellation_msg::Msg as ConstellationMsg;
use servo_msg::constellation_msg::ConstellationChan;
use servo_net::image::holder::ImageHolder;
use servo_util::cursor::{DefaultCursor, TextCursor, VerticalTextCursor};
use servo_util::geometry::{mod, Au, ZERO_POINT};
use servo_util::logical_geometry::{LogicalRect, WritingMode};
use servo_util::geometry::{mod, Au};
use servo_util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize};
use servo_util::opts;
use std::default::Default;
use std::num::FloatMath;
@ -133,16 +134,19 @@ pub trait FragmentDisplayListBuilding {
absolute_bounds: &Rect<Au>,
clip: &ClippingRegion);
/// Adds display items necessary to draw debug boxes around a scanned text fragment.
fn build_debug_borders_around_text_fragments(&self,
style: &ComputedValues,
display_list: &mut DisplayList,
flow_origin: Point2D<Au>,
stacking_relative_border_box: &Rect<Au>,
stacking_relative_content_box: &Rect<Au>,
text_fragment: &ScannedTextFragmentInfo,
clip: &ClippingRegion);
/// Adds display items necessary to draw debug boxes around this fragment.
fn build_debug_borders_around_fragment(&self,
display_list: &mut DisplayList,
flow_origin: Point2D<Au>,
stacking_relative_border_box: &Rect<Au>,
clip: &ClippingRegion);
/// Adds the display items for this fragment to the given display list.
@ -152,12 +156,16 @@ pub trait FragmentDisplayListBuilding {
/// * `display_list`: The 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.
/// * `stacking_relative_flow_origin`: Position of the origin of the owning flow with respect
/// to its nearest ancestor stacking context.
/// * `relative_containing_block_size`: The size of the containing block that
/// `position: relative` makes use of.
/// * `clip`: The region to clip the display items to.
fn build_display_list(&mut self,
display_list: &mut DisplayList,
layout_context: &LayoutContext,
flow_origin: Point2D<Au>,
stacking_relative_flow_origin: &Point2D<Au>,
relative_containing_block_size: &LogicalSize<Au>,
background_and_border_level: BackgroundAndBorderLevel,
clip: &ClippingRegion);
@ -168,12 +176,17 @@ pub trait FragmentDisplayListBuilding {
offset: Point2D<Au>,
layout_context: &LayoutContext);
fn clipping_region_for_children(&self, current_clip: &ClippingRegion, flow_origin: &Point2D<Au>)
/// Returns the appropriate clipping region for descendants of this flow.
fn clipping_region_for_children(&self,
current_clip: &ClippingRegion,
stacking_relative_border_box: &Rect<Au>)
-> ClippingRegion;
/// Calculates the clipping rectangle for a fragment, taking the `clip` property into account
/// per CSS 2.1 § 11.1.2.
fn calculate_style_specified_clip(&self, parent_clip: &ClippingRegion, origin: &Point2D<Au>)
fn calculate_style_specified_clip(&self,
parent_clip: &ClippingRegion,
stacking_relative_border_box: &Rect<Au>)
-> ClippingRegion;
/// Creates the text display item for one text fragment.
@ -181,23 +194,20 @@ pub trait FragmentDisplayListBuilding {
display_list: &mut DisplayList,
text_fragment: &ScannedTextFragmentInfo,
text_color: RGBA,
offset: &Point2D<Au>,
flow_origin: &Point2D<Au>,
stacking_relative_content_box: &Rect<Au>,
clip: &ClippingRegion);
/// Creates the display item for a text decoration: underline, overline, or line-through.
fn build_display_list_for_text_decoration(&self,
display_list: &mut DisplayList,
color: RGBA,
flow_origin: &Point2D<Au>,
clip: &ClippingRegion,
logical_bounds: &LogicalRect<Au>,
offset: &Point2D<Au>);
color: &RGBA,
stacking_relative_box: &LogicalRect<Au>,
clip: &ClippingRegion);
/// A helper method that `build_display_list` calls to create per-fragment-type display items.
fn build_fragment_type_specific_display_items(&mut self,
display_list: &mut DisplayList,
flow_origin: Point2D<Au>,
stacking_relative_border_box: &Rect<Au>,
clip: &ClippingRegion);
}
@ -580,20 +590,16 @@ impl FragmentDisplayListBuilding for Fragment {
fn build_debug_borders_around_text_fragments(&self,
style: &ComputedValues,
display_list: &mut DisplayList,
flow_origin: Point2D<Au>,
stacking_relative_border_box: &Rect<Au>,
stacking_relative_content_box: &Rect<Au>,
text_fragment: &ScannedTextFragmentInfo,
clip: &ClippingRegion) {
// FIXME(#2795): Get the real container size
// FIXME(pcwalton, #2795): Get the real container size.
let container_size = Size2D::zero();
// Fragment position wrt to the owning flow.
let fragment_bounds = self.border_box.to_physical(self.style.writing_mode, container_size);
let absolute_fragment_bounds = Rect(
fragment_bounds.origin + flow_origin,
fragment_bounds.size);
// Compute the text fragment bounds and draw a border surrounding them.
display_list.content.push_back(DisplayItem::BorderClass(box BorderDisplayItem {
base: BaseDisplayItem::new(absolute_fragment_bounds,
base: BaseDisplayItem::new(*stacking_relative_border_box,
DisplayItemMetadata::new(self.node, style, DefaultCursor),
(*clip).clone()),
border_widths: SideOffsets2D::new_all_same(Au::from_px(1)),
@ -603,12 +609,12 @@ impl FragmentDisplayListBuilding for Fragment {
}));
// Draw a rectangle representing the baselines.
let ascent = text_fragment.run.ascent();
let mut baseline = self.border_box.clone();
baseline.start.b = baseline.start.b + ascent;
let mut baseline = LogicalRect::from_physical(self.style.writing_mode,
*stacking_relative_content_box,
container_size);
baseline.start.b = baseline.start.b + text_fragment.run.ascent();
baseline.size.block = Au(0);
let mut baseline = baseline.to_physical(self.style.writing_mode, container_size);
baseline.origin = baseline.origin + flow_origin;
let baseline = baseline.to_physical(self.style.writing_mode, container_size);
let line_display_item = box LineDisplayItem {
base: BaseDisplayItem::new(baseline,
@ -622,19 +628,11 @@ impl FragmentDisplayListBuilding for Fragment {
fn build_debug_borders_around_fragment(&self,
display_list: &mut DisplayList,
flow_origin: Point2D<Au>,
stacking_relative_border_box: &Rect<Au>,
clip: &ClippingRegion) {
// FIXME(#2795): Get the real container size
let container_size = Size2D::zero();
// Fragment position wrt to the owning flow.
let fragment_bounds = self.border_box.to_physical(self.style.writing_mode, container_size);
let absolute_fragment_bounds = Rect(
fragment_bounds.origin + flow_origin,
fragment_bounds.size);
// This prints a debug border around the border of this fragment.
display_list.content.push_back(DisplayItem::BorderClass(box BorderDisplayItem {
base: BaseDisplayItem::new(absolute_fragment_bounds,
base: BaseDisplayItem::new(*stacking_relative_border_box,
DisplayItemMetadata::new(self.node,
&*self.style,
DefaultCursor),
@ -646,7 +644,9 @@ impl FragmentDisplayListBuilding for Fragment {
}));
}
fn calculate_style_specified_clip(&self, parent_clip: &ClippingRegion, origin: &Point2D<Au>)
fn calculate_style_specified_clip(&self,
parent_clip: &ClippingRegion,
stacking_relative_border_box: &Rect<Au>)
-> ClippingRegion {
// Account for `clip` per CSS 2.1 § 11.1.2.
let style_clip_rect = match (self.style().get_box().position,
@ -656,54 +656,49 @@ impl FragmentDisplayListBuilding for Fragment {
};
// FIXME(pcwalton, #2795): Get the real container size.
let border_box = self.border_box.to_physical(self.style.writing_mode, Size2D::zero());
let clip_origin = Point2D(border_box.origin.x + style_clip_rect.left,
border_box.origin.y + style_clip_rect.top);
let new_clip_rect =
Rect(clip_origin + *origin,
Size2D(style_clip_rect.right.unwrap_or(border_box.size.width) - clip_origin.x,
style_clip_rect.bottom.unwrap_or(border_box.size.height) - clip_origin.y));
(*parent_clip).clone().intersect_rect(&new_clip_rect)
let clip_origin = Point2D(stacking_relative_border_box.origin.x + style_clip_rect.left,
stacking_relative_border_box.origin.y + style_clip_rect.top);
let right = style_clip_rect.right.unwrap_or(stacking_relative_border_box.size.width);
let bottom = style_clip_rect.bottom.unwrap_or(stacking_relative_border_box.size.height);
let clip_size = Size2D(right - clip_origin.x, bottom - clip_origin.y);
(*parent_clip).clone().intersect_rect(&Rect(clip_origin, clip_size))
}
fn build_display_list(&mut self,
display_list: &mut DisplayList,
layout_context: &LayoutContext,
flow_origin: Point2D<Au>,
stacking_relative_flow_origin: &Point2D<Au>,
relative_containing_block_size: &LogicalSize<Au>,
background_and_border_level: BackgroundAndBorderLevel,
clip: &ClippingRegion) {
// Compute the fragment position relative to the parent stacking context. If the fragment
// itself establishes a stacking context, then the origin of its position will be (0, 0)
// for the purposes of this computation.
let stacking_relative_flow_origin = if self.establishes_stacking_context() {
ZERO_POINT
} else {
flow_origin
};
let absolute_fragment_bounds =
self.stacking_relative_bounds(&stacking_relative_flow_origin);
let stacking_relative_border_box =
self.stacking_relative_border_box(stacking_relative_flow_origin,
relative_containing_block_size,
CoordinateSystem::Self);
debug!("Fragment::build_display_list at rel={}, abs={}: {}",
debug!("Fragment::build_display_list at rel={}, abs={}, dirty={}, flow origin={}: {}",
self.border_box,
absolute_fragment_bounds,
self);
debug!("Fragment::build_display_list: dirty={}, flow_origin={}",
stacking_relative_border_box,
layout_context.shared.dirty,
flow_origin);
stacking_relative_flow_origin,
self);
if self.style().get_inheritedbox().visibility != visibility::visible {
return
}
if !absolute_fragment_bounds.intersects(&layout_context.shared.dirty) {
if !stacking_relative_border_box.intersects(&layout_context.shared.dirty) {
debug!("Fragment::build_display_list: Did not intersect...");
return
}
// Calculate the clip rect. If there's nothing to render at all, don't even construct
// display list items.
let clip = self.calculate_style_specified_clip(clip, &absolute_fragment_bounds.origin);
if !clip.might_intersect_rect(&absolute_fragment_bounds) {
let clip = self.calculate_style_specified_clip(clip, &stacking_relative_border_box);
if !clip.might_intersect_rect(&stacking_relative_border_box) {
return;
}
@ -713,14 +708,33 @@ impl FragmentDisplayListBuilding for Fragment {
let level =
StackingLevel::from_background_and_border_level(background_and_border_level);
// Add a shadow to the list, if applicable.
// Add shadows, background, borders, and outlines, if applicable.
if let Some(ref inline_context) = self.inline_context {
for style in inline_context.styles.iter().rev() {
self.build_display_list_for_box_shadow_if_applicable(&**style,
self.build_display_list_for_box_shadow_if_applicable(
&**style,
display_list,
layout_context,
level,
&absolute_fragment_bounds,
&stacking_relative_border_box,
&clip);
self.build_display_list_for_background_if_applicable(
&**style,
display_list,
layout_context,
level,
&stacking_relative_border_box,
&clip);
self.build_display_list_for_borders_if_applicable(
&**style,
display_list,
&stacking_relative_border_box,
level,
&clip);
self.build_display_list_for_outline_if_applicable(
&**style,
display_list,
&stacking_relative_border_box,
&clip);
}
}
@ -729,62 +743,35 @@ impl FragmentDisplayListBuilding for Fragment {
display_list,
layout_context,
level,
&absolute_fragment_bounds,
&stacking_relative_border_box,
&clip);
}
// Add the background to the list, if applicable.
if let Some(ref inline_context) = self.inline_context {
for style in inline_context.styles.iter().rev() {
self.build_display_list_for_background_if_applicable(&**style,
display_list,
layout_context,
level,
&absolute_fragment_bounds,
&clip);
}
}
if !self.is_scanned_text_fragment() {
self.build_display_list_for_background_if_applicable(&*self.style,
display_list,
layout_context,
level,
&absolute_fragment_bounds,
&stacking_relative_border_box,
&clip);
}
// Add a border and outlines, if applicable.
if let Some(ref inline_context) = self.inline_context {
for style in inline_context.styles.iter().rev() {
self.build_display_list_for_borders_if_applicable(&**style,
display_list,
&absolute_fragment_bounds,
level,
&clip);
self.build_display_list_for_outline_if_applicable(&**style,
display_list,
&absolute_fragment_bounds,
&clip);
}
}
if !self.is_scanned_text_fragment() {
self.build_display_list_for_borders_if_applicable(&*self.style,
display_list,
&absolute_fragment_bounds,
&stacking_relative_border_box,
level,
&clip);
self.build_display_list_for_outline_if_applicable(&*self.style,
display_list,
&absolute_fragment_bounds,
&stacking_relative_border_box,
&clip);
}
}
// Create special per-fragment-type display items.
self.build_fragment_type_specific_display_items(display_list, flow_origin, &clip);
self.build_fragment_type_specific_display_items(display_list,
&stacking_relative_border_box,
&clip);
if opts::get().show_debug_fragment_borders {
self.build_debug_borders_around_fragment(display_list, flow_origin, &clip)
self.build_debug_borders_around_fragment(display_list,
&stacking_relative_border_box,
&clip)
}
// If this is an iframe, then send its position and size up to the constellation.
@ -799,31 +786,18 @@ impl FragmentDisplayListBuilding for Fragment {
// the iframe is actually going to be displayed.
if let SpecificFragmentInfo::Iframe(ref iframe_fragment) = self.specific {
self.finalize_position_and_size_of_iframe(&**iframe_fragment,
absolute_fragment_bounds.origin,
stacking_relative_border_box.origin,
layout_context)
}
}
fn build_fragment_type_specific_display_items(&mut self,
display_list: &mut DisplayList,
flow_origin: Point2D<Au>,
stacking_relative_border_box: &Rect<Au>,
clip: &ClippingRegion) {
// Compute the fragment position relative to the parent stacking context. If the fragment
// itself establishes a stacking context, then the origin of its position will be (0, 0)
// for the purposes of this computation.
let stacking_relative_flow_origin = if self.establishes_stacking_context() {
ZERO_POINT
} else {
flow_origin
};
// FIXME(#2795): Get the real container size.
let content_box = self.content_box();
let container_size = Size2D::zero();
let rect_to_absolute = |writing_mode: WritingMode, logical_rect: LogicalRect<Au>| {
let physical_rect = logical_rect.to_physical(writing_mode, container_size);
Rect(physical_rect.origin + stacking_relative_flow_origin, physical_rect.size)
};
// Compute the context box position relative to the parent stacking context.
let stacking_relative_content_box =
self.stacking_relative_content_box(stacking_relative_border_box);
match self.specific {
SpecificFragmentInfo::UnscannedText(_) => {
@ -838,16 +812,16 @@ impl FragmentDisplayListBuilding for Fragment {
self.build_display_list_for_text_fragment(display_list,
&**text_fragment,
text_color,
&flow_origin,
&Point2D(Au(0), Au(0)),
&stacking_relative_content_box,
clip);
if opts::get().show_debug_fragment_borders {
self.build_debug_borders_around_text_fragments(self.style(),
display_list,
flow_origin,
stacking_relative_border_box,
&stacking_relative_content_box,
&**text_fragment,
clip);
clip)
}
}
SpecificFragmentInfo::Generic |
@ -859,25 +833,25 @@ impl FragmentDisplayListBuilding for Fragment {
SpecificFragmentInfo::InlineBlock(_) |
SpecificFragmentInfo::InlineAbsoluteHypothetical(_) => {
if opts::get().show_debug_fragment_borders {
self.build_debug_borders_around_fragment(display_list, flow_origin, clip);
self.build_debug_borders_around_fragment(display_list,
stacking_relative_border_box,
clip);
}
}
SpecificFragmentInfo::Image(ref mut image_fragment) => {
let image_ref = &mut image_fragment.image;
if let Some(image) = image_ref.get_image(self.node.to_untrusted_node_address()) {
debug!("(building display list) building image fragment");
let absolute_content_box = rect_to_absolute(self.style.writing_mode,
content_box);
// Place the image into the display list.
display_list.content.push_back(DisplayItem::ImageClass(box ImageDisplayItem {
base: BaseDisplayItem::new(absolute_content_box,
base: BaseDisplayItem::new(stacking_relative_content_box,
DisplayItemMetadata::new(self.node,
&*self.style,
DefaultCursor),
(*clip).clone()),
image: image.clone(),
stretch_size: absolute_content_box.size,
stretch_size: stacking_relative_content_box.size,
}));
} else {
// No image data at all? Do nothing.
@ -910,7 +884,9 @@ impl FragmentDisplayListBuilding for Fragment {
iframe_rect));
}
fn clipping_region_for_children(&self, current_clip: &ClippingRegion, flow_origin: &Point2D<Au>)
fn clipping_region_for_children(&self,
current_clip: &ClippingRegion,
stacking_relative_border_box: &Rect<Au>)
-> ClippingRegion {
// Don't clip if we're text.
if self.is_scanned_text_fragment() {
@ -918,27 +894,24 @@ impl FragmentDisplayListBuilding for Fragment {
}
// Account for style-specified `clip`.
let current_clip = self.calculate_style_specified_clip(current_clip, flow_origin);
let current_clip = self.calculate_style_specified_clip(current_clip,
stacking_relative_border_box);
// Only clip if `overflow` tells us to.
match self.style.get_box().overflow {
overflow::hidden | overflow::auto | overflow::scroll => {}
_ => return current_clip,
}
overflow::hidden | overflow::auto | overflow::scroll => {
// Create a new clip rect.
//
// FIXME(#2795): Get the real container size.
let physical_rect = self.border_box.to_physical(self.style.writing_mode, Size2D::zero());
current_clip.intersect_rect(&Rect(physical_rect.origin + *flow_origin, physical_rect.size))
current_clip.intersect_rect(stacking_relative_border_box)
}
_ => current_clip,
}
}
fn build_display_list_for_text_fragment(&self,
display_list: &mut DisplayList,
text_fragment: &ScannedTextFragmentInfo,
text_color: RGBA,
flow_origin: &Point2D<Au>,
offset: &Point2D<Au>,
stacking_relative_content_box: &Rect<Au>,
clip: &ClippingRegion) {
// Determine the orientation and cursor to use.
let (orientation, cursor) = if self.style.writing_mode.is_vertical() {
@ -955,29 +928,16 @@ impl FragmentDisplayListBuilding for Fragment {
//
// FIXME(pcwalton): Get the real container size.
let container_size = Size2D::zero();
let content_box = self.content_box();
let metrics = &text_fragment.run.font_metrics;
let baseline_origin = {
let mut content_box_start = content_box.start;
content_box_start.b = content_box_start.b + metrics.ascent;
content_box_start.to_physical(self.style.writing_mode, container_size) + *flow_origin +
*offset
};
let stacking_relative_flow_origin = if self.establishes_stacking_context() {
ZERO_POINT
} else {
*flow_origin
};
let rect_to_absolute = |writing_mode: WritingMode, logical_rect: LogicalRect<Au>| {
let physical_rect = logical_rect.to_physical(writing_mode, container_size);
Rect(physical_rect.origin + stacking_relative_flow_origin, physical_rect.size)
};
let content_rect = rect_to_absolute(self.style.writing_mode,
content_box).translate(offset);
let baseline_origin = stacking_relative_content_box.origin +
LogicalPoint::new(self.style.writing_mode,
Au(0),
metrics.ascent).to_physical(self.style.writing_mode,
container_size);
// Create the text display item.
display_list.content.push_back(DisplayItem::TextClass(box TextDisplayItem {
base: BaseDisplayItem::new(content_rect,
base: BaseDisplayItem::new(*stacking_relative_content_box,
DisplayItemMetadata::new(self.node, self.style(), cursor),
(*clip).clone()),
text_run: text_fragment.run.clone(),
@ -989,63 +949,55 @@ impl FragmentDisplayListBuilding for Fragment {
// Create display items for text decorations.
let text_decorations = self.style().get_inheritedtext()._servo_text_decorations_in_effect;
if let Some(underline_color) = text_decorations.underline {
let mut rect = content_box.clone();
rect.start.b = rect.start.b + metrics.ascent - metrics.underline_offset;
rect.size.block = metrics.underline_size;
let stacking_relative_content_box =
LogicalRect::from_physical(self.style.writing_mode,
*stacking_relative_content_box,
container_size);
if let Some(ref underline_color) = text_decorations.underline {
let mut stacking_relative_box = stacking_relative_content_box;
stacking_relative_box.start.b = stacking_relative_content_box.start.b +
metrics.ascent - metrics.underline_offset;
stacking_relative_box.size.block = metrics.underline_size;
self.build_display_list_for_text_decoration(display_list,
underline_color,
flow_origin,
clip,
&rect,
offset)
&stacking_relative_box,
clip)
}
if let Some(overline_color) = text_decorations.overline {
let mut rect = content_box.clone();
rect.size.block = metrics.underline_size;
if let Some(ref overline_color) = text_decorations.overline {
let mut stacking_relative_box = stacking_relative_content_box;
stacking_relative_box.size.block = metrics.underline_size;
self.build_display_list_for_text_decoration(display_list,
overline_color,
flow_origin,
clip,
&rect,
offset)
&stacking_relative_box,
clip)
}
if let Some(line_through_color) = text_decorations.line_through {
let mut rect = content_box.clone();
rect.start.b = rect.start.b + metrics.ascent - metrics.strikeout_offset;
rect.size.block = metrics.strikeout_size;
if let Some(ref line_through_color) = text_decorations.line_through {
let mut stacking_relative_box = stacking_relative_content_box;
stacking_relative_box.start.b = stacking_relative_box.start.b + metrics.ascent -
metrics.strikeout_offset;
stacking_relative_box.size.block = metrics.strikeout_size;
self.build_display_list_for_text_decoration(display_list,
line_through_color,
flow_origin,
clip,
&rect,
offset)
&stacking_relative_box,
clip)
}
}
fn build_display_list_for_text_decoration(&self,
display_list: &mut DisplayList,
color: RGBA,
flow_origin: &Point2D<Au>,
clip: &ClippingRegion,
logical_bounds: &LogicalRect<Au>,
offset: &Point2D<Au>) {
// FIXME(pcwalton): Get the real container size.
color: &RGBA,
stacking_relative_box: &LogicalRect<Au>,
clip: &ClippingRegion) {
// FIXME(pcwalton, #2795): Get the real container size.
let container_size = Size2D::zero();
let stacking_relative_flow_origin = if self.establishes_stacking_context() {
ZERO_POINT
} else {
*flow_origin
};
let physical_rect = logical_bounds.to_physical(self.style.writing_mode, container_size);
let stacking_relative_box = stacking_relative_box.to_physical(self.style.writing_mode,
container_size);
let bounds = Rect(physical_rect.origin + stacking_relative_flow_origin,
physical_rect.size).translate(offset);
let metadata = DisplayItemMetadata::new(self.node, &*self.style, DefaultCursor);
display_list.content.push_back(DisplayItem::SolidColorClass(box SolidColorDisplayItem {
base: BaseDisplayItem::new(bounds, metadata, (*clip).clone()),
base: BaseDisplayItem::new(stacking_relative_box, metadata, (*clip).clone()),
color: color.to_gfx_color(),
}))
}
@ -1081,14 +1033,16 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
layout_context: &LayoutContext,
background_border_level: BackgroundAndBorderLevel) {
// Add the box that starts the block context.
let stacking_relative_fragment_origin =
self.base.stacking_relative_position_of_child_fragment(&self.fragment);
self.fragment.build_display_list(display_list,
layout_context,
stacking_relative_fragment_origin,
&self.base.stacking_relative_position,
&self.base
.absolute_position_info
.relative_containing_block_size,
background_border_level,
&self.base.clip);
// Add children.
for kid in self.base.children.iter_mut() {
flow::mut_base(kid).display_list_building_result.add_to(display_list);
}
@ -1180,19 +1134,70 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
display_list: Box<DisplayList>,
layer: Option<Arc<PaintLayer>>)
-> Arc<StackingContext> {
let size = self.base.position.size.to_physical(self.base.writing_mode);
let bounds = Rect(self.base.stacking_relative_position, size);
let z_index = self.fragment.style().get_box().z_index.number_or_zero();
let opacity = self.fragment.style().get_effects().opacity as f32;
debug_assert!(self.fragment.establishes_stacking_context());
let border_box = self.fragment
.stacking_relative_border_box(&self.base.stacking_relative_position,
&self.base
.absolute_position_info
.relative_containing_block_size,
CoordinateSystem::Parent);
// FIXME(pcwalton): Is this vertical-writing-direction-safe?
let margin = self.fragment.margin.to_physical(self.base.writing_mode);
let overflow = self.base.overflow.translate(&-Point2D(margin.left, Au(0)));
Arc::new(StackingContext::new(display_list,
&bounds,
&self.base.overflow,
z_index,
opacity,
&border_box,
&overflow,
self.fragment.style().get_box().z_index.number_or_zero(),
self.fragment.style().get_effects().opacity as f32,
layer))
}
}
pub trait InlineFlowDisplayListBuilding {
fn build_display_list_for_inline(&mut self, layout_context: &LayoutContext);
}
impl InlineFlowDisplayListBuilding for InlineFlow {
fn build_display_list_for_inline(&mut self, layout_context: &LayoutContext) {
// TODO(#228): Once we form lines and have their cached bounds, we can be smarter and
// not recurse on a line if nothing in it can intersect the dirty region.
debug!("Flow: building display list for {:u} inline fragments", self.fragments.len());
let mut display_list = box DisplayList::new();
for fragment in self.fragments.fragments.iter_mut() {
fragment.build_display_list(&mut *display_list,
layout_context,
&self.base.stacking_relative_position,
&self.base
.absolute_position_info
.relative_containing_block_size,
BackgroundAndBorderLevel::Content,
&self.base.clip);
match fragment.specific {
SpecificFragmentInfo::InlineBlock(ref mut block_flow) => {
let block_flow = block_flow.flow_ref.deref_mut();
flow::mut_base(block_flow).display_list_building_result
.add_to(&mut *display_list)
}
SpecificFragmentInfo::InlineAbsoluteHypothetical(ref mut block_flow) => {
let block_flow = block_flow.flow_ref.deref_mut();
flow::mut_base(block_flow).display_list_building_result
.add_to(&mut *display_list)
}
_ => {}
}
}
self.base.display_list_building_result = DisplayListBuildingResult::Normal(display_list);
if opts::get().validate_display_list_geometry {
self.base.validate_display_list_geometry();
}
}
}
pub trait ListItemFlowDisplayListBuilding {
fn build_display_list_for_list_item(&mut self,
display_list: Box<DisplayList>,
@ -1204,18 +1209,17 @@ impl ListItemFlowDisplayListBuilding for ListItemFlow {
mut display_list: Box<DisplayList>,
layout_context: &LayoutContext) {
// Draw the marker, if applicable.
match self.marker {
None => {}
Some(ref mut marker) => {
let stacking_relative_fragment_origin =
self.block_flow.base.stacking_relative_position_of_child_fragment(marker);
if let Some(ref mut marker) = self.marker {
marker.build_display_list(&mut *display_list,
layout_context,
stacking_relative_fragment_origin,
&self.block_flow.base.stacking_relative_position,
&self.block_flow
.base
.absolute_position_info
.relative_containing_block_size,
BackgroundAndBorderLevel::Content,
&self.block_flow.base.clip);
}
}
// Draw the rest of the block.
self.block_flow.build_display_list_for_block(display_list, layout_context)
@ -1292,3 +1296,4 @@ impl StackingContextConstruction for DisplayList {
}
}
}

View file

@ -32,7 +32,7 @@ use display_list_builder::DisplayListBuildingResult;
use floats::Floats;
use flow_list::{FlowList, FlowListIterator, MutFlowListIterator};
use flow_ref::FlowRef;
use fragment::{Fragment, FragmentOverflowIterator, SpecificFragmentInfo};
use fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo};
use incremental::{RECONSTRUCT_FLOW, REFLOW, REFLOW_OUT_OF_FLOW, RestyleDamage};
use inline::InlineFlow;
use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo};
@ -223,8 +223,10 @@ pub trait Flow: fmt::Show + ToString + Sync {
/// Returns the union of all overflow rects of all of this flow's fragments.
fn compute_overflow(&self) -> Rect<Au>;
/// Iterates through overflow rects of all of this flow's fragments.
fn iterate_through_fragment_overflow(&self, iterator: &mut FragmentOverflowIterator);
/// Iterates through border boxes of all of this flow's fragments.
fn iterate_through_fragment_border_boxes(&self,
iterator: &mut FragmentBorderBoxIterator,
stacking_context_position: &Point2D<Au>);
fn compute_collapsible_block_start_margin(&mut self,
_layout_context: &mut LayoutContext,
@ -958,17 +960,6 @@ impl BaseFlow {
}
}
}
/// Returns the position of the given fragment relative to the start of the nearest ancestor
/// stacking context. The fragment must be a child fragment of this flow.
pub fn stacking_relative_position_of_child_fragment(&self, fragment: &Fragment)
-> Point2D<Au> {
let relative_offset =
fragment.relative_position(&self
.absolute_position_info
.relative_containing_block_size);
self.stacking_relative_position.add_size(&relative_offset.to_physical(self.writing_mode))
}
}
impl<'a> ImmutableFlowUtils for &'a Flow + 'a {
@ -1065,13 +1056,13 @@ impl<'a> ImmutableFlowUtils for &'a Flow + 'a {
let flow = match self.class() {
FlowClass::Table | FlowClass::TableRowGroup => {
let fragment =
Fragment::new_anonymous_table_fragment(node,
Fragment::new_anonymous_from_specific_info(node,
SpecificFragmentInfo::TableRow);
box TableRowFlow::from_node_and_fragment(node, fragment) as Box<Flow>
},
FlowClass::TableRow => {
let fragment =
Fragment::new_anonymous_table_fragment(node,
Fragment::new_anonymous_from_specific_info(node,
SpecificFragmentInfo::TableCell);
let hide = node.style().get_inheritedtable().empty_cells == empty_cells::hide;
box TableCellFlow::from_node_fragment_and_visibility_flag(node, fragment, !hide) as

View file

@ -31,8 +31,7 @@ use serialize::{Encodable, Encoder};
use servo_msg::constellation_msg::{PipelineId, SubpageId};
use servo_net::image::holder::ImageHolder;
use servo_net::local_image_cache::LocalImageCache;
use servo_util::geometry::Au;
use servo_util::geometry;
use servo_util::geometry::{mod, Au, ZERO_POINT};
use servo_util::logical_geometry::{LogicalRect, LogicalSize, LogicalMargin};
use servo_util::range::*;
use servo_util::smallvec::SmallVec;
@ -62,8 +61,8 @@ use url::Url;
/// positioned as if it were a block fragment, but its children are positioned according to
/// inline flow.
///
/// A `SpecificFragmentInfo::Generic` is an empty fragment that contributes only borders, margins, padding, and
/// backgrounds. It is analogous to a CSS nonreplaced content box.
/// A `SpecificFragmentInfo::Generic` is an empty fragment that contributes only borders, margins,
/// padding, and backgrounds. It is analogous to a CSS nonreplaced content box.
///
/// A fragment's type influences how its styles are interpreted during layout. For example,
/// replaced content such as images are resized differently from tables, text, or other content.
@ -83,8 +82,10 @@ pub struct Fragment {
/// The CSS style of this fragment.
pub style: Arc<ComputedValues>,
/// The position of this fragment relative to its owning flow.
/// The size includes padding and border, but not margin.
/// The position of this fragment relative to its owning flow. The size includes padding and
/// border, but not margin.
///
/// NB: This does not account for relative positioning.
pub border_box: LogicalRect<Au>,
/// The sum of border and padding; i.e. the distance from the edge of the border box to the
@ -520,8 +521,26 @@ impl Fragment {
}
}
/// Constructs a new `Fragment` instance for an anonymous object.
pub fn new_anonymous(constructor: &mut FlowConstructor, node: &ThreadSafeLayoutNode)
-> Fragment {
let node_style = cascade_anonymous(&**node.style());
let writing_mode = node_style.writing_mode;
Fragment {
node: OpaqueNodeMethods::from_thread_safe_layout_node(node),
style: Arc::new(node_style),
restyle_damage: node.restyle_damage(),
border_box: LogicalRect::zero(writing_mode),
border_padding: LogicalMargin::zero(writing_mode),
margin: LogicalMargin::zero(writing_mode),
specific: constructor.build_specific_fragment_info_for_node(node),
inline_context: None,
debug_id: layout_debug::generate_unique_debug_id(),
}
}
/// Constructs a new `Fragment` instance for an anonymous table object.
pub fn new_anonymous_table_fragment(node: &ThreadSafeLayoutNode,
pub fn new_anonymous_from_specific_info(node: &ThreadSafeLayoutNode,
specific: SpecificFragmentInfo)
-> Fragment {
// CSS 2.1 § 17.2.1 This is for non-inherited properties on anonymous table fragments
@ -1508,10 +1527,17 @@ impl Fragment {
/// because the corresponding table flow is the primary fragment.
pub fn is_primary_fragment(&self) -> bool {
match self.specific {
SpecificFragmentInfo::InlineBlock(_) | SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
SpecificFragmentInfo::InlineBlock(_) |
SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
SpecificFragmentInfo::TableWrapper => false,
SpecificFragmentInfo::Generic | SpecificFragmentInfo::Iframe(_) | SpecificFragmentInfo::Image(_) | SpecificFragmentInfo::ScannedText(_) |
SpecificFragmentInfo::Table | SpecificFragmentInfo::TableCell | SpecificFragmentInfo::TableColumn(_) | SpecificFragmentInfo::TableRow |
SpecificFragmentInfo::Generic |
SpecificFragmentInfo::Iframe(_) |
SpecificFragmentInfo::Image(_) |
SpecificFragmentInfo::ScannedText(_) |
SpecificFragmentInfo::Table |
SpecificFragmentInfo::TableCell |
SpecificFragmentInfo::TableColumn(_) |
SpecificFragmentInfo::TableRow |
SpecificFragmentInfo::UnscannedText(_) => true,
}
}
@ -1540,17 +1566,50 @@ impl Fragment {
self.style = (*new_style).clone()
}
/// Given the stacking-context-relative position of the containing flow, returns the boundaries
/// of this fragment relative to the parent stacking context.
pub fn stacking_relative_bounds(&self, stacking_relative_flow_origin: &Point2D<Au>)
/// Given the stacking-context-relative position of the containing flow, returns the border box
/// of this fragment relative to the parent stacking context. This takes `position: relative`
/// into account.
///
/// If `coordinate_system` is `Parent`, this returns the border box in the parent stacking
/// context's coordinate system. Otherwise, if `coordinate_system` is `Self` and this fragment
/// establishes a stacking context itself, this returns a border box anchored at (0, 0). (If
/// this fragment does not establish a stacking context, then it always belongs to its parent
/// stacking context and thus `coordinate_system` is ignored.)
///
/// This is the method you should use for display list construction as well as
/// `getBoundingClientRect()` and so forth.
pub fn stacking_relative_border_box(&self,
stacking_relative_flow_origin: &Point2D<Au>,
relative_containing_block_size: &LogicalSize<Au>,
coordinate_system: CoordinateSystem)
-> Rect<Au> {
// FIXME(#2795): Get the real container size
// FIXME(pcwalton, #2795): Get the real container size.
let container_size = Size2D::zero();
self.border_box
.to_physical(self.style.writing_mode, container_size)
let border_box = self.border_box.to_physical(self.style.writing_mode, container_size);
if coordinate_system == CoordinateSystem::Self && self.establishes_stacking_context() {
return Rect(ZERO_POINT, border_box.size)
}
// FIXME(pcwalton): This can double-count relative position sometimes for inlines (e.g.
// `<div style="position:relative">x</div>`, because the `position:relative` trickles down
// to the inline flow. Possibly we should extend the notion of "primary fragment" to fix
// this.
let relative_position = self.relative_position(relative_containing_block_size);
border_box.translate_by_size(&relative_position.to_physical(self.style.writing_mode))
.translate(stacking_relative_flow_origin)
}
/// Given the stacking-context-relative border box, returns the stacking-context-relative
/// content box.
pub fn stacking_relative_content_box(&self, stacking_relative_border_box: &Rect<Au>)
-> Rect<Au> {
let border_padding = self.border_padding.to_physical(self.style.writing_mode);
Rect(Point2D(stacking_relative_border_box.origin.x + border_padding.left,
stacking_relative_border_box.origin.y + border_padding.top),
Size2D(stacking_relative_border_box.size.width - border_padding.horizontal(),
stacking_relative_border_box.size.height - border_padding.vertical()))
}
/// Returns true if this fragment establishes a new stacking context and false otherwise.
pub fn establishes_stacking_context(&self) -> bool {
if self.style().get_effects().opacity != 1.0 {
@ -1637,13 +1696,23 @@ bitflags! {
}
}
/// A top-down fragment overflow region iteration handler.
pub trait FragmentOverflowIterator {
/// A top-down fragment border box iteration handler.
pub trait FragmentBorderBoxIterator {
/// The operation to perform.
fn process(&mut self, fragment: &Fragment, overflow: Rect<Au>);
fn process(&mut self, fragment: &Fragment, overflow: &Rect<Au>);
/// Returns true if this fragment must be processed in-order. If this returns false,
/// we skip the operation for this fragment, but continue processing siblings.
fn should_process(&mut self, fragment: &Fragment) -> bool;
}
/// The coordinate system used in `stacking_relative_border_box()`. See the documentation of that
/// method for details.
#[deriving(Clone, PartialEq, Show)]
pub enum CoordinateSystem {
/// The border box returned is relative to the fragment's parent stacking context.
Parent,
/// The border box returned is relative to the fragment's own stacking context, if applicable.
Self,
}

View file

@ -6,13 +6,13 @@
use css::node_style::StyledNode;
use context::LayoutContext;
use display_list_builder::{BackgroundAndBorderLevel, DisplayListBuildingResult, FragmentDisplayListBuilding};
use display_list_builder::{FragmentDisplayListBuilding, InlineFlowDisplayListBuilding};
use floats::{FloatKind, Floats, PlacementInfo};
use flow::{BaseFlow, FlowClass, Flow, MutableFlowUtils, ForceNonfloatedFlag};
use flow::{IS_ABSOLUTELY_POSITIONED};
use flow;
use fragment::{Fragment, SpecificFragmentInfo};
use fragment::{FragmentOverflowIterator, ScannedTextFragmentInfo};
use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, ScannedTextFragmentInfo};
use fragment::{SpecificFragmentInfo};
use fragment::SplitInfo;
use incremental::{REFLOW, REFLOW_OUT_OF_FLOW};
use layout_debug;
@ -20,16 +20,14 @@ use model::IntrinsicISizesContribution;
use text;
use collections::{RingBuf};
use geom::{Rect, Size2D};
use gfx::display_list::DisplayList;
use geom::{Point2D, Rect};
use gfx::font::FontMetrics;
use gfx::font_context::FontContext;
use gfx::text::glyph::CharIndex;
use servo_util::arc_ptr_eq;
use servo_util::geometry::{Au, ZERO_RECT};
use servo_util::logical_geometry::{LogicalRect, LogicalSize, WritingMode};
use servo_util::opts;
use servo_util::range::{Range, RangeIndex};
use servo_util::arc_ptr_eq;
use std::cmp::max;
use std::fmt;
use std::mem;
@ -1186,44 +1184,29 @@ impl Flow for InlineFlow {
fn compute_absolute_position(&mut self) {
for fragment in self.fragments.fragments.iter_mut() {
let stacking_relative_position = match fragment.specific {
SpecificFragmentInfo::InlineBlock(ref mut info) => {
let block_flow = info.flow_ref.as_block();
block_flow.base.absolute_position_info = self.base.absolute_position_info;
// FIXME(#2795): Get the real container size
let container_size = Size2D::zero();
block_flow.base.stacking_relative_position =
self.base.stacking_relative_position +
fragment.border_box.start.to_physical(self.base.writing_mode,
container_size);
block_flow.base.stacking_relative_position
}
SpecificFragmentInfo::InlineAbsoluteHypothetical(ref mut info) => {
let block_flow = info.flow_ref.as_block();
block_flow.base.absolute_position_info = self.base.absolute_position_info;
// FIXME(#2795): Get the real container size
let container_size = Size2D::zero();
block_flow.base.stacking_relative_position =
self.base.stacking_relative_position +
fragment.border_box.start.to_physical(self.base.writing_mode,
container_size);
block_flow.base.stacking_relative_position
}
_ => continue,
};
let stacking_relative_border_box =
fragment.stacking_relative_border_box(&self.base.stacking_relative_position,
&self.base
.absolute_position_info
.relative_containing_block_size,
CoordinateSystem::Self);
let clip = fragment.clipping_region_for_children(&self.base.clip,
&stacking_relative_position);
&stacking_relative_border_box);
match fragment.specific {
SpecificFragmentInfo::InlineBlock(ref mut info) => {
flow::mut_base(info.flow_ref.deref_mut()).clip = clip
flow::mut_base(info.flow_ref.deref_mut()).clip = clip;
let block_flow = info.flow_ref.as_block();
block_flow.base.absolute_position_info = self.base.absolute_position_info;
block_flow.base.stacking_relative_position =
stacking_relative_border_box.origin;
}
SpecificFragmentInfo::InlineAbsoluteHypothetical(ref mut info) => {
flow::mut_base(info.flow_ref.deref_mut()).clip = clip
flow::mut_base(info.flow_ref.deref_mut()).clip = clip;
let block_flow = info.flow_ref.as_block();
block_flow.base.absolute_position_info = self.base.absolute_position_info;
block_flow.base.stacking_relative_position =
stacking_relative_border_box.origin
}
_ => {}
}
@ -1235,38 +1218,7 @@ impl Flow for InlineFlow {
fn update_late_computed_block_position_if_necessary(&mut self, _: Au) {}
fn build_display_list(&mut self, layout_context: &LayoutContext) {
// TODO(#228): Once we form lines and have their cached bounds, we can be smarter and
// not recurse on a line if nothing in it can intersect the dirty region.
debug!("Flow: building display list for {} inline fragments", self.fragments.len());
let mut display_list = box DisplayList::new();
for fragment in self.fragments.fragments.iter_mut() {
let fragment_origin = self.base.stacking_relative_position_of_child_fragment(fragment);
fragment.build_display_list(&mut *display_list,
layout_context,
fragment_origin,
BackgroundAndBorderLevel::Content,
&self.base.clip);
match fragment.specific {
SpecificFragmentInfo::InlineBlock(ref mut block_flow) => {
let block_flow = block_flow.flow_ref.deref_mut();
flow::mut_base(block_flow).display_list_building_result
.add_to(&mut *display_list)
}
SpecificFragmentInfo::InlineAbsoluteHypothetical(ref mut block_flow) => {
let block_flow = block_flow.flow_ref.deref_mut();
flow::mut_base(block_flow).display_list_building_result
.add_to(&mut *display_list)
}
_ => {}
}
}
self.base.display_list_building_result = DisplayListBuildingResult::Normal(display_list);
if opts::get().validate_display_list_geometry {
self.base.validate_display_list_geometry();
}
self.build_display_list_for_inline(layout_context)
}
fn repair_style(&mut self, _: &Arc<ComputedValues>) {}
@ -1279,13 +1231,23 @@ impl Flow for InlineFlow {
overflow
}
fn iterate_through_fragment_overflow(&self, iterator: &mut FragmentOverflowIterator) {
fn iterate_through_fragment_border_boxes(&self,
iterator: &mut FragmentBorderBoxIterator,
stacking_context_position: &Point2D<Au>) {
// FIXME(#2795): Get the real container size.
for fragment in self.fragments.fragments.iter() {
if iterator.should_process(fragment) {
let fragment_origin =
self.base.stacking_relative_position_of_child_fragment(fragment);
iterator.process(fragment, fragment.stacking_relative_bounds(&fragment_origin));
if !iterator.should_process(fragment) {
continue
}
let stacking_relative_position = &self.base.stacking_relative_position;
let relative_containing_block_size =
&self.base.absolute_position_info.relative_containing_block_size;
iterator.process(fragment,
&fragment.stacking_relative_border_box(stacking_relative_position,
relative_containing_block_size,
CoordinateSystem::Parent)
.translate(stacking_context_position))
}
}
}

View file

@ -10,7 +10,7 @@ use construct::ConstructionResult;
use context::SharedLayoutContext;
use flow::{mod, Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils};
use flow_ref::FlowRef;
use fragment::{Fragment, FragmentOverflowIterator};
use fragment::{Fragment, FragmentBorderBoxIterator};
use incremental::{LayoutDamageComputation, REFLOW, REFLOW_ENTIRE_DOCUMENT, REPAINT};
use layout_debug;
use parallel::{mod, UnsafeFlow};
@ -604,8 +604,8 @@ impl LayoutTask {
// FIXME(pcwalton): This has not been updated to handle the stacking context relative
// stuff. So the position is wrong in most cases.
let requested_node: OpaqueNode = OpaqueNodeMethods::from_script_node(requested_node);
let mut iterator = UnioningFragmentOverflowIterator::new(requested_node);
sequential::iterate_through_flow_tree_fragment_bounds(layout_root, &mut iterator);
let mut iterator = UnioningFragmentBorderBoxIterator::new(requested_node);
sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator);
rw_data.content_box_response = iterator.rect;
}
@ -616,8 +616,8 @@ impl LayoutTask {
// FIXME(pcwalton): This has not been updated to handle the stacking context relative
// stuff. So the position is wrong in most cases.
let requested_node: OpaqueNode = OpaqueNodeMethods::from_script_node(requested_node);
let mut iterator = CollectingFragmentOverflowIterator::new(requested_node);
sequential::iterate_through_flow_tree_fragment_bounds(layout_root, &mut iterator);
let mut iterator = CollectingFragmentBorderBoxIterator::new(requested_node);
sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator);
rw_data.content_boxes_response = iterator.rects;
}
@ -1017,26 +1017,26 @@ impl LayoutRPC for LayoutRPCImpl {
}
}
struct UnioningFragmentOverflowIterator {
struct UnioningFragmentBorderBoxIterator {
node_address: OpaqueNode,
rect: Rect<Au>,
}
impl UnioningFragmentOverflowIterator {
fn new(node_address: OpaqueNode) -> UnioningFragmentOverflowIterator {
UnioningFragmentOverflowIterator {
impl UnioningFragmentBorderBoxIterator {
fn new(node_address: OpaqueNode) -> UnioningFragmentBorderBoxIterator {
UnioningFragmentBorderBoxIterator {
node_address: node_address,
rect: Rect::zero(),
}
}
}
impl FragmentOverflowIterator for UnioningFragmentOverflowIterator {
fn process(&mut self, _: &Fragment, bounds: Rect<Au>) {
if self.rect.is_empty() {
self.rect = bounds;
impl FragmentBorderBoxIterator for UnioningFragmentBorderBoxIterator {
fn process(&mut self, _: &Fragment, border_box: &Rect<Au>) {
self.rect = if self.rect.is_empty() {
*border_box
} else {
self.rect = self.rect.union(&bounds);
self.rect.union(border_box)
}
}
@ -1045,23 +1045,23 @@ impl FragmentOverflowIterator for UnioningFragmentOverflowIterator {
}
}
struct CollectingFragmentOverflowIterator {
struct CollectingFragmentBorderBoxIterator {
node_address: OpaqueNode,
rects: Vec<Rect<Au>>,
}
impl CollectingFragmentOverflowIterator {
fn new(node_address: OpaqueNode) -> CollectingFragmentOverflowIterator {
CollectingFragmentOverflowIterator {
impl CollectingFragmentBorderBoxIterator {
fn new(node_address: OpaqueNode) -> CollectingFragmentBorderBoxIterator {
CollectingFragmentBorderBoxIterator {
node_address: node_address,
rects: Vec::new(),
}
}
}
impl FragmentOverflowIterator for CollectingFragmentOverflowIterator {
fn process(&mut self, _: &Fragment, bounds: Rect<Au>) {
self.rects.push(bounds);
impl FragmentBorderBoxIterator for CollectingFragmentBorderBoxIterator {
fn process(&mut self, _: &Fragment, border_box: &Rect<Au>) {
self.rects.push(*border_box);
}
fn should_process(&mut self, fragment: &Fragment) -> bool {

View file

@ -12,10 +12,10 @@ use construct::FlowConstructor;
use context::LayoutContext;
use display_list_builder::ListItemFlowDisplayListBuilding;
use flow::{Flow, FlowClass};
use fragment::{Fragment, FragmentOverflowIterator};
use fragment::{Fragment, FragmentBorderBoxIterator};
use wrapper::ThreadSafeLayoutNode;
use geom::Rect;
use geom::{Point2D, Rect};
use gfx::display_list::DisplayList;
use servo_util::geometry::Au;
use servo_util::opts;
@ -116,8 +116,10 @@ impl Flow for ListItemFlow {
self.block_flow.compute_overflow()
}
fn iterate_through_fragment_overflow(&self, iterator: &mut FragmentOverflowIterator) {
self.block_flow.iterate_through_fragment_overflow(iterator);
fn iterate_through_fragment_border_boxes(&self,
iterator: &mut FragmentBorderBoxIterator,
stacking_context_position: &Point2D<Au>) {
self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position)
}
}

View file

@ -5,11 +5,10 @@
//! Implements sequential traversals over the DOM and flow trees.
use context::{LayoutContext, SharedLayoutContext};
use flow::{Flow, MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal};
use flow;
use flow::{mod, Flow, ImmutableFlowUtils, MutableFlowUtils, PostorderFlowTraversal};
use flow::{PreorderFlowTraversal};
use flow_ref::FlowRef;
use fragment::FragmentOverflowIterator;
use servo_util::opts;
use fragment::FragmentBorderBoxIterator;
use traversal::{BubbleISizes, RecalcStyleForNode, ConstructFlows};
use traversal::{AssignBSizesAndStoreOverflow, AssignISizes};
use traversal::{ComputeAbsolutePositions, BuildDisplayList};
@ -17,6 +16,10 @@ use wrapper::LayoutNode;
use wrapper::{PostorderNodeMutTraversal};
use wrapper::{PreorderDomTraversal, PostorderDomTraversal};
use geom::point::Point2D;
use servo_util::geometry::{Au, ZERO_POINT};
use servo_util::opts;
pub fn traverse_dom_preorder(root: LayoutNode,
shared_layout_context: &SharedLayoutContext) {
fn doit(node: LayoutNode, recalc_style: RecalcStyleForNode, construct_flows: ConstructFlows) {
@ -94,15 +97,25 @@ pub fn build_display_list_for_subtree(root: &mut FlowRef,
doit(root.deref_mut(), compute_absolute_positions, build_display_list);
}
pub fn iterate_through_flow_tree_fragment_bounds(root: &mut FlowRef,
iterator: &mut FragmentOverflowIterator) {
fn doit(flow: &mut Flow, iterator: &mut FragmentOverflowIterator) {
flow.iterate_through_fragment_overflow(iterator);
pub fn iterate_through_flow_tree_fragment_border_boxes(root: &mut FlowRef,
iterator: &mut FragmentBorderBoxIterator) {
fn doit(flow: &mut Flow,
iterator: &mut FragmentBorderBoxIterator,
stacking_context_position: &Point2D<Au>) {
flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position);
for kid in flow::mut_base(flow).child_iter() {
doit(kid, iterator);
let stacking_context_position =
if kid.is_block_flow() && kid.as_block().fragment.establishes_stacking_context() {
*stacking_context_position + flow::base(kid).stacking_relative_position
} else {
*stacking_context_position
};
// FIXME(#2795): Get the real container size.
doit(kid, iterator, &stacking_context_position);
}
}
doit(root.deref_mut(), iterator);
doit(root.deref_mut(), iterator, &ZERO_POINT);
}

View file

@ -13,14 +13,14 @@ use context::LayoutContext;
use floats::FloatKind;
use flow::{mod, Flow, FlowClass, IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS};
use flow::ImmutableFlowUtils;
use fragment::{Fragment, FragmentOverflowIterator};
use fragment::{Fragment, FragmentBorderBoxIterator};
use layout_debug;
use model::{IntrinsicISizes, IntrinsicISizesContribution};
use table_row::CellIntrinsicInlineSize;
use table_wrapper::TableLayout;
use wrapper::ThreadSafeLayoutNode;
use geom::Rect;
use geom::{Point2D, Rect};
use servo_util::geometry::Au;
use servo_util::logical_geometry::LogicalRect;
use std::cmp::max;
@ -389,8 +389,10 @@ impl Flow for TableFlow {
self.block_flow.compute_overflow()
}
fn iterate_through_fragment_overflow(&self, iterator: &mut FragmentOverflowIterator) {
self.block_flow.iterate_through_fragment_overflow(iterator);
fn iterate_through_fragment_border_boxes(&self,
iterator: &mut FragmentBorderBoxIterator,
stacking_context_position: &Point2D<Au>) {
self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position)
}
}

View file

@ -10,10 +10,10 @@ use block::BlockFlow;
use construct::FlowConstructor;
use context::LayoutContext;
use flow::{FlowClass, Flow};
use fragment::FragmentOverflowIterator;
use fragment::FragmentBorderBoxIterator;
use wrapper::ThreadSafeLayoutNode;
use geom::Rect;
use geom::{Point2D, Rect};
use servo_util::geometry::Au;
use std::fmt;
use style::ComputedValues;
@ -86,8 +86,10 @@ impl Flow for TableCaptionFlow {
self.block_flow.compute_overflow()
}
fn iterate_through_fragment_overflow(&self, iterator: &mut FragmentOverflowIterator) {
self.block_flow.iterate_through_fragment_overflow(iterator);
fn iterate_through_fragment_border_boxes(&self,
iterator: &mut FragmentBorderBoxIterator,
stacking_context_position: &Point2D<Au>) {
self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position)
}
}

View file

@ -9,13 +9,13 @@
use block::{BlockFlow, ISizeAndMarginsComputer, MarginsMayCollapseFlag};
use context::LayoutContext;
use flow::{Flow, FlowClass};
use fragment::{Fragment, FragmentOverflowIterator};
use fragment::{Fragment, FragmentBorderBoxIterator};
use model::{MaybeAuto};
use layout_debug;
use table::InternalTable;
use wrapper::ThreadSafeLayoutNode;
use geom::Rect;
use geom::{Point2D, Rect};
use servo_util::geometry::Au;
use std::fmt;
use style::{UnsignedIntegerAttribute, ComputedValues};
@ -167,8 +167,10 @@ impl Flow for TableCellFlow {
self.block_flow.compute_overflow()
}
fn iterate_through_fragment_overflow(&self, iterator: &mut FragmentOverflowIterator) {
self.block_flow.iterate_through_fragment_overflow(iterator);
fn iterate_through_fragment_border_boxes(&self,
iterator: &mut FragmentBorderBoxIterator,
stacking_context_position: &Point2D<Au>) {
self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position)
}
}

View file

@ -9,11 +9,11 @@
use context::LayoutContext;
use css::node_style::StyledNode;
use flow::{BaseFlow, FlowClass, Flow, ForceNonfloatedFlag};
use fragment::{Fragment, FragmentOverflowIterator, SpecificFragmentInfo};
use fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo};
use layout_debug;
use wrapper::ThreadSafeLayoutNode;
use geom::Rect;
use geom::{Point2D, Rect};
use servo_util::geometry::{Au, ZERO_RECT};
use std::cmp::max;
use std::fmt;
@ -101,8 +101,9 @@ impl Flow for TableColGroupFlow {
ZERO_RECT
}
fn iterate_through_fragment_overflow(&self, _: &mut FragmentOverflowIterator) {
}
fn iterate_through_fragment_border_boxes(&self,
_: &mut FragmentBorderBoxIterator,
_: &Point2D<Au>) {}
}
impl fmt::Show for TableColGroupFlow {

View file

@ -12,13 +12,13 @@ use construct::FlowConstructor;
use context::LayoutContext;
use flow::{FlowClass, Flow, ImmutableFlowUtils};
use flow;
use fragment::{Fragment, FragmentOverflowIterator};
use fragment::{Fragment, FragmentBorderBoxIterator};
use layout_debug;
use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, InternalTable};
use model::MaybeAuto;
use wrapper::ThreadSafeLayoutNode;
use geom::Rect;
use geom::{Point2D, Rect};
use servo_util::geometry::Au;
use std::cmp::max;
use std::fmt;
@ -320,8 +320,10 @@ impl Flow for TableRowFlow {
self.block_flow.compute_overflow()
}
fn iterate_through_fragment_overflow(&self, iterator: &mut FragmentOverflowIterator) {
self.block_flow.iterate_through_fragment_overflow(iterator);
fn iterate_through_fragment_border_boxes(&self,
iterator: &mut FragmentBorderBoxIterator,
stacking_context_position: &Point2D<Au>) {
self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position)
}
}

View file

@ -10,12 +10,12 @@ use block::{BlockFlow, ISizeAndMarginsComputer, MarginsMayCollapseFlag};
use construct::FlowConstructor;
use context::LayoutContext;
use flow::{FlowClass, Flow};
use fragment::{Fragment, FragmentOverflowIterator};
use fragment::{Fragment, FragmentBorderBoxIterator};
use layout_debug;
use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize, InternalTable};
use wrapper::ThreadSafeLayoutNode;
use geom::Rect;
use geom::{Point2D, Rect};
use servo_util::geometry::Au;
use std::fmt;
use style::ComputedValues;
@ -155,8 +155,10 @@ impl Flow for TableRowGroupFlow {
self.block_flow.compute_overflow()
}
fn iterate_through_fragment_overflow(&self, iterator: &mut FragmentOverflowIterator) {
self.block_flow.iterate_through_fragment_overflow(iterator);
fn iterate_through_fragment_border_boxes(&self,
iterator: &mut FragmentBorderBoxIterator,
stacking_context_position: &Point2D<Au>) {
self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position)
}
}

View file

@ -19,11 +19,11 @@ use context::LayoutContext;
use floats::FloatKind;
use flow::{FlowClass, Flow, ImmutableFlowUtils};
use flow::{IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS};
use fragment::{Fragment, FragmentOverflowIterator};
use fragment::{Fragment, FragmentBorderBoxIterator};
use table::{ColumnComputedInlineSize, ColumnIntrinsicInlineSize};
use wrapper::ThreadSafeLayoutNode;
use geom::Rect;
use geom::{Point2D, Rect};
use servo_util::geometry::Au;
use std::cmp::{max, min};
use std::fmt;
@ -363,8 +363,10 @@ impl Flow for TableWrapperFlow {
self.block_flow.compute_overflow()
}
fn iterate_through_fragment_overflow(&self, iterator: &mut FragmentOverflowIterator) {
self.block_flow.iterate_through_fragment_overflow(iterator);
fn iterate_through_fragment_border_boxes(&self,
iterator: &mut FragmentBorderBoxIterator,
stacking_context_position: &Point2D<Au>) {
self.block_flow.iterate_through_fragment_border_boxes(iterator, stacking_context_position)
}
}

View file

@ -44,7 +44,7 @@ pub use selector_matching::{matches, matches_simple_selector, common_style_affec
pub use selector_matching::{rare_style_affecting_attributes};
pub use selector_matching::{RECOMMENDED_SELECTOR_BLOOM_FILTER_SIZE, SELECTOR_WHITESPACE};
pub use properties::{cascade, cascade_anonymous, computed, longhands_from_shorthand};
pub use properties::is_supported_property;
pub use properties::{is_supported_property, make_inline};
pub use properties::{PropertyDeclaration, ComputedValues, computed_values, style_structs};
pub use properties::{PropertyDeclarationBlock, parse_style_attribute}; // Style attributes
pub use properties::{CSSFloat, DeclaredValue, PropertyDeclarationParseResult};

View file

@ -3183,6 +3183,15 @@ pub fn cascade_anonymous(parent_style: &ComputedValues) -> ComputedValues {
result
}
/// Sets `display` to `inline` and `position` to `static`.
#[inline]
pub fn make_inline(style: &ComputedValues) -> ComputedValues {
let mut style = (*style).clone();
style.box_.make_unique().display = longhands::display::computed_value::T::inline;
style.box_.make_unique().position = longhands::position::computed_value::T::static_;
style
}
pub fn is_supported_property(property: &str) -> bool {
match property {
% for property in SHORTHANDS:

View file

@ -22,7 +22,7 @@ function _pass(s, m) {
function _printer(opstr, op) {
return function (a, b, msg) {
let f = op(a,b) ? _pass : _fail;
var f = op(a,b) ? _pass : _fail;
if (!msg) msg = "";
f(a + " " + opstr + " " + b, msg);
};