aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/layout
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/layout')
-rw-r--r--src/components/layout/block.rs2428
-rw-r--r--src/components/layout/construct.rs1049
-rw-r--r--src/components/layout/context.rs123
-rw-r--r--src/components/layout/css/matching.rs558
-rw-r--r--src/components/layout/css/node_style.rs30
-rw-r--r--src/components/layout/css/node_util.rs90
-rw-r--r--src/components/layout/extra.rs44
-rw-r--r--src/components/layout/floats.rs439
-rw-r--r--src/components/layout/flow.rs1138
-rw-r--r--src/components/layout/flow_list.rs296
-rw-r--r--src/components/layout/flow_ref.rs84
-rw-r--r--src/components/layout/fragment.rs1597
-rw-r--r--src/components/layout/incremental.rs78
-rw-r--r--src/components/layout/inline.rs1170
-rw-r--r--src/components/layout/layout.rs71
-rw-r--r--src/components/layout/layout_debug.rs126
-rw-r--r--src/components/layout/layout_task.rs1020
-rw-r--r--src/components/layout/model.rs337
-rw-r--r--src/components/layout/parallel.rs561
-rw-r--r--src/components/layout/table.rs324
-rw-r--r--src/components/layout/table_caption.rs73
-rw-r--r--src/components/layout/table_cell.rs121
-rw-r--r--src/components/layout/table_colgroup.rs88
-rw-r--r--src/components/layout/table_row.rs225
-rw-r--r--src/components/layout/table_rowgroup.rs208
-rw-r--r--src/components/layout/table_wrapper.rs325
-rw-r--r--src/components/layout/text.rs327
-rw-r--r--src/components/layout/util.rs164
-rw-r--r--src/components/layout/wrapper.rs783
29 files changed, 0 insertions, 13877 deletions
diff --git a/src/components/layout/block.rs b/src/components/layout/block.rs
deleted file mode 100644
index b84e0da50f7..00000000000
--- a/src/components/layout/block.rs
+++ /dev/null
@@ -1,2428 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//! CSS block formatting contexts.
-//!
-//! Terminology Note:
-//! As per the CSS Spec, the term 'absolute positioning' here refers to
-//! elements with position = 'absolute' or 'fixed'.
-//! The term 'positioned element' refers to elements with position =
-//! 'relative', 'absolute', or 'fixed'.
-//!
-//! CB: Containing Block of the current flow.
-
-#![deny(unsafe_block)]
-
-use construct::FlowConstructor;
-use context::LayoutContext;
-use floats::{ClearBoth, ClearLeft, ClearRight, FloatKind, Floats, PlacementInfo};
-use flow::{BaseFlow, BlockFlowClass, FlowClass, Flow, ImmutableFlowUtils};
-use flow::{MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal, mut_base};
-use flow;
-use fragment::{Fragment, ImageFragment, ScannedTextFragment};
-use layout_debug;
-use model::{Auto, IntrinsicISizes, MarginCollapseInfo, MarginsCollapse};
-use model::{MarginsCollapseThrough, MaybeAuto, NoCollapsibleMargins, Specified, specified};
-use model::{specified_or_none};
-use wrapper::ThreadSafeLayoutNode;
-use style::ComputedValues;
-use style::computed_values::{clear, position};
-
-use collections::dlist::DList;
-use geom::{Size2D, Point2D, Rect};
-use gfx::color;
-use gfx::display_list::{BackgroundAndBorderLevel, BlockLevel, ContentStackingLevel, DisplayList};
-use gfx::display_list::{FloatStackingLevel, PositionedDescendantStackingLevel};
-use gfx::display_list::{RootOfStackingContextLevel};
-use gfx::render_task::RenderLayer;
-use servo_msg::compositor_msg::{FixedPosition, LayerId, Scrollable};
-use servo_util::geometry::Au;
-use servo_util::geometry;
-use servo_util::logical_geometry::WritingMode;
-use servo_util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize};
-use std::fmt;
-use std::mem;
-use style::computed_values::{LPA_Auto, LPA_Length, LPA_Percentage, LPN_Length, LPN_None};
-use style::computed_values::{LPN_Percentage, LP_Length, LP_Percentage};
-use style::computed_values::{display, float, overflow};
-use sync::Arc;
-
-/// Information specific to floated blocks.
-#[deriving(Encodable)]
-pub struct FloatedBlockInfo {
- pub containing_inline_size: Au,
-
- /// Offset relative to where the parent tried to position this flow
- pub rel_pos: LogicalPoint<Au>,
-
- /// Index into the fragment list for inline floats
- pub index: Option<uint>,
-
- /// Left or right?
- pub float_kind: FloatKind,
-}
-
-impl FloatedBlockInfo {
- pub fn new(float_kind: FloatKind, writing_mode: WritingMode) -> FloatedBlockInfo {
- FloatedBlockInfo {
- containing_inline_size: Au(0),
- rel_pos: LogicalPoint::new(writing_mode, Au(0), Au(0)),
- index: None,
- float_kind: float_kind,
- }
- }
-}
-
-/// The solutions for the block-size-and-margins constraint equation.
-struct BSizeConstraintSolution {
- block_start: Au,
- _block_end: Au,
- block_size: Au,
- margin_block_start: Au,
- margin_block_end: Au
-}
-
-impl BSizeConstraintSolution {
- fn new(block_start: Au, block_end: Au, block_size: Au, margin_block_start: Au, margin_block_end: Au)
- -> BSizeConstraintSolution {
- BSizeConstraintSolution {
- block_start: block_start,
- _block_end: block_end,
- block_size: block_size,
- margin_block_start: margin_block_start,
- margin_block_end: margin_block_end,
- }
- }
-
- /// Solve the vertical constraint equation for absolute non-replaced elements.
- ///
- /// CSS Section 10.6.4
- /// Constraint equation:
- /// block-start + block-end + block-size + margin-block-start + margin-block-end
- /// = absolute containing block block-size - (vertical padding and border)
- /// [aka available_block-size]
- ///
- /// Return the solution for the equation.
- fn solve_vertical_constraints_abs_nonreplaced(block_size: MaybeAuto,
- block_start_margin: MaybeAuto,
- block_end_margin: MaybeAuto,
- block_start: MaybeAuto,
- block_end: MaybeAuto,
- content_block_size: Au,
- available_block_size: Au,
- static_b_offset: Au)
- -> BSizeConstraintSolution {
- // Distance from the block-start edge of the Absolute Containing Block to the
- // block-start margin edge of a hypothetical box that would have been the
- // first box of the element.
- let static_position_block_start = static_b_offset;
-
- let (block_start, block_end, block_size, margin_block_start, margin_block_end) = match (block_start, block_end, block_size) {
- (Auto, Auto, Auto) => {
- let margin_block_start = block_start_margin.specified_or_zero();
- let margin_block_end = block_end_margin.specified_or_zero();
- let block_start = static_position_block_start;
- // Now it is the same situation as block-start Specified and block-end
- // and block-size Auto.
-
- let block_size = content_block_size;
- let sum = block_start + block_size + margin_block_start + margin_block_end;
- (block_start, available_block_size - sum, block_size, margin_block_start, margin_block_end)
- }
- (Specified(block_start), Specified(block_end), Specified(block_size)) => {
- match (block_start_margin, block_end_margin) {
- (Auto, Auto) => {
- let total_margin_val = available_block_size - block_start - block_end - block_size;
- (block_start, block_end, block_size,
- total_margin_val.scale_by(0.5),
- total_margin_val.scale_by(0.5))
- }
- (Specified(margin_block_start), Auto) => {
- let sum = block_start + block_end + block_size + margin_block_start;
- (block_start, block_end, block_size, margin_block_start, available_block_size - sum)
- }
- (Auto, Specified(margin_block_end)) => {
- let sum = block_start + block_end + block_size + margin_block_end;
- (block_start, block_end, block_size, available_block_size - sum, margin_block_end)
- }
- (Specified(margin_block_start), Specified(margin_block_end)) => {
- // Values are over-constrained. Ignore value for 'block-end'.
- let sum = block_start + block_size + margin_block_start + margin_block_end;
- (block_start, available_block_size - sum, block_size, margin_block_start, margin_block_end)
- }
- }
- }
-
- // For the rest of the cases, auto values for margin are set to 0
-
- // If only one is Auto, solve for it
- (Auto, Specified(block_end), Specified(block_size)) => {
- let margin_block_start = block_start_margin.specified_or_zero();
- let margin_block_end = block_end_margin.specified_or_zero();
- let sum = block_end + block_size + margin_block_start + margin_block_end;
- (available_block_size - sum, block_end, block_size, margin_block_start, margin_block_end)
- }
- (Specified(block_start), Auto, Specified(block_size)) => {
- let margin_block_start = block_start_margin.specified_or_zero();
- let margin_block_end = block_end_margin.specified_or_zero();
- let sum = block_start + block_size + margin_block_start + margin_block_end;
- (block_start, available_block_size - sum, block_size, margin_block_start, margin_block_end)
- }
- (Specified(block_start), Specified(block_end), Auto) => {
- let margin_block_start = block_start_margin.specified_or_zero();
- let margin_block_end = block_end_margin.specified_or_zero();
- let sum = block_start + block_end + margin_block_start + margin_block_end;
- (block_start, block_end, available_block_size - sum, margin_block_start, margin_block_end)
- }
-
- // If block-size is auto, then block-size is content block-size. Solve for the
- // non-auto value.
- (Specified(block_start), Auto, Auto) => {
- let margin_block_start = block_start_margin.specified_or_zero();
- let margin_block_end = block_end_margin.specified_or_zero();
- let block_size = content_block_size;
- let sum = block_start + block_size + margin_block_start + margin_block_end;
- (block_start, available_block_size - sum, block_size, margin_block_start, margin_block_end)
- }
- (Auto, Specified(block_end), Auto) => {
- let margin_block_start = block_start_margin.specified_or_zero();
- let margin_block_end = block_end_margin.specified_or_zero();
- let block_size = content_block_size;
- let sum = block_end + block_size + margin_block_start + margin_block_end;
- (available_block_size - sum, block_end, block_size, margin_block_start, margin_block_end)
- }
-
- (Auto, Auto, Specified(block_size)) => {
- let margin_block_start = block_start_margin.specified_or_zero();
- let margin_block_end = block_end_margin.specified_or_zero();
- let block_start = static_position_block_start;
- let sum = block_start + block_size + margin_block_start + margin_block_end;
- (block_start, available_block_size - sum, block_size, margin_block_start, margin_block_end)
- }
- };
- BSizeConstraintSolution::new(block_start, block_end, block_size, margin_block_start, margin_block_end)
- }
-
- /// Solve the vertical constraint equation for absolute replaced elements.
- ///
- /// Assumption: The used value for block-size has already been calculated.
- ///
- /// CSS Section 10.6.5
- /// Constraint equation:
- /// block-start + block-end + block-size + margin-block-start + margin-block-end
- /// = absolute containing block block-size - (vertical padding and border)
- /// [aka available_block-size]
- ///
- /// Return the solution for the equation.
- fn solve_vertical_constraints_abs_replaced(block_size: Au,
- block_start_margin: MaybeAuto,
- block_end_margin: MaybeAuto,
- block_start: MaybeAuto,
- block_end: MaybeAuto,
- _: Au,
- available_block_size: Au,
- static_b_offset: Au)
- -> BSizeConstraintSolution {
- // Distance from the block-start edge of the Absolute Containing Block to the
- // block-start margin edge of a hypothetical box that would have been the
- // first box of the element.
- let static_position_block_start = static_b_offset;
-
- let (block_start, block_end, block_size, margin_block_start, margin_block_end) = match (block_start, block_end) {
- (Auto, Auto) => {
- let margin_block_start = block_start_margin.specified_or_zero();
- let margin_block_end = block_end_margin.specified_or_zero();
- let block_start = static_position_block_start;
- let sum = block_start + block_size + margin_block_start + margin_block_end;
- (block_start, available_block_size - sum, block_size, margin_block_start, margin_block_end)
- }
- (Specified(block_start), Specified(block_end)) => {
- match (block_start_margin, block_end_margin) {
- (Auto, Auto) => {
- let total_margin_val = available_block_size - block_start - block_end - block_size;
- (block_start, block_end, block_size,
- total_margin_val.scale_by(0.5),
- total_margin_val.scale_by(0.5))
- }
- (Specified(margin_block_start), Auto) => {
- let sum = block_start + block_end + block_size + margin_block_start;
- (block_start, block_end, block_size, margin_block_start, available_block_size - sum)
- }
- (Auto, Specified(margin_block_end)) => {
- let sum = block_start + block_end + block_size + margin_block_end;
- (block_start, block_end, block_size, available_block_size - sum, margin_block_end)
- }
- (Specified(margin_block_start), Specified(margin_block_end)) => {
- // Values are over-constrained. Ignore value for 'block-end'.
- let sum = block_start + block_size + margin_block_start + margin_block_end;
- (block_start, available_block_size - sum, block_size, margin_block_start, margin_block_end)
- }
- }
- }
-
- // If only one is Auto, solve for it
- (Auto, Specified(block_end)) => {
- let margin_block_start = block_start_margin.specified_or_zero();
- let margin_block_end = block_end_margin.specified_or_zero();
- let sum = block_end + block_size + margin_block_start + margin_block_end;
- (available_block_size - sum, block_end, block_size, margin_block_start, margin_block_end)
- }
- (Specified(block_start), Auto) => {
- let margin_block_start = block_start_margin.specified_or_zero();
- let margin_block_end = block_end_margin.specified_or_zero();
- let sum = block_start + block_size + margin_block_start + margin_block_end;
- (block_start, available_block_size - sum, block_size, margin_block_start, margin_block_end)
- }
- };
- BSizeConstraintSolution::new(block_start, block_end, block_size, margin_block_start, margin_block_end)
- }
-}
-
-/// Performs block-size calculations potentially multiple times, taking
-/// (assuming an horizontal writing mode) `height`, `min-height`, and `max-height`
-/// into account. After each call to `next()`, the caller must call `.try()` with the
-/// current calculated value of `height`.
-///
-/// See CSS 2.1 § 10.7.
-struct CandidateBSizeIterator {
- block_size: MaybeAuto,
- max_block_size: Option<Au>,
- min_block_size: Au,
- candidate_value: Au,
- status: CandidateBSizeIteratorStatus,
-}
-
-impl CandidateBSizeIterator {
- /// Creates a new candidate block-size iterator. `block_container_block-size` is `None` if the block-size
- /// of the block container has not been determined yet. It will always be `Some` in the case of
- /// absolutely-positioned containing blocks.
- pub fn new(style: &ComputedValues, block_container_block_size: Option<Au>)
- -> CandidateBSizeIterator {
- // Per CSS 2.1 § 10.7, (assuming an horizontal writing mode,)
- // percentages in `min-height` and `max-height` refer to the height of
- // the containing block.
- // If that is not determined yet by the time we need to resolve
- // `min-height` and `max-height`, percentage values are ignored.
-
- let block_size = match (style.content_block_size(), block_container_block_size) {
- (LPA_Percentage(percent), Some(block_container_block_size)) => {
- Specified(block_container_block_size.scale_by(percent))
- }
- (LPA_Percentage(_), None) | (LPA_Auto, _) => Auto,
- (LPA_Length(length), _) => Specified(length),
- };
- let max_block_size = match (style.max_block_size(), block_container_block_size) {
- (LPN_Percentage(percent), Some(block_container_block_size)) => {
- Some(block_container_block_size.scale_by(percent))
- }
- (LPN_Percentage(_), None) | (LPN_None, _) => None,
- (LPN_Length(length), _) => Some(length),
- };
- let min_block_size = match (style.min_block_size(), block_container_block_size) {
- (LP_Percentage(percent), Some(block_container_block_size)) => {
- block_container_block_size.scale_by(percent)
- }
- (LP_Percentage(_), None) => Au(0),
- (LP_Length(length), _) => length,
- };
-
- CandidateBSizeIterator {
- block_size: block_size,
- max_block_size: max_block_size,
- min_block_size: min_block_size,
- candidate_value: Au(0),
- status: InitialCandidateBSizeStatus,
- }
- }
-}
-
-impl Iterator<MaybeAuto> for CandidateBSizeIterator {
- fn next(&mut self) -> Option<MaybeAuto> {
- self.status = match self.status {
- InitialCandidateBSizeStatus => TryingBSizeCandidateBSizeStatus,
- TryingBSizeCandidateBSizeStatus => {
- match self.max_block_size {
- Some(max_block_size) if self.candidate_value > max_block_size => {
- TryingMaxCandidateBSizeStatus
- }
- _ if self.candidate_value < self.min_block_size => TryingMinCandidateBSizeStatus,
- _ => FoundCandidateBSizeStatus,
- }
- }
- TryingMaxCandidateBSizeStatus => {
- if self.candidate_value < self.min_block_size {
- TryingMinCandidateBSizeStatus
- } else {
- FoundCandidateBSizeStatus
- }
- }
- TryingMinCandidateBSizeStatus | FoundCandidateBSizeStatus => {
- FoundCandidateBSizeStatus
- }
- };
-
- match self.status {
- TryingBSizeCandidateBSizeStatus => Some(self.block_size),
- TryingMaxCandidateBSizeStatus => {
- Some(Specified(self.max_block_size.unwrap()))
- }
- TryingMinCandidateBSizeStatus => {
- Some(Specified(self.min_block_size))
- }
- FoundCandidateBSizeStatus => None,
- InitialCandidateBSizeStatus => fail!(),
- }
- }
-}
-
-enum CandidateBSizeIteratorStatus {
- InitialCandidateBSizeStatus,
- TryingBSizeCandidateBSizeStatus,
- TryingMaxCandidateBSizeStatus,
- TryingMinCandidateBSizeStatus,
- FoundCandidateBSizeStatus,
-}
-
-// A helper function used in block-size calculation.
-fn translate_including_floats(cur_b: &mut Au, delta: Au, floats: &mut Floats) {
- *cur_b = *cur_b + delta;
- let writing_mode = floats.writing_mode;
- floats.translate(LogicalSize::new(writing_mode, Au(0), -delta));
-}
-
-/// The real assign-block-sizes traversal for flows with position 'absolute'.
-///
-/// This is a traversal of an Absolute Flow tree.
-/// - Relatively positioned flows and the Root flow start new Absolute flow trees.
-/// - The kids of a flow in this tree will be the flows for which it is the
-/// absolute Containing Block.
-/// - Thus, leaf nodes and inner non-root nodes are all Absolute Flows.
-///
-/// A Flow tree can have several Absolute Flow trees (depending on the number
-/// of relatively positioned flows it has).
-///
-/// Note that flows with position 'fixed' just form a flat list as they all
-/// have the Root flow as their CB.
-struct AbsoluteAssignBSizesTraversal<'a>(&'a LayoutContext<'a>);
-
-impl<'a> PreorderFlowTraversal for AbsoluteAssignBSizesTraversal<'a> {
- #[inline]
- fn process(&mut self, flow: &mut Flow) -> bool {
- let block_flow = flow.as_block();
-
- // The root of the absolute flow tree is definitely not absolutely
- // positioned. Nothing to process here.
- if block_flow.is_root_of_absolute_flow_tree() {
- return true;
- }
-
-
- let AbsoluteAssignBSizesTraversal(ref ctx) = *self;
- block_flow.calculate_abs_block_size_and_margins(*ctx);
- true
- }
-}
-
-/// The store-overflow traversal particular to absolute flows.
-///
-/// Propagate overflow up the Absolute flow tree and update overflow up to and
-/// not including the root of the Absolute flow tree.
-/// After that, it is up to the normal store-overflow traversal to propagate
-/// it further up.
-struct AbsoluteStoreOverflowTraversal<'a>{
- layout_context: &'a LayoutContext<'a>,
-}
-
-impl<'a> PostorderFlowTraversal for AbsoluteStoreOverflowTraversal<'a> {
- #[inline]
- fn process(&mut self, flow: &mut Flow) -> bool {
- // This will be taken care of by the normal store-overflow traversal.
- if flow.is_root_of_absolute_flow_tree() {
- return true;
- }
-
- flow.store_overflow(self.layout_context);
- true
- }
-}
-
-enum BlockType {
- BlockReplacedType,
- BlockNonReplacedType,
- AbsoluteReplacedType,
- AbsoluteNonReplacedType,
- FloatReplacedType,
- FloatNonReplacedType,
-}
-
-#[deriving(Clone, PartialEq)]
-pub enum MarginsMayCollapseFlag {
- MarginsMayCollapse,
- MarginsMayNotCollapse,
-}
-
-#[deriving(PartialEq)]
-enum FormattingContextType {
- NonformattingContext,
- BlockFormattingContext,
- OtherFormattingContext,
-}
-
-// Propagates the `layers_needed_for_descendants` flag appropriately from a child. This is called
-// as part of block-size assignment.
-//
-// If any fixed descendants of kids are present, this kid needs a layer.
-//
-// FIXME(#2006, pcwalton): This is too layer-happy. Like WebKit, we shouldn't do this unless
-// the positioned descendants are actually on top of the fixed kids.
-//
-// TODO(#1244, #2007, pcwalton): Do this for CSS transforms and opacity too, at least if they're
-// animating.
-fn propagate_layer_flag_from_child(layers_needed_for_descendants: &mut bool, kid: &mut Flow) {
- if kid.is_absolute_containing_block() {
- let kid_base = flow::mut_base(kid);
- if kid_base.flags.needs_layer() {
- *layers_needed_for_descendants = true
- }
- } else {
- let kid_base = flow::mut_base(kid);
- if kid_base.flags.layers_needed_for_descendants() {
- *layers_needed_for_descendants = true
- }
- }
-}
-
-// A block formatting context.
-#[deriving(Encodable)]
-pub struct BlockFlow {
- /// Data common to all flows.
- pub base: BaseFlow,
-
- /// The associated fragment.
- pub fragment: Fragment,
-
- /// TODO: is_root should be a bit field to conserve memory.
- /// Whether this block flow is the root flow.
- pub is_root: bool,
-
- /// Static y offset of an absolute flow from its CB.
- pub static_b_offset: Au,
-
- /// The inline-size of the last float prior to this block. This is used to speculatively lay out
- /// block formatting contexts.
- previous_float_inline_size: Option<Au>,
-
- /// Additional floating flow members.
- pub float: Option<Box<FloatedBlockInfo>>
-}
-
-impl BlockFlow {
- pub fn from_node(constructor: &mut FlowConstructor, node: &ThreadSafeLayoutNode) -> BlockFlow {
- BlockFlow {
- base: BaseFlow::new((*node).clone()),
- fragment: Fragment::new(constructor, node),
- is_root: false,
- static_b_offset: Au::new(0),
- previous_float_inline_size: None,
- float: None
- }
- }
-
- pub fn from_node_and_fragment(node: &ThreadSafeLayoutNode, fragment: Fragment) -> BlockFlow {
- BlockFlow {
- base: BaseFlow::new((*node).clone()),
- fragment: fragment,
- is_root: false,
- static_b_offset: Au::new(0),
- previous_float_inline_size: None,
- float: None
- }
- }
-
- pub fn float_from_node(constructor: &mut FlowConstructor,
- node: &ThreadSafeLayoutNode,
- float_kind: FloatKind)
- -> BlockFlow {
- let base = BaseFlow::new((*node).clone());
- BlockFlow {
- fragment: Fragment::new(constructor, node),
- is_root: false,
- static_b_offset: Au::new(0),
- previous_float_inline_size: None,
- float: Some(box FloatedBlockInfo::new(float_kind, base.writing_mode)),
- base: base,
- }
- }
-
- /// Return the type of this block.
- ///
- /// This determines the algorithm used to calculate inline-size, block-size, and the
- /// relevant margins for this Block.
- fn block_type(&self) -> BlockType {
- if self.is_absolutely_positioned() {
- if self.is_replaced_content() {
- AbsoluteReplacedType
- } else {
- AbsoluteNonReplacedType
- }
- } else if self.is_float() {
- if self.is_replaced_content() {
- FloatReplacedType
- } else {
- FloatNonReplacedType
- }
- } else {
- if self.is_replaced_content() {
- BlockReplacedType
- } else {
- BlockNonReplacedType
- }
- }
- }
-
- /// Compute the used value of inline-size for this Block.
- fn compute_used_inline_size(&mut self, ctx: &LayoutContext, containing_block_inline_size: Au) {
- let block_type = self.block_type();
- match block_type {
- AbsoluteReplacedType => {
- let inline_size_computer = AbsoluteReplaced;
- inline_size_computer.compute_used_inline_size(self, ctx, containing_block_inline_size);
- }
- AbsoluteNonReplacedType => {
- let inline_size_computer = AbsoluteNonReplaced;
- inline_size_computer.compute_used_inline_size(self, ctx, containing_block_inline_size);
- }
- FloatReplacedType => {
- let inline_size_computer = FloatReplaced;
- inline_size_computer.compute_used_inline_size(self, ctx, containing_block_inline_size);
- }
- FloatNonReplacedType => {
- let inline_size_computer = FloatNonReplaced;
- inline_size_computer.compute_used_inline_size(self, ctx, containing_block_inline_size);
- }
- BlockReplacedType => {
- let inline_size_computer = BlockReplaced;
- inline_size_computer.compute_used_inline_size(self, ctx, containing_block_inline_size);
- }
- BlockNonReplacedType => {
- let inline_size_computer = BlockNonReplaced;
- inline_size_computer.compute_used_inline_size(self, ctx, containing_block_inline_size);
- }
- }
- }
-
- /// Return this flow's fragment.
- pub fn fragment<'a>(&'a mut self) -> &'a mut Fragment {
- &mut self.fragment
- }
-
- /// Return the static x offset from the appropriate Containing Block for this flow.
- pub fn static_i_offset(&self) -> Au {
- if self.is_fixed() {
- self.base.fixed_static_i_offset
- } else {
- self.base.absolute_static_i_offset
- }
- }
-
- /// Return the size of the Containing Block for this flow.
- ///
- /// Right now, this only gets the Containing Block size for absolutely
- /// positioned elements.
- /// Note: Assume this is called in a top-down traversal, so it is ok to
- /// reference the CB.
- #[inline]
- pub fn containing_block_size(&mut self, viewport_size: Size2D<Au>) -> LogicalSize<Au> {
- assert!(self.is_absolutely_positioned());
- if self.is_fixed() {
- // Initial containing block is the CB for the root
- LogicalSize::from_physical(self.base.writing_mode, viewport_size)
- } else {
- self.base.absolute_cb.generated_containing_block_rect().size
- }
- }
-
- /// Traverse the Absolute flow tree in preorder.
- ///
- /// Traverse all your direct absolute descendants, who will then traverse
- /// their direct absolute descendants.
- /// Also, set the static y offsets for each descendant (using the value
- /// which was bubbled up during normal assign-block-size).
- ///
- /// Return true if the traversal is to continue or false to stop.
- fn traverse_preorder_absolute_flows<T:PreorderFlowTraversal>(&mut self,
- traversal: &mut T)
- -> bool {
- let flow = self as &mut Flow;
- if traversal.should_prune(flow) {
- return true
- }
-
- if !traversal.process(flow) {
- return false
- }
-
- let cb_block_start_edge_offset = flow.generated_containing_block_rect().start.b;
- let mut descendant_offset_iter = mut_base(flow).abs_descendants.iter_with_offset();
- // Pass in the respective static y offset for each descendant.
- for (ref mut descendant_link, ref y_offset) in descendant_offset_iter {
- let block = descendant_link.as_block();
- // The stored y_offset is wrt to the flow box.
- // Translate it to the CB (which is the padding box).
- block.static_b_offset = **y_offset - cb_block_start_edge_offset;
- if !block.traverse_preorder_absolute_flows(traversal) {
- return false
- }
- }
-
- true
- }
-
- /// Traverse the Absolute flow tree in postorder.
- ///
- /// Return true if the traversal is to continue or false to stop.
- fn traverse_postorder_absolute_flows<T:PostorderFlowTraversal>(&mut self,
- traversal: &mut T)
- -> bool {
- let flow = self as &mut Flow;
- if traversal.should_prune(flow) {
- return true
- }
-
- for descendant_link in mut_base(flow).abs_descendants.iter() {
- let block = descendant_link.as_block();
- if !block.traverse_postorder_absolute_flows(traversal) {
- return false
- }
- }
-
- traversal.process(flow)
- }
-
- /// Return true if this has a replaced fragment.
- ///
- /// The only two types of replaced fragments currently are text fragments
- /// and image fragments.
- fn is_replaced_content(&self) -> bool {
- match self.fragment.specific {
- ScannedTextFragment(_) | ImageFragment(_) => true,
- _ => false,
- }
- }
-
- /// Return shrink-to-fit inline-size.
- ///
- /// This is where we use the preferred inline-sizes and minimum inline-sizes
- /// calculated in the bubble-inline-sizes traversal.
- fn get_shrink_to_fit_inline_size(&self, available_inline_size: Au) -> Au {
- geometry::min(self.base.intrinsic_inline_sizes.preferred_inline_size,
- geometry::max(self.base.intrinsic_inline_sizes.minimum_inline_size, available_inline_size))
- }
-
- /// Collect and update static y-offsets bubbled up by kids.
- ///
- /// This would essentially give us offsets of all absolutely positioned
- /// direct descendants and all fixed descendants, in tree order.
- ///
- /// Assume that this is called in a bottom-up traversal (specifically, the
- /// assign-block-size traversal). So, kids have their flow origin already set.
- /// In the case of absolute flow kids, they have their hypothetical box
- /// position already set.
- fn collect_static_b_offsets_from_kids(&mut self) {
- let mut abs_descendant_y_offsets = Vec::new();
- for kid in self.base.child_iter() {
- let mut gives_abs_offsets = true;
- if kid.is_block_like() {
- let kid_block = kid.as_block();
- if kid_block.is_fixed() || kid_block.is_absolutely_positioned() {
- // It won't contribute any offsets for descendants because it
- // would be the CB for them.
- gives_abs_offsets = false;
- // Give the offset for the current absolute flow alone.
- abs_descendant_y_offsets.push(kid_block.get_hypothetical_block_start_edge());
- } else if kid_block.is_positioned() {
- // It won't contribute any offsets because it would be the CB
- // for the descendants.
- gives_abs_offsets = false;
- }
- }
-
- if gives_abs_offsets {
- let kid_base = flow::mut_base(kid);
- // Avoid copying the offset vector.
- let offsets = mem::replace(&mut kid_base.abs_descendants.static_b_offsets, Vec::new());
- // Consume all the static y-offsets bubbled up by kid.
- for y_offset in offsets.move_iter() {
- // The offsets are wrt the kid flow box. Translate them to current flow.
- abs_descendant_y_offsets.push(y_offset + kid_base.position.start.b);
- }
- }
- }
- self.base.abs_descendants.static_b_offsets = abs_descendant_y_offsets;
- }
-
- /// If this is the root flow, shifts all kids down and adjusts our size to account for
- /// root flow margins, which should never be collapsed according to CSS § 8.3.1.
- ///
- /// TODO(#2017, pcwalton): This is somewhat inefficient (traverses kids twice); can we do
- /// better?
- fn adjust_fragments_for_collapsed_margins_if_root(&mut self) {
- if !self.is_root() {
- return
- }
-
- let (block_start_margin_value, block_end_margin_value) = match self.base.collapsible_margins {
- MarginsCollapseThrough(_) => fail!("Margins unexpectedly collapsed through root flow."),
- MarginsCollapse(block_start_margin, block_end_margin) => {
- (block_start_margin.collapse(), block_end_margin.collapse())
- }
- NoCollapsibleMargins(block_start, block_end) => (block_start, block_end),
- };
-
- // Shift all kids down (or up, if margins are negative) if necessary.
- if block_start_margin_value != Au(0) {
- for kid in self.base.child_iter() {
- let kid_base = flow::mut_base(kid);
- kid_base.position.start.b = kid_base.position.start.b + block_start_margin_value
- }
- }
-
- self.base.position.size.block = self.base.position.size.block + block_start_margin_value +
- block_end_margin_value;
- self.fragment.border_box.size.block = self.fragment.border_box.size.block + block_start_margin_value +
- block_end_margin_value;
- }
-
- /// Assign block-size for current flow.
- ///
- /// * Collapse margins for flow's children and set in-flow child flows' y-coordinates now that
- /// we know their block-sizes.
- /// * Calculate and set the block-size of the current flow.
- /// * Calculate block-size, vertical margins, and y-coordinate for the flow's box. Ideally, this
- /// should be calculated using CSS § 10.6.7.
- ///
- /// For absolute flows, we store the calculated content block-size for the flow. We defer the
- /// calculation of the other values until a later traversal.
- ///
- /// `inline(always)` because this is only ever called by in-order or non-in-order top-level
- /// methods
- #[inline(always)]
- pub fn assign_block_size_block_base<'a>(&mut self,
- layout_context: &'a LayoutContext<'a>,
- margins_may_collapse: MarginsMayCollapseFlag) {
- let _scope = layout_debug_scope!("assign_block_size_block_base {:s}", self.base.debug_id());
-
- // Our current border-box position.
- let mut cur_b = Au(0);
-
- // Absolute positioning establishes a block formatting context. Don't propagate floats
- // in or out. (But do propagate them between kids.)
- if self.is_absolutely_positioned() {
- self.base.floats = Floats::new(self.fragment.style.writing_mode);
- }
- if margins_may_collapse != MarginsMayCollapse {
- self.base.floats = Floats::new(self.fragment.style.writing_mode);
- }
-
- let mut margin_collapse_info = MarginCollapseInfo::new();
- self.base.floats.translate(LogicalSize::new(
- self.fragment.style.writing_mode, -self.fragment.inline_start_offset(), Au(0)));
-
- // The sum of our block-start border and block-start padding.
- let block_start_offset = self.fragment.border_padding.block_start;
- translate_including_floats(&mut cur_b, block_start_offset, &mut self.base.floats);
-
- let can_collapse_block_start_margin_with_kids =
- margins_may_collapse == MarginsMayCollapse &&
- !self.is_absolutely_positioned() &&
- self.fragment.border_padding.block_start == Au(0);
- margin_collapse_info.initialize_block_start_margin(&self.fragment,
- can_collapse_block_start_margin_with_kids);
-
- // At this point, `cur_b` is at the content edge of our box. Now iterate over children.
- let mut floats = self.base.floats.clone();
- let mut layers_needed_for_descendants = false;
- for kid in self.base.child_iter() {
- if kid.is_absolutely_positioned() {
- // Assume that the *hypothetical box* for an absolute flow starts immediately after
- // the block-end border edge of the previous flow.
- flow::mut_base(kid).position.start.b = cur_b;
- kid.assign_block_size_for_inorder_child_if_necessary(layout_context);
- propagate_layer_flag_from_child(&mut layers_needed_for_descendants, kid);
-
- // Skip the collapsing and float processing for absolute flow kids and continue
- // with the next flow.
- continue
- }
-
- // Assign block-size now for the child if it was impacted by floats and we couldn't before.
- flow::mut_base(kid).floats = floats.clone();
- if kid.is_float() {
- // FIXME(pcwalton): Using `position.start.b` to mean the float ceiling is a
- // bit of a hack.
- flow::mut_base(kid).position.start.b =
- margin_collapse_info.current_float_ceiling();
- propagate_layer_flag_from_child(&mut layers_needed_for_descendants, kid);
-
- let kid_was_impacted_by_floats =
- kid.assign_block_size_for_inorder_child_if_necessary(layout_context);
- assert!(kid_was_impacted_by_floats); // As it was a float itself...
-
- let kid_base = flow::mut_base(kid);
- kid_base.position.start.b = cur_b;
- floats = kid_base.floats.clone();
- continue
- }
-
-
- // If we have clearance, assume there are no floats in.
- //
- // FIXME(#2008, pcwalton): This could be wrong if we have `clear: left` or `clear:
- // right` and there are still floats to impact, of course. But this gets complicated
- // with margin collapse. Possibly the right thing to do is to lay out the block again
- // in this rare case. (Note that WebKit can lay blocks out twice; this may be related,
- // although I haven't looked into it closely.)
- if kid.float_clearance() != clear::none {
- flow::mut_base(kid).floats = Floats::new(self.fragment.style.writing_mode)
- }
-
- // Lay the child out if this was an in-order traversal.
- let kid_was_impacted_by_floats =
- kid.assign_block_size_for_inorder_child_if_necessary(layout_context);
-
- // Mark flows for layerization if necessary to handle painting order correctly.
- propagate_layer_flag_from_child(&mut layers_needed_for_descendants, kid);
-
- // Handle any (possibly collapsed) top margin.
- let delta = margin_collapse_info.advance_block_start_margin(
- &flow::base(kid).collapsible_margins);
- translate_including_floats(&mut cur_b, delta, &mut floats);
-
- // Clear past the floats that came in, if necessary.
- let clearance = match kid.float_clearance() {
- clear::none => Au(0),
- clear::left => floats.clearance(ClearLeft),
- clear::right => floats.clearance(ClearRight),
- clear::both => floats.clearance(ClearBoth),
- };
- cur_b = cur_b + clearance;
-
- // At this point, `cur_b` is at the border edge of the child.
- flow::mut_base(kid).position.start.b = cur_b;
-
- // Now pull out the child's outgoing floats. We didn't do this immediately after the
- // `assign_block-size_for_inorder_child_if_necessary` call because clearance on a block
- // operates on the floats that come *in*, not the floats that go *out*.
- if kid_was_impacted_by_floats {
- floats = flow::mut_base(kid).floats.clone()
- }
-
- // Move past the child's border box. Do not use the `translate_including_floats`
- // function here because the child has already translated floats past its border box.
- let kid_base = flow::mut_base(kid);
- cur_b = cur_b + kid_base.position.size.block;
-
- // Handle any (possibly collapsed) block-end margin.
- let delta = margin_collapse_info.advance_block_end_margin(&kid_base.collapsible_margins);
- translate_including_floats(&mut cur_b, delta, &mut floats);
- }
-
- // Mark ourselves for layerization if that will be necessary to paint in the proper order
- // (CSS 2.1, Appendix E).
- self.base.flags.set_layers_needed_for_descendants(layers_needed_for_descendants);
-
- // Collect various offsets needed by absolutely positioned descendants.
- self.collect_static_b_offsets_from_kids();
-
- // Add in our block-end margin and compute our collapsible margins.
- let can_collapse_block_end_margin_with_kids =
- margins_may_collapse == MarginsMayCollapse &&
- !self.is_absolutely_positioned() &&
- self.fragment.border_padding.block_end == Au(0);
- let (collapsible_margins, delta) =
- margin_collapse_info.finish_and_compute_collapsible_margins(
- &self.fragment,
- can_collapse_block_end_margin_with_kids);
- self.base.collapsible_margins = collapsible_margins;
- translate_including_floats(&mut cur_b, delta, &mut floats);
-
- // FIXME(#2003, pcwalton): The max is taken here so that you can scroll the page, but this
- // is not correct behavior according to CSS 2.1 § 10.5. Instead I think we should treat the
- // root element as having `overflow: scroll` and use the layers-based scrolling
- // infrastructure to make it scrollable.
- let mut block_size = cur_b - block_start_offset;
- if self.is_root() {
- let screen_size = LogicalSize::from_physical(
- self.fragment.style.writing_mode, layout_context.shared.screen_size);
- block_size = Au::max(screen_size.block, block_size)
- }
-
- if self.is_absolutely_positioned() {
- // The content block-size includes all the floats per CSS 2.1 § 10.6.7. The easiest way to
- // handle this is to just treat this as clearance.
- block_size = block_size + floats.clearance(ClearBoth);
-
- // Fixed position layers get layers.
- if self.is_fixed() {
- self.base.flags.set_needs_layer(true)
- }
-
- // Store the content block-size for use in calculating the absolute flow's dimensions
- // later.
- self.fragment.border_box.size.block = block_size;
- return
- }
-
- let mut candidate_block_size_iterator = CandidateBSizeIterator::new(self.fragment.style(),
- None);
- for candidate_block_size in candidate_block_size_iterator {
- candidate_block_size_iterator.candidate_value = match candidate_block_size {
- Auto => block_size,
- Specified(value) => value
- }
- }
-
- // Adjust `cur_b` as necessary to account for the explicitly-specified block-size.
- block_size = candidate_block_size_iterator.candidate_value;
- let delta = block_size - (cur_b - block_start_offset);
- translate_including_floats(&mut cur_b, delta, &mut floats);
-
- // Compute content block-size and noncontent block-size.
- let block_end_offset = self.fragment.border_padding.block_end;
- translate_including_floats(&mut cur_b, block_end_offset, &mut floats);
-
- // Now that `cur_b` is at the block-end of the border box, compute the final border box
- // position.
- self.fragment.border_box.size.block = cur_b;
- self.fragment.border_box.start.b = Au(0);
- self.base.position.size.block = cur_b;
-
- self.base.floats = floats.clone();
- self.adjust_fragments_for_collapsed_margins_if_root();
-
- if self.is_root_of_absolute_flow_tree() {
- // Assign block-sizes for all flows in this Absolute flow tree.
- // This is preorder because the block-size of an absolute flow may depend on
- // the block-size of its CB, which may also be an absolute flow.
- self.traverse_preorder_absolute_flows(&mut AbsoluteAssignBSizesTraversal(
- layout_context));
- // Store overflow for all absolute descendants.
- self.traverse_postorder_absolute_flows(&mut AbsoluteStoreOverflowTraversal {
- layout_context: layout_context,
- });
- }
- }
-
- /// Add placement information about current float flow for use by the parent.
- ///
- /// Also, use information given by parent about other floats to find out our relative position.
- ///
- /// This does not give any information about any float descendants because they do not affect
- /// elements outside of the subtree rooted at this float.
- ///
- /// This function is called on a kid flow by a parent. Therefore, `assign_block-size_float` was
- /// already called on this kid flow by the traversal function. So, the values used are
- /// well-defined.
- pub fn place_float(&mut self) {
- let block_size = self.fragment.border_box.size.block;
- let clearance = match self.fragment.clear() {
- None => Au(0),
- Some(clear) => self.base.floats.clearance(clear),
- };
-
- let margin_block_size = self.fragment.margin.block_start_end();
- let info = PlacementInfo {
- size: LogicalSize::new(
- self.fragment.style.writing_mode,
- self.base.position.size.inline + self.fragment.margin.inline_start_end() +
- self.fragment.border_padding.inline_start_end(),
- block_size + margin_block_size),
- ceiling: clearance + self.base.position.start.b,
- max_inline_size: self.float.get_ref().containing_inline_size,
- kind: self.float.get_ref().float_kind,
- };
-
- // Place the float and return the `Floats` back to the parent flow.
- // After, grab the position and use that to set our position.
- self.base.floats.add_float(&info);
-
- self.float.get_mut_ref().rel_pos = self.base.floats.last_float_pos().unwrap();
- }
-
- /// Assign block-size for current flow.
- ///
- /// + Set in-flow child flows' y-coordinates now that we know their
- /// block-sizes. This _doesn't_ do any margin collapsing for its children.
- /// + Calculate block-size and y-coordinate for the flow's box. Ideally, this
- /// should be calculated using CSS Section 10.6.7
- ///
- /// It does not calculate the block-size of the flow itself.
- pub fn assign_block_size_float<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
- let _scope = layout_debug_scope!("assign_block_size_float {:s}", self.base.debug_id());
-
- let mut floats = Floats::new(self.fragment.style.writing_mode);
- for kid in self.base.child_iter() {
- flow::mut_base(kid).floats = floats.clone();
- kid.assign_block_size_for_inorder_child_if_necessary(ctx);
- floats = flow::mut_base(kid).floats.clone();
- }
-
- let block_start_offset = self.fragment.margin.block_start + self.fragment.border_padding.block_start;
- let mut cur_b = block_start_offset;
-
- // cur_b is now at the block-start content edge
-
- for kid in self.base.child_iter() {
- let child_base = flow::mut_base(kid);
- child_base.position.start.b = cur_b;
- // cur_b is now at the block-end margin edge of kid
- cur_b = cur_b + child_base.position.size.block;
- }
-
- // Intrinsic height should include floating descendants with a margin
- // below the element's bottom edge (see CSS Section 10.6.7).
- let content_block_size = geometry::max(
- cur_b - block_start_offset,
- floats.clearance(ClearBoth));
-
- // Floats establish a block formatting context, so we discard the output floats here.
- drop(floats);
-
- // The associated fragment has the border box of this flow.
- self.fragment.border_box.start.b = self.fragment.margin.block_start;
-
- // Calculate content block-size, taking `min-block-size` and `max-block-size` into account.
- let mut candidate_block_size_iterator = CandidateBSizeIterator::new(self.fragment.style(), None);
- for candidate_block_size in candidate_block_size_iterator {
- candidate_block_size_iterator.candidate_value = match candidate_block_size {
- Auto => content_block_size,
- Specified(value) => value,
- }
- }
-
- let content_block_size = candidate_block_size_iterator.candidate_value;
- let noncontent_block_size = self.fragment.border_padding.block_start_end();
- debug!("assign_block_size_float -- block_size: {}", content_block_size + noncontent_block_size);
- self.fragment.border_box.size.block = content_block_size + noncontent_block_size;
- }
-
- fn build_display_list_block_common(&mut self,
- layout_context: &LayoutContext,
- offset: LogicalPoint<Au>,
- background_border_level: BackgroundAndBorderLevel) {
- let rel_offset =
- self.fragment.relative_position(&self.base
- .absolute_position_info
- .relative_containing_block_size);
-
- // FIXME(#2795): Get the real container size
- let container_size = Size2D::zero();
-
- // Add the box that starts the block context.
- let mut display_list = DisplayList::new();
- let mut accumulator = self.fragment.build_display_list(
- &mut display_list,
- layout_context,
- self.base.abs_position + (offset + rel_offset).to_physical(
- self.base.writing_mode, container_size),
- background_border_level);
-
- let mut child_layers = DList::new();
- for kid in self.base.child_iter() {
- if kid.is_absolutely_positioned() {
- // All absolute flows will be handled by their containing block.
- continue
- }
-
- accumulator.push_child(&mut display_list, kid);
- child_layers.append(mem::replace(&mut flow::mut_base(kid).layers, DList::new()))
- }
-
- // Process absolute descendant links.
- for abs_descendant_link in self.base.abs_descendants.iter() {
- // TODO(pradeep): Send in our absolute position directly.
- accumulator.push_child(&mut display_list, abs_descendant_link);
- child_layers.append(mem::replace(&mut flow::mut_base(abs_descendant_link).layers,
- DList::new()));
- }
-
- accumulator.finish(&mut *self, display_list);
- self.base.layers = child_layers
- }
-
- /// Add display items for current block.
- ///
- /// Set the absolute position for children after doing any offsetting for
- /// position: relative.
- pub fn build_display_list_block(&mut self, layout_context: &LayoutContext) {
- if self.is_float() {
- // TODO(#2009, pcwalton): This is a pseudo-stacking context. We need to merge `z-index:
- // auto` kids into the parent stacking context, when that is supported.
- self.build_display_list_float(layout_context)
- } else if self.is_absolutely_positioned() {
- self.build_display_list_abs(layout_context)
- } else {
- let writing_mode = self.base.writing_mode;
- self.build_display_list_block_common(
- layout_context, LogicalPoint::zero(writing_mode), BlockLevel)
- }
- }
-
- pub fn build_display_list_float(&mut self, layout_context: &LayoutContext) {
- let float_offset = self.float.get_ref().rel_pos;
- self.build_display_list_block_common(layout_context,
- float_offset,
- RootOfStackingContextLevel);
- self.base.display_list = mem::replace(&mut self.base.display_list,
- DisplayList::new()).flatten(FloatStackingLevel)
- }
-
- /// Calculate and set the block-size, offsets, etc. for absolutely positioned flow.
- ///
- /// The layout for its in-flow children has been done during normal layout.
- /// This is just the calculation of:
- /// + block-size for the flow
- /// + y-coordinate of the flow wrt its Containing Block.
- /// + block-size, vertical margins, and y-coordinate for the flow's box.
- fn calculate_abs_block_size_and_margins(&mut self, ctx: &LayoutContext) {
- let containing_block_block_size = self.containing_block_size(ctx.shared.screen_size).block;
- let static_b_offset = self.static_b_offset;
-
- // This is the stored content block-size value from assign-block-size
- let content_block_size = self.fragment.content_box().size.block;
-
- let mut solution = None;
- {
- // Non-auto margin-block-start and margin-block-end values have already been
- // calculated during assign-inline-size.
- let margin = self.fragment.style().logical_margin();
- let margin_block_start = match margin.block_start {
- LPA_Auto => Auto,
- _ => Specified(self.fragment.margin.block_start)
- };
- let margin_block_end = match margin.block_end {
- LPA_Auto => Auto,
- _ => Specified(self.fragment.margin.block_end)
- };
-
- let block_start;
- let block_end;
- {
- let position = self.fragment.style().logical_position();
- block_start = MaybeAuto::from_style(position.block_start, containing_block_block_size);
- block_end = MaybeAuto::from_style(position.block_end, containing_block_block_size);
- }
-
- let available_block_size = containing_block_block_size - self.fragment.border_padding.block_start_end();
- if self.is_replaced_content() {
- // Calculate used value of block-size just like we do for inline replaced elements.
- // TODO: Pass in the containing block block-size when Fragment's
- // assign-block-size can handle it correctly.
- self.fragment.assign_replaced_block_size_if_necessary();
- // TODO: Right now, this content block-size value includes the
- // margin because of erroneous block-size calculation in fragment.
- // Check this when that has been fixed.
- let block_size_used_val = self.fragment.border_box.size.block;
- solution = Some(BSizeConstraintSolution::solve_vertical_constraints_abs_replaced(
- block_size_used_val,
- margin_block_start,
- margin_block_end,
- block_start,
- block_end,
- content_block_size,
- available_block_size,
- static_b_offset));
- } else {
- let style = self.fragment.style();
- let mut candidate_block_size_iterator =
- CandidateBSizeIterator::new(style, Some(containing_block_block_size));
-
- for block_size_used_val in candidate_block_size_iterator {
- solution =
- Some(BSizeConstraintSolution::solve_vertical_constraints_abs_nonreplaced(
- block_size_used_val,
- margin_block_start,
- margin_block_end,
- block_start,
- block_end,
- content_block_size,
- available_block_size,
- static_b_offset));
-
- candidate_block_size_iterator.candidate_value = solution.unwrap().block_size
- }
- }
- }
-
- let solution = solution.unwrap();
- self.fragment.margin.block_start = solution.margin_block_start;
- self.fragment.margin.block_end = solution.margin_block_end;
- self.fragment.border_box.start.b = Au(0);
- self.fragment.border_box.size.block = solution.block_size + self.fragment.border_padding.block_start_end();
-
- self.base.position.start.b = solution.block_start + self.fragment.margin.block_start;
- self.base.position.size.block = solution.block_size + self.fragment.border_padding.block_start_end();
- }
-
- /// Add display items for Absolutely Positioned flow.
- fn build_display_list_abs(&mut self, layout_context: &LayoutContext) {
- let writing_mode = self.base.writing_mode;
- self.build_display_list_block_common(layout_context,
- LogicalPoint::zero(writing_mode),
- RootOfStackingContextLevel);
-
- if !self.base.absolute_position_info.layers_needed_for_positioned_flows &&
- !self.base.flags.needs_layer() {
- // We didn't need a layer.
- //
- // TODO(#781, pcwalton): `z-index`.
- self.base.display_list =
- mem::replace(&mut self.base.display_list,
- DisplayList::new()).flatten(PositionedDescendantStackingLevel(0));
- return
- }
-
- // If we got here, then we need a new layer.
- let layer_rect = self.base.position.union(&self.base.overflow);
- let size = Size2D(layer_rect.size.inline.to_nearest_px() as uint,
- layer_rect.size.block.to_nearest_px() as uint);
- let origin = Point2D(layer_rect.start.i.to_nearest_px() as uint,
- layer_rect.start.b.to_nearest_px() as uint);
- let scroll_policy = if self.is_fixed() {
- FixedPosition
- } else {
- Scrollable
- };
- let display_list = mem::replace(&mut self.base.display_list, DisplayList::new());
- let new_layer = RenderLayer {
- id: self.layer_id(0),
- display_list: Arc::new(display_list.flatten(ContentStackingLevel)),
- position: Rect(origin, size),
- background_color: color::rgba(1.0, 1.0, 1.0, 0.0),
- scroll_policy: scroll_policy,
- };
- self.base.layers.push(new_layer)
- }
-
- /// Return the block-start outer edge of the hypothetical box for an absolute flow.
- ///
- /// This is wrt its parent flow box.
- ///
- /// During normal layout assign-block-size, the absolute flow's position is
- /// roughly set to its static position (the position it would have had in
- /// the normal flow).
- fn get_hypothetical_block_start_edge(&self) -> Au {
- self.base.position.start.b
- }
-
- /// Assigns the computed inline-start content edge and inline-size to all the children of this block flow.
- /// Also computes whether each child will be impacted by floats.
- ///
- /// `#[inline(always)]` because this is called only from block or table inline-size assignment and
- /// the code for block layout is significantly simpler.
- #[inline(always)]
- pub fn propagate_assigned_inline_size_to_children(&mut self,
- inline_start_content_edge: Au,
- content_inline_size: Au,
- opt_col_inline_sizes: Option<Vec<Au>>) {
- // Keep track of whether floats could impact each child.
- let mut inline_start_floats_impact_child = self.base.flags.impacted_by_left_floats();
- let mut inline_end_floats_impact_child = self.base.flags.impacted_by_right_floats();
-
- let absolute_static_i_offset = if self.is_positioned() {
- // This flow is the containing block. The static X offset will be the inline-start padding
- // edge.
- self.fragment.border_padding.inline_start
- - self.fragment.style().logical_border_width().inline_start
- } else {
- // For kids, the inline-start margin edge will be at our inline-start content edge. The current static
- // offset is at our inline-start margin edge. So move in to the inline-start content edge.
- self.base.absolute_static_i_offset + inline_start_content_edge
- };
-
- let fixed_static_i_offset = self.base.fixed_static_i_offset + inline_start_content_edge;
- let flags = self.base.flags.clone();
-
- // This value is used only for table cells.
- let mut inline_start_margin_edge = inline_start_content_edge;
-
- // The inline-size of the last float, if there was one. This is used for estimating the inline-sizes of
- // block formatting contexts. (We estimate that the inline-size of any block formatting context
- // that we see will be based on the inline-size of the containing block as well as the last float
- // seen before it.)
- let mut last_float_inline_size = None;
-
- for (i, kid) in self.base.child_iter().enumerate() {
- if kid.is_block_flow() {
- let kid_block = kid.as_block();
- kid_block.base.absolute_static_i_offset = absolute_static_i_offset;
- kid_block.base.fixed_static_i_offset = fixed_static_i_offset;
-
- if kid_block.is_float() {
- last_float_inline_size = Some(kid_block.base.intrinsic_inline_sizes.preferred_inline_size)
- } else {
- kid_block.previous_float_inline_size = last_float_inline_size
- }
- }
-
- // The inline-start margin edge of the child flow is at our inline-start content edge, and its inline-size
- // is our content inline-size.
- flow::mut_base(kid).position.start.i = inline_start_content_edge;
- flow::mut_base(kid).position.size.inline = content_inline_size;
-
- // Determine float impaction.
- match kid.float_clearance() {
- clear::none => {}
- clear::left => inline_start_floats_impact_child = false,
- clear::right => inline_end_floats_impact_child = false,
- clear::both => {
- inline_start_floats_impact_child = false;
- inline_end_floats_impact_child = false;
- }
- }
- {
- let kid_base = flow::mut_base(kid);
- inline_start_floats_impact_child = inline_start_floats_impact_child ||
- kid_base.flags.has_left_floated_descendants();
- inline_end_floats_impact_child = inline_end_floats_impact_child ||
- kid_base.flags.has_right_floated_descendants();
- kid_base.flags.set_impacted_by_left_floats(inline_start_floats_impact_child);
- kid_base.flags.set_impacted_by_right_floats(inline_end_floats_impact_child);
- }
-
- // Handle tables.
- match opt_col_inline_sizes {
- Some(ref col_inline_sizes) => {
- propagate_column_inline_sizes_to_child(kid,
- i,
- content_inline_size,
- col_inline_sizes.as_slice(),
- &mut inline_start_margin_edge)
- }
- None => {}
- }
-
- // Per CSS 2.1 § 16.3.1, text alignment propagates to all children in flow.
- //
- // TODO(#2018, pcwalton): Do this in the cascade instead.
- flow::mut_base(kid).flags.propagate_text_alignment_from_parent(flags.clone())
- }
- }
-
- /// Determines the type of formatting context this is. See the definition of
- /// `FormattingContextType`.
- fn formatting_context_type(&self) -> FormattingContextType {
- let style = self.fragment.style();
- if style.get_box().float != float::none {
- return OtherFormattingContext
- }
- match style.get_box().display {
- display::table_cell | display::table_caption | display::inline_block => {
- OtherFormattingContext
- }
- _ if style.get_box().position == position::static_ &&
- style.get_box().overflow != overflow::visible => {
- BlockFormattingContext
- }
- _ => NonformattingContext,
- }
- }
-}
-
-impl Flow for BlockFlow {
- fn class(&self) -> FlowClass {
- BlockFlowClass
- }
-
- fn as_block<'a>(&'a mut self) -> &'a mut BlockFlow {
- self
- }
-
- fn as_immutable_block<'a>(&'a self) -> &'a BlockFlow {
- self
- }
-
- /// Returns the direction that this flow clears floats in, if any.
- fn float_clearance(&self) -> clear::T {
- self.fragment.style().get_box().clear
- }
-
- /// Pass 1 of reflow: computes minimum and preferred inline-sizes.
- ///
- /// Recursively (bottom-up) determine the flow's minimum and preferred inline-sizes. When called on
- /// this flow, all child flows have had their minimum and preferred inline-sizes set. This function
- /// must decide minimum/preferred inline-sizes based on its children's inline-sizes and the dimensions of
- /// any fragments it is responsible for flowing.
- ///
- /// TODO(pcwalton): Inline blocks.
- fn bubble_inline_sizes(&mut self, _: &LayoutContext) {
- let _scope = layout_debug_scope!("bubble_inline_sizes {:s}", self.base.debug_id());
-
- let mut flags = self.base.flags;
- flags.set_has_left_floated_descendants(false);
- flags.set_has_right_floated_descendants(false);
-
- // If this block has a fixed width, just use that for the minimum
- // and preferred width, rather than bubbling up children inline
- // width.
- let fixed_width = match self.fragment.style().get_box().width {
- LPA_Length(_) => true,
- _ => false,
- };
-
- // Find the maximum inline-size from children.
- let mut intrinsic_inline_sizes = IntrinsicISizes::new();
- for child_ctx in self.base.child_iter() {
- assert!(child_ctx.is_block_flow() ||
- child_ctx.is_inline_flow() ||
- child_ctx.is_table_kind());
-
- let child_base = flow::mut_base(child_ctx);
-
- if !fixed_width {
- intrinsic_inline_sizes.minimum_inline_size =
- geometry::max(intrinsic_inline_sizes.minimum_inline_size,
- child_base.intrinsic_inline_sizes.total_minimum_inline_size());
- intrinsic_inline_sizes.preferred_inline_size =
- geometry::max(intrinsic_inline_sizes.preferred_inline_size,
- child_base.intrinsic_inline_sizes.total_preferred_inline_size());
- }
-
- flags.union_floated_descendants_flags(child_base.flags);
- }
-
- let fragment_intrinsic_inline_sizes = self.fragment.intrinsic_inline_sizes();
- intrinsic_inline_sizes.minimum_inline_size = geometry::max(intrinsic_inline_sizes.minimum_inline_size,
- fragment_intrinsic_inline_sizes.minimum_inline_size);
- intrinsic_inline_sizes.preferred_inline_size = geometry::max(intrinsic_inline_sizes.preferred_inline_size,
- fragment_intrinsic_inline_sizes.preferred_inline_size);
- intrinsic_inline_sizes.surround_inline_size = fragment_intrinsic_inline_sizes.surround_inline_size;
- self.base.intrinsic_inline_sizes = intrinsic_inline_sizes;
-
- match self.fragment.style().get_box().float {
- float::none => {}
- float::left => flags.set_has_left_floated_descendants(true),
- float::right => flags.set_has_right_floated_descendants(true),
- }
- self.base.flags = flags
- }
-
- /// Recursively (top-down) determines the actual inline-size of child contexts and fragments. When
- /// called on this context, the context has had its inline-size set by the parent context.
- ///
- /// Dual fragments consume some inline-size first, and the remainder is assigned to all child (block)
- /// contexts.
- fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
- let _scope = layout_debug_scope!("block::assign_inline_sizes {:s}", self.base.debug_id());
-
- debug!("assign_inline_sizes({}): assigning inline_size for flow",
- if self.is_float() {
- "float"
- } else {
- "block"
- });
-
- if self.is_root() {
- debug!("Setting root position");
- self.base.position.start = LogicalPoint::zero(self.base.writing_mode);
- self.base.position.size.inline = LogicalSize::from_physical(
- self.base.writing_mode, layout_context.shared.screen_size).inline;
- self.base.floats = Floats::new(self.base.writing_mode);
-
- // The root element is never impacted by floats.
- self.base.flags.set_impacted_by_left_floats(false);
- self.base.flags.set_impacted_by_right_floats(false);
- }
-
- // Our inline-size was set to the inline-size of the containing block by the flow's parent. Now compute
- // the real value.
- let containing_block_inline_size = self.base.position.size.inline;
- self.compute_used_inline_size(layout_context, containing_block_inline_size);
- if self.is_float() {
- self.float.get_mut_ref().containing_inline_size = containing_block_inline_size;
- }
-
- // Formatting contexts are never impacted by floats.
- match self.formatting_context_type() {
- NonformattingContext => {}
- BlockFormattingContext => {
- self.base.flags.set_impacted_by_left_floats(false);
- self.base.flags.set_impacted_by_right_floats(false);
-
- // We can't actually compute the inline-size of this block now, because floats might
- // affect it. Speculate that its inline-size is equal to the inline-size computed above minus
- // the inline-size of the previous float.
- match self.previous_float_inline_size {
- None => {}
- Some(previous_float_inline_size) => {
- self.fragment.border_box.size.inline =
- self.fragment.border_box.size.inline - previous_float_inline_size
- }
- }
- }
- OtherFormattingContext => {
- self.base.flags.set_impacted_by_left_floats(false);
- self.base.flags.set_impacted_by_right_floats(false);
- }
- }
-
- // Move in from the inline-start border edge
- let inline_start_content_edge = self.fragment.border_box.start.i + self.fragment.border_padding.inline_start;
- let padding_and_borders = self.fragment.border_padding.inline_start_end();
- let content_inline_size = self.fragment.border_box.size.inline - padding_and_borders;
-
- if self.is_float() {
- self.base.position.size.inline = content_inline_size;
- }
-
- self.propagate_assigned_inline_size_to_children(inline_start_content_edge, content_inline_size, None);
- }
-
- /// Assigns block-sizes in-order; or, if this is a float, places the float. The default
- /// implementation simply assigns block-sizes if this flow is impacted by floats. Returns true if
- /// this child was impacted by floats or false otherwise.
- ///
- /// This is called on child flows by the parent. Hence, we can assume that `assign_block-size` has
- /// already been called on the child (because of the bottom-up traversal).
- fn assign_block_size_for_inorder_child_if_necessary<'a>(&mut self, layout_context: &'a LayoutContext<'a>)
- -> bool {
- if self.is_float() {
- self.place_float();
- return true
- }
-
- let impacted = self.base.flags.impacted_by_floats();
- if impacted {
- self.assign_block_size(layout_context);
- }
- impacted
- }
-
- fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
-
- if self.is_replaced_content() {
- // Assign block-size for fragment if it is an image fragment.
- self.fragment.assign_replaced_block_size_if_necessary();
- } else if self.is_float() {
- debug!("assign_block_size_float: assigning block_size for float");
- self.assign_block_size_float(ctx);
- } else if self.is_root() {
- // Root element margins should never be collapsed according to CSS § 8.3.1.
- debug!("assign_block_size: assigning block_size for root flow");
- self.assign_block_size_block_base(ctx, MarginsMayNotCollapse);
- } else {
- debug!("assign_block_size: assigning block_size for block");
- self.assign_block_size_block_base(ctx, MarginsMayCollapse);
- }
- }
-
- fn compute_absolute_position(&mut self) {
- // FIXME(#2795): Get the real container size
- let container_size = Size2D::zero();
-
- if self.is_absolutely_positioned() {
- let position_start = self.base.position.start.to_physical(
- self.base.writing_mode, container_size);
- self.base
- .absolute_position_info
- .absolute_containing_block_position = if self.is_fixed() {
- // The viewport is initially at (0, 0).
- position_start
- } else {
- // Absolute position of the containing block + position of absolute flow w/r/t the
- // containing block.
- self.base.absolute_position_info.absolute_containing_block_position
- + position_start
- };
-
- // Set the absolute position, which will be passed down later as part
- // of containing block details for absolute descendants.
- self.base.abs_position =
- self.base.absolute_position_info.absolute_containing_block_position;
- }
-
- // For relatively-positioned descendants, the containing block formed by a block is just
- // the content box. The containing block for absolutely-positioned descendants, on the
- // other hand, is only established if we are positioned.
- let relative_offset =
- self.fragment.relative_position(&self.base
- .absolute_position_info
- .relative_containing_block_size);
- if self.is_positioned() {
- self.base.absolute_position_info.absolute_containing_block_position =
- self.base.abs_position
- + (self.generated_containing_block_rect().start
- + relative_offset).to_physical(self.base.writing_mode, container_size)
- }
-
- let float_offset = if self.is_float() {
- self.float.get_ref().rel_pos
- } else {
- LogicalPoint::zero(self.base.writing_mode)
- };
-
- // Compute absolute position info for children.
- let mut absolute_position_info = self.base.absolute_position_info;
- absolute_position_info.relative_containing_block_size = self.fragment.content_box().size;
- absolute_position_info.layers_needed_for_positioned_flows =
- self.base.flags.layers_needed_for_descendants();
-
- // Process children.
- let this_position = self.base.abs_position;
- let writing_mode = self.base.writing_mode;
- for kid in self.base.child_iter() {
- if !kid.is_absolutely_positioned() {
- let kid_base = flow::mut_base(kid);
- kid_base.abs_position = this_position + (
- kid_base.position.start
- .add_point(&float_offset)
- + relative_offset).to_physical(writing_mode, container_size);
- kid_base.absolute_position_info = absolute_position_info
- }
- }
-
- // Process absolute descendant links.
- for absolute_descendant in self.base.abs_descendants.iter() {
- flow::mut_base(absolute_descendant).absolute_position_info = absolute_position_info
- }
- }
-
- fn mark_as_root(&mut self) {
- self.is_root = true
- }
-
- /// Return true if store overflow is delayed for this flow.
- ///
- /// Currently happens only for absolutely positioned flows.
- fn is_store_overflow_delayed(&mut self) -> bool {
- self.is_absolutely_positioned()
- }
-
- fn is_root(&self) -> bool {
- self.is_root
- }
-
- fn is_float(&self) -> bool {
- self.float.is_some()
- }
-
- /// The 'position' property of this flow.
- fn positioning(&self) -> position::T {
- self.fragment.style.get_box().position
- }
-
- /// Return true if this is the root of an Absolute flow tree.
- ///
- /// It has to be either relatively positioned or the Root flow.
- fn is_root_of_absolute_flow_tree(&self) -> bool {
- self.is_relatively_positioned() || self.is_root()
- }
-
- /// Return the dimensions of the containing block generated by this flow for absolutely-
- /// positioned descendants. For block flows, this is the padding box.
- fn generated_containing_block_rect(&self) -> LogicalRect<Au> {
- self.fragment.border_box - self.fragment.style().logical_border_width()
- }
-
- fn layer_id(&self, fragment_index: uint) -> LayerId {
- // FIXME(#2010, pcwalton): This is a hack and is totally bogus in the presence of pseudo-
- // elements. But until we have incremental reflow we can't do better--we recreate the flow
- // for every DOM node so otherwise we nuke layers on every reflow.
- LayerId(self.fragment.node.id(), fragment_index)
- }
-
- fn is_absolute_containing_block(&self) -> bool {
- self.is_positioned()
- }
-}
-
-impl fmt::Show for BlockFlow {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- if self.is_float() {
- write!(f, "FloatFlow: {}", self.fragment)
- } else if self.is_root() {
- write!(f, "RootFlow: {}", self.fragment)
- } else {
- write!(f, "BlockFlow: {}", self.fragment)
- }
- }
-}
-
-/// The inputs for the inline-sizes-and-margins constraint equation.
-pub struct ISizeConstraintInput {
- pub computed_inline_size: MaybeAuto,
- pub inline_start_margin: MaybeAuto,
- pub inline_end_margin: MaybeAuto,
- pub inline_start: MaybeAuto,
- pub inline_end: MaybeAuto,
- pub available_inline_size: Au,
- pub static_i_offset: Au,
-}
-
-impl ISizeConstraintInput {
- pub fn new(computed_inline_size: MaybeAuto,
- inline_start_margin: MaybeAuto,
- inline_end_margin: MaybeAuto,
- inline_start: MaybeAuto,
- inline_end: MaybeAuto,
- available_inline_size: Au,
- static_i_offset: Au)
- -> ISizeConstraintInput {
- ISizeConstraintInput {
- computed_inline_size: computed_inline_size,
- inline_start_margin: inline_start_margin,
- inline_end_margin: inline_end_margin,
- inline_start: inline_start,
- inline_end: inline_end,
- available_inline_size: available_inline_size,
- static_i_offset: static_i_offset,
- }
- }
-}
-
-/// The solutions for the inline-size-and-margins constraint equation.
-pub struct ISizeConstraintSolution {
- pub inline_start: Au,
- pub inline_end: Au,
- pub inline_size: Au,
- pub margin_inline_start: Au,
- pub margin_inline_end: Au
-}
-
-impl ISizeConstraintSolution {
- pub fn new(inline_size: Au, margin_inline_start: Au, margin_inline_end: Au) -> ISizeConstraintSolution {
- ISizeConstraintSolution {
- inline_start: Au(0),
- inline_end: Au(0),
- inline_size: inline_size,
- margin_inline_start: margin_inline_start,
- margin_inline_end: margin_inline_end,
- }
- }
-
- fn for_absolute_flow(inline_start: Au,
- inline_end: Au,
- inline_size: Au,
- margin_inline_start: Au,
- margin_inline_end: Au)
- -> ISizeConstraintSolution {
- ISizeConstraintSolution {
- inline_start: inline_start,
- inline_end: inline_end,
- inline_size: inline_size,
- margin_inline_start: margin_inline_start,
- margin_inline_end: margin_inline_end,
- }
- }
-}
-
-// Trait to encapsulate the ISize and Margin calculation.
-//
-// CSS Section 10.3
-pub trait ISizeAndMarginsComputer {
- /// Compute the inputs for the ISize constraint equation.
- ///
- /// This is called only once to compute the initial inputs. For
- /// calculation involving min-inline-size and max-inline-size, we don't need to
- /// recompute these.
- fn compute_inline_size_constraint_inputs(&self,
- block: &mut BlockFlow,
- parent_flow_inline_size: Au,
- ctx: &LayoutContext)
- -> ISizeConstraintInput {
- let containing_block_inline_size = self.containing_block_inline_size(block, parent_flow_inline_size, ctx);
- let computed_inline_size = self.initial_computed_inline_size(block, parent_flow_inline_size, ctx);
-
- block.fragment.compute_border_padding_margins(containing_block_inline_size);
-
- let style = block.fragment.style();
-
- // The text alignment of a block flow is the text alignment of its box's style.
- block.base.flags.set_text_align(style.get_inheritedtext().text_align);
-
- let margin = style.logical_margin();
- let position = style.logical_position();
-
- let available_inline_size = containing_block_inline_size - block.fragment.border_padding.inline_start_end();
- return ISizeConstraintInput::new(
- computed_inline_size,
- MaybeAuto::from_style(margin.inline_start, containing_block_inline_size),
- MaybeAuto::from_style(margin.inline_end, containing_block_inline_size),
- MaybeAuto::from_style(position.inline_start, containing_block_inline_size),
- MaybeAuto::from_style(position.inline_end, containing_block_inline_size),
- available_inline_size,
- block.static_i_offset());
- }
-
- /// Set the used values for inline-size and margins got from the relevant constraint equation.
- ///
- /// This is called only once.
- ///
- /// Set:
- /// + used values for content inline-size, inline-start margin, and inline-end margin for this flow's box.
- /// + x-coordinate of this flow's box.
- /// + x-coordinate of the flow wrt its Containing Block (if this is an absolute flow).
- fn set_inline_size_constraint_solutions(&self,
- block: &mut BlockFlow,
- solution: ISizeConstraintSolution) {
- let inline_size;
- {
- let fragment = block.fragment();
- fragment.margin.inline_start = solution.margin_inline_start;
- fragment.margin.inline_end = solution.margin_inline_end;
-
- // The associated fragment has the border box of this flow.
- // Left border edge.
- fragment.border_box.start.i = fragment.margin.inline_start;
- // Border box inline-size.
- inline_size = solution.inline_size + fragment.border_padding.inline_start_end();
- fragment.border_box.size.inline = inline_size;
- }
-
- // We also resize the block itself, to ensure that overflow is not calculated
- // as the inline-size of our parent. We might be smaller and we might be larger if we
- // overflow.
- let flow = flow::mut_base(block);
- flow.position.size.inline = inline_size;
- }
-
- /// Set the x coordinate of the given flow if it is absolutely positioned.
- fn set_flow_x_coord_if_necessary(&self, _: &mut BlockFlow, _: ISizeConstraintSolution) {}
-
- /// Solve the inline-size and margins constraints for this block flow.
- fn solve_inline_size_constraints(&self,
- block: &mut BlockFlow,
- input: &ISizeConstraintInput)
- -> ISizeConstraintSolution;
-
- fn initial_computed_inline_size(&self,
- block: &mut BlockFlow,
- parent_flow_inline_size: Au,
- ctx: &LayoutContext)
- -> MaybeAuto {
- MaybeAuto::from_style(block.fragment().style().content_inline_size(),
- self.containing_block_inline_size(block, parent_flow_inline_size, ctx))
- }
-
- fn containing_block_inline_size(&self,
- _: &mut BlockFlow,
- parent_flow_inline_size: Au,
- _: &LayoutContext)
- -> Au {
- parent_flow_inline_size
- }
-
- /// Compute the used value of inline-size, taking care of min-inline-size and max-inline-size.
- ///
- /// CSS Section 10.4: Minimum and Maximum inline-sizes
- fn compute_used_inline_size(&self,
- block: &mut BlockFlow,
- ctx: &LayoutContext,
- parent_flow_inline_size: Au) {
- let mut input = self.compute_inline_size_constraint_inputs(block, parent_flow_inline_size, ctx);
-
- let containing_block_inline_size = self.containing_block_inline_size(block, parent_flow_inline_size, ctx);
-
- let mut solution = self.solve_inline_size_constraints(block, &input);
-
- // If the tentative used inline-size is greater than 'max-inline-size', inline-size should be recalculated,
- // but this time using the computed value of 'max-inline-size' as the computed value for 'inline-size'.
- match specified_or_none(block.fragment().style().max_inline_size(), containing_block_inline_size) {
- Some(max_inline_size) if max_inline_size < solution.inline_size => {
- input.computed_inline_size = Specified(max_inline_size);
- solution = self.solve_inline_size_constraints(block, &input);
- }
- _ => {}
- }
-
- // If the resulting inline-size is smaller than 'min-inline-size', inline-size should be recalculated,
- // but this time using the value of 'min-inline-size' as the computed value for 'inline-size'.
- let computed_min_inline_size = specified(block.fragment().style().min_inline_size(),
- containing_block_inline_size);
- if computed_min_inline_size > solution.inline_size {
- input.computed_inline_size = Specified(computed_min_inline_size);
- solution = self.solve_inline_size_constraints(block, &input);
- }
-
- self.set_inline_size_constraint_solutions(block, solution);
- self.set_flow_x_coord_if_necessary(block, solution);
- }
-
- /// Computes inline-start and inline-end margins and inline-size.
- ///
- /// This is used by both replaced and non-replaced Blocks.
- ///
- /// CSS 2.1 Section 10.3.3.
- /// Constraint Equation: margin-inline-start + margin-inline-end + inline-size = available_inline-size
- /// where available_inline-size = CB inline-size - (horizontal border + padding)
- fn solve_block_inline_size_constraints(&self,
- _: &mut BlockFlow,
- input: &ISizeConstraintInput)
- -> ISizeConstraintSolution {
- let (computed_inline_size, inline_start_margin, inline_end_margin, available_inline_size) = (input.computed_inline_size,
- input.inline_start_margin,
- input.inline_end_margin,
- input.available_inline_size);
-
- // If inline-size is not 'auto', and inline-size + margins > available_inline-size, all
- // 'auto' margins are treated as 0.
- let (inline_start_margin, inline_end_margin) = match computed_inline_size {
- Auto => (inline_start_margin, inline_end_margin),
- Specified(inline_size) => {
- let inline_start = inline_start_margin.specified_or_zero();
- let inline_end = inline_end_margin.specified_or_zero();
-
- if (inline_start + inline_end + inline_size) > available_inline_size {
- (Specified(inline_start), Specified(inline_end))
- } else {
- (inline_start_margin, inline_end_margin)
- }
- }
- };
-
- // Invariant: inline-start_margin + inline-size + inline-end_margin == available_inline-size
- let (inline_start_margin, inline_size, inline_end_margin) = match (inline_start_margin, computed_inline_size, inline_end_margin) {
- // If all have a computed value other than 'auto', the system is
- // over-constrained so we discard the end margin.
- (Specified(margin_start), Specified(inline_size), Specified(_margin_end)) =>
- (margin_start, inline_size, available_inline_size - (margin_start + inline_size)),
-
- // If exactly one value is 'auto', solve for it
- (Auto, Specified(inline_size), Specified(margin_end)) =>
- (available_inline_size - (inline_size + margin_end), inline_size, margin_end),
- (Specified(margin_start), Auto, Specified(margin_end)) =>
- (margin_start, available_inline_size - (margin_start + margin_end), margin_end),
- (Specified(margin_start), Specified(inline_size), Auto) =>
- (margin_start, inline_size, available_inline_size - (margin_start + inline_size)),
-
- // If inline-size is set to 'auto', any other 'auto' value becomes '0',
- // and inline-size is solved for
- (Auto, Auto, Specified(margin_end)) =>
- (Au::new(0), available_inline_size - margin_end, margin_end),
- (Specified(margin_start), Auto, Auto) =>
- (margin_start, available_inline_size - margin_start, Au::new(0)),
- (Auto, Auto, Auto) =>
- (Au::new(0), available_inline_size, Au::new(0)),
-
- // If inline-start and inline-end margins are auto, they become equal
- (Auto, Specified(inline_size), Auto) => {
- let margin = (available_inline_size - inline_size).scale_by(0.5);
- (margin, inline_size, margin)
- }
- };
- ISizeConstraintSolution::new(inline_size, inline_start_margin, inline_end_margin)
- }
-}
-
-/// The different types of Blocks.
-///
-/// They mainly differ in the way inline-size and block-sizes and margins are calculated
-/// for them.
-struct AbsoluteNonReplaced;
-struct AbsoluteReplaced;
-struct BlockNonReplaced;
-struct BlockReplaced;
-struct FloatNonReplaced;
-struct FloatReplaced;
-
-impl ISizeAndMarginsComputer for AbsoluteNonReplaced {
- /// Solve the horizontal constraint equation for absolute non-replaced elements.
- ///
- /// CSS Section 10.3.7
- /// Constraint equation:
- /// inline-start + inline-end + inline-size + margin-inline-start + margin-inline-end
- /// = absolute containing block inline-size - (horizontal padding and border)
- /// [aka available_inline-size]
- ///
- /// Return the solution for the equation.
- fn solve_inline_size_constraints(&self,
- block: &mut BlockFlow,
- input: &ISizeConstraintInput)
- -> ISizeConstraintSolution {
- let &ISizeConstraintInput {
- computed_inline_size,
- inline_start_margin,
- inline_end_margin,
- inline_start,
- inline_end,
- available_inline_size,
- static_i_offset,
- ..
- } = input;
-
- // TODO: Check for direction of parent flow (NOT Containing Block)
- // when right-to-left is implemented.
- // Assume direction is 'ltr' for now
-
- // Distance from the inline-start edge of the Absolute Containing Block to the
- // inline-start margin edge of a hypothetical box that would have been the
- // first box of the element.
- let static_position_inline_start = static_i_offset;
-
- let (inline_start, inline_end, inline_size, margin_inline_start, margin_inline_end) = match (inline_start, inline_end, computed_inline_size) {
- (Auto, Auto, Auto) => {
- let margin_start = inline_start_margin.specified_or_zero();
- let margin_end = inline_end_margin.specified_or_zero();
- let inline_start = static_position_inline_start;
- // Now it is the same situation as inline-start Specified and inline-end
- // and inline-size Auto.
-
- // Set inline-end to zero to calculate inline-size
- let inline_size = block.get_shrink_to_fit_inline_size(
- available_inline_size - (inline_start + margin_start + margin_end));
- let sum = inline_start + inline_size + margin_start + margin_end;
- (inline_start, available_inline_size - sum, inline_size, margin_start, margin_end)
- }
- (Specified(inline_start), Specified(inline_end), Specified(inline_size)) => {
- match (inline_start_margin, inline_end_margin) {
- (Auto, Auto) => {
- let total_margin_val = available_inline_size - inline_start - inline_end - inline_size;
- if total_margin_val < Au(0) {
- // margin-inline-start becomes 0 because direction is 'ltr'.
- // TODO: Handle 'rtl' when it is implemented.
- (inline_start, inline_end, inline_size, Au(0), total_margin_val)
- } else {
- // Equal margins
- (inline_start, inline_end, inline_size,
- total_margin_val.scale_by(0.5),
- total_margin_val.scale_by(0.5))
- }
- }
- (Specified(margin_start), Auto) => {
- let sum = inline_start + inline_end + inline_size + margin_start;
- (inline_start, inline_end, inline_size, margin_start, available_inline_size - sum)
- }
- (Auto, Specified(margin_end)) => {
- let sum = inline_start + inline_end + inline_size + margin_end;
- (inline_start, inline_end, inline_size, available_inline_size - sum, margin_end)
- }
- (Specified(margin_start), Specified(margin_end)) => {
- // Values are over-constrained.
- // Ignore value for 'inline-end' cos direction is 'ltr'.
- // TODO: Handle 'rtl' when it is implemented.
- let sum = inline_start + inline_size + margin_start + margin_end;
- (inline_start, available_inline_size - sum, inline_size, margin_start, margin_end)
- }
- }
- }
- // For the rest of the cases, auto values for margin are set to 0
-
- // If only one is Auto, solve for it
- (Auto, Specified(inline_end), Specified(inline_size)) => {
- let margin_start = inline_start_margin.specified_or_zero();
- let margin_end = inline_end_margin.specified_or_zero();
- let sum = inline_end + inline_size + margin_start + margin_end;
- (available_inline_size - sum, inline_end, inline_size, margin_start, margin_end)
- }
- (Specified(inline_start), Auto, Specified(inline_size)) => {
- let margin_start = inline_start_margin.specified_or_zero();
- let margin_end = inline_end_margin.specified_or_zero();
- let sum = inline_start + inline_size + margin_start + margin_end;
- (inline_start, available_inline_size - sum, inline_size, margin_start, margin_end)
- }
- (Specified(inline_start), Specified(inline_end), Auto) => {
- let margin_start = inline_start_margin.specified_or_zero();
- let margin_end = inline_end_margin.specified_or_zero();
- let sum = inline_start + inline_end + margin_start + margin_end;
- (inline_start, inline_end, available_inline_size - sum, margin_start, margin_end)
- }
-
- // If inline-size is auto, then inline-size is shrink-to-fit. Solve for the
- // non-auto value.
- (Specified(inline_start), Auto, Auto) => {
- let margin_start = inline_start_margin.specified_or_zero();
- let margin_end = inline_end_margin.specified_or_zero();
- // Set inline-end to zero to calculate inline-size
- let inline_size = block.get_shrink_to_fit_inline_size(
- available_inline_size - (inline_start + margin_start + margin_end));
- let sum = inline_start + inline_size + margin_start + margin_end;
- (inline_start, available_inline_size - sum, inline_size, margin_start, margin_end)
- }
- (Auto, Specified(inline_end), Auto) => {
- let margin_start = inline_start_margin.specified_or_zero();
- let margin_end = inline_end_margin.specified_or_zero();
- // Set inline-start to zero to calculate inline-size
- let inline_size = block.get_shrink_to_fit_inline_size(
- available_inline_size - (inline_end + margin_start + margin_end));
- let sum = inline_end + inline_size + margin_start + margin_end;
- (available_inline_size - sum, inline_end, inline_size, margin_start, margin_end)
- }
-
- (Auto, Auto, Specified(inline_size)) => {
- let margin_start = inline_start_margin.specified_or_zero();
- let margin_end = inline_end_margin.specified_or_zero();
- // Setting 'inline-start' to static position because direction is 'ltr'.
- // TODO: Handle 'rtl' when it is implemented.
- let inline_start = static_position_inline_start;
- let sum = inline_start + inline_size + margin_start + margin_end;
- (inline_start, available_inline_size - sum, inline_size, margin_start, margin_end)
- }
- };
- ISizeConstraintSolution::for_absolute_flow(inline_start, inline_end, inline_size, margin_inline_start, margin_inline_end)
- }
-
- fn containing_block_inline_size(&self, block: &mut BlockFlow, _: Au, ctx: &LayoutContext) -> Au {
- block.containing_block_size(ctx.shared.screen_size).inline
- }
-
- fn set_flow_x_coord_if_necessary(&self,
- block: &mut BlockFlow,
- solution: ISizeConstraintSolution) {
- // Set the x-coordinate of the absolute flow wrt to its containing block.
- block.base.position.start.i = solution.inline_start;
- }
-}
-
-impl ISizeAndMarginsComputer for AbsoluteReplaced {
- /// Solve the horizontal constraint equation for absolute replaced elements.
- ///
- /// `static_i_offset`: total offset of current flow's hypothetical
- /// position (static position) from its actual Containing Block.
- ///
- /// CSS Section 10.3.8
- /// Constraint equation:
- /// inline-start + inline-end + inline-size + margin-inline-start + margin-inline-end
- /// = absolute containing block inline-size - (horizontal padding and border)
- /// [aka available_inline-size]
- ///
- /// Return the solution for the equation.
- fn solve_inline_size_constraints(&self, _: &mut BlockFlow, input: &ISizeConstraintInput)
- -> ISizeConstraintSolution {
- let &ISizeConstraintInput {
- computed_inline_size,
- inline_start_margin,
- inline_end_margin,
- inline_start,
- inline_end,
- available_inline_size,
- static_i_offset,
- ..
- } = input;
- // TODO: Check for direction of static-position Containing Block (aka
- // parent flow, _not_ the actual Containing Block) when right-to-left
- // is implemented
- // Assume direction is 'ltr' for now
- // TODO: Handle all the cases for 'rtl' direction.
-
- let inline_size = match computed_inline_size {
- Specified(w) => w,
- _ => fail!("{} {}",
- "The used value for inline_size for absolute replaced flow",
- "should have already been calculated by now.")
- };
-
- // Distance from the inline-start edge of the Absolute Containing Block to the
- // inline-start margin edge of a hypothetical box that would have been the
- // first box of the element.
- let static_position_inline_start = static_i_offset;
-
- let (inline_start, inline_end, inline_size, margin_inline_start, margin_inline_end) = match (inline_start, inline_end) {
- (Auto, Auto) => {
- let inline_start = static_position_inline_start;
- let margin_start = inline_start_margin.specified_or_zero();
- let margin_end = inline_end_margin.specified_or_zero();
- let sum = inline_start + inline_size + margin_start + margin_end;
- (inline_start, available_inline_size - sum, inline_size, margin_start, margin_end)
- }
- // If only one is Auto, solve for it
- (Auto, Specified(inline_end)) => {
- let margin_start = inline_start_margin.specified_or_zero();
- let margin_end = inline_end_margin.specified_or_zero();
- let sum = inline_end + inline_size + margin_start + margin_end;
- (available_inline_size - sum, inline_end, inline_size, margin_start, margin_end)
- }
- (Specified(inline_start), Auto) => {
- let margin_start = inline_start_margin.specified_or_zero();
- let margin_end = inline_end_margin.specified_or_zero();
- let sum = inline_start + inline_size + margin_start + margin_end;
- (inline_start, available_inline_size - sum, inline_size, margin_start, margin_end)
- }
- (Specified(inline_start), Specified(inline_end)) => {
- match (inline_start_margin, inline_end_margin) {
- (Auto, Auto) => {
- let total_margin_val = available_inline_size - inline_start - inline_end - inline_size;
- if total_margin_val < Au(0) {
- // margin-inline-start becomes 0 because direction is 'ltr'.
- (inline_start, inline_end, inline_size, Au(0), total_margin_val)
- } else {
- // Equal margins
- (inline_start, inline_end, inline_size,
- total_margin_val.scale_by(0.5),
- total_margin_val.scale_by(0.5))
- }
- }
- (Specified(margin_start), Auto) => {
- let sum = inline_start + inline_end + inline_size + margin_start;
- (inline_start, inline_end, inline_size, margin_start, available_inline_size - sum)
- }
- (Auto, Specified(margin_end)) => {
- let sum = inline_start + inline_end + inline_size + margin_end;
- (inline_start, inline_end, inline_size, available_inline_size - sum, margin_end)
- }
- (Specified(margin_start), Specified(margin_end)) => {
- // Values are over-constrained.
- // Ignore value for 'inline-end' cos direction is 'ltr'.
- let sum = inline_start + inline_size + margin_start + margin_end;
- (inline_start, available_inline_size - sum, inline_size, margin_start, margin_end)
- }
- }
- }
- };
- ISizeConstraintSolution::for_absolute_flow(inline_start, inline_end, inline_size, margin_inline_start, margin_inline_end)
- }
-
- /// Calculate used value of inline-size just like we do for inline replaced elements.
- fn initial_computed_inline_size(&self,
- block: &mut BlockFlow,
- _: Au,
- ctx: &LayoutContext)
- -> MaybeAuto {
- let containing_block_inline_size = block.containing_block_size(ctx.shared.screen_size).inline;
- let fragment = block.fragment();
- fragment.assign_replaced_inline_size_if_necessary(containing_block_inline_size);
- // For replaced absolute flow, the rest of the constraint solving will
- // take inline-size to be specified as the value computed here.
- Specified(fragment.content_inline_size())
- }
-
- fn containing_block_inline_size(&self, block: &mut BlockFlow, _: Au, ctx: &LayoutContext) -> Au {
- block.containing_block_size(ctx.shared.screen_size).inline
- }
-
- fn set_flow_x_coord_if_necessary(&self, block: &mut BlockFlow, solution: ISizeConstraintSolution) {
- // Set the x-coordinate of the absolute flow wrt to its containing block.
- block.base.position.start.i = solution.inline_start;
- }
-}
-
-impl ISizeAndMarginsComputer for BlockNonReplaced {
- /// Compute inline-start and inline-end margins and inline-size.
- fn solve_inline_size_constraints(&self,
- block: &mut BlockFlow,
- input: &ISizeConstraintInput)
- -> ISizeConstraintSolution {
- self.solve_block_inline_size_constraints(block, input)
- }
-}
-
-impl ISizeAndMarginsComputer for BlockReplaced {
- /// Compute inline-start and inline-end margins and inline-size.
- ///
- /// ISize has already been calculated. We now calculate the margins just
- /// like for non-replaced blocks.
- fn solve_inline_size_constraints(&self,
- block: &mut BlockFlow,
- input: &ISizeConstraintInput)
- -> ISizeConstraintSolution {
- match input.computed_inline_size {
- Specified(_) => {},
- Auto => fail!("BlockReplaced: inline_size should have been computed by now")
- };
- self.solve_block_inline_size_constraints(block, input)
- }
-
- /// Calculate used value of inline-size just like we do for inline replaced elements.
- fn initial_computed_inline_size(&self,
- block: &mut BlockFlow,
- parent_flow_inline_size: Au,
- _: &LayoutContext)
- -> MaybeAuto {
- let fragment = block.fragment();
- fragment.assign_replaced_inline_size_if_necessary(parent_flow_inline_size);
- // For replaced block flow, the rest of the constraint solving will
- // take inline-size to be specified as the value computed here.
- Specified(fragment.content_inline_size())
- }
-
-}
-
-impl ISizeAndMarginsComputer for FloatNonReplaced {
- /// CSS Section 10.3.5
- ///
- /// If inline-size is computed as 'auto', the used value is the 'shrink-to-fit' inline-size.
- fn solve_inline_size_constraints(&self,
- block: &mut BlockFlow,
- input: &ISizeConstraintInput)
- -> ISizeConstraintSolution {
- let (computed_inline_size, inline_start_margin, inline_end_margin, available_inline_size) = (input.computed_inline_size,
- input.inline_start_margin,
- input.inline_end_margin,
- input.available_inline_size);
- let margin_inline_start = inline_start_margin.specified_or_zero();
- let margin_inline_end = inline_end_margin.specified_or_zero();
- let available_inline_size_float = available_inline_size - margin_inline_start - margin_inline_end;
- let shrink_to_fit = block.get_shrink_to_fit_inline_size(available_inline_size_float);
- let inline_size = computed_inline_size.specified_or_default(shrink_to_fit);
- debug!("assign_inline_sizes_float -- inline_size: {}", inline_size);
- ISizeConstraintSolution::new(inline_size, margin_inline_start, margin_inline_end)
- }
-}
-
-impl ISizeAndMarginsComputer for FloatReplaced {
- /// CSS Section 10.3.5
- ///
- /// If inline-size is computed as 'auto', the used value is the 'shrink-to-fit' inline-size.
- fn solve_inline_size_constraints(&self, _: &mut BlockFlow, input: &ISizeConstraintInput)
- -> ISizeConstraintSolution {
- let (computed_inline_size, inline_start_margin, inline_end_margin) = (input.computed_inline_size,
- input.inline_start_margin,
- input.inline_end_margin);
- let margin_inline_start = inline_start_margin.specified_or_zero();
- let margin_inline_end = inline_end_margin.specified_or_zero();
- let inline_size = match computed_inline_size {
- Specified(w) => w,
- Auto => fail!("FloatReplaced: inline_size should have been computed by now")
- };
- debug!("assign_inline_sizes_float -- inline_size: {}", inline_size);
- ISizeConstraintSolution::new(inline_size, margin_inline_start, margin_inline_end)
- }
-
- /// Calculate used value of inline-size just like we do for inline replaced elements.
- fn initial_computed_inline_size(&self,
- block: &mut BlockFlow,
- parent_flow_inline_size: Au,
- _: &LayoutContext)
- -> MaybeAuto {
- let fragment = block.fragment();
- fragment.assign_replaced_inline_size_if_necessary(parent_flow_inline_size);
- // For replaced block flow, the rest of the constraint solving will
- // take inline-size to be specified as the value computed here.
- Specified(fragment.content_inline_size())
- }
-}
-
-fn propagate_column_inline_sizes_to_child(kid: &mut Flow,
- child_index: uint,
- content_inline_size: Au,
- column_inline_sizes: &[Au],
- inline_start_margin_edge: &mut Au) {
- // If kid is table_rowgroup or table_row, the column inline-sizes info should be copied from its
- // parent.
- //
- // FIXME(pcwalton): This seems inefficient. Reference count it instead?
- let inline_size = if kid.is_table() || kid.is_table_rowgroup() || kid.is_table_row() {
- *kid.col_inline_sizes() = column_inline_sizes.iter().map(|&x| x).collect();
-
- // ISize of kid flow is our content inline-size.
- content_inline_size
- } else if kid.is_table_cell() {
- // If kid is table_cell, the x offset and inline-size for each cell should be
- // calculated from parent's column inline-sizes info.
- *inline_start_margin_edge = if child_index == 0 {
- Au(0)
- } else {
- *inline_start_margin_edge + column_inline_sizes[child_index - 1]
- };
-
- column_inline_sizes[child_index]
- } else {
- // ISize of kid flow is our content inline-size.
- content_inline_size
- };
-
- let kid_base = flow::mut_base(kid);
- kid_base.position.start.i = *inline_start_margin_edge;
- kid_base.position.size.inline = inline_size;
-}
-
diff --git a/src/components/layout/construct.rs b/src/components/layout/construct.rs
deleted file mode 100644
index 0f832bacfb8..00000000000
--- a/src/components/layout/construct.rs
+++ /dev/null
@@ -1,1049 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//! Creates flows and fragments from a DOM tree via a bottom-up, incremental traversal of the DOM.
-//!
-//! Each step of the traversal considers the node and existing flow, if there is one. If a node is
-//! not dirty and an existing flow exists, then the traversal reuses that flow. Otherwise, it
-//! proceeds to construct either a flow or a `ConstructionItem`. A construction item is a piece of
-//! intermediate data that goes with a DOM node and hasn't found its "home" yet-maybe it's a box,
-//! maybe it's an absolute or fixed position thing that hasn't found its containing block yet.
-//! Construction items bubble up the tree from children to parents until they find their homes.
-//!
-//! TODO(pcwalton): There is no incremental reflow yet. This scheme requires that nodes either have
-//! weak references to flows or that there be some mechanism to efficiently (O(1) time) "blow
-//! apart" a flow tree and have the flows migrate "home" to their respective DOM nodes while we
-//! perform flow tree construction. The precise mechanism for this will take some experimentation
-//! to get right.
-
-#![deny(unsafe_block)]
-
-use css::node_style::StyledNode;
-use block::BlockFlow;
-use context::LayoutContext;
-use floats::FloatKind;
-use flow::{Flow, ImmutableFlowUtils, MutableOwnedFlowUtils};
-use flow::{Descendants, AbsDescendants};
-use flow;
-use flow_ref::FlowRef;
-use fragment::{Fragment, GenericFragment, IframeFragment, IframeFragmentInfo};
-use fragment::{ImageFragment, ImageFragmentInfo, SpecificFragmentInfo, TableFragment};
-use fragment::{TableCellFragment, TableColumnFragment, TableColumnFragmentInfo};
-use fragment::{TableRowFragment, TableWrapperFragment, UnscannedTextFragment};
-use fragment::{UnscannedTextFragmentInfo};
-use inline::{InlineFragments, InlineFlow};
-use parallel;
-use table_wrapper::TableWrapperFlow;
-use table::TableFlow;
-use table_caption::TableCaptionFlow;
-use table_colgroup::TableColGroupFlow;
-use table_rowgroup::TableRowGroupFlow;
-use table_row::TableRowFlow;
-use table_cell::TableCellFlow;
-use text::TextRunScanner;
-use util::{LayoutDataAccess, OpaqueNodeMethods};
-use wrapper::{PostorderNodeMutTraversal, TLayoutNode, ThreadSafeLayoutNode};
-use wrapper::{Before, BeforeBlock, After, AfterBlock, Normal};
-
-use gfx::display_list::OpaqueNode;
-use script::dom::element::{HTMLIFrameElementTypeId, HTMLImageElementTypeId};
-use script::dom::element::{HTMLObjectElementTypeId};
-use script::dom::element::{HTMLTableColElementTypeId, HTMLTableDataCellElementTypeId};
-use script::dom::element::{HTMLTableElementTypeId, HTMLTableHeaderCellElementTypeId};
-use script::dom::element::{HTMLTableRowElementTypeId, HTMLTableSectionElementTypeId};
-use script::dom::node::{CommentNodeTypeId, DoctypeNodeTypeId, DocumentFragmentNodeTypeId};
-use script::dom::node::{DocumentNodeTypeId, ElementNodeTypeId, ProcessingInstructionNodeTypeId};
-use script::dom::node::{TextNodeTypeId};
-use script::dom::htmlobjectelement::is_image_data;
-use servo_util::namespace;
-use std::mem;
-use std::sync::atomics::Relaxed;
-use style::ComputedValues;
-use style::computed_values::{display, position, float};
-use sync::Arc;
-use url::Url;
-
-/// The results of flow construction for a DOM node.
-pub enum ConstructionResult {
- /// This node contributes nothing at all (`display: none`). Alternately, this is what newly
- /// created nodes have their `ConstructionResult` set to.
- NoConstructionResult,
-
- /// This node contributed a flow at the proper position in the tree.
- /// Nothing more needs to be done for this node. It has bubbled up fixed
- /// and absolute descendant flows that have a CB above it.
- FlowConstructionResult(FlowRef, AbsDescendants),
-
- /// This node contributed some object or objects that will be needed to construct a proper flow
- /// later up the tree, but these objects have not yet found their home.
- ConstructionItemConstructionResult(ConstructionItem),
-}
-
-/// Represents the output of flow construction for a DOM node that has not yet resulted in a
-/// complete flow. Construction items bubble up the tree until they find a `Flow` to be
-/// attached to.
-pub enum ConstructionItem {
- /// Inline fragments and associated {ib} splits that have not yet found flows.
- InlineFragmentsConstructionItem(InlineFragmentsConstructionResult),
- /// Potentially ignorable whitespace.
- WhitespaceConstructionItem(OpaqueNode, Arc<ComputedValues>),
- /// TableColumn Fragment
- TableColumnFragmentConstructionItem(Fragment),
-}
-
-/// Represents inline fragments and {ib} splits that are bubbling up from an inline.
-pub struct InlineFragmentsConstructionResult {
- /// Any {ib} splits that we're bubbling up.
- pub splits: Vec<InlineBlockSplit>,
-
- /// Any fragments that succeed the {ib} splits.
- pub fragments: InlineFragments,
-
- /// Any absolute descendants that we're bubbling up.
- pub abs_descendants: AbsDescendants,
-}
-
-/// Represents an {ib} split that has not yet found the containing block that it belongs to. This
-/// is somewhat tricky. An example may be helpful. For this DOM fragment:
-///
-/// <span>
-/// A
-/// <div>B</div>
-/// C
-/// </span>
-///
-/// The resulting `ConstructionItem` for the outer `span` will be:
-///
-/// InlineFragmentsConstructionItem(Some(~[
-/// InlineBlockSplit {
-/// predecessor_fragments: ~[
-/// A
-/// ],
-/// block: ~BlockFlow {
-/// B
-/// },
-/// }),~[
-/// C
-/// ])
-pub struct InlineBlockSplit {
- /// The inline fragments that precede the flow.
- pub predecessors: InlineFragments,
-
- /// The flow that caused this {ib} split.
- pub flow: FlowRef,
-}
-
-/// Holds inline fragments that we're gathering for children of an inline node.
-struct InlineFragmentsAccumulator {
- /// The list of fragments.
- fragments: InlineFragments,
-
- /// Whether we've created a range to enclose all the fragments. This will be Some() if the outer node
- /// is an inline and None otherwise.
- enclosing_style: Option<Arc<ComputedValues>>,
-}
-
-impl InlineFragmentsAccumulator {
- fn new() -> InlineFragmentsAccumulator {
- InlineFragmentsAccumulator {
- fragments: InlineFragments::new(),
- enclosing_style: None,
- }
- }
-
- fn from_inline_node(node: &ThreadSafeLayoutNode) -> InlineFragmentsAccumulator {
- let fragments = InlineFragments::new();
- InlineFragmentsAccumulator {
- fragments: fragments,
- enclosing_style: Some(node.style().clone()),
- }
- }
-
- fn finish(self) -> InlineFragments {
- let InlineFragmentsAccumulator {
- fragments: mut fragments,
- enclosing_style
- } = self;
-
- match enclosing_style {
- Some(enclosing_style) => {
- for frag in fragments.fragments.mut_iter() {
- frag.add_inline_context_style(enclosing_style.clone());
- }
- }
- None => {}
- }
- fragments
- }
-}
-
-enum WhitespaceStrippingMode {
- NoWhitespaceStripping,
- StripWhitespaceFromStart,
- StripWhitespaceFromEnd,
-}
-
-/// An object that knows how to create flows.
-pub struct FlowConstructor<'a, 'b> {
- /// The layout context.
- pub layout_context: &'b LayoutContext<'b>,
-}
-
-impl<'a, 'b> FlowConstructor<'a, 'b> {
- /// Creates a new flow constructor.
- pub fn new<'b>(layout_context: &'b LayoutContext)
- -> FlowConstructor<'a, 'b> {
- FlowConstructor {
- layout_context: layout_context,
- }
- }
-
- /// Builds the `ImageFragmentInfo` for the given image. This is out of line to guide inlining.
- fn build_fragment_info_for_image(&mut self, node: &ThreadSafeLayoutNode, url: Option<Url>)
- -> SpecificFragmentInfo {
- match url {
- None => GenericFragment,
- Some(url) => {
- // FIXME(pcwalton): The fact that image fragments store the cache within them makes
- // little sense to me.
- ImageFragment(ImageFragmentInfo::new(node, url, self.layout_context.shared.image_cache.clone()))
- }
- }
- }
-
- /// Builds specific `Fragment` info for the given node.
- pub fn build_specific_fragment_info_for_node(&mut self, node: &ThreadSafeLayoutNode)
- -> SpecificFragmentInfo {
- match node.type_id() {
- Some(ElementNodeTypeId(HTMLImageElementTypeId)) => {
- self.build_fragment_info_for_image(node, node.image_url())
- }
- Some(ElementNodeTypeId(HTMLIFrameElementTypeId)) => {
- IframeFragment(IframeFragmentInfo::new(node))
- }
- Some(ElementNodeTypeId(HTMLObjectElementTypeId)) => {
- let data = node.get_object_data();
- self.build_fragment_info_for_image(node, data)
- }
- Some(ElementNodeTypeId(HTMLTableElementTypeId)) => TableWrapperFragment,
- Some(ElementNodeTypeId(HTMLTableColElementTypeId)) => {
- TableColumnFragment(TableColumnFragmentInfo::new(node))
- }
- Some(ElementNodeTypeId(HTMLTableDataCellElementTypeId)) |
- Some(ElementNodeTypeId(HTMLTableHeaderCellElementTypeId)) => TableCellFragment,
- Some(ElementNodeTypeId(HTMLTableRowElementTypeId)) |
- Some(ElementNodeTypeId(HTMLTableSectionElementTypeId)) => TableRowFragment,
- None | Some(TextNodeTypeId) => UnscannedTextFragment(UnscannedTextFragmentInfo::new(node)),
- _ => GenericFragment,
- }
- }
-
- /// Creates an inline flow from a set of inline fragments, then adds it as a child of the given flow
- /// or pushes it onto the given flow list.
- ///
- /// `#[inline(always)]` because this is performance critical and LLVM will not inline it
- /// otherwise.
- #[inline(always)]
- fn flush_inline_fragments_to_flow_or_list(&mut self,
- fragment_accumulator: InlineFragmentsAccumulator,
- flow: &mut FlowRef,
- flow_list: &mut Vec<FlowRef>,
- whitespace_stripping: WhitespaceStrippingMode,
- node: &ThreadSafeLayoutNode) {
- let mut fragments = fragment_accumulator.finish();
- if fragments.is_empty() { return };
-
- match whitespace_stripping {
- NoWhitespaceStripping => {}
- StripWhitespaceFromStart => {
- fragments.strip_ignorable_whitespace_from_start();
- if fragments.is_empty() { return };
- }
- StripWhitespaceFromEnd => {
- fragments.strip_ignorable_whitespace_from_end();
- if fragments.is_empty() { return };
- }
- }
-
- let mut inline_flow = box InlineFlow::from_fragments((*node).clone(), fragments);
- let (ascent, descent) = inline_flow.compute_minimum_ascent_and_descent(self.layout_context.font_context(), &**node.style());
- inline_flow.minimum_block_size_above_baseline = ascent;
- inline_flow.minimum_depth_below_baseline = descent;
- let mut inline_flow = inline_flow as Box<Flow>;
- TextRunScanner::new().scan_for_runs(self.layout_context.font_context(), inline_flow);
- let mut inline_flow = FlowRef::new(inline_flow);
- inline_flow.finish(self.layout_context);
-
- if flow.get().need_anonymous_flow(inline_flow.get()) {
- flow_list.push(inline_flow)
- } else {
- flow.add_new_child(inline_flow)
- }
- }
-
- fn build_block_flow_using_children_construction_result(&mut self,
- flow: &mut FlowRef,
- consecutive_siblings: &mut Vec<FlowRef>,
- node: &ThreadSafeLayoutNode,
- kid: ThreadSafeLayoutNode,
- inline_fragment_accumulator:
- &mut InlineFragmentsAccumulator,
- abs_descendants: &mut Descendants,
- first_fragment: &mut bool) {
- match kid.swap_out_construction_result() {
- NoConstructionResult => {}
- FlowConstructionResult(kid_flow, kid_abs_descendants) => {
- // If kid_flow is TableCaptionFlow, kid_flow should be added under
- // TableWrapperFlow.
- if flow.get().is_table() && kid_flow.get().is_table_caption() {
- kid.set_flow_construction_result(FlowConstructionResult(
- kid_flow,
- Descendants::new()))
- } else if flow.get().need_anonymous_flow(kid_flow.get()) {
- consecutive_siblings.push(kid_flow)
- } else {
- // Flush any inline fragments that we were gathering up. This allows us to handle
- // {ib} splits.
- debug!("flushing {} inline box(es) to flow A",
- inline_fragment_accumulator.fragments.len());
- self.flush_inline_fragments_to_flow_or_list(
- mem::replace(inline_fragment_accumulator, InlineFragmentsAccumulator::new()),
- flow,
- consecutive_siblings,
- StripWhitespaceFromStart,
- node);
- if !consecutive_siblings.is_empty() {
- let consecutive_siblings = mem::replace(consecutive_siblings, vec!());
- self.generate_anonymous_missing_child(consecutive_siblings,
- flow,
- node);
- }
- flow.add_new_child(kid_flow);
- }
- abs_descendants.push_descendants(kid_abs_descendants);
- }
- ConstructionItemConstructionResult(InlineFragmentsConstructionItem(
- InlineFragmentsConstructionResult {
- splits: splits,
- fragments: successor_fragments,
- abs_descendants: kid_abs_descendants,
- })) => {
- // Add any {ib} splits.
- for split in splits.move_iter() {
- // Pull apart the {ib} split object and push its predecessor fragments
- // onto the list.
- let InlineBlockSplit {
- predecessors: predecessors,
- flow: kid_flow
- } = split;
- inline_fragment_accumulator.fragments.push_all(predecessors);
-
- // If this is the first fragment in flow, then strip ignorable
- // whitespace per CSS 2.1 § 9.2.1.1.
- let whitespace_stripping = if *first_fragment {
- *first_fragment = false;
- StripWhitespaceFromStart
- } else {
- NoWhitespaceStripping
- };
-
- // Flush any inline fragments that we were gathering up.
- debug!("flushing {} inline box(es) to flow A",
- inline_fragment_accumulator.fragments.len());
- self.flush_inline_fragments_to_flow_or_list(
- mem::replace(inline_fragment_accumulator,
- InlineFragmentsAccumulator::new()),
- flow,
- consecutive_siblings,
- whitespace_stripping,
- node);
-
- // Push the flow generated by the {ib} split onto our list of
- // flows.
- if flow.get().need_anonymous_flow(kid_flow.get()) {
- consecutive_siblings.push(kid_flow)
- } else {
- flow.add_new_child(kid_flow)
- }
- }
-
- // Add the fragments to the list we're maintaining.
- inline_fragment_accumulator.fragments.push_all(successor_fragments);
- abs_descendants.push_descendants(kid_abs_descendants);
- }
- ConstructionItemConstructionResult(WhitespaceConstructionItem(whitespace_node, whitespace_style)) => {
- // Add whitespace results. They will be stripped out later on when
- // between block elements, and retained when between inline elements.
- let fragment_info = UnscannedTextFragment(UnscannedTextFragmentInfo::from_text(" ".to_string()));
- let mut fragment = Fragment::from_opaque_node_and_style(whitespace_node,
- whitespace_style.clone(),
- fragment_info);
- inline_fragment_accumulator.fragments.push(&mut fragment, whitespace_style);
- }
- ConstructionItemConstructionResult(TableColumnFragmentConstructionItem(_)) => {
- // TODO: Implement anonymous table objects for missing parents
- // CSS 2.1 § 17.2.1, step 3-2
- }
- }
- }
-
- /// Build block flow for current node using information from children nodes.
- ///
- /// Consume results from children and combine them, handling {ib} splits.
- /// Block flows and inline flows thus created will become the children of
- /// this block flow.
- /// Also, deal with the absolute and fixed descendants bubbled up by
- /// children nodes.
- fn build_flow_using_children(&mut self, mut flow: FlowRef, node: &ThreadSafeLayoutNode)
- -> ConstructionResult {
- // Gather up fragments for the inline flows we might need to create.
- let mut inline_fragment_accumulator = InlineFragmentsAccumulator::new();
- let mut consecutive_siblings = vec!();
- let mut first_fragment = true;
-
- // List of absolute descendants, in tree order.
- let mut abs_descendants = Descendants::new();
- for kid in node.children() {
- if kid.get_pseudo_element_type() != Normal {
- self.process(&kid);
- }
-
- self.build_block_flow_using_children_construction_result(&mut flow,
- &mut consecutive_siblings,
- node,
- kid,
- &mut inline_fragment_accumulator,
- &mut abs_descendants,
- &mut first_fragment);
- }
-
- // Perform a final flush of any inline fragments that we were gathering up to handle {ib}
- // splits, after stripping ignorable whitespace.
- self.flush_inline_fragments_to_flow_or_list(inline_fragment_accumulator,
- &mut flow,
- &mut consecutive_siblings,
- StripWhitespaceFromEnd,
- node);
- if !consecutive_siblings.is_empty() {
- self.generate_anonymous_missing_child(consecutive_siblings, &mut flow, node);
- }
-
- // The flow is done.
- flow.finish(self.layout_context);
- let is_positioned = flow.get_mut().as_block().is_positioned();
- let is_fixed_positioned = flow.get_mut().as_block().is_fixed();
- let is_absolutely_positioned = flow.get_mut().as_block().is_absolutely_positioned();
- if is_positioned {
- // This is the CB for all the absolute descendants.
- flow.set_abs_descendants(abs_descendants);
-
- abs_descendants = Descendants::new();
-
- if is_fixed_positioned || is_absolutely_positioned {
- // This is now the only absolute flow in the subtree which hasn't yet
- // reached its CB.
- abs_descendants.push(flow.clone());
- }
- }
- FlowConstructionResult(flow, abs_descendants)
- }
-
- /// Builds a flow for a node with `display: block`. This yields a `BlockFlow` with possibly
- /// other `BlockFlow`s or `InlineFlow`s underneath it, depending on whether {ib} splits needed
- /// to happen.
- fn build_flow_for_block(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult {
- let flow = box BlockFlow::from_node(self, node) as Box<Flow>;
- self.build_flow_using_children(FlowRef::new(flow), node)
- }
-
- /// Builds the flow for a node with `float: {left|right}`. This yields a float `BlockFlow` with
- /// a `BlockFlow` underneath it.
- fn build_flow_for_floated_block(&mut self, node: &ThreadSafeLayoutNode, float_kind: FloatKind)
- -> ConstructionResult {
- let flow = box BlockFlow::float_from_node(self, node, float_kind) as Box<Flow>;
- self.build_flow_using_children(FlowRef::new(flow), node)
- }
-
- /// Concatenates the fragments of kids, adding in our own borders/padding/margins if necessary.
- /// Returns the `InlineFragmentsConstructionResult`, if any. There will be no
- /// `InlineFragmentsConstructionResult` if this node consisted entirely of ignorable whitespace.
- fn build_fragments_for_nonreplaced_inline_content(&mut self, node: &ThreadSafeLayoutNode)
- -> ConstructionResult {
- let mut opt_inline_block_splits: Vec<InlineBlockSplit> = Vec::new();
- let mut fragment_accumulator = InlineFragmentsAccumulator::from_inline_node(node);
- let mut abs_descendants = Descendants::new();
-
- // Concatenate all the fragments of our kids, creating {ib} splits as necessary.
- for kid in node.children() {
- if kid.get_pseudo_element_type() != Normal {
- self.process(&kid);
- }
- match kid.swap_out_construction_result() {
- NoConstructionResult => {}
- FlowConstructionResult(flow, kid_abs_descendants) => {
- // {ib} split. Flush the accumulator to our new split and make a new
- // accumulator to hold any subsequent fragments we come across.
- let split = InlineBlockSplit {
- predecessors:
- mem::replace(&mut fragment_accumulator,
- InlineFragmentsAccumulator::from_inline_node(node)).finish(),
- flow: flow,
- };
- opt_inline_block_splits.push(split);
- abs_descendants.push_descendants(kid_abs_descendants);
- }
- ConstructionItemConstructionResult(InlineFragmentsConstructionItem(
- InlineFragmentsConstructionResult {
- splits: splits,
- fragments: successors,
- abs_descendants: kid_abs_descendants,
- })) => {
-
- // Bubble up {ib} splits.
- for split in splits.move_iter() {
- let InlineBlockSplit {
- predecessors: predecessors,
- flow: kid_flow
- } = split;
- fragment_accumulator.fragments.push_all(predecessors);
-
- let split = InlineBlockSplit {
- predecessors:
- mem::replace(&mut fragment_accumulator,
- InlineFragmentsAccumulator::from_inline_node(node))
- .finish(),
- flow: kid_flow,
- };
- opt_inline_block_splits.push(split)
- }
-
- // Push residual fragments.
- fragment_accumulator.fragments.push_all(successors);
- abs_descendants.push_descendants(kid_abs_descendants);
- }
- ConstructionItemConstructionResult(WhitespaceConstructionItem(whitespace_node,
- whitespace_style))
- => {
- // Instantiate the whitespace fragment.
- let fragment_info = UnscannedTextFragment(UnscannedTextFragmentInfo::from_text(" ".to_string()));
- let mut fragment = Fragment::from_opaque_node_and_style(whitespace_node,
- whitespace_style.clone(),
- fragment_info);
- fragment_accumulator.fragments.push(&mut fragment, whitespace_style)
- }
- ConstructionItemConstructionResult(TableColumnFragmentConstructionItem(_)) => {
- // TODO: Implement anonymous table objects for missing parents
- // CSS 2.1 § 17.2.1, step 3-2
- }
- }
- }
-
- // Finally, make a new construction result.
- if opt_inline_block_splits.len() > 0 || fragment_accumulator.fragments.len() > 0
- || abs_descendants.len() > 0 {
- let construction_item = InlineFragmentsConstructionItem(InlineFragmentsConstructionResult {
- splits: opt_inline_block_splits,
- fragments: fragment_accumulator.finish(),
- abs_descendants: abs_descendants,
- });
- ConstructionItemConstructionResult(construction_item)
- } else {
- NoConstructionResult
- }
- }
-
- /// Creates an `InlineFragmentsConstructionResult` for replaced content. Replaced content doesn't
- /// render its children, so this just nukes a child's fragments and creates a `Fragment`.
- fn build_fragments_for_replaced_inline_content(&mut self, node: &ThreadSafeLayoutNode)
- -> ConstructionResult {
- for kid in node.children() {
- kid.set_flow_construction_result(NoConstructionResult)
- }
-
- // If this node is ignorable whitespace, bail out now.
- //
- // FIXME(#2001, pcwalton): Don't do this if there's padding or borders.
- if node.is_ignorable_whitespace() {
- let opaque_node = OpaqueNodeMethods::from_thread_safe_layout_node(node);
- return ConstructionItemConstructionResult(WhitespaceConstructionItem(
- opaque_node,
- node.style().clone()))
- }
-
- let mut fragments = InlineFragments::new();
- fragments.push(&mut Fragment::new(self, node), node.style().clone());
-
- let construction_item = InlineFragmentsConstructionItem(InlineFragmentsConstructionResult {
- splits: Vec::new(),
- fragments: fragments,
- abs_descendants: Descendants::new(),
- });
- ConstructionItemConstructionResult(construction_item)
- }
-
- /// Builds one or more fragments for a node with `display: inline`. This yields an
- /// `InlineFragmentsConstructionResult`.
- fn build_fragments_for_inline(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult {
- // Is this node replaced content?
- if !node.is_replaced_content() {
- // Go to a path that concatenates our kids' fragments.
- self.build_fragments_for_nonreplaced_inline_content(node)
- } else {
- // Otherwise, just nuke our kids' fragments, create our fragment if any, and be done
- // with it.
- self.build_fragments_for_replaced_inline_content(node)
- }
- }
-
- /// TableCaptionFlow is populated underneath TableWrapperFlow
- fn place_table_caption_under_table_wrapper(&mut self,
- table_wrapper_flow: &mut FlowRef,
- node: &ThreadSafeLayoutNode) {
- for kid in node.children() {
- match kid.swap_out_construction_result() {
- NoConstructionResult | ConstructionItemConstructionResult(_) => {}
- FlowConstructionResult(kid_flow, _) => {
- // Only kid flows with table-caption are matched here.
- assert!(kid_flow.get().is_table_caption());
- table_wrapper_flow.add_new_child(kid_flow);
- }
- }
- }
- }
-
- /// Generates an anonymous table flow according to CSS 2.1 § 17.2.1, step 2.
- /// If necessary, generate recursively another anonymous table flow.
- fn generate_anonymous_missing_child(&mut self,
- child_flows: Vec<FlowRef>,
- flow: &mut FlowRef,
- node: &ThreadSafeLayoutNode) {
- let mut anonymous_flow = flow.get().generate_missing_child_flow(node);
- let mut consecutive_siblings = vec!();
- for kid_flow in child_flows.move_iter() {
- if anonymous_flow.get().need_anonymous_flow(kid_flow.get()) {
- consecutive_siblings.push(kid_flow);
- continue;
- }
- if !consecutive_siblings.is_empty() {
- self.generate_anonymous_missing_child(consecutive_siblings,
- &mut anonymous_flow,
- node);
- consecutive_siblings = vec!();
- }
- anonymous_flow.add_new_child(kid_flow);
- }
- if !consecutive_siblings.is_empty() {
- self.generate_anonymous_missing_child(consecutive_siblings, &mut anonymous_flow, node);
- }
- // The flow is done.
- anonymous_flow.finish(self.layout_context);
- flow.add_new_child(anonymous_flow);
- }
-
- /// Builds a flow for a node with `display: table`. This yields a `TableWrapperFlow` with possibly
- /// other `TableCaptionFlow`s or `TableFlow`s underneath it.
- fn build_flow_for_table_wrapper(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult {
- let fragment = Fragment::new_from_specific_info(node, TableWrapperFragment);
- let wrapper_flow = box TableWrapperFlow::from_node_and_fragment(node, fragment);
- let mut wrapper_flow = FlowRef::new(wrapper_flow as Box<Flow>);
-
- let table_fragment = Fragment::new_from_specific_info(node, TableFragment);
- let table_flow = box TableFlow::from_node_and_fragment(node, table_fragment);
- let table_flow = FlowRef::new(table_flow as Box<Flow>);
-
- // We first populate the TableFlow with other flows than TableCaptionFlow.
- // We then populate the TableWrapperFlow with TableCaptionFlow, and attach
- // the TableFlow to the TableWrapperFlow
- let construction_result = self.build_flow_using_children(table_flow, node);
- self.place_table_caption_under_table_wrapper(&mut wrapper_flow, node);
-
- let mut abs_descendants = Descendants::new();
- let mut fixed_descendants = Descendants::new();
-
- // NOTE: The order of captions and table are not the same order as in the DOM tree.
- // All caption blocks are placed before the table flow
- match construction_result {
- FlowConstructionResult(table_flow, table_abs_descendants) => {
- wrapper_flow.add_new_child(table_flow);
- abs_descendants.push_descendants(table_abs_descendants);
- }
- _ => {}
- }
-
- // The flow is done.
- wrapper_flow.finish(self.layout_context);
- let is_positioned = wrapper_flow.get_mut().as_block().is_positioned();
- let is_fixed_positioned = wrapper_flow.get_mut().as_block().is_fixed();
- let is_absolutely_positioned = wrapper_flow.get_mut()
- .as_block()
- .is_absolutely_positioned();
- if is_positioned {
- // This is the CB for all the absolute descendants.
- wrapper_flow.set_abs_descendants(abs_descendants);
-
- abs_descendants = Descendants::new();
-
- if is_fixed_positioned {
- // Send itself along with the other fixed descendants.
- fixed_descendants.push(wrapper_flow.clone());
- } else if is_absolutely_positioned {
- // This is now the only absolute flow in the subtree which hasn't yet
- // reached its CB.
- abs_descendants.push(wrapper_flow.clone());
- }
- }
- FlowConstructionResult(wrapper_flow, abs_descendants)
- }
-
- /// Builds a flow for a node with `display: table-caption`. This yields a `TableCaptionFlow`
- /// with possibly other `BlockFlow`s or `InlineFlow`s underneath it.
- fn build_flow_for_table_caption(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult {
- let flow = box TableCaptionFlow::from_node(self, node) as Box<Flow>;
- self.build_flow_using_children(FlowRef::new(flow), node)
- }
-
- /// Builds a flow for a node with `display: table-row-group`. This yields a `TableRowGroupFlow`
- /// with possibly other `TableRowFlow`s underneath it.
- fn build_flow_for_table_rowgroup(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult {
- let fragment = Fragment::new_from_specific_info(node, TableRowFragment);
- let flow = box TableRowGroupFlow::from_node_and_fragment(node, fragment);
- let flow = flow as Box<Flow>;
- self.build_flow_using_children(FlowRef::new(flow), node)
- }
-
- /// Builds a flow for a node with `display: table-row`. This yields a `TableRowFlow` with
- /// possibly other `TableCellFlow`s underneath it.
- fn build_flow_for_table_row(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult {
- let fragment = Fragment::new_from_specific_info(node, TableRowFragment);
- let flow = box TableRowFlow::from_node_and_fragment(node, fragment) as Box<Flow>;
- self.build_flow_using_children(FlowRef::new(flow), node)
- }
-
- /// Builds a flow for a node with `display: table-cell`. This yields a `TableCellFlow` with
- /// possibly other `BlockFlow`s or `InlineFlow`s underneath it.
- fn build_flow_for_table_cell(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult {
- let fragment = Fragment::new_from_specific_info(node, TableCellFragment);
- let flow = box TableCellFlow::from_node_and_fragment(node, fragment) as Box<Flow>;
- self.build_flow_using_children(FlowRef::new(flow), node)
- }
-
- /// Creates a fragment for a node with `display: table-column`.
- fn build_fragments_for_table_column(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult {
- // CSS 2.1 § 17.2.1. Treat all child fragments of a `table-column` as `display: none`.
- for kid in node.children() {
- kid.set_flow_construction_result(NoConstructionResult)
- }
-
- let specific = TableColumnFragment(TableColumnFragmentInfo::new(node));
- let construction_item = TableColumnFragmentConstructionItem(
- Fragment::new_from_specific_info(node, specific)
- );
- ConstructionItemConstructionResult(construction_item)
- }
-
- /// Builds a flow for a node with `display: table-column-group`.
- /// This yields a `TableColGroupFlow`.
- fn build_flow_for_table_colgroup(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult {
- let fragment = Fragment::new_from_specific_info(node,
- TableColumnFragment(TableColumnFragmentInfo::new(node)));
- let mut col_fragments = vec!();
- for kid in node.children() {
- // CSS 2.1 § 17.2.1. Treat all non-column child fragments of `table-column-group`
- // as `display: none`.
- match kid.swap_out_construction_result() {
- ConstructionItemConstructionResult(TableColumnFragmentConstructionItem(fragment)) => {
- col_fragments.push(fragment);
- }
- _ => {}
- }
- }
- if col_fragments.is_empty() {
- debug!("add TableColumnFragment for empty colgroup");
- let specific = TableColumnFragment(TableColumnFragmentInfo::new(node));
- col_fragments.push(Fragment::new_from_specific_info(node, specific));
- }
- let flow = box TableColGroupFlow::from_node_and_fragments(node, fragment, col_fragments);
- let mut flow = FlowRef::new(flow as Box<Flow>);
- flow.finish(self.layout_context);
-
- FlowConstructionResult(flow, Descendants::new())
- }
-}
-
-impl<'a, 'b> PostorderNodeMutTraversal for FlowConstructor<'a, 'b> {
- // Construct Flow based on 'display', 'position', and 'float' values.
- //
- // CSS 2.1 Section 9.7
- //
- // TODO: This should actually consult the table in that section to get the
- // final computed value for 'display'.
- //
- // `#[inline(always)]` because this is always called from the traversal function and for some
- // reason LLVM's inlining heuristics go awry here.
- #[inline(always)]
- fn process(&mut self, node: &ThreadSafeLayoutNode) -> bool {
- // Get the `display` property for this node, and determine whether this node is floated.
- let (display, float, positioning) = match node.type_id() {
- None => {
- // Pseudo-element.
- let style = node.style();
- (display::inline, style.get_box().float, style.get_box().position)
- }
- Some(ElementNodeTypeId(_)) => {
- let style = node.style();
- (style.get_box().display, style.get_box().float, style.get_box().position)
- }
- Some(TextNodeTypeId) => (display::inline, float::none, position::static_),
- Some(CommentNodeTypeId) |
- Some(DoctypeNodeTypeId) |
- Some(DocumentFragmentNodeTypeId) |
- Some(DocumentNodeTypeId) |
- Some(ProcessingInstructionNodeTypeId) => {
- (display::none, float::none, position::static_)
- }
- };
-
- debug!("building flow for node: {:?} {:?}", display, float);
-
- // Switch on display and floatedness.
- match (display, float, positioning) {
- // `display: none` contributes no flow construction result. Nuke the flow construction
- // results of children.
- (display::none, _, _) => {
- for child in node.children() {
- drop(child.swap_out_construction_result())
- }
- }
-
- // Table items contribute table flow construction results.
- (display::table, _, _) => {
- let construction_result = self.build_flow_for_table_wrapper(node);
- node.set_flow_construction_result(construction_result)
- }
-
- // Absolutely positioned elements will have computed value of
- // `float` as 'none' and `display` as per the table.
- // Only match here for block items. If an item is absolutely
- // positioned, but inline we shouldn't try to construct a block
- // flow here - instead, let it match the inline case
- // below.
- (display::block, _, position::absolute) | (_, _, position::fixed) => {
- node.set_flow_construction_result(self.build_flow_for_block(node))
- }
-
- // Inline items contribute inline fragment construction results.
- (display::inline, float::none, _) => {
- let construction_result = self.build_fragments_for_inline(node);
- node.set_flow_construction_result(construction_result)
- }
-
- // Table items contribute table flow construction results.
- (display::table_caption, _, _) => {
- let construction_result = self.build_flow_for_table_caption(node);
- node.set_flow_construction_result(construction_result)
- }
-
- // Table items contribute table flow construction results.
- (display::table_column_group, _, _) => {
- let construction_result = self.build_flow_for_table_colgroup(node);
- node.set_flow_construction_result(construction_result)
- }
-
- // Table items contribute table flow construction results.
- (display::table_column, _, _) => {
- let construction_result = self.build_fragments_for_table_column(node);
- node.set_flow_construction_result(construction_result)
- }
-
- // Table items contribute table flow construction results.
- (display::table_row_group, _, _) | (display::table_header_group, _, _) |
- (display::table_footer_group, _, _) => {
- let construction_result = self.build_flow_for_table_rowgroup(node);
- node.set_flow_construction_result(construction_result)
- }
-
- // Table items contribute table flow construction results.
- (display::table_row, _, _) => {
- let construction_result = self.build_flow_for_table_row(node);
- node.set_flow_construction_result(construction_result)
- }
-
- // Table items contribute table flow construction results.
- (display::table_cell, _, _) => {
- let construction_result = self.build_flow_for_table_cell(node);
- node.set_flow_construction_result(construction_result)
- }
-
- // Block flows that are not floated contribute block flow construction results.
- //
- // TODO(pcwalton): Make this only trigger for blocks and handle the other `display`
- // properties separately.
-
- (_, float::none, _) => {
- node.set_flow_construction_result(self.build_flow_for_block(node))
- }
-
- // Floated flows contribute float flow construction results.
- (_, float_value, _) => {
- let float_kind = FloatKind::from_property(float_value);
- node.set_flow_construction_result(
- self.build_flow_for_floated_block(node, float_kind))
- }
- }
-
- true
- }
-}
-
-/// A utility trait with some useful methods for node queries.
-trait NodeUtils {
- /// Returns true if this node doesn't render its kids and false otherwise.
- fn is_replaced_content(&self) -> bool;
-
- /// Sets the construction result of a flow.
- fn set_flow_construction_result(&self, result: ConstructionResult);
-
- /// Replaces the flow construction result in a node with `NoConstructionResult` and returns the
- /// old value.
- fn swap_out_construction_result(&self) -> ConstructionResult;
-}
-
-impl<'ln> NodeUtils for ThreadSafeLayoutNode<'ln> {
- fn is_replaced_content(&self) -> bool {
- match self.type_id() {
- Some(TextNodeTypeId) |
- Some(ProcessingInstructionNodeTypeId) |
- Some(CommentNodeTypeId) |
- Some(DoctypeNodeTypeId) |
- Some(DocumentFragmentNodeTypeId) |
- Some(DocumentNodeTypeId) |
- None |
- Some(ElementNodeTypeId(HTMLImageElementTypeId)) => true,
- Some(ElementNodeTypeId(HTMLObjectElementTypeId)) => self.has_object_data(),
- Some(ElementNodeTypeId(_)) => false,
- }
- }
-
- #[inline(always)]
- fn set_flow_construction_result(&self, result: ConstructionResult) {
- let mut layout_data_ref = self.mutate_layout_data();
- match &mut *layout_data_ref {
- &Some(ref mut layout_data) =>{
- match self.get_pseudo_element_type() {
- Before | BeforeBlock => {
- layout_data.data.before_flow_construction_result = result
- },
- After | AfterBlock => {
- layout_data.data.after_flow_construction_result = result
- },
- Normal => layout_data.data.flow_construction_result = result,
- }
- },
- &None => fail!("no layout data"),
- }
- }
-
- #[inline(always)]
- fn swap_out_construction_result(&self) -> ConstructionResult {
- let mut layout_data_ref = self.mutate_layout_data();
- match &mut *layout_data_ref {
- &Some(ref mut layout_data) => {
- match self.get_pseudo_element_type() {
- Before | BeforeBlock => {
- mem::replace(&mut layout_data.data.before_flow_construction_result,
- NoConstructionResult)
- }
- After | AfterBlock => {
- mem::replace(&mut layout_data.data.after_flow_construction_result,
- NoConstructionResult)
- }
- Normal => {
- mem::replace(&mut layout_data.data.flow_construction_result,
- NoConstructionResult)
- }
- }
- }
- &None => fail!("no layout data"),
- }
- }
-}
-
-/// Methods for interacting with HTMLObjectElement nodes
-trait ObjectElement {
- /// Returns None if this node is not matching attributes.
- fn get_type_and_data(&self) -> (Option<&'static str>, Option<&'static str>);
-
- /// Returns true if this node has object data that is correct uri.
- fn has_object_data(&self) -> bool;
-
- /// Returns the "data" attribute value parsed as a URL
- fn get_object_data(&self) -> Option<Url>;
-}
-
-impl<'ln> ObjectElement for ThreadSafeLayoutNode<'ln> {
- fn get_type_and_data(&self) -> (Option<&'static str>, Option<&'static str>) {
- let elem = self.as_element();
- (elem.get_attr(&namespace::Null, "type"), elem.get_attr(&namespace::Null, "data"))
- }
-
- fn has_object_data(&self) -> bool {
- match self.get_type_and_data() {
- (None, Some(uri)) => is_image_data(uri),
- _ => false
- }
- }
-
- fn get_object_data(&self) -> Option<Url> {
- match self.get_type_and_data() {
- (None, Some(uri)) if is_image_data(uri) => Url::parse(uri).ok(),
- _ => None
- }
- }
-}
-
-pub trait FlowConstructionUtils {
- /// Adds a new flow as a child of this flow. Removes the flow from the given leaf set if
- /// it's present.
- fn add_new_child(&mut self, new_child: FlowRef);
-
- /// Finishes a flow. Once a flow is finished, no more child flows or boxes may be added to it.
- /// This will normally run the bubble-inline-sizes (minimum and preferred -- i.e. intrinsic -- inline-size)
- /// calculation, unless the global `bubble_inline-sizes_separately` flag is on.
- ///
- /// All flows must be finished at some point, or they will not have their intrinsic inline-sizes
- /// properly computed. (This is not, however, a memory safety problem.)
- fn finish(&mut self, context: &LayoutContext);
-}
-
-impl FlowConstructionUtils for FlowRef {
- /// Adds a new flow as a child of this flow. Fails if this flow is marked as a leaf.
- ///
- /// This must not be public because only the layout constructor can do this.
- fn add_new_child(&mut self, mut new_child: FlowRef) {
- {
- let kid_base = flow::mut_base(new_child.get_mut());
- kid_base.parallel.parent = parallel::mut_owned_flow_to_unsafe_flow(self);
- }
-
- let base = flow::mut_base(self.get_mut());
- base.children.push_back(new_child);
- let _ = base.parallel.children_count.fetch_add(1, Relaxed);
- let _ = base.parallel.children_and_absolute_descendant_count.fetch_add(1, Relaxed);
- }
-
- /// Finishes a flow. Once a flow is finished, no more child flows or fragments may be added to
- /// it. This will normally run the bubble-inline-sizes (minimum and preferred -- i.e. intrinsic --
- /// inline-size) calculation, unless the global `bubble_inline-sizes_separately` flag is on.
- ///
- /// All flows must be finished at some point, or they will not have their intrinsic inline-sizes
- /// properly computed. (This is not, however, a memory safety problem.)
- ///
- /// This must not be public because only the layout constructor can do this.
- fn finish(&mut self, context: &LayoutContext) {
- if !context.shared.opts.bubble_inline_sizes_separately {
- self.get_mut().bubble_inline_sizes(context)
- }
- }
-}
-
diff --git a/src/components/layout/context.rs b/src/components/layout/context.rs
deleted file mode 100644
index 936314dcb63..00000000000
--- a/src/components/layout/context.rs
+++ /dev/null
@@ -1,123 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//! Data needed by the layout task.
-
-use css::matching::{ApplicableDeclarationsCache, StyleSharingCandidateCache};
-
-use geom::{Rect, Size2D};
-use gfx::display_list::OpaqueNode;
-use gfx::font_context::FontContext;
-use gfx::font_cache_task::FontCacheTask;
-use script::layout_interface::LayoutChan;
-use servo_msg::constellation_msg::ConstellationChan;
-use servo_net::local_image_cache::LocalImageCache;
-use servo_util::geometry::Au;
-use servo_util::opts::Opts;
-use sync::{Arc, Mutex};
-use std::mem;
-use style::Stylist;
-use url::Url;
-
-struct LocalLayoutContext {
- font_context: FontContext,
- applicable_declarations_cache: ApplicableDeclarationsCache,
- style_sharing_candidate_cache: StyleSharingCandidateCache,
-}
-
-local_data_key!(local_context_key: *mut LocalLayoutContext)
-
-fn create_or_get_local_context(shared_layout_context: &SharedLayoutContext) -> *mut LocalLayoutContext {
- let maybe_context = local_context_key.get();
-
- let context = match maybe_context {
- None => {
- let context = box LocalLayoutContext {
- font_context: FontContext::new(shared_layout_context.font_cache_task.clone()),
- applicable_declarations_cache: ApplicableDeclarationsCache::new(),
- style_sharing_candidate_cache: StyleSharingCandidateCache::new(),
- };
- local_context_key.replace(Some(unsafe { mem::transmute(context) }));
- local_context_key.get().unwrap()
- },
- Some(context) => context
- };
-
- *context
-}
-
-pub struct SharedLayoutContext {
- /// The local image cache.
- pub image_cache: Arc<Mutex<LocalImageCache>>,
-
- /// The current screen size.
- pub screen_size: Size2D<Au>,
-
- /// A channel up to the constellation.
- pub constellation_chan: ConstellationChan,
-
- /// A channel up to the layout task.
- pub layout_chan: LayoutChan,
-
- /// Interface to the font cache task.
- pub font_cache_task: FontCacheTask,
-
- /// The CSS selector stylist.
- ///
- /// FIXME(#2604): Make this no longer an unsafe pointer once we have fast `RWArc`s.
- pub stylist: *const Stylist,
-
- /// The root node at which we're starting the layout.
- pub reflow_root: OpaqueNode,
-
- /// The URL.
- pub url: Url,
-
- /// The command line options.
- pub opts: Opts,
-
- /// The dirty rectangle, used during display list building.
- pub dirty: Rect<Au>,
-}
-
-pub struct LayoutContext<'a> {
- pub shared: &'a SharedLayoutContext,
- cached_local_layout_context: *mut LocalLayoutContext,
-}
-
-impl<'a> LayoutContext<'a> {
- pub fn new(shared_layout_context: &'a SharedLayoutContext) -> LayoutContext<'a> {
-
- let local_context = create_or_get_local_context(shared_layout_context);
-
- LayoutContext {
- shared: shared_layout_context,
- cached_local_layout_context: local_context,
- }
- }
-
- #[inline(always)]
- pub fn font_context<'a>(&'a self) -> &'a mut FontContext {
- unsafe {
- let cached_context = &*self.cached_local_layout_context;
- mem::transmute(&cached_context.font_context)
- }
- }
-
- #[inline(always)]
- pub fn applicable_declarations_cache<'a>(&'a self) -> &'a mut ApplicableDeclarationsCache {
- unsafe {
- let cached_context = &*self.cached_local_layout_context;
- mem::transmute(&cached_context.applicable_declarations_cache)
- }
- }
-
- #[inline(always)]
- pub fn style_sharing_candidate_cache<'a>(&'a self) -> &'a mut StyleSharingCandidateCache {
- unsafe {
- let cached_context = &*self.cached_local_layout_context;
- mem::transmute(&cached_context.style_sharing_candidate_cache)
- }
- }
-}
diff --git a/src/components/layout/css/matching.rs b/src/components/layout/css/matching.rs
deleted file mode 100644
index c1d766c32ca..00000000000
--- a/src/components/layout/css/matching.rs
+++ /dev/null
@@ -1,558 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-// High-level interface to CSS selector matching.
-
-use css::node_style::StyledNode;
-use construct::FlowConstructor;
-use context::LayoutContext;
-use extra::LayoutAuxMethods;
-use util::{LayoutDataAccess, LayoutDataWrapper};
-use wrapper::{LayoutElement, LayoutNode, PostorderNodeMutTraversal, ThreadSafeLayoutNode};
-
-use servo_util::atom::Atom;
-use servo_util::cache::{Cache, LRUCache, SimpleHashCache};
-use servo_util::namespace::Null;
-use servo_util::smallvec::{SmallVec, SmallVec16};
-use servo_util::str::DOMString;
-use std::mem;
-use std::hash::{Hash, sip};
-use std::slice::Items;
-use style::{After, Before, ComputedValues, DeclarationBlock, Stylist, TElement, TNode, cascade};
-use sync::Arc;
-
-pub struct ApplicableDeclarations {
- pub normal: SmallVec16<DeclarationBlock>,
- pub before: Vec<DeclarationBlock>,
- pub after: Vec<DeclarationBlock>,
-
- /// Whether the `normal` declarations are shareable with other nodes.
- pub normal_shareable: bool,
-}
-
-impl ApplicableDeclarations {
- pub fn new() -> ApplicableDeclarations {
- ApplicableDeclarations {
- normal: SmallVec16::new(),
- before: Vec::new(),
- after: Vec::new(),
- normal_shareable: false,
- }
- }
-
- pub fn clear(&mut self) {
- self.normal = SmallVec16::new();
- self.before = Vec::new();
- self.after = Vec::new();
- self.normal_shareable = false;
- }
-}
-
-#[deriving(Clone)]
-pub struct ApplicableDeclarationsCacheEntry {
- pub declarations: Vec<DeclarationBlock>,
-}
-
-impl ApplicableDeclarationsCacheEntry {
- fn new(slice: &[DeclarationBlock]) -> ApplicableDeclarationsCacheEntry {
- let mut entry_declarations = Vec::new();
- for declarations in slice.iter() {
- entry_declarations.push(declarations.clone());
- }
- ApplicableDeclarationsCacheEntry {
- declarations: entry_declarations,
- }
- }
-}
-
-impl PartialEq for ApplicableDeclarationsCacheEntry {
- fn eq(&self, other: &ApplicableDeclarationsCacheEntry) -> bool {
- let this_as_query = ApplicableDeclarationsCacheQuery::new(self.declarations.as_slice());
- this_as_query.equiv(other)
- }
-}
-
-impl Hash for ApplicableDeclarationsCacheEntry {
- fn hash(&self, state: &mut sip::SipState) {
- let tmp = ApplicableDeclarationsCacheQuery::new(self.declarations.as_slice());
- tmp.hash(state);
- }
-}
-
-struct ApplicableDeclarationsCacheQuery<'a> {
- declarations: &'a [DeclarationBlock],
-}
-
-impl<'a> ApplicableDeclarationsCacheQuery<'a> {
- fn new(declarations: &'a [DeclarationBlock]) -> ApplicableDeclarationsCacheQuery<'a> {
- ApplicableDeclarationsCacheQuery {
- declarations: declarations,
- }
- }
-}
-
-// Workaround for lack of `ptr_eq` on Arcs...
-#[inline]
-fn arc_ptr_eq<T>(a: &Arc<T>, b: &Arc<T>) -> bool {
- unsafe {
- let a: uint = mem::transmute_copy(a);
- let b: uint = mem::transmute_copy(b);
- a == b
- }
-}
-
-impl<'a> Equiv<ApplicableDeclarationsCacheEntry> for ApplicableDeclarationsCacheQuery<'a> {
- fn equiv(&self, other: &ApplicableDeclarationsCacheEntry) -> bool {
- if self.declarations.len() != other.declarations.len() {
- return false
- }
- for (this, other) in self.declarations.iter().zip(other.declarations.iter()) {
- if !arc_ptr_eq(&this.declarations, &other.declarations) {
- return false
- }
- }
- return true
- }
-}
-
-
-impl<'a> Hash for ApplicableDeclarationsCacheQuery<'a> {
- fn hash(&self, state: &mut sip::SipState) {
- for declaration in self.declarations.iter() {
- let ptr: uint = unsafe {
- mem::transmute_copy(declaration)
- };
- ptr.hash(state);
- }
- }
-}
-
-static APPLICABLE_DECLARATIONS_CACHE_SIZE: uint = 32;
-
-pub struct ApplicableDeclarationsCache {
- cache: SimpleHashCache<ApplicableDeclarationsCacheEntry,Arc<ComputedValues>>,
-}
-
-impl ApplicableDeclarationsCache {
- pub fn new() -> ApplicableDeclarationsCache {
- ApplicableDeclarationsCache {
- cache: SimpleHashCache::new(APPLICABLE_DECLARATIONS_CACHE_SIZE),
- }
- }
-
- fn find(&self, declarations: &[DeclarationBlock]) -> Option<Arc<ComputedValues>> {
- match self.cache.find_equiv(&ApplicableDeclarationsCacheQuery::new(declarations)) {
- None => None,
- Some(ref values) => Some((*values).clone()),
- }
- }
-
- fn insert(&mut self, declarations: &[DeclarationBlock], style: Arc<ComputedValues>) {
- self.cache.insert(ApplicableDeclarationsCacheEntry::new(declarations), style)
- }
-}
-
-/// An LRU cache of the last few nodes seen, so that we can aggressively try to reuse their styles.
-pub struct StyleSharingCandidateCache {
- cache: LRUCache<StyleSharingCandidate,()>,
-}
-
-#[deriving(Clone)]
-pub struct StyleSharingCandidate {
- pub style: Arc<ComputedValues>,
- pub parent_style: Arc<ComputedValues>,
- pub local_name: Atom,
- pub class: Option<DOMString>,
-}
-
-impl PartialEq for StyleSharingCandidate {
- fn eq(&self, other: &StyleSharingCandidate) -> bool {
- arc_ptr_eq(&self.style, &other.style) &&
- arc_ptr_eq(&self.parent_style, &other.parent_style) &&
- self.local_name == other.local_name &&
- self.class == other.class
- }
-}
-
-impl StyleSharingCandidate {
- /// Attempts to create a style sharing candidate from this node. Returns
- /// the style sharing candidate or `None` if this node is ineligible for
- /// style sharing.
- fn new(node: &LayoutNode) -> Option<StyleSharingCandidate> {
- let parent_node = match node.parent_node() {
- None => return None,
- Some(parent_node) => parent_node,
- };
- if !parent_node.is_element() {
- return None
- }
-
- let style = unsafe {
- match *node.borrow_layout_data_unchecked() {
- None => return None,
- Some(ref layout_data_ref) => {
- match layout_data_ref.shared_data.style {
- None => return None,
- Some(ref data) => (*data).clone(),
- }
- }
- }
- };
- let parent_style = unsafe {
- match *parent_node.borrow_layout_data_unchecked() {
- None => return None,
- Some(ref parent_layout_data_ref) => {
- match parent_layout_data_ref.shared_data.style {
- None => return None,
- Some(ref data) => (*data).clone(),
- }
- }
- }
- };
-
- let mut style = Some(style);
- let mut parent_style = Some(parent_style);
- let element = node.as_element();
- if element.style_attribute().is_some() {
- return None
- }
-
- Some(StyleSharingCandidate {
- style: style.take_unwrap(),
- parent_style: parent_style.take_unwrap(),
- local_name: element.get_local_name().clone(),
- class: element.get_attr(&Null, "class")
- .map(|string| string.to_string()),
- })
- }
-
- fn can_share_style_with(&self, element: &LayoutElement) -> bool {
- if *element.get_local_name() != self.local_name {
- return false
- }
- match (&self.class, element.get_attr(&Null, "class")) {
- (&None, Some(_)) | (&Some(_), None) => return false,
- (&Some(ref this_class), Some(element_class)) if element_class != this_class.as_slice() => {
- return false
- }
- (&Some(_), Some(_)) | (&None, None) => {}
- }
- true
- }
-}
-
-static STYLE_SHARING_CANDIDATE_CACHE_SIZE: uint = 40;
-
-impl StyleSharingCandidateCache {
- pub fn new() -> StyleSharingCandidateCache {
- StyleSharingCandidateCache {
- cache: LRUCache::new(STYLE_SHARING_CANDIDATE_CACHE_SIZE),
- }
- }
-
- pub fn iter<'a>(&'a self) -> Items<'a,(StyleSharingCandidate,())> {
- self.cache.iter()
- }
-
- pub fn insert_if_possible(&mut self, node: &LayoutNode) {
- match StyleSharingCandidate::new(node) {
- None => {}
- Some(candidate) => self.cache.insert(candidate, ())
- }
- }
-
- pub fn touch(&mut self, index: uint) {
- self.cache.touch(index)
- }
-}
-
-/// The results of attempting to share a style.
-pub enum StyleSharingResult<'ln> {
- /// We didn't find anybody to share the style with. The boolean indicates whether the style
- /// is shareable at all.
- CannotShare(bool),
- /// The node's style can be shared. The integer specifies the index in the LRU cache that was
- /// hit.
- StyleWasShared(uint),
-}
-
-pub trait MatchMethods {
- /// Performs aux initialization, selector matching, cascading, and flow construction
- /// sequentially.
- fn recalc_style_for_subtree(&self,
- stylist: &Stylist,
- layout_context: &LayoutContext,
- applicable_declarations: &mut ApplicableDeclarations,
- parent: Option<LayoutNode>);
-
- fn match_node(&self,
- stylist: &Stylist,
- applicable_declarations: &mut ApplicableDeclarations,
- shareable: &mut bool);
-
- /// Attempts to share a style with another node. This method is unsafe because it depends on
- /// the `style_sharing_candidate_cache` having only live nodes in it, and we have no way to
- /// guarantee that at the type system level yet.
- unsafe fn share_style_if_possible(&self,
- style_sharing_candidate_cache:
- &mut StyleSharingCandidateCache,
- parent: Option<LayoutNode>)
- -> StyleSharingResult;
-
- unsafe fn cascade_node(&self,
- parent: Option<LayoutNode>,
- applicable_declarations: &ApplicableDeclarations,
- applicable_declarations_cache: &mut ApplicableDeclarationsCache);
-}
-
-trait PrivateMatchMethods {
- fn cascade_node_pseudo_element(&self,
- parent_style: Option<&Arc<ComputedValues>>,
- applicable_declarations: &[DeclarationBlock],
- style: &mut Option<Arc<ComputedValues>>,
- applicable_declarations_cache: &mut
- ApplicableDeclarationsCache,
- shareable: bool);
-
- fn share_style_with_candidate_if_possible(&self,
- parent_node: Option<LayoutNode>,
- candidate: &StyleSharingCandidate)
- -> Option<Arc<ComputedValues>>;
-}
-
-impl<'ln> PrivateMatchMethods for LayoutNode<'ln> {
- fn cascade_node_pseudo_element(&self,
- parent_style: Option<&Arc<ComputedValues>>,
- applicable_declarations: &[DeclarationBlock],
- style: &mut Option<Arc<ComputedValues>>,
- applicable_declarations_cache: &mut
- ApplicableDeclarationsCache,
- shareable: bool) {
- let this_style;
- let cacheable;
- match parent_style {
- Some(ref parent_style) => {
- let cache_entry = applicable_declarations_cache.find(applicable_declarations);
- let cached_computed_values = match cache_entry {
- None => None,
- Some(ref style) => Some(&**style),
- };
- let (the_style, is_cacheable) = cascade(applicable_declarations,
- shareable,
- Some(&***parent_style),
- cached_computed_values);
- cacheable = is_cacheable;
- this_style = Arc::new(the_style);
- }
- None => {
- let (the_style, is_cacheable) = cascade(applicable_declarations,
- shareable,
- None,
- None);
- cacheable = is_cacheable;
- this_style = Arc::new(the_style);
- }
- };
-
- // Cache the resolved style if it was cacheable.
- if cacheable {
- applicable_declarations_cache.insert(applicable_declarations, this_style.clone());
- }
-
- *style = Some(this_style);
- }
-
-
- fn share_style_with_candidate_if_possible(&self,
- parent_node: Option<LayoutNode>,
- candidate: &StyleSharingCandidate)
- -> Option<Arc<ComputedValues>> {
- assert!(self.is_element());
-
- let parent_node = match parent_node {
- Some(ref parent_node) if parent_node.is_element() => parent_node,
- Some(_) | None => return None,
- };
-
- let parent_layout_data: &Option<LayoutDataWrapper> = unsafe {
- mem::transmute(parent_node.borrow_layout_data_unchecked())
- };
- match parent_layout_data {
- &Some(ref parent_layout_data_ref) => {
- // Check parent style.
- let parent_style = parent_layout_data_ref.shared_data.style.as_ref().unwrap();
- if !arc_ptr_eq(parent_style, &candidate.parent_style) {
- return None
- }
-
- // Check tag names, classes, etc.
- if !candidate.can_share_style_with(&self.as_element()) {
- return None
- }
-
- return Some(candidate.style.clone())
- }
- _ => {}
- }
-
- None
- }
-}
-
-impl<'ln> MatchMethods for LayoutNode<'ln> {
- fn match_node(&self,
- stylist: &Stylist,
- applicable_declarations: &mut ApplicableDeclarations,
- shareable: &mut bool) {
- let style_attribute = self.as_element().style_attribute().as_ref();
-
- applicable_declarations.normal_shareable =
- stylist.push_applicable_declarations(self,
- style_attribute,
- None,
- &mut applicable_declarations.normal);
- stylist.push_applicable_declarations(self,
- None,
- Some(Before),
- &mut applicable_declarations.before);
- stylist.push_applicable_declarations(self,
- None,
- Some(After),
- &mut applicable_declarations.after);
-
- *shareable = applicable_declarations.normal_shareable
- }
-
- unsafe fn share_style_if_possible(&self,
- style_sharing_candidate_cache:
- &mut StyleSharingCandidateCache,
- parent: Option<LayoutNode>)
- -> StyleSharingResult {
- if !self.is_element() {
- return CannotShare(false)
- }
- let ok = {
- let element = self.as_element();
- element.style_attribute().is_none() && element.get_attr(&Null, "id").is_none()
- };
- if !ok {
- return CannotShare(false)
- }
-
- for (i, &(ref candidate, ())) in style_sharing_candidate_cache.iter().enumerate() {
- match self.share_style_with_candidate_if_possible(parent.clone(), candidate) {
- Some(shared_style) => {
- // Yay, cache hit. Share the style.
- let mut layout_data_ref = self.mutate_layout_data();
- layout_data_ref.get_mut_ref().shared_data.style = Some(shared_style);
- return StyleWasShared(i)
- }
- None => {}
- }
- }
-
- CannotShare(true)
- }
-
- fn recalc_style_for_subtree(&self,
- stylist: &Stylist,
- layout_context: &LayoutContext,
- applicable_declarations: &mut ApplicableDeclarations,
- parent: Option<LayoutNode>) {
- self.initialize_layout_data(layout_context.shared.layout_chan.clone());
-
- // First, check to see whether we can share a style with someone.
- let sharing_result = unsafe {
- self.share_style_if_possible(layout_context.style_sharing_candidate_cache(), parent.clone())
- };
-
- // Otherwise, match and cascade selectors.
- match sharing_result {
- CannotShare(mut shareable) => {
- if self.is_element() {
- self.match_node(stylist, applicable_declarations, &mut shareable)
- }
-
- unsafe {
- self.cascade_node(parent,
- applicable_declarations,
- layout_context.applicable_declarations_cache())
- }
-
- applicable_declarations.clear();
-
- // Add ourselves to the LRU cache.
- if shareable {
- layout_context.style_sharing_candidate_cache().insert_if_possible(self)
- }
- }
- StyleWasShared(index) => layout_context.style_sharing_candidate_cache().touch(index),
- }
-
- for kid in self.children() {
- kid.recalc_style_for_subtree(stylist,
- layout_context,
- applicable_declarations,
- Some(self.clone()))
- }
-
- // Construct flows.
- let layout_node = ThreadSafeLayoutNode::new(self);
- let mut flow_constructor = FlowConstructor::new(layout_context);
- flow_constructor.process(&layout_node);
- }
-
- unsafe fn cascade_node(&self,
- parent: Option<LayoutNode>,
- applicable_declarations: &ApplicableDeclarations,
- applicable_declarations_cache: &mut ApplicableDeclarationsCache) {
- // Get our parent's style. This must be unsafe so that we don't touch the parent's
- // borrow flags.
- //
- // FIXME(pcwalton): Isolate this unsafety into the `wrapper` module to allow
- // enforced safe, race-free access to the parent style.
- let parent_style = match parent {
- None => None,
- Some(parent_node) => {
- let parent_layout_data = parent_node.borrow_layout_data_unchecked();
- match *parent_layout_data {
- None => fail!("no parent data?!"),
- Some(ref parent_layout_data) => {
- match parent_layout_data.shared_data.style {
- None => fail!("parent hasn't been styled yet?!"),
- Some(ref style) => Some(style),
- }
- }
- }
- }
- };
-
- let mut layout_data_ref = self.mutate_layout_data();
- match &mut *layout_data_ref {
- &None => fail!("no layout data"),
- &Some(ref mut layout_data) => {
- self.cascade_node_pseudo_element(parent_style,
- applicable_declarations.normal.as_slice(),
- &mut layout_data.shared_data.style,
- applicable_declarations_cache,
- applicable_declarations.normal_shareable);
- if applicable_declarations.before.len() > 0 {
- self.cascade_node_pseudo_element(Some(layout_data.shared_data.style.get_ref()),
- applicable_declarations.before.as_slice(),
- &mut layout_data.data.before_style,
- applicable_declarations_cache,
- false);
- }
- if applicable_declarations.after.len() > 0 {
- self.cascade_node_pseudo_element(Some(layout_data.shared_data.style.get_ref()),
- applicable_declarations.after.as_slice(),
- &mut layout_data.data.after_style,
- applicable_declarations_cache,
- false);
- }
- }
- }
- }
-}
-
diff --git a/src/components/layout/css/node_style.rs b/src/components/layout/css/node_style.rs
deleted file mode 100644
index e201b0050aa..00000000000
--- a/src/components/layout/css/node_style.rs
+++ /dev/null
@@ -1,30 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-// Style retrieval from DOM elements.
-
-use css::node_util::NodeUtil;
-use incremental::RestyleDamage;
-use wrapper::ThreadSafeLayoutNode;
-
-use style::ComputedValues;
-use sync::Arc;
-
-/// Node mixin providing `style` method that returns a `NodeStyle`
-pub trait StyledNode {
- fn style<'a>(&'a self) -> &'a Arc<ComputedValues>;
- fn restyle_damage(&self) -> RestyleDamage;
-}
-
-impl<'ln> StyledNode for ThreadSafeLayoutNode<'ln> {
- #[inline]
- fn style<'a>(&'a self) -> &'a Arc<ComputedValues> {
- self.get_css_select_results()
- }
-
- fn restyle_damage(&self) -> RestyleDamage {
- self.get_restyle_damage()
- }
-}
-
diff --git a/src/components/layout/css/node_util.rs b/src/components/layout/css/node_util.rs
deleted file mode 100644
index 150995428ea..00000000000
--- a/src/components/layout/css/node_util.rs
+++ /dev/null
@@ -1,90 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-use incremental::RestyleDamage;
-use util::LayoutDataAccess;
-use wrapper::{TLayoutNode, ThreadSafeLayoutNode};
-use wrapper::{After, AfterBlock, Before, BeforeBlock, Normal};
-use std::mem;
-use style::ComputedValues;
-use sync::Arc;
-
-pub trait NodeUtil {
- fn get_css_select_results<'a>(&'a self) -> &'a Arc<ComputedValues>;
- fn have_css_select_results(&self) -> bool;
-
- fn get_restyle_damage(&self) -> RestyleDamage;
- fn set_restyle_damage(&self, damage: RestyleDamage);
-}
-
-impl<'ln> NodeUtil for ThreadSafeLayoutNode<'ln> {
- /// Returns the style results for the given node. If CSS selector
- /// matching has not yet been performed, fails.
- #[inline]
- fn get_css_select_results<'a>(&'a self) -> &'a Arc<ComputedValues> {
- unsafe {
- let layout_data_ref = self.borrow_layout_data();
- match self.get_pseudo_element_type() {
- Before | BeforeBlock => {
- mem::transmute(layout_data_ref.as_ref()
- .unwrap()
- .data
- .before_style
- .as_ref()
- .unwrap())
- }
- After | AfterBlock => {
- mem::transmute(layout_data_ref.as_ref()
- .unwrap()
- .data
- .after_style
- .as_ref()
- .unwrap())
- }
- Normal => {
- mem::transmute(layout_data_ref.as_ref()
- .unwrap()
- .shared_data
- .style
- .as_ref()
- .unwrap())
- }
- }
- }
- }
-
- /// Does this node have a computed style yet?
- fn have_css_select_results(&self) -> bool {
- let layout_data_ref = self.borrow_layout_data();
- layout_data_ref.get_ref().shared_data.style.is_some()
- }
-
- /// Get the description of how to account for recent style changes.
- /// This is a simple bitfield and fine to copy by value.
- fn get_restyle_damage(&self) -> RestyleDamage {
- // For DOM elements, if we haven't computed damage yet, assume the worst.
- // Other nodes don't have styles.
- let default = if self.node_is_element() {
- RestyleDamage::all()
- } else {
- RestyleDamage::empty()
- };
-
- let layout_data_ref = self.borrow_layout_data();
- layout_data_ref
- .get_ref()
- .data
- .restyle_damage
- .unwrap_or(default)
- }
-
- /// Set the restyle damage field.
- fn set_restyle_damage(&self, damage: RestyleDamage) {
- let mut layout_data_ref = self.mutate_layout_data();
- match &mut *layout_data_ref {
- &Some(ref mut layout_data) => layout_data.data.restyle_damage = Some(damage),
- _ => fail!("no layout data for this node"),
- }
- }
-}
diff --git a/src/components/layout/extra.rs b/src/components/layout/extra.rs
deleted file mode 100644
index 7b731185272..00000000000
--- a/src/components/layout/extra.rs
+++ /dev/null
@@ -1,44 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//! Code for managing the layout data in the DOM.
-
-use util::{PrivateLayoutData, LayoutDataAccess, LayoutDataWrapper};
-use wrapper::LayoutNode;
-use script::dom::node::SharedLayoutData;
-use script::layout_interface::LayoutChan;
-
-/// Functionality useful for querying the layout-specific data on DOM nodes.
-pub trait LayoutAuxMethods {
- fn initialize_layout_data(&self, chan: LayoutChan);
- fn initialize_style_for_subtree(&self, chan: LayoutChan);
-}
-
-impl<'ln> LayoutAuxMethods for LayoutNode<'ln> {
- /// Resets layout data and styles for the node.
- ///
- /// FIXME(pcwalton): Do this as part of fragment building instead of in a traversal.
- fn initialize_layout_data(&self, chan: LayoutChan) {
- let mut layout_data_ref = self.mutate_layout_data();
- match *layout_data_ref {
- None => {
- *layout_data_ref = Some(LayoutDataWrapper {
- chan: Some(chan),
- shared_data: SharedLayoutData { style: None },
- data: box PrivateLayoutData::new(),
- });
- }
- Some(_) => {}
- }
- }
-
- /// Resets layout data and styles for a Node tree.
- ///
- /// FIXME(pcwalton): Do this as part of fragment building instead of in a traversal.
- fn initialize_style_for_subtree(&self, chan: LayoutChan) {
- for n in self.traverse_preorder() {
- n.initialize_layout_data(chan.clone());
- }
- }
-}
diff --git a/src/components/layout/floats.rs b/src/components/layout/floats.rs
deleted file mode 100644
index 94017e6b3f7..00000000000
--- a/src/components/layout/floats.rs
+++ /dev/null
@@ -1,439 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-use servo_util::geometry::{Au, max, min};
-use servo_util::logical_geometry::WritingMode;
-use servo_util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize};
-use std::i32;
-use std::fmt;
-use style::computed_values::float;
-use sync::Arc;
-
-/// The kind of float: left or right.
-#[deriving(Clone, Encodable)]
-pub enum FloatKind {
- FloatLeft,
- FloatRight
-}
-
-impl FloatKind {
- pub fn from_property(property: float::T) -> FloatKind {
- match property {
- float::none => fail!("can't create a float type from an unfloated property"),
- float::left => FloatLeft,
- float::right => FloatRight,
- }
- }
-}
-
-/// The kind of clearance: left, right, or both.
-pub enum ClearType {
- ClearLeft,
- ClearRight,
- ClearBoth,
-}
-
-/// Information about a single float.
-#[deriving(Clone)]
-struct Float {
- /// The boundaries of this float.
- bounds: LogicalRect<Au>,
- /// The kind of float: left or right.
- kind: FloatKind,
-}
-
-impl fmt::Show for Float {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "bounds={} kind={:?}", self.bounds, self.kind)
- }
-}
-
-/// Information about the floats next to a flow.
-///
-/// FIXME(pcwalton): When we have fast `MutexArc`s, try removing `#[deriving(Clone)]` and wrap in a
-/// mutex.
-#[deriving(Clone)]
-struct FloatList {
- /// Information about each of the floats here.
- floats: Vec<Float>,
- /// Cached copy of the maximum block-start offset of the float.
- max_block_start: Au,
-}
-
-impl FloatList {
- fn new() -> FloatList {
- FloatList {
- floats: vec!(),
- max_block_start: Au(0),
- }
- }
-}
-
-impl fmt::Show for FloatList {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "max_block_start={} floats={:?}", self.max_block_start, self.floats)
- }
-}
-
-/// Wraps a `FloatList` to avoid allocation in the common case of no floats.
-///
-/// FIXME(pcwalton): When we have fast `MutexArc`s, try removing `CowArc` and use a mutex instead.
-#[deriving(Clone)]
-struct FloatListRef {
- list: Option<Arc<FloatList>>,
-}
-
-impl FloatListRef {
- fn new() -> FloatListRef {
- FloatListRef {
- list: None,
- }
- }
-
- /// Returns true if the list is allocated and false otherwise. If false, there are guaranteed
- /// not to be any floats.
- fn is_present(&self) -> bool {
- self.list.is_some()
- }
-
- #[inline]
- fn get<'a>(&'a self) -> Option<&'a FloatList> {
- match self.list {
- None => None,
- Some(ref list) => Some(&**list),
- }
- }
-
- #[allow(experimental)]
- #[inline]
- fn get_mut<'a>(&'a mut self) -> &'a mut FloatList {
- if self.list.is_none() {
- self.list = Some(Arc::new(FloatList::new()))
- }
- self.list.as_mut().unwrap().make_unique()
- }
-}
-
-/// All the information necessary to place a float.
-pub struct PlacementInfo {
- /// The dimensions of the float.
- pub size: LogicalSize<Au>,
- /// The minimum block-start of the float, as determined by earlier elements.
- pub ceiling: Au,
- /// The maximum inline-end position of the float, generally determined by the containing block.
- pub max_inline_size: Au,
- /// The kind of float.
- pub kind: FloatKind
-}
-
-impl fmt::Show for PlacementInfo {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "size={} ceiling={} max_inline_size={} kind={:?}", self.size, self.ceiling, self.max_inline_size, self.kind)
- }
-}
-
-fn range_intersect(block_start_1: Au, block_end_1: Au, block_start_2: Au, block_end_2: Au) -> (Au, Au) {
- (max(block_start_1, block_start_2), min(block_end_1, block_end_2))
-}
-
-/// Encapsulates information about floats. This is optimized to avoid allocation if there are
-/// no floats, and to avoid copying when translating the list of floats downward.
-#[deriving(Clone)]
-pub struct Floats {
- /// The list of floats.
- list: FloatListRef,
- /// The offset of the flow relative to the first float.
- offset: LogicalSize<Au>,
- pub writing_mode: WritingMode,
-}
-
-impl fmt::Show for Floats {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self.list.get() {
- None => {
- write!(f, "[empty]")
- }
- Some(list) => {
- write!(f, "offset={} floats={}", self.offset, list)
- }
- }
- }
-}
-
-impl Floats {
- /// Creates a new `Floats` object.
- pub fn new(writing_mode: WritingMode) -> Floats {
- Floats {
- list: FloatListRef::new(),
- offset: LogicalSize::zero(writing_mode),
- writing_mode: writing_mode,
- }
- }
-
- /// Adjusts the recorded offset of the flow relative to the first float.
- pub fn translate(&mut self, delta: LogicalSize<Au>) {
- self.offset = self.offset + delta
- }
-
- /// Returns the position of the last float in flow coordinates.
- pub fn last_float_pos(&self) -> Option<LogicalPoint<Au>> {
- match self.list.get() {
- None => None,
- Some(list) => {
- match list.floats.last() {
- None => None,
- Some(float) => Some(float.bounds.start + self.offset),
- }
- }
- }
- }
-
- /// Returns a rectangle that encloses the region from block-start to block-start + block-size, with inline-size small
- /// enough that it doesn't collide with any floats. max_x is the x-coordinate beyond which
- /// floats have no effect. (Generally this is the containing block inline-size.)
- pub fn available_rect(&self, block_start: Au, block_size: Au, max_x: Au) -> Option<LogicalRect<Au>> {
- let list = match self.list.get() {
- None => return None,
- Some(list) => list,
- };
-
- let block_start = block_start - self.offset.block;
-
- debug!("available_rect: trying to find space at {}", block_start);
-
- // Relevant dimensions for the inline-end-most inline-start float
- let mut max_inline_start = Au(0) - self.offset.inline;
- let mut l_block_start = None;
- let mut l_block_end = None;
- // Relevant dimensions for the inline-start-most inline-end float
- let mut min_inline_end = max_x - self.offset.inline;
- let mut r_block_start = None;
- let mut r_block_end = None;
-
- // Find the float collisions for the given vertical range.
- for float in list.floats.iter() {
- debug!("available_rect: Checking for collision against float");
- let float_pos = float.bounds.start;
- let float_size = float.bounds.size;
-
- debug!("float_pos: {}, float_size: {}", float_pos, float_size);
- match float.kind {
- FloatLeft if float_pos.i + float_size.inline > max_inline_start &&
- float_pos.b + float_size.block > block_start && float_pos.b < block_start + block_size => {
- max_inline_start = float_pos.i + float_size.inline;
-
- l_block_start = Some(float_pos.b);
- l_block_end = Some(float_pos.b + float_size.block);
-
- debug!("available_rect: collision with inline_start float: new max_inline_start is {}",
- max_inline_start);
- }
- FloatRight if float_pos.i < min_inline_end &&
- float_pos.b + float_size.block > block_start && float_pos.b < block_start + block_size => {
- min_inline_end = float_pos.i;
-
- r_block_start = Some(float_pos.b);
- r_block_end = Some(float_pos.b + float_size.block);
- debug!("available_rect: collision with inline_end float: new min_inline_end is {}",
- min_inline_end);
- }
- FloatLeft | FloatRight => {}
- }
- }
-
- // Extend the vertical range of the rectangle to the closest floats.
- // If there are floats on both sides, take the intersection of the
- // two areas. Also make sure we never return a block-start smaller than the
- // given upper bound.
- let (block_start, block_end) = match (r_block_start, r_block_end, l_block_start, l_block_end) {
- (Some(r_block_start), Some(r_block_end), Some(l_block_start), Some(l_block_end)) =>
- range_intersect(max(block_start, r_block_start), r_block_end, max(block_start, l_block_start), l_block_end),
-
- (None, None, Some(l_block_start), Some(l_block_end)) => (max(block_start, l_block_start), l_block_end),
- (Some(r_block_start), Some(r_block_end), None, None) => (max(block_start, r_block_start), r_block_end),
- (None, None, None, None) => return None,
- _ => fail!("Reached unreachable state when computing float area")
- };
-
- // FIXME(eatkinson): This assertion is too strong and fails in some cases. It is OK to
- // return negative inline-sizes since we check against that inline-end away, but we should still
- // undersrtand why they occur and add a stronger assertion here.
- // assert!(max_inline-start < min_inline-end);
-
- assert!(block_start <= block_end, "Float position error");
-
- Some(LogicalRect::new(
- self.writing_mode, max_inline_start + self.offset.inline, block_start + self.offset.block,
- min_inline_end - max_inline_start, block_end - block_start
- ))
- }
-
- /// Adds a new float to the list.
- pub fn add_float(&mut self, info: &PlacementInfo) {
- let new_info;
- {
- let list = self.list.get_mut();
- new_info = PlacementInfo {
- size: info.size,
- ceiling: max(info.ceiling, list.max_block_start + self.offset.block),
- max_inline_size: info.max_inline_size,
- kind: info.kind
- }
- }
-
- debug!("add_float: added float with info {:?}", new_info);
-
- let new_float = Float {
- bounds: LogicalRect::from_point_size(
- self.writing_mode,
- self.place_between_floats(&new_info).start - self.offset,
- info.size,
- ),
- kind: info.kind
- };
-
- let list = self.list.get_mut();
- list.floats.push(new_float);
- list.max_block_start = max(list.max_block_start, new_float.bounds.start.b);
- }
-
- /// Given the block-start 3 sides of the rectangle, finds the largest block-size that will result in the
- /// rectangle not colliding with any floats. Returns None if that block-size is infinite.
- fn max_block_size_for_bounds(&self, inline_start: Au, block_start: Au, inline_size: Au) -> Option<Au> {
- let list = match self.list.get() {
- None => return None,
- Some(list) => list,
- };
-
- let block_start = block_start - self.offset.block;
- let inline_start = inline_start - self.offset.inline;
- let mut max_block_size = None;
-
- for float in list.floats.iter() {
- if float.bounds.start.b + float.bounds.size.block > block_start &&
- float.bounds.start.i + float.bounds.size.inline > inline_start &&
- float.bounds.start.i < inline_start + inline_size {
- let new_y = float.bounds.start.b;
- max_block_size = Some(min(max_block_size.unwrap_or(new_y), new_y));
- }
- }
-
- max_block_size.map(|h| h + self.offset.block)
- }
-
- /// Given placement information, finds the closest place a fragment can be positioned without
- /// colliding with any floats.
- pub fn place_between_floats(&self, info: &PlacementInfo) -> LogicalRect<Au> {
- debug!("place_between_floats: Placing object with {}", info.size);
-
- // If no floats, use this fast path.
- if !self.list.is_present() {
- match info.kind {
- FloatLeft => {
- return LogicalRect::new(
- self.writing_mode,
- Au(0),
- info.ceiling,
- info.max_inline_size,
- Au(i32::MAX))
- }
- FloatRight => {
- return LogicalRect::new(
- self.writing_mode,
- info.max_inline_size - info.size.inline,
- info.ceiling,
- info.max_inline_size,
- Au(i32::MAX))
- }
- }
- }
-
- // Can't go any higher than previous floats or previous elements in the document.
- let mut float_b = info.ceiling;
- loop {
- let maybe_location = self.available_rect(float_b, info.size.block, info.max_inline_size);
- debug!("place_float: Got available rect: {:?} for y-pos: {}", maybe_location, float_b);
- match maybe_location {
- // If there are no floats blocking us, return the current location
- // TODO(eatkinson): integrate with overflow
- None => {
- return match info.kind {
- FloatLeft => {
- LogicalRect::new(
- self.writing_mode,
- Au(0),
- float_b,
- info.max_inline_size,
- Au(i32::MAX))
- }
- FloatRight => {
- LogicalRect::new(
- self.writing_mode,
- info.max_inline_size - info.size.inline,
- float_b,
- info.max_inline_size,
- Au(i32::MAX))
- }
- }
- }
- Some(rect) => {
- assert!(rect.start.b + rect.size.block != float_b,
- "Non-terminating float placement");
-
- // Place here if there is enough room
- if rect.size.inline >= info.size.inline {
- let block_size = self.max_block_size_for_bounds(rect.start.i,
- rect.start.b,
- rect.size.inline);
- let block_size = block_size.unwrap_or(Au(i32::MAX));
- return match info.kind {
- FloatLeft => {
- LogicalRect::new(
- self.writing_mode,
- rect.start.i,
- float_b,
- rect.size.inline,
- block_size)
- }
- FloatRight => {
- LogicalRect::new(
- self.writing_mode,
- rect.start.i + rect.size.inline - info.size.inline,
- float_b,
- rect.size.inline,
- block_size)
- }
- }
- }
-
- // Try to place at the next-lowest location.
- // Need to be careful of fencepost errors.
- float_b = rect.start.b + rect.size.block;
- }
- }
- }
- }
-
- pub fn clearance(&self, clear: ClearType) -> Au {
- let list = match self.list.get() {
- None => return Au(0),
- Some(list) => list,
- };
-
- let mut clearance = Au(0);
- for float in list.floats.iter() {
- match (clear, float.kind) {
- (ClearLeft, FloatLeft) |
- (ClearRight, FloatRight) |
- (ClearBoth, _) => {
- let b = self.offset.block + float.bounds.start.b + float.bounds.size.block;
- clearance = max(clearance, b);
- }
- _ => {}
- }
- }
- clearance
- }
-}
-
diff --git a/src/components/layout/flow.rs b/src/components/layout/flow.rs
deleted file mode 100644
index 2759ebbe74b..00000000000
--- a/src/components/layout/flow.rs
+++ /dev/null
@@ -1,1138 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//! Servo's experimental layout system builds a tree of `Flow` and `Fragment` objects and solves
-//! layout constraints to obtain positions and display attributes of tree nodes. Positions are
-//! computed in several tree traversals driven by the fundamental data dependencies required by
-/// inline and block layout.
-///
-/// Flows are interior nodes in the layout tree and correspond closely to *flow contexts* in the
-/// CSS specification. Flows are responsible for positioning their child flow contexts and fragments.
-/// Flows have purpose-specific fields, such as auxiliary line structs, out-of-flow child
-/// lists, and so on.
-///
-/// Currently, the important types of flows are:
-///
-/// * `BlockFlow`: A flow that establishes a block context. It has several child flows, each of
-/// which are positioned according to block formatting context rules (CSS block boxes). Block
-/// flows also contain a single box to represent their rendered borders, padding, etc.
-/// The BlockFlow at the root of the tree has special behavior: it stretches to the boundaries of
-/// the viewport.
-///
-/// * `InlineFlow`: A flow that establishes an inline context. It has a flat list of child
-/// fragments/flows that are subject to inline layout and line breaking and structs to represent
-/// line breaks and mapping to CSS boxes, for the purpose of handling `getClientRects()` and
-/// similar methods.
-
-use css::node_style::StyledNode;
-use block::BlockFlow;
-use context::LayoutContext;
-use floats::Floats;
-use flow_list::{FlowList, Link, FlowListIterator, MutFlowListIterator};
-use flow_ref::FlowRef;
-use fragment::{Fragment, TableRowFragment, TableCellFragment};
-use incremental::RestyleDamage;
-use inline::InlineFlow;
-use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo};
-use parallel::FlowParallelInfo;
-use table_wrapper::TableWrapperFlow;
-use table::TableFlow;
-use table_colgroup::TableColGroupFlow;
-use table_rowgroup::TableRowGroupFlow;
-use table_row::TableRowFlow;
-use table_caption::TableCaptionFlow;
-use table_cell::TableCellFlow;
-use wrapper::ThreadSafeLayoutNode;
-
-use collections::dlist::DList;
-use geom::Point2D;
-use gfx::display_list::DisplayList;
-use gfx::render_task::RenderLayer;
-use serialize::{Encoder, Encodable};
-use servo_msg::compositor_msg::LayerId;
-use servo_util::geometry::Au;
-use servo_util::logical_geometry::WritingMode;
-use servo_util::logical_geometry::{LogicalRect, LogicalSize};
-use std::mem;
-use std::num::Zero;
-use std::fmt;
-use std::iter::Zip;
-use std::raw;
-use std::sync::atomics::{AtomicUint, Relaxed, SeqCst};
-use std::slice::MutItems;
-use style::computed_values::{clear, position, text_align};
-
-/// Virtual methods that make up a float context.
-///
-/// Note that virtual methods have a cost; we should not overuse them in Servo. Consider adding
-/// methods to `ImmutableFlowUtils` or `MutableFlowUtils` before adding more methods here.
-pub trait Flow: fmt::Show + ToString + Share {
- // RTTI
- //
- // TODO(pcwalton): Use Rust's RTTI, once that works.
-
- /// Returns the class of flow that this is.
- fn class(&self) -> FlowClass;
-
- /// If this is a block flow, returns the underlying object, borrowed immutably. Fails
- /// otherwise.
- fn as_immutable_block<'a>(&'a self) -> &'a BlockFlow {
- fail!("called as_immutable_block() on a non-block flow")
- }
-
- /// If this is a block flow, returns the underlying object. Fails otherwise.
- fn as_block<'a>(&'a mut self) -> &'a mut BlockFlow {
- debug!("called as_block() on a flow of type {}", self.class());
- fail!("called as_block() on a non-block flow")
- }
-
- /// If this is an inline flow, returns the underlying object, borrowed immutably. Fails
- /// otherwise.
- fn as_immutable_inline<'a>(&'a self) -> &'a InlineFlow {
- fail!("called as_immutable_inline() on a non-inline flow")
- }
-
- /// If this is an inline flow, returns the underlying object. Fails otherwise.
- fn as_inline<'a>(&'a mut self) -> &'a mut InlineFlow {
- fail!("called as_inline() on a non-inline flow")
- }
-
- /// If this is a table wrapper flow, returns the underlying object. Fails otherwise.
- fn as_table_wrapper<'a>(&'a mut self) -> &'a mut TableWrapperFlow {
- fail!("called as_table_wrapper() on a non-tablewrapper flow")
- }
-
- /// If this is a table flow, returns the underlying object. Fails otherwise.
- fn as_table<'a>(&'a mut self) -> &'a mut TableFlow {
- fail!("called as_table() on a non-table flow")
- }
-
- /// If this is a table colgroup flow, returns the underlying object. Fails otherwise.
- fn as_table_colgroup<'a>(&'a mut self) -> &'a mut TableColGroupFlow {
- fail!("called as_table_colgroup() on a non-tablecolgroup flow")
- }
-
- /// If this is a table rowgroup flow, returns the underlying object. Fails otherwise.
- fn as_table_rowgroup<'a>(&'a mut self) -> &'a mut TableRowGroupFlow {
- fail!("called as_table_rowgroup() on a non-tablerowgroup flow")
- }
-
- /// If this is a table row flow, returns the underlying object. Fails otherwise.
- fn as_table_row<'a>(&'a mut self) -> &'a mut TableRowFlow {
- fail!("called as_table_row() on a non-tablerow flow")
- }
-
- /// If this is a table cell flow, returns the underlying object. Fails otherwise.
- fn as_table_caption<'a>(&'a mut self) -> &'a mut TableCaptionFlow {
- fail!("called as_table_caption() on a non-tablecaption flow")
- }
-
- /// If this is a table cell flow, returns the underlying object. Fails otherwise.
- fn as_table_cell<'a>(&'a mut self) -> &'a mut TableCellFlow {
- fail!("called as_table_cell() on a non-tablecell flow")
- }
-
- /// If this is a table row or table rowgroup or table flow, returns column inline-sizes.
- /// Fails otherwise.
- fn col_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<Au> {
- fail!("called col_inline_sizes() on an other flow than table-row/table-rowgroup/table")
- }
-
- /// If this is a table row flow or table rowgroup flow or table flow, returns column min inline-sizes.
- /// Fails otherwise.
- fn col_min_inline_sizes<'a>(&'a self) -> &'a Vec<Au> {
- fail!("called col_min_inline_sizes() on an other flow than table-row/table-rowgroup/table")
- }
-
- /// If this is a table row flow or table rowgroup flow or table flow, returns column min inline-sizes.
- /// Fails otherwise.
- fn col_pref_inline_sizes<'a>(&'a self) -> &'a Vec<Au> {
- fail!("called col_pref_inline_sizes() on an other flow than table-row/table-rowgroup/table")
- }
-
- // Main methods
-
- /// Pass 1 of reflow: computes minimum and preferred inline-sizes.
- ///
- /// Recursively (bottom-up) determine the flow's minimum and preferred inline-sizes. When called on
- /// this flow, all child flows have had their minimum and preferred inline-sizes set. This function
- /// must decide minimum/preferred inline-sizes based on its children's inline-sizes and the dimensions of
- /// any boxes it is responsible for flowing.
- fn bubble_inline_sizes(&mut self, _ctx: &LayoutContext) {
- fail!("bubble_inline_sizes not yet implemented")
- }
-
- /// Pass 2 of reflow: computes inline-size.
- fn assign_inline_sizes(&mut self, _ctx: &LayoutContext) {
- fail!("assign_inline_sizes not yet implemented")
- }
-
- /// Pass 3a of reflow: computes block-size.
- fn assign_block_size<'a>(&mut self, _ctx: &'a LayoutContext<'a>) {
- fail!("assign_block_size not yet implemented")
- }
-
- /// Assigns block-sizes in-order; or, if this is a float, places the float. The default
- /// implementation simply assigns block-sizes if this flow is impacted by floats. Returns true if
- /// this child was impacted by floats or false otherwise.
- fn assign_block_size_for_inorder_child_if_necessary<'a>(&mut self, layout_context: &'a LayoutContext<'a>)
- -> bool {
- let impacted = base(&*self).flags.impacted_by_floats();
- if impacted {
- self.assign_block_size(layout_context);
- }
- impacted
- }
-
- /// Phase 4 of reflow: computes absolute positions.
- fn compute_absolute_position(&mut self) {
- // The default implementation is a no-op.
- }
-
- /// Returns the direction that this flow clears floats in, if any.
- fn float_clearance(&self) -> clear::T {
- clear::none
- }
-
- /// Returns true if this float is a block formatting context and false otherwise. The default
- /// implementation returns false.
- fn is_block_formatting_context(&self, _only_impactable_by_floats: bool) -> bool {
- false
- }
-
- fn compute_collapsible_block_start_margin(&mut self,
- _layout_context: &mut LayoutContext,
- _margin_collapse_info: &mut MarginCollapseInfo) {
- // The default implementation is a no-op.
- }
-
- /// Marks this flow as the root flow. The default implementation is a no-op.
- fn mark_as_root(&mut self) {}
-
- // Note that the following functions are mostly called using static method
- // dispatch, so it's ok to have them in this trait. Plus, they have
- // different behaviour for different types of Flow, so they can't go into
- // the Immutable / Mutable Flow Utils traits without additional casts.
-
- /// Return true if store overflow is delayed for this flow.
- ///
- /// Currently happens only for absolutely positioned flows.
- fn is_store_overflow_delayed(&mut self) -> bool {
- false
- }
-
- fn is_root(&self) -> bool {
- false
- }
-
- fn is_float(&self) -> bool {
- false
- }
-
- /// The 'position' property of this flow.
- fn positioning(&self) -> position::T {
- position::static_
- }
-
- /// Return true if this flow has position 'fixed'.
- fn is_fixed(&self) -> bool {
- self.positioning() == position::fixed
- }
-
- fn is_positioned(&self) -> bool {
- self.is_relatively_positioned() || self.is_absolutely_positioned()
- }
-
- fn is_relatively_positioned(&self) -> bool {
- self.positioning() == position::relative
- }
-
- fn is_absolutely_positioned(&self) -> bool {
- self.positioning() == position::absolute || self.is_fixed()
- }
-
- /// Return true if this is the root of an Absolute flow tree.
- fn is_root_of_absolute_flow_tree(&self) -> bool {
- false
- }
-
- /// Returns true if this is an absolute containing block.
- fn is_absolute_containing_block(&self) -> bool {
- false
- }
-
- /// Return the dimensions of the containing block generated by this flow for absolutely-
- /// positioned descendants. For block flows, this is the padding box.
- fn generated_containing_block_rect(&self) -> LogicalRect<Au> {
- fail!("generated_containing_block_position not yet implemented for this flow")
- }
-
- /// Returns a layer ID for the given fragment.
- fn layer_id(&self, fragment_id: uint) -> LayerId {
- unsafe {
- let pointer: uint = mem::transmute(self);
- LayerId(pointer, fragment_id)
- }
- }
-}
-
-impl<'a, E, S: Encoder<E>> Encodable<S, E> for &'a Flow {
- fn encode(&self, e: &mut S) -> Result<(), E> {
- e.emit_struct("flow", 0, |e| {
- try!(e.emit_struct_field("class", 0, |e| self.class().encode(e)))
- e.emit_struct_field("data", 1, |e| {
- match self.class() {
- BlockFlowClass => self.as_immutable_block().encode(e),
- InlineFlowClass => self.as_immutable_inline().encode(e),
- _ => { Ok(()) } // TODO: Support tables
- }
- })
- })
- }
-}
-
-// Base access
-
-#[inline(always)]
-pub fn base<'a>(this: &'a Flow) -> &'a BaseFlow {
- unsafe {
- let obj = mem::transmute::<&'a Flow, raw::TraitObject>(this);
- mem::transmute::<*mut (), &'a BaseFlow>(obj.data)
- }
-}
-
-/// Iterates over the children of this immutable flow.
-pub fn imm_child_iter<'a>(flow: &'a Flow) -> FlowListIterator<'a> {
- base(flow).children.iter()
-}
-
-#[inline(always)]
-pub fn mut_base<'a>(this: &'a mut Flow) -> &'a mut BaseFlow {
- unsafe {
- let obj = mem::transmute::<&'a mut Flow, raw::TraitObject>(this);
- mem::transmute::<*mut (), &'a mut BaseFlow>(obj.data)
- }
-}
-
-/// Iterates over the children of this flow.
-pub fn child_iter<'a>(flow: &'a mut Flow) -> MutFlowListIterator<'a> {
- mut_base(flow).children.mut_iter()
-}
-
-pub trait ImmutableFlowUtils {
- // Convenience functions
-
- /// Returns true if this flow is a block or a float flow.
- fn is_block_like(self) -> bool;
-
- /// Returns true if this flow is a table flow.
- fn is_table(self) -> bool;
-
- /// Returns true if this flow is a table caption flow.
- fn is_table_caption(self) -> bool;
-
- /// Returns true if this flow is a proper table child.
- fn is_proper_table_child(self) -> bool;
-
- /// Returns true if this flow is a table row flow.
- fn is_table_row(self) -> bool;
-
- /// Returns true if this flow is a table cell flow.
- fn is_table_cell(self) -> bool;
-
- /// Returns true if this flow is a table colgroup flow.
- fn is_table_colgroup(self) -> bool;
-
- /// Returns true if this flow is a table rowgroup flow.
- fn is_table_rowgroup(self) -> bool;
-
- /// Returns true if this flow is one of table-related flows.
- fn is_table_kind(self) -> bool;
-
- /// Returns true if anonymous flow is needed between this flow and child flow.
- fn need_anonymous_flow(self, child: &Flow) -> bool;
-
- /// Generates missing child flow of this flow.
- fn generate_missing_child_flow(self, node: &ThreadSafeLayoutNode) -> FlowRef;
-
- /// Returns true if this flow has no children.
- fn is_leaf(self) -> bool;
-
- /// Returns the number of children that this flow possesses.
- fn child_count(self) -> uint;
-
- /// Return true if this flow is a Block Container.
- fn is_block_container(self) -> bool;
-
- /// Returns true if this flow is a block flow.
- fn is_block_flow(self) -> bool;
-
- /// Returns true if this flow is an inline flow.
- fn is_inline_flow(self) -> bool;
-
- /// Dumps the flow tree for debugging.
- fn dump(self);
-
- /// Dumps the flow tree for debugging, with a prefix to indicate that we're at the given level.
- fn dump_with_level(self, level: uint);
-}
-
-pub trait MutableFlowUtils {
- // Traversals
-
- /// Traverses the tree in preorder.
- fn traverse_preorder<T:PreorderFlowTraversal>(self, traversal: &mut T) -> bool;
-
- /// Traverses the tree in postorder.
- fn traverse_postorder<T:PostorderFlowTraversal>(self, traversal: &mut T) -> bool;
-
- // Mutators
-
- /// Computes the overflow region for this flow.
- fn store_overflow(self, _: &LayoutContext);
-
- /// Builds the display lists for this flow.
- fn build_display_list(self, layout_context: &LayoutContext);
-}
-
-pub trait MutableOwnedFlowUtils {
- /// Set absolute descendants for this flow.
- ///
- /// Set this flow as the Containing Block for all the absolute descendants.
- fn set_abs_descendants(&mut self, abs_descendants: AbsDescendants);
-}
-
-#[deriving(Encodable, PartialEq, Show)]
-pub enum FlowClass {
- BlockFlowClass,
- InlineFlowClass,
- TableWrapperFlowClass,
- TableFlowClass,
- TableColGroupFlowClass,
- TableRowGroupFlowClass,
- TableRowFlowClass,
- TableCaptionFlowClass,
- TableCellFlowClass,
-}
-
-/// A top-down traversal.
-pub trait PreorderFlowTraversal {
- /// The operation to perform. Return true to continue or false to stop.
- fn process(&mut self, flow: &mut Flow) -> bool;
-
- /// Returns true if this node should be pruned. If this returns true, we skip the operation
- /// entirely and do not process any descendant nodes. This is called *before* child nodes are
- /// visited. The default implementation never prunes any nodes.
- fn should_prune(&mut self, _flow: &mut Flow) -> bool {
- false
- }
-}
-
-/// A bottom-up traversal, with a optional in-order pass.
-pub trait PostorderFlowTraversal {
- /// The operation to perform. Return true to continue or false to stop.
- fn process(&mut self, flow: &mut Flow) -> bool;
-
- /// Returns false if this node must be processed in-order. If this returns false, we skip the
- /// operation for this node, but continue processing the ancestors. This is called *after*
- /// child nodes are visited.
- fn should_process(&mut self, _flow: &mut Flow) -> bool {
- true
- }
-
- /// Returns true if this node should be pruned. If this returns true, we skip the operation
- /// entirely and do not process any descendant nodes. This is called *before* child nodes are
- /// visited. The default implementation never prunes any nodes.
- fn should_prune(&mut self, _flow: &mut Flow) -> bool {
- false
- }
-}
-
-/// Flags used in flows, tightly packed to save space.
-#[deriving(Clone, Encodable)]
-pub struct FlowFlags(pub u8);
-
-/// The bitmask of flags that represent the `has_left_floated_descendants` and
-/// `has_right_floated_descendants` fields.
-///
-/// NB: If you update this field, you must update the bitfields below.
-static HAS_FLOATED_DESCENDANTS_BITMASK: u8 = 0b0000_0011;
-
-// Whether this flow has descendants that float left in the same block formatting context.
-bitfield!(FlowFlags, has_left_floated_descendants, set_has_left_floated_descendants, 0b0000_0001)
-
-// Whether this flow has descendants that float right in the same block formatting context.
-bitfield!(FlowFlags, has_right_floated_descendants, set_has_right_floated_descendants, 0b0000_0010)
-
-// Whether this flow is impacted by floats to the left in the same block formatting context (i.e.
-// its block-size depends on some prior flows with `float: left`).
-bitfield!(FlowFlags, impacted_by_left_floats, set_impacted_by_left_floats, 0b0000_0100)
-
-// Whether this flow is impacted by floats to the right in the same block formatting context (i.e.
-// its block-size depends on some prior flows with `float: right`).
-bitfield!(FlowFlags, impacted_by_right_floats, set_impacted_by_right_floats, 0b0000_1000)
-
-/// The bitmask of flags that represent the text alignment field.
-///
-/// NB: If you update this field, you must update the bitfields below.
-static TEXT_ALIGN_BITMASK: u8 = 0b0011_0000;
-
-/// The number of bits we must shift off to handle the text alignment field.
-///
-/// NB: If you update this field, you must update the bitfields below.
-static TEXT_ALIGN_SHIFT: u8 = 4;
-
-// Whether this flow contains a flow that has its own layer within the same absolute containing
-// block.
-bitfield!(FlowFlags,
- layers_needed_for_descendants,
- set_layers_needed_for_descendants,
- 0b0100_0000)
-
-// Whether this flow must have its own layer. Even if this flag is not set, it might get its own
-// layer if it's deemed to be likely to overlap flows with their own layer.
-bitfield!(FlowFlags, needs_layer, set_needs_layer, 0b1000_0000)
-
-impl FlowFlags {
- /// Creates a new set of flow flags.
- pub fn new() -> FlowFlags {
- FlowFlags(0)
- }
-
- /// Propagates text alignment flags from an appropriate parent flow per CSS 2.1.
- ///
- /// FIXME(#2265, pcwalton): It would be cleaner and faster to make this a derived CSS property
- /// `-servo-text-align-in-effect`.
- pub fn propagate_text_alignment_from_parent(&mut self, parent_flags: FlowFlags) {
- self.set_text_align_override(parent_flags);
- }
-
- #[inline]
- pub fn text_align(self) -> text_align::T {
- let FlowFlags(ff) = self;
- FromPrimitive::from_u8((ff & TEXT_ALIGN_BITMASK) >> TEXT_ALIGN_SHIFT as uint).unwrap()
- }
-
- #[inline]
- pub fn set_text_align(&mut self, value: text_align::T) {
- let FlowFlags(ff) = *self;
- *self = FlowFlags((ff & !TEXT_ALIGN_BITMASK) | ((value as u8) << TEXT_ALIGN_SHIFT as uint))
- }
-
- #[inline]
- pub fn set_text_align_override(&mut self, parent: FlowFlags) {
- let FlowFlags(ff) = *self;
- let FlowFlags(pff) = parent;
- *self = FlowFlags(ff | (pff & TEXT_ALIGN_BITMASK))
- }
-
- #[inline]
- pub fn union_floated_descendants_flags(&mut self, other: FlowFlags) {
- let FlowFlags(my_flags) = *self;
- let FlowFlags(other_flags) = other;
- *self = FlowFlags(my_flags | (other_flags & HAS_FLOATED_DESCENDANTS_BITMASK))
- }
-
- #[inline]
- pub fn impacted_by_floats(&self) -> bool {
- self.impacted_by_left_floats() || self.impacted_by_right_floats()
- }
-}
-
-/// The Descendants of a flow.
-///
-/// Also, details about their position wrt this flow.
-pub struct Descendants {
- /// Links to every descendant. This must be private because it is unsafe to leak `FlowRef`s to
- /// layout.
- descendant_links: Vec<FlowRef>,
-
- /// Static y offsets of all descendants from the start of this flow box.
- pub static_b_offsets: Vec<Au>,
-}
-
-impl Descendants {
- pub fn new() -> Descendants {
- Descendants {
- descendant_links: Vec::new(),
- static_b_offsets: Vec::new(),
- }
- }
-
- pub fn len(&self) -> uint {
- self.descendant_links.len()
- }
-
- pub fn push(&mut self, given_descendant: FlowRef) {
- self.descendant_links.push(given_descendant);
- }
-
- /// Push the given descendants on to the existing descendants.
- ///
- /// Ignore any static y offsets, because they are None before layout.
- pub fn push_descendants(&mut self, given_descendants: Descendants) {
- for elem in given_descendants.descendant_links.move_iter() {
- self.descendant_links.push(elem);
- }
- }
-
- /// Return an iterator over the descendant flows.
- pub fn iter<'a>(&'a mut self) -> DescendantIter<'a> {
- DescendantIter {
- iter: self.descendant_links.mut_slice_from(0).mut_iter(),
- }
- }
-
- /// Return an iterator over (descendant, static y offset).
- pub fn iter_with_offset<'a>(&'a mut self) -> DescendantOffsetIter<'a> {
- let descendant_iter = DescendantIter {
- iter: self.descendant_links.mut_slice_from(0).mut_iter(),
- };
- descendant_iter.zip(self.static_b_offsets.mut_slice_from(0).mut_iter())
- }
-}
-
-pub type AbsDescendants = Descendants;
-
-pub struct DescendantIter<'a> {
- iter: MutItems<'a, FlowRef>,
-}
-
-impl<'a> Iterator<&'a mut Flow> for DescendantIter<'a> {
- fn next(&mut self) -> Option<&'a mut Flow> {
- match self.iter.next() {
- None => None,
- Some(ref mut flow) => {
- unsafe {
- let result: &'a mut Flow = mem::transmute(flow.get_mut());
- Some(result)
- }
- }
- }
- }
-}
-
-pub type DescendantOffsetIter<'a> = Zip<DescendantIter<'a>, MutItems<'a, Au>>;
-
-/// Information needed to compute absolute (i.e. viewport-relative) flow positions (not to be
-/// confused with absolutely-positioned flows).
-#[deriving(Encodable)]
-pub struct AbsolutePositionInfo {
- /// The size of the containing block for relatively-positioned descendants.
- pub relative_containing_block_size: LogicalSize<Au>,
- /// The position of the absolute containing block.
- pub absolute_containing_block_position: Point2D<Au>,
- /// Whether the absolute containing block forces positioned descendants to be layerized.
- ///
- /// FIXME(pcwalton): Move into `FlowFlags`.
- pub layers_needed_for_positioned_flows: bool,
-}
-
-impl AbsolutePositionInfo {
- pub fn new(writing_mode: WritingMode) -> AbsolutePositionInfo {
- // FIXME(pcwalton): The initial relative containing block-size should be equal to the size
- // of the root layer.
- AbsolutePositionInfo {
- relative_containing_block_size: LogicalSize::zero(writing_mode),
- absolute_containing_block_position: Zero::zero(),
- layers_needed_for_positioned_flows: false,
- }
- }
-}
-
-/// Data common to all flows.
-pub struct BaseFlow {
- /// NB: Must be the first element.
- ///
- /// The necessity of this will disappear once we have dynamically-sized types.
- ref_count: AtomicUint,
-
- pub restyle_damage: RestyleDamage,
-
- /// The children of this flow.
- pub children: FlowList,
- pub next_sibling: Link,
- pub prev_sibling: Link,
-
- /* layout computations */
- // TODO: min/pref and position are used during disjoint phases of
- // layout; maybe combine into a single enum to save space.
- pub intrinsic_inline_sizes: IntrinsicISizes,
-
- /// The upper left corner of the box representing this flow, relative to the box representing
- /// its parent flow.
- ///
- /// For absolute flows, this represents the position with respect to its *containing block*.
- ///
- /// This does not include margins in the block flow direction, because those can collapse. So
- /// for the block direction (usually vertical), this represents the *border box*. For the
- /// inline direction (usually horizontal), this represents the *margin box*.
- pub position: LogicalRect<Au>,
-
- /// The amount of overflow of this flow, relative to the containing block. Must include all the
- /// pixels of all the display list items for correct invalidation.
- pub overflow: LogicalRect<Au>,
-
- /// Data used during parallel traversals.
- ///
- /// TODO(pcwalton): Group with other transient data to save space.
- pub parallel: FlowParallelInfo,
-
- /// The floats next to this flow.
- pub floats: Floats,
-
- /// The collapsible margins for this flow, if any.
- pub collapsible_margins: CollapsibleMargins,
-
- /// The position of this flow in page coordinates, computed during display list construction.
- pub abs_position: Point2D<Au>,
-
- /// Details about descendants with position 'absolute' or 'fixed' for which we are the
- /// containing block. This is in tree order. This includes any direct children.
- pub abs_descendants: AbsDescendants,
-
- /// Offset wrt the nearest positioned ancestor - aka the Containing Block
- /// for any absolutely positioned elements.
- pub absolute_static_i_offset: Au,
-
- /// Offset wrt the Initial Containing Block.
- pub fixed_static_i_offset: Au,
-
- /// Reference to the Containing Block, if this flow is absolutely positioned.
- pub absolute_cb: ContainingBlockLink,
-
- /// Information needed to compute absolute (i.e. viewport-relative) flow positions (not to be
- /// confused with absolutely-positioned flows).
- ///
- /// FIXME(pcwalton): Merge with `absolute_static_i_offset` and `fixed_static_i_offset` above?
- pub absolute_position_info: AbsolutePositionInfo,
-
- /// The unflattened display items for this flow.
- pub display_list: DisplayList,
-
- /// Any layers that we're bubbling up, in a linked list.
- pub layers: DList<RenderLayer>,
-
- /// Various flags for flows, tightly packed to save space.
- pub flags: FlowFlags,
-
- pub writing_mode: WritingMode,
-}
-
-impl<E, S: Encoder<E>> Encodable<S, E> for BaseFlow {
- fn encode(&self, e: &mut S) -> Result<(), E> {
- e.emit_struct("base", 0, |e| {
- try!(e.emit_struct_field("id", 0, |e| self.debug_id().encode(e)))
- try!(e.emit_struct_field("abs_position", 1, |e| self.abs_position.encode(e)))
- try!(e.emit_struct_field("intrinsic_inline_sizes", 2, |e| self.intrinsic_inline_sizes.encode(e)))
- try!(e.emit_struct_field("position", 3, |e| self.position.encode(e)))
- e.emit_struct_field("children", 4, |e| {
- e.emit_seq(self.children.len(), |e| {
- for (i, c) in self.children.iter().enumerate() {
- try!(e.emit_seq_elt(i, |e| c.encode(e)))
- }
- Ok(())
- })
-
- })
- })
- }
-}
-
-#[unsafe_destructor]
-impl Drop for BaseFlow {
- fn drop(&mut self) {
- if self.ref_count.load(SeqCst) != 0 {
- fail!("Flow destroyed before its ref count hit zero—this is unsafe!")
- }
- }
-}
-
-impl BaseFlow {
- #[inline]
- pub fn new(node: ThreadSafeLayoutNode) -> BaseFlow {
- let writing_mode = node.style().writing_mode;
- BaseFlow {
- ref_count: AtomicUint::new(1),
-
- restyle_damage: node.restyle_damage(),
-
- children: FlowList::new(),
- next_sibling: None,
- prev_sibling: None,
-
- intrinsic_inline_sizes: IntrinsicISizes::new(),
- position: LogicalRect::zero(writing_mode),
- overflow: LogicalRect::zero(writing_mode),
-
- parallel: FlowParallelInfo::new(),
-
- floats: Floats::new(writing_mode),
- collapsible_margins: CollapsibleMargins::new(),
- abs_position: Zero::zero(),
- abs_descendants: Descendants::new(),
- absolute_static_i_offset: Au::new(0),
- fixed_static_i_offset: Au::new(0),
- absolute_cb: ContainingBlockLink::new(),
- display_list: DisplayList::new(),
- layers: DList::new(),
- absolute_position_info: AbsolutePositionInfo::new(writing_mode),
-
- flags: FlowFlags::new(),
- writing_mode: writing_mode,
- }
- }
-
- pub fn child_iter<'a>(&'a mut self) -> MutFlowListIterator<'a> {
- self.children.mut_iter()
- }
-
- pub unsafe fn ref_count<'a>(&'a self) -> &'a AtomicUint {
- &self.ref_count
- }
-
- pub fn debug_id(&self) -> String {
- format!("{:p}", self as *const _)
- }
-}
-
-impl<'a> ImmutableFlowUtils for &'a Flow {
- /// Returns true if this flow is a block or a float flow.
- fn is_block_like(self) -> bool {
- match self.class() {
- BlockFlowClass => true,
- _ => false,
- }
- }
-
- /// Returns true if this flow is a proper table child.
- /// 'Proper table child' is defined as table-row flow, table-rowgroup flow,
- /// table-column-group flow, or table-caption flow.
- fn is_proper_table_child(self) -> bool {
- match self.class() {
- TableRowFlowClass | TableRowGroupFlowClass |
- TableColGroupFlowClass | TableCaptionFlowClass => true,
- _ => false,
- }
- }
-
- /// Returns true if this flow is a table row flow.
- fn is_table_row(self) -> bool {
- match self.class() {
- TableRowFlowClass => true,
- _ => false,
- }
- }
-
- /// Returns true if this flow is a table cell flow.
- fn is_table_cell(self) -> bool {
- match self.class() {
- TableCellFlowClass => true,
- _ => false,
- }
- }
-
- /// Returns true if this flow is a table colgroup flow.
- fn is_table_colgroup(self) -> bool {
- match self.class() {
- TableColGroupFlowClass => true,
- _ => false,
- }
- }
-
- /// Returns true if this flow is a table flow.
- fn is_table(self) -> bool {
- match self.class() {
- TableFlowClass => true,
- _ => false,
- }
- }
-
- /// Returns true if this flow is a table caption flow.
- fn is_table_caption(self) -> bool {
- match self.class() {
- TableCaptionFlowClass => true,
- _ => false,
- }
- }
-
- /// Returns true if this flow is a table rowgroup flow.
- fn is_table_rowgroup(self) -> bool {
- match self.class() {
- TableRowGroupFlowClass => true,
- _ => false,
- }
- }
-
- /// Returns true if this flow is one of table-related flows.
- fn is_table_kind(self) -> bool {
- match self.class() {
- TableWrapperFlowClass | TableFlowClass |
- TableColGroupFlowClass | TableRowGroupFlowClass |
- TableRowFlowClass | TableCaptionFlowClass | TableCellFlowClass => true,
- _ => false,
- }
- }
-
- /// Returns true if anonymous flow is needed between this flow and child flow.
- /// Spec: http://www.w3.org/TR/CSS21/tables.html#anonymous-boxes
- fn need_anonymous_flow(self, child: &Flow) -> bool {
- match self.class() {
- TableFlowClass => !child.is_proper_table_child(),
- TableRowGroupFlowClass => !child.is_table_row(),
- TableRowFlowClass => !child.is_table_cell(),
- _ => false
- }
- }
-
- /// Generates missing child flow of this flow.
- fn generate_missing_child_flow(self, node: &ThreadSafeLayoutNode) -> FlowRef {
- let flow = match self.class() {
- TableFlowClass | TableRowGroupFlowClass => {
- let fragment = Fragment::new_anonymous_table_fragment(node, TableRowFragment);
- box TableRowFlow::from_node_and_fragment(node, fragment) as Box<Flow>
- },
- TableRowFlowClass => {
- let fragment = Fragment::new_anonymous_table_fragment(node, TableCellFragment);
- box TableCellFlow::from_node_and_fragment(node, fragment) as Box<Flow>
- },
- _ => {
- fail!("no need to generate a missing child")
- }
- };
- FlowRef::new(flow)
- }
-
- /// Returns true if this flow has no children.
- fn is_leaf(self) -> bool {
- base(self).children.len() == 0
- }
-
- /// Returns the number of children that this flow possesses.
- fn child_count(self) -> uint {
- base(self).children.len()
- }
-
- /// Return true if this flow is a Block Container.
- ///
- /// Except for table fragments and replaced elements, block-level fragments (`BlockFlow`) are
- /// also block container fragments.
- /// Non-replaced inline blocks and non-replaced table cells are also block
- /// containers.
- fn is_block_container(self) -> bool {
- match self.class() {
- // TODO: Change this when inline-blocks are supported.
- BlockFlowClass | TableCaptionFlowClass | TableCellFlowClass => {
- // FIXME: Actually check the type of the node
- self.child_count() != 0
- }
- _ => false,
- }
- }
-
- /// Returns true if this flow is a block flow.
- fn is_block_flow(self) -> bool {
- match self.class() {
- BlockFlowClass => true,
- _ => false,
- }
- }
-
- /// Returns true if this flow is an inline flow.
- fn is_inline_flow(self) -> bool {
- match self.class() {
- InlineFlowClass => true,
- _ => false,
- }
- }
-
- /// Dumps the flow tree for debugging.
- fn dump(self) {
- self.dump_with_level(0)
- }
-
- /// Dumps the flow tree for debugging, with a prefix to indicate that we're at the given level.
- fn dump_with_level(self, level: uint) {
- let mut indent = String::new();
- for _ in range(0, level) {
- indent.push_str("| ")
- }
- debug!("{}+ {}", indent, self.to_string());
- for kid in imm_child_iter(self) {
- kid.dump_with_level(level + 1)
- }
- }
-}
-
-impl<'a> MutableFlowUtils for &'a mut Flow {
- /// Traverses the tree in preorder.
- fn traverse_preorder<T:PreorderFlowTraversal>(self, traversal: &mut T) -> bool {
- if traversal.should_prune(self) {
- return true
- }
-
- if !traversal.process(self) {
- return false
- }
-
- for kid in child_iter(self) {
- if !kid.traverse_preorder(traversal) {
- return false
- }
- }
-
- true
- }
-
- /// Traverses the tree in postorder.
- fn traverse_postorder<T:PostorderFlowTraversal>(self, traversal: &mut T) -> bool {
- if traversal.should_prune(self) {
- return true
- }
-
- for kid in child_iter(self) {
- if !kid.traverse_postorder(traversal) {
- return false
- }
- }
-
- if !traversal.should_process(self) {
- return true
- }
-
- traversal.process(self)
- }
-
- /// Calculate and set overflow for current flow.
- ///
- /// CSS Section 11.1
- /// This is the union of rectangles of the flows for which we define the
- /// Containing Block.
- ///
- /// Assumption: This is called in a bottom-up traversal, so kids' overflows have
- /// already been set.
- /// Assumption: Absolute descendants have had their overflow calculated.
- fn store_overflow(self, _: &LayoutContext) {
- let my_position = mut_base(self).position;
- let mut overflow = my_position;
-
- if self.is_block_container() {
- for kid in child_iter(self) {
- if kid.is_store_overflow_delayed() {
- // Absolute flows will be handled by their CB. If we are
- // their CB, they will show up in `abs_descendants`.
- continue;
- }
- let mut kid_overflow = base(kid).overflow;
- kid_overflow = kid_overflow.translate(&my_position.start);
- overflow = overflow.union(&kid_overflow)
- }
-
- // FIXME(#2004, pcwalton): This is wrong for `position: fixed`.
- for descendant_link in mut_base(self).abs_descendants.iter() {
- let mut kid_overflow = base(descendant_link).overflow;
- kid_overflow = kid_overflow.translate(&my_position.start);
- overflow = overflow.union(&kid_overflow)
- }
- }
- mut_base(self).overflow = overflow;
- }
-
- /// Push display items for current flow and its descendants onto the appropriate display lists
- /// of the given stacking context.
- ///
- /// Arguments:
- ///
- /// * `builder`: The display list builder, which contains information used during the entire
- /// display list building pass.
- ///
- /// * `info`: Per-flow display list building information.
- fn build_display_list(self, layout_context: &LayoutContext) {
- debug!("Flow: building display list");
- match self.class() {
- BlockFlowClass => self.as_block().build_display_list_block(layout_context),
- InlineFlowClass => self.as_inline().build_display_list_inline(layout_context),
- TableWrapperFlowClass => {
- self.as_table_wrapper().build_display_list_table_wrapper(layout_context)
- }
- TableFlowClass => self.as_table().build_display_list_table(layout_context),
- TableRowGroupFlowClass => {
- self.as_table_rowgroup().build_display_list_table_rowgroup(layout_context)
- }
- TableRowFlowClass => self.as_table_row().build_display_list_table_row(layout_context),
- TableCaptionFlowClass => {
- self.as_table_caption().build_display_list_table_caption(layout_context)
- }
- TableCellFlowClass => {
- self.as_table_cell().build_display_list_table_cell(layout_context)
- }
- TableColGroupFlowClass => {
- // Nothing to do here, as column groups don't render.
- }
- }
- }
-}
-
-impl MutableOwnedFlowUtils for FlowRef {
- /// Set absolute descendants for this flow.
- ///
- /// Set yourself as the Containing Block for all the absolute descendants.
- ///
- /// This is called during flow construction, so nothing else can be accessing the descendant
- /// flows. This is enforced by the fact that we have a mutable `FlowRef`, which only flow
- /// construction is allowed to possess.
- fn set_abs_descendants(&mut self, abs_descendants: AbsDescendants) {
- let this = self.clone();
-
- let block = self.get_mut().as_block();
- block.base.abs_descendants = abs_descendants;
- block.base
- .parallel
- .children_and_absolute_descendant_count
- .fetch_add(block.base.abs_descendants.len() as int, Relaxed);
-
- for descendant_link in block.base.abs_descendants.iter() {
- let base = mut_base(descendant_link);
- base.absolute_cb.set(this.clone());
- }
- }
-}
-
-/// A link to a flow's containing block.
-///
-/// This cannot safely be a `Flow` pointer because this is a pointer *up* the tree, not *down* the
-/// tree. A pointer up the tree is unsafe during layout because it can be used to access a node
-/// with an immutable reference while that same node is being laid out, causing possible iterator
-/// invalidation and use-after-free.
-///
-/// FIXME(pcwalton): I think this would be better with a borrow flag instead of `unsafe`.
-pub struct ContainingBlockLink {
- /// The pointer up to the containing block.
- link: Option<FlowRef>,
-}
-
-impl ContainingBlockLink {
- fn new() -> ContainingBlockLink {
- ContainingBlockLink {
- link: None,
- }
- }
-
- fn set(&mut self, link: FlowRef) {
- self.link = Some(link)
- }
-
- pub unsafe fn get<'a>(&'a mut self) -> &'a mut Option<FlowRef> {
- &mut self.link
- }
-
- #[inline]
- pub fn generated_containing_block_rect(&mut self) -> LogicalRect<Au> {
- match self.link {
- None => fail!("haven't done it"),
- Some(ref mut link) => link.get_mut().generated_containing_block_rect(),
- }
- }
-}
-
diff --git a/src/components/layout/flow_list.rs b/src/components/layout/flow_list.rs
deleted file mode 100644
index 4277326a624..00000000000
--- a/src/components/layout/flow_list.rs
+++ /dev/null
@@ -1,296 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//! A variant of `DList` specialized to store `Flow`s without an extra
-//! indirection.
-
-use flow::{Flow, base, mut_base};
-use flow_ref::FlowRef;
-
-use std::kinds::marker::ContravariantLifetime;
-use std::mem;
-use std::ptr;
-use std::raw;
-
-pub type Link = Option<FlowRef>;
-
-
-#[allow(raw_pointer_deriving)]
-pub struct Rawlink<'a> {
- object: raw::TraitObject,
- marker: ContravariantLifetime<'a>,
-}
-
-/// Doubly-linked list of Flows.
-///
-/// The forward links are strong references.
-/// The backward links are weak references.
-pub struct FlowList {
- length: uint,
- list_head: Link,
- list_tail: Link,
-}
-
-/// Double-ended FlowList iterator
-pub struct FlowListIterator<'a> {
- head: &'a Link,
- nelem: uint,
-}
-
-/// Double-ended mutable FlowList iterator
-pub struct MutFlowListIterator<'a> {
- head: Rawlink<'a>,
- nelem: uint,
-}
-
-impl<'a> Rawlink<'a> {
- /// Like Option::None for Rawlink
- pub fn none() -> Rawlink<'static> {
- Rawlink {
- object: raw::TraitObject {
- vtable: ptr::mut_null(),
- data: ptr::mut_null(),
- },
- marker: ContravariantLifetime,
- }
- }
-
- /// Like Option::Some for Rawlink
- pub fn some(n: &Flow) -> Rawlink {
- unsafe {
- Rawlink {
- object: mem::transmute::<&Flow, raw::TraitObject>(n),
- marker: ContravariantLifetime,
- }
- }
- }
-
- pub unsafe fn resolve_mut(&self) -> Option<&'a mut Flow> {
- if self.object.data.is_null() {
- None
- } else {
- Some(mem::transmute_copy::<raw::TraitObject, &mut Flow>(&self.object))
- }
- }
-}
-
-/// Set the .prev field on `next`, then return `Some(next)`
-unsafe fn link_with_prev(mut next: FlowRef, prev: Option<FlowRef>) -> Link {
- mut_base(next.get_mut()).prev_sibling = prev;
- Some(next)
-}
-
-impl Collection for FlowList {
- /// O(1)
- #[inline]
- fn is_empty(&self) -> bool {
- self.list_head.is_none()
- }
- /// O(1)
- #[inline]
- fn len(&self) -> uint {
- self.length
- }
-}
-
-// This doesn't quite fit the Deque trait because of the need to switch between
-// &Flow and ~Flow.
-impl FlowList {
- /// Provide a reference to the front element, or None if the list is empty
- #[inline]
- pub fn front<'a>(&'a self) -> Option<&'a Flow> {
- self.list_head.as_ref().map(|head| head.get())
- }
-
- /// Provide a mutable reference to the front element, or None if the list is empty
- #[inline]
- pub unsafe fn front_mut<'a>(&'a mut self) -> Option<&'a mut Flow> {
- self.list_head.as_mut().map(|head| head.get_mut())
- }
-
- /// Provide a reference to the back element, or None if the list is empty
- #[inline]
- pub fn back<'a>(&'a self) -> Option<&'a Flow> {
- match self.list_tail {
- None => None,
- Some(ref list_tail) => Some(list_tail.get())
- }
- }
-
- /// Provide a mutable reference to the back element, or None if the list is empty
- #[inline]
- pub unsafe fn back_mut<'a>(&'a mut self) -> Option<&'a mut Flow> {
- // Can't use map() due to error:
- // lifetime of `tail` is too short to guarantee its contents can be safely reborrowed
- match self.list_tail {
- None => None,
- Some(ref mut tail) => {
- let x: &mut Flow = tail.get_mut();
- Some(mem::transmute_copy(&x))
- }
- }
- }
-
- /// Add an element first in the list
- ///
- /// O(1)
- pub fn push_front(&mut self, mut new_head: FlowRef) {
- unsafe {
- match self.list_head {
- None => {
- self.list_tail = Some(new_head.clone());
- self.list_head = link_with_prev(new_head, None);
- }
- Some(ref mut head) => {
- mut_base(new_head.get_mut()).prev_sibling = None;
- mut_base(head.get_mut()).prev_sibling = Some(new_head.clone());
- mem::swap(head, &mut new_head);
- mut_base(head.get_mut()).next_sibling = Some(new_head);
- }
- }
- self.length += 1;
- }
- }
-
- /// Remove the first element and return it, or None if the list is empty
- ///
- /// O(1)
- pub fn pop_front(&mut self) -> Option<FlowRef> {
- self.list_head.take().map(|mut front_node| {
- self.length -= 1;
- unsafe {
- match mut_base(front_node.get_mut()).next_sibling.take() {
- Some(node) => self.list_head = link_with_prev(node, None),
- None => self.list_tail = None,
- }
- }
- front_node
- })
- }
-
- /// Add an element last in the list
- ///
- /// O(1)
- pub fn push_back(&mut self, new_tail: FlowRef) {
- if self.list_tail.is_none() {
- return self.push_front(new_tail);
- }
-
- let old_tail = self.list_tail.clone();
- self.list_tail = Some(new_tail.clone());
- let mut tail = (*old_tail.as_ref().unwrap()).clone();
- let tail_clone = Some(tail.clone());
- unsafe {
- mut_base(tail.get_mut()).next_sibling = link_with_prev(new_tail, tail_clone);
- }
- self.length += 1;
- }
-
- /// Create an empty list
- #[inline]
- pub fn new() -> FlowList {
- FlowList {
- list_head: None,
- list_tail: None,
- length: 0,
- }
- }
-
- /// Provide a forward iterator
- #[inline]
- pub fn iter<'a>(&'a self) -> FlowListIterator<'a> {
- FlowListIterator {
- nelem: self.len(),
- head: &self.list_head,
- }
- }
-
- /// Provide a forward iterator with mutable references
- #[inline]
- pub fn mut_iter<'a>(&'a mut self) -> MutFlowListIterator<'a> {
- let len = self.len();
- let head_raw = match self.list_head {
- Some(ref mut h) => Rawlink::some(h.get()),
- None => Rawlink::none(),
- };
- MutFlowListIterator {
- nelem: len,
- head: head_raw,
- }
- }
-}
-
-#[unsafe_destructor]
-impl Drop for FlowList {
- fn drop(&mut self) {
- // Dissolve the list in backwards direction
- // Just dropping the list_head can lead to stack exhaustion
- // when length is >> 1_000_000
- let mut tail = mem::replace(&mut self.list_tail, None);
- loop {
- let new_tail = match tail {
- None => break,
- Some(ref mut prev) => {
- let prev_base = mut_base(prev.get_mut());
- prev_base.next_sibling.take();
- prev_base.prev_sibling.clone()
- }
- };
- tail = new_tail
- }
- self.length = 0;
- self.list_head = None;
- }
-}
-
-impl<'a> Iterator<&'a Flow> for FlowListIterator<'a> {
- #[inline]
- fn next(&mut self) -> Option<&'a Flow> {
- if self.nelem == 0 {
- return None;
- }
- self.head.as_ref().map(|head| {
- let head_base = base(head.get());
- self.nelem -= 1;
- self.head = &head_base.next_sibling;
- let ret: &Flow = head.get();
- ret
- })
- }
-
- #[inline]
- fn size_hint(&self) -> (uint, Option<uint>) {
- (self.nelem, Some(self.nelem))
- }
-}
-
-impl<'a> Iterator<&'a mut Flow> for MutFlowListIterator<'a> {
- #[inline]
- fn next(&mut self) -> Option<&'a mut Flow> {
- if self.nelem == 0 {
- return None;
- }
- unsafe {
- self.head.resolve_mut().map(|next| {
- self.nelem -= 1;
- self.head = match mut_base(next).next_sibling {
- Some(ref mut node) => {
- let x: &mut Flow = node.get_mut();
- // NOTE: transmute needed here to break the link
- // between x and next so that it is no longer
- // borrowed.
- mem::transmute(Rawlink::some(x))
- }
- None => Rawlink::none(),
- };
- next
- })
- }
- }
-
- #[inline]
- fn size_hint(&self) -> (uint, Option<uint>) {
- (self.nelem, Some(self.nelem))
- }
-}
diff --git a/src/components/layout/flow_ref.rs b/src/components/layout/flow_ref.rs
deleted file mode 100644
index d90d9ac4cc0..00000000000
--- a/src/components/layout/flow_ref.rs
+++ /dev/null
@@ -1,84 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-/// Reference-counted pointers to flows.
-///
-/// Eventually, with dynamically sized types in Rust, much of this code will be superfluous.
-
-use flow::Flow;
-use flow;
-
-use std::mem;
-use std::ptr;
-use std::raw;
-use std::sync::atomics::SeqCst;
-
-#[unsafe_no_drop_flag]
-pub struct FlowRef {
- object: raw::TraitObject,
-}
-
-impl FlowRef {
- pub fn new(mut flow: Box<Flow>) -> FlowRef {
- unsafe {
- let result = {
- let flow_ref: &mut Flow = flow;
- let object = mem::transmute::<&mut Flow, raw::TraitObject>(flow_ref);
- FlowRef { object: object }
- };
- mem::forget(flow);
- result
- }
- }
-
- pub fn get<'a>(&'a self) -> &'a Flow {
- unsafe {
- mem::transmute_copy::<raw::TraitObject, &'a Flow>(&self.object)
- }
- }
-
- pub fn get_mut<'a>(&'a mut self) -> &'a mut Flow {
- unsafe {
- mem::transmute_copy::<raw::TraitObject, &'a mut Flow>(&self.object)
- }
- }
-}
-
-impl Drop for FlowRef {
- fn drop(&mut self) {
- unsafe {
- if self.object.vtable.is_null() {
- return
- }
- if flow::base(self.get()).ref_count().fetch_sub(1, SeqCst) > 1 {
- return
- }
- let flow_ref: FlowRef = mem::replace(self, FlowRef {
- object: raw::TraitObject {
- vtable: ptr::mut_null(),
- data: ptr::mut_null(),
- }
- });
- drop(mem::transmute::<raw::TraitObject, Box<Flow>>(flow_ref.object));
- mem::forget(flow_ref);
- self.object.vtable = ptr::mut_null();
- self.object.data = ptr::mut_null();
- }
- }
-}
-
-impl Clone for FlowRef {
- fn clone(&self) -> FlowRef {
- unsafe {
- drop(flow::base(self.get()).ref_count().fetch_add(1, SeqCst));
- FlowRef {
- object: raw::TraitObject {
- vtable: self.object.vtable,
- data: self.object.data,
- }
- }
- }
- }
-}
-
diff --git a/src/components/layout/fragment.rs b/src/components/layout/fragment.rs
deleted file mode 100644
index 191283603b9..00000000000
--- a/src/components/layout/fragment.rs
+++ /dev/null
@@ -1,1597 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//! The `Fragment` type, which represents the leaves of the layout tree.
-
-#![deny(unsafe_block)]
-
-use css::node_style::StyledNode;
-use construct::FlowConstructor;
-use context::LayoutContext;
-use floats::{ClearBoth, ClearLeft, ClearRight, ClearType};
-use flow::Flow;
-use flow;
-use inline::{InlineFragmentContext, InlineMetrics};
-use layout_debug;
-use model::{Auto, IntrinsicISizes, MaybeAuto, Specified, specified};
-use model;
-use text;
-use util::{OpaqueNodeMethods, ToGfxColor};
-use wrapper::{TLayoutNode, ThreadSafeLayoutNode};
-
-use geom::{Point2D, Rect, Size2D, SideOffsets2D};
-use geom::approxeq::ApproxEq;
-use gfx::color::rgb;
-use gfx::display_list::{BackgroundAndBorderLevel, BaseDisplayItem, BorderDisplayItem};
-use gfx::display_list::{BorderDisplayItemClass, ClipDisplayItem, ClipDisplayItemClass};
-use gfx::display_list::{ContentStackingLevel, DisplayItem, DisplayList, ImageDisplayItem};
-use gfx::display_list::{ImageDisplayItemClass, LineDisplayItem};
-use gfx::display_list::{LineDisplayItemClass, OpaqueNode, PseudoDisplayItemClass};
-use gfx::display_list::{SolidColorDisplayItem, SolidColorDisplayItemClass, StackingLevel};
-use gfx::display_list::{TextDisplayItem, TextDisplayItemClass};
-use gfx::display_list::{Upright, SidewaysLeft, SidewaysRight};
-use gfx::font::FontStyle;
-use gfx::text::glyph::CharIndex;
-use gfx::text::text_run::TextRun;
-use serialize::{Encodable, Encoder};
-use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg, 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::logical_geometry::{LogicalRect, LogicalSize, LogicalMargin};
-use servo_util::range::*;
-use servo_util::namespace;
-use servo_util::smallvec::SmallVec;
-use servo_util::str::is_whitespace;
-use std::fmt;
-use std::from_str::FromStr;
-use std::mem;
-use std::num::Zero;
-use style::{ComputedValues, TElement, TNode, cascade_anonymous, RGBA};
-use style::computed_values::{LengthOrPercentageOrAuto, overflow, LPA_Auto, background_attachment};
-use style::computed_values::{background_repeat, border_style, clear, position, text_align};
-use style::computed_values::{text_decoration, vertical_align, visibility, white_space};
-use sync::{Arc, Mutex};
-use url::Url;
-
-/// Fragments (`struct Fragment`) are the leaves of the layout tree. They cannot position themselves. In
-/// general, fragments do not have a simple correspondence with CSS fragments in the specification:
-///
-/// * Several fragments may correspond to the same CSS box or DOM node. For example, a CSS text box
-/// broken across two lines is represented by two fragments.
-///
-/// * Some CSS fragments are not created at all, such as some anonymous block fragments induced by inline
-/// fragments with block-level sibling fragments. In that case, Servo uses an `InlineFlow` with
-/// `BlockFlow` siblings; the `InlineFlow` is block-level, but not a block container. It is
-/// positioned as if it were a block fragment, but its children are positioned according to inline
-/// flow.
-///
-/// A `GenericFragment` 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. Different
-/// types of fragments may also contain custom data; for example, text fragments contain text.
-///
-/// FIXME(#2260, pcwalton): This can be slimmed down some.
-#[deriving(Clone)]
-pub struct Fragment {
- /// An opaque reference to the DOM node that this `Fragment` originates from.
- pub node: OpaqueNode,
-
- /// 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.
- pub border_box: LogicalRect<Au>,
-
- /// The sum of border and padding; i.e. the distance from the edge of the border box to the
- /// content edge of the fragment.
- pub border_padding: LogicalMargin<Au>,
-
- /// The margin of the content box.
- pub margin: LogicalMargin<Au>,
-
- /// Info specific to the kind of fragment. Keep this enum small.
- pub specific: SpecificFragmentInfo,
-
- /// New-line chracter(\n)'s positions(relative, not absolute)
- ///
- /// FIXME(#2260, pcwalton): This is very inefficient; remove.
- pub new_line_pos: Vec<CharIndex>,
-
- /// Holds the style context information for fragments
- /// that are part of an inline formatting context.
- pub inline_context: Option<InlineFragmentContext>,
-
- /// A debug ID that is consistent for the life of
- /// this fragment (via transform etc).
- pub debug_id: uint,
-}
-
-impl<E, S: Encoder<E>> Encodable<S, E> for Fragment {
- fn encode(&self, e: &mut S) -> Result<(), E> {
- e.emit_struct("fragment", 0, |e| {
- try!(e.emit_struct_field("id", 0, |e| self.debug_id().encode(e)))
- try!(e.emit_struct_field("border_box", 1, |e| self.border_box.encode(e)))
- e.emit_struct_field("margin", 2, |e| self.margin.encode(e))
- })
- }
-}
-
-/// Info specific to the kind of fragment. Keep this enum small.
-#[deriving(Clone)]
-pub enum SpecificFragmentInfo {
- GenericFragment,
- ImageFragment(ImageFragmentInfo),
- IframeFragment(IframeFragmentInfo),
- ScannedTextFragment(ScannedTextFragmentInfo),
- TableFragment,
- TableCellFragment,
- TableColumnFragment(TableColumnFragmentInfo),
- TableRowFragment,
- TableWrapperFragment,
- UnscannedTextFragment(UnscannedTextFragmentInfo),
-}
-
-/// A fragment that represents a replaced content image and its accompanying borders, shadows, etc.
-#[deriving(Clone)]
-pub struct ImageFragmentInfo {
- /// The image held within this fragment.
- pub image: ImageHolder,
- pub computed_inline_size: Option<Au>,
- pub computed_block_size: Option<Au>,
- pub dom_inline_size: Option<Au>,
- pub dom_block_size: Option<Au>,
- pub writing_mode_is_vertical: bool,
-}
-
-impl ImageFragmentInfo {
- /// Creates a new image fragment from the given URL and local image cache.
- ///
- /// FIXME(pcwalton): The fact that image fragments store the cache in the fragment makes little sense to
- /// me.
- pub fn new(node: &ThreadSafeLayoutNode,
- image_url: Url,
- local_image_cache: Arc<Mutex<LocalImageCache>>)
- -> ImageFragmentInfo {
- fn convert_length(node: &ThreadSafeLayoutNode, name: &str) -> Option<Au> {
- let element = node.as_element();
- element.get_attr(&namespace::Null, name).and_then(|string| {
- let n: Option<int> = FromStr::from_str(string);
- n
- }).and_then(|pixels| Some(Au::from_px(pixels)))
- }
-
- let is_vertical = node.style().writing_mode.is_vertical();
- let dom_width = convert_length(node, "width");
- let dom_height = convert_length(node, "height");
- ImageFragmentInfo {
- image: ImageHolder::new(image_url, local_image_cache),
- computed_inline_size: None,
- computed_block_size: None,
- dom_inline_size: if is_vertical { dom_height } else { dom_width },
- dom_block_size: if is_vertical { dom_width } else { dom_height },
- writing_mode_is_vertical: is_vertical,
- }
- }
-
- /// Returns the calculated inline-size of the image, accounting for the inline-size attribute.
- pub fn computed_inline_size(&self) -> Au {
- self.computed_inline_size.expect("image inline_size is not computed yet!")
- }
-
- /// Returns the calculated block-size of the image, accounting for the block-size attribute.
- pub fn computed_block_size(&self) -> Au {
- self.computed_block_size.expect("image block_size is not computed yet!")
- }
-
- /// Returns the original inline-size of the image.
- pub fn image_inline_size(&mut self) -> Au {
- let size = self.image.get_size().unwrap_or(Size2D::zero());
- Au::from_px(if self.writing_mode_is_vertical { size.height } else { size.width })
- }
-
- /// Returns the original block-size of the image.
- pub fn image_block_size(&mut self) -> Au {
- let size = self.image.get_size().unwrap_or(Size2D::zero());
- Au::from_px(if self.writing_mode_is_vertical { size.width } else { size.height })
- }
-
- // Return used value for inline-size or block-size.
- //
- // `dom_length`: inline-size or block-size as specified in the `img` tag.
- // `style_length`: inline-size as given in the CSS
- pub fn style_length(style_length: LengthOrPercentageOrAuto,
- dom_length: Option<Au>,
- container_inline_size: Au) -> MaybeAuto {
- match (MaybeAuto::from_style(style_length,container_inline_size),dom_length) {
- (Specified(length),_) => {
- Specified(length)
- },
- (Auto,Some(length)) => {
- Specified(length)
- },
- (Auto,None) => {
- Auto
- }
- }
- }
-}
-
-/// A fragment that represents an inline frame (iframe). This stores the pipeline ID so that the size
-/// of this iframe can be communicated via the constellation to the iframe's own layout task.
-#[deriving(Clone)]
-pub struct IframeFragmentInfo {
- /// The pipeline ID of this iframe.
- pub pipeline_id: PipelineId,
- /// The subpage ID of this iframe.
- pub subpage_id: SubpageId,
-}
-
-impl IframeFragmentInfo {
- /// Creates the information specific to an iframe fragment.
- pub fn new(node: &ThreadSafeLayoutNode) -> IframeFragmentInfo {
- let (pipeline_id, subpage_id) = node.iframe_pipeline_and_subpage_ids();
- IframeFragmentInfo {
- pipeline_id: pipeline_id,
- subpage_id: subpage_id,
- }
- }
-}
-
-/// A scanned text fragment represents a single run of text with a distinct style. A `TextFragment`
-/// may be split into two or more fragments across line breaks. Several `TextFragment`s may
-/// correspond to a single DOM text node. Split text fragments are implemented by referring to
-/// subsets of a single `TextRun` object.
-#[deriving(Clone)]
-pub struct ScannedTextFragmentInfo {
- /// The text run that this represents.
- pub run: Arc<Box<TextRun>>,
-
- /// The range within the above text run that this represents.
- pub range: Range<CharIndex>,
-}
-
-impl ScannedTextFragmentInfo {
- /// Creates the information specific to a scanned text fragment from a range and a text run.
- pub fn new(run: Arc<Box<TextRun>>, range: Range<CharIndex>) -> ScannedTextFragmentInfo {
- ScannedTextFragmentInfo {
- run: run,
- range: range,
- }
- }
-}
-
-#[deriving(Show)]
-pub struct SplitInfo {
- // TODO(bjz): this should only need to be a single character index, but both values are
- // currently needed for splitting in the `inline::try_append_*` functions.
- pub range: Range<CharIndex>,
- pub inline_size: Au,
-}
-
-impl SplitInfo {
- fn new(range: Range<CharIndex>, info: &ScannedTextFragmentInfo) -> SplitInfo {
- SplitInfo {
- range: range,
- inline_size: info.run.advance_for_range(&range),
- }
- }
-}
-
-/// Data for an unscanned text fragment. Unscanned text fragments are the results of flow construction that
-/// have not yet had their inline-size determined.
-#[deriving(Clone)]
-pub struct UnscannedTextFragmentInfo {
- /// The text inside the fragment.
- pub text: String,
-}
-
-impl UnscannedTextFragmentInfo {
- /// Creates a new instance of `UnscannedTextFragmentInfo` from the given DOM node.
- pub fn new(node: &ThreadSafeLayoutNode) -> UnscannedTextFragmentInfo {
- // FIXME(pcwalton): Don't copy text; atomically reference count it instead.
- UnscannedTextFragmentInfo {
- text: node.text(),
- }
- }
-
- /// Creates a new instance of `UnscannedTextFragmentInfo` from the given text.
- #[inline]
- pub fn from_text(text: String) -> UnscannedTextFragmentInfo {
- UnscannedTextFragmentInfo {
- text: text,
- }
- }
-}
-
-/// A fragment that represents a table column.
-#[deriving(Clone)]
-pub struct TableColumnFragmentInfo {
- /// the number of columns a <col> element should span
- pub span: Option<int>,
-}
-
-impl TableColumnFragmentInfo {
- /// Create the information specific to an table column fragment.
- pub fn new(node: &ThreadSafeLayoutNode) -> TableColumnFragmentInfo {
- let span = {
- let element = node.as_element();
- element.get_attr(&namespace::Null, "span").and_then(|string| {
- let n: Option<int> = FromStr::from_str(string);
- n
- })
- };
- TableColumnFragmentInfo {
- span: span,
- }
- }
-}
-
-impl Fragment {
- /// Constructs a new `Fragment` instance for the given node.
- ///
- /// Arguments:
- ///
- /// * `constructor`: The flow constructor.
- ///
- /// * `node`: The node to create a fragment for.
- pub fn new(constructor: &mut FlowConstructor, node: &ThreadSafeLayoutNode) -> Fragment {
- let style = node.style().clone();
- let writing_mode = style.writing_mode;
- Fragment {
- node: OpaqueNodeMethods::from_thread_safe_layout_node(node),
- style: style,
- 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),
- new_line_pos: vec!(),
- inline_context: None,
- debug_id: layout_debug::generate_unique_debug_id(),
- }
- }
-
- /// Constructs a new `Fragment` instance from a specific info.
- pub fn new_from_specific_info(node: &ThreadSafeLayoutNode, specific: SpecificFragmentInfo) -> Fragment {
- let style = node.style().clone();
- let writing_mode = style.writing_mode;
- Fragment {
- node: OpaqueNodeMethods::from_thread_safe_layout_node(node),
- style: style,
- border_box: LogicalRect::zero(writing_mode),
- border_padding: LogicalMargin::zero(writing_mode),
- margin: LogicalMargin::zero(writing_mode),
- specific: specific,
- new_line_pos: vec!(),
- 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, specific: SpecificFragmentInfo) -> Fragment {
- // CSS 2.1 § 17.2.1 This is for non-inherited properties on anonymous table fragments
- // example:
- //
- // <div style="display: table">
- // Foo
- // </div>
- //
- // Anonymous table fragments, TableRowFragment and TableCellFragment, are generated around `Foo`, but it shouldn't inherit the border.
-
- 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),
- border_box: LogicalRect::zero(writing_mode),
- border_padding: LogicalMargin::zero(writing_mode),
- margin: LogicalMargin::zero(writing_mode),
- specific: specific,
- new_line_pos: vec!(),
- inline_context: None,
- debug_id: layout_debug::generate_unique_debug_id(),
- }
- }
-
- /// Constructs a new `Fragment` instance from an opaque node.
- pub fn from_opaque_node_and_style(node: OpaqueNode,
- style: Arc<ComputedValues>,
- specific: SpecificFragmentInfo)
- -> Fragment {
- let writing_mode = style.writing_mode;
- Fragment {
- node: node,
- style: style,
- border_box: LogicalRect::zero(writing_mode),
- border_padding: LogicalMargin::zero(writing_mode),
- margin: LogicalMargin::zero(writing_mode),
- specific: specific,
- new_line_pos: vec!(),
- inline_context: None,
- debug_id: layout_debug::generate_unique_debug_id(),
- }
- }
-
- /// Returns a debug ID of this fragment. This ID should not be considered stable across multiple
- /// layouts or fragment manipulations.
- pub fn debug_id(&self) -> uint {
- self.debug_id
- }
-
- /// Transforms this fragment into another fragment of the given type, with the given size, preserving all
- /// the other data.
- pub fn transform(&self, size: LogicalSize<Au>, specific: SpecificFragmentInfo) -> Fragment {
- Fragment {
- node: self.node,
- style: self.style.clone(),
- border_box: LogicalRect::from_point_size(
- self.style.writing_mode, self.border_box.start, size),
- border_padding: self.border_padding,
- margin: self.margin,
- specific: specific,
- new_line_pos: self.new_line_pos.clone(),
- inline_context: self.inline_context.clone(),
- debug_id: self.debug_id,
- }
- }
-
- /// Adds a style to the inline context for this fragment. If the inline
- /// context doesn't exist yet, it will be created.
- pub fn add_inline_context_style(&mut self, style: Arc<ComputedValues>) {
- if self.inline_context.is_none() {
- self.inline_context = Some(InlineFragmentContext::new());
- }
- self.inline_context.get_mut_ref().styles.push(style.clone());
- }
-
- /// Uses the style only to estimate the intrinsic inline-sizes. These may be modified for text or
- /// replaced elements.
- fn style_specified_intrinsic_inline_size(&self) -> IntrinsicISizes {
- let (use_margins, use_padding) = match self.specific {
- GenericFragment | IframeFragment(_) | ImageFragment(_) => (true, true),
- TableFragment | TableCellFragment => (false, true),
- TableWrapperFragment => (true, false),
- TableRowFragment => (false, false),
- ScannedTextFragment(_) | TableColumnFragment(_) | UnscannedTextFragment(_) => {
- // Styles are irrelevant for these kinds of fragments.
- return IntrinsicISizes::new()
- }
- };
-
- let style = self.style();
- let inline_size = MaybeAuto::from_style(style.content_inline_size(), Au::new(0)).specified_or_zero();
-
- let margin = style.logical_margin();
- let (margin_inline_start, margin_inline_end) = if use_margins {
- (MaybeAuto::from_style(margin.inline_start, Au(0)).specified_or_zero(),
- MaybeAuto::from_style(margin.inline_end, Au(0)).specified_or_zero())
- } else {
- (Au(0), Au(0))
- };
-
- let padding = style.logical_padding();
- let (padding_inline_start, padding_inline_end) = if use_padding {
- (model::specified(padding.inline_start, Au(0)),
- model::specified(padding.inline_end, Au(0)))
- } else {
- (Au(0), Au(0))
- };
-
- // FIXME(#2261, pcwalton): This won't work well for inlines: is this OK?
- let border = self.border_width();
- let surround_inline_size = margin_inline_start + margin_inline_end + padding_inline_start + padding_inline_end +
- border.inline_start_end();
-
- IntrinsicISizes {
- minimum_inline_size: inline_size,
- preferred_inline_size: inline_size,
- surround_inline_size: surround_inline_size,
- }
- }
-
- pub fn calculate_line_height(&self, layout_context: &LayoutContext) -> Au {
- let font_style = text::computed_style_to_font_style(&*self.style);
- let font_metrics = text::font_metrics_for_style(layout_context.font_context(), &font_style);
- text::line_height_from_style(&*self.style, &font_metrics)
- }
-
- /// Returns the sum of the inline-sizes of all the borders of this fragment. This is private because
- /// it should only be called during intrinsic inline-size computation or computation of
- /// `border_padding`. Other consumers of this information should simply consult that field.
- #[inline]
- fn border_width(&self) -> LogicalMargin<Au> {
- match self.inline_context {
- None => self.style().logical_border_width(),
- Some(ref inline_fragment_context) => {
- let zero = LogicalMargin::zero(self.style.writing_mode);
- inline_fragment_context.styles.iter().fold(zero, |acc, style| acc + style.logical_border_width())
- }
- }
- }
-
- /// Computes the border, padding, and vertical margins from the containing block inline-size and the
- /// style. After this call, the `border_padding` and the vertical direction of the `margin`
- /// field will be correct.
- pub fn compute_border_padding_margins(&mut self,
- containing_block_inline_size: Au) {
- // Compute vertical margins. Note that this value will be ignored by layout if the style
- // specifies `auto`.
- match self.specific {
- TableFragment | TableCellFragment | TableRowFragment | TableColumnFragment(_) => {
- self.margin.block_start = Au(0);
- self.margin.block_end = Au(0)
- }
- _ => {
- // NB: Percentages are relative to containing block inline-size (not block-size) per CSS 2.1.
- let margin = self.style().logical_margin();
- self.margin.block_start = MaybeAuto::from_style(margin.block_start, containing_block_inline_size)
- .specified_or_zero();
- self.margin.block_end = MaybeAuto::from_style(margin.block_end, containing_block_inline_size)
- .specified_or_zero()
- }
- }
-
- // Compute border.
- let border = self.border_width();
-
- // Compute padding.
- let padding = match self.specific {
- TableColumnFragment(_) | TableRowFragment |
- TableWrapperFragment => LogicalMargin::zero(self.style.writing_mode),
- _ => {
- match self.inline_context {
- None => model::padding_from_style(self.style(), containing_block_inline_size),
- Some(ref inline_fragment_context) => {
- let zero = LogicalMargin::zero(self.style.writing_mode);
- inline_fragment_context.styles.iter()
- .fold(zero, |acc, style| acc + model::padding_from_style(&**style, Au(0)))
- }
- }
- }
- };
-
- self.border_padding = border + padding
- }
-
- // Return offset from original position because of `position: relative`.
- pub fn relative_position(&self,
- containing_block_size: &LogicalSize<Au>)
- -> LogicalSize<Au> {
- fn from_style(style: &ComputedValues, container_size: &LogicalSize<Au>)
- -> LogicalSize<Au> {
- let offsets = style.logical_position();
- let offset_i = if offsets.inline_start != LPA_Auto {
- MaybeAuto::from_style(offsets.inline_start, container_size.inline).specified_or_zero()
- } else {
- -MaybeAuto::from_style(offsets.inline_end, container_size.inline).specified_or_zero()
- };
- let offset_b = if offsets.block_start != LPA_Auto {
- MaybeAuto::from_style(offsets.block_start, container_size.inline).specified_or_zero()
- } else {
- -MaybeAuto::from_style(offsets.block_end, container_size.inline).specified_or_zero()
- };
- LogicalSize::new(style.writing_mode, offset_i, offset_b)
- }
-
- // Go over the ancestor fragments and add all relative offsets (if any).
- let mut rel_pos = LogicalSize::zero(self.style.writing_mode);
- match self.inline_context {
- None => {
- if self.style().get_box().position == position::relative {
- rel_pos = rel_pos + from_style(self.style(), containing_block_size);
- }
- }
- Some(ref inline_fragment_context) => {
- for style in inline_fragment_context.styles.iter() {
- if style.get_box().position == position::relative {
- rel_pos = rel_pos + from_style(&**style, containing_block_size);
- }
- }
- },
- }
- rel_pos
- }
-
- /// Always inline for SCCP.
- ///
- /// FIXME(pcwalton): Just replace with the clear type from the style module for speed?
- #[inline(always)]
- pub fn clear(&self) -> Option<ClearType> {
- let style = self.style();
- match style.get_box().clear {
- clear::none => None,
- clear::left => Some(ClearLeft),
- clear::right => Some(ClearRight),
- clear::both => Some(ClearBoth),
- }
- }
-
- /// Converts this fragment's computed style to a font style used for rendering.
- pub fn font_style(&self) -> FontStyle {
- text::computed_style_to_font_style(self.style())
- }
-
- #[inline(always)]
- pub fn style<'a>(&'a self) -> &'a ComputedValues {
- &*self.style
- }
-
- /// Returns the text alignment of the computed style of the nearest ancestor-or-self `Element`
- /// node.
- pub fn text_align(&self) -> text_align::T {
- self.style().get_inheritedtext().text_align
- }
-
- pub fn vertical_align(&self) -> vertical_align::T {
- self.style().get_box().vertical_align
- }
-
- pub fn white_space(&self) -> white_space::T {
- self.style().get_inheritedtext().white_space
- }
-
- /// Returns the text decoration of this fragment, 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 {
- self.style().get_text().text_decoration
- }
-
- /// Returns the inline-start offset from margin edge to content edge.
- ///
- /// FIXME(#2262, pcwalton): I think this method is pretty bogus, because it won't work for
- /// inlines.
- pub fn inline_start_offset(&self) -> Au {
- match self.specific {
- TableWrapperFragment => self.margin.inline_start,
- TableFragment | TableCellFragment | TableRowFragment => self.border_padding.inline_start,
- TableColumnFragment(_) => Au(0),
- _ => self.margin.inline_start + self.border_padding.inline_start,
- }
- }
-
- /// Returns true if this element can be split. This is true for text fragments.
- pub fn can_split(&self) -> bool {
- match self.specific {
- ScannedTextFragment(..) => true,
- _ => false,
- }
- }
-
- /// Adds the display items necessary to paint the background of this fragment to the display
- /// list if necessary.
- pub fn build_display_list_for_background_if_applicable(&self,
- style: &ComputedValues,
- list: &mut DisplayList,
- layout_context: &LayoutContext,
- level: StackingLevel,
- absolute_bounds: &Rect<Au>) {
- // FIXME: This causes a lot of background colors to be displayed when they are clearly not
- // needed. We could use display list optimization to clean this up, but it still seems
- // inefficient. What we really want is something like "nearest ancestor element that
- // doesn't have a fragment".
- let background_color = style.resolve_color(style.get_background().background_color);
- if !background_color.alpha.approx_eq(&0.0) {
- let display_item = box SolidColorDisplayItem {
- base: BaseDisplayItem::new(*absolute_bounds, self.node, level),
- color: background_color.to_gfx_color(),
- };
-
- list.push(SolidColorDisplayItemClass(display_item))
- }
-
- // The background image is painted on top of the background color.
- // Implements background image, per spec:
- // http://www.w3.org/TR/CSS21/colors.html#background
- let background = style.get_background();
- let image_url = match background.background_image {
- None => return,
- Some(ref image_url) => image_url,
- };
-
- let mut holder = ImageHolder::new(image_url.clone(), layout_context.shared.image_cache.clone());
- let image = match holder.get_image() {
- None => {
- // No image data at all? Do nothing.
- //
- // TODO: Add some kind of placeholder background image.
- debug!("(building display list) no background image :(");
- return
- }
- Some(image) => image,
- };
- debug!("(building display list) building background image");
-
- // Adjust bounds for `background-position` and `background-attachment`.
- let mut bounds = *absolute_bounds;
- let horizontal_position = model::specified(background.background_position.horizontal,
- bounds.size.width);
- let vertical_position = model::specified(background.background_position.vertical,
- bounds.size.height);
-
- let clip_display_item;
- match background.background_attachment {
- background_attachment::scroll => {
- clip_display_item = None;
- bounds.origin.x = bounds.origin.x + horizontal_position;
- bounds.origin.y = bounds.origin.y + vertical_position;
- bounds.size.width = bounds.size.width - horizontal_position;
- bounds.size.height = bounds.size.height - vertical_position;
- }
- background_attachment::fixed => {
- clip_display_item = Some(box ClipDisplayItem {
- base: BaseDisplayItem::new(bounds, self.node, level),
- children: DisplayList::new(),
- });
-
- bounds = Rect {
- origin: Point2D(horizontal_position, vertical_position),
- size: Size2D(bounds.origin.x + bounds.size.width,
- bounds.origin.y + bounds.size.height),
- }
- }
- }
-
- // Adjust sizes for `background-repeat`.
- match background.background_repeat {
- background_repeat::no_repeat => {
- bounds.size.width = Au::from_px(image.width as int);
- bounds.size.height = Au::from_px(image.height as int)
- }
- background_repeat::repeat_x => {
- bounds.size.height = Au::from_px(image.height as int)
- }
- background_repeat::repeat_y => {
- bounds.size.width = Au::from_px(image.width as int)
- }
- background_repeat::repeat => {}
- };
-
- // Create the image display item.
- let image_display_item = ImageDisplayItemClass(box ImageDisplayItem {
- base: BaseDisplayItem::new(bounds, self.node, level),
- image: image.clone(),
- stretch_size: Size2D(Au::from_px(image.width as int),
- Au::from_px(image.height as int)),
- });
-
- match clip_display_item {
- None => list.push(image_display_item),
- Some(mut clip_display_item) => {
- clip_display_item.children.push(image_display_item);
- list.push(ClipDisplayItemClass(clip_display_item))
- }
- }
- }
-
- /// Adds the display items necessary to paint the borders of this fragment to a display list if
- /// necessary.
- pub fn build_display_list_for_borders_if_applicable(&self,
- style: &ComputedValues,
- list: &mut DisplayList,
- abs_bounds: &Rect<Au>,
- level: StackingLevel) {
- let border = style.logical_border_width();
- if border.is_zero() {
- return
- }
-
- let top_color = style.resolve_color(style.get_border().border_top_color);
- let right_color = style.resolve_color(style.get_border().border_right_color);
- let bottom_color = style.resolve_color(style.get_border().border_bottom_color);
- let left_color = style.resolve_color(style.get_border().border_left_color);
-
- // Append the border to the display list.
- let border_display_item = box BorderDisplayItem {
- base: BaseDisplayItem::new(*abs_bounds, self.node, level),
- border: border.to_physical(style.writing_mode),
- color: SideOffsets2D::new(top_color.to_gfx_color(),
- right_color.to_gfx_color(),
- bottom_color.to_gfx_color(),
- left_color.to_gfx_color()),
- style: SideOffsets2D::new(style.get_border().border_top_style,
- style.get_border().border_right_style,
- style.get_border().border_bottom_style,
- style.get_border().border_left_style)
- };
-
- list.push(BorderDisplayItemClass(border_display_item))
- }
-
- fn build_debug_borders_around_text_fragments(&self,
- display_list: &mut DisplayList,
- flow_origin: Point2D<Au>,
- text_fragment: &ScannedTextFragmentInfo) {
- // 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);
-
- // Compute the text fragment bounds and draw a border surrounding them.
- let border_display_item = box BorderDisplayItem {
- base: BaseDisplayItem::new(absolute_fragment_bounds, self.node, ContentStackingLevel),
- border: SideOffsets2D::new_all_same(Au::from_px(1)),
- color: SideOffsets2D::new_all_same(rgb(0, 0, 200)),
- style: SideOffsets2D::new_all_same(border_style::solid)
- };
- display_list.push(BorderDisplayItemClass(border_display_item));
-
- // 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;
- baseline.size.block = Au(0);
- let mut baseline = baseline.to_physical(self.style.writing_mode, container_size);
- baseline.origin = baseline.origin + flow_origin;
-
- let line_display_item = box LineDisplayItem {
- base: BaseDisplayItem::new(baseline, self.node, ContentStackingLevel),
- color: rgb(0, 200, 0),
- style: border_style::dashed,
- };
- display_list.push(LineDisplayItemClass(line_display_item));
- }
-
- fn build_debug_borders_around_fragment(&self,
- display_list: &mut DisplayList,
- flow_origin: Point2D<Au>) {
- // 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.
- let border_display_item = box BorderDisplayItem {
- base: BaseDisplayItem::new(absolute_fragment_bounds, self.node, ContentStackingLevel),
- border: SideOffsets2D::new_all_same(Au::from_px(1)),
- color: SideOffsets2D::new_all_same(rgb(0, 0, 200)),
- style: SideOffsets2D::new_all_same(border_style::solid)
- };
- display_list.push(BorderDisplayItemClass(border_display_item))
- }
-
- /// Adds the display items for this fragment to the given stacking context.
- ///
- /// Arguments:
- ///
- /// * `display_list`: The unflattened display list to add display items to.
- /// * `layout_context`: The layout context.
- /// * `dirty`: The dirty rectangle in the coordinate system of the owning flow.
- /// * `flow_origin`: Position of the origin of the owning flow wrt the display list root flow.
- pub fn build_display_list(&self,
- display_list: &mut DisplayList,
- layout_context: &LayoutContext,
- flow_origin: Point2D<Au>,
- background_and_border_level: BackgroundAndBorderLevel)
- -> ChildDisplayListAccumulator {
- // FIXME(#2795): Get the real container size
- let container_size = Size2D::zero();
- let rect_to_absolute = |logical_rect: LogicalRect<Au>| {
- let physical_rect = logical_rect.to_physical(self.style.writing_mode, container_size);
- Rect(physical_rect.origin + flow_origin, physical_rect.size)
- };
- // Fragment position wrt to the owning flow.
- let absolute_fragment_bounds = rect_to_absolute(self.border_box);
- debug!("Fragment::build_display_list at rel={}, abs={}: {}",
- self.border_box,
- absolute_fragment_bounds,
- self);
- debug!("Fragment::build_display_list: dirty={}, flow_origin={}",
- layout_context.shared.dirty,
- flow_origin);
-
- let mut accumulator = ChildDisplayListAccumulator::new(self.style(),
- absolute_fragment_bounds,
- self.node,
- ContentStackingLevel);
- if self.style().get_inheritedbox().visibility != visibility::visible {
- return accumulator
- }
-
- if !absolute_fragment_bounds.intersects(&layout_context.shared.dirty) {
- debug!("Fragment::build_display_list: Did not intersect...");
- return accumulator
- }
-
- debug!("Fragment::build_display_list: intersected. Adding display item...");
-
- {
- let level =
- StackingLevel::from_background_and_border_level(background_and_border_level);
-
- // Add a pseudo-display item for content box queries. This is a very bogus thing to do.
- let base_display_item = box BaseDisplayItem::new(absolute_fragment_bounds, self.node, level);
- display_list.push(PseudoDisplayItemClass(base_display_item));
-
- // Add the background to the list, if applicable.
- match self.inline_context {
- Some(ref 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);
- }
- }
- None => {
- self.build_display_list_for_background_if_applicable(&*self.style,
- display_list,
- layout_context,
- level,
- &absolute_fragment_bounds);
- }
- }
-
- // Add a border, if applicable.
- //
- // TODO: Outlines.
- match self.inline_context {
- Some(ref 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);
- }
- }
- None => {
- self.build_display_list_for_borders_if_applicable(&*self.style,
- display_list,
- &absolute_fragment_bounds,
- level);
- }
- }
- }
-
- let content_box = self.content_box();
- let absolute_content_box = rect_to_absolute(content_box);
-
- // Add a clip, if applicable.
- match self.specific {
- UnscannedTextFragment(_) => fail!("Shouldn't see unscanned fragments here."),
- TableColumnFragment(_) => fail!("Shouldn't see table column fragments here."),
- ScannedTextFragment(ref text_fragment) => {
- // Create the text display item.
- let orientation = if self.style.writing_mode.is_vertical() {
- if self.style.writing_mode.is_sideways_left() {
- SidewaysLeft
- } else {
- SidewaysRight
- }
- } else {
- Upright
- };
-
- let metrics = &text_fragment.run.font_metrics;
- let baseline_origin ={
- let mut tmp = content_box.start;
- tmp.b = tmp.b + metrics.ascent;
- tmp.to_physical(self.style.writing_mode, container_size) + flow_origin
- };
-
- let text_display_item = box TextDisplayItem {
- base: BaseDisplayItem::new(
- absolute_content_box, self.node, ContentStackingLevel),
- text_run: text_fragment.run.clone(),
- range: text_fragment.range,
- text_color: self.style().get_color().color.to_gfx_color(),
- orientation: orientation,
- baseline_origin: baseline_origin,
- };
- accumulator.push(display_list, TextDisplayItemClass(text_display_item));
-
-
- // Create display items for text decoration
- {
- let line = |maybe_color: Option<RGBA>, rect: || -> LogicalRect<Au>| {
- match maybe_color {
- None => {},
- Some(color) => {
- accumulator.push(display_list, SolidColorDisplayItemClass(
- box SolidColorDisplayItem {
- base: BaseDisplayItem::new(
- rect_to_absolute(rect()),
- self.node, ContentStackingLevel),
- color: color.to_gfx_color(),
- }
- ));
- }
- }
- };
-
- let text_decorations =
- self.style().get_inheritedtext()._servo_text_decorations_in_effect;
- line(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;
- rect
- });
-
- line(text_decorations.overline, || {
- let mut rect = content_box.clone();
- rect.size.block = metrics.underline_size;
- rect
- });
-
- line(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;
- rect
- });
- }
-
- // Draw debug frames for text bounds.
- //
- // FIXME(#2263, pcwalton): This is a bit of an abuse of the logging infrastructure.
- // We should have a real `SERVO_DEBUG` system.
- debug!("{:?}", self.build_debug_borders_around_text_fragments(display_list,
- flow_origin,
- text_fragment))
- },
- GenericFragment | IframeFragment(..) | TableFragment | TableCellFragment | TableRowFragment |
- TableWrapperFragment => {
- // FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We
- // should have a real `SERVO_DEBUG` system.
- debug!("{:?}", self.build_debug_borders_around_fragment(display_list, flow_origin))
- },
- ImageFragment(_) => {
- match self.specific {
- ImageFragment(ref image_fragment) => {
- let image_ref = &image_fragment.image;
- match image_ref.get_image_if_present() {
- Some(image) => {
- debug!("(building display list) building image fragment");
-
- // Place the image into the display list.
- let image_display_item = box ImageDisplayItem {
- base: BaseDisplayItem::new(absolute_content_box,
- self.node,
- ContentStackingLevel),
- image: image.clone(),
- stretch_size: absolute_content_box.size,
- };
- accumulator.push(display_list,
- ImageDisplayItemClass(image_display_item))
- }
- None => {
- // No image data at all? Do nothing.
- //
- // TODO: Add some kind of placeholder image.
- debug!("(building display list) no image :(");
- }
- }
- }
- _ => fail!("shouldn't get here"),
- }
-
- // FIXME(pcwalton): This is a bit of an abuse of the logging
- // infrastructure. We should have a real `SERVO_DEBUG` system.
- debug!("{:?}", self.build_debug_borders_around_fragment(display_list, flow_origin))
- }
- }
-
- // If this is an iframe, then send its position and size up to the constellation.
- //
- // FIXME(pcwalton): Doing this during display list construction seems potentially
- // problematic if iframes are outside the area we're computing the display list for, since
- // they won't be able to reflow at all until the user scrolls to them. Perhaps we should
- // separate this into two parts: first we should send the size only to the constellation
- // once that's computed during assign-block-sizes, and second we should should send the origin
- // to the constellation here during display list construction. This should work because
- // layout for the iframe only needs to know size, and origin is only relevant if the
- // iframe is actually going to be displayed.
- match self.specific {
- IframeFragment(ref iframe_fragment) => {
- self.finalize_position_and_size_of_iframe(iframe_fragment, flow_origin, layout_context)
- }
- _ => {}
- }
-
- accumulator
- }
-
- /// Returns the intrinsic inline-sizes of this fragment.
- pub fn intrinsic_inline_sizes(&mut self)
- -> IntrinsicISizes {
- let mut result = self.style_specified_intrinsic_inline_size();
-
- match self.specific {
- GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment | TableColumnFragment(_) | TableRowFragment |
- TableWrapperFragment => {}
- ImageFragment(ref mut image_fragment_info) => {
- let image_inline_size = image_fragment_info.image_inline_size();
- result.minimum_inline_size = geometry::max(result.minimum_inline_size, image_inline_size);
- result.preferred_inline_size = geometry::max(result.preferred_inline_size, image_inline_size);
- }
- ScannedTextFragment(ref text_fragment_info) => {
- let range = &text_fragment_info.range;
- let min_line_inline_size = text_fragment_info.run.min_width_for_range(range);
-
- // See http://dev.w3.org/csswg/css-sizing/#max-content-inline-size.
- // TODO: Account for soft wrap opportunities.
- let max_line_inline_size = text_fragment_info.run.metrics_for_range(range).advance_width;
-
- result.minimum_inline_size = geometry::max(result.minimum_inline_size, min_line_inline_size);
- result.preferred_inline_size = geometry::max(result.preferred_inline_size, max_line_inline_size);
- }
- UnscannedTextFragment(..) => fail!("Unscanned text fragments should have been scanned by now!"),
- }
-
- // Take borders and padding for parent inline fragments into account, if necessary.
- match self.inline_context {
- None => {}
- Some(ref context) => {
- for style in context.styles.iter() {
- let border_width = style.logical_border_width().inline_start_end();
- let padding_inline_size = model::padding_from_style(&**style, Au(0)).inline_start_end();
- result.minimum_inline_size = result.minimum_inline_size + border_width + padding_inline_size;
- result.preferred_inline_size = result.preferred_inline_size + border_width + padding_inline_size;
- }
- }
- }
-
- result
- }
-
-
- /// TODO: What exactly does this function return? Why is it Au(0) for GenericFragment?
- pub fn content_inline_size(&self) -> Au {
- match self.specific {
- GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment | TableRowFragment |
- TableWrapperFragment => Au(0),
- ImageFragment(ref image_fragment_info) => {
- image_fragment_info.computed_inline_size()
- }
- ScannedTextFragment(ref text_fragment_info) => {
- let (range, run) = (&text_fragment_info.range, &text_fragment_info.run);
- let text_bounds = run.metrics_for_range(range).bounding_box;
- text_bounds.size.width
- }
- TableColumnFragment(_) => fail!("Table column fragments do not have inline_size"),
- UnscannedTextFragment(_) => fail!("Unscanned text fragments should have been scanned by now!"),
- }
- }
-
- /// Returns, and computes, the block-size of this fragment.
- pub fn content_block_size(&self, layout_context: &LayoutContext) -> Au {
- match self.specific {
- GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment | TableRowFragment |
- TableWrapperFragment => Au(0),
- ImageFragment(ref image_fragment_info) => {
- image_fragment_info.computed_block_size()
- }
- ScannedTextFragment(_) => {
- // Compute the block-size based on the line-block-size and font size.
- self.calculate_line_height(layout_context)
- }
- TableColumnFragment(_) => fail!("Table column fragments do not have block_size"),
- UnscannedTextFragment(_) => fail!("Unscanned text fragments should have been scanned by now!"),
- }
- }
-
- /// Returns the dimensions of the content box.
- ///
- /// This is marked `#[inline]` because it is frequently called when only one or two of the
- /// values are needed and that will save computation.
- #[inline]
- pub fn content_box(&self) -> LogicalRect<Au> {
- self.border_box - self.border_padding
- }
-
- /// Find the split of a fragment that includes a new-line character.
- ///
- /// A return value of `None` indicates that the fragment is not splittable.
- /// Otherwise the split information is returned. The right information is
- /// optional due to the possibility of it being whitespace.
- //
- // TODO(bjz): The text run should be removed in the future, but it is currently needed for
- // the current method of fragment splitting in the `inline::try_append_*` functions.
- pub fn find_split_info_by_new_line(&self)
- -> Option<(SplitInfo, Option<SplitInfo>, Arc<Box<TextRun>> /* TODO(bjz): remove */)> {
- match self.specific {
- GenericFragment | IframeFragment(_) | ImageFragment(_) | TableFragment | TableCellFragment |
- TableRowFragment | TableWrapperFragment => None,
- TableColumnFragment(_) => fail!("Table column fragments do not need to split"),
- UnscannedTextFragment(_) => fail!("Unscanned text fragments should have been scanned by now!"),
- ScannedTextFragment(ref text_fragment_info) => {
- let mut new_line_pos = self.new_line_pos.clone();
- let cur_new_line_pos = new_line_pos.remove(0).unwrap();
-
- let inline_start_range = Range::new(text_fragment_info.range.begin(), cur_new_line_pos);
- let inline_end_range = Range::new(text_fragment_info.range.begin() + cur_new_line_pos + CharIndex(1),
- text_fragment_info.range.length() - (cur_new_line_pos + CharIndex(1)));
-
- // Left fragment is for inline-start text of first founded new-line character.
- let inline_start_fragment = SplitInfo::new(inline_start_range, text_fragment_info);
-
- // Right fragment is for inline-end text of first founded new-line character.
- let inline_end_fragment = if inline_end_range.length() > CharIndex(0) {
- Some(SplitInfo::new(inline_end_range, text_fragment_info))
- } else {
- None
- };
-
- Some((inline_start_fragment, inline_end_fragment, text_fragment_info.run.clone()))
- }
- }
- }
-
- /// Attempts to find the split positions of a text fragment so that its inline-size is
- /// no more than `max_inline-size`.
- ///
- /// A return value of `None` indicates that the fragment could not be split.
- /// Otherwise the information pertaining to the split is returned. The inline-start
- /// and inline-end split information are both optional due to the possibility of
- /// them being whitespace.
- //
- // TODO(bjz): The text run should be removed in the future, but it is currently needed for
- // the current method of fragment splitting in the `inline::try_append_*` functions.
- pub fn find_split_info_for_inline_size(&self, start: CharIndex, max_inline_size: Au, starts_line: bool)
- -> Option<(Option<SplitInfo>, Option<SplitInfo>, Arc<Box<TextRun>> /* TODO(bjz): remove */)> {
- match self.specific {
- GenericFragment | IframeFragment(_) | ImageFragment(_) | TableFragment | TableCellFragment |
- TableRowFragment | TableWrapperFragment => None,
- TableColumnFragment(_) => fail!("Table column fragments do not have inline_size"),
- UnscannedTextFragment(_) => fail!("Unscanned text fragments should have been scanned by now!"),
- ScannedTextFragment(ref text_fragment_info) => {
- let mut pieces_processed_count: uint = 0;
- let mut remaining_inline_size: Au = max_inline_size;
- let mut inline_start_range = Range::new(text_fragment_info.range.begin() + start, CharIndex(0));
- let mut inline_end_range: Option<Range<CharIndex>> = None;
-
- debug!("split_to_inline_size: splitting text fragment (strlen={}, range={}, avail_inline_size={})",
- text_fragment_info.run.text.len(),
- text_fragment_info.range,
- max_inline_size);
-
- for (glyphs, offset, slice_range) in text_fragment_info.run.iter_slices_for_range(
- &text_fragment_info.range) {
- debug!("split_to_inline_size: considering slice (offset={}, range={}, \
- remain_inline_size={})",
- offset,
- slice_range,
- remaining_inline_size);
-
- let metrics = text_fragment_info.run.metrics_for_slice(glyphs, &slice_range);
- let advance = metrics.advance_width;
-
- let should_continue;
- if advance <= remaining_inline_size {
- should_continue = true;
-
- if starts_line && pieces_processed_count == 0 && glyphs.is_whitespace() {
- debug!("split_to_inline_size: case=skipping leading trimmable whitespace");
- inline_start_range.shift_by(slice_range.length());
- } else {
- debug!("split_to_inline_size: case=enlarging span");
- remaining_inline_size = remaining_inline_size - advance;
- inline_start_range.extend_by(slice_range.length());
- }
- } else {
- // The advance is more than the remaining inline-size.
- should_continue = false;
- let slice_begin = offset + slice_range.begin();
- let slice_end = offset + slice_range.end();
-
- if glyphs.is_whitespace() {
- // If there are still things after the trimmable whitespace, create the
- // inline-end chunk.
- if slice_end < text_fragment_info.range.end() {
- debug!("split_to_inline_size: case=skipping trimmable trailing \
- whitespace, then split remainder");
- let inline_end_range_end = text_fragment_info.range.end() - slice_end;
- inline_end_range = Some(Range::new(slice_end, inline_end_range_end));
- } else {
- debug!("split_to_inline_size: case=skipping trimmable trailing \
- whitespace");
- }
- } else if slice_begin < text_fragment_info.range.end() {
- // There are still some things inline-start over at the end of the line. Create
- // the inline-end chunk.
- let inline_end_range_end = text_fragment_info.range.end() - slice_begin;
- inline_end_range = Some(Range::new(slice_begin, inline_end_range_end));
- debug!("split_to_inline_size: case=splitting remainder with inline_end range={:?}",
- inline_end_range);
- }
- }
-
- pieces_processed_count += 1;
-
- if !should_continue {
- break
- }
- }
-
- let inline_start_is_some = inline_start_range.length() > CharIndex(0);
-
- if (pieces_processed_count == 1 || !inline_start_is_some) && !starts_line {
- None
- } else {
- let inline_start = if inline_start_is_some {
- Some(SplitInfo::new(inline_start_range, text_fragment_info))
- } else {
- None
- };
- let inline_end = inline_end_range.map(|inline_end_range| SplitInfo::new(inline_end_range, text_fragment_info));
-
- Some((inline_start, inline_end, text_fragment_info.run.clone()))
- }
- }
- }
- }
-
- /// Returns true if this fragment is an unscanned text fragment that consists entirely of whitespace.
- pub fn is_whitespace_only(&self) -> bool {
- match self.specific {
- UnscannedTextFragment(ref text_fragment_info) => is_whitespace(text_fragment_info.text.as_slice()),
- _ => false,
- }
- }
-
- /// Assigns replaced inline-size, padding, and margins for this fragment only if it is replaced
- /// content per CSS 2.1 § 10.3.2.
- pub fn assign_replaced_inline_size_if_necessary(&mut self,
- container_inline_size: Au) {
- match self.specific {
- GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment | TableRowFragment |
- TableWrapperFragment => return,
- TableColumnFragment(_) => fail!("Table column fragments do not have inline_size"),
- UnscannedTextFragment(_) => fail!("Unscanned text fragments should have been scanned by now!"),
- ImageFragment(_) | ScannedTextFragment(_) => {}
- };
-
- self.compute_border_padding_margins(container_inline_size);
-
- let style_inline_size = self.style().content_inline_size();
- let style_block_size = self.style().content_block_size();
- let noncontent_inline_size = self.border_padding.inline_start_end();
-
- match self.specific {
- ScannedTextFragment(_) => {
- // Scanned text fragments will have already had their content inline-sizes assigned by this
- // point.
- self.border_box.size.inline = self.border_box.size.inline + noncontent_inline_size
- }
- ImageFragment(ref mut image_fragment_info) => {
- // TODO(ksh8281): compute border,margin
- let inline_size = ImageFragmentInfo::style_length(style_inline_size,
- image_fragment_info.dom_inline_size,
- container_inline_size);
- let block_size = ImageFragmentInfo::style_length(style_block_size,
- image_fragment_info.dom_block_size,
- Au(0));
-
- let inline_size = match (inline_size,block_size) {
- (Auto, Auto) => image_fragment_info.image_inline_size(),
- (Auto,Specified(h)) => {
- let scale = image_fragment_info.
- image_block_size().to_f32().unwrap() / h.to_f32().unwrap();
- Au::new((image_fragment_info.image_inline_size().to_f32().unwrap() / scale) as i32)
- },
- (Specified(w), _) => w,
- };
-
- self.border_box.size.inline = inline_size + noncontent_inline_size;
- image_fragment_info.computed_inline_size = Some(inline_size);
- }
- _ => fail!("this case should have been handled above"),
- }
- }
-
- /// Assign block-size for this fragment if it is replaced content. The inline-size must have been assigned
- /// first.
- ///
- /// Ideally, this should follow CSS 2.1 § 10.6.2.
- pub fn assign_replaced_block_size_if_necessary(&mut self) {
- match self.specific {
- GenericFragment | IframeFragment(_) | TableFragment | TableCellFragment | TableRowFragment |
- TableWrapperFragment => return,
- TableColumnFragment(_) => fail!("Table column fragments do not have block_size"),
- UnscannedTextFragment(_) => fail!("Unscanned text fragments should have been scanned by now!"),
- ImageFragment(_) | ScannedTextFragment(_) => {}
- }
-
- let style_inline_size = self.style().content_inline_size();
- let style_block_size = self.style().content_block_size();
- let noncontent_block_size = self.border_padding.block_start_end();
-
- match self.specific {
- ImageFragment(ref mut image_fragment_info) => {
- // TODO(ksh8281): compute border,margin,padding
- let inline_size = image_fragment_info.computed_inline_size();
- // FIXME(ksh8281): we shouldn't assign block-size this way
- // we don't know about size of parent's block-size
- let block_size = ImageFragmentInfo::style_length(style_block_size,
- image_fragment_info.dom_block_size,
- Au(0));
-
- let block_size = match (style_inline_size, image_fragment_info.dom_inline_size, block_size) {
- (LPA_Auto, None, Auto) => {
- image_fragment_info.image_block_size()
- },
- (_,_,Auto) => {
- let scale = image_fragment_info.image_inline_size().to_f32().unwrap()
- / inline_size.to_f32().unwrap();
- Au::new((image_fragment_info.image_block_size().to_f32().unwrap() / scale) as i32)
- },
- (_,_,Specified(h)) => {
- h
- }
- };
-
- image_fragment_info.computed_block_size = Some(block_size);
- self.border_box.size.block = block_size + noncontent_block_size
- }
- ScannedTextFragment(_) => {
- // Scanned text fragments' content block-sizes are calculated by the text run scanner
- // during flow construction.
- self.border_box.size.block = self.border_box.size.block + noncontent_block_size
- }
- _ => fail!("should have been handled above"),
- }
- }
-
- /// Calculates block-size above baseline, depth below baseline, and ascent for this fragment when
- /// used in an inline formatting context. See CSS 2.1 § 10.8.1.
- pub fn inline_metrics(&self, layout_context: &LayoutContext) -> InlineMetrics {
- match self.specific {
- ImageFragment(ref image_fragment_info) => {
- let computed_block_size = image_fragment_info.computed_block_size();
- InlineMetrics {
- block_size_above_baseline: computed_block_size + self.border_padding.block_start_end(),
- depth_below_baseline: Au(0),
- ascent: computed_block_size + self.border_padding.block_end,
- }
- }
- ScannedTextFragment(ref text_fragment) => {
- // See CSS 2.1 § 10.8.1.
- let line_height = self.calculate_line_height(layout_context);
- InlineMetrics::from_font_metrics(&text_fragment.run.font_metrics, line_height)
- }
- _ => {
- InlineMetrics {
- block_size_above_baseline: self.border_box.size.block,
- depth_below_baseline: Au(0),
- ascent: self.border_box.size.block,
- }
- }
- }
- }
-
- /// Returns true if this fragment can merge with another adjacent fragment or false otherwise.
- pub fn can_merge_with_fragment(&self, other: &Fragment) -> bool {
- match (&self.specific, &other.specific) {
- (&UnscannedTextFragment(_), &UnscannedTextFragment(_)) => {
- // FIXME: Should probably use a whitelist of styles that can safely differ (#3165)
- self.font_style() == other.font_style() &&
- self.text_decoration() == other.text_decoration() &&
- self.white_space() == other.white_space()
- }
- _ => false,
- }
- }
-
- /// Sends the size and position of this iframe fragment to the constellation. This is out of
- /// line to guide inlining.
- #[inline(never)]
- fn finalize_position_and_size_of_iframe(&self,
- iframe_fragment: &IframeFragmentInfo,
- offset: Point2D<Au>,
- layout_context: &LayoutContext) {
- let mbp = (self.margin + self.border_padding).to_physical(self.style.writing_mode);
- let content_size = self.content_box().size.to_physical(self.style.writing_mode);
-
- let left = offset.x + mbp.left;
- let top = offset.y + mbp.top;
- let width = content_size.width;
- let height = content_size.height;
- let origin = Point2D(geometry::to_frac_px(left) as f32, geometry::to_frac_px(top) as f32);
- let size = Size2D(geometry::to_frac_px(width) as f32, geometry::to_frac_px(height) as f32);
- let rect = Rect(origin, size);
-
- debug!("finalizing position and size of iframe for {:?},{:?}",
- iframe_fragment.pipeline_id,
- iframe_fragment.subpage_id);
- let msg = FrameRectMsg(iframe_fragment.pipeline_id, iframe_fragment.subpage_id, rect);
- let ConstellationChan(ref chan) = layout_context.shared.constellation_chan;
- chan.send(msg)
- }
-}
-
-impl fmt::Show for Fragment {
- /// Outputs a debugging string describing this fragment.
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- try!(write!(f, "({} ",
- match self.specific {
- GenericFragment => "GenericFragment",
- IframeFragment(_) => "IframeFragment",
- ImageFragment(_) => "ImageFragment",
- ScannedTextFragment(_) => "ScannedTextFragment",
- TableFragment => "TableFragment",
- TableCellFragment => "TableCellFragment",
- TableColumnFragment(_) => "TableColumnFragment",
- TableRowFragment => "TableRowFragment",
- TableWrapperFragment => "TableWrapperFragment",
- UnscannedTextFragment(_) => "UnscannedTextFragment",
- }));
- try!(write!(f, "bp {}", self.border_padding));
- try!(write!(f, " "));
- try!(write!(f, "m {}", self.margin));
- write!(f, ")")
- }
-}
-
-/// An object that accumulates display lists of child flows, applying a clipping rect if necessary.
-pub struct ChildDisplayListAccumulator {
- clip_display_item: Option<Box<ClipDisplayItem>>,
-}
-
-impl ChildDisplayListAccumulator {
- /// Creates a `ChildDisplayListAccumulator` from the `overflow` property in the given style.
- fn new(style: &ComputedValues, bounds: Rect<Au>, node: OpaqueNode, level: StackingLevel)
- -> ChildDisplayListAccumulator {
- ChildDisplayListAccumulator {
- clip_display_item: match style.get_box().overflow {
- overflow::hidden | overflow::auto | overflow::scroll => {
- Some(box ClipDisplayItem {
- base: BaseDisplayItem::new(bounds, node, level),
- children: DisplayList::new(),
- })
- },
- overflow::visible => None,
- }
- }
- }
-
- /// Pushes the given display item onto this display list.
- pub fn push(&mut self, parent_display_list: &mut DisplayList, item: DisplayItem) {
- match self.clip_display_item {
- None => parent_display_list.push(item),
- Some(ref mut clip_display_item) => clip_display_item.children.push(item),
- }
- }
-
- /// Pushes the display items from the given child onto this display list.
- pub fn push_child(&mut self, parent_display_list: &mut DisplayList, child: &mut Flow) {
- let kid_display_list = mem::replace(&mut flow::mut_base(child).display_list,
- DisplayList::new());
- match self.clip_display_item {
- None => parent_display_list.push_all_move(kid_display_list),
- Some(ref mut clip_display_item) => {
- clip_display_item.children.push_all_move(kid_display_list)
- }
- }
- }
-
- /// Consumes this accumulator and pushes the clipping item, if any, onto the display list
- /// associated with the given flow, along with the items in the given display list.
- pub fn finish(self, parent: &mut Flow, mut display_list: DisplayList) {
- let ChildDisplayListAccumulator {
- clip_display_item
- } = self;
- match clip_display_item {
- None => {}
- Some(clip_display_item) => display_list.push(ClipDisplayItemClass(clip_display_item)),
- }
- flow::mut_base(parent).display_list = display_list
- }
-}
diff --git a/src/components/layout/incremental.rs b/src/components/layout/incremental.rs
deleted file mode 100644
index d04c068b6aa..00000000000
--- a/src/components/layout/incremental.rs
+++ /dev/null
@@ -1,78 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-use style::ComputedValues;
-
-bitflags! {
- #[doc = "Individual layout actions that may be necessary after restyling."]
- flags RestyleDamage: int {
- #[doc = "Repaint the node itself."]
- #[doc = "Currently unused; need to decide how this propagates."]
- static Repaint = 0x01,
-
- #[doc = "Recompute intrinsic inline_sizes (minimum and preferred)."]
- #[doc = "Propagates down the flow tree because the computation is"]
- #[doc = "bottom-up."]
- static BubbleISizes = 0x02,
-
- #[doc = "Recompute actual inline_sizes and block_sizes."]
- #[doc = "Propagates up the flow tree because the computation is"]
- #[doc = "top-down."]
- static Reflow = 0x04
- }
-}
-
-impl RestyleDamage {
- /// Elements of self which should also get set on any ancestor flow.
- pub fn propagate_up(self) -> RestyleDamage {
- self & Reflow
- }
-
- /// Elements of self which should also get set on any child flows.
- pub fn propagate_down(self) -> RestyleDamage {
- self & BubbleISizes
- }
-}
-
-// NB: We need the braces inside the RHS due to Rust #8012. This particular
-// version of this macro might be safe anyway, but we want to avoid silent
-// breakage on modifications.
-macro_rules! add_if_not_equal(
- ($old:ident, $new:ident, $damage:ident,
- [ $($effect:ident),* ], [ $($style_struct_getter:ident.$name:ident),* ]) => ({
- if $( ($old.$style_struct_getter().$name != $new.$style_struct_getter().$name) )||* {
- $damage.insert($($effect)|*);
- }
- })
-)
-
-pub fn compute_damage(old: &ComputedValues, new: &ComputedValues) -> RestyleDamage {
- let mut damage = RestyleDamage::empty();
-
- // This checks every CSS property, as enumerated in
- // impl<'self> CssComputedStyle<'self>
- // in src/support/netsurfcss/rust-netsurfcss/netsurfcss.rc.
-
- // FIXME: We can short-circuit more of this.
-
- add_if_not_equal!(old, new, damage, [ Repaint ],
- [ get_color.color, get_background.background_color,
- get_border.border_top_color, get_border.border_right_color,
- get_border.border_bottom_color, get_border.border_left_color ]);
-
- add_if_not_equal!(old, new, damage, [ Repaint, BubbleISizes, Reflow ],
- [ get_border.border_top_width, get_border.border_right_width,
- get_border.border_bottom_width, get_border.border_left_width,
- get_margin.margin_top, get_margin.margin_right,
- get_margin.margin_bottom, get_margin.margin_left,
- get_padding.padding_top, get_padding.padding_right,
- get_padding.padding_bottom, get_padding.padding_left,
- get_box.position, get_box.width, get_box.height, get_box.float, get_box.display,
- get_font.font_family, get_font.font_size, get_font.font_style, get_font.font_weight,
- get_inheritedtext.text_align, get_text.text_decoration, get_inheritedbox.line_height ]);
-
- // FIXME: test somehow that we checked every CSS property
-
- damage
-}
diff --git a/src/components/layout/inline.rs b/src/components/layout/inline.rs
deleted file mode 100644
index 6c22b25746e..00000000000
--- a/src/components/layout/inline.rs
+++ /dev/null
@@ -1,1170 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#![deny(unsafe_block)]
-
-use css::node_style::StyledNode;
-use context::LayoutContext;
-use floats::{FloatLeft, Floats, PlacementInfo};
-use flow::{BaseFlow, FlowClass, Flow, InlineFlowClass};
-use flow;
-use fragment::{Fragment, ScannedTextFragment, ScannedTextFragmentInfo, SplitInfo};
-use layout_debug;
-use model::IntrinsicISizes;
-use text;
-use wrapper::ThreadSafeLayoutNode;
-
-use collections::{Deque, RingBuf};
-use geom::Rect;
-use gfx::display_list::ContentLevel;
-use gfx::font::FontMetrics;
-use gfx::font_context::FontContext;
-use gfx::text::glyph::CharIndex;
-use servo_util::geometry::Au;
-use servo_util::geometry;
-use servo_util::logical_geometry::{LogicalRect, LogicalSize};
-use servo_util::range;
-use servo_util::range::{EachIndex, Range, RangeIndex, IntRangeIndex};
-use std::fmt;
-use std::mem;
-use std::num;
-use std::u16;
-use style::computed_values::{text_align, vertical_align, white_space};
-use style::ComputedValues;
-use sync::Arc;
-
-/// `Line`s are represented as offsets into the child list, rather than
-/// as an object that "owns" fragments. Choosing a different set of line
-/// breaks requires a new list of offsets, and possibly some splitting and
-/// merging of TextFragments.
-///
-/// A similar list will keep track of the mapping between CSS fragments and
-/// the corresponding fragments in the inline flow.
-///
-/// After line breaks are determined, render fragments in the inline flow may
-/// overlap visually. For example, in the case of nested inline CSS fragments,
-/// outer inlines must be at least as large as the inner inlines, for
-/// purposes of drawing noninherited things like backgrounds, borders,
-/// outlines.
-///
-/// N.B. roc has an alternative design where the list instead consists of
-/// things like "start outer fragment, text, start inner fragment, text, end inner
-/// fragment, text, end outer fragment, text". This seems a little complicated to
-/// serve as the starting point, but the current design doesn't make it
-/// hard to try out that alternative.
-///
-/// Line fragments also contain some metadata used during line breaking. The
-/// green zone is the area that the line can expand to before it collides
-/// with a float or a horizontal wall of the containing block. The block-start
-/// inline-start corner of the green zone is the same as that of the line, but
-/// the green zone can be taller and wider than the line itself.
-#[deriving(Encodable)]
-pub struct Line {
- /// A range of line indices that describe line breaks.
- ///
- /// For example, consider the following HTML and rendered element with
- /// linebreaks:
- ///
- /// ~~~html
- /// <span>I <span>like truffles, <img></span> yes I do.</span>
- /// ~~~
- ///
- /// ~~~text
- /// +------------+
- /// | I like |
- /// | truffles, |
- /// | +----+ |
- /// | | | |
- /// | +----+ yes |
- /// | I do. |
- /// +------------+
- /// ~~~
- ///
- /// The ranges that describe these lines would be:
- ///
- /// | [0.0, 1.4) | [1.5, 2.0) | [2.0, 3.4) | [3.4, 4.0) |
- /// |------------|-------------|-------------|------------|
- /// | 'I like' | 'truffles,' | '<img> yes' | 'I do.' |
- pub range: Range<LineIndices>,
- /// The bounds are the exact position and extents of the line with respect
- /// to the parent box.
- ///
- /// For example, for the HTML below...
- ///
- /// ~~~html
- /// <div><span>I <span>like truffles, <img></span></div>
- /// ~~~
- ///
- /// ...the bounds would be:
- ///
- /// ~~~text
- /// +-----------------------------------------------------------+
- /// | ^ |
- /// | | |
- /// | origin.y |
- /// | | |
- /// | v |
- /// |< - origin.x ->+ - - - - - - - - +---------+---- |
- /// | | | | ^ |
- /// | | | <img> | size.block-size |
- /// | I like truffles, | | v |
- /// | + - - - - - - - - +---------+---- |
- /// | | | |
- /// | |<------ size.inline-size ------->| |
- /// | |
- /// | |
- /// +-----------------------------------------------------------+
- /// ~~~
- pub bounds: LogicalRect<Au>,
- /// The green zone is the greatest extent from wich a line can extend to
- /// before it collides with a float.
- ///
- /// ~~~text
- /// +-----------------------+
- /// |::::::::::::::::: |
- /// |:::::::::::::::::FFFFFF|
- /// |============:::::FFFFFF|
- /// |:::::::::::::::::FFFFFF|
- /// |:::::::::::::::::FFFFFF|
- /// |::::::::::::::::: |
- /// | FFFFFFFFF |
- /// | FFFFFFFFF |
- /// | FFFFFFFFF |
- /// | |
- /// +-----------------------+
- ///
- /// === line
- /// ::: green zone
- /// FFF float
- /// ~~~
- pub green_zone: LogicalSize<Au>
-}
-
-int_range_index! {
- #[deriving(Encodable)]
- #[doc = "The index of a fragment in a flattened vector of DOM elements."]
- struct FragmentIndex(int)
-}
-
-/// A line index consists of two indices: a fragment index that refers to the
-/// index of a DOM fragment within a flattened inline element; and a glyph index
-/// where the 0th glyph refers to the first glyph of that fragment.
-#[deriving(Clone, Encodable, PartialEq, PartialOrd, Eq, Ord, Zero)]
-pub struct LineIndices {
- /// The index of a fragment into the flattened vector of DOM elements.
- ///
- /// For example, given the HTML below:
- ///
- /// ~~~html
- /// <span>I <span>like truffles, <img></span> yes I do.</span>
- /// ~~~
- ///
- /// The fragments would be indexed as follows:
- ///
- /// | 0 | 1 | 2 | 3 |
- /// |------|------------------|---------|--------------|
- /// | 'I ' | 'like truffles,' | `<img>` | ' yes I do.' |
- pub fragment_index: FragmentIndex,
- /// The index of a character in a DOM fragment. Continuous runs of whitespace
- /// are treated as single characters. Non-breakable DOM fragments such as
- /// images are treated as having a range length of `1`.
- ///
- /// For example, given the HTML below:
- ///
- /// ~~~html
- /// <span>I <span>like truffles, <img></span> yes I do.</span>
- /// ~~~
- ///
- /// The characters would be indexed as follows:
- ///
- /// | 0 | 1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
- /// |---|---|---|---|---|---|---|---|---|---|---|---|----|----|----|----|----|
- /// | I | | l | i | k | e | | t | r | u | f | f | l | e | s | , | |
- ///
- /// | 0 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
- /// |---------|---|---|---|---|---|---|---|---|---|---|
- /// | `<img>` | | y | e | s | | I | | d | o | . |
- pub char_index: CharIndex,
-}
-
-impl RangeIndex for LineIndices {}
-
-impl Add<LineIndices, LineIndices> for LineIndices {
- fn add(&self, other: &LineIndices) -> LineIndices {
- // TODO: use debug_assert! after rustc upgrade
- if cfg!(not(ndebug)) {
- assert!(other.fragment_index == num::zero() || other.char_index == num::zero(),
- "Attempted to add {} to {}. Both the fragment_index and \
- char_index of the RHS are non-zero. This probably was a \
- mistake!", self, other);
- }
- LineIndices {
- fragment_index: self.fragment_index + other.fragment_index,
- char_index: self.char_index + other.char_index,
- }
- }
-}
-
-impl Sub<LineIndices, LineIndices> for LineIndices {
- fn sub(&self, other: &LineIndices) -> LineIndices {
- // TODO: use debug_assert! after rustc upgrade
- if cfg!(not(ndebug)) {
- assert!(other.fragment_index == num::zero() || other.char_index == num::zero(),
- "Attempted to subtract {} from {}. Both the fragment_index \
- and char_index of the RHS are non-zero. This probably was \
- a mistake!", self, other);
- }
- LineIndices {
- fragment_index: self.fragment_index - other.fragment_index,
- char_index: self.char_index - other.char_index,
- }
- }
-}
-
-impl Neg<LineIndices> for LineIndices {
- fn neg(&self) -> LineIndices {
- // TODO: use debug_assert! after rustc upgrade
- if cfg!(not(ndebug)) {
- assert!(self.fragment_index == num::zero() || self.char_index == num::zero(),
- "Attempted to negate {}. Both the fragment_index and \
- char_index are non-zero. This probably was a mistake!",
- self);
- }
- LineIndices {
- fragment_index: -self.fragment_index,
- char_index: -self.char_index,
- }
- }
-}
-
-impl fmt::Show for LineIndices {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "{}.{}", self.fragment_index, self.char_index)
- }
-}
-
-pub fn each_fragment_index(range: &Range<LineIndices>) -> EachIndex<int, FragmentIndex> {
- range::each_index(range.begin().fragment_index, range.end().fragment_index)
-}
-
-pub fn each_char_index(range: &Range<LineIndices>) -> EachIndex<int, CharIndex> {
- range::each_index(range.begin().char_index, range.end().char_index)
-}
-
-struct LineBreaker {
- pub floats: Floats,
- pub new_fragments: Vec<Fragment>,
- pub work_list: RingBuf<Fragment>,
- pub pending_line: Line,
- pub lines: Vec<Line>,
- pub cur_b: Au, // Current position on the block direction
-}
-
-impl LineBreaker {
- pub fn new(float_ctx: Floats) -> LineBreaker {
- LineBreaker {
- new_fragments: Vec::new(),
- work_list: RingBuf::new(),
- pending_line: Line {
- range: Range::empty(),
- bounds: LogicalRect::zero(float_ctx.writing_mode),
- green_zone: LogicalSize::zero(float_ctx.writing_mode)
- },
- floats: float_ctx,
- lines: Vec::new(),
- cur_b: Au::new(0)
- }
- }
-
- pub fn floats(&mut self) -> Floats {
- self.floats.clone()
- }
-
- fn reset_scanner(&mut self) {
- debug!("Resetting LineBreaker's state for flow.");
- self.lines = Vec::new();
- self.new_fragments = Vec::new();
- self.cur_b = Au(0);
- self.reset_line();
- }
-
- fn reset_line(&mut self) {
- self.pending_line.range.reset(num::zero(), num::zero());
- self.pending_line.bounds = LogicalRect::new(
- self.floats.writing_mode, Au::new(0), self.cur_b, Au::new(0), Au::new(0));
- self.pending_line.green_zone = LogicalSize::zero(self.floats.writing_mode)
- }
-
- pub fn scan_for_lines(&mut self, flow: &mut InlineFlow, layout_context: &LayoutContext) {
- self.reset_scanner();
-
- let mut old_fragments = mem::replace(&mut flow.fragments, InlineFragments::new());
-
- { // Enter a new scope so that old_fragment_iter's borrow is released
- let mut old_fragment_iter = old_fragments.fragments.iter();
- loop {
- // acquire the next fragment to lay out from work list or fragment list
- let cur_fragment = if self.work_list.is_empty() {
- match old_fragment_iter.next() {
- None => break,
- Some(fragment) => {
- debug!("LineBreaker: Working with fragment from flow: b{}",
- fragment.debug_id());
- (*fragment).clone()
- }
- }
- } else {
- let fragment = self.work_list.pop_front().unwrap();
- debug!("LineBreaker: Working with fragment from work list: b{}",
- fragment.debug_id());
- fragment
- };
-
- let fragment_was_appended = match cur_fragment.white_space() {
- white_space::normal => self.try_append_to_line(cur_fragment, flow, layout_context),
- white_space::pre => self.try_append_to_line_by_new_line(cur_fragment),
- };
-
- if !fragment_was_appended {
- debug!("LineBreaker: Fragment wasn't appended, because line {:u} was full.",
- self.lines.len());
- self.flush_current_line();
- } else {
- debug!("LineBreaker: appended a fragment to line {:u}", self.lines.len());
- }
- }
-
- if self.pending_line.range.length() > num::zero() {
- debug!("LineBreaker: Partially full line {:u} inline_start at end of scanning.",
- self.lines.len());
- self.flush_current_line();
- }
- }
-
- old_fragments.fragments = mem::replace(&mut self.new_fragments, vec![]);
- flow.fragments = old_fragments;
- flow.lines = mem::replace(&mut self.lines, Vec::new());
- }
-
- fn flush_current_line(&mut self) {
- debug!("LineBreaker: Flushing line {:u}: {:?}",
- self.lines.len(), self.pending_line);
-
- // clear line and add line mapping
- debug!("LineBreaker: Saving information for flushed line {:u}.", self.lines.len());
- self.lines.push(self.pending_line);
- self.cur_b = self.pending_line.bounds.start.b + self.pending_line.bounds.size.block;
- self.reset_line();
- }
-
- // FIXME(eatkinson): this assumes that the tallest fragment in the line determines the line block-size
- // This might not be the case with some weird text fonts.
- fn new_block_size_for_line(&self, new_fragment: &Fragment, layout_context: &LayoutContext) -> Au {
- let fragment_block_size = new_fragment.content_block_size(layout_context);
- if fragment_block_size > self.pending_line.bounds.size.block {
- fragment_block_size
- } else {
- self.pending_line.bounds.size.block
- }
- }
-
- /// Computes the position of a line that has only the provided fragment. Returns the bounding
- /// rect of the line's green zone (whose origin coincides with the line's origin) and the actual
- /// inline-size of the first fragment after splitting.
- fn initial_line_placement(&self, first_fragment: &Fragment, ceiling: Au, flow: &InlineFlow)
- -> (LogicalRect<Au>, Au) {
- debug!("LineBreaker: Trying to place first fragment of line {}", self.lines.len());
-
- let first_fragment_size = first_fragment.border_box.size;
- let splittable = first_fragment.can_split();
- debug!("LineBreaker: fragment size: {}, splittable: {}", first_fragment_size, splittable);
-
- // Initally, pretend a splittable fragment has 0 inline-size.
- // We will move it later if it has nonzero inline-size
- // and that causes problems.
- let placement_inline_size = if splittable {
- Au::new(0)
- } else {
- first_fragment_size.inline
- };
-
- let info = PlacementInfo {
- size: LogicalSize::new(
- self.floats.writing_mode, placement_inline_size, first_fragment_size.block),
- ceiling: ceiling,
- max_inline_size: flow.base.position.size.inline,
- kind: FloatLeft,
- };
-
- let line_bounds = self.floats.place_between_floats(&info);
-
- debug!("LineBreaker: found position for line: {} using placement_info: {:?}",
- line_bounds,
- info);
-
- // Simple case: if the fragment fits, then we can stop here
- if line_bounds.size.inline > first_fragment_size.inline {
- debug!("LineBreaker: case=fragment fits");
- return (line_bounds, first_fragment_size.inline);
- }
-
- // If not, but we can't split the fragment, then we'll place
- // the line here and it will overflow.
- if !splittable {
- debug!("LineBreaker: case=line doesn't fit, but is unsplittable");
- return (line_bounds, first_fragment_size.inline);
- }
-
- debug!("LineBreaker: used to call split_to_inline_size here");
- return (line_bounds, first_fragment_size.inline);
- }
-
- /// Performs float collision avoidance. This is called when adding a fragment is going to increase
- /// the block-size, and because of that we will collide with some floats.
- ///
- /// We have two options here:
- /// 1) Move the entire line so that it doesn't collide any more.
- /// 2) Break the line and put the new fragment on the next line.
- ///
- /// The problem with option 1 is that we might move the line and then wind up breaking anyway,
- /// which violates the standard.
- /// But option 2 is going to look weird sometimes.
- ///
- /// So we'll try to move the line whenever we can, but break if we have to.
- ///
- /// Returns false if and only if we should break the line.
- fn avoid_floats(&mut self,
- in_fragment: Fragment,
- flow: &InlineFlow,
- new_block_size: Au,
- line_is_empty: bool)
- -> bool {
- debug!("LineBreaker: entering float collision avoider!");
-
- // First predict where the next line is going to be.
- let this_line_y = self.pending_line.bounds.start.b;
- let (next_line, first_fragment_inline_size) = self.initial_line_placement(&in_fragment, this_line_y, flow);
- let next_green_zone = next_line.size;
-
- let new_inline_size = self.pending_line.bounds.size.inline + first_fragment_inline_size;
-
- // Now, see if everything can fit at the new location.
- if next_green_zone.inline >= new_inline_size && next_green_zone.block >= new_block_size {
- debug!("LineBreaker: case=adding fragment collides vertically with floats: moving line");
-
- self.pending_line.bounds.start = next_line.start;
- self.pending_line.green_zone = next_green_zone;
-
- assert!(!line_is_empty, "Non-terminating line breaking");
- self.work_list.push_front(in_fragment);
- return true
- }
-
- debug!("LineBreaker: case=adding fragment collides vertically with floats: breaking line");
- self.work_list.push_front(in_fragment);
- false
- }
-
- fn try_append_to_line_by_new_line(&mut self, in_fragment: Fragment) -> bool {
- if in_fragment.new_line_pos.len() == 0 {
- debug!("LineBreaker: Did not find a new-line character, so pushing the fragment to \
- the line without splitting.");
- self.push_fragment_to_line(in_fragment);
- true
- } else {
- debug!("LineBreaker: Found a new-line character, so splitting theline.");
-
- let (inline_start, inline_end, run) = in_fragment.find_split_info_by_new_line()
- .expect("LineBreaker: This split case makes no sense!");
- let writing_mode = self.floats.writing_mode;
-
- // TODO(bjz): Remove fragment splitting
- let split_fragment = |split: SplitInfo| {
- let info = ScannedTextFragmentInfo::new(run.clone(), split.range);
- let specific = ScannedTextFragment(info);
- let size = LogicalSize::new(
- writing_mode, split.inline_size, in_fragment.border_box.size.block);
- in_fragment.transform(size, specific)
- };
-
- debug!("LineBreaker: Pushing the fragment to the inline_start of the new-line character \
- to the line.");
- let mut inline_start = split_fragment(inline_start);
- inline_start.new_line_pos = vec![];
- self.push_fragment_to_line(inline_start);
-
- for inline_end in inline_end.move_iter() {
- debug!("LineBreaker: Deferring the fragment to the inline_end of the new-line \
- character to the line.");
- let mut inline_end = split_fragment(inline_end);
- inline_end.new_line_pos = in_fragment.new_line_pos.clone();
- self.work_list.push_front(inline_end);
- }
- false
- }
- }
-
- /// Tries to append the given fragment to the line, splitting it if necessary. Returns false only if
- /// we should break the line.
- fn try_append_to_line(&mut self, in_fragment: Fragment, flow: &InlineFlow, layout_context: &LayoutContext) -> bool {
- let line_is_empty = self.pending_line.range.length() == num::zero();
- if line_is_empty {
- let (line_bounds, _) = self.initial_line_placement(&in_fragment, self.cur_b, flow);
- self.pending_line.bounds.start = line_bounds.start;
- self.pending_line.green_zone = line_bounds.size;
- }
-
- debug!("LineBreaker: Trying to append fragment to line {:u} (fragment size: {}, green zone: \
- {}): {}",
- self.lines.len(),
- in_fragment.border_box.size,
- self.pending_line.green_zone,
- in_fragment);
-
- let green_zone = self.pending_line.green_zone;
-
- // NB: At this point, if `green_zone.inline-size < self.pending_line.bounds.size.inline-size` or
- // `green_zone.block-size < self.pending_line.bounds.size.block-size`, then we committed a line
- // that overlaps with floats.
-
- let new_block_size = self.new_block_size_for_line(&in_fragment, layout_context);
- if new_block_size > green_zone.block {
- // Uh-oh. Float collision imminent. Enter the float collision avoider
- return self.avoid_floats(in_fragment, flow, new_block_size, line_is_empty)
- }
-
- // If we're not going to overflow the green zone vertically, we might still do so
- // horizontally. We'll try to place the whole fragment on this line and break somewhere if it
- // doesn't fit.
-
- let new_inline_size = self.pending_line.bounds.size.inline + in_fragment.border_box.size.inline;
- if new_inline_size <= green_zone.inline {
- debug!("LineBreaker: case=fragment fits without splitting");
- self.push_fragment_to_line(in_fragment);
- return true
- }
-
- if !in_fragment.can_split() {
- // TODO(eatkinson, issue #224): Signal that horizontal overflow happened?
- if line_is_empty {
- debug!("LineBreaker: case=fragment can't split and line {:u} is empty, so \
- overflowing.",
- self.lines.len());
- self.push_fragment_to_line(in_fragment);
- return true
- }
- }
-
- let available_inline_size = green_zone.inline - self.pending_line.bounds.size.inline;
- let split = in_fragment.find_split_info_for_inline_size(CharIndex(0), available_inline_size, line_is_empty);
- match split.map(|(inline_start, inline_end, run)| {
- // TODO(bjz): Remove fragment splitting
- let split_fragment = |split: SplitInfo| {
- let info = ScannedTextFragmentInfo::new(run.clone(), split.range);
- let specific = ScannedTextFragment(info);
- let size = LogicalSize::new(
- self.floats.writing_mode, split.inline_size, in_fragment.border_box.size.block);
- in_fragment.transform(size, specific)
- };
-
- (inline_start.map(|x| { debug!("LineBreaker: Left split {}", x); split_fragment(x) }),
- inline_end.map(|x| { debug!("LineBreaker: Right split {}", x); split_fragment(x) }))
- }) {
- None => {
- debug!("LineBreaker: Tried to split unsplittable render fragment! Deferring to next \
- line. {}", in_fragment);
- self.work_list.push_front(in_fragment);
- false
- },
- Some((Some(inline_start_fragment), Some(inline_end_fragment))) => {
- debug!("LineBreaker: Line break found! Pushing inline_start fragment to line and deferring \
- inline_end fragment to next line.");
- self.push_fragment_to_line(inline_start_fragment);
- self.work_list.push_front(inline_end_fragment);
- true
- },
- Some((Some(inline_start_fragment), None)) => {
- debug!("LineBreaker: Pushing inline_start fragment to line.");
- self.push_fragment_to_line(inline_start_fragment);
- true
- },
- Some((None, Some(inline_end_fragment))) => {
- debug!("LineBreaker: Pushing inline_end fragment to line.");
- self.push_fragment_to_line(inline_end_fragment);
- true
- },
- Some((None, None)) => {
- error!("LineBreaker: This split case makes no sense!");
- true
- },
- }
- }
-
- // An unconditional push
- fn push_fragment_to_line(&mut self, fragment: Fragment) {
- debug!("LineBreaker: Pushing fragment {} to line {:u}", fragment.debug_id(), self.lines.len());
-
- if self.pending_line.range.length() == num::zero() {
- assert!(self.new_fragments.len() <= (u16::MAX as uint));
- self.pending_line.range.reset(
- LineIndices {
- fragment_index: FragmentIndex(self.new_fragments.len() as int),
- char_index: CharIndex(0) /* unused for now */,
- },
- num::zero()
- );
- }
- self.pending_line.range.extend_by(LineIndices {
- fragment_index: FragmentIndex(1),
- char_index: CharIndex(0) /* unused for now */ ,
- });
- self.pending_line.bounds.size.inline = self.pending_line.bounds.size.inline +
- fragment.border_box.size.inline;
- self.pending_line.bounds.size.block = Au::max(self.pending_line.bounds.size.block,
- fragment.border_box.size.block);
- self.new_fragments.push(fragment);
- }
-}
-
-/// Represents a list of inline fragments, including element ranges.
-#[deriving(Encodable)]
-pub struct InlineFragments {
- /// The fragments themselves.
- pub fragments: Vec<Fragment>,
-}
-
-impl InlineFragments {
- /// Creates an empty set of inline fragments.
- pub fn new() -> InlineFragments {
- InlineFragments {
- fragments: vec![],
- }
- }
-
- /// Returns the number of inline fragments.
- pub fn len(&self) -> uint {
- self.fragments.len()
- }
-
- /// Returns true if this list contains no fragments and false if it contains at least one fragment.
- pub fn is_empty(&self) -> bool {
- self.len() == 0
- }
-
- /// Pushes a new inline fragment.
- pub fn push(&mut self, fragment: &mut Fragment, style: Arc<ComputedValues>) {
- fragment.add_inline_context_style(style);
- self.fragments.push(fragment.clone());
- }
-
- /// Merges another set of inline fragments with this one.
- pub fn push_all(&mut self, fragments: InlineFragments) {
- self.fragments.push_all_move(fragments.fragments);
- }
-
- /// A convenience function to return the fragment at a given index.
- pub fn get<'a>(&'a self, index: uint) -> &'a Fragment {
- &self.fragments[index]
- }
-
- /// A convenience function to return a mutable reference to the fragment at a given index.
- pub fn get_mut<'a>(&'a mut self, index: uint) -> &'a mut Fragment {
- self.fragments.get_mut(index)
- }
-
- /// Strips ignorable whitespace from the start of a list of fragments.
- pub fn strip_ignorable_whitespace_from_start(&mut self) {
- if self.is_empty() { return }; // Fast path
-
- // FIXME (rust#16151): This can be reverted back to using skip_while once
- // the upstream bug is fixed.
- let mut fragments = mem::replace(&mut self.fragments, vec![]).move_iter();
- let mut new_fragments = Vec::new();
- let mut skipping = true;
- for fragment in fragments {
- if skipping && fragment.is_whitespace_only() {
- debug!("stripping ignorable whitespace from start");
- continue
- }
-
- skipping = false;
- new_fragments.push(fragment);
- }
-
- self.fragments = new_fragments;
- }
-
- /// Strips ignorable whitespace from the end of a list of fragments.
- pub fn strip_ignorable_whitespace_from_end(&mut self) {
- if self.is_empty() {
- return;
- }
-
- let mut new_fragments = self.fragments.clone();
- while new_fragments.len() > 0 && new_fragments.as_slice().last().get_ref().is_whitespace_only() {
- debug!("stripping ignorable whitespace from end");
- drop(new_fragments.pop());
- }
-
-
- self.fragments = new_fragments;
- }
-}
-
-/// Flows for inline layout.
-#[deriving(Encodable)]
-pub struct InlineFlow {
- /// Data common to all flows.
- pub base: BaseFlow,
-
- /// A vector of all inline fragments. Several fragments may correspond to one node/element.
- pub fragments: InlineFragments,
-
- /// A vector of ranges into fragments that represents line positions. These ranges are disjoint and
- /// are the result of inline layout. This also includes some metadata used for positioning
- /// lines.
- pub lines: Vec<Line>,
-
- /// The minimum block-size above the baseline for each line, as specified by the line block-size and
- /// font style.
- pub minimum_block_size_above_baseline: Au,
-
- /// The minimum depth below the baseline for each line, as specified by the line block-size and
- /// font style.
- pub minimum_depth_below_baseline: Au,
-}
-
-impl InlineFlow {
- pub fn from_fragments(node: ThreadSafeLayoutNode, fragments: InlineFragments) -> InlineFlow {
- InlineFlow {
- base: BaseFlow::new(node),
- fragments: fragments,
- lines: Vec::new(),
- minimum_block_size_above_baseline: Au(0),
- minimum_depth_below_baseline: Au(0),
- }
- }
-
- pub fn build_display_list_inline(&mut self, layout_context: &LayoutContext) {
- let size = self.base.position.size.to_physical(self.base.writing_mode);
- if !Rect(self.base.abs_position, size).intersects(&layout_context.shared.dirty) {
- return
- }
-
- // 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());
-
- for fragment in self.fragments.fragments.mut_iter() {
- let rel_offset = fragment.relative_position(&self.base
- .absolute_position_info
- .relative_containing_block_size);
- drop(fragment.build_display_list(&mut self.base.display_list,
- layout_context,
- self.base.abs_position.add_size(
- &rel_offset.to_physical(self.base.writing_mode)),
- ContentLevel));
- }
-
- // TODO(#225): Should `inline-block` elements have flows as children of the inline flow or
- // should the flow be nested inside the fragment somehow?
-
- // For now, don't traverse the subtree rooted here.
- }
-
- /// Returns the distance from the baseline for the logical block-start inline-start corner of this fragment,
- /// taking into account the value of the CSS `vertical-align` property. Negative values mean
- /// "toward the logical block-start" and positive values mean "toward the logical block-end".
- ///
- /// The extra boolean is set if and only if `biggest_block-start` and/or `biggest_block-end` were updated.
- /// That is, if the box has a `block-start` or `block-end` value, true is returned.
- fn distance_from_baseline(fragment: &Fragment,
- ascent: Au,
- parent_text_block_start: Au,
- parent_text_block_end: Au,
- block_size_above_baseline: &mut Au,
- depth_below_baseline: &mut Au,
- largest_block_size_for_top_fragments: &mut Au,
- largest_block_size_for_bottom_fragments: &mut Au,
- layout_context: &LayoutContext)
- -> (Au, bool) {
- match fragment.vertical_align() {
- vertical_align::baseline => (-ascent, false),
- vertical_align::middle => {
- // TODO: x-block-size value should be used from font info.
- let xblock_size = Au(0);
- let fragment_block_size = fragment.content_block_size(layout_context);
- let offset_block_start = -(xblock_size + fragment_block_size).scale_by(0.5);
- *block_size_above_baseline = offset_block_start.scale_by(-1.0);
- *depth_below_baseline = fragment_block_size - *block_size_above_baseline;
- (offset_block_start, false)
- },
- vertical_align::sub => {
- // TODO: The proper position for subscripts should be used. Lower the baseline to
- // the proper position for subscripts.
- let sub_offset = Au(0);
- (sub_offset - ascent, false)
- },
- vertical_align::super_ => {
- // TODO: The proper position for superscripts should be used. Raise the baseline to
- // the proper position for superscripts.
- let super_offset = Au(0);
- (-super_offset - ascent, false)
- },
- vertical_align::text_top => {
- let fragment_block_size = *block_size_above_baseline + *depth_below_baseline;
- let prev_depth_below_baseline = *depth_below_baseline;
- *block_size_above_baseline = parent_text_block_start;
- *depth_below_baseline = fragment_block_size - *block_size_above_baseline;
- (*depth_below_baseline - prev_depth_below_baseline - ascent, false)
- },
- vertical_align::text_bottom => {
- let fragment_block_size = *block_size_above_baseline + *depth_below_baseline;
- let prev_depth_below_baseline = *depth_below_baseline;
- *depth_below_baseline = parent_text_block_end;
- *block_size_above_baseline = fragment_block_size - *depth_below_baseline;
- (*depth_below_baseline - prev_depth_below_baseline - ascent, false)
- },
- vertical_align::top => {
- *largest_block_size_for_top_fragments =
- Au::max(*largest_block_size_for_top_fragments,
- *block_size_above_baseline + *depth_below_baseline);
- let offset_top = *block_size_above_baseline - ascent;
- (offset_top, true)
- },
- vertical_align::bottom => {
- *largest_block_size_for_bottom_fragments =
- Au::max(*largest_block_size_for_bottom_fragments,
- *block_size_above_baseline + *depth_below_baseline);
- let offset_bottom = -(*depth_below_baseline + ascent);
- (offset_bottom, true)
- },
- vertical_align::Length(length) => (-(length + ascent), false),
- vertical_align::Percentage(p) => {
- let line_height = fragment.calculate_line_height(layout_context);
- let percent_offset = line_height.scale_by(p);
- (-(percent_offset + ascent), false)
- }
- }
- }
-
- /// Sets fragment X positions based on alignment for one line.
- fn set_horizontal_fragment_positions(fragments: &mut InlineFragments,
- line: &Line,
- line_align: text_align::T) {
- // Figure out how much inline-size we have.
- let slack_inline_size = Au::max(Au(0), line.green_zone.inline - line.bounds.size.inline);
-
- // Set the fragment x positions based on that alignment.
- let mut offset_x = line.bounds.start.i;
- offset_x = offset_x + match line_align {
- // So sorry, but justified text is more complicated than shuffling line
- // coordinates.
- //
- // TODO(burg, issue #213): Implement `text-align: justify`.
- text_align::left | text_align::justify => Au(0),
- text_align::center => slack_inline_size.scale_by(0.5),
- text_align::right => slack_inline_size,
- };
-
- for i in each_fragment_index(&line.range) {
- let fragment = fragments.get_mut(i.to_uint());
- let size = fragment.border_box.size;
- fragment.border_box = LogicalRect::new(
- fragment.style.writing_mode, offset_x, fragment.border_box.start.b,
- size.inline, size.block);
- offset_x = offset_x + size.inline;
- }
- }
-
- /// Computes the minimum ascent and descent for each line. This is done during flow
- /// construction.
- ///
- /// `style` is the style of the block.
- pub fn compute_minimum_ascent_and_descent(&self,
- font_context: &mut FontContext,
- style: &ComputedValues) -> (Au, Au) {
- let font_style = text::computed_style_to_font_style(style);
- let font_metrics = text::font_metrics_for_style(font_context, &font_style);
- let line_height = text::line_height_from_style(style, &font_metrics);
- let inline_metrics = InlineMetrics::from_font_metrics(&font_metrics, line_height);
- (inline_metrics.block_size_above_baseline, inline_metrics.depth_below_baseline)
- }
-}
-
-impl Flow for InlineFlow {
- fn class(&self) -> FlowClass {
- InlineFlowClass
- }
-
- fn as_immutable_inline<'a>(&'a self) -> &'a InlineFlow {
- self
- }
-
- fn as_inline<'a>(&'a mut self) -> &'a mut InlineFlow {
- self
- }
-
- fn bubble_inline_sizes(&mut self, _: &LayoutContext) {
- let _scope = layout_debug_scope!("inline::bubble_inline_sizes {:s}", self.base.debug_id());
-
- let writing_mode = self.base.writing_mode;
- for kid in self.base.child_iter() {
- flow::mut_base(kid).floats = Floats::new(writing_mode);
- }
-
- let mut intrinsic_inline_sizes = IntrinsicISizes::new();
- for fragment in self.fragments.fragments.mut_iter() {
- debug!("Flow: measuring {}", *fragment);
-
- let fragment_intrinsic_inline_sizes =
- fragment.intrinsic_inline_sizes();
- intrinsic_inline_sizes.minimum_inline_size = geometry::max(
- intrinsic_inline_sizes.minimum_inline_size,
- fragment_intrinsic_inline_sizes.minimum_inline_size);
- intrinsic_inline_sizes.preferred_inline_size =
- intrinsic_inline_sizes.preferred_inline_size +
- fragment_intrinsic_inline_sizes.preferred_inline_size;
- }
-
- self.base.intrinsic_inline_sizes = intrinsic_inline_sizes;
- }
-
- /// Recursively (top-down) determines the actual inline-size of child contexts and fragments. When called
- /// on this context, the context has had its inline-size set by the parent context.
- fn assign_inline_sizes(&mut self, _: &LayoutContext) {
- let _scope = layout_debug_scope!("inline::assign_inline_sizes {:s}", self.base.debug_id());
-
- // Initialize content fragment inline-sizes if they haven't been initialized already.
- //
- // TODO: Combine this with `LineBreaker`'s walk in the fragment list, or put this into `Fragment`.
-
- debug!("InlineFlow::assign_inline_sizes: floats in: {:?}", self.base.floats);
-
- {
- let inline_size = self.base.position.size.inline;
- let this = &mut *self;
- for fragment in this.fragments.fragments.mut_iter() {
- fragment.assign_replaced_inline_size_if_necessary(inline_size);
- }
- }
-
- assert!(self.base.children.len() == 0,
- "InlineFlow: should not have children flows in the current layout implementation.");
-
- // There are no child contexts, so stop here.
-
- // TODO(Issue #225): once there are 'inline-block' elements, this won't be
- // true. In that case, set the InlineBlockFragment's inline-size to the
- // shrink-to-fit inline-size, perform inline flow, and set the block
- // flow context's inline-size as the assigned inline-size of the
- // 'inline-block' fragment that created this flow before recursing.
- }
-
- /// Calculate and set the block-size of this flow. See CSS 2.1 § 10.6.1.
- fn assign_block_size(&mut self, ctx: &LayoutContext) {
- let _scope = layout_debug_scope!("inline::assign_block_size {:s}", self.base.debug_id());
-
- // Divide the fragments into lines.
- //
- // TODO(#226): Get the CSS `line-block-size` property from the containing block's style to
- // determine minimum line block-size.
- //
- // TODO(#226): Get the CSS `line-block-size` property from each non-replaced inline element to
- // determine its block-size for computing line block-size.
- //
- // TODO(pcwalton): Cache the line scanner?
- debug!("assign_block_size_inline: floats in: {:?}", self.base.floats);
-
- // assign block-size for inline fragments
- for fragment in self.fragments.fragments.mut_iter() {
- fragment.assign_replaced_block_size_if_necessary();
- }
-
- let scanner_floats = self.base.floats.clone();
- let mut scanner = LineBreaker::new(scanner_floats);
- scanner.scan_for_lines(self, ctx);
-
- // All lines use text alignment of the flow.
- let text_align = self.base.flags.text_align();
-
- // Now, go through each line and lay out the fragments inside.
- let mut line_distance_from_flow_block_start = Au(0);
- for line in self.lines.mut_iter() {
- // Lay out fragments horizontally.
- InlineFlow::set_horizontal_fragment_positions(&mut self.fragments, line, text_align);
-
- // Set the block-start y position of the current line.
- // `line_height_offset` is updated at the end of the previous loop.
- line.bounds.start.b = line_distance_from_flow_block_start;
-
- // Calculate the distance from the baseline to the block-start and block-end of the line.
- let mut largest_block_size_above_baseline = self.minimum_block_size_above_baseline;
- let mut largest_depth_below_baseline = self.minimum_depth_below_baseline;
-
- // Calculate the largest block-size among fragments with 'top' and 'bottom' values
- // respectively.
- let (mut largest_block_size_for_top_fragments, mut largest_block_size_for_bottom_fragments) =
- (Au(0), Au(0));
-
- for fragment_i in each_fragment_index(&line.range) {
- let fragment = self.fragments.fragments.get_mut(fragment_i.to_uint());
-
- let InlineMetrics {
- block_size_above_baseline: mut block_size_above_baseline,
- depth_below_baseline: mut depth_below_baseline,
- ascent
- } = fragment.inline_metrics(ctx);
-
- // To calculate text-top and text-bottom value when `vertical-align` is involved,
- // we should find the top and bottom of the content area of the parent fragment.
- // "Content area" is defined in CSS 2.1 § 10.6.1.
- //
- // TODO: We should extract em-box info from the font size of the parent and
- // calculate the distances from the baseline to the block-start and the block-end of the
- // parent's content area.
-
- // We should calculate the distance from baseline to the top of parent's content
- // area. But for now we assume it's the font size.
- //
- // CSS 2.1 does not state which font to use. Previous versions of the code used
- // the parent's font; this code uses the current font.
- let parent_text_top = fragment.style().get_font().font_size;
-
- // We should calculate the distance from baseline to the bottom of the parent's
- // content area. But for now we assume it's zero.
- let parent_text_bottom = Au(0);
-
- // Calculate the final block-size above the baseline for this fragment.
- //
- // The no-update flag decides whether `largest_block-size_for_top_fragments` and
- // `largest_block-size_for_bottom_fragments` are to be updated or not. This will be set
- // if and only if the fragment has `vertical-align` set to `top` or `bottom`.
- let (distance_from_baseline, no_update_flag) =
- InlineFlow::distance_from_baseline(
- fragment,
- ascent,
- parent_text_top,
- parent_text_bottom,
- &mut block_size_above_baseline,
- &mut depth_below_baseline,
- &mut largest_block_size_for_top_fragments,
- &mut largest_block_size_for_bottom_fragments,
- ctx);
-
- // Unless the current fragment has `vertical-align` set to `top` or `bottom`,
- // `largest_block-size_above_baseline` and `largest_depth_below_baseline` are updated.
- if !no_update_flag {
- largest_block_size_above_baseline = Au::max(block_size_above_baseline,
- largest_block_size_above_baseline);
- largest_depth_below_baseline = Au::max(depth_below_baseline,
- largest_depth_below_baseline);
- }
-
- // Temporarily use `fragment.border_box.start.b` to mean "the distance from the
- // baseline". We will assign the real value later.
- fragment.border_box.start.b = distance_from_baseline
- }
-
- // Calculate the distance from the baseline to the top of the largest fragment with a
- // value for `bottom`. Then, if necessary, update `largest_block-size_above_baseline`.
- largest_block_size_above_baseline =
- Au::max(largest_block_size_above_baseline,
- largest_block_size_for_bottom_fragments - largest_depth_below_baseline);
-
- // Calculate the distance from baseline to the bottom of the largest fragment with a value
- // for `top`. Then, if necessary, update `largest_depth_below_baseline`.
- largest_depth_below_baseline =
- Au::max(largest_depth_below_baseline,
- largest_block_size_for_top_fragments - largest_block_size_above_baseline);
-
- // Now, the distance from the logical block-start of the line to the baseline can be
- // computed as `largest_block-size_above_baseline`.
- let baseline_distance_from_block_start = largest_block_size_above_baseline;
-
- // Compute the final positions in the block direction of each fragment. Recall that
- // `fragment.border_box.start.b` was set to the distance from the baseline above.
- for fragment_i in each_fragment_index(&line.range) {
- let fragment = self.fragments.get_mut(fragment_i.to_uint());
- match fragment.vertical_align() {
- vertical_align::top => {
- fragment.border_box.start.b = fragment.border_box.start.b +
- line_distance_from_flow_block_start
- }
- vertical_align::bottom => {
- fragment.border_box.start.b = fragment.border_box.start.b +
- line_distance_from_flow_block_start + baseline_distance_from_block_start +
- largest_depth_below_baseline
- }
- _ => {
- fragment.border_box.start.b = fragment.border_box.start.b +
- line_distance_from_flow_block_start + baseline_distance_from_block_start
- }
- }
- }
-
- // This is used to set the block-start y position of the next line in the next loop.
- line.bounds.size.block = largest_block_size_above_baseline + largest_depth_below_baseline;
- line_distance_from_flow_block_start = line_distance_from_flow_block_start + line.bounds.size.block;
- } // End of `lines.each` loop.
-
- self.base.position.size.block = match self.lines.as_slice().last() {
- Some(ref last_line) => last_line.bounds.start.b + last_line.bounds.size.block,
- None => Au::new(0)
- };
-
- self.base.floats = scanner.floats();
- self.base.floats.translate(LogicalSize::new(
- self.base.writing_mode, Au::new(0), -self.base.position.size.block));
- }
-}
-
-impl fmt::Show for InlineFlow {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- try!(write!(f, "InlineFlow"));
- for (i, fragment) in self.fragments.fragments.iter().enumerate() {
- if i == 0 {
- try!(write!(f, ": {}", fragment))
- } else {
- try!(write!(f, ", {}", fragment))
- }
- }
- Ok(())
- }
-}
-
-#[deriving(Clone)]
-pub struct InlineFragmentContext {
- pub styles: Vec<Arc<ComputedValues>>,
-}
-
-impl InlineFragmentContext {
- pub fn new() -> InlineFragmentContext {
- InlineFragmentContext {
- styles: vec!()
- }
- }
-}
-
-/// BSize above the baseline, depth below the baseline, and ascent for a fragment. See CSS 2.1 §
-/// 10.8.1.
-pub struct InlineMetrics {
- pub block_size_above_baseline: Au,
- pub depth_below_baseline: Au,
- pub ascent: Au,
-}
-
-impl InlineMetrics {
- /// Calculates inline metrics from font metrics and line block-size per CSS 2.1 § 10.8.1.
- #[inline]
- pub fn from_font_metrics(font_metrics: &FontMetrics, line_height: Au) -> InlineMetrics {
- let leading = line_height - (font_metrics.ascent + font_metrics.descent);
- InlineMetrics {
- block_size_above_baseline: font_metrics.ascent + leading.scale_by(0.5),
- depth_below_baseline: font_metrics.descent + leading.scale_by(0.5),
- ascent: font_metrics.ascent,
- }
- }
-}
-
diff --git a/src/components/layout/layout.rs b/src/components/layout/layout.rs
deleted file mode 100644
index a9ee00319f7..00000000000
--- a/src/components/layout/layout.rs
+++ /dev/null
@@ -1,71 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#![crate_name = "layout"]
-#![crate_type = "rlib"]
-
-#![comment = "The Servo Parallel Browser Project"]
-#![license = "MPL"]
-
-#![feature(globs, macro_rules, phase, thread_local, unsafe_destructor)]
-
-#[phase(plugin, link)]
-extern crate log;
-
-extern crate debug;
-
-extern crate geom;
-extern crate gfx;
-extern crate layout_traits;
-extern crate script;
-extern crate script_traits;
-extern crate serialize;
-extern crate style;
-#[phase(plugin)]
-extern crate servo_macros = "macros";
-extern crate servo_net = "net";
-extern crate servo_msg = "msg";
-#[phase(plugin, link)]
-extern crate servo_util = "util";
-
-extern crate collections;
-extern crate green;
-extern crate libc;
-extern crate sync;
-extern crate url;
-
-// Listed first because of macro definitions
-pub mod layout_debug;
-
-pub mod block;
-pub mod construct;
-pub mod context;
-pub mod floats;
-pub mod flow;
-pub mod flow_list;
-pub mod flow_ref;
-pub mod fragment;
-pub mod layout_task;
-pub mod inline;
-pub mod model;
-pub mod parallel;
-pub mod table_wrapper;
-pub mod table;
-pub mod table_caption;
-pub mod table_colgroup;
-pub mod table_rowgroup;
-pub mod table_row;
-pub mod table_cell;
-pub mod text;
-pub mod util;
-pub mod incremental;
-pub mod wrapper;
-pub mod extra;
-
-pub mod css {
- mod node_util;
-
- pub mod matching;
- pub mod node_style;
-}
diff --git a/src/components/layout/layout_debug.rs b/src/components/layout/layout_debug.rs
deleted file mode 100644
index 58db599c9e2..00000000000
--- a/src/components/layout/layout_debug.rs
+++ /dev/null
@@ -1,126 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//! Supports writing a trace file created during each layout scope
-//! that can be viewed by an external tool to make layout debugging easier.
-
-#![macro_escape]
-
-use flow_ref::FlowRef;
-use serialize::json;
-use std::cell::RefCell;
-use std::io::File;
-use std::sync::atomics::{AtomicUint, SeqCst, INIT_ATOMIC_UINT};
-
-local_data_key!(state_key: RefCell<State>)
-
-static mut DEBUG_ID_COUNTER: AtomicUint = INIT_ATOMIC_UINT;
-
-pub struct Scope;
-
-#[macro_export]
-macro_rules! layout_debug_scope(
- ($($arg:tt)*) => (
- if cfg!(not(ndebug)) {
- layout_debug::Scope::new(format!($($arg)*))
- } else {
- layout_debug::Scope
- }
- )
-)
-
-#[deriving(Encodable)]
-struct ScopeData {
- name: String,
- pre: String,
- post: String,
- children: Vec<Box<ScopeData>>,
-}
-
-impl ScopeData {
- fn new(name: String, pre: String) -> ScopeData {
- ScopeData {
- name: name,
- pre: pre,
- post: String::new(),
- children: vec!(),
- }
- }
-}
-
-struct State {
- flow_root: FlowRef,
- scope_stack: Vec<Box<ScopeData>>,
-}
-
-/// A layout debugging scope. The entire state of the flow tree
-/// will be output at the beginning and end of this scope.
-impl Scope {
- pub fn new(name: String) -> Scope {
- let maybe_refcell = state_key.get();
- match maybe_refcell {
- Some(refcell) => {
- let mut state = refcell.borrow_mut();
- let flow_trace = json::encode(&state.flow_root.get());
- let data = box ScopeData::new(name, flow_trace);
- state.scope_stack.push(data);
- }
- None => {}
- }
- Scope
- }
-}
-
-#[cfg(not(ndebug))]
-impl Drop for Scope {
- fn drop(&mut self) {
- let maybe_refcell = state_key.get();
- match maybe_refcell {
- Some(refcell) => {
- let mut state = refcell.borrow_mut();
- let mut current_scope = state.scope_stack.pop().unwrap();
- current_scope.post = json::encode(&state.flow_root.get());
- let previous_scope = state.scope_stack.mut_last().unwrap();
- previous_scope.children.push(current_scope);
- }
- None => {}
- }
- }
-}
-
-/// Generate a unique ID. This is used for items such as Fragment
-/// which are often reallocated but represent essentially the
-/// same data.
-pub fn generate_unique_debug_id() -> uint {
- unsafe { DEBUG_ID_COUNTER.fetch_add(1, SeqCst) }
-}
-
-/// Begin a layout debug trace. If this has not been called,
-/// creating debug scopes has no effect.
-pub fn begin_trace(flow_root: FlowRef) {
- assert!(state_key.get().is_none());
-
- let flow_trace = json::encode(&flow_root.get());
- let state = State {
- scope_stack: vec![box ScopeData::new("root".to_string(), flow_trace)],
- flow_root: flow_root,
- };
- state_key.replace(Some(RefCell::new(state)));
-}
-
-/// End the debug layout trace. This will write the layout
-/// trace to disk in the current directory. The output
-/// file can then be viewed with an external tool.
-pub fn end_trace() {
- let task_state_cell = state_key.replace(None).unwrap();
- let mut task_state = task_state_cell.borrow_mut();
- assert!(task_state.scope_stack.len() == 1);
- let mut root_scope = task_state.scope_stack.pop().unwrap();
- root_scope.post = json::encode(&task_state.flow_root.get());
-
- let result = json::encode(&root_scope);
- let path = Path::new("layout_trace.json");
- let mut file = File::create(&path).unwrap();
- file.write_str(result.as_slice()).unwrap();
-}
diff --git a/src/components/layout/layout_task.rs b/src/components/layout/layout_task.rs
deleted file mode 100644
index c3c463408e6..00000000000
--- a/src/components/layout/layout_task.rs
+++ /dev/null
@@ -1,1020 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//! The layout task. Performs layout on the DOM, builds display lists and sends them to be
-//! rendered.
-
-use css::matching::{ApplicableDeclarations, MatchMethods};
-use css::node_style::StyledNode;
-use construct::{FlowConstructionResult, NoConstructionResult};
-use context::{LayoutContext, SharedLayoutContext};
-use flow::{Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils};
-use flow::{PreorderFlowTraversal, PostorderFlowTraversal};
-use flow;
-use flow_ref::FlowRef;
-use incremental::RestyleDamage;
-use layout_debug;
-use parallel::UnsafeFlow;
-use parallel;
-use util::{LayoutDataAccess, LayoutDataWrapper, OpaqueNodeMethods, ToGfxColor};
-use wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode};
-
-use collections::dlist::DList;
-use geom::point::Point2D;
-use geom::rect::Rect;
-use geom::size::Size2D;
-use gfx::display_list::{ClipDisplayItemClass, ContentStackingLevel, DisplayItem};
-use gfx::display_list::{DisplayItemIterator, DisplayList, OpaqueNode};
-use gfx::render_task::{RenderInitMsg, RenderChan, RenderLayer};
-use gfx::{render_task, color};
-use layout_traits;
-use layout_traits::{LayoutControlMsg, LayoutTaskFactory};
-use script::dom::bindings::js::JS;
-use script::dom::node::{ElementNodeTypeId, LayoutDataRef, Node};
-use script::dom::element::{HTMLBodyElementTypeId, HTMLHtmlElementTypeId};
-use script::layout_interface::{AddStylesheetMsg, ScriptLayoutChan};
-use script::layout_interface::{TrustedNodeAddress, ContentBoxesResponse, ExitNowMsg};
-use script::layout_interface::{ContentBoxResponse, HitTestResponse, MouseOverResponse};
-use script::layout_interface::{ContentChangedDocumentDamage, LayoutChan, Msg, PrepareToExitMsg};
-use script::layout_interface::{GetRPCMsg, LayoutRPC, ReapLayoutDataMsg, Reflow, UntrustedNodeAddress};
-use script::layout_interface::{ReflowForDisplay, ReflowMsg};
-use script_traits::{SendEventMsg, ReflowEvent, ReflowCompleteMsg, OpaqueScriptLayoutChannel, ScriptControlChan};
-use servo_msg::compositor_msg::Scrollable;
-use servo_msg::constellation_msg::{ConstellationChan, PipelineId, Failure, FailureMsg};
-use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg};
-use gfx::font_cache_task::{FontCacheTask};
-use servo_net::local_image_cache::{ImageResponder, LocalImageCache};
-use servo_util::geometry::Au;
-use servo_util::geometry;
-use servo_util::logical_geometry::LogicalPoint;
-use servo_util::opts::Opts;
-use servo_util::smallvec::{SmallVec, SmallVec1};
-use servo_util::time::{TimeProfilerChan, profile};
-use servo_util::time;
-use servo_util::task::spawn_named_with_send_on_failure;
-use servo_util::workqueue::WorkQueue;
-use std::comm::{channel, Sender, Receiver, Select};
-use std::mem;
-use std::ptr;
-use style::{AuthorOrigin, Stylesheet, Stylist};
-use style::iter_font_face_rules;
-use sync::{Arc, Mutex};
-use url::Url;
-
-/// Mutable data belonging to the LayoutTask.
-///
-/// This needs to be protected by a mutex so we can do fast RPCs.
-pub struct LayoutTaskData {
- /// The local image cache.
- pub local_image_cache: Arc<Mutex<LocalImageCache>>,
-
- /// The size of the viewport.
- pub screen_size: Size2D<Au>,
-
- /// A cached display list.
- pub display_list: Option<Arc<DisplayList>>,
-
- pub stylist: Box<Stylist>,
-
- /// The workers that we use for parallel operation.
- pub parallel_traversal: Option<WorkQueue<*const SharedLayoutContext, UnsafeFlow>>,
-
- /// The dirty rect. Used during display list construction.
- pub dirty: Rect<Au>,
-}
-
-/// Information needed by the layout task.
-pub struct LayoutTask {
- /// The ID of the pipeline that we belong to.
- pub id: PipelineId,
-
- /// The port on which we receive messages from the script task.
- pub port: Receiver<Msg>,
-
- /// The port on which we receive messages from the constellation
- pub pipeline_port: Receiver<LayoutControlMsg>,
-
- //// The channel to send messages to ourself.
- pub chan: LayoutChan,
-
- /// The channel on which messages can be sent to the constellation.
- pub constellation_chan: ConstellationChan,
-
- /// The channel on which messages can be sent to the script task.
- pub script_chan: ScriptControlChan,
-
- /// The channel on which messages can be sent to the painting task.
- pub render_chan: RenderChan,
-
- /// The channel on which messages can be sent to the time profiler.
- pub time_profiler_chan: TimeProfilerChan,
-
- /// The channel on which messages can be sent to the image cache.
- pub image_cache_task: ImageCacheTask,
-
- /// Public interface to the font cache task.
- pub font_cache_task: FontCacheTask,
-
- /// The command-line options.
- pub opts: Opts,
-
- /// A mutex to allow for fast, read-only RPC of layout's internal data
- /// structures, while still letting the LayoutTask modify them.
- ///
- /// All the other elements of this struct are read-only.
- pub rw_data: Arc<Mutex<LayoutTaskData>>,
-}
-
-/// The damage computation traversal.
-#[deriving(Clone)]
-struct ComputeDamageTraversal;
-
-impl PostorderFlowTraversal for ComputeDamageTraversal {
- #[inline]
- fn process(&mut self, flow: &mut Flow) -> bool {
- let mut damage = flow::base(flow).restyle_damage;
- for child in flow::child_iter(flow) {
- damage.insert(flow::base(child).restyle_damage.propagate_up())
- }
- flow::mut_base(flow).restyle_damage = damage;
- true
- }
-}
-
-/// Propagates restyle damage up and down the tree as appropriate.
-///
-/// FIXME(pcwalton): Merge this with flow tree building and/or other traversals.
-struct PropagateDamageTraversal {
- all_style_damage: bool,
-}
-
-impl PreorderFlowTraversal for PropagateDamageTraversal {
- #[inline]
- fn process(&mut self, flow: &mut Flow) -> bool {
- if self.all_style_damage {
- flow::mut_base(flow).restyle_damage.insert(RestyleDamage::all())
- }
- debug!("restyle damage = {:?}", flow::base(flow).restyle_damage);
-
- let prop = flow::base(flow).restyle_damage.propagate_down();
- if !prop.is_empty() {
- for kid_ctx in flow::child_iter(flow) {
- flow::mut_base(kid_ctx).restyle_damage.insert(prop)
- }
- }
- true
- }
-}
-
-/// The flow tree verification traversal. This is only on in debug builds.
-#[cfg(debug)]
-struct FlowTreeVerificationTraversal;
-
-#[cfg(debug)]
-impl PreorderFlowTraversal for FlowTreeVerificationTraversal {
- #[inline]
- fn process(&mut self, flow: &mut Flow) -> bool {
- let base = flow::base(flow);
- if !base.flags.is_leaf() && !base.flags.is_nonleaf() {
- println("flow tree verification failed: flow wasn't a leaf or a nonleaf!");
- flow.dump();
- fail!("flow tree verification failed")
- }
- true
- }
-}
-
-/// The bubble-inline-sizes traversal, the first part of layout computation. This computes preferred
-/// and intrinsic inline-sizes and bubbles them up the tree.
-pub struct BubbleISizesTraversal<'a> {
- pub layout_context: &'a LayoutContext<'a>,
-}
-
-impl<'a> PostorderFlowTraversal for BubbleISizesTraversal<'a> {
- #[inline]
- fn process(&mut self, flow: &mut Flow) -> bool {
- flow.bubble_inline_sizes(self.layout_context);
- true
- }
-
- // FIXME: We can't prune until we start reusing flows
- /*
- #[inline]
- fn should_prune(&mut self, flow: &mut Flow) -> bool {
- flow::mut_base(flow).restyle_damage.lacks(BubbleISizes)
- }
- */
-}
-
-/// The assign-inline-sizes traversal. In Gecko this corresponds to `Reflow`.
-pub struct AssignISizesTraversal<'a> {
- pub layout_context: &'a LayoutContext<'a>,
-}
-
-impl<'a> PreorderFlowTraversal for AssignISizesTraversal<'a> {
- #[inline]
- fn process(&mut self, flow: &mut Flow) -> bool {
- flow.assign_inline_sizes(self.layout_context);
- true
- }
-}
-
-/// The assign-block-sizes-and-store-overflow traversal, the last (and most expensive) part of layout
-/// computation. Determines the final block-sizes for all layout objects, computes positions, and
-/// computes overflow regions. In Gecko this corresponds to `FinishAndStoreOverflow`.
-pub struct AssignBSizesAndStoreOverflowTraversal<'a> {
- pub layout_context: &'a LayoutContext<'a>,
-}
-
-impl<'a> PostorderFlowTraversal for AssignBSizesAndStoreOverflowTraversal<'a> {
- #[inline]
- fn process(&mut self, flow: &mut Flow) -> bool {
- flow.assign_block_size(self.layout_context);
- // Skip store-overflow for absolutely positioned flows. That will be
- // done in a separate traversal.
- if !flow.is_store_overflow_delayed() {
- flow.store_overflow(self.layout_context);
- }
- true
- }
-
- #[inline]
- fn should_process(&mut self, flow: &mut Flow) -> bool {
- !flow::base(flow).flags.impacted_by_floats()
- }
-}
-
-/// The display list construction traversal.
-pub struct BuildDisplayListTraversal<'a> {
- layout_context: &'a LayoutContext<'a>,
-}
-
-impl<'a> BuildDisplayListTraversal<'a> {
- #[inline]
- fn process(&mut self, flow: &mut Flow) {
- flow.compute_absolute_position();
-
- for kid in flow::mut_base(flow).child_iter() {
- if !kid.is_absolutely_positioned() {
- self.process(kid)
- }
- }
-
- for absolute_descendant_link in flow::mut_base(flow).abs_descendants.iter() {
- self.process(absolute_descendant_link)
- }
-
- flow.build_display_list(self.layout_context)
- }
-}
-
-struct LayoutImageResponder {
- id: PipelineId,
- script_chan: ScriptControlChan,
-}
-
-impl ImageResponder for LayoutImageResponder {
- fn respond(&self) -> proc(ImageResponseMsg):Send {
- let id = self.id.clone();
- let script_chan = self.script_chan.clone();
- let f: proc(ImageResponseMsg):Send = proc(_) {
- let ScriptControlChan(chan) = script_chan;
- drop(chan.send_opt(SendEventMsg(id.clone(), ReflowEvent)))
- };
- f
- }
-}
-
-impl LayoutTaskFactory for LayoutTask {
- /// Spawns a new layout task.
- fn create(_phantom: Option<&mut LayoutTask>,
- id: PipelineId,
- chan: OpaqueScriptLayoutChannel,
- pipeline_port: Receiver<LayoutControlMsg>,
- constellation_chan: ConstellationChan,
- failure_msg: Failure,
- script_chan: ScriptControlChan,
- render_chan: RenderChan,
- img_cache_task: ImageCacheTask,
- font_cache_task: FontCacheTask,
- opts: Opts,
- time_profiler_chan: TimeProfilerChan,
- shutdown_chan: Sender<()>) {
- let ConstellationChan(con_chan) = constellation_chan.clone();
- spawn_named_with_send_on_failure("LayoutTask", proc() {
- { // Ensures layout task is destroyed before we send shutdown message
- let sender = chan.sender();
- let layout =
- LayoutTask::new(
- id,
- chan.receiver(),
- LayoutChan(sender),
- pipeline_port,
- constellation_chan,
- script_chan,
- render_chan,
- img_cache_task,
- font_cache_task,
- &opts,
- time_profiler_chan);
- layout.start();
- }
- shutdown_chan.send(());
- }, FailureMsg(failure_msg), con_chan, false);
- }
-}
-
-impl LayoutTask {
- /// Creates a new `LayoutTask` structure.
- fn new(id: PipelineId,
- port: Receiver<Msg>,
- chan: LayoutChan,
- pipeline_port: Receiver<LayoutControlMsg>,
- constellation_chan: ConstellationChan,
- script_chan: ScriptControlChan,
- render_chan: RenderChan,
- image_cache_task: ImageCacheTask,
- font_cache_task: FontCacheTask,
- opts: &Opts,
- time_profiler_chan: TimeProfilerChan)
- -> LayoutTask {
- let local_image_cache = Arc::new(Mutex::new(LocalImageCache::new(image_cache_task.clone())));
- let screen_size = Size2D(Au(0), Au(0));
- let parallel_traversal = if opts.layout_threads != 1 {
- Some(WorkQueue::new("LayoutWorker", opts.layout_threads, ptr::null()))
- } else {
- None
- };
-
- LayoutTask {
- id: id,
- port: port,
- pipeline_port: pipeline_port,
- chan: chan,
- constellation_chan: constellation_chan,
- script_chan: script_chan,
- render_chan: render_chan,
- time_profiler_chan: time_profiler_chan,
- image_cache_task: image_cache_task.clone(),
- font_cache_task: font_cache_task,
- opts: opts.clone(),
- rw_data: Arc::new(Mutex::new(
- LayoutTaskData {
- local_image_cache: local_image_cache,
- screen_size: screen_size,
- display_list: None,
- stylist: box Stylist::new(),
- parallel_traversal: parallel_traversal,
- dirty: Rect::zero(),
- })),
- }
- }
-
- /// Starts listening on the port.
- fn start(self) {
- while self.handle_request() {
- // Loop indefinitely.
- }
- }
-
- // Create a layout context for use in building display lists, hit testing, &c.
- fn build_shared_layout_context(&self, rw_data: &LayoutTaskData, reflow_root: &LayoutNode, url: &Url) -> SharedLayoutContext {
- SharedLayoutContext {
- image_cache: rw_data.local_image_cache.clone(),
- screen_size: rw_data.screen_size.clone(),
- constellation_chan: self.constellation_chan.clone(),
- layout_chan: self.chan.clone(),
- font_cache_task: self.font_cache_task.clone(),
- stylist: &*rw_data.stylist,
- url: (*url).clone(),
- reflow_root: OpaqueNodeMethods::from_layout_node(reflow_root),
- opts: self.opts.clone(),
- dirty: Rect::zero(),
- }
- }
-
- /// Receives and dispatches messages from the script and constellation tasks
- fn handle_request(&self) -> bool {
- enum PortToRead {
- Pipeline,
- Script,
- }
-
- let port_to_read = {
- let sel = Select::new();
- let mut port1 = sel.handle(&self.port);
- let mut port2 = sel.handle(&self.pipeline_port);
- unsafe {
- port1.add();
- port2.add();
- }
- let ret = sel.wait();
- if ret == port1.id() {
- Script
- } else if ret == port2.id() {
- Pipeline
- } else {
- fail!("invalid select result");
- }
- };
-
- match port_to_read {
- Pipeline => match self.pipeline_port.recv() {
- layout_traits::ExitNowMsg => self.handle_script_request(ExitNowMsg),
- },
- Script => {
- let msg = self.port.recv();
- self.handle_script_request(msg)
- }
- }
- }
-
- /// Receives and dispatches messages from the script task.
- fn handle_script_request(&self, request: Msg) -> bool {
- match request {
- AddStylesheetMsg(sheet) => self.handle_add_stylesheet(sheet),
- GetRPCMsg(response_chan) => {
- response_chan.send(
- box LayoutRPCImpl(
- self.rw_data.clone()) as Box<LayoutRPC + Send>);
- },
- ReflowMsg(data) => {
- profile(time::LayoutPerformCategory, self.time_profiler_chan.clone(), || {
- self.handle_reflow(&*data);
- });
- },
- ReapLayoutDataMsg(dead_layout_data) => {
- unsafe {
- LayoutTask::handle_reap_layout_data(dead_layout_data)
- }
- },
- PrepareToExitMsg(response_chan) => {
- debug!("layout: PrepareToExitMsg received");
- self.prepare_to_exit(response_chan);
- return false
- },
- ExitNowMsg => {
- debug!("layout: ExitNowMsg received");
- self.exit_now();
- return false
- }
- }
-
- true
- }
-
- /// Enters a quiescent state in which no new messages except for `ReapLayoutDataMsg` will be
- /// processed until an `ExitNowMsg` is received. A pong is immediately sent on the given
- /// response channel.
- fn prepare_to_exit(&self, response_chan: Sender<()>) {
- response_chan.send(());
- loop {
- match self.port.recv() {
- ReapLayoutDataMsg(dead_layout_data) => {
- unsafe {
- LayoutTask::handle_reap_layout_data(dead_layout_data)
- }
- }
- ExitNowMsg => {
- debug!("layout task is exiting...");
- self.exit_now();
- break
- }
- _ => {
- fail!("layout: message that wasn't `ExitNowMsg` received after \
- `PrepareToExitMsg`")
- }
- }
- }
- }
-
- /// Shuts down the layout task now. If there are any DOM nodes left, layout will now (safely)
- /// crash.
- fn exit_now(&self) {
- let (response_chan, response_port) = channel();
-
- {
- let mut rw_data = self.rw_data.lock();
- match rw_data.deref_mut().parallel_traversal {
- None => {}
- Some(ref mut traversal) => traversal.shutdown(),
- }
- }
-
- self.render_chan.send(render_task::ExitMsg(Some(response_chan)));
- response_port.recv()
- }
-
- fn handle_add_stylesheet(&self, sheet: Stylesheet) {
- // Find all font-face rules and notify the font cache of them.
- // GWTODO: Need to handle unloading web fonts (when we handle unloading stylesheets!)
- iter_font_face_rules(&sheet, |family, url| {
- self.font_cache_task.add_web_font(family.to_string(), url.clone());
- });
- let mut rw_data = self.rw_data.lock();
- rw_data.stylist.add_stylesheet(sheet, AuthorOrigin);
- }
-
- /// Retrieves the flow tree root from the root node.
- fn get_layout_root(&self, node: LayoutNode) -> FlowRef {
- let mut layout_data_ref = node.mutate_layout_data();
- let result = match &mut *layout_data_ref {
- &Some(ref mut layout_data) => {
- mem::replace(&mut layout_data.data.flow_construction_result, NoConstructionResult)
- }
- &None => fail!("no layout data for root node"),
- };
- let mut flow = match result {
- FlowConstructionResult(mut flow, abs_descendants) => {
- // Note: Assuming that the root has display 'static' (as per
- // CSS Section 9.3.1). Otherwise, if it were absolutely
- // positioned, it would return a reference to itself in
- // `abs_descendants` and would lead to a circular reference.
- // Set Root as CB for any remaining absolute descendants.
- flow.set_abs_descendants(abs_descendants);
- flow
- }
- _ => fail!("Flow construction didn't result in a flow at the root of the tree!"),
- };
- flow.get_mut().mark_as_root();
- flow
- }
-
- /// Performs layout constraint solving.
- ///
- /// This corresponds to `Reflow()` in Gecko and `layout()` in WebKit/Blink and should be
- /// benchmarked against those two. It is marked `#[inline(never)]` to aid profiling.
- #[inline(never)]
- fn solve_constraints<'a>(&self,
- layout_root: &mut Flow,
- layout_context: &'a LayoutContext<'a>) {
- let _scope = layout_debug_scope!("solve_constraints");
-
- if layout_context.shared.opts.bubble_inline_sizes_separately {
- let mut traversal = BubbleISizesTraversal {
- layout_context: layout_context,
- };
- layout_root.traverse_postorder(&mut traversal);
- }
-
- // FIXME(kmc): We want to prune nodes without the Reflow restyle damage
- // bit, but FloatContext values can't be reused, so we need to
- // recompute them every time.
- // NOTE: this currently computes borders, so any pruning should separate that operation
- // out.
- {
- let mut traversal = AssignISizesTraversal {
- layout_context: layout_context,
- };
- layout_root.traverse_preorder(&mut traversal);
- }
-
- // FIXME(pcwalton): Prune this pass as well.
- {
- let mut traversal = AssignBSizesAndStoreOverflowTraversal {
- layout_context: layout_context,
- };
- layout_root.traverse_postorder(&mut traversal);
- }
- }
-
- /// Performs layout constraint solving in parallel.
- ///
- /// This corresponds to `Reflow()` in Gecko and `layout()` in WebKit/Blink and should be
- /// benchmarked against those two. It is marked `#[inline(never)]` to aid profiling.
- #[inline(never)]
- fn solve_constraints_parallel(&self,
- rw_data: &mut LayoutTaskData,
- layout_root: &mut FlowRef,
- shared_layout_context: &SharedLayoutContext) {
- if shared_layout_context.opts.bubble_inline_sizes_separately {
- let mut traversal = BubbleISizesTraversal {
- layout_context: &LayoutContext::new(shared_layout_context),
- };
- layout_root.get_mut().traverse_postorder(&mut traversal);
- }
-
- match rw_data.parallel_traversal {
- None => fail!("solve_contraints_parallel() called with no parallel traversal ready"),
- Some(ref mut traversal) => {
- // NOTE: this currently computes borders, so any pruning should separate that
- // operation out.
- parallel::traverse_flow_tree_preorder(layout_root,
- self.time_profiler_chan.clone(),
- shared_layout_context,
- traversal);
- }
- }
- }
-
- /// Verifies that every node was either marked as a leaf or as a nonleaf in the flow tree.
- /// This is only on in debug builds.
- #[inline(never)]
- #[cfg(debug)]
- fn verify_flow_tree(&self, layout_root: &mut FlowRef) {
- let mut traversal = FlowTreeVerificationTraversal;
- layout_root.traverse_preorder(&mut traversal);
- }
-
- #[cfg(not(debug))]
- fn verify_flow_tree(&self, _: &mut FlowRef) {
- }
-
- /// The high-level routine that performs layout tasks.
- fn handle_reflow(&self, data: &Reflow) {
- // FIXME: Isolate this transmutation into a "bridge" module.
- // FIXME(rust#16366): The following line had to be moved because of a
- // rustc bug. It should be in the next unsafe block.
- let mut node: JS<Node> = unsafe { JS::from_trusted_node_address(data.document_root) };
- let node: &mut LayoutNode = unsafe {
- mem::transmute(&mut node)
- };
-
- debug!("layout: received layout request for: {:s}", data.url.serialize());
- debug!("layout: damage is {:?}", data.damage);
- debug!("layout: parsed Node tree");
- debug!("{:?}", node.dump());
-
- let mut rw_data = self.rw_data.lock();
-
- {
- // Reset the image cache.
- let mut local_image_cache = rw_data.local_image_cache.lock();
- local_image_cache.next_round(self.make_on_image_available_cb());
- }
-
- // true => Do the reflow with full style damage, because content
- // changed or the window was resized.
- let mut all_style_damage = match data.damage.level {
- ContentChangedDocumentDamage => true,
- _ => false
- };
-
- // TODO: Calculate the "actual viewport":
- // http://www.w3.org/TR/css-device-adapt/#actual-viewport
- let viewport_size = data.window_size.initial_viewport;
-
- let current_screen_size = Size2D(Au::from_frac32_px(viewport_size.width.get()),
- Au::from_frac32_px(viewport_size.height.get()));
- if rw_data.screen_size != current_screen_size {
- all_style_damage = true
- }
- rw_data.screen_size = current_screen_size;
-
- // Create a layout context for use throughout the following passes.
- let mut shared_layout_ctx = self.build_shared_layout_context(rw_data.deref(), node, &data.url);
-
- let mut layout_root = profile(time::LayoutStyleRecalcCategory,
- self.time_profiler_chan.clone(),
- || {
- // Perform CSS selector matching and flow construction.
- let rw_data = rw_data.deref_mut();
- match rw_data.parallel_traversal {
- None => {
- let layout_ctx = LayoutContext::new(&shared_layout_ctx);
- let mut applicable_declarations = ApplicableDeclarations::new();
- node.recalc_style_for_subtree(&*rw_data.stylist,
- &layout_ctx,
- &mut applicable_declarations,
- None)
- }
- Some(ref mut traversal) => {
- parallel::recalc_style_for_subtree(node, &mut shared_layout_ctx, traversal)
- }
- }
-
- self.get_layout_root((*node).clone())
- });
-
- // Verification of the flow tree, which ensures that all nodes were either marked as leaves
- // or as non-leaves. This becomes a no-op in release builds. (It is inconsequential to
- // memory safety but is a useful debugging tool.)
- self.verify_flow_tree(&mut layout_root);
-
- if self.opts.trace_layout {
- layout_debug::begin_trace(layout_root.clone());
- }
-
- // Propagate damage.
- profile(time::LayoutDamagePropagateCategory, self.time_profiler_chan.clone(), || {
- layout_root.get_mut().traverse_preorder(&mut PropagateDamageTraversal {
- all_style_damage: all_style_damage
- });
- layout_root.get_mut().traverse_postorder(&mut ComputeDamageTraversal.clone());
- });
-
- // Perform the primary layout passes over the flow tree to compute the locations of all
- // the boxes.
- profile(time::LayoutMainCategory, self.time_profiler_chan.clone(), || {
- let rw_data = rw_data.deref_mut();
- match rw_data.parallel_traversal {
- None => {
- // Sequential mode.
- let layout_ctx = LayoutContext::new(&shared_layout_ctx);
- self.solve_constraints(layout_root.get_mut(), &layout_ctx)
- }
- Some(_) => {
- // Parallel mode.
- self.solve_constraints_parallel(rw_data, &mut layout_root, &mut shared_layout_ctx)
- }
- }
- });
-
- // Build the display list if necessary, and send it to the renderer.
- if data.goal == ReflowForDisplay {
- let writing_mode = flow::base(layout_root.get()).writing_mode;
- profile(time::LayoutDispListBuildCategory, self.time_profiler_chan.clone(), || {
- shared_layout_ctx.dirty = flow::base(layout_root.get()).position.to_physical(
- writing_mode, rw_data.screen_size);
- flow::mut_base(layout_root.get_mut()).abs_position =
- LogicalPoint::zero(writing_mode).to_physical(writing_mode, rw_data.screen_size);
-
- let rw_data = rw_data.deref_mut();
- match rw_data.parallel_traversal {
- None => {
- let layout_ctx = LayoutContext::new(&shared_layout_ctx);
- let mut traversal = BuildDisplayListTraversal {
- layout_context: &layout_ctx,
- };
- traversal.process(layout_root.get_mut());
- }
- Some(ref mut traversal) => {
- parallel::build_display_list_for_subtree(&mut layout_root,
- self.time_profiler_chan.clone(),
- &mut shared_layout_ctx,
- traversal);
- }
- }
-
- let root_display_list =
- mem::replace(&mut flow::mut_base(layout_root.get_mut()).display_list,
- DisplayList::new());
- root_display_list.debug();
- let display_list = Arc::new(root_display_list.flatten(ContentStackingLevel));
-
- // FIXME(pcwalton): This is really ugly and can't handle overflow: scroll. Refactor
- // it with extreme prejudice.
- let mut color = color::rgba(1.0, 1.0, 1.0, 1.0);
- for child in node.traverse_preorder() {
- if child.type_id() == Some(ElementNodeTypeId(HTMLHtmlElementTypeId)) ||
- child.type_id() == Some(ElementNodeTypeId(HTMLBodyElementTypeId)) {
- let element_bg_color = {
- let thread_safe_child = ThreadSafeLayoutNode::new(&child);
- thread_safe_child.style()
- .resolve_color(thread_safe_child.style()
- .get_background()
- .background_color)
- .to_gfx_color()
- };
- match element_bg_color {
- color::rgba(0., 0., 0., 0.) => {}
- _ => {
- color = element_bg_color;
- break;
- }
- }
- }
- }
-
- let root_size = {
- let root_flow = flow::base(layout_root.get());
- root_flow.position.size.to_physical(root_flow.writing_mode)
- };
- let root_size = Size2D(root_size.width.to_nearest_px() as uint,
- root_size.height.to_nearest_px() as uint);
- let render_layer = RenderLayer {
- id: layout_root.get().layer_id(0),
- display_list: display_list.clone(),
- position: Rect(Point2D(0u, 0u), root_size),
- background_color: color,
- scroll_policy: Scrollable,
- };
-
- rw_data.display_list = Some(display_list.clone());
-
- // TODO(pcwalton): Eventually, when we have incremental reflow, this will have to
- // be smarter in order to handle retained layer contents properly from reflow to
- // reflow.
- let mut layers = SmallVec1::new();
- layers.push(render_layer);
- for layer in mem::replace(&mut flow::mut_base(layout_root.get_mut()).layers,
- DList::new()).move_iter() {
- layers.push(layer)
- }
-
- debug!("Layout done!");
-
- self.render_chan.send(RenderInitMsg(layers));
- });
- }
-
- if self.opts.trace_layout {
- layout_debug::end_trace();
- }
-
- // Tell script that we're done.
- //
- // FIXME(pcwalton): This should probably be *one* channel, but we can't fix this without
- // either select or a filtered recv() that only looks for messages of a given type.
- data.script_join_chan.send(());
- let ScriptControlChan(ref chan) = data.script_chan;
- chan.send(ReflowCompleteMsg(self.id, data.id));
- }
-
-
- // When images can't be loaded in time to display they trigger
- // this callback in some task somewhere. This will send a message
- // to the script task, and ultimately cause the image to be
- // re-requested. We probably don't need to go all the way back to
- // the script task for this.
- fn make_on_image_available_cb(&self) -> Box<ImageResponder+Send> {
- // This has a crazy signature because the image cache needs to
- // make multiple copies of the callback, and the dom event
- // channel is not a copyable type, so this is actually a
- // little factory to produce callbacks
- box LayoutImageResponder {
- id: self.id.clone(),
- script_chan: self.script_chan.clone(),
- } as Box<ImageResponder+Send>
- }
-
- /// Handles a message to destroy layout data. Layout data must be destroyed on *this* task
- /// because it contains local managed pointers.
- unsafe fn handle_reap_layout_data(layout_data: LayoutDataRef) {
- let mut layout_data_ref = layout_data.borrow_mut();
- let _: Option<LayoutDataWrapper> = mem::transmute(
- mem::replace(&mut *layout_data_ref, None));
- }
-}
-
-struct LayoutRPCImpl(Arc<Mutex<LayoutTaskData>>);
-
-impl LayoutRPC for LayoutRPCImpl {
- // The neat thing here is that in order to answer the following two queries we only
- // need to compare nodes for equality. Thus we can safely work only with `OpaqueNode`.
- fn content_box(&self, node: TrustedNodeAddress) -> ContentBoxResponse {
- let node: OpaqueNode = OpaqueNodeMethods::from_script_node(node);
- fn union_boxes_for_node(accumulator: &mut Option<Rect<Au>>,
- mut iter: DisplayItemIterator,
- node: OpaqueNode) {
- for item in iter {
- union_boxes_for_node(accumulator, item.children(), node);
- if item.base().node == node {
- match *accumulator {
- None => *accumulator = Some(item.base().bounds),
- Some(ref mut acc) => *acc = acc.union(&item.base().bounds),
- }
- }
- }
- }
-
- let mut rect = None;
- {
- let &LayoutRPCImpl(ref rw_data) = self;
- let rw_data = rw_data.lock();
- match rw_data.display_list {
- None => fail!("no display list!"),
- Some(ref display_list) => {
- union_boxes_for_node(&mut rect, display_list.iter(), node)
- }
- }
- }
- ContentBoxResponse(rect.unwrap_or(Rect::zero()))
- }
-
- /// Requests the dimensions of all the content boxes, as in the `getClientRects()` call.
- fn content_boxes(&self, node: TrustedNodeAddress) -> ContentBoxesResponse {
- let node: OpaqueNode = OpaqueNodeMethods::from_script_node(node);
-
- fn add_boxes_for_node(accumulator: &mut Vec<Rect<Au>>,
- mut iter: DisplayItemIterator,
- node: OpaqueNode) {
- for item in iter {
- add_boxes_for_node(accumulator, item.children(), node);
- if item.base().node == node {
- accumulator.push(item.base().bounds)
- }
- }
- }
-
- let mut boxes = vec!();
- {
- let &LayoutRPCImpl(ref rw_data) = self;
- let rw_data = rw_data.lock();
- match rw_data.display_list {
- None => fail!("no display list!"),
- Some(ref display_list) => {
- add_boxes_for_node(&mut boxes, display_list.iter(), node)
- }
- }
- }
- ContentBoxesResponse(boxes)
- }
-
- /// Requests the node containing the point of interest
- fn hit_test(&self, _: TrustedNodeAddress, point: Point2D<f32>) -> Result<HitTestResponse, ()> {
- fn hit_test<'a,I:Iterator<&'a DisplayItem>>(x: Au, y: Au, mut iterator: I)
- -> Option<HitTestResponse> {
- for item in iterator {
- match *item {
- ClipDisplayItemClass(ref cc) => {
- if geometry::rect_contains_point(cc.base.bounds, Point2D(x, y)) {
- let ret = hit_test(x, y, cc.children.list.iter().rev());
- if !ret.is_none() {
- return ret
- }
- }
- continue
- }
- _ => {}
- }
-
- let bounds = item.bounds();
-
- // TODO(tikue): This check should really be performed by a method of
- // DisplayItem.
- if x < bounds.origin.x + bounds.size.width &&
- bounds.origin.x <= x &&
- y < bounds.origin.y + bounds.size.height &&
- bounds.origin.y <= y {
- return Some(HitTestResponse(item.base()
- .node
- .to_untrusted_node_address()))
- }
- }
- let ret: Option<HitTestResponse> = None;
- ret
- }
- let (x, y) = (Au::from_frac_px(point.x as f64),
- Au::from_frac_px(point.y as f64));
-
- let resp = {
- let &LayoutRPCImpl(ref rw_data) = self;
- let rw_data = rw_data.lock();
- match rw_data.display_list {
- None => fail!("no display list!"),
- Some(ref display_list) => hit_test(x, y, display_list.list.iter().rev()),
- }
- };
-
- if resp.is_some() {
- return Ok(resp.unwrap());
- }
- Err(())
- }
-
- fn mouse_over(&self, _: TrustedNodeAddress, point: Point2D<f32>) -> Result<MouseOverResponse, ()> {
- fn mouse_over_test<'a,
- I:Iterator<&'a DisplayItem>>(
- x: Au,
- y: Au,
- mut iterator: I,
- result: &mut Vec<UntrustedNodeAddress>) {
- for item in iterator {
- match *item {
- ClipDisplayItemClass(ref cc) => {
- mouse_over_test(x, y, cc.children.list.iter().rev(), result);
- }
- _ => {
- let bounds = item.bounds();
-
- // TODO(tikue): This check should really be performed by a method
- // of DisplayItem.
- if x < bounds.origin.x + bounds.size.width &&
- bounds.origin.x <= x &&
- y < bounds.origin.y + bounds.size.height &&
- bounds.origin.y <= y {
- result.push(item.base()
- .node
- .to_untrusted_node_address());
- }
- }
- }
- }
- }
-
- let mut mouse_over_list: Vec<UntrustedNodeAddress> = vec!();
- let (x, y) = (Au::from_frac_px(point.x as f64), Au::from_frac_px(point.y as f64));
-
- {
- let &LayoutRPCImpl(ref rw_data) = self;
- let rw_data = rw_data.lock();
- match rw_data.display_list {
- None => fail!("no display list!"),
- Some(ref display_list) => {
- mouse_over_test(x,
- y,
- display_list.list.iter().rev(),
- &mut mouse_over_list);
- }
- };
- }
-
- if mouse_over_list.is_empty() {
- Err(())
- } else {
- Ok(MouseOverResponse(mouse_over_list))
- }
- }
-}
diff --git a/src/components/layout/model.rs b/src/components/layout/model.rs
deleted file mode 100644
index 23647b1b77d..00000000000
--- a/src/components/layout/model.rs
+++ /dev/null
@@ -1,337 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//! Borders, padding, and margins.
-
-#![deny(unsafe_block)]
-
-use fragment::Fragment;
-
-use computed = style::computed_values;
-use geom::SideOffsets2D;
-use style::computed_values::{LPA_Auto, LPA_Length, LPA_Percentage, LP_Length, LP_Percentage};
-use style::ComputedValues;
-use servo_util::geometry::Au;
-use servo_util::geometry;
-use servo_util::logical_geometry::LogicalMargin;
-use std::fmt;
-
-/// A collapsible margin. See CSS 2.1 § 8.3.1.
-pub struct AdjoiningMargins {
- /// The value of the greatest positive margin.
- pub most_positive: Au,
-
- /// The actual value (not the absolute value) of the negative margin with the largest absolute
- /// value. Since this is not the absolute value, this is always zero or negative.
- pub most_negative: Au,
-}
-
-impl AdjoiningMargins {
- pub fn new() -> AdjoiningMargins {
- AdjoiningMargins {
- most_positive: Au(0),
- most_negative: Au(0),
- }
- }
-
- pub fn from_margin(margin_value: Au) -> AdjoiningMargins {
- if margin_value >= Au(0) {
- AdjoiningMargins {
- most_positive: margin_value,
- most_negative: Au(0),
- }
- } else {
- AdjoiningMargins {
- most_positive: Au(0),
- most_negative: margin_value,
- }
- }
- }
-
- pub fn union(&mut self, other: AdjoiningMargins) {
- self.most_positive = geometry::max(self.most_positive, other.most_positive);
- self.most_negative = geometry::min(self.most_negative, other.most_negative)
- }
-
- pub fn collapse(&self) -> Au {
- self.most_positive + self.most_negative
- }
-}
-
-/// Represents the block-start and block-end margins of a flow with collapsible margins. See CSS 2.1 § 8.3.1.
-pub enum CollapsibleMargins {
- /// Margins may not collapse with this flow.
- NoCollapsibleMargins(Au, Au),
-
- /// Both the block-start and block-end margins (specified here in that order) may collapse, but the
- /// margins do not collapse through this flow.
- MarginsCollapse(AdjoiningMargins, AdjoiningMargins),
-
- /// Margins collapse *through* this flow. This means, essentially, that the flow doesn’t
- /// have any border, padding, or out-of-flow (floating or positioned) content
- MarginsCollapseThrough(AdjoiningMargins),
-}
-
-impl CollapsibleMargins {
- pub fn new() -> CollapsibleMargins {
- NoCollapsibleMargins(Au(0), Au(0))
- }
-}
-
-enum FinalMarginState {
- MarginsCollapseThroughFinalMarginState,
- BottomMarginCollapsesFinalMarginState,
-}
-
-pub struct MarginCollapseInfo {
- pub state: MarginCollapseState,
- pub block_start_margin: AdjoiningMargins,
- pub margin_in: AdjoiningMargins,
-}
-
-impl MarginCollapseInfo {
- /// TODO(#2012, pcwalton): Remove this method once `fragment` is not an `Option`.
- pub fn new() -> MarginCollapseInfo {
- MarginCollapseInfo {
- state: AccumulatingCollapsibleTopMargin,
- block_start_margin: AdjoiningMargins::new(),
- margin_in: AdjoiningMargins::new(),
- }
- }
-
- pub fn initialize_block_start_margin(&mut self,
- fragment: &Fragment,
- can_collapse_block_start_margin_with_kids: bool) {
- if !can_collapse_block_start_margin_with_kids {
- self.state = AccumulatingMarginIn
- }
-
- self.block_start_margin = AdjoiningMargins::from_margin(fragment.margin.block_start)
- }
-
- pub fn finish_and_compute_collapsible_margins(mut self,
- fragment: &Fragment,
- can_collapse_block_end_margin_with_kids: bool)
- -> (CollapsibleMargins, Au) {
- let state = match self.state {
- AccumulatingCollapsibleTopMargin => {
- match fragment.style().content_block_size() {
- LPA_Auto | LPA_Length(Au(0)) | LPA_Percentage(0.) => {
- match fragment.style().min_block_size() {
- LP_Length(Au(0)) | LP_Percentage(0.) => {
- MarginsCollapseThroughFinalMarginState
- },
- _ => {
- // If the fragment has non-zero min-block-size, margins may not
- // collapse through it.
- BottomMarginCollapsesFinalMarginState
- }
- }
- },
- _ => {
- // If the fragment has an explicitly specified block-size, margins may not
- // collapse through it.
- BottomMarginCollapsesFinalMarginState
- }
- }
- }
- AccumulatingMarginIn => BottomMarginCollapsesFinalMarginState,
- };
-
- // Different logic is needed here depending on whether this flow can collapse its block-end
- // margin with its children.
- let block_end_margin = fragment.margin.block_end;
- if !can_collapse_block_end_margin_with_kids {
- match state {
- MarginsCollapseThroughFinalMarginState => {
- let advance = self.block_start_margin.collapse();
- self.margin_in.union(AdjoiningMargins::from_margin(block_end_margin));
- (MarginsCollapse(self.block_start_margin, self.margin_in), advance)
- }
- BottomMarginCollapsesFinalMarginState => {
- let advance = self.margin_in.collapse();
- self.margin_in.union(AdjoiningMargins::from_margin(block_end_margin));
- (MarginsCollapse(self.block_start_margin, self.margin_in), advance)
- }
- }
- } else {
- match state {
- MarginsCollapseThroughFinalMarginState => {
- self.block_start_margin.union(AdjoiningMargins::from_margin(block_end_margin));
- (MarginsCollapseThrough(self.block_start_margin), Au(0))
- }
- BottomMarginCollapsesFinalMarginState => {
- self.margin_in.union(AdjoiningMargins::from_margin(block_end_margin));
- (MarginsCollapse(self.block_start_margin, self.margin_in), Au(0))
- }
- }
- }
- }
-
- pub fn current_float_ceiling(&mut self) -> Au {
- match self.state {
- AccumulatingCollapsibleTopMargin => self.block_start_margin.collapse(),
- AccumulatingMarginIn => self.margin_in.collapse(),
- }
- }
-
- /// Adds the child's potentially collapsible block-start margin to the current margin state and
- /// advances the Y offset by the appropriate amount to handle that margin. Returns the amount
- /// that should be added to the Y offset during block layout.
- pub fn advance_block_start_margin(&mut self, child_collapsible_margins: &CollapsibleMargins) -> Au {
- match (self.state, *child_collapsible_margins) {
- (AccumulatingCollapsibleTopMargin, NoCollapsibleMargins(block_start, _)) => {
- self.state = AccumulatingMarginIn;
- block_start
- }
- (AccumulatingCollapsibleTopMargin, MarginsCollapse(block_start, _)) => {
- self.block_start_margin.union(block_start);
- self.state = AccumulatingMarginIn;
- Au(0)
- }
- (AccumulatingMarginIn, NoCollapsibleMargins(block_start, _)) => {
- let previous_margin_value = self.margin_in.collapse();
- self.margin_in = AdjoiningMargins::new();
- previous_margin_value + block_start
- }
- (AccumulatingMarginIn, MarginsCollapse(block_start, _)) => {
- self.margin_in.union(block_start);
- let margin_value = self.margin_in.collapse();
- self.margin_in = AdjoiningMargins::new();
- margin_value
- }
- (_, MarginsCollapseThrough(_)) => {
- // For now, we ignore this; this will be handled by `advance_block-end_margin` below.
- Au(0)
- }
- }
- }
-
- /// Adds the child's potentially collapsible block-end margin to the current margin state and
- /// advances the Y offset by the appropriate amount to handle that margin. Returns the amount
- /// that should be added to the Y offset during block layout.
- pub fn advance_block_end_margin(&mut self, child_collapsible_margins: &CollapsibleMargins) -> Au {
- match (self.state, *child_collapsible_margins) {
- (AccumulatingCollapsibleTopMargin, NoCollapsibleMargins(..)) |
- (AccumulatingCollapsibleTopMargin, MarginsCollapse(..)) => {
- // Can't happen because the state will have been replaced with
- // `AccumulatingMarginIn` above.
- fail!("should not be accumulating collapsible block_start margins anymore!")
- }
- (AccumulatingCollapsibleTopMargin, MarginsCollapseThrough(margin)) => {
- self.block_start_margin.union(margin);
- Au(0)
- }
- (AccumulatingMarginIn, NoCollapsibleMargins(_, block_end)) => {
- assert_eq!(self.margin_in.most_positive, Au(0));
- assert_eq!(self.margin_in.most_negative, Au(0));
- block_end
- }
- (AccumulatingMarginIn, MarginsCollapse(_, block_end)) |
- (AccumulatingMarginIn, MarginsCollapseThrough(block_end)) => {
- self.margin_in.union(block_end);
- Au(0)
- }
- }
- }
-}
-
-pub enum MarginCollapseState {
- AccumulatingCollapsibleTopMargin,
- AccumulatingMarginIn,
-}
-
-/// Intrinsic inline-sizes, which consist of minimum and preferred.
-#[deriving(Encodable)]
-pub struct IntrinsicISizes {
- /// The *minimum inline-size* of the content.
- pub minimum_inline_size: Au,
- /// The *preferred inline-size* of the content.
- pub preferred_inline_size: Au,
- /// The estimated sum of borders, padding, and margins. Some calculations use this information
- /// when computing intrinsic inline-sizes.
- pub surround_inline_size: Au,
-}
-
-impl fmt::Show for IntrinsicISizes {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "min={}, pref={}, surr={}", self.minimum_inline_size, self.preferred_inline_size, self.surround_inline_size)
- }
-}
-
-impl IntrinsicISizes {
- pub fn new() -> IntrinsicISizes {
- IntrinsicISizes {
- minimum_inline_size: Au(0),
- preferred_inline_size: Au(0),
- surround_inline_size: Au(0),
- }
- }
-
- pub fn total_minimum_inline_size(&self) -> Au {
- self.minimum_inline_size + self.surround_inline_size
- }
-
- pub fn total_preferred_inline_size(&self) -> Au {
- self.preferred_inline_size + self.surround_inline_size
- }
-}
-
-/// Useful helper data type when computing values for blocks and positioned elements.
-pub enum MaybeAuto {
- Auto,
- Specified(Au),
-}
-
-impl MaybeAuto {
- #[inline]
- pub fn from_style(length: computed::LengthOrPercentageOrAuto, containing_length: Au)
- -> MaybeAuto {
- match length {
- computed::LPA_Auto => Auto,
- computed::LPA_Percentage(percent) => Specified(containing_length.scale_by(percent)),
- computed::LPA_Length(length) => Specified(length)
- }
- }
-
- #[inline]
- pub fn specified_or_default(&self, default: Au) -> Au {
- match *self {
- Auto => default,
- Specified(value) => value,
- }
- }
-
- #[inline]
- pub fn specified_or_zero(&self) -> Au {
- self.specified_or_default(Au::new(0))
- }
-}
-
-pub fn specified_or_none(length: computed::LengthOrPercentageOrNone, containing_length: Au) -> Option<Au> {
- match length {
- computed::LPN_None => None,
- computed::LPN_Percentage(percent) => Some(containing_length.scale_by(percent)),
- computed::LPN_Length(length) => Some(length),
- }
-}
-
-pub fn specified(length: computed::LengthOrPercentage, containing_length: Au) -> Au {
- match length {
- computed::LP_Length(length) => length,
- computed::LP_Percentage(p) => containing_length.scale_by(p)
- }
-}
-
-#[inline]
-pub fn padding_from_style(style: &ComputedValues, containing_block_inline_size: Au)
- -> LogicalMargin<Au> {
- let padding_style = style.get_padding();
- LogicalMargin::from_physical(style.writing_mode, SideOffsets2D::new(
- specified(padding_style.padding_top, containing_block_inline_size),
- specified(padding_style.padding_right, containing_block_inline_size),
- specified(padding_style.padding_bottom, containing_block_inline_size),
- specified(padding_style.padding_left, containing_block_inline_size)))
-}
-
diff --git a/src/components/layout/parallel.rs b/src/components/layout/parallel.rs
deleted file mode 100644
index a2786e8ba91..00000000000
--- a/src/components/layout/parallel.rs
+++ /dev/null
@@ -1,561 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//! Implements parallel traversals over the DOM and flow trees.
-//!
-//! This code is highly unsafe. Keep this file small and easy to audit.
-
-use css::matching::{ApplicableDeclarations, CannotShare, MatchMethods, StyleWasShared};
-use construct::FlowConstructor;
-use context::{LayoutContext, SharedLayoutContext};
-use extra::LayoutAuxMethods;
-use flow::{Flow, MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal};
-use flow;
-use flow_ref::FlowRef;
-use layout_task::{AssignBSizesAndStoreOverflowTraversal, AssignISizesTraversal};
-use layout_task::{BubbleISizesTraversal};
-use util::{LayoutDataAccess, LayoutDataWrapper, OpaqueNodeMethods};
-use wrapper::{layout_node_to_unsafe_layout_node, layout_node_from_unsafe_layout_node, LayoutNode, PostorderNodeMutTraversal};
-use wrapper::{ThreadSafeLayoutNode, UnsafeLayoutNode};
-
-use gfx::display_list::OpaqueNode;
-use servo_util::time::{TimeProfilerChan, profile};
-use servo_util::time;
-use servo_util::workqueue::{WorkQueue, WorkUnit, WorkerProxy};
-use std::mem;
-use std::ptr;
-use std::sync::atomics::{AtomicInt, Relaxed, SeqCst};
-use style::TNode;
-
-#[allow(dead_code)]
-fn static_assertion(node: UnsafeLayoutNode) {
- unsafe {
- let _: UnsafeFlow = ::std::intrinsics::transmute(node);
- }
-}
-
-/// Vtable + pointer representation of a Flow trait object.
-pub type UnsafeFlow = (uint, uint);
-
-fn null_unsafe_flow() -> UnsafeFlow {
- (0, 0)
-}
-
-pub fn owned_flow_to_unsafe_flow(flow: *const FlowRef) -> UnsafeFlow {
- unsafe {
- mem::transmute_copy(&*flow)
- }
-}
-
-pub fn mut_owned_flow_to_unsafe_flow(flow: *mut FlowRef) -> UnsafeFlow {
- unsafe {
- mem::transmute_copy(&*flow)
- }
-}
-
-pub fn borrowed_flow_to_unsafe_flow(flow: &Flow) -> UnsafeFlow {
- unsafe {
- mem::transmute_copy(&flow)
- }
-}
-
-pub fn mut_borrowed_flow_to_unsafe_flow(flow: &mut Flow) -> UnsafeFlow {
- unsafe {
- mem::transmute_copy(&flow)
- }
-}
-
-/// Information that we need stored in each DOM node.
-pub struct DomParallelInfo {
- /// The number of children that still need work done.
- pub children_count: AtomicInt,
-}
-
-impl DomParallelInfo {
- pub fn new() -> DomParallelInfo {
- DomParallelInfo {
- children_count: AtomicInt::new(0),
- }
- }
-}
-
-/// Information that we need stored in each flow.
-pub struct FlowParallelInfo {
- /// The number of children that still need work done.
- pub children_count: AtomicInt,
- /// The number of children and absolute descendants that still need work done.
- pub children_and_absolute_descendant_count: AtomicInt,
- /// The address of the parent flow.
- pub parent: UnsafeFlow,
-}
-
-impl FlowParallelInfo {
- pub fn new() -> FlowParallelInfo {
- FlowParallelInfo {
- children_count: AtomicInt::new(0),
- children_and_absolute_descendant_count: AtomicInt::new(0),
- parent: null_unsafe_flow(),
- }
- }
-}
-
-/// A parallel bottom-up flow traversal.
-trait ParallelPostorderFlowTraversal : PostorderFlowTraversal {
- /// Process current flow and potentially traverse its ancestors.
- ///
- /// If we are the last child that finished processing, recursively process
- /// our parent. Else, stop.
- /// Also, stop at the root (obviously :P).
- ///
- /// Thus, if we start with all the leaves of a tree, we end up traversing
- /// the whole tree bottom-up because each parent will be processed exactly
- /// once (by the last child that finishes processing).
- ///
- /// The only communication between siblings is that they both
- /// fetch-and-subtract the parent's children count.
- fn run_parallel(&mut self,
- mut unsafe_flow: UnsafeFlow,
- _: &mut WorkerProxy<*const SharedLayoutContext,UnsafeFlow>) {
- loop {
- unsafe {
- // Get a real flow.
- let flow: &mut FlowRef = mem::transmute(&unsafe_flow);
-
- // Perform the appropriate traversal.
- if self.should_process(flow.get_mut()) {
- self.process(flow.get_mut());
- }
-
- let base = flow::mut_base(flow.get_mut());
-
- // Reset the count of children for the next layout traversal.
- base.parallel.children_count.store(base.children.len() as int, Relaxed);
-
- // Possibly enqueue the parent.
- let unsafe_parent = base.parallel.parent;
- if unsafe_parent == null_unsafe_flow() {
- // We're done!
- break
- }
-
- // No, we're not at the root yet. Then are we the last child
- // of our parent to finish processing? If so, we can continue
- // on with our parent; otherwise, we've gotta wait.
- let parent: &mut FlowRef = mem::transmute(&unsafe_parent);
- let parent_base = flow::mut_base(parent.get_mut());
- if parent_base.parallel.children_count.fetch_sub(1, SeqCst) == 1 {
- // We were the last child of our parent. Reflow our parent.
- unsafe_flow = unsafe_parent
- } else {
- // Stop.
- break
- }
- }
- }
- }
-}
-
-/// A parallel top-down flow traversal.
-trait ParallelPreorderFlowTraversal : PreorderFlowTraversal {
- fn run_parallel(&mut self,
- unsafe_flow: UnsafeFlow,
- proxy: &mut WorkerProxy<*const SharedLayoutContext,UnsafeFlow>);
-
- #[inline(always)]
- fn run_parallel_helper(&mut self,
- unsafe_flow: UnsafeFlow,
- proxy: &mut WorkerProxy<*const SharedLayoutContext,UnsafeFlow>,
- top_down_func: extern "Rust" fn(UnsafeFlow,
- &mut WorkerProxy<*const SharedLayoutContext,
- UnsafeFlow>),
- bottom_up_func: extern "Rust" fn(UnsafeFlow,
- &mut WorkerProxy<*const SharedLayoutContext,
- UnsafeFlow>)) {
- let mut had_children = false;
- unsafe {
- // Get a real flow.
- let flow: &mut FlowRef = mem::transmute(&unsafe_flow);
-
- // Perform the appropriate traversal.
- self.process(flow.get_mut());
-
- // Possibly enqueue the children.
- for kid in flow::child_iter(flow.get_mut()) {
- had_children = true;
- proxy.push(WorkUnit {
- fun: top_down_func,
- data: borrowed_flow_to_unsafe_flow(kid),
- });
- }
-
- }
-
- // If there were no more children, start assigning block-sizes.
- if !had_children {
- bottom_up_func(unsafe_flow, proxy)
- }
- }
-}
-
-impl<'a> ParallelPostorderFlowTraversal for BubbleISizesTraversal<'a> {}
-
-impl<'a> ParallelPreorderFlowTraversal for AssignISizesTraversal<'a> {
- fn run_parallel(&mut self,
- unsafe_flow: UnsafeFlow,
- proxy: &mut WorkerProxy<*const SharedLayoutContext,UnsafeFlow>) {
- self.run_parallel_helper(unsafe_flow,
- proxy,
- assign_inline_sizes,
- assign_block_sizes_and_store_overflow)
- }
-}
-
-impl<'a> ParallelPostorderFlowTraversal for AssignBSizesAndStoreOverflowTraversal<'a> {}
-
-fn recalc_style_for_node(unsafe_layout_node: UnsafeLayoutNode,
- proxy: &mut WorkerProxy<*const SharedLayoutContext,UnsafeLayoutNode>) {
- let shared_layout_context = unsafe { &**proxy.user_data() };
- let layout_context = LayoutContext::new(shared_layout_context);
-
- // Get a real layout node.
- let node: LayoutNode = unsafe {
- layout_node_from_unsafe_layout_node(&unsafe_layout_node)
- };
-
- // Initialize layout data.
- //
- // FIXME(pcwalton): Stop allocating here. Ideally this should just be done by the HTML
- // parser.
- node.initialize_layout_data(layout_context.shared.layout_chan.clone());
-
- // Get the parent node.
- let opaque_node: OpaqueNode = OpaqueNodeMethods::from_layout_node(&node);
- let parent_opt = if opaque_node == layout_context.shared.reflow_root {
- None
- } else {
- node.parent_node()
- };
-
- // First, check to see whether we can share a style with someone.
- let style_sharing_candidate_cache = layout_context.style_sharing_candidate_cache();
- let sharing_result = unsafe {
- node.share_style_if_possible(style_sharing_candidate_cache,
- parent_opt.clone())
- };
-
- // Otherwise, match and cascade selectors.
- match sharing_result {
- CannotShare(mut shareable) => {
- let mut applicable_declarations = ApplicableDeclarations::new();
-
- if node.is_element() {
- // Perform the CSS selector matching.
- let stylist = unsafe { &*layout_context.shared.stylist };
- node.match_node(stylist, &mut applicable_declarations, &mut shareable);
- }
-
- // Perform the CSS cascade.
- unsafe {
- node.cascade_node(parent_opt,
- &applicable_declarations,
- layout_context.applicable_declarations_cache());
- }
-
- // Add ourselves to the LRU cache.
- if shareable {
- style_sharing_candidate_cache.insert_if_possible(&node);
- }
- }
- StyleWasShared(index) => style_sharing_candidate_cache.touch(index),
- }
-
- // Prepare for flow construction by counting the node's children and storing that count.
- let mut child_count = 0u;
- for _ in node.children() {
- child_count += 1;
- }
- if child_count != 0 {
- let mut layout_data_ref = node.mutate_layout_data();
- match &mut *layout_data_ref {
- &Some(ref mut layout_data) => {
- layout_data.data.parallel.children_count.store(child_count as int, Relaxed)
- }
- &None => fail!("no layout data"),
- }
- }
-
- // It's *very* important that this block is in a separate scope to the block above,
- // to avoid a data race that can occur (github issue #2308). The block above issues
- // a borrow on the node layout data. That borrow must be dropped before the child
- // nodes are actually pushed into the work queue. Otherwise, it's possible for a child
- // node to get into construct_flows() and move up it's parent hierarchy, which can call
- // borrow on the layout data before it is dropped from the block above.
- if child_count != 0 {
- // Enqueue kids.
- for kid in node.children() {
- proxy.push(WorkUnit {
- fun: recalc_style_for_node,
- data: layout_node_to_unsafe_layout_node(&kid),
- });
- }
- return
- }
-
- // If we got here, we're a leaf. Start construction of flows for this node.
- construct_flows(unsafe_layout_node, &layout_context)
-}
-
-fn construct_flows(mut unsafe_layout_node: UnsafeLayoutNode,
- layout_context: &LayoutContext) {
- loop {
- // Get a real layout node.
- let node: LayoutNode = unsafe {
- layout_node_from_unsafe_layout_node(&unsafe_layout_node)
- };
-
- // Construct flows for this node.
- {
- let mut flow_constructor = FlowConstructor::new(layout_context);
- flow_constructor.process(&ThreadSafeLayoutNode::new(&node));
- }
-
- // Reset the count of children for the next traversal.
- //
- // FIXME(pcwalton): Use children().len() when the implementation of that is efficient.
- let mut child_count = 0u;
- for _ in node.children() {
- child_count += 1
- }
- {
- let mut layout_data_ref = node.mutate_layout_data();
- match &mut *layout_data_ref {
- &Some(ref mut layout_data) => {
- layout_data.data.parallel.children_count.store(child_count as int, Relaxed)
- }
- &None => fail!("no layout data"),
- }
- }
-
- // If this is the reflow root, we're done.
- let opaque_node: OpaqueNode = OpaqueNodeMethods::from_layout_node(&node);
- if layout_context.shared.reflow_root == opaque_node {
- break
- }
-
- // Otherwise, enqueue the parent.
- match node.parent_node() {
- Some(parent) => {
-
- // No, we're not at the root yet. Then are we the last sibling of our parent?
- // If so, we can continue on with our parent; otherwise, we've gotta wait.
- unsafe {
- match *parent.borrow_layout_data_unchecked() {
- Some(ref parent_layout_data) => {
- let parent_layout_data: &mut LayoutDataWrapper = mem::transmute(parent_layout_data);
- if parent_layout_data.data
- .parallel
- .children_count
- .fetch_sub(1, SeqCst) == 1 {
- // We were the last child of our parent. Construct flows for our
- // parent.
- unsafe_layout_node = layout_node_to_unsafe_layout_node(&parent)
- } else {
- // Get out of here and find another node to work on.
- break
- }
- }
- None => fail!("no layout data for parent?!"),
- }
- }
- }
- None => fail!("no parent and weren't at reflow root?!"),
- }
- }
-}
-
-fn assign_inline_sizes(unsafe_flow: UnsafeFlow,
- proxy: &mut WorkerProxy<*const SharedLayoutContext,UnsafeFlow>) {
- let shared_layout_context = unsafe { &**proxy.user_data() };
- let layout_context = LayoutContext::new(shared_layout_context);
- let mut assign_inline_sizes_traversal = AssignISizesTraversal {
- layout_context: &layout_context,
- };
- assign_inline_sizes_traversal.run_parallel(unsafe_flow, proxy)
-}
-
-fn assign_block_sizes_and_store_overflow(unsafe_flow: UnsafeFlow,
- proxy: &mut WorkerProxy<*const SharedLayoutContext,UnsafeFlow>) {
- let shared_layout_context = unsafe { &**proxy.user_data() };
- let layout_context = LayoutContext::new(shared_layout_context);
- let mut assign_block_sizes_traversal = AssignBSizesAndStoreOverflowTraversal {
- layout_context: &layout_context,
- };
- assign_block_sizes_traversal.run_parallel(unsafe_flow, proxy)
-}
-
-fn compute_absolute_position(unsafe_flow: UnsafeFlow,
- proxy: &mut WorkerProxy<*const SharedLayoutContext,UnsafeFlow>) {
- let mut had_descendants = false;
- unsafe {
- // Get a real flow.
- let flow: &mut FlowRef = mem::transmute(&unsafe_flow);
-
- // Compute the absolute position for the flow.
- flow.get_mut().compute_absolute_position();
-
- // Count the number of absolutely-positioned children, so that we can subtract it from
- // from `children_and_absolute_descendant_count` to get the number of real children.
- let mut absolutely_positioned_child_count = 0u;
- for kid in flow::child_iter(flow.get_mut()) {
- if kid.is_absolutely_positioned() {
- absolutely_positioned_child_count += 1;
- }
- }
-
- // Don't enqueue absolutely positioned children.
- drop(flow::mut_base(flow.get_mut()).parallel
- .children_and_absolute_descendant_count
- .fetch_sub(absolutely_positioned_child_count as int,
- SeqCst));
-
- // Possibly enqueue the children.
- for kid in flow::child_iter(flow.get_mut()) {
- if !kid.is_absolutely_positioned() {
- had_descendants = true;
- proxy.push(WorkUnit {
- fun: compute_absolute_position,
- data: borrowed_flow_to_unsafe_flow(kid),
- });
- }
- }
-
- // Possibly enqueue absolute descendants.
- for absolute_descendant_link in flow::mut_base(flow.get_mut()).abs_descendants.iter() {
- had_descendants = true;
- let descendant = absolute_descendant_link;
- proxy.push(WorkUnit {
- fun: compute_absolute_position,
- data: borrowed_flow_to_unsafe_flow(descendant),
- });
- }
-
- // If there were no more descendants, start building the display list.
- if !had_descendants {
- build_display_list(mut_owned_flow_to_unsafe_flow(flow),
- proxy)
- }
- }
-}
-
-fn build_display_list(mut unsafe_flow: UnsafeFlow,
- proxy: &mut WorkerProxy<*const SharedLayoutContext,UnsafeFlow>) {
- let shared_layout_context = unsafe { &**proxy.user_data() };
- let layout_context = LayoutContext::new(shared_layout_context);
-
- loop {
- unsafe {
- // Get a real flow.
- let flow: &mut FlowRef = mem::transmute(&unsafe_flow);
-
- // Build display lists.
- flow.get_mut().build_display_list(&layout_context);
-
- {
- let base = flow::mut_base(flow.get_mut());
-
- // Reset the count of children and absolute descendants for the next layout
- // traversal.
- let children_and_absolute_descendant_count = base.children.len() +
- base.abs_descendants.len();
- base.parallel
- .children_and_absolute_descendant_count
- .store(children_and_absolute_descendant_count as int, Relaxed);
- }
-
- // Possibly enqueue the parent.
- let unsafe_parent = if flow.get().is_absolutely_positioned() {
- match *flow::mut_base(flow.get_mut()).absolute_cb.get() {
- None => fail!("no absolute containing block for absolutely positioned?!"),
- Some(ref mut absolute_cb) => {
- mut_borrowed_flow_to_unsafe_flow(absolute_cb.get_mut())
- }
- }
- } else {
- flow::mut_base(flow.get_mut()).parallel.parent
- };
- if unsafe_parent == null_unsafe_flow() {
- // We're done!
- break
- }
-
- // No, we're not at the root yet. Then are we the last child
- // of our parent to finish processing? If so, we can continue
- // on with our parent; otherwise, we've gotta wait.
- let parent: &mut FlowRef = mem::transmute(&unsafe_parent);
- let parent_base = flow::mut_base(parent.get_mut());
- if parent_base.parallel
- .children_and_absolute_descendant_count
- .fetch_sub(1, SeqCst) == 1 {
- // We were the last child of our parent. Build display lists for our parent.
- unsafe_flow = unsafe_parent
- } else {
- // Stop.
- break
- }
- }
- }
-}
-
-pub fn recalc_style_for_subtree(root_node: &LayoutNode,
- shared_layout_context: &SharedLayoutContext,
- queue: &mut WorkQueue<*const SharedLayoutContext,UnsafeLayoutNode>) {
- queue.data = shared_layout_context as *const _;
-
- // Enqueue the root node.
- queue.push(WorkUnit {
- fun: recalc_style_for_node,
- data: layout_node_to_unsafe_layout_node(root_node),
- });
-
- queue.run();
-
- queue.data = ptr::null()
-}
-
-pub fn traverse_flow_tree_preorder(root: &mut FlowRef,
- time_profiler_chan: TimeProfilerChan,
- shared_layout_context: &SharedLayoutContext,
- queue: &mut WorkQueue<*const SharedLayoutContext,UnsafeFlow>) {
- queue.data = shared_layout_context as *const _;
-
- profile(time::LayoutParallelWarmupCategory, time_profiler_chan, || {
- queue.push(WorkUnit {
- fun: assign_inline_sizes,
- data: mut_owned_flow_to_unsafe_flow(root),
- })
- });
-
- queue.run();
-
- queue.data = ptr::null()
-}
-
-pub fn build_display_list_for_subtree(root: &mut FlowRef,
- time_profiler_chan: TimeProfilerChan,
- shared_layout_context: &SharedLayoutContext,
- queue: &mut WorkQueue<*const SharedLayoutContext,UnsafeFlow>) {
- queue.data = shared_layout_context as *const _;
-
- profile(time::LayoutParallelWarmupCategory, time_profiler_chan, || {
- queue.push(WorkUnit {
- fun: compute_absolute_position,
- data: mut_owned_flow_to_unsafe_flow(root),
- })
- });
-
- queue.run();
-
- queue.data = ptr::null()
-}
-
diff --git a/src/components/layout/table.rs b/src/components/layout/table.rs
deleted file mode 100644
index 98569d68c95..00000000000
--- a/src/components/layout/table.rs
+++ /dev/null
@@ -1,324 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//! CSS table formatting contexts.
-
-#![deny(unsafe_block)]
-
-use block::{BlockFlow, MarginsMayNotCollapse, ISizeAndMarginsComputer};
-use block::{ISizeConstraintInput, ISizeConstraintSolution};
-use construct::FlowConstructor;
-use context::LayoutContext;
-use floats::FloatKind;
-use flow::{TableFlowClass, FlowClass, Flow, ImmutableFlowUtils};
-use fragment::Fragment;
-use table_wrapper::{TableLayout, FixedLayout, AutoLayout};
-use wrapper::ThreadSafeLayoutNode;
-
-use servo_util::geometry::Au;
-use servo_util::geometry;
-use std::fmt;
-use style::computed_values::table_layout;
-
-/// A table flow corresponded to the table's internal table fragment under a table wrapper flow.
-/// The properties `position`, `float`, and `margin-*` are used on the table wrapper fragment,
-/// not table fragment per CSS 2.1 § 10.5.
-pub struct TableFlow {
- pub block_flow: BlockFlow,
-
- /// Column inline-sizes
- pub col_inline_sizes: Vec<Au>,
-
- /// Column min inline-sizes.
- pub col_min_inline_sizes: Vec<Au>,
-
- /// Column pref inline-sizes.
- pub col_pref_inline_sizes: Vec<Au>,
-
- /// Table-layout property
- pub table_layout: TableLayout,
-}
-
-impl TableFlow {
- pub fn from_node_and_fragment(node: &ThreadSafeLayoutNode,
- fragment: Fragment)
- -> TableFlow {
- let mut block_flow = BlockFlow::from_node_and_fragment(node, fragment);
- let table_layout = if block_flow.fragment().style().get_table().table_layout ==
- table_layout::fixed {
- FixedLayout
- } else {
- AutoLayout
- };
- TableFlow {
- block_flow: block_flow,
- col_inline_sizes: vec!(),
- col_min_inline_sizes: vec!(),
- col_pref_inline_sizes: vec!(),
- table_layout: table_layout
- }
- }
-
- pub fn from_node(constructor: &mut FlowConstructor,
- node: &ThreadSafeLayoutNode)
- -> TableFlow {
- let mut block_flow = BlockFlow::from_node(constructor, node);
- let table_layout = if block_flow.fragment().style().get_table().table_layout ==
- table_layout::fixed {
- FixedLayout
- } else {
- AutoLayout
- };
- TableFlow {
- block_flow: block_flow,
- col_inline_sizes: vec!(),
- col_min_inline_sizes: vec!(),
- col_pref_inline_sizes: vec!(),
- table_layout: table_layout
- }
- }
-
- pub fn float_from_node(constructor: &mut FlowConstructor,
- node: &ThreadSafeLayoutNode,
- float_kind: FloatKind)
- -> TableFlow {
- let mut block_flow = BlockFlow::float_from_node(constructor, node, float_kind);
- let table_layout = if block_flow.fragment().style().get_table().table_layout ==
- table_layout::fixed {
- FixedLayout
- } else {
- AutoLayout
- };
- TableFlow {
- block_flow: block_flow,
- col_inline_sizes: vec!(),
- col_min_inline_sizes: vec!(),
- col_pref_inline_sizes: vec!(),
- table_layout: table_layout
- }
- }
-
- /// Update the corresponding value of self_inline-sizes if a value of kid_inline-sizes has larger value
- /// than one of self_inline-sizes.
- pub fn update_col_inline_sizes(self_inline_sizes: &mut Vec<Au>, kid_inline_sizes: &Vec<Au>) -> Au {
- let mut sum_inline_sizes = Au(0);
- let mut kid_inline_sizes_it = kid_inline_sizes.iter();
- for self_inline_size in self_inline_sizes.mut_iter() {
- match kid_inline_sizes_it.next() {
- Some(kid_inline_size) => {
- if *self_inline_size < *kid_inline_size {
- *self_inline_size = *kid_inline_size;
- }
- },
- None => {}
- }
- sum_inline_sizes = sum_inline_sizes + *self_inline_size;
- }
- sum_inline_sizes
- }
-
- /// Assign block-size for table flow.
- ///
- /// TODO(#2014, pcwalton): This probably doesn't handle margin collapse right.
- ///
- /// inline(always) because this is only ever called by in-order or non-in-order top-level
- /// methods
- #[inline(always)]
- fn assign_block_size_table_base<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
- self.block_flow.assign_block_size_block_base(layout_context, MarginsMayNotCollapse);
- }
-
- pub fn build_display_list_table(&mut self, layout_context: &LayoutContext) {
- debug!("build_display_list_table: same process as block flow");
- self.block_flow.build_display_list_block(layout_context);
- }
-}
-
-impl Flow for TableFlow {
- fn class(&self) -> FlowClass {
- TableFlowClass
- }
-
- fn as_table<'a>(&'a mut self) -> &'a mut TableFlow {
- self
- }
-
- fn as_block<'a>(&'a mut self) -> &'a mut BlockFlow {
- &mut self.block_flow
- }
-
- fn col_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<Au> {
- &mut self.col_inline_sizes
- }
-
- fn col_min_inline_sizes<'a>(&'a self) -> &'a Vec<Au> {
- &self.col_min_inline_sizes
- }
-
- fn col_pref_inline_sizes<'a>(&'a self) -> &'a Vec<Au> {
- &self.col_pref_inline_sizes
- }
-
- /// The specified column inline-sizes are set from column group and the first row for the fixed
- /// table layout calculation.
- /// The maximum min/pref inline-sizes of each column are set from the rows for the automatic
- /// table layout calculation.
- fn bubble_inline_sizes(&mut self, _: &LayoutContext) {
- let mut min_inline_size = Au(0);
- let mut pref_inline_size = Au(0);
- let mut did_first_row = false;
-
- for kid in self.block_flow.base.child_iter() {
- assert!(kid.is_proper_table_child());
-
- if kid.is_table_colgroup() {
- self.col_inline_sizes.push_all(kid.as_table_colgroup().inline_sizes.as_slice());
- self.col_min_inline_sizes = self.col_inline_sizes.clone();
- self.col_pref_inline_sizes = self.col_inline_sizes.clone();
- } else if kid.is_table_rowgroup() || kid.is_table_row() {
- // read column inline-sizes from table-row-group/table-row, and assign
- // inline-size=0 for the columns not defined in column-group
- // FIXME: need to read inline-sizes from either table-header-group OR
- // first table-row
- match self.table_layout {
- FixedLayout => {
- let kid_col_inline_sizes = kid.col_inline_sizes();
- if !did_first_row {
- did_first_row = true;
- let mut child_inline_sizes = kid_col_inline_sizes.iter();
- for col_inline_size in self.col_inline_sizes.mut_iter() {
- match child_inline_sizes.next() {
- Some(child_inline_size) => {
- if *col_inline_size == Au::new(0) {
- *col_inline_size = *child_inline_size;
- }
- },
- None => break
- }
- }
- }
- let num_child_cols = kid_col_inline_sizes.len();
- let num_cols = self.col_inline_sizes.len();
- debug!("table until the previous row has {} column(s) and this row has {} column(s)",
- num_cols, num_child_cols);
- for i in range(num_cols, num_child_cols) {
- self.col_inline_sizes.push((*kid_col_inline_sizes)[i]);
- }
- },
- AutoLayout => {
- min_inline_size = TableFlow::update_col_inline_sizes(&mut self.col_min_inline_sizes, kid.col_min_inline_sizes());
- pref_inline_size = TableFlow::update_col_inline_sizes(&mut self.col_pref_inline_sizes, kid.col_pref_inline_sizes());
-
- // update the number of column inline-sizes from table-rows.
- let num_cols = self.col_min_inline_sizes.len();
- let num_child_cols = kid.col_min_inline_sizes().len();
- debug!("table until the previous row has {} column(s) and this row has {} column(s)",
- num_cols, num_child_cols);
- for i in range(num_cols, num_child_cols) {
- self.col_inline_sizes.push(Au::new(0));
- let new_kid_min = kid.col_min_inline_sizes()[i];
- self.col_min_inline_sizes.push( new_kid_min );
- let new_kid_pref = kid.col_pref_inline_sizes()[i];
- self.col_pref_inline_sizes.push( new_kid_pref );
- min_inline_size = min_inline_size + new_kid_min;
- pref_inline_size = pref_inline_size + new_kid_pref;
- }
- }
- }
- }
- }
- self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size = min_inline_size;
- self.block_flow.base.intrinsic_inline_sizes.preferred_inline_size =
- geometry::max(min_inline_size, pref_inline_size);
- }
-
- /// Recursively (top-down) determines the actual inline-size of child contexts and fragments. When
- /// called on this context, the context has had its inline-size set by the parent context.
- fn assign_inline_sizes(&mut self, ctx: &LayoutContext) {
- debug!("assign_inline_sizes({}): assigning inline_size for flow", "table");
-
- // The position was set to the containing block by the flow's parent.
- let containing_block_inline_size = self.block_flow.base.position.size.inline;
-
- let mut num_unspecified_inline_sizes = 0;
- let mut total_column_inline_size = Au::new(0);
- for col_inline_size in self.col_inline_sizes.iter() {
- if *col_inline_size == Au::new(0) {
- num_unspecified_inline_sizes += 1;
- } else {
- total_column_inline_size = total_column_inline_size.add(col_inline_size);
- }
- }
-
- let inline_size_computer = InternalTable;
- inline_size_computer.compute_used_inline_size(&mut self.block_flow, ctx, containing_block_inline_size);
-
- let inline_start_content_edge = self.block_flow.fragment.border_padding.inline_start;
- let padding_and_borders = self.block_flow.fragment.border_padding.inline_start_end();
- let content_inline_size = self.block_flow.fragment.border_box.size.inline - padding_and_borders;
-
- match self.table_layout {
- FixedLayout => {
- // In fixed table layout, we distribute extra space among the unspecified columns if there are
- // any, or among all the columns if all are specified.
- if (total_column_inline_size < content_inline_size) && (num_unspecified_inline_sizes == 0) {
- let ratio = content_inline_size.to_f64().unwrap() / total_column_inline_size.to_f64().unwrap();
- for col_inline_size in self.col_inline_sizes.mut_iter() {
- *col_inline_size = (*col_inline_size).scale_by(ratio);
- }
- } else if num_unspecified_inline_sizes != 0 {
- let extra_column_inline_size = (content_inline_size - total_column_inline_size) / Au::new(num_unspecified_inline_sizes);
- for col_inline_size in self.col_inline_sizes.mut_iter() {
- if *col_inline_size == Au(0) {
- *col_inline_size = extra_column_inline_size;
- }
- }
- }
- }
- _ => {}
- }
-
- self.block_flow.propagate_assigned_inline_size_to_children(inline_start_content_edge, content_inline_size, Some(self.col_inline_sizes.clone()));
- }
-
- fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
- debug!("assign_block_size: assigning block_size for table");
- self.assign_block_size_table_base(ctx);
- }
-
- fn compute_absolute_position(&mut self) {
- self.block_flow.compute_absolute_position()
- }
-}
-
-impl fmt::Show for TableFlow {
- /// Outputs a debugging string describing this table flow.
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "TableFlow: {}", self.block_flow)
- }
-}
-
-/// Table, TableRowGroup, TableRow, TableCell types.
-/// Their inline-sizes are calculated in the same way and do not have margins.
-pub struct InternalTable;
-
-impl ISizeAndMarginsComputer for InternalTable {
- /// Compute the used value of inline-size, taking care of min-inline-size and max-inline-size.
- ///
- /// CSS Section 10.4: Minimum and Maximum inline-sizes
- fn compute_used_inline_size(&self,
- block: &mut BlockFlow,
- ctx: &LayoutContext,
- parent_flow_inline_size: Au) {
- let input = self.compute_inline_size_constraint_inputs(block, parent_flow_inline_size, ctx);
- let solution = self.solve_inline_size_constraints(block, &input);
- self.set_inline_size_constraint_solutions(block, solution);
- }
-
- /// Solve the inline-size and margins constraints for this block flow.
- fn solve_inline_size_constraints(&self, _: &mut BlockFlow, input: &ISizeConstraintInput)
- -> ISizeConstraintSolution {
- ISizeConstraintSolution::new(input.available_inline_size, Au::new(0), Au::new(0))
- }
-}
diff --git a/src/components/layout/table_caption.rs b/src/components/layout/table_caption.rs
deleted file mode 100644
index 8c1dba3e7ca..00000000000
--- a/src/components/layout/table_caption.rs
+++ /dev/null
@@ -1,73 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//! CSS table formatting contexts.
-
-#![deny(unsafe_block)]
-
-use block::BlockFlow;
-use construct::FlowConstructor;
-use context::LayoutContext;
-use flow::{TableCaptionFlowClass, FlowClass, Flow};
-use wrapper::ThreadSafeLayoutNode;
-
-use std::fmt;
-
-/// A table formatting context.
-pub struct TableCaptionFlow {
- pub block_flow: BlockFlow,
-}
-
-impl TableCaptionFlow {
- pub fn from_node(constructor: &mut FlowConstructor,
- node: &ThreadSafeLayoutNode)
- -> TableCaptionFlow {
- TableCaptionFlow {
- block_flow: BlockFlow::from_node(constructor, node)
- }
- }
-
- pub fn build_display_list_table_caption(&mut self, layout_context: &LayoutContext) {
- debug!("build_display_list_table_caption: same process as block flow");
- self.block_flow.build_display_list_block(layout_context)
- }
-}
-
-impl Flow for TableCaptionFlow {
- fn class(&self) -> FlowClass {
- TableCaptionFlowClass
- }
-
- fn as_table_caption<'a>(&'a mut self) -> &'a mut TableCaptionFlow {
- self
- }
-
- fn as_block<'a>(&'a mut self) -> &'a mut BlockFlow {
- &mut self.block_flow
- }
-
- fn bubble_inline_sizes(&mut self, ctx: &LayoutContext) {
- self.block_flow.bubble_inline_sizes(ctx);
- }
-
- fn assign_inline_sizes(&mut self, ctx: &LayoutContext) {
- debug!("assign_inline_sizes({}): assigning inline_size for flow", "table_caption");
- self.block_flow.assign_inline_sizes(ctx);
- }
-
- fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
- debug!("assign_block_size: assigning block_size for table_caption");
- self.block_flow.assign_block_size(ctx);
- }
-
- fn compute_absolute_position(&mut self) {
- self.block_flow.compute_absolute_position()
- }
-}
-
-impl fmt::Show for TableCaptionFlow {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "TableCaptionFlow: {}", self.block_flow)
- }
-}
diff --git a/src/components/layout/table_cell.rs b/src/components/layout/table_cell.rs
deleted file mode 100644
index 0011cd29fbc..00000000000
--- a/src/components/layout/table_cell.rs
+++ /dev/null
@@ -1,121 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//! CSS table formatting contexts.
-
-#![deny(unsafe_block)]
-
-use block::{BlockFlow, MarginsMayNotCollapse, ISizeAndMarginsComputer};
-use context::LayoutContext;
-use flow::{TableCellFlowClass, FlowClass, Flow};
-use fragment::Fragment;
-use model::{MaybeAuto};
-use table::InternalTable;
-use wrapper::ThreadSafeLayoutNode;
-
-use servo_util::geometry::Au;
-use std::fmt;
-
-/// A table formatting context.
-pub struct TableCellFlow {
- /// Data common to all flows.
- pub block_flow: BlockFlow,
-}
-
-impl TableCellFlow {
- pub fn from_node_and_fragment(node: &ThreadSafeLayoutNode, fragment: Fragment) -> TableCellFlow {
- TableCellFlow {
- block_flow: BlockFlow::from_node_and_fragment(node, fragment)
- }
- }
-
- pub fn fragment<'a>(&'a mut self) -> &'a Fragment {
- &self.block_flow.fragment
- }
-
- pub fn mut_fragment<'a>(&'a mut self) -> &'a mut Fragment {
- &mut self.block_flow.fragment
- }
-
- /// Assign block-size for table-cell flow.
- ///
- /// TODO(#2015, pcwalton): This doesn't handle floats right.
- ///
- /// inline(always) because this is only ever called by in-order or non-in-order top-level
- /// methods
- #[inline(always)]
- fn assign_block_size_table_cell_base<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
- self.block_flow.assign_block_size_block_base(layout_context, MarginsMayNotCollapse)
- }
-
- pub fn build_display_list_table_cell(&mut self, layout_context: &LayoutContext) {
- debug!("build_display_list_table: same process as block flow");
- self.block_flow.build_display_list_block(layout_context)
- }
-}
-
-impl Flow for TableCellFlow {
- fn class(&self) -> FlowClass {
- TableCellFlowClass
- }
-
- fn as_table_cell<'a>(&'a mut self) -> &'a mut TableCellFlow {
- self
- }
-
- fn as_block<'a>(&'a mut self) -> &'a mut BlockFlow {
- &mut self.block_flow
- }
-
- /// Minimum/preferred inline-sizes set by this function are used in automatic table layout calculation.
- fn bubble_inline_sizes(&mut self, ctx: &LayoutContext) {
- self.block_flow.bubble_inline_sizes(ctx);
- let specified_inline_size = MaybeAuto::from_style(self.block_flow.fragment.style().content_inline_size(),
- Au::new(0)).specified_or_zero();
- if self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size < specified_inline_size {
- self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size = specified_inline_size;
- }
- if self.block_flow.base.intrinsic_inline_sizes.preferred_inline_size <
- self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size {
- self.block_flow.base.intrinsic_inline_sizes.preferred_inline_size =
- self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size;
- }
- }
-
- /// Recursively (top-down) determines the actual inline-size of child contexts and fragments. When
- /// called on this context, the context has had its inline-size set by the parent table row.
- fn assign_inline_sizes(&mut self, ctx: &LayoutContext) {
- debug!("assign_inline_sizes({}): assigning inline_size for flow", "table_cell");
-
- // The position was set to the column inline-size by the parent flow, table row flow.
- let containing_block_inline_size = self.block_flow.base.position.size.inline;
-
- let inline_size_computer = InternalTable;
- inline_size_computer.compute_used_inline_size(&mut self.block_flow, ctx, containing_block_inline_size);
-
- let inline_start_content_edge = self.block_flow.fragment.border_box.start.i +
- self.block_flow.fragment.border_padding.inline_start;
- let padding_and_borders = self.block_flow.fragment.border_padding.inline_start_end();
- let content_inline_size = self.block_flow.fragment.border_box.size.inline - padding_and_borders;
-
- self.block_flow.propagate_assigned_inline_size_to_children(inline_start_content_edge,
- content_inline_size,
- None);
- }
-
- fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
- debug!("assign_block_size: assigning block_size for table_cell");
- self.assign_block_size_table_cell_base(ctx);
- }
-
- fn compute_absolute_position(&mut self) {
- self.block_flow.compute_absolute_position()
- }
-}
-
-impl fmt::Show for TableCellFlow {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "TableCellFlow: {}", self.block_flow)
- }
-}
diff --git a/src/components/layout/table_colgroup.rs b/src/components/layout/table_colgroup.rs
deleted file mode 100644
index 270f55970b2..00000000000
--- a/src/components/layout/table_colgroup.rs
+++ /dev/null
@@ -1,88 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//! CSS table formatting contexts.
-
-#![deny(unsafe_block)]
-
-use context::LayoutContext;
-use flow::{BaseFlow, TableColGroupFlowClass, FlowClass, Flow};
-use fragment::{Fragment, TableColumnFragment};
-use model::{MaybeAuto};
-use wrapper::ThreadSafeLayoutNode;
-
-use servo_util::geometry::Au;
-use std::fmt;
-
-/// A table formatting context.
-pub struct TableColGroupFlow {
- /// Data common to all flows.
- pub base: BaseFlow,
-
- /// The associated fragment.
- pub fragment: Option<Fragment>,
-
- /// The table column fragments
- pub cols: Vec<Fragment>,
-
- /// The specified inline-sizes of table columns
- pub inline_sizes: Vec<Au>,
-}
-
-impl TableColGroupFlow {
- pub fn from_node_and_fragments(node: &ThreadSafeLayoutNode,
- fragment: Fragment,
- fragments: Vec<Fragment>) -> TableColGroupFlow {
- TableColGroupFlow {
- base: BaseFlow::new((*node).clone()),
- fragment: Some(fragment),
- cols: fragments,
- inline_sizes: vec!(),
- }
- }
-}
-
-impl Flow for TableColGroupFlow {
- fn class(&self) -> FlowClass {
- TableColGroupFlowClass
- }
-
- fn as_table_colgroup<'a>(&'a mut self) -> &'a mut TableColGroupFlow {
- self
- }
-
- fn bubble_inline_sizes(&mut self, _: &LayoutContext) {
- for fragment in self.cols.iter() {
- // get the specified value from inline-size property
- let inline_size = MaybeAuto::from_style(fragment.style().content_inline_size(),
- Au::new(0)).specified_or_zero();
-
- let span: int = match fragment.specific {
- TableColumnFragment(col_fragment) => col_fragment.span.unwrap_or(1),
- _ => fail!("Other fragment come out in TableColGroupFlow. {:?}", fragment.specific)
- };
- for _ in range(0, span) {
- self.inline_sizes.push(inline_size);
- }
- }
- }
-
- /// Table column inline-sizes are assigned in table flow and propagated to table row or rowgroup flow.
- /// Therefore, table colgroup flow does not need to assign its inline-size.
- fn assign_inline_sizes(&mut self, _ctx: &LayoutContext) {
- }
-
- /// Table column do not have block-size.
- fn assign_block_size(&mut self, _ctx: &LayoutContext) {
- }
-}
-
-impl fmt::Show for TableColGroupFlow {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self.fragment {
- Some(ref rb) => write!(f, "TableColGroupFlow: {}", rb),
- None => write!(f, "TableColGroupFlow"),
- }
- }
-}
diff --git a/src/components/layout/table_row.rs b/src/components/layout/table_row.rs
deleted file mode 100644
index 101f00eb5cc..00000000000
--- a/src/components/layout/table_row.rs
+++ /dev/null
@@ -1,225 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//! CSS table formatting contexts.
-
-#![deny(unsafe_block)]
-
-use block::BlockFlow;
-use block::ISizeAndMarginsComputer;
-use construct::FlowConstructor;
-use context::LayoutContext;
-use flow::{TableRowFlowClass, FlowClass, Flow, ImmutableFlowUtils};
-use flow;
-use fragment::Fragment;
-use table::InternalTable;
-use model::{MaybeAuto, Specified, Auto};
-use wrapper::ThreadSafeLayoutNode;
-
-use servo_util::geometry::Au;
-use servo_util::geometry;
-use std::fmt;
-
-/// A table formatting context.
-pub struct TableRowFlow {
- pub block_flow: BlockFlow,
-
- /// Column inline-sizes.
- pub col_inline_sizes: Vec<Au>,
-
- /// Column min inline-sizes.
- pub col_min_inline_sizes: Vec<Au>,
-
- /// Column pref inline-sizes.
- pub col_pref_inline_sizes: Vec<Au>,
-}
-
-impl TableRowFlow {
- pub fn from_node_and_fragment(node: &ThreadSafeLayoutNode,
- fragment: Fragment)
- -> TableRowFlow {
- TableRowFlow {
- block_flow: BlockFlow::from_node_and_fragment(node, fragment),
- col_inline_sizes: vec!(),
- col_min_inline_sizes: vec!(),
- col_pref_inline_sizes: vec!(),
- }
- }
-
- pub fn from_node(constructor: &mut FlowConstructor,
- node: &ThreadSafeLayoutNode)
- -> TableRowFlow {
- TableRowFlow {
- block_flow: BlockFlow::from_node(constructor, node),
- col_inline_sizes: vec!(),
- col_min_inline_sizes: vec!(),
- col_pref_inline_sizes: vec!(),
- }
- }
-
- pub fn fragment<'a>(&'a mut self) -> &'a Fragment {
- &self.block_flow.fragment
- }
-
- fn initialize_offsets(&mut self) -> (Au, Au, Au) {
- // TODO: If border-collapse: collapse, block-start_offset, block-end_offset, and inline-start_offset
- // should be updated. Currently, they are set as Au(0).
- (Au(0), Au(0), Au(0))
- }
-
- /// Assign block-size for table-row flow.
- ///
- /// TODO(pcwalton): This doesn't handle floats and positioned elements right.
- ///
- /// inline(always) because this is only ever called by in-order or non-in-order top-level
- /// methods
- #[inline(always)]
- fn assign_block_size_table_row_base<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
- let (block_start_offset, _, _) = self.initialize_offsets();
-
- let /* mut */ cur_y = block_start_offset;
-
- // Per CSS 2.1 § 17.5.3, find max_y = max( computed `block-size`, minimum block-size of all cells )
- let mut max_y = Au::new(0);
- for kid in self.block_flow.base.child_iter() {
- kid.assign_block_size_for_inorder_child_if_necessary(layout_context);
-
- {
- let child_fragment = kid.as_table_cell().fragment();
- // TODO: Percentage block-size
- let child_specified_block_size = MaybeAuto::from_style(child_fragment.style().content_block_size(),
- Au::new(0)).specified_or_zero();
- max_y =
- geometry::max(max_y,
- child_specified_block_size + child_fragment.border_padding.block_start_end());
- }
- let child_node = flow::mut_base(kid);
- child_node.position.start.b = cur_y;
- max_y = geometry::max(max_y, child_node.position.size.block);
- }
-
- let mut block_size = max_y;
- // TODO: Percentage block-size
- block_size = match MaybeAuto::from_style(self.block_flow.fragment.style().content_block_size(), Au(0)) {
- Auto => block_size,
- Specified(value) => geometry::max(value, block_size)
- };
- // cur_y = cur_y + block-size;
-
- // Assign the block-size of own fragment
- //
- // FIXME(pcwalton): Take `cur_y` into account.
- let mut position = self.block_flow.fragment.border_box;
- position.size.block = block_size;
- self.block_flow.fragment.border_box = position;
- self.block_flow.base.position.size.block = block_size;
-
- // Assign the block-size of kid fragments, which is the same value as own block-size.
- for kid in self.block_flow.base.child_iter() {
- {
- let kid_fragment = kid.as_table_cell().mut_fragment();
- let mut position = kid_fragment.border_box;
- position.size.block = block_size;
- kid_fragment.border_box = position;
- }
- let child_node = flow::mut_base(kid);
- child_node.position.size.block = block_size;
- }
- }
-
- pub fn build_display_list_table_row(&mut self, layout_context: &LayoutContext) {
- debug!("build_display_list_table_row: same process as block flow");
- self.block_flow.build_display_list_block(layout_context)
- }
-}
-
-impl Flow for TableRowFlow {
- fn class(&self) -> FlowClass {
- TableRowFlowClass
- }
-
- fn as_table_row<'a>(&'a mut self) -> &'a mut TableRowFlow {
- self
- }
-
- fn as_block<'a>(&'a mut self) -> &'a mut BlockFlow {
- &mut self.block_flow
- }
-
- fn col_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<Au> {
- &mut self.col_inline_sizes
- }
-
- fn col_min_inline_sizes<'a>(&'a self) -> &'a Vec<Au> {
- &self.col_min_inline_sizes
- }
-
- fn col_pref_inline_sizes<'a>(&'a self) -> &'a Vec<Au> {
- &self.col_pref_inline_sizes
- }
-
- /// Recursively (bottom-up) determines the context's preferred and minimum inline-sizes. When called
- /// on this context, all child contexts have had their min/pref inline-sizes set. This function must
- /// decide min/pref inline-sizes based on child context inline-sizes and dimensions of any fragments it is
- /// responsible for flowing.
- /// Min/pref inline-sizes set by this function are used in automatic table layout calculation.
- /// The specified column inline-sizes of children cells are used in fixed table layout calculation.
- fn bubble_inline_sizes(&mut self, _: &LayoutContext) {
- let mut min_inline_size = Au(0);
- let mut pref_inline_size = Au(0);
- /* find the specified inline_sizes from child table-cell contexts */
- for kid in self.block_flow.base.child_iter() {
- assert!(kid.is_table_cell());
-
- // collect the specified column inline-sizes of cells. These are used in fixed table layout calculation.
- {
- let child_fragment = kid.as_table_cell().fragment();
- let child_specified_inline_size = MaybeAuto::from_style(child_fragment.style().content_inline_size(),
- Au::new(0)).specified_or_zero();
- self.col_inline_sizes.push(child_specified_inline_size);
- }
-
- // collect min_inline-size & pref_inline-size of children cells for automatic table layout calculation.
- let child_base = flow::mut_base(kid);
- self.col_min_inline_sizes.push(child_base.intrinsic_inline_sizes.minimum_inline_size);
- self.col_pref_inline_sizes.push(child_base.intrinsic_inline_sizes.preferred_inline_size);
- min_inline_size = min_inline_size + child_base.intrinsic_inline_sizes.minimum_inline_size;
- pref_inline_size = pref_inline_size + child_base.intrinsic_inline_sizes.preferred_inline_size;
- }
- self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size = min_inline_size;
- self.block_flow.base.intrinsic_inline_sizes.preferred_inline_size = geometry::max(min_inline_size,
- pref_inline_size);
- }
-
- /// Recursively (top-down) determines the actual inline-size of child contexts and fragments. When called
- /// on this context, the context has had its inline-size set by the parent context.
- fn assign_inline_sizes(&mut self, ctx: &LayoutContext) {
- debug!("assign_inline_sizes({}): assigning inline_size for flow", "table_row");
-
- // The position was set to the containing block by the flow's parent.
- let containing_block_inline_size = self.block_flow.base.position.size.inline;
- // FIXME: In case of border-collapse: collapse, inline-start_content_edge should be border-inline-start
- let inline_start_content_edge = Au::new(0);
-
- let inline_size_computer = InternalTable;
- inline_size_computer.compute_used_inline_size(&mut self.block_flow, ctx, containing_block_inline_size);
-
- self.block_flow.propagate_assigned_inline_size_to_children(inline_start_content_edge, Au(0), Some(self.col_inline_sizes.clone()));
- }
-
- fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
- debug!("assign_block_size: assigning block_size for table_row");
- self.assign_block_size_table_row_base(ctx);
- }
-
- fn compute_absolute_position(&mut self) {
- self.block_flow.compute_absolute_position()
- }
-}
-
-impl fmt::Show for TableRowFlow {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "TableRowFlow: {}", self.block_flow.fragment)
- }
-}
diff --git a/src/components/layout/table_rowgroup.rs b/src/components/layout/table_rowgroup.rs
deleted file mode 100644
index 48f9d376af3..00000000000
--- a/src/components/layout/table_rowgroup.rs
+++ /dev/null
@@ -1,208 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//! CSS table formatting contexts.
-
-#![deny(unsafe_block)]
-
-use block::BlockFlow;
-use block::ISizeAndMarginsComputer;
-use construct::FlowConstructor;
-use context::LayoutContext;
-use flow::{TableRowGroupFlowClass, FlowClass, Flow, ImmutableFlowUtils};
-use flow;
-use fragment::Fragment;
-use table::{InternalTable, TableFlow};
-use wrapper::ThreadSafeLayoutNode;
-
-use servo_util::geometry::Au;
-use servo_util::geometry;
-use std::fmt;
-
-/// A table formatting context.
-pub struct TableRowGroupFlow {
- pub block_flow: BlockFlow,
-
- /// Column inline-sizes
- pub col_inline_sizes: Vec<Au>,
-
- /// Column min inline-sizes.
- pub col_min_inline_sizes: Vec<Au>,
-
- /// Column pref inline-sizes.
- pub col_pref_inline_sizes: Vec<Au>,
-}
-
-impl TableRowGroupFlow {
- pub fn from_node_and_fragment(node: &ThreadSafeLayoutNode,
- fragment: Fragment)
- -> TableRowGroupFlow {
- TableRowGroupFlow {
- block_flow: BlockFlow::from_node_and_fragment(node, fragment),
- col_inline_sizes: vec!(),
- col_min_inline_sizes: vec!(),
- col_pref_inline_sizes: vec!(),
- }
- }
-
- pub fn from_node(constructor: &mut FlowConstructor,
- node: &ThreadSafeLayoutNode)
- -> TableRowGroupFlow {
- TableRowGroupFlow {
- block_flow: BlockFlow::from_node(constructor, node),
- col_inline_sizes: vec!(),
- col_min_inline_sizes: vec!(),
- col_pref_inline_sizes: vec!(),
- }
- }
-
- pub fn fragment<'a>(&'a mut self) -> &'a Fragment {
- &self.block_flow.fragment
- }
-
- fn initialize_offsets(&mut self) -> (Au, Au, Au) {
- // TODO: If border-collapse: collapse, block-start_offset, block-end_offset, and inline-start_offset
- // should be updated. Currently, they are set as Au(0).
- (Au(0), Au(0), Au(0))
- }
-
- /// Assign block-size for table-rowgroup flow.
- ///
- /// FIXME(pcwalton): This doesn't handle floats right.
- ///
- /// inline(always) because this is only ever called by in-order or non-in-order top-level
- /// methods
- #[inline(always)]
- fn assign_block_size_table_rowgroup_base<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
- let (block_start_offset, _, _) = self.initialize_offsets();
-
- let mut cur_y = block_start_offset;
-
- for kid in self.block_flow.base.child_iter() {
- kid.assign_block_size_for_inorder_child_if_necessary(layout_context);
-
- let child_node = flow::mut_base(kid);
- child_node.position.start.b = cur_y;
- cur_y = cur_y + child_node.position.size.block;
- }
-
- let block_size = cur_y - block_start_offset;
-
- let mut position = self.block_flow.fragment.border_box;
- position.size.block = block_size;
- self.block_flow.fragment.border_box = position;
- self.block_flow.base.position.size.block = block_size;
- }
-
- pub fn build_display_list_table_rowgroup(&mut self, layout_context: &LayoutContext) {
- debug!("build_display_list_table_rowgroup: same process as block flow");
- self.block_flow.build_display_list_block(layout_context)
- }
-}
-
-impl Flow for TableRowGroupFlow {
- fn class(&self) -> FlowClass {
- TableRowGroupFlowClass
- }
-
- fn as_table_rowgroup<'a>(&'a mut self) -> &'a mut TableRowGroupFlow {
- self
- }
-
- fn as_block<'a>(&'a mut self) -> &'a mut BlockFlow {
- &mut self.block_flow
- }
-
- fn col_inline_sizes<'a>(&'a mut self) -> &'a mut Vec<Au> {
- &mut self.col_inline_sizes
- }
-
- fn col_min_inline_sizes<'a>(&'a self) -> &'a Vec<Au> {
- &self.col_min_inline_sizes
- }
-
- fn col_pref_inline_sizes<'a>(&'a self) -> &'a Vec<Au> {
- &self.col_pref_inline_sizes
- }
-
- /// Recursively (bottom-up) determines the context's preferred and minimum inline-sizes. When called
- /// on this context, all child contexts have had their min/pref inline-sizes set. This function must
- /// decide min/pref inline-sizes based on child context inline-sizes and dimensions of any fragments it is
- /// responsible for flowing.
- /// Min/pref inline-sizes set by this function are used in automatic table layout calculation.
- /// Also, this function finds the specified column inline-sizes from the first row.
- /// Those are used in fixed table layout calculation
- fn bubble_inline_sizes(&mut self, _: &LayoutContext) {
- let mut min_inline_size = Au(0);
- let mut pref_inline_size = Au(0);
-
- for kid in self.block_flow.base.child_iter() {
- assert!(kid.is_table_row());
-
- // calculate min_inline-size & pref_inline-size for automatic table layout calculation
- // 'self.col_min_inline-sizes' collects the maximum value of cells' min-inline-sizes for each column.
- // 'self.col_pref_inline-sizes' collects the maximum value of cells' pref-inline-sizes for each column.
- if self.col_inline_sizes.is_empty() { // First Row
- assert!(self.col_min_inline_sizes.is_empty() && self.col_pref_inline_sizes.is_empty());
- // 'self.col_inline-sizes' collects the specified column inline-sizes from the first table-row for fixed table layout calculation.
- self.col_inline_sizes = kid.col_inline_sizes().clone();
- self.col_min_inline_sizes = kid.col_min_inline_sizes().clone();
- self.col_pref_inline_sizes = kid.col_pref_inline_sizes().clone();
- } else {
- min_inline_size = TableFlow::update_col_inline_sizes(&mut self.col_min_inline_sizes, kid.col_min_inline_sizes());
- pref_inline_size = TableFlow::update_col_inline_sizes(&mut self.col_pref_inline_sizes, kid.col_pref_inline_sizes());
-
- // update the number of column inline-sizes from table-rows.
- let num_cols = self.col_inline_sizes.len();
- let num_child_cols = kid.col_min_inline_sizes().len();
- for i in range(num_cols, num_child_cols) {
- self.col_inline_sizes.push(Au::new(0));
- let new_kid_min = kid.col_min_inline_sizes()[i];
- self.col_min_inline_sizes.push(kid.col_min_inline_sizes()[i]);
- let new_kid_pref = kid.col_pref_inline_sizes()[i];
- self.col_pref_inline_sizes.push(kid.col_pref_inline_sizes()[i]);
- min_inline_size = min_inline_size + new_kid_min;
- pref_inline_size = pref_inline_size + new_kid_pref;
- }
- }
- }
-
- self.block_flow.base.intrinsic_inline_sizes.minimum_inline_size = min_inline_size;
- self.block_flow.base.intrinsic_inline_sizes.preferred_inline_size = geometry::max(min_inline_size,
- pref_inline_size);
- }
-
- /// Recursively (top-down) determines the actual inline-size of child contexts and fragments. When
- /// called on this context, the context has had its inline-size set by the parent context.
- fn assign_inline_sizes(&mut self, ctx: &LayoutContext) {
- debug!("assign_inline_sizes({}): assigning inline_size for flow", "table_rowgroup");
-
- // The position was set to the containing block by the flow's parent.
- let containing_block_inline_size = self.block_flow.base.position.size.inline;
- // FIXME: In case of border-collapse: collapse, inline-start_content_edge should be
- // the border width on the inline-start side.
- let inline_start_content_edge = Au::new(0);
- let content_inline_size = containing_block_inline_size;
-
- let inline_size_computer = InternalTable;
- inline_size_computer.compute_used_inline_size(&mut self.block_flow, ctx, containing_block_inline_size);
-
- self.block_flow.propagate_assigned_inline_size_to_children(inline_start_content_edge, content_inline_size, Some(self.col_inline_sizes.clone()));
- }
-
- fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
- debug!("assign_block_size: assigning block_size for table_rowgroup");
- self.assign_block_size_table_rowgroup_base(ctx);
- }
-
- fn compute_absolute_position(&mut self) {
- self.block_flow.compute_absolute_position()
- }
-}
-
-impl fmt::Show for TableRowGroupFlow {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "TableRowGroupFlow: {}", self.block_flow.fragment)
- }
-}
diff --git a/src/components/layout/table_wrapper.rs b/src/components/layout/table_wrapper.rs
deleted file mode 100644
index 2084ef52bdc..00000000000
--- a/src/components/layout/table_wrapper.rs
+++ /dev/null
@@ -1,325 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//! CSS table formatting contexts.
-
-#![deny(unsafe_block)]
-
-use block::{BlockFlow, MarginsMayNotCollapse, ISizeAndMarginsComputer};
-use block::{ISizeConstraintInput, ISizeConstraintSolution};
-use construct::FlowConstructor;
-use context::LayoutContext;
-use floats::FloatKind;
-use flow::{TableWrapperFlowClass, FlowClass, Flow, ImmutableFlowUtils};
-use fragment::Fragment;
-use model::{Specified, Auto, specified};
-use wrapper::ThreadSafeLayoutNode;
-
-use servo_util::geometry::Au;
-use servo_util::geometry;
-use std::fmt;
-use style::computed_values::table_layout;
-
-pub enum TableLayout {
- FixedLayout,
- AutoLayout
-}
-
-/// A table wrapper flow based on a block formatting context.
-pub struct TableWrapperFlow {
- pub block_flow: BlockFlow,
-
- /// Column inline-sizes
- pub col_inline_sizes: Vec<Au>,
-
- /// Table-layout property
- pub table_layout: TableLayout,
-}
-
-impl TableWrapperFlow {
- pub fn from_node_and_fragment(node: &ThreadSafeLayoutNode,
- fragment: Fragment)
- -> TableWrapperFlow {
- let mut block_flow = BlockFlow::from_node_and_fragment(node, fragment);
- let table_layout = if block_flow.fragment().style().get_table().table_layout ==
- table_layout::fixed {
- FixedLayout
- } else {
- AutoLayout
- };
- TableWrapperFlow {
- block_flow: block_flow,
- col_inline_sizes: vec!(),
- table_layout: table_layout
- }
- }
-
- pub fn from_node(constructor: &mut FlowConstructor,
- node: &ThreadSafeLayoutNode)
- -> TableWrapperFlow {
- let mut block_flow = BlockFlow::from_node(constructor, node);
- let table_layout = if block_flow.fragment().style().get_table().table_layout ==
- table_layout::fixed {
- FixedLayout
- } else {
- AutoLayout
- };
- TableWrapperFlow {
- block_flow: block_flow,
- col_inline_sizes: vec!(),
- table_layout: table_layout
- }
- }
-
- pub fn float_from_node(constructor: &mut FlowConstructor,
- node: &ThreadSafeLayoutNode,
- float_kind: FloatKind)
- -> TableWrapperFlow {
- let mut block_flow = BlockFlow::float_from_node(constructor, node, float_kind);
- let table_layout = if block_flow.fragment().style().get_table().table_layout ==
- table_layout::fixed {
- FixedLayout
- } else {
- AutoLayout
- };
- TableWrapperFlow {
- block_flow: block_flow,
- col_inline_sizes: vec!(),
- table_layout: table_layout
- }
- }
-
- pub fn is_float(&self) -> bool {
- self.block_flow.float.is_some()
- }
-
- /// Assign block-size for table-wrapper flow.
- /// `Assign block-size` of table-wrapper flow follows a similar process to that of block flow.
- ///
- /// inline(always) because this is only ever called by in-order or non-in-order top-level
- /// methods
- #[inline(always)]
- fn assign_block_size_table_wrapper_base<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
- self.block_flow.assign_block_size_block_base(layout_context, MarginsMayNotCollapse);
- }
-
- pub fn build_display_list_table_wrapper(&mut self, layout_context: &LayoutContext) {
- debug!("build_display_list_table_wrapper: same process as block flow");
- self.block_flow.build_display_list_block(layout_context);
- }
-}
-
-impl Flow for TableWrapperFlow {
- fn class(&self) -> FlowClass {
- TableWrapperFlowClass
- }
-
- fn as_table_wrapper<'a>(&'a mut self) -> &'a mut TableWrapperFlow {
- self
- }
-
- fn as_block<'a>(&'a mut self) -> &'a mut BlockFlow {
- &mut self.block_flow
- }
-
- /* Recursively (bottom-up) determine the context's preferred and
- minimum inline_sizes. When called on this context, all child contexts
- have had their min/pref inline_sizes set. This function must decide
- min/pref inline_sizes based on child context inline_sizes and dimensions of
- any fragments it is responsible for flowing. */
-
- fn bubble_inline_sizes(&mut self, ctx: &LayoutContext) {
- // get column inline-sizes info from table flow
- for kid in self.block_flow.base.child_iter() {
- assert!(kid.is_table_caption() || kid.is_table());
-
- if kid.is_table() {
- self.col_inline_sizes.push_all(kid.as_table().col_inline_sizes.as_slice());
- }
- }
-
- self.block_flow.bubble_inline_sizes(ctx);
- }
-
- /// Recursively (top-down) determines the actual inline-size of child contexts and fragments. When
- /// called on this context, the context has had its inline-size set by the parent context.
- ///
- /// Dual fragments consume some inline-size first, and the remainder is assigned to all child (block)
- /// contexts.
- fn assign_inline_sizes(&mut self, ctx: &LayoutContext) {
- debug!("assign_inline_sizes({}): assigning inline_size for flow",
- if self.is_float() {
- "floated table_wrapper"
- } else {
- "table_wrapper"
- });
-
- // The position was set to the containing block by the flow's parent.
- let containing_block_inline_size = self.block_flow.base.position.size.inline;
-
- let inline_size_computer = TableWrapper;
- inline_size_computer.compute_used_inline_size_table_wrapper(self, ctx, containing_block_inline_size);
-
- let inline_start_content_edge = self.block_flow.fragment.border_box.start.i;
- let content_inline_size = self.block_flow.fragment.border_box.size.inline;
-
- match self.table_layout {
- FixedLayout | _ if self.is_float() =>
- self.block_flow.base.position.size.inline = content_inline_size,
- _ => {}
- }
-
- // In case of fixed layout, column inline-sizes are calculated in table flow.
- let assigned_col_inline_sizes = match self.table_layout {
- FixedLayout => None,
- AutoLayout => Some(self.col_inline_sizes.clone())
- };
- self.block_flow.propagate_assigned_inline_size_to_children(inline_start_content_edge, content_inline_size, assigned_col_inline_sizes);
- }
-
- fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
- if self.is_float() {
- debug!("assign_block_size_float: assigning block_size for floated table_wrapper");
- self.block_flow.assign_block_size_float(ctx);
- } else {
- debug!("assign_block_size: assigning block_size for table_wrapper");
- self.assign_block_size_table_wrapper_base(ctx);
- }
- }
-
- fn compute_absolute_position(&mut self) {
- self.block_flow.compute_absolute_position()
- }
-}
-
-impl fmt::Show for TableWrapperFlow {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- if self.is_float() {
- write!(f, "TableWrapperFlow(Float): {}", self.block_flow.fragment)
- } else {
- write!(f, "TableWrapperFlow: {}", self.block_flow.fragment)
- }
- }
-}
-
-struct TableWrapper;
-
-impl TableWrapper {
- fn compute_used_inline_size_table_wrapper(&self,
- table_wrapper: &mut TableWrapperFlow,
- ctx: &LayoutContext,
- parent_flow_inline_size: Au) {
- let input = self.compute_inline_size_constraint_inputs_table_wrapper(table_wrapper,
- parent_flow_inline_size,
- ctx);
-
- let solution = self.solve_inline_size_constraints(&mut table_wrapper.block_flow, &input);
-
- self.set_inline_size_constraint_solutions(&mut table_wrapper.block_flow, solution);
- self.set_flow_x_coord_if_necessary(&mut table_wrapper.block_flow, solution);
- }
-
- fn compute_inline_size_constraint_inputs_table_wrapper(&self,
- table_wrapper: &mut TableWrapperFlow,
- parent_flow_inline_size: Au,
- ctx: &LayoutContext)
- -> ISizeConstraintInput {
- let mut input = self.compute_inline_size_constraint_inputs(&mut table_wrapper.block_flow,
- parent_flow_inline_size,
- ctx);
- let computed_inline_size = match table_wrapper.table_layout {
- FixedLayout => {
- let fixed_cells_inline_size = table_wrapper.col_inline_sizes.iter().fold(Au(0),
- |sum, inline_size| sum.add(inline_size));
-
- let mut computed_inline_size = input.computed_inline_size.specified_or_zero();
- let style = table_wrapper.block_flow.fragment.style();
-
- // Get inline-start and inline-end paddings, borders for table.
- // We get these values from the fragment's style since table_wrapper doesn't have it's own border or padding.
- // input.available_inline-size is same as containing_block_inline-size in table_wrapper.
- let padding = style.logical_padding();
- let border = style.logical_border_width();
- let padding_and_borders =
- specified(padding.inline_start, input.available_inline_size) +
- specified(padding.inline_end, input.available_inline_size) +
- border.inline_start +
- border.inline_end;
- // Compare border-edge inline-sizes. Because fixed_cells_inline-size indicates content-inline-size,
- // padding and border values are added to fixed_cells_inline-size.
- computed_inline_size = geometry::max(
- fixed_cells_inline_size + padding_and_borders, computed_inline_size);
- computed_inline_size
- },
- AutoLayout => {
- // Automatic table layout is calculated according to CSS 2.1 § 17.5.2.2.
- let mut cap_min = Au(0);
- let mut cols_min = Au(0);
- let mut cols_max = Au(0);
- let mut col_min_inline_sizes = &vec!();
- let mut col_pref_inline_sizes = &vec!();
- for kid in table_wrapper.block_flow.base.child_iter() {
- if kid.is_table_caption() {
- cap_min = kid.as_block().base.intrinsic_inline_sizes.minimum_inline_size;
- } else {
- assert!(kid.is_table());
- cols_min = kid.as_block().base.intrinsic_inline_sizes.minimum_inline_size;
- cols_max = kid.as_block().base.intrinsic_inline_sizes.preferred_inline_size;
- col_min_inline_sizes = kid.col_min_inline_sizes();
- col_pref_inline_sizes = kid.col_pref_inline_sizes();
- }
- }
- // 'extra_inline-size': difference between the calculated table inline-size and minimum inline-size
- // required by all columns. It will be distributed over the columns.
- let (inline_size, extra_inline_size) = match input.computed_inline_size {
- Auto => {
- if input.available_inline_size > geometry::max(cols_max, cap_min) {
- if cols_max > cap_min {
- table_wrapper.col_inline_sizes = col_pref_inline_sizes.clone();
- (cols_max, Au(0))
- } else {
- (cap_min, cap_min - cols_min)
- }
- } else {
- let max = if cols_min >= input.available_inline_size && cols_min >= cap_min {
- table_wrapper.col_inline_sizes = col_min_inline_sizes.clone();
- cols_min
- } else {
- geometry::max(input.available_inline_size, cap_min)
- };
- (max, max - cols_min)
- }
- },
- Specified(inline_size) => {
- let max = if cols_min >= inline_size && cols_min >= cap_min {
- table_wrapper.col_inline_sizes = col_min_inline_sizes.clone();
- cols_min
- } else {
- geometry::max(inline_size, cap_min)
- };
- (max, max - cols_min)
- }
- };
- // The extra inline-size is distributed over the columns
- if extra_inline_size > Au(0) {
- let cell_len = table_wrapper.col_inline_sizes.len() as f64;
- table_wrapper.col_inline_sizes = col_min_inline_sizes.iter().map(|inline_size| {
- inline_size + extra_inline_size.scale_by(1.0 / cell_len)
- }).collect();
- }
- inline_size
- }
- };
- input.computed_inline_size = Specified(computed_inline_size);
- input
- }
-}
-
-impl ISizeAndMarginsComputer for TableWrapper {
- /// Solve the inline-size and margins constraints for this block flow.
- fn solve_inline_size_constraints(&self, block: &mut BlockFlow, input: &ISizeConstraintInput)
- -> ISizeConstraintSolution {
- self.solve_block_inline_size_constraints(block, input)
- }
-}
diff --git a/src/components/layout/text.rs b/src/components/layout/text.rs
deleted file mode 100644
index e90272e218a..00000000000
--- a/src/components/layout/text.rs
+++ /dev/null
@@ -1,327 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//! Text layout.
-
-#![deny(unsafe_block)]
-
-use flow::Flow;
-use fragment::{Fragment, ScannedTextFragment, ScannedTextFragmentInfo, UnscannedTextFragment};
-
-use gfx::font::{FontMetrics, FontStyle, RunMetrics};
-use gfx::font_context::FontContext;
-use gfx::text::glyph::CharIndex;
-use gfx::text::text_run::TextRun;
-use gfx::text::util::{CompressWhitespaceNewline, transform_text, CompressNone};
-use servo_util::geometry::Au;
-use servo_util::logical_geometry::{LogicalSize, WritingMode};
-use servo_util::range::Range;
-use style::ComputedValues;
-use style::computed_values::{font_family, line_height, text_orientation, white_space};
-use sync::Arc;
-
-struct NewLinePositions {
- new_line_pos: Vec<CharIndex>,
-}
-
-// A helper function.
-fn can_coalesce_text_nodes(fragments: &[Fragment], left_i: uint, right_i: uint) -> bool {
- assert!(left_i != right_i);
- fragments[left_i].can_merge_with_fragment(&fragments[right_i])
-}
-
-/// A stack-allocated object for scanning an inline flow into `TextRun`-containing `TextFragment`s.
-pub struct TextRunScanner {
- pub clump: Range<CharIndex>,
-}
-
-impl TextRunScanner {
- pub fn new() -> TextRunScanner {
- TextRunScanner {
- clump: Range::empty(),
- }
- }
-
- pub fn scan_for_runs(&mut self, font_context: &mut FontContext, flow: &mut Flow) {
- {
- let inline = flow.as_immutable_inline();
- debug!("TextRunScanner: scanning {:u} fragments for text runs...", inline.fragments.len());
- }
-
- let fragments = &mut flow.as_inline().fragments;
-
- let mut last_whitespace = true;
- let mut new_fragments = Vec::new();
- for fragment_i in range(0, fragments.fragments.len()) {
- debug!("TextRunScanner: considering fragment: {:u}", fragment_i);
- if fragment_i > 0 && !can_coalesce_text_nodes(fragments.fragments.as_slice(), fragment_i - 1, fragment_i) {
- last_whitespace = self.flush_clump_to_list(font_context,
- fragments.fragments.as_slice(),
- &mut new_fragments,
- last_whitespace);
- }
-
- self.clump.extend_by(CharIndex(1));
- }
-
- // Handle remaining clumps.
- if self.clump.length() > CharIndex(0) {
- drop(self.flush_clump_to_list(font_context,
- fragments.fragments.as_slice(),
- &mut new_fragments,
- last_whitespace))
- }
-
- debug!("TextRunScanner: swapping out fragments.");
-
- fragments.fragments = new_fragments;
- }
-
- /// A "clump" is a range of inline flow leaves that can be merged together into a single
- /// fragment. Adjacent text with the same style can be merged, and nothing else can.
- ///
- /// The flow keeps track of the fragments contained by all non-leaf DOM nodes. This is necessary
- /// for correct painting order. Since we compress several leaf fragments here, the mapping must
- /// be adjusted.
- ///
- /// FIXME(#2267, pcwalton): Stop cloning fragments. Instead we will need to replace each
- /// `in_fragment` with some smaller stub.
- fn flush_clump_to_list(&mut self,
- font_context: &mut FontContext,
- in_fragments: &[Fragment],
- out_fragments: &mut Vec<Fragment>,
- last_whitespace: bool)
- -> bool {
- assert!(self.clump.length() > CharIndex(0));
-
- debug!("TextRunScanner: flushing fragments in range={}", self.clump);
- let is_singleton = self.clump.length() == CharIndex(1);
-
- let is_text_clump = match in_fragments[self.clump.begin().to_uint()].specific {
- UnscannedTextFragment(_) => true,
- _ => false,
- };
-
- let mut new_whitespace = last_whitespace;
- match (is_singleton, is_text_clump) {
- (false, false) => {
- fail!("WAT: can't coalesce non-text nodes in flush_clump_to_list()!")
- }
- (true, false) => {
- // FIXME(pcwalton): Stop cloning fragments, as above.
- debug!("TextRunScanner: pushing single non-text fragment in range: {}", self.clump);
- let new_fragment = in_fragments[self.clump.begin().to_uint()].clone();
- out_fragments.push(new_fragment)
- },
- (true, true) => {
- let old_fragment = &in_fragments[self.clump.begin().to_uint()];
- let text = match old_fragment.specific {
- UnscannedTextFragment(ref text_fragment_info) => &text_fragment_info.text,
- _ => fail!("Expected an unscanned text fragment!"),
- };
-
- let font_style = old_fragment.font_style();
-
- let compression = match old_fragment.white_space() {
- white_space::normal => CompressWhitespaceNewline,
- white_space::pre => CompressNone,
- };
-
- let mut new_line_pos = vec![];
-
- let (transformed_text, whitespace) = transform_text(text.as_slice(),
- compression,
- last_whitespace,
- &mut new_line_pos);
-
- new_whitespace = whitespace;
-
- if transformed_text.len() > 0 {
- // TODO(#177): Text run creation must account for the renderability of text by
- // font group fonts. This is probably achieved by creating the font group above
- // and then letting `FontGroup` decide which `Font` to stick into the text run.
- let fontgroup = font_context.get_layout_font_group_for_style(&font_style);
- let run = box fontgroup.create_textrun(
- transformed_text.clone());
-
- debug!("TextRunScanner: pushing single text fragment in range: {} ({})",
- self.clump,
- *text);
- let range = Range::new(CharIndex(0), run.char_len());
- let new_metrics = run.metrics_for_range(&range);
- let bounding_box_size = bounding_box_for_run_metrics(
- &new_metrics, old_fragment.style.writing_mode);
- let new_text_fragment_info = ScannedTextFragmentInfo::new(Arc::new(run), range);
- let mut new_fragment = old_fragment.transform(
- bounding_box_size, ScannedTextFragment(new_text_fragment_info));
- new_fragment.new_line_pos = new_line_pos;
- out_fragments.push(new_fragment)
- }
- },
- (false, true) => {
- // TODO(#177): Text run creation must account for the renderability of text by
- // font group fonts. This is probably achieved by creating the font group above
- // and then letting `FontGroup` decide which `Font` to stick into the text run.
- let in_fragment = &in_fragments[self.clump.begin().to_uint()];
- let font_style = in_fragment.font_style();
- let fontgroup = font_context.get_layout_font_group_for_style(&font_style);
-
- let compression = match in_fragment.white_space() {
- white_space::normal => CompressWhitespaceNewline,
- white_space::pre => CompressNone,
- };
-
- let mut new_line_positions: Vec<NewLinePositions> = vec![];
-
- // First, transform/compress text of all the nodes.
- let mut last_whitespace_in_clump = new_whitespace;
- let transformed_strs: Vec<String> = Vec::from_fn(self.clump.length().to_uint(), |i| {
- let idx = CharIndex(i as int) + self.clump.begin();
- let in_fragment = match in_fragments[idx.to_uint()].specific {
- UnscannedTextFragment(ref text_fragment_info) => &text_fragment_info.text,
- _ => fail!("Expected an unscanned text fragment!"),
- };
-
- let mut new_line_pos = vec![];
-
- let (new_str, new_whitespace) = transform_text(in_fragment.as_slice(),
- compression,
- last_whitespace_in_clump,
- &mut new_line_pos);
- new_line_positions.push(NewLinePositions { new_line_pos: new_line_pos });
-
- last_whitespace_in_clump = new_whitespace;
- new_str
- });
- new_whitespace = last_whitespace_in_clump;
-
- // Next, concatenate all of the transformed strings together, saving the new
- // character indices.
- let mut run_str = String::new();
- let mut new_ranges: Vec<Range<CharIndex>> = vec![];
- let mut char_total = CharIndex(0);
- for i in range(0, transformed_strs.len() as int) {
- let added_chars = CharIndex(transformed_strs[i as uint].as_slice().char_len() as int);
- new_ranges.push(Range::new(char_total, added_chars));
- run_str.push_str(transformed_strs[i as uint].as_slice());
- char_total = char_total + added_chars;
- }
-
- // Now create the run.
- // TextRuns contain a cycle which is usually resolved by the teardown
- // sequence. If no clump takes ownership, however, it will leak.
- let clump = self.clump;
- let run = if clump.length() != CharIndex(0) && run_str.len() > 0 {
- Some(Arc::new(box TextRun::new(
- &mut *fontgroup.fonts[0].borrow_mut(),
- run_str.to_string())))
- } else {
- None
- };
-
- // Make new fragments with the run and adjusted text indices.
- debug!("TextRunScanner: pushing fragment(s) in range: {}", self.clump);
- for i in clump.each_index() {
- let logical_offset = i - self.clump.begin();
- let range = new_ranges[logical_offset.to_uint()];
- if range.length() == CharIndex(0) {
- debug!("Elided an `UnscannedTextFragment` because it was zero-length after \
- compression; {}", in_fragments[i.to_uint()]);
- continue
- }
-
- let new_text_fragment_info = ScannedTextFragmentInfo::new(run.get_ref().clone(), range);
- let old_fragment = &in_fragments[i.to_uint()];
- let new_metrics = new_text_fragment_info.run.metrics_for_range(&range);
- let bounding_box_size = bounding_box_for_run_metrics(
- &new_metrics, old_fragment.style.writing_mode);
- let mut new_fragment = old_fragment.transform(
- bounding_box_size, ScannedTextFragment(new_text_fragment_info));
- new_fragment.new_line_pos = new_line_positions[logical_offset.to_uint()].new_line_pos.clone();
- out_fragments.push(new_fragment)
- }
- }
- } // End of match.
-
- let end = self.clump.end(); // FIXME: borrow checker workaround
- self.clump.reset(end, CharIndex(0));
-
- new_whitespace
- } // End of `flush_clump_to_list`.
-}
-
-
-#[inline]
-fn bounding_box_for_run_metrics(metrics: &RunMetrics, writing_mode: WritingMode)
- -> LogicalSize<Au> {
-
- // This does nothing, but it will fail to build
- // when more values are added to the `text-orientation` CSS property.
- // This will be a reminder to update the code below.
- let dummy: Option<text_orientation::T> = None;
- match dummy {
- Some(text_orientation::sideways_right) |
- Some(text_orientation::sideways_left) |
- Some(text_orientation::sideways) |
- None => {}
- }
-
- // In vertical sideways or horizontal upgright text,
- // the "width" of text metrics is always inline
- // This will need to be updated when other text orientations are supported.
- LogicalSize::new(
- writing_mode,
- metrics.bounding_box.size.width,
- metrics.bounding_box.size.height)
-
-}
-
-/// Returns the metrics of the font represented by the given `FontStyle`, respectively.
-///
-/// `#[inline]` because often the caller only needs a few fields from the font metrics.
-#[inline]
-pub fn font_metrics_for_style(font_context: &mut FontContext, font_style: &FontStyle)
- -> FontMetrics {
- let fontgroup = font_context.get_layout_font_group_for_style(font_style);
- fontgroup.fonts[0].borrow().metrics.clone()
-}
-
-/// Converts a computed style to a font style used for rendering.
-///
-/// FIXME(pcwalton): This should not be necessary; just make the font part of the style sharable
-/// with the display list somehow. (Perhaps we should use an ARC.)
-pub fn computed_style_to_font_style(style: &ComputedValues) -> FontStyle {
- debug!("(font style) start");
-
- // FIXME: Too much allocation here.
- let mut font_families = style.get_font().font_family.iter().map(|family| {
- match *family {
- font_family::FamilyName(ref name) => (*name).clone(),
- }
- });
- debug!("(font style) font families: `{:?}`", font_families);
-
- let font_size = style.get_font().font_size.to_f64().unwrap() / 60.0;
- debug!("(font style) font size: `{:f}px`", font_size);
-
- FontStyle {
- pt_size: font_size,
- weight: style.get_font().font_weight,
- style: style.get_font().font_style,
- families: font_families.collect(),
- }
-}
-
-/// Returns the line block-size needed by the given computed style and font size.
-pub fn line_height_from_style(style: &ComputedValues, metrics: &FontMetrics) -> Au {
- let font_size = style.get_font().font_size;
- let from_inline = match style.get_inheritedbox().line_height {
- line_height::Normal => metrics.line_gap,
- line_height::Number(l) => font_size.scale_by(l),
- line_height::Length(l) => l
- };
- let minimum = style.get_inheritedbox()._servo_minimum_line_height;
- Au::max(from_inline, minimum)
-}
-
diff --git a/src/components/layout/util.rs b/src/components/layout/util.rs
deleted file mode 100644
index a0e466e8875..00000000000
--- a/src/components/layout/util.rs
+++ /dev/null
@@ -1,164 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-use construct::{ConstructionResult, NoConstructionResult};
-use incremental::RestyleDamage;
-use parallel::DomParallelInfo;
-use wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode};
-
-use gfx::display_list::OpaqueNode;
-use gfx;
-use libc::uintptr_t;
-use script::dom::bindings::js::JS;
-use script::dom::bindings::utils::Reflectable;
-use script::dom::node::{Node, SharedLayoutData};
-use script::layout_interface::{LayoutChan, UntrustedNodeAddress, TrustedNodeAddress};
-use std::mem;
-use std::cell::{Ref, RefMut};
-use style::ComputedValues;
-use style;
-use sync::Arc;
-
-/// Data that layout associates with a node.
-pub struct PrivateLayoutData {
- /// The results of CSS styling for this node's `before` pseudo-element, if any.
- pub before_style: Option<Arc<ComputedValues>>,
-
- /// The results of CSS styling for this node's `after` pseudo-element, if any.
- pub after_style: Option<Arc<ComputedValues>>,
-
- /// Description of how to account for recent style changes.
- pub restyle_damage: Option<RestyleDamage>,
-
- /// The current results of flow construction for this node. This is either a flow or a
- /// `ConstructionItem`. See comments in `construct.rs` for more details.
- pub flow_construction_result: ConstructionResult,
-
- pub before_flow_construction_result: ConstructionResult,
-
- pub after_flow_construction_result: ConstructionResult,
-
- /// Information needed during parallel traversals.
- pub parallel: DomParallelInfo,
-}
-
-impl PrivateLayoutData {
- /// Creates new layout data.
- pub fn new() -> PrivateLayoutData {
- PrivateLayoutData {
- before_style: None,
- after_style: None,
- restyle_damage: None,
- flow_construction_result: NoConstructionResult,
- before_flow_construction_result: NoConstructionResult,
- after_flow_construction_result: NoConstructionResult,
- parallel: DomParallelInfo::new(),
- }
- }
-}
-
-pub struct LayoutDataWrapper {
- pub chan: Option<LayoutChan>,
- pub shared_data: SharedLayoutData,
- pub data: Box<PrivateLayoutData>,
-}
-
-/// A trait that allows access to the layout data of a DOM node.
-pub trait LayoutDataAccess {
- /// Borrows the layout data without checks.
- unsafe fn borrow_layout_data_unchecked(&self) -> *const Option<LayoutDataWrapper>;
- /// Borrows the layout data immutably. Fails on a conflicting borrow.
- fn borrow_layout_data<'a>(&'a self) -> Ref<'a,Option<LayoutDataWrapper>>;
- /// Borrows the layout data mutably. Fails on a conflicting borrow.
- fn mutate_layout_data<'a>(&'a self) -> RefMut<'a,Option<LayoutDataWrapper>>;
-}
-
-impl<'ln> LayoutDataAccess for LayoutNode<'ln> {
- #[inline(always)]
- unsafe fn borrow_layout_data_unchecked(&self) -> *const Option<LayoutDataWrapper> {
- mem::transmute(self.get().layout_data.borrow_unchecked())
- }
-
- #[inline(always)]
- fn borrow_layout_data<'a>(&'a self) -> Ref<'a,Option<LayoutDataWrapper>> {
- unsafe {
- mem::transmute(self.get().layout_data.borrow())
- }
- }
-
- #[inline(always)]
- fn mutate_layout_data<'a>(&'a self) -> RefMut<'a,Option<LayoutDataWrapper>> {
- unsafe {
- mem::transmute(self.get().layout_data.borrow_mut())
- }
- }
-}
-
-pub trait OpaqueNodeMethods {
- /// Converts a DOM node (layout view) to an `OpaqueNode`.
- fn from_layout_node(node: &LayoutNode) -> Self;
-
- /// Converts a thread-safe DOM node (layout view) to an `OpaqueNode`.
- fn from_thread_safe_layout_node(node: &ThreadSafeLayoutNode) -> Self;
-
- /// Converts a DOM node (script view) to an `OpaqueNode`.
- fn from_script_node(node: TrustedNodeAddress) -> Self;
-
- /// Converts a DOM node to an `OpaqueNode'.
- fn from_jsmanaged(node: &JS<Node>) -> Self;
-
- /// Converts this node to an `UntrustedNodeAddress`. An `UntrustedNodeAddress` is just the type
- /// of node that script expects to receive in a hit test.
- fn to_untrusted_node_address(&self) -> UntrustedNodeAddress;
-}
-
-impl OpaqueNodeMethods for OpaqueNode {
- fn from_layout_node(node: &LayoutNode) -> OpaqueNode {
- unsafe {
- OpaqueNodeMethods::from_jsmanaged(node.get_jsmanaged())
- }
- }
-
- fn from_thread_safe_layout_node(node: &ThreadSafeLayoutNode) -> OpaqueNode {
- unsafe {
- let abstract_node = node.get_jsmanaged();
- let ptr: uintptr_t = abstract_node.reflector().get_jsobject() as uint;
- OpaqueNode(ptr)
- }
- }
-
- fn from_script_node(node: TrustedNodeAddress) -> OpaqueNode {
- unsafe {
- OpaqueNodeMethods::from_jsmanaged(&JS::from_trusted_node_address(node))
- }
- }
-
- fn from_jsmanaged(node: &JS<Node>) -> OpaqueNode {
- unsafe {
- let ptr: uintptr_t = mem::transmute(node.reflector().get_jsobject());
- OpaqueNode(ptr)
- }
- }
-
- fn to_untrusted_node_address(&self) -> UntrustedNodeAddress {
- unsafe {
- let OpaqueNode(addr) = *self;
- let addr: UntrustedNodeAddress = mem::transmute(addr);
- addr
- }
- }
-}
-
-/// Allows a CSS color to be converted into a graphics color.
-pub trait ToGfxColor {
- /// Converts a CSS color to a graphics color.
- fn to_gfx_color(&self) -> gfx::color::Color;
-}
-
-impl ToGfxColor for style::computed_values::RGBA {
- fn to_gfx_color(&self) -> gfx::color::Color {
- gfx::color::rgba(self.red, self.green, self.blue, self.alpha)
- }
-}
-
diff --git a/src/components/layout/wrapper.rs b/src/components/layout/wrapper.rs
deleted file mode 100644
index d052c263655..00000000000
--- a/src/components/layout/wrapper.rs
+++ /dev/null
@@ -1,783 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-//! A safe wrapper for DOM nodes that prevents layout from mutating the DOM, from letting DOM nodes
-//! escape, and from generally doing anything that it isn't supposed to. This is accomplished via
-//! a simple whitelist of allowed operations, along with some lifetime magic to prevent nodes from
-//! escaping.
-//!
-//! As a security wrapper is only as good as its whitelist, be careful when adding operations to
-//! this list. The cardinal rules are:
-//!
-//! 1. Layout is not allowed to mutate the DOM.
-//!
-//! 2. Layout is not allowed to see anything with `JS` in the name, because it could hang
-//! onto these objects and cause use-after-free.
-//!
-//! When implementing wrapper functions, be careful that you do not touch the borrow flags, or you
-//! will race and cause spurious task failure. (Note that I do not believe these races are
-//! exploitable, but they'll result in brokenness nonetheless.)
-//!
-//! Rules of the road for this file:
-//!
-//! * In general, you must not use the `Cast` functions; use explicit checks and `transmute_copy`
-//! instead.
-//!
-//! * You must also not use `.get()`; instead, use `.unsafe_get()`.
-//!
-//! * Do not call any methods on DOM nodes without checking to see whether they use borrow flags.
-//!
-//! o Instead of `get_attr()`, use `.get_attr_val_for_layout()`.
-//!
-//! o Instead of `html_element_in_html_document()`, use
-//! `html_element_in_html_document_for_layout()`.
-
-use css::node_style::StyledNode;
-use util::LayoutDataWrapper;
-
-use script::dom::bindings::codegen::InheritTypes::{HTMLIFrameElementDerived};
-use script::dom::bindings::codegen::InheritTypes::{HTMLImageElementDerived, TextDerived};
-use script::dom::bindings::js::JS;
-use script::dom::element::{Element, HTMLAreaElementTypeId, HTMLAnchorElementTypeId};
-use script::dom::element::{HTMLLinkElementTypeId, LayoutElementHelpers, RawLayoutElementHelpers};
-use script::dom::htmliframeelement::HTMLIFrameElement;
-use script::dom::htmlimageelement::{HTMLImageElement, LayoutHTMLImageElementHelpers};
-use script::dom::node::{DocumentNodeTypeId, ElementNodeTypeId, Node, NodeTypeId};
-use script::dom::node::{LayoutNodeHelpers, RawLayoutNodeHelpers, TextNodeTypeId};
-use script::dom::text::Text;
-use servo_msg::constellation_msg::{PipelineId, SubpageId};
-use servo_util::atom::Atom;
-use servo_util::namespace::Namespace;
-use servo_util::namespace;
-use servo_util::str::is_whitespace;
-use std::cell::{RefCell, Ref, RefMut};
-use std::kinds::marker::ContravariantLifetime;
-use std::mem;
-use style::computed_values::{content, display, white_space};
-use style::{AnyNamespace, AttrSelector, PropertyDeclarationBlock, SpecificNamespace, TElement};
-use style::{TNode};
-use url::Url;
-
-/// Allows some convenience methods on generic layout nodes.
-pub trait TLayoutNode {
- /// Creates a new layout node with the same lifetime as this layout node.
- unsafe fn new_with_this_lifetime(&self, node: &JS<Node>) -> Self;
-
- /// Returns the type ID of this node. Fails if this node is borrowed mutably. Returns `None`
- /// if this is a pseudo-element; otherwise, returns `Some`.
- fn type_id(&self) -> Option<NodeTypeId>;
-
- /// Returns the interior of this node as a `JS`. This is highly unsafe for layout to
- /// call and as such is marked `unsafe`.
- unsafe fn get_jsmanaged<'a>(&'a self) -> &'a JS<Node>;
-
- /// Returns the interior of this node as a `Node`. This is highly unsafe for layout to call
- /// and as such is marked `unsafe`.
- unsafe fn get<'a>(&'a self) -> &'a Node {
- &*self.get_jsmanaged().unsafe_get()
- }
-
- fn node_is_element(&self) -> bool {
- match self.type_id() {
- Some(ElementNodeTypeId(..)) => true,
- _ => false
- }
- }
-
- fn node_is_document(&self) -> bool {
- match self.type_id() {
- Some(DocumentNodeTypeId(..)) => true,
- _ => false
- }
- }
-
- /// If this is an image element, returns its URL. If this is not an image element, fails.
- ///
- /// FIXME(pcwalton): Don't copy URLs.
- fn image_url(&self) -> Option<Url> {
- unsafe {
- if !self.get().is_htmlimageelement() {
- fail!("not an image!")
- }
- let image_element: JS<HTMLImageElement> = self.get_jsmanaged().transmute_copy();
- image_element.image().as_ref().map(|url| (*url).clone())
- }
- }
-
- /// If this node is an iframe element, returns its pipeline and subpage IDs. If this node is
- /// not an iframe element, fails.
- fn iframe_pipeline_and_subpage_ids(&self) -> (PipelineId, SubpageId) {
- unsafe {
- if !self.get().is_htmliframeelement() {
- fail!("not an iframe element!")
- }
- let iframe_element: JS<HTMLIFrameElement> = self.get_jsmanaged().transmute_copy();
- let size = (*iframe_element.unsafe_get()).size.deref().get().unwrap();
- (size.pipeline_id, size.subpage_id)
- }
- }
-
- /// If this is a text node, copies out the text. If this is not a text node, fails.
- ///
- /// FIXME(pcwalton): Don't copy text. Atomically reference count instead.
- fn text(&self) -> String;
-
- /// Returns the first child of this node.
- fn first_child(&self) -> Option<Self>;
-
- /// Dumps this node tree, for debugging.
- fn dump(&self) {
- // TODO(pcwalton): Reimplement this in a way that's safe for layout to call.
- }
-}
-
-/// A wrapper so that layout can access only the methods that it should have access to. Layout must
-/// only ever see these and must never see instances of `JS`.
-pub struct LayoutNode<'a> {
- /// The wrapped node.
- node: JS<Node>,
-
- /// Being chained to a ContravariantLifetime prevents `LayoutNode`s from escaping.
- pub chain: ContravariantLifetime<'a>,
-}
-
-impl<'ln> Clone for LayoutNode<'ln> {
- fn clone(&self) -> LayoutNode<'ln> {
- LayoutNode {
- node: self.node.clone(),
- chain: self.chain,
- }
- }
-}
-
-impl<'a> PartialEq for LayoutNode<'a> {
- #[inline]
- fn eq(&self, other: &LayoutNode) -> bool {
- self.node == other.node
- }
-}
-
-
-impl<'ln> TLayoutNode for LayoutNode<'ln> {
- unsafe fn new_with_this_lifetime(&self, node: &JS<Node>) -> LayoutNode<'ln> {
- LayoutNode {
- node: node.transmute_copy(),
- chain: self.chain,
- }
- }
-
- fn type_id(&self) -> Option<NodeTypeId> {
- unsafe {
- Some(self.node.type_id_for_layout())
- }
- }
-
- unsafe fn get_jsmanaged<'a>(&'a self) -> &'a JS<Node> {
- &self.node
- }
-
- fn first_child(&self) -> Option<LayoutNode<'ln>> {
- unsafe {
- self.get_jsmanaged().first_child_ref().map(|node| self.new_with_this_lifetime(&node))
- }
- }
-
- fn text(&self) -> String {
- unsafe {
- if !self.get().is_text() {
- fail!("not text!")
- }
- let text: JS<Text> = self.get_jsmanaged().transmute_copy();
- (*text.unsafe_get()).characterdata.data.deref().borrow().clone()
- }
- }
-}
-
-impl<'ln> LayoutNode<'ln> {
- /// Creates a new layout node, scoped to the given closure.
- pub unsafe fn with_layout_node<R>(node: JS<Node>, f: <'a> |LayoutNode<'a>| -> R) -> R {
- f(LayoutNode {
- node: node,
- chain: ContravariantLifetime,
- })
- }
-
- /// Iterates over this node and all its descendants, in preorder.
- ///
- /// FIXME(pcwalton): Terribly inefficient. We should use parallelism.
- pub fn traverse_preorder(&self) -> LayoutTreeIterator<'ln> {
- let mut nodes = vec!();
- gather_layout_nodes(self, &mut nodes, false);
- LayoutTreeIterator::new(nodes)
- }
-
- /// Returns an iterator over this node's children.
- pub fn children(&self) -> LayoutNodeChildrenIterator<'ln> {
- LayoutNodeChildrenIterator {
- current_node: self.first_child(),
- }
- }
-
- pub unsafe fn get_jsmanaged<'a>(&'a self) -> &'a JS<Node> {
- &self.node
- }
-}
-
-impl<'ln> TNode<LayoutElement<'ln>> for LayoutNode<'ln> {
- fn parent_node(&self) -> Option<LayoutNode<'ln>> {
- unsafe {
- self.node.parent_node_ref().map(|node| self.new_with_this_lifetime(&node))
- }
- }
-
- fn prev_sibling(&self) -> Option<LayoutNode<'ln>> {
- unsafe {
- self.node.prev_sibling_ref().map(|node| self.new_with_this_lifetime(&node))
- }
- }
-
- fn next_sibling(&self) -> Option<LayoutNode<'ln>> {
- unsafe {
- self.node.next_sibling_ref().map(|node| self.new_with_this_lifetime(&node))
- }
- }
-
- /// If this is an element, accesses the element data. Fails if this is not an element node.
- #[inline]
- fn as_element(&self) -> LayoutElement<'ln> {
- unsafe {
- assert!(self.node.is_element_for_layout());
- let elem: JS<Element> = self.node.transmute_copy();
- let element = &*elem.unsafe_get();
- LayoutElement {
- element: mem::transmute(element),
- }
- }
- }
-
- fn is_element(&self) -> bool {
- self.node_is_element()
- }
-
- fn is_document(&self) -> bool {
- self.node_is_document()
- }
-
- fn match_attr(&self, attr: &AttrSelector, test: |&str| -> bool) -> bool {
- assert!(self.is_element())
- let name = if self.is_html_element_in_html_document() {
- attr.lower_name.as_slice()
- } else {
- attr.name.as_slice()
- };
- match attr.namespace {
- SpecificNamespace(ref ns) => {
- let element = self.as_element();
- element.get_attr(ns, name)
- .map_or(false, |attr| test(attr))
- },
- // FIXME: https://github.com/mozilla/servo/issues/1558
- AnyNamespace => false,
- }
- }
-
- fn is_html_element_in_html_document(&self) -> bool {
- unsafe {
- self.is_element() && {
- let element: JS<Element> = self.node.transmute_copy();
- element.html_element_in_html_document_for_layout()
- }
- }
- }
-}
-
-pub struct LayoutNodeChildrenIterator<'a> {
- current_node: Option<LayoutNode<'a>>,
-}
-
-impl<'a> Iterator<LayoutNode<'a>> for LayoutNodeChildrenIterator<'a> {
- fn next(&mut self) -> Option<LayoutNode<'a>> {
- let node = self.current_node.clone();
- self.current_node = node.clone().and_then(|node| {
- node.next_sibling()
- });
- node
- }
-}
-
-// FIXME: Do this without precomputing a vector of refs.
-// Easy for preorder; harder for postorder.
-//
-// FIXME(pcwalton): Parallelism! Eventually this should just be nuked.
-pub struct LayoutTreeIterator<'a> {
- nodes: Vec<LayoutNode<'a>>,
- index: uint,
-}
-
-impl<'a> LayoutTreeIterator<'a> {
- fn new(nodes: Vec<LayoutNode<'a>>) -> LayoutTreeIterator<'a> {
- LayoutTreeIterator {
- nodes: nodes,
- index: 0,
- }
- }
-}
-
-impl<'a> Iterator<LayoutNode<'a>> for LayoutTreeIterator<'a> {
- fn next(&mut self) -> Option<LayoutNode<'a>> {
- if self.index >= self.nodes.len() {
- None
- } else {
- let v = self.nodes[self.index].clone();
- self.index += 1;
- Some(v)
- }
- }
-}
-
-/// FIXME(pcwalton): This is super inefficient.
-fn gather_layout_nodes<'a>(cur: &LayoutNode<'a>, refs: &mut Vec<LayoutNode<'a>>, postorder: bool) {
- if !postorder {
- refs.push(cur.clone());
- }
- for kid in cur.children() {
- gather_layout_nodes(&kid, refs, postorder)
- }
- if postorder {
- refs.push(cur.clone());
- }
-}
-
-/// A wrapper around elements that ensures layout can only ever access safe properties.
-pub struct LayoutElement<'le> {
- element: &'le Element,
-}
-
-impl<'le> LayoutElement<'le> {
- pub fn style_attribute(&self) -> &'le Option<PropertyDeclarationBlock> {
- let style: &Option<PropertyDeclarationBlock> = unsafe {
- let style: &RefCell<Option<PropertyDeclarationBlock>> = self.element.style_attribute.deref();
- // cast to the direct reference to T placed on the head of RefCell<T>
- mem::transmute(style)
- };
- style
- }
-}
-
-impl<'le> TElement for LayoutElement<'le> {
- #[inline]
- fn get_local_name<'a>(&'a self) -> &'a Atom {
- &self.element.local_name
- }
-
- #[inline]
- fn get_namespace<'a>(&'a self) -> &'a Namespace {
- &self.element.namespace
- }
-
- #[inline]
- fn get_attr(&self, namespace: &Namespace, name: &str) -> Option<&'static str> {
- unsafe { self.element.get_attr_val_for_layout(namespace, name) }
- }
-
- fn get_link(&self) -> Option<&'static str> {
- // FIXME: This is HTML only.
- match self.element.node.type_id_for_layout() {
- // http://www.whatwg.org/specs/web-apps/current-work/multipage/selectors.html#
- // selector-link
- ElementNodeTypeId(HTMLAnchorElementTypeId) |
- ElementNodeTypeId(HTMLAreaElementTypeId) |
- ElementNodeTypeId(HTMLLinkElementTypeId) => {
- unsafe { self.element.get_attr_val_for_layout(&namespace::Null, "href") }
- }
- _ => None,
- }
- }
-
- fn get_hover_state(&self) -> bool {
- unsafe {
- self.element.node.get_hover_state_for_layout()
- }
- }
-
- #[inline]
- fn get_id(&self) -> Option<Atom> {
- unsafe { self.element.get_attr_atom_for_layout(&namespace::Null, "id") }
- }
-
- fn get_disabled_state(&self) -> bool {
- unsafe {
- self.element.node.get_disabled_state_for_layout()
- }
- }
-
- fn get_enabled_state(&self) -> bool {
- unsafe {
- self.element.node.get_enabled_state_for_layout()
- }
- }
-}
-
-fn get_content(content_list: &content::T) -> String {
- match *content_list {
- content::Content(ref value) => {
- let iter = &mut value.clone().move_iter().peekable();
- match iter.next() {
- Some(content::StringContent(content)) => content,
- _ => "".to_string(),
- }
- }
- _ => "".to_string(),
- }
-}
-
-#[deriving(PartialEq, Clone)]
-pub enum PseudoElementType {
- Normal,
- Before,
- After,
- BeforeBlock,
- AfterBlock,
-}
-
-/// A thread-safe version of `LayoutNode`, used during flow construction. This type of layout
-/// node does not allow any parents or siblings of nodes to be accessed, to avoid races.
-#[deriving(Clone)]
-pub struct ThreadSafeLayoutNode<'ln> {
- /// The wrapped node.
- node: LayoutNode<'ln>,
-
- pseudo: PseudoElementType,
-}
-
-impl<'ln> TLayoutNode for ThreadSafeLayoutNode<'ln> {
- /// Creates a new layout node with the same lifetime as this layout node.
- unsafe fn new_with_this_lifetime(&self, node: &JS<Node>) -> ThreadSafeLayoutNode<'ln> {
- ThreadSafeLayoutNode {
- node: LayoutNode {
- node: node.transmute_copy(),
- chain: self.node.chain,
- },
- pseudo: Normal,
- }
- }
-
- /// Returns `None` if this is a pseudo-element.
- fn type_id(&self) -> Option<NodeTypeId> {
- if self.pseudo == Before || self.pseudo == After {
- return None
- }
-
- self.node.type_id()
- }
-
- unsafe fn get_jsmanaged<'a>(&'a self) -> &'a JS<Node> {
- self.node.get_jsmanaged()
- }
-
- unsafe fn get<'a>(&'a self) -> &'a Node { // this change.
- mem::transmute::<*mut Node,&'a Node>(self.get_jsmanaged().unsafe_get())
- }
-
- fn first_child(&self) -> Option<ThreadSafeLayoutNode<'ln>> {
- if self.pseudo == Before || self.pseudo == After {
- return None
- }
-
- if self.has_before_pseudo() {
- if self.is_block(Before) && self.pseudo == Normal {
- let pseudo_before_node = self.with_pseudo(BeforeBlock);
- return Some(pseudo_before_node)
- } else if self.pseudo == Normal || self.pseudo == BeforeBlock {
- let pseudo_before_node = self.with_pseudo(Before);
- return Some(pseudo_before_node)
- }
- }
-
- unsafe {
- self.get_jsmanaged().first_child_ref().map(|node| self.new_with_this_lifetime(&node))
- }
- }
-
- fn text(&self) -> String {
- if self.pseudo == Before || self.pseudo == After {
- let layout_data_ref = self.borrow_layout_data();
- let node_layout_data_wrapper = layout_data_ref.get_ref();
-
- if self.pseudo == Before {
- let before_style = node_layout_data_wrapper.data.before_style.get_ref();
- return get_content(&before_style.get_box().content)
- } else {
- let after_style = node_layout_data_wrapper.data.after_style.get_ref();
- return get_content(&after_style.get_box().content)
- }
- }
-
- unsafe {
- if !self.get().is_text() {
- fail!("not text!")
- }
- let text: JS<Text> = self.get_jsmanaged().transmute_copy();
- (*text.unsafe_get()).characterdata.data.deref().borrow().clone()
- }
- }
-}
-
-
-impl<'ln> ThreadSafeLayoutNode<'ln> {
- /// Creates a new `ThreadSafeLayoutNode` from the given `LayoutNode`.
- pub fn new<'a>(node: &LayoutNode<'a>) -> ThreadSafeLayoutNode<'a> {
- ThreadSafeLayoutNode {
- node: node.clone(),
- pseudo: Normal,
- }
- }
-
- /// Creates a new `ThreadSafeLayoutNode` for the same `LayoutNode`
- /// with a different pseudo-element type.
- fn with_pseudo(&self, pseudo: PseudoElementType) -> ThreadSafeLayoutNode<'ln> {
- ThreadSafeLayoutNode {
- node: self.node.clone(),
- pseudo: pseudo,
- }
- }
-
- /// Returns the next sibling of this node. Unsafe and private because this can lead to races.
- unsafe fn next_sibling(&self) -> Option<ThreadSafeLayoutNode<'ln>> {
- if self.pseudo == Before || self.pseudo == BeforeBlock {
- return self.get_jsmanaged().first_child_ref().map(|node| self.new_with_this_lifetime(&node))
- }
-
- self.get_jsmanaged().next_sibling_ref().map(|node| self.new_with_this_lifetime(&node))
- }
-
- /// Returns an iterator over this node's children.
- pub fn children(&self) -> ThreadSafeLayoutNodeChildrenIterator<'ln> {
- ThreadSafeLayoutNodeChildrenIterator {
- current_node: self.first_child(),
- parent_node: Some(self.clone()),
- }
- }
-
- /// If this is an element, accesses the element data. Fails if this is not an element node.
- #[inline]
- pub fn as_element(&self) -> ThreadSafeLayoutElement {
- unsafe {
- assert!(self.get_jsmanaged().is_element_for_layout());
- let elem: JS<Element> = self.get_jsmanaged().transmute_copy();
- let element = elem.unsafe_get();
- // FIXME(pcwalton): Workaround until Rust gets multiple lifetime parameters on
- // implementations.
- ThreadSafeLayoutElement {
- element: &mut *element,
- }
- }
- }
-
- pub fn get_pseudo_element_type(&self) -> PseudoElementType {
- self.pseudo
- }
-
- pub fn is_block(&self, kind: PseudoElementType) -> bool {
- let mut layout_data_ref = self.mutate_layout_data();
- let node_layout_data_wrapper = layout_data_ref.get_mut_ref();
-
- let display = match kind {
- Before | BeforeBlock => {
- let before_style = node_layout_data_wrapper.data.before_style.get_ref();
- before_style.get_box().display
- }
- After | AfterBlock => {
- let after_style = node_layout_data_wrapper.data.after_style.get_ref();
- after_style.get_box().display
- }
- Normal => {
- let after_style = node_layout_data_wrapper.shared_data.style.get_ref();
- after_style.get_box().display
- }
- };
-
- display == display::block
- }
-
- pub fn has_before_pseudo(&self) -> bool {
- let layout_data_wrapper = self.borrow_layout_data();
- let layout_data_wrapper_ref = layout_data_wrapper.get_ref();
- layout_data_wrapper_ref.data.before_style.is_some()
- }
-
- pub fn has_after_pseudo(&self) -> bool {
- let layout_data_wrapper = self.borrow_layout_data();
- let layout_data_wrapper_ref = layout_data_wrapper.get_ref();
- layout_data_wrapper_ref.data.after_style.is_some()
- }
-
- /// Borrows the layout data immutably. Fails on a conflicting borrow.
- #[inline(always)]
- pub fn borrow_layout_data<'a>(&'a self) -> Ref<'a,Option<LayoutDataWrapper>> {
- unsafe {
- mem::transmute(self.get().layout_data.borrow())
- }
- }
-
- /// Borrows the layout data mutably. Fails on a conflicting borrow.
- #[inline(always)]
- pub fn mutate_layout_data<'a>(&'a self) -> RefMut<'a,Option<LayoutDataWrapper>> {
- unsafe {
- mem::transmute(self.get().layout_data.borrow_mut())
- }
- }
-
- /// Traverses the tree in postorder.
- ///
- /// TODO(pcwalton): Offer a parallel version with a compatible API.
- pub fn traverse_postorder_mut<T:PostorderNodeMutTraversal>(&mut self, traversal: &mut T)
- -> bool {
- if traversal.should_prune(self) {
- return true
- }
-
- let mut opt_kid = self.first_child();
- loop {
- match opt_kid {
- None => break,
- Some(mut kid) => {
- if !kid.traverse_postorder_mut(traversal) {
- return false
- }
- unsafe {
- opt_kid = kid.next_sibling()
- }
- }
- }
- }
-
- traversal.process(self)
- }
-
- pub fn is_ignorable_whitespace(&self) -> bool {
- match self.type_id() {
- Some(TextNodeTypeId) => {
- unsafe {
- let text: JS<Text> = self.get_jsmanaged().transmute_copy();
- if !is_whitespace((*text.unsafe_get()).characterdata.data.deref().borrow().as_slice()) {
- return false
- }
-
- // NB: See the rules for `white-space` here:
- //
- // http://www.w3.org/TR/CSS21/text.html#propdef-white-space
- //
- // If you implement other values for this property, you will almost certainly
- // want to update this check.
- match self.style().get_inheritedtext().white_space {
- white_space::normal => true,
- _ => false,
- }
- }
- }
- _ => false
- }
- }
-}
-
-pub struct ThreadSafeLayoutNodeChildrenIterator<'a> {
- current_node: Option<ThreadSafeLayoutNode<'a>>,
- parent_node: Option<ThreadSafeLayoutNode<'a>>,
-}
-
-impl<'a> Iterator<ThreadSafeLayoutNode<'a>> for ThreadSafeLayoutNodeChildrenIterator<'a> {
- fn next(&mut self) -> Option<ThreadSafeLayoutNode<'a>> {
- let node = self.current_node.clone();
-
- match node {
- Some(ref node) => {
- if node.pseudo == After || node.pseudo == AfterBlock {
- return None
- }
-
- match self.parent_node {
- Some(ref parent_node) => {
- if parent_node.pseudo == Normal {
- self.current_node = self.current_node.clone().and_then(|node| {
- unsafe {
- node.next_sibling()
- }
- });
- } else {
- self.current_node = None;
- }
- }
- None => {}
- }
- }
- None => {
- match self.parent_node {
- Some(ref parent_node) => {
- if parent_node.has_after_pseudo() {
- let pseudo_after_node = if parent_node.is_block(After) && parent_node.pseudo == Normal {
- let pseudo_after_node = parent_node.with_pseudo(AfterBlock);
- Some(pseudo_after_node)
- } else if parent_node.pseudo == Normal || parent_node.pseudo == AfterBlock {
- let pseudo_after_node = parent_node.with_pseudo(After);
- Some(pseudo_after_node)
- } else {
- None
- };
- self.current_node = pseudo_after_node;
- return self.current_node.clone()
- }
- }
- None => {}
- }
- }
- }
-
- node
- }
-}
-
-/// A wrapper around elements that ensures layout can only ever access safe properties and cannot
-/// race on elements.
-pub struct ThreadSafeLayoutElement<'le> {
- element: &'le Element,
-}
-
-impl<'le> ThreadSafeLayoutElement<'le> {
- #[inline]
- pub fn get_attr(&self, namespace: &Namespace, name: &str) -> Option<&'static str> {
- unsafe { self.element.get_attr_val_for_layout(namespace, name) }
- }
-}
-
-/// A bottom-up, parallelizable traversal.
-pub trait PostorderNodeMutTraversal {
- /// The operation to perform. Return true to continue or false to stop.
- fn process<'a>(&'a mut self, node: &ThreadSafeLayoutNode<'a>) -> bool;
-
- /// Returns true if this node should be pruned. If this returns true, we skip the operation
- /// entirely and do not process any descendant nodes. This is called *before* child nodes are
- /// visited. The default implementation never prunes any nodes.
- fn should_prune<'a>(&'a self, _node: &ThreadSafeLayoutNode<'a>) -> bool {
- false
- }
-}
-
-/// Opaque type stored in type-unsafe work queues for parallel layout.
-/// Must be transmutable to and from LayoutNode/ThreadSafeLayoutNode.
-pub type UnsafeLayoutNode = (uint, uint);
-
-pub fn layout_node_to_unsafe_layout_node(node: &LayoutNode) -> UnsafeLayoutNode {
- unsafe {
- let ptr: uint = mem::transmute_copy(node);
- (ptr, 0)
- }
-}
-
-// FIXME(#3044): This should be updated to use a real lifetime instead of
-// faking one.
-pub unsafe fn layout_node_from_unsafe_layout_node(node: &UnsafeLayoutNode) -> LayoutNode<'static> {
- let (node, _) = *node;
- mem::transmute(node)
-}