aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/layout/fragment.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/layout/fragment.rs')
-rw-r--r--src/components/layout/fragment.rs1597
1 files changed, 0 insertions, 1597 deletions
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
- }
-}