diff options
Diffstat (limited to 'components/layout/fragment.rs')
-rw-r--r-- | components/layout/fragment.rs | 759 |
1 files changed, 270 insertions, 489 deletions
diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index f2ba5e83916..e5d86e78ed5 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -23,7 +23,8 @@ use inline::{InlineMetrics, LAST_FRAGMENT_OF_ELEMENT, LineMetrics}; use ipc_channel::ipc::IpcSender; #[cfg(debug_assertions)] use layout_debug; -use model::{self, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto}; +use model::{self, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, SizeConstraint}; +use model::style_length; use msg::constellation_msg::PipelineId; use net_traits::image::base::{Image, ImageMetadata}; use net_traits::image_cache_thread::{ImageOrMetadataAvailable, UsePlaceholder}; @@ -34,7 +35,7 @@ use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayou use serde::{Serialize, Serializer}; use servo_url::ServoUrl; use std::borrow::ToOwned; -use std::cmp::{max, min}; +use std::cmp::{Ordering, max, min}; use std::collections::LinkedList; use std::fmt; use std::sync::{Arc, Mutex}; @@ -43,7 +44,6 @@ use style::computed_values::{border_collapse, box_sizing, clear, color, display, use style::computed_values::{overflow_wrap, overflow_x, position, text_decoration}; use style::computed_values::{transform_style, vertical_align, white_space, word_break, z_index}; use style::computed_values::content::ContentItem; -use style::context::SharedStyleContext; use style::logical_geometry::{Direction, LogicalMargin, LogicalRect, LogicalSize, WritingMode}; use style::properties::ServoComputedValues; use style::selector_parser::RestyleDamage; @@ -51,7 +51,6 @@ use style::servo::restyle_damage::RECONSTRUCT_FLOW; use style::str::char_is_whitespace; use style::values::Either; use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto}; -use style::values::computed::LengthOrPercentageOrNone; use text; use text::TextRunScanner; @@ -59,6 +58,10 @@ use text::TextRunScanner; static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20; static FONT_SUPERSCRIPT_OFFSET_RATIO: f32 = 0.34; +// https://drafts.csswg.org/css-images/#default-object-size +static DEFAULT_REPLACED_WIDTH: i32 = 300; +static DEFAULT_REPLACED_HEIGHT: i32 = 150; + /// 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: @@ -247,21 +250,6 @@ impl fmt::Debug for SpecificFragmentInfo { } } -/// Clamp a value obtained from style_length, based on min / max lengths. -fn clamp_size(size: Au, - min_size: LengthOrPercentage, - max_size: LengthOrPercentageOrNone, - container_size: Au) - -> Au { - let min_size = model::specified(min_size, container_size); - let max_size = model::specified_or_none(max_size, container_size); - - max(min_size, match max_size { - None => size, - Some(max_size) => min(size, max_size), - }) -} - /// Information for generated content. #[derive(Clone)] pub enum GeneratedContentInfo { @@ -326,89 +314,41 @@ impl InlineAbsoluteFragmentInfo { #[derive(Clone)] pub struct CanvasFragmentInfo { - pub replaced_image_fragment_info: ReplacedImageFragmentInfo, pub ipc_renderer: Option<Arc<Mutex<IpcSender<CanvasMsg>>>>, pub dom_width: Au, pub dom_height: Au, } impl CanvasFragmentInfo { - pub fn new<N: ThreadSafeLayoutNode>(node: &N, - data: HTMLCanvasData, - ctx: &SharedStyleContext) - -> CanvasFragmentInfo { + pub fn new(data: HTMLCanvasData) -> CanvasFragmentInfo { CanvasFragmentInfo { - replaced_image_fragment_info: ReplacedImageFragmentInfo::new(node, ctx), ipc_renderer: data.ipc_renderer .map(|renderer| Arc::new(Mutex::new(renderer))), dom_width: Au::from_px(data.width as i32), dom_height: Au::from_px(data.height as i32), } } - - /// Returns the original inline-size of the canvas. - pub fn canvas_inline_size(&self) -> Au { - if self.replaced_image_fragment_info.writing_mode_is_vertical { - self.dom_height - } else { - self.dom_width - } - } - - /// Returns the original block-size of the canvas. - pub fn canvas_block_size(&self) -> Au { - if self.replaced_image_fragment_info.writing_mode_is_vertical { - self.dom_width - } else { - self.dom_height - } - } } #[derive(Clone)] pub struct SvgFragmentInfo { - pub replaced_image_fragment_info: ReplacedImageFragmentInfo, pub dom_width: Au, pub dom_height: Au, } impl SvgFragmentInfo { - pub fn new<N: ThreadSafeLayoutNode>(node: &N, - data: SVGSVGData, - ctx: &SharedStyleContext) - -> SvgFragmentInfo { + pub fn new(data: SVGSVGData) -> SvgFragmentInfo { SvgFragmentInfo { - replaced_image_fragment_info: ReplacedImageFragmentInfo::new(node, ctx), dom_width: Au::from_px(data.width as i32), dom_height: Au::from_px(data.height as i32), } } - - /// Returns the original inline-size of the SVG element. - pub fn svg_inline_size(&self) -> Au { - if self.replaced_image_fragment_info.writing_mode_is_vertical { - self.dom_height - } else { - self.dom_width - } - } - - /// Returns the original block-size of the SVG element. - pub fn svg_block_size(&self) -> Au { - if self.replaced_image_fragment_info.writing_mode_is_vertical { - self.dom_width - } else { - self.dom_height - } - } } /// A fragment that represents a replaced content image and its accompanying borders, shadows, etc. #[derive(Clone)] pub struct ImageFragmentInfo { - /// The image held within this fragment. - pub replaced_image_fragment_info: ReplacedImageFragmentInfo, pub image: Option<Arc<Image>>, pub metadata: Option<ImageMetadata>, } @@ -418,9 +358,9 @@ impl ImageFragmentInfo { /// /// FIXME(pcwalton): The fact that image fragments store the cache in the fragment makes little /// sense to me. - pub fn new<N: ThreadSafeLayoutNode>(node: &N, url: Option<ServoUrl>, - shared_layout_context: &SharedLayoutContext) - -> ImageFragmentInfo { + pub fn new(url: Option<ServoUrl>, + shared_layout_context: &SharedLayoutContext) + -> ImageFragmentInfo { let image_or_metadata = url.and_then(|url| { shared_layout_context.get_or_request_image_or_meta(url, UsePlaceholder::Yes) }); @@ -438,40 +378,11 @@ impl ImageFragmentInfo { }; ImageFragmentInfo { - replaced_image_fragment_info: ReplacedImageFragmentInfo::new(node, &shared_layout_context.style_context), image: image, metadata: metadata, } } - /// Returns the original inline-size of the image. - pub fn image_inline_size(&mut self) -> Au { - match self.metadata { - Some(ref metadata) => { - Au::from_px(if self.replaced_image_fragment_info.writing_mode_is_vertical { - metadata.height - } else { - metadata.width - } as i32) - } - None => Au(0) - } - } - - /// Returns the original block-size of the image. - pub fn image_block_size(&mut self) -> Au { - match self.metadata { - Some(ref metadata) => { - Au::from_px(if self.replaced_image_fragment_info.writing_mode_is_vertical { - metadata.width - } else { - metadata.height - } as i32) - } - None => Au(0) - } - } - pub fn tile_image_round(position: &mut Au, size: &mut Au, absolute_anchor_origin: Au, @@ -544,149 +455,6 @@ impl ImageFragmentInfo { } } -#[derive(Clone)] -pub struct ReplacedImageFragmentInfo { - pub computed_inline_size: Option<Au>, - pub computed_block_size: Option<Au>, - pub writing_mode_is_vertical: bool, -} - -impl ReplacedImageFragmentInfo { - pub fn new<N>(node: &N, ctx: &SharedStyleContext) -> ReplacedImageFragmentInfo - where N: ThreadSafeLayoutNode { - let is_vertical = node.style(ctx).writing_mode.is_vertical(); - ReplacedImageFragmentInfo { - computed_inline_size: None, - computed_block_size: None, - 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!") - } - - // 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, - container_size: Option<Au>) -> MaybeAuto { - match (style_length, container_size) { - (LengthOrPercentageOrAuto::Length(length), _) => MaybeAuto::Specified(length), - (LengthOrPercentageOrAuto::Percentage(pc), Some(container_size)) => { - MaybeAuto::Specified(container_size.scale_by(pc)) - } - (LengthOrPercentageOrAuto::Percentage(_), None) => MaybeAuto::Auto, - (LengthOrPercentageOrAuto::Calc(calc), Some(container_size)) => { - MaybeAuto::Specified(calc.length() + container_size.scale_by(calc.percentage())) - } - (LengthOrPercentageOrAuto::Calc(_), None) => MaybeAuto::Auto, - (LengthOrPercentageOrAuto::Auto, _) => MaybeAuto::Auto, - } - } - - pub fn calculate_replaced_inline_size(&mut self, - style: &ServoComputedValues, - noncontent_inline_size: Au, - container_inline_size: Au, - container_block_size: Option<Au>, - fragment_inline_size: Au, - fragment_block_size: Au) - -> Au { - let style_inline_size = style.content_inline_size(); - let style_block_size = style.content_block_size(); - let style_min_inline_size = style.min_inline_size(); - let style_max_inline_size = style.max_inline_size(); - let style_min_block_size = style.min_block_size(); - let style_max_block_size = style.max_block_size(); - - // TODO(ksh8281): compute border,margin - let inline_size = ReplacedImageFragmentInfo::style_length( - style_inline_size, - Some(container_inline_size)); - - let inline_size = match inline_size { - MaybeAuto::Auto => { - let intrinsic_width = fragment_inline_size; - let intrinsic_height = fragment_block_size; - if intrinsic_height == Au(0) { - intrinsic_width - } else { - let ratio = intrinsic_width.to_f32_px() / - intrinsic_height.to_f32_px(); - - let specified_height = ReplacedImageFragmentInfo::style_length( - style_block_size, - container_block_size); - let specified_height = match specified_height { - MaybeAuto::Auto => intrinsic_height, - MaybeAuto::Specified(h) => h, - }; - let specified_height = clamp_size(specified_height, - style_min_block_size, - style_max_block_size, - Au(0)); - Au::from_f32_px(specified_height.to_f32_px() * ratio) - } - }, - MaybeAuto::Specified(w) => w, - }; - - let inline_size = clamp_size(inline_size, - style_min_inline_size, - style_max_inline_size, - container_inline_size); - - self.computed_inline_size = Some(inline_size); - inline_size + noncontent_inline_size - } - - /// Here, `noncontent_block_size` represents the sum of border and padding, but not margin. - pub fn calculate_replaced_block_size(&mut self, - style: &ServoComputedValues, - noncontent_block_size: Au, - containing_block_block_size: Option<Au>, - fragment_inline_size: Au, - fragment_block_size: Au) - -> Au { - let style_block_size = style.content_block_size(); - let style_min_block_size = style.min_block_size(); - let style_max_block_size = style.max_block_size(); - - let inline_size = self.computed_inline_size(); - let block_size = ReplacedImageFragmentInfo::style_length( - style_block_size, - containing_block_block_size); - - let block_size = match block_size { - MaybeAuto::Auto => { - let intrinsic_width = fragment_inline_size; - let intrinsic_height = fragment_block_size; - let scale = intrinsic_width.to_f32_px() / inline_size.to_f32_px(); - Au::from_f32_px(intrinsic_height.to_f32_px() / scale) - }, - MaybeAuto::Specified(h) => { - h - } - }; - - let block_size = clamp_size(block_size, - style_min_block_size, - style_max_block_size, - Au(0)); - - self.computed_block_size = Some(block_size); - block_size + noncontent_block_size - } -} - /// 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 thread. #[derive(Clone)] @@ -703,52 +471,6 @@ impl IframeFragmentInfo { pipeline_id: pipeline_id, } } - - #[inline] - pub fn calculate_replaced_inline_size(&self, style: &ServoComputedValues, containing_size: Au) - -> Au { - // Calculate the replaced inline size (or default) as per CSS 2.1 § 10.3.2 - IframeFragmentInfo::calculate_replaced_size(style.content_inline_size(), - style.min_inline_size(), - style.max_inline_size(), - Some(containing_size), - Au::from_px(300)) - } - - #[inline] - pub fn calculate_replaced_block_size(&self, style: &ServoComputedValues, containing_size: Option<Au>) - -> Au { - // Calculate the replaced block size (or default) as per CSS 2.1 § 10.3.2 - IframeFragmentInfo::calculate_replaced_size(style.content_block_size(), - style.min_block_size(), - style.max_block_size(), - containing_size, - Au::from_px(150)) - - } - - fn calculate_replaced_size(content_size: LengthOrPercentageOrAuto, - style_min_size: LengthOrPercentage, - style_max_size: LengthOrPercentageOrNone, - containing_size: Option<Au>, - default_size: Au) -> Au { - let computed_size = match (content_size, containing_size) { - (LengthOrPercentageOrAuto::Length(length), _) => length, - (LengthOrPercentageOrAuto::Percentage(pc), Some(container_size)) => container_size.scale_by(pc), - (LengthOrPercentageOrAuto::Calc(calc), Some(container_size)) => { - container_size.scale_by(calc.percentage()) + calc.length() - }, - (LengthOrPercentageOrAuto::Calc(calc), None) => calc.length(), - (LengthOrPercentageOrAuto::Percentage(_), None) => default_size, - (LengthOrPercentageOrAuto::Auto, _) => default_size, - }; - - let containing_size = containing_size.unwrap_or(Au(0)); - clamp_size(computed_size, - style_min_size, - style_max_size, - containing_size) - } } /// A scanned text fragment represents a single run of text with a distinct style. A `TextFragment` @@ -1202,6 +924,198 @@ impl Fragment { } } + /// intrinsic width of this replaced element. + #[inline] + pub fn intrinsic_width(&self) -> Au { + match self.specific { + SpecificFragmentInfo::Image(ref info) => { + if let Some(ref data) = info.metadata { + Au::from_px(data.width as i32) + } else { + Au(0) + } + } + SpecificFragmentInfo::Canvas(ref info) => info.dom_width, + SpecificFragmentInfo::Svg(ref info) => info.dom_width, + // Note: Currently for replaced element with no intrinsic size, + // this function simply returns the default object size. As long as + // these elements do not have intrinsic aspect ratio this should be + // sufficient, but we may need to investigate if this is enough for + // use cases like SVG. + SpecificFragmentInfo::Iframe(_) => Au::from_px(DEFAULT_REPLACED_WIDTH), + _ => panic!("Trying to get intrinsic width on non-replaced element!") + } + } + + /// intrinsic width of this replaced element. + #[inline] + pub fn intrinsic_height(&self) -> Au { + match self.specific { + SpecificFragmentInfo::Image(ref info) => { + if let Some(ref data) = info.metadata { + Au::from_px(data.height as i32) + } else { + Au(0) + } + } + SpecificFragmentInfo::Canvas(ref info) => info.dom_height, + SpecificFragmentInfo::Svg(ref info) => info.dom_height, + SpecificFragmentInfo::Iframe(_) => Au::from_px(DEFAULT_REPLACED_HEIGHT), + _ => panic!("Trying to get intrinsic height on non-replaced element!") + } + } + + /// Whether this replace element has intrinsic aspect ratio. + pub fn has_intrinsic_ratio(&self) -> bool { + match self.specific { + SpecificFragmentInfo::Image(_) | + SpecificFragmentInfo::Canvas(_) | + // TODO(stshine): According to the SVG spec, whether a SVG element has intrinsic + // aspect ratio is determined by the `preserveAspectRatio` attribute. Since for + // now SVG is far from implemented, we simply choose the default behavior that + // the intrinsic aspect ratio is preserved. + // https://svgwg.org/svg2-draft/coords.html#PreserveAspectRatioAttribute + SpecificFragmentInfo::Svg(_) => + self.intrinsic_width() != Au(0) && self.intrinsic_height() != Au(0), + _ => false + } + } + + /// CSS 2.1 § 10.3.2 & 10.6.2 Calculate the used width and height of a replaced element. + /// When a parameter is `None` it means the specified size in certain direction + /// is unconstrained. The inline containing size can also be `None` since this + /// method is also used for calculating intrinsic inline size contribution. + pub fn calculate_replaced_sizes(&self, + containing_inline_size: Option<Au>, + containing_block_size: Option<Au>) + -> (Au, Au) { + let (intrinsic_inline_size, intrinsic_block_size) = if self.style.writing_mode.is_vertical() { + (self.intrinsic_height(), self.intrinsic_width()) + } else { + (self.intrinsic_width(), self.intrinsic_height()) + }; + + // Make sure the size we used here is for content box since they may be + // transferred by the intrinsic aspect ratio. + let inline_size = style_length(self.style.content_inline_size(), containing_inline_size) + .map(|x| x - self.box_sizing_boundary(Direction::Inline)); + let block_size = style_length(self.style.content_block_size(), containing_block_size) + .map(|x| x - self.box_sizing_boundary(Direction::Block)); + let inline_constraint = self.size_constraint(containing_inline_size, Direction::Inline); + let block_constraint = self.size_constraint(containing_block_size, Direction::Block); + + // https://drafts.csswg.org/css-images-3/#default-sizing + match (inline_size, block_size) { + // If the specified size is a definite width and height, the concrete + // object size is given that width and height. + (MaybeAuto::Specified(inline_size), MaybeAuto::Specified(block_size)) => + (inline_constraint.clamp(inline_size), block_constraint.clamp(block_size)), + + // If the specified size is only a width or height (but not both) + // then the concrete object size is given that specified width or + // height. The other dimension is calculated as follows: + // + // If the object has an intrinsic aspect ratio, the missing dimension + // of the concrete object size is calculated using the intrinsic + // aspect ratio and the present dimension. + // + // Otherwise, if the missing dimension is present in the object’s intrinsic + // dimensions, the missing dimension is taken from the object’s intrinsic + // dimensions. Otherwise it is taken from the default object size. + (MaybeAuto::Specified(inline_size), MaybeAuto::Auto) => { + let inline_size = inline_constraint.clamp(inline_size); + let block_size = if self.has_intrinsic_ratio() { + // Note: We can not precompute the ratio and store it as a float, because + // doing so may result one pixel difference in calculation for certain + // images, thus make some tests fail. + inline_size * intrinsic_block_size.0 / intrinsic_inline_size.0 + } else { + intrinsic_block_size + }; + (inline_size, block_constraint.clamp(block_size)) + } + (MaybeAuto::Auto, MaybeAuto::Specified(block_size)) => { + let block_size = block_constraint.clamp(block_size); + let inline_size = if self.has_intrinsic_ratio() { + block_size * intrinsic_inline_size.0 / intrinsic_block_size.0 + } else { + intrinsic_inline_size + }; + (inline_constraint.clamp(inline_size), block_size) + } + // https://drafts.csswg.org/css2/visudet.html#min-max-widths + (MaybeAuto::Auto, MaybeAuto::Auto) => { + if self.has_intrinsic_ratio() { + // This approch follows the spirit of cover and contain constraint. + // https://drafts.csswg.org/css-images-3/#cover-contain + + // First, create two rectangles that keep aspect ratio while may be clamped + // by the contraints; + let first_isize = inline_constraint.clamp(intrinsic_inline_size); + let first_bsize = first_isize * intrinsic_block_size.0 / intrinsic_inline_size.0; + let second_bsize = block_constraint.clamp(intrinsic_block_size); + let second_isize = second_bsize * intrinsic_inline_size.0 / intrinsic_block_size.0; + + let (inline_size, block_size) = match (first_isize.cmp(&intrinsic_inline_size) , + second_isize.cmp(&intrinsic_inline_size)) { + (Ordering::Equal, Ordering::Equal) => + (first_isize, first_bsize), + // When only one rectangle is clamped, use it; + (Ordering::Equal, _) => + (second_isize, second_bsize), + (_, Ordering::Equal) => + (first_isize, first_bsize), + // When both rectangles grow (smaller than min sizes), + // Choose the larger one; + (Ordering::Greater, Ordering::Greater) => + if first_isize > second_isize { + (first_isize, first_bsize) + } else { + (second_isize, second_bsize) + }, + // When both rectangles shrink (larger than max sizes), + // Choose the smaller one; + (Ordering::Less, Ordering::Less) => + if first_isize > second_isize { + (second_isize, second_bsize) + } else { + (first_isize, first_bsize) + }, + // It does not matter which we choose here, because both sizes + // will be clamped to constraint; + (Ordering::Less, Ordering::Greater) | (Ordering::Greater, Ordering::Less) => + (first_isize, first_bsize) + }; + // Clamp the result and we are done :-) + (inline_constraint.clamp(inline_size), block_constraint.clamp(block_size)) + } else { + (inline_constraint.clamp(intrinsic_inline_size), + block_constraint.clamp(intrinsic_block_size)) + } + } + } + } + + /// Return a size constraint that can be used the clamp size in given direction. + /// To take `box-sizing: border-box` into account, the `border_padding` field + /// must be initialized first. + /// + /// TODO(stshine): Maybe there is a more convenient way. + pub fn size_constraint(&self, containing_size: Option<Au>, direction: Direction) -> SizeConstraint { + let (style_min_size, style_max_size) = match direction { + Direction::Inline => (self.style.min_inline_size(), self.style.max_inline_size()), + Direction::Block => (self.style.min_block_size(), self.style.max_block_size()) + }; + + let border = if self.style().get_position().box_sizing == box_sizing::T::border_box { + Some(self.border_padding.start_end(direction)) + } else { + None + }; + + SizeConstraint::new(containing_size, style_min_size, style_max_size, border) + } + /// Returns a guess as to the distances from the margin edge of this fragment to its content /// in the inline direction. This will generally be correct unless percentages are involved. /// @@ -1254,7 +1168,7 @@ impl Fragment { } /// Returns the border width in given direction if this fragment has property - /// 'box-sizing: border-box'. The `border_padding` field should have been initialized. + /// 'box-sizing: border-box'. The `border_padding` field must have been initialized. pub fn box_sizing_boundary(&self, direction: Direction) -> Au { match (self.style().get_position().box_sizing, direction) { (box_sizing::T::border_box, Direction::Inline) => { @@ -1531,7 +1445,6 @@ impl Fragment { match self.specific { SpecificFragmentInfo::Generic | SpecificFragmentInfo::GeneratedContent(_) | - SpecificFragmentInfo::Iframe(_) | SpecificFragmentInfo::Table | SpecificFragmentInfo::TableCell | SpecificFragmentInfo::TableColumn(_) | @@ -1548,66 +1461,42 @@ impl Fragment { let block_flow = info.flow_ref.as_block(); result.union_block(&block_flow.base.intrinsic_inline_sizes) } - SpecificFragmentInfo::Image(ref mut image_fragment_info) => { - let mut image_inline_size = match self.style.content_inline_size() { - LengthOrPercentageOrAuto::Auto | - LengthOrPercentageOrAuto::Percentage(_) => { - image_fragment_info.image_inline_size() - } - LengthOrPercentageOrAuto::Length(length) => length, - LengthOrPercentageOrAuto::Calc(calc) => calc.length(), - }; - - image_inline_size = max(model::specified(self.style.min_inline_size(), Au(0)), image_inline_size); - if let Some(max) = model::specified_or_none(self.style.max_inline_size(), Au(0)) { - image_inline_size = min(image_inline_size, max) - } - - result.union_block(&IntrinsicISizes { - minimum_inline_size: image_inline_size, - preferred_inline_size: image_inline_size, - }); - } - SpecificFragmentInfo::Canvas(ref mut canvas_fragment_info) => { - let mut canvas_inline_size = match self.style.content_inline_size() { + SpecificFragmentInfo::Image(_) | + SpecificFragmentInfo::Canvas(_) | + SpecificFragmentInfo::Iframe(_) | + SpecificFragmentInfo::Svg(_) => { + let mut inline_size = match self.style.content_inline_size() { LengthOrPercentageOrAuto::Auto | LengthOrPercentageOrAuto::Percentage(_) => { - canvas_fragment_info.canvas_inline_size() + // We have to initialize the `border_padding` field first to make + // the size constraints work properly. + // TODO(stshine): Find a cleaner way to do this. + let padding = self.style.logical_padding(); + self.border_padding.inline_start = model::specified(padding.inline_start, Au(0)); + self.border_padding.inline_end = model::specified(padding.inline_end, Au(0)); + self.border_padding.block_start = model::specified(padding.block_start, Au(0)); + self.border_padding.block_end = model::specified(padding.block_end, Au(0)); + let border = self.border_width(); + self.border_padding.inline_start += border.inline_start; + self.border_padding.inline_end += border.inline_end; + self.border_padding.block_start += border.block_start; + self.border_padding.block_end += border.block_end; + let (result_inline, _) = self.calculate_replaced_sizes(None, None); + result_inline } LengthOrPercentageOrAuto::Length(length) => length, LengthOrPercentageOrAuto::Calc(calc) => calc.length(), }; - canvas_inline_size = max(model::specified(self.style.min_inline_size(), Au(0)), canvas_inline_size); - if let Some(max) = model::specified_or_none(self.style.max_inline_size(), Au(0)) { - canvas_inline_size = min(canvas_inline_size, max) - } + let size_constraint = self.size_constraint(None, Direction::Inline); + inline_size = size_constraint.clamp(inline_size); result.union_block(&IntrinsicISizes { - minimum_inline_size: canvas_inline_size, - preferred_inline_size: canvas_inline_size, + minimum_inline_size: inline_size, + preferred_inline_size: inline_size, }); } - SpecificFragmentInfo::Svg(ref mut svg_fragment_info) => { - let mut svg_inline_size = match self.style.content_inline_size() { - LengthOrPercentageOrAuto::Auto | - LengthOrPercentageOrAuto::Percentage(_) => { - svg_fragment_info.svg_inline_size() - } - LengthOrPercentageOrAuto::Length(length) => length, - LengthOrPercentageOrAuto::Calc(calc) => calc.length(), - }; - - svg_inline_size = max(model::specified(self.style.min_inline_size(), Au(0)), svg_inline_size); - if let Some(max) = model::specified_or_none(self.style.max_inline_size(), Au(0)) { - svg_inline_size = min(svg_inline_size, max) - } - result.union_block(&IntrinsicISizes { - minimum_inline_size: svg_inline_size, - preferred_inline_size: svg_inline_size, - }); - } SpecificFragmentInfo::ScannedText(ref text_fragment_info) => { let range = &text_fragment_info.range; @@ -1676,45 +1565,6 @@ impl Fragment { } } - /// TODO: What exactly does this function return? Why is it Au(0) for - /// `SpecificFragmentInfo::Generic`? - pub fn content_inline_size(&self) -> Au { - match self.specific { - SpecificFragmentInfo::Generic | - SpecificFragmentInfo::GeneratedContent(_) | - SpecificFragmentInfo::Iframe(_) | - SpecificFragmentInfo::Table | - SpecificFragmentInfo::TableCell | - SpecificFragmentInfo::TableRow | - SpecificFragmentInfo::TableWrapper | - SpecificFragmentInfo::Multicol | - SpecificFragmentInfo::MulticolColumn | - SpecificFragmentInfo::InlineBlock(_) | - SpecificFragmentInfo::InlineAbsoluteHypothetical(_) | - SpecificFragmentInfo::InlineAbsolute(_) => Au(0), - SpecificFragmentInfo::Canvas(ref canvas_fragment_info) => { - canvas_fragment_info.replaced_image_fragment_info.computed_inline_size() - } - SpecificFragmentInfo::Svg(ref svg_fragment_info) => { - svg_fragment_info.replaced_image_fragment_info.computed_inline_size() - } - SpecificFragmentInfo::Image(ref image_fragment_info) => { - image_fragment_info.replaced_image_fragment_info.computed_inline_size() - } - SpecificFragmentInfo::ScannedText(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 - } - SpecificFragmentInfo::TableColumn(_) => { - panic!("Table column fragments do not have inline_size") - } - SpecificFragmentInfo::UnscannedText(_) => { - panic!("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 @@ -1991,10 +1841,8 @@ impl Fragment { SpecificFragmentInfo::Svg(_) => {} }; - let style = &*self.style; - let noncontent_inline_size = self.border_padding.inline_start_end(); - match self.specific { + // Inline blocks SpecificFragmentInfo::InlineAbsoluteHypothetical(ref mut info) => { let block_flow = FlowRef::deref_mut(&mut info.flow_ref).as_mut_block(); block_flow.base.position.size.inline = @@ -2019,54 +1867,24 @@ impl Fragment { block_flow.base.block_container_inline_size = self.border_box.size.inline; block_flow.base.block_container_writing_mode = self.style.writing_mode; } + + // Text SpecificFragmentInfo::ScannedText(ref info) => { // Scanned text fragments will have already had their content inline-sizes assigned // by this point. - self.border_box.size.inline = info.content_size.inline + noncontent_inline_size + self.border_box.size.inline = info.content_size.inline + + self.border_padding.inline_start_end(); } - SpecificFragmentInfo::Image(ref mut image_fragment_info) => { - let fragment_inline_size = image_fragment_info.image_inline_size(); - let fragment_block_size = image_fragment_info.image_block_size(); - self.border_box.size.inline = - image_fragment_info.replaced_image_fragment_info - .calculate_replaced_inline_size(style, - noncontent_inline_size, - container_inline_size, - container_block_size, - fragment_inline_size, - fragment_block_size); - } - SpecificFragmentInfo::Canvas(ref mut canvas_fragment_info) => { - let fragment_inline_size = canvas_fragment_info.canvas_inline_size(); - let fragment_block_size = canvas_fragment_info.canvas_block_size(); - self.border_box.size.inline = - canvas_fragment_info.replaced_image_fragment_info - .calculate_replaced_inline_size(style, - noncontent_inline_size, - container_inline_size, - container_block_size, - fragment_inline_size, - fragment_block_size); - } - SpecificFragmentInfo::Svg(ref mut svg_fragment_info) => { - let fragment_inline_size = svg_fragment_info.svg_inline_size(); - let fragment_block_size = svg_fragment_info.svg_block_size(); - self.border_box.size.inline = - svg_fragment_info.replaced_image_fragment_info - .calculate_replaced_inline_size(style, - noncontent_inline_size, - container_inline_size, - container_block_size, - fragment_inline_size, - fragment_block_size); - } - SpecificFragmentInfo::Iframe(ref iframe_fragment_info) => { - self.border_box.size.inline = - iframe_fragment_info.calculate_replaced_inline_size(style, - container_inline_size) + - noncontent_inline_size; + + // Replaced elements + _ if self.is_replaced() => { + let (inline_size, block_size) = + self.calculate_replaced_sizes(Some(container_inline_size), container_block_size); + self.border_box.size.inline = inline_size + self.border_padding.inline_start_end(); + self.border_box.size.block = block_size + self.border_padding.block_start_end(); } - _ => panic!("this case should have been handled above"), + + ref unhandled @ _ => panic!("this case should have been handled above: {:?}", unhandled), } } @@ -2074,7 +1892,7 @@ impl Fragment { /// been assigned first. /// /// Ideally, this should follow CSS 2.1 § 10.6.2. - pub fn assign_replaced_block_size_if_necessary(&mut self, containing_block_block_size: Option<Au>) { + pub fn assign_replaced_block_size_if_necessary(&mut self) { match self.specific { SpecificFragmentInfo::Generic | SpecificFragmentInfo::GeneratedContent(_) | @@ -2100,48 +1918,16 @@ impl Fragment { SpecificFragmentInfo::Svg(_) => {} } - let style = &*self.style; - let noncontent_block_size = self.border_padding.block_start_end(); - match self.specific { - SpecificFragmentInfo::Image(ref mut image_fragment_info) => { - let fragment_inline_size = image_fragment_info.image_inline_size(); - let fragment_block_size = image_fragment_info.image_block_size(); - self.border_box.size.block = - image_fragment_info.replaced_image_fragment_info - .calculate_replaced_block_size(style, - noncontent_block_size, - containing_block_block_size, - fragment_inline_size, - fragment_block_size); - } - SpecificFragmentInfo::Canvas(ref mut canvas_fragment_info) => { - let fragment_inline_size = canvas_fragment_info.canvas_inline_size(); - let fragment_block_size = canvas_fragment_info.canvas_block_size(); - self.border_box.size.block = - canvas_fragment_info.replaced_image_fragment_info - .calculate_replaced_block_size(style, - noncontent_block_size, - containing_block_block_size, - fragment_inline_size, - fragment_block_size); - } - SpecificFragmentInfo::Svg(ref mut svg_fragment_info) => { - let fragment_inline_size = svg_fragment_info.svg_inline_size(); - let fragment_block_size = svg_fragment_info.svg_block_size(); - self.border_box.size.block = - svg_fragment_info.replaced_image_fragment_info - .calculate_replaced_block_size(style, - noncontent_block_size, - containing_block_block_size, - fragment_inline_size, - fragment_block_size); - } + // Text SpecificFragmentInfo::ScannedText(ref info) => { // Scanned text fragments' content block-sizes are calculated by the text run // scanner during flow construction. - self.border_box.size.block = info.content_size.block + noncontent_block_size + self.border_box.size.block = info.content_size.block + + self.border_padding.block_start_end(); } + + // Inline blocks SpecificFragmentInfo::InlineBlock(ref mut info) => { // Not the primary fragment, so we do not take the noncontent size into account. let block_flow = FlowRef::deref_mut(&mut info.flow_ref).as_block(); @@ -2159,36 +1945,31 @@ impl Fragment { self.border_box.size.block = block_flow.base.position.size.block + block_flow.fragment.margin.block_start_end() } - SpecificFragmentInfo::Iframe(ref info) => { - self.border_box.size.block = - info.calculate_replaced_block_size(style, containing_block_block_size) + - noncontent_block_size; - } - _ => panic!("should have been handled above"), + + // Replaced elements + _ if self.is_replaced() => {}, + + ref unhandled @ _ => panic!("should have been handled above: {:?}", unhandled), } } - /// Returns true if this fragment is replaced content or an inline-block or false otherwise. - pub fn is_replaced_or_inline_block(&self) -> bool { + /// Returns true if this fragment is replaced content. + pub fn is_replaced(&self) -> bool { match self.specific { - SpecificFragmentInfo::Canvas(_) | SpecificFragmentInfo::Iframe(_) | + SpecificFragmentInfo::Canvas(_) | SpecificFragmentInfo::Image(_) | - SpecificFragmentInfo::InlineAbsoluteHypothetical(_) | - SpecificFragmentInfo::InlineBlock(_) | SpecificFragmentInfo::Svg(_) => true, - SpecificFragmentInfo::Generic | - SpecificFragmentInfo::GeneratedContent(_) | - SpecificFragmentInfo::InlineAbsolute(_) | - SpecificFragmentInfo::Table | - SpecificFragmentInfo::TableCell | - SpecificFragmentInfo::TableColumn(_) | - SpecificFragmentInfo::TableRow | - SpecificFragmentInfo::TableWrapper | - SpecificFragmentInfo::Multicol | - SpecificFragmentInfo::MulticolColumn | - SpecificFragmentInfo::ScannedText(_) | - SpecificFragmentInfo::UnscannedText(_) => false, + _ => false + } + } + + /// Returns true if this fragment is replaced content or an inline-block or false otherwise. + pub fn is_replaced_or_inline_block(&self) -> bool { + match self.specific { + SpecificFragmentInfo::InlineAbsoluteHypothetical(_) | + SpecificFragmentInfo::InlineBlock(_) => true, + _ => self.is_replaced(), } } @@ -2209,10 +1990,10 @@ impl Fragment { SpecificFragmentInfo::Canvas(_) | SpecificFragmentInfo::Iframe(_) | SpecificFragmentInfo::Image(_) | SpecificFragmentInfo::Svg(_) | SpecificFragmentInfo::Generic | SpecificFragmentInfo::GeneratedContent(_) => { - let ascent = self.border_box.size.block + self.margin.block_start; + let ascent = self.border_box.size.block + self.margin.block_end; InlineMetrics { - space_above_baseline: ascent, - space_below_baseline: self.margin.block_end, + space_above_baseline: ascent + self.margin.block_start, + space_below_baseline: Au(0), ascent: ascent, } } |