diff options
20 files changed, 401 insertions, 669 deletions
diff --git a/components/layout/block.rs b/components/layout/block.rs index 45de8786778..6db30de9bec 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -42,12 +42,11 @@ use flow::IS_ABSOLUTELY_POSITIONED; use flow_list::FlowList; use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, Overflow}; use fragment::{IS_INLINE_FLEX_ITEM, IS_BLOCK_FLEX_ITEM}; -use fragment::SpecificFragmentInfo; use gfx::display_list::{ClippingRegion, StackingContext}; use gfx_traits::ScrollRootId; use gfx_traits::print_tree::PrintTree; use layout_debug; -use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo, MaybeAuto}; +use model::{AdjoiningMargins, CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo, MaybeAuto}; use model::{specified, specified_or_none}; use sequential; use serde::{Serialize, Serializer}; @@ -555,7 +554,7 @@ impl BlockFlow { /// relevant margins for this Block. pub fn block_type(&self) -> BlockType { if self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) { - if self.is_replaced_content() { + if self.fragment.is_replaced() { BlockType::AbsoluteReplaced } else { BlockType::AbsoluteNonReplaced @@ -563,19 +562,19 @@ impl BlockFlow { } else if self.is_inline_flex_item() { BlockType::InlineFlexItem } else if self.base.flags.is_float() { - if self.is_replaced_content() { + if self.fragment.is_replaced() { BlockType::FloatReplaced } else { BlockType::FloatNonReplaced } } else if self.is_inline_block() { - if self.is_replaced_content() { + if self.fragment.is_replaced() { BlockType::InlineBlockReplaced } else { BlockType::InlineBlockNonReplaced } } else { - if self.is_replaced_content() { + if self.fragment.is_replaced() { BlockType::Replaced } else { BlockType::NonReplaced @@ -668,22 +667,6 @@ impl BlockFlow { } } - /// Return true if this has a replaced fragment. - /// - /// Text, Images, Inline Block and Canvas - /// (https://html.spec.whatwg.org/multipage/#replaced-elements) fragments are considered as - /// replaced fragments. - fn is_replaced_content(&self) -> bool { - match self.fragment.specific { - SpecificFragmentInfo::ScannedText(_) | - SpecificFragmentInfo::Svg(_) | - SpecificFragmentInfo::Image(_) | - SpecificFragmentInfo::Canvas(_) | - SpecificFragmentInfo::InlineBlock(_) => true, - _ => false, - } - } - /// Return shrink-to-fit inline-size. /// /// This is where we use the preferred inline-sizes and minimum inline-sizes @@ -1267,11 +1250,11 @@ impl BlockFlow { let available_block_size = containing_block_block_size - self.fragment.border_padding.block_start_end(); - if self.is_replaced_content() { + if self.fragment.is_replaced() { // Calculate used value of block-size just like we do for inline replaced elements. // TODO: Pass in the containing block block-size when Fragment's // assign-block-size can handle it correctly. - self.fragment.assign_replaced_block_size_if_necessary(Some(containing_block_block_size)); + self.fragment.assign_replaced_block_size_if_necessary(); // TODO: Right now, this content block-size value includes the // margin because of erroneous block-size calculation in fragment. // Check this when that has been fixed. @@ -1896,16 +1879,22 @@ impl Flow for BlockFlow { fn fragment(&mut self, layout_context: &LayoutContext, fragmentation_context: Option<FragmentationContext>) -> Option<Arc<Flow>> { - if self.is_replaced_content() { + if self.fragment.is_replaced() { let _scope = layout_debug_scope!("assign_replaced_block_size_if_necessary {:x}", self.base.debug_id()); // Assign block-size for fragment if it is an image fragment. - let containing_block_block_size = - self.base.block_container_explicit_block_size; - self.fragment.assign_replaced_block_size_if_necessary(containing_block_block_size); + self.fragment.assign_replaced_block_size_if_necessary(); if !self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) { self.base.position.size.block = self.fragment.border_box.size.block; + let mut block_start = AdjoiningMargins::from_margin(self.fragment.margin.block_start); + let block_end = AdjoiningMargins::from_margin(self.fragment.margin.block_end); + if self.fragment.border_box.size.block == Au(0) { + block_start.union(block_end); + self.base.collapsible_margins = CollapsibleMargins::CollapseThrough(block_start); + } else { + self.base.collapsible_margins = CollapsibleMargins::Collapse(block_start, block_end); + } self.base.restyle_damage.remove(REFLOW_OUT_OF_FLOW | REFLOW); self.fragment.restyle_damage.remove(REFLOW_OUT_OF_FLOW | REFLOW); } @@ -2870,7 +2859,7 @@ impl ISizeAndMarginsComputer for AbsoluteReplaced { fragment.assign_replaced_inline_size_if_necessary(containing_block_inline_size, container_block_size); // For replaced absolute flow, the rest of the constraint solving will // take inline-size to be specified as the value computed here. - MaybeAuto::Specified(fragment.content_inline_size()) + MaybeAuto::Specified(fragment.content_box().size.inline) } fn containing_block_inline_size(&self, @@ -2929,7 +2918,7 @@ impl ISizeAndMarginsComputer for BlockReplaced { fragment.assign_replaced_inline_size_if_necessary(parent_flow_inline_size, container_block_size); // For replaced block flow, the rest of the constraint solving will // take inline-size to be specified as the value computed here. - MaybeAuto::Specified(fragment.content_inline_size()) + MaybeAuto::Specified(fragment.content_box().size.inline) } } @@ -2987,7 +2976,7 @@ impl ISizeAndMarginsComputer for FloatReplaced { fragment.assign_replaced_inline_size_if_necessary(parent_flow_inline_size, container_block_size); // For replaced block flow, the rest of the constraint solving will // take inline-size to be specified as the value computed here. - MaybeAuto::Specified(fragment.content_inline_size()) + MaybeAuto::Specified(fragment.content_box().size.inline) } } @@ -3075,7 +3064,7 @@ impl ISizeAndMarginsComputer for InlineBlockReplaced { fragment.assign_replaced_inline_size_if_necessary(parent_flow_inline_size, container_block_size); // For replaced block flow, the rest of the constraint solving will // take inline-size to be specified as the value computed here. - MaybeAuto::Specified(fragment.content_inline_size()) + MaybeAuto::Specified(fragment.content_box().size.inline) } } diff --git a/components/layout/construct.rs b/components/layout/construct.rs index f3309b97b75..b25923d19c2 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -346,14 +346,12 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> SpecificFragmentInfo::Iframe(IframeFragmentInfo::new(node)) } Some(LayoutNodeType::Element(LayoutElementType::HTMLImageElement)) => { - let image_info = box ImageFragmentInfo::new(node, - node.image_url(), + let image_info = box ImageFragmentInfo::new(node.image_url(), &self.layout_context.shared); SpecificFragmentInfo::Image(image_info) } Some(LayoutNodeType::Element(LayoutElementType::HTMLObjectElement)) => { - let image_info = box ImageFragmentInfo::new(node, - node.object_data(), + let image_info = box ImageFragmentInfo::new(node.object_data(), &self.layout_context.shared); SpecificFragmentInfo::Image(image_info) } @@ -372,11 +370,11 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> } Some(LayoutNodeType::Element(LayoutElementType::HTMLCanvasElement)) => { let data = node.canvas_data().unwrap(); - SpecificFragmentInfo::Canvas(box CanvasFragmentInfo::new(node, data, self.style_context())) + SpecificFragmentInfo::Canvas(box CanvasFragmentInfo::new(data)) } Some(LayoutNodeType::Element(LayoutElementType::SVGSVGElement)) => { let data = node.svg_data().unwrap(); - SpecificFragmentInfo::Svg(box SvgFragmentInfo::new(node, data, self.style_context())) + SpecificFragmentInfo::Svg(box SvgFragmentInfo::new(data)) } _ => { // This includes pseudo-elements. @@ -1207,8 +1205,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode> let flotation = FloatKind::from_property(flotation); let marker_fragments = match node.style(self.style_context()).get_list().list_style_image { Either::First(ref url_value) => { - let image_info = box ImageFragmentInfo::new(node, - url_value.url().map(|u| u.clone()), + let image_info = box ImageFragmentInfo::new(url_value.url().map(|u| u.clone()), &self.layout_context.shared); vec![Fragment::new(node, SpecificFragmentInfo::Image(image_info), self.layout_context)] } diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 0c34a147655..60b1ecedc19 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -1489,57 +1489,51 @@ impl FragmentDisplayListBuilding for Fragment { } } SpecificFragmentInfo::Canvas(ref canvas_fragment_info) => { - let width = canvas_fragment_info.replaced_image_fragment_info - .computed_inline_size.map_or(0, |w| w.to_px() as usize); - let height = canvas_fragment_info.replaced_image_fragment_info - .computed_block_size.map_or(0, |h| h.to_px() as usize); - if width > 0 && height > 0 { - let computed_width = canvas_fragment_info.canvas_inline_size().to_px(); - let computed_height = canvas_fragment_info.canvas_block_size().to_px(); - - let canvas_data = match canvas_fragment_info.ipc_renderer { - Some(ref ipc_renderer) => { - let ipc_renderer = ipc_renderer.lock().unwrap(); - let (sender, receiver) = ipc::channel().unwrap(); - ipc_renderer.send(CanvasMsg::FromLayout( - FromLayoutMsg::SendData(sender))).unwrap(); - receiver.recv().unwrap() - }, - None => return, - }; + let computed_width = canvas_fragment_info.dom_width.to_px(); + let computed_height = canvas_fragment_info.dom_height.to_px(); + + let canvas_data = match canvas_fragment_info.ipc_renderer { + Some(ref ipc_renderer) => { + let ipc_renderer = ipc_renderer.lock().unwrap(); + let (sender, receiver) = ipc::channel().unwrap(); + ipc_renderer.send(CanvasMsg::FromLayout( + FromLayoutMsg::SendData(sender))).unwrap(); + receiver.recv().unwrap() + }, + None => return, + }; + + let base = state.create_base_display_item( + &stacking_relative_content_box, + clip, + self.node, + self.style.get_cursor(Cursor::Default), + DisplayListSection::Content); + let display_item = match canvas_data { + CanvasData::Image(canvas_data) => { + DisplayItem::Image(box ImageDisplayItem { + base: base, + webrender_image: WebRenderImageInfo { + width: computed_width as u32, + height: computed_height as u32, + format: PixelFormat::RGBA8, + key: Some(canvas_data.image_key), + }, + image_data: None, + stretch_size: stacking_relative_content_box.size, + tile_spacing: Size2D::zero(), + image_rendering: image_rendering::T::auto, + }) + } + CanvasData::WebGL(context_id) => { + DisplayItem::WebGL(box WebGLDisplayItem { + base: base, + context_id: context_id, + }) + } + }; - let base = state.create_base_display_item( - &stacking_relative_content_box, - clip, - self.node, - self.style.get_cursor(Cursor::Default), - DisplayListSection::Content); - let display_item = match canvas_data { - CanvasData::Image(canvas_data) => { - DisplayItem::Image(box ImageDisplayItem { - base: base, - webrender_image: WebRenderImageInfo { - width: computed_width as u32, - height: computed_height as u32, - format: PixelFormat::RGBA8, - key: Some(canvas_data.image_key), - }, - image_data: None, - stretch_size: stacking_relative_content_box.size, - tile_spacing: Size2D::zero(), - image_rendering: image_rendering::T::auto, - }) - } - CanvasData::WebGL(context_id) => { - DisplayItem::WebGL(box WebGLDisplayItem { - base: base, - context_id: context_id, - }) - } - }; - - state.add_display_item(display_item); - } + state.add_display_item(display_item); } SpecificFragmentInfo::UnscannedText(_) => { panic!("Shouldn't see unscanned fragments here.") diff --git a/components/layout/flex.rs b/components/layout/flex.rs index d84373a7c21..7b169674363 100644 --- a/components/layout/flex.rs +++ b/components/layout/flex.rs @@ -19,7 +19,7 @@ use fragment::{Fragment, FragmentBorderBoxIterator, Overflow}; use gfx::display_list::StackingContext; use gfx_traits::ScrollRootId; use layout_debug; -use model::{IntrinsicISizes, MaybeAuto, MinMaxConstraint}; +use model::{IntrinsicISizes, MaybeAuto, SizeConstraint}; use model::{specified, specified_or_none}; use std::cmp::{max, min}; use std::ops::Range; @@ -38,7 +38,7 @@ use style::values::computed::{LengthOrPercentageOrAutoOrContent, LengthOrPercent #[derive(Debug)] enum AxisSize { Definite(Au), - MinMax(MinMaxConstraint), + MinMax(SizeConstraint), Infinite, } @@ -62,7 +62,7 @@ impl AxisSize { } } LengthOrPercentageOrAuto::Auto => { - AxisSize::MinMax(MinMaxConstraint::new(content_size, min, max)) + AxisSize::MinMax(SizeConstraint::new(content_size, min, max, None)) } } } 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, } } diff --git a/components/layout/inline.rs b/components/layout/inline.rs index 57eb16f1b84..cefb77bb592 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -1394,11 +1394,9 @@ impl Flow for InlineFlow { debug!("assign_block_size_inline: floats in: {:?}", self.base.floats); // Assign the block-size and late-computed inline-sizes for the inline fragments. - let containing_block_block_size = - self.base.block_container_explicit_block_size; for fragment in &mut self.fragments.fragments { fragment.update_late_computed_replaced_inline_size_if_necessary(); - fragment.assign_replaced_block_size_if_necessary(containing_block_block_size); + fragment.assign_replaced_block_size_if_necessary(); } // Reset our state, so that we handle incremental reflow correctly. diff --git a/components/layout/list_item.rs b/components/layout/list_item.rs index b3f8df9752d..20cc7bfa850 100644 --- a/components/layout/list_item.rs +++ b/components/layout/list_item.rs @@ -111,9 +111,7 @@ impl Flow for ListItemFlow { &mut layout_context.font_context(), &*self.block_flow.fragment.style); for marker in &mut self.marker_fragments { - let containing_block_block_size = - self.block_flow.base.block_container_explicit_block_size; - marker.assign_replaced_block_size_if_necessary(containing_block_block_size); + marker.assign_replaced_block_size_if_necessary(); let marker_inline_metrics = marker.aligned_inline_metrics(layout_context, &marker_line_metrics, Some(&marker_line_metrics)); diff --git a/components/layout/model.rs b/components/layout/model.rs index a0693e0d8f8..89847e0190b 100644 --- a/components/layout/model.rs +++ b/components/layout/model.rs @@ -436,6 +436,21 @@ impl MaybeAuto { } } +/// Receive an optional container size and return used value for width or height. +/// +/// `style_length`: content size as given in the CSS. +pub fn style_length(style_length: LengthOrPercentageOrAuto, + container_size: Option<Au>) -> MaybeAuto { + match container_size { + Some(length) => MaybeAuto::from_style(style_length, length), + None => if let LengthOrPercentageOrAuto::Length(length) = style_length { + MaybeAuto::Specified(length) + } else { + MaybeAuto::Auto + } + } +} + pub fn specified_or_none(length: LengthOrPercentageOrNone, containing_length: Au) -> Option<Au> { match length { LengthOrPercentageOrNone::None => None, @@ -504,64 +519,60 @@ impl ToGfxMatrix for ComputedMatrix { } } -// https://drafts.csswg.org/css2/visudet.html#min-max-widths -// https://drafts.csswg.org/css2/visudet.html#min-max-heights -/// A min or max constraint -/// -/// A `max` of `None` is equivalent to no limmit for the size in the given -/// dimension. The `min` is >= 0, as negative values are illegal and by -/// default `min` is 0. -#[derive(Debug)] -pub struct MinMaxConstraint { - min: Au, - max: Option<Au> +/// A min-size and max-size constraint. The constructor has a optional `border` +/// parameter, and when it is present the constraint will be subtracted. This is +/// used to adjust the constraint for `box-sizing: border-box`, and when you do so +/// make sure the size you want to clamp is intended to be used for content box. +#[derive(Clone, Copy, Debug)] +pub struct SizeConstraint { + min_size: Au, + max_size: Option<Au>, } -impl MinMaxConstraint { - /// Create a `MinMaxConstraint` for a dimension given the min, max, and content box size for - /// an axis - pub fn new(content_size: Option<Au>, min: LengthOrPercentage, - max: LengthOrPercentageOrNone) -> MinMaxConstraint { - let min = match min { - LengthOrPercentage::Length(length) => length, - LengthOrPercentage::Percentage(percent) => { - match content_size { - Some(size) => size.scale_by(percent), - None => Au(0), - } - }, - LengthOrPercentage::Calc(calc) => { - match content_size { - Some(size) => size.scale_by(calc.percentage()), - None => Au(0), - } +impl SizeConstraint { + /// Create a `SizeConstraint` for an axis. + pub fn new(container_size: Option<Au>, + min_size: LengthOrPercentage, + max_size: LengthOrPercentageOrNone, + border: Option<Au>) -> SizeConstraint { + let mut min_size = match container_size { + Some(container_size) => specified(min_size, container_size), + None => if let LengthOrPercentage::Length(length) = min_size { + length + } else { + Au(0) } }; - let max = match max { - LengthOrPercentageOrNone::Length(length) => Some(length), - LengthOrPercentageOrNone::Percentage(percent) => { - content_size.map(|size| size.scale_by(percent)) - }, - LengthOrPercentageOrNone::Calc(calc) => { - content_size.map(|size| size.scale_by(calc.percentage())) + let mut max_size = match container_size { + Some(container_size) => specified_or_none(max_size, container_size), + None => if let LengthOrPercentageOrNone::Length(length) = max_size { + Some(length) + } else { + None } - LengthOrPercentageOrNone::None => None, }; + // Make sure max size is not smaller than min size. + max_size = max_size.map(|x| max(x, min_size)); + + if let Some(border) = border { + min_size = max((min_size - border), Au(0)); + max_size = max_size.map(|x| max(x - border, Au(0))); + } - MinMaxConstraint { - min: min, - max: max + SizeConstraint { + min_size: min_size, + max_size: max_size } } - /// Clamp the given size by the given `min` and `max` constraints. + /// Clamp the given size by the given min size and max size constraint. pub fn clamp(&self, other: Au) -> Au { - if other < self.min { - self.min + if other < self.min_size { + self.min_size } else { - match self.max { - Some(max) if max < other => max, + match self.max_size { + Some(max_size) if max_size < other => max_size, _ => other } } diff --git a/tests/wpt/metadata-css/css21_dev/html4/block-replaced-height-004.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/block-replaced-height-004.htm.ini deleted file mode 100644 index 77d7591b943..00000000000 --- a/tests/wpt/metadata-css/css21_dev/html4/block-replaced-height-004.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[block-replaced-height-004.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata-css/css21_dev/html4/block-replaced-height-005.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/block-replaced-height-005.htm.ini deleted file mode 100644 index 809b647e787..00000000000 --- a/tests/wpt/metadata-css/css21_dev/html4/block-replaced-height-005.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[block-replaced-height-005.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata-css/css21_dev/html4/block-replaced-height-007.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/block-replaced-height-007.htm.ini deleted file mode 100644 index 4e7ca96d91b..00000000000 --- a/tests/wpt/metadata-css/css21_dev/html4/block-replaced-height-007.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[block-replaced-height-007.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata-css/css21_dev/html4/c43-rpl-bbx-002.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/c43-rpl-bbx-002.htm.ini deleted file mode 100644 index faf1586d11f..00000000000 --- a/tests/wpt/metadata-css/css21_dev/html4/c43-rpl-bbx-002.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[c43-rpl-bbx-002.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata-css/css21_dev/html4/c44-ln-box-003.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/c44-ln-box-003.htm.ini deleted file mode 100644 index 1606d37ec43..00000000000 --- a/tests/wpt/metadata-css/css21_dev/html4/c44-ln-box-003.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[c44-ln-box-003.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata-css/css21_dev/html4/float-replaced-height-004.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/float-replaced-height-004.htm.ini deleted file mode 100644 index 79e9eef8833..00000000000 --- a/tests/wpt/metadata-css/css21_dev/html4/float-replaced-height-004.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[float-replaced-height-004.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata-css/css21_dev/html4/float-replaced-height-005.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/float-replaced-height-005.htm.ini deleted file mode 100644 index 2c55c9b3713..00000000000 --- a/tests/wpt/metadata-css/css21_dev/html4/float-replaced-height-005.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[float-replaced-height-005.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata-css/css21_dev/html4/float-replaced-height-007.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/float-replaced-height-007.htm.ini deleted file mode 100644 index 8a2cfba07f0..00000000000 --- a/tests/wpt/metadata-css/css21_dev/html4/float-replaced-height-007.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[float-replaced-height-007.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata-css/css21_dev/html4/inline-block-replaced-height-004.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/inline-block-replaced-height-004.htm.ini deleted file mode 100644 index 1562237ce6e..00000000000 --- a/tests/wpt/metadata-css/css21_dev/html4/inline-block-replaced-height-004.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[inline-block-replaced-height-004.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata-css/css21_dev/html4/inline-block-replaced-height-005.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/inline-block-replaced-height-005.htm.ini deleted file mode 100644 index 5932e779dc7..00000000000 --- a/tests/wpt/metadata-css/css21_dev/html4/inline-block-replaced-height-005.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[inline-block-replaced-height-005.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata-css/css21_dev/html4/inline-block-replaced-height-007.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/inline-block-replaced-height-007.htm.ini deleted file mode 100644 index 5fbf45656e7..00000000000 --- a/tests/wpt/metadata-css/css21_dev/html4/inline-block-replaced-height-007.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[inline-block-replaced-height-007.htm] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata-css/css21_dev/html4/max-height-percentage-003.htm.ini b/tests/wpt/metadata-css/css21_dev/html4/max-height-percentage-003.htm.ini deleted file mode 100644 index 0b2d7e75a54..00000000000 --- a/tests/wpt/metadata-css/css21_dev/html4/max-height-percentage-003.htm.ini +++ /dev/null @@ -1,3 +0,0 @@ -[max-height-percentage-003.htm] - type: reftest - expected: FAIL |