Refactor how calc() clamping is done on computed values (fixes #15296)

This commit is contained in:
Anthony Ramine 2017-05-16 15:44:59 +02:00
parent f935f2da01
commit d0b9bd9c64
13 changed files with 173 additions and 154 deletions

View file

@ -46,7 +46,6 @@ use gfx_traits::print_tree::PrintTree;
use incremental::RelayoutMode; use incremental::RelayoutMode;
use layout_debug; use layout_debug;
use model::{AdjoiningMargins, CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo, MaybeAuto}; use model::{AdjoiningMargins, CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo, MaybeAuto};
use model::{specified, specified_or_none};
use sequential; use sequential;
use serde::{Serialize, Serializer}; use serde::{Serialize, Serializer};
use servo_geometry::max_rect; use servo_geometry::max_rect;
@ -326,7 +325,7 @@ impl CandidateBSizeIterator {
MaybeAuto::Specified(block_container_block_size.scale_by(percent)) MaybeAuto::Specified(block_container_block_size.scale_by(percent))
} }
(LengthOrPercentageOrAuto::Calc(calc), _) => { (LengthOrPercentageOrAuto::Calc(calc), _) => {
MaybeAuto::from_option(calc.to_computed(block_container_block_size)) MaybeAuto::from_option(calc.to_used_value(block_container_block_size))
} }
(LengthOrPercentageOrAuto::Percentage(_), None) | (LengthOrPercentageOrAuto::Percentage(_), None) |
(LengthOrPercentageOrAuto::Auto, _) => MaybeAuto::Auto, (LengthOrPercentageOrAuto::Auto, _) => MaybeAuto::Auto,
@ -337,7 +336,7 @@ impl CandidateBSizeIterator {
Some(block_container_block_size.scale_by(percent)) Some(block_container_block_size.scale_by(percent))
} }
(LengthOrPercentageOrNone::Calc(calc), _) => { (LengthOrPercentageOrNone::Calc(calc), _) => {
calc.to_computed(block_container_block_size) calc.to_used_value(block_container_block_size)
} }
(LengthOrPercentageOrNone::Percentage(_), None) | (LengthOrPercentageOrNone::Percentage(_), None) |
(LengthOrPercentageOrNone::None, _) => None, (LengthOrPercentageOrNone::None, _) => None,
@ -348,7 +347,7 @@ impl CandidateBSizeIterator {
block_container_block_size.scale_by(percent) block_container_block_size.scale_by(percent)
} }
(LengthOrPercentage::Calc(calc), _) => { (LengthOrPercentage::Calc(calc), _) => {
calc.to_computed(block_container_block_size).unwrap_or(Au(0)) calc.to_used_value(block_container_block_size).unwrap_or(Au(0))
} }
(LengthOrPercentage::Percentage(_), None) => Au(0), (LengthOrPercentage::Percentage(_), None) => Au(0),
(LengthOrPercentage::Length(length), _) => length, (LengthOrPercentage::Length(length), _) => length,
@ -1167,7 +1166,7 @@ impl BlockFlow {
match (content_block_size, containing_block_size) { match (content_block_size, containing_block_size) {
(LengthOrPercentageOrAuto::Calc(calc), _) => { (LengthOrPercentageOrAuto::Calc(calc), _) => {
calc.to_computed(containing_block_size) calc.to_used_value(containing_block_size)
} }
(LengthOrPercentageOrAuto::Length(length), _) => Some(length), (LengthOrPercentageOrAuto::Length(length), _) => Some(length),
(LengthOrPercentageOrAuto::Percentage(percent), Some(container_size)) => { (LengthOrPercentageOrAuto::Percentage(percent), Some(container_size)) => {
@ -1417,8 +1416,8 @@ impl BlockFlow {
// we know. // we know.
if kid.is_inline_flow() { if kid.is_inline_flow() {
kid.as_mut_inline().first_line_indentation = kid.as_mut_inline().first_line_indentation =
specified(self.fragment.style().get_inheritedtext().text_indent, self.fragment.style().get_inheritedtext().text_indent
containing_block_size); .to_used_value(containing_block_size);
} }
} }
} }
@ -1512,14 +1511,12 @@ impl BlockFlow {
} else { } else {
content_box.size.inline content_box.size.inline
} - self.fragment.margin.inline_start_end(); } - self.fragment.margin.inline_start_end();
let max_inline_size = specified_or_none( let max_inline_size =
self.fragment.style().max_inline_size(), self.fragment.style().max_inline_size()
self.base.block_container_inline_size .to_used_value(self.base.block_container_inline_size)
).unwrap_or(MAX_AU); .unwrap_or(MAX_AU);
let min_inline_size = specified( let min_inline_size =
self.fragment.style().min_inline_size(), self.fragment.style().min_inline_size().to_used_value(self.base.block_container_inline_size);
self.base.block_container_inline_size
);
let specified_inline_size = self.fragment.style().content_inline_size(); let specified_inline_size = self.fragment.style().content_inline_size();
let container_size = self.base.block_container_inline_size; let container_size = self.base.block_container_inline_size;
let inline_size = let inline_size =
@ -2413,8 +2410,7 @@ pub trait ISizeAndMarginsComputer {
// If the tentative used inline-size is greater than 'max-inline-size', inline-size should // If the tentative used inline-size is greater than 'max-inline-size', inline-size should
// be recalculated, but this time using the computed value of 'max-inline-size' as the // be recalculated, but this time using the computed value of 'max-inline-size' as the
// computed value for 'inline-size'. // computed value for 'inline-size'.
match specified_or_none(block.fragment().style().max_inline_size(), match block.fragment().style().max_inline_size().to_used_value(containing_block_inline_size) {
containing_block_inline_size) {
Some(max_inline_size) if max_inline_size < solution.inline_size => { Some(max_inline_size) if max_inline_size < solution.inline_size => {
input.computed_inline_size = MaybeAuto::Specified(max_inline_size); input.computed_inline_size = MaybeAuto::Specified(max_inline_size);
solution = self.solve_inline_size_constraints(block, &input); solution = self.solve_inline_size_constraints(block, &input);
@ -2425,8 +2421,8 @@ pub trait ISizeAndMarginsComputer {
// If the resulting inline-size is smaller than 'min-inline-size', inline-size should be // If the resulting inline-size is smaller than 'min-inline-size', inline-size should be
// recalculated, but this time using the value of 'min-inline-size' as the computed value // recalculated, but this time using the value of 'min-inline-size' as the computed value
// for 'inline-size'. // for 'inline-size'.
let computed_min_inline_size = specified(block.fragment().style().min_inline_size(), let computed_min_inline_size =
containing_block_inline_size); block.fragment().style().min_inline_size().to_used_value(containing_block_inline_size);
if computed_min_inline_size > solution.inline_size { if computed_min_inline_size > solution.inline_size {
input.computed_inline_size = MaybeAuto::Specified(computed_min_inline_size); input.computed_inline_size = MaybeAuto::Specified(computed_min_inline_size);
solution = self.solve_inline_size_constraints(block, &input); solution = self.solve_inline_size_constraints(block, &input);

View file

@ -33,7 +33,7 @@ use gfx_traits::{combine_id_with_fragment_type, FragmentType, StackingContextId}
use inline::{FIRST_FRAGMENT_OF_ELEMENT, InlineFlow, LAST_FRAGMENT_OF_ELEMENT}; use inline::{FIRST_FRAGMENT_OF_ELEMENT, InlineFlow, LAST_FRAGMENT_OF_ELEMENT};
use ipc_channel::ipc; use ipc_channel::ipc;
use list_item::ListItemFlow; use list_item::ListItemFlow;
use model::{self, MaybeAuto, specified}; use model::{self, MaybeAuto};
use msg::constellation_msg::BrowsingContextId; use msg::constellation_msg::BrowsingContextId;
use net_traits::image::base::PixelFormat; use net_traits::image::base::PixelFormat;
use net_traits::image_cache::UsePlaceholder; use net_traits::image_cache::UsePlaceholder;
@ -1026,10 +1026,8 @@ impl FragmentDisplayListBuilding for Fragment {
let horiz_position = *get_cyclic(&background.background_position_x.0, index); let horiz_position = *get_cyclic(&background.background_position_x.0, index);
let vert_position = *get_cyclic(&background.background_position_y.0, index); let vert_position = *get_cyclic(&background.background_position_y.0, index);
// Use `background-position` to get the offset. // Use `background-position` to get the offset.
let horizontal_position = model::specified(horiz_position, let horizontal_position = horiz_position.to_used_value(bounds.size.width - image_size.width);
bounds.size.width - image_size.width); let vertical_position = vert_position.to_used_value(bounds.size.height - image_size.height);
let vertical_position = model::specified(vert_position,
bounds.size.height - image_size.height);
// The anchor position for this background, based on both the background-attachment // The anchor position for this background, based on both the background-attachment
// and background-position properties. // and background-position properties.
@ -1185,8 +1183,8 @@ impl FragmentDisplayListBuilding for Fragment {
repeating: bool, repeating: bool,
style: &ServoComputedValues) style: &ServoComputedValues)
-> display_list::RadialGradient { -> display_list::RadialGradient {
let center = Point2D::new(specified(center.horizontal, bounds.size.width), let center = Point2D::new(center.horizontal.to_used_value(bounds.size.width),
specified(center.vertical, bounds.size.height)); center.vertical.to_used_value(bounds.size.height));
let radius = match *shape { let radius = match *shape {
GenericEndingShape::Circle(Circle::Radius(length)) => { GenericEndingShape::Circle(Circle::Radius(length)) => {
Size2D::new(length, length) Size2D::new(length, length)
@ -1195,7 +1193,7 @@ impl FragmentDisplayListBuilding for Fragment {
convert_circle_size_keyword(extent, &bounds.size, &center) convert_circle_size_keyword(extent, &bounds.size, &center)
}, },
GenericEndingShape::Ellipse(Ellipse::Radii(x, y)) => { GenericEndingShape::Ellipse(Ellipse::Radii(x, y)) => {
Size2D::new(specified(x, bounds.size.width), specified(y, bounds.size.height)) Size2D::new(x.to_used_value(bounds.size.width), y.to_used_value(bounds.size.height))
}, },
GenericEndingShape::Ellipse(Ellipse::Extent(extent)) => { GenericEndingShape::Ellipse(Ellipse::Extent(extent)) => {
convert_ellipse_size_keyword(extent, &bounds.size, &center) convert_ellipse_size_keyword(extent, &bounds.size, &center)

View file

@ -18,7 +18,6 @@ use flow::{INLINE_POSITION_IS_STATIC, IS_ABSOLUTELY_POSITIONED};
use fragment::{Fragment, FragmentBorderBoxIterator, Overflow}; use fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
use layout_debug; use layout_debug;
use model::{IntrinsicISizes, MaybeAuto, SizeConstraint}; use model::{IntrinsicISizes, MaybeAuto, SizeConstraint};
use model::{specified, specified_or_none};
use std::cmp::{max, min}; use std::cmp::{max, min};
use std::ops::Range; use std::ops::Range;
use style::computed_values::{align_content, align_self, flex_direction, flex_wrap, justify_content}; use style::computed_values::{align_content, align_self, flex_direction, flex_wrap, justify_content};
@ -52,7 +51,7 @@ impl AxisSize {
} }
} }
LengthOrPercentageOrAuto::Calc(calc) => { LengthOrPercentageOrAuto::Calc(calc) => {
match calc.to_computed(content_size) { match calc.to_used_value(content_size) {
Some(length) => AxisSize::Definite(length), Some(length) => AxisSize::Definite(length),
None => AxisSize::Infinite, None => AxisSize::Infinite,
} }
@ -79,7 +78,7 @@ fn from_flex_basis(flex_basis: LengthOrPercentageOrAutoOrContent,
(LengthOrPercentageOrAutoOrContent::Percentage(_), None) => (LengthOrPercentageOrAutoOrContent::Percentage(_), None) =>
MaybeAuto::Auto, MaybeAuto::Auto,
(LengthOrPercentageOrAutoOrContent::Calc(calc), _) => (LengthOrPercentageOrAutoOrContent::Calc(calc), _) =>
MaybeAuto::from_option(calc.to_computed(containing_length)), MaybeAuto::from_option(calc.to_used_value(containing_length)),
(LengthOrPercentageOrAutoOrContent::Content, _) => (LengthOrPercentageOrAutoOrContent::Content, _) =>
MaybeAuto::Auto, MaybeAuto::Auto,
(LengthOrPercentageOrAutoOrContent::Auto, Some(size)) => (LengthOrPercentageOrAutoOrContent::Auto, Some(size)) =>
@ -169,10 +168,11 @@ impl FlexItem {
- margin - margin
+ block.fragment.box_sizing_boundary(direction); + block.fragment.box_sizing_boundary(direction);
self.base_size = basis.specified_or_default(content_size); self.base_size = basis.specified_or_default(content_size);
self.max_size = specified_or_none(block.fragment.style.max_inline_size(), self.max_size =
containing_length).unwrap_or(MAX_AU); block.fragment.style.max_inline_size()
self.min_size = specified(block.fragment.style.min_inline_size(), .to_used_value(containing_length)
containing_length); .unwrap_or(MAX_AU);
self.min_size = block.fragment.style.min_inline_size().to_used_value(containing_length);
} }
Direction::Block => { Direction::Block => {
let basis = from_flex_basis(block.fragment.style.get_position().flex_basis, let basis = from_flex_basis(block.fragment.style.get_position().flex_basis,
@ -182,10 +182,11 @@ impl FlexItem {
- block.fragment.border_padding.block_start_end() - block.fragment.border_padding.block_start_end()
+ block.fragment.box_sizing_boundary(direction); + block.fragment.box_sizing_boundary(direction);
self.base_size = basis.specified_or_default(content_size); self.base_size = basis.specified_or_default(content_size);
self.max_size = specified_or_none(block.fragment.style.max_block_size(), self.max_size =
containing_length).unwrap_or(MAX_AU); block.fragment.style.max_block_size()
self.min_size = specified(block.fragment.style.min_block_size(), .to_used_value(containing_length)
containing_length); .unwrap_or(MAX_AU);
self.min_size = block.fragment.style.min_block_size().to_used_value(containing_length);
} }
} }
} }

View file

@ -907,8 +907,8 @@ impl Fragment {
// cascading. // cascading.
let padding = if flags.contains(INTRINSIC_INLINE_SIZE_INCLUDES_PADDING) { let padding = if flags.contains(INTRINSIC_INLINE_SIZE_INCLUDES_PADDING) {
let padding = style.logical_padding(); let padding = style.logical_padding();
(model::specified(padding.inline_start, Au(0)) + (padding.inline_start.to_used_value(Au(0)) +
model::specified(padding.inline_end, Au(0))) padding.inline_end.to_used_value(Au(0)))
} else { } else {
Au(0) Au(0)
}; };
@ -935,8 +935,8 @@ impl Fragment {
if flags.contains(INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED) { if flags.contains(INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED) {
specified = MaybeAuto::from_style(style.content_inline_size(), specified = MaybeAuto::from_style(style.content_inline_size(),
Au(0)).specified_or_zero(); Au(0)).specified_or_zero();
specified = max(model::specified(style.min_inline_size(), Au(0)), specified); specified = max(style.min_inline_size().to_used_value(Au(0)), specified);
if let Some(max) = model::specified_or_none(style.max_inline_size(), Au(0)) { if let Some(max) = style.max_inline_size().to_used_value(Au(0)) {
specified = min(specified, max) specified = min(specified, max)
} }
@ -1159,10 +1159,10 @@ impl Fragment {
let border_width = self.border_width(); let border_width = self.border_width();
SpeculatedInlineContentEdgeOffsets { SpeculatedInlineContentEdgeOffsets {
start: MaybeAuto::from_style(logical_margin.inline_start, Au(0)).specified_or_zero() + start: MaybeAuto::from_style(logical_margin.inline_start, Au(0)).specified_or_zero() +
model::specified(logical_padding.inline_start, Au(0)) + logical_padding.inline_start.to_used_value(Au(0)) +
border_width.inline_start, border_width.inline_start,
end: MaybeAuto::from_style(logical_margin.inline_end, Au(0)).specified_or_zero() + end: MaybeAuto::from_style(logical_margin.inline_end, Au(0)).specified_or_zero() +
model::specified(logical_padding.inline_end, Au(0)) + logical_padding.inline_end.to_used_value(Au(0)) +
border_width.inline_end, border_width.inline_end,
} }
} }
@ -1491,10 +1491,10 @@ impl Fragment {
// the size constraints work properly. // the size constraints work properly.
// TODO(stshine): Find a cleaner way to do this. // TODO(stshine): Find a cleaner way to do this.
let padding = self.style.logical_padding(); let padding = self.style.logical_padding();
self.border_padding.inline_start = model::specified(padding.inline_start, Au(0)); self.border_padding.inline_start = padding.inline_start.to_used_value(Au(0));
self.border_padding.inline_end = model::specified(padding.inline_end, Au(0)); self.border_padding.inline_end = padding.inline_end.to_used_value(Au(0));
self.border_padding.block_start = model::specified(padding.block_start, Au(0)); self.border_padding.block_start = padding.block_start.to_used_value(Au(0));
self.border_padding.block_end = model::specified(padding.block_end, Au(0)); self.border_padding.block_end = padding.block_end.to_used_value(Au(0));
let border = self.border_width(); let border = self.border_width();
self.border_padding.inline_start += border.inline_start; self.border_padding.inline_start += border.inline_start;
self.border_padding.inline_end += border.inline_end; self.border_padding.inline_end += border.inline_end;
@ -2847,12 +2847,14 @@ impl Fragment {
let mut transform = Matrix4D::identity(); let mut transform = Matrix4D::identity();
let transform_origin = &self.style.get_box().transform_origin; let transform_origin = &self.style.get_box().transform_origin;
let transform_origin_x = model::specified(transform_origin.horizontal, let transform_origin_x =
stacking_relative_border_box.size transform_origin.horizontal
.width).to_f32_px(); .to_used_value(stacking_relative_border_box.size.width)
let transform_origin_y = model::specified(transform_origin.vertical, .to_f32_px();
stacking_relative_border_box.size let transform_origin_y =
.height).to_f32_px(); transform_origin.vertical
.to_used_value(stacking_relative_border_box.size.height)
.to_f32_px();
let transform_origin_z = transform_origin.depth.to_f32_px(); let transform_origin_z = transform_origin.depth.to_f32_px();
let pre_transform = Matrix4D::create_translation(transform_origin_x, let pre_transform = Matrix4D::create_translation(transform_origin_x,
@ -2875,10 +2877,8 @@ impl Fragment {
Matrix4D::create_scale(sx, sy, sz) Matrix4D::create_scale(sx, sy, sz)
} }
transform::ComputedOperation::Translate(tx, ty, tz) => { transform::ComputedOperation::Translate(tx, ty, tz) => {
let tx = let tx = tx.to_used_value(stacking_relative_border_box.size.width).to_f32_px();
model::specified(tx, stacking_relative_border_box.size.width).to_f32_px(); let ty = ty.to_used_value(stacking_relative_border_box.size.height).to_f32_px();
let ty =
model::specified(ty, stacking_relative_border_box.size.height).to_f32_px();
let tz = tz.to_f32_px(); let tz = tz.to_f32_px();
Matrix4D::create_translation(tx, ty, tz) Matrix4D::create_translation(tx, ty, tz)
} }
@ -2907,10 +2907,13 @@ impl Fragment {
Either::First(length) => { Either::First(length) => {
let perspective_origin = self.style().get_box().perspective_origin; let perspective_origin = self.style().get_box().perspective_origin;
let perspective_origin = let perspective_origin =
Point2D::new(model::specified(perspective_origin.horizontal, Point2D::new(
stacking_relative_border_box.size.width).to_f32_px(), perspective_origin.horizontal
model::specified(perspective_origin.vertical, .to_used_value(stacking_relative_border_box.size.width)
stacking_relative_border_box.size.height).to_f32_px()); .to_f32_px(),
perspective_origin.vertical
.to_used_value(stacking_relative_border_box.size.height)
.to_f32_px());
let pre_transform = Matrix4D::create_translation(perspective_origin.x, let pre_transform = Matrix4D::create_translation(perspective_origin.x,
perspective_origin.y, perspective_origin.y,

View file

@ -412,7 +412,7 @@ impl MaybeAuto {
MaybeAuto::Specified(containing_length.scale_by(percent)) MaybeAuto::Specified(containing_length.scale_by(percent))
} }
LengthOrPercentageOrAuto::Calc(calc) => { LengthOrPercentageOrAuto::Calc(calc) => {
MaybeAuto::from_option(calc.to_computed(Some(containing_length))) MaybeAuto::from_option(calc.to_used_value(Some(containing_length)))
} }
LengthOrPercentageOrAuto::Length(length) => MaybeAuto::Specified(length) LengthOrPercentageOrAuto::Length(length) => MaybeAuto::Specified(length)
} }
@ -463,24 +463,6 @@ pub fn style_length(style_length: LengthOrPercentageOrAuto,
} }
} }
pub fn specified_or_none(length: LengthOrPercentageOrNone, containing_length: Au) -> Option<Au> {
match length {
LengthOrPercentageOrNone::None => None,
LengthOrPercentageOrNone::Percentage(percent) => Some(containing_length.scale_by(percent)),
LengthOrPercentageOrNone::Calc(calc) => calc.to_computed(Some(containing_length)),
LengthOrPercentageOrNone::Length(length) => Some(length),
}
}
pub fn specified(length: LengthOrPercentage, containing_length: Au) -> Au {
match length {
LengthOrPercentage::Length(length) => length,
LengthOrPercentage::Percentage(p) => containing_length.scale_by(p),
LengthOrPercentage::Calc(calc) =>
containing_length.scale_by(calc.percentage()) + calc.length(),
}
}
/// Computes a border radius size against the containing size. /// Computes a border radius size against the containing size.
/// ///
/// Note that percentages in `border-radius` are resolved against the relevant /// Note that percentages in `border-radius` are resolved against the relevant
@ -495,8 +477,8 @@ pub fn specified_border_radius(
-> Size2D<Au> -> Size2D<Au>
{ {
let generics::BorderRadiusSize(size) = radius; let generics::BorderRadiusSize(size) = radius;
let w = specified(size.width, containing_size.width); let w = size.width.to_used_value(containing_size.width);
let h = specified(size.height, containing_size.height); let h = size.height.to_used_value(containing_size.height);
Size2D::new(w, h) Size2D::new(w, h)
} }
@ -507,10 +489,10 @@ pub fn padding_from_style(style: &ServoComputedValues,
-> LogicalMargin<Au> { -> LogicalMargin<Au> {
let padding_style = style.get_padding(); let padding_style = style.get_padding();
LogicalMargin::from_physical(writing_mode, SideOffsets2D::new( LogicalMargin::from_physical(writing_mode, SideOffsets2D::new(
specified(padding_style.padding_top, containing_block_inline_size), padding_style.padding_top.to_used_value(containing_block_inline_size),
specified(padding_style.padding_right, containing_block_inline_size), padding_style.padding_right.to_used_value(containing_block_inline_size),
specified(padding_style.padding_bottom, containing_block_inline_size), padding_style.padding_bottom.to_used_value(containing_block_inline_size),
specified(padding_style.padding_left, containing_block_inline_size))) padding_style.padding_left.to_used_value(containing_block_inline_size)))
} }
/// Returns the explicitly-specified margin lengths from the given style. Percentage and auto /// Returns the explicitly-specified margin lengths from the given style. Percentage and auto
@ -559,7 +541,7 @@ impl SizeConstraint {
max_size: LengthOrPercentageOrNone, max_size: LengthOrPercentageOrNone,
border: Option<Au>) -> SizeConstraint { border: Option<Au>) -> SizeConstraint {
let mut min_size = match container_size { let mut min_size = match container_size {
Some(container_size) => specified(min_size, container_size), Some(container_size) => min_size.to_used_value(container_size),
None => if let LengthOrPercentage::Length(length) = min_size { None => if let LengthOrPercentage::Length(length) = min_size {
length length
} else { } else {
@ -568,7 +550,7 @@ impl SizeConstraint {
}; };
let mut max_size = match container_size { let mut max_size = match container_size {
Some(container_size) => specified_or_none(max_size, container_size), Some(container_size) => max_size.to_used_value(container_size),
None => if let LengthOrPercentageOrNone::Length(length) = max_size { None => if let LengthOrPercentageOrNone::Length(length) = max_size {
Some(length) Some(length)
} else { } else {

View file

@ -24,7 +24,7 @@ impl From<CalcLengthOrPercentage> for nsStyleCoord_CalcValue {
fn from(other: CalcLengthOrPercentage) -> nsStyleCoord_CalcValue { fn from(other: CalcLengthOrPercentage) -> nsStyleCoord_CalcValue {
let has_percentage = other.percentage.is_some(); let has_percentage = other.percentage.is_some();
nsStyleCoord_CalcValue { nsStyleCoord_CalcValue {
mLength: other.length.0, mLength: other.length().0,
mPercent: other.percentage.unwrap_or(0.0), mPercent: other.percentage.unwrap_or(0.0),
mHasPercent: has_percentage, mHasPercent: has_percentage,
} }
@ -38,10 +38,7 @@ impl From<nsStyleCoord_CalcValue> for CalcLengthOrPercentage {
} else { } else {
None None
}; };
CalcLengthOrPercentage { Self::new(Au(other.mLength), percentage)
length: Au(other.mLength),
percentage: percentage,
}
} }
} }

View file

@ -1035,11 +1035,9 @@ impl Animatable for CalcLengthOrPercentage {
} }
} }
Ok(CalcLengthOrPercentage { let length = self.length().add_weighted(&other.length(), self_portion, other_portion)?;
length: try!(self.length.add_weighted(&other.length, self_portion, other_portion)), let percentage = add_weighted_half(self.percentage, other.percentage, self_portion, other_portion)?;
percentage: try!(add_weighted_half(self.percentage, other.percentage, Ok(CalcLengthOrPercentage::with_clamping_mode(length, percentage, self.clamping_mode))
self_portion, other_portion)),
})
} }
#[inline] #[inline]

View file

@ -8,6 +8,7 @@ use app_units::{Au, AU_PER_PX};
use ordered_float::NotNaN; use ordered_float::NotNaN;
use std::fmt; use std::fmt;
use style_traits::ToCss; use style_traits::ToCss;
use style_traits::values::specified::AllowedLengthType;
use super::{Number, ToComputedValue, Context}; use super::{Number, ToComputedValue, Context};
use values::{Auto, CSSFloat, Either, ExtremumLength, None_, Normal, specified}; use values::{Auto, CSSFloat, Either, ExtremumLength, None_, Normal, specified};
use values::specified::length::{AbsoluteLength, FontBaseSize, FontRelativeLength, ViewportPercentageLength}; use values::specified::length::{AbsoluteLength, FontBaseSize, FontRelativeLength, ViewportPercentageLength};
@ -48,7 +49,7 @@ impl ToComputedValue for specified::Length {
fn to_computed_value(&self, context: &Context) -> Au { fn to_computed_value(&self, context: &Context) -> Au {
match *self { match *self {
specified::Length::NoCalc(l) => l.to_computed_value(context), specified::Length::NoCalc(l) => l.to_computed_value(context),
specified::Length::Calc(range, ref calc) => range.clamp(calc.to_computed_value(context).length()), specified::Length::Calc(ref calc) => calc.to_computed_value(context).length(),
} }
} }
@ -62,15 +63,35 @@ impl ToComputedValue for specified::Length {
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[allow(missing_docs)] #[allow(missing_docs)]
pub struct CalcLengthOrPercentage { pub struct CalcLengthOrPercentage {
pub length: Au, pub clamping_mode: AllowedLengthType,
length: Au,
pub percentage: Option<CSSFloat>, pub percentage: Option<CSSFloat>,
} }
impl CalcLengthOrPercentage { impl CalcLengthOrPercentage {
/// Returns a new `CalcLengthOrPercentage`.
#[inline]
pub fn new(length: Au, percentage: Option<CSSFloat>) -> Self {
Self::with_clamping_mode(length, percentage, AllowedLengthType::All)
}
/// Returns a new `CalcLengthOrPercentage` with a specific clamping mode.
#[inline]
pub fn with_clamping_mode(length: Au,
percentage: Option<CSSFloat>,
clamping_mode: AllowedLengthType)
-> Self {
Self {
clamping_mode: clamping_mode,
length: length,
percentage: percentage,
}
}
#[inline] #[inline]
#[allow(missing_docs)] #[allow(missing_docs)]
pub fn length(&self) -> Au { pub fn length(&self) -> Au {
self.length self.clamping_mode.clamp(self.length)
} }
#[inline] #[inline]
@ -81,10 +102,12 @@ impl CalcLengthOrPercentage {
/// If there are special rules for computing percentages in a value (e.g. the height property), /// If there are special rules for computing percentages in a value (e.g. the height property),
/// they apply whenever a calc() expression contains percentages. /// they apply whenever a calc() expression contains percentages.
pub fn to_computed(&self, container_len: Option<Au>) -> Option<Au> { pub fn to_used_value(&self, container_len: Option<Au>) -> Option<Au> {
match (container_len, self.percentage) { match (container_len, self.percentage) {
(Some(len), Some(percent)) => Some(self.length + len.scale_by(percent)), (Some(len), Some(percent)) => {
(_, None) => Some(self.length), Some(self.clamping_mode.clamp(self.length + len.scale_by(percent)))
},
(_, None) => Some(self.length()),
_ => None, _ => None,
} }
} }
@ -94,16 +117,10 @@ impl From<LengthOrPercentage> for CalcLengthOrPercentage {
fn from(len: LengthOrPercentage) -> CalcLengthOrPercentage { fn from(len: LengthOrPercentage) -> CalcLengthOrPercentage {
match len { match len {
LengthOrPercentage::Percentage(this) => { LengthOrPercentage::Percentage(this) => {
CalcLengthOrPercentage { CalcLengthOrPercentage::new(Au(0), Some(this))
length: Au(0),
percentage: Some(this),
}
} }
LengthOrPercentage::Length(this) => { LengthOrPercentage::Length(this) => {
CalcLengthOrPercentage { CalcLengthOrPercentage::new(this, None)
length: this,
percentage: None,
}
} }
LengthOrPercentage::Calc(this) => { LengthOrPercentage::Calc(this) => {
this this
@ -116,16 +133,10 @@ impl From<LengthOrPercentageOrAuto> for Option<CalcLengthOrPercentage> {
fn from(len: LengthOrPercentageOrAuto) -> Option<CalcLengthOrPercentage> { fn from(len: LengthOrPercentageOrAuto) -> Option<CalcLengthOrPercentage> {
match len { match len {
LengthOrPercentageOrAuto::Percentage(this) => { LengthOrPercentageOrAuto::Percentage(this) => {
Some(CalcLengthOrPercentage { Some(CalcLengthOrPercentage::new(Au(0), Some(this)))
length: Au(0),
percentage: Some(this),
})
} }
LengthOrPercentageOrAuto::Length(this) => { LengthOrPercentageOrAuto::Length(this) => {
Some(CalcLengthOrPercentage { Some(CalcLengthOrPercentage::new(this, None))
length: this,
percentage: None,
})
} }
LengthOrPercentageOrAuto::Calc(this) => { LengthOrPercentageOrAuto::Calc(this) => {
Some(this) Some(this)
@ -176,6 +187,7 @@ impl ToComputedValue for specified::CalcLengthOrPercentage {
} }
CalcLengthOrPercentage { CalcLengthOrPercentage {
clamping_mode: self.clamping_mode,
length: length, length: length,
percentage: self.percentage, percentage: self.percentage,
} }
@ -184,6 +196,7 @@ impl ToComputedValue for specified::CalcLengthOrPercentage {
#[inline] #[inline]
fn from_computed_value(computed: &CalcLengthOrPercentage) -> Self { fn from_computed_value(computed: &CalcLengthOrPercentage) -> Self {
specified::CalcLengthOrPercentage { specified::CalcLengthOrPercentage {
clamping_mode: computed.clamping_mode,
absolute: Some(computed.length), absolute: Some(computed.length),
percentage: computed.percentage, percentage: computed.percentage,
..Default::default() ..Default::default()
@ -235,6 +248,17 @@ impl LengthOrPercentage {
Calc(c) => (c.length(), NotNaN::new(c.percentage()).unwrap()), Calc(c) => (c.length(), NotNaN::new(c.percentage()).unwrap()),
} }
} }
/// Returns the used value.
pub fn to_used_value(&self, containing_length: Au) -> Au {
match *self {
LengthOrPercentage::Length(length) => length,
LengthOrPercentage::Percentage(p) => containing_length.scale_by(p),
LengthOrPercentage::Calc(ref calc) => {
calc.to_used_value(Some(containing_length)).unwrap()
},
}
}
} }
impl fmt::Debug for LengthOrPercentage { impl fmt::Debug for LengthOrPercentage {
@ -481,6 +505,18 @@ pub enum LengthOrPercentageOrNone {
None, None,
} }
impl LengthOrPercentageOrNone {
/// Returns the used value.
pub fn to_used_value(&self, containing_length: Au) -> Option<Au> {
match *self {
LengthOrPercentageOrNone::None => None,
LengthOrPercentageOrNone::Length(length) => Some(length),
LengthOrPercentageOrNone::Percentage(percent) => Some(containing_length.scale_by(percent)),
LengthOrPercentageOrNone::Calc(ref calc) => calc.to_used_value(Some(containing_length)),
}
}
}
impl fmt::Debug for LengthOrPercentageOrNone { impl fmt::Debug for LengthOrPercentageOrNone {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self { match *self {

View file

@ -12,6 +12,7 @@ use parser::ParserContext;
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
use std::fmt; use std::fmt;
use style_traits::ToCss; use style_traits::ToCss;
use style_traits::values::specified::AllowedLengthType;
use values::{CSSInteger, CSSFloat, HasViewportPercentage}; use values::{CSSInteger, CSSFloat, HasViewportPercentage};
use values::specified::{Angle, Time}; use values::specified::{Angle, Time};
use values::specified::length::{FontRelativeLength, NoCalcLength, ViewportPercentageLength}; use values::specified::length::{FontRelativeLength, NoCalcLength, ViewportPercentageLength};
@ -63,6 +64,7 @@ pub enum CalcUnit {
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[allow(missing_docs)] #[allow(missing_docs)]
pub struct CalcLengthOrPercentage { pub struct CalcLengthOrPercentage {
pub clamping_mode: AllowedLengthType,
pub absolute: Option<Au>, pub absolute: Option<Au>,
pub vw: Option<CSSFloat>, pub vw: Option<CSSFloat>,
pub vh: Option<CSSFloat>, pub vh: Option<CSSFloat>,
@ -271,8 +273,12 @@ impl CalcNode {
/// Tries to simplify this expression into a `<length>` or `<percentage`> /// Tries to simplify this expression into a `<length>` or `<percentage`>
/// value. /// value.
fn to_length_or_percentage(&self) -> Result<CalcLengthOrPercentage, ()> { fn to_length_or_percentage(&self, clamping_mode: AllowedLengthType)
let mut ret = CalcLengthOrPercentage::default(); -> Result<CalcLengthOrPercentage, ()> {
let mut ret = CalcLengthOrPercentage {
clamping_mode: clamping_mode,
.. Default::default()
};
self.add_length_or_percentage_to(&mut ret, 1.0)?; self.add_length_or_percentage_to(&mut ret, 1.0)?;
Ok(ret) Ok(ret)
} }
@ -498,21 +504,23 @@ impl CalcNode {
/// Convenience parsing function for `<length> | <percentage>`. /// Convenience parsing function for `<length> | <percentage>`.
pub fn parse_length_or_percentage( pub fn parse_length_or_percentage(
context: &ParserContext, context: &ParserContext,
input: &mut Parser) input: &mut Parser,
clamping_mode: AllowedLengthType)
-> Result<CalcLengthOrPercentage, ()> -> Result<CalcLengthOrPercentage, ()>
{ {
Self::parse(context, input, CalcUnit::LengthOrPercentage)? Self::parse(context, input, CalcUnit::LengthOrPercentage)?
.to_length_or_percentage() .to_length_or_percentage(clamping_mode)
} }
/// Convenience parsing function for `<length>`. /// Convenience parsing function for `<length>`.
pub fn parse_length( pub fn parse_length(
context: &ParserContext, context: &ParserContext,
input: &mut Parser) input: &mut Parser,
clamping_mode: AllowedLengthType)
-> Result<CalcLengthOrPercentage, ()> -> Result<CalcLengthOrPercentage, ()>
{ {
Self::parse(context, input, CalcUnit::Length)? Self::parse(context, input, CalcUnit::Length)?
.to_length_or_percentage() .to_length_or_percentage(clamping_mode)
} }
/// Convenience parsing function for `<number>`. /// Convenience parsing function for `<number>`.

View file

@ -539,7 +539,7 @@ pub enum Length {
/// A calc expression. /// A calc expression.
/// ///
/// https://drafts.csswg.org/css-values/#calc-notation /// https://drafts.csswg.org/css-values/#calc-notation
Calc(AllowedLengthType, Box<CalcLengthOrPercentage>), Calc(Box<CalcLengthOrPercentage>),
} }
impl From<NoCalcLength> for Length { impl From<NoCalcLength> for Length {
@ -553,7 +553,7 @@ impl HasViewportPercentage for Length {
fn has_viewport_percentage(&self) -> bool { fn has_viewport_percentage(&self) -> bool {
match *self { match *self {
Length::NoCalc(ref inner) => inner.has_viewport_percentage(), Length::NoCalc(ref inner) => inner.has_viewport_percentage(),
Length::Calc(_, ref calc) => calc.has_viewport_percentage(), Length::Calc(ref calc) => calc.has_viewport_percentage(),
} }
} }
} }
@ -562,7 +562,7 @@ impl ToCss for Length {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self { match *self {
Length::NoCalc(ref inner) => inner.to_css(dest), Length::NoCalc(ref inner) => inner.to_css(dest),
Length::Calc(_, ref calc) => calc.to_css(dest), Length::Calc(ref calc) => calc.to_css(dest),
} }
} }
} }
@ -637,10 +637,7 @@ impl Length {
}, },
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => Token::Function(ref name) if name.eq_ignore_ascii_case("calc") =>
input.parse_nested_block(|input| { input.parse_nested_block(|input| {
CalcNode::parse_length(context, input) CalcNode::parse_length(context, input, num_context).map(|calc| Length::Calc(Box::new(calc)))
.map(|calc| {
Length::Calc(num_context, Box::new(calc))
})
}), }),
_ => Err(()) _ => Err(())
} }
@ -770,7 +767,7 @@ impl From<Length> for LengthOrPercentage {
fn from(len: Length) -> LengthOrPercentage { fn from(len: Length) -> LengthOrPercentage {
match len { match len {
Length::NoCalc(l) => LengthOrPercentage::Length(l), Length::NoCalc(l) => LengthOrPercentage::Length(l),
Length::Calc(_, l) => LengthOrPercentage::Calc(l), Length::Calc(l) => LengthOrPercentage::Calc(l),
} }
} }
} }
@ -832,7 +829,7 @@ impl LengthOrPercentage {
Ok(LengthOrPercentage::Length(NoCalcLength::from_px(value.value))), Ok(LengthOrPercentage::Length(NoCalcLength::from_px(value.value))),
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
let calc = try!(input.parse_nested_block(|i| { let calc = try!(input.parse_nested_block(|i| {
CalcNode::parse_length_or_percentage(context, i) CalcNode::parse_length_or_percentage(context, i, num_context)
})); }));
Ok(LengthOrPercentage::Calc(Box::new(calc))) Ok(LengthOrPercentage::Calc(Box::new(calc)))
}, },
@ -986,7 +983,7 @@ impl LengthOrPercentageOrAuto {
Ok(LengthOrPercentageOrAuto::Auto), Ok(LengthOrPercentageOrAuto::Auto),
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
let calc = try!(input.parse_nested_block(|i| { let calc = try!(input.parse_nested_block(|i| {
CalcNode::parse_length_or_percentage(context, i) CalcNode::parse_length_or_percentage(context, i, num_context)
})); }));
Ok(LengthOrPercentageOrAuto::Calc(Box::new(calc))) Ok(LengthOrPercentageOrAuto::Calc(Box::new(calc)))
}, },
@ -1092,7 +1089,7 @@ impl LengthOrPercentageOrNone {
} }
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
let calc = try!(input.parse_nested_block(|i| { let calc = try!(input.parse_nested_block(|i| {
CalcNode::parse_length_or_percentage(context, i) CalcNode::parse_length_or_percentage(context, i, num_context)
})); }));
Ok(LengthOrPercentageOrNone::Calc(Box::new(calc))) Ok(LengthOrPercentageOrNone::Calc(Box::new(calc)))
}, },
@ -1169,7 +1166,7 @@ impl LengthOrPercentageOrAutoOrContent {
Ok(LengthOrPercentageOrAutoOrContent::Content), Ok(LengthOrPercentageOrAutoOrContent::Content),
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => { Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
let calc = try!(input.parse_nested_block(|i| { let calc = try!(input.parse_nested_block(|i| {
CalcNode::parse_length_or_percentage(context, i) CalcNode::parse_length_or_percentage(context, i, num_context)
})); }));
Ok(LengthOrPercentageOrAutoOrContent::Calc(Box::new(calc))) Ok(LengthOrPercentageOrAutoOrContent::Calc(Box::new(calc)))
}, },

View file

@ -234,19 +234,14 @@ impl<S: Side> ToComputedValue for PositionComponent<S> {
PositionComponent::Side(ref keyword, Some(ref length)) if !keyword.is_start() => { PositionComponent::Side(ref keyword, Some(ref length)) if !keyword.is_start() => {
match length.to_computed_value(context) { match length.to_computed_value(context) {
ComputedLengthOrPercentage::Length(length) => { ComputedLengthOrPercentage::Length(length) => {
ComputedLengthOrPercentage::Calc(CalcLengthOrPercentage { ComputedLengthOrPercentage::Calc(CalcLengthOrPercentage::new(-length, Some(1.0)))
length: -length,
percentage: Some(1.0),
})
}, },
ComputedLengthOrPercentage::Percentage(p) => { ComputedLengthOrPercentage::Percentage(p) => {
ComputedLengthOrPercentage::Percentage(1.0 - p) ComputedLengthOrPercentage::Percentage(1.0 - p)
}, },
ComputedLengthOrPercentage::Calc(calc) => { ComputedLengthOrPercentage::Calc(calc) => {
ComputedLengthOrPercentage::Calc(CalcLengthOrPercentage { let p = 1. - calc.percentage.unwrap_or(0.);
length: -calc.length, ComputedLengthOrPercentage::Calc(CalcLengthOrPercentage::new(-calc.length(), Some(p)))
percentage: Some(1.0 - calc.percentage.unwrap_or(0.)),
})
}, },
} }
}, },

View file

@ -189,6 +189,13 @@ pub mod specified {
NonNegative NonNegative
} }
impl Default for AllowedLengthType {
#[inline]
fn default() -> Self {
AllowedLengthType::All
}
}
impl AllowedLengthType { impl AllowedLengthType {
/// Whether value is valid for this allowed length type. /// Whether value is valid for this allowed length type.
#[inline] #[inline]

View file

@ -5,17 +5,18 @@
use app_units::Au; use app_units::Au;
use style::attr::{AttrValue, LengthOrPercentageOrAuto, parse_length}; use style::attr::{AttrValue, LengthOrPercentageOrAuto, parse_length};
use style::values::computed::CalcLengthOrPercentage; use style::values::computed::CalcLengthOrPercentage;
use style_traits::values::specified::AllowedLengthType;
#[test] #[test]
fn test_length_calc() { fn test_length_calc() {
let calc = CalcLengthOrPercentage { length: Au(10), percentage: Some(0.2) }; let calc = CalcLengthOrPercentage::new(Au(10), Some(0.2));
assert_eq!(calc.to_computed(Some(Au(10))), Some(Au(12))); assert_eq!(calc.to_used_value(Some(Au(10))), Some(Au(12)));
assert_eq!(calc.to_computed(Some(Au(0))), Some(Au(10))); assert_eq!(calc.to_used_value(Some(Au(0))), Some(Au(10)));
assert_eq!(calc.to_computed(None), None); assert_eq!(calc.to_used_value(None), None);
let calc = CalcLengthOrPercentage { length: Au(10), percentage: None }; let calc = CalcLengthOrPercentage::new(Au(10), None);
assert_eq!(calc.to_computed(Some(Au(0))), Some(Au(10))); assert_eq!(calc.to_used_value(Some(Au(0))), Some(Au(10)));
assert_eq!(calc.to_computed(None), Some(Au(10))); assert_eq!(calc.to_used_value(None), Some(Au(10)));
} }
#[test] #[test]