Auto merge of #14490 - stshine:replaced-size, r=emilio

layout: Unify size calculation of replaced elements

<!-- Please describe your changes on the following line: -->

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: -->
- [ ] `./mach build -d` does not report any errors
- [ ] `./mach test-tidy` does not report any errors
- [ ] These changes fix #__ (github issue number if applicable).

<!-- Either: -->
- [ ] There are tests for these changes OR
- [ ] These changes do not require tests because _____

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/14490)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2016-12-12 03:25:03 -08:00 committed by GitHub
commit 1993b6e812
20 changed files with 399 additions and 667 deletions

View file

@ -42,12 +42,11 @@ use flow::IS_ABSOLUTELY_POSITIONED;
use flow_list::FlowList; use flow_list::FlowList;
use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, Overflow}; use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, Overflow};
use fragment::{IS_INLINE_FLEX_ITEM, IS_BLOCK_FLEX_ITEM}; use fragment::{IS_INLINE_FLEX_ITEM, IS_BLOCK_FLEX_ITEM};
use fragment::SpecificFragmentInfo;
use gfx::display_list::{ClippingRegion, StackingContext}; use gfx::display_list::{ClippingRegion, StackingContext};
use gfx_traits::ScrollRootId; use gfx_traits::ScrollRootId;
use gfx_traits::print_tree::PrintTree; use gfx_traits::print_tree::PrintTree;
use layout_debug; use layout_debug;
use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo, MaybeAuto}; use model::{AdjoiningMargins, CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo, MaybeAuto};
use model::{specified, specified_or_none}; use model::{specified, specified_or_none};
use sequential; use sequential;
use serde::{Serialize, Serializer}; use serde::{Serialize, Serializer};
@ -555,7 +554,7 @@ impl BlockFlow {
/// relevant margins for this Block. /// relevant margins for this Block.
pub fn block_type(&self) -> BlockType { pub fn block_type(&self) -> BlockType {
if self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) { if self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) {
if self.is_replaced_content() { if self.fragment.is_replaced() {
BlockType::AbsoluteReplaced BlockType::AbsoluteReplaced
} else { } else {
BlockType::AbsoluteNonReplaced BlockType::AbsoluteNonReplaced
@ -563,19 +562,19 @@ impl BlockFlow {
} else if self.is_inline_flex_item() { } else if self.is_inline_flex_item() {
BlockType::InlineFlexItem BlockType::InlineFlexItem
} else if self.base.flags.is_float() { } else if self.base.flags.is_float() {
if self.is_replaced_content() { if self.fragment.is_replaced() {
BlockType::FloatReplaced BlockType::FloatReplaced
} else { } else {
BlockType::FloatNonReplaced BlockType::FloatNonReplaced
} }
} else if self.is_inline_block() { } else if self.is_inline_block() {
if self.is_replaced_content() { if self.fragment.is_replaced() {
BlockType::InlineBlockReplaced BlockType::InlineBlockReplaced
} else { } else {
BlockType::InlineBlockNonReplaced BlockType::InlineBlockNonReplaced
} }
} else { } else {
if self.is_replaced_content() { if self.fragment.is_replaced() {
BlockType::Replaced BlockType::Replaced
} else { } else {
BlockType::NonReplaced 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. /// Return shrink-to-fit inline-size.
/// ///
/// This is where we use the preferred inline-sizes and minimum inline-sizes /// 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 - let available_block_size = containing_block_block_size -
self.fragment.border_padding.block_start_end(); 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. // 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 // TODO: Pass in the containing block block-size when Fragment's
// assign-block-size can handle it correctly. // 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 // TODO: Right now, this content block-size value includes the
// margin because of erroneous block-size calculation in fragment. // margin because of erroneous block-size calculation in fragment.
// Check this when that has been fixed. // Check this when that has been fixed.
@ -1896,16 +1879,22 @@ impl Flow for BlockFlow {
fn fragment(&mut self, layout_context: &LayoutContext, fn fragment(&mut self, layout_context: &LayoutContext,
fragmentation_context: Option<FragmentationContext>) fragmentation_context: Option<FragmentationContext>)
-> Option<Arc<Flow>> { -> 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}", let _scope = layout_debug_scope!("assign_replaced_block_size_if_necessary {:x}",
self.base.debug_id()); self.base.debug_id());
// Assign block-size for fragment if it is an image fragment. // Assign block-size for fragment if it is an image fragment.
let containing_block_block_size = self.fragment.assign_replaced_block_size_if_necessary();
self.base.block_container_explicit_block_size;
self.fragment.assign_replaced_block_size_if_necessary(containing_block_block_size);
if !self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) { if !self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) {
self.base.position.size.block = self.fragment.border_box.size.block; 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.base.restyle_damage.remove(REFLOW_OUT_OF_FLOW | REFLOW);
self.fragment.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); 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 // For replaced absolute flow, the rest of the constraint solving will
// take inline-size to be specified as the value computed here. // 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, 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); 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 // For replaced block flow, the rest of the constraint solving will
// take inline-size to be specified as the value computed here. // 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); 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 // For replaced block flow, the rest of the constraint solving will
// take inline-size to be specified as the value computed here. // 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); 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 // For replaced block flow, the rest of the constraint solving will
// take inline-size to be specified as the value computed here. // take inline-size to be specified as the value computed here.
MaybeAuto::Specified(fragment.content_inline_size()) MaybeAuto::Specified(fragment.content_box().size.inline)
} }
} }

View file

@ -346,14 +346,12 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
SpecificFragmentInfo::Iframe(IframeFragmentInfo::new(node)) SpecificFragmentInfo::Iframe(IframeFragmentInfo::new(node))
} }
Some(LayoutNodeType::Element(LayoutElementType::HTMLImageElement)) => { Some(LayoutNodeType::Element(LayoutElementType::HTMLImageElement)) => {
let image_info = box ImageFragmentInfo::new(node, let image_info = box ImageFragmentInfo::new(node.image_url(),
node.image_url(),
&self.layout_context.shared); &self.layout_context.shared);
SpecificFragmentInfo::Image(image_info) SpecificFragmentInfo::Image(image_info)
} }
Some(LayoutNodeType::Element(LayoutElementType::HTMLObjectElement)) => { Some(LayoutNodeType::Element(LayoutElementType::HTMLObjectElement)) => {
let image_info = box ImageFragmentInfo::new(node, let image_info = box ImageFragmentInfo::new(node.object_data(),
node.object_data(),
&self.layout_context.shared); &self.layout_context.shared);
SpecificFragmentInfo::Image(image_info) SpecificFragmentInfo::Image(image_info)
} }
@ -372,11 +370,11 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
} }
Some(LayoutNodeType::Element(LayoutElementType::HTMLCanvasElement)) => { Some(LayoutNodeType::Element(LayoutElementType::HTMLCanvasElement)) => {
let data = node.canvas_data().unwrap(); 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)) => { Some(LayoutNodeType::Element(LayoutElementType::SVGSVGElement)) => {
let data = node.svg_data().unwrap(); 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. // This includes pseudo-elements.
@ -1207,8 +1205,7 @@ impl<'a, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode>
let flotation = FloatKind::from_property(flotation); let flotation = FloatKind::from_property(flotation);
let marker_fragments = match node.style(self.style_context()).get_list().list_style_image { let marker_fragments = match node.style(self.style_context()).get_list().list_style_image {
Either::First(ref url_value) => { Either::First(ref url_value) => {
let image_info = box ImageFragmentInfo::new(node, let image_info = box ImageFragmentInfo::new(url_value.url().map(|u| u.clone()),
url_value.url().map(|u| u.clone()),
&self.layout_context.shared); &self.layout_context.shared);
vec![Fragment::new(node, SpecificFragmentInfo::Image(image_info), self.layout_context)] vec![Fragment::new(node, SpecificFragmentInfo::Image(image_info), self.layout_context)]
} }

View file

@ -1489,13 +1489,8 @@ impl FragmentDisplayListBuilding for Fragment {
} }
} }
SpecificFragmentInfo::Canvas(ref canvas_fragment_info) => { SpecificFragmentInfo::Canvas(ref canvas_fragment_info) => {
let width = canvas_fragment_info.replaced_image_fragment_info let computed_width = canvas_fragment_info.dom_width.to_px();
.computed_inline_size.map_or(0, |w| w.to_px() as usize); let computed_height = canvas_fragment_info.dom_height.to_px();
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 { let canvas_data = match canvas_fragment_info.ipc_renderer {
Some(ref ipc_renderer) => { Some(ref ipc_renderer) => {
@ -1540,7 +1535,6 @@ impl FragmentDisplayListBuilding for Fragment {
state.add_display_item(display_item); state.add_display_item(display_item);
} }
}
SpecificFragmentInfo::UnscannedText(_) => { SpecificFragmentInfo::UnscannedText(_) => {
panic!("Shouldn't see unscanned fragments here.") panic!("Shouldn't see unscanned fragments here.")
} }

View file

@ -19,7 +19,7 @@ use fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
use gfx::display_list::StackingContext; use gfx::display_list::StackingContext;
use gfx_traits::ScrollRootId; use gfx_traits::ScrollRootId;
use layout_debug; use layout_debug;
use model::{IntrinsicISizes, MaybeAuto, MinMaxConstraint}; use model::{IntrinsicISizes, MaybeAuto, SizeConstraint};
use model::{specified, specified_or_none}; use model::{specified, specified_or_none};
use std::cmp::{max, min}; use std::cmp::{max, min};
use std::ops::Range; use std::ops::Range;
@ -38,7 +38,7 @@ use style::values::computed::{LengthOrPercentageOrAutoOrContent, LengthOrPercent
#[derive(Debug)] #[derive(Debug)]
enum AxisSize { enum AxisSize {
Definite(Au), Definite(Au),
MinMax(MinMaxConstraint), MinMax(SizeConstraint),
Infinite, Infinite,
} }
@ -62,7 +62,7 @@ impl AxisSize {
} }
} }
LengthOrPercentageOrAuto::Auto => { LengthOrPercentageOrAuto::Auto => {
AxisSize::MinMax(MinMaxConstraint::new(content_size, min, max)) AxisSize::MinMax(SizeConstraint::new(content_size, min, max, None))
} }
} }
} }

View file

@ -23,7 +23,8 @@ use inline::{InlineMetrics, LAST_FRAGMENT_OF_ELEMENT, LineMetrics};
use ipc_channel::ipc::IpcSender; use ipc_channel::ipc::IpcSender;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
use layout_debug; 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 msg::constellation_msg::PipelineId;
use net_traits::image::base::{Image, ImageMetadata}; use net_traits::image::base::{Image, ImageMetadata};
use net_traits::image_cache_thread::{ImageOrMetadataAvailable, UsePlaceholder}; 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 serde::{Serialize, Serializer};
use servo_url::ServoUrl; use servo_url::ServoUrl;
use std::borrow::ToOwned; use std::borrow::ToOwned;
use std::cmp::{max, min}; use std::cmp::{Ordering, max, min};
use std::collections::LinkedList; use std::collections::LinkedList;
use std::fmt; use std::fmt;
use std::sync::{Arc, Mutex}; 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::{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::{transform_style, vertical_align, white_space, word_break, z_index};
use style::computed_values::content::ContentItem; use style::computed_values::content::ContentItem;
use style::context::SharedStyleContext;
use style::logical_geometry::{Direction, LogicalMargin, LogicalRect, LogicalSize, WritingMode}; use style::logical_geometry::{Direction, LogicalMargin, LogicalRect, LogicalSize, WritingMode};
use style::properties::ServoComputedValues; use style::properties::ServoComputedValues;
use style::selector_parser::RestyleDamage; use style::selector_parser::RestyleDamage;
@ -51,7 +51,6 @@ use style::servo::restyle_damage::RECONSTRUCT_FLOW;
use style::str::char_is_whitespace; use style::str::char_is_whitespace;
use style::values::Either; use style::values::Either;
use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto}; use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};
use style::values::computed::LengthOrPercentageOrNone;
use text; use text;
use text::TextRunScanner; use text::TextRunScanner;
@ -59,6 +58,10 @@ use text::TextRunScanner;
static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20; static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20;
static FONT_SUPERSCRIPT_OFFSET_RATIO: f32 = 0.34; 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 /// 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 /// themselves. In general, fragments do not have a simple correspondence with CSS fragments in the
/// specification: /// 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. /// Information for generated content.
#[derive(Clone)] #[derive(Clone)]
pub enum GeneratedContentInfo { pub enum GeneratedContentInfo {
@ -326,89 +314,41 @@ impl InlineAbsoluteFragmentInfo {
#[derive(Clone)] #[derive(Clone)]
pub struct CanvasFragmentInfo { pub struct CanvasFragmentInfo {
pub replaced_image_fragment_info: ReplacedImageFragmentInfo,
pub ipc_renderer: Option<Arc<Mutex<IpcSender<CanvasMsg>>>>, pub ipc_renderer: Option<Arc<Mutex<IpcSender<CanvasMsg>>>>,
pub dom_width: Au, pub dom_width: Au,
pub dom_height: Au, pub dom_height: Au,
} }
impl CanvasFragmentInfo { impl CanvasFragmentInfo {
pub fn new<N: ThreadSafeLayoutNode>(node: &N, pub fn new(data: HTMLCanvasData) -> CanvasFragmentInfo {
data: HTMLCanvasData,
ctx: &SharedStyleContext)
-> CanvasFragmentInfo {
CanvasFragmentInfo { CanvasFragmentInfo {
replaced_image_fragment_info: ReplacedImageFragmentInfo::new(node, ctx),
ipc_renderer: data.ipc_renderer ipc_renderer: data.ipc_renderer
.map(|renderer| Arc::new(Mutex::new(renderer))), .map(|renderer| Arc::new(Mutex::new(renderer))),
dom_width: Au::from_px(data.width as i32), dom_width: Au::from_px(data.width as i32),
dom_height: Au::from_px(data.height 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)] #[derive(Clone)]
pub struct SvgFragmentInfo { pub struct SvgFragmentInfo {
pub replaced_image_fragment_info: ReplacedImageFragmentInfo,
pub dom_width: Au, pub dom_width: Au,
pub dom_height: Au, pub dom_height: Au,
} }
impl SvgFragmentInfo { impl SvgFragmentInfo {
pub fn new<N: ThreadSafeLayoutNode>(node: &N, pub fn new(data: SVGSVGData) -> SvgFragmentInfo {
data: SVGSVGData,
ctx: &SharedStyleContext)
-> SvgFragmentInfo {
SvgFragmentInfo { SvgFragmentInfo {
replaced_image_fragment_info: ReplacedImageFragmentInfo::new(node, ctx),
dom_width: Au::from_px(data.width as i32), dom_width: Au::from_px(data.width as i32),
dom_height: Au::from_px(data.height 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. /// A fragment that represents a replaced content image and its accompanying borders, shadows, etc.
#[derive(Clone)] #[derive(Clone)]
pub struct ImageFragmentInfo { pub struct ImageFragmentInfo {
/// The image held within this fragment.
pub replaced_image_fragment_info: ReplacedImageFragmentInfo,
pub image: Option<Arc<Image>>, pub image: Option<Arc<Image>>,
pub metadata: Option<ImageMetadata>, pub metadata: Option<ImageMetadata>,
} }
@ -418,7 +358,7 @@ impl ImageFragmentInfo {
/// ///
/// FIXME(pcwalton): The fact that image fragments store the cache in the fragment makes little /// FIXME(pcwalton): The fact that image fragments store the cache in the fragment makes little
/// sense to me. /// sense to me.
pub fn new<N: ThreadSafeLayoutNode>(node: &N, url: Option<ServoUrl>, pub fn new(url: Option<ServoUrl>,
shared_layout_context: &SharedLayoutContext) shared_layout_context: &SharedLayoutContext)
-> ImageFragmentInfo { -> ImageFragmentInfo {
let image_or_metadata = url.and_then(|url| { let image_or_metadata = url.and_then(|url| {
@ -438,40 +378,11 @@ impl ImageFragmentInfo {
}; };
ImageFragmentInfo { ImageFragmentInfo {
replaced_image_fragment_info: ReplacedImageFragmentInfo::new(node, &shared_layout_context.style_context),
image: image, image: image,
metadata: metadata, 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, pub fn tile_image_round(position: &mut Au,
size: &mut Au, size: &mut Au,
absolute_anchor_origin: 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 /// 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. /// size of this iframe can be communicated via the constellation to the iframe's own layout thread.
#[derive(Clone)] #[derive(Clone)]
@ -703,52 +471,6 @@ impl IframeFragmentInfo {
pipeline_id: pipeline_id, 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` /// 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 objects intrinsic
// dimensions, the missing dimension is taken from the objects 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 /// 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. /// 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 /// 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 { pub fn box_sizing_boundary(&self, direction: Direction) -> Au {
match (self.style().get_position().box_sizing, direction) { match (self.style().get_position().box_sizing, direction) {
(box_sizing::T::border_box, Direction::Inline) => { (box_sizing::T::border_box, Direction::Inline) => {
@ -1531,7 +1445,6 @@ impl Fragment {
match self.specific { match self.specific {
SpecificFragmentInfo::Generic | SpecificFragmentInfo::Generic |
SpecificFragmentInfo::GeneratedContent(_) | SpecificFragmentInfo::GeneratedContent(_) |
SpecificFragmentInfo::Iframe(_) |
SpecificFragmentInfo::Table | SpecificFragmentInfo::Table |
SpecificFragmentInfo::TableCell | SpecificFragmentInfo::TableCell |
SpecificFragmentInfo::TableColumn(_) | SpecificFragmentInfo::TableColumn(_) |
@ -1548,66 +1461,42 @@ impl Fragment {
let block_flow = info.flow_ref.as_block(); let block_flow = info.flow_ref.as_block();
result.union_block(&block_flow.base.intrinsic_inline_sizes) result.union_block(&block_flow.base.intrinsic_inline_sizes)
} }
SpecificFragmentInfo::Image(ref mut image_fragment_info) => { SpecificFragmentInfo::Image(_) |
let mut image_inline_size = match self.style.content_inline_size() { SpecificFragmentInfo::Canvas(_) |
SpecificFragmentInfo::Iframe(_) |
SpecificFragmentInfo::Svg(_) => {
let mut inline_size = match self.style.content_inline_size() {
LengthOrPercentageOrAuto::Auto | LengthOrPercentageOrAuto::Auto |
LengthOrPercentageOrAuto::Percentage(_) => { LengthOrPercentageOrAuto::Percentage(_) => {
image_fragment_info.image_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::Length(length) => length,
LengthOrPercentageOrAuto::Calc(calc) => calc.length(), LengthOrPercentageOrAuto::Calc(calc) => calc.length(),
}; };
image_inline_size = max(model::specified(self.style.min_inline_size(), Au(0)), image_inline_size); let size_constraint = self.size_constraint(None, Direction::Inline);
if let Some(max) = model::specified_or_none(self.style.max_inline_size(), Au(0)) { inline_size = size_constraint.clamp(inline_size);
image_inline_size = min(image_inline_size, max)
}
result.union_block(&IntrinsicISizes { result.union_block(&IntrinsicISizes {
minimum_inline_size: image_inline_size, minimum_inline_size: inline_size,
preferred_inline_size: image_inline_size, preferred_inline_size: inline_size,
}); });
} }
SpecificFragmentInfo::Canvas(ref mut canvas_fragment_info) => {
let mut canvas_inline_size = match self.style.content_inline_size() {
LengthOrPercentageOrAuto::Auto |
LengthOrPercentageOrAuto::Percentage(_) => {
canvas_fragment_info.canvas_inline_size()
}
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)
}
result.union_block(&IntrinsicISizes {
minimum_inline_size: canvas_inline_size,
preferred_inline_size: canvas_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) => { SpecificFragmentInfo::ScannedText(ref text_fragment_info) => {
let range = &text_fragment_info.range; 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. /// Returns the dimensions of the content box.
/// ///
/// This is marked `#[inline]` because it is frequently called when only one or two of the /// This is marked `#[inline]` because it is frequently called when only one or two of the
@ -1991,10 +1841,8 @@ impl Fragment {
SpecificFragmentInfo::Svg(_) => {} SpecificFragmentInfo::Svg(_) => {}
}; };
let style = &*self.style;
let noncontent_inline_size = self.border_padding.inline_start_end();
match self.specific { match self.specific {
// Inline blocks
SpecificFragmentInfo::InlineAbsoluteHypothetical(ref mut info) => { SpecificFragmentInfo::InlineAbsoluteHypothetical(ref mut info) => {
let block_flow = FlowRef::deref_mut(&mut info.flow_ref).as_mut_block(); let block_flow = FlowRef::deref_mut(&mut info.flow_ref).as_mut_block();
block_flow.base.position.size.inline = 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_inline_size = self.border_box.size.inline;
block_flow.base.block_container_writing_mode = self.style.writing_mode; block_flow.base.block_container_writing_mode = self.style.writing_mode;
} }
// Text
SpecificFragmentInfo::ScannedText(ref info) => { SpecificFragmentInfo::ScannedText(ref info) => {
// Scanned text fragments will have already had their content inline-sizes assigned // Scanned text fragments will have already had their content inline-sizes assigned
// by this point. // 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(); // Replaced elements
let fragment_block_size = image_fragment_info.image_block_size(); _ if self.is_replaced() => {
self.border_box.size.inline = let (inline_size, block_size) =
image_fragment_info.replaced_image_fragment_info self.calculate_replaced_sizes(Some(container_inline_size), container_block_size);
.calculate_replaced_inline_size(style, self.border_box.size.inline = inline_size + self.border_padding.inline_start_end();
noncontent_inline_size, self.border_box.size.block = block_size + self.border_padding.block_start_end();
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(); ref unhandled @ _ => panic!("this case should have been handled above: {:?}", unhandled),
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;
}
_ => panic!("this case should have been handled above"),
} }
} }
@ -2074,7 +1892,7 @@ impl Fragment {
/// been assigned first. /// been assigned first.
/// ///
/// Ideally, this should follow CSS 2.1 § 10.6.2. /// 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 { match self.specific {
SpecificFragmentInfo::Generic | SpecificFragmentInfo::Generic |
SpecificFragmentInfo::GeneratedContent(_) | SpecificFragmentInfo::GeneratedContent(_) |
@ -2100,48 +1918,16 @@ impl Fragment {
SpecificFragmentInfo::Svg(_) => {} SpecificFragmentInfo::Svg(_) => {}
} }
let style = &*self.style;
let noncontent_block_size = self.border_padding.block_start_end();
match self.specific { match self.specific {
SpecificFragmentInfo::Image(ref mut image_fragment_info) => { // Text
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);
}
SpecificFragmentInfo::ScannedText(ref info) => { SpecificFragmentInfo::ScannedText(ref info) => {
// Scanned text fragments' content block-sizes are calculated by the text run // Scanned text fragments' content block-sizes are calculated by the text run
// scanner during flow construction. // 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) => { SpecificFragmentInfo::InlineBlock(ref mut info) => {
// Not the primary fragment, so we do not take the noncontent size into account. // 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(); 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 + self.border_box.size.block = block_flow.base.position.size.block +
block_flow.fragment.margin.block_start_end() block_flow.fragment.margin.block_start_end()
} }
SpecificFragmentInfo::Iframe(ref info) => {
self.border_box.size.block = // Replaced elements
info.calculate_replaced_block_size(style, containing_block_block_size) + _ if self.is_replaced() => {},
noncontent_block_size;
ref unhandled @ _ => panic!("should have been handled above: {:?}", unhandled),
} }
_ => panic!("should have been handled above"), }
/// Returns true if this fragment is replaced content.
pub fn is_replaced(&self) -> bool {
match self.specific {
SpecificFragmentInfo::Iframe(_) |
SpecificFragmentInfo::Canvas(_) |
SpecificFragmentInfo::Image(_) |
SpecificFragmentInfo::Svg(_) => true,
_ => false
} }
} }
/// Returns true if this fragment is replaced content or an inline-block or false otherwise. /// Returns true if this fragment is replaced content or an inline-block or false otherwise.
pub fn is_replaced_or_inline_block(&self) -> bool { pub fn is_replaced_or_inline_block(&self) -> bool {
match self.specific { match self.specific {
SpecificFragmentInfo::Canvas(_) |
SpecificFragmentInfo::Iframe(_) |
SpecificFragmentInfo::Image(_) |
SpecificFragmentInfo::InlineAbsoluteHypothetical(_) | SpecificFragmentInfo::InlineAbsoluteHypothetical(_) |
SpecificFragmentInfo::InlineBlock(_) | SpecificFragmentInfo::InlineBlock(_) => true,
SpecificFragmentInfo::Svg(_) => true, _ => self.is_replaced(),
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,
} }
} }
@ -2209,10 +1990,10 @@ impl Fragment {
SpecificFragmentInfo::Canvas(_) | SpecificFragmentInfo::Iframe(_) | SpecificFragmentInfo::Canvas(_) | SpecificFragmentInfo::Iframe(_) |
SpecificFragmentInfo::Image(_) | SpecificFragmentInfo::Svg(_) | SpecificFragmentInfo::Image(_) | SpecificFragmentInfo::Svg(_) |
SpecificFragmentInfo::Generic | SpecificFragmentInfo::GeneratedContent(_) => { 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 { InlineMetrics {
space_above_baseline: ascent, space_above_baseline: ascent + self.margin.block_start,
space_below_baseline: self.margin.block_end, space_below_baseline: Au(0),
ascent: ascent, ascent: ascent,
} }
} }

View file

@ -1394,11 +1394,9 @@ impl Flow for InlineFlow {
debug!("assign_block_size_inline: floats in: {:?}", self.base.floats); debug!("assign_block_size_inline: floats in: {:?}", self.base.floats);
// Assign the block-size and late-computed inline-sizes for the inline fragments. // 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 { for fragment in &mut self.fragments.fragments {
fragment.update_late_computed_replaced_inline_size_if_necessary(); 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. // Reset our state, so that we handle incremental reflow correctly.

View file

@ -111,9 +111,7 @@ impl Flow for ListItemFlow {
&mut layout_context.font_context(), &mut layout_context.font_context(),
&*self.block_flow.fragment.style); &*self.block_flow.fragment.style);
for marker in &mut self.marker_fragments { for marker in &mut self.marker_fragments {
let containing_block_block_size = marker.assign_replaced_block_size_if_necessary();
self.block_flow.base.block_container_explicit_block_size;
marker.assign_replaced_block_size_if_necessary(containing_block_block_size);
let marker_inline_metrics = marker.aligned_inline_metrics(layout_context, let marker_inline_metrics = marker.aligned_inline_metrics(layout_context,
&marker_line_metrics, &marker_line_metrics,
Some(&marker_line_metrics)); Some(&marker_line_metrics));

View file

@ -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> { pub fn specified_or_none(length: LengthOrPercentageOrNone, containing_length: Au) -> Option<Au> {
match length { match length {
LengthOrPercentageOrNone::None => None, LengthOrPercentageOrNone::None => None,
@ -504,64 +519,60 @@ impl ToGfxMatrix for ComputedMatrix {
} }
} }
// https://drafts.csswg.org/css2/visudet.html#min-max-widths /// A min-size and max-size constraint. The constructor has a optional `border`
// https://drafts.csswg.org/css2/visudet.html#min-max-heights /// parameter, and when it is present the constraint will be subtracted. This is
/// A min or max constraint /// 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.
/// A `max` of `None` is equivalent to no limmit for the size in the given #[derive(Clone, Copy, Debug)]
/// dimension. The `min` is >= 0, as negative values are illegal and by pub struct SizeConstraint {
/// default `min` is 0. min_size: Au,
#[derive(Debug)] max_size: Option<Au>,
pub struct MinMaxConstraint {
min: Au,
max: Option<Au>
} }
impl MinMaxConstraint { impl SizeConstraint {
/// Create a `MinMaxConstraint` for a dimension given the min, max, and content box size for /// Create a `SizeConstraint` for an axis.
/// an axis pub fn new(container_size: Option<Au>,
pub fn new(content_size: Option<Au>, min: LengthOrPercentage, min_size: LengthOrPercentage,
max: LengthOrPercentageOrNone) -> MinMaxConstraint { max_size: LengthOrPercentageOrNone,
let min = match min { border: Option<Au>) -> SizeConstraint {
LengthOrPercentage::Length(length) => length, let mut min_size = match container_size {
LengthOrPercentage::Percentage(percent) => { Some(container_size) => specified(min_size, container_size),
match content_size { None => if let LengthOrPercentage::Length(length) = min_size {
Some(size) => size.scale_by(percent), length
None => Au(0),
}
},
LengthOrPercentage::Calc(calc) => {
match content_size {
Some(size) => size.scale_by(calc.percentage()),
None => 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()))
}
LengthOrPercentageOrNone::None => None,
};
MinMaxConstraint {
min: min,
max: max
}
}
/// Clamp the given size by the given `min` and `max` constraints.
pub fn clamp(&self, other: Au) -> Au {
if other < self.min {
self.min
} else { } else {
match self.max { Au(0)
Some(max) if max < other => max, }
};
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
}
};
// 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)));
}
SizeConstraint {
min_size: min_size,
max_size: max_size
}
}
/// Clamp the given size by the given min size and max size constraint.
pub fn clamp(&self, other: Au) -> Au {
if other < self.min_size {
self.min_size
} else {
match self.max_size {
Some(max_size) if max_size < other => max_size,
_ => other _ => other
} }
} }

View file

@ -1,3 +0,0 @@
[block-replaced-height-004.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[block-replaced-height-005.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[block-replaced-height-007.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[c43-rpl-bbx-002.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[c44-ln-box-003.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[float-replaced-height-004.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[float-replaced-height-005.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[float-replaced-height-007.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[inline-block-replaced-height-004.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[inline-block-replaced-height-005.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[inline-block-replaced-height-007.htm]
type: reftest
expected: FAIL

View file

@ -1,3 +0,0 @@
[max-height-percentage-003.htm]
type: reftest
expected: FAIL