mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
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:
commit
1993b6e812
20 changed files with 399 additions and 667 deletions
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1489,57 +1489,51 @@ 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) => {
|
||||||
let ipc_renderer = ipc_renderer.lock().unwrap();
|
let ipc_renderer = ipc_renderer.lock().unwrap();
|
||||||
let (sender, receiver) = ipc::channel().unwrap();
|
let (sender, receiver) = ipc::channel().unwrap();
|
||||||
ipc_renderer.send(CanvasMsg::FromLayout(
|
ipc_renderer.send(CanvasMsg::FromLayout(
|
||||||
FromLayoutMsg::SendData(sender))).unwrap();
|
FromLayoutMsg::SendData(sender))).unwrap();
|
||||||
receiver.recv().unwrap()
|
receiver.recv().unwrap()
|
||||||
},
|
},
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
let base = state.create_base_display_item(
|
let base = state.create_base_display_item(
|
||||||
&stacking_relative_content_box,
|
&stacking_relative_content_box,
|
||||||
clip,
|
clip,
|
||||||
self.node,
|
self.node,
|
||||||
self.style.get_cursor(Cursor::Default),
|
self.style.get_cursor(Cursor::Default),
|
||||||
DisplayListSection::Content);
|
DisplayListSection::Content);
|
||||||
let display_item = match canvas_data {
|
let display_item = match canvas_data {
|
||||||
CanvasData::Image(canvas_data) => {
|
CanvasData::Image(canvas_data) => {
|
||||||
DisplayItem::Image(box ImageDisplayItem {
|
DisplayItem::Image(box ImageDisplayItem {
|
||||||
base: base,
|
base: base,
|
||||||
webrender_image: WebRenderImageInfo {
|
webrender_image: WebRenderImageInfo {
|
||||||
width: computed_width as u32,
|
width: computed_width as u32,
|
||||||
height: computed_height as u32,
|
height: computed_height as u32,
|
||||||
format: PixelFormat::RGBA8,
|
format: PixelFormat::RGBA8,
|
||||||
key: Some(canvas_data.image_key),
|
key: Some(canvas_data.image_key),
|
||||||
},
|
},
|
||||||
image_data: None,
|
image_data: None,
|
||||||
stretch_size: stacking_relative_content_box.size,
|
stretch_size: stacking_relative_content_box.size,
|
||||||
tile_spacing: Size2D::zero(),
|
tile_spacing: Size2D::zero(),
|
||||||
image_rendering: image_rendering::T::auto,
|
image_rendering: image_rendering::T::auto,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
CanvasData::WebGL(context_id) => {
|
CanvasData::WebGL(context_id) => {
|
||||||
DisplayItem::WebGL(box WebGLDisplayItem {
|
DisplayItem::WebGL(box WebGLDisplayItem {
|
||||||
base: base,
|
base: base,
|
||||||
context_id: context_id,
|
context_id: context_id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
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.")
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,9 +358,9 @@ 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| {
|
||||||
shared_layout_context.get_or_request_image_or_meta(url, UsePlaceholder::Yes)
|
shared_layout_context.get_or_request_image_or_meta(url, UsePlaceholder::Yes)
|
||||||
});
|
});
|
||||||
|
@ -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 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
|
/// 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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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),
|
} else {
|
||||||
}
|
Au(0)
|
||||||
},
|
|
||||||
LengthOrPercentage::Calc(calc) => {
|
|
||||||
match content_size {
|
|
||||||
Some(size) => size.scale_by(calc.percentage()),
|
|
||||||
None => Au(0),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let max = match max {
|
let mut max_size = match container_size {
|
||||||
LengthOrPercentageOrNone::Length(length) => Some(length),
|
Some(container_size) => specified_or_none(max_size, container_size),
|
||||||
LengthOrPercentageOrNone::Percentage(percent) => {
|
None => if let LengthOrPercentageOrNone::Length(length) = max_size {
|
||||||
content_size.map(|size| size.scale_by(percent))
|
Some(length)
|
||||||
},
|
} else {
|
||||||
LengthOrPercentageOrNone::Calc(calc) => {
|
None
|
||||||
content_size.map(|size| size.scale_by(calc.percentage()))
|
|
||||||
}
|
}
|
||||||
LengthOrPercentageOrNone::None => None,
|
|
||||||
};
|
};
|
||||||
|
// Make sure max size is not smaller than min size.
|
||||||
|
max_size = max_size.map(|x| max(x, min_size));
|
||||||
|
|
||||||
MinMaxConstraint {
|
if let Some(border) = border {
|
||||||
min: min,
|
min_size = max((min_size - border), Au(0));
|
||||||
max: max
|
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` and `max` constraints.
|
/// Clamp the given size by the given min size and max size constraint.
|
||||||
pub fn clamp(&self, other: Au) -> Au {
|
pub fn clamp(&self, other: Au) -> Au {
|
||||||
if other < self.min {
|
if other < self.min_size {
|
||||||
self.min
|
self.min_size
|
||||||
} else {
|
} else {
|
||||||
match self.max {
|
match self.max_size {
|
||||||
Some(max) if max < other => max,
|
Some(max_size) if max_size < other => max_size,
|
||||||
_ => other
|
_ => other
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[block-replaced-height-004.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[block-replaced-height-005.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[block-replaced-height-007.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[c43-rpl-bbx-002.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[c44-ln-box-003.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[float-replaced-height-004.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[float-replaced-height-005.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[float-replaced-height-007.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[inline-block-replaced-height-004.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[inline-block-replaced-height-005.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[inline-block-replaced-height-007.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[max-height-percentage-003.htm]
|
|
||||||
type: reftest
|
|
||||||
expected: FAIL
|
|
Loading…
Add table
Add a link
Reference in a new issue